gloss now works correctly
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 27 Oct 2002 13:15:43 +0000 (13:15 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 27 Oct 2002 13:15:43 +0000 (13:15 +0000)
major speedups to realtime lighting mode (it's now actually playable on my machine)

world lights are now handled by r_shadow.c instead of the lights list in the world model
world lights can be ingame edited/loaded/saved (the file name ends in .rtlights), this is all controlled by r_editlights* commands/cvars
realtime lighting cvars renamed from r_light_* to r_shadow_*
r_shadow_realtime takes the place of r_shadows modes 2 and 3, mode 1 is realtime lighting, mode 2 is realtime lighting plus visible shadow volumes overlaid
r_shadow_lightingmode variable added (updated each frame) indicating whether or not realtime lighting is active
changed how player model and chase_active is handled (player model is now considered a forced RENDER_EXTERIORMODEL), so player now casts shadows
R_Shadow_Volume no longer takes a vertex parameter, it is implied that it is in varray_vertex (since all the code relied on this fact anyway)
R_Shadow_Volume's code has been split out into reusable functions (R_Shadow_ProjectVertices, R_Shadow_MakeTriangleShadowFlags, R_Shadow_BuildShadowVolumeTriangles)
R_Shadow_RenderLighting split into R_Shadow_DiffuseLighting and R_Shadow_SpecularLighting since sometimes it's usable to use more than one call to diffuse lighting (colormapped models)
R_Model_Alias_DrawLight now handles colormapping
added Light_CullBox and LightAndVis_CullBox functions for culling shadow volumes
R_DrawWorldLightShadowVolume has been moved to r_shadow.c
Mod_ShadowMesh_Begin now takes an initial number of triangles to hold, number of triangles in each additional shadowmesh is now based on previous mesh
R_Model_Brush_DrawLightForSurfaceList added to greatly reduce wasted lighting
rewrote visibility logic in R_Model_Brush_DrawLight, now uses different approachs for bmodels and world
lights are now pvs culled (according to what leafs are visible to the viewer, and what leafs are visible to the light)
R_Shadow_Stage_EraseShadowVolumes now returns an int, indicating whether or not to draw the shadow volumes again to erase
rewrote how texture info based on entity and time is updated (it is now a function called R_UpdateTextureInfo)
fixed a ton of bugs relating to animated textures and transparency of bmodels (also changed texture_t currentframe to no longer be an array)
disabled building of surface neighbors (oh sure I made it a lot faster and stuff, but it's still not practical, and is no longer necessary)
changed parameters of model_t DrawLight function to no longer take distbias and subtract
Mod_ProcessLightList is no longer called
mlight_t fields relating to shadow volumes have been commented out
Mod_ValidateElements function added
notes on enormous cost added to Mod_BuildTextureVectorsAndNormals (188 float operations per triangle, and 39 float operations per vertex)
added (disabled) code for allocating and freeing shadowmeshs in pieces, just for obsessive memory corruption detection (none was found)
Mod_ShadowMesh_AddTriangle and Mod_ShadowMesh_AddMesh functions added, and Mod_ShadowMesh_AddPolygon now uses AddTriangle (this is a slowdown but I'm not sure it matters that it is, it fills out meshs more completely)
added (disabled) code for Mod_ShadowMesh_Finish to work without reallocating meshs
R_Shadow_ResizeTriangleFacingLight and R_Shadow_ResizeShadowElements added
R_Shadow_RenderShadowMeshVolume added (properly renders shadowmesh_t chains, fixing all the bugs with static shadow volume rendering)
r_shadow_blankbumptexture is now 1x1
r_shadow_blankglosstexture and r_shadow_blankwhitetexture added (both 1x1)
added a 4 texture path for r_shadowtexture3d 0 mode
when static shadow volumes are constructed for world lights, it is now done by constructing a combined mesh of all lit geometry, and then casting shadows from that (this reduces shadow volume edges greatly, since separate polygons become a continuous mesh during the mesh creation)

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

12 files changed:
cl_main.c
gl_models.c
gl_rmain.c
gl_rsurf.c
model_alias.c
model_brush.c
model_brush.h
model_shared.c
model_shared.h
r_light.c
r_shadow.c
r_shadow.h

index 819dae8..59babb2 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -546,6 +546,8 @@ static void CL_RelinkNetworkEntities()
 
                VectorCopy (neworg, ent->render.origin);
                ent->render.flags = ent->state_current.flags;
+               if (i == cl.viewentity)
+                       ent->render.flags |= RENDER_EXTERIORMODEL;
                ent->render.effects = effects = ent->state_current.effects;
                if (ent->state_current.flags & RENDER_COLORMAPPED)
                        ent->render.colormap = ent->state_current.colormap;
@@ -722,16 +724,8 @@ static void CL_RelinkNetworkEntities()
                        CL_AllocDlight (&ent->render, v, 1, dlightcolor[0], dlightcolor[1], dlightcolor[2], 0, 0);
                }
 
-               if (chase_active.integer)
-               {
-                       if (ent->render.flags & RENDER_VIEWMODEL)
-                               continue;
-               }
-               else
-               {
-                       if (i == cl.viewentity || (ent->render.flags & RENDER_EXTERIORMODEL))
-                               continue;
-               }
+               if (chase_active.integer && (ent->render.flags & RENDER_VIEWMODEL))
+                       continue;
 
                // don't show entities with no modelindex (note: this still shows
                // entities which have a modelindex that resolved to a NULL model)
index f77d722..36e172e 100644 (file)
@@ -436,7 +436,6 @@ void R_Model_Alias_Draw(entity_render_t *ent)
                R_DrawQ1Q2AliasModelCallback(ent, 0);
 }
 
-extern cvar_t r_shadows;
 void R_Model_Alias_DrawFakeShadow (entity_render_t *ent)
 {
        int i;
@@ -500,19 +499,63 @@ void R_Model_Alias_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightor
                R_Mesh_Matrix(&ent->matrix);
                R_Mesh_ResizeCheck(ent->model->numverts * 2);
                R_LerpMDLMD2Vertices(ent, varray_vertex, aliasvert_normals);
-               R_Shadow_Volume(ent->model->numverts, ent->model->numtris, varray_vertex, ent->model->mdlmd2data_indices, ent->model->mdlmd2data_triangleneighbors, relativelightorigin, lightradius, projectdistance);
+               R_Shadow_Volume(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, ent->model->mdlmd2data_triangleneighbors, relativelightorigin, lightradius, projectdistance);
        }
 }
 
-void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float lightdistbias, float lightsubtract, float *lightcolor)
+void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor)
 {
+       int c;
+       float lightcolor2[3];
+       qbyte *bcolor;
        skinframe_t *skinframe;
        R_Mesh_Matrix(&ent->matrix);
-       skinframe = R_FetchSkinFrame(ent);
        R_Mesh_ResizeCheck(ent->model->numverts);
        R_LerpMDLMD2Vertices(ent, varray_vertex, aliasvert_normals);
        Mod_BuildTextureVectorsAndNormals(ent->model->numverts, ent->model->numtris, varray_vertex, ent->model->mdlmd2data_texcoords, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals);
-       R_Shadow_RenderLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, skinframe->base, skinframe->gloss, skinframe->nmap, NULL);
+       skinframe = R_FetchSkinFrame(ent);
+
+       // note: to properly handle fog this should scale the lightcolor into lightcolor2 according to 1-fog scaling
+
+       R_Shadow_SpecularLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, NULL, NULL, NULL);
+
+       if (!skinframe->base && !skinframe->pants && !skinframe->shirt && !skinframe->glow)
+       {
+               R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor, r_notexture, NULL, NULL);
+               return;
+       }
+
+       if (!skinframe->merged || (ent->colormap >= 0 && skinframe->base && (skinframe->pants || skinframe->shirt)))
+       {
+               // 128-224 are backwards ranges
+               // we only render non-fullbright ranges here
+               if (skinframe->pants && (ent->colormap & 0xF) < 0xE)
+               {
+                       c = (ent->colormap & 0xF) << 4;c += (c >= 128 && c < 224) ? 4 : 12;
+                       bcolor = (qbyte *) (&palette_complete[c]);
+                       lightcolor2[0] = lightcolor[0] * bcolor[0] * (1.0f / 255.0f);
+                       lightcolor2[1] = lightcolor[1] * bcolor[1] * (1.0f / 255.0f);
+                       lightcolor2[2] = lightcolor[2] * bcolor[2] * (1.0f / 255.0f);
+                       R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor2, skinframe->pants, skinframe->nmap, NULL);
+               }
+
+               // we only render non-fullbright ranges here
+               if (skinframe->shirt && (ent->colormap & 0xF0) < 0xE0)
+               {
+                       c = (ent->colormap & 0xF0);c += (c >= 128 && c < 224) ? 4 : 12;
+                       bcolor = (qbyte *) (&palette_complete[c]);
+                       lightcolor2[0] = lightcolor[0] * bcolor[0] * (1.0f / 255.0f);
+                       lightcolor2[1] = lightcolor[1] * bcolor[1] * (1.0f / 255.0f);
+                       lightcolor2[2] = lightcolor[2] * bcolor[2] * (1.0f / 255.0f);
+                       R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor2, skinframe->shirt, skinframe->nmap, NULL);
+               }
+
+               if (skinframe->base)
+                       R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor, skinframe->base, skinframe->nmap, NULL);
+       }
+       else
+               if (skinframe->merged)
+                       R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor, skinframe->merged, skinframe->nmap, NULL);
 }
 
 int ZymoticLerpBones(int count, const zymbonematrix *bonebase, const frameblend_t *blend, const zymbone_t *bone)
index 5a6b892..7ba8402 100644 (file)
@@ -540,7 +540,8 @@ static void R_MarkEntities (void)
                Matrix4x4_Invert_Simple(&ent->inversematrix, &ent->matrix);
                R_LerpAnimation(ent);
                R_UpdateEntLights(ent);
-               if (!VIS_CullSphere(ent->origin, ent->model->radius * ent->scale)
+               if ((chase_active.integer || !(ent->flags & RENDER_EXTERIORMODEL))
+                && !VIS_CullSphere(ent->origin, ent->model->radius * ent->scale)
                 && !VIS_CullBox(ent->mins, ent->maxs))
                {
                        ent->visframe = r_framecount;
@@ -637,7 +638,64 @@ void R_DrawFakeShadows (void)
 
 #include "r_shadow.h"
 
-void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t lightorigin, float cullradius, float lightradius)
+int shadowframecount = 0;
+
+int Light_CullBox(const vec3_t mins, const vec3_t maxs)
+{
+       int stackpos, sides;
+       mnode_t *node, *stack[4096];
+       stackpos = 0;
+       stack[stackpos++] = cl.worldmodel->nodes;
+       while (stackpos)
+       {
+               node = stack[--stackpos];
+               if (node->contents < 0)
+               {
+                       if (((mleaf_t *)node)->worldnodeframe == shadowframecount)
+                               return false;
+               }
+               else
+               {
+                       sides = BoxOnPlaneSide(mins, maxs, node->plane);
+                       if (sides & 2 && stackpos < 4096)
+                               stack[stackpos++] = node->children[1];
+                       if (sides & 1 && stackpos < 4096)
+                               stack[stackpos++] = node->children[0];
+               }
+       }
+       return true;
+}
+
+int LightAndVis_CullBox(const vec3_t mins, const vec3_t maxs)
+{
+       int stackpos, sides;
+       mnode_t *node, *stack[4096];
+       if (R_CullBox(mins, maxs))
+               return true;
+       stackpos = 0;
+       stack[stackpos++] = cl.worldmodel->nodes;
+       while (stackpos)
+       {
+               node = stack[--stackpos];
+               if (node->contents < 0)
+               {
+                       if (((mleaf_t *)node)->visframe == r_framecount && ((mleaf_t *)node)->worldnodeframe == shadowframecount)
+                               return false;
+               }
+               else
+               {
+                       sides = BoxOnPlaneSide(mins, maxs, node->plane);
+                       if (sides & 2 && stackpos < 4096)
+                               stack[stackpos++] = node->children[1];
+                       if (sides & 1 && stackpos < 4096)
+                               stack[stackpos++] = node->children[0];
+               }
+       }
+       return true;
+}
+
+
+void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t lightorigin, float cullradius, float lightradius, vec3_t clipmins, vec3_t clipmaxs)
 {
        int i;
        vec3_t p, p2, temp, relativelightorigin, mins, maxs;
@@ -651,42 +709,38 @@ void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t lightorigin, float c
                dist = DotProduct(temp, temp);
                if (dist < cullradius * cullradius)
                {
-                       projectdistance = cullradius - sqrt(dist);
-                       // calculate projected bounding box and decide if it is on-screen
-                       VectorCopy(ent->mins, mins);
-                       VectorCopy(ent->maxs, maxs);
-                       for (i = 0;i < 8;i++)
+                       if (!Light_CullBox(ent->mins, ent->maxs))
                        {
-                               p[0] = i & 1 ? ent->maxs[0] : ent->mins[0];
-                               p[1] = i & 2 ? ent->maxs[1] : ent->mins[1];
-                               p[2] = i & 4 ? ent->maxs[2] : ent->mins[2];
-                               VectorSubtract(p, lightorigin, temp);
-                               dist = projectdistance / sqrt(DotProduct(temp, temp));
-                               VectorMA(p, dist, temp, p2);
-                               if (mins[0] > p2[0]) mins[0] = p2[0];if (maxs[0] < p2[0]) maxs[0] = p2[0];
-                               if (mins[1] > p2[1]) mins[1] = p2[1];if (maxs[1] < p2[1]) maxs[1] = p2[1];
-                               if (mins[2] > p2[2]) mins[2] = p2[2];if (maxs[2] < p2[2]) maxs[2] = p2[2];
-                       }
-                       if (!VIS_CullBox(mins, maxs))
-                       {
-                               Matrix4x4_Transform(&ent->inversematrix, lightorigin, relativelightorigin);
-                               ent->model->DrawShadowVolume (ent, relativelightorigin, lightradius);
+                               projectdistance = cullradius - sqrt(dist);
+                               // calculate projected bounding box and decide if it is on-screen
+                               VectorCopy(ent->mins, mins);
+                               VectorCopy(ent->maxs, maxs);
+                               for (i = 0;i < 8;i++)
+                               {
+                                       p[0] = i & 1 ? ent->maxs[0] : ent->mins[0];
+                                       p[1] = i & 2 ? ent->maxs[1] : ent->mins[1];
+                                       p[2] = i & 4 ? ent->maxs[2] : ent->mins[2];
+                                       VectorSubtract(p, lightorigin, temp);
+                                       dist = projectdistance / sqrt(DotProduct(temp, temp));
+                                       VectorMA(p, dist, temp, p2);
+                                       if (mins[0] > p2[0]) mins[0] = p2[0];if (maxs[0] < p2[0]) maxs[0] = p2[0];
+                                       if (mins[1] > p2[1]) mins[1] = p2[1];if (maxs[1] < p2[1]) maxs[1] = p2[1];
+                                       if (mins[2] > p2[2]) mins[2] = p2[2];if (maxs[2] < p2[2]) maxs[2] = p2[2];
+                               }
+                               if (mins[0] < clipmaxs[0] && maxs[0] > clipmins[0]
+                                && mins[1] < clipmaxs[1] && maxs[1] > clipmins[1]
+                                && mins[2] < clipmaxs[2] && maxs[2] > clipmins[2]
+                                && !LightAndVis_CullBox(mins, maxs))
+                               {
+                                       Matrix4x4_Transform(&ent->inversematrix, lightorigin, relativelightorigin);
+                                       ent->model->DrawShadowVolume (ent, relativelightorigin, lightradius);
+                               }
                        }
                }
        }
 }
 
-void R_DrawWorldLightShadowVolume(mlight_t *sl)
-{
-       shadowmesh_t *mesh;
-       R_Mesh_Matrix(&cl_entities[0].render.matrix);
-       for (mesh = sl->shadowvolume;mesh;mesh = mesh->next)
-       {
-               R_Mesh_ResizeCheck(mesh->numverts);
-               memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
-               R_Shadow_RenderVolume(mesh->numverts, mesh->numtriangles, mesh->elements);
-       }
-}
+void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light);
 
 #define SHADOWSPHERE_SEGMENTS 16
 
@@ -696,7 +750,7 @@ void R_CreateShadowSphere(void)
        int i, j;
        vec3_t angles, angles2, angles3, angles4;
        float verts[12];
-       shadowsphere = Mod_ShadowMesh_Begin(zonemempool);
+       shadowsphere = Mod_ShadowMesh_Begin(zonemempool, SHADOWSPHERE_SEGMENTS * SHADOWSPHERE_SEGMENTS / 2);
        for (i = 0;i < SHADOWSPHERE_SEGMENTS / 2;i++)
        {
                for (j = 0;j < SHADOWSPHERE_SEGMENTS;j++)
@@ -750,16 +804,19 @@ void R_DrawShadowSphere(vec3_t origin, float cullradius, float lightradius)
        }
 }
 
