tenebrae2/gl_svbsp.c
2003-01-17 21:18:53 +00:00

572 lines
No EOL
11 KiB
C

/*
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_svbsp.c shadow volume bsp
#include "quakedef.h"
int svBsp_NumCutPolys;
int svBsp_NumKeptPolys;
int svBsp_NumAddedPolys;
int svBsp_NumMissedPlanes;
int svBsp_NumMissedNodes;
//FIXME: Decent allocation
//1Meg plane pool :)
#define MAX_PLANE_POOL 65536/2
#define MAX_NODE_POOL 65536
plane_t PlanePool[MAX_PLANE_POOL];
int planesusedpool;
svnode_t NodePool[MAX_NODE_POOL];
int nodesusedpool;
//Returns an plane, tries to find the same one as tryplane, if a match if found it return the found one.
plane_t *AllocPlane(plane_t *tryplane) {
if (tryplane) {
int i;
//try to fit one that fits
for (i=0; i<planesusedpool; i++) {
if ((PlanePool[i].normal[0] == tryplane->normal[0]) &&
(PlanePool[i].normal[1] == tryplane->normal[1]) &&
(PlanePool[i].normal[2] == tryplane->normal[2]) &&
(PlanePool[i].dist == tryplane->dist)) {
return &PlanePool[i];
}
}
}
if (planesusedpool >= MAX_PLANE_POOL) {
Con_Printf("Too many planes...");
svBsp_NumMissedPlanes++;
return NULL;
}
planesusedpool++;
return &PlanePool[planesusedpool-1];
}
svnode_t *AllocNode(void) {
if (nodesusedpool >= MAX_NODE_POOL) {
Con_Printf("Too many nodes...");
svBsp_NumMissedNodes++;
return NULL;
}
nodesusedpool++;
return &NodePool[nodesusedpool-1];
}
int Epsilon_Sign(float value) {
if (value < -ON_EPSILON) return 1;
else if (value > ON_EPSILON) return 2;
else return 0;
}
/*
=============
R_CreateEmptyTree
Do some tricks to make sure we have a 360 deg. field of view.
This routine destroys the last svbsp the program has generated.
plane
/ \
-plane open
/ \
solid open
=============
*/
svnode_t *R_CreateEmptyTree(void) {
plane_t *plane1;
plane_t *plane2;
svnode_t *node1;
svnode_t *node2;
planesusedpool = 0;
nodesusedpool = 0;
svBsp_NumMissedPlanes = 0;
svBsp_NumMissedNodes = 0;
plane1 = AllocPlane (NULL);
plane2 = AllocPlane (NULL);
node1 = AllocNode ();
node2 = AllocNode ();
//all planes go trough the light origin
plane1->normal[0] = 1;
plane1->normal[1] = 0;
plane1->normal[2] = 0;
plane1->dist = DotProduct (currentshadowlight->origin, plane1->normal);
plane2->normal[0] = -1;
plane2->normal[1] = 0;
plane2->normal[2] = 0;
plane2->dist = DotProduct (currentshadowlight->origin, plane2->normal);
node1->splitplane = plane1;
node1->children[0] = node2;
node1->children[1] = NULL;
node2->splitplane = plane2;
node2->children[0] = NULL;
node2->children[1] = NULL;
return node1;
}
#define MAX_POLY_VERT 32
void SplitPolygon(vec3_t *polygon,int *signs, int vnum, plane_t *plane, vec3_t *inpts, int *innum, vec3_t *outpts, int *outnum) {
int out_c = 0;
int in_c = 0;
vec3_t *ptA = &polygon[vnum-1];
vec3_t *ptB, v, newVert;
int sideA = signs[vnum-1];
int sideB;
int i;
float sect;
for (i=0; i<vnum; i++) {
ptB = &polygon[i];
sideB = signs[i];
//is b on "right side"
if (sideB == 2) {
if (sideA == 1) {
// compute the intersection point of the line
// from point A to point B with the partition
// plane. This is a simple ray-plane intersection.
VectorSubtract ((*ptB), (*ptA), v);
sect = - (DotProduct (plane->normal, (*ptA) )-plane->dist) /
DotProduct (plane->normal, v);
VectorScale (v,sect,v);
//add a new vertex
VectorAdd ((*ptA), v, newVert);
VectorCopy (newVert, inpts[in_c]);
VectorCopy (newVert, outpts[out_c]);
out_c++;
in_c++;
}
VectorCopy (polygon[i], outpts[out_c]);
out_c++;
}
//b is on "left" side
else if (sideB ==1) {
if (sideA == 2) {
// compute the intersection point of the line
// from point A to point B with the partition
// plane. This is a simple ray-plane intersection.
VectorSubtract ((*ptB), (*ptA), v);
sect = - (DotProduct (plane->normal, (*ptA) )-plane->dist) /
DotProduct (plane->normal, v);
VectorScale (v,sect,v);
//add a new vertex
VectorAdd ((*ptA), v, newVert);
VectorCopy (newVert, inpts[in_c]);
VectorCopy (newVert, outpts[out_c]);
out_c++;
in_c++;
}
VectorCopy (polygon[i], inpts[in_c]);
in_c++;
}
//b is almost on plane
else {
VectorCopy (polygon[i], inpts[in_c]);
VectorCopy (inpts[in_c], outpts[out_c]);
in_c++;
out_c++;
}
ptA = ptB;
sideA = sideB;
if ((out_c > MAX_POLY_VERT) || (in_c > MAX_POLY_VERT)) {
Con_Printf ("MAX_POLY_VERT exceeded: %i %i\n", in_c, out_c);
//just return what we've got
(*innum) = in_c;
(*outnum) = out_c;
return;
}
}
(*innum) = in_c;
(*outnum) = out_c;
}
/*
=============
R_AddShadowCaster
Polygons must be added in front to back order!
=============
*/
svnode_t *R_AddShadowCaster(svnode_t *node, vec3_t *v, int vnum, msurface_t *surf,int depth) {
int sign;
int signs[MAX_POLY_VERT],signs2[MAX_POLY_VERT];
vec3_t v1[MAX_POLY_VERT],v2[MAX_POLY_VERT];
int vnum1,vnum2;
int i;
if (depth > 1500) {
Con_Printf("to deep\n");
return NULL;
}
if (vnum == 0) return NULL;
sign = 0;
for (i=0; i<vnum; i++) {
sign |= signs[i] = Epsilon_Sign (DotProduct (v[i], node->splitplane->normal)- node->splitplane->dist);
}
if (sign == 1) {
if (node->children[0] != NULL) {
R_AddShadowCaster (node->children[0], v, vnum, surf, depth+1);
} else {
svBsp_NumCutPolys++;
}
} else if (sign == 2) {
if (node->children[1] != NULL) {
R_AddShadowCaster (node->children[1], v, vnum, surf, depth+1);
} else {
node->children[1] = ExpandVolume(v, signs, vnum, surf);
if (surf->visframe != r_lightTimestamp) {
//Store it out as visible
surf->shadowchain = shadowchain;
surf->visframe = r_lightTimestamp;
surf->polys->lightTimestamp = r_lightTimestamp;
shadowchain = surf;
svBsp_NumKeptPolys++;
}
}
} else if (sign == 3) {
SplitPolygon(&v[0], &signs[0], vnum, node->splitplane, &v1[0], &vnum1, &v2[0], &vnum2);
if (node->children[0] != NULL) {
R_AddShadowCaster (node->children[0], v1, vnum1, surf, depth+1);
} else {
svBsp_NumCutPolys++;
}
if (vnum2 == 0) return NULL;
if (node->children[1] != NULL) {
R_AddShadowCaster (node->children[1], v2, vnum2, surf, depth+1);
} else {
node->children[1] = ExpandVolume (v2, signs2, vnum2, surf);
if (surf->visframe != r_lightTimestamp) {
//Store it out as visible
surf->shadowchain = shadowchain;
surf->visframe = r_lightTimestamp;
surf->polys->lightTimestamp = r_lightTimestamp;
shadowchain = surf;
svBsp_NumKeptPolys++;
}
}
}
return NULL;
}
/*
=============
ExpandVolume
=============
*/
svnode_t *ExpandVolume(vec3_t *v,int *sign, int vnum, msurface_t *surf) {
svnode_t *res, *currnode;
int i;
#ifdef SHADOW_DEBUG
Con_Printf ("expand volume %i\n",vnum);
#endif
if (vnum == 0) return NULL;
res = NodeFromEdge (v, vnum, 0);
if (!res) return NULL;
currnode = res;
for (i=1; i<vnum; i++) {
currnode->children[0] = NodeFromEdge (v, vnum, i);
if (currnode->children[0] == NULL) break;
currnode = currnode->children[0];
}
//Con_Printf ("expand done");
return res;
}
/*
=============
NodeFromEdge
=============
*/
svnode_t *NodeFromEdge(vec3_t *v, int vnum, int edgeindex) {
vec3_t *v1,*v2;
vec3_t vert1,vert2,normal;
vec_t dist;
plane_t *plane, tryplane;
svnode_t *res;
//Extract one vertex so we can calultate the normal of this shadow plane
v1 = &v[edgeindex];
v2 = &v[(edgeindex+1)% vnum];
VectorSubtract ((*v1), currentshadowlight->origin, vert1);
VectorSubtract ((*v1), (*v2), vert2);
CrossProduct (vert1, vert2, normal);
VectorNormalize (normal);
//VectorInverse(normal);
dist = DotProduct(normal, currentshadowlight->origin);
//make a node with it
VectorCopy (normal, tryplane.normal);
tryplane.dist = dist;
plane = AllocPlane (&tryplane);
if (!plane) return NULL;
VectorCopy (normal, plane->normal);
plane->dist = dist;
res = AllocNode ();
if (!res) {
return res;
}
res->splitplane = plane;
res->children[0] = NULL;
res->children[1] = NULL;
return res;
}
/*
Code scrap yard
*/
/*
==================
FaceSide
PENTA: From qbsp code in "solidbsp.c" and adapted a bit.
==================
*/
/*
int FaceSide (msurface_t *in, mplane_t *split)
{
int frontcount, backcount;
vec_t dot;
int i;
vec_t *p;
frontcount = backcount = 0;
// axial planes are fast
if (split->type < 3)
for (i=0, p = in->polys->verts[0]+split->type ; i<in->numedges ; i++, p+=VERTEXSIZE)
{
if (*p > split->dist + ON_EPSILON)
{
if (backcount)
return SIDE_ON;
frontcount = 1;
}
else if (*p < split->dist - ON_EPSILON)
{
if (frontcount)
return SIDE_ON;
backcount = 1;
}
}
else
// sloping planes take longer
for (i=0, p = in->polys->verts[0] ; i<in->numedges ; i++, p+=VERTEXSIZE)
{
dot = DotProduct (p, split->normal);
dot -= split->dist;
if (dot > ON_EPSILON)
{
if (backcount)
return SIDE_ON;
frontcount = 1;
}
else if (dot < -ON_EPSILON)
{
if (frontcount)
return SIDE_ON;
backcount = 1;
}
}
if (!frontcount)
return SIDE_BACK;
if (!backcount)
return SIDE_FRONT;
return SIDE_ON;
}
*/
/*svnode_t *R_AddShadowCaster(svnode_t *node, msurface_t *surf) {
int side;
if (node == &OUT_NODE) {
//Polygon casts a shadow add the volume to the svbsp
return ExpandVolumeBsp(surf);
} else if (node == &IN_NODE) {
//Polygons is obscured by others
return node;
} else {
side = FaceSide(surf,node->splitplane);
if (side == SIDE_FRONT) {
node->children[0] = R_AddShadowCaster(node->children[0],surf);
}
else if (side == SIDE_BACK) {
node->children[1] = R_AddShadowCaster(node->children[1],surf);
}
else {
node->children[0] = R_AddShadowCaster(node->children[0],surf);
node->children[1] = R_AddShadowCaster(node->children[1],surf);
}
}
}
*/
/*
int GetLeftVerts(int *signs,vec3_t *v,int num,vec3_d *dest,int *destnum) {
//find first 0 or 1
while (!((signs[i] == 0) || (signs[i] == 1))) {
i++;
//none found
if (i == num) return 0;
}
//find consecutive 0's and 1's
j = i;
while ((signs[j] == 0) || (signs[j] == 1)) {
VectorCopy(v[j],dest[j]);
j++;
//all found
if (j == num) return num;
}
//include final 2's
if (signs[j-1] == 1) {
if (signs[j] == 2) {
VectorCopy(v[j],dest[j]);
} else Con_Printf("strange stuff...");
}
if (signs[i] == 1) {
if (signs[i-1] == 2) {
VectorCopy(v[i-1],dest[j+1]);
} else Con_Printf("strange stuff...");
}
*/