mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-29 23:22:01 +00:00
d039841d21
git-svn-id: https://svn.code.sf.net/p/fteqw/code/branches/wip@3812 fc73d0e0-1445-4013-8a0c-d673dee63da5
2137 lines
53 KiB
C
2137 lines
53 KiB
C
#include "quakedef.h"
|
||
|
||
#ifdef GLQUAKE
|
||
#ifdef RTLIGHTS
|
||
|
||
#include "glquake.h"
|
||
#include "shader.h"
|
||
|
||
#define SHADOWMAP_SIZE 512
|
||
|
||
#define nearplane (16)
|
||
|
||
static int shadow_fbo_id;
|
||
|
||
static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour);
|
||
|
||
|
||
struct {
|
||
int numlights;
|
||
int shadowsurfcount;
|
||
|
||
int numfrustumculled;
|
||
int numpvsculled;
|
||
int numscissorculled;
|
||
} bench;
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
typedef struct {
|
||
unsigned int count;
|
||
unsigned int max;
|
||
mesh_t **s;
|
||
} shadowmeshsurfs_t;
|
||
typedef struct shadowmesh_s {
|
||
unsigned int numindicies;
|
||
unsigned int maxindicies;
|
||
index_t *indicies;
|
||
|
||
unsigned int numverts;
|
||
unsigned int maxverts;
|
||
vecV_t *verts;
|
||
|
||
//we also have a list of all the surfaces that this light lights.
|
||
unsigned int numsurftextures;
|
||
shadowmeshsurfs_t *litsurfs;
|
||
|
||
unsigned int leafbytes;
|
||
unsigned char *litleaves;
|
||
} shadowmesh_t;
|
||
|
||
/*state of the current shadow mesh*/
|
||
#define inc 128
|
||
int sh_shadowframe;
|
||
static int sh_firstindex;
|
||
static int sh_vertnum; //vertex number (set to 0 at SH_Begin)
|
||
static shadowmesh_t *sh_shmesh, sh_tempshmesh;
|
||
|
||
/* functions to add geometry to the shadow mesh */
|
||
static void SHM_BeginQuads (void)
|
||
{
|
||
sh_firstindex = sh_shmesh->numverts;
|
||
}
|
||
static void SHM_End (void)
|
||
{
|
||
int i;
|
||
i = (sh_shmesh->numindicies+(sh_vertnum/4)*6+inc+5)&~(inc-1); //and a bit of padding
|
||
if (sh_shmesh->maxindicies != i)
|
||
{
|
||
sh_shmesh->maxindicies = i;
|
||
sh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, i * sizeof(*sh_shmesh->indicies));
|
||
}
|
||
//add the extra triangles
|
||
for (i = 0; i < sh_vertnum; i+=4)
|
||
{
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+0;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+1;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+2;
|
||
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+0;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+2;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+3;
|
||
}
|
||
sh_vertnum = 0;
|
||
}
|
||
static void SHM_Vertex3fv (const GLfloat *v)
|
||
{
|
||
int i;
|
||
|
||
//add the verts as we go
|
||
i = (sh_shmesh->numverts+inc+5)&~(inc-1); //and a bit of padding
|
||
if (sh_shmesh->maxverts < i)
|
||
{
|
||
sh_shmesh->maxverts = i;
|
||
sh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, i * sizeof(*sh_shmesh->verts));
|
||
}
|
||
|
||
sh_shmesh->verts[sh_shmesh->numverts][0] = v[0];
|
||
sh_shmesh->verts[sh_shmesh->numverts][1] = v[1];
|
||
sh_shmesh->verts[sh_shmesh->numverts][2] = v[2];
|
||
|
||
sh_vertnum++;
|
||
sh_shmesh->numverts++;
|
||
|
||
|
||
if (sh_vertnum == 4)
|
||
{
|
||
SHM_End();
|
||
sh_firstindex = sh_shmesh->numverts;
|
||
}
|
||
}
|
||
|
||
void SHM_TriangleFan(int numverts, vecV_t *verts, vec3_t lightorg, float pd)
|
||
{
|
||
int v, i, idxs;
|
||
float *v1;
|
||
vec3_t v3;
|
||
vecV_t *outv;
|
||
index_t *outi;
|
||
|
||
/*make sure there's space*/
|
||
v = (sh_shmesh->numverts+numverts*2 + inc)&~(inc-1); //and a bit of padding
|
||
if (sh_shmesh->maxverts < v)
|
||
{
|
||
sh_shmesh->maxverts = v;
|
||
sh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, v * sizeof(*sh_shmesh->verts));
|
||
}
|
||
outv = sh_shmesh->verts + sh_shmesh->numverts;
|
||
|
||
for (v = 0; v < numverts; v++)
|
||
{
|
||
v1 = verts[v];
|
||
VectorCopy(v1, outv[v]);
|
||
|
||
v3[0] = ( v1[0]-lightorg[0] )*pd;
|
||
v3[1] = ( v1[1]-lightorg[1] )*pd;
|
||
v3[2] = ( v1[2]-lightorg[2] )*pd;
|
||
|
||
outv[v+numverts][0] = v1[0]+v3[0];
|
||
outv[v+numverts][1] = v1[1]+v3[1];
|
||
outv[v+numverts][2] = v1[2]+v3[2];
|
||
}
|
||
|
||
idxs = (numverts-2)*3;
|
||
/*now add the verts in a fan*/
|
||
v = (sh_shmesh->numindicies+idxs*2+inc)&~(inc-1); //and a bit of padding
|
||
if (sh_shmesh->maxindicies < v)
|
||
{
|
||
sh_shmesh->maxindicies = v;
|
||
sh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, v * sizeof(*sh_shmesh->indicies));
|
||
}
|
||
outi = sh_shmesh->indicies + sh_shmesh->numindicies;
|
||
|
||
for (v = 2, i = 0; v < numverts; v++, i+=3)
|
||
{
|
||
outi[i+0] = sh_shmesh->numverts;
|
||
outi[i+1] = sh_shmesh->numverts+v-1;
|
||
outi[i+2] = sh_shmesh->numverts+v;
|
||
|
||
outi[i+0+idxs] = sh_shmesh->numverts+numverts+v;
|
||
outi[i+1+idxs] = sh_shmesh->numverts+numverts+v-1;
|
||
outi[i+2+idxs] = sh_shmesh->numverts+numverts;
|
||
}
|
||
|
||
/*we added this many*/
|
||
sh_shmesh->numverts += numverts*2;
|
||
sh_shmesh->numindicies += i*2;
|
||
}
|
||
|
||
static void SHM_Shadow_Cache_Surface(msurface_t *surf)
|
||
{
|
||
int i;
|
||
|
||
i = surf->texinfo->texture->wtexno;
|
||
if (i < 0)
|
||
return;
|
||
|
||
if (sh_shmesh->litsurfs[i].count == sh_shmesh->litsurfs[i].max)
|
||
{
|
||
sh_shmesh->litsurfs[i].max += 64;
|
||
sh_shmesh->litsurfs[i].s = BZ_Realloc(sh_shmesh->litsurfs[i].s, sizeof(void*)*(sh_shmesh->litsurfs[i].max));
|
||
}
|
||
sh_shmesh->litsurfs[i].s[sh_shmesh->litsurfs[i].count] = surf->mesh;
|
||
sh_shmesh->litsurfs[i].count++;
|
||
}
|
||
|
||
static void SHM_Shadow_Cache_Leaf(mleaf_t *leaf)
|
||
{
|
||
int i;
|
||
|
||
i = leaf - cl.worldmodel->leafs;
|
||
sh_shmesh->litleaves[i>>3] |= 1<<(i&7);
|
||
}
|
||
|
||
void SH_FreeShadowMesh(shadowmesh_t *sm)
|
||
{
|
||
unsigned int i;
|
||
for (i = 0; i < sm->numsurftextures; i++)
|
||
Z_Free(sm->litsurfs[i].s);
|
||
Z_Free(sm->litsurfs);
|
||
Z_Free(sm->indicies);
|
||
Z_Free(sm->verts);
|
||
Z_Free(sm);
|
||
}
|
||
|
||
static void SHM_BeginShadowMesh(dlight_t *dl)
|
||
{
|
||
unsigned int i;
|
||
unsigned int lb;
|
||
sh_vertnum = 0;
|
||
|
||
lb = (cl.worldmodel->numleafs+7)/8;
|
||
if (!dl->die || !dl->key)
|
||
{
|
||
sh_shmesh = dl->worldshadowmesh;
|
||
if (!sh_shmesh || sh_shmesh->leafbytes != lb)
|
||
{
|
||
/*this shouldn't happen too often*/
|
||
if (sh_shmesh)
|
||
{
|
||
SH_FreeShadowMesh(sh_shmesh);
|
||
}
|
||
|
||
/*Create a new shadowmesh for this light*/
|
||
sh_shmesh = Z_Malloc(sizeof(*sh_shmesh) + lb);
|
||
sh_shmesh->leafbytes = lb;
|
||
sh_shmesh->litleaves = (unsigned char*)(sh_shmesh+1);
|
||
|
||
dl->worldshadowmesh = sh_shmesh;
|
||
}
|
||
dl->rebuildcache = false;
|
||
}
|
||
else
|
||
{
|
||
sh_shmesh = &sh_tempshmesh;
|
||
if (sh_shmesh->leafbytes != lb)
|
||
{
|
||
/*this happens on map changes*/
|
||
sh_shmesh->leafbytes = lb;
|
||
Z_Free(sh_shmesh->litleaves);
|
||
sh_shmesh->litleaves = Z_Malloc(lb);
|
||
}
|
||
}
|
||
sh_shmesh->maxverts = 0;
|
||
sh_shmesh->numverts = 0;
|
||
sh_shmesh->maxindicies = 0;
|
||
sh_shmesh->numindicies = 0;
|
||
|
||
if (sh_shmesh->numsurftextures != cl.worldmodel->numtextures)
|
||
{
|
||
if (sh_shmesh->litsurfs)
|
||
{
|
||
for (i = 0; i < sh_shmesh->numsurftextures; i++)
|
||
Z_Free(sh_shmesh->litsurfs[i].s);
|
||
Z_Free(sh_shmesh->litsurfs);
|
||
}
|
||
sh_shmesh->litsurfs = Z_Malloc(sizeof(shadowmeshsurfs_t)*cl.worldmodel->numtextures);
|
||
sh_shmesh->numsurftextures=cl.worldmodel->numtextures;
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < sh_shmesh->numsurftextures; i++)
|
||
sh_shmesh->litsurfs[i].count = 0;
|
||
}
|
||
}
|
||
static struct shadowmesh_s *SHM_FinishShadowMesh(dlight_t *dl)
|
||
{
|
||
return sh_shmesh;
|
||
}
|
||
|
||
|
||
/*state of the world that is still to compile*/
|
||
static struct {
|
||
short count;
|
||
short count2;
|
||
int next;
|
||
int prev;
|
||
} edge[MAX_MAP_EDGES];
|
||
static int firstedge;
|
||
|
||
static void SHM_RecursiveWorldNodeQ1_r (dlight_t *dl, mnode_t *node)
|
||
{
|
||
int c, side;
|
||
mplane_t *plane;
|
||
msurface_t *surf, **mark;
|
||
mleaf_t *pleaf;
|
||
double dot;
|
||
int v;
|
||
|
||
float l, maxdist;
|
||
int j, s, t;
|
||
vec3_t impact;
|
||
|
||
if (node->shadowframe != sh_shadowframe)
|
||
return;
|
||
|
||
if (node->contents == Q1CONTENTS_SOLID)
|
||
return; // solid
|
||
|
||
|
||
//if light areabox is outside node, ignore node + children
|
||
for (c = 0; c < 3; c++)
|
||
{
|
||
if (dl->origin[c] + dl->radius < node->minmaxs[c])
|
||
return;
|
||
if (dl->origin[c] - dl->radius > node->minmaxs[3+c])
|
||
return;
|
||
}
|
||
|
||
// if a leaf node, draw stuff
|
||
if (node->contents < 0)
|
||
{
|
||
pleaf = (mleaf_t *)node;
|
||
SHM_Shadow_Cache_Leaf(pleaf);
|
||
|
||
mark = pleaf->firstmarksurface;
|
||
c = pleaf->nummarksurfaces;
|
||
|
||
if (c)
|
||
{
|
||
do
|
||
{
|
||
(*mark++)->shadowframe = sh_shadowframe;
|
||
} while (--c);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// node is just a decision point, so go down the apropriate sides
|
||
|
||
// find which side of the node we are on
|
||
plane = node->plane;
|
||
|
||
switch (plane->type)
|
||
{
|
||
case PLANE_X:
|
||
dot = dl->origin[0] - plane->dist;
|
||
break;
|
||
case PLANE_Y:
|
||
dot = dl->origin[1] - plane->dist;
|
||
break;
|
||
case PLANE_Z:
|
||
dot = dl->origin[2] - plane->dist;
|
||
break;
|
||
default:
|
||
dot = DotProduct (dl->origin, plane->normal) - plane->dist;
|
||
break;
|
||
}
|
||
|
||
if (dot >= 0)
|
||
side = 0;
|
||
else
|
||
side = 1;
|
||
|
||
// recurse down the children, front side first
|
||
SHM_RecursiveWorldNodeQ1_r (dl, node->children[side]);
|
||
|
||
// draw stuff
|
||
c = node->numsurfaces;
|
||
|
||
if (c)
|
||
{
|
||
surf = cl.worldmodel->surfaces + node->firstsurface;
|
||
|
||
{
|
||
|
||
maxdist = dl->radius*dl->radius;
|
||
|
||
for ( ; c ; c--, surf++)
|
||
{
|
||
if (surf->shadowframe != sh_shadowframe)
|
||
continue;
|
||
|
||
// if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))
|
||
// continue; // wrong side
|
||
|
||
// if (surf->flags & SURF_PLANEBACK)
|
||
// continue;
|
||
|
||
if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
|
||
{ // no shadows
|
||
continue;
|
||
}
|
||
|
||
//is the light on the right side?
|
||
if (surf->flags & SURF_PLANEBACK)
|
||
{//inverted normal.
|
||
if (-DotProduct(surf->plane->normal, dl->origin)+surf->plane->dist >= dl->radius)
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (DotProduct(surf->plane->normal, dl->origin)-surf->plane->dist >= dl->radius)
|
||
continue;
|
||
}
|
||
|
||
//Yeah, you can blame LordHavoc for this alternate code here.
|
||
for (j=0 ; j<3 ; j++)
|
||
impact[j] = dl->origin[j] - surf->plane->normal[j]*dot;
|
||
|
||
// clamp center of light to corner and check brightness
|
||
l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
|
||
s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];
|
||
s = l - s;
|
||
l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
|
||
t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];
|
||
t = l - t;
|
||
// compare to minimum light
|
||
if ((s*s+t*t+dot*dot) < maxdist)
|
||
{
|
||
SHM_Shadow_Cache_Surface(surf);
|
||
|
||
#define PROJECTION_DISTANCE (float)(dl->radius*2)//0x7fffffff
|
||
|
||
//build a list of the edges that are to be drawn.
|
||
for (v = 0; v < surf->numedges; v++)
|
||
{
|
||
int e, delta;
|
||
e = cl.worldmodel->surfedges[surf->firstedge+v];
|
||
//negative edge means backwards edge.
|
||
if (e < 0)
|
||
{
|
||
e=-e;
|
||
delta = -1;
|
||
}
|
||
else
|
||
{
|
||
delta = 1;
|
||
}
|
||
|
||
if (!edge[e].count)
|
||
{
|
||
if (firstedge)
|
||
edge[firstedge].prev = e;
|
||
edge[e].next = firstedge;
|
||
edge[e].prev = 0;
|
||
firstedge = e;
|
||
edge[e].count = delta;
|
||
}
|
||
else
|
||
{
|
||
edge[e].count += delta;
|
||
|
||
if (!edge[e].count) //unlink
|
||
{
|
||
if (edge[e].next)
|
||
{
|
||
edge[edge[e].next].prev = edge[e].prev;
|
||
}
|
||
if (edge[e].prev)
|
||
edge[edge[e].prev].next = edge[e].next;
|
||
else
|
||
firstedge = edge[e].next;
|
||
}
|
||
}
|
||
}
|
||
|
||
SHM_TriangleFan(surf->mesh->numvertexes, surf->mesh->xyz_array, dl->origin, PROJECTION_DISTANCE);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// recurse down the back side
|
||
SHM_RecursiveWorldNodeQ1_r (dl, node->children[!side]);
|
||
}
|
||
|
||
static void SHM_RecursiveWorldNodeQ2_r (dlight_t *dl, mnode_t *node)
|
||
{
|
||
int c, side;
|
||
mplane_t *plane;
|
||
msurface_t *surf, **mark;
|
||
mleaf_t *pleaf;
|
||
double dot;
|
||
int v;
|
||
|
||
float l, maxdist;
|
||
int j, s, t;
|
||
vec3_t impact;
|
||
|
||
if (node->shadowframe != sh_shadowframe)
|
||
return;
|
||
|
||
if (node->contents == Q2CONTENTS_SOLID)
|
||
return; // solid
|
||
|
||
|
||
//if light areabox is outside node, ignore node + children
|
||
for (c = 0; c < 3; c++)
|
||
{
|
||
if (dl->origin[c] + dl->radius < node->minmaxs[c])
|
||
return;
|
||
if (dl->origin[c] - dl->radius > node->minmaxs[3+c])
|
||
return;
|
||
}
|
||
|
||
// if a leaf node, draw stuff
|
||
if (node->contents != -1)
|
||
{
|
||
pleaf = (mleaf_t *)node;
|
||
SHM_Shadow_Cache_Leaf(pleaf);
|
||
|
||
mark = pleaf->firstmarksurface;
|
||
c = pleaf->nummarksurfaces;
|
||
|
||
if (c)
|
||
{
|
||
do
|
||
{
|
||
(*mark++)->shadowframe = sh_shadowframe;
|
||
} while (--c);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// node is just a decision point, so go down the apropriate sides
|
||
|
||
// find which side of the node we are on
|
||
plane = node->plane;
|
||
|
||
switch (plane->type)
|
||
{
|
||
case PLANE_X:
|
||
dot = dl->origin[0] - plane->dist;
|
||
break;
|
||
case PLANE_Y:
|
||
dot = dl->origin[1] - plane->dist;
|
||
break;
|
||
case PLANE_Z:
|
||
dot = dl->origin[2] - plane->dist;
|
||
break;
|
||
default:
|
||
dot = DotProduct (dl->origin, plane->normal) - plane->dist;
|
||
break;
|
||
}
|
||
|
||
if (dot >= 0)
|
||
side = 0;
|
||
else
|
||
side = 1;
|
||
|
||
// recurse down the children, front side first
|
||
SHM_RecursiveWorldNodeQ2_r (dl, node->children[side]);
|
||
|
||
// draw stuff
|
||
c = node->numsurfaces;
|
||
|
||
if (c)
|
||
{
|
||
surf = cl.worldmodel->surfaces + node->firstsurface;
|
||
|
||
{
|
||
|
||
maxdist = dl->radius*dl->radius;
|
||
|
||
for ( ; c ; c--, surf++)
|
||
{
|
||
if (surf->shadowframe != sh_shadowframe)
|
||
continue;
|
||
|
||
// if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))
|
||
// continue; // wrong side
|
||
|
||
// if (surf->flags & SURF_PLANEBACK)
|
||
// continue;
|
||
|
||
if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
|
||
{ // no shadows
|
||
continue;
|
||
}
|
||
|
||
//is the light on the right side?
|
||
if (surf->flags & SURF_PLANEBACK)
|
||
{//inverted normal.
|
||
if (-DotProduct(surf->plane->normal, dl->origin)+surf->plane->dist >= dl->radius)
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (DotProduct(surf->plane->normal, dl->origin)-surf->plane->dist >= dl->radius)
|
||
continue;
|
||
}
|
||
|
||
//Yeah, you can blame LordHavoc for this alternate code here.
|
||
for (j=0 ; j<3 ; j++)
|
||
impact[j] = dl->origin[j] - surf->plane->normal[j]*dot;
|
||
|
||
// clamp center of light to corner and check brightness
|
||
l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
|
||
s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];
|
||
s = l - s;
|
||
l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
|
||
t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];
|
||
t = l - t;
|
||
// compare to minimum light
|
||
if ((s*s+t*t+dot*dot) < maxdist)
|
||
{
|
||
SHM_Shadow_Cache_Surface(surf);
|
||
|
||
|
||
|
||
#define PROJECTION_DISTANCE (float)(dl->radius*2)//0x7fffffff
|
||
|
||
//build a list of the edges that are to be drawn.
|
||
for (v = 0; v < surf->numedges; v++)
|
||
{
|
||
int e, delta;
|
||
e = cl.worldmodel->surfedges[surf->firstedge+v];
|
||
//negative edge means backwards edge.
|
||
if (e < 0)
|
||
{
|
||
e=-e;
|
||
delta = -1;
|
||
}
|
||
else
|
||
{
|
||
delta = 1;
|
||
}
|
||
|
||
if (!edge[e].count)
|
||
{
|
||
if (firstedge)
|
||
edge[firstedge].prev = e;
|
||
edge[e].next = firstedge;
|
||
edge[e].prev = 0;
|
||
firstedge = e;
|
||
edge[e].count = delta;
|
||
}
|
||
else
|
||
{
|
||
edge[e].count += delta;
|
||
|
||
if (!edge[e].count) //unlink
|
||
{
|
||
if (edge[e].next)
|
||
{
|
||
edge[edge[e].next].prev = edge[e].prev;
|
||
}
|
||
if (edge[e].prev)
|
||
edge[edge[e].prev].next = edge[e].next;
|
||
else
|
||
firstedge = edge[e].next;
|
||
}
|
||
}
|
||
}
|
||
|
||
SHM_TriangleFan(surf->mesh->numvertexes, surf->mesh->xyz_array, dl->origin, PROJECTION_DISTANCE);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// recurse down the back side
|
||
SHM_RecursiveWorldNodeQ2_r (dl, node->children[!side]);
|
||
}
|
||
|
||
#ifdef Q2BSPS
|
||
static void SHM_MarkLeavesQ2(dlight_t *dl, unsigned char *lvis, unsigned char *vvis)
|
||
{
|
||
mnode_t *node;
|
||
int i;
|
||
mleaf_t *leaf;
|
||
int cluster;
|
||
sh_shadowframe++;
|
||
|
||
if (!dl->die)
|
||
{
|
||
//static
|
||
//variation on mark leaves
|
||
for (i=0,leaf=cl.worldmodel->leafs ; i<cl.worldmodel->numleafs ; i++, leaf++)
|
||
{
|
||
cluster = leaf->cluster;
|
||
if (cluster == -1)
|
||
continue;
|
||
if (lvis[cluster>>3] & (1<<(cluster&7)))// && vvis[cluster>>3] & (1<<(cluster&7)))
|
||
{
|
||
node = (mnode_t *)leaf;
|
||
do
|
||
{
|
||
if (node->shadowframe == sh_shadowframe)
|
||
break;
|
||
node->shadowframe = sh_shadowframe;
|
||
node = node->parent;
|
||
} while (node);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//dynamic lights will be discarded after this frame anyway, so only include leafs that are visible
|
||
//variation on mark leaves
|
||
for (i=0,leaf=cl.worldmodel->leafs ; i<cl.worldmodel->numleafs ; i++, leaf++)
|
||
{
|
||
cluster = leaf->cluster;
|
||
if (cluster == -1)
|
||
continue;
|
||
if (lvis[cluster>>3] & vvis[cluster>>3] & (1<<(cluster&7)))
|
||
{
|
||
node = (mnode_t *)leaf;
|
||
do
|
||
{
|
||
if (node->shadowframe == sh_shadowframe)
|
||
break;
|
||
node->shadowframe = sh_shadowframe;
|
||
node = node->parent;
|
||
} while (node);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static void SHM_MarkLeavesQ1(dlight_t *dl, unsigned char *lvis)
|
||
{
|
||
mnode_t *node;
|
||
int i;
|
||
sh_shadowframe++;
|
||
|
||
//variation on mark leaves
|
||
for (i=0 ; i<cl.worldmodel->numleafs ; i++)
|
||
{
|
||
if (lvis[i>>3] & (1<<(i&7)))
|
||
{
|
||
node = (mnode_t *)&cl.worldmodel->leafs[i+1];
|
||
do
|
||
{
|
||
if (node->shadowframe == sh_shadowframe)
|
||
break;
|
||
node->shadowframe = sh_shadowframe;
|
||
node = node->parent;
|
||
} while (node);
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef Q3BSPS
|
||
void SHM_RecursiveWorldNodeQ3_r (dlight_t *dl, mnode_t *node)
|
||
{
|
||
mplane_t *splitplane;
|
||
float dist;
|
||
msurface_t **msurf;
|
||
msurface_t *surf;
|
||
mleaf_t *leaf;
|
||
int i;
|
||
|
||
if (node->contents != -1)
|
||
{
|
||
leaf = (mleaf_t *)node;
|
||
SHM_Shadow_Cache_Leaf(leaf);
|
||
|
||
// mark the polygons
|
||
msurf = leaf->firstmarksurface;
|
||
for (i=0 ; i<leaf->nummarksurfaces ; i++, msurf++)
|
||
{
|
||
surf = *msurf;
|
||
|
||
//only check each surface once. it can appear in multiple leafs.
|
||
if (surf->shadowframe == sh_shadowframe)
|
||
continue;
|
||
surf->shadowframe = sh_shadowframe;
|
||
|
||
SHM_Shadow_Cache_Surface(surf);
|
||
}
|
||
return;
|
||
}
|
||
|
||
splitplane = node->plane;
|
||
dist = DotProduct (dl->origin, splitplane->normal) - splitplane->dist;
|
||
|
||
if (dist > dl->radius)
|
||
{
|
||
SHM_RecursiveWorldNodeQ3_r (dl, node->children[0]);
|
||
return;
|
||
}
|
||
if (dist < -dl->radius)
|
||
{
|
||
SHM_RecursiveWorldNodeQ3_r (dl, node->children[1]);
|
||
return;
|
||
}
|
||
SHM_RecursiveWorldNodeQ3_r (dl, node->children[0]);
|
||
SHM_RecursiveWorldNodeQ3_r (dl, node->children[1]);
|
||
}
|
||
#endif
|
||
|
||
static void SHM_ComposeVolume_BruteForce(dlight_t *dl)
|
||
{
|
||
shadowmeshsurfs_t *sms;
|
||
unsigned int tno;
|
||
unsigned int sno;
|
||
unsigned int vno, vno2;
|
||
unsigned int fvert, lvert;
|
||
mesh_t *sm;
|
||
for (tno = 0; tno < sh_shmesh->numsurftextures; tno++)
|
||
{
|
||
sms = &sh_shmesh->litsurfs[tno];
|
||
if (!sms->count)
|
||
continue;
|
||
for (sno = 0; sno < sms->count; sno++)
|
||
{
|
||
sm = sms->s[sno];
|
||
|
||
if (sm->istrifan)
|
||
{
|
||
//planer poly
|
||
//if ((rand()&63)!=63)
|
||
//continue;
|
||
fvert = sh_shmesh->numverts;
|
||
|
||
SHM_TriangleFan(sm->numvertexes, sm->xyz_array, dl->origin, PROJECTION_DISTANCE);
|
||
|
||
vno = (sh_shmesh->numindicies+sm->numvertexes*6); //and a bit of padding
|
||
if (sh_shmesh->maxindicies < vno)
|
||
{
|
||
sh_shmesh->maxindicies = vno;
|
||
sh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, vno * sizeof(*sh_shmesh->indicies));
|
||
}
|
||
lvert = fvert + sm->numvertexes*2-1;
|
||
for (vno = 0; vno < sm->numvertexes; vno++)
|
||
{
|
||
if (vno == sm->numvertexes-1)
|
||
vno2 = 0;
|
||
else
|
||
vno2 = vno+1;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = fvert+vno;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = lvert-vno;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = fvert+vno2;
|
||
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = lvert-vno;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = lvert-vno2;
|
||
sh_shmesh->indicies[sh_shmesh->numindicies++] = fvert+vno2;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*each triangle may or may not face the light*/
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* unsigned int numindicies;
|
||
unsigned int maxindicies;
|
||
index_t *indicies;
|
||
|
||
unsigned int numverts;
|
||
unsigned int maxverts;
|
||
vec3_t *verts;
|
||
|
||
//we also have a list of all the surfaces that this light lights.
|
||
unsigned int numsurftextures;
|
||
shadowmeshsurfs_t *litsurfs;
|
||
|
||
unsigned int leafbytes;
|
||
unsigned char *litleaves;
|
||
*/
|
||
}
|
||
|
||
static struct shadowmesh_s *SHM_BuildShadowVolumeMesh(dlight_t *dl, unsigned char *lvis, unsigned char *vvis)
|
||
{
|
||
float *v1, *v2;
|
||
vec3_t v3, v4;
|
||
|
||
if (dl->worldshadowmesh && !dl->rebuildcache)
|
||
return dl->worldshadowmesh;
|
||
|
||
if (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife)
|
||
{
|
||
SHM_BeginShadowMesh(dl);
|
||
SHM_MarkLeavesQ1(dl, lvis);
|
||
SHM_RecursiveWorldNodeQ1_r(dl, cl.worldmodel->nodes);
|
||
}
|
||
#ifdef Q3BSPS
|
||
else if (cl.worldmodel->fromgame == fg_quake3)
|
||
{
|
||
SHM_BeginShadowMesh(dl);
|
||
sh_shadowframe++;
|
||
SHM_RecursiveWorldNodeQ3_r(dl, cl.worldmodel->nodes);
|
||
SHM_ComposeVolume_BruteForce(dl);
|
||
return SHM_FinishShadowMesh(dl);
|
||
// SHM_RecursiveWorldNodeQ3_r(cl.worldmodel->nodes);
|
||
|
||
//if generating shadow volumes too:
|
||
// decompose the shadow-casting faces into triangles
|
||
// find neighbours
|
||
// emit front faces (clip back faces to the light's cube?)
|
||
// emit edges where there were no neighbours
|
||
}
|
||
#endif
|
||
#ifdef Q2BSPS
|
||
else if (cl.worldmodel->fromgame == fg_quake2)
|
||
{
|
||
SHM_BeginShadowMesh(dl);
|
||
SHM_MarkLeavesQ2(dl, lvis, vvis);
|
||
SHM_RecursiveWorldNodeQ2_r(dl, cl.worldmodel->nodes);
|
||
}
|
||
#endif
|
||
else
|
||
return NULL;
|
||
|
||
SHM_BeginQuads();
|
||
while(firstedge)
|
||
{
|
||
//border
|
||
v1 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[0]].position;
|
||
v2 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[1]].position;
|
||
|
||
//get positions of v3 and v4 based on the light position
|
||
v3[0] = v1[0] + ( v1[0]-dl->origin[0] )*PROJECTION_DISTANCE;
|
||
v3[1] = v1[1] + ( v1[1]-dl->origin[1] )*PROJECTION_DISTANCE;
|
||
v3[2] = v1[2] + ( v1[2]-dl->origin[2] )*PROJECTION_DISTANCE;
|
||
|
||
v4[0] = v2[0] + ( v2[0]-dl->origin[0] )*PROJECTION_DISTANCE;
|
||
v4[1] = v2[1] + ( v2[1]-dl->origin[1] )*PROJECTION_DISTANCE;
|
||
v4[2] = v2[2] + ( v2[2]-dl->origin[2] )*PROJECTION_DISTANCE;
|
||
|
||
if (edge[firstedge].count > 0)
|
||
{
|
||
SHM_Vertex3fv(v3);
|
||
SHM_Vertex3fv(v4);
|
||
SHM_Vertex3fv(v2);
|
||
SHM_Vertex3fv(v1);
|
||
}
|
||
else
|
||
{
|
||
SHM_Vertex3fv(v1);
|
||
SHM_Vertex3fv(v2);
|
||
SHM_Vertex3fv(v4);
|
||
SHM_Vertex3fv(v3);
|
||
}
|
||
edge[firstedge].count=0;
|
||
|
||
firstedge = edge[firstedge].next;
|
||
}
|
||
SHM_End();
|
||
|
||
firstedge=0;
|
||
|
||
return SHM_FinishShadowMesh(dl);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
static qboolean Sh_VisOverlaps(qbyte *v1, qbyte *v2)
|
||
{
|
||
int i, m;
|
||
if (!v2)
|
||
return false;
|
||
m = (cl.worldmodel->numleafs-1)>>3;
|
||
for (i=0 ; i<m ; i++)
|
||
{
|
||
if (v1[i] & v2[i])
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static qboolean Sh_LeafInView(qbyte *lightvis, qbyte *vvis)
|
||
{
|
||
int i;
|
||
int m = (cl.worldmodel->numleafs);
|
||
mleaf_t *wl = cl.worldmodel->leafs;
|
||
unsigned char lv;
|
||
|
||
/*we can potentially walk off the end of the leafs, but lightvis shouldn't be set for those*/
|
||
|
||
|
||
for (i = 0; i < m; i += 1<<3)
|
||
{
|
||
lv = lightvis[i>>3];// & vvis[i>>3];
|
||
if (!lv)
|
||
continue;
|
||
if ((lv&0x01) && wl[i+0].visframe == r_visframecount) return true;
|
||
if ((lv&0x02) && wl[i+1].visframe == r_visframecount) return true;
|
||
if ((lv&0x04) && wl[i+2].visframe == r_visframecount) return true;
|
||
if ((lv&0x08) && wl[i+3].visframe == r_visframecount) return true;
|
||
if ((lv&0x10) && wl[i+4].visframe == r_visframecount) return true;
|
||
if ((lv&0x20) && wl[i+5].visframe == r_visframecount) return true;
|
||
if ((lv&0x40) && wl[i+6].visframe == r_visframecount) return true;
|
||
if ((lv&0x80) && wl[i+7].visframe == r_visframecount) return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
static void Sh_Scissor (int x, int y, int width, int height)
|
||
{
|
||
#if 0 //visible scissors
|
||
glMatrixMode(GL_PROJECTION);
|
||
glPushMatrix();
|
||
glLoadIdentity();
|
||
glOrtho (0, glwidth, glheight, 0, -99999, 99999);
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glPushMatrix();
|
||
glLoadIdentity();
|
||
// GL_Set2D();
|
||
|
||
glColor4f(1,1,1,1);
|
||
glDisable(GL_DEPTH_TEST);
|
||
glDisable(GL_SCISSOR_TEST);
|
||
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE );
|
||
glDisable(GL_TEXTURE_2D);
|
||
GL_TexEnv(GL_REPLACE);
|
||
|
||
glBegin(GL_LINE_LOOP);
|
||
glVertex2f(x, y);
|
||
glVertex2f(x+glwidth, y);
|
||
glVertex2f(x+glwidth, y+glheight);
|
||
glVertex2f(x, y+glheight);
|
||
glEnd();
|
||
|
||
glMatrixMode(GL_PROJECTION);
|
||
glPopMatrix();
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glPopMatrix();
|
||
#endif
|
||
qglScissor(x, vid.pixelheight - (y + height),width,height);
|
||
}
|
||
|
||
#define BoxesOverlap(a,b,c,d) ((a)[0] <= (d)[0] && (b)[0] >= (c)[0] && (a)[1] <= (d)[1] && (b)[1] >= (c)[1] && (a)[2] <= (d)[2] && (b)[2] >= (c)[2])
|
||
static qboolean Sh_ScissorForBox(vec3_t mins, vec3_t maxs)
|
||
{
|
||
int i, ix1, iy1, ix2, iy2;
|
||
float x1, y1, x2, y2, x, y, f;
|
||
vec3_t smins, smaxs;
|
||
vec4_t v, v2;
|
||
int r_view_x = 0;
|
||
int r_view_y = 0;
|
||
int r_view_width = vid.pixelwidth;
|
||
int r_view_height = vid.pixelheight;
|
||
if (0)//!r_shadow_scissor.integer)
|
||
{
|
||
Sh_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
|
||
return false;
|
||
}
|
||
// if view is inside the box, just say yes it's visible
|
||
if (BoxesOverlap(r_refdef.vieworg, r_refdef.vieworg, mins, maxs))
|
||
{
|
||
Sh_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
|
||
return false;
|
||
}
|
||
for (i = 0;i < 3;i++)
|
||
{
|
||
if (vpn[i] >= 0)
|
||
{
|
||
v[i] = mins[i];
|
||
v2[i] = maxs[i];
|
||
}
|
||
else
|
||
{
|
||
v[i] = maxs[i];
|
||
v2[i] = mins[i];
|
||
}
|
||
}
|
||
f = DotProduct(vpn, r_refdef.vieworg) + 1;
|
||
if (DotProduct(vpn, v2) <= f)
|
||
{
|
||
// entirely behind nearclip plane
|
||
Sh_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
|
||
return true;
|
||
}
|
||
if (DotProduct(vpn, v) >= f)
|
||
{
|
||
// entirely infront of nearclip plane
|
||
x1 = y1 = x2 = y2 = 0;
|
||
for (i = 0;i < 8;i++)
|
||
{
|
||
v[0] = (i & 1) ? mins[0] : maxs[0];
|
||
v[1] = (i & 2) ? mins[1] : maxs[1];
|
||
v[2] = (i & 4) ? mins[2] : maxs[2];
|
||
v[3] = 1.0f;
|
||
Matrix4_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y);
|
||
v2[0]*=r_view_width;
|
||
v2[1]*=r_view_height;
|
||
// GL_TransformToScreen(v, v2);
|
||
//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
|
||
x = v2[0];
|
||
y = v2[1];
|
||
if (i)
|
||
{
|
||
if (x1 > x) x1 = x;
|
||
if (x2 < x) x2 = x;
|
||
if (y1 > y) y1 = y;
|
||
if (y2 < y) y2 = y;
|
||
}
|
||
else
|
||
{
|
||
x1 = x2 = x;
|
||
y1 = y2 = y;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// clipped by nearclip plane
|
||
// this is nasty and crude...
|
||
// create viewspace bbox
|
||
i = 0;
|
||
/*unrolled the first iteration to avoid warnings*/
|
||
v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_refdef.vieworg[0];
|
||
v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_refdef.vieworg[1];
|
||
v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_refdef.vieworg[2];
|
||
v2[0] = DotProduct(v, vright);
|
||
v2[1] = DotProduct(v, vup);
|
||
v2[2] = DotProduct(v, vpn);
|
||
smins[0] = smaxs[0] = v2[0];
|
||
smins[1] = smaxs[1] = v2[1];
|
||
smins[2] = smaxs[2] = v2[2];
|
||
for (i = 1;i < 8;i++)
|
||
{
|
||
v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_refdef.vieworg[0];
|
||
v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_refdef.vieworg[1];
|
||
v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_refdef.vieworg[2];
|
||
v2[0] = DotProduct(v, vright);
|
||
v2[1] = DotProduct(v, vup);
|
||
v2[2] = DotProduct(v, vpn);
|
||
if (smins[0] > v2[0]) smins[0] = v2[0];
|
||
if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
|
||
if (smins[1] > v2[1]) smins[1] = v2[1];
|
||
if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
|
||
if (smins[2] > v2[2]) smins[2] = v2[2];
|
||
if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
|
||
}
|
||
// now we have a bbox in viewspace
|
||
// clip it to the view plane
|
||
if (smins[2] < 1)
|
||
smins[2] = 1;
|
||
// return true if that culled the box
|
||
if (smins[2] >= smaxs[2])
|
||
return true;
|
||
// ok some of it is infront of the view, transform each corner back to
|
||
// worldspace and then to screenspace and make screen rect
|
||
// initialize these variables just to avoid compiler warnings
|
||
x1 = y1 = x2 = y2 = 0;
|
||
for (i = 0;i < 8;i++)
|
||
{
|
||
v2[0] = (i & 1) ? smins[0] : smaxs[0];
|
||
v2[1] = (i & 2) ? smins[1] : smaxs[1];
|
||
v2[2] = (i & 4) ? smins[2] : smaxs[2];
|
||
v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_refdef.vieworg[0];
|
||
v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_refdef.vieworg[1];
|
||
v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_refdef.vieworg[2];
|
||
v[3] = 1.0f;
|
||
Matrix4_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y);
|
||
v2[0]*=r_view_width;
|
||
v2[1]*=r_view_height;
|
||
//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
|
||
x = v2[0];
|
||
y = v2[1];
|
||
if (i)
|
||
{
|
||
if (x1 > x) x1 = x;
|
||
if (x2 < x) x2 = x;
|
||
if (y1 > y) y1 = y;
|
||
if (y2 < y) y2 = y;
|
||
}
|
||
else
|
||
{
|
||
x1 = x2 = x;
|
||
y1 = y2 = y;
|
||
}
|
||
}
|
||
#if 1
|
||
// this code doesn't handle boxes with any points behind view properly
|
||
x1 = 1000;x2 = -1000;
|
||
y1 = 1000;y2 = -1000;
|
||
for (i = 0;i < 8;i++)
|
||
{
|
||
v[0] = (i & 1) ? mins[0] : maxs[0];
|
||
v[1] = (i & 2) ? mins[1] : maxs[1];
|
||
v[2] = (i & 4) ? mins[2] : maxs[2];
|
||
v[3] = 1.0f;
|
||
Matrix4_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y);
|
||
v2[0]*=r_view_width;
|
||
v2[1]*=r_view_height;
|
||
//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
|
||
if (v2[2] > 0)
|
||
{
|
||
x = v2[0];
|
||
y = v2[1];
|
||
|
||
if (x1 > x) x1 = x;
|
||
if (x2 < x) x2 = x;
|
||
if (y1 > y) y1 = y;
|
||
if (y2 < y) y2 = y;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
ix1 = x1 - 1.0f;
|
||
iy1 = y1 - 1.0f;
|
||
ix2 = x2 + 1.0f;
|
||
iy2 = y2 + 1.0f;
|
||
//Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
|
||
if (ix1 < r_view_x) ix1 = r_view_x;
|
||
if (iy1 < r_view_y) iy1 = r_view_y;
|
||
if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
|
||
if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
|
||
if (ix2 <= ix1 || iy2 <= iy1)
|
||
return true;
|
||
// set up the scissor rectangle
|
||
qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
|
||
//qglEnable(GL_SCISSOR_TEST);
|
||
return false;
|
||
}
|
||
|
||
|
||
void GL_BeginRenderBuffer_DepthOnly(texid_t depthtexture)
|
||
{
|
||
if (gl_config.ext_framebuffer_objects)
|
||
{
|
||
if (!shadow_fbo_id)
|
||
{
|
||
qglGenRenderbuffersEXT(1, &shadow_fbo_id);
|
||
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadow_fbo_id);
|
||
qglDrawBuffer(GL_NONE);
|
||
qglReadBuffer(GL_NONE);
|
||
}
|
||
else
|
||
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadow_fbo_id);
|
||
|
||
if (TEXVALID(depthtexture))
|
||
qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depthtexture.num, 0);
|
||
}
|
||
}
|
||
void GL_EndRenderBuffer_DepthOnly(texid_t depthtexture, int texsize)
|
||
{
|
||
if (gl_config.ext_framebuffer_objects)
|
||
{
|
||
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||
}
|
||
else
|
||
{
|
||
GL_MTBind(0, GL_TEXTURE_2D, depthtexture);
|
||
qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texsize, texsize);
|
||
}
|
||
}
|
||
|
||
static void Sh_GenShadowFace(dlight_t *l, shadowmesh_t *smesh, int face, float proj[16])
|
||
{
|
||
float mvm[16];
|
||
vec3_t t1,t2;
|
||
|
||
int smsize = SHADOWMAP_SIZE;
|
||
int tno;
|
||
mesh_t *m;
|
||
texture_t *tex;
|
||
|
||
// qglDepthRange(0, 1);
|
||
|
||
if (l->fov)
|
||
qglViewport (0, 0, smsize, smsize);
|
||
else
|
||
qglViewport (((face/2)*smsize)/3, ((face&1)*smsize)/2, smsize/3, smsize/2);
|
||
|
||
switch(face)
|
||
{
|
||
case 0:
|
||
//forward
|
||
Matrix4_ModelViewMatrixFromAxis(mvm, l->axis[0], l->axis[1], l->axis[2], l->origin);
|
||
break;
|
||
case 1:
|
||
//back
|
||
VectorNegate(l->axis[0], t1);
|
||
VectorNegate(l->axis[1], t2);
|
||
Matrix4_ModelViewMatrixFromAxis(mvm, t1, t2, l->axis[2], l->origin);
|
||
break;
|
||
case 2:
|
||
//left
|
||
VectorNegate(l->axis[1], t1);
|
||
VectorNegate(l->axis[0], t2);
|
||
Matrix4_ModelViewMatrixFromAxis(mvm, l->axis[1], t2, l->axis[2], l->origin);
|
||
break;
|
||
case 3:
|
||
//right
|
||
VectorNegate(l->axis[1], t1);
|
||
Matrix4_ModelViewMatrixFromAxis(mvm, t1, l->axis[0], l->axis[2], l->origin);
|
||
break;
|
||
case 4:
|
||
//up
|
||
VectorNegate(l->axis[0], t2);
|
||
Matrix4_ModelViewMatrixFromAxis(mvm, l->axis[2], l->axis[1], t2, l->origin);
|
||
break;
|
||
case 5:
|
||
//down
|
||
VectorNegate(l->axis[2], t1);
|
||
Matrix4_ModelViewMatrixFromAxis(mvm, t1, l->axis[1], l->axis[0], l->origin);
|
||
break;
|
||
}
|
||
|
||
qglMatrixMode(GL_MODELVIEW);
|
||
qglLoadMatrixf(mvm);
|
||
|
||
R_SetFrustum(proj, mvm);
|
||
|
||
if (smesh)
|
||
for (tno = 0; tno < smesh->numsurftextures; tno++)
|
||
{
|
||
m = NULL;
|
||
if (!smesh->litsurfs[tno].count)
|
||
continue;
|
||
tex = cl.worldmodel->textures[tno];
|
||
BE_DrawMesh_List(tex->shader, smesh->litsurfs[tno].count, smesh->litsurfs[tno].s, &tex->vbo, &tex->shader->defaulttextures, 0);
|
||
}
|
||
|
||
BE_SelectMode(BEM_DEPTHONLY);
|
||
BE_BaseEntTextures();
|
||
|
||
if (0)
|
||
{
|
||
int i;
|
||
static float depth[SHADOWMAP_SIZE*SHADOWMAP_SIZE];
|
||
qglReadPixels(0, 0, smsize, smsize,
|
||
GL_DEPTH_COMPONENT, GL_FLOAT, depth);
|
||
for (i = SHADOWMAP_SIZE*SHADOWMAP_SIZE; i --> 0; )
|
||
{
|
||
if (depth[i] == 1)
|
||
*((unsigned int*)depth+i) = 0;
|
||
else
|
||
*((unsigned int*)depth+i) = 0xff000000|((((unsigned char)(int)(depth[i]*128)))*0x10101);
|
||
}
|
||
qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
|
||
smsize, smsize, 0,
|
||
GL_RGBA, GL_UNSIGNED_BYTE, depth);
|
||
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||
}
|
||
}
|
||
|
||
void Sh_Shutdown(void)
|
||
{
|
||
if (shadow_fbo_id)
|
||
{
|
||
qglDeleteRenderbuffersEXT(1, &shadow_fbo_id);
|
||
shadow_fbo_id = 0;
|
||
}
|
||
}
|
||
|
||
void Sh_GenShadowMap (dlight_t *l, qbyte *lvis)
|
||
{
|
||
int f;
|
||
int smsize = SHADOWMAP_SIZE;
|
||
float proj[16];
|
||
|
||
shadowmesh_t *smesh;
|
||
|
||
if (!TEXVALID(l->stexture))
|
||
{
|
||
l->stexture = GL_AllocNewTexture(smsize, smsize);
|
||
|
||
GL_MTBind(0, GL_TEXTURE_2D, l->stexture);
|
||
qglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32_ARB, smsize, smsize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
|
||
// qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, smsize, smsize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||
}
|
||
|
||
smesh = SHM_BuildShadowVolumeMesh(l, lvis, NULL);
|
||
|
||
/*polygon offsets. urgh.*/
|
||
qglEnable(GL_POLYGON_OFFSET_FILL);
|
||
qglPolygonOffset(5, 25);
|
||
BE_SetupForShadowMap();
|
||
|
||
/*set framebuffer*/
|
||
GL_BeginRenderBuffer_DepthOnly(l->stexture);
|
||
qglClear (GL_DEPTH_BUFFER_BIT);
|
||
|
||
if (l->fov)
|
||
{
|
||
Matrix4_Projection_Far(proj, l->fov, l->fov, nearplane, l->radius);
|
||
qglMatrixMode(GL_PROJECTION);
|
||
qglLoadMatrixf(proj);
|
||
|
||
/*single face*/
|
||
Sh_GenShadowFace(l, smesh, 0, proj);
|
||
}
|
||
else
|
||
{
|
||
Matrix4_Projection_Far(proj, 90, 90, nearplane, l->radius);
|
||
qglMatrixMode(GL_PROJECTION);
|
||
qglLoadMatrixf(proj);
|
||
|
||
/*generate faces*/
|
||
for (f = 0; f < 6; f++)
|
||
{
|
||
Sh_GenShadowFace(l, smesh, f, proj);
|
||
}
|
||
}
|
||
/*end framebuffer*/
|
||
GL_EndRenderBuffer_DepthOnly(l->stexture, smsize);
|
||
|
||
qglDisable(GL_POLYGON_OFFSET_FILL);
|
||
|
||
qglMatrixMode(GL_PROJECTION);
|
||
qglLoadMatrixf(r_refdef.m_projection);
|
||
|
||
qglMatrixMode(GL_MODELVIEW);
|
||
qglLoadMatrixf(r_refdef.m_view);
|
||
|
||
qglViewport(r_refdef.pxrect.x, vid.pixelheight - r_refdef.pxrect.y, r_refdef.pxrect.width, r_refdef.pxrect.height);
|
||
|
||
R_SetFrustum(r_refdef.m_projection, r_refdef.m_view);
|
||
}
|
||
|
||
static float shadowprojectionbias[16] =
|
||
{
|
||
0.5f, 0.0f, 0.0f, 0.0f,
|
||
0.0f, 0.5f, 0.0f, 0.0f,
|
||
0.0f, 0.0f, 0.5f, 0.0f,
|
||
0.5f, 0.5f, 0.4993f, 1.0f
|
||
};
|
||
|
||
static void Sh_DrawShadowMapLight(dlight_t *l, vec3_t colour, qbyte *vvis)
|
||
{
|
||
float t[16];
|
||
float bp[16];
|
||
float proj[16], view[16];
|
||
vec3_t biasorg;
|
||
int ve;
|
||
vec3_t mins, maxs;
|
||
qbyte *lvis;
|
||
qbyte lvisb[MAX_MAP_LEAFS/8];
|
||
|
||
if (R_CullSphere(l->origin, l->radius))
|
||
{
|
||
bench.numfrustumculled++;
|
||
return; //this should be the more common case
|
||
}
|
||
|
||
mins[0] = l->origin[0] - l->radius;
|
||
mins[1] = l->origin[1] - l->radius;
|
||
mins[2] = l->origin[2] - l->radius;
|
||
|
||
maxs[0] = l->origin[0] + l->radius;
|
||
maxs[1] = l->origin[1] + l->radius;
|
||
maxs[2] = l->origin[2] + l->radius;
|
||
|
||
if (Sh_ScissorForBox(mins, maxs))
|
||
{
|
||
bench.numscissorculled++;
|
||
return;
|
||
}
|
||
|
||
if (l->worldshadowmesh)
|
||
{
|
||
lvis = l->worldshadowmesh->litleaves;
|
||
//fixme: check head node first?
|
||
if (!Sh_LeafInView(l->worldshadowmesh->litleaves, vvis))
|
||
{
|
||
bench.numpvsculled++;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int leaf;
|
||
leaf = cl.worldmodel->funcs.LeafnumForPoint(cl.worldmodel, l->origin);
|
||
lvis = cl.worldmodel->funcs.LeafPVS(cl.worldmodel, leaf, lvisb, sizeof(lvisb));
|
||
if (!Sh_VisOverlaps(lvis, vvis)) //The two viewing areas do not intersect.
|
||
{
|
||
bench.numpvsculled++;
|
||
return;
|
||
}
|
||
}
|
||
|
||
Sh_GenShadowMap(l, lvis);
|
||
|
||
if (l->fov)
|
||
Matrix4_Projection_Far(proj, l->fov, l->fov, nearplane, l->radius);
|
||
else
|
||
Matrix4_Projection_Far(proj, 90, 90, nearplane, l->radius);
|
||
VectorMA(l->origin, 0, l->axis[0], biasorg);
|
||
Matrix4_ModelViewMatrixFromAxis(view, l->axis[0], l->axis[1], l->axis[2], l->origin);
|
||
|
||
//bp = shadowprojectionbias*proj*view;
|
||
Matrix4_Multiply(shadowprojectionbias, proj, t);
|
||
Matrix4_Multiply(t, view, bp);
|
||
|
||
t[0] = bp[0];
|
||
t[1] = bp[4];
|
||
t[2] = bp[8];
|
||
t[3] = bp[12];
|
||
t[4] = bp[1];
|
||
t[5] = bp[5];
|
||
t[6] = bp[9];
|
||
t[7] = bp[13];
|
||
t[8] = bp[2];
|
||
t[9] = bp[6];
|
||
t[10] = bp[10];
|
||
t[11] = bp[14];
|
||
t[12] = bp[3];
|
||
t[13] = bp[7];
|
||
t[14] = bp[11];
|
||
t[15] = bp[15];
|
||
|
||
bench.numlights++;
|
||
|
||
qglMatrixMode(GL_TEXTURE);
|
||
GL_MTBind(7, GL_TEXTURE_2D, l->stexture);
|
||
|
||
// qglEnable(GL_TEXTURE_2D);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
|
||
qglTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
|
||
qglLoadMatrixf(bp);
|
||
qglMatrixMode(GL_MODELVIEW);
|
||
|
||
GL_SelectTexture(0);
|
||
|
||
ve = 0;
|
||
|
||
BE_SelectDLight(l, colour);
|
||
BE_SelectMode(l->fov?BEM_SMAPLIGHTSPOT:BEM_SMAPLIGHT);
|
||
Sh_DrawEntLighting(l, colour);
|
||
|
||
GL_SelectTexture(7);
|
||
qglDisable(GL_TEXTURE_2D);
|
||
qglMatrixMode(GL_TEXTURE);
|
||
qglLoadIdentity();
|
||
qglMatrixMode(GL_MODELVIEW);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// warning: <20>Sh_WorldLightingPass<73> defined but not used
|
||
/*
|
||
static void Sh_WorldLightingPass(void)
|
||
{
|
||
msurface_t *s;
|
||
int i;
|
||
int ve;
|
||
|
||
ve = 0;
|
||
for (i = 0; i < cl.worldmodel->numsurfaces; i++)
|
||
{
|
||
s = &cl.worldmodel->surfaces[i];
|
||
if(s->visframe != r_framecount)
|
||
continue;
|
||
|
||
if (ve != s->texinfo->texture->vbo.vboe)
|
||
{
|
||
ve = s->texinfo->texture->vbo.vboe;
|
||
|
||
GL_SelectVBO(s->texinfo->texture->vbo.vbocoord);
|
||
GL_SelectEBO(s->texinfo->texture->vbo.vboe);
|
||
qglVertexPointer(3, GL_FLOAT, sizeof(vecV_t), s->texinfo->texture->vbo.coord);
|
||
}
|
||
qglDrawRangeElements(GL_TRIANGLES, s->mesh->vbofirstvert, s->mesh->numvertexes, s->mesh->numindexes, GL_INDEX_TYPE, (index_t*)(s->mesh->vbofirstelement*sizeof(index_t)));
|
||
RQuantAdd(RQUANT_LITFACES, s->mesh->numindexes);
|
||
}
|
||
}
|
||
*/
|
||
|
||
/*
|
||
draws faces facing the light
|
||
Note: Backend mode must have been selected in advance, as must the light to light from
|
||
*/
|
||
static void Sh_DrawEntLighting(dlight_t *light, vec3_t colour)
|
||
{
|
||
int tno;
|
||
texture_t *tex;
|
||
shadowmesh_t *sm;
|
||
|
||
sm = light->worldshadowmesh;
|
||
if (light->rebuildcache)
|
||
sm = &sh_tempshmesh;
|
||
if (sm)
|
||
{
|
||
for (tno = 0; tno < sm->numsurftextures; tno++)
|
||
{
|
||
if (!sm->litsurfs[tno].count)
|
||
continue;
|
||
tex = cl.worldmodel->textures[tno];
|
||
if (tex->shader->flags & SHADER_NODLIGHT)
|
||
continue;
|
||
BE_DrawMesh_List(tex->shader, sm->litsurfs[tno].count, sm->litsurfs[tno].s, &tex->vbo, &tex->shader->defaulttextures, 0);
|
||
}
|
||
|
||
BE_BaseEntTextures();
|
||
}
|
||
}
|
||
|
||
|
||
#define PROJECTION_DISTANCE (float)(dl->radius*2)//0x7fffffff
|
||
/*Fixme: this is brute forced*/
|
||
#ifdef _MSC_VER
|
||
#pragma message("brush shadows are bruteforced")
|
||
#endif
|
||
static void Sh_DrawBrushModelShadow(dlight_t *dl, entity_t *e)
|
||
{
|
||
int v;
|
||
float *v1, *v2;
|
||
vec3_t v3, v4;
|
||
vec3_t lightorg;
|
||
|
||
int i;
|
||
model_t *model;
|
||
msurface_t *surf;
|
||
|
||
if (BE_LightCullModel(e->origin, e->model))
|
||
return;
|
||
|
||
RotateLightVector((void *)e->axis, e->origin, dl->origin, lightorg);
|
||
|
||
BE_SelectEntity(e);
|
||
|
||
GL_SelectVBO(0);
|
||
GL_SelectEBO(0);
|
||
qglEnableClientState(GL_VERTEX_ARRAY);
|
||
|
||
BE_PushOffsetShadow(true);
|
||
|
||
model = e->model;
|
||
surf = model->surfaces+model->firstmodelsurface;
|
||
for (i = 0; i < model->nummodelsurfaces; i++, surf++)
|
||
{
|
||
if (surf->flags & SURF_PLANEBACK)
|
||
{//inverted normal.
|
||
if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= -0.1)
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= 0.1)
|
||
continue;
|
||
}
|
||
|
||
if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
|
||
{ // no shadows
|
||
continue;
|
||
}
|
||
|
||
if (!surf->mesh)
|
||
continue;
|
||
|
||
//front face
|
||
qglVertexPointer(3, GL_FLOAT, sizeof(vecV_t), surf->mesh->xyz_array);
|
||
qglDrawArrays(GL_POLYGON, 0, surf->mesh->numvertexes);
|
||
// qglDrawRangeElements(GL_TRIANGLES, 0, surf->mesh->numvertexes, surf->mesh->numindexes, GL_INDEX_TYPE, surf->mesh->indexes);
|
||
RQuantAdd(RQUANT_SHADOWFACES, surf->mesh->numvertexes);
|
||
|
||
for (v = 0; v < surf->mesh->numvertexes; v++)
|
||
{
|
||
//border
|
||
v1 = surf->mesh->xyz_array[v];
|
||
v2 = surf->mesh->xyz_array[( v+1 )%surf->mesh->numvertexes];
|
||
|
||
//get positions of v3 and v4 based on the light position
|
||
v3[0] = ( v1[0]-lightorg[0] )*PROJECTION_DISTANCE;
|
||
v3[1] = ( v1[1]-lightorg[1] )*PROJECTION_DISTANCE;
|
||
v3[2] = ( v1[2]-lightorg[2] )*PROJECTION_DISTANCE;
|
||
|
||
v4[0] = ( v2[0]-lightorg[0] )*PROJECTION_DISTANCE;
|
||
v4[1] = ( v2[1]-lightorg[1] )*PROJECTION_DISTANCE;
|
||
v4[2] = ( v2[2]-lightorg[2] )*PROJECTION_DISTANCE;
|
||
|
||
//Now draw the quad from the two verts to the projected light
|
||
//verts
|
||
qglBegin( GL_QUAD_STRIP );
|
||
qglVertex3fv(v1);
|
||
qglVertex3f (v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2]);
|
||
qglVertex3fv(v2);
|
||
qglVertex3f (v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2]);
|
||
qglEnd();
|
||
}
|
||
|
||
//back
|
||
//the same applies as earlier
|
||
qglBegin(GL_POLYGON);
|
||
for (v = surf->mesh->numvertexes-1; v >=0; v--)
|
||
{
|
||
v1 = surf->mesh->xyz_array[v];
|
||
v3[0] = (v1[0]-lightorg[0])*PROJECTION_DISTANCE;
|
||
v3[1] = (v1[1]-lightorg[1])*PROJECTION_DISTANCE;
|
||
v3[2] = (v1[2]-lightorg[2])*PROJECTION_DISTANCE;
|
||
|
||
qglVertex3f(v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2]);
|
||
}
|
||
qglEnd();
|
||
}
|
||
|
||
BE_PushOffsetShadow(false);
|
||
}
|
||
|
||
|
||
/*when this is called, the gl state has been set up to draw the stencil volumes using whatever extensions we have
|
||
if secondside is set, then the gpu sucks and we're drawing stuff the slow 2-pass way, and this is the second pass.
|
||
*/
|
||
static void Sh_DrawStencilLightShadows(dlight_t *dl, qbyte *lvis, qbyte *vvis, qboolean secondside)
|
||
{
|
||
extern cvar_t gl_part_flame;
|
||
int i;
|
||
struct shadowmesh_s *sm;
|
||
entity_t *ent;
|
||
|
||
BE_PushOffsetShadow(false);
|
||
|
||
sm = SHM_BuildShadowVolumeMesh(dl, lvis, vvis);
|
||
if (!sm)
|
||
Sh_DrawBrushModelShadow(dl, &r_worldentity);
|
||
else
|
||
{
|
||
//qglEnable(GL_POLYGON_OFFSET_FILL);
|
||
//qglPolygonOffset(shaderstate.curpolyoffset.factor, shaderstate.curpolyoffset.unit);
|
||
|
||
GL_SelectVBO(0);
|
||
GL_SelectEBO(0);
|
||
qglEnableClientState(GL_VERTEX_ARRAY);
|
||
//draw cached world shadow mesh
|
||
qglVertexPointer(3, GL_FLOAT, sizeof(vecV_t), sm->verts);
|
||
qglDrawRangeElements(GL_TRIANGLES, 0, sm->numverts, sm->numindicies, GL_INDEX_TYPE, sm->indicies);
|
||
RQuantAdd(RQUANT_SHADOWFACES, sm->numindicies);
|
||
|
||
//qglEnable(GL_POLYGON_OFFSET_FILL);
|
||
//qglPolygonOffset(shaderstate.curpolyoffset.factor, shaderstate.curpolyoffset.unit);
|
||
}
|
||
if (!r_drawentities.value)
|
||
return;
|
||
|
||
// draw sprites seperately, because of alpha blending
|
||
for (i=0 ; i<cl_numvisedicts ; i++)
|
||
{
|
||
ent = &cl_visedicts[i];
|
||
|
||
if (ent->flags & (RF_NOSHADOW|Q2RF_BEAM))
|
||
continue;
|
||
|
||
{
|
||
if (ent->keynum == dl->key && ent->keynum)
|
||
continue;
|
||
}
|
||
if (!ent->model)
|
||
continue;
|
||
|
||
if (cls.allow_anyparticles || ent->visframe) //allowed or static
|
||
{
|
||
if (ent->model->engineflags & MDLF_ENGULPHS)
|
||
{
|
||
if (gl_part_flame.value)
|
||
continue;
|
||
}
|
||
}
|
||
|
||
switch (ent->model->type)
|
||
{
|
||
case mod_alias:
|
||
R_DrawGAliasShadowVolume (ent, dl->origin, dl->radius);
|
||
break;
|
||
|
||
case mod_brush:
|
||
Sh_DrawBrushModelShadow (dl, ent);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
BE_SelectEntity(&r_worldentity);
|
||
}
|
||
|
||
//draws a light using stencil shadows.
|
||
//redraws world geometry up to 3 times per light...
|
||
static qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, qbyte *vvis)
|
||
{
|
||
int sdecrw;
|
||
int sincrw;
|
||
int leaf;
|
||
qbyte *lvis;
|
||
|
||
qbyte lvisb[MAX_MAP_LEAFS/8];
|
||
|
||
vec3_t mins;
|
||
vec3_t maxs;
|
||
|
||
if (R_CullSphere(dl->origin, dl->radius))
|
||
{
|
||
bench.numfrustumculled++;
|
||
return false; //this should be the more common case
|
||
}
|
||
|
||
mins[0] = dl->origin[0] - dl->radius;
|
||
mins[1] = dl->origin[1] - dl->radius;
|
||
mins[2] = dl->origin[2] - dl->radius;
|
||
|
||
maxs[0] = dl->origin[0] + dl->radius;
|
||
maxs[1] = dl->origin[1] + dl->radius;
|
||
maxs[2] = dl->origin[2] + dl->radius;
|
||
|
||
if (!dl->rebuildcache)
|
||
{
|
||
//fixme: check head node first?
|
||
if (!Sh_LeafInView(dl->worldshadowmesh->litleaves, vvis))
|
||
{
|
||
bench.numpvsculled++;
|
||
return false;
|
||
}
|
||
lvis = NULL;
|
||
}
|
||
else
|
||
{
|
||
leaf = cl.worldmodel->funcs.LeafnumForPoint(cl.worldmodel, dl->origin);
|
||
lvis = cl.worldmodel->funcs.LeafPVS(cl.worldmodel, leaf, lvisb, sizeof(lvisb));
|
||
|
||
if (!Sh_VisOverlaps(lvis, vvis)) //The two viewing areas do not intersect.
|
||
{
|
||
bench.numpvsculled++;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
//sets up the gl scissor (and culls to view)
|
||
if (Sh_ScissorForBox(mins, maxs))
|
||
{
|
||
bench.numscissorculled++;
|
||
return false; //this doesn't cull often.
|
||
}
|
||
bench.numlights++;
|
||
|
||
BE_SelectDLight(dl, colour);
|
||
BE_SelectMode(BEM_STENCIL);
|
||
|
||
//The backend doesn't maintain scissor state.
|
||
// qglEnable(GL_SCISSOR_TEST);
|
||
//The backend doesn't maintain stencil test state either - it needs to be active for more than just stencils, or disabled. its awkward.
|
||
qglEnable(GL_STENCIL_TEST);
|
||
|
||
//FIXME: is it practical to test to see if scissors allow not clearing the stencil buffer?
|
||
|
||
/*we don't need all that much stencil buffer depth, and if we don't get enough or have dodgy volumes, wrap if we can*/
|
||
sincrw = GL_INCR;
|
||
sdecrw = GL_DECR;
|
||
if (gl_config.ext_stencil_wrap)
|
||
{ //minimise damage...
|
||
sincrw = GL_INCR_WRAP_EXT;
|
||
sdecrw = GL_DECR_WRAP_EXT;
|
||
}
|
||
//our stencil writes.
|
||
|
||
#ifdef _DEBUG
|
||
/* if (r_shadows.value == 666) //testing (visible shadow volumes)
|
||
{
|
||
checkerror();
|
||
qglColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
|
||
qglColor3f(dl->color[0], dl->color[1], dl->color[2]);
|
||
qglDisable(GL_STENCIL_TEST);
|
||
qglEnable(GL_POLYGON_OFFSET_FILL);
|
||
qglPolygonOffset(-1, -1);
|
||
// qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||
Sh_DrawStencilLightShadows(dl, lvis, false);
|
||
qglDisable(GL_POLYGON_OFFSET_FILL);
|
||
checkerror();
|
||
qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||
}*/
|
||
#endif
|
||
|
||
if (qglStencilOpSeparateATI)
|
||
{
|
||
qglClearStencil(0);
|
||
qglClear(GL_STENCIL_BUFFER_BIT);
|
||
GL_CullFace(0);
|
||
|
||
qglStencilFunc( GL_ALWAYS, 1, ~0 );
|
||
|
||
qglStencilOpSeparateATI(GL_BACK, GL_KEEP, sincrw, GL_KEEP);
|
||
qglStencilOpSeparateATI(GL_FRONT, GL_KEEP, sdecrw, GL_KEEP);
|
||
|
||
Sh_DrawStencilLightShadows(dl, lvis, vvis, false);
|
||
qglStencilOpSeparateATI(GL_FRONT_AND_BACK, GL_KEEP, GL_KEEP, GL_KEEP);
|
||
|
||
GL_CullFace(SHADER_CULL_FRONT);
|
||
|
||
qglStencilFunc( GL_EQUAL, 0, ~0 );
|
||
}
|
||
else if (qglActiveStencilFaceEXT)
|
||
{
|
||
/*personally I prefer the ATI way (nvidia method)*/
|
||
qglClearStencil(0);
|
||
qglClear(GL_STENCIL_BUFFER_BIT);
|
||
GL_CullFace(0);
|
||
|
||
qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
|
||
|
||
qglActiveStencilFaceEXT(GL_BACK);
|
||
qglStencilOp(GL_KEEP, sincrw, GL_KEEP);
|
||
qglStencilFunc( GL_ALWAYS, 1, ~0 );
|
||
|
||
qglActiveStencilFaceEXT(GL_FRONT);
|
||
qglStencilOp(GL_KEEP, sdecrw, GL_KEEP);
|
||
qglStencilFunc( GL_ALWAYS, 1, ~0 );
|
||
|
||
Sh_DrawStencilLightShadows(dl, lvis, vvis, false);
|
||
|
||
qglActiveStencilFaceEXT(GL_BACK);
|
||
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||
qglStencilFunc( GL_ALWAYS, 0, ~0 );
|
||
|
||
qglActiveStencilFaceEXT(GL_FRONT);
|
||
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||
qglStencilFunc( GL_EQUAL, 0, ~0 );
|
||
|
||
qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
|
||
|
||
GL_CullFace(SHADER_CULL_FRONT);
|
||
}
|
||
else //your graphics card sucks and lacks efficient stencil shadow techniques.
|
||
{ //centered around 0. Will only be increased then decreased less.
|
||
qglClearStencil(0);
|
||
qglClear(GL_STENCIL_BUFFER_BIT);
|
||
|
||
qglStencilFunc(GL_ALWAYS, 0, ~0);
|
||
|
||
GL_CullFace(SHADER_CULL_BACK);
|
||
qglStencilOp(GL_KEEP, sincrw, GL_KEEP);
|
||
Sh_DrawStencilLightShadows(dl, lvis, vvis, false);
|
||
|
||
GL_CullFace(SHADER_CULL_FRONT);
|
||
qglStencilOp(GL_KEEP, sdecrw, GL_KEEP);
|
||
Sh_DrawStencilLightShadows(dl, lvis, vvis, true);
|
||
|
||
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||
qglStencilFunc(GL_EQUAL, 0, ~0);
|
||
}
|
||
//end stencil writing.
|
||
|
||
#if 0 //draw the stencil stuff to the red channel
|
||
qglMatrixMode(GL_PROJECTION);
|
||
qglPushMatrix();
|
||
qglMatrixMode(GL_MODELVIEW);
|
||
qglPushMatrix();
|
||
GL_Set2D();
|
||
|
||
{
|
||
qglColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);
|
||
qglStencilFunc(GL_GREATER, 1, ~0);
|
||
R2D_ConsoleBackground(vid.height);
|
||
|
||
qglColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||
qglStencilFunc(GL_LESS, 1, ~0);
|
||
R2D_ConsoleBackground(vid.height);
|
||
|
||
qglColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
|
||
qglStencilFunc(GL_NEVER, 1, ~0);
|
||
R2D_ConsoleBackground(vid.height);
|
||
}
|
||
|
||
qglMatrixMode(GL_PROJECTION);
|
||
qglPopMatrix();
|
||
qglMatrixMode(GL_MODELVIEW);
|
||
qglPopMatrix();
|
||
#endif
|
||
|
||
PPL_RevertToKnownState();
|
||
|
||
BE_SelectMode(BEM_LIGHT);
|
||
Sh_DrawEntLighting(dl, colour);
|
||
|
||
qglDisable(GL_STENCIL_TEST);
|
||
qglStencilFunc( GL_ALWAYS, 0, ~0 );
|
||
|
||
return true;
|
||
}
|
||
|
||
static void Sh_DrawShadowlessLight(dlight_t *dl, vec3_t colour, qbyte *vvis)
|
||
{
|
||
vec3_t mins, maxs;
|
||
|
||
if (R_CullSphere(dl->origin, dl->radius))
|
||
{
|
||
bench.numfrustumculled++;
|
||
return; //this should be the more common case
|
||
}
|
||
|
||
if (!dl->rebuildcache)
|
||
{
|
||
//fixme: check head node first?
|
||
if (!Sh_LeafInView(dl->worldshadowmesh->litleaves, vvis))
|
||
{
|
||
bench.numpvsculled++;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int leaf;
|
||
qbyte *lvis;
|
||
qbyte lvisb[MAX_MAP_LEAFS/8];
|
||
|
||
leaf = cl.worldmodel->funcs.LeafnumForPoint(cl.worldmodel, dl->origin);
|
||
lvis = cl.worldmodel->funcs.LeafPVS(cl.worldmodel, leaf, lvisb, sizeof(lvisb));
|
||
|
||
if (!dl->die)
|
||
SHM_BuildShadowVolumeMesh(dl, lvis, vvis);
|
||
|
||
if (!Sh_VisOverlaps(lvis, vvis)) //The two viewing areas do not intersect.
|
||
{
|
||
bench.numpvsculled++;
|
||
return;
|
||
}
|
||
}
|
||
|
||
mins[0] = dl->origin[0] - dl->radius;
|
||
mins[1] = dl->origin[1] - dl->radius;
|
||
mins[2] = dl->origin[2] - dl->radius;
|
||
|
||
maxs[0] = dl->origin[0] + dl->radius;
|
||
maxs[1] = dl->origin[1] + dl->radius;
|
||
maxs[2] = dl->origin[2] + dl->radius;
|
||
|
||
|
||
//sets up the gl scissor (and culls to view)
|
||
if (Sh_ScissorForBox(mins, maxs))
|
||
{
|
||
bench.numscissorculled++;
|
||
return; //was culled.
|
||
}
|
||
|
||
bench.numlights++;
|
||
|
||
BE_SelectDLight(dl, colour);
|
||
BE_SelectMode(BEM_LIGHT);
|
||
Sh_DrawEntLighting(dl, colour);
|
||
}
|
||
|
||
void Sh_DrawLights(qbyte *vis)
|
||
{
|
||
vec3_t colour;
|
||
dlight_t *dl;
|
||
int i;
|
||
unsigned int ignoreflags;
|
||
extern cvar_t r_shadow_realtime_world, r_shadow_realtime_dlight;
|
||
extern cvar_t r_shadow_realtime_world_shadows, r_shadow_realtime_dlight_shadows;
|
||
|
||
if (!r_shadow_realtime_world.ival && !r_shadow_realtime_dlight.ival)
|
||
return;
|
||
|
||
/*no stencil?*/
|
||
if (gl_config.nofixedfunc)
|
||
{
|
||
Con_Printf("FTE does not support stencil shadows without a fixed-function pipeline\n");
|
||
r_shadow_realtime_world.ival = 0;
|
||
r_shadow_realtime_dlight.ival = 0;
|
||
return;
|
||
}
|
||
|
||
if (!gl_config.arb_shader_objects)
|
||
{
|
||
Con_Printf("Missing GL extensions: switching off realtime lighting.\n");
|
||
r_shadow_realtime_world.ival = 0;
|
||
r_shadow_realtime_dlight.ival = 0;
|
||
return;
|
||
}
|
||
|
||
ignoreflags = (r_shadow_realtime_world.value?LFLAG_REALTIMEMODE:LFLAG_NORMALMODE);
|
||
|
||
for (dl = cl_dlights+rtlights_first, i=rtlights_first; i<rtlights_max; i++, dl++)
|
||
{
|
||
if (!dl->radius)
|
||
continue; //dead
|
||
|
||
if (!(dl->flags & ignoreflags))
|
||
continue;
|
||
|
||
if (dl->die)
|
||
{
|
||
colour[0] = dl->color[0]*10;
|
||
colour[1] = dl->color[1]*10;
|
||
colour[2] = dl->color[2]*10;
|
||
}
|
||
else
|
||
{
|
||
colour[0] = dl->color[0];
|
||
colour[1] = dl->color[1];
|
||
colour[2] = dl->color[2];
|
||
}
|
||
if (dl->style)
|
||
{
|
||
if (cl_lightstyle[dl->style-1].colour & 1)
|
||
colour[0] *= d_lightstylevalue[dl->style-1]/255.0f;
|
||
else
|
||
colour[0] = 0;
|
||
if (cl_lightstyle[dl->style-1].colour & 2)
|
||
colour[1] *= d_lightstylevalue[dl->style-1]/255.0f;
|
||
else
|
||
colour[1] = 0;
|
||
if (cl_lightstyle[dl->style-1].colour & 4)
|
||
colour[2] *= d_lightstylevalue[dl->style-1]/255.0f;
|
||
else
|
||
colour[2] = 0;
|
||
}
|
||
|
||
if (colour[0] < 0.001 && colour[1] < 0.001 && colour[2] < 0.001)
|
||
continue; //just switch these off.
|
||
|
||
if (((!dl->die)?!r_shadow_realtime_world_shadows.ival:!r_shadow_realtime_dlight_shadows.ival) || dl->flags & LFLAG_NOSHADOWS)
|
||
{
|
||
Sh_DrawShadowlessLight(dl, colour, vis);
|
||
}
|
||
else if (dl->flags & LFLAG_SHADOWMAP)
|
||
{
|
||
Sh_DrawShadowMapLight(dl, colour, vis);
|
||
}
|
||
else
|
||
{
|
||
Sh_DrawStencilLight(dl, colour, vis);
|
||
}
|
||
}
|
||
|
||
qglDisable(GL_SCISSOR_TEST);
|
||
BE_SelectMode(BEM_STANDARD);
|
||
|
||
// if (developer.value)
|
||
// Con_Printf("%i lights drawn, %i frustum culled, %i pvs culled, %i scissor culled\n", bench.numlights, bench.numfrustumculled, bench.numpvsculled, bench.numscissorculled);
|
||
// memset(&bench, 0, sizeof(bench));
|
||
}
|
||
#endif
|
||
#endif
|