+extern void R_Model_Brush_DrawLightForSurfaceList(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, msurface_t **surflist, int numsurfaces);
 void R_ShadowVolumeLighting (int visiblevolumes)
 {
        int i;
        entity_render_t *ent;
        int lnum;
        float f, lightradius, cullradius;
-       vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
-       mlight_t *sl;
+       vec3_t relativelightorigin, relativeeyeorigin, lightcolor, clipmins, clipmaxs;
+       worldlight_t *wl;
+       //mlight_t *sl;
        rdlight_t *rd;
        rmeshstate_t m;
+       mleaf_t *leaf;
 
        if (visiblevolumes)
        {
@@ -771,15 +828,137 @@ void R_ShadowVolumeLighting (int visiblevolumes)
        }
        else
                R_Shadow_Stage_Begin();
+       shadowframecount++;
+       for (lnum = 0, wl = r_shadow_worldlightchain;wl;wl = wl->next, lnum++)
+       {
+               if (d_lightstylevalue[wl->style] <= 0)
+                       continue;
+               cullradius = wl->cullradius;
+               lightradius = wl->lightradius;
+               if (R_CullSphere(wl->origin, lightradius))
+                       continue;
+               //if (R_CullBox(wl->mins, wl->maxs) || R_CullSphere(wl->origin, lightradius))
+               //      continue;
+               //if (VIS_CullBox(wl->mins, wl->maxs) || VIS_CullSphere(wl->origin, lightradius))
+               //      continue;
+               if (r_shadow_debuglight.integer >= 0 && lnum != r_shadow_debuglight.integer)
+                       continue;
+
+               for (i = 0;i < wl->numleafs;i++)
+                       if (wl->leafs[i]->visframe == r_framecount)
+                               break;
+               if (i == wl->numleafs)
+                       continue;
+               leaf = wl->leafs[i];
+               VectorCopy(leaf->mins, clipmins);
+               VectorCopy(leaf->maxs, clipmaxs);
+               for (i = 0;i < wl->numleafs;i++)
+               {
+                       leaf = wl->leafs[i];
+                       if (leaf->visframe == r_framecount)
+                       {
+                               if (clipmins[0] > leaf->mins[0]) clipmins[0] = leaf->mins[0];
+                               if (clipmaxs[0] < leaf->maxs[0]) clipmaxs[0] = leaf->maxs[0];
+                               if (clipmins[1] > leaf->mins[1]) clipmins[1] = leaf->mins[1];
+                               if (clipmaxs[1] < leaf->maxs[1]) clipmaxs[1] = leaf->maxs[1];
+                               if (clipmins[2] > leaf->mins[2]) clipmins[2] = leaf->mins[2];
+                               if (clipmaxs[2] < leaf->maxs[2]) clipmaxs[2] = leaf->maxs[2];
+                       }
+               }
+               // mark the leafs we care about so only things in those leafs will matter
+               for (i = 0;i < wl->numleafs;i++)
+                       wl->leafs[i]->worldnodeframe = shadowframecount;
+
+
+               f = d_lightstylevalue[wl->style] * (1.0f / 256.0f);
+               VectorScale(wl->light, f, lightcolor);
+               if (wl->selected)
+               {
+                       f = 2 + sin(realtime * M_PI * 4.0);
+                       VectorScale(lightcolor, f, lightcolor);
+               }
+
+               if (!visiblevolumes)
+                       R_Shadow_Stage_ShadowVolumes();
+               if (wl->shadowvolume && r_staticworldlights.integer)
+                       R_Shadow_DrawWorldLightShadowVolume(&cl_entities[0].render.matrix, wl);
+               else
+                       R_TestAndDrawShadowVolume(&cl_entities[0].render, wl->origin, cullradius, lightradius, clipmins, clipmaxs);
+               if (r_drawentities.integer)
+               {
+                       for (i = 0;i < r_refdef.numentities;i++)
+                       {
+                               ent = r_refdef.entities[i];
+                               if (ent->maxs[0] >= wl->mins[0] && ent->mins[0] <= wl->maxs[0]
+                                && ent->maxs[1] >= wl->mins[1] && ent->mins[1] <= wl->maxs[1]
+                                && ent->maxs[2] >= wl->mins[2] && ent->mins[2] <= wl->maxs[2]
+                                && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
+                                       R_TestAndDrawShadowVolume(r_refdef.entities[i], wl->origin, cullradius, lightradius, clipmins, clipmaxs);
+                       }
+               }
+
+               if (!visiblevolumes)
+               {
+                       R_Shadow_Stage_Light();
+                       ent = &cl_entities[0].render;
+                       if (ent->model && ent->model->DrawLight)
+                       {
+                               Matrix4x4_Transform(&ent->inversematrix, wl->origin, relativelightorigin);
+                               Matrix4x4_Transform(&ent->inversematrix, r_origin, relativeeyeorigin);
+                               if (wl->numsurfaces)
+                                       R_Model_Brush_DrawLightForSurfaceList(ent, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, wl->surfaces, wl->numsurfaces);
+                               else
+                                       ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius, lightcolor);
+                       }
+                       if (r_drawentities.integer)
+                       {
+                               for (i = 0;i < r_refdef.numentities;i++)
+                               {
+                                       ent = r_refdef.entities[i];
+                                       if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
+                                        && ent->maxs[0] >= wl->mins[0] && ent->mins[0] <= wl->maxs[0]
+                                        && ent->maxs[1] >= wl->mins[1] && ent->mins[1] <= wl->maxs[1]
+                                        && ent->maxs[2] >= wl->mins[2] && ent->mins[2] <= wl->maxs[2]
+                                        && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
+                                       {
+                                               Matrix4x4_Transform(&ent->inversematrix, wl->origin, relativelightorigin);
+                                               Matrix4x4_Transform(&ent->inversematrix, r_origin, relativeeyeorigin);
+                                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius, lightcolor);
+                                       }
+                               }
+                       }
+
+                       if (R_Shadow_Stage_EraseShadowVolumes())
+                       {
+                               if (wl->shadowvolume && r_staticworldlights.integer)
+                                       R_Shadow_DrawWorldLightShadowVolume(&cl_entities[0].render.matrix, wl);
+                               else
+                                       R_TestAndDrawShadowVolume(&cl_entities[0].render, wl->origin, cullradius, lightradius, clipmins, clipmaxs);
+                               if (r_drawentities.integer)
+                               {
+                                       for (i = 0;i < r_refdef.numentities;i++)
+                                       {
+                                               ent = r_refdef.entities[i];
+                                               if (ent->maxs[0] >= wl->mins[0] && ent->mins[0] <= wl->maxs[0]
+                                                && ent->maxs[1] >= wl->mins[1] && ent->mins[1] <= wl->maxs[1]
+                                                && ent->maxs[2] >= wl->mins[2] && ent->mins[2] <= wl->maxs[2]
+                                                && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
+                                                       R_TestAndDrawShadowVolume(r_refdef.entities[i], wl->origin, cullradius, lightradius, clipmins, clipmaxs);
+                                       }
+                               }
+                       }
+               }
+       }
+       /*
        for (lnum = 0, sl = cl.worldmodel->lights;lnum < cl.worldmodel->numlights;lnum++, sl++)
        {
                if (d_lightstylevalue[sl->style] <= 0)
                        continue;
-               if (r_light_debuglight.integer >= 0 && lnum != r_light_debuglight.integer)
+               if (r_shadow_debuglight.integer >= 0 && lnum != r_shadow_debuglight.integer)
                        continue;
                cullradius = sl->cullradius;
                lightradius = sl->lightradius;
-               if (VIS_CullBox(sl->mins, sl->maxs) || VIS_CullSphere(sl->origin, cullradius))
+               if (VIS_CullBox(sl->mins, sl->maxs) || VIS_CullSphere(sl->origin, lightradius))
                        continue;
 
                f = d_lightstylevalue[sl->style] * (1.0f / 32768.0f);
@@ -788,12 +967,9 @@ void R_ShadowVolumeLighting (int visiblevolumes)
                if (!visiblevolumes)
                        R_Shadow_Stage_ShadowVolumes();
                if (sl->shadowvolume && r_staticworldlights.integer)
-                       R_DrawWorldLightShadowVolume(sl);
+                       R_DrawWorldLightShadowVolume(&cl_entities[0].render.matrix, sl->shadowvolume);
                else
-               {
-                       //R_DrawShadowSphere(sl->origin, cullradius, lightradius * 2);
                        R_TestAndDrawShadowVolume(&cl_entities[0].render, sl->origin, cullradius, lightradius);
-               }
                if (r_drawentities.integer)
                {
                        for (i = 0;i < r_refdef.numentities;i++)
@@ -837,45 +1013,49 @@ void R_ShadowVolumeLighting (int visiblevolumes)
 
                        R_Shadow_Stage_EraseShadowVolumes();
                        if (sl->shadowvolume && r_staticworldlights.integer)
-                               R_DrawWorldLightShadowVolume(sl);
+                               R_DrawWorldLightShadowVolume(&cl_entities[0].render.matrix, sl->shadowvolume);
                        else
-                       {
-                               //R_DrawShadowSphere(sl->origin, cullradius, lightradius * 2);
                                R_TestAndDrawShadowVolume(&cl_entities[0].render, sl->origin, cullradius, lightradius);
-                       }
                        if (r_drawentities.integer)
                        {
                                for (i = 0;i < r_refdef.numentities;i++)
                                {
                                        ent = r_refdef.entities[i];
                                        if (ent->maxs[0] >= sl->mins[0] && ent->mins[0] <= sl->maxs[0]
-                                        && ent->maxs[1] >= sl->mins[1] && ent->mins[1] <= sl->maxs[1]
-                                        && ent->maxs[2] >= sl->mins[2] && ent->mins[2] <= sl->maxs[2]
-                                        && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
+                                       && ent->maxs[1] >= sl->mins[1] && ent->mins[1] <= sl->maxs[1]
+                                       && ent->maxs[2] >= sl->mins[2] && ent->mins[2] <= sl->maxs[2]
+                                       && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
                                                R_TestAndDrawShadowVolume(r_refdef.entities[i], sl->origin, cullradius, lightradius);
                                }
                        }
                }
        }
+       */
        for (lnum = 0, rd = r_dlight;lnum < r_numdlights;lnum++, rd++)
        {
                cullradius = rd->cullradius;
                lightradius = rd->cullradius;
-               if (VIS_CullSphere(rd->origin, cullradius))
+               if (VIS_CullSphere(rd->origin, lightradius))
                        continue;
 
-               VectorScale(rd->light, 16.0f, lightcolor);
+               VectorScale(rd->light, (1.0f / 8192.0f), lightcolor);
+               clipmins[0] = rd->origin[0] - cullradius;
+               clipmins[1] = rd->origin[1] - cullradius;
+               clipmins[2] = rd->origin[2] - cullradius;
+               clipmaxs[0] = rd->origin[0] + cullradius;
+               clipmaxs[1] = rd->origin[1] + cullradius;
+               clipmaxs[2] = rd->origin[2] + cullradius;
 
                if (!visiblevolumes)
                        R_Shadow_Stage_ShadowVolumes();
-               R_TestAndDrawShadowVolume(&cl_entities[0].render, rd->origin, cullradius, lightradius);
+               R_TestAndDrawShadowVolume(&cl_entities[0].render, rd->origin, cullradius, lightradius, clipmins, clipmaxs);
                if (r_drawentities.integer)
                {
                        for (i = 0;i < r_refdef.numentities;i++)
                        {
                                ent = r_refdef.entities[i];
                                if (ent != rd->ent && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
-                                       R_TestAndDrawShadowVolume(ent, rd->origin, cullradius, lightradius);
+                                       R_TestAndDrawShadowVolume(ent, rd->origin, cullradius, lightradius, clipmins, clipmaxs);
                        }
                }
 
@@ -887,7 +1067,7 @@ void R_ShadowVolumeLighting (int visiblevolumes)
                        {
                                Matrix4x4_Transform(&ent->inversematrix, rd->origin, relativelightorigin);
                                Matrix4x4_Transform(&ent->inversematrix, r_origin, relativeeyeorigin);
-                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius, LIGHTOFFSET, rd->subtract, lightcolor);
+                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius, lightcolor);
                        }
                        if (r_drawentities.integer)
                        {
@@ -899,20 +1079,22 @@ void R_ShadowVolumeLighting (int visiblevolumes)
                                        {
                                                Matrix4x4_Transform(&ent->inversematrix, rd->origin, relativelightorigin);
                                                Matrix4x4_Transform(&ent->inversematrix, r_origin, relativeeyeorigin);
-                                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius, LIGHTOFFSET, rd->subtract, lightcolor);
+                                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius, lightcolor);
                                        }
                                }
                        }
 
-                       R_Shadow_Stage_EraseShadowVolumes();
-                       R_TestAndDrawShadowVolume(&cl_entities[0].render, rd->origin, cullradius, lightradius);
-                       if (r_drawentities.integer)
+                       if (R_Shadow_Stage_EraseShadowVolumes())
                        {
-                               for (i = 0;i < r_refdef.numentities;i++)
+                               R_TestAndDrawShadowVolume(&cl_entities[0].render, rd->origin, cullradius, lightradius, clipmins, clipmaxs);
+                               if (r_drawentities.integer)
                                {
-                                       ent = r_refdef.entities[i];
-                                       if (ent != rd->ent && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
-                                               R_TestAndDrawShadowVolume(ent, rd->origin, cullradius, lightradius);
+                                       for (i = 0;i < r_refdef.numentities;i++)
+                                       {
+                                               ent = r_refdef.entities[i];
+                                               if (ent != rd->ent && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
+                                                       R_TestAndDrawShadowVolume(ent, rd->origin, cullradius, lightradius, clipmins, clipmaxs);
+                                       }
                                }
                        }
                }
@@ -1016,6 +1198,32 @@ void R_RenderView (void)
        if (!r_refdef.entities/* || !cl.worldmodel*/)
                return; //Host_Error ("R_RenderView: NULL worldmodel");
 
+       if (r_shadow_realtime.integer == 1)
+       {
+               if (!gl_texturecubemap)
+               {
+                       Con_Printf("Cubemap texture support not detected, turning off r_shadow_realtime\n");
+                       Cvar_SetValueQuick(&r_shadow_realtime, 0);
+               }
+               else if (!gl_dot3arb)
+               {
+                       Con_Printf("Bumpmapping support not detected, turning off r_shadow_realtime\n");
+                       Cvar_SetValueQuick(&r_shadow_realtime, 0);
+               }
+               else if (!gl_stencil)
+               {
+                       Con_Printf("Stencil not enabled, turning off r_shadow_realtime, please type vid_stencil 1;vid_restart and try again\n");
+                       Cvar_SetValueQuick(&r_shadow_realtime, 0);
+               }
+               else if (!gl_combine.integer)
+               {
+                       Con_Printf("Combine disabled, please turn on gl_combine, turning off r_shadow_realtime\n");
+                       Cvar_SetValueQuick(&r_shadow_realtime, 0);
+               }
+       }
+
+       R_Shadow_UpdateLightingMode();
+
        world = &cl_entities[0].render;
 
        // FIXME: move to client
@@ -1039,7 +1247,7 @@ void R_RenderView (void)
        R_TimeReport("markentity");
 
        GL_SetupView_ViewPort(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
-       if (r_shadows.integer == 3)
+       if (r_shadow_lightingmode > 0)
                GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_refdef.fov_x, r_refdef.fov_y, 1.0f);
        else
                GL_SetupView_Mode_Perspective(r_refdef.fov_x, r_refdef.fov_y, 1.0f, r_farclip);
@@ -1049,29 +1257,8 @@ void R_RenderView (void)
        R_Mesh_Start();
        R_MeshQueue_BeginScene();
 
-       if (r_shadows.integer == 3)
-       {
-               if (!gl_texturecubemap)
-               {
-                       Con_Printf("Cubemap texture support not detected, turning off r_shadows 3\n");
-                       Cvar_SetValueQuick(&r_shadows, 0);
-               }
-               else if (!gl_dot3arb)
-               {
-                       Con_Printf("Bumpmapping support not detected, turning off r_shadows 3\n");
-                       Cvar_SetValueQuick(&r_shadows, 0);
-               }
-               else if (!gl_stencil)
-               {
-                       Con_Printf("Stencil not enabled, turning off r_shadows 3, please type vid_stencil 1;vid_restart and try again\n");
-                       Cvar_SetValueQuick(&r_shadows, 0);
-               }
-               else if (!gl_combine.integer)
-               {
-                       Con_Printf("Combine disabled, please turn on gl_combine, turning off r_shadows 3\n");
-                       Cvar_SetValueQuick(&r_shadows, 0);
-               }
-       }
+       if (r_shadow_lightingmode)
+               R_Shadow_UpdateWorldLightSelection();
 
        if (R_DrawBrushModelsSky())
                R_TimeReport("bmodelsky");
@@ -1084,7 +1271,7 @@ void R_RenderView (void)
        if (!intimerefresh && !r_speeds.integer)
                S_ExtraUpdate ();
 
-       R_DrawModels(r_shadows.integer == 3);
+       R_DrawModels(r_shadow_lightingmode > 0);
        R_TimeReport("models");
 
        if (r_shadows.integer == 1)
@@ -1093,7 +1280,7 @@ void R_RenderView (void)
                R_TimeReport("fakeshadow");
        }
 
