/* Copyright (C) 1996-1997 Id Software, Inc. 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 See file, 'COPYING', for details. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif static __attribute__ ((used)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include #endif #include "QF/sys.h" #include "compat.h" #include "brush.h" #include "bsp5.h" #include "options.h" #include "winding.h" #include "tjunc.h" typedef struct wvert_s { vec_t t; struct wvert_s *prev, *next; } wvert_t; typedef struct wedge_s { struct wedge_s *next; vec3_t dir; vec3_t origin; wvert_t head; } wedge_t; int numwedges, numwverts; int tjuncs; int tjuncfaces; int degenerdges; #define MAXWVERTS 0x20000 #define MAXWEDGES 0x10000 wvert_t wverts[MAXWVERTS]; wedge_t wedges[MAXWEDGES]; #define NUM_HASH 1024 wedge_t *wedge_hash[NUM_HASH]; static vec3_t hash_min, hash_scale; /** Initialize the edge hash table. \param mins The minimum of the bounding box in which the edges reside. \param maxs The maximum of the bounding box in which the edges reside. */ static void InitHash (vec3_t mins, vec3_t maxs) { int newsize[2]; vec3_t size; vec_t volume; vec_t scale; VectorCopy (mins, hash_min); VectorSubtract (maxs, mins, size); memset (wedge_hash, 0, sizeof (wedge_hash)); volume = size[0] * size[1]; scale = sqrt (volume / NUM_HASH); newsize[0] = size[0] / scale; newsize[1] = size[1] / scale; hash_scale[0] = newsize[0] / size[0]; hash_scale[1] = newsize[1] / size[1]; hash_scale[2] = newsize[1]; } /** Calulate the hash value of a vector. \param vec The vector for which to calculate the hash value. \return The hash value of the vector. */ static unsigned HashVec (vec3_t vec) { unsigned h; h = hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2] + hash_scale[1] * (vec[1] - hash_min[1]); if (h >= NUM_HASH) return NUM_HASH - 1; return h; } /** Make the vector canonical. A canonical vector is a unit vector with the first non-zero axis positive. \param vec The vector to make canonical. \return false is the vector is (0, 0, 0), otherwise true. */ static qboolean CanonicalVector (vec3_t vec) { vec_t len = _VectorNormalize (vec); if (len < EQUAL_EPSILON) return false; if (vec[0] > EQUAL_EPSILON) { return true; } else if (vec[0] < -EQUAL_EPSILON) { VectorNegate (vec, vec); return true; } else { vec[0] = 0; } if (vec[1] > EQUAL_EPSILON) { return true; } else if (vec[1] < -EQUAL_EPSILON) { VectorNegate (vec, vec); return true; } else { vec[1] = 0; } if (vec[2] > EQUAL_EPSILON) { return true; } else if (vec[2] < -EQUAL_EPSILON) { VectorNegate (vec, vec); return true; } else { vec[2] = 0; } return false; } /** Find a wedge for the two points. A wedge is an infinitely long line passing through the two points with an "origin" at t=0. \param p1 The first point. \param p2 The second point. \param t1 The "time" of one of the points. \param t2 The "time" of the other point. \return Pointer to the wedge, or NULL for a degenerate edge (zero length). \note t2 will always be greater than t1, so there's no guarantee which point is represented by t1 and t2. */ static wedge_t * FindEdge (vec3_t p1, vec3_t p2, vec_t *t1, vec_t *t2) { int h; vec3_t dir, origin; vec_t temp; wedge_t *w; VectorSubtract (p2, p1, dir); if (!CanonicalVector (dir)) { degenerdges++; return 0; } *t1 = DotProduct (p1, dir); *t2 = DotProduct (p2, dir); VectorMultSub (p1, *t1, dir, origin); if (*t1 > *t2) { // swap t1 and t2 temp = *t1; *t1 = *t2; *t2 = temp; } h = HashVec (origin); for (w = wedge_hash[h]; w; w = w->next) { if (!_VectorCompare (w->origin, origin)) continue; if (!_VectorCompare (w->dir, dir)) continue; return w; } // create a new wedge if (numwedges == MAXWEDGES) Sys_Error ("FindEdge: numwedges == MAXWEDGES"); w = &wedges[numwedges]; numwedges++; w->next = wedge_hash[h]; wedge_hash[h] = w; VectorCopy (origin, w->origin); VectorCopy (dir, w->dir); w->head.next = w->head.prev = &w->head; w->head.t = 99999; return w; } #define T_EPSILON 0.01 /** Add a wvert to the wedge. A wvert is a vertex on the wedge and is specified by the time along the wedge from the wedge's origin. If a wvert with the same time already exists, a new one will not be added. \param w The wedge to which the wvert will be added. \param t The "time" of the wvert. */ static void AddVert (wedge_t *w, vec_t t) { wvert_t *v, *newv; v = w->head.next; do { if (fabs (v->t - t) < T_EPSILON) return; if (v->t > t) break; v = v->next; } while (1); // insert a new wvert before v if (numwverts == MAXWVERTS) Sys_Error ("AddVert: numwverts == MAXWVERTS"); newv = &wverts[numwverts]; numwverts++; newv->t = t; newv->next = v; newv->prev = v->prev; v->prev->next = newv; v->prev = newv; } /** Add a faces edge to the wedge system. \param p1 The first point of the face's edge. \param p2 The second point of the face's edge. */ static void AddEdge (vec3_t p1, vec3_t p2) { wedge_t *w; vec_t t1, t2; if ((w = FindEdge (p1, p2, &t1, &t2))) { AddVert (w, t1); AddVert (w, t2); } } /** Add the edges from the face. \param f The face from which to add the faces. */ static void AddFaceEdges (face_t *f) { int i, j; for (i = 0; i < f->points->numpoints; i++) { j = (i + 1) % f->points->numpoints; AddEdge (f->points->points[i], f->points->points[j]); } } face_t *newlist; /** Check and fix the edges of the face. If any vertices from other faces lie on an edge of the face between the edge's end points, add matching vertices along that edge. \param f The face of which the edges will be checked. */ static void FixFaceEdges (face_t *f) { int i, j, k; vec_t t1, t2; wedge_t *w; winding_t *fp = f->points; wvert_t *v; restart: for (i = 0; i < fp->numpoints; i++) { j = (i + 1) % fp->numpoints; if (!(w = FindEdge (fp->points[i], fp->points[j], &t1, &t2))) continue; for (v = w->head.next; v->t < t1 + T_EPSILON; v = v->next) { } if (v->t < t2 - T_EPSILON) { tjuncs++; // insert a new vertex here fp = realloc (fp, field_offset (winding_t, points[fp->numpoints + 1])); for (k = fp->numpoints; k > j; k--) { VectorCopy (fp->points[k - 1], fp->points[k]); } VectorMultAdd (w->origin, v->t, w->dir, fp->points[j]); fp->numpoints++; goto restart; } } f->points = fp; f->next = newlist; newlist = f; } static void tjunc_find_r (node_t *node) { face_t *f; if (node->planenum == PLANENUM_LEAF) return; for (f = node->faces; f; f = f->next) if (f->texturenum >= 0) AddFaceEdges (f); tjunc_find_r (node->children[0]); tjunc_find_r (node->children[1]); } /** Check and fix the edges from the faces in the bsp tree. \param node The current node in the bsp tree. */ static void tjunc_fix_r (node_t *node) { face_t *next, *f; if (node->planenum == PLANENUM_LEAF) return; newlist = NULL; for (f = node->faces; f; f = next) { next = f->next; if (f->texturenum < 0) continue; FixFaceEdges (f); } node->faces = newlist; tjunc_fix_r (node->children[0]); tjunc_fix_r (node->children[1]); } void tjunc (node_t *headnode) { int i; vec3_t maxs, mins; qprintf ("---- tjunc ----\n"); if (options.notjunc) return; // origin points won't allways be inside the map, so extend the hash area for (i = 0; i < 3; i++) { if (fabs (brushset->maxs[i]) > fabs (brushset->mins[i])) maxs[i] = fabs (brushset->maxs[i]); else maxs[i] = fabs (brushset->mins[i]); } VectorNegate (maxs, mins); InitHash (mins, maxs); numwedges = numwverts = 0; // identify all points on common edges tjunc_find_r (headnode); qprintf ("%i world edges %i edge points\n", numwedges, numwverts); // add extra vertexes on edges where needed tjuncs = tjuncfaces = 0; degenerdges = 0; tjunc_fix_r (headnode); qprintf ("%i degenerate edges\n", degenerdges); qprintf ("%i edges added by tjunctions\n", tjuncs); qprintf ("%i faces added by tjunctions\n", tjuncfaces); }