/* =========================================================================== 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 ; inumsides ; i++) { w = brush->sides[i].winding; if (!w) continue; for (j=0 ; jnumpoints ; 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 ; inumsides ; 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