-       if (r_shadows.integer == 3)
+       if (r_shadow_lightingmode > 0)
        {
                R_ShadowVolumeLighting(false);
                R_TimeReport("dynlight");
@@ -1119,7 +1306,7 @@ void R_RenderView (void)
 
        R_MeshQueue_Render();
        R_MeshQueue_EndScene();
-       if (r_shadows.integer == 2)
+       if (r_shadow_realtime.integer == 2)
        {
                R_ShadowVolumeLighting(true);
                R_TimeReport("shadowvolume");
index 8bd97db..263bb96 100644 (file)
@@ -807,7 +807,7 @@ static void RSurfShader_Water_Callback(const void *calldata1, int calldata2)
        float f, colorscale;
        const surfmesh_t *mesh;
        rmeshstate_t m;
-       float alpha = ent->alpha * (surf->flags & SURF_WATERALPHA ? r_wateralpha.value : 1);
+       float alpha;
        float modelorg[3];
        texture_t *texture;
        Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg);
@@ -815,7 +815,8 @@ static void RSurfShader_Water_Callback(const void *calldata1, int calldata2)
        R_Mesh_Matrix(&ent->matrix);
 
        memset(&m, 0, sizeof(m));
-       texture = surf->texinfo->texture->currentframe[ent->frame != 0];
+       texture = surf->texinfo->texture->currentframe;
+       alpha = texture->currentalpha;
        if (texture->rendertype == SURFRENDER_ADD)
        {
                m.blendfunc1 = GL_SRC_ALPHA;
@@ -898,22 +899,20 @@ static void RSurfShader_Water(const entity_render_t *ent, const texture_t *textu
                                RSurfShader_Water_Callback(ent, surf - ent->model->surfaces);
 }
 
-static void RSurfShader_Wall_Pass_BaseVertex(const entity_render_t *ent, const msurface_t *surf)
+static void RSurfShader_Wall_Pass_BaseVertex(const entity_render_t *ent, const msurface_t *surf, const texture_t *texture, int rendertype, float currentalpha)
 {
        float base, colorscale;
        const surfmesh_t *mesh;
        rmeshstate_t m;
        float modelorg[3];
-       texture_t *texture;
        Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg);
        memset(&m, 0, sizeof(m));
-       texture = surf->texinfo->texture->currentframe[ent->frame != 0];
-       if (texture->rendertype == SURFRENDER_ADD)
+       if (rendertype == SURFRENDER_ADD)
        {
                m.blendfunc1 = GL_SRC_ALPHA;
                m.blendfunc2 = GL_ONE;
        }
-       else if (texture->rendertype == SURFRENDER_ALPHA)
+       else if (rendertype == SURFRENDER_ALPHA)
        {
                m.blendfunc1 = GL_SRC_ALPHA;
                m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
@@ -938,7 +937,7 @@ static void RSurfShader_Wall_Pass_BaseVertex(const entity_render_t *ent, const m
                R_Mesh_ResizeCheck(mesh->numverts);
                memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
                memcpy(varray_texcoord[0], mesh->str, mesh->numverts * sizeof(float[4]));
-               R_FillColors(varray_color, mesh->numverts, base, base, base, ent->alpha);
+               R_FillColors(varray_color, mesh->numverts, base, base, base, currentalpha);
                if (!(ent->effects & EF_FULLBRIGHT))
                {
                        if (surf->dlightframe == r_framecount)
@@ -951,13 +950,11 @@ static void RSurfShader_Wall_Pass_BaseVertex(const entity_render_t *ent, const m
        }
 }
 
-static void RSurfShader_Wall_Pass_Glow(const entity_render_t *ent, const msurface_t *surf)
+static void RSurfShader_Wall_Pass_Glow(const entity_render_t *ent, const msurface_t *surf, const texture_t *texture, int rendertype, float currentalpha)
 {
        const surfmesh_t *mesh;
        rmeshstate_t m;
        float modelorg[3];
-       texture_t *texture;
-       texture = surf->texinfo->texture->currentframe[ent->frame != 0];
        Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg);
        memset(&m, 0, sizeof(m));
        m.blendfunc1 = GL_SRC_ALPHA;
@@ -970,18 +967,16 @@ static void RSurfShader_Wall_Pass_Glow(const entity_render_t *ent, const msurfac
                R_Mesh_ResizeCheck(mesh->numverts);
                memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
                memcpy(varray_texcoord[0], mesh->str, mesh->numverts * sizeof(float[4]));
-               RSurf_FoggedColors(varray_vertex, varray_color, 1, 1, 1, ent->alpha, r_colorscale, mesh->numverts, modelorg);
+               RSurf_FoggedColors(varray_vertex, varray_color, 1, 1, 1, currentalpha, r_colorscale, mesh->numverts, modelorg);
                R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->index);
        }
 }
 
-static void RSurfShader_Wall_Pass_Fog(const entity_render_t *ent, const msurface_t *surf)
+static void RSurfShader_Wall_Pass_Fog(const entity_render_t *ent, const msurface_t *surf, const texture_t *texture, int rendertype, float currentalpha)
 {
        const surfmesh_t *mesh;
        rmeshstate_t m;
        float modelorg[3];
-       texture_t *texture;
-       texture = surf->texinfo->texture->currentframe[ent->frame != 0];
        Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg);
        memset(&m, 0, sizeof(m));
        m.blendfunc1 = GL_SRC_ALPHA;
@@ -995,7 +990,7 @@ static void RSurfShader_Wall_Pass_Fog(const entity_render_t *ent, const msurface
                memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
                if (m.tex[0])
                        memcpy(varray_texcoord[0], mesh->str, mesh->numverts * sizeof(float[4]));
-               RSurf_FogPassColors(varray_vertex, varray_color, fogcolor[0], fogcolor[1], fogcolor[2], ent->alpha, r_colorscale, mesh->numverts, modelorg);
+               RSurf_FogPassColors(varray_vertex, varray_color, fogcolor[0], fogcolor[1], fogcolor[2], currentalpha, r_colorscale, mesh->numverts, modelorg);
                R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->index);
        }
 }
@@ -1294,17 +1289,32 @@ static void RSurfShader_Wall_Vertex_Callback(const void *calldata1, int calldata
 {
        const entity_render_t *ent = calldata1;
        const msurface_t *surf = ent->model->surfaces + calldata2;
+       int rendertype;
+       float currentalpha;
        texture_t *texture;
-       texture = surf->texinfo->texture->currentframe[ent->frame != 0];
        R_Mesh_Matrix(&ent->matrix);
-       RSurfShader_Wall_Pass_BaseVertex(ent, surf);
+
+       texture = surf->texinfo->texture;
+       if (texture->animated)
+               texture = texture->anim_frames[ent->frame != 0][(texture->anim_total[ent->frame != 0] >= 2) ? ((int) (cl.time * 5.0f) % texture->anim_total[ent->frame != 0]) : 0];
+
+       currentalpha = ent->alpha;
+       if (texture->flags & SURF_WATERALPHA)
+               currentalpha *= r_wateralpha.value;
+       if (ent->effects & EF_ADDITIVE)
+               rendertype = SURFRENDER_ADD;
+       else if (currentalpha < 1 || texture->fogtexture != NULL)
+               rendertype = SURFRENDER_ALPHA;
+       else
+               rendertype = SURFRENDER_OPAQUE;
+
+       RSurfShader_Wall_Pass_BaseVertex(ent, surf, texture, rendertype, currentalpha);
        if (texture->glowtexture)
-               RSurfShader_Wall_Pass_Glow(ent, surf);
+               RSurfShader_Wall_Pass_Glow(ent, surf, texture, rendertype, currentalpha);
        if (fogenabled)
-               RSurfShader_Wall_Pass_Fog(ent, surf);
+               RSurfShader_Wall_Pass_Fog(ent, surf, texture, rendertype, currentalpha);
 }
 
-extern cvar_t r_shadows;
 static void RSurfShader_Wall_Lightmap(const entity_render_t *ent, const texture_t *texture, const msurface_t *firstsurf)
 {
        const msurface_t *surf;
@@ -1321,7 +1331,7 @@ static void RSurfShader_Wall_Lightmap(const entity_render_t *ent, const texture_
                        }
                }
        }
-       else if (r_shadows.integer == 3 && cl.worldmodel->numlights)
+       else if (r_shadow_lightingmode >= 2)
        {
                // opaque base lighting
                RSurfShader_OpaqueWall_Pass_OpaqueGlow(ent, texture, firstsurf);
@@ -1333,15 +1343,15 @@ static void RSurfShader_Wall_Lightmap(const entity_render_t *ent, const texture_
                // opaque vertex shaded from lightmap
                for (surf = firstsurf;surf;surf = surf->texturechain)
                        if (surf->visframe == r_framecount)
-                               RSurfShader_Wall_Pass_BaseVertex(ent, surf);
+                               RSurfShader_Wall_Pass_BaseVertex(ent, surf, texture, texture->rendertype, texture->currentalpha);
                if (texture->glowtexture)
                        for (surf = firstsurf;surf;surf = surf->texturechain)
                                if (surf->visframe == r_framecount)
-                                       RSurfShader_Wall_Pass_Glow(ent, surf);
+                                       RSurfShader_Wall_Pass_Glow(ent, surf, texture, texture->rendertype, texture->currentalpha);
                if (fogenabled)
                        for (surf = firstsurf;surf;surf = surf->texturechain)
                                if (surf->visframe == r_framecount)
-                                       RSurfShader_Wall_Pass_Fog(ent, surf);
+                                       RSurfShader_Wall_Pass_Fog(ent, surf, texture, texture->rendertype, texture->currentalpha);
        }
        else
        {
@@ -1385,42 +1395,54 @@ Cshader_t *Cshaders[3] =
        &Cshader_sky
 };
 
-extern cvar_t r_shadows;
-void R_PrepareSurfaces(entity_render_t *ent)
+void R_UpdateTextureInfo(entity_render_t *ent)
 {
-       int i, texframe, numsurfaces, *surfacevisframes;
-       model_t *model;
-       msurface_t *surf, *surfaces;
+       int i, texframe, alttextures;
        texture_t *t;
-       vec3_t modelorg;
 
        if (!ent->model)
                return;
 
-       model = ent->model;
-       Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg);
-       numsurfaces = model->nummodelsurfaces;
-       surfaces = model->surfaces + model->firstmodelsurface;
-       surfacevisframes = model->surfacevisframes + model->firstmodelsurface;
-
+       alttextures = ent->frame != 0;
        texframe = (int)(cl.time * 5.0f);
-       for (i = 0;i < model->numtextures;i++)
+       for (i = 0;i < ent->model->numtextures;i++)
        {
-               t = model->textures + i;
+               t = ent->model->textures + i;
+               t->currentalpha = ent->alpha;
+               if (t->flags & SURF_WATERALPHA)
+                       t->currentalpha *= r_wateralpha.value;
                if (ent->effects & EF_ADDITIVE)
                        t->rendertype = SURFRENDER_ADD;
-               else if (ent->alpha < 1 || (t->flags & SURF_WATERALPHA && r_wateralpha.value < 1) || t->fogtexture != NULL)
+               else if (t->currentalpha < 1 || t->fogtexture != NULL)
                        t->rendertype = SURFRENDER_ALPHA;
                else
                        t->rendertype = SURFRENDER_OPAQUE;
+               // we don't need to set currentframe if t->animated is false because
+               // it was already set up by the texture loader for non-animating
                if (t->animated)
-               {
-                       t->currentframe[0] = t->anim_frames[0][(t->anim_total[0] >= 2) ? (texframe % t->anim_total[0]) : 0];
-                       t->currentframe[1] = t->anim_frames[1][(t->anim_total[1] >= 2) ? (texframe % t->anim_total[1]) : 0];
-               }
+                       t->currentframe = t->anim_frames[alttextures][(t->anim_total[alttextures] >= 2) ? (texframe % t->anim_total[alttextures]) : 0];
        }
+}
 
-       if (r_dynamic.integer && r_shadows.integer != 3)
+void R_PrepareSurfaces(entity_render_t *ent)
+{
+       int i, numsurfaces, *surfacevisframes;
+       model_t *model;
+       msurface_t *surf, *surfaces;
+       vec3_t modelorg;
+
+       if (!ent->model)
+               return;
+
+       model = ent->model;
+       Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg);
+       numsurfaces = model->nummodelsurfaces;
+       surfaces = model->surfaces + model->firstmodelsurface;
+       surfacevisframes = model->surfacevisframes + model->firstmodelsurface;
+
+       R_UpdateTextureInfo(ent);
+
+       if (r_dynamic.integer && r_shadow_lightingmode < 1)
                R_MarkLights(ent);
 
        for (i = 0, surf = surfaces;i < numsurfaces;i++, surf++)
@@ -1473,7 +1495,7 @@ void R_DrawSurfaces(entity_render_t *ent, int type)
        R_Mesh_Matrix(&ent->matrix);
        for (i = 0, t = ent->model->textures;i < ent->model->numtextures;i++, t++)
                if (t->shader->shaderfunc[type] && ent->model->texturesurfacechains[i])
-                       t->shader->shaderfunc[type](ent, t, ent->model->texturesurfacechains[i]);
+                       t->shader->shaderfunc[type](ent, t->currentframe, ent->model->texturesurfacechains[i]);
 }
 
 static void R_DrawPortal_Callback(const void *calldata1, int calldata2)
@@ -1736,21 +1758,19 @@ void R_PVSUpdate (entity_render_t *ent, mleaf_t *viewleaf)
                                                        // mark surfaces bounding this leaf as visible
                                                        for (c = leaf->nummarksurfaces, mark = leaf->firstmarksurface;c;c--, mark++)
                                                        {
-                                                               if (surfacepvsframes[*mark] != model->pvsframecount)
-                                                               {
+                                                               //if (surfacepvsframes[*mark] != model->pvsframecount)
+                                                               //{
                                                                        surfacepvsframes[*mark] = model->pvsframecount;
-                                                                       model->pvssurflist[model->pvssurflistlength++] = *mark;
-                                                               }
+                                                               //      model->pvssurflist[model->pvssurflistlength++] = *mark;
+                                                               //}
                                                        }
                                                }
                                        }
                                }
                        }
-                       /*
                        for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
                                if (model->surfacepvsframes[j] == model->pvsframecount)
                                        model->pvssurflist[model->pvssurflistlength++] = j;
-                       */
                }
        }
 }
@@ -1797,31 +1817,16 @@ void R_Model_Brush_Draw (entity_render_t *ent)
 
 void R_Model_Brush_DrawShadowVolume (entity_render_t *ent, vec3_t relativelightorigin, float lightradius)
 {
-#if 0
-       float projectdistance, temp[3];
-       shadowmesh_t *mesh;
-       VectorSubtract(relativelightorigin, ent->model->shadowmesh_center, temp);
-       projectdistance = lightradius + ent->model->shadowmesh_radius - sqrt(DotProduct(temp, temp));
-       if (projectdistance >= 0.1)
-       {
-               R_Mesh_Matrix(&ent->matrix);
-               for (mesh = ent->model->shadowmesh;mesh;mesh = mesh->next)
-               {
-                       R_Mesh_ResizeCheck(mesh->numverts * 2);
-                       memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
-                       R_Shadow_Volume(mesh->numverts, mesh->numtriangles, varray_vertex, mesh->elements, mesh->neighbors, relativelightorigin, lightradius, projectdistance);
-               }
-       }
-#else
        int i;
        msurface_t *surf;
        float projectdistance, f, temp[3], lightradius2;
        surfmesh_t *mesh;
        R_Mesh_Matrix(&ent->matrix);
        lightradius2 = lightradius * lightradius;
+       R_UpdateTextureInfo(ent);
        for (i = 0, surf = ent->model->surfaces + ent->model->firstmodelsurface;i < ent->model->nummodelsurfaces;i++, surf++)
        {
-               if (surf->flags & SURF_SHADOWCAST)
+               if (surf->texinfo->texture->rendertype == SURFRENDER_OPAQUE && surf->flags & SURF_SHADOWCAST)
                {
                        f = PlaneDiff(relativelightorigin, surf->plane);
                        if (surf->flags & SURF_PLANEBACK)
@@ -1837,46 +1842,111 @@ void R_Model_Brush_DrawShadowVolume (entity_render_t *ent, vec3_t relativelighto
                                        {
                                                R_Mesh_ResizeCheck(mesh->numverts * 2);
                                                memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
-                                               R_Shadow_Volume(mesh->numverts, mesh->numtriangles, varray_vertex, mesh->index, mesh->triangleneighbors, relativelightorigin, lightradius, projectdistance);
+                                               R_Shadow_Volume(mesh->numverts, mesh->numtriangles, mesh->index, mesh->triangleneighbors, relativelightorigin, lightradius, projectdistance);
                                        }
                                }
                        }
                }
        }
-#endif
 }
 
