mirror of
https://github.com/unknownworlds/NS.git
synced 2024-11-23 04:52:16 +00:00
769 lines
24 KiB
C++
769 lines
24 KiB
C++
|
|
||
|
//new collision replacemnet
|
||
|
|
||
|
#include <vector>
|
||
|
#include <memory>
|
||
|
|
||
|
#include "common/mathlib.h"
|
||
|
#include "common/const.h"
|
||
|
#include "common/com_model.h"
|
||
|
#include "pm_defs.h"
|
||
|
#include "common/vector_util.h"
|
||
|
#include "mod/AvHHulls.h"
|
||
|
#include "types.h"
|
||
|
#include "util/MathUtil.h"
|
||
|
|
||
|
#include "CollisionChecker.h"
|
||
|
|
||
|
using std::auto_ptr;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// PlaneTest classes - used by CollisionChecker for testing against hulls
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
class CollisionTest
|
||
|
{
|
||
|
public:
|
||
|
virtual ~CollisionTest(void) {}
|
||
|
virtual int GetPlanarIntersectionType(const mplane_t* plane) const = 0;
|
||
|
virtual bool GetAABBIntersection(const nspoint_t& mins, const nspoint_t& maxs) const = 0;
|
||
|
virtual auto_ptr<CollisionTest> RotateAndShift(const nspoint_t& forward, const nspoint_t& up, const nspoint_t& right, const nspoint_t& origin) const = 0;
|
||
|
virtual auto_ptr<CollisionTest> Shift(const nspoint_t& origin) const = 0;
|
||
|
};
|
||
|
|
||
|
class PointCollisionTest : public CollisionTest
|
||
|
{
|
||
|
public:
|
||
|
PointCollisionTest(const nspoint_t& point)
|
||
|
{ SetPoint(point); }
|
||
|
|
||
|
void SetPoint(const nspoint_t& point)
|
||
|
{ VectorCopy(point,point_); }
|
||
|
|
||
|
int GetPlanarIntersectionType(const mplane_t* plane) const
|
||
|
{
|
||
|
float d;
|
||
|
if(plane->type < 3) // first three plane types are axial by index
|
||
|
{
|
||
|
d = point_[plane->type]*plane->normal[plane->type] - plane->dist;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d = DotProduct(plane->normal,point_) - plane->dist;
|
||
|
}
|
||
|
return ((d < 0) ? CollisionChecker::INTERSECTION_RELATION_BACK : CollisionChecker::INTERSECTION_RELATION_FRONT);
|
||
|
}
|
||
|
|
||
|
bool GetAABBIntersection(const nspoint_t& mins, const nspoint_t& maxs) const
|
||
|
{
|
||
|
return (
|
||
|
(point_[0] >= mins[0]) &&
|
||
|
(point_[0] <= maxs[0]) &&
|
||
|
(point_[1] >= mins[1]) &&
|
||
|
(point_[1] <= maxs[1]) &&
|
||
|
(point_[2] >= mins[2]) &&
|
||
|
(point_[2] <= maxs[1])
|
||
|
);
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> RotateAndShift(const nspoint_t& forward, const nspoint_t& right, const nspoint_t& up, const nspoint_t& origin) const
|
||
|
{
|
||
|
return Shift(origin);
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> Shift(const nspoint_t& origin) const
|
||
|
{
|
||
|
nspoint_t new_origin;
|
||
|
VectorAdd(origin,point_,new_origin);
|
||
|
return auto_ptr<CollisionTest>(new PointCollisionTest(new_origin));
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
nspoint_t point_;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
class CylinderCollisionTest : public CollisionTest
|
||
|
{
|
||
|
public:
|
||
|
CylinderCollisionTest(const nspoint_t& up, const nspoint_t& base, const float radius, const float height) : point_test_(base)
|
||
|
{
|
||
|
VectorCopy(up,up_);
|
||
|
VectorNormalize(up_);
|
||
|
VectorCopy(base,base_);
|
||
|
radius_ = radius;
|
||
|
height_ = height;
|
||
|
}
|
||
|
|
||
|
int GetPlanarIntersectionType(const mplane_t* plane) const
|
||
|
{
|
||
|
nspoint_t in_plane,out_of_plane,point_to_check;
|
||
|
CrossProduct(up_,plane->normal,in_plane);
|
||
|
CrossProduct(up_,in_plane,out_of_plane); //perp to up and pointing directly out of plane
|
||
|
VectorNormalize(out_of_plane);
|
||
|
|
||
|
VectorMA(base_,radius_,out_of_plane,point_to_check);
|
||
|
point_test_.SetPoint(point_to_check);
|
||
|
int intersect = point_test_.GetPlanarIntersectionType(plane);
|
||
|
|
||
|
VectorMA(point_to_check,height_,up_,point_to_check);
|
||
|
point_test_.SetPoint(point_to_check);
|
||
|
intersect |= point_test_.GetPlanarIntersectionType(plane);
|
||
|
|
||
|
if(intersect != CollisionChecker::INTERSECTION_RELATION_BOTH)
|
||
|
{
|
||
|
VectorMA(base_,-radius_,out_of_plane,point_to_check);
|
||
|
point_test_.SetPoint(point_to_check);
|
||
|
intersect |= point_test_.GetPlanarIntersectionType(plane);
|
||
|
}
|
||
|
|
||
|
if(intersect != CollisionChecker::INTERSECTION_RELATION_BOTH)
|
||
|
{
|
||
|
VectorMA(point_to_check,height_,up_,point_to_check);
|
||
|
point_test_.SetPoint(point_to_check);
|
||
|
intersect |= point_test_.GetPlanarIntersectionType(plane);
|
||
|
}
|
||
|
|
||
|
return intersect;
|
||
|
}
|
||
|
|
||
|
bool GetAABBIntersection(const nspoint_t& mins, const nspoint_t& maxs) const
|
||
|
{
|
||
|
//special case of unrotated cylinder
|
||
|
if(up_[2] == 1.0f)
|
||
|
{
|
||
|
if( maxs[2] < base_[2] ||
|
||
|
mins[2] > base_[2] + height_ ||
|
||
|
maxs[0] < base_[0] - radius_ ||
|
||
|
mins[0] > base_[0] + radius_ ||
|
||
|
maxs[1] < base_[1] - radius_ ||
|
||
|
maxs[1] > base_[1] + radius_
|
||
|
) { return false; }
|
||
|
//TODO: tighten definition to use overlap of circle instead of box with circle inscribed
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//TODO: handle general case by turning bbox into hull and testing against that hull
|
||
|
ASSERT(0 && "Need to handle case of rotated cylinder & AABB collision");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> RotateAndShift(const nspoint_t& forward, const nspoint_t& right, const nspoint_t& up, const nspoint_t& origin) const
|
||
|
{
|
||
|
nspoint_t new_up, new_base;
|
||
|
TransformVector(up_,forward,right,up,new_up);
|
||
|
VectorAdd(base_,origin,new_base);
|
||
|
return auto_ptr<CollisionTest>(new CylinderCollisionTest(new_up,new_base,radius_,height_));
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> Shift(const nspoint_t& origin) const
|
||
|
{
|
||
|
nspoint_t new_base;
|
||
|
VectorAdd(base_,origin,new_base);
|
||
|
return auto_ptr<CollisionTest>(new CylinderCollisionTest(up_,new_base,radius_,height_));
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
mutable PointCollisionTest point_test_;
|
||
|
nspoint_t up_;
|
||
|
nspoint_t base_;
|
||
|
float radius_;
|
||
|
float height_;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
//TODO: determine if there's a faster way than scanning all 8 corners
|
||
|
class OBBCollisionTest : public CollisionTest
|
||
|
{
|
||
|
public:
|
||
|
OBBCollisionTest(const OBBox& box) : point_test_(box.mOrigin)
|
||
|
{
|
||
|
box_ = box;
|
||
|
nspoint_t offset;
|
||
|
//calculate eight corners of the box given input angles:
|
||
|
for(int index = 0; index < 8; ++index)
|
||
|
{
|
||
|
offset[0] = box.mExtents[0] * box.mAxis[0][0];
|
||
|
offset[0] += box.mExtents[1] * box.mAxis[1][0];
|
||
|
offset[0] += box.mExtents[2] * box.mAxis[2][0];
|
||
|
if(index % 2) //every other item
|
||
|
{ offset[0] *= -1; }
|
||
|
|
||
|
offset[1] = box.mExtents[0] * box.mAxis[0][1];
|
||
|
offset[1] += box.mExtents[1] * box.mAxis[1][1];
|
||
|
offset[1] += box.mExtents[2] * box.mAxis[2][1];
|
||
|
if((index / 2) % 2) //every other pair of items
|
||
|
{ offset[1] *= -1; }
|
||
|
|
||
|
offset[2] = box.mExtents[0] * box.mAxis[0][2];
|
||
|
offset[2] += box.mExtents[1] * box.mAxis[1][2];
|
||
|
offset[2] += box.mExtents[2] * box.mAxis[2][2];
|
||
|
if(index < 4) //4 false, then 4 true
|
||
|
{ offset[2] *= -1; }
|
||
|
|
||
|
VectorAdd(box.mOrigin,offset,corners[index]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int GetPlanarIntersectionType(const mplane_t* plane) const
|
||
|
{
|
||
|
int intersection_type = CollisionChecker::INTERSECTION_RELATION_NONE;
|
||
|
for(int index = 0; index < 8; ++index)
|
||
|
{
|
||
|
point_test_.SetPoint(corners[index]);
|
||
|
intersection_type |= point_test_.GetPlanarIntersectionType(plane);
|
||
|
if(intersection_type == CollisionChecker::INTERSECTION_RELATION_BOTH)
|
||
|
{ break; }
|
||
|
}
|
||
|
return intersection_type;
|
||
|
}
|
||
|
|
||
|
bool GetAABBIntersection(const nspoint_t& mins, const nspoint_t& maxs) const
|
||
|
{
|
||
|
bool intersection = false;
|
||
|
for(int index = 0; index < 8; ++index)
|
||
|
{
|
||
|
point_test_.SetPoint(corners[index]);
|
||
|
intersection = point_test_.GetAABBIntersection(mins,maxs);
|
||
|
if(intersection)
|
||
|
{ break; }
|
||
|
}
|
||
|
return intersection;
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> RotateAndShift(const nspoint_t& forward, const nspoint_t& right, const nspoint_t& up, const nspoint_t& origin) const
|
||
|
{
|
||
|
//make this an OBB by rotation around the ent angles - not a cheap operation.
|
||
|
OBBox box = box_;
|
||
|
|
||
|
VectorAdd(box_.mOrigin,origin,box.mOrigin);
|
||
|
TransformVector(box_.mAxis[0],forward,right,up,box.mAxis[0]);
|
||
|
TransformVector(box_.mAxis[1],forward,right,up,box.mAxis[1]);
|
||
|
TransformVector(box_.mAxis[2],forward,right,up,box.mAxis[2]);
|
||
|
|
||
|
//rotate axes!
|
||
|
return auto_ptr<CollisionTest>(new OBBCollisionTest(box));
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> Shift(const nspoint_t& origin) const
|
||
|
{
|
||
|
//make this an OBB by rotation around the ent angles - not a cheap operation.
|
||
|
OBBox box = box_;
|
||
|
VectorAdd(box_.mOrigin,origin,box.mOrigin);
|
||
|
return auto_ptr<CollisionTest>(new OBBCollisionTest(box));
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
OBBox box_;
|
||
|
nspoint_t corners[8];
|
||
|
mutable PointCollisionTest point_test_;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
class AABBCollisionTest : public CollisionTest
|
||
|
{
|
||
|
public:
|
||
|
AABBCollisionTest(const nspoint_t& mins, const nspoint_t& maxs) : point_test_(mins)
|
||
|
{
|
||
|
VectorCopy(maxs,maxs_);
|
||
|
VectorCopy(mins,mins_);
|
||
|
}
|
||
|
|
||
|
int GetPlanarIntersectionType(const mplane_t* plane) const
|
||
|
{
|
||
|
nspoint_t orientedMins = {
|
||
|
plane->normal[0] < 0 ? maxs_[0] : mins_[0],
|
||
|
plane->normal[1] < 0 ? maxs_[1] : mins_[1],
|
||
|
plane->normal[2] < 0 ? maxs_[2] : mins_[2]
|
||
|
};
|
||
|
point_test_.SetPoint(orientedMins);
|
||
|
int intersect_type = point_test_.GetPlanarIntersectionType(plane);
|
||
|
|
||
|
nspoint_t orientedMaxes = {
|
||
|
plane->normal[0] < 0 ? mins_[0] : maxs_[0],
|
||
|
plane->normal[1] < 0 ? mins_[1] : maxs_[1],
|
||
|
plane->normal[2] < 0 ? mins_[2] : maxs_[2]
|
||
|
};
|
||
|
point_test_.SetPoint(orientedMaxes);
|
||
|
intersect_type |= point_test_.GetPlanarIntersectionType(plane);
|
||
|
|
||
|
return intersect_type;
|
||
|
}
|
||
|
|
||
|
bool GetAABBIntersection(const nspoint_t& mins, const nspoint_t& maxs) const
|
||
|
{
|
||
|
return (
|
||
|
(maxs_[0] >= mins[0]) &&
|
||
|
(mins_[0] <= maxs[0]) &&
|
||
|
(maxs_[1] >= mins[1]) &&
|
||
|
(mins_[1] <= maxs[1]) &&
|
||
|
(maxs_[2] >= mins[2]) &&
|
||
|
(mins_[2] <= maxs[2])
|
||
|
);
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> RotateAndShift(const nspoint_t& forward, const nspoint_t& right, const nspoint_t& up, const nspoint_t& origin) const
|
||
|
{
|
||
|
//make this an OBB by rotation around the ent angles - not a cheap operation.
|
||
|
OBBox box;
|
||
|
|
||
|
//set up origin = ((mins + maxs) / 2) - ent origin
|
||
|
VectorCopy(mins_,box.mOrigin);
|
||
|
VectorAdd(maxs_,box.mOrigin,box.mOrigin);
|
||
|
VectorScale(box.mOrigin,0.5f,box.mOrigin);
|
||
|
VectorSubtract(box.mOrigin,origin,box.mOrigin);
|
||
|
|
||
|
//set up extents = ((maxs - mins) / 2)
|
||
|
VectorCopy(maxs_,box.mExtents);
|
||
|
VectorSubtract(box.mExtents,mins_,box.mExtents);
|
||
|
VectorScale(box.mExtents,0.5f,box.mExtents);
|
||
|
|
||
|
//set up axis = axis defined by ent angles
|
||
|
nspoint_t axis[3] = {{1,0,0},{0,1,0},{0,0,1}};
|
||
|
VectorCopy(forward,box.mAxis[0]);
|
||
|
VectorCopy(up,box.mAxis[1]);
|
||
|
VectorCopy(right,box.mAxis[2]);
|
||
|
|
||
|
return auto_ptr<CollisionTest>(new OBBCollisionTest(box));
|
||
|
}
|
||
|
|
||
|
auto_ptr<CollisionTest> Shift(const nspoint_t& origin) const
|
||
|
{
|
||
|
nspoint_t new_mins, new_maxs;
|
||
|
VectorAdd(origin,mins_,new_mins);
|
||
|
VectorAdd(origin,maxs_,new_maxs);
|
||
|
return auto_ptr<CollisionTest>(new AABBCollisionTest(new_mins,new_maxs));
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
nspoint_t mins_, maxs_;
|
||
|
mutable PointCollisionTest point_test_;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CollisionChecker class - making a fresh start on collision checking code
|
||
|
// that runs properly on both the server and client; requires that pmove is
|
||
|
// filled to function -- won't function fully for initial frames
|
||
|
//
|
||
|
// TODO: write quick functions for when pmove isn't available
|
||
|
// KGP - 4/25/04
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
const int CollisionChecker::NO_ENTITY = -1;
|
||
|
const int CollisionChecker::WORLD_ENTITY = 0;
|
||
|
|
||
|
const bool CollisionChecker::IGNORE_PLAYERS_FALSE = false;
|
||
|
const bool CollisionChecker::IGNORE_PLAYERS_TRUE = true;
|
||
|
|
||
|
const int CollisionChecker::HULL_TYPE_BBOX = 1;
|
||
|
const int CollisionChecker::HULL_TYPE_BSP = 2;
|
||
|
const int CollisionChecker::HULL_TYPE_ALL = 3;
|
||
|
|
||
|
const int CollisionChecker::INTERSECTION_RELATION_NONE = 0;
|
||
|
const int CollisionChecker::INTERSECTION_RELATION_FRONT = 1;
|
||
|
const int CollisionChecker::INTERSECTION_RELATION_BACK = 2;
|
||
|
const int CollisionChecker::INTERSECTION_RELATION_BOTH = 3;
|
||
|
|
||
|
CollisionChecker::CollisionChecker(const playermove_t* pmove) : pmove_(pmove)
|
||
|
{
|
||
|
SetHullNumber(0);
|
||
|
SetHullTypeMask(HULL_TYPE_ALL);
|
||
|
SetIgnorePlayers(false);
|
||
|
SetIgnoreEntityClass(IGNORE_NONE);
|
||
|
ClearEntityToIgnore();
|
||
|
}
|
||
|
|
||
|
CollisionChecker::CollisionChecker(const playermove_t* pmove, int ns_hull_number, int hull_type_mask, bool ignore_players, int ignore_entity_class, int entity_index_to_ignore) : pmove_(pmove)
|
||
|
{
|
||
|
SetHullNumber(ns_hull_number);
|
||
|
SetHullTypeMask(hull_type_mask);
|
||
|
SetIgnorePlayers(ignore_players);
|
||
|
SetIgnoreEntityClass(ignore_entity_class);
|
||
|
SetEntityToIgnore(entity_index_to_ignore);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
// Configuration
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
void CollisionChecker::SetHullNumber(int ns_hull_number)
|
||
|
{
|
||
|
switch(ns_hull_number)
|
||
|
{
|
||
|
case kHLPointHullIndex:
|
||
|
valve_hull_number_ = 0;
|
||
|
break;
|
||
|
case kHLCrouchHullIndex:
|
||
|
valve_hull_number_ = 1;
|
||
|
break;
|
||
|
case kHLStandHullIndex:
|
||
|
valve_hull_number_ = 2;
|
||
|
break;
|
||
|
case kNSHugeHullIndex:
|
||
|
valve_hull_number_ = 3;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(0 && "unknown hull index passed to CollisionChecker::SetHullNumber");
|
||
|
valve_hull_number_ = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
void CollisionChecker::SetHullTypeMask(int hull_type_mask)
|
||
|
{
|
||
|
hull_type_mask_ = hull_type_mask;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
void CollisionChecker::SetEntityToIgnore(int entity_index_to_ignore)
|
||
|
{
|
||
|
ASSERT(entity_index_to_ignore > 0 || entity_index_to_ignore == NO_ENTITY);
|
||
|
entity_index_to_ignore_ = entity_index_to_ignore;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
void CollisionChecker::ClearEntityToIgnore(void)
|
||
|
{
|
||
|
entity_index_to_ignore_ = NO_ENTITY;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
void CollisionChecker::SetIgnorePlayers(bool ignore_players)
|
||
|
{
|
||
|
ignore_players_ = ignore_players;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
void CollisionChecker::SetIgnoreEntityClass(int ignore_entity_class)
|
||
|
{
|
||
|
ignore_entity_class_ = ignore_entity_class;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
// Public point routines
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetContentsAtPoint(const nspoint_t& point) const
|
||
|
{
|
||
|
return GetContents(&PointCollisionTest(point));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetWorldContentsAtPoint(const nspoint_t& point) const
|
||
|
{
|
||
|
return GetSingleEntityContentsAtPoint(point, WORLD_ENTITY);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetAllEntityContentsAtPoint(const nspoint_t& point) const
|
||
|
{
|
||
|
return GetAllEntityContents(&PointCollisionTest(point));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetSingleEntityContentsAtPoint(const nspoint_t& point, int entity_index) const
|
||
|
{
|
||
|
return GetSingleEntityContents(&PointCollisionTest(point),entity_index);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
// Public Cylinder routines
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
const static nspoint_t CYLINDER_UP_DEFAULT = {0,0,1.0f};
|
||
|
|
||
|
int CollisionChecker::GetContentsInCylinder(const nspoint_t& base, float radius, float height) const
|
||
|
{
|
||
|
return GetContents(&CylinderCollisionTest(CYLINDER_UP_DEFAULT,base,radius,height));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetWorldContentsInCylinder(const nspoint_t& base, float radius, float height) const
|
||
|
{
|
||
|
return GetSingleEntityContents(&CylinderCollisionTest(CYLINDER_UP_DEFAULT,base,radius,height),WORLD_ENTITY);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetAllEntityContentsInCylinder(const nspoint_t& base, float radius, float height) const
|
||
|
{
|
||
|
return GetAllEntityContents(&CylinderCollisionTest(CYLINDER_UP_DEFAULT,base,radius,height));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetSingleEntityContentsInCylinder(const nspoint_t& base, float radius, float height, int entity_index) const
|
||
|
{
|
||
|
return GetSingleEntityContents(&CylinderCollisionTest(CYLINDER_UP_DEFAULT,base,radius,height),entity_index);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
// Public AABB routines
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetContentsInAABB(const nspoint_t& mins, const nspoint_t& maxs) const
|
||
|
{
|
||
|
return GetContents(&AABBCollisionTest(mins,maxs));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetWorldContentsInAABB(const nspoint_t& mins, const nspoint_t& maxs) const
|
||
|
{
|
||
|
return GetSingleEntityContents(&AABBCollisionTest(mins,maxs),WORLD_ENTITY);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetAllEntityContentsInAABB(const nspoint_t& mins, const nspoint_t& maxs) const
|
||
|
{
|
||
|
return GetAllEntityContents(&AABBCollisionTest(mins,maxs));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetSingleEntityContentsInAABB(const nspoint_t& mins, const nspoint_t& maxs, int entity_index) const
|
||
|
{
|
||
|
return GetSingleEntityContents(&AABBCollisionTest(mins,maxs),entity_index);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
// Private utility routines
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::CombineContents(const int old_contents, const int new_contents) const
|
||
|
{
|
||
|
if(new_contents == CONTENTS_EMPTY)
|
||
|
{ return old_contents; }
|
||
|
if(old_contents == CONTENT_EMPTY)
|
||
|
{ return new_contents; }
|
||
|
return max(old_contents,new_contents);
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
// Private content check routines
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetContents(const CollisionTest* test) const
|
||
|
{
|
||
|
int total_contents = GetSingleEntityContents(test,WORLD_ENTITY);
|
||
|
if(total_contents != CONTENTS_SOLID)
|
||
|
{
|
||
|
total_contents = CombineContents(total_contents,GetAllEntityContents(test));
|
||
|
}
|
||
|
return total_contents;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetAllEntityContents(const CollisionTest* test) const
|
||
|
{
|
||
|
int total_contents = CONTENTS_EMPTY;
|
||
|
|
||
|
UpdateNumEntities();
|
||
|
for(int counter = WORLD_ENTITY+1; counter < entity_count_; ++counter)
|
||
|
{
|
||
|
total_contents = CombineContents(total_contents,GetSingleEntityContents(test,counter));
|
||
|
if(total_contents == CONTENTS_SOLID)
|
||
|
{ break; }
|
||
|
}
|
||
|
|
||
|
return total_contents;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
int CollisionChecker::GetSingleEntityContents(const CollisionTest* test, int entity_index) const
|
||
|
{
|
||
|
int total_contents = CONTENTS_EMPTY;
|
||
|
const physent_t* ent = GetPhysentForIndex(entity_index);
|
||
|
if(ent && ent->info != entity_index_to_ignore_ && (!ignore_players_ || ent->info == WORLD_ENTITY || ent->info > 32))
|
||
|
{
|
||
|
if(ent->solid == SOLID_BBOX)
|
||
|
{
|
||
|
nspoint_t bbox_mins, bbox_maxs;
|
||
|
VectorSubtract(ent->mins,ent->origin,bbox_mins);
|
||
|
VectorSubtract(ent->maxs,ent->origin,bbox_maxs);
|
||
|
|
||
|
if(valve_hull_number_) //adjust for input hull
|
||
|
{
|
||
|
const hull_t* hull = &(&pmove_->physents[WORLD_ENTITY])->model->hulls[valve_hull_number_];
|
||
|
VectorAdd(bbox_mins,hull->clip_mins,bbox_mins);
|
||
|
VectorAdd(bbox_mins,hull->clip_maxs,bbox_maxs);
|
||
|
}
|
||
|
|
||
|
if(test->GetAABBIntersection(bbox_mins,bbox_maxs))
|
||
|
{
|
||
|
total_contents = CONTENTS_SOLID;
|
||
|
}
|
||
|
}
|
||
|
else if(ent->model && ent->model->type == mod_brush)
|
||
|
{
|
||
|
const hull_t* hull = &ent->model->hulls[valve_hull_number_];
|
||
|
|
||
|
if(ent->angles[0] || ent->angles[1] || ent->angles[2])
|
||
|
{
|
||
|
float axes[3][3];
|
||
|
AngleVectors(ent->angles,axes[0],axes[1],axes[2]);
|
||
|
auto_ptr<CollisionTest> rotated_test = test->RotateAndShift(axes[0],axes[1],axes[2],ent->origin);
|
||
|
total_contents = GetHullContent(hull,hull->firstclipnode,rotated_test.get());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
auto_ptr<CollisionTest> shifted_test = test->Shift(ent->origin);
|
||
|
total_contents = GetHullContent(hull,hull->firstclipnode,shifted_test.get());
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int a = 0; //breakpoint to be used as catch-all
|
||
|
}
|
||
|
}
|
||
|
return total_contents;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------
|
||
|
|
||
|
//DFS search for solid hull leaf with passed in intersection test and short circuit on solid leaf found
|
||
|
int CollisionChecker::GetHullContent(const hull_t* hull, int current_node, const CollisionTest* test) const
|
||
|
{
|
||
|
ASSERT(current_node >= hull->firstclipnode);
|
||
|
ASSERT(current_node <= hull->lastclipnode);
|
||
|
int total_contents = CONTENTS_EMPTY;
|
||
|
|
||
|
if(current_node < 0)
|
||
|
{ total_contents = current_node; }
|
||
|
else
|
||
|
{
|
||
|
dclipnode_t *node = &hull->clipnodes[current_node];
|
||
|
mplane_t *plane;
|
||
|
int intersection;
|
||
|
while(true)
|
||
|
{
|
||
|
ASSERT(total_contents < 0); // 0 + is a node number and means we've had a bad assignment
|
||
|
|
||
|
//short circuit on solid found
|
||
|
if(total_contents == CONTENTS_SOLID)
|
||
|
{
|
||
|
this->intersected_nodes_.clear();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
plane = &hull->planes[node->planenum];
|
||
|
|
||
|
intersection = test->GetPlanarIntersectionType(&hull->planes[node->planenum]);
|
||
|
|
||
|
switch(intersection)
|
||
|
{
|
||
|
case INTERSECTION_RELATION_BOTH:
|
||
|
if(node->children[0] > CONTENTS_EMPTY) //non-leaf
|
||
|
{
|
||
|
if(node->children[1] > CONTENTS_EMPTY) //non-leaf
|
||
|
{
|
||
|
this->intersected_nodes_.push_back(node->children[0]); //store one path for later
|
||
|
node = &hull->clipnodes[node->children[1]]; //follow other path
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
total_contents = CombineContents(total_contents,node->children[1]);
|
||
|
node = &hull->clipnodes[node->children[0]];
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
total_contents = CombineContents(total_contents,node->children[0]);
|
||
|
|
||
|
if(node->children[1] > CONTENTS_EMPTY) //non-leaf
|
||
|
{
|
||
|
node = &hull->clipnodes[node->children[1]];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
total_contents = CombineContents(total_contents,node->children[1]);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case INTERSECTION_RELATION_FRONT:
|
||
|
if(node->children[0] > CONTENTS_EMPTY)
|
||
|
{
|
||
|
node = &hull->clipnodes[node->children[0]];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
total_contents = CombineContents(total_contents,node->children[0]);
|
||
|
break;
|
||
|
|
||
|
case INTERSECTION_RELATION_BACK:
|
||
|
if(node->children[1] > CONTENTS_EMPTY)
|
||
|
{
|
||
|
node = &hull->clipnodes[node->children[1]];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
total_contents = CombineContents(total_contents,node->children[1]);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//check for other subtrees to process
|
||
|
if(this->intersected_nodes_.empty())
|
||
|
{ break; }
|
||
|
|
||
|
node = &hull->clipnodes[this->intersected_nodes_.back()];
|
||
|
this->intersected_nodes_.pop_back();
|
||
|
}
|
||
|
}
|
||
|
return total_contents;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Client/server specific code :( ... client version, server version in own file
|
||
|
//------------------------------------------------------------------------------
|
||
|
#ifdef AVH_CLIENT
|
||
|
const int CollisionChecker::IGNORE_NONE = 0;
|
||
|
const int CollisionChecker::IGNORE_INVISIBLE = 1;
|
||
|
const int CollisionChecker::IGNORE_INTANGIBLE = 2;
|
||
|
|
||
|
void CollisionChecker::UpdateNumEntities() const
|
||
|
{
|
||
|
switch(ignore_entity_class_)
|
||
|
{
|
||
|
case IGNORE_NONE:
|
||
|
case IGNORE_INVISIBLE:
|
||
|
entity_count_ = pmove_ ? pmove_->numvisent : 0;
|
||
|
break;
|
||
|
case IGNORE_INTANGIBLE:
|
||
|
entity_count_ = pmove_ ? pmove_->numphysent : 0;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(0 && "Unknown ignore entity class");
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const physent_t* CollisionChecker::GetPhysentForIndex(int entity_index) const
|
||
|
{
|
||
|
switch(ignore_entity_class_)
|
||
|
{
|
||
|
case IGNORE_NONE:
|
||
|
case IGNORE_INVISIBLE:
|
||
|
return pmove_ ? &pmove_->visents[entity_index] : (physent_t*)NULL;
|
||
|
case IGNORE_INTANGIBLE:
|
||
|
return pmove_ ? &pmove_->physents[entity_index] : (physent_t*)NULL;
|
||
|
}
|
||
|
ASSERT(0 && "Unknown ignore entity class");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#endif
|