/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" static hull_t box_hull; static dclipnode_t box_clipnodes[6]; static mplane_t box_planes[6]; extern vec3_t player_mins; extern vec3_t player_maxs; /* =================== PM_InitBoxHull Set up the planes and clipnodes so that the six floats of a bounding box can just be stored out and get a proper hull_t structure. =================== */ void PM_InitBoxHull (void) { int i; int side; box_hull.clipnodes = box_clipnodes; box_hull.planes = box_planes; box_hull.firstclipnode = 0; box_hull.lastclipnode = 5; Q1BSP_SetHullFuncs(&box_hull); for (i=0 ; i<6 ; i++) { box_clipnodes[i].planenum = i; side = i&1; box_clipnodes[i].children[side] = Q1CONTENTS_EMPTY; if (i != 5) box_clipnodes[i].children[side^1] = i + 1; else box_clipnodes[i].children[side^1] = Q1CONTENTS_SOLID; box_planes[i].type = i>>1; box_planes[i].normal[i>>1] = 1; } } /* =================== PM_HullForBox To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. =================== */ hull_t *PM_HullForBox (vec3_t mins, vec3_t maxs) { box_planes[0].dist = maxs[0]; box_planes[1].dist = mins[0]; box_planes[2].dist = maxs[1]; box_planes[3].dist = mins[1]; box_planes[4].dist = maxs[2]; box_planes[5].dist = mins[2]; return &box_hull; } int PM_TransformedHullPointContents (hull_t *hull, vec3_t p, vec3_t origin, vec3_t angles) { vec3_t p_l, forward, up, right, temp; VectorSubtract (p, origin, p_l); if (!player_mins[2]) p_l[2] -= hull->clip_mins[2]+0.1; // rotate start and end into the models frame of reference if (hull != &box_hull && (angles[0] || angles[1] || angles[2]) ) { 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); } return hull->funcs.HullPointContents(hull, p_l); } /* ================== PM_PointContents ================== */ int PM_PointContents (vec3_t p) { hull_t *hull; int num; int pc; hull = &pmove.physents[0].model->hulls[0]; pc = hull->funcs.HullPointContents(hull, p); //we need this for e2m2 - waterjumping on to plats wouldn't work otherwise. for (num = 1; num < pmove.numphysent; num++) { if (pmove.physents[num].model) pc |= PM_TransformedHullPointContents(&pmove.physents[num].model->hulls[0], p, pmove.physents[num].origin, pmove.physents[num].angles); } return pc; } /* =============================================================================== LINE TESTING IN HULLS =============================================================================== */ // 1/32 epsilon to keep floating point happy #define DIST_EPSILON (0.03125) vec3_t trace_extents; qboolean PM_TransformedHullCheck (hull_t *hull, vec3_t start, vec3_t end, trace_t *trace, vec3_t origin, vec3_t angles) { vec3_t start_l, end_l; vec3_t a; vec3_t forward, right, up; vec3_t temp; qboolean rotated; qboolean result; // subtract origin offset VectorSubtract (start, origin, start_l); VectorSubtract (end, origin, end_l); if (!player_mins[2]) //hmm.. hull bigger than player? Or is this hexen2? { start_l[2] -= hull->clip_mins[2]+0.1; end_l[2] -= hull->clip_mins[2]+0.1; } // rotate start and end into the models frame of reference if (hull != &box_hull && (angles[0] || angles[1] || angles[2]) ) 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 result = hull->funcs.RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, trace); if (rotated && trace->fraction != 1.0) { // FIXME: figure out how to do this with existing angles // VectorNegate (angles, a); a[0] = angles[0]; a[1] = angles[1]; a[2] = angles[2]; 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]); // if (!player_mins[2]) //hmm.. hull bigger than player? Or is this hexen2? // trace->endpos[2] += hull->clip_mins[2]+0.1; return result; } /* ================ PM_TestPlayerPosition Returns false if the given player position is not valid (in solid) ================ */ int CM_TransformedPointContents (vec3_t p, int headnode, vec3_t origin, vec3_t angles); qboolean PM_TestPlayerPosition (vec3_t pos) { int i; physent_t *pe; vec3_t mins, maxs; hull_t *hull; for (i=0 ; i< pmove.numphysent ; i++) { pe = &pmove.physents[i]; // get the clipping hull if (pe->model) { #ifdef Q2BSPS if (pe->model->fromgame == fg_quake2 || pe->model->fromgame == fg_quake3) { trace_t trace = CM_TransformedBoxTrace(pos, pos, player_mins, player_maxs, pe->model->hulls[0].firstclipnode, MASK_PLAYERSOLID, pe->origin, pe->angles); if (trace.fraction == 0) return false; continue; } #endif hull = &pe->model->hulls[pmove.hullnum]; if (!hull->available || !hull->planes || !hull->clipnodes) hull = &pe->model->hulls[1]; } else { VectorSubtract (pe->mins, player_maxs, mins); VectorSubtract (pe->maxs, player_mins, maxs); hull = PM_HullForBox (mins, maxs); } if (PM_TransformedHullPointContents (hull, pos, pe->origin, pe->angles) & FTECONTENTS_SOLID) return false; } return true; } /* ================ PM_PlayerTrace ================ */ trace_t PM_PlayerTrace (vec3_t start, vec3_t end) { trace_t trace, total; hull_t *hull; int i; physent_t *pe; vec3_t mins, maxs; // fill in a default trace memset (&total, 0, sizeof(trace_t)); total.fraction = 1; total.entnum = -1; VectorCopy (end, total.endpos); for (i=0 ; i< pmove.numphysent ; i++) { pe = &pmove.physents[i]; // get the clipping hull if (pe->model) { hull = &pmove.physents[i].model->hulls[pmove.hullnum]; if (!hull->available || !hull->clipnodes || !hull->planes) hull = &pmove.physents[i].model->hulls[1]; //fallback } else { VectorSubtract (pe->mins, player_maxs, mins); VectorSubtract (pe->maxs, player_mins, maxs); hull = PM_HullForBox (mins, maxs); } // fill in a default trace memset (&trace, 0, sizeof(trace_t)); trace.fraction = 1; trace.allsolid = true; // trace.startsolid = true; VectorCopy (end, trace.endpos); // trace a line through the apropriate clipping hull #ifdef Q2BSPS if (pmove.physents[i].model && (pmove.physents[i].model->fromgame == fg_quake2 || pmove.physents[i].model->fromgame == fg_quake3)) { trace_t tr; tr = CM_TransformedBoxTrace (start, end, player_mins, player_maxs, pmove.physents[i].model->hulls[0].firstclipnode, MASK_PLAYERSOLID, pe->origin, pe->angles); trace.startsolid = tr.startsolid; trace.allsolid = tr.allsolid; trace.fraction = tr.fraction; memcpy(&trace.plane, &tr.plane, sizeof(tr.plane)); VectorCopy(tr.endpos, trace.endpos); } else #endif PM_TransformedHullCheck (hull, start, end, &trace, pe->origin, pe->angles); if (trace.allsolid) trace.startsolid = true; if (trace.startsolid) trace.fraction = 0; // did we clip the move? if (trace.fraction < total.fraction) { // fix trace up by the offset total = trace; total.entnum = i; } } return total; }