-void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float lightdistbias, float lightsubtract, float *lightcolor)
+void R_Model_Brush_DrawLightForSurfaceList(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, msurface_t **surflist, int numsurfaces)
 {
-       int tnum;
+       int surfnum;
        msurface_t *surf;
        texture_t *t;
-       float f, lightradius2;
        surfmesh_t *mesh;
        R_Mesh_Matrix(&ent->matrix);
-       if (ent != &cl_entities[0].render)
-               R_PrepareBrushModel(ent);
+       R_UpdateTextureInfo(ent);
+       for (surfnum = 0;surfnum < numsurfaces;surfnum++)
+       {
+               surf = surflist[surfnum];
+               if (surf->visframe == r_framecount)
+               {
+                       t = surf->texinfo->texture->currentframe;
+                       if (t->rendertype == SURFRENDER_OPAQUE && t->flags & SURF_SHADOWLIGHT)
+                       {
+                               for (mesh = surf->mesh;mesh;mesh = mesh->chain)
+                               {
+                                       R_Mesh_ResizeCheck(mesh->numverts);
+                                       memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
+                                       R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals, mesh->str, relativelightorigin, lightradius, lightcolor, t->texture, t->nmaptexture, NULL);
+                                       R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals, mesh->str, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, t->glosstexture, t->nmaptexture, NULL);
+                               }
+                       }
+               }
+       }
+}
+
+void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor)
+{
+       int surfnum;
+       msurface_t *surf;
+       texture_t *t;
+       float f, lightradius2, temp[3];
+       surfmesh_t *mesh;
+       R_Mesh_Matrix(&ent->matrix);
        lightradius2 = lightradius * lightradius;
-       for (tnum = 0;tnum < ent->model->numtextures;tnum++)
+       R_UpdateTextureInfo(ent);
+       if (ent != &cl_entities[0].render)
        {
-               t = ent->model->textures + tnum;
-               if (ent->model->texturesurfacechains[tnum] && t->rendertype == SURFRENDER_OPAQUE && t->flags & SURF_SHADOWLIGHT)
+               // bmodel, cull crudely to view and light
+               for (surfnum = 0, surf = ent->model->surfaces + ent->model->firstmodelsurface;surfnum < ent->model->nummodelsurfaces;surfnum++, surf++)
                {
-                       t = t->currentframe[ent->frame != 0];
-                       for (surf = ent->model->texturesurfacechains[tnum];surf;surf = surf->texturechain)
+                       VectorSubtract(relativelightorigin, surf->poly_center, temp);
+                       if (DotProduct(temp, temp) < lightradius2 + surf->poly_radius2)
                        {
-                               if (surf->visframe == r_framecount)
+                               f = PlaneDiff(relativelightorigin, surf->plane);
+                               if (surf->flags & SURF_PLANEBACK)
+                                       f = -f;
+                               if (f >= -0.1 && f < lightradius)
+                               {
+                                       f = PlaneDiff(relativeeyeorigin, surf->plane);
+                                       if (surf->flags & SURF_PLANEBACK)
+                                               f = -f;
+                                       if (f > 0)
+                                       {
+                                               t = surf->texinfo->texture->currentframe;
+                                               if (t->rendertype == SURFRENDER_OPAQUE && t->flags & SURF_SHADOWLIGHT)
+                                               {
+                                                       for (mesh = surf->mesh;mesh;mesh = mesh->chain)
+                                                       {
+                                                               R_Mesh_ResizeCheck(mesh->numverts);
+                                                               memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
+                                                               R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals, mesh->str, relativelightorigin, lightradius, lightcolor, t->texture, t->nmaptexture, NULL);
+                                                               R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals, mesh->str, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, t->glosstexture, t->nmaptexture, NULL);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       else
+       {
+               // world, already culled to view, just cull to light
+               for (surfnum = 0, surf = ent->model->surfaces + ent->model->firstmodelsurface;surfnum < ent->model->nummodelsurfaces;surfnum++, surf++)
+               {
+                       if (surf->visframe == r_framecount)
+                       {
+                               VectorSubtract(relativelightorigin, surf->poly_center, temp);
+                               if (DotProduct(temp, temp) < lightradius2 + surf->poly_radius2)
                                {
                                        f = PlaneDiff(relativelightorigin, surf->plane);
                                        if (surf->flags & SURF_PLANEBACK)
                                                f = -f;
                                        if (f >= -0.1 && f < lightradius)
                                        {
-                                               for (mesh = surf->mesh;mesh;mesh = mesh->chain)
+                                               t = surf->texinfo->texture->currentframe;
+                                               if (t->rendertype == SURFRENDER_OPAQUE && t->flags & SURF_SHADOWLIGHT)
                                                {
-                                                       R_Mesh_ResizeCheck(mesh->numverts);
-                                                       memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
-                                                       R_Shadow_RenderLighting(mesh->numverts, mesh->numtriangles, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals, mesh->str, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, t->texture, t->glosstexture, t->nmaptexture, NULL);
+                                                       for (mesh = surf->mesh;mesh;mesh = mesh->chain)
+                                                       {
+                                                               R_Mesh_ResizeCheck(mesh->numverts);
+                                                               memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
+                                                               R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals, mesh->str, relativelightorigin, lightradius, lightcolor, t->texture, t->nmaptexture, NULL);
+                                                               R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals, mesh->str, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, t->glosstexture, t->nmaptexture, NULL);
+                                                       }
                                                }
                                        }
                                }
index 9adc1b7..14faa5f 100644 (file)
@@ -212,7 +212,7 @@ static int Mod_LoadInternalSkin (char *basename, qbyte *skindata, int width, int
 extern void R_Model_Alias_Draw(entity_render_t *ent);
 extern void R_Model_Alias_DrawFakeShadow(entity_render_t *ent);
 extern void R_Model_Alias_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor);
+extern void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor);
 void Mod_LoadAliasModel (model_t *mod, void *buffer)
 {
        int                                             i, j, version, numverts, totalposes, totalskins, skinwidth, skinheight, totalverts, groupframes, groupskins;
@@ -764,7 +764,7 @@ extern void R_Model_Zymotic_DrawSky(entity_render_t *ent);
 extern void R_Model_Zymotic_Draw(entity_render_t *ent);
 extern void R_Model_Zymotic_DrawFakeShadow(entity_render_t *ent);
 extern void R_Model_Zymotic_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Model_Zymotic_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor);
+extern void R_Model_Zymotic_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor);
 void Mod_LoadZymoticModel(model_t *mod, void *buffer)
 {
        zymtype1header_t *pinmodel, *pheader;
index d3876ca..050fed9 100644 (file)
@@ -511,8 +511,7 @@ static void Mod_LoadTextures (lump_t *l)
                }
 
                // start out with no animation
-               tx->currentframe[0] = tx;
-               tx->currentframe[1] = tx;
+               tx->currentframe = tx;
        }
 
        // sequence the animations
@@ -745,6 +744,7 @@ void Mod_LoadLightList(void)
        }
 }
 
+/*
 static int castshadowcount = 0;
 void Mod_ProcessLightList(void)
 {
@@ -845,7 +845,7 @@ void Mod_ProcessLightList(void)
                        float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
                        float f, *v0, *v1, projectdistance;
 
-                       e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool);
+                       e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
 #if 0
                        {
                        vec3_t outermins, outermaxs, innermins, innermaxs;
@@ -998,6 +998,7 @@ void Mod_ProcessLightList(void)
                }
        }
 }
+*/
 
 
 /*
@@ -2702,16 +2703,18 @@ static void Mod_MakePortals(void)
 
 static void Mod_BuildSurfaceNeighbors (msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
 {
-       int surfnum, vertnum, snum, vnum;
+#if 0
+       int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
        msurface_t *surf, *s;
        float *v0, *v1, *v2, *v3;
        for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
-       {
                surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
-               for (vertnum = 0;vertnum < surf->poly_numverts;vertnum++)
+       for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
+       {
+               for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
                {
-                       v0 = surf->poly_verts + ((vertnum + 1) % surf->poly_numverts) * 3;
-                       v1 = surf->poly_verts + vertnum * 3;
+                       if (surf->neighborsurfaces[vertnum])
+                               continue;
                        surf->neighborsurfaces[vertnum] = NULL;
                        for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
                        {
@@ -2720,11 +2723,19 @@ static void Mod_BuildSurfaceNeighbors (msurface_t *surfaces, int numsurfaces, me
                                 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
                                 || s == surf)
                                        continue;
-                               for (vnum = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum < s->poly_numverts;vnum++, v2 = v3, v3 += 3)
+                               for (vnum = 0;vnum < s->poly_numverts;vnum++)
+                                       if (s->neighborsurfaces[vnum] == surf)
+                                               break;
+                               if (vnum < s->poly_numverts)
+                                       continue;
+                               for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
                                {
-                                       if (v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
+                                       if (s->neighborsurfaces[vnum] == NULL
+                                        && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
+                                         || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
                                        {
                                                surf->neighborsurfaces[vertnum] = s;
+                                               s->neighborsurfaces[vnum] = surf;
                                                break;
                                        }
                                }
@@ -2733,6 +2744,7 @@ static void Mod_BuildSurfaceNeighbors (msurface_t *surfaces, int numsurfaces, me
                        }
                }
        }
+#endif
 }
 
 /*
@@ -2743,7 +2755,7 @@ Mod_LoadBrushModel
 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
 extern void R_Model_Brush_Draw(entity_render_t *ent);
 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
-extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor);
+extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor);
 void Mod_LoadBrushModel (model_t *mod, void *buffer)
 {
        int                     i, j;
@@ -2883,7 +2895,7 @@ void Mod_LoadBrushModel (model_t *mod, void *buffer)
                        mod->radius2 = modelradius * modelradius;
                        // LordHavoc: build triangle meshs for entire model's geometry
                        // (only used for shadow volumes)
-                       mod->shadowmesh = Mod_ShadowMesh_Begin(originalloadmodel->mempool);
+                       mod->shadowmesh = Mod_ShadowMesh_Begin(originalloadmodel->mempool, 1024);
                        for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
                                if (surf->flags & SURF_SHADOWCAST)
                                        Mod_ShadowMesh_AddPolygon(originalloadmodel->mempool, mod->shadowmesh, surf->poly_numverts, surf->poly_verts);
@@ -2926,6 +2938,6 @@ void Mod_LoadBrushModel (model_t *mod, void *buffer)
        }
 
        loadmodel = originalloadmodel;
-       Mod_ProcessLightList ();
+       //Mod_ProcessLightList ();
 }
 
index 7ae603f..fb4c30a 100644 (file)
@@ -127,9 +127,10 @@ typedef struct texture_s
        // set if animated or there is an alternate frame set
        // (this is an optimization in the renderer)
        int animated;
-       // the current texture frames in animation
-       // (index with entity frame != 0)
-       struct texture_s *currentframe[2];
+       // the current texture frame in animation
+       struct texture_s *currentframe;
+       // current alpha of the texture
+       float currentalpha;
 }
 texture_t;
 
@@ -356,6 +357,7 @@ typedef struct mlight_s
        // maximum extent of the light for culling purposes
        float cullradius;
        float cullradius2;
+       /*
        // surfaces this shines on
        int numsurfaces;
        msurface_t **surfaces;
@@ -365,6 +367,7 @@ typedef struct mlight_s
        //svbspmesh_t *shadowvolume;
        //vec3_t shadowvolumemins, shadowvolumemaxs;
        shadowmesh_t *shadowvolume;
+       */
 }
 mlight_t;
 
index 10d3762..7cf2603 100644 (file)
@@ -410,6 +410,30 @@ void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtria
        }
 }
 
+void Mod_ValidateElements(const int *elements, int numtriangles, int numverts, const char *filename, int fileline)
+{
+       int i;
+       for (i = 0;i < numtriangles * 3;i++)
+               if ((unsigned int)elements[i] >= numverts)
+                       Con_Printf("Mod_ValidateElements: out of bounds element detected at %s:%d\n", filename, fileline);
+}
+
+/*
+a note on the cost of executing this function:
+per triangle: 188 (83 42 13 45 4 1)
+assignments: 83 (20 3 3 3 1 4 4 1 3 4 3 4 30)
+adds: 42 (2 2 2 2 3 2 2 27)
+subtracts: 13 (3 3 3 1 3)
+multiplies: 45 (6 3 6 6 3 3 6 6 6)
+rsqrts: 4 (1 1 1 1)
+compares: 1 (1)
+per vertex: 39 (12 6 18 3)
+assignments: 12 (4 4 4)
+adds: 6 (2 2 2)
+multiplies: 18 (6 6 6)
+rsqrts: 3 (1 1 1)
+*/
+
 void Mod_BuildTextureVectorsAndNormals(int numverts, int numtriangles, const float *vertex, const float *texcoord, const int *elements, float *svectors, float *tvectors, float *normals)
 {
        int i, tnum, voffset;
@@ -423,6 +447,7 @@ void Mod_BuildTextureVectorsAndNormals(int numverts, int numtriangles, const flo
        for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
        {
                // calculate texture matrix for triangle
+               // 20 assignments
                voffset = e[0] * 4;
                vert[0][0] = vertex[voffset+0];
                vert[0][1] = vertex[voffset+1];
@@ -438,23 +463,35 @@ void Mod_BuildTextureVectorsAndNormals(int numverts, int numtriangles, const flo
                vert[2][1] = vertex[voffset+1];
                vert[2][2] = vertex[voffset+2];
                vert[2][3] = texcoord[voffset];
+               // 3 assignments, 3 subtracts
                VectorSubtract(vert[1], vert[0], vec[0]);
+               // 3 assignments, 3 subtracts
                VectorSubtract(vert[2], vert[0], vec[1]);
+               // 3 assignments, 3 subtracts, 6 multiplies
                CrossProduct(vec[0], vec[1], normal);
+               // 1 assignment, 2 adds, 3 multiplies, 1 compare
                if (DotProduct(normal, normal) >= 0.001)
                {
+                       // 4 assignments, 1 rsqrt, 2 adds, 6 multiplies
                        VectorNormalize(normal);
                        sdir[0] = (vert[1][3] - vert[0][3]) * (vert[2][0] - vert[0][0]) - (vert[2][3] - vert[0][3]) * (vert[1][0] - vert[0][0]);
                        sdir[1] = (vert[1][3] - vert[0][3]) * (vert[2][1] - vert[0][1]) - (vert[2][3] - vert[0][3]) * (vert[1][1] - vert[0][1]);
                        sdir[2] = (vert[1][3] - vert[0][3]) * (vert[2][2] - vert[0][2]) - (vert[2][3] - vert[0][3]) * (vert[1][2] - vert[0][2]);
+                       // 4 assignments, 1 rsqrt, 2 adds, 6 multiplies
                        VectorNormalize(sdir);
+                       // 1 assignments, 1 negates, 2 adds, 3 multiplies
                        f = -DotProduct(sdir, normal);
+                       // 3 assignments, 3 adds, 3 multiplies
                        VectorMA(sdir, f, normal, sdir);
+                       // 4 assignments, 1 rsqrt, 2 adds, 6 multiplies
                        VectorNormalize(sdir);
+                       // 3 assignments, 3 subtracts, 6 multiplies
                        CrossProduct(sdir, normal, tdir);
                        // this is probably not necessary
+                       // 4 assignments, 1 rsqrt, 2 adds, 6 multiplies
                        VectorNormalize(tdir);
                        // accumulate matrix onto verts used by triangle
+                       // 30 assignments, 27 adds
                        for (i = 0;i < 3;i++)
                        {
                                voffset = e[i] * 4;
@@ -473,36 +510,60 @@ void Mod_BuildTextureVectorsAndNormals(int numverts, int numtriangles, const flo
        // now we could divide the vectors by the number of averaged values on
        // each vertex...  but instead normalize them
        for (i = 0, v = svectors;i < numverts;i++, v += 4)
+               // 4 assignments, 1 rsqrt, 2 adds, 6 multiplies
                VectorNormalize(v);
        for (i = 0, v = tvectors;i < numverts;i++, v += 4)
+               // 4 assignments, 1 rsqrt, 2 adds, 6 multiplies
                VectorNormalize(v);
        for (i = 0, v = normals;i < numverts;i++, v += 4)
+               // 4 assignments, 1 rsqrt, 2 adds, 6 multiplies
                VectorNormalize(v);
 }
 
 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts)
 {
        shadowmesh_t *mesh;
+#define ALLOCMESHINPIECES 0
+#if ALLOCMESHINPIECES
+       mesh = Mem_Alloc(mempool, sizeof(shadowmesh_t));
+#else
        mesh = Mem_Alloc(mempool, sizeof(shadowmesh_t) + maxverts * sizeof(float[4]) + maxverts * sizeof(int[3]) + maxverts * sizeof(int[3]));
+#endif
        mesh->maxverts = maxverts;
        mesh->maxtriangles = maxverts;
        mesh->numverts = 0;
        mesh->numtriangles = 0;
+#if ALLOCMESHINPIECES
+       mesh->verts = Mem_Alloc(mempool, maxverts * sizeof(float[4]));
+       mesh->elements = Mem_Alloc(mempool, maxverts * sizeof(int[3]));
+       mesh->neighbors = Mem_Alloc(mempool, maxverts * sizeof(int[3]));
+#else
        mesh->verts = (float *)(mesh + 1);
        mesh->elements = (int *)(mesh->verts + mesh->maxverts * 4);
        mesh->neighbors = (int *)(mesh->elements + mesh->maxtriangles * 3);
+#endif
        return mesh;
 }
 
 shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh)
 {
        shadowmesh_t *newmesh;
+#if ALLOCMESHINPIECES
+       newmesh = Mem_Alloc(mempool, sizeof(shadowmesh_t));
+#else
        newmesh = Mem_Alloc(mempool, sizeof(shadowmesh_t) + oldmesh->numverts * sizeof(float[4]) + oldmesh->numtriangles * sizeof(int[3]) + oldmesh->numtriangles * sizeof(int[3]));
+#endif
        newmesh->maxverts = newmesh->numverts = oldmesh->numverts;
        newmesh->maxtriangles = newmesh->numtriangles = oldmesh->numtriangles;
+#if ALLOCMESHINPIECES
+       newmesh->verts = Mem_Alloc(mempool, newmesh->maxverts * sizeof(float[4]));
+       newmesh->elements = Mem_Alloc(mempool, newmesh->numtriangles * sizeof(int[3]));
+       newmesh->neighbors = Mem_Alloc(mempool, newmesh->numtriangles * sizeof(int[3]));
+#else
        newmesh->verts = (float *)(newmesh + 1);
        newmesh->elements = (int *)(newmesh->verts + newmesh->maxverts * 4);
        newmesh->neighbors = (int *)(newmesh->elements + newmesh->maxtriangles * 3);
+#endif
        memcpy(newmesh->verts, oldmesh->verts, newmesh->numverts * sizeof(float[4]));
        memcpy(newmesh->elements, oldmesh->elements, newmesh->numtriangles * sizeof(int[3]));
        memcpy(newmesh->neighbors, oldmesh->neighbors, newmesh->numtriangles * sizeof(int[3]));
@@ -524,14 +585,33 @@ int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, float *v)
        return j;
 }
 
+void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, float *vert0, float *vert1, float *vert2)
+{
+       while (mesh->numverts + 3 > mesh->maxverts || mesh->numtriangles + 1 > mesh->maxtriangles)
+       {
+               if (mesh->next == NULL)
+                       mesh->next = Mod_ShadowMesh_Alloc(mempool, max(mesh->maxtriangles, 1));
+               mesh = mesh->next;
+       }
+       mesh->elements[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vert0);
+       mesh->elements[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vert1);
+       mesh->elements[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vert2);
+       mesh->numtriangles++;
+}
+
 void Mod_ShadowMesh_AddPolygon(mempool_t *mempool, shadowmesh_t *mesh, int numverts, float *verts)
 {
+       int i;
+       float *v;
+       for (i = 0, v = verts + 3;i < numverts - 2;i++, v += 3)
+               Mod_ShadowMesh_AddTriangle(mempool, mesh, verts, v, v + 3);
+       /*
        int i, i1, i2, i3;
        float *v;
-       while (numverts + mesh->numverts > mesh->maxverts || (numverts - 2) + mesh->numtriangles > mesh->maxtriangles)
+       while (mesh->numverts + numverts > mesh->maxverts || mesh->numtriangles + (numverts - 2) > mesh->maxtriangles)
        {
                if (mesh->next == NULL)
-                       mesh->next = Mod_ShadowMesh_Alloc(mempool, max(4096, numverts));
+                       mesh->next = Mod_ShadowMesh_Alloc(mempool, max(mesh->maxtriangles, numverts));
                mesh = mesh->next;
        }
        i1 = Mod_ShadowMesh_AddVertex(mesh, verts);
@@ -546,15 +626,24 @@ void Mod_ShadowMesh_AddPolygon(mempool_t *mempool, shadowmesh_t *mesh, int numve
                mesh->elements[mesh->numtriangles * 3 + 2] = i3;
                mesh->numtriangles++;
        }
+       */
 }
 
-shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool)
+void Mod_ShadowMesh_AddMesh(mempool_t *mempool, shadowmesh_t *mesh, int numverts, float *verts, int numtris, int *elements)
 {
-       return Mod_ShadowMesh_Alloc(mempool, 4096);
+       int i;
+       for (i = 0;i < numtris;i++, elements += 3)
+               Mod_ShadowMesh_AddTriangle(mempool, mesh, verts + elements[0] * 4, verts + elements[1] * 4, verts + elements[2] * 4);
+}
+
+shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int initialnumtriangles)
+{
+       return Mod_ShadowMesh_Alloc(mempool, initialnumtriangles);
 }
 
 shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh)
 {
+#if 1
        //int i;
        shadowmesh_t *mesh, *newmesh, *nextmesh;
        // reallocate meshs to conserve space
@@ -573,6 +662,11 @@ shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh)
                }
                Mem_Free(mesh);
        }
