diff options
Diffstat (limited to 'code/bspc/map_q1.c')
-rwxr-xr-x | code/bspc/map_q1.c | 2348 |
1 files changed, 1174 insertions, 1174 deletions
diff --git a/code/bspc/map_q1.c b/code/bspc/map_q1.c index 772a358..fa9d117 100755 --- a/code/bspc/map_q1.c +++ b/code/bspc/map_q1.c @@ -1,1174 +1,1174 @@ -/*
-===========================================================================
-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
-===========================================================================
-*/
-
-#include "qbsp.h"
-#include "l_bsp_q1.h"
-#include "aas_map.h" //AAS_CreateMapBrushes
-
-int q1_numbrushes;
-int q1_numclipbrushes;
-
-//#define Q1_PRINT
-
-//===========================================================================
-// water, slime and lava brush textures names always start with a *
-// followed by the type: "slime", "lava" or otherwise water
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Q1_TextureContents(char *name)
-{
- if (!Q_strcasecmp(name, "clip")) return CONTENTS_SOLID;
- if (name[0] == '*')
- {
- if (!Q_strncasecmp(name+1,"lava",4)) return CONTENTS_LAVA;
- else if (!Q_strncasecmp(name+1,"slime",5)) return CONTENTS_SLIME;
- else return CONTENTS_WATER;
- } //end if
- else if (!Q_strncasecmp(name, "sky", 3)) return CONTENTS_SOLID;
- else return CONTENTS_SOLID;
-} //end of the function Q1_TextureContents
-//===========================================================================
-// Generates two new brushes, leaving the original
-// unchanged
-//
-// modified for Half-Life because there are quite a lot of tiny node leaves
-// in the Half-Life bsps
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Q1_SplitBrush(bspbrush_t *brush, int planenum, int nodenum,
- bspbrush_t **front, bspbrush_t **back)
-{
- bspbrush_t *b[2];
- int i, j;
- winding_t *w, *cw[2], *midwinding;
- plane_t *plane, *plane2;
- side_t *s, *cs;
- float d, d_front, d_back;
-
- *front = *back = NULL;
- plane = &mapplanes[planenum];
-
- // check all points
- d_front = d_back = 0;
- for (i=0 ; i<brush->numsides ; i++)
- {
- w = brush->sides[i].winding;
- if (!w)
- continue;
- for (j=0 ; j<w->numpoints ; j++)
- {
- d = DotProduct (w->p[j], plane->normal) - plane->dist;
- if (d > 0 && d > d_front)
- d_front = d;
- if (d < 0 && d < d_back)
- d_back = d;
- } //end for
- } //end for
-
- if (d_front < 0.1) // PLANESIDE_EPSILON)
- { // only on back
- *back = CopyBrush (brush);
- Log_Print("Q1_SplitBrush: only on back\n");
- return;
- } //end if
- if (d_back > -0.1) // PLANESIDE_EPSILON)
- { // only on front
- *front = CopyBrush (brush);
- Log_Print("Q1_SplitBrush: only on front\n");
- return;
- } //end if
-
- // create a new winding from the split plane
-
- w = BaseWindingForPlane (plane->normal, plane->dist);
- for (i = 0; i < brush->numsides && w; i++)
- {
- plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
- ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
- } //end for
-
- if (!w || WindingIsTiny(w))
- { // the brush isn't really split
- int side;
-
- Log_Print("Q1_SplitBrush: no split winding\n");
- side = BrushMostlyOnSide (brush, plane);
- if (side == PSIDE_FRONT)
- *front = CopyBrush (brush);
- if (side == PSIDE_BACK)
- *back = CopyBrush (brush);
- return;
- }
-
- if (WindingIsHuge(w))
- {
- Log_Print("Q1_SplitBrush: WARNING huge split winding\n");
- } //end of
-
- midwinding = w;
-
- // split it for real
-
- for (i = 0; i < 2; i++)
- {
- b[i] = AllocBrush (brush->numsides+1);
- b[i]->original = brush->original;
- } //end for
-
- // split all the current windings
-
- for (i=0 ; i<brush->numsides ; i++)
- {
- s = &brush->sides[i];
- w = s->winding;
- if (!w)
- continue;
- ClipWindingEpsilon (w, plane->normal, plane->dist,
- 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
- for (j=0 ; j<2 ; j++)
- {
- if (!cw[j])
- continue;
-#if 0
- if (WindingIsTiny (cw[j]))
- {
- FreeWinding (cw[j]);
- continue;
- }
-#endif
- cs = &b[j]->sides[b[j]->numsides];
- b[j]->numsides++;
- *cs = *s;
-// cs->planenum = s->planenum;
-// cs->texinfo = s->texinfo;
-// cs->visible = s->visible;
-// cs->original = s->original;
- cs->winding = cw[j];
- cs->flags &= ~SFL_TESTED;
- } //end for
- } //end for
-
-
- // see if we have valid polygons on both sides
-
- for (i=0 ; i<2 ; i++)
- {
- BoundBrush (b[i]);
- for (j=0 ; j<3 ; j++)
- {
- if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096)
- {
- Log_Print("Q1_SplitBrush: bogus brush after clip\n");
- break;
- } //end if
- } //end for
-
- if (b[i]->numsides < 3 || j < 3)
- {
- FreeBrush (b[i]);
- b[i] = NULL;
- Log_Print("Q1_SplitBrush: numsides < 3\n");
- } //end if
- } //end for
-
- if ( !(b[0] && b[1]) )
- {
- if (!b[0] && !b[1])
- Log_Print("Q1_SplitBrush: split removed brush\n");
- else
- Log_Print("Q1_SplitBrush: split not on both sides\n");
- if (b[0])
- {
- FreeBrush (b[0]);
- *front = CopyBrush (brush);
- } //end if
- if (b[1])
- {
- FreeBrush (b[1]);
- *back = CopyBrush (brush);
- } //end if
- return;
- } //end if
-
- // add the midwinding to both sides
- for (i = 0; i < 2; i++)
- {
- cs = &b[i]->sides[b[i]->numsides];
- b[i]->numsides++;
-
- cs->planenum = planenum^i^1;
- cs->texinfo = 0;
- //store the node number in the surf to find the texinfo later on
- cs->surf = nodenum;
- //
- cs->flags &= ~SFL_VISIBLE;
- cs->flags &= ~SFL_TESTED;
- cs->flags &= ~SFL_TEXTURED;
- if (i==0)
- cs->winding = CopyWinding (midwinding);
- else
- cs->winding = midwinding;
- } //end for
-
-
-{
- vec_t v1;
- int i;
-
- for (i=0 ; i<2 ; i++)
- {
- v1 = BrushVolume (b[i]);
- if (v1 < 1)
- {
- FreeBrush (b[i]);
- b[i] = NULL;
- Log_Print("Q1_SplitBrush: tiny volume after clip\n");
- } //end if
- } //end for
-} //*/
-
- *front = b[0];
- *back = b[1];
-} //end of the function Q1_SplitBrush
-//===========================================================================
-// returns true if the tree starting at nodenum has only solid leaves
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-int Q1_SolidTree_r(int nodenum)
-{
- if (nodenum < 0)
- {
- switch(q1_dleafs[(-nodenum) - 1].contents)
- {
- case Q1_CONTENTS_EMPTY:
- {
- return false;
- } //end case
- case Q1_CONTENTS_SOLID:
-#ifdef HLCONTENTS
- case Q1_CONTENTS_CLIP:
-#endif HLCONTENTS
- case Q1_CONTENTS_SKY:
-#ifdef HLCONTENTS
- case Q1_CONTENTS_TRANSLUCENT:
-#endif HLCONTENTS
- {
- return true;
- } //end case
- case Q1_CONTENTS_WATER:
- case Q1_CONTENTS_SLIME:
- case Q1_CONTENTS_LAVA:
-#ifdef HLCONTENTS
- //these contents should not be found in the BSP
- case Q1_CONTENTS_ORIGIN:
- case Q1_CONTENTS_CURRENT_0:
- case Q1_CONTENTS_CURRENT_90:
- case Q1_CONTENTS_CURRENT_180:
- case Q1_CONTENTS_CURRENT_270:
- case Q1_CONTENTS_CURRENT_UP:
- case Q1_CONTENTS_CURRENT_DOWN:
-#endif HLCONTENTS
- default:
- {
- return false;
- } //end default
- } //end switch
- return false;
- } //end if
- if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[0])) return false;
- if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[1])) return false;
- return true;
-} //end of the function Q1_SolidTree_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bspbrush_t *Q1_CreateBrushes_r(bspbrush_t *brush, int nodenum)
-{
- int planenum;
- bspbrush_t *front, *back;
- q1_dleaf_t *leaf;
-
- //if it is a leaf
- if (nodenum < 0)
- {
- leaf = &q1_dleafs[(-nodenum) - 1];
- if (leaf->contents != Q1_CONTENTS_EMPTY)
- {
-#ifdef Q1_PRINT
- qprintf("\r%5i", ++q1_numbrushes);
-#endif //Q1_PRINT
- } //end if
- switch(leaf->contents)
- {
- case Q1_CONTENTS_EMPTY:
- {
- FreeBrush(brush);
- return NULL;
- } //end case
- case Q1_CONTENTS_SOLID:
-#ifdef HLCONTENTS
- case Q1_CONTENTS_CLIP:
-#endif HLCONTENTS
- case Q1_CONTENTS_SKY:
-#ifdef HLCONTENTS
- case Q1_CONTENTS_TRANSLUCENT:
-#endif HLCONTENTS
- {
- brush->side = CONTENTS_SOLID;
- return brush;
- } //end case
- case Q1_CONTENTS_WATER:
- {
- brush->side = CONTENTS_WATER;
- return brush;
- } //end case
- case Q1_CONTENTS_SLIME:
- {
- brush->side = CONTENTS_SLIME;
- return brush;
- } //end case
- case Q1_CONTENTS_LAVA:
- {
- brush->side = CONTENTS_LAVA;
- return brush;
- } //end case
-#ifdef HLCONTENTS
- //these contents should not be found in the BSP
- case Q1_CONTENTS_ORIGIN:
- case Q1_CONTENTS_CURRENT_0:
- case Q1_CONTENTS_CURRENT_90:
- case Q1_CONTENTS_CURRENT_180:
- case Q1_CONTENTS_CURRENT_270:
- case Q1_CONTENTS_CURRENT_UP:
- case Q1_CONTENTS_CURRENT_DOWN:
- {
- Error("Q1_CreateBrushes_r: found contents %d in Half-Life BSP", leaf->contents);
- return NULL;
- } //end case
-#endif HLCONTENTS
- default:
- {
- Error("Q1_CreateBrushes_r: unknown contents %d in Half-Life BSP", leaf->contents);
- return NULL;
- } //end default
- } //end switch
- return NULL;
- } //end if
- //if the rest of the tree is solid
- /*if (Q1_SolidTree_r(nodenum))
- {
- brush->side = CONTENTS_SOLID;
- return brush;
- } //end if*/
- //
- planenum = q1_dnodes[nodenum].planenum;
- planenum = FindFloatPlane(q1_dplanes[planenum].normal, q1_dplanes[planenum].dist);
- //split the brush with the node plane
- Q1_SplitBrush(brush, planenum, nodenum, &front, &back);
- //free the original brush
- FreeBrush(brush);
- //every node must split the brush in two
- if (!front || !back)
- {
- Log_Print("Q1_CreateBrushes_r: WARNING node not splitting brush\n");
- //return NULL;
- } //end if
- //create brushes recursively
- if (front) front = Q1_CreateBrushes_r(front, q1_dnodes[nodenum].children[0]);
- if (back) back = Q1_CreateBrushes_r(back, q1_dnodes[nodenum].children[1]);
- //link the brushes if possible and return them
- if (front)
- {
- for (brush = front; brush->next; brush = brush->next);
- brush->next = back;
- return front;
- } //end if
- else
- {
- return back;
- } //end else
-} //end of the function Q1_CreateBrushes_r
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bspbrush_t *Q1_CreateBrushesFromBSP(int modelnum)
-{
- bspbrush_t *brushlist;
- bspbrush_t *brush;
- q1_dnode_t *headnode;
- vec3_t mins, maxs;
- int i;
-
- //
- headnode = &q1_dnodes[q1_dmodels[modelnum].headnode[0]];
- //get the mins and maxs of the world
- VectorCopy(headnode->mins, mins);
- VectorCopy(headnode->maxs, maxs);
- //enlarge these mins and maxs
- for (i = 0; i < 3; i++)
- {
- mins[i] -= 8;
- maxs[i] += 8;
- } //end for
- //NOTE: have to add the BSP tree mins and maxs to the MAP mins and maxs
- AddPointToBounds(mins, map_mins, map_maxs);
- AddPointToBounds(maxs, map_mins, map_maxs);
- //
- if (!modelnum)
- {
- Log_Print("brush size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n",
- map_mins[0], map_mins[1], map_mins[2],
- map_maxs[0], map_maxs[1], map_maxs[2]);
- } //end if
- //create one huge brush containing the whole world
- brush = BrushFromBounds(mins, maxs);
- VectorCopy(mins, brush->mins);
- VectorCopy(maxs, brush->maxs);
- //
-#ifdef Q1_PRINT
- qprintf("creating Quake brushes\n");
- qprintf("%5d brushes", q1_numbrushes = 0);
-#endif //Q1_PRINT
- //create the brushes
- brushlist = Q1_CreateBrushes_r(brush, q1_dmodels[modelnum].headnode[0]);
- //
-#ifdef Q1_PRINT
- qprintf("\n");
-#endif //Q1_PRINT
- //now we've got a list with brushes!
- return brushlist;
-} //end of the function Q1_CreateBrushesFromBSP
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-q1_dleaf_t *Q1_PointInLeaf(int startnode, vec3_t point)
-{
- int nodenum;
- vec_t dist;
- q1_dnode_t *node;
- q1_dplane_t *plane;
-
- nodenum = startnode;
- while (nodenum >= 0)
- {
- node = &q1_dnodes[nodenum];
- plane = &q1_dplanes[node->planenum];
- dist = DotProduct(point, plane->normal) - plane->dist;
- if (dist > 0)
- nodenum = node->children[0];
- else
- nodenum = node->children[1];
- } //end while
-
- return &q1_dleafs[-nodenum - 1];
-} //end of the function Q1_PointInLeaf
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float Q1_FaceArea(q1_dface_t *face)
-{
- int i;
- float total;
- vec_t *v;
- vec3_t d1, d2, cross;
- q1_dedge_t *edge;
-
- edge = &q1_dedges[face->firstedge];
- v = q1_dvertexes[edge->v[0]].point;
-
- total = 0;
- for (i = 1; i < face->numedges - 1; i++)
- {
- edge = &q1_dedges[face->firstedge + i];
- VectorSubtract(q1_dvertexes[edge->v[0]].point, v, d1);
- VectorSubtract(q1_dvertexes[edge->v[1]].point, v, d2);
- CrossProduct(d1, d2, cross);
- total += 0.5 * VectorLength(cross);
- } //end for
- return total;
-} //end of the function AAS_FaceArea
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Q1_FacePlane(q1_dface_t *face, vec3_t normal, float *dist)
-{
- vec_t *v1, *v2, *v3;
- vec3_t vec1, vec2;
- int side, edgenum;
-
- edgenum = q1_dsurfedges[face->firstedge];
- side = edgenum < 0;
- v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point;
- v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
- edgenum = q1_dsurfedges[face->firstedge+1];
- side = edgenum < 0;
- v3 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
- //
- VectorSubtract(v2, v1, vec1);
- VectorSubtract(v3, v1, vec2);
-
- CrossProduct(vec1, vec2, normal);
- VectorNormalize(normal);
- *dist = DotProduct(v1, normal);
-} //end of the function Q1_FacePlane
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bspbrush_t *Q1_MergeBrushes(bspbrush_t *brushlist, int modelnum)
-{
- int nummerges, merged;
- bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist;
- bspbrush_t *lastb2;
-
- if (!brushlist) return NULL;
-
- if (!modelnum) qprintf("%5d brushes merged", nummerges = 0);
- do
- {
- for (tail = brushlist; tail; tail = tail->next)
- {
- if (!tail->next) break;
- } //end for
- merged = 0;
- newbrushlist = NULL;
- for (b1 = brushlist; b1; b1 = brushlist)
- {
- lastb2 = b1;
- for (b2 = b1->next; b2; b2 = b2->next)
- {
- //can't merge brushes with different contents
- if (b1->side != b2->side) newbrush = NULL;
- else newbrush = TryMergeBrushes(b1, b2);
- //if a merged brush is created
- if (newbrush)
- {
- //copy the brush contents
- newbrush->side = b1->side;
- //add the new brush to the end of the list
- tail->next = newbrush;
- //remove the second brush from the list
- lastb2->next = b2->next;
- //remove the first brush from the list
- brushlist = brushlist->next;
- //free the merged brushes
- FreeBrush(b1);
- FreeBrush(b2);
- //get a new tail brush
- for (tail = brushlist; tail; tail = tail->next)
- {
- if (!tail->next) break;
- } //end for
- merged++;
- if (!modelnum) qprintf("\r%5d", nummerges++);
- break;
- } //end if
- lastb2 = b2;
- } //end for
- //if b1 can't be merged with any of the other brushes
- if (!b2)
- {
- brushlist = brushlist->next;
- //keep b1
- b1->next = newbrushlist;
- newbrushlist = b1;
- } //end else
- } //end for
- brushlist = newbrushlist;
- } while(merged);
- if (!modelnum) qprintf("\n");
- return newbrushlist;
-} //end of the function Q1_MergeBrushes
-//===========================================================================
-// returns the amount the face and the winding overlap
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-float Q1_FaceOnWinding(q1_dface_t *face, winding_t *winding)
-{
- int i, edgenum, side;
- float dist, area;
- q1_dplane_t plane;
- vec_t *v1, *v2;
- vec3_t normal, edgevec;
- winding_t *w;
-
- //
- w = CopyWinding(winding);
- memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t));
- //check on which side of the plane the face is
- if (face->side)
- {
- VectorNegate(plane.normal, plane.normal);
- plane.dist = -plane.dist;
- } //end if
- for (i = 0; i < face->numedges && w; i++)
- {
- //get the first and second vertex of the edge
- edgenum = q1_dsurfedges[face->firstedge + i];
- side = edgenum > 0;
- //if the face plane is flipped
- v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point;
- v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
- //create a plane through the edge vector, orthogonal to the face plane
- //and with the normal vector pointing out of the face
- VectorSubtract(v1, v2, edgevec);
- CrossProduct(edgevec, plane.normal, normal);
- VectorNormalize(normal);
- dist = DotProduct(normal, v1);
- //
- ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON
- } //end for
- if (w)
- {
- area = WindingArea(w);
- FreeWinding(w);
- return area;
- } //end if
- return 0;
-} //end of the function Q1_FaceOnWinding
-//===========================================================================
-// returns a list with brushes created by splitting the given brush with
-// planes that go through the face edges and are orthogonal to the face plane
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bspbrush_t *Q1_SplitBrushWithFace(bspbrush_t *brush, q1_dface_t *face)
-{
- int i, edgenum, side, planenum, splits;
- float dist;
- q1_dplane_t plane;
- vec_t *v1, *v2;
- vec3_t normal, edgevec;
- bspbrush_t *front, *back, *brushlist;
-
- memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t));
- //check on which side of the plane the face is
- if (face->side)
- {
- VectorNegate(plane.normal, plane.normal);
- plane.dist = -plane.dist;
- } //end if
- splits = 0;
- brushlist = NULL;
- for (i = 0; i < face->numedges; i++)
- {
- //get the first and second vertex of the edge
- edgenum = q1_dsurfedges[face->firstedge + i];
- side = edgenum > 0;
- //if the face plane is flipped
- v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point;
- v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
- //create a plane through the edge vector, orthogonal to the face plane
- //and with the normal vector pointing out of the face
- VectorSubtract(v1, v2, edgevec);
- CrossProduct(edgevec, plane.normal, normal);
- VectorNormalize(normal);
- dist = DotProduct(normal, v1);
- //
- planenum = FindFloatPlane(normal, dist);
- //split the current brush
- SplitBrush(brush, planenum, &front, &back);
- //if there is a back brush just put it in the list
- if (back)
- {
- //copy the brush contents
- back->side = brush->side;
- //
- back->next = brushlist;
- brushlist = back;
- splits++;
- } //end if
- if (!front)
- {
- Log_Print("Q1_SplitBrushWithFace: no new brush\n");
- FreeBrushList(brushlist);
- return NULL;
- } //end if
- //copy the brush contents
- front->side = brush->side;
- //continue splitting the front brush
- brush = front;
- } //end for
- if (!splits)
- {
- FreeBrush(front);
- return NULL;
- } //end if
- front->next = brushlist;
- brushlist = front;
- return brushlist;
-} //end of the function Q1_SplitBrushWithFace
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-bspbrush_t *Q1_TextureBrushes(bspbrush_t *brushlist, int modelnum)
-{
- float area, largestarea;
- int i, n, texinfonum, sn, numbrushes, ofs;
- int bestfacenum, sidenodenum;
- side_t *side;
- q1_dmiptexlump_t *miptexlump;
- q1_miptex_t *miptex;
- bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend;
- vec_t defaultvec[4] = {1, 0, 0, 0};
-
- if (!modelnum) qprintf("texturing brushes\n");
- if (!modelnum) qprintf("%5d brushes", numbrushes = 0);
- //get a pointer to the last brush in the list
- for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next)
- {
- if (!brushlistend->next) break;
- } //end for
- //there's no previous brush when at the start of the list
- prevbrush = NULL;
- //go over the brush list
- for (brush = brushlist; brush; brush = nextbrush)
- {
- nextbrush = brush->next;
- //find a texinfo for every brush side
- for (sn = 0; sn < brush->numsides; sn++)
- {
- side = &brush->sides[sn];
- //
- if (side->flags & SFL_TEXTURED) continue;
- //number of the node that created this brush side
- sidenodenum = side->surf; //see midwinding in Q1_SplitBrush
- //no face found yet
- bestfacenum = -1;
- //minimum face size
- largestarea = 1;
- //if optimizing the texture placement and not going for the
- //least number of brushes
- if (!lessbrushes)
- {
- for (i = 0; i < q1_numfaces; i++)
- {
- //the face must be in the same plane as the node plane that created
- //this brush side
- if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum)
- {
- //get the area the face and the brush side overlap
- area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding);
- //if this face overlaps the brush side winding more than previous faces
- if (area > largestarea)
- {
- //if there already was a face for texturing this brush side with
- //a different texture
- if (bestfacenum >= 0 &&
- (q1_dfaces[bestfacenum].texinfo != q1_dfaces[i].texinfo))
- {
- //split the brush to fit the texture
- newbrushes = Q1_SplitBrushWithFace(brush, &q1_dfaces[i]);
- //if new brushes where created
- if (newbrushes)
- {
- //remove the current brush from the list
- if (prevbrush) prevbrush->next = brush->next;
- else brushlist = brush->next;
- if (brushlistend == brush)
- {
- brushlistend = prevbrush;
- nextbrush = newbrushes;
- } //end if
- //add the new brushes to the end of the list
- if (brushlistend) brushlistend->next = newbrushes;
- else brushlist = newbrushes;
- //free the current brush
- FreeBrush(brush);
- //don't forget about the prevbrush pointer at the bottom of
- //the outer loop
- brush = prevbrush;
- //find the end of the list
- for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next)
- {
- if (!brushlistend->next) break;
- } //end for
- break;
- } //end if
- else
- {
- Log_Write("brush %d: no real texture split", numbrushes);
- } //end else
- } //end if
- else
- {
- //best face for texturing this brush side
- bestfacenum = i;
- } //end else
- } //end if
- } //end if
- } //end for
- //if the brush was split the original brush is removed
- //and we just continue with the next one in the list
- if (i < q1_numfaces) break;
- } //end if
- else
- {
- //find the face with the largest overlap with this brush side
- //for texturing the brush side
- for (i = 0; i < q1_numfaces; i++)
- {
- //the face must be in the same plane as the node plane that created
- //this brush side
- if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum)
- {
- //get the area the face and the brush side overlap
- area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding);
- //if this face overlaps the brush side winding more than previous faces
- if (area > largestarea)
- {
- largestarea = area;
- bestfacenum = i;
- } //end if
- } //end if
- } //end for
- } //end else
- //if a face was found for texturing this brush side
- if (bestfacenum >= 0)
- {
- //set the MAP texinfo values
- texinfonum = q1_dfaces[bestfacenum].texinfo;
- for (n = 0; n < 4; n++)
- {
- map_texinfo[texinfonum].vecs[0][n] = q1_texinfo[texinfonum].vecs[0][n];
- map_texinfo[texinfonum].vecs[1][n] = q1_texinfo[texinfonum].vecs[1][n];
- } //end for
- //make sure the two vectors aren't of zero length otherwise use the default
- //vector to prevent a divide by zero in the map writing
- if (VectorLength(map_texinfo[texinfonum].vecs[0]) < 0.01)
- memcpy(map_texinfo[texinfonum].vecs[0], defaultvec, sizeof(defaultvec));
- if (VectorLength(map_texinfo[texinfonum].vecs[1]) < 0.01)
- memcpy(map_texinfo[texinfonum].vecs[1], defaultvec, sizeof(defaultvec));
- //
- map_texinfo[texinfonum].flags = q1_texinfo[texinfonum].flags;
- map_texinfo[texinfonum].value = 0; //Q1 and HL texinfos don't have a value
- //the mip texture
- miptexlump = (q1_dmiptexlump_t *) q1_dtexdata;
- ofs = miptexlump->dataofs[q1_texinfo[texinfonum].miptex];
- if ( ofs > q1_texdatasize ) {
- ofs = miptexlump->dataofs[0];
- }
- miptex = (q1_miptex_t *)((byte *)miptexlump + ofs);
- //get the mip texture name
- strcpy(map_texinfo[texinfonum].texture, miptex->name);
- //no animations in Quake1 and Half-Life mip textures
- map_texinfo[texinfonum].nexttexinfo = -1;
- //store the texinfo number
- side->texinfo = texinfonum;
- //
- if (texinfonum > map_numtexinfo) map_numtexinfo = texinfonum;
- //this side is textured
- side->flags |= SFL_TEXTURED;
- } //end if
- else
- {
- //no texture for this side
- side->texinfo = TEXINFO_NODE;
- //this side is textured
- side->flags |= SFL_TEXTURED;
- } //end if
- } //end for
- //
- if (!modelnum && prevbrush != brush) qprintf("\r%5d", ++numbrushes);
- //previous brush in the list
- prevbrush = brush;
- } //end for
- if (!modelnum) qprintf("\n");
- //return the new list with brushes
- return brushlist;
-} //end of the function Q1_TextureBrushes
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Q1_FixContentsTextures(bspbrush_t *brushlist)
-{
- int i, texinfonum;
- bspbrush_t *brush;
-
- for (brush = brushlist; brush; brush = brush->next)
- {
- //only fix the textures of water, slime and lava brushes
- if (brush->side != CONTENTS_WATER &&
- brush->side != CONTENTS_SLIME &&
- brush->side != CONTENTS_LAVA) continue;
- //
- for (i = 0; i < brush->numsides; i++)
- {
- texinfonum = brush->sides[i].texinfo;
- if (Q1_TextureContents(map_texinfo[texinfonum].texture) == brush->side) break;
- } //end for
- //if no specific contents texture was found
- if (i >= brush->numsides)
- {
- texinfonum = -1;
- for (i = 0; i < map_numtexinfo; i++)
- {
- if (Q1_TextureContents(map_texinfo[i].texture) == brush->side)
- {
- texinfonum = i;
- break;
- } //end if
- } //end for
- } //end if
- //
- if (texinfonum >= 0)
- {
- //give all the brush sides this contents texture
- for (i = 0; i < brush->numsides; i++)
- {
- brush->sides[i].texinfo = texinfonum;
- } //end for
- } //end if
- else Log_Print("brush contents %d with wrong textures\n", brush->side);
- //
- } //end for
- /*
- for (brush = brushlist; brush; brush = brush->next)
- {
- //give all the brush sides this contents texture
- for (i = 0; i < brush->numsides; i++)
- {
- if (Q1_TextureContents(map_texinfo[texinfonum].texture) != brush->side)
- {
- Error("brush contents %d with wrong contents textures %s\n", brush->side,
- Q1_TextureContents(map_texinfo[texinfonum].texture));
- } //end if
- } //end for
- } //end for*/
-} //end of the function Q1_FixContentsTextures
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Q1_BSPBrushToMapBrush(bspbrush_t *bspbrush, entity_t *mapent)
-{
- mapbrush_t *mapbrush;
- side_t *side;
- int i, besttexinfo;
-
- CheckBSPBrush(bspbrush);
-
- if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
- Error ("nummapbrushes == MAX_MAPFILE_BRUSHES");
-
- mapbrush = &mapbrushes[nummapbrushes];
- mapbrush->original_sides = &brushsides[nummapbrushsides];
- mapbrush->entitynum = mapent - entities;
- mapbrush->brushnum = nummapbrushes - mapent->firstbrush;
- mapbrush->leafnum = -1;
- mapbrush->numsides = 0;
-
- besttexinfo = TEXINFO_NODE;
- for (i = 0; i < bspbrush->numsides; i++)
- {
- if (!bspbrush->sides[i].winding) continue;
- //
- if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
- Error ("MAX_MAPFILE_BRUSHSIDES");
- side = &brushsides[nummapbrushsides];
- //the contents of the bsp brush is stored in the side variable
- side->contents = bspbrush->side;
- side->surf = 0;
- side->planenum = bspbrush->sides[i].planenum;
- side->texinfo = bspbrush->sides[i].texinfo;
- if (side->texinfo != TEXINFO_NODE)
- {
- //this brush side is textured
- side->flags |= SFL_TEXTURED;
- besttexinfo = side->texinfo;
- } //end if
- //
- nummapbrushsides++;
- mapbrush->numsides++;
- } //end for
- //
- if (besttexinfo == TEXINFO_NODE)
- {
- mapbrush->numsides = 0;
- q1_numclipbrushes++;
- return;
- } //end if
- //set the texinfo for all the brush sides without texture
- for (i = 0; i < mapbrush->numsides; i++)
- {
- if (mapbrush->original_sides[i].texinfo == TEXINFO_NODE)
- {
- mapbrush->original_sides[i].texinfo = besttexinfo;
- } //end if
- } //end for
- //contents of the brush
- mapbrush->contents = bspbrush->side;
- //
- if (create_aas)
- {
- //create the AAS brushes from this brush, add brush bevels
- AAS_CreateMapBrushes(mapbrush, mapent, true);
- return;
- } //end if
- //create windings for sides and bounds for brush
- MakeBrushWindings(mapbrush);
- //add brush bevels
- AddBrushBevels(mapbrush);
- //a new brush has been created
- nummapbrushes++;
- mapent->numbrushes++;
-} //end of the function Q1_BSPBrushToMapBrush
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Q1_CreateMapBrushes(entity_t *mapent, int modelnum)
-{
- bspbrush_t *brushlist, *brush, *nextbrush;
- int i;
-
- //create brushes from the model BSP tree
- brushlist = Q1_CreateBrushesFromBSP(modelnum);
- //texture the brushes and split them when necesary
- brushlist = Q1_TextureBrushes(brushlist, modelnum);
- //fix the contents textures of all brushes
- Q1_FixContentsTextures(brushlist);
- //
- if (!nobrushmerge)
- {
- brushlist = Q1_MergeBrushes(brushlist, modelnum);
- //brushlist = Q1_MergeBrushes(brushlist, modelnum);
- } //end if
- //
- if (!modelnum) qprintf("converting brushes to map brushes\n");
- if (!modelnum) qprintf("%5d brushes", i = 0);
- for (brush = brushlist; brush; brush = nextbrush)
- {
- nextbrush = brush->next;
- Q1_BSPBrushToMapBrush(brush, mapent);
- brush->next = NULL;
- FreeBrush(brush);
- if (!modelnum) qprintf("\r%5d", ++i);
- } //end for
- if (!modelnum) qprintf("\n");
-} //end of the function Q1_CreateMapBrushes
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Q1_ResetMapLoading(void)
-{
-} //end of the function Q1_ResetMapLoading
-//===========================================================================
-//
-// Parameter: -
-// Returns: -
-// Changes Globals: -
-//===========================================================================
-void Q1_LoadMapFromBSP(char *filename, int offset, int length)
-{
- int i, modelnum;
- char *model, *classname;
-
- Log_Print("-- Q1_LoadMapFromBSP --\n");
- //the loaded map type
- loadedmaptype = MAPTYPE_QUAKE1;
- //
- qprintf("loading map from %s at %d\n", filename, offset);
- //load the Half-Life BSP file
- Q1_LoadBSPFile(filename, offset, length);
- //
- q1_numclipbrushes = 0;
- //CreatePath(path);
- //Q1_CreateQ2WALFiles(path);
- //parse the entities from the BSP
- Q1_ParseEntities();
- //clear the map mins and maxs
- ClearBounds(map_mins, map_maxs);
- //
- qprintf("creating Quake1 brushes\n");
- if (lessbrushes) qprintf("creating minimum number of brushes\n");
- else qprintf("placing textures correctly\n");
- //
- for (i = 0; i < num_entities; i++)
- {
- entities[i].firstbrush = nummapbrushes;
- entities[i].numbrushes = 0;
- //
- classname = ValueForKey(&entities[i], "classname");
- if (classname && !strcmp(classname, "worldspawn"))
- {
- modelnum = 0;
- } //end if
- else
- {
- //
- model = ValueForKey(&entities[i], "model");
- if (!model || *model != '*') continue;
- model++;
- modelnum = atoi(model);
- } //end else
- //create map brushes for the entity
- Q1_CreateMapBrushes(&entities[i], modelnum);
- } //end for
- //
- qprintf("%5d map brushes\n", nummapbrushes);
- qprintf("%5d clip brushes\n", q1_numclipbrushes);
-} //end of the function Q1_LoadMapFromBSP
+/* +=========================================================================== +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 +=========================================================================== +*/ + +#include "qbsp.h" +#include "l_bsp_q1.h" +#include "aas_map.h" //AAS_CreateMapBrushes + +int q1_numbrushes; +int q1_numclipbrushes; + +//#define Q1_PRINT + +//=========================================================================== +// water, slime and lava brush textures names always start with a * +// followed by the type: "slime", "lava" or otherwise water +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Q1_TextureContents(char *name) +{ + if (!Q_strcasecmp(name, "clip")) return CONTENTS_SOLID; + if (name[0] == '*') + { + if (!Q_strncasecmp(name+1,"lava",4)) return CONTENTS_LAVA; + else if (!Q_strncasecmp(name+1,"slime",5)) return CONTENTS_SLIME; + else return CONTENTS_WATER; + } //end if + else if (!Q_strncasecmp(name, "sky", 3)) return CONTENTS_SOLID; + else return CONTENTS_SOLID; +} //end of the function Q1_TextureContents +//=========================================================================== +// Generates two new brushes, leaving the original +// unchanged +// +// modified for Half-Life because there are quite a lot of tiny node leaves +// in the Half-Life bsps +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_SplitBrush(bspbrush_t *brush, int planenum, int nodenum, + bspbrush_t **front, bspbrush_t **back) +{ + bspbrush_t *b[2]; + int i, j; + winding_t *w, *cw[2], *midwinding; + plane_t *plane, *plane2; + side_t *s, *cs; + float d, d_front, d_back; + + *front = *back = NULL; + plane = &mapplanes[planenum]; + + // check all points + d_front = d_back = 0; + for (i=0 ; i<brush->numsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; j<w->numpoints ; j++) + { + d = DotProduct (w->p[j], plane->normal) - plane->dist; + if (d > 0 && d > d_front) + d_front = d; + if (d < 0 && d < d_back) + d_back = d; + } //end for + } //end for + + if (d_front < 0.1) // PLANESIDE_EPSILON) + { // only on back + *back = CopyBrush (brush); + Log_Print("Q1_SplitBrush: only on back\n"); + return; + } //end if + if (d_back > -0.1) // PLANESIDE_EPSILON) + { // only on front + *front = CopyBrush (brush); + Log_Print("Q1_SplitBrush: only on front\n"); + return; + } //end if + + // create a new winding from the split plane + + w = BaseWindingForPlane (plane->normal, plane->dist); + for (i = 0; i < brush->numsides && w; i++) + { + plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; + ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON); + } //end for + + if (!w || WindingIsTiny(w)) + { // the brush isn't really split + int side; + + Log_Print("Q1_SplitBrush: no split winding\n"); + side = BrushMostlyOnSide (brush, plane); + if (side == PSIDE_FRONT) + *front = CopyBrush (brush); + if (side == PSIDE_BACK) + *back = CopyBrush (brush); + return; + } + + if (WindingIsHuge(w)) + { + Log_Print("Q1_SplitBrush: WARNING huge split winding\n"); + } //end of + + midwinding = w; + + // split it for real + + for (i = 0; i < 2; i++) + { + b[i] = AllocBrush (brush->numsides+1); + b[i]->original = brush->original; + } //end for + + // split all the current windings + + for (i=0 ; i<brush->numsides ; i++) + { + s = &brush->sides[i]; + w = s->winding; + if (!w) + continue; + ClipWindingEpsilon (w, plane->normal, plane->dist, + 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); + for (j=0 ; j<2 ; j++) + { + if (!cw[j]) + continue; +#if 0 + if (WindingIsTiny (cw[j])) + { + FreeWinding (cw[j]); + continue; + } +#endif + cs = &b[j]->sides[b[j]->numsides]; + b[j]->numsides++; + *cs = *s; +// cs->planenum = s->planenum; +// cs->texinfo = s->texinfo; +// cs->visible = s->visible; +// cs->original = s->original; + cs->winding = cw[j]; + cs->flags &= ~SFL_TESTED; + } //end for + } //end for + + + // see if we have valid polygons on both sides + + for (i=0 ; i<2 ; i++) + { + BoundBrush (b[i]); + for (j=0 ; j<3 ; j++) + { + if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096) + { + Log_Print("Q1_SplitBrush: bogus brush after clip\n"); + break; + } //end if + } //end for + + if (b[i]->numsides < 3 || j < 3) + { + FreeBrush (b[i]); + b[i] = NULL; + Log_Print("Q1_SplitBrush: numsides < 3\n"); + } //end if + } //end for + + if ( !(b[0] && b[1]) ) + { + if (!b[0] && !b[1]) + Log_Print("Q1_SplitBrush: split removed brush\n"); + else + Log_Print("Q1_SplitBrush: split not on both sides\n"); + if (b[0]) + { + FreeBrush (b[0]); + *front = CopyBrush (brush); + } //end if + if (b[1]) + { + FreeBrush (b[1]); + *back = CopyBrush (brush); + } //end if + return; + } //end if + + // add the midwinding to both sides + for (i = 0; i < 2; i++) + { + cs = &b[i]->sides[b[i]->numsides]; + b[i]->numsides++; + + cs->planenum = planenum^i^1; + cs->texinfo = 0; + //store the node number in the surf to find the texinfo later on + cs->surf = nodenum; + // + cs->flags &= ~SFL_VISIBLE; + cs->flags &= ~SFL_TESTED; + cs->flags &= ~SFL_TEXTURED; + if (i==0) + cs->winding = CopyWinding (midwinding); + else + cs->winding = midwinding; + } //end for + + +{ + vec_t v1; + int i; + + for (i=0 ; i<2 ; i++) + { + v1 = BrushVolume (b[i]); + if (v1 < 1) + { + FreeBrush (b[i]); + b[i] = NULL; + Log_Print("Q1_SplitBrush: tiny volume after clip\n"); + } //end if + } //end for +} //*/ + + *front = b[0]; + *back = b[1]; +} //end of the function Q1_SplitBrush +//=========================================================================== +// returns true if the tree starting at nodenum has only solid leaves +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Q1_SolidTree_r(int nodenum) +{ + if (nodenum < 0) + { + switch(q1_dleafs[(-nodenum) - 1].contents) + { + case Q1_CONTENTS_EMPTY: + { + return false; + } //end case + case Q1_CONTENTS_SOLID: +#ifdef HLCONTENTS + case Q1_CONTENTS_CLIP: +#endif HLCONTENTS + case Q1_CONTENTS_SKY: +#ifdef HLCONTENTS + case Q1_CONTENTS_TRANSLUCENT: +#endif HLCONTENTS + { + return true; + } //end case + case Q1_CONTENTS_WATER: + case Q1_CONTENTS_SLIME: + case Q1_CONTENTS_LAVA: +#ifdef HLCONTENTS + //these contents should not be found in the BSP + case Q1_CONTENTS_ORIGIN: + case Q1_CONTENTS_CURRENT_0: + case Q1_CONTENTS_CURRENT_90: + case Q1_CONTENTS_CURRENT_180: + case Q1_CONTENTS_CURRENT_270: + case Q1_CONTENTS_CURRENT_UP: + case Q1_CONTENTS_CURRENT_DOWN: +#endif HLCONTENTS + default: + { + return false; + } //end default + } //end switch + return false; + } //end if + if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[0])) return false; + if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[1])) return false; + return true; +} //end of the function Q1_SolidTree_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_CreateBrushes_r(bspbrush_t *brush, int nodenum) +{ + int planenum; + bspbrush_t *front, *back; + q1_dleaf_t *leaf; + + //if it is a leaf + if (nodenum < 0) + { + leaf = &q1_dleafs[(-nodenum) - 1]; + if (leaf->contents != Q1_CONTENTS_EMPTY) + { +#ifdef Q1_PRINT + qprintf("\r%5i", ++q1_numbrushes); +#endif //Q1_PRINT + } //end if + switch(leaf->contents) + { + case Q1_CONTENTS_EMPTY: + { + FreeBrush(brush); + return NULL; + } //end case + case Q1_CONTENTS_SOLID: +#ifdef HLCONTENTS + case Q1_CONTENTS_CLIP: +#endif HLCONTENTS + case Q1_CONTENTS_SKY: +#ifdef HLCONTENTS + case Q1_CONTENTS_TRANSLUCENT: +#endif HLCONTENTS + { + brush->side = CONTENTS_SOLID; + return brush; + } //end case + case Q1_CONTENTS_WATER: + { + brush->side = CONTENTS_WATER; + return brush; + } //end case + case Q1_CONTENTS_SLIME: + { + brush->side = CONTENTS_SLIME; + return brush; + } //end case + case Q1_CONTENTS_LAVA: + { + brush->side = CONTENTS_LAVA; + return brush; + } //end case +#ifdef HLCONTENTS + //these contents should not be found in the BSP + case Q1_CONTENTS_ORIGIN: + case Q1_CONTENTS_CURRENT_0: + case Q1_CONTENTS_CURRENT_90: + case Q1_CONTENTS_CURRENT_180: + case Q1_CONTENTS_CURRENT_270: + case Q1_CONTENTS_CURRENT_UP: + case Q1_CONTENTS_CURRENT_DOWN: + { + Error("Q1_CreateBrushes_r: found contents %d in Half-Life BSP", leaf->contents); + return NULL; + } //end case +#endif HLCONTENTS + default: + { + Error("Q1_CreateBrushes_r: unknown contents %d in Half-Life BSP", leaf->contents); + return NULL; + } //end default + } //end switch + return NULL; + } //end if + //if the rest of the tree is solid + /*if (Q1_SolidTree_r(nodenum)) + { + brush->side = CONTENTS_SOLID; + return brush; + } //end if*/ + // + planenum = q1_dnodes[nodenum].planenum; + planenum = FindFloatPlane(q1_dplanes[planenum].normal, q1_dplanes[planenum].dist); + //split the brush with the node plane + Q1_SplitBrush(brush, planenum, nodenum, &front, &back); + //free the original brush + FreeBrush(brush); + //every node must split the brush in two + if (!front || !back) + { + Log_Print("Q1_CreateBrushes_r: WARNING node not splitting brush\n"); + //return NULL; + } //end if + //create brushes recursively + if (front) front = Q1_CreateBrushes_r(front, q1_dnodes[nodenum].children[0]); + if (back) back = Q1_CreateBrushes_r(back, q1_dnodes[nodenum].children[1]); + //link the brushes if possible and return them + if (front) + { + for (brush = front; brush->next; brush = brush->next); + brush->next = back; + return front; + } //end if + else + { + return back; + } //end else +} //end of the function Q1_CreateBrushes_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_CreateBrushesFromBSP(int modelnum) +{ + bspbrush_t *brushlist; + bspbrush_t *brush; + q1_dnode_t *headnode; + vec3_t mins, maxs; + int i; + + // + headnode = &q1_dnodes[q1_dmodels[modelnum].headnode[0]]; + //get the mins and maxs of the world + VectorCopy(headnode->mins, mins); + VectorCopy(headnode->maxs, maxs); + //enlarge these mins and maxs + for (i = 0; i < 3; i++) + { + mins[i] -= 8; + maxs[i] += 8; + } //end for + //NOTE: have to add the BSP tree mins and maxs to the MAP mins and maxs + AddPointToBounds(mins, map_mins, map_maxs); + AddPointToBounds(maxs, map_mins, map_maxs); + // + if (!modelnum) + { + Log_Print("brush size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", + map_mins[0], map_mins[1], map_mins[2], + map_maxs[0], map_maxs[1], map_maxs[2]); + } //end if + //create one huge brush containing the whole world + brush = BrushFromBounds(mins, maxs); + VectorCopy(mins, brush->mins); + VectorCopy(maxs, brush->maxs); + // +#ifdef Q1_PRINT + qprintf("creating Quake brushes\n"); + qprintf("%5d brushes", q1_numbrushes = 0); +#endif //Q1_PRINT + //create the brushes + brushlist = Q1_CreateBrushes_r(brush, q1_dmodels[modelnum].headnode[0]); + // +#ifdef Q1_PRINT + qprintf("\n"); +#endif //Q1_PRINT + //now we've got a list with brushes! + return brushlist; +} //end of the function Q1_CreateBrushesFromBSP +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +q1_dleaf_t *Q1_PointInLeaf(int startnode, vec3_t point) +{ + int nodenum; + vec_t dist; + q1_dnode_t *node; + q1_dplane_t *plane; + + nodenum = startnode; + while (nodenum >= 0) + { + node = &q1_dnodes[nodenum]; + plane = &q1_dplanes[node->planenum]; + dist = DotProduct(point, plane->normal) - plane->dist; + if (dist > 0) + nodenum = node->children[0]; + else + nodenum = node->children[1]; + } //end while + + return &q1_dleafs[-nodenum - 1]; +} //end of the function Q1_PointInLeaf +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Q1_FaceArea(q1_dface_t *face) +{ + int i; + float total; + vec_t *v; + vec3_t d1, d2, cross; + q1_dedge_t *edge; + + edge = &q1_dedges[face->firstedge]; + v = q1_dvertexes[edge->v[0]].point; + + total = 0; + for (i = 1; i < face->numedges - 1; i++) + { + edge = &q1_dedges[face->firstedge + i]; + VectorSubtract(q1_dvertexes[edge->v[0]].point, v, d1); + VectorSubtract(q1_dvertexes[edge->v[1]].point, v, d2); + CrossProduct(d1, d2, cross); + total += 0.5 * VectorLength(cross); + } //end for + return total; +} //end of the function AAS_FaceArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_FacePlane(q1_dface_t *face, vec3_t normal, float *dist) +{ + vec_t *v1, *v2, *v3; + vec3_t vec1, vec2; + int side, edgenum; + + edgenum = q1_dsurfedges[face->firstedge]; + side = edgenum < 0; + v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point; + v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; + edgenum = q1_dsurfedges[face->firstedge+1]; + side = edgenum < 0; + v3 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; + // + VectorSubtract(v2, v1, vec1); + VectorSubtract(v3, v1, vec2); + + CrossProduct(vec1, vec2, normal); + VectorNormalize(normal); + *dist = DotProduct(v1, normal); +} //end of the function Q1_FacePlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_MergeBrushes(bspbrush_t *brushlist, int modelnum) +{ + int nummerges, merged; + bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist; + bspbrush_t *lastb2; + + if (!brushlist) return NULL; + + if (!modelnum) qprintf("%5d brushes merged", nummerges = 0); + do + { + for (tail = brushlist; tail; tail = tail->next) + { + if (!tail->next) break; + } //end for + merged = 0; + newbrushlist = NULL; + for (b1 = brushlist; b1; b1 = brushlist) + { + lastb2 = b1; + for (b2 = b1->next; b2; b2 = b2->next) + { + //can't merge brushes with different contents + if (b1->side != b2->side) newbrush = NULL; + else newbrush = TryMergeBrushes(b1, b2); + //if a merged brush is created + if (newbrush) + { + //copy the brush contents + newbrush->side = b1->side; + //add the new brush to the end of the list + tail->next = newbrush; + //remove the second brush from the list + lastb2->next = b2->next; + //remove the first brush from the list + brushlist = brushlist->next; + //free the merged brushes + FreeBrush(b1); + FreeBrush(b2); + //get a new tail brush + for (tail = brushlist; tail; tail = tail->next) + { + if (!tail->next) break; + } //end for + merged++; + if (!modelnum) qprintf("\r%5d", nummerges++); + break; + } //end if + lastb2 = b2; + } //end for + //if b1 can't be merged with any of the other brushes + if (!b2) + { + brushlist = brushlist->next; + //keep b1 + b1->next = newbrushlist; + newbrushlist = b1; + } //end else + } //end for + brushlist = newbrushlist; + } while(merged); + if (!modelnum) qprintf("\n"); + return newbrushlist; +} //end of the function Q1_MergeBrushes +//=========================================================================== +// returns the amount the face and the winding overlap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Q1_FaceOnWinding(q1_dface_t *face, winding_t *winding) +{ + int i, edgenum, side; + float dist, area; + q1_dplane_t plane; + vec_t *v1, *v2; + vec3_t normal, edgevec; + winding_t *w; + + // + w = CopyWinding(winding); + memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t)); + //check on which side of the plane the face is + if (face->side) + { + VectorNegate(plane.normal, plane.normal); + plane.dist = -plane.dist; + } //end if + for (i = 0; i < face->numedges && w; i++) + { + //get the first and second vertex of the edge + edgenum = q1_dsurfedges[face->firstedge + i]; + side = edgenum > 0; + //if the face plane is flipped + v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point; + v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; + //create a plane through the edge vector, orthogonal to the face plane + //and with the normal vector pointing out of the face + VectorSubtract(v1, v2, edgevec); + CrossProduct(edgevec, plane.normal, normal); + VectorNormalize(normal); + dist = DotProduct(normal, v1); + // + ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON + } //end for + if (w) + { + area = WindingArea(w); + FreeWinding(w); + return area; + } //end if + return 0; +} //end of the function Q1_FaceOnWinding +//=========================================================================== +// returns a list with brushes created by splitting the given brush with +// planes that go through the face edges and are orthogonal to the face plane +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_SplitBrushWithFace(bspbrush_t *brush, q1_dface_t *face) +{ + int i, edgenum, side, planenum, splits; + float dist; + q1_dplane_t plane; + vec_t *v1, *v2; + vec3_t normal, edgevec; + bspbrush_t *front, *back, *brushlist; + + memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t)); + //check on which side of the plane the face is + if (face->side) + { + VectorNegate(plane.normal, plane.normal); + plane.dist = -plane.dist; + } //end if + splits = 0; + brushlist = NULL; + for (i = 0; i < face->numedges; i++) + { + //get the first and second vertex of the edge + edgenum = q1_dsurfedges[face->firstedge + i]; + side = edgenum > 0; + //if the face plane is flipped + v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point; + v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point; + //create a plane through the edge vector, orthogonal to the face plane + //and with the normal vector pointing out of the face + VectorSubtract(v1, v2, edgevec); + CrossProduct(edgevec, plane.normal, normal); + VectorNormalize(normal); + dist = DotProduct(normal, v1); + // + planenum = FindFloatPlane(normal, dist); + //split the current brush + SplitBrush(brush, planenum, &front, &back); + //if there is a back brush just put it in the list + if (back) + { + //copy the brush contents + back->side = brush->side; + // + back->next = brushlist; + brushlist = back; + splits++; + } //end if + if (!front) + { + Log_Print("Q1_SplitBrushWithFace: no new brush\n"); + FreeBrushList(brushlist); + return NULL; + } //end if + //copy the brush contents + front->side = brush->side; + //continue splitting the front brush + brush = front; + } //end for + if (!splits) + { + FreeBrush(front); + return NULL; + } //end if + front->next = brushlist; + brushlist = front; + return brushlist; +} //end of the function Q1_SplitBrushWithFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_TextureBrushes(bspbrush_t *brushlist, int modelnum) +{ + float area, largestarea; + int i, n, texinfonum, sn, numbrushes, ofs; + int bestfacenum, sidenodenum; + side_t *side; + q1_dmiptexlump_t *miptexlump; + q1_miptex_t *miptex; + bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend; + vec_t defaultvec[4] = {1, 0, 0, 0}; + + if (!modelnum) qprintf("texturing brushes\n"); + if (!modelnum) qprintf("%5d brushes", numbrushes = 0); + //get a pointer to the last brush in the list + for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) + { + if (!brushlistend->next) break; + } //end for + //there's no previous brush when at the start of the list + prevbrush = NULL; + //go over the brush list + for (brush = brushlist; brush; brush = nextbrush) + { + nextbrush = brush->next; + //find a texinfo for every brush side + for (sn = 0; sn < brush->numsides; sn++) + { + side = &brush->sides[sn]; + // + if (side->flags & SFL_TEXTURED) continue; + //number of the node that created this brush side + sidenodenum = side->surf; //see midwinding in Q1_SplitBrush + //no face found yet + bestfacenum = -1; + //minimum face size + largestarea = 1; + //if optimizing the texture placement and not going for the + //least number of brushes + if (!lessbrushes) + { + for (i = 0; i < q1_numfaces; i++) + { + //the face must be in the same plane as the node plane that created + //this brush side + if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum) + { + //get the area the face and the brush side overlap + area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding); + //if this face overlaps the brush side winding more than previous faces + if (area > largestarea) + { + //if there already was a face for texturing this brush side with + //a different texture + if (bestfacenum >= 0 && + (q1_dfaces[bestfacenum].texinfo != q1_dfaces[i].texinfo)) + { + //split the brush to fit the texture + newbrushes = Q1_SplitBrushWithFace(brush, &q1_dfaces[i]); + //if new brushes where created + if (newbrushes) + { + //remove the current brush from the list + if (prevbrush) prevbrush->next = brush->next; + else brushlist = brush->next; + if (brushlistend == brush) + { + brushlistend = prevbrush; + nextbrush = newbrushes; + } //end if + //add the new brushes to the end of the list + if (brushlistend) brushlistend->next = newbrushes; + else brushlist = newbrushes; + //free the current brush + FreeBrush(brush); + //don't forget about the prevbrush pointer at the bottom of + //the outer loop + brush = prevbrush; + //find the end of the list + for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next) + { + if (!brushlistend->next) break; + } //end for + break; + } //end if + else + { + Log_Write("brush %d: no real texture split", numbrushes); + } //end else + } //end if + else + { + //best face for texturing this brush side + bestfacenum = i; + } //end else + } //end if + } //end if + } //end for + //if the brush was split the original brush is removed + //and we just continue with the next one in the list + if (i < q1_numfaces) break; + } //end if + else + { + //find the face with the largest overlap with this brush side + //for texturing the brush side + for (i = 0; i < q1_numfaces; i++) + { + //the face must be in the same plane as the node plane that created + //this brush side + if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum) + { + //get the area the face and the brush side overlap + area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding); + //if this face overlaps the brush side winding more than previous faces + if (area > largestarea) + { + largestarea = area; + bestfacenum = i; + } //end if + } //end if + } //end for + } //end else + //if a face was found for texturing this brush side + if (bestfacenum >= 0) + { + //set the MAP texinfo values + texinfonum = q1_dfaces[bestfacenum].texinfo; + for (n = 0; n < 4; n++) + { + map_texinfo[texinfonum].vecs[0][n] = q1_texinfo[texinfonum].vecs[0][n]; + map_texinfo[texinfonum].vecs[1][n] = q1_texinfo[texinfonum].vecs[1][n]; + } //end for + //make sure the two vectors aren't of zero length otherwise use the default + //vector to prevent a divide by zero in the map writing + if (VectorLength(map_texinfo[texinfonum].vecs[0]) < 0.01) + memcpy(map_texinfo[texinfonum].vecs[0], defaultvec, sizeof(defaultvec)); + if (VectorLength(map_texinfo[texinfonum].vecs[1]) < 0.01) + memcpy(map_texinfo[texinfonum].vecs[1], defaultvec, sizeof(defaultvec)); + // + map_texinfo[texinfonum].flags = q1_texinfo[texinfonum].flags; + map_texinfo[texinfonum].value = 0; //Q1 and HL texinfos don't have a value + //the mip texture + miptexlump = (q1_dmiptexlump_t *) q1_dtexdata; + ofs = miptexlump->dataofs[q1_texinfo[texinfonum].miptex]; + if ( ofs > q1_texdatasize ) { + ofs = miptexlump->dataofs[0]; + } + miptex = (q1_miptex_t *)((byte *)miptexlump + ofs); + //get the mip texture name + strcpy(map_texinfo[texinfonum].texture, miptex->name); + //no animations in Quake1 and Half-Life mip textures + map_texinfo[texinfonum].nexttexinfo = -1; + //store the texinfo number + side->texinfo = texinfonum; + // + if (texinfonum > map_numtexinfo) map_numtexinfo = texinfonum; + //this side is textured + side->flags |= SFL_TEXTURED; + } //end if + else + { + //no texture for this side + side->texinfo = TEXINFO_NODE; + //this side is textured + side->flags |= SFL_TEXTURED; + } //end if + } //end for + // + if (!modelnum && prevbrush != brush) qprintf("\r%5d", ++numbrushes); + //previous brush in the list + prevbrush = brush; + } //end for + if (!modelnum) qprintf("\n"); + //return the new list with brushes + return brushlist; +} //end of the function Q1_TextureBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_FixContentsTextures(bspbrush_t *brushlist) +{ + int i, texinfonum; + bspbrush_t *brush; + + for (brush = brushlist; brush; brush = brush->next) + { + //only fix the textures of water, slime and lava brushes + if (brush->side != CONTENTS_WATER && + brush->side != CONTENTS_SLIME && + brush->side != CONTENTS_LAVA) continue; + // + for (i = 0; i < brush->numsides; i++) + { + texinfonum = brush->sides[i].texinfo; + if (Q1_TextureContents(map_texinfo[texinfonum].texture) == brush->side) break; + } //end for + //if no specific contents texture was found + if (i >= brush->numsides) + { + texinfonum = -1; + for (i = 0; i < map_numtexinfo; i++) + { + if (Q1_TextureContents(map_texinfo[i].texture) == brush->side) + { + texinfonum = i; + break; + } //end if + } //end for + } //end if + // + if (texinfonum >= 0) + { + //give all the brush sides this contents texture + for (i = 0; i < brush->numsides; i++) + { + brush->sides[i].texinfo = texinfonum; + } //end for + } //end if + else Log_Print("brush contents %d with wrong textures\n", brush->side); + // + } //end for + /* + for (brush = brushlist; brush; brush = brush->next) + { + //give all the brush sides this contents texture + for (i = 0; i < brush->numsides; i++) + { + if (Q1_TextureContents(map_texinfo[texinfonum].texture) != brush->side) + { + Error("brush contents %d with wrong contents textures %s\n", brush->side, + Q1_TextureContents(map_texinfo[texinfonum].texture)); + } //end if + } //end for + } //end for*/ +} //end of the function Q1_FixContentsTextures +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_BSPBrushToMapBrush(bspbrush_t *bspbrush, entity_t *mapent) +{ + mapbrush_t *mapbrush; + side_t *side; + int i, besttexinfo; + + CheckBSPBrush(bspbrush); + + if (nummapbrushes >= MAX_MAPFILE_BRUSHES) + Error ("nummapbrushes == MAX_MAPFILE_BRUSHES"); + + mapbrush = &mapbrushes[nummapbrushes]; + mapbrush->original_sides = &brushsides[nummapbrushsides]; + mapbrush->entitynum = mapent - entities; + mapbrush->brushnum = nummapbrushes - mapent->firstbrush; + mapbrush->leafnum = -1; + mapbrush->numsides = 0; + + besttexinfo = TEXINFO_NODE; + for (i = 0; i < bspbrush->numsides; i++) + { + if (!bspbrush->sides[i].winding) continue; + // + if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) + Error ("MAX_MAPFILE_BRUSHSIDES"); + side = &brushsides[nummapbrushsides]; + //the contents of the bsp brush is stored in the side variable + side->contents = bspbrush->side; + side->surf = 0; + side->planenum = bspbrush->sides[i].planenum; + side->texinfo = bspbrush->sides[i].texinfo; + if (side->texinfo != TEXINFO_NODE) + { + //this brush side is textured + side->flags |= SFL_TEXTURED; + besttexinfo = side->texinfo; + } //end if + // + nummapbrushsides++; + mapbrush->numsides++; + } //end for + // + if (besttexinfo == TEXINFO_NODE) + { + mapbrush->numsides = 0; + q1_numclipbrushes++; + return; + } //end if + //set the texinfo for all the brush sides without texture + for (i = 0; i < mapbrush->numsides; i++) + { + if (mapbrush->original_sides[i].texinfo == TEXINFO_NODE) + { + mapbrush->original_sides[i].texinfo = besttexinfo; + } //end if + } //end for + //contents of the brush + mapbrush->contents = bspbrush->side; + // + if (create_aas) + { + //create the AAS brushes from this brush, add brush bevels + AAS_CreateMapBrushes(mapbrush, mapent, true); + return; + } //end if + //create windings for sides and bounds for brush + MakeBrushWindings(mapbrush); + //add brush bevels + AddBrushBevels(mapbrush); + //a new brush has been created + nummapbrushes++; + mapent->numbrushes++; +} //end of the function Q1_BSPBrushToMapBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_CreateMapBrushes(entity_t *mapent, int modelnum) +{ + bspbrush_t *brushlist, *brush, *nextbrush; + int i; + + //create brushes from the model BSP tree + brushlist = Q1_CreateBrushesFromBSP(modelnum); + //texture the brushes and split them when necesary + brushlist = Q1_TextureBrushes(brushlist, modelnum); + //fix the contents textures of all brushes + Q1_FixContentsTextures(brushlist); + // + if (!nobrushmerge) + { + brushlist = Q1_MergeBrushes(brushlist, modelnum); + //brushlist = Q1_MergeBrushes(brushlist, modelnum); + } //end if + // + if (!modelnum) qprintf("converting brushes to map brushes\n"); + if (!modelnum) qprintf("%5d brushes", i = 0); + for (brush = brushlist; brush; brush = nextbrush) + { + nextbrush = brush->next; + Q1_BSPBrushToMapBrush(brush, mapent); + brush->next = NULL; + FreeBrush(brush); + if (!modelnum) qprintf("\r%5d", ++i); + } //end for + if (!modelnum) qprintf("\n"); +} //end of the function Q1_CreateMapBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_ResetMapLoading(void) +{ +} //end of the function Q1_ResetMapLoading +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_LoadMapFromBSP(char *filename, int offset, int length) +{ + int i, modelnum; + char *model, *classname; + + Log_Print("-- Q1_LoadMapFromBSP --\n"); + //the loaded map type + loadedmaptype = MAPTYPE_QUAKE1; + // + qprintf("loading map from %s at %d\n", filename, offset); + //load the Half-Life BSP file + Q1_LoadBSPFile(filename, offset, length); + // + q1_numclipbrushes = 0; + //CreatePath(path); + //Q1_CreateQ2WALFiles(path); + //parse the entities from the BSP + Q1_ParseEntities(); + //clear the map mins and maxs + ClearBounds(map_mins, map_maxs); + // + qprintf("creating Quake1 brushes\n"); + if (lessbrushes) qprintf("creating minimum number of brushes\n"); + else qprintf("placing textures correctly\n"); + // + for (i = 0; i < num_entities; i++) + { + entities[i].firstbrush = nummapbrushes; + entities[i].numbrushes = 0; + // + classname = ValueForKey(&entities[i], "classname"); + if (classname && !strcmp(classname, "worldspawn")) + { + modelnum = 0; + } //end if + else + { + // + model = ValueForKey(&entities[i], "model"); + if (!model || *model != '*') continue; + model++; + modelnum = atoi(model); + } //end else + //create map brushes for the entity + Q1_CreateMapBrushes(&entities[i], modelnum); + } //end for + // + qprintf("%5d map brushes\n", nummapbrushes); + qprintf("%5d clip brushes\n", q1_numclipbrushes); +} //end of the function Q1_LoadMapFromBSP |