diff options
Diffstat (limited to 'code/bspc/faces.c')
-rwxr-xr-x | code/bspc/faces.c | 1956 |
1 files changed, 978 insertions, 978 deletions
diff --git a/code/bspc/faces.c b/code/bspc/faces.c index bcd3ef9..f05f5ae 100755 --- a/code/bspc/faces.c +++ b/code/bspc/faces.c @@ -1,978 +1,978 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code 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.
-
-Quake III Arena source code 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 Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-// faces.c
-
-#include "qbsp.h"
-#include "l_mem.h"
-
-/*
-
- some faces will be removed before saving, but still form nodes:
-
- the insides of sky volumes
- meeting planes of different water current volumes
-
-*/
-
-// undefine for dumb linear searches
-#define USE_HASHING
-
-#define INTEGRAL_EPSILON 0.01
-#define POINT_EPSILON 0.5
-#define OFF_EPSILON 0.5
-
-int c_merge;
-int c_subdivide;
-
-int c_totalverts;
-int c_uniqueverts;
-int c_degenerate;
-int c_tjunctions;
-int c_faceoverflows;
-int c_facecollapse;
-int c_badstartverts;
-
-#define MAX_SUPERVERTS 512
-int superverts[MAX_SUPERVERTS];
-int numsuperverts;
-
-face_t *edgefaces[MAX_MAP_EDGES][2];
-int firstmodeledge = 1;
-int firstmodelface;
-
-int c_tryedges;
-
-vec3_t edge_dir;
-vec3_t edge_start;
-vec_t edge_len;
-
-int num_edge_verts;
-int edge_verts[MAX_MAP_VERTS];
-
-face_t *NewFaceFromFace (face_t *f);
-
-//===========================================================================
-
-typedef struct hashvert_s
-{
- struct hashvert_s *next;
- int num;
-} hashvert_t;
-
-
-#define HASH_SIZE 64
-
-
-int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain
-int hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts
-
-face_t *edgefaces[MAX_MAP_EDGES][2];
-
-//============================================================================
-
-
-unsigned HashVec (vec3_t vec)
-{
- int x, y;
-
- x = (4096 + (int)(vec[0]+0.5)) >> 7;
- y = (4096 + (int)(vec[1]+0.5)) >> 7;
-
- if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )
- Error ("HashVec: point outside valid range");
-
- return y*HASH_SIZE + x;
-}
-
-#ifdef USE_HASHING
-/*
-=============
-GetVertex
-
-Uses hashing
-=============
-*/
-int GetVertexnum (vec3_t in)
-{
- int h;
- int i;
- float *p;
- vec3_t vert;
- int vnum;
-
- c_totalverts++;
-
- for (i=0 ; i<3 ; i++)
- {
- if ( fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON)
- vert[i] = Q_rint(in[i]);
- else
- vert[i] = in[i];
- }
-
- h = HashVec (vert);
-
- for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])
- {
- p = dvertexes[vnum].point;
- if ( fabs(p[0]-vert[0])<POINT_EPSILON
- && fabs(p[1]-vert[1])<POINT_EPSILON
- && fabs(p[2]-vert[2])<POINT_EPSILON )
- return vnum;
- }
-
-// emit a vertex
- if (numvertexes == MAX_MAP_VERTS)
- Error ("numvertexes == MAX_MAP_VERTS");
-
- dvertexes[numvertexes].point[0] = vert[0];
- dvertexes[numvertexes].point[1] = vert[1];
- dvertexes[numvertexes].point[2] = vert[2];
-
- vertexchain[numvertexes] = hashverts[h];
- hashverts[h] = numvertexes;
-
- c_uniqueverts++;
-
- numvertexes++;
-
- return numvertexes-1;
-}
-#else
-/*
-==================
-GetVertexnum
-
-Dumb linear search
-==================
-*/
-int GetVertexnum (vec3_t v)
-{
- int i, j;
- dvertex_t *dv;
- vec_t d;
-
- c_totalverts++;
-
- // make really close values exactly integral
- for (i=0 ; i<3 ; i++)
- {
- if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )
- v[i] = (int)(v[i]+0.5);
- if (v[i] < -4096 || v[i] > 4096)
- Error ("GetVertexnum: outside +/- 4096");
- }
-
- // search for an existing vertex match
- for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)
- {
- for (j=0 ; j<3 ; j++)
- {
- d = v[j] - dv->point[j];
- if ( d > POINT_EPSILON || d < -POINT_EPSILON)
- break;
- }
- if (j == 3)
- return i; // a match
- }
-
- // new point
- if (numvertexes == MAX_MAP_VERTS)
- Error ("MAX_MAP_VERTS");
- VectorCopy (v, dv->point);
- numvertexes++;
- c_uniqueverts++;
-
- return numvertexes-1;
-}
-#endif
-
-
-/*
-==================
-FaceFromSuperverts
-
-The faces vertexes have been added to the superverts[] array,
-and there may be more there than can be held in a face (MAXEDGES).
-
-If less, the faces vertexnums[] will be filled in, otherwise
-face will reference a tree of split[] faces until all of the
-vertexnums can be added.
-
-superverts[base] will become face->vertexnums[0], and the others
-will be circularly filled in.
-==================
-*/
-void FaceFromSuperverts (node_t *node, face_t *f, int base)
-{
- face_t *newf;
- int remaining;
- int i;
-
- remaining = numsuperverts;
- while (remaining > MAXEDGES)
- { // must split into two faces, because of vertex overload
- c_faceoverflows++;
-
- newf = f->split[0] = NewFaceFromFace (f);
- newf = f->split[0];
- newf->next = node->faces;
- node->faces = newf;
-
- newf->numpoints = MAXEDGES;
- for (i=0 ; i<MAXEDGES ; i++)
- newf->vertexnums[i] = superverts[(i+base)%numsuperverts];
-
- f->split[1] = NewFaceFromFace (f);
- f = f->split[1];
- f->next = node->faces;
- node->faces = f;
-
- remaining -= (MAXEDGES-2);
- base = (base+MAXEDGES-1)%numsuperverts;
- }
-
- // copy the vertexes back to the face
- f->numpoints = remaining;
- for (i=0 ; i<remaining ; i++)
- f->vertexnums[i] = superverts[(i+base)%numsuperverts];
-}
-
-
-/*
-==================
-EmitFaceVertexes
-==================
-*/
-void EmitFaceVertexes (node_t *node, face_t *f)
-{
- winding_t *w;
- int i;
-
- if (f->merged || f->split[0] || f->split[1])
- return;
-
- w = f->w;
- for (i=0 ; i<w->numpoints ; i++)
- {
- if (noweld)
- { // make every point unique
- if (numvertexes == MAX_MAP_VERTS)
- Error ("MAX_MAP_VERTS");
- superverts[i] = numvertexes;
- VectorCopy (w->p[i], dvertexes[numvertexes].point);
- numvertexes++;
- c_uniqueverts++;
- c_totalverts++;
- }
- else
- superverts[i] = GetVertexnum (w->p[i]);
- }
- numsuperverts = w->numpoints;
-
- // this may fragment the face if > MAXEDGES
- FaceFromSuperverts (node, f, 0);
-}
-
-/*
-==================
-EmitVertexes_r
-==================
-*/
-void EmitVertexes_r (node_t *node)
-{
- int i;
- face_t *f;
-
- if (node->planenum == PLANENUM_LEAF)
- return;
-
- for (f=node->faces ; f ; f=f->next)
- {
- EmitFaceVertexes (node, f);
- }
-
- for (i=0 ; i<2 ; i++)
- EmitVertexes_r (node->children[i]);
-}
-
-
-#ifdef USE_HASHING
-/*
-==========
-FindEdgeVerts
-
-Uses the hash tables to cut down to a small number
-==========
-*/
-void FindEdgeVerts (vec3_t v1, vec3_t v2)
-{
- int x1, x2, y1, y2, t;
- int x, y;
- int vnum;
-
-#if 0
-{
- int i;
- num_edge_verts = numvertexes-1;
- for (i=0 ; i<numvertexes-1 ; i++)
- edge_verts[i] = i+1;
-}
-#endif
-
- x1 = (4096 + (int)(v1[0]+0.5)) >> 7;
- y1 = (4096 + (int)(v1[1]+0.5)) >> 7;
- x2 = (4096 + (int)(v2[0]+0.5)) >> 7;
- y2 = (4096 + (int)(v2[1]+0.5)) >> 7;
-
- if (x1 > x2)
- {
- t = x1;
- x1 = x2;
- x2 = t;
- }
- if (y1 > y2)
- {
- t = y1;
- y1 = y2;
- y2 = t;
- }
-#if 0
- x1--;
- x2++;
- y1--;
- y2++;
- if (x1 < 0)
- x1 = 0;
- if (x2 >= HASH_SIZE)
- x2 = HASH_SIZE;
- if (y1 < 0)
- y1 = 0;
- if (y2 >= HASH_SIZE)
- y2 = HASH_SIZE;
-#endif
- num_edge_verts = 0;
- for (x=x1 ; x <= x2 ; x++)
- {
- for (y=y1 ; y <= y2 ; y++)
- {
- for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])
- {
- edge_verts[num_edge_verts++] = vnum;
- }
- }
- }
-}
-
-#else
-/*
-==========
-FindEdgeVerts
-
-Forced a dumb check of everything
-==========
-*/
-void FindEdgeVerts (vec3_t v1, vec3_t v2)
-{
- int i;
-
- num_edge_verts = numvertexes-1;
- for (i=0 ; i<num_edge_verts ; i++)
- edge_verts[i] = i+1;
-}
-#endif
-
-/*
-==========
-TestEdge
-
-Can be recursively reentered
-==========
-*/
-void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)
-{
- int j, k;
- vec_t dist;
- vec3_t delta;
- vec3_t exact;
- vec3_t off;
- vec_t error;
- vec3_t p;
-
- if (p1 == p2)
- {
- c_degenerate++;
- return; // degenerate edge
- }
-
- for (k=startvert ; k<num_edge_verts ; k++)
- {
- j = edge_verts[k];
- if (j==p1 || j == p2)
- continue;
-
- VectorCopy (dvertexes[j].point, p);
-
- VectorSubtract (p, edge_start, delta);
- dist = DotProduct (delta, edge_dir);
- if (dist <=start || dist >= end)
- continue; // off an end
- VectorMA (edge_start, dist, edge_dir, exact);
- VectorSubtract (p, exact, off);
- error = VectorLength (off);
-
- if (fabs(error) > OFF_EPSILON)
- continue; // not on the edge
-
- // break the edge
- c_tjunctions++;
- TestEdge (start, dist, p1, j, k+1);
- TestEdge (dist, end, j, p2, k+1);
- return;
- }
-
- // the edge p1 to p2 is now free of tjunctions
- if (numsuperverts >= MAX_SUPERVERTS)
- Error ("MAX_SUPERVERTS");
- superverts[numsuperverts] = p1;
- numsuperverts++;
-}
-
-/*
-==================
-FixFaceEdges
-
-==================
-*/
-void FixFaceEdges (node_t *node, face_t *f)
-{
- int p1, p2;
- int i;
- vec3_t e2;
- vec_t len;
- int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
- int base;
-
- if (f->merged || f->split[0] || f->split[1])
- return;
-
- numsuperverts = 0;
-
- for (i=0 ; i<f->numpoints ; i++)
- {
- p1 = f->vertexnums[i];
- p2 = f->vertexnums[(i+1)%f->numpoints];
-
- VectorCopy (dvertexes[p1].point, edge_start);
- VectorCopy (dvertexes[p2].point, e2);
-
- FindEdgeVerts (edge_start, e2);
-
- VectorSubtract (e2, edge_start, edge_dir);
- len = VectorNormalize(edge_dir);
-
- start[i] = numsuperverts;
- TestEdge (0, len, p1, p2, 0);
-
- count[i] = numsuperverts - start[i];
- }
-
- if (numsuperverts < 3)
- { // entire face collapsed
- f->numpoints = 0;
- c_facecollapse++;
- return;
- }
-
- // we want to pick a vertex that doesn't have tjunctions
- // on either side, which can cause artifacts on trifans,
- // especially underwater
- for (i=0 ; i<f->numpoints ; i++)
- {
- if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)
- break;
- }
- if (i == f->numpoints)
- {
- f->badstartvert = true;
- c_badstartverts++;
- base = 0;
- }
- else
- { // rotate the vertex order
- base = start[i];
- }
-
- // this may fragment the face if > MAXEDGES
- FaceFromSuperverts (node, f, base);
-}
-
-/*
-==================
-FixEdges_r
-==================
-*/
-void FixEdges_r (node_t *node)
-{
- int i;
- face_t *f;
-
- if (node->planenum == PLANENUM_LEAF)
- return;
-
- for (f=node->faces ; f ; f=f->next)
- FixFaceEdges (node, f);
-
- for (i=0 ; i<2 ; i++)
- FixEdges_r (node->children[i]);
-}
-
-/*
-===========
-FixTjuncs
-
-===========
-*/
-void FixTjuncs (node_t *headnode)
-{
- // snap and merge all vertexes
- qprintf ("---- snap verts ----\n");
- memset (hashverts, 0, sizeof(hashverts));
- c_totalverts = 0;
- c_uniqueverts = 0;
- c_faceoverflows = 0;
- EmitVertexes_r (headnode);
- qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts);
-
- // break edges on tjunctions
- qprintf ("---- tjunc ----\n");
- c_tryedges = 0;
- c_degenerate = 0;
- c_facecollapse = 0;
- c_tjunctions = 0;
- if (!notjunc)
- FixEdges_r (headnode);
- qprintf ("%5i edges degenerated\n", c_degenerate);
- qprintf ("%5i faces degenerated\n", c_facecollapse);
- qprintf ("%5i edges added by tjunctions\n", c_tjunctions);
- qprintf ("%5i faces added by tjunctions\n", c_faceoverflows);
- qprintf ("%5i bad start verts\n", c_badstartverts);
-}
-
-
-//========================================================
-
-int c_faces;
-
-face_t *AllocFace (void)
-{
- face_t *f;
-
- f = GetMemory(sizeof(*f));
- memset (f, 0, sizeof(*f));
- c_faces++;
-
- return f;
-}
-
-face_t *NewFaceFromFace (face_t *f)
-{
- face_t *newf;
-
- newf = AllocFace ();
- *newf = *f;
- newf->merged = NULL;
- newf->split[0] = newf->split[1] = NULL;
- newf->w = NULL;
- return newf;
-}
-
-void FreeFace (face_t *f)
-{
- if (f->w)
- FreeWinding (f->w);
- FreeMemory(f);
- c_faces--;
-}
-
-//========================================================
-
-/*
-==================
-GetEdge
-
-Called by writebsp.
-Don't allow four way edges
-==================
-*/
-int GetEdge2 (int v1, int v2, face_t *f)
-{
- dedge_t *edge;
- int i;
-
- c_tryedges++;
-
- if (!noshare)
- {
- for (i=firstmodeledge ; i < numedges ; i++)
- {
- edge = &dedges[i];
- if (v1 == edge->v[1] && v2 == edge->v[0]
- && edgefaces[i][0]->contents == f->contents)
- {
- if (edgefaces[i][1])
- // printf ("WARNING: multiple backward edge\n");
- continue;
- edgefaces[i][1] = f;
- return -i;
- }
- #if 0
- if (v1 == edge->v[0] && v2 == edge->v[1])
- {
- printf ("WARNING: multiple forward edge\n");
- return i;
- }
- #endif
- }
- }
-
-// emit an edge
- if (numedges >= MAX_MAP_EDGES)
- Error ("numedges == MAX_MAP_EDGES");
- edge = &dedges[numedges];
- numedges++;
- edge->v[0] = v1;
- edge->v[1] = v2;
- edgefaces[numedges-1][0] = f;
-
- return numedges-1;
-}
-
-/*
-===========================================================================
-
-FACE MERGING
-
-===========================================================================
-*/
-
-/*
-=============
-TryMerge
-
-If two polygons share a common edge and the edges that meet at the
-common points are both inside the other polygons, merge them
-
-Returns NULL if the faces couldn't be merged, or the new face.
-The originals will NOT be freed.
-=============
-*/
-face_t *TryMerge (face_t *f1, face_t *f2, vec3_t planenormal)
-{
- face_t *newf;
- winding_t *nw;
-
- if (!f1->w || !f2->w)
- return NULL;
- if (f1->texinfo != f2->texinfo)
- return NULL;
- if (f1->planenum != f2->planenum) // on front and back sides
- return NULL;
- if (f1->contents != f2->contents)
- return NULL;
-
-
- nw = TryMergeWinding (f1->w, f2->w, planenormal);
- if (!nw)
- return NULL;
-
- c_merge++;
- newf = NewFaceFromFace (f1);
- newf->w = nw;
-
- f1->merged = newf;
- f2->merged = newf;
-
- return newf;
-}
-
-/*
-===============
-MergeNodeFaces
-===============
-*/
-void MergeNodeFaces (node_t *node)
-{
- face_t *f1, *f2, *end;
- face_t *merged;
- plane_t *plane;
-
- plane = &mapplanes[node->planenum];
- merged = NULL;
-
- for (f1 = node->faces ; f1 ; f1 = f1->next)
- {
- if (f1->merged || f1->split[0] || f1->split[1])
- continue;
-
- for (f2 = node->faces ; f2 != f1 ; f2=f2->next)
- {
- if (f2->merged || f2->split[0] || f2->split[1])
- continue;
-
- //IDBUG: always passes the face's node's normal to TryMerge()
- //regardless of which side the face is on. Approximately 50% of
- //the time the face will be on the other side of node, and thus
- //the result of the convex/concave test in TryMergeWinding(),
- //which depends on the normal, is flipped. This causes faces
- //that shouldn't be merged to be merged and faces that
- //should be merged to not be merged.
- //the following added line fixes this bug
- //thanks to: Alexander Malmberg <alexander@malmberg.org>
- plane = &mapplanes[f1->planenum];
- //
- merged = TryMerge (f1, f2, plane->normal);
- if (!merged)
- continue;
-
- // add merged to the end of the node face list
- // so it will be checked against all the faces again
- for (end = node->faces ; end->next ; end = end->next)
- ;
- merged->next = NULL;
- end->next = merged;
- break;
- }
- }
-}
-
-//=====================================================================
-
-/*
-===============
-SubdivideFace
-
-Chop up faces that are larger than we want in the surface cache
-===============
-*/
-void SubdivideFace (node_t *node, face_t *f)
-{
- float mins, maxs;
- vec_t v;
- int axis, i;
- texinfo_t *tex;
- vec3_t temp;
- vec_t dist;
- winding_t *w, *frontw, *backw;
-
- if (f->merged)
- return;
-
-// special (non-surface cached) faces don't need subdivision
- tex = &texinfo[f->texinfo];
-
- if ( tex->flags & (SURF_WARP|SURF_SKY) )
- {
- return;
- }
-
- for (axis = 0 ; axis < 2 ; axis++)
- {
- while (1)
- {
- mins = 999999;
- maxs = -999999;
-
- VectorCopy (tex->vecs[axis], temp);
- w = f->w;
- for (i=0 ; i<w->numpoints ; i++)
- {
- v = DotProduct (w->p[i], temp);
- if (v < mins)
- mins = v;
- if (v > maxs)
- maxs = v;
- }
-#if 0
- if (maxs - mins <= 0)
- Error ("zero extents");
-#endif
- if (axis == 2)
- { // allow double high walls
- if (maxs - mins <= subdivide_size/* *2 */)
- break;
- }
- else if (maxs - mins <= subdivide_size)
- break;
-
- // split it
- c_subdivide++;
-
- v = VectorNormalize (temp);
-
- dist = (mins + subdivide_size - 16)/v;
-
- ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);
- if (!frontw || !backw)
- Error ("SubdivideFace: didn't split the polygon");
-
- f->split[0] = NewFaceFromFace (f);
- f->split[0]->w = frontw;
- f->split[0]->next = node->faces;
- node->faces = f->split[0];
-
- f->split[1] = NewFaceFromFace (f);
- f->split[1]->w = backw;
- f->split[1]->next = node->faces;
- node->faces = f->split[1];
-
- SubdivideFace (node, f->split[0]);
- SubdivideFace (node, f->split[1]);
- return;
- }
- }
-}
-
-void SubdivideNodeFaces (node_t *node)
-{
- face_t *f;
-
- for (f = node->faces ; f ; f=f->next)
- {
- SubdivideFace (node, f);
- }
-}
-
-//===========================================================================
-
-int c_nodefaces;
-
-
-/*
-============
-FaceFromPortal
-
-============
-*/
-face_t *FaceFromPortal (portal_t *p, int pside)
-{
- face_t *f;
- side_t *side;
-
- side = p->side;
- if (!side)
- return NULL; // portal does not bridge different visible contents
-
- f = AllocFace ();
-
- f->texinfo = side->texinfo;
- f->planenum = (side->planenum & ~1) | pside;
- f->portal = p;
-
- if ( (p->nodes[pside]->contents & CONTENTS_WINDOW)
- && VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents) == CONTENTS_WINDOW )
- return NULL; // don't show insides of windows
-
- if (pside)
- {
- f->w = ReverseWinding(p->winding);
- f->contents = p->nodes[1]->contents;
- }
- else
- {
- f->w = CopyWinding(p->winding);
- f->contents = p->nodes[0]->contents;
- }
- return f;
-}
-
-
-/*
-===============
-MakeFaces_r
-
-If a portal will make a visible face,
-mark the side that originally created it
-
- solid / empty : solid
- solid / water : solid
- water / empty : water
- water / water : none
-===============
-*/
-void MakeFaces_r (node_t *node)
-{
- portal_t *p;
- int s;
-
- // recurse down to leafs
- if (node->planenum != PLANENUM_LEAF)
- {
- MakeFaces_r (node->children[0]);
- MakeFaces_r (node->children[1]);
-
- // merge together all visible faces on the node
- if (!nomerge)
- MergeNodeFaces (node);
- if (!nosubdiv)
- SubdivideNodeFaces (node);
-
- return;
- }
-
- // solid leafs never have visible faces
- if (node->contents & CONTENTS_SOLID)
- return;
-
- // see which portals are valid
- for (p=node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
-
- p->face[s] = FaceFromPortal (p, s);
- if (p->face[s])
- {
- c_nodefaces++;
- p->face[s]->next = p->onnode->faces;
- p->onnode->faces = p->face[s];
- }
- }
-}
-
-/*
-============
-MakeFaces
-============
-*/
-void MakeFaces (node_t *node)
-{
- qprintf ("--- MakeFaces ---\n");
- c_merge = 0;
- c_subdivide = 0;
- c_nodefaces = 0;
-
- MakeFaces_r (node);
-
- qprintf ("%5i makefaces\n", c_nodefaces);
- qprintf ("%5i merged\n", c_merge);
- qprintf ("%5i subdivided\n", c_subdivide);
-}
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code 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. + +Quake III Arena source code 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// faces.c + +#include "qbsp.h" +#include "l_mem.h" + +/* + + some faces will be removed before saving, but still form nodes: + + the insides of sky volumes + meeting planes of different water current volumes + +*/ + +// undefine for dumb linear searches +#define USE_HASHING + +#define INTEGRAL_EPSILON 0.01 +#define POINT_EPSILON 0.5 +#define OFF_EPSILON 0.5 + +int c_merge; +int c_subdivide; + +int c_totalverts; +int c_uniqueverts; +int c_degenerate; +int c_tjunctions; +int c_faceoverflows; +int c_facecollapse; +int c_badstartverts; + +#define MAX_SUPERVERTS 512 +int superverts[MAX_SUPERVERTS]; +int numsuperverts; + +face_t *edgefaces[MAX_MAP_EDGES][2]; +int firstmodeledge = 1; +int firstmodelface; + +int c_tryedges; + +vec3_t edge_dir; +vec3_t edge_start; +vec_t edge_len; + +int num_edge_verts; +int edge_verts[MAX_MAP_VERTS]; + +face_t *NewFaceFromFace (face_t *f); + +//=========================================================================== + +typedef struct hashvert_s +{ + struct hashvert_s *next; + int num; +} hashvert_t; + + +#define HASH_SIZE 64 + + +int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain +int hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts + +face_t *edgefaces[MAX_MAP_EDGES][2]; + +//============================================================================ + + +unsigned HashVec (vec3_t vec) +{ + int x, y; + + x = (4096 + (int)(vec[0]+0.5)) >> 7; + y = (4096 + (int)(vec[1]+0.5)) >> 7; + + if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE ) + Error ("HashVec: point outside valid range"); + + return y*HASH_SIZE + x; +} + +#ifdef USE_HASHING +/* +============= +GetVertex + +Uses hashing +============= +*/ +int GetVertexnum (vec3_t in) +{ + int h; + int i; + float *p; + vec3_t vert; + int vnum; + + c_totalverts++; + + for (i=0 ; i<3 ; i++) + { + if ( fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON) + vert[i] = Q_rint(in[i]); + else + vert[i] = in[i]; + } + + h = HashVec (vert); + + for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum]) + { + p = dvertexes[vnum].point; + if ( fabs(p[0]-vert[0])<POINT_EPSILON + && fabs(p[1]-vert[1])<POINT_EPSILON + && fabs(p[2]-vert[2])<POINT_EPSILON ) + return vnum; + } + +// emit a vertex + if (numvertexes == MAX_MAP_VERTS) + Error ("numvertexes == MAX_MAP_VERTS"); + + dvertexes[numvertexes].point[0] = vert[0]; + dvertexes[numvertexes].point[1] = vert[1]; + dvertexes[numvertexes].point[2] = vert[2]; + + vertexchain[numvertexes] = hashverts[h]; + hashverts[h] = numvertexes; + + c_uniqueverts++; + + numvertexes++; + + return numvertexes-1; +} +#else +/* +================== +GetVertexnum + +Dumb linear search +================== +*/ +int GetVertexnum (vec3_t v) +{ + int i, j; + dvertex_t *dv; + vec_t d; + + c_totalverts++; + + // make really close values exactly integral + for (i=0 ; i<3 ; i++) + { + if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON ) + v[i] = (int)(v[i]+0.5); + if (v[i] < -4096 || v[i] > 4096) + Error ("GetVertexnum: outside +/- 4096"); + } + + // search for an existing vertex match + for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++) + { + for (j=0 ; j<3 ; j++) + { + d = v[j] - dv->point[j]; + if ( d > POINT_EPSILON || d < -POINT_EPSILON) + break; + } + if (j == 3) + return i; // a match + } + + // new point + if (numvertexes == MAX_MAP_VERTS) + Error ("MAX_MAP_VERTS"); + VectorCopy (v, dv->point); + numvertexes++; + c_uniqueverts++; + + return numvertexes-1; +} +#endif + + +/* +================== +FaceFromSuperverts + +The faces vertexes have been added to the superverts[] array, +and there may be more there than can be held in a face (MAXEDGES). + +If less, the faces vertexnums[] will be filled in, otherwise +face will reference a tree of split[] faces until all of the +vertexnums can be added. + +superverts[base] will become face->vertexnums[0], and the others +will be circularly filled in. +================== +*/ +void FaceFromSuperverts (node_t *node, face_t *f, int base) +{ + face_t *newf; + int remaining; + int i; + + remaining = numsuperverts; + while (remaining > MAXEDGES) + { // must split into two faces, because of vertex overload + c_faceoverflows++; + + newf = f->split[0] = NewFaceFromFace (f); + newf = f->split[0]; + newf->next = node->faces; + node->faces = newf; + + newf->numpoints = MAXEDGES; + for (i=0 ; i<MAXEDGES ; i++) + newf->vertexnums[i] = superverts[(i+base)%numsuperverts]; + + f->split[1] = NewFaceFromFace (f); + f = f->split[1]; + f->next = node->faces; + node->faces = f; + + remaining -= (MAXEDGES-2); + base = (base+MAXEDGES-1)%numsuperverts; + } + + // copy the vertexes back to the face + f->numpoints = remaining; + for (i=0 ; i<remaining ; i++) + f->vertexnums[i] = superverts[(i+base)%numsuperverts]; +} + + +/* +================== +EmitFaceVertexes +================== +*/ +void EmitFaceVertexes (node_t *node, face_t *f) +{ + winding_t *w; + int i; + + if (f->merged || f->split[0] || f->split[1]) + return; + + w = f->w; + for (i=0 ; i<w->numpoints ; i++) + { + if (noweld) + { // make every point unique + if (numvertexes == MAX_MAP_VERTS) + Error ("MAX_MAP_VERTS"); + superverts[i] = numvertexes; + VectorCopy (w->p[i], dvertexes[numvertexes].point); + numvertexes++; + c_uniqueverts++; + c_totalverts++; + } + else + superverts[i] = GetVertexnum (w->p[i]); + } + numsuperverts = w->numpoints; + + // this may fragment the face if > MAXEDGES + FaceFromSuperverts (node, f, 0); +} + +/* +================== +EmitVertexes_r +================== +*/ +void EmitVertexes_r (node_t *node) +{ + int i; + face_t *f; + + if (node->planenum == PLANENUM_LEAF) + return; + + for (f=node->faces ; f ; f=f->next) + { + EmitFaceVertexes (node, f); + } + + for (i=0 ; i<2 ; i++) + EmitVertexes_r (node->children[i]); +} + + +#ifdef USE_HASHING +/* +========== +FindEdgeVerts + +Uses the hash tables to cut down to a small number +========== +*/ +void FindEdgeVerts (vec3_t v1, vec3_t v2) +{ + int x1, x2, y1, y2, t; + int x, y; + int vnum; + +#if 0 +{ + int i; + num_edge_verts = numvertexes-1; + for (i=0 ; i<numvertexes-1 ; i++) + edge_verts[i] = i+1; +} +#endif + + x1 = (4096 + (int)(v1[0]+0.5)) >> 7; + y1 = (4096 + (int)(v1[1]+0.5)) >> 7; + x2 = (4096 + (int)(v2[0]+0.5)) >> 7; + y2 = (4096 + (int)(v2[1]+0.5)) >> 7; + + if (x1 > x2) + { + t = x1; + x1 = x2; + x2 = t; + } + if (y1 > y2) + { + t = y1; + y1 = y2; + y2 = t; + } +#if 0 + x1--; + x2++; + y1--; + y2++; + if (x1 < 0) + x1 = 0; + if (x2 >= HASH_SIZE) + x2 = HASH_SIZE; + if (y1 < 0) + y1 = 0; + if (y2 >= HASH_SIZE) + y2 = HASH_SIZE; +#endif + num_edge_verts = 0; + for (x=x1 ; x <= x2 ; x++) + { + for (y=y1 ; y <= y2 ; y++) + { + for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum]) + { + edge_verts[num_edge_verts++] = vnum; + } + } + } +} + +#else +/* +========== +FindEdgeVerts + +Forced a dumb check of everything +========== +*/ +void FindEdgeVerts (vec3_t v1, vec3_t v2) +{ + int i; + + num_edge_verts = numvertexes-1; + for (i=0 ; i<num_edge_verts ; i++) + edge_verts[i] = i+1; +} +#endif + +/* +========== +TestEdge + +Can be recursively reentered +========== +*/ +void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert) +{ + int j, k; + vec_t dist; + vec3_t delta; + vec3_t exact; + vec3_t off; + vec_t error; + vec3_t p; + + if (p1 == p2) + { + c_degenerate++; + return; // degenerate edge + } + + for (k=startvert ; k<num_edge_verts ; k++) + { + j = edge_verts[k]; + if (j==p1 || j == p2) + continue; + + VectorCopy (dvertexes[j].point, p); + + VectorSubtract (p, edge_start, delta); + dist = DotProduct (delta, edge_dir); + if (dist <=start || dist >= end) + continue; // off an end + VectorMA (edge_start, dist, edge_dir, exact); + VectorSubtract (p, exact, off); + error = VectorLength (off); + + if (fabs(error) > OFF_EPSILON) + continue; // not on the edge + + // break the edge + c_tjunctions++; + TestEdge (start, dist, p1, j, k+1); + TestEdge (dist, end, j, p2, k+1); + return; + } + + // the edge p1 to p2 is now free of tjunctions + if (numsuperverts >= MAX_SUPERVERTS) + Error ("MAX_SUPERVERTS"); + superverts[numsuperverts] = p1; + numsuperverts++; +} + +/* +================== +FixFaceEdges + +================== +*/ +void FixFaceEdges (node_t *node, face_t *f) +{ + int p1, p2; + int i; + vec3_t e2; + vec_t len; + int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS]; + int base; + + if (f->merged || f->split[0] || f->split[1]) + return; + + numsuperverts = 0; + + for (i=0 ; i<f->numpoints ; i++) + { + p1 = f->vertexnums[i]; + p2 = f->vertexnums[(i+1)%f->numpoints]; + + VectorCopy (dvertexes[p1].point, edge_start); + VectorCopy (dvertexes[p2].point, e2); + + FindEdgeVerts (edge_start, e2); + + VectorSubtract (e2, edge_start, edge_dir); + len = VectorNormalize(edge_dir); + + start[i] = numsuperverts; + TestEdge (0, len, p1, p2, 0); + + count[i] = numsuperverts - start[i]; + } + + if (numsuperverts < 3) + { // entire face collapsed + f->numpoints = 0; + c_facecollapse++; + return; + } + + // we want to pick a vertex that doesn't have tjunctions + // on either side, which can cause artifacts on trifans, + // especially underwater + for (i=0 ; i<f->numpoints ; i++) + { + if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1) + break; + } + if (i == f->numpoints) + { + f->badstartvert = true; + c_badstartverts++; + base = 0; + } + else + { // rotate the vertex order + base = start[i]; + } + + // this may fragment the face if > MAXEDGES + FaceFromSuperverts (node, f, base); +} + +/* +================== +FixEdges_r +================== +*/ +void FixEdges_r (node_t *node) +{ + int i; + face_t *f; + + if (node->planenum == PLANENUM_LEAF) + return; + + for (f=node->faces ; f ; f=f->next) + FixFaceEdges (node, f); + + for (i=0 ; i<2 ; i++) + FixEdges_r (node->children[i]); +} + +/* +=========== +FixTjuncs + +=========== +*/ +void FixTjuncs (node_t *headnode) +{ + // snap and merge all vertexes + qprintf ("---- snap verts ----\n"); + memset (hashverts, 0, sizeof(hashverts)); + c_totalverts = 0; + c_uniqueverts = 0; + c_faceoverflows = 0; + EmitVertexes_r (headnode); + qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts); + + // break edges on tjunctions + qprintf ("---- tjunc ----\n"); + c_tryedges = 0; + c_degenerate = 0; + c_facecollapse = 0; + c_tjunctions = 0; + if (!notjunc) + FixEdges_r (headnode); + qprintf ("%5i edges degenerated\n", c_degenerate); + qprintf ("%5i faces degenerated\n", c_facecollapse); + qprintf ("%5i edges added by tjunctions\n", c_tjunctions); + qprintf ("%5i faces added by tjunctions\n", c_faceoverflows); + qprintf ("%5i bad start verts\n", c_badstartverts); +} + + +//======================================================== + +int c_faces; + +face_t *AllocFace (void) +{ + face_t *f; + + f = GetMemory(sizeof(*f)); + memset (f, 0, sizeof(*f)); + c_faces++; + + return f; +} + +face_t *NewFaceFromFace (face_t *f) +{ + face_t *newf; + + newf = AllocFace (); + *newf = *f; + newf->merged = NULL; + newf->split[0] = newf->split[1] = NULL; + newf->w = NULL; + return newf; +} + +void FreeFace (face_t *f) +{ + if (f->w) + FreeWinding (f->w); + FreeMemory(f); + c_faces--; +} + +//======================================================== + +/* +================== +GetEdge + +Called by writebsp. +Don't allow four way edges +================== +*/ +int GetEdge2 (int v1, int v2, face_t *f) +{ + dedge_t *edge; + int i; + + c_tryedges++; + + if (!noshare) + { + for (i=firstmodeledge ; i < numedges ; i++) + { + edge = &dedges[i]; + if (v1 == edge->v[1] && v2 == edge->v[0] + && edgefaces[i][0]->contents == f->contents) + { + if (edgefaces[i][1]) + // printf ("WARNING: multiple backward edge\n"); + continue; + edgefaces[i][1] = f; + return -i; + } + #if 0 + if (v1 == edge->v[0] && v2 == edge->v[1]) + { + printf ("WARNING: multiple forward edge\n"); + return i; + } + #endif + } + } + +// emit an edge + if (numedges >= MAX_MAP_EDGES) + Error ("numedges == MAX_MAP_EDGES"); + edge = &dedges[numedges]; + numedges++; + edge->v[0] = v1; + edge->v[1] = v2; + edgefaces[numedges-1][0] = f; + + return numedges-1; +} + +/* +=========================================================================== + +FACE MERGING + +=========================================================================== +*/ + +/* +============= +TryMerge + +If two polygons share a common edge and the edges that meet at the +common points are both inside the other polygons, merge them + +Returns NULL if the faces couldn't be merged, or the new face. +The originals will NOT be freed. +============= +*/ +face_t *TryMerge (face_t *f1, face_t *f2, vec3_t planenormal) +{ + face_t *newf; + winding_t *nw; + + if (!f1->w || !f2->w) + return NULL; + if (f1->texinfo != f2->texinfo) + return NULL; + if (f1->planenum != f2->planenum) // on front and back sides + return NULL; + if (f1->contents != f2->contents) + return NULL; + + + nw = TryMergeWinding (f1->w, f2->w, planenormal); + if (!nw) + return NULL; + + c_merge++; + newf = NewFaceFromFace (f1); + newf->w = nw; + + f1->merged = newf; + f2->merged = newf; + + return newf; +} + +/* +=============== +MergeNodeFaces +=============== +*/ +void MergeNodeFaces (node_t *node) +{ + face_t *f1, *f2, *end; + face_t *merged; + plane_t *plane; + + plane = &mapplanes[node->planenum]; + merged = NULL; + + for (f1 = node->faces ; f1 ; f1 = f1->next) + { + if (f1->merged || f1->split[0] || f1->split[1]) + continue; + + for (f2 = node->faces ; f2 != f1 ; f2=f2->next) + { + if (f2->merged || f2->split[0] || f2->split[1]) + continue; + + //IDBUG: always passes the face's node's normal to TryMerge() + //regardless of which side the face is on. Approximately 50% of + //the time the face will be on the other side of node, and thus + //the result of the convex/concave test in TryMergeWinding(), + //which depends on the normal, is flipped. This causes faces + //that shouldn't be merged to be merged and faces that + //should be merged to not be merged. + //the following added line fixes this bug + //thanks to: Alexander Malmberg <alexander@malmberg.org> + plane = &mapplanes[f1->planenum]; + // + merged = TryMerge (f1, f2, plane->normal); + if (!merged) + continue; + + // add merged to the end of the node face list + // so it will be checked against all the faces again + for (end = node->faces ; end->next ; end = end->next) + ; + merged->next = NULL; + end->next = merged; + break; + } + } +} + +//===================================================================== + +/* +=============== +SubdivideFace + +Chop up faces that are larger than we want in the surface cache +=============== +*/ +void SubdivideFace (node_t *node, face_t *f) +{ + float mins, maxs; + vec_t v; + int axis, i; + texinfo_t *tex; + vec3_t temp; + vec_t dist; + winding_t *w, *frontw, *backw; + + if (f->merged) + return; + +// special (non-surface cached) faces don't need subdivision + tex = &texinfo[f->texinfo]; + + if ( tex->flags & (SURF_WARP|SURF_SKY) ) + { + return; + } + + for (axis = 0 ; axis < 2 ; axis++) + { + while (1) + { + mins = 999999; + maxs = -999999; + + VectorCopy (tex->vecs[axis], temp); + w = f->w; + for (i=0 ; i<w->numpoints ; i++) + { + v = DotProduct (w->p[i], temp); + if (v < mins) + mins = v; + if (v > maxs) + maxs = v; + } +#if 0 + if (maxs - mins <= 0) + Error ("zero extents"); +#endif + if (axis == 2) + { // allow double high walls + if (maxs - mins <= subdivide_size/* *2 */) + break; + } + else if (maxs - mins <= subdivide_size) + break; + + // split it + c_subdivide++; + + v = VectorNormalize (temp); + + dist = (mins + subdivide_size - 16)/v; + + ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw); + if (!frontw || !backw) + Error ("SubdivideFace: didn't split the polygon"); + + f->split[0] = NewFaceFromFace (f); + f->split[0]->w = frontw; + f->split[0]->next = node->faces; + node->faces = f->split[0]; + + f->split[1] = NewFaceFromFace (f); + f->split[1]->w = backw; + f->split[1]->next = node->faces; + node->faces = f->split[1]; + + SubdivideFace (node, f->split[0]); + SubdivideFace (node, f->split[1]); + return; + } + } +} + +void SubdivideNodeFaces (node_t *node) +{ + face_t *f; + + for (f = node->faces ; f ; f=f->next) + { + SubdivideFace (node, f); + } +} + +//=========================================================================== + +int c_nodefaces; + + +/* +============ +FaceFromPortal + +============ +*/ +face_t *FaceFromPortal (portal_t *p, int pside) +{ + face_t *f; + side_t *side; + + side = p->side; + if (!side) + return NULL; // portal does not bridge different visible contents + + f = AllocFace (); + + f->texinfo = side->texinfo; + f->planenum = (side->planenum & ~1) | pside; + f->portal = p; + + if ( (p->nodes[pside]->contents & CONTENTS_WINDOW) + && VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents) == CONTENTS_WINDOW ) + return NULL; // don't show insides of windows + + if (pside) + { + f->w = ReverseWinding(p->winding); + f->contents = p->nodes[1]->contents; + } + else + { + f->w = CopyWinding(p->winding); + f->contents = p->nodes[0]->contents; + } + return f; +} + + +/* +=============== +MakeFaces_r + +If a portal will make a visible face, +mark the side that originally created it + + solid / empty : solid + solid / water : solid + water / empty : water + water / water : none +=============== +*/ +void MakeFaces_r (node_t *node) +{ + portal_t *p; + int s; + + // recurse down to leafs + if (node->planenum != PLANENUM_LEAF) + { + MakeFaces_r (node->children[0]); + MakeFaces_r (node->children[1]); + + // merge together all visible faces on the node + if (!nomerge) + MergeNodeFaces (node); + if (!nosubdiv) + SubdivideNodeFaces (node); + + return; + } + + // solid leafs never have visible faces + if (node->contents & CONTENTS_SOLID) + return; + + // see which portals are valid + for (p=node->portals ; p ; p = p->next[s]) + { + s = (p->nodes[1] == node); + + p->face[s] = FaceFromPortal (p, s); + if (p->face[s]) + { + c_nodefaces++; + p->face[s]->next = p->onnode->faces; + p->onnode->faces = p->face[s]; + } + } +} + +/* +============ +MakeFaces +============ +*/ +void MakeFaces (node_t *node) +{ + qprintf ("--- MakeFaces ---\n"); + c_merge = 0; + c_subdivide = 0; + c_nodefaces = 0; + + MakeFaces_r (node); + + qprintf ("%5i makefaces\n", c_nodefaces); + qprintf ("%5i merged\n", c_merge); + qprintf ("%5i subdivided\n", c_subdivide); +} |