+#else
+       shadowmesh_t *mesh;
+       for (mesh = firstmesh;mesh;mesh = mesh->next)
+               Mod_BuildTriangleNeighbors(mesh->neighbors, mesh->elements, mesh->numtriangles);
+#endif
        return firstmesh;
 }
 
@@ -629,6 +723,11 @@ void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
        for (;mesh;mesh = nextmesh)
        {
                nextmesh = mesh->next;
+#if ALLOCMESHINPIECES
+               Mem_Free(mesh->verts);
+               Mem_Free(mesh->elements);
+               Mem_Free(mesh->neighbors);
+#endif
                Mem_Free(mesh);
        }
 }
index ab499f5..cebb49d 100644 (file)
@@ -251,7 +251,7 @@ typedef struct model_s
        // draw a shadow volume for the model based on light source
        void(*DrawShadowVolume)(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
        // draw the lighting on a model (through stencil)
-       void(*DrawLight)(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float lightdistbias, float lightsubtract, float *lightcolor);
+       void(*DrawLight)(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor);
 
        // memory pool for allocations
        mempool_t               *mempool;
@@ -290,13 +290,16 @@ extern char loadname[32]; // for hunk tags
 
 int Mod_FindTriangleWithEdge(const int *elements, int numtriangles, int start, int end);
 void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtriangles);
+void Mod_ValidateElements(const int *elements, int numtriangles, int numverts, const char *filename, int fileline);
 void Mod_BuildTextureVectorsAndNormals(int numverts, int numtriangles, const float *vertex, const float *texcoord, const int *elements, float *svectors, float *tvectors, float *normals);
 
 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts);
 shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh);
 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, float *v);
+void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, float *vert0, float *vert1, float *vert2);
 void Mod_ShadowMesh_AddPolygon(mempool_t *mempool, shadowmesh_t *mesh, int numverts, float *verts);
-shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool);
+void Mod_ShadowMesh_AddMesh(mempool_t *mempool, shadowmesh_t *mesh, int numverts, float *verts, int numtris, int *elements);
+shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int initialnumtriangles);
 shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh);
 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius);
 void Mod_ShadowMesh_Free(shadowmesh_t *mesh);
index 1c46419..8e65878 100644 (file)
--- a/r_light.c
+++ b/r_light.c
@@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 #include "cl_collision.h"
+#include "r_shadow.h"
 
 rdlight_t r_dlight[MAX_DLIGHTS];
 int r_numdlights = 0;
@@ -658,11 +659,10 @@ void R_ModelLightPoint (const entity_render_t *ent, vec3_t color, const vec3_t p
        }
 
        color[0] = color[1] = color[2] = r_ambient.value * (2.0f / 128.0f);
-       if (!cl.worldmodel->numlights)
+       if (!cl.worldmodel->numlights && r_shadow_lightingmode < 2)
                RecursiveLightPoint (color, cl.worldmodel->nodes, p[0], p[1], p[2], p[2] - 65536);
 }
 
