572 lines
No EOL
11 KiB
C
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...");
|
|
}
|
|
*/ |