//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef COLLISIONPROPERTY_H #define COLLISIONPROPERTY_H #ifdef _WIN32 #pragma once #endif #include "engine/ICollideable.h" #include "ispatialpartition.h" #include "mathlib/vector.h" #include "networkvar.h" //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- class CBaseEntity; class IHandleEntity; class QAngle; class Vector; struct Ray_t; class IPhysicsObject; //----------------------------------------------------------------------------- // Force spatial partition updates (to avoid threading problems caused by lazy // update) //----------------------------------------------------------------------------- void UpdateDirtySpatialPartitionEntities(); //----------------------------------------------------------------------------- // Specifies how to compute the surrounding box //----------------------------------------------------------------------------- enum SurroundingBoundsType_t { USE_OBB_COLLISION_BOUNDS = 0, USE_BEST_COLLISION_BOUNDS, // Always use the best bounds (most expensive) USE_HITBOXES, USE_SPECIFIED_BOUNDS, USE_GAME_CODE, USE_ROTATION_EXPANDED_BOUNDS, USE_COLLISION_BOUNDS_NEVER_VPHYSICS, SURROUNDING_TYPE_BIT_COUNT = 3 }; //----------------------------------------------------------------------------- // Encapsulates collision representation for an entity //----------------------------------------------------------------------------- class CCollisionProperty : public ICollideable { DECLARE_CLASS_NOBASE(CCollisionProperty); DECLARE_EMBEDDED_NETWORKVAR(); DECLARE_PREDICTABLE(); #ifdef GAME_DLL DECLARE_DATADESC(); #endif public: CCollisionProperty(); ~CCollisionProperty(); void Init(CBaseEntity *pEntity); // Methods of ICollideable virtual IHandleEntity *GetEntityHandle(); virtual const Vector &OBBMinsPreScaled() const { return m_vecMinsPreScaled.Get(); } virtual const Vector &OBBMaxsPreScaled() const { return m_vecMaxsPreScaled.Get(); } virtual const Vector &OBBMins() const { return m_vecMins.Get(); } virtual const Vector &OBBMaxs() const { return m_vecMaxs.Get(); } virtual void WorldSpaceTriggerBounds(Vector *pVecWorldMins, Vector *pVecWorldMaxs) const; virtual bool TestCollision(const Ray_t &ray, unsigned int fContentsMask, trace_t &tr); virtual bool TestHitboxes(const Ray_t &ray, unsigned int fContentsMask, trace_t &tr); virtual int GetCollisionModelIndex(); virtual const model_t *GetCollisionModel(); virtual const Vector &GetCollisionOrigin() const; virtual const QAngle &GetCollisionAngles() const; virtual const matrix3x4_t &CollisionToWorldTransform() const; virtual SolidType_t GetSolid() const; virtual int GetSolidFlags() const; virtual IClientUnknown *GetIClientUnknown(); virtual int GetCollisionGroup() const; virtual void WorldSpaceSurroundingBounds(Vector *pVecMins, Vector *pVecMaxs); virtual bool ShouldTouchTrigger(int triggerSolidFlags) const; virtual const matrix3x4_t *GetRootParentToWorldTransform() const; public: // Spatial partition management void CreatePartitionHandle(); void DestroyPartitionHandle(); unsigned short GetPartitionHandle() const; // Marks the spatial partition dirty void MarkPartitionHandleDirty(); // Sets the collision bounds + the size (OBB) void SetCollisionBounds(const Vector &mins, const Vector &maxs); // Rebuilds the scaled bounds from the pre-scaled bounds after a model's // scale has changed void RefreshScaledCollisionBounds(void); // Sets special trigger bounds. The bloat amount indicates how much bigger // the trigger bounds should be beyond the bounds set in SetCollisionBounds // This method will also set the FSOLID flag FSOLID_USE_TRIGGER_BOUNDS void UseTriggerBounds(bool bEnable, float flBloat = 0.0f); // Sets the method by which the surrounding collision bounds is set // You must pass in values for mins + maxs if you select the // USE_SPECIFIED_BOUNDS type. void SetSurroundingBoundsType(SurroundingBoundsType_t type, const Vector *pMins = NULL, const Vector *pMaxs = NULL); // Sets the solid type (which type of collision representation) void SetSolid(SolidType_t val); // Methods related to size. The OBB here is measured in CollisionSpace // (specified by GetCollisionToWorld) const Vector &OBBSize() const; // Returns a radius (or the square of the radius) of a sphere // *centered at the world space center* bounding the collision // representation of the entity. NOTE: The world space center *may* move // when the entity rotates. float BoundingRadius() const; float BoundingRadius2D() const; // Returns the center of the OBB in collision space const Vector &OBBCenter() const; // center point of entity measured in world space // NOTE: This point *may* move when the entity moves depending on // which solid type is being used. const Vector &WorldSpaceCenter() const; // Methods related to solid flags void ClearSolidFlags(void); void RemoveSolidFlags(int flags); void AddSolidFlags(int flags); bool IsSolidFlagSet(int flagMask) const; void SetSolidFlags(int flags); bool IsSolid() const; // Updates the spatial partition void UpdatePartition(); // Are the bounds defined in entity space? bool IsBoundsDefinedInEntitySpace() const; // Transforms a point in OBB space to world space const Vector &CollisionToWorldSpace(const Vector &in, Vector *pResult) const; // Transforms a point in world space to OBB space const Vector &WorldToCollisionSpace(const Vector &in, Vector *pResult) const; // Transforms a direction in world space to OBB space const Vector &WorldDirectionToCollisionSpace(const Vector &in, Vector *pResult) const; // Selects a random point in the bounds given the normalized 0-1 bounds void RandomPointInBounds(const Vector &vecNormalizedMins, const Vector &vecNormalizedMaxs, Vector *pPoint) const; // Is a worldspace point within the bounds of the OBB? bool IsPointInBounds(const Vector &vecWorldPt) const; // Computes a bounding box in world space surrounding the collision bounds void WorldSpaceAABB(Vector *pWorldMins, Vector *pWorldMaxs) const; // Get the collision space mins directly const Vector &CollisionSpaceMins(void) const; // Get the collision space maxs directly const Vector &CollisionSpaceMaxs(void) const; // Computes a "normalized" point (range 0,0,0 - 1,1,1) in collision space // Useful for things like getting a point 75% of the way along z on the OBB, // for example const Vector &NormalizedToCollisionSpace(const Vector &in, Vector *pResult) const; // Computes a "normalized" point (range 0,0,0 - 1,1,1) in world space const Vector &NormalizedToWorldSpace(const Vector &in, Vector *pResult) const; // Transforms a point in world space to normalized space const Vector &WorldToNormalizedSpace(const Vector &in, Vector *pResult) const; // Transforms a point in collision space to normalized space const Vector &CollisionToNormalizedSpace(const Vector &in, Vector *pResult) const; // Computes the nearest point in the OBB to a point specified in world space void CalcNearestPoint(const Vector &vecWorldPt, Vector *pVecNearestWorldPt) const; // Computes the distance from a point in world space to the OBB float CalcDistanceFromPoint(const Vector &vecWorldPt) const; // Does a rotation make us need to recompute the surrounding box? bool DoesRotationInvalidateSurroundingBox() const; // Does VPhysicsUpdate make us need to recompute the surrounding box? bool DoesVPhysicsInvalidateSurroundingBox() const; // Marks the entity has having a dirty surrounding box void MarkSurroundingBoundsDirty(); // Compute the largest dot product of the OBB and the specified direction // vector float ComputeSupportMap(const Vector &vecDirection) const; private: // Transforms an AABB measured in collision space to a box that surrounds it // in world space void CollisionAABBToWorldAABB(const Vector &entityMins, const Vector &entityMaxs, Vector *pWorldMins, Vector *pWorldMaxs) const; // Expand trigger bounds.. void ComputeVPhysicsSurroundingBox(Vector *pVecWorldMins, Vector *pVecWorldMaxs); // Expand trigger bounds.. bool ComputeHitboxSurroundingBox(Vector *pVecWorldMins, Vector *pVecWorldMaxs); bool ComputeEntitySpaceHitboxSurroundingBox(Vector *pVecWorldMins, Vector *pVecWorldMaxs); // Computes the surrounding collision bounds based on whatever algorithm we // want... void ComputeCollisionSurroundingBox(bool bUseVPhysics, Vector *pVecWorldMins, Vector *pVecWorldMaxs); // Computes the surrounding collision bounds from the the OBB (not vphysics) void ComputeRotationExpandedBounds(Vector *pVecWorldMins, Vector *pVecWorldMaxs); // Computes the surrounding collision bounds based on whatever algorithm we // want... void ComputeSurroundingBox(Vector *pVecWorldMins, Vector *pVecWorldMaxs); // Check for untouch void CheckForUntouch(); // Updates the spatial partition void UpdateServerPartitionMask(); // Outer CBaseEntity *GetOuter(); const CBaseEntity *GetOuter() const; private: CBaseEntity *m_pOuter; CNetworkVector(m_vecMinsPreScaled); CNetworkVector(m_vecMaxsPreScaled); CNetworkVector(m_vecMins); CNetworkVector(m_vecMaxs); float m_flRadius; CNetworkVar(unsigned short, m_usSolidFlags); // Spatial partition SpatialPartitionHandle_t m_Partition; CNetworkVar(unsigned char, m_nSurroundType); // One of the SOLID_ defines. Use GetSolid/SetSolid. CNetworkVar(unsigned char, m_nSolidType); CNetworkVar(unsigned char, m_triggerBloat); // SUCKY: We didn't use to have to store this previously // but storing it here means that we can network it + avoid a ton of // client-side mismatch problems CNetworkVector(m_vecSpecifiedSurroundingMinsPreScaled); CNetworkVector(m_vecSpecifiedSurroundingMaxsPreScaled); CNetworkVector(m_vecSpecifiedSurroundingMins); CNetworkVector(m_vecSpecifiedSurroundingMaxs); // Cached off world-aligned surrounding bounds #if 0 short m_surroundingMins[3]; short m_surroundingMaxs[3]; #else Vector m_vecSurroundingMins; Vector m_vecSurroundingMaxs; #endif // pointer to the entity's physics object (vphysics.dll) // IPhysicsObject *m_pPhysicsObject; friend class CBaseEntity; }; //----------------------------------------------------------------------------- // For networking this bad boy //----------------------------------------------------------------------------- #ifdef CLIENT_DLL EXTERN_RECV_TABLE(DT_CollisionProperty); #else EXTERN_SEND_TABLE(DT_CollisionProperty); #endif //----------------------------------------------------------------------------- // Inline methods //----------------------------------------------------------------------------- inline CBaseEntity *CCollisionProperty::GetOuter() { return m_pOuter; } inline const CBaseEntity *CCollisionProperty::GetOuter() const { return m_pOuter; } //----------------------------------------------------------------------------- // Spatial partition //----------------------------------------------------------------------------- inline unsigned short CCollisionProperty::GetPartitionHandle() const { return m_Partition; } //----------------------------------------------------------------------------- // Methods related to size //----------------------------------------------------------------------------- inline const Vector &CCollisionProperty::OBBSize() const { // NOTE: Could precache this, but it's not used that often.. Vector &temp = AllocTempVector(); VectorSubtract(m_vecMaxs, m_vecMins, temp); return temp; } //----------------------------------------------------------------------------- // Bounding radius size //----------------------------------------------------------------------------- inline float CCollisionProperty::BoundingRadius() const { return m_flRadius; } //----------------------------------------------------------------------------- // Methods relating to solid flags //----------------------------------------------------------------------------- inline bool CCollisionProperty::IsBoundsDefinedInEntitySpace() const { return ((m_usSolidFlags & FSOLID_FORCE_WORLD_ALIGNED) == 0) && (m_nSolidType != SOLID_BBOX) && (m_nSolidType != SOLID_NONE); } inline void CCollisionProperty::ClearSolidFlags(void) { SetSolidFlags(0); } inline void CCollisionProperty::RemoveSolidFlags(int flags) { SetSolidFlags(m_usSolidFlags & ~flags); } inline void CCollisionProperty::AddSolidFlags(int flags) { SetSolidFlags(m_usSolidFlags | flags); } inline int CCollisionProperty::GetSolidFlags(void) const { return m_usSolidFlags; } inline bool CCollisionProperty::IsSolidFlagSet(int flagMask) const { return (m_usSolidFlags & flagMask) != 0; } inline bool CCollisionProperty::IsSolid() const { return ::IsSolid((SolidType_t)(unsigned char)m_nSolidType, m_usSolidFlags); } //----------------------------------------------------------------------------- // Returns the center in OBB space //----------------------------------------------------------------------------- inline const Vector &CCollisionProperty::OBBCenter() const { Vector &vecResult = AllocTempVector(); VectorLerp(m_vecMins, m_vecMaxs, 0.5f, vecResult); return vecResult; } //----------------------------------------------------------------------------- // center point of entity //----------------------------------------------------------------------------- inline const Vector &CCollisionProperty::WorldSpaceCenter() const { Vector &vecResult = AllocTempVector(); CollisionToWorldSpace(OBBCenter(), &vecResult); return vecResult; } //----------------------------------------------------------------------------- // Transforms a point in OBB space to world space //----------------------------------------------------------------------------- inline const Vector &CCollisionProperty::CollisionToWorldSpace( const Vector &in, Vector *pResult) const { // Makes sure we don't re-use the same temp twice if (!IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle)) { VectorAdd(in, GetCollisionOrigin(), *pResult); } else { VectorTransform(in, CollisionToWorldTransform(), *pResult); } return *pResult; } //----------------------------------------------------------------------------- // Transforms a point in world space to OBB space //----------------------------------------------------------------------------- inline const Vector &CCollisionProperty::WorldToCollisionSpace( const Vector &in, Vector *pResult) const { if (!IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle)) { VectorSubtract(in, GetCollisionOrigin(), *pResult); } else { VectorITransform(in, CollisionToWorldTransform(), *pResult); } return *pResult; } //----------------------------------------------------------------------------- // Transforms a direction in world space to OBB space //----------------------------------------------------------------------------- inline const Vector &CCollisionProperty::WorldDirectionToCollisionSpace( const Vector &in, Vector *pResult) const { if (!IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle)) { *pResult = in; } else { VectorIRotate(in, CollisionToWorldTransform(), *pResult); } return *pResult; } //----------------------------------------------------------------------------- // Computes a bounding box in world space surrounding the collision bounds //----------------------------------------------------------------------------- inline void CCollisionProperty::WorldSpaceAABB(Vector *pWorldMins, Vector *pWorldMaxs) const { CollisionAABBToWorldAABB(m_vecMins, m_vecMaxs, pWorldMins, pWorldMaxs); } // Get the collision space mins directly inline const Vector &CCollisionProperty::CollisionSpaceMins(void) const { return m_vecMins; } // Get the collision space maxs directly inline const Vector &CCollisionProperty::CollisionSpaceMaxs(void) const { return m_vecMaxs; } //----------------------------------------------------------------------------- // Does a rotation make us need to recompute the surrounding box? //----------------------------------------------------------------------------- inline bool CCollisionProperty::DoesRotationInvalidateSurroundingBox() const { if (IsSolidFlagSet(FSOLID_ROOT_PARENT_ALIGNED)) return true; switch (m_nSurroundType) { case USE_COLLISION_BOUNDS_NEVER_VPHYSICS: case USE_OBB_COLLISION_BOUNDS: case USE_BEST_COLLISION_BOUNDS: return IsBoundsDefinedInEntitySpace(); // In the case of game code, we don't really know, so we have to assume // it does case USE_HITBOXES: case USE_GAME_CODE: return true; case USE_ROTATION_EXPANDED_BOUNDS: case USE_SPECIFIED_BOUNDS: return false; default: Assert(0); return true; } } #endif // COLLISIONPROPERTY_H