-extern cvar_t r_shadows;
 void R_LightModel(const entity_render_t *ent, int numverts, float *vertices, float *normals, float *colors, float colorr, float colorg, float colorb, int worldcoords)
 {
        int i, j, nearlights = 0, maxnearlights = r_modellights.integer;
@@ -691,7 +691,7 @@ void R_LightModel(const entity_render_t *ent, int numverts, float *vertices, flo
        {
                R_ModelLightPoint(ent, basecolor, ent->origin);
 
-               if (r_shadows.integer != 3)
+               if (r_shadow_lightingmode < 2)
                {
                        nl = &nearlight[0];
                        for (i = 0;i < ent->numentlights;i++)
@@ -744,74 +744,77 @@ void R_LightModel(const entity_render_t *ent, int numverts, float *vertices, flo
                                        nl->offset = sl->distbias;
                                }
                        }
-                       for (i = 0;i < r_numdlights;i++)
+                       if (r_shadow_lightingmode < 1)
                        {
-                               rd = r_dlight + i;
-                               VectorCopy(rd->origin, v);
-                               if (v[0] < ent->mins[0]) v[0] = ent->mins[0];if (v[0] > ent->maxs[0]) v[0] = ent->maxs[0];
-                               if (v[1] < ent->mins[1]) v[1] = ent->mins[1];if (v[1] > ent->maxs[1]) v[1] = ent->maxs[1];
-                               if (v[2] < ent->mins[2]) v[2] = ent->mins[2];if (v[2] > ent->maxs[2]) v[2] = ent->maxs[2];
-                               VectorSubtract (v, rd->origin, v);
-                               if (DotProduct(v, v) < rd->cullradius2)
+                               for (i = 0;i < r_numdlights;i++)
                                {
-                                       if (CL_TraceLine(ent->origin, rd->origin, NULL, NULL, 0, false, NULL) != 1)
-                                               continue;
-                                       VectorSubtract (ent->origin, rd->origin, v);
-                                       f = ((1.0f / (DotProduct(v, v) + LIGHTOFFSET)) - rd->subtract);
-                                       VectorScale(rd->light, f, ambientcolor);
-                                       intensity = DotProduct(ambientcolor, ambientcolor);
-                                       if (f < 0)
-                                               intensity *= -1.0f;
-                                       if (nearlights < maxnearlights)
-                                               j = nearlights++;
-                                       else
+                                       rd = r_dlight + i;
+                                       VectorCopy(rd->origin, v);
+                                       if (v[0] < ent->mins[0]) v[0] = ent->mins[0];if (v[0] > ent->maxs[0]) v[0] = ent->maxs[0];
+                                       if (v[1] < ent->mins[1]) v[1] = ent->mins[1];if (v[1] > ent->maxs[1]) v[1] = ent->maxs[1];
+                                       if (v[2] < ent->mins[2]) v[2] = ent->mins[2];if (v[2] > ent->maxs[2]) v[2] = ent->maxs[2];
+                                       VectorSubtract (v, rd->origin, v);
+                                       if (DotProduct(v, v) < rd->cullradius2)
                                        {
-                                               for (j = 0;j < maxnearlights;j++)
+                                               if (CL_TraceLine(ent->origin, rd->origin, NULL, NULL, 0, false, NULL) != 1)
+                                                       continue;
+                                               VectorSubtract (ent->origin, rd->origin, v);
+                                               f = ((1.0f / (DotProduct(v, v) + LIGHTOFFSET)) - rd->subtract);
+                                               VectorScale(rd->light, f, ambientcolor);
+                                               intensity = DotProduct(ambientcolor, ambientcolor);
+                                               if (f < 0)
+                                                       intensity *= -1.0f;
+                                               if (nearlights < maxnearlights)
+                                                       j = nearlights++;
+                                               else
                                                {
-                                                       if (nearlight[j].intensity < intensity)
+                                                       for (j = 0;j < maxnearlights;j++)
                                                        {
-                                                               if (nearlight[j].intensity > 0)
-                                                                       VectorAdd(basecolor, nearlight[j].ambientlight, basecolor);
-                                                               break;
+                                                               if (nearlight[j].intensity < intensity)
+                                                               {
+                                                                       if (nearlight[j].intensity > 0)
+                                                                               VectorAdd(basecolor, nearlight[j].ambientlight, basecolor);
+                                                                       break;
+                                                               }
                                                        }
                                                }
-                                       }
-                                       if (j >= maxnearlights)
-                                       {
-                                               // this light is less significant than all others,
-                                               // add it to ambient
-                                               if (intensity > 0)
-                                                       VectorAdd(basecolor, ambientcolor, basecolor);
-                                       }
-                                       else
-                                       {
-                                               nl = nearlight + j;
-                                               nl->intensity = intensity;
-                                               // transform the light into the model's coordinate system
-                                               if (worldcoords)
-                                                       VectorCopy(rd->origin, nl->origin);
+                                               if (j >= maxnearlights)
+                                               {
+                                                       // this light is less significant than all others,
+                                                       // add it to ambient
+                                                       if (intensity > 0)
+                                                               VectorAdd(basecolor, ambientcolor, basecolor);
+                                               }
                                                else
                                                {
-                                                       Matrix4x4_Transform(&ent->inversematrix, rd->origin, nl->origin);
-                                                       /*
-                                                       Con_Printf("%i %s : %f %f %f : %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n"
-                                                       , rd - r_dlight, ent->model->name
-                                                       , rd->origin[0], rd->origin[1], rd->origin[2]
-                                                       , nl->origin[0], nl->origin[1], nl->origin[2]
-                                                       , ent->inversematrix.m[0][0], ent->inversematrix.m[0][1], ent->inversematrix.m[0][2], ent->inversematrix.m[0][3]
-                                                       , ent->inversematrix.m[1][0], ent->inversematrix.m[1][1], ent->inversematrix.m[1][2], ent->inversematrix.m[1][3]
-                                                       , ent->inversematrix.m[2][0], ent->inversematrix.m[2][1], ent->inversematrix.m[2][2], ent->inversematrix.m[2][3]
-                                                       , ent->inversematrix.m[3][0], ent->inversematrix.m[3][1], ent->inversematrix.m[3][2], ent->inversematrix.m[3][3]);
-                                                       */
+                                                       nl = nearlight + j;
+                                                       nl->intensity = intensity;
+                                                       // transform the light into the model's coordinate system
+                                                       if (worldcoords)
+                                                               VectorCopy(rd->origin, nl->origin);
+                                                       else
+                                                       {
+                                                               Matrix4x4_Transform(&ent->inversematrix, rd->origin, nl->origin);
+                                                               /*
+                                                               Con_Printf("%i %s : %f %f %f : %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n"
+                                                               , rd - r_dlight, ent->model->name
+                                                               , rd->origin[0], rd->origin[1], rd->origin[2]
+                                                               , nl->origin[0], nl->origin[1], nl->origin[2]
+                                                               , ent->inversematrix.m[0][0], ent->inversematrix.m[0][1], ent->inversematrix.m[0][2], ent->inversematrix.m[0][3]
+                                                               , ent->inversematrix.m[1][0], ent->inversematrix.m[1][1], ent->inversematrix.m[1][2], ent->inversematrix.m[1][3]
+                                                               , ent->inversematrix.m[2][0], ent->inversematrix.m[2][1], ent->inversematrix.m[2][2], ent->inversematrix.m[2][3]
+                                                               , ent->inversematrix.m[3][0], ent->inversematrix.m[3][1], ent->inversematrix.m[3][2], ent->inversematrix.m[3][3]);
+                                                               */
+                                                       }
+                                                       // integrate mscale into falloff, for maximum speed
+                                                       nl->falloff = mscale;
+                                                       VectorCopy(ambientcolor, nl->ambientlight);
+                                                       nl->light[0] = rd->light[0] * colorr * 4.0f;
+                                                       nl->light[1] = rd->light[1] * colorg * 4.0f;
+                                                       nl->light[2] = rd->light[2] * colorb * 4.0f;
+                                                       nl->subtract = rd->subtract;
+                                                       nl->offset = LIGHTOFFSET;
                                                }
-                                               // integrate mscale into falloff, for maximum speed
-                                               nl->falloff = mscale;
-                                               VectorCopy(ambientcolor, nl->ambientlight);
-                                               nl->light[0] = rd->light[0] * colorr * 4.0f;
-                                               nl->light[1] = rd->light[1] * colorg * 4.0f;
-                                               nl->light[2] = rd->light[2] * colorb * 4.0f;
-                                               nl->subtract = rd->subtract;
-                                               nl->offset = LIGHTOFFSET;
                                        }
                                }
                        }
@@ -881,6 +884,8 @@ void R_UpdateEntLights(entity_render_t *ent)
        int i;
        const mlight_t *sl;
        vec3_t v;
+       if (r_shadow_lightingmode >= 2)
+               return;
        VectorSubtract(ent->origin, ent->entlightsorigin, v);
        if (ent->entlightsframe != (r_framecount - 1) || (realtime > ent->entlightstime && DotProduct(v,v) >= 1.0f))
        {
index 94b09b3..4ee1b62 100644 (file)
@@ -1,6 +1,9 @@
 
 #include "quakedef.h"
 #include "r_shadow.h"
+#include "cl_collision.h"
+
+extern void R_Shadow_EditLights_Init(void);
 
 #define SHADOWSTAGE_NONE 0
 #define SHADOWSTAGE_STENCIL 1
@@ -8,6 +11,9 @@
 #define SHADOWSTAGE_ERASESTENCIL 3
 
 int r_shadowstage = SHADOWSTAGE_NONE;
+int r_shadow_reloadlights = false;
+
+int r_shadow_lightingmode = 0;
 
 mempool_t *r_shadow_mempool;
 
@@ -21,18 +27,18 @@ rtexture_t *r_shadow_normalsattenuationtexture;
 rtexture_t *r_shadow_normalscubetexture;
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_blankbumptexture;
+rtexture_t *r_shadow_blankglosstexture;
+rtexture_t *r_shadow_blankwhitetexture;
 
-cvar_t r_shadow1 = {0, "r_shadow1", "2"};
-cvar_t r_shadow2 = {0, "r_shadow2", "0"};
-cvar_t r_shadow3 = {0, "r_shadow3", "32768"};
-cvar_t r_shadow4 = {0, "r_shadow4", "0"};
-cvar_t r_shadow5 = {0, "r_shadow5", "0"};
-cvar_t r_shadow6 = {0, "r_shadow6", "0"};
-cvar_t r_light_realtime = {0, "r_light_realtime", "0"};
-cvar_t r_light_quality = {0, "r_light_quality", "1"};
-cvar_t r_light_gloss = {0, "r_light_gloss", "0"};
-cvar_t r_light_debuglight = {0, "r_light_debuglight", "-1"};
+cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "2"};
+cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
+cvar_t r_shadow_realtime = {0, "r_shadow_realtime", "0"};
+cvar_t r_shadow_erasebydrawing = {0, "r_shadow_erasebydrawing", "0"};
+cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "0"};
+cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
+cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
 
+void R_Shadow_ClearWorldLights(void);
 void r_shadow_start(void)
 {
        // allocate vertex processing arrays
@@ -45,15 +51,23 @@ void r_shadow_start(void)
        r_shadow_normalscubetexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_blankbumptexture = NULL;
+       r_shadow_blankglosstexture = NULL;
+       r_shadow_blankwhitetexture = NULL;
        r_shadow_texturepool = NULL;
+       R_Shadow_ClearWorldLights();
+       r_shadow_reloadlights = true;
 }
 
 void r_shadow_shutdown(void)
 {
+       R_Shadow_ClearWorldLights();
+       r_shadow_reloadlights = true;
        r_shadow_normalsattenuationtexture = NULL;
        r_shadow_normalscubetexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_blankbumptexture = NULL;
+       r_shadow_blankglosstexture = NULL;
+       r_shadow_blankwhitetexture = NULL;
        R_FreeTexturePool(&r_shadow_texturepool);
        maxshadowelements = 0;
        shadowelements = NULL;
@@ -62,102 +76,59 @@ void r_shadow_shutdown(void)
        Mem_FreePool(&r_shadow_mempool);
 }
 
+void R_Shadow_LoadWorldLights(const char *mapname);
 void r_shadow_newmap(void)
 {
+       R_Shadow_ClearWorldLights();
+       r_shadow_reloadlights = true;
 }
 
 void R_Shadow_Init(void)
 {
-       Cvar_RegisterVariable(&r_shadow1);
-       Cvar_RegisterVariable(&r_shadow2);
-       Cvar_RegisterVariable(&r_shadow3);
-       Cvar_RegisterVariable(&r_shadow4);
-       Cvar_RegisterVariable(&r_shadow5);
-       Cvar_RegisterVariable(&r_shadow6);
-       Cvar_RegisterVariable(&r_light_realtime);
-       Cvar_RegisterVariable(&r_light_quality);
-       Cvar_RegisterVariable(&r_light_gloss);
-       Cvar_RegisterVariable(&r_light_debuglight);
+       Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
+       Cvar_RegisterVariable(&r_shadow_lightintensityscale);
+       Cvar_RegisterVariable(&r_shadow_realtime);
+       Cvar_RegisterVariable(&r_shadow_texture3d);
+       Cvar_RegisterVariable(&r_shadow_gloss);
+       Cvar_RegisterVariable(&r_shadow_debuglight);
+       Cvar_RegisterVariable(&r_shadow_erasebydrawing);
+       R_Shadow_EditLights_Init();
        R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
 }
 
-void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
+void R_Shadow_ProjectVertices(const float *in, float *out, int numverts, const float *relativelightorigin, float projectdistance)
 {
-       int i, *e, *n, *out, tris;
-       float *v0, *v1, *v2, temp[3], f;
-       if (projectdistance < 0.1)
-       {
-               Con_Printf("R_Shadow_Volume: projectdistance %f\n");
-               return;
-       }
-       projectdistance = lightradius;
-// terminology:
-//
-// frontface:
-// a triangle facing the light source
-//
-// backface:
-// a triangle not facing the light source
-//
-// shadow volume:
-// an extrusion of the backfaces, beginning at the original geometry and
-// ending further from the light source than the original geometry
-// (presumably at least as far as the light's radius, if the light has a
-// radius at all), capped at both front and back to avoid any problems
-//
-// description:
-// draws the shadow volumes of the model.
-// requirements:
-// vertex loations must already be in vertex before use.
-// vertex must have capacity for numverts * 2.
-
-       // make sure trianglefacinglight is big enough for this volume
-       if (maxtrianglefacinglight < numtris)
-       {
-               maxtrianglefacinglight = numtris;
-               if (trianglefacinglight)
-                       Mem_Free(trianglefacinglight);
-               trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
-       }
-
-       // make sure shadowelements is big enough for this volume
-       if (maxshadowelements < numtris * 24)
-       {
-               maxshadowelements = numtris * 24;
-               if (shadowelements)
-                       Mem_Free(shadowelements);
-               shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
-       }
-
-       // make projected vertices
-       // by clever use of elements we'll construct the whole shadow from
-       // the unprojected vertices and these projected vertices
-       for (i = 0, v0 = vertex, v1 = vertex + numverts * 4;i < numverts;i++, v0 += 4, v1 += 4)
+       int i;
+       for (i = 0;i < numverts;i++, in += 4, out += 4)
        {
-#if 0
-               v1[0] = v0[0] + 250.0f * (v0[0] - relativelightorigin[0]);
-               v1[1] = v0[1] + 250.0f * (v0[1] - relativelightorigin[1]);
-               v1[2] = v0[2] + 250.0f * (v0[2] - relativelightorigin[2]);
+#if 1
+               out[0] = in[0] + 1000000.0f * (in[0] - relativelightorigin[0]);
+               out[1] = in[1] + 1000000.0f * (in[1] - relativelightorigin[1]);
+               out[2] = in[2] + 1000000.0f * (in[2] - relativelightorigin[2]);
 #elif 0
-               VectorSubtract(v0, relativelightorigin, temp);
+               VectorSubtract(in, relativelightorigin, temp);
                f = lightradius / sqrt(DotProduct(temp,temp));
                if (f < 1)
                        f = 1;
-               VectorMA(relativelightorigin, f, temp, v1);
+               VectorMA(relativelightorigin, f, temp, out);
 #else
-               VectorSubtract(v0, relativelightorigin, temp);
+               VectorSubtract(in, relativelightorigin, temp);
                f = projectdistance / sqrt(DotProduct(temp,temp));
-               VectorMA(v0, f, temp, v1);
+               VectorMA(in, f, temp, out);
 #endif
        }
+}
 
-       // check which triangles are facing the light
-       for (i = 0, e = elements;i < numtris;i++, e += 3)
+void R_Shadow_MakeTriangleShadowFlags(const int *elements, const float *vertex, int numtris, qbyte *trianglefacinglight, const float *relativelightorigin, float lightradius)
+{
+       int i;
+       const float *v0, *v1, *v2;
+       for (i = 0;i < numtris;i++, elements += 3)
        {
                // calculate triangle facing flag
-               v0 = vertex + e[0] * 4;
-               v1 = vertex + e[1] * 4;
-               v2 = vertex + e[2] * 4;
+               v0 = vertex + elements[0] * 4;
+               v1 = vertex + elements[1] * 4;
+               v2 = vertex + elements[2] * 4;
                // we do not need to normalize the surface normal because both sides
                // of the comparison use it, therefore they are both multiplied the
                // same amount...  furthermore the subtract can be done on the
@@ -176,7 +147,7 @@ void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, in
 #else
                // readable version
                {
-               float dir0[3], dir1[3];
+               float dir0[3], dir1[3], temp[3], f;
 
                // calculate two mostly perpendicular edge directions
                VectorSubtract(v0, v1, dir0);
@@ -201,136 +172,140 @@ void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, in
                }
 #endif
        }
+}
 
-       // output triangle elements
-       out = shadowelements;
-       tris = 0;
-
-       // check each backface for bordering frontfaces,
+int R_Shadow_BuildShadowVolumeTriangles(const int *elements, const int *neighbors, int numtris, int numverts, const qbyte *trianglefacinglight, int *out)
+{
+       int i, tris;
+       // check each frontface for bordering backfaces,
        // and cast shadow polygons from those edges,
        // also create front and back caps for shadow volume
-       for (i = 0, e = elements, n = neighbors;i < numtris;i++, e += 3, n += 3)
+       tris = 0;
+       for (i = 0;i < numtris;i++, elements += 3, neighbors += 3)
        {
-#if 1
                if (trianglefacinglight[i])
                {
-                       // triangle is backface and therefore casts shadow,
+                       // triangle is frontface and therefore casts shadow,
                        // output front and back caps for shadow volume
-#if 1
                        // front cap
-                       out[0] = e[0];
-                       out[1] = e[1];
-                       out[2] = e[2];
+                       out[0] = elements[0];
+                       out[1] = elements[1];
+                       out[2] = elements[2];
                        // rear cap (with flipped winding order)
-                       out[3] = e[0] + numverts;
-                       out[4] = e[2] + numverts;
-                       out[5] = e[1] + numverts;
-                       out += 6;
-                       tris += 2;
-#elif 1
-                       // rear cap
-                       out[0] = e[0] + numverts;
-                       out[1] = e[2] + numverts;
-                       out[2] = e[1] + numverts;
-                       out += 3;
-                       tris += 1;
-#endif
-                       // check the edges
-                       if (n[0] < 0 || !trianglefacinglight[n[0]])
-                       {
-                               out[0] = e[1];
-                               out[1] = e[0];
-                               out[2] = e[0] + numverts;
-                               out[3] = e[1];
-                               out[4] = e[0] + numverts;
-                               out[5] = e[1] + numverts;
-                               out += 6;
-                               tris += 2;
-                       }
-                       if (n[1] < 0 || !trianglefacinglight[n[1]])
-                       {
-                               out[0] = e[2];
-                               out[1] = e[1];
-                               out[2] = e[1] + numverts;
-                               out[3] = e[2];
-                               out[4] = e[1] + numverts;
-                               out[5] = e[2] + numverts;
-                               out += 6;
-                               tris += 2;
-                       }
-                       if (n[2] < 0 || !trianglefacinglight[n[2]])
-                       {
-                               out[0] = e[0];
-                               out[1] = e[2];
-                               out[2] = e[2] + numverts;
-                               out[3] = e[0];
-                               out[4] = e[2] + numverts;
-                               out[5] = e[0] + numverts;
-                               out += 6;
-                               tris += 2;
-                       }
-               }
-#else
-               if (!trianglefacinglight[i])
-               {
-                       // triangle is backface and therefore casts shadow,
-                       // output front and back caps for shadow volume
-#if 1
-                       // front cap (with flipped winding order)
-                       out[0] = e[0];
-                       out[1] = e[2];
-                       out[2] = e[1];
-                       // rear cap
-                       out[3] = e[0] + numverts;
-                       out[4] = e[1] + numverts;
-                       out[5] = e[2] + numverts;
+                       out[3] = elements[0] + numverts;
+                       out[4] = elements[2] + numverts;
+                       out[5] = elements[1] + numverts;
                        out += 6;
                        tris += 2;
-#elif 1
-                       // rear cap
-                       out[0] = e[0] + numverts;
-                       out[1] = e[1] + numverts;
-                       out[2] = e[2] + numverts;
-                       out += 3;
-                       tris += 1;
-#endif
                        // check the edges
-                       if (n[0] < 0 || trianglefacinglight[n[0]])
+                       if (neighbors[0] < 0 || !trianglefacinglight[neighbors[0]])
                        {
-                               out[0] = e[0];
-                               out[1] = e[1];
-                               out[2] = e[1] + numverts;
-                               out[3] = e[0];
-                               out[4] = e[1] + numverts;
-                               out[5] = e[0] + numverts;
+                               out[0] = elements[1];
+                               out[1] = elements[0];
+                               out[2] = elements[0] + numverts;
+                               out[3] = elements[1];
+                               out[4] = elements[0] + numverts;
+                               out[5] = elements[1] + numverts;
                                out += 6;
                                tris += 2;
                        }
-                       if (n[1] < 0 || trianglefacinglight[n[1]])
+                       if (neighbors[1] < 0 || !trianglefacinglight[neighbors[1]])
                        {
-                               out[0] = e[1];
-                               out[1] = e[2];
-                               out[2] = e[2] + numverts;
-                               out[3] = e[1];
-                               out[4] = e[2] + numverts;
-                               out[5] = e[1] + numverts;
+                               out[0] = elements[2];
+                               out[1] = elements[1];
+                               out[2] = elements[1] + numverts;
+                               out[3] = elements[2];
+                               out[4] = elements[1] + numverts;
+                               out[5] = elements[2] + numverts;
                                out += 6;
                                tris += 2;
                        }
-                       if (n[2] < 0 || trianglefacinglight[n[2]])
+                       if (neighbors[2] < 0 || !trianglefacinglight[neighbors[2]])
                        {
-                               out[0] = e[2];
-                               out[1] = e[0];
-                               out[2] = e[0] + numverts;
-                               out[3] = e[2];
-                               out[4] = e[0] + numverts;
-                               out[5] = e[2] + numverts;
+                               out[0] = elements[0];
+                               out[1] = elements[2];
+                               out[2] = elements[2] + numverts;
+                               out[3] = elements[0];
+                               out[4] = elements[2] + numverts;
+                               out[5] = elements[0] + numverts;
                                out += 6;
                                tris += 2;
                        }
                }
-#endif
        }
+       return tris;
+}
+
+void R_Shadow_ResizeTriangleFacingLight(int numtris)
+{
+       // make sure trianglefacinglight is big enough for this volume
+       if (maxtrianglefacinglight < numtris)
+       {
+               maxtrianglefacinglight = numtris;
+               if (trianglefacinglight)
+                       Mem_Free(trianglefacinglight);
+               trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
+       }
+}
+
+void R_Shadow_ResizeShadowElements(int numtris)
+{
+       // make sure shadowelements is big enough for this volume
+       if (maxshadowelements < numtris * 24)
+       {
+               maxshadowelements = numtris * 24;
+               if (shadowelements)
+                       Mem_Free(shadowelements);
+               shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
+       }
+}
+
+void R_Shadow_Volume(int numverts, int numtris, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
+{
+       int tris;
+       if (projectdistance < 0.1)
+       {
+               Con_Printf("R_Shadow_Volume: projectdistance %f\n");
+               return;
+       }
+// terminology:
+//
+// frontface:
+// a triangle facing the light source
+//
+// backface:
+// a triangle not facing the light source
+//
+// shadow volume:
+// an extrusion of the frontfaces, beginning at the original geometry and
+// ending further from the light source than the original geometry
+// (presumably at least as far as the light's radius, if the light has a
+// radius at all), capped at both front and back to avoid any problems
+//
+// description:
+// draws the shadow volumes of the model.
+// requirements:
+// vertex locations must already be in varray_vertex before use.
+// varray_vertex must have capacity for numverts * 2.
+
+       // make sure trianglefacinglight is big enough for this volume
+       if (maxtrianglefacinglight < numtris)
+               R_Shadow_ResizeTriangleFacingLight(numtris);
+
+       // make sure shadowelements is big enough for this volume
+       if (maxshadowelements < numtris * 24)
+               R_Shadow_ResizeShadowElements(numtris);
+
+       // generate projected vertices
+       // by clever use of elements we'll construct the whole shadow from
+       // the unprojected vertices and these projected vertices
+       R_Shadow_ProjectVertices(varray_vertex, varray_vertex + numverts * 4, numverts, relativelightorigin, projectdistance);
+
+       // check which triangles are facing the light
+       R_Shadow_MakeTriangleShadowFlags(elements, varray_vertex, numtris, trianglefacinglight, relativelightorigin, lightradius);
+
+       // output triangle elements
+       tris = R_Shadow_BuildShadowVolumeTriangles(elements, neighbors, numtris, numverts, trianglefacinglight, shadowelements);
        R_Shadow_RenderVolume(numverts * 2, tris, shadowelements);
 }
 
@@ -338,7 +313,6 @@ void R_Shadow_RenderVolume(int numverts, int numtris, int *elements)
 {
        if (!numverts || !numtris)
                return;
-       // draw the volume
        if (r_shadowstage == SHADOWSTAGE_STENCIL)
        {
                // increment stencil if backface is behind depthbuffer
@@ -348,20 +322,44 @@ void R_Shadow_RenderVolume(int numverts, int numtris, int *elements)
                // decrement stencil if frontface is behind depthbuffer
                qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
                qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
-               R_Mesh_Draw(numverts, numtris, elements);
        }
-       else
-               R_Mesh_Draw(numverts, numtris, elements);
+       R_Mesh_Draw(numverts, numtris, elements);
+}
+
+void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
+{
+       shadowmesh_t *mesh;
+       if (r_shadowstage == SHADOWSTAGE_STENCIL)
+       {
+               // increment stencil if backface is behind depthbuffer
+               qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+               for (mesh = firstmesh;mesh;mesh = mesh->next)
+               {
+                       R_Mesh_ResizeCheck(mesh->numverts);
+                       memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
+                       R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
+               }
+               // decrement stencil if frontface is behind depthbuffer
+               qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+       }
+       for (mesh = firstmesh;mesh;mesh = mesh->next)
+       {
+               R_Mesh_ResizeCheck(mesh->numverts);
+               memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
+               R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
+       }
 }
 
-float r_shadow_atten1, r_shadow_atten2, r_shadow_atten5;
+float r_shadow_atten1;
 #define ATTEN3DSIZE 64
 static void R_Shadow_Make3DTextures(void)
 {
        int x, y, z;
        float v[3], intensity, ilen, bordercolor[4];
        qbyte *data;
-       if (r_light_quality.integer != 1 || !gl_texture3d)
+       if (r_shadow_texture3d.integer != 1 || !gl_texture3d)
                return;
        data = Mem_Alloc(tempmempool, ATTEN3DSIZE * ATTEN3DSIZE * ATTEN3DSIZE * 4);
        for (z = 0;z < ATTEN3DSIZE;z++)
@@ -401,20 +399,22 @@ static void R_Shadow_MakeTextures(void)
        data = Mem_Alloc(tempmempool, 6*128*128*4);
        R_FreeTexturePool(&r_shadow_texturepool);
        r_shadow_texturepool = R_AllocTexturePool();
-       r_shadow_atten1 = r_shadow1.value;
-       r_shadow_atten2 = r_shadow2.value;
-       r_shadow_atten5 = r_shadow5.value;
-       for (y = 0;y < 128;y++)
-       {
-               for (x = 0;x < 128;x++)
-               {
-                       data[((0*128+y)*128+x)*4+0] = 128;
-                       data[((0*128+y)*128+x)*4+1] = 128;
-                       data[((0*128+y)*128+x)*4+2] = 255;
-                       data[((0*128+y)*128+x)*4+3] = 255;
-               }
-       }
-       r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 128, 128, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+       r_shadow_atten1 = r_shadow_lightattenuationscale.value;
+       data[0] = 128;
+       data[1] = 128;
+       data[2] = 255;
+       data[3] = 255;
+       r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+       data[0] = 255;
+       data[1] = 255;
+       data[2] = 255;
+       data[3] = 255;
+       r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+       data[0] = 255;
+       data[1] = 255;
+       data[2] = 255;
+       data[3] = 255;
+       r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
        for (side = 0;side < 6;side++)
        {
                for (y = 0;y < 128;y++)
@@ -492,18 +492,21 @@ void R_Shadow_Stage_Begin(void)
 {
        rmeshstate_t m;
 
-       if (r_light_quality.integer == 1 && !gl_texture3d)
+       if (r_shadow_texture3d.integer == 1 && !gl_texture3d)
        {
                Con_Printf("3D texture support not detected, falling back on slower 2D + 1D + normalization lighting\n");
-               Cvar_SetValueQuick(&r_light_quality, 0);
+               Cvar_SetValueQuick(&r_shadow_texture3d, 0);
        }
        //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
        if (!r_shadow_attenuation2dtexture
-        || (r_light_quality.integer == 1 && !r_shadow_normalsattenuationtexture)
-        || r_shadow1.value != r_shadow_atten1
-        || r_shadow2.value != r_shadow_atten2
-        || r_shadow5.value != r_shadow_atten5)
+        || (r_shadow_texture3d.integer == 1 && !r_shadow_normalsattenuationtexture)
+        || r_shadow_lightattenuationscale.value != r_shadow_atten1)
                R_Shadow_MakeTextures();
+       if (r_shadow_reloadlights && cl.worldmodel)
+       {
+               r_shadow_reloadlights = false;
+               R_Shadow_LoadWorldLights(cl.worldmodel->name);
+       }
 
        memset(&m, 0, sizeof(m));
        m.blendfunc1 = GL_ONE;
@@ -523,8 +526,6 @@ void R_Shadow_Stage_ShadowVolumes(void)
        qglDisable(GL_BLEND);
        qglDepthMask(0);
        qglDepthFunc(GL_LESS);
-       qglClearStencil(0);
-       qglClear(GL_STENCIL_BUFFER_BIT);
        qglEnable(GL_STENCIL_TEST);
        qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        qglStencilFunc(GL_ALWAYS, 0, 0xFF);
@@ -556,28 +557,36 @@ void R_Shadow_Stage_Light(void)
        r_shadowstage = SHADOWSTAGE_LIGHT;
 }
 
-void R_Shadow_Stage_EraseShadowVolumes(void)
+int R_Shadow_Stage_EraseShadowVolumes(void)
 {
-       rmeshstate_t m;
-       memset(&m, 0, sizeof(m));
-       R_Mesh_TextureState(&m);
-       GL_Color(1, 1, 1, 1);
-       qglColorMask(0, 0, 0, 0);
-       qglDisable(GL_BLEND);
-       qglDepthMask(0);
-       qglDepthFunc(GL_LESS);
-       qglClearStencil(0);
-       qglClear(GL_STENCIL_BUFFER_BIT);
-       qglEnable(GL_STENCIL_TEST);
-       qglStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
-       qglStencilFunc(GL_NOTEQUAL, 0, 0xFF);
-       qglDisable(GL_CULL_FACE);
-       qglDisable(GL_DEPTH_TEST);
-       r_shadowstage = SHADOWSTAGE_ERASESTENCIL;
+       if (r_shadow_erasebydrawing.integer)
+       {
+               rmeshstate_t m;
+               memset(&m, 0, sizeof(m));
+               R_Mesh_TextureState(&m);
+               GL_Color(1, 1, 1, 1);
+               qglColorMask(0, 0, 0, 0);
+               qglDisable(GL_BLEND);
+               qglDepthMask(0);
+               qglDepthFunc(GL_LESS);
+               qglEnable(GL_STENCIL_TEST);
+               qglStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
+               qglStencilFunc(GL_ALWAYS, 0, 0xFF);
+               qglDisable(GL_CULL_FACE);
+               qglDisable(GL_DEPTH_TEST);
+               r_shadowstage = SHADOWSTAGE_ERASESTENCIL;
+               return true;
+       }
+       else
+       {
+               qglClear(GL_STENCIL_BUFFER_BIT);
+               return false;
+       }
 }
 
 void R_Shadow_Stage_End(void)
 {
+       rmeshstate_t m;
        // attempt to restore state to what Mesh_State thinks it is
        qglDisable(GL_BLEND);
        qglBlendFunc(GL_ONE, GL_ZERO);
@@ -591,6 +600,14 @@ void R_Shadow_Stage_End(void)
        qglStencilFunc(GL_ALWAYS, 0, 0xFF);
        qglEnable(GL_CULL_FACE);
        qglEnable(GL_DEPTH_TEST);
+       // force mesh state to reset by using various combinations of features
+       memset(&m, 0, sizeof(m));
+       m.blendfunc1 = GL_SRC_ALPHA;
+       m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
+       R_Mesh_State(&m);
+       m.blendfunc1 = GL_ONE;
+       m.blendfunc2 = GL_ZERO;
+       R_Mesh_State(&m);
        r_shadowstage = SHADOWSTAGE_NONE;
 }
 
@@ -687,149 +704,863 @@ void R_Shadow_GenTexCoords_LightCubeMap(float *out, int numverts, const float *v
                VectorSubtract(vertex, relativelightorigin, out);
 }
 
-void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
+void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
 {
-       int mult;
+       int renders, mult;
        float scale, colorscale;
        rmeshstate_t m;
        memset(&m, 0, sizeof(m));
        if (!bumptexture)
                bumptexture = r_shadow_blankbumptexture;
        // colorscale accounts for how much we multiply the brightness during combine
-       if (r_light_quality.integer == 1)
-       {
-               if (r_textureunits.integer >= 4)
-                       colorscale = r_colorscale * 0.125f / r_shadow3.value;
-               else
-                       colorscale = r_colorscale * 0.5f / r_shadow3.value;
-       }
-       else
-               colorscale = r_colorscale * 0.5f / r_shadow3.value;
+       // mult is how many times the final pass of the lighting will be
+       // performed to get more brightness than otherwise possible
        // limit mult to 64 for sanity sake
-       for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
-       colorscale *= scale;
-       for (;mult > 0;mult--)
+       if (r_shadow_texture3d.integer)
        {
-               if (r_light_quality.integer == 1)
+               if (r_textureunits.integer >= 4 && !lightcubemap)
                {
-                       if (r_textureunits.integer >= 4)
-                       {
-                               // 4 texture 3D path, two pass
-                               m.tex[0] = R_GetTexture(bumptexture);
-                               m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
-                               m.tex[2] = R_GetTexture(basetexture);
-                               m.texcubemap[3] = R_GetTexture(lightcubemap);
-                               m.tex[3] = R_GetTexture(r_notexture);
-                               m.texcombinergb[0] = GL_REPLACE;
-                               m.texcombinergb[1] = GL_DOT3_RGB_ARB;
-                               m.texcombinergb[2] = GL_MODULATE;
-                               m.texcombinergb[3] = GL_MODULATE;
-                               m.texrgbscale[1] = 2;
-                               m.texrgbscale[3] = 4;
-                               R_Mesh_TextureState(&m);
-                               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
-                               memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
-                               memcpy(varray_texcoord[2], texcoords, numverts * sizeof(float[4]));
-                               if (lightcubemap)
-                                       R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[3], numverts, varray_vertex, relativelightorigin);
-                               else
-                               {
-                                       qglActiveTexture(GL_TEXTURE3_ARB);
-                                       qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
-                               }
-                               R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
-                               if (!lightcubemap)
-                               {
-                                       qglActiveTexture(GL_TEXTURE3_ARB);
-                                       qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
-                               }
-                               if (r_light_gloss.integer && glosstexture)
-                               {
-                                       m.tex[2] = R_GetTexture(glosstexture);
-                                       R_Mesh_TextureState(&m);
-                                       R_Shadow_GenTexCoords_Specular_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin, lightradius);
-                                       if (!lightcubemap)
-                                       {
-                                               qglActiveTexture(GL_TEXTURE3_ARB);
-                                               qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
-                                       }
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
-                                       if (!lightcubemap)
-                                       {
-                                               qglActiveTexture(GL_TEXTURE3_ARB);
-                                               qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               // 2 texture 3D path, four pass
-                               m.tex[0] = R_GetTexture(bumptexture);
-                               m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
-                               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                               m.texalphascale[1] = 2;
-                               R_Mesh_TextureState(&m);
-                               qglColorMask(0,0,0,1);
-                               qglDisable(GL_BLEND);
-                               GL_Color(1,1,1,1);
-                               memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
-                               R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
-
-                               m.tex[0] = R_GetTexture(basetexture);
-                               m.tex3d[1] = 0;
-                               m.texcubemap[1] = R_GetTexture(lightcubemap);
-                               m.texcombinergb[1] = GL_MODULATE;
-                               m.texrgbscale[1] = 1;
-                               m.texalphascale[1] = 1;
-                               R_Mesh_TextureState(&m);
-                               qglColorMask(1,1,1,1);
-                               qglBlendFunc(GL_DST_ALPHA, GL_ONE);
-                               qglEnable(GL_BLEND);
-                               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
-                               if (lightcubemap)
-                                       R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+                       // 4 texture 3D combine path, one pass, no light cubemap support
+                       m.tex[0] = R_GetTexture(bumptexture);
+                       m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
+                       m.tex[2] = R_GetTexture(basetexture);
+                       m.tex[3] = R_GetTexture(r_shadow_blankwhitetexture);
+                       m.texcombinergb[0] = GL_REPLACE;
+                       m.texcombinergb[1] = GL_DOT3_RGB_ARB;
+                       m.texcombinergb[2] = GL_MODULATE;
+                       m.texcombinergb[3] = GL_MODULATE;
+                       m.texrgbscale[1] = 1;
+                       m.texrgbscale[3] = 4;
+                       R_Mesh_TextureState(&m);
+                       memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+                       memcpy(varray_texcoord[2], texcoords, numverts * sizeof(float[4]));
+                       R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+                       qglActiveTexture(GL_TEXTURE3_ARB);
+                       qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+                       colorscale = r_colorscale * 0.25f * r_shadow_lightintensityscale.value;
+                       for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+                       colorscale *= scale;
+                       GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+                       for (renders = 0;renders < mult;renders++)
                                R_Mesh_Draw(numverts, numtriangles, elements);
-                       }
+                       qglActiveTexture(GL_TEXTURE3_ARB);
+                       qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
                }
                else
                {
-                       // 2 texture no3D path, six pass
-                       m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-                       m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+                       // 2 texture no3D combine path, two pass
+                       m.tex[0] = R_GetTexture(bumptexture);
+                       m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
+                       m.texcombinergb[0] = GL_REPLACE;
+                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+                       m.texalphascale[1] = 1;
                        R_Mesh_TextureState(&m);
                        qglColorMask(0,0,0,1);
                        qglDisable(GL_BLEND);
                        GL_Color(1,1,1,1);
-                       R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
-                       R_Mesh_Draw(numverts, numtriangles, elements);
-
-                       m.tex[0] = R_GetTexture(bumptexture);
-                       m.tex[1] = 0;
-                       m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
-                       m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                       m.texalphascale[1] = 2;
-                       R_Mesh_TextureState(&m);
-                       qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
-                       qglEnable(GL_BLEND);
                        memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
-                       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
+                       R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
                        R_Mesh_Draw(numverts, numtriangles, elements);
 
                        m.tex[0] = R_GetTexture(basetexture);
+                       m.tex3d[1] = 0;
                        m.texcubemap[1] = R_GetTexture(lightcubemap);
+                       m.texcombinergb[0] = GL_MODULATE;
                        m.texcombinergb[1] = GL_MODULATE;
                        m.texrgbscale[1] = 1;
                        m.texalphascale[1] = 1;
                        R_Mesh_TextureState(&m);
                        qglColorMask(1,1,1,1);
                        qglBlendFunc(GL_DST_ALPHA, GL_ONE);
-                       GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+                       qglEnable(GL_BLEND);
                        if (lightcubemap)
                                R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+                       colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
+                       for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+                       colorscale *= scale;
+                       GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+                       for (renders = 0;renders < mult;renders++)
+                               R_Mesh_Draw(numverts, numtriangles, elements);
+               }
+       }
+       else if (r_textureunits.integer >= 4)
+       {
+               // 4 texture no3D combine path, two pass
+               m.tex[0] = R_GetTexture(bumptexture);
+               m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
+               m.texcombinergb[0] = GL_REPLACE;
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
+               R_Mesh_TextureState(&m);
+               qglColorMask(0,0,0,1);
+               qglDisable(GL_BLEND);
+               GL_Color(1,1,1,1);
+               memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
+               R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[2], varray_texcoord[3], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+               R_Mesh_Draw(numverts, numtriangles, elements);
+
+               m.tex[0] = R_GetTexture(basetexture);
+               m.texcubemap[1] = R_GetTexture(lightcubemap);
+               m.texcombinergb[0] = GL_MODULATE;
+               m.texcombinergb[1] = GL_MODULATE;
+               m.tex[2] = 0;
+               m.tex[3] = 0;
+               R_Mesh_TextureState(&m);
+               qglColorMask(1,1,1,1);
+               qglBlendFunc(GL_DST_ALPHA, GL_ONE);
+               qglEnable(GL_BLEND);
+               if (lightcubemap)
+                       R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+               colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
+               for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+               colorscale *= scale;
+               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+               for (renders = 0;renders < mult;renders++)
+                       R_Mesh_Draw(numverts, numtriangles, elements);
+       }
+       else
+       {
+               // 2 texture no3D combine path, three pass
+               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               R_Mesh_TextureState(&m);
+               qglColorMask(0,0,0,1);
+               qglDisable(GL_BLEND);
+               GL_Color(1,1,1,1);
+               R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+               R_Mesh_Draw(numverts, numtriangles, elements);
+
+               m.tex[0] = R_GetTexture(bumptexture);
+               m.tex[1] = 0;
+               m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               R_Mesh_TextureState(&m);
+               qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
+               qglEnable(GL_BLEND);
+               memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
+               R_Mesh_Draw(numverts, numtriangles, elements);
+
+               m.tex[0] = R_GetTexture(basetexture);
+               m.texcubemap[1] = R_GetTexture(lightcubemap);
+               m.texcombinergb[1] = GL_MODULATE;
+               R_Mesh_TextureState(&m);
+               qglColorMask(1,1,1,1);
+               qglBlendFunc(GL_DST_ALPHA, GL_ONE);
+               if (lightcubemap)
+                       R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+               colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
+               for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+               colorscale *= scale;
+               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+               for (renders = 0;renders < mult;renders++)
+                       R_Mesh_Draw(numverts, numtriangles, elements);
+       }
+}
+
+void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
+{
+       int renders, mult;
+       float scale, colorscale;
+       rmeshstate_t m;
+       memset(&m, 0, sizeof(m));
+       if (!bumptexture)
+               bumptexture = r_shadow_blankbumptexture;
+       if (!glosstexture)
+               glosstexture = r_shadow_blankglosstexture;
+       if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
+       {
+               // 2 texture no3D combine path, five pass
+               memset(&m, 0, sizeof(m));
+
+               m.tex[0] = R_GetTexture(bumptexture);
+               m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               R_Mesh_TextureState(&m);
+               qglColorMask(0,0,0,1);
+               qglDisable(GL_BLEND);
+               GL_Color(1,1,1,1);
+               memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+               R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
+               R_Mesh_Draw(numverts, numtriangles, elements);
+
+               m.tex[0] = 0;
+               m.texcubemap[1] = 0;
+               m.texcombinergb[1] = GL_MODULATE;
+               R_Mesh_TextureState(&m);
+               // square alpha in framebuffer a few times to make it shiny
+               qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
+               qglEnable(GL_BLEND);
+               // these comments are a test run through this math for intensity 0.5
+               // 0.5 * 0.5 = 0.25
+               R_Mesh_Draw(numverts, numtriangles, elements);
+               // 0.25 * 0.25 = 0.0625
+               R_Mesh_Draw(numverts, numtriangles, elements);
+               // 0.0625 * 0.0625 = 0.00390625
+               R_Mesh_Draw(numverts, numtriangles, elements);
+
+               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               R_Mesh_TextureState(&m);
+               qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
+               R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+               R_Mesh_Draw(numverts, numtriangles, elements);
+
+               m.tex[0] = R_GetTexture(glosstexture);
+               m.texcubemap[1] = R_GetTexture(lightcubemap);
+               R_Mesh_TextureState(&m);
+               qglColorMask(1,1,1,1);
+               qglBlendFunc(GL_DST_ALPHA, GL_ONE);
+               memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+               if (lightcubemap)
+                       R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+               // the 0.25f makes specular lighting much dimmer than diffuse (intentionally)
+               colorscale = r_colorscale * 0.25f * r_shadow_lightintensityscale.value;
+               for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+               colorscale *= scale;
+               GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+               for (renders = 0;renders < mult;renders++)
                        R_Mesh_Draw(numverts, numtriangles, elements);
+       }
+}
+
+#define PRECOMPUTEDSHADOWVOLUMES 1
+void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
+{
+#if PRECOMPUTEDSHADOWVOLUMES
+       R_Mesh_Matrix(matrix);
+       R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
+#else
+       shadowmesh_t *mesh;
+       R_Mesh_Matrix(matrix);
+       for (mesh = light->shadowvolume;mesh;mesh = mesh->next)
+       {
+               R_Mesh_ResizeCheck(mesh->numverts * 2);
+               memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
+               R_Shadow_Volume(mesh->numverts, mesh->numtriangles, varray_vertex, mesh->elements, mesh->neighbors, light->origin, light->lightradius, light->lightradius);
+       }
+#endif
+}
+
+cvar_t r_editlights = {0, "r_editlights", "0"};
+cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
+cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
+cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
+cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
+worldlight_t *r_shadow_worldlightchain;
+worldlight_t *r_shadow_selectedlight;
+vec3_t r_editlights_cursorlocation;
+
+static int castshadowcount = 1;
+void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname)
+{
+       int i, j, k, l, maxverts, *mark;
+       float *verts, *v, *v0, *v1, f, projectdistance, temp[3], temp2[3], temp3[3], radius2;
+       worldlight_t *e;
+       shadowmesh_t *mesh;
+       mleaf_t *leaf;
+       msurface_t *surf;
+       qbyte *pvs;
+
+       e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
+       VectorCopy(origin, e->origin);
+       VectorCopy(color, e->light);
+       e->lightradius = radius;
+       VectorCopy(origin, e->mins);
+       VectorCopy(origin, e->maxs);
+       e->cullradius = 0;
+       e->style = style;
+       e->next = r_shadow_worldlightchain;
+       r_shadow_worldlightchain = e;
+       if (cubemapname)
+       {
+               e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
+               strcpy(e->cubemapname, cubemapname);
+               // FIXME: add cubemap loading (and don't load a cubemap twice)
+       }
+       if (cl.worldmodel)
+       {
+               castshadowcount++;
+               leaf = Mod_PointInLeaf(origin, cl.worldmodel);
+               pvs = Mod_LeafPVS(leaf, cl.worldmodel);
+               for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+               {
+                       if (pvs[i >> 3] & (1 << (i & 7)))
+                       {
+                               VectorCopy(origin, temp);
+                               if (temp[0] < leaf->mins[0]) temp[0] = leaf->mins[0];
+                               if (temp[0] > leaf->maxs[0]) temp[0] = leaf->maxs[0];
+                               if (temp[1] < leaf->mins[1]) temp[1] = leaf->mins[1];
+                               if (temp[1] > leaf->maxs[1]) temp[1] = leaf->maxs[1];
+                               if (temp[2] < leaf->mins[2]) temp[2] = leaf->mins[2];
+                               if (temp[2] > leaf->maxs[2]) temp[2] = leaf->maxs[2];
+                               VectorSubtract(temp, origin, temp);
+                               if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
+                               {
+                                       leaf->worldnodeframe = castshadowcount;
+                                       for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
+                                       {
+                                               surf = cl.worldmodel->surfaces + *mark;
+                                               if (surf->castshadow != castshadowcount)
+                                               {
+                                                       f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
+                                                       if (surf->flags & SURF_PLANEBACK)
+                                                               f = -f;
+                                                       if (f > 0 && f < e->lightradius)
+                                                       {
+                                                               VectorSubtract(e->origin, surf->poly_center, temp);
+                                                               if (DotProduct(temp, temp) - surf->poly_radius2 < e->lightradius * e->lightradius)
+                                                                       surf->castshadow = castshadowcount;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               e->numleafs = 0;
+               for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+                       if (leaf->worldnodeframe == castshadowcount)
+                               e->numleafs++;
+               e->numsurfaces = 0;
+               for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
+                       if (surf->castshadow == castshadowcount)
+                               e->numsurfaces++;
+
+               if (e->numleafs)
+                       e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
+               if (e->numsurfaces)
+                       e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
+               e->numleafs = 0;
+               for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+                       if (leaf->worldnodeframe == castshadowcount)
+                               e->leafs[e->numleafs++] = leaf;
+               e->numsurfaces = 0;
+               for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
+                       if (surf->castshadow == castshadowcount)
+                               e->surfaces[e->numsurfaces++] = surf;
+               // find bounding box and sphere of lit surfaces
+               // (these will be used for creating a shape to clip the light)
+               radius2 = 0;
+               VectorCopy(e->origin, e->mins);
+               VectorCopy(e->origin, e->maxs);
+               for (j = 0;j < e->numsurfaces;j++)
+               {
+                       surf = e->surfaces[j];
+                       for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
+                       {
+                               if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
+                               if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
+                               if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
+                               VectorSubtract(v, e->origin, temp);
+                               f = DotProduct(temp, temp);
+                               if (radius2 < f)
+                                       radius2 = f;
+                       }
+               }
+               e->cullradius = sqrt(radius2);
+               if (e->cullradius > e->lightradius)
+                       e->cullradius = e->lightradius;
+               if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
+               if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
+               if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
+               if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
+               if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
+               if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
+               Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
+               // clip shadow volumes against eachother to remove unnecessary
+               // polygons (and sections of polygons)
+               maxverts = 256;
+               verts = NULL;
+               castshadowcount++;
+               for (j = 0;j < e->numsurfaces;j++)
+               {
+                       surf = e->surfaces[j];
+                       if (surf->flags & SURF_SHADOWCAST)
+                       {
+                               surf->castshadow = castshadowcount;
+                               if (maxverts < surf->poly_numverts)
+                                       maxverts = surf->poly_numverts;
+                       }
+               }
+               e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 32768);
+#if !PRECOMPUTEDSHADOWVOLUMES
+               // make a mesh to cast a shadow volume from
+               for (j = 0;j < e->numsurfaces;j++)
+                       if (e->surfaces[j]->castshadow == castshadowcount)
+                               Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, e->surfaces[j]->poly_numverts, e->surfaces[j]->poly_verts);
+#else
+#if 1
+               {
+               int tris;
+               shadowmesh_t *castmesh, *mesh;
+               surfmesh_t *surfmesh;
+               // make a mesh to cast a shadow volume from
+               castmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, 32768);
+               for (j = 0;j < e->numsurfaces;j++)
+                       if (e->surfaces[j]->castshadow == castshadowcount)
+                               for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
+                                       Mod_ShadowMesh_AddMesh(loadmodel->mempool, castmesh, surfmesh->numverts, surfmesh->verts, surfmesh->numtriangles, surfmesh->index);
+               castmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, castmesh);
+
+               // cast shadow volume from castmesh
+               for (mesh = castmesh;mesh;mesh = mesh->next)
+               {
+                       R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
+                       R_Shadow_ResizeShadowElements(castmesh->numtriangles);
+
+                       if (maxverts < castmesh->numverts * 2)
+                       {
+                               maxverts = castmesh->numverts * 2;
+                               if (verts)
+                                       Mem_Free(verts);
+                               verts = NULL;
+                       }
+                       if (verts == NULL && maxverts > 0)
+                               verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[4]));
+
+                       // now that we have the buffers big enough, construct shadow volume mesh
+                       memcpy(verts, castmesh->verts, castmesh->numverts * sizeof(float[4]));
+                       R_Shadow_ProjectVertices(verts, verts + castmesh->numverts * 4, castmesh->numverts, e->origin, e->lightradius);
+                       R_Shadow_MakeTriangleShadowFlags(castmesh->elements, verts, castmesh->numtriangles, trianglefacinglight, e->origin, e->lightradius);
+                       tris = R_Shadow_BuildShadowVolumeTriangles(castmesh->elements, castmesh->neighbors, castmesh->numtriangles, castmesh->numverts, trianglefacinglight, shadowelements);
+                       // add the constructed shadow volume mesh
+                       Mod_ShadowMesh_AddMesh(loadmodel->mempool, e->shadowvolume, castmesh->numverts, verts, tris, shadowelements);
                }
+               // we're done with castmesh now
+               Mod_ShadowMesh_Free(castmesh);
+               }
+#else
+               // make a shadow volume mesh
+               if (verts == NULL && maxverts > 0)
+                       verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[4]));
+               for (j = 0;j < e->numsurfaces;j++)
+               {
+                       surf = e->surfaces[j];
+                       if (surf->castshadow != castshadowcount)
+                               continue;
+                       projectdistance = 1000000.0f;//e->lightradius;
+                       // copy the original polygon, for the front cap of the volume
+                       for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
+                               VectorCopy(v0, v1);
+                       Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
+                       // project the original polygon, reversed, for the back cap of the volume
+                       for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
+                       {
+                               VectorSubtract(v0, e->origin, temp);
+                               //VectorNormalize(temp);
+                               VectorMA(v0, projectdistance, temp, v1);
+                       }
+                       Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
+                       // project the shadow volume sides
+                       for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
+                       {
+                               if (surf->neighborsurfaces == NULL || surf->neighborsurfaces[l] == NULL || surf->neighborsurfaces[l]->castshadow != castshadowcount)
+                               {
+                                       VectorCopy(v1, &verts[0]);
+                                       VectorCopy(v0, &verts[3]);
+                                       VectorCopy(v0, &verts[6]);
+                                       VectorCopy(v1, &verts[9]);
+                                       VectorSubtract(&verts[6], e->origin, temp);
+                                       //VectorNormalize(temp);
+                                       VectorMA(&verts[6], projectdistance, temp, &verts[6]);
+                                       VectorSubtract(&verts[9], e->origin, temp);
+                                       //VectorNormalize(temp);
+                                       VectorMA(&verts[9], projectdistance, temp, &verts[9]);
+
+#if 0
+                                       VectorSubtract(&verts[0], &verts[3], temp);
+                                       VectorSubtract(&verts[6], &verts[3], temp2);
+                                       CrossProduct(temp, temp2, temp3);
+                                       VectorNormalize(temp3);
+                                       if (DotProduct(surf->poly_center, temp3) > DotProduct(&verts[0], temp3))
+                                       {
+                                               VectorCopy(v0, &verts[0]);
+                                               VectorCopy(v1, &verts[3]);
+                                               VectorCopy(v1, &verts[6]);
+                                               VectorCopy(v0, &verts[9]);
+                                               VectorSubtract(&verts[6], e->origin, temp);
+                                               //VectorNormalize(temp);
+                                               VectorMA(&verts[6], projectdistance, temp, &verts[6]);
+                                               VectorSubtract(&verts[9], e->origin, temp);
+                                               //VectorNormalize(temp);
+                                               VectorMA(&verts[9], projectdistance, temp, &verts[9]);
+                                               Con_Printf("flipped shadow volume edge %8p %i\n", surf, l);
+                                       }
+#endif
+
+                                       Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
+                               }
+                       }
+               }
+#endif
+#endif
+               e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
+               for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
+                       l += mesh->numtriangles;
+               Con_Printf("static shadow volume built containing %i triangles\n", l);
        }
 }
 
+void R_Shadow_FreeWorldLight(worldlight_t *light)
+{
+       worldlight_t **lightpointer;
+       for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
+       if (*lightpointer != light)
+               Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
+       *lightpointer = light->next;
+       if (light->cubemapname)
+               Mem_Free(light->cubemapname);
+       if (light->shadowvolume)
+               Mod_ShadowMesh_Free(light->shadowvolume);
+       if (light->surfaces)
+               Mem_Free(light->surfaces);
+       if (light->leafs)
+               Mem_Free(light->leafs);
+       Mem_Free(light);
+}
+
+void R_Shadow_ClearWorldLights(void)
+{
+       while (r_shadow_worldlightchain)
+               R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
+       r_shadow_selectedlight = NULL;
+}
+
+void R_Shadow_SelectLight(worldlight_t *light)
+{
+       if (r_shadow_selectedlight)
+               r_shadow_selectedlight->selected = false;
+       r_shadow_selectedlight = light;
+       if (r_shadow_selectedlight)
+               r_shadow_selectedlight->selected = true;
+}
+
+void R_Shadow_FreeSelectedWorldLight(void)
+{
+       if (r_shadow_selectedlight)
+       {
+               R_Shadow_FreeWorldLight(r_shadow_selectedlight);
+               r_shadow_selectedlight = NULL;
+       }
+}
+
+void R_Shadow_SelectLightInView(void)
+{
+       float bestrating, rating, temp[3], dist;
+       worldlight_t *best, *light;
+       best = NULL;
+       bestrating = 1e30;
+       for (light = r_shadow_worldlightchain;light;light = light->next)
+       {
+               VectorSubtract(light->origin, r_refdef.vieworg, temp);
+               dist = sqrt(DotProduct(temp, temp));
+               if (DotProduct(temp, vpn) >= 0.97 * dist && bestrating > dist && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
+               {
+                       bestrating = dist;
+                       best = light;
+               }
+       }
+       R_Shadow_SelectLight(best);
+}
+
+void R_Shadow_LoadWorldLights(const char *mapname)
+{
+       int n, a, style;
+       char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
+       float origin[3], radius, color[3];
+       COM_StripExtension(mapname, name);
+       strcat(name, ".rtlights");
+       lightsstring = COM_LoadFile(name, false);
+       if (lightsstring)
+       {
+               s = lightsstring;
+               n = 0;
+               while (*s)
+               {
+                       t = s;
+                       while (*s && *s != '\n')
+                               s++;
+                       if (!*s)
+                               break;
+                       *s = 0;
+                       a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, &cubemapname);
+                       if (a < 9)
+                               cubemapname[0] = 0;
+                       *s = '\n';
+                       if (a < 8)
+                       {
+                               Con_Printf("found %d parameters on line %i, should be 8 or 9 parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style cubemapname)\n", a, n + 1);
+                               break;
+                       }
+                       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
+                       s++;
+                       n++;
+               }
+               if (*s)
+                       Con_Printf("invalid rtlights file \"%s\"\n", name);
+               Mem_Free(lightsstring);
+       }
+}
+
+void R_Shadow_SaveWorldLights(const char *mapname)
+{
+       worldlight_t *light;
+       int bufchars, bufmaxchars;
+       char *buf, *oldbuf;
+       char name[MAX_QPATH];
+       char line[1024];
+       if (!r_shadow_worldlightchain)
+               return;
+       COM_StripExtension(mapname, name);
+       strcat(name, ".rtlights");
+       bufchars = bufmaxchars = 0;
+       buf = NULL;
+       for (light = r_shadow_worldlightchain;light;light = light->next)
+       {
+               sprintf(line, "%g %g %g %g %g %g %g %d %s\n", light->origin[0], light->origin[1], light->origin[2], light->lightradius, light->light[0], light->light[1], light->light[2], light->style, light->cubemapname ? light->cubemapname : "");
+               if (bufchars + strlen(line) > bufmaxchars)
+               {
+                       bufmaxchars = bufchars + strlen(line) + 2048;
+                       oldbuf = buf;
+                       buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
+                       if (oldbuf)
+                       {
+                               if (bufchars)
+                                       memcpy(buf, oldbuf, bufchars);
+                               Mem_Free(oldbuf);
+                       }
+               }
+               if (strlen(line))
+               {
+                       memcpy(buf + bufchars, line, strlen(line));
+                       bufchars += strlen(line);
+               }
+       }
+       if (bufchars)
+               COM_WriteFile(name, buf, bufchars);
+       if (buf)
+               Mem_Free(buf);
+}
+
+void R_Shadow_SetCursorLocationForView(void)
+{
+       vec_t dist, push, frac;
+       vec3_t dest, endpos, normal;
+       VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
+       frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
+       if (frac < 1)
+       {
+               dist = frac * r_editlights_cursordistance.value;
+               push = r_editlights_cursorpushback.value;
+               if (push > dist)
+                       push = dist;
+               push = -push;
+               VectorMA(endpos, push, vpn, endpos);
+               VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
+       }
+       r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
+       r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
+       r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
+}
+
+extern void R_DrawCrosshairSprite(rtexture_t *texture, vec3_t origin, vec_t scale, float cr, float cg, float cb, float ca);
+void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
+{
+       cachepic_t *pic;
+       pic = Draw_CachePic("gfx/crosshair1.tga");
+       if (pic)
+               R_DrawCrosshairSprite(pic->tex, r_editlights_cursorlocation, r_editlights_cursorgrid.value * 0.5f, 1, 1, 1, 1);
+}
+
+void R_Shadow_DrawCursor(void)
+{
+       R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
+}
+
+void R_Shadow_UpdateLightingMode(void)
+{
+       r_shadow_lightingmode = 0;
+       if (r_shadow_realtime.integer)
+       {
+               if (r_shadow_worldlightchain)
+                       r_shadow_lightingmode = 2;
+               else
+                       r_shadow_lightingmode = 1;
+       }
+}
+
+void R_Shadow_UpdateWorldLightSelection(void)
+{
+       if (r_editlights.integer)
+       {
+               R_Shadow_SelectLightInView();
+               R_Shadow_SetCursorLocationForView();
+               R_Shadow_DrawCursor();
+       }
+       else
+               R_Shadow_SelectLight(NULL);
+}
+
+void R_Shadow_EditLights_Clear_f(void)
+{
+       R_Shadow_ClearWorldLights();
+}
+
+void R_Shadow_EditLights_Reload_f(void)
+{
+       if (cl.worldmodel)
+       {
+               R_Shadow_ClearWorldLights();
+               R_Shadow_LoadWorldLights(cl.worldmodel->name);
+       }
+}
+
+void R_Shadow_EditLights_Save_f(void)
+{
+       if (cl.worldmodel)
+               R_Shadow_SaveWorldLights(cl.worldmodel->name);
+}
+
+void R_Shadow_EditLights_Spawn_f(void)
+{
+       vec3_t origin, color;
+       vec_t radius;
+       int style;
+       const char *cubemapname;
+       if (!r_editlights.integer)
+       {
+               Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
+               return;
+       }
+       if (Cmd_Argc() <= 7)
+       {
+               radius = 200;
+               color[0] = color[1] = color[2] = 1;
+               style = 0;
+               cubemapname = NULL;
+               if (Cmd_Argc() >= 2)
+               {
+                       radius = atof(Cmd_Argv(1));
+                       if (Cmd_Argc() >= 3)
+                       {
+                               color[0] = atof(Cmd_Argv(2));
+                               color[1] = color[0];
+                               color[2] = color[0];
+                               if (Cmd_Argc() >= 5)
+                               {
+                                       color[1] = atof(Cmd_Argv(3));
+                                       color[2] = atof(Cmd_Argv(4));
+                                       if (Cmd_Argc() >= 6)
+                                       {
+                                               style = atoi(Cmd_Argv(5));
+                                               if (Cmd_Argc() >= 7)
+                                                       cubemapname = Cmd_Argv(6);
+                                       }
+                               }
+                       }
+               }
+               if (cubemapname && !cubemapname[0])
+                       cubemapname = NULL;
+               if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
+               {
+                       VectorCopy(r_editlights_cursorlocation, origin);
+                       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
+                       return;
+               }
+       }
+       Con_Printf("usage: r_editlights_spawn radius red green blue [style [cubemap]]\n");
+}
+
+void R_Shadow_EditLights_Edit_f(void)
+{
+       vec3_t origin, color;
+       vec_t radius;
+       int style;
+       const char *cubemapname;
+       if (!r_editlights.integer)
+       {
+               Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
+               return;
+       }
+       if (!r_shadow_selectedlight)
+       {
+               Con_Printf("No selected light.\n");
+               return;
+       }
+       if (Cmd_Argc() <= 7)
+       {
+               radius = 200;
+               color[0] = color[1] = color[2] = 1;
+               style = 0;
+               cubemapname = NULL;
+               if (Cmd_Argc() >= 2)
+               {
+                       radius = atof(Cmd_Argv(1));
+                       if (Cmd_Argc() >= 3)
+                       {
+                               color[0] = atof(Cmd_Argv(2));
+                               color[1] = color[0];
+                               color[2] = color[0];
+                               if (Cmd_Argc() >= 5)
+                               {
+                                       color[1] = atof(Cmd_Argv(3));
+                                       color[2] = atof(Cmd_Argv(4));
+                                       if (Cmd_Argc() >= 6)
+                                       {
+                                               style = atoi(Cmd_Argv(5));
+                                               if (Cmd_Argc() >= 7)
+                                                       cubemapname = Cmd_Argv(6);
+                                       }
+                               }
+                       }
+               }
+               if (cubemapname && !cubemapname[0])
+                       cubemapname = NULL;
+               if (radius >= 16 && color[0] >= 0 && color[1] >= 0 && color[2] >= 0 && style >= 0 && style < 256 && (color[0] >= 0.1 || color[1] >= 0.1 || color[2] >= 0.1))
+               {
+                       VectorCopy(r_shadow_selectedlight->origin, origin);
+                       R_Shadow_FreeWorldLight(r_shadow_selectedlight);
+                       r_shadow_selectedlight = NULL;
+                       R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname);
+                       return;
+               }
+       }
+       Con_Printf("usage: r_editlights_edit radius red green blue [style [cubemap]]\n");
+}
+
+void R_Shadow_EditLights_Remove_f(void)
+{
+       if (!r_editlights.integer)
+       {
+               Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
+               return;
+       }
+       if (!r_shadow_selectedlight)
+       {
+               Con_Printf("No selected light.\n");
+               return;
+       }
+       R_Shadow_FreeSelectedWorldLight();
+}
+
+void R_Shadow_EditLights_Init(void)
+{
+       Cvar_RegisterVariable(&r_editlights);
+       Cvar_RegisterVariable(&r_editlights_cursordistance);
+       Cvar_RegisterVariable(&r_editlights_cursorpushback);
+       Cvar_RegisterVariable(&r_editlights_cursorpushoff);
+       Cvar_RegisterVariable(&r_editlights_cursorgrid);
+       Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
+       Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
+       Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
+       Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
+       Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
+       Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
+}
index 01ae581..1aa8532 100644 (file)
@@ -2,21 +2,56 @@
 #ifndef R_SHADOW_H
 #define R_SHADOW_H
 
