diff options
author | Holger Zickner <[email protected]> | 2004-07-07 19:59:59 +0000 |
---|---|---|
committer | Holger Zickner <[email protected]> | 2004-07-07 19:59:59 +0000 |
commit | 6e23fc1074d1f0c2c2812f4c2e663f5a21a43c20 (patch) | |
tree | 46ecc6d0255c874ba4cd26dc3d0733f785019896 /src/jake2/qcommon/CM.java |
import of Jake2 version sunrisesunrise
Diffstat (limited to 'src/jake2/qcommon/CM.java')
-rw-r--r-- | src/jake2/qcommon/CM.java | 1927 |
1 files changed, 1927 insertions, 0 deletions
diff --git a/src/jake2/qcommon/CM.java b/src/jake2/qcommon/CM.java new file mode 100644 index 0000000..6cb141f --- /dev/null +++ b/src/jake2/qcommon/CM.java @@ -0,0 +1,1927 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 02.01.2004 by RST. +// $Id: CM.java,v 1.1 2004-07-07 19:59:29 hzi Exp $ + +package jake2.qcommon; + +import jake2.Defines; +import jake2.game.*; +import jake2.util.Lib; +import jake2.util.Math3D; +import jake2.util.Vargs; + +import java.io.FileNotFoundException; +import java.io.RandomAccessFile; +import java.nio.*; +import java.util.Arrays; + +public class CM extends Game { + + public static class cnode_t { + cplane_t plane; // ptr + int children[] = { 0, 0 }; // negative numbers are leafs + } + + public static class cbrushside_t { + cplane_t plane; // ptr + mapsurface_t surface; // ptr + } + + public static class cleaf_t { + int contents; + int cluster; + int area; + + // was unsigned short, but is ok (rst) + short firstleafbrush; + // was unsigned short, but is ok (rst) + short numleafbrushes; + } + + public static class cbrush_t { + int contents; + int numsides; + int firstbrushside; + int checkcount; // to avoid repeated testings + } + + public static class carea_t { + int numareaportals; + int firstareaportal; + int floodnum; // if two areas have equal floodnums, they are connected + int floodvalid; + } + + static int checkcount; + + static String map_name = ""; + + static int numbrushsides; + static cbrushside_t map_brushsides[] = new cbrushside_t[MAX_MAP_BRUSHSIDES]; + static { + for (int n = 0; n < MAX_MAP_BRUSHSIDES; n++) + map_brushsides[n] = new cbrushside_t(); + } + + public static int numtexinfo; + public static mapsurface_t map_surfaces[] = new mapsurface_t[MAX_MAP_TEXINFO]; + static { + for (int n = 0; n < MAX_MAP_TEXINFO; n++) + map_surfaces[n] = new mapsurface_t(); + } + + static int numplanes; + // extra for box hull ( +6) + static cplane_t map_planes[] = new cplane_t[MAX_MAP_PLANES + 6]; + + static { + for (int n = 0; n < MAX_MAP_PLANES + 6; n++) + map_planes[n] = new cplane_t(); + } + + static int numnodes; + // extra for box hull ( +6) + static cnode_t map_nodes[] = new cnode_t[MAX_MAP_NODES + 6]; + + static { + for (int n = 0; n < MAX_MAP_NODES + 6; n++) + map_nodes[n] = new cnode_t(); + } + + static int numleafs = 1; // allow leaf funcs to be called without a map + static cleaf_t map_leafs[] = new cleaf_t[MAX_MAP_LEAFS]; + static { + for (int n = 0; n < MAX_MAP_LEAFS; n++) + map_leafs[n] = new cleaf_t(); + } + + static int emptyleaf, solidleaf; + + static int numleafbrushes; + //static unsigned short map_leafbrushes[MAX_MAP_LEAFBRUSHES]; + public static int map_leafbrushes[] = new int[MAX_MAP_LEAFBRUSHES]; + public static int numcmodels; + public static cmodel_t map_cmodels[] = new cmodel_t[MAX_MAP_MODELS]; + static { + for (int n = 0; n < MAX_MAP_MODELS; n++) + map_cmodels[n] = new cmodel_t(); + + } + + public static int numbrushes; + public static cbrush_t map_brushes[] = new cbrush_t[MAX_MAP_BRUSHES]; + static { + for (int n = 0; n < MAX_MAP_BRUSHES; n++) + map_brushes[n] = new cbrush_t(); + + } + + public static int numvisibility; + public static byte map_visibility[] = new byte[MAX_MAP_VISIBILITY]; + + // main visibility data. rst + // was: static dvis_t *map_vis = (dvis_t *)map_visibility; + public static qfiles.dvis_t map_vis = new qfiles.dvis_t(ByteBuffer.wrap(map_visibility)); + + public static int numentitychars; + public static String map_entitystring; + + public static int numareas = 1; + public static carea_t map_areas[] = new carea_t[MAX_MAP_AREAS]; + static { + for (int n = 0; n < MAX_MAP_AREAS; n++) + map_areas[n] = new carea_t(); + + } + public static int numareaportals; + public static qfiles.dareaportal_t map_areaportals[] = new qfiles.dareaportal_t[MAX_MAP_AREAPORTALS]; + + static { + for (int n = 0; n < MAX_MAP_AREAPORTALS; n++) + map_areaportals[n] = new qfiles.dareaportal_t(); + + } + + public static int numclusters = 1; + + public static mapsurface_t nullsurface = new mapsurface_t(); + + public static int floodvalid; + + public static boolean portalopen[] = new boolean[MAX_MAP_AREAPORTALS]; + + public static cvar_t map_noareas; + + public static int c_pointcontents; + public static int c_traces, c_brush_traces; + + /* + =============================================================================== + + MAP LOADING + + =============================================================================== + */ + + public static byte cmod_base[]; + + // is that right (rst) ? + public static int checksum; + public static int last_checksum; + + /* + ================== + CM_LoadMap + + Loads in the map and all submodels + ================== + */ + public static cmodel_t CM_LoadMap(String name, boolean clientload, intwrap checksum) { + Com.DPrintf("CM_LoadMap...\n"); + byte buf[]; + int i; + qfiles.dheader_t header; + int length; + + map_noareas = Cvar.Get("map_noareas", "0", 0); + + if (0 == strcmp(map_name, name) && (clientload || 0 == Cvar.VariableValue("flushmap"))) { + + checksum.i = last_checksum; + + if (!clientload) { + Arrays.fill(portalopen, false); + FloodAreaConnections(); + } + return map_cmodels[0]; // still have the right version + } + + // free old stuff + numnodes = 0; + numleafs = 0; + numcmodels = 0; + numvisibility = 0; + numentitychars = 0; + map_entitystring = ""; + map_name = ""; + + if (name == null || name.length() == 0) { + numleafs = 1; + numclusters = 1; + numareas = 1; + checksum.i = 0; + return map_cmodels[0]; + // cinematic servers won't have anything at all + } + + // + // load the file + // + buf = FS.LoadFile(name); + + if (buf == null) + Com.Error(ERR_DROP, "Couldn't load " + name); + + length = buf.length; + + ByteBuffer bbuf = ByteBuffer.wrap(buf); + + last_checksum = MD4.Com_BlockChecksum(buf, length); + checksum.i = last_checksum; + + header = new qfiles.dheader_t(bbuf.slice()); + + if (header.version != BSPVERSION) + Com.Error( + ERR_DROP, + "CMod_LoadBrushModel: " + name + " has wrong version number (" + header.version + " should be " + BSPVERSION + ")"); + + cmod_base = buf; + + // load into heap + CMod_LoadSurfaces(header.lumps[LUMP_TEXINFO]); // ok. + CMod_LoadLeafs(header.lumps[LUMP_LEAFS]); + CMod_LoadLeafBrushes(header.lumps[LUMP_LEAFBRUSHES]); + CMod_LoadPlanes(header.lumps[LUMP_PLANES]); + CMod_LoadBrushes(header.lumps[LUMP_BRUSHES]); + CMod_LoadBrushSides(header.lumps[LUMP_BRUSHSIDES]); + CMod_LoadSubmodels(header.lumps[LUMP_MODELS]); + + CMod_LoadNodes(header.lumps[LUMP_NODES]); + CMod_LoadAreas(header.lumps[LUMP_AREAS]); + CMod_LoadAreaPortals(header.lumps[LUMP_AREAPORTALS]); + CMod_LoadVisibility(header.lumps[LUMP_VISIBILITY]); + CMod_LoadEntityString(header.lumps[LUMP_ENTITIES]); + + FS.FreeFile(buf); + + CM_InitBoxHull(); + + Arrays.fill(portalopen, false); + + FloodAreaConnections(); + + map_name = name; + + // debug (rst) + Com.p("Testing pointleafes:"); + for (int n = 0; n < 20; n++) { + float pos[] = new float[] {(float) (Math.random() * 1000), (float) (Math.random() * 1000), 0 }; + int x = CM_PointLeafnum(pos); + + Com.p(Lib.vtofsbeaty(pos) + " ---> leaf=" + x + " area = " +map_leafs[x].area); + } + + return map_cmodels[0]; + } + + /* + ================= + CMod_LoadSubmodels + ================= + */ + public static void CMod_LoadSubmodels(lump_t l) { + Com.DPrintf("CMod_LoadSubmodels...\n"); + qfiles.dmodel_t in; + cmodel_t out; + int i, j, count; + + if ((l.filelen % qfiles.dmodel_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dmodel_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map with no models"); + if (count > MAX_MAP_MODELS) + Com.Error(ERR_DROP, "Map has too many models"); + + Com.DPrintf(" numcmodels=" + count + "\n"); + numcmodels = count; + + if (debugloadmap) { + Com.DPrintf("submodles(headnode, <origin>, <mins>, <maxs>)\n"); + } + for (i = 0; i < count; i++) { + in = new qfiles.dmodel_t(ByteBuffer.wrap(cmod_base, i * qfiles.dmodel_t.SIZE + l.fileofs, qfiles.dmodel_t.SIZE)); + out = map_cmodels[i]; + + for (j = 0; j < 3; j++) { // spread the mins / maxs by a pixel + out.mins[j] = in.mins[j] - 1; + out.maxs[j] = in.maxs[j] + 1; + out.origin[j] = in.origin[j]; + } + out.headnode = in.headnode; + if (debugloadmap) { + Com.DPrintf( + "|%6i|%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n", + new Vargs() + .add(out.headnode) + .add(out.origin[0]) + .add(out.origin[1]) + .add(out.origin[2]) + .add(out.mins[0]) + .add(out.mins[1]) + .add(out.mins[2]) + .add(out.maxs[0]) + .add(out.maxs[1]) + .add(out.maxs[2])); + } + } + } + static boolean debugloadmap = false; + + /* + ================= + CMod_LoadSurfaces + ================= + */ + public static void CMod_LoadSurfaces(lump_t l) { + Com.DPrintf("CMod_LoadSurfaces...\n"); + texinfo_t in; + mapsurface_t out; + int i, count; + + if ((l.filelen % texinfo_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / texinfo_t.SIZE; + if (count < 1) + Com.Error(ERR_DROP, "Map with no surfaces"); + if (count > MAX_MAP_TEXINFO) + Com.Error(ERR_DROP, "Map has too many surfaces"); + + numtexinfo = count; + Com.DPrintf("numtexinfo=" + count + "\n"); + if (debugloadmap) + Com.DPrintf("surfaces:\n"); + + for (i = 0; i < count; i++) { + out = map_surfaces[i] = new mapsurface_t(); + in = new texinfo_t(cmod_base, l.fileofs + i * texinfo_t.SIZE, texinfo_t.SIZE); + + out.c.name = in.texture; + out.rname = in.texture; + out.c.flags = in.flags; + out.c.value = in.value; + + if (debugloadmap) { + Com.DPrintf("|%20s|%20s|%6i|%6i|\n", new Vargs().add(out.c.name).add(out.rname).add(out.c.value).add(out.c.flags)); + } + + } + } + + /* + ================= + CMod_LoadNodes + + ================= + */ + public static void CMod_LoadNodes(lump_t l) { + Com.DPrintf("CMod_LoadNodes...\n"); + qfiles.dnode_t in; + int child; + cnode_t out; + int i, j, count; + + if ((l.filelen % qfiles.dnode_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size:" + l.fileofs + "," + qfiles.dnode_t.SIZE); + count = l.filelen / qfiles.dnode_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map has no nodes"); + if (count > MAX_MAP_NODES) + Com.Error(ERR_DROP, "Map has too many nodes"); + + numnodes = count; + Com.DPrintf(" numnodes=" + count + "\n"); + + if (debugloadmap) { + Com.DPrintf("nodes(planenum, child[0], child[1])\n"); + } + + for (i = 0; i < count; i++) { + in = new qfiles.dnode_t(ByteBuffer.wrap(cmod_base, qfiles.dnode_t.SIZE * i + l.fileofs, qfiles.dnode_t.SIZE)); + out = map_nodes[i]; + + out.plane = map_planes[in.planenum]; + for (j = 0; j < 2; j++) { + child = in.children[j]; + out.children[j] = child; + } + if (debugloadmap) { + Com.DPrintf("|%6i| %6i| %6i|\n", new Vargs().add(in.planenum).add(out.children[0]).add(out.children[1])); + } + } + } + + /* + ================= + CMod_LoadBrushes + + ================= + */ + public static void CMod_LoadBrushes(lump_t l) { + Com.DPrintf("CMod_LoadBrushes...\n"); + qfiles.dbrush_t in; + cbrush_t out; + int i, count; + + if ((l.filelen % qfiles.dbrush_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dbrush_t.SIZE; + + if (count > MAX_MAP_BRUSHES) + Com.Error(ERR_DROP, "Map has too many brushes"); + + numbrushes = count; + Com.DPrintf(" numbrushes=" + count + "\n"); + if (debugloadmap) { + Com.DPrintf("brushes:(firstbrushside, numsides, contents)\n"); + } + for (i = 0; i < count; i++) { + in = new qfiles.dbrush_t(ByteBuffer.wrap(cmod_base, i * qfiles.dbrush_t.SIZE + l.fileofs, qfiles.dbrush_t.SIZE)); + out = map_brushes[i]; + out.firstbrushside = in.firstside; + out.numsides = in.numsides; + out.contents = in.contents; + + if (debugloadmap) { + Com.DPrintf("| %6i| %6i| %8X|\n", new Vargs().add(out.firstbrushside).add(out.numsides).add(out.contents)); + } + } + } + + /* + ================= + CMod_LoadLeafs + ================= + */ + public static void CMod_LoadLeafs(lump_t l) { + Com.DPrintf("CMod_LoadLeafs...\n"); + int i; + cleaf_t out; + qfiles.dleaf_t in; + int count; + + if ((l.filelen % qfiles.dleaf_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dleaf_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map with no leafs"); + + // need to save space for box planes + if (count > MAX_MAP_PLANES) + Com.Error(ERR_DROP, "Map has too many planes"); + + Com.DPrintf(" numleafes=" + count + "\n"); + + numleafs = count; + numclusters = 0; + if (debugloadmap) + Com.DPrintf("cleaf-list:(contents, cluster, area, firstleafbrush, numleafbrushes)\n"); + for (i = 0; i < count; i++) { + in = new qfiles.dleaf_t(cmod_base, i * qfiles.dleaf_t.SIZE + l.fileofs, qfiles.dleaf_t.SIZE); + + out = map_leafs[i]; + + out.contents = in.contents; + out.cluster = in.cluster; + out.area = in.area; + out.firstleafbrush = (short) in.firstleafbrush; + out.numleafbrushes = (short) in.numleafbrushes; + + if (out.cluster >= numclusters) + numclusters = out.cluster + 1; + + if (debugloadmap) { + Com.DPrintf( + "|%8x|%6i|%6i|%6i|\n", + new Vargs().add(out.contents).add(out.cluster).add(out.area).add(out.firstleafbrush).add(out.numleafbrushes)); + } + + } + + Com.DPrintf(" numclusters=" + numclusters + "\n"); + + if (map_leafs[0].contents != CONTENTS_SOLID) + Com.Error(ERR_DROP, "Map leaf 0 is not CONTENTS_SOLID"); + + solidleaf = 0; + emptyleaf = -1; + + for (i = 1; i < numleafs; i++) { + if (map_leafs[i].contents == 0) { + emptyleaf = i; + break; + } + } + + if (emptyleaf == -1) + Com.Error(ERR_DROP, "Map does not have an empty leaf"); + } + + /* + ================= + CMod_LoadPlanes + ================= + */ + public static void CMod_LoadPlanes(lump_t l) { + Com.DPrintf("CMod_LoadPlanes...\n"); + int i, j; + cplane_t out; + qfiles.dplane_t in; + int count; + int bits; + + if ((l.filelen % qfiles.dplane_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.dplane_t.SIZE; + + if (count < 1) + Com.Error(ERR_DROP, "Map with no planes"); + + // need to save space for box planes + if (count > MAX_MAP_PLANES) + Com.Error(ERR_DROP, "Map has too many planes"); + + Com.DPrintf(" numplanes=" + count + "\n"); + + numplanes = count; + if (debugloadmap) { + Com.DPrintf("cplanes(normal[0],normal[1],normal[2], dist, type, signbits)\n"); + } + + for (i = 0; i < count; i++) { + in = new qfiles.dplane_t(ByteBuffer.wrap(cmod_base, i * qfiles.dplane_t.SIZE + l.fileofs, qfiles.dplane_t.SIZE)); + + out = map_planes[i]; + + bits = 0; + for (j = 0; j < 3; j++) { + out.normal[j] = in.normal[j]; + + if (out.normal[j] < 0) + bits |= 1 << j; + } + + out.dist = in.dist; + out.type = (byte) in.type; + out.signbits = (byte) bits; + + if (debugloadmap) { + Com.DPrintf( + "|%6.2f|%6.2f|%6.2f| %10.2f|%3i| %1i|\n", + new Vargs().add(out.normal[0]).add(out.normal[1]).add(out.normal[2]).add(out.dist).add(out.type).add( + out.signbits)); + } + } + } + + /* + ================= + CMod_LoadLeafBrushes + ================= + */ + public static void CMod_LoadLeafBrushes(lump_t l) { + Com.DPrintf("CMod_LoadLeafBrushes...\n"); + int i; + int out[]; + short in[]; + int count; + + if ((l.filelen % 2) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / 2; + + Com.DPrintf(" numbrushes=" + count + "\n"); + + if (count < 1) + Com.Error(ERR_DROP, "Map with no planes"); + + // need to save space for box planes + if (count > MAX_MAP_LEAFBRUSHES) + Com.Error(ERR_DROP, "Map has too many leafbrushes"); + + out = map_leafbrushes; + numleafbrushes = count; + + ByteBuffer bb = ByteBuffer.wrap(cmod_base, l.fileofs, count * 2).order(ByteOrder.LITTLE_ENDIAN); + + if (debugloadmap) { + Com.DPrintf("map_brushes:\n"); + } + + for (i = 0; i < count; i++) { + out[i] = bb.getShort(); + if (debugloadmap) { + Com.DPrintf("|%6i|%6i|\n", new Vargs().add(i).add(out[i])); + } + } + } + + /* + ================= + CMod_LoadBrushSides + ================= + */ + public static void CMod_LoadBrushSides(lump_t l) { + Com.DPrintf("CMod_LoadBrushSides...\n"); + int i, j; + cbrushside_t out; + qfiles.dbrushside_t in; + int count; + int num; + + if ((l.filelen % qfiles.dbrushside_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + count = l.filelen / qfiles.dbrushside_t.SIZE; + + // need to save space for box planes + if (count > MAX_MAP_BRUSHSIDES) + Com.Error(ERR_DROP, "Map has too many planes"); + + numbrushsides = count; + + Com.DPrintf(" numbrushsides=" + count + "\n"); + + if (debugloadmap) { + Com.DPrintf("brushside(planenum, surfacenum):\n"); + } + for (i = 0; i < count; i++) { + + in = + new qfiles.dbrushside_t( + ByteBuffer.wrap(cmod_base, i * qfiles.dbrushside_t.SIZE + l.fileofs, qfiles.dbrushside_t.SIZE)); + + out = map_brushsides[i]; + + num = in.planenum; + + out.plane = map_planes[num]; // pointer + + j = in.texinfo; + + if (j >= numtexinfo) + Com.Error(ERR_DROP, "Bad brushside texinfo"); + + // rst: some mysterious happens here, even in the original code ???, texinfo is -1!!! + // hoz: checked against c version: ok. + if (j == -1) + out.surface = new mapsurface_t(); // just for safety + else + out.surface = map_surfaces[j]; + + if (debugloadmap) { + Com.DPrintf("| %6i| %6i|\n", new Vargs().add(num).add(j)); + } + } + } + + /* + ================= + CMod_LoadAreas + ================= + */ + public static void CMod_LoadAreas(lump_t l) { + Com.DPrintf("CMod_LoadAreas...\n"); + int i; + carea_t out; + qfiles.darea_t in; + int count; + + if ((l.filelen % qfiles.darea_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + + count = l.filelen / qfiles.darea_t.SIZE; + + if (count > MAX_MAP_AREAS) + Com.Error(ERR_DROP, "Map has too many areas"); + + Com.DPrintf(" numareas=" + count + "\n"); + numareas = count; + + if (debugloadmap) { + Com.DPrintf("areas(numportals, firstportal)\n"); + } + + for (i = 0; i < count; i++) { + + in = new qfiles.darea_t(ByteBuffer.wrap(cmod_base, i * qfiles.darea_t.SIZE + l.fileofs, qfiles.darea_t.SIZE)); + out = map_areas[i]; + + out.numareaportals = in.numareaportals; + out.firstareaportal = in.firstareaportal; + out.floodvalid = 0; + out.floodnum = 0; + if (debugloadmap) { + Com.DPrintf("| %6i| %6i|\n", new Vargs().add(out.numareaportals).add(out.firstareaportal)); + } + } + } + + /* + ================= + CMod_LoadAreaPortals + ================= + */ + public static void CMod_LoadAreaPortals(lump_t l) { + Com.DPrintf("CMod_LoadAreaPortals...\n"); + int i; + qfiles.dareaportal_t out; + qfiles.dareaportal_t in; + int count; + + if ((l.filelen % qfiles.dareaportal_t.SIZE) != 0) + Com.Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); + count = l.filelen / qfiles.dareaportal_t.SIZE; + + if (count > MAX_MAP_AREAS) + Com.Error(ERR_DROP, "Map has too many areas"); + + numareaportals = count; + Com.DPrintf(" numareaportals=" + count + "\n"); + if (debugloadmap) { + Com.DPrintf("areaportals(portalnum, otherarea)\n"); + } + for (i = 0; i < count; i++) { + in = + new qfiles.dareaportal_t( + ByteBuffer.wrap(cmod_base, i * qfiles.dareaportal_t.SIZE + l.fileofs, qfiles.dareaportal_t.SIZE)); + + out = map_areaportals[i]; + + out.portalnum = in.portalnum; + out.otherarea = in.otherarea; + + if (debugloadmap) { + Com.DPrintf("|%6i|%6i|\n", new Vargs().add(out.portalnum).add(out.otherarea)); + } + } + } + + /* + ================= + CMod_LoadVisibility + ================= + */ + public static void CMod_LoadVisibility(lump_t l) { + Com.DPrintf("CMod_LoadVisibility...\n"); + int i; + + numvisibility = l.filelen; + + Com.DPrintf(" numvisibility=" + numvisibility + "\n"); + + if (l.filelen > MAX_MAP_VISIBILITY) + Com.Error(ERR_DROP, "Map has too large visibility lump"); + + //was: memcpy(map_visibility, cmod_base + l.fileofs, l.filelen); + System.arraycopy(cmod_base, l.fileofs, map_visibility, 0, l.filelen); + + ByteBuffer bb = ByteBuffer.wrap(map_visibility, 0, l.filelen); + bb.order(ByteOrder.LITTLE_ENDIAN); + + map_vis = new qfiles.dvis_t(bb); + + } + + /* + ================= + CMod_LoadEntityString + ================= + */ + public static void CMod_LoadEntityString(lump_t l) { + Com.DPrintf("CMod_LoadEntityString...\n"); + + numentitychars = l.filelen; + + if (l.filelen > MAX_MAP_ENTSTRING) + Com.Error(ERR_DROP, "Map has too large entity lump"); + + int x = 0; + for (; x < l.filelen && cmod_base[x + l.fileofs] != 0; x++); + + map_entitystring = new String(cmod_base, l.fileofs, x).trim(); + } + + /* + ================== + CM_InlineModel + ================== + */ + + // works fine + public static cmodel_t InlineModel(String name) { + int num; + + if (name == null || name.charAt(0) != '*') + Com.Error(ERR_DROP, "CM_InlineModel: bad name"); + + num = atoi(name.substring(1)); + + if (num < 1 || num >= numcmodels) + Com.Error(ERR_DROP, "CM_InlineModel: bad number"); + + return map_cmodels[num]; + } + + public static int CM_NumClusters() { + return numclusters; + } + + public static int CM_NumInlineModels() { + return numcmodels; + } + + public static String CM_EntityString() { + return map_entitystring; + } + + public static int CM_LeafContents(int leafnum) { + if (leafnum < 0 || leafnum >= numleafs) + Com.Error(ERR_DROP, "CM_LeafContents: bad number"); + return map_leafs[leafnum].contents; + } + + public static int CM_LeafCluster(int leafnum) { + if (leafnum < 0 || leafnum >= numleafs) + Com.Error(ERR_DROP, "CM_LeafCluster: bad number"); + return map_leafs[leafnum].cluster; + } + + public static int CM_LeafArea(int leafnum) { + if (leafnum < 0 || leafnum >= numleafs) + Com.Error(ERR_DROP, "CM_LeafArea: bad number"); + return map_leafs[leafnum].area; + } + + //======================================================================= + + static cplane_t box_planes[]; + static int box_headnode; + static cbrush_t box_brush; + static cleaf_t box_leaf; + + /* + =================== + CM_InitBoxHull + + Set up the planes and nodes so that the six floats of a bounding box + can just be stored out and get a proper clipping hull structure. + =================== + */ + public static void CM_InitBoxHull() { + int i; + int side; + cnode_t c; + cplane_t p; + cbrushside_t s; + + box_headnode = numnodes; // noch platz f?r 6 brushes + + box_planes = + new cplane_t[] { + map_planes[numplanes], + map_planes[numplanes + 1], + map_planes[numplanes + 2], + map_planes[numplanes + 3], + map_planes[numplanes + 4], + map_planes[numplanes + 5], + map_planes[numplanes + 6], + map_planes[numplanes + 7], + map_planes[numplanes + 8], + map_planes[numplanes + 9], + map_planes[numplanes + 10], + map_planes[numplanes + 11], + map_planes[numplanes + 12] }; + + if (numnodes + 6 > MAX_MAP_NODES + || numbrushes + 1 > MAX_MAP_BRUSHES + || numleafbrushes + 1 > MAX_MAP_LEAFBRUSHES + || numbrushsides + 6 > MAX_MAP_BRUSHSIDES + || numplanes + 12 > MAX_MAP_PLANES) + Com.Error(ERR_DROP, "Not enough room for box tree"); + + box_brush = map_brushes[numbrushes]; + box_brush.numsides = 6; + box_brush.firstbrushside = numbrushsides; + box_brush.contents = CONTENTS_MONSTER; + + box_leaf = map_leafs[numleafs]; + box_leaf.contents = CONTENTS_MONSTER; + box_leaf.firstleafbrush = (short) numleafbrushes; + box_leaf.numleafbrushes = 1; + + map_leafbrushes[numleafbrushes] = numbrushes; + + for (i = 0; i < 6; i++) { + side = i & 1; + + // brush sides + s = map_brushsides[numbrushsides + i]; + s.plane = map_planes[(numplanes + i * 2 + side)]; + s.surface = nullsurface; + + // nodes + c = map_nodes[box_headnode + i]; + c.plane = map_planes[(numplanes + i * 2)]; + c.children[side] = -1 - emptyleaf; + if (i != 5) + c.children[side ^ 1] = box_headnode + i + 1; + else + c.children[side ^ 1] = -1 - numleafs; + + // planes + p = box_planes[i * 2]; + p.type = (byte) (i >> 1); + p.signbits = 0; + VectorClear(p.normal); + p.normal[i >> 1] = 1; + + p = box_planes[i * 2 + 1]; + p.type = (byte) (3 + (i >> 1)); + p.signbits = 0; + VectorClear(p.normal); + p.normal[i >> 1] = -1; + } + } + + /* + =================== + CM_HeadnodeForBox + + To keep everything totally uniform, bounding boxes are turned into small + BSP trees instead of being compared directly. + =================== + */ + public static int HeadnodeForBox(float[] mins, float[] maxs) { + box_planes[0].dist = maxs[0]; + box_planes[1].dist = -maxs[0]; + box_planes[2].dist = mins[0]; + box_planes[3].dist = -mins[0]; + box_planes[4].dist = maxs[1]; + box_planes[5].dist = -maxs[1]; + box_planes[6].dist = mins[1]; + box_planes[7].dist = -mins[1]; + box_planes[8].dist = maxs[2]; + box_planes[9].dist = -maxs[2]; + box_planes[10].dist = mins[2]; + box_planes[11].dist = -mins[2]; + + return box_headnode; + } + + /* + ================== + CM_PointLeafnum_r + ================== + */ + public static int CM_PointLeafnum_r(float[] p, int num) { + float d; + cnode_t node; + cplane_t plane; + + while (num >= 0) { + node = map_nodes[num]; + plane = node.plane; + + if (plane.type < 3) + d = p[plane.type] - plane.dist; + else + d = DotProduct(plane.normal, p) - plane.dist; + if (d < 0) + num = node.children[1]; + else + num = node.children[0]; + } + + c_pointcontents++; // optimize counter + + return -1 - num; + } + + public static int CM_PointLeafnum(float[] p) { + if (numplanes == 0) + return 0; // sound may call this without map loaded + return CM_PointLeafnum_r(p, 0); + } + + /* + ============= + CM_BoxLeafnums + + Fills in a list of all the leafs touched + ============= + */ + static int leaf_count, leaf_maxcount; + static int leaf_list[]; + static float leaf_mins[], leaf_maxs[]; + static int leaf_topnode; + + public static void CM_BoxLeafnums_r(int nodenum) { + cplane_t plane; + cnode_t node; + int s; + + while (true) { + if (nodenum < 0) { + if (leaf_count >= leaf_maxcount) { + Com.DPrintf("CM_BoxLeafnums_r: overflow\n"); + return; + } + leaf_list[leaf_count++] = -1 - nodenum; + return; + } + + node = map_nodes[nodenum]; + plane = node.plane; + + s = Math3D.BoxOnPlaneSide(leaf_mins, leaf_maxs, plane); + + if (s == 1) + nodenum = node.children[0]; + else if (s == 2) + nodenum = node.children[1]; + else { + // go down both + if (leaf_topnode == -1) + leaf_topnode = nodenum; + CM_BoxLeafnums_r(node.children[0]); + nodenum = node.children[1]; + } + } + } + + public static int CM_BoxLeafnums_headnode(float[] mins, float[] maxs, int list[], int listsize, int headnode, intwrap topnode) { + leaf_list = list; + leaf_count = 0; + leaf_maxcount = listsize; + leaf_mins = mins; + leaf_maxs = maxs; + + leaf_topnode = -1; + + CM_BoxLeafnums_r(headnode); + + if (topnode != null) + topnode.i = leaf_topnode; + + return leaf_count; + } + + public static int CM_BoxLeafnums(float[] mins, float[] maxs, int list[], int listsize, intwrap topnode) { + return CM_BoxLeafnums_headnode(mins, maxs, list, listsize, map_cmodels[0].headnode, topnode); + } + + public static class intwrap { + public intwrap(int i) { + this.i = i; + } + public int i; + } + + /* + ================== + CM_PointContents + ================== + */ + public static int PointContents(float[] p, int headnode) { + int l; + + if (numnodes == 0) // map not loaded + return 0; + + l = CM_PointLeafnum_r(p, headnode); + + return map_leafs[l].contents; + } + + /* + ================== + CM_TransformedPointContents + + Handles offseting and rotation of the end points for moving and + rotating entities + ================== + */ + public static int TransformedPointContents(float[] p, int headnode, float[] origin, float[] angles) { + float[] p_l = { 0, 0, 0 }; + float[] temp = { 0, 0, 0 }; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + int l; + + // subtract origin offset + VectorSubtract(p, origin, p_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0)) { + AngleVectors(angles, forward, right, up); + + VectorCopy(p_l, temp); + p_l[0] = DotProduct(temp, forward); + p_l[1] = -DotProduct(temp, right); + p_l[2] = DotProduct(temp, up); + } + + l = CM_PointLeafnum_r(p_l, headnode); + + return map_leafs[l].contents; + } + + /* + =============================================================================== + + BOX TRACING + + =============================================================================== + */ + + // 1/32 epsilon to keep floating point happy + private static final float DIST_EPSILON = 0.03125f; + + private static float[] trace_start = { 0, 0, 0 }, trace_end = { 0, 0, 0 }; + private static float[] trace_mins = { 0, 0, 0 }, trace_maxs = { 0, 0, 0 }; + private static float[] trace_extents = { 0, 0, 0 }; + + private static trace_t trace_trace = new trace_t(); + private static int trace_contents; + private static boolean trace_ispoint; // optimized case + + /* + ================ + CM_ClipBoxToBrush + ================ + */ + public static void CM_ClipBoxToBrush(float[] mins, float[] maxs, float[] p1, float[] p2, trace_t trace, cbrush_t brush) { + int i, j; + cplane_t plane, clipplane; + float dist; + float enterfrac, leavefrac; + float[] ofs = { 0, 0, 0 }; + float d1, d2; + boolean getout, startout; + float f; + cbrushside_t side, leadside; + + enterfrac = -1; + leavefrac = 1; + clipplane = null; + + if (brush.numsides == 0) + return; + + c_brush_traces++; + + getout = false; + startout = false; + leadside = null; + + for (i = 0; i < brush.numsides; i++) { + side = map_brushsides[brush.firstbrushside + i]; + plane = side.plane; + + // FIXME: special case for axial + + if (!trace_ispoint) { // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j = 0; j < 3; j++) { + if (plane.normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct(ofs, plane.normal); + dist = plane.dist - dist; + } + else { // special point case + dist = plane.dist; + } + + d1 = DotProduct(p1, plane.normal) - dist; + d2 = DotProduct(p2, plane.normal) - dist; + + if (d2 > 0) + getout = true; // endpoint is not in solid + if (d1 > 0) + startout = true; + + // if completely in front of face, no intersection + if (d1 > 0 && d2 >= d1) + return; + + if (d1 <= 0 && d2 <= 0) + continue; + + // crosses face + if (d1 > d2) { // enter + f = (d1 - DIST_EPSILON) / (d1 - d2); + if (f > enterfrac) { + enterfrac = f; + clipplane = plane; + leadside = side; + } + } + else { // leave + f = (d1 + DIST_EPSILON) / (d1 - d2); + if (f < leavefrac) + leavefrac = f; + } + } + + if (!startout) { // original point was inside brush + trace.startsolid = true; + if (!getout) + trace.allsolid = true; + return; + } + if (enterfrac < leavefrac) { + if (enterfrac > -1 && enterfrac < trace.fraction) { + if (enterfrac < 0) + enterfrac = 0; + trace.fraction = enterfrac; + // copy + trace.plane.set(clipplane); + trace.surface = leadside.surface.c; + trace.contents = brush.contents; + } + } + } + + /* + ================ + CM_TestBoxInBrush + ================ + */ + public static void CM_TestBoxInBrush(float[] mins, float[] maxs, float[] p1, trace_t trace, cbrush_t brush) { + int i, j; + cplane_t plane; + float dist; + float[] ofs = { 0, 0, 0 }; + float d1; + cbrushside_t side; + + if (brush.numsides == 0) + return; + + for (i = 0; i < brush.numsides; i++) { + side = map_brushsides[brush.firstbrushside + i]; + plane = side.plane; + + // FIXME: special case for axial + // general box case + // push the plane out apropriately for mins/maxs + // FIXME: use signbits into 8 way lookup for each mins/maxs + + for (j = 0; j < 3; j++) { + if (plane.normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct(ofs, plane.normal); + dist = plane.dist - dist; + + d1 = DotProduct(p1, plane.normal) - dist; + + // if completely in front of face, no intersection + if (d1 > 0) + return; + + } + + // inside this brush + trace.startsolid = trace.allsolid = true; + trace.fraction = 0; + trace.contents = brush.contents; + } + + /* + ================ + CM_TraceToLeaf + ================ + */ + public static void CM_TraceToLeaf(int leafnum) { + int k; + int brushnum; + cleaf_t leaf; + cbrush_t b; + + leaf = map_leafs[leafnum]; + if (0 == (leaf.contents & trace_contents)) + return; + + // trace line against all brushes in the leaf + for (k = 0; k < leaf.numleafbrushes; k++) { + + brushnum = map_leafbrushes[leaf.firstleafbrush + k]; + b = map_brushes[brushnum]; + if (b.checkcount == checkcount) + continue; // already checked this brush in another leaf + b.checkcount = checkcount; + + if (0 == (b.contents & trace_contents)) + continue; + CM_ClipBoxToBrush(trace_mins, trace_maxs, trace_start, trace_end, trace_trace, b); + if (0 == trace_trace.fraction) + return; + } + + } + + /* + ================ + CM_TestInLeaf + ================ + */ + public static void CM_TestInLeaf(int leafnum) { + int k; + int brushnum; + cleaf_t leaf; + cbrush_t b; + + leaf = map_leafs[leafnum]; + if (0 == (leaf.contents & trace_contents)) + return; + // trace line against all brushes in the leaf + for (k = 0; k < leaf.numleafbrushes; k++) { + brushnum = map_leafbrushes[leaf.firstleafbrush + k]; + b = map_brushes[brushnum]; + if (b.checkcount == checkcount) + continue; // already checked this brush in another leaf + b.checkcount = checkcount; + + if (0 == (b.contents & trace_contents)) + continue; + CM_TestBoxInBrush(trace_mins, trace_maxs, trace_start, trace_trace, b); + if (0 == trace_trace.fraction) + return; + } + + } + + /* + ================== + CM_RecursiveHullCheck + ================== + */ + public static void CM_RecursiveHullCheck(int num, float p1f, float p2f, float[] p1, float[] p2) { + cnode_t node; + cplane_t plane; + float t1, t2, offset; + float frac, frac2; + float idist; + int i; + float[] mid = { 0, 0, 0 }; + int side; + float midf; + + if (trace_trace.fraction <= p1f) + return; // already hit something nearer + + // if < 0, we are in a leaf node + if (num < 0) { + CM_TraceToLeaf(-1 - num); + return; + } + + // + // find the point distances to the seperating plane + // and the offset for the size of the box + // + node = map_nodes[num]; + plane = node.plane; + + if (plane.type < 3) { + t1 = p1[plane.type] - plane.dist; + t2 = p2[plane.type] - plane.dist; + offset = trace_extents[plane.type]; + } + else { + t1 = DotProduct(plane.normal, p1) - plane.dist; + t2 = DotProduct(plane.normal, p2) - plane.dist; + if (trace_ispoint) + offset = 0; + else + offset = + Math.abs(trace_extents[0] * plane.normal[0]) + + Math.abs(trace_extents[1] * plane.normal[1]) + + Math.abs(trace_extents[2] * plane.normal[2]); + } + + // see which sides we need to consider + if (t1 >= offset && t2 >= offset) { + CM_RecursiveHullCheck(node.children[0], p1f, p2f, p1, p2); + return; + } + if (t1 < -offset && t2 < -offset) { + CM_RecursiveHullCheck(node.children[1], p1f, p2f, p1, p2); + return; + } + + // put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < t2) { + idist = 1.0f / (t1 - t2); + side = 1; + frac2 = (t1 + offset + DIST_EPSILON) * idist; + frac = (t1 - offset + DIST_EPSILON) * idist; + } + else if (t1 > t2) { + idist = 1.0f / (t1 - t2); + side = 0; + frac2 = (t1 - offset - DIST_EPSILON) * idist; + frac = (t1 + offset + DIST_EPSILON) * idist; + } + else { + side = 0; + frac = 1; + frac2 = 0; + } + + // move up to the node + if (frac < 0) + frac = 0; + if (frac > 1) + frac = 1; + + midf = p1f + (p2f - p1f) * frac; + for (i = 0; i < 3; i++) + mid[i] = p1[i] + frac * (p2[i] - p1[i]); + + CM_RecursiveHullCheck(node.children[side], p1f, midf, p1, mid); + + // go past the node + if (frac2 < 0) + frac2 = 0; + if (frac2 > 1) + frac2 = 1; + + midf = p1f + (p2f - p1f) * frac2; + for (i = 0; i < 3; i++) + mid[i] = p1[i] + frac2 * (p2[i] - p1[i]); + + CM_RecursiveHullCheck(node.children[side ^ 1], midf, p2f, mid, p2); + } + + //====================================================================== + + /* + ================== + CM_BoxTrace + ================== + */ + public static trace_t BoxTrace(float[] start, float[] end, float[] mins, float[] maxs, int headnode, int brushmask) { + + // for multi-check avoidance + checkcount++; + + // for statistics, may be zeroed + c_traces++; + + // fill in a default trace + //was: memset(& trace_trace, 0, sizeof(trace_trace)); + trace_trace = new trace_t(); + + trace_trace.fraction = 1; + trace_trace.surface = nullsurface.c; + + if (numnodes == 0) // map not loaded + { + Com.DPrintf("dummy trace zurueck, da map not loaded!\n"); + return trace_trace; + } + + trace_contents = brushmask; + VectorCopy(start, trace_start); + VectorCopy(end, trace_end); + VectorCopy(mins, trace_mins); + VectorCopy(maxs, trace_maxs); + + // + // check for position test special case + // + if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) { + + int leafs[] = new int[1024]; + int i, numleafs; + float[] c1 = { 0, 0, 0 }, c2 = { 0, 0, 0 }; + int topnode = 0; + + VectorAdd(start, mins, c1); + VectorAdd(start, maxs, c2); + + for (i = 0; i < 3; i++) { + c1[i] -= 1; + c2[i] += 1; + } + + intwrap tn = new intwrap(topnode); + + numleafs = CM_BoxLeafnums_headnode(c1, c2, leafs, 1024, headnode, tn); + topnode = tn.i; + for (i = 0; i < numleafs; i++) { + CM_TestInLeaf(leafs[i]); + if (trace_trace.allsolid) + break; + } + VectorCopy(start, trace_trace.endpos); + return trace_trace; + } + + // + // check for point special case + // + if (mins[0] == 0 && mins[1] == 0 && mins[2] == 0 && maxs[0] == 0 && maxs[1] == 0 && maxs[2] == 0) { + trace_ispoint = true; + VectorClear(trace_extents); + } + else { + trace_ispoint = false; + trace_extents[0] = -mins[0] > maxs[0] ? -mins[0] : maxs[0]; + trace_extents[1] = -mins[1] > maxs[1] ? -mins[1] : maxs[1]; + trace_extents[2] = -mins[2] > maxs[2] ? -mins[2] : maxs[2]; + } + + // + // general sweeping through world + // + CM_RecursiveHullCheck(headnode, 0, 1, start, end); + + if (trace_trace.fraction == 1) { + VectorCopy(end, trace_trace.endpos); + } + else { + for (int i = 0; i < 3; i++) + trace_trace.endpos[i] = start[i] + trace_trace.fraction * (end[i] - start[i]); + } + return trace_trace; + } + + /* + ================== + CM_TransformedBoxTrace + + Handles offseting and rotation of the end points for moving and + rotating entities + ================== + */ + public static trace_t TransformedBoxTrace( + float[] start, + float[] end, + float[] mins, + float[] maxs, + int headnode, + int brushmask, + float[] origin, + float[] angles) { + trace_t trace; + float[] start_l = { 0, 0, 0 }, end_l = { 0, 0, 0 }; + float[] a = { 0, 0, 0 }; + float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 }; + float[] temp = { 0, 0, 0 }; + boolean rotated; + + // subtract origin offset + VectorSubtract(start, origin, start_l); + VectorSubtract(end, origin, end_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0)) + rotated = true; + else + rotated = false; + + if (rotated) { + AngleVectors(angles, forward, right, up); + + VectorCopy(start_l, temp); + start_l[0] = DotProduct(temp, forward); + start_l[1] = -DotProduct(temp, right); + start_l[2] = DotProduct(temp, up); + + VectorCopy(end_l, temp); + end_l[0] = DotProduct(temp, forward); + end_l[1] = -DotProduct(temp, right); + end_l[2] = DotProduct(temp, up); + } + + // sweep the box through the model + trace = BoxTrace(start_l, end_l, mins, maxs, headnode, brushmask); + + if (rotated && trace.fraction != 1.0) { + // FIXME: figure out how to do this with existing angles + VectorNegate(angles, a); + AngleVectors(a, forward, right, up); + + VectorCopy(trace.plane.normal, temp); + trace.plane.normal[0] = DotProduct(temp, forward); + trace.plane.normal[1] = -DotProduct(temp, right); + trace.plane.normal[2] = DotProduct(temp, up); + } + + trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]); + trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]); + trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]); + + return trace; + } + + /* + =============================================================================== + PVS / PHS + =============================================================================== + */ + + /* + =================== + CM_DecompressVis + =================== + */ + public static void CM_DecompressVis(byte in[], int offset, byte out[]) { + int c; + + int row; + + row = (numclusters + 7) >> 3; + int outp = 0; + int inp = offset; + + if (in == null || numvisibility == 0) { // no vis info, so make all visible + while (row != 0) { + out[outp++] = (byte) 0xFF; + row--; + } + return; + } + + do { + if (in[inp] != 0) { + out[outp++] = in[inp++]; + continue; + } + + c = in[inp + 1] & 0xFF; + inp += 2; + if (outp + c > row) { + c = row - (outp); + Com.DPrintf("warning: Vis decompression overrun\n"); + } + while (c != 0) { + out[outp++] = 0; + c--; + } + } + while (outp < row); + } + + public static byte pvsrow[] = new byte[MAX_MAP_LEAFS / 8]; + public static byte phsrow[] = new byte[MAX_MAP_LEAFS / 8]; + + public static byte[] CM_ClusterPVS(int cluster) { + if (cluster == -1) + Lib.memset(pvsrow, (byte) 0, (numclusters + 7) >> 3); + else + CM_DecompressVis(map_visibility, map_vis.bitofs[cluster][DVIS_PVS], pvsrow); + return pvsrow; + } + + public static byte[] CM_ClusterPHS(int cluster) { + if (cluster == -1) + Lib.memset(phsrow, (byte) 0, (numclusters + 7) >> 3); + else + CM_DecompressVis(map_visibility, map_vis.bitofs[cluster][Defines.DVIS_PHS], phsrow); + return phsrow; + } + + /* + =============================================================================== + AREAPORTALS + =============================================================================== + */ + + public static void FloodArea_r(carea_t area, int floodnum) { + Com.DPrintf("FloodArea_r(" + floodnum + ")...\n"); + int i; + qfiles.dareaportal_t p; + + if (area.floodvalid == floodvalid) { + if (area.floodnum == floodnum) + return; + Com.Error(ERR_DROP, "FloodArea_r: reflooded"); + } + + area.floodnum = floodnum; + area.floodvalid = floodvalid; + + for (i = 0; i < area.numareaportals; i++) { + p = map_areaportals[area.firstareaportal + i]; + if (portalopen[p.portalnum]) + FloodArea_r(map_areas[p.otherarea], floodnum); + } + } + + /* + ==================== + FloodAreaConnections + ==================== + */ + public static void FloodAreaConnections() { + Com.DPrintf("FloodAreaConnections...\n"); + + int i; + carea_t area; + int floodnum; + + // all current floods are now invalid + floodvalid++; + floodnum = 0; + + // area 0 is not used + for (i = 1; i < numareas; i++) { + + area = map_areas[i]; + + if (area.floodvalid == floodvalid) + continue; // already flooded into + floodnum++; + FloodArea_r(area, floodnum); + } + } + + /* + ================= + CM_SetAreaPortalState + ================= + */ + public static void CM_SetAreaPortalState(int portalnum, boolean open) { + if (portalnum > numareaportals) + Com.Error(ERR_DROP, "areaportal > numareaportals"); + + portalopen[portalnum] = open; + FloodAreaConnections(); + } + + /* + ================= + CM_AreasConnected + ================= + */ + + public static boolean CM_AreasConnected(int area1, int area2) { + if (map_noareas.value != 0) + return true; + + if (area1 > numareas || area2 > numareas) + Com.Error(ERR_DROP, "area > numareas"); + + if (map_areas[area1].floodnum == map_areas[area2].floodnum) + return true; + + return false; + } + + /* + ================= + CM_WriteAreaBits + + Writes a length byte followed by a bit vector of all the areas + that area in the same flood as the area parameter + + This is used by the client refreshes to cull visibility + ================= + */ + public static int CM_WriteAreaBits(byte buffer[], int area) { + int i; + int floodnum; + int bytes; + + bytes = (numareas + 7) >> 3; + + if (map_noareas.value != 0) { // for debugging, send everything + Lib.memset(buffer, 255, bytes); + } + else { + Lib.memset(buffer, 0, bytes); + floodnum = map_areas[area].floodnum; + for (i = 0; i < numareas; i++) { + if (map_areas[i].floodnum == floodnum || area == 0) + buffer[i >> 3] |= 1 << (i & 7); + } + } + + return bytes; + } + + /* + =================== + CM_WritePortalState + + Writes the portal state to a savegame file + =================== + */ + + public static void CM_WritePortalState(RandomAccessFile os) { + + //was: fwrite(portalopen, sizeof(portalopen), 1, f); + try { + + for (int n = 0; n < portalopen.length; n++) + if (portalopen[n]) + os.writeInt(1); + else + os.writeInt(0); + } + catch (Exception e) { + Com.Printf("ERROR:" + e); + e.printStackTrace(); + } + } + + /* + =================== + CM_ReadPortalState + + Reads the portal state from a savegame file + and recalculates the area connections + =================== + */ + public static void CM_ReadPortalState(RandomAccessFile f) { + + //was: FS_Read(portalopen, sizeof(portalopen), f); + int len = portalopen.length * 4; + + byte buf[] = new byte[len]; + + try { + FS.Read(buf, len, f); + } + catch (FileNotFoundException e) { + Com.Printf("ERROR:" + e); + } + ByteBuffer bb = ByteBuffer.wrap(buf); + IntBuffer ib = bb.asIntBuffer(); + + //TODO: i assume upward counting (rst) + for (int n = 0; n < portalopen.length; n++) + portalopen[n] = ib.get() != 0; + + FloodAreaConnections(); + } + + /* + ============= + CM_HeadnodeVisible + + Returns true if any leaf under headnode has a cluster that + is potentially visible + ============= + */ + public static boolean CM_HeadnodeVisible(int nodenum, byte visbits[]) { + int leafnum; + int cluster; + cnode_t node; + + if (nodenum < 0) { + leafnum = -1 - nodenum; + cluster = map_leafs[leafnum].cluster; + if (cluster == -1) + return false; + if (0 != (visbits[cluster >>> 3] & (1 << (cluster & 7)))) + return true; + return false; + } + + node = map_nodes[nodenum]; + if (CM_HeadnodeVisible(node.children[0], visbits)) + return true; + return CM_HeadnodeVisible(node.children[1], visbits); + } +} |