/* 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; inormal[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; inormal, (*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; isplitplane->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; ichildren[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 ; inumedges ; 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] ; inumedges ; 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..."); } */