-extern cvar_t r_light_realtime;
-extern cvar_t r_light_quality;
-extern cvar_t r_light_gloss;
-extern cvar_t r_light_debuglight;
+extern cvar_t r_shadow_lightattenuationscale;
+extern cvar_t r_shadow_lightintensityscale;
+extern cvar_t r_shadow_realtime;
+extern cvar_t r_shadow_texture3d;
+extern cvar_t r_shadow_gloss;
+extern cvar_t r_shadow_debuglight;
 
 void R_Shadow_Init(void);
-void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance);
-void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap);
+void R_Shadow_Volume(int numverts, int numtris, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance);
+void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap);
+void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap);
 void R_Shadow_ClearStencil(void);
 
 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements);
+void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *mesh);
 void R_Shadow_Stage_Begin(void);
 void R_Shadow_Stage_ShadowVolumes(void);
 void R_Shadow_Stage_Light(void);
-void R_Shadow_Stage_EraseShadowVolumes(void);
+// returns true if shadow volumes should be drawn again to erase,
+// otherwise clears stencil
+int R_Shadow_Stage_EraseShadowVolumes(void);
 void R_Shadow_Stage_End(void);
 
+typedef struct worldlight_s
+{
+       vec3_t origin;
+       vec3_t light;
+       vec3_t mins;
+       vec3_t maxs;
+       vec_t lightradius;
+       vec_t cullradius;
+       struct worldlight_s *next;
+       msurface_t **surfaces;
+       int numsurfaces;
+       mleaf_t **leafs;
+       int numleafs;
+       rtexture_t *cubemap;
+       char *cubemapname;
+       int style;
+       shadowmesh_t *shadowvolume;
+       int selected;
+}
+worldlight_t;
+
+extern worldlight_t *r_shadow_worldlightchain;
+
+// 0 = normal, 1 = dynamic light shadows, 2 = world and dynamic light shadows
+extern int r_shadow_lightingmode;
+void R_Shadow_UpdateLightingMode(void);
+
+void R_Shadow_UpdateWorldLightSelection(void);
+
 #endif