tenebrae2/gl_shadow.c
2003-06-29 21:38:58 +00:00

2747 lines
63 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];
extern aabox_t worldbox;
/* -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];
}
void DrawLightVolumeInfo(void) {
int i;
aabox_t b;
if (!sh_showlightvolume.value) return;
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glColor3f(0.0,0.0,1.0);
for (i=0; i<numUsedShadowLights; i++) {
shadowlight_t *l = usedshadowlights[i];
if (!l->shadowchainfilled) continue;
b = intersectBoxes(&l->box, &worldbox);
drawBoxWireframe(&b);
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
}
void BoxForRadius(shadowlight_t *l) {
l->box.mins[0] = l->origin[0] - l->radius;
l->box.mins[1] = l->origin[1] - l->radius;
l->box.mins[2] = l->origin[2] - l->radius;
l->box.maxs[0] = l->origin[0] + l->radius;
l->box.maxs[1] = l->origin[1] + l->radius;
l->box.maxs[2] = l->origin[2] + l->radius;
l->radiusv[0] = l->radius;
l->radiusv[1] = l->radius;
l->radiusv[2] = l->radius;
//l->filtercube = 0;
}
/*
=============
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;
}
BoxForRadius(l);
}
/*
=============
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);
}
}
#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;
}
BoxForRadius(l);
}
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;
}
}
}
#define MAX_LEAF_LIST 32
mleaf_t *leafList[MAX_LEAF_LIST];
int numLeafList;
extern vec3_t r_emins, r_emaxs; // <AWE> added "extern".
/*
=============
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=32 ; i>=0 ; i--)
{
a = i/32.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;
}
*/
/*
=============
R_ProjectBoundingBox
Returns the screen rectangle this light's bounding box will be in when it is drawn.
=============
*/
void R_ProjectBoundingBox (shadowlight_t *light, int *rect) {
vec3_t dest;
double minx, maxx, miny, maxy;
double px, py, pz;
int i, j, k;
static float verts[9*3];
matrix_4x4 modelviewproj, world, proj;
matrix_1x4 point, res;
aabox_t box;
minx = 100000000;
miny = 100000000;
maxx = -100000000;
maxy = -100000000;
box = intersectBoxes(&light->box, &worldbox);
i=0;
VectorConstruct(box.maxs[0],box.maxs[1],box.maxs[2],&verts[i]);
i+=3;
VectorConstruct(box.mins[0],box.maxs[1],box.maxs[2],&verts[i]);
i+=3;
VectorConstruct(box.maxs[0],box.mins[1],box.maxs[2],&verts[i]);
i+=3;
VectorConstruct(box.mins[0],box.mins[1],box.maxs[2],&verts[i]);
i+=3;
VectorConstruct(box.mins[0],box.mins[1],box.mins[2],&verts[i]);
i+=3;
VectorConstruct(box.maxs[0],box.mins[1],box.mins[2],&verts[i]);
i+=3;
VectorConstruct(box.maxs[0],box.maxs[1],box.mins[2],&verts[i]);
i+=3;
VectorConstruct(box.mins[0],box.maxs[1],box.mins[2],&verts[i]);
i+=3;
glGetFloatv (GL_MODELVIEW_MATRIX, &world[0][0]);
// glMatrixMode(GL_PROJECTION);
// glPushMatrix();
//gluPerspective(90, r_Iviewport[0]/ (float)r_Iviewport[1], 0.1, 2000.0);
glGetFloatv (GL_PROJECTION_MATRIX, &proj[0][0]);
// glGetDoublev (GL_PROJECTION_MATRIX, &r_Dproject_matrix[0]);
Mat_Mul_4x4_4x4(world, proj, modelviewproj);
for (i=0; i<8; i++) {
//float s;
//float *v = &verts[i*3];
point[0] = verts[i*3+0];
point[1] = verts[i*3+1];
point[2] = verts[i*3+2];
point[3] = 1.0f;
Mat_Mul_1x4_4x4(point, modelviewproj, res);
//gluProject(v[0], v[1], v[2], r_Dworld_matrix, r_Dproject_matrix,
// (GLint *) r_Iviewport, &px, &py, &pz);
//if (res[3] < 0) {
//Con_Printf("%f %f\n", (float)px, (float)py);
//s = (pz < 0) ? -1 : 1;
px = (res[0]*(1/res[3])+1.0) * 0.5;
py = (res[1]*(1/res[3])+1.0) * 0.5;
px = px * r_Iviewport[2] + r_Iviewport[0];
py = py * r_Iviewport[3] + r_Iviewport[1];
//Con_Printf("%f %f\n", (float)px, (float)py);
//continue;
//}
/*
if (px < r_Iviewport[0]) px = r_Iviewport[0];
if (py < r_Iviewport[1]) py = r_Iviewport[1];
if (px > r_Iviewport[0]+r_Iviewport[2]) px = r_Iviewport[0]+r_Iviewport[2];
if (py > r_Iviewport[1]+r_Iviewport[3]) py = r_Iviewport[1]+r_Iviewport[3];
*/
if (px > maxx) maxx = px;
if (px < minx) minx = px;
if (py > maxy) maxy = py;
if (py < miny) miny = py;
}
/* glPopMatrix();
glGetDoublev (GL_PROJECTION_MATRIX, &r_Dproject_matrix[0]);
glMatrixMode(GL_MODELVIEW);*/
/*
for (i=-1; i<=1; i+=2) {
for (j=-1; j<=1; j+=2) {
for (k=-1; k<=1; k+=2) {
MakeVertex(i*light->radius, j*light->radius, k*light->radius, light->origin, dest);
Con_Printf("%f %f %f\n",dest[0],dest[1],dest[2]);
gluProject(dest[0], dest[1], dest[2], r_Dworld_matrix, r_Dproject_matrix,
(GLint *) r_Iviewport, &px, &py, &pz);
if (px > maxx) maxx = px;
if (px < minx) minx = px;
if (py > maxy) maxy = py;
if (py < miny) miny = py;
}
}
}
*/
/*
if (minx < (double)r_Iviewport[0]) minx = (double)r_Iviewport[0];
if (miny < (double)r_Iviewport[1]) miny = (double)r_Iviewport[1];
if (maxx > (double)r_Iviewport[0]+r_Iviewport[2]) maxx = (double)r_Iviewport[0]+r_Iviewport[2];
if (maxy > (double)r_Iviewport[1]+r_Iviewport[3]) maxy = (double)r_Iviewport[1]+r_Iviewport[3];
*/
rect[0] = (int)minx;
rect[1] = (int)miny;
rect[2] = (int)maxx;
rect[3] = (int)maxy;
}
/*
=============
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;
}
/*
=============
HasVisibleClusters
Returns true if the light has leaves shared with the clusters in v2.
=============
*/
qboolean HasVisibleClusters(shadowlight_t *l, byte *v2) {
int i;
for (i=0 ; i<cl.worldmodel->numleafs; i++)
{
int cluster = cl.worldmodel->leafs[i].cluster;
//leaf is visible from the light?
if (l->leafvis[i>>3] & (1<<(i&7)))
{
//cluster is visible from the camera?
if (v2[cluster>>3] & (1<<(cluster&7)))
return true;
}
}
return false;
}
/**************************************************************
Code to determine what stuff is visible to the light.
***************************************************************/
/*
=============
CheckSurfInLight
Returns true if the surface is lit by the light. (Of course approximately)
=============
*/
qboolean CheckSurfInLight(msurface_t *surf, shadowlight_t *light)
{
mplane_t *plane;
float dist;
glpoly_t *poly;
//Doesn't recieve per pixel light or cast shadows...
if (!(surf->flags & SURF_PPLIGHT) && (surf->flags & SURF_NOSHADOW)) {
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;
}
//Doesn't intersect light volume
if (light->isStatic)
{
vec3_t mins = {10e10,10e10,10e10};
vec3_t maxs = {-10e10,-10e10,-10e10};
int i;
for (i=0; i<poly->numindecies; i++) {
VectorMax(maxs,globalVertexTable[poly->indecies[i]].position,maxs);
VectorMin(mins,globalVertexTable[poly->indecies[i]].position,mins);
}
if (!intersectsMinsMaxs(&light->box, mins, maxs)) {
return false;
}
}
poly->lightTimestamp = r_lightTimestamp;
return true;
}
qboolean CheckMeshInLight(mesh_t *mesh, shadowlight_t *light) {
//Doesn't recieve per pixel light or cast shadows...
if (!(mesh->shader->shader->flags & SURF_PPLIGHT) && (mesh->shader->shader->flags & SURF_NOSHADOW)) {
return false;
}
//Already flagged
if (mesh->lightTimestamp == r_lightTimestamp) {
return false;
}
//Doesn't intersect light volume
if (!intersectsMinsMaxs(&light->box, mesh->mins, mesh->maxs)) {
return false;
}
mesh->lightTimestamp = r_lightTimestamp;
return true;
}
/*
=============
R_MarkLightSurfaces
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_MarkLightSurfaces (shadowlight_t *light, mnode_t *node)
{
mplane_t *plane;
float dist;
msurface_t **surf;
mleaf_t *leaf;
int c,leafindex;
mesh_t *mesh;
//Not in the current light's bouding box
if (!intersectsMinsMaxs(&light->box,node->minmaxs,node->minmaxs+3))
return;
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 (CheckSurfInLight ((*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 (CheckMeshInLight(mesh, light)) {
mesh->shadowchain = meshshadowchain;
meshshadowchain = mesh;
}
}
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_MarkLightSurfaces (light, node->children[0]);
R_MarkLightSurfaces (light, node->children[1]);
}
/*
=============
CheckEntityInLight
Checks if Entity is in the light's volume and visible to the light (using vis)
=============
*/
qboolean CheckEntityInLight(entity_t *ent) {
int i, leafindex;
model_t *entmodel = ent->model;
mleaf_t *leaf;
vec3_t mins, maxs;
VectorAdd(ent->origin, entmodel->mins, mins);
VectorAdd(ent->origin, entmodel->maxs, maxs);
if (intersectsMinsMaxs(&currentshadowlight->box,mins,maxs)) {
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->entclustervis[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_MarkLightEntities
Adds shadow casting ents to the list
=============
*/
void R_MarkLightEntities() {
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 (CheckEntityInLight(currententity)) {
currententity->lightTimestamp = r_lightTimestamp;
cl_lightvisedicts[cl_numlightvisedicts] = currententity;
cl_numlightvisedicts++;
} else cut_ent++;
}
}
}
/*
=============
R_ContributeFrame
Returns true if the light should be rendered.
We try to throw away as much ligts as possible here. Most of the lights are culled here
but some are culled further down the pipeline (but rarely).
=============
*/
void boxScreenSpaceRect(aabox_t *b, int *rect);
qboolean R_ContributeFrame (shadowlight_t *light)
{
mleaf_t *lightleaf;
float dist;
aabox_t lightbox;
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) {
//Con_Printf("Culled: Weak light\n");
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;
}
*/
if (R_CullBox(light->box.mins, light->box.maxs)) {
//Con_Printf("Culled: Box outside frustum\n");
return false;
}
lightbox.mins[0] = light->origin[0] - light->radius;
lightbox.mins[1] = light->origin[1] - light->radius;
lightbox.mins[2] = light->origin[2] - light->radius;
lightbox.maxs[0] = light->origin[0] + light->radius;
lightbox.maxs[1] = light->origin[1] + light->radius;
lightbox.maxs[2] = light->origin[2] + light->radius;
//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*/!intersectsBoxPoint(&light->box, r_refdef.vieworg)) {
//R_ProjectBoundingBox (light, light->scizz.coords);
boxScreenSpaceRect(&light->box, 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->leafvis[0];
} else {
lightleaf = Mod_PointInLeaf (light->origin, cl.worldmodel);
lightvis = Mod_LeafPVS (lightleaf, cl.worldmodel);
Q_memcpy(&light->leafvis,lightvis,MAX_MAP_LEAFS/8);
Q_memcpy(&light->entclustervis, lightvis, MAX_MAP_LEAFS/8);
light->area = lightleaf->area;
}
//not in a visible area => skip it
if (!(r_refdef.areabits[light->area>>3] & (1<<(light->area&7)))) {
//Con_Printf("Culled: Not in same area\n");
return false;
}
/*
if (!intersectsBox(&worldbox, &lightbox)) {
Con_Printf("Culled: Box outside the visible world\n");
return false;
}
*/
if (light->isStatic) {
qboolean b = HasVisibleClusters(light,&worldvis[0]);
//if (!b) Con_Printf("Culled: No visible clusters\n");
return b;
} else {
qboolean b = HasSharedLeafs(lightvis,&worldvis[0]);
//if (!b) Con_Printf("Culled: No shared leafs\n");
return b;
}
/*
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 {
Con_Printf("No shared leafs\n");
return false;
}
*/
}
/*
=============
R_FillLightChains
Returns true if the light should be rendered.
It's here lights are seldomly removed further down the pipeline.
=============
*/
qboolean R_FillLightChains (shadowlight_t *light)
{
r_lightTimestamp++;
shadowchain = NULL;
meshshadowchain = NULL;
//lightvis = &light->vis[0];
//numUsedShadowLights++;
//mark shadow casting ents
R_MarkLightEntities();
//mark shadow casting polygons
if (!light->isStatic) {
R_MarkLightSurfaces ( light, cl.worldmodel->nodes);
} else {
return true;
}
return (shadowchain || meshshadowchain) ? true : false;
}
/**************************************************************
Shadow volume calculations / drawing
***************************************************************/
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->lightCmdsMesh = &lightCmdsBuffMesh[0];
light->numlightcmdsmesh = numLightCmdsMesh;
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) {
GL_SelectTexture(GL_TEXTURE0_ARB);
glDisable(GL_TEXTURE_2D);
GL_SelectTexture(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
GL_SelectTexture(GL_TEXTURE2_ARB);
glDisable(GL_TEXTURE_2D);
GL_SelectTexture(GL_TEXTURE3_ARB);
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);
GL_SelectTexture(GL_TEXTURE0_ARB);
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++)
{
CheckSurfInLight(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;
if (surf->flags & SURF_NOSHADOW)
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;
}
*/
if (surf->shader->shader->flags & SURF_NOSHADOW)
goto noshadow;
//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;
}
noshadow:
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;
if (surf->shader->shader->flags & SURF_NOSHADOW) continue;
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();
*/
}
}
/**************************************************************
Mesh shadow volume code (Curves/Inlines)
***************************************************************/
int extrudeTimeStamp = 0;
vec3_t extrudedVerts[MAXALIASVERTS]; //PENTA: Temp buffer for extruded vertices
int extrudedTimestamp[MAXALIASVERTS]; //PENTA: Temp buffer for extruded vertices
qboolean triangleVis[MAXALIASTRIS*2]; //PENTA: Temp buffer for light facingness of triangles
void SetupMeshToLightVisibility(shadowlight_t *light, mesh_t *mesh) {
float d, scale;
int i, j;
vec3_t v2, *v1;
if (mesh->numtriangles > MAXALIASTRIS*2) return;
if (mesh->numvertices > MAXALIASVERTS) return;
extrudeTimeStamp++;
//calculate visibility
for (i=0; i<mesh->numtriangles; i++) {
d = DotProduct(mesh->triplanes[i].normal, light->origin) - mesh->triplanes[i].dist;
if (d > 0)
triangleVis[i] = true;
else
triangleVis[i] = false;
}
//extude vertices
for (i=0; i<mesh->numtriangles; i++) {
if (triangleVis[i]) {//backfacing extrude it!
for (j=0; j<3; j++) {
int index = mesh->indecies[i*3+j];
if (extrudedTimestamp[index] == extrudeTimeStamp) continue;
extrudedTimestamp[index] = extrudeTimeStamp;
v1 = &extrudedVerts[index];
VectorCopy(globalVertexTable[mesh->firstvertex+index].position,v2);
VectorSubtract (v2, light->origin, (*v1));
scale = Length ((*v1));
if (sh_visiblevolumes.value) {
//make them short so that we see them
VectorScale ((*v1), (1/scale)* 70, (*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)* light->radius*10, (*v1));
}
VectorAdd ((*v1), v2 ,(*v1));
}
}
}
}
void DrawMeshVolume(mesh_t *mesh) {
int i, j;
if (mesh->numtriangles > MAXALIASTRIS*2) return;
if (mesh->numvertices > MAXALIASVERTS) return;
for (i=0; i<mesh->numtriangles; i++) {
if (triangleVis[i]) {
for (j=0; j<3; j++) {
qboolean shadow = false;
if (mesh->neighbours[i*3+j] == -1) {
shadow = true;
} else if (!triangleVis[mesh->neighbours[i*3+j]]) {
shadow = true;
}
if (shadow) {
int index = mesh->indecies[i*3+j];
glBegin(GL_QUAD_STRIP);
glVertex3fv(&globalVertexTable[mesh->firstvertex+index].position[0]);
glVertex3fv(&extrudedVerts[index][0]);
index = mesh->indecies[i*3+(j+1)%3];
glVertex3fv(&globalVertexTable[mesh->firstvertex+index].position[0]);
glVertex3fv(&extrudedVerts[index][0]);
glEnd();
}
}
/* }
}
glBegin(GL_TRIANGLES);
for (i=0; i<mesh->numtriangles; i++) {
if (triangleVis[i]) {
*/
glBegin(GL_TRIANGLES);
for (j=0; j<3; j++) {
int index = mesh->indecies[i*3+j];
glVertex3fv(&globalVertexTable[mesh->firstvertex+index].position[0]);
}
glEnd();
glBegin(GL_TRIANGLES);
for (j=2; j>=0; j--) {
int index = mesh->indecies[i*3+j];
glVertex3fv(&extrudedVerts[index][0]);
}
glEnd();
}
}
//glEnd();
}
void StencilMeshVolume(mesh_t *mesh) {
if (mesh->shader->shader->flags & SURF_NOSHADOW) return;
SetupMeshToLightVisibility(currentshadowlight, mesh);
//
//Pass 1 increase
//
glCullFace(GL_BACK);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
//glCullFace(GL_FRONT);
DrawMeshVolume(mesh);
//
// Second Pass. Decrease Stencil Value In The Shadow
//
glCullFace(GL_FRONT);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
//glCullFace(GL_BACK);
DrawMeshVolume(mesh);
glCullFace(GL_BACK);
}
void StencilMeshVolumes() {
int i;
for (i=0; i<currentshadowlight->numlightcmdsmesh-1; i++) {
StencilMeshVolume((mesh_t *)currentshadowlight->lightCmdsMesh[i].asVoid);
}
}
/**************************************************************
Stuff that should be elsewhere
***************************************************************/
/*
=============
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;
aabox_t leafbox;
aabox_t visbox = emptyBox();
if (sh_norevis.value) return;
Q_memset(currentshadowlight->leafvis,0,MAX_MAP_LEAFS/8);
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)))
{
leafbox = constructBox(leaf->minmaxs,leaf->minmaxs+3);
if (!intersectsBox(&currentshadowlight->box,&leafbox)) continue;
visbox = addBoxes(&visbox,&leafbox);
//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) {
currentshadowlight->leafvis[i>>3] |= (1<<(i&7));
removed++;
}
/*
if (!found) {
//set vis bit on false
vis[i>>3] &= ~(1<<(i&7));
removed++;
}
*/
}
}
leafbox = intersectBoxes(&currentshadowlight->box,&visbox);
currentshadowlight->box = leafbox;
//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_MarkLightSurfaces (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_StaticLightFromEnt
Spawn a static lights for the given entity.
================
*/
void R_StaticLightFromEnt(entity_t *ent) {
int i;
msurface_t *surf;
msurface_t *s;
mapshader_t *t;
//Con_Printf("Shadow volumes start\n");
if (!ent->model) {
Con_Printf("Light with null model");
return;
}
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_StaticLightFromEnt: More than MAXSHADOWLIGHTS lights");
return;
}
currentshadowlight = &shadowlights[numShadowLights-1];
currentshadowlight->isStatic = true;
//Get origin / box from the model
VectorAdd(ent->model->mins, ent->model-> maxs, currentshadowlight->origin);
VectorScale(currentshadowlight->origin, 0.5f, currentshadowlight->origin);
VectorCopy(ent->model->mins, currentshadowlight->box.mins);
VectorCopy(ent->model->maxs, currentshadowlight->box.maxs);
VectorSubtract(ent->model->maxs,ent->model->mins,currentshadowlight->radiusv);
VectorScale(currentshadowlight->radiusv, 0.5f, currentshadowlight->radiusv);
currentshadowlight->radius = max(Length(ent->model->mins),Length(ent->model->maxs));
//Shader to use on light == shader on the ent's model
if ((ent->model->type == mod_brush) && (ent->model->nummodelbrushes)) {
model_t *mod = ent->model;
mbrush_t *b = &mod->brushes[mod->firstmodelbrush];
mbrushside_t *bs = &mod->brushsides[b->firstbrushside];
currentshadowlight->shader = bs->shader->shader;
} else {
currentshadowlight->shader = GL_ShaderForName("textures/lights/default");
}
//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->entclustervis[0], lightvis, MAX_MAP_LEAFS/8);
CutLeafs(lightvis);
//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;
}
/**************************************************************
Client side light entity loading code
***************************************************************/
/**
Hacked entitiy loading code, this parses the entities client side to find lights in it
**/
void LightFromFile(entity_t *fakeEnt)
{
R_StaticLightFromEnt(fakeEnt);
}
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, entity_t *ent)
{
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, "rt_light")) {
*isLight = true;
} else {
*isLight = false;
}
} else if (!strcmp(keyname, "_color")) {
ParseVector(com_token, ent->color);
} else if (!strcmp(keyname, "origin")) {
ParseVector(com_token, ent->origin);
} else if (!strcmp(keyname, "light")) {
ent->light_lev = atof(com_token);
} else if (!strcmp(keyname, "model")) {
ent->model = Mod_ForName(com_token, true);
} else if (!strcmp(keyname, "skin")) {
ent->skinnum = atoi(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")) {
vec3_t temp;
ParseVector(com_token, temp);
Cvar_SetValue("fog_r",temp[0]);
Cvar_SetValue("fog_g",temp[1]);
Cvar_SetValue("fog_b",temp[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;
entity_t fakeEnt;
Cvar_SetValue ("fog_start",0.0);
Cvar_SetValue ("fog_end",0.0);
// 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;
Q_memset(&fakeEnt,0,sizeof(entity_t));
data = ParseEnt (data, &isLight, &fakeEnt);
if (isLight) {
LightFromFile(&fakeEnt);
//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);
}