/* Relay -- a tool to record and play Quake2 demos Copyright (C) 2000 Conor Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Conor Davis cedavis@planetquake.com */ #include #include "shared.h" #include "block.h" #include "bsp.h" #include "endian.h" #include "q2defines.h" #define VIS(x) ((vis_t *)((x)->visdata)) // sizes for each element of a lump static int lumpsizes[] = { 1, // LUMP_ENTITIES 20, // LUMP_PLANES 12, // LUMP_VERTEXES 1, // LUMP_VISIBILITY 28, // LUMP_NODES 76, // LUMP_TEXINFO 20, // LUMP_FACES 1, // LUMP_LIGHTING 28, // LUMP_LEAFS 2, // LUMP_LEAFFACES 2, // LUMP_LEAFBRUSHES 4, // LUMP_EDGES 4, // LUMP_SURFEDGES 48, // LUMP_MODELS 12, // LUMP_BRUSHES 4, // LUMP_BRUSHSIDES 1, // LUMP_POP 8, // LUMP_AREAS 8, // LUMP_AREAPORTALS }; int ReadBSP(bsp_t *bsp, block_t *block) { byte ident[4]; int i, j, k; lump_t lumps[HEADER_LUMPS]; int lumpnum; BlockRewindRead(block); BlockRead(block, ident, 4); bsp->version = ReadLong(block); if (memcmp(ident, "IBSP", 4)) return -1; for (lumpnum = 0; lumpnum < HEADER_LUMPS; lumpnum++) { lumps[lumpnum].fileofs = ReadLong(block); lumps[lumpnum].filelen = ReadLong(block); if (lumps[lumpnum].filelen % lumpsizes[lumpnum]) return -1; } for (lumpnum = 0; lumpnum < HEADER_LUMPS; lumpnum++) { bsp->num[lumpnum] = lumps[lumpnum].filelen / lumpsizes[lumpnum]; block->readoffset = lumps[lumpnum].fileofs; switch(lumpnum) { case LUMP_ENTITIES: BlockRead(block, bsp->entdata, lumps[LUMP_ENTITIES].filelen); break; case LUMP_PLANES: for (i = 0; i < bsp->num[LUMP_PLANES]; i++) { for (j = 0; j < 3; j++) bsp->planes[i].normal[j] = ReadFloat(block); bsp->planes[i].dist = ReadFloat(block); bsp->planes[i].type = ReadLong(block); } break; case LUMP_VERTEXES: for (i = 0; i < bsp->num[LUMP_VERTEXES]; i++) { for (j = 0; j < 3; j++) bsp->vertexes[i].point[j] = ReadFloat(block); } break; case LUMP_VISIBILITY: BlockRead(block, bsp->visdata, lumps[LUMP_VISIBILITY].filelen); VIS(bsp)->numclusters = LittleLong(VIS(bsp)->numclusters); for (j = 0; j < VIS(bsp)->numclusters; j++) { for (k = 0; k < 2; k++) VIS(bsp)->bitofs[j][k] = LittleLong(VIS(bsp)->bitofs[j][k]); } break; case LUMP_NODES: for (i = 0; i < bsp->num[LUMP_NODES]; i++) { bsp->nodes[i].planenum = ReadLong(block); for (j = 0; j < 2; j++) bsp->nodes[i].children[j] = ReadLong(block); for (j = 0; j < 3; j++) bsp->nodes[i].mins[j] = ReadShort(block); for (j = 0; j < 3; j++) bsp->nodes[i].maxs[j] = ReadShort(block); bsp->nodes[i].firstface = ReadShort(block); bsp->nodes[i].numfaces = ReadShort(block); } break; case LUMP_TEXINFO: for (i = 0; i < bsp->num[LUMP_TEXINFO]; i++) { for (j = 0; j < 2; j++) { for (k = 0; k < 4; k++) bsp->texinfo[i].vecs[j][k] = ReadFloat(block); } bsp->texinfo[i].flags = ReadLong(block); bsp->texinfo[i].value = ReadLong(block); BlockRead(block, bsp->texinfo[i].texture, 32); bsp->texinfo[i].nexttexinfo = ReadLong(block); } break; case LUMP_FACES: for (i = 0; i < bsp->num[LUMP_FACES]; i++) { bsp->faces[i].planenum = ReadShort(block); bsp->faces[i].side = ReadShort(block); bsp->faces[i].firstedge = ReadLong(block); bsp->faces[i].numedges = ReadShort(block); bsp->faces[i].texinfo = ReadShort(block); BlockRead(block, bsp->faces[i].styles, MAXLIGHTMAPS); bsp->faces[i].lightofs = ReadLong(block); } break; case LUMP_LIGHTING: BlockRead(block, bsp->lightdata, lumps[LUMP_LIGHTING].filelen); break; case LUMP_LEAFS: for (i = 0; i < bsp->num[LUMP_LEAFS]; i++) { bsp->leafs[i].contents = ReadLong(block); bsp->leafs[i].cluster = ReadShort(block); bsp->leafs[i].area = ReadShort(block); for (j = 0; j < 3; j++) bsp->leafs[i].mins[j] = ReadShort(block); for (j = 0; j < 3; j++) bsp->leafs[i].maxs[j] = ReadShort(block); bsp->leafs[i].firstleafface = ReadShort(block); bsp->leafs[i].numleaffaces = ReadShort(block); bsp->leafs[i].firstleafbrush = ReadShort(block); bsp->leafs[i].numleafbrushes = ReadShort(block); } break; case LUMP_LEAFFACES: for (i = 0; i < bsp->num[LUMP_LEAFFACES]; i++) bsp->leaffaces[i] = ReadShort(block); break; case LUMP_LEAFBRUSHES: for (i = 0; i < bsp->num[LUMP_LEAFBRUSHES]; i++) bsp->leafbrushes[i] = ReadShort(block); break; case LUMP_EDGES: for (i = 0; i < bsp->num[LUMP_EDGES]; i++) { for (j = 0; j < 2; j++) bsp->edges[i].v[j] = ReadShort(block); } break; case LUMP_SURFEDGES: for (i = 0; i < bsp->num[LUMP_SURFEDGES]; i++) bsp->surfedges[i] = ReadLong(block); break; case LUMP_MODELS: for (i = 0; i < bsp->num[LUMP_MODELS]; i++) { for (j = 0; j < 3; j++) bsp->models[i].mins[j] = ReadFloat(block); for (j = 0; j < 3; j++) bsp->models[i].maxs[j] = ReadFloat(block); for (j = 0; j < 3; j++) bsp->models[i].origin[j] = ReadFloat(block); bsp->models[i].headnode = ReadLong(block); bsp->models[i].firstface = ReadLong(block); bsp->models[i].numfaces = ReadLong(block); } break; case LUMP_BRUSHES: for (i = 0; i < bsp->num[LUMP_BRUSHES]; i++) { bsp->brushes[i].firstside = ReadLong(block); bsp->brushes[i].numsides = ReadLong(block); bsp->brushes[i].contents = ReadLong(block); } break; case LUMP_BRUSHSIDES: for (i = 0; i < bsp->num[LUMP_BRUSHSIDES]; i++) { bsp->brushsides[i].planenum = ReadShort(block); bsp->brushsides[i].texinfo = ReadShort(block); } break; case LUMP_AREAS: for (i = 0; i < bsp->num[LUMP_AREAS]; i++) { bsp->areas[i].numareaportals = ReadLong(block); bsp->areas[i].firstareaportal = ReadLong(block); } break; case LUMP_AREAPORTALS: for (i = 0; i < bsp->num[LUMP_AREAPORTALS]; i++) { bsp->areaportals[i].portalnum = ReadLong(block); bsp->areaportals[i].otherarea = ReadLong(block); } break; } } if (ReadOverflow(block)) return -1; return 0; } int PointInLeafnum(bsp_t *bsp, vec3_t point) { int nodenum; vec_t dist; node_t *node; plane_t *plane; nodenum = 0; while (nodenum >= 0) { node = &bsp->nodes[nodenum]; plane = &bsp->planes[node->planenum]; dist = DotProduct(point, plane->normal) - plane->dist; if (dist > 0) nodenum = node->children[0]; else nodenum = node->children[1]; } return -nodenum - 1; } int PointInCluster(bsp_t *bsp, vec3_t point) { return bsp->leafs[PointInLeafnum(bsp, point)].cluster; } int CompressVis (bsp_t *bsp, byte *vis, byte *dest) { int j; int rep; int visrow; byte *dest_p; dest_p = dest; visrow = (VIS(bsp)->numclusters + 7)>>3; for (j=0 ; jnumclusters+7)>>3; out = decompressed; do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - decompressed < row); } qboolean ClusterVisible(bsp_t *bsp, int cluster1, int cluster2, int viewset) { byte pvs[MAX_MAP_LEAFS/8]; // cluster -1 == void, solid space if (cluster1 == -1 || cluster2 == -1) return false; // quick success check if (cluster1 == cluster2) return true; DecompressVis(bsp, bsp->visdata + VIS(bsp)->bitofs[cluster1][viewset], pvs); if (pvs[cluster2>>3] & (1<<(cluster2&7))) return true; return false; } qboolean inPVS(bsp_t *bsp, vec3_t p1, vec3_t p2) { int cluster1, cluster2; cluster1 = PointInCluster(bsp, p1); cluster2 = PointInCluster(bsp, p2); return ClusterVisible(bsp, cluster1, cluster2, DVIS_PVS); } qboolean inPHS(bsp_t *bsp, vec3_t p1, vec3_t p2) { int cluster1, cluster2; cluster1 = PointInCluster(bsp, p1); cluster2 = PointInCluster(bsp, p2); return ClusterVisible(bsp, cluster1, cluster2, DVIS_PHS); }