tenebrae2/gl_shadow.c

2378 lines
53 KiB
C
Raw Blame History

/*
Copyright (C) 2001-2002 Charles Hollemeersch
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
PENTA: the whole file is freakin penta...
*/
// gl_shadow.c stencil shadow support for quake
#include "quakedef.h"
int numShadowLights;
int numStaticShadowLights;
int numUsedShadowLights; //number of shadow lights acutally drawn this frame
shadowlight_t shadowlights[MAXSHADOWLIGHTS];
shadowlight_t *usedshadowlights[MAXUSEDSHADOWLIGHS];
shadowlight_t *currentshadowlight;
int volumeCmdsBuff[MAX_VOLUME_COMMANDS+128]; //Hack protect against slight overflows
float volumeVertsBuff[MAX_VOLUME_VERTS+128];
lightcmd_t lightCmdsBuff[MAX_LIGHT_COMMANDS+128];
lightcmd_t lightCmdsBuffMesh[MAX_LIGHT_COMMANDS+128];
int numVolumeCmds;
int numLightCmds;
int numLightCmdsMesh;
int numVolumeVerts;
msurface_t *shadowchain; //linked list of polygons that are shadowed
mesh_t *meshshadowchain;
byte *lightvis;
byte worldvis[MAX_MAP_LEAFS/8];
/* -DC- isn't that volumeVertsBuff ?
vec3_t volumevertices[MAX_VOLUME_VERTICES];//buffer for the vertices of the shadow volume
int usedvolumevertices;
*/
void DrawVolumeFromCmds(int *volumeCmds, lightcmd_t *lightCmds, float *volumeVerts);
void DrawAttentFromCmds(lightcmd_t *lightCmds);
void DrawBumpFromCmds(lightcmd_t *lightCmds);
void DrawSpecularBumpFromCmds(lightcmd_t *lightCmds);
void PrecalcVolumesForLight(model_t *model);
int getVertexIndexFromSurf(msurface_t *surf, int index, model_t *model);
qboolean R_ContributeFrame(shadowlight_t *light);
/*
=============
AllocShadowLight
=============
*/
shadowlight_t* AllocShadowLight(void) {
if (numShadowLights >= MAXSHADOWLIGHTS) {
return NULL;
}
shadowlights[numShadowLights].owner = NULL;
numShadowLights++;
return &shadowlights[numShadowLights-1];
}
/*
=============
R_ShadowFromDlight
=============
*/
void R_ShadowFromDlight(dlight_t *light) {
shadowlight_t *l;
l = AllocShadowLight();
if (!l) return;
VectorCopy(light->origin,l->origin);
l->radius = light->radius;
l->color[0] = light->color[0];
l->color[1] = light->color[1];
l->color[2] = light->color[2];
l->baseColor[0] = light->color[0];
l->baseColor[1] = light->color[1];
l->baseColor[2] = light->color[2];
l->style = 0;
l->brightness = 1;
l->isStatic = false;
l->numVisSurf = 0;
l->visSurf = NULL;
l->style = light->style;
l->owner = light->owner;
//VectorCopy(light->angles,l->angles);
//We use some different angle convention
l->angles[1] = light->angles[0];
l->angles[0] = light->angles[2];
l->angles[2] = light->angles[1];
l->filtercube = light->filtercube;
l->rspeed = 0;
l->cubescale = 1;
l->castShadow = true;
//Some people will be instulted by the mere existence of this flag.
if (light->pflags & PFLAG_NOSHADOW) {
l->castShadow = false;
} else {
l->castShadow = true;
}
if (light->pflags & PFLAG_HALO) {
l->halo = true;
} else {
l->halo = false;
}
}
#define NUM_CUBEMAPS 64
int cubemap_tex_obj [NUM_CUBEMAPS];
/*
=============
R_CubeMapLookup
=============
*/
int R_CubeMapLookup(int i) {
if (i > NUM_CUBEMAPS) {
return 0;
} else {
if (!cubemap_tex_obj[i]) {
cubemap_tex_obj[i] = GL_LoadCubeMap(i);
}
return cubemap_tex_obj[i];
}
}
/*
=============
R_ShadowFromEntity
=============
*/
void R_ShadowFromEntity(entity_t *ent) {
shadowlight_t *l;
l = AllocShadowLight();
if (!l) return;
VectorCopy(ent->origin,l->origin);
l->radius = 350;
l->color[0] = 1;
l->color[1] = 1;
l->color[2] = 1;
l->style = 0;
l->brightness = 1;
l->isStatic = false;
l->numVisSurf = 0;
l->visSurf = NULL;
l->style = 0;
l->owner = ent;
l->style = ent->style;
if (ent->light_lev != 0) {
l->radius = ent->light_lev;
} else {
l->radius = 350;
}
VectorCopy(ent->color,l->baseColor);
if ((l->baseColor[0] == 0) && (l->baseColor[1] == 0) && (l->baseColor[2] == 0)) {
l->baseColor[0] = 1;
l->baseColor[1] = 1;
l->baseColor[2] = 1;
}
if (ent->skinnum >= 16) {
l->filtercube = R_CubeMapLookup(ent->skinnum);
} else {
l->filtercube = 0;
}
//We use some different angle convention
l->angles[1] = ent->angles[0];
l->angles[0] = ent->angles[2];
l->angles[2] = ent->angles[1];
l->rspeed = ent->alpha*512;
l->cubescale = 1;
//Some people will be instulted by the mere existence of this flag.
if (ent->pflags & PFLAG_NOSHADOW) {
l->castShadow = false;
} else {
l->castShadow = true;
}
if (ent->pflags & PFLAG_HALO) {
l->halo = true;
} else {
l->halo = false;
}
}
/*
=============
R_MarkDLights
Adds dynamic lights to the shadow light list
=============
*/
void R_MarkDlights (void)
{
int i;
dlight_t *l;
l = cl_dlights;
for (i=0 ; i<MAX_DLIGHTS ; i++, l++)
{
if (l->die < cl.time || !l->radius)
continue;
R_ShadowFromDlight(l);
}
}
/*
=============
R_MarkEntities
Adds entities that have a lightsource attched to
the shadow light list.
(used for static ents)
We just check if they have a torch/flame model or not.
=============
*/
void R_MarkEntities (void)
{
int i;
entity_t *current;
for (i=0 ; i<cl.num_statics; i++)
{
current = &cl_static_entities[i];
if (!strcmp (current->model->name, "progs/flame2.mdl")
|| !strcmp (current->model->name, "progs/flame.mdl") )
{
R_ShadowFromEntity(current);
}
}
}
int cut_ent;
/*
=============
R_InitShadowsForFrame
Do per frame intitialization for the shadows
=============
*/
void R_InitShadowsForFrame(void) {
byte *vis;
int i;
numShadowLights = numStaticShadowLights;
R_MarkDlights (); //add dynamic lights to the list
// R_ShadowFromPlayer();//give the player some sort of torch
numUsedShadowLights = 0;
//if (cut_ent) Con_Printf("cut ents: %i\n",cut_ent);
cut_ent = 0;
Q_memset (&worldvis, 0, MAX_MAP_LEAFS/8); //all invisible
vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel);
Q_memcpy(&worldvis, vis, MAX_MAP_LEAFS/8);
for (i=0; i<numShadowLights; i++) {
currentshadowlight = &shadowlights[i];
if (R_ContributeFrame(currentshadowlight)) {
currentshadowlight->visible = true;
if (numUsedShadowLights < MAXUSEDSHADOWLIGHS) {
usedshadowlights[numUsedShadowLights] = currentshadowlight;
numUsedShadowLights++;
} else {
Con_Printf("R_InitShadowsForFrame: More than MAXUSEDSHADOWLIGHS lights for frame\n");
}
} else {
currentshadowlight->visible = false;
}
}
}
qboolean R_MarkShadowSurf(msurface_t *surf, shadowlight_t *light)
{
mplane_t *plane;
float dist;
glpoly_t *poly;
//we don't cast shadows with water
if (( surf->flags & SURF_DRAWTURB ) || ( surf->flags & SURF_DRAWSKY )) {
return false;
}
if (!( surf->flags & SURF_PPLIGHT)) {
return false;
}
plane = surf->plane;
poly = surf->polys;
if (poly->lightTimestamp == r_lightTimestamp) {
return false;
}
switch (plane->type)
{
case PLANE_X:
dist = light->origin[0] - plane->dist;
break;
case PLANE_Y:
dist = light->origin[1] - plane->dist;
break;
case PLANE_Z:
dist = light->origin[2] - plane->dist;
break;
default:
dist = DotProduct (light->origin, plane->normal) - plane->dist;
break;
}
//the normals are flipped when surf_planeback is 1
if (((surf->flags & SURF_PLANEBACK) && (dist > 0)) ||
(!(surf->flags & SURF_PLANEBACK) && (dist < 0)))
{
return false;
}
//the normals are flipped when surf_planeback is 1
if ( abs(dist) > light->radius)
{
return false;
}
poly->lightTimestamp = r_lightTimestamp;
return true;
}
#define MAX_LEAF_LIST 32
mleaf_t *leafList[MAX_LEAF_LIST];
int numLeafList;
extern vec3_t r_emins, r_emaxs; // <AWE> added "extern".
/*
=============
InShadowEntity
Some efrag based sceme may cut even more ents!
=============
*/
qboolean InShadowEntity(entity_t *ent) {
int i, leafindex;
model_t *entmodel = ent->model;
mleaf_t *leaf;
vec3_t dst;
float radius, d;
radius = entmodel->radius;
VectorSubtract (currentshadowlight->origin, ent->origin, dst);
d = Length (dst);
if (d < (currentshadowlight->radius + radius)) {
if (sh_noefrags.value) return true;
for (i=0; i<ent->numleafs;i++) {
leafindex = ent->leafnums[i];
//leaf ent is in is visible from light
leaf = cl.worldmodel->leafs+leafindex;
if (currentshadowlight->entvis[leaf->cluster>>3] & (1<<(leaf->cluster&7)))
{
return true;
}
}
if (ent->numleafs == 0) {
//Con_Printf("Ent with no leafs");
return true;
}
return false;
}
return false;
}
/*
=============
R_VisibleEntity
Adds shadow casting ents to the list
=============
*/
void MarkShadowEntities() {
int i;
vec3_t mins, maxs;
if (!cg_showentities.value)
return;
cl_numlightvisedicts = 0;
for (i=0 ; i<cl_numvisedicts ; i++)
{
currententity = cl_visedicts[i];
if ((currententity->model->flags & EF_NOSHADOW) && (currententity->model->flags & EF_FULLBRIGHT)) {
continue;
}
if (mirror) {
VectorAdd (currententity->origin,currententity->model->mins, mins);
VectorAdd (currententity->origin,currententity->model->maxs, maxs);
if (mirror_clipside == BoxOnPlaneSide(mins, maxs, mirror_plane)) {
continue;
}
if ( BoxOnPlaneSide(mins, maxs, &mirror_far_plane) == 1) {
return;
}
}
//Dont cast shadows with the ent this light is attached to because
//when the light is partially in the model shadows will look weird.
//FIXME: Model is not lit by its own light.
if (currententity != currentshadowlight->owner)
{
if (InShadowEntity(currententity)) {
currententity->lightTimestamp = r_lightTimestamp;
cl_lightvisedicts[cl_numlightvisedicts] = currententity;
cl_numlightvisedicts++;
} else cut_ent++;
}
}
}
/*
=============
R_ProjectSphere
Returns the rectangle the sphere will be in when it is drawn.
FIXME: This is crappy code we draw a "sprite" and project those points
it should be possible to analytically derive a eq.
=============
*/
void R_ProjectSphere (shadowlight_t *light, int *rect)
{
int i, j;
float a;
vec3_t v, vp2;
float rad;
double minx, maxx, miny, maxy;
double px, py, pz;
rad = light->radius;
/*
rect[0] = 100;
rect[1] = 100;
rect[2] = 300;
rect[3] = 300;
return;
*/
VectorSubtract (light->origin, r_origin, v);
//dave - slight fix
VectorSubtract (light->origin, r_origin, vp2);
VectorNormalize(vp2);
minx = 1000000;
miny = 1000000;
maxx = -1000000;
maxy = -1000000;
for (i=16 ; i>=0 ; i--)
{
a = i/16.0 * M_PI*2;
for (j=0 ; j<3 ; j++)
v[j] = light->origin[j] + vright[j]*cos(a)*rad + vup[j]*sin(a)*rad;
gluProject(v[0], v[1], v[2], r_Dworld_matrix, r_Dproject_matrix,
(GLint *) r_Iviewport, &px, &py, &pz); // <AWE> added cast.
if (px > maxx) maxx = px;
if (px < minx) minx = px;
if (py > maxy) maxy = py;
if (py < miny) miny = py;
}
rect[0] = (int)minx;
rect[1] = (int)miny;
rect[2] = (int)maxx;
rect[3] = (int)maxy;
}
/**************************************************************
World model shadow volumes
***************************************************************/
/*
=============
HasSharedLeaves
Returns true if both vis arrays have shared leafs visible.
FIXME: compare bytes at a time (what does quake fill the unused bits with??)
=============
*/
qboolean HasSharedLeafs(byte *v1, byte *v2) {
int i;
for (i=0 ; i<cl.worldmodel->numclusters; i++)
{
if (v1[i>>3] & (1<<(i&7)))
{
if (v2[i>>3] & (1<<(i&7)))
return true;
}
}
return false;
}
/*
=============
R_MarkShadowCasting
Fills the shadow chain with polygons we should consider.
Polygons that will be added are:
1. In the light volume. (sphere)
2. "Visible" to the light.
Visible is:
a. facing the light (dotprod > 0)
b. in a leaf that is visible from the light's leaf. (based on vis data)
This is crude for satic lights we use extra tricks (svbsp / revis) to
reduce the number of polyons.
=============
*/
void R_MarkShadowCasting (shadowlight_t *light, mnode_t *node)
{
mplane_t *plane;
float dist;
msurface_t **surf;
mleaf_t *leaf;
int c,leafindex;
mesh_t *mesh;
if (node->contents & CONTENTS_LEAF) {
//we are in a leaf
leaf = (mleaf_t *)node;
leafindex = leaf->cluster;
//is this leaf visible from the light
if (!(lightvis[leafindex>>3] & (1<<(leafindex&7)))) {
return;
}
c = leaf->nummarksurfaces;
surf = leaf->firstmarksurface;
for (c=0; c<leaf->nummarksurfaces; c++, surf++) {
if (R_MarkShadowSurf ((*surf), light)) {
(*surf)->shadowchain = shadowchain;
shadowchain = (*surf);
//svBsp_NumKeptPolys++;
}
}
c = leaf->nummeshes;
for (c=0; c<leaf->nummeshes; c++) {
mesh = &cl.worldmodel->meshes[cl.worldmodel->leafmeshes[leaf->firstmesh+c]];
if (mesh->lightTimestamp == r_lightTimestamp) continue;
mesh->shadowchain = meshshadowchain;
meshshadowchain = mesh;
mesh->lightTimestamp = r_lightTimestamp;
}
return;
}
plane = node->plane;
dist = DotProduct (light->origin, plane->normal) - plane->dist;
if (dist > light->radius)
{
R_MarkShadowCasting (light, node->children[0]);
return;
}
if (dist < -light->radius)
{
R_MarkShadowCasting (light, node->children[1]);
return;
}
R_MarkShadowCasting (light, node->children[0]);
R_MarkShadowCasting (light, node->children[1]);
}
float SphereInFrustum( vec3_t o, float radius )
{
int p;
float d;
for( p = 0; p < 6; p++ )
{
d = frustumPlanes[p][0] * o[0] + frustumPlanes[p][1] * o[1] + frustumPlanes[p][2] * o[2] + frustumPlanes[p][3];
if( d <= -radius )
return 0;
}
return d + radius;
}
/*
=============
R_ContributeFrame
Returns true if the light should be rendered.
=============
*/
qboolean R_ContributeFrame (shadowlight_t *light)
{
mleaf_t *lightleaf;
float dist;
float b = d_lightstylevalue[light->style]/255.0;
light->color[0] = light->baseColor[0] * b;
light->color[1] = light->baseColor[1] * b;
light->color[2] = light->baseColor[2] * b;
//verry soft light, don't bother.
if (b < 0.1) return false;
//not in a visible area => skip it
if (!(r_refdef.areabits[light->area>>3] & (1<<(light->area&7)))) {
return false;
}
//frustum scissor testing
dist = SphereInFrustum(light->origin, light->radius);
if (dist == 0) {
//whole sphere is out ouf frustum so cut it.
return false;
}
//fully/partially in frustum
if (!sh_noscissor.value) {
vec3_t dst;
float d;
VectorSubtract (light->origin, r_refdef.vieworg, dst);
d = Length (dst);
if (d > light->radius) {
R_ProjectSphere (light, light->scizz.coords);
//glScissor(light->scizz.coords[0], light->scizz.coords[1],
// light->scizz.coords[2]-light->scizz.coords[0], light->scizz.coords[3]-light->scizz.coords[1]);
} else {
//viewport is ofs/width based
light->scizz.coords[0] = r_Iviewport[0];
light->scizz.coords[1] = r_Iviewport[1];
light->scizz.coords[2] = r_Iviewport[0]+r_Iviewport[2];
light->scizz.coords[3] = r_Iviewport[1]+r_Iviewport[3];
//glScissor(r_Iviewport[0], r_Iviewport[1], r_Iviewport[2], r_Iviewport[3]);
}
}
//r_lightTimestamp++;
shadowchain = NULL;
meshshadowchain = NULL;
if (light->isStatic) {
lightvis = &light->vis[0];
} else {
lightleaf = Mod_PointInLeaf (light->origin, cl.worldmodel);
lightvis = Mod_LeafPVS (lightleaf, cl.worldmodel);
Q_memcpy(&light->vis,lightvis,MAX_MAP_LEAFS/8);
Q_memcpy(&light->entvis, lightvis, MAX_MAP_LEAFS/8);
light->area = lightleaf->area;
}
if (HasSharedLeafs(lightvis,&worldvis[0])) {
//light->visible = true;
//numUsedShadowLights++;
//mark shadow casting ents
//MarkShadowEntities();
//mark shadow casting polygons
//if (!light->isStatic) {
// R_MarkShadowCasting ( light, cl.worldmodel->nodes);
//} else {
// return true;
//}
//return (shadowchain) ? true : false;
return true;
} else {
return false;
}
}
/*
=============
R_FillShadowChain
Returns true if the light should be rendered.
=============
*/
qboolean R_FillShadowChain (shadowlight_t *light)
{
r_lightTimestamp++;
shadowchain = NULL;
meshshadowchain = NULL;
lightvis = &light->vis[0];
//numUsedShadowLights++;
//mark shadow casting ents
MarkShadowEntities();
//mark shadow casting polygons
if (!light->isStatic) {
R_MarkShadowCasting ( light, cl.worldmodel->nodes);
} else {
return true;
}
return (shadowchain || meshshadowchain) ? true : false;
}
void *VolumeVertsPointer;
/*
=============
R_ConstructShadowVolume
Calculate the shadow volume commands for the light.
(only if dynamic)
=============
*/
void R_ConstructShadowVolume(shadowlight_t *light) {
if (!light->isStatic) {
PrecalcVolumesForLight(cl.worldmodel);
light->volumeCmds = &volumeCmdsBuff[0];
light->volumeVerts = &volumeVertsBuff[0];
light->lightCmds = &lightCmdsBuff[0];
light->numlightcmds = numLightCmds;
}
VolumeVertsPointer = light->volumeVerts;
if (gl_var) {
if (light->numVolumeVerts < AGP_BUFFER_SIZE/4) {
memcpy(AGP_Buffer,light->volumeVerts,light->numVolumeVerts*4);
VolumeVertsPointer = AGP_Buffer;
}
}
}
/*
=============
R_DrawShadowVolume
Draws the shadow volume, for statics this is the precalc one
for dynamics this is the R_ConstructShadowVolume one.
=============
*/
void R_DrawShadowVolume(shadowlight_t *light) {
glDisable(GL_TEXTURE_2D);
/* if (!light->isStatic) {
glColor3f(0,1,0);
DrawVolumeFromCmds (&volumeCmdsBuff[0], &lightCmdsBuff[0]);
} else {
*/
glColor3f(1,0,0);
DrawVolumeFromCmds (light->volumeCmds, light->lightCmds, VolumeVertsPointer);
// }
glColor3f(1,1,1);
glEnable(GL_TEXTURE_2D);
}
/**************************************************************
Brush model shadow volume support
***************************************************************/
/*
=============
R_MarkBrushModelSurfaces
Set the light timestamps of the brush model.
=============
*/
void R_MarkBrushModelSurfaces(entity_t *e) {
vec3_t mins, maxs;
int i;
msurface_t *psurf;
model_t *clmodel;
qboolean rotated;
vec3_t oldlightorigin;
//backup light origin since we will have to translate
//light into model space
VectorCopy (currentshadowlight->origin, oldlightorigin);
currententity = e;
currenttexture = -1;
clmodel = e->model;
if (e->angles[0] || e->angles[1] || e->angles[2])
{
rotated = true;
for (i=0 ; i<3 ; i++)
{
mins[i] = e->origin[i] - clmodel->radius;
maxs[i] = e->origin[i] + clmodel->radius;
}
}
else
{
rotated = false;
VectorAdd (e->origin, clmodel->mins, mins);
VectorAdd (e->origin, clmodel->maxs, maxs);
}
VectorSubtract (r_refdef.vieworg, e->origin, modelorg);
VectorSubtract (currentshadowlight->origin, e->origin, currentshadowlight->origin);
if (rotated)
{
vec3_t temp;
vec3_t forward, right, up;
VectorCopy (modelorg, temp);
AngleVectors (e->angles, forward, right, up);
modelorg[0] = DotProduct (temp, forward);
modelorg[1] = -DotProduct (temp, right);
modelorg[2] = DotProduct (temp, up);
VectorCopy (currentshadowlight->origin, temp);
currentshadowlight->origin[0] = DotProduct (temp, forward);
currentshadowlight->origin[1] = -DotProduct (temp, right);
currentshadowlight->origin[2] = DotProduct (temp, up);
}
psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
glPushMatrix ();
e->angles[0] = -e->angles[0]; // stupid quake bug
R_RotateForEntity (e);
e->angles[0] = -e->angles[0]; // stupid quake bug
for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++)
{
R_MarkShadowSurf(psurf, currentshadowlight);
}
VectorCopy(oldlightorigin,currentshadowlight->origin);
glPopMatrix ();
}
/*
=============
R_DrawBrushModelVolumes
Draw the shadow volumes of the brush model.
They are dynamically calculated.
=============
*/
/*
void R_DrawBrushModelVolumes(entity_t *e) {
int j, k;
vec3_t mins, maxs;
int i, numsurfaces;
msurface_t *surf;
float dot;
mplane_t *pplane;
model_t *clmodel;
qboolean rotated;
glpoly_t *poly;
vec3_t v1,*v2;
float scale;
qboolean shadow;
vec3_t temp[32];
vec3_t oldlightorigin;
//backup light origin since we will have to translate
//light into model space
VectorCopy (currentshadowlight->origin, oldlightorigin);
currententity = e;
currenttexture = -1;
clmodel = e->model;
if (e->angles[0] || e->angles[1] || e->angles[2])
{
rotated = true;
for (i=0 ; i<3 ; i++)
{
mins[i] = e->origin[i] - clmodel->radius;
maxs[i] = e->origin[i] + clmodel->radius;
}
}
else
{
rotated = false;
VectorAdd (e->origin, clmodel->mins, mins);
VectorAdd (e->origin, clmodel->maxs, maxs);
}
VectorSubtract (r_refdef.vieworg, e->origin, modelorg);
VectorSubtract (currentshadowlight->origin, e->origin, currentshadowlight->origin);
if (rotated)
{
vec3_t temp;
vec3_t forward, right, up;
VectorCopy (modelorg, temp);
AngleVectors (e->angles, forward, right, up);
modelorg[0] = DotProduct (temp, forward);
modelorg[1] = -DotProduct (temp, right);
modelorg[2] = DotProduct (temp, up);
VectorCopy (currentshadowlight->origin, temp);
currentshadowlight->origin[0] = DotProduct (temp, forward);
currentshadowlight->origin[1] = -DotProduct (temp, right);
currentshadowlight->origin[2] = DotProduct (temp, up);
}
surf = &clmodel->surfaces[clmodel->firstmodelsurface];
glPushMatrix ();
e->angles[0] = -e->angles[0]; // stupid quake bug
R_RotateForEntity (e);
e->angles[0] = -e->angles[0]; // stupid quake bug
for (i=0 ; i<clmodel->nummodelsurfaces ; i++, surf++)
{
if (surf->polys->lightTimestamp != r_lightTimestamp) continue;
poly = surf->polys;
for (j=0 ; j<surf->numedges ; j++)
{
v2 = (vec3_t *)&poly->verts[j];
VectorSubtract ( (*v2) ,currentshadowlight->origin, v1);
scale = Length (v1);
if (sh_visiblevolumes.value)
VectorScale (v1, (1/scale)*50, v1);
else
VectorScale (v1, (1/scale)*currentshadowlight->radius*2, v1);
VectorAdd (v1, (*v2), temp[j]);
}
//check if neighbouring polygons are shadowed
for (j=0 ; j<surf->numedges ; j++)
{
shadow = false;
if (poly->neighbours[j] != NULL) {
if ( poly->neighbours[j]->lightTimestamp != poly->lightTimestamp) {
shadow = true;
}
} else {
shadow = true;
}
if (shadow) {
//we extend the shadow volumes by projecting them on the
//light's sphere.
//This sometimes gives problems when the light is verry close to a big
//polygon. But further extending the volume wastes fill rate.
//So ill have to fix it.
glBegin(GL_QUAD_STRIP);
glVertex3fv(&poly->verts[j][0]);
glVertex3fv(&temp[j][0]);
glVertex3fv(&poly->verts[((j+1)% poly->numverts)][0]);
glVertex3fv(&temp[((j+1)% poly->numverts)][0]);
glEnd();
}
}
//Draw near light cap
glBegin(GL_POLYGON);
for (j=0; j<surf->numedges ; j++)
{
glVertex3fv(&poly->verts[j][0]);
}
glEnd();
//Draw extruded cap
glBegin(GL_POLYGON);
for (j=surf->numedges-1; j>=0 ; j--)
{
glVertex3fv(&temp[j][0]);
}
glEnd();
}
//PrecalcVolumesForLight(clmodel);
VectorCopy(oldlightorigin,currentshadowlight->origin);
glPopMatrix ();
}
*/
/*
=============
R_DrawBrushModelVolumes
Draw the shadow volumes of the brush model.
They are dynamically calculated.
=============
*/
void R_DrawBrushModelVolumes(entity_t *e) {
model_t *model = e->model;
msurface_t *surf;
glpoly_t *poly;
int i, j, count;
brushlightinstant_t *ins = e->brushlightinstant;
float *v;
count = 0;
glPushMatrix ();
e->angles[0] = -e->angles[0]; // stupid quake bug
R_RotateForEntity (e);
e->angles[0] = -e->angles[0]; // stupid quake bug
surf = &model->surfaces[model->firstmodelsurface];
for (i=0; i<model->nummodelsurfaces; i++, surf++)
{
if (!ins->polygonVis[i]) continue;
poly = surf->polys;
//extrude edges
for (j=0 ; j<poly->numneighbours ; j++)
{
mneighbour_t *neigh = &poly->neighbours[j];
if (ins->neighbourVis[count+j]) {
glBegin(GL_QUAD_STRIP);
//Note: The neighbour p1,p2 are absolute vertex indecies, for the extruded ones
//we want polygon relative indecies.
//glVertex3fv(&poly->verts[j][0]);
glVertex3fv((float *)(&globalVertexTable[neigh->p1]));
glVertex3fv(&ins->extvertices[count+neigh->p1-poly->firstvertex][0]);
//glVertex3fv(&poly->verts[((j+1)% poly->numverts)][0]);
glVertex3fv((float *)(&globalVertexTable[neigh->p2]));
glVertex3fv(&ins->extvertices[count+neigh->p2-poly->firstvertex][0]);
glEnd();
}
}
//Draw near light cap
/*
glBegin(GL_TRIANGLE_FAN);
for (j=0; j<surf->numedges ; j++)
{
//glVertex3fv(&poly->verts[j][0]);
glVertex3fv((float *)(&globalVertexTable[surf->polys->firstvertex+j]));
}
glEnd();
*/
glBegin(GL_TRIANGLES);
//v = surf->polys->verts[0];
for (j=0; j<surf->polys->numindecies; j++) {
v = (float *)(&globalVertexTable[surf->polys->indecies[j]]);
glVertex3fv(&v[0]);
}
glEnd();
//Draw extruded cap
/*
glBegin(GL_TRIANGLE_FAN);
for (j=surf->numedges-1; j>=0 ; j--)
{
glVertex3fv(&ins->extvertices[count+j][0]);
}
glEnd();
*/
glBegin(GL_TRIANGLES);
//v = surf->polys->verts[0];
for (j=surf->polys->numindecies-1; j>=0; j--) {
v = (float *)(&ins->extvertices[count+surf->polys->indecies[j]-surf->polys->firstvertex]);
glVertex3fv(&v[0]);
}
glEnd();
count+=surf->numedges;
}
glPopMatrix ();
}
/**************************************************************
Shadow volume precalculation & storing
***************************************************************/
/*
=============
getVertexIndexFromSurf
Gets index of the i'th vertex of the surface in the models vertex array
=============
*/
int getVertexIndexFromSurf(msurface_t *surf, int index, model_t *model) {
int lindex = model->surfedges[surf->firstedge + index];
medge_t *r_pedge;
if (lindex > 0)
{
r_pedge = &model->edges[lindex];
//if (r_pedge->v[0] == 0) Con_Printf("moord en brand");
return r_pedge->v[0];
}
else
{
r_pedge = &model->edges[-lindex];
//if (r_pedge->v[1] == 0) Con_Printf("moord en brand");
return r_pedge->v[1];
}
}
/*
=============
PrecalcVolumesForLight
This will create arrays with gl-commands that define the shadow volumes
(something similar to the mesh arrays that are created.)
They are stored for static lights and recalculated every frame for dynamic ones.
Non calculated vertices are not saved in the list but the index in the vertex array
of the model is saved.
We store them in volumeCmdsBuff and lightCmdsBuff
=============
*/
void PrecalcVolumesForLight(model_t *model) {
msurface_t *surf;
int *volumeCmds = &volumeCmdsBuff[0];
lightcmd_t *lightCmds = &lightCmdsBuff[0];
lightcmd_t *lightCmdsMesh = &lightCmdsBuffMesh[0];
float *volumeVerts = &volumeVertsBuff[0];
int volumePos = 0;
int lightPos = 0;
int lightPosMesh = 0;
int vertPos = 0;
int startVerts, startNearVerts, numPos = 0, stripLen = 0, i;
glpoly_t *poly;
qboolean lastshadow;
vec3_t v1, *v2, vert1;
float scale;
qboolean shadow;
vec3_t *s, *t, nearPt, nearToVert;
float dist, colorscale;
mplane_t *splitplane;
int j;
float *v;
mesh_t *mesh;
surf = shadowchain;
//1. Calculate shadow volumes
while (surf)
{
poly = surf->polys;
if (( surf->flags & SURF_DRAWTURB ) || ( surf->flags & SURF_DRAWSKY )) {
surf = surf->shadowchain;
Con_Printf ("Water/Sky in shadow chain!!");
continue;
}
//a. far cap
// volumeCmds[volumePos++] = GL_POLYGON;
volumeCmds[volumePos++] = GL_TRIANGLES;
volumeCmds[volumePos++] = poly->numindecies;
//extrude verts
startVerts = (int)vertPos/3;
for (i=0 ; i<surf->numedges ; i++)
{
//v2 = (vec3_t *)&poly->verts[i];
v2 = (vec3_t *)(&globalVertexTable[surf->polys->firstvertex+i]);
VectorSubtract ( (*v2), currentshadowlight->origin, v1);
scale = Length (v1);
if (sh_visiblevolumes.value) {
//make them short so that we see them
VectorScale (v1, (1/scale)*50, v1);
} else {
//we don't have to be afraid they will clip with the far plane
//since we use the infinite matrix trick
VectorScale (v1, (1/scale)* currentshadowlight->radius*10, v1);
}
VectorAdd (v1, (*v2) ,vert1);
VectorCopy (vert1, ((vec3_t *)(volumeVerts+vertPos))[0]);
vertPos+=3;
}
//create indexes
for (i=poly->numindecies-1; i>=0; i--)
{
volumeCmds[volumePos++] = startVerts+poly->indecies[i]-poly->firstvertex;
}
//copy vertices
startNearVerts = (int)vertPos/3;
for (i=0 ; i<surf->numedges ; i++)
{
//v2 = (vec3_t *)&poly->verts[i];
v2 = (vec3_t *)(&globalVertexTable[surf->polys->firstvertex+i]);
/*(float)*/volumeVerts[vertPos++] = (*v2)[0]; // <AWE> lvalue cast. what da...?
/*(float)*/volumeVerts[vertPos++] = (*v2)[1]; // <AWE> a float is a float is a...
/*(float)*/volumeVerts[vertPos++] = (*v2)[2];
}
if (vertPos > MAX_VOLUME_VERTS) {
Con_Printf ("More than MAX_VOLUME_VERTS vetices! %i\n", volumePos);
break;
}
//b. borders of volume
//we make quad strips if we have continuous borders
lastshadow = false;
for (i=0 ; i<poly->numneighbours ; i++)
{
mneighbour_t *neigh = &poly->neighbours[i];
shadow = false;
if (neigh->n != NULL) {
if ( neigh->n->lightTimestamp != poly->lightTimestamp) {
shadow = true;
}
} else {
shadow = true;
}
//if (shadow) {
// if (!lastshadow) {
//begin new strip
volumeCmds[volumePos++] = GL_QUAD_STRIP;
numPos = volumePos;
volumeCmds[volumePos++] = 4;
stripLen = 2;
//copy vertices
volumeCmds[volumePos++] = startNearVerts+neigh->p1-poly->firstvertex;//-getVertexIndexFromSurf(surf, i, model);
volumeCmds[volumePos++] = startVerts+neigh->p1-poly->firstvertex;
// }
volumeCmds[volumePos++] = startNearVerts+neigh->p2-poly->firstvertex;//-getVertexIndexFromSurf(surf, (i+1)%poly->numverts, model);
volumeCmds[volumePos++] = startVerts+neigh->p2-poly->firstvertex;
stripLen+=2;
// } else {
// if (lastshadow) {
//close list up
volumeCmds[numPos] = stripLen;
// }
// }
// lastshadow = shadow;
}
if (lastshadow) {
//close list up
volumeCmds[numPos] = stripLen;
}
if (volumePos > MAX_VOLUME_COMMANDS) {
Con_Printf ("More than MAX_VOLUME_COMMANDS commands! %i\n", volumePos);
break;
}
lightCmds[lightPos++].asVoid = surf;
//c. glow surfaces/texture coordinates
//leftright vectors of plane
/*
s = (vec3_t *)&surf->tangent;
t = (vec3_t *)&surf->binormal;
//VectorNormalize(*s);
//VectorNormalize(*t);
splitplane = surf->plane;
dist = DotProduct (currentshadowlight->origin, splitplane->normal) - splitplane->dist;
dist = abs(dist);
if (dist > currentshadowlight->radius) Con_Printf("Polygon to far\n");
ProjectPlane (currentshadowlight->origin, (*s), (*t), nearPt);
scale = 1 /((2 * currentshadowlight->radius) - dist);
colorscale = (1 - (dist / currentshadowlight->radius));
if (colorscale <0) colorscale = 0;
lightCmds[lightPos++].asInt = GL_TRIANGLE_FAN;
lightCmds[lightPos++].asVoid = surf;
lightCmds[lightPos++].asFloat = currentshadowlight->color[0]*colorscale;
lightCmds[lightPos++].asFloat = currentshadowlight->color[1]*colorscale;
lightCmds[lightPos++].asFloat = currentshadowlight->color[2]*colorscale;
lightCmds[lightPos++].asFloat = colorscale;
//v = poly->verts[0];
v = (float *)(&globalVertexTable[surf->polys->firstvertex]);
for (j=0 ; j<poly->numverts ; j++, v+= VERTEXSIZE)
{
// Project the light image onto the face
VectorSubtract (v, nearPt, nearToVert);
// Get our texture coordinates, transform into tangent plane
lightCmds[lightPos++].asVec = DotProduct (nearToVert, (*s)) * scale + 0.5;
lightCmds[lightPos++].asVec = DotProduct (nearToVert, (*t)) * scale + 0.5;
//calculate local light vector and put it into tangent space
{
vec3_t lightDir, tsLightDir;
VectorSubtract( currentshadowlight->origin,v,lightDir);
if (surf->flags & SURF_PLANEBACK) {
tsLightDir[2] = -DotProduct(lightDir,surf->plane->normal);
} else {
tsLightDir[2] = DotProduct(lightDir,surf->plane->normal);
}
tsLightDir[1] = -DotProduct(lightDir,(*t));
tsLightDir[0] = DotProduct(lightDir,(*s));
lightCmds[lightPos++].asVec = tsLightDir[0];
lightCmds[lightPos++].asVec = tsLightDir[1];
lightCmds[lightPos++].asVec = tsLightDir[2];
}
}
*/
if (lightPos > MAX_LIGHT_COMMANDS) {
Con_Printf ("More than MAX_LIGHT_COMMANDS commands %i\n", lightPos);
break;
}
surf = surf->shadowchain;
}
lightPosMesh = 0;
mesh = meshshadowchain;
while (mesh) {
lightCmdsMesh[lightPosMesh++].asVoid = mesh;
mesh = mesh->shadowchain;
}
//Con_Printf("used %i\n",volumePos);
//finish them off with 0
lightCmds[lightPos++].asVoid = NULL;
lightCmdsMesh[lightPosMesh++].asVoid = NULL;
volumeCmds[volumePos++] = 0;
numLightCmds = lightPos;
numLightCmdsMesh = lightPosMesh;
numVolumeCmds = volumePos;
numVolumeVerts = vertPos;
}
/*
=============
DrawVolumeFromCmds
Draws the generated commands as shadow volumes
=============
*/
void DrawVolumeFromCmds(int *volumeCmds, lightcmd_t *lightCmds, float *volumeVerts) {
int command, num, i;
int volumePos = 0;
int lightPos = 0;
// int count = 0; // <AWE> no longer required.
msurface_t *surf;
float *v;
glVertexPointer(3, GL_FLOAT, 0, volumeVerts);
glEnableClientState(GL_VERTEX_ARRAY);
while (1) {
command = volumeCmds[volumePos++];
if (command == 0) break; //end of list
num = volumeCmds[volumePos++];
glDrawElements(command,num,GL_UNSIGNED_INT,&volumeCmds[volumePos]);
volumePos+=num;
/*glBegin(command);
if ((command == GL_QUAD_STRIP) || (command == GL_QUADS)) {
for (i=0; i<num; i++) {
ind = volumeCmds[volumePos++];
// glVertex3fv((float *)(&volumeCmds[ind]));
glVertex3fv(&volumeVerts[ind*3]);
}
} else {
//caps point inwards
//volumePos+=num*3;
for (i=0; i<num; i++) {
ind = volumeCmds[volumePos++];
//extuded verts have w component
//glVertex3fv((float *)(volumeCmds+(volumePos-(i+1)*3)));
glVertex3fv(&volumeVerts[ind*3]);
}
//volumePos+=num*3;
}
glEnd();*/
// count++; // <AWE> no longer required.
}
glDisableClientState(GL_VERTEX_ARRAY);
//Con_Printf("%i objects drawn\n",count);
if (sh_visiblevolumes.value) return;
while (1) {
/*
command = lightCmds[lightPos++].asInt;
if (command == 0) break; //end of list
surf = lightCmds[lightPos++].asVoid;
lightPos+=4; //skip color
num = surf->polys->numverts;
*/
surf = lightCmds[lightPos++].asVoid;
if (surf == NULL)
break;
glBegin(GL_TRIANGLES);
//v = surf->polys->verts[0];
for (i=0; i<surf->polys->numindecies; i++) {
v = (float *)(&globalVertexTable[surf->polys->indecies[i]]);
glVertex3fv(&v[0]);
}
glEnd();
/*
glBegin(command);
//v = surf->polys->verts[0];
v = (float *)(&globalVertexTable[surf->polys->firstvertex]);
for (i=0; i<num; i++, v+= VERTEXSIZE) {
//skip attent texture coord.
lightPos+=2;
//skip tangent space light vector
lightPos+=3;
glVertex3fv(&v[0]);
}
glEnd();
*/
}
}
/*
=============
R_RenderGlow
Render a halo around a light.
The idea is similar to the UT2003 lensflares.
=============
*/
//speed at wich halo grows fainter when it gets occluded
#define HALO_FALLOF 2
//the scale the alpa is multipied with (none is verry bright)
#define HALO_ALPHA_SCALE 0.7
//the maximum radius (in pixels) of the halo
#define HALO_SIZE 30
//the distance of the eye at wich the halo stops shrinking
#define HALO_MIN_DIST 300
void R_RenderGlow (shadowlight_t *light)
{
vec3_t hit = {0, 0, 0};
double x,y,z;
float fdist,realz;
int ofsx, ofsy;
qboolean hitWorld;
if (!light->halo || gl_wireframe.value)
return;
//trace a from the eye to the light source
TraceLine (r_refdef.vieworg, light->origin, hit);
//if it's not visible anymore slowly fade it
if (Length(hit) != 0) {
light->haloalpha = light->haloalpha - (cl.time - cl.oldtime)*HALO_FALLOF;
if (light->haloalpha < 0) return;
hitWorld = true;
} else
hitWorld = false;
/*
VectorSubtract(r_refdef.vieworg, light->origin, dist);
fdist = Length(dist);
if (fdist < HALO_MIN_DIST) fdist = HALO_MIN_DIST;
fdist = (1-1/fdist)*HALO_SIZE;
*/
fdist = HALO_SIZE;
gluProject(light->origin[0], light->origin[1], light->origin[2], r_Dworld_matrix,
r_Dproject_matrix, (GLint *) r_Iviewport, &x, &y, &z); // <AWE> added cast.
//Con_Printf("Viewp %i %i %i %i\n",r_Iviewport[0],r_Iviewport[1],r_Iviewport[2],r_Iviewport[3]);
if (!hitWorld) {
//we didn't hit any bsp try to read the z buffer (wich is totally utterly freakingly
// <20>ber evil!)
glReadPixels((int)x,(int)y,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,&realz);
if (realz < z) {
light->haloalpha = light->haloalpha - (cl.time - cl.oldtime)*HALO_FALLOF;
if (light->haloalpha < 0) return;
} else {
//nothing in the way make it fully bright
light->haloalpha = 1.0;
}
}
ofsx = r_Iviewport[0];
ofsy = r_Iviewport[1];
x = x;
y = glheight-y;
x = (x/(float)glwidth)*vid.width;
y = (y/(float)glheight)*vid.height;
//glDisable (GL_TEXTURE_2D)
GL_Bind(halo_texture_object);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glShadeModel (GL_SMOOTH);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE);
glDisable(GL_ALPHA_TEST);
glBegin (GL_QUADS);
glColor4f(light->color[0],light->color[1],light->color[2],light->haloalpha*HALO_ALPHA_SCALE);
//glColor4f(1,1,1,light->haloalpha);
glTexCoord2f (0, 0);
glVertex2f (x-fdist, y-fdist);
glTexCoord2f (1, 0);
glVertex2f (x+fdist, y-fdist);
glTexCoord2f (1, 1);
glVertex2f (x+fdist, y+fdist);
glTexCoord2f (0, 1);
glVertex2f (x-fdist, y+fdist);
glEnd ();
/*
rad = 40;//light->radius * 0.35;
VectorSubtract (light->origin, r_origin, v);
//dave - slight fix
VectorSubtract (light->origin, r_origin, vp2);
VectorNormalize(vp2);
glBegin (GL_TRIANGLE_FAN);
glColor3f (light->color[0]*0.2,light->color[1]*0.2,light->color[2]*0.2);
for (i=0 ; i<3 ; i++)
v[i] = light->origin[i] - vp2[i]*rad;
glVertex3fv (v);
glColor3f (0,0,0);
for (i=16 ; i>=0 ; i--)
{
a = i/16.0 * M_PI*2;
for (j=0 ; j<3 ; j++)
v[j] = light->origin[j] + vright[j]*cos(a)*rad
+ vup[j]*sin(a)*rad;
glVertex3fv (v);
}
glEnd ();
*/
glEnable(GL_ALPHA_TEST);
glColor3f (1,1,1);
//glDisable (GL_BLEND);
//glEnable (GL_TEXTURE_2D);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask (1);
glColor3f (1,1,1);
}
/**************************************************************
Shadow volume bsp's
Some of this suff should be put in gl_svbsp.c
***************************************************************/
svnode_t *currentlightroot;
vec3_t testvect = {10,10,10};
/*
================
CutLeafs
Removes leaves that were cut by using the svbsp from the light's
visibility list.
This gains some speed in certain cases.
================
*/
void CutLeafs(byte *vis) {
int c, i;
msurface_t **surf;
mleaf_t *leaf;
qboolean found;
int removed = 0;
if (sh_norevis.value) return;
for (i=0 ; i<cl.worldmodel->numleafs ; i++)//overloop alle leafs
{
leaf = &cl.worldmodel->leafs[i];
if (vis[leaf->cluster>>3] & (1<<(leaf->cluster&7)))
{
//this leaf is visible from the leaf the current light (in a brute force way)
//now check if we entirely cut this leaf by means of the svbsp
c = leaf->nummarksurfaces;
surf = leaf->firstmarksurface;
// if (leaf->index != i) Con_Printf("Weird leaf index %i, %i\n",i,leaf->index);
found = false;
for (c=0; c<leaf->nummarksurfaces; c++, surf++) {
if ((*surf)->polys->lightTimestamp == r_lightTimestamp) {
found = true;
break;
}
}
/*
if (!found) {
//set vis bit on false
vis[i>>3] &= ~(1<<(i&7));
removed++;
}
*/
}
}
//Con_Printf(" Removed leafs: %i\n", removed);
}
/*
================
AddToShadowBsp
Add a surface as potential shadow caster to the svbsp, it can be
cut when it is occluded by other surfaces.
================
*/
void AddToShadowBsp(msurface_t *surf) {
vec3_t surfvects[32];
int numsurfvects;
int i;
float *v;
/*
PENTA: removed we checked this twice
//we don't cast shadows with water
if (( surf->flags & SURF_DRAWTURB ) || ( surf->flags & SURF_DRAWSKY )) {
return;
}
plane = surf->plane;
switch (plane->type)
{
case PLANE_X:
dist = currentshadowlight->origin[0] - plane->dist;
break;
case PLANE_Y:
dist = currentshadowlight->origin[1] - plane->dist;
break;
case PLANE_Z:
dist = currentshadowlight->origin[2] - plane->dist;
break;
default:
dist = DotProduct (currentshadowlight->origin, plane->normal) - plane->dist;
break;
}
//the normals are flipped when surf_planeback is 1
if (((surf->flags & SURF_PLANEBACK) && (dist > 0)) ||
(!(surf->flags & SURF_PLANEBACK) && (dist < 0)))
{
return;
}
//the normals are flipped when surf_planeback is 1
if ( abs(dist) > currentshadowlight->radius)
{
return;
}
*/
//FIXME: use constant instead of 32
if (surf->numedges > 32) {
Con_Printf("Error: to many edges");
return;
}
surf->visframe = 0;
//Make temp copy of suface polygon
numsurfvects = surf->numedges;
for (i=0, v=(float *)(&globalVertexTable[surf->polys->firstvertex]); i<numsurfvects; i++, v+=VERTEXSIZE) {
VectorCopy(v,surfvects[i]);
}
if (!sh_nosvbsp.value) {
svBsp_NumAddedPolys++;
R_AddShadowCaster(currentlightroot,surfvects,numsurfvects,surf,0);
} else {
surf->shadowchain = shadowchain;
surf->polys->lightTimestamp = r_lightTimestamp;
shadowchain = surf;
svBsp_NumKeptPolys++;
}
}
/*
================
R_RecursiveShadowAdd
Add surfaces front to back to the shadow bsp.
================
*/
void R_RecursiveShadowAdd(mnode_t *node)
{
int c, side;
mplane_t *plane;
msurface_t *surf;
double dot;
if (node->contents == CONTENTS_SOLID) {
return; // solid
}
if (node->contents & CONTENTS_LEAF) {
return; // leaf
}
//find which side of the node we are on
plane = node->plane;
switch (plane->type)
{
case PLANE_X:
dot = currentshadowlight->origin[0] - plane->dist;
break;
case PLANE_Y:
dot = currentshadowlight->origin[1] - plane->dist;
break;
case PLANE_Z:
dot = currentshadowlight->origin[2] - plane->dist;
break;
default:
dot = DotProduct (currentshadowlight->origin, plane->normal) - plane->dist;
break;
}
if (dot >= 0)
side = 0;
else
side = 1;
//recurse down the children, front side first
R_RecursiveShadowAdd (node->children[side]);
//draw stuff
c = node->numsurfaces;
if (c)
{
surf = cl.worldmodel->surfaces + node->firstsurface;
do
{
if (surf->polys) {
if ((surf->polys->lightTimestamp == r_lightTimestamp))
{
AddToShadowBsp (surf);
}
}
surf++;
} while (--c);
}
//recurse down the back side
R_RecursiveShadowAdd (node->children[!side]);
}
/*
===============
R_MarkLightLeaves
Marks nodes from the light, this is used for
gross culling during svbsp creation.
===============
*/
void R_MarkLightLeaves (void)
{
byte solid[4096];
mleaf_t *lightleaf;
//we use the same timestamp as for rendering (may cause errors maybe)
r_visframecount++;
lightleaf = Mod_PointInLeaf (currentshadowlight->origin, cl.worldmodel);
if (r_novis.value)
{
lightvis = solid;
memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3);
}
else
lightvis = Mod_LeafPVS (lightleaf, cl.worldmodel);
}
/*
================
ShadowVolumeBsp
Create the shadow volume bsp for currentshadowlight
================
*/
void ShadowVolumeBsp() {
msurface_t *n;
currentlightroot = R_CreateEmptyTree();
R_MarkLightLeaves();
R_MarkShadowCasting (currentshadowlight,cl.worldmodel->nodes);
//PENTA: Q3 hack until svbsp's are fixed again
svBsp_NumKeptPolys = 0;
n = shadowchain;
while (n) {
svBsp_NumKeptPolys++;
n = n->shadowchain;
}
//shadowchain = NULL;
//svBsp_NumKeptPolys = 0;
//R_RecursiveShadowAdd(cl.worldmodel->nodes);
}
int done = 0;
/*
================
R_CalcSvBsp
Called for every static ent during spawning of the client
================
*/
void R_CalcSvBsp(entity_t *ent) {
int i;
msurface_t *surf;
msurface_t *s;
mapshader_t *t;
//Con_Printf("Shadow volumes start\n");
if (ent->model == NULL) {
Con_Printf("null model");
return;
}
if (!strcmp (ent->model->name, "progs/flame2.mdl")
|| !strcmp (ent->model->name, "progs/flame.mdl")
|| !strcmp (ent->model->name, "progs/s_light.spr")
|| !strcmp (ent->model->name, "progs/b_light.spr")
|| !strcmp (ent->model->name, "progs/w_light.spr"))
{
shadowchain = NULL;
meshshadowchain = NULL;
done++;
Con_Printf("->Light %i\n",done);
r_lightTimestamp++;
r_framecount++;
svBsp_NumCutPolys = 0;
svBsp_NumKeptPolys = 0;
svBsp_NumAddedPolys = 0;
//Create a light and make it static
R_ShadowFromEntity(ent);
numStaticShadowLights++;
if (numShadowLights >= MAXSHADOWLIGHTS) {
Con_Printf("R_CalcSvBsp: More than MAXSHADOWLIGHTS lights");
return;
}
currentshadowlight = &shadowlights[numShadowLights-1];
//Hack: support quake light_* entities
if (!strcmp (ent->model->name, "progs/flame2.mdl")) {
currentshadowlight->baseColor[0] = 1;
currentshadowlight->baseColor[1] = 0.9;
currentshadowlight->baseColor[2] = 0.75;
} else if (!strcmp (ent->model->name, "progs/flame.mdl")) {
currentshadowlight->baseColor[0] = 1;
currentshadowlight->baseColor[1] = 0.9;
currentshadowlight->baseColor[2] = 0.75;
} else if (!strcmp (ent->model->name, "progs/s_light.spr")) {
currentshadowlight->baseColor[0] = 1;
currentshadowlight->baseColor[1] = 0.9;
currentshadowlight->baseColor[2] = 0.75;
}
currentshadowlight->isStatic = true;
//Calculate visible polygons
ShadowVolumeBsp();
//Print stats
/*
Con_Printf(" Thrown away: %i\n",svBsp_NumAddedPolys-svBsp_NumKeptPolys);
Con_Printf(" Total in volume: %i\n",svBsp_NumKeptPolys);
*/
currentshadowlight->visSurf = Hunk_Alloc(4*svBsp_NumKeptPolys);
currentshadowlight->numVisSurf = svBsp_NumKeptPolys;
surf = shadowchain;
//Clear texture chains
for (i=0 ; i<cl.worldmodel->nummapshaders; i++)
{
cl.worldmodel->mapshaders[i].texturechain = NULL;
}
//Remark polys since polygons may have been removed since the last time stamp
r_lightTimestamp++;
for (i=0; i<svBsp_NumKeptPolys; i++,surf = surf->shadowchain) {
surf->polys->lightTimestamp = r_lightTimestamp;
currentshadowlight->visSurf[i] = surf;
//put it in the correct texture chain
surf->texturechain = surf->shader->texturechain;
surf->shader->texturechain = surf;
}
//Sort surfs in our list per texture
shadowchain = NULL;
//meshshadowchain = NULL;
for (i=0 ; i<cl.worldmodel->nummapshaders ; i++)
{
t = &cl.worldmodel->mapshaders[i];
if (!t)
continue;
s = t->texturechain;
if (!s)
continue;
else
{
for ( ; s ; s=s->texturechain) {
s->shadowchain = shadowchain;
shadowchain = s;
}
}
t->texturechain = NULL;
}
//Recalculate vis for this light
currentshadowlight->leaf = Mod_PointInLeaf (currentshadowlight->origin, cl.worldmodel);
lightvis = Mod_LeafPVS (currentshadowlight->leaf, cl.worldmodel);
currentshadowlight->area = currentshadowlight->leaf->area;
Q_memcpy(&currentshadowlight->vis[0], lightvis, MAX_MAP_LEAFS/8);
Q_memcpy(&currentshadowlight->entvis[0], lightvis, MAX_MAP_LEAFS/8);
CutLeafs(currentshadowlight->vis);
//Precalculate the shadow volume / glow-texcoords
PrecalcVolumesForLight(cl.worldmodel);
currentshadowlight->volumeCmds = Hunk_Alloc(4*numVolumeCmds);
Q_memcpy(currentshadowlight->volumeCmds, &volumeCmdsBuff, 4*numVolumeCmds);
currentshadowlight->volumeVerts = Hunk_Alloc(4*numVolumeVerts);
currentshadowlight->numVolumeVerts = numVolumeVerts;
Q_memcpy(currentshadowlight->volumeVerts, &volumeVertsBuff, 4*numVolumeVerts);
currentshadowlight->lightCmds = Hunk_Alloc(4*numLightCmds);
Q_memcpy(currentshadowlight->lightCmds, &lightCmdsBuff, 4*numLightCmds);
currentshadowlight->numlightcmds = numLightCmds;
currentshadowlight->lightCmdsMesh = Hunk_Alloc(4*numLightCmdsMesh);
Q_memcpy(currentshadowlight->lightCmdsMesh, &lightCmdsBuffMesh, 4*numLightCmdsMesh);
currentshadowlight->numlightcmdsmesh = numLightCmdsMesh;
//Con_Printf("light done\n");
} else {
//Con_Printf("thrown away");
}
}
#define surfaceLightRadius 350.0
/*
Not used places lights in front of things that look like lights in a map
*/
void LightFromSurface(msurface_t *surf) {
vec3_t center, orig, normal;
glpoly_t *poly;
float *v, invnum;
int i;
shadowlight_t *light;
qboolean tooClose;
entity_t fakeEnt;
poly = surf->polys;
invnum = 1.0/poly->numverts;
//Calculate origin for the light we are possibly going to spawn
//v = poly->verts[0];
v = (float *)(&globalVertexTable[poly->firstvertex]);
center[0] = center[1] = center[2] = 0;
for (i=0 ; i<poly->numverts ; i++, v+= VERTEXSIZE)
{
VectorAdd(center,v,center);
}
VectorScale(center,invnum,center);
if (surf->flags & SURF_PLANEBACK) {
VectorScale(surf->plane->normal,-1,normal);
} else {
VectorCopy(surf->plane->normal,normal);
}
VectorMA(center,16,normal,orig);
//not to close to other lights then add it
tooClose = false;
for (i=0; i<numShadowLights; i++) {
vec3_t dist;
float length;
light = &shadowlights[i];
VectorSubtract(orig,light->origin,dist);
length = Length(dist);
if (length < (surfaceLightRadius*sh_radiusscale.value + light->radius*sh_radiusscale.value)) {
tooClose = true;
}
}
if (!tooClose) {
Q_memset(&fakeEnt,0,sizeof(entity_t));
fakeEnt.light_lev = surfaceLightRadius;
VectorCopy(orig,fakeEnt.origin);
fakeEnt.model = Mod_ForName("progs/w_light.spr",true);
R_CalcSvBsp(&fakeEnt);
Con_Printf("Added surface light");
}
}
/**
Hacked entitiy loading code, this parses the entities client side to find lights in it
**/
void LightFromFile(vec3_t orig, vec3_t color, float light_lev) {
int i;
shadowlight_t *light;
qboolean tooClose;
entity_t fakeEnt;
//not to close to other lights then add it
tooClose = false;
for (i=0; i<numShadowLights; i++) {
vec3_t dist;
float length;
light = &shadowlights[i];
VectorSubtract(orig,light->origin,dist);
length = Length(dist);
if (length < (light_lev*sh_radiusscale.value + light->radius*sh_radiusscale.value)) {
tooClose = true;
}
}
if (!tooClose) {
Q_memset(&fakeEnt,0,sizeof(entity_t));
fakeEnt.light_lev = light_lev;
VectorCopy(orig,fakeEnt.origin);
VectorCopy(color,fakeEnt.color);
fakeEnt.model = Mod_ForName("progs/w_light.spr",true);
R_CalcSvBsp(&fakeEnt);
//Con_Printf("Added file light");
}
}
void ParseVector (char *s, float *d)
{
int i;
char string[128];
char *v, *w;
strncpy (string, s,sizeof(string));
v = string;
w = string;
for (i=0 ; i<3 ; i++)
{
while (*v && *v != ' ')
v++;
*v = 0;
d[i] = atof (w);
w = v = v+1;
}
}
char *ParseEnt (char *data, qboolean *isLight, vec3_t origin, vec3_t color, float *light)
{
qboolean init;
char keyname[256];
qboolean foundworld = false;
init = false;
// go through all the dictionary pairs
while (1)
{
// parse key
data = COM_Parse (data);
if (com_token[0] == '}')
break;
if (!data)
Sys_Error ("ParseEnt: EOF without closing brace");
strncpy (keyname, com_token,sizeof(keyname));
// parse value
data = COM_Parse (data);
if (!data)
Sys_Error ("ED_ParseEntity: EOF without closing brace");
if (com_token[0] == '}')
Sys_Error ("ED_ParseEntity: closing brace without data");
init = true;
if (!strcmp(keyname, "classname")) {
if (!strcmp(com_token, "light")) {
*isLight = true;
} else {
*isLight = false;
}
} else if (!strcmp(keyname, "_color")) {
ParseVector(com_token, color);
} else if (!strcmp(keyname, "origin")) {
ParseVector(com_token, origin);
} else if (!strcmp(keyname, "light")) {
*light = atof(com_token);
} else if (!strcmp(keyname, "_noautolight")) {
Con_Printf("Automatic light gen disabled\n");//XYW \n
foundworld = true;
} else if (!strcmp(keyname, "_skybox")) {
strncpy(skybox_name,com_token,sizeof(skybox_name));
} else if (!strcmp(keyname, "_cloudspeed")) {
skybox_cloudspeed = atof(com_token);
} else if (!strcmp(keyname, "_lightmapbright")) {
Cvar_Set("sh_lightmapbright",com_token);
Con_Printf("Lightmap brightness set to %f\n",sh_lightmapbright.value);
} else if (!strcmp(keyname, "_fog_color")) {
ParseVector(com_token, origin);
Cvar_SetValue("fog_r",origin[0]);
Cvar_SetValue("fog_g",origin[1]);
Cvar_SetValue("fog_b",origin[2]);
} else if (!strcmp(keyname, "_fog_start")) {
Cvar_Set("fog_start",com_token);
} else if (!strcmp(keyname, "_fog_end")) {
Cvar_Set("fog_end",com_token);
} else {
//just do nothing
}
}
if (foundworld) return NULL;
return data;
}
void LoadLightsFromFile (char *data)
{
qboolean isLight;
vec3_t origin, color;
float light;
Cvar_SetValue ("fog_start",0.0);
Cvar_SetValue ("fog_end",0.0);
color[0] = 1;
color[1] = 1;
color[2] = 1;
light = 300;
// parse ents
while (1)
{
// parse the opening brace
data = COM_Parse (data);
if (!data)
break;
if (com_token[0] != '{')
Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token);
isLight = false;
data = ParseEnt (data, &isLight, origin, color, &light);
if (isLight) {
LightFromFile(origin, color, light);
//Con_Printf("found light in file");
}
}
if ((!fog_start.value) && (!fog_end.value)) {
Cvar_SetValue ("gl_fog",0.0);
}
}
void R_AutomaticLightPos() {
/*
for (i=0; i<m->numsurfaces; i++) {
surf = &m->surfaces[i];
if (strstr(surf->texinfo->texture->name,"light")) {
LightFromSurface(surf);
}
}*/
LoadLightsFromFile(cl.worldmodel->entities);
}