//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef DISPCOLL_COMMON_H #define DISPCOLL_COMMON_H #pragma once #include "bitvec.h" #include "builddisp.h" #include "trace.h" #ifdef ENGINE_DLL #include "../engine/zone.h" #endif #ifdef ENGINE_DLL template class CDispVector : public CUtlVector > {}; #else template class CDispVector : public CUtlVector > {}; #endif FORWARD_DECLARE_HANDLE(memhandle_t); #define DISPCOLL_TREETRI_SIZE MAX_DISPTRIS #define DISPCOLL_DIST_EPSILON 0.03125f #define DISPCOLL_ROOTNODE_INDEX 0 #define DISPCOLL_INVALID_TRI -1 #define DISPCOLL_INVALID_FRAC -99999.9f #define DISPCOLL_NORMAL_UNDEF 0xffff extern double g_flDispCollSweepTimer; extern double g_flDispCollIntersectTimer; extern double g_flDispCollInCallTimer; struct RayDispOutput_t { short ndxVerts[4]; // 3 verts and a pad float u, v; // the u, v paramters (edgeU = v1 - v0, edgeV = v2 - v0) float dist; // intersection distance }; // Assumptions: // Max patch is 17x17, therefore 9 bits needed to represent a triangle //index // //============================================================================= // Displacement Collision Triangle class CDispCollTri { struct index_t { union { struct { unsigned short uiVert : 9; unsigned short uiMin : 2; unsigned short uiMax : 2; } m_Index; unsigned short m_IndexDummy; }; }; index_t m_TriData[3]; public: unsigned short m_ucSignBits : 3; // Plane test. unsigned short m_ucPlaneType : 3; // Axial test? unsigned short m_uiFlags : 5; // Uses 5-bits - maybe look into merging it // with something? Vector m_vecNormal; // Triangle normal (plane normal). float m_flDist; // Triangle plane dist. // Creation. CDispCollTri(); void Init(void); void CalcPlane(CDispVector &m_aVerts); void FindMinMax(CDispVector &m_aVerts); // Triangle data. inline void SetVert(int iPos, int iVert) { Assert((iPos >= 0) && (iPos < 3)); Assert((iVert >= 0) && (iVert < (1 << 9))); m_TriData[iPos].m_Index.uiVert = iVert; } inline int GetVert(int iPos) const { Assert((iPos >= 0) && (iPos < 3)); return m_TriData[iPos].m_Index.uiVert; } inline void SetMin(int iAxis, int iMin) { Assert((iAxis >= 0) && (iAxis < 3)); Assert((iMin >= 0) && (iMin < 3)); m_TriData[iAxis].m_Index.uiMin = iMin; } inline int GetMin(int iAxis) const { Assert((iAxis >= 0) && (iAxis < 3)); return m_TriData[iAxis].m_Index.uiMin; } inline void SetMax(int iAxis, int iMax) { Assert((iAxis >= 0) && (iAxis < 3)); Assert((iMax >= 0) && (iMax < 3)); m_TriData[iAxis].m_Index.uiMax = iMax; } inline int GetMax(int iAxis) const { Assert((iAxis >= 0) && (iAxis < 3)); return m_TriData[iAxis].m_Index.uiMax; } }; //============================================================================= // Helper class CDispCollHelper { public: float m_flStartFrac; float m_flEndFrac; Vector m_vecImpactNormal; float m_flImpactDist; }; //============================================================================= // Cache #pragma pack(1) class CDispCollTriCache { public: unsigned short m_iCrossX[3]; unsigned short m_iCrossY[3]; unsigned short m_iCrossZ[3]; }; #pragma pack() #include "mathlib/ssemath.h" class CDispCollNode { public: FourVectors m_mins; FourVectors m_maxs; }; class CDispCollLeaf { public: short m_tris[2]; }; // a power 4 displacement can have 341 nodes, pad out to 344 for 16-byte // alignment const int MAX_DISP_AABB_NODES = 341; const int MAX_AABB_LIST = 344; struct rayleaflist_t { FourVectors rayStart; FourVectors rayExtents; FourVectors invDelta; int nodeList[MAX_AABB_LIST]; int maxIndex; }; //============================================================================= // // Displacement Collision Tree Data // class CDispCollTree { public: // Creation/Destruction. CDispCollTree(); ~CDispCollTree(); virtual bool Create(CCoreDispInfo *pDisp); // Raycasts. // NOTE: These assume you've precalculated invDelta as well as culled to the // bounds of this disp bool AABBTree_Ray(const Ray_t &ray, const Vector &invDelta, CBaseTrace *pTrace, bool bSide = true); bool AABBTree_Ray(const Ray_t &ray, const Vector &invDelta, RayDispOutput_t &output); // NOTE: Lower perf helper function, should not be used in the game runtime bool AABBTree_Ray(const Ray_t &ray, RayDispOutput_t &output); // Hull Sweeps. // NOTE: These assume you've precalculated invDelta as well as culled to the // bounds of this disp bool AABBTree_SweepAABB(const Ray_t &ray, const Vector &invDelta, CBaseTrace *pTrace); // Hull Intersection. bool AABBTree_IntersectAABB(const Vector &absMins, const Vector &absMaxs); // Point/Box vs. Bounds. bool PointInBounds(Vector const &vecBoxCenter, Vector const &vecBoxMin, Vector const &vecBoxMax, bool bPoint); // Utility. inline void SetPower(int power) { m_nPower = power; } inline int GetPower(void) { return m_nPower; } inline int GetFlags(void) { return m_nFlags; } inline void SetFlags(int nFlags) { m_nFlags = nFlags; } inline bool CheckFlags(int nFlags) { return ((nFlags & GetFlags()) != 0) ? true : false; } inline int GetWidth(void) { return ((1 << m_nPower) + 1); } inline int GetHeight(void) { return ((1 << m_nPower) + 1); } inline int GetSize(void) { return ((1 << m_nPower) + 1) * ((1 << m_nPower) + 1); } inline int GetTriSize(void) { return ((1 << m_nPower) * (1 << m_nPower) * 2); } // inline void SetTriFlags( short iTri, unsigned short nFlags ) { //m_aTris[iTri].m_uiFlags = nFlags; } inline void GetStabDirection(Vector &vecDir) { vecDir = m_vecStabDir; } inline void GetBounds(Vector &vecBoxMin, Vector &vecBoxMax) { vecBoxMin = m_mins; vecBoxMax = m_maxs; } inline int GetContents(void) { return m_nContents; } inline void SetSurfaceProps(int iProp, short nSurfProp) { Assert((iProp >= 0) && (iProp < 2)); m_nSurfaceProps[iProp] = nSurfProp; } inline short GetSurfaceProps(int iProp) { return m_nSurfaceProps[iProp]; } inline unsigned int GetMemorySize(void) { return m_nSize; } inline unsigned int GetCacheMemorySize(void) { return (m_aTrisCache.Count() * sizeof(CDispCollTriCache) + m_aEdgePlanes.Count() * sizeof(Vector)); } inline bool IsCached(void) { return m_aTrisCache.Count() == m_aTris.Count(); } void GetVirtualMeshList(struct virtualmeshlist_t *pList); int AABBTree_GetTrisInSphere(const Vector ¢er, float radius, unsigned short *pIndexOut, int indexMax); public: inline int Nodes_GetChild(int iNode, int nDirection); inline int Nodes_CalcCount(int nPower); inline int Nodes_GetParent(int iNode); inline int Nodes_GetLevel(int iNode); inline int Nodes_GetIndexFromComponents(int x, int y); void LockCache(); void UnlockCache(); void Cache(void); void Uncache() { m_aTrisCache.Purge(); m_aEdgePlanes.Purge(); } #ifdef ENGINE_DLL // Data manager methods static size_t EstimatedSize(CDispCollTree *pTree) { return pTree->GetCacheMemorySize(); } static CDispCollTree *CreateResource(CDispCollTree *pTree) { // Created ahead of time return pTree; } bool GetData() { return IsCached(); } size_t Size() { return GetCacheMemorySize(); } void DestroyResource() { Uncache(); m_hCache = NULL; } #endif protected: bool AABBTree_Create(CCoreDispInfo *pDisp); void AABBTree_CopyDispData(CCoreDispInfo *pDisp); void AABBTree_CreateLeafs(void); void AABBTree_GenerateBoxes_r(int nodeIndex, Vector *pMins, Vector *pMaxs); void AABBTree_CalcBounds(void); int AABBTree_BuildTreeTrisInSphere_r(const Vector ¢er, float radius, int iNode, unsigned short *pIndexOut, unsigned short indexMax); void AABBTree_TreeTrisRayTest(const Ray_t &ray, const Vector &vecInvDelta, int iNode, CBaseTrace *pTrace, bool bSide, CDispCollTri **pImpactTri); void AABBTree_TreeTrisRayBarycentricTest(const Ray_t &ray, const Vector &vecInvDelta, int iNode, RayDispOutput_t &output, CDispCollTri **pImpactTri); int FORCEINLINE BuildRayLeafList(int iNode, rayleaflist_t &list); struct AABBTree_TreeTrisSweepTest_Args_t { AABBTree_TreeTrisSweepTest_Args_t(const Ray_t &ray, const Vector &vecInvDelta, const Vector &rayDir, CBaseTrace *pTrace) : ray(ray), vecInvDelta(vecInvDelta), rayDir(rayDir), pTrace(pTrace) {} const Ray_t &ray; const Vector &vecInvDelta; const Vector &rayDir; CBaseTrace *pTrace; }; protected: void SweepAABBTriIntersect(const Ray_t &ray, const Vector &rayDir, int iTri, CDispCollTri *pTri, CBaseTrace *pTrace); void Cache_Create(CDispCollTri *pTri, int iTri); // Testing! bool Cache_EdgeCrossAxisX(const Vector &vecEdge, const Vector &vecOnEdge, const Vector &vecOffEdge, CDispCollTri *pTri, unsigned short &iPlane); bool Cache_EdgeCrossAxisY(const Vector &vecEdge, const Vector &vecOnEdge, const Vector &vecOffEdge, CDispCollTri *pTri, unsigned short &iPlane); bool Cache_EdgeCrossAxisZ(const Vector &vecEdge, const Vector &vecOnEdge, const Vector &vecOffEdge, CDispCollTri *pTri, unsigned short &iPlane); inline bool FacePlane(const Ray_t &ray, const Vector &rayDir, CDispCollTri *pTri, CDispCollHelper *pHelper); bool FORCEINLINE AxisPlanesXYZ(const Ray_t &ray, CDispCollTri *pTri, CDispCollHelper *pHelper); inline bool EdgeCrossAxisX(const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper); inline bool EdgeCrossAxisY(const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper); inline bool EdgeCrossAxisZ(const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper); bool ResolveRayPlaneIntersect(float flStart, float flEnd, const Vector &vecNormal, float flDist, CDispCollHelper *pHelper); template bool FORCEINLINE TestOneAxisPlaneMin(const Ray_t &ray, CDispCollTri *pTri); template bool FORCEINLINE TestOneAxisPlaneMax(const Ray_t &ray, CDispCollTri *pTri); template bool EdgeCrossAxis(const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper); // Utility inline void CalcClosestBoxPoint(const Vector &vecPlaneNormal, const Vector &vecBoxStart, const Vector &vecBoxExtents, Vector &vecBoxPoint); inline void CalcClosestExtents(const Vector &vecPlaneNormal, const Vector &vecBoxExtents, Vector &vecBoxPoint); int AddPlane(const Vector &vecNormal); bool FORCEINLINE IsLeafNode(int iNode); public: Vector m_mins; // Bounding box of the displacement surface and base face int m_iCounter; Vector m_maxs; // Bounding box of the displacement surface and base face protected: int m_nContents; // The displacement surface "contents" (solid, etc...) #ifdef ENGINE_DLL memhandle_t m_hCache; #endif int m_nPower; // Size of the displacement ( 2^power + 1 ) int m_nFlags; Vector m_vecSurfPoints[4]; // Base surface points. // Collision data. Vector m_vecStabDir; // Direction to stab for this displacement surface (is // the base face normal) short m_nSurfaceProps[2]; // Surface properties (save off from texdata for // impact responses) protected: CDispVector m_aVerts; // Displacement verts. CDispVector m_aTris; // Displacement triangles. CDispVector m_nodes; // Nodes. CDispVector m_leaves; // Leaves. // Cache CUtlVector m_aTrisCache; CUtlVector m_aEdgePlanes; CDispCollHelper m_Helper; unsigned int m_nSize; }; FORCEINLINE bool CDispCollTree::IsLeafNode(int iNode) { return iNode >= m_nodes.Count() ? true : false; } //----------------------------------------------------------------------------- // Purpose: get the child node index given the current node index and direction // of the child (1 of 4) // Input: iNode - current node index // nDirection - direction of the child ( [0...3] - SW, SE, NW, NE ) // Output: int - the index of the child node //----------------------------------------------------------------------------- inline int CDispCollTree::Nodes_GetChild(int iNode, int nDirection) { // node range [0...m_NodeCount) Assert(iNode >= 0); Assert(iNode < m_nodes.Count()); // ( node index * 4 ) + ( direction + 1 ) return ((iNode << 2) + (nDirection + 1)); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- inline int CDispCollTree::Nodes_CalcCount(int nPower) { Assert(nPower >= 1); Assert(nPower <= 4); return ((1 << ((nPower + 1) << 1)) / 3); } //----------------------------------------------------------------------------- // Purpose: get the parent node index given the current node // Input: iNode - current node index // Output: int - the index of the parent node //----------------------------------------------------------------------------- inline int CDispCollTree::Nodes_GetParent(int iNode) { // node range [0...m_NodeCount) Assert(iNode >= 0); Assert(iNode < m_nodes.Count()); // ( node index - 1 ) / 4 return ((iNode - 1) >> 2); } //----------------------------------------------------------------------------- // Purpose: // TODO: should make this a function - not a hardcoded set of statements!!! //----------------------------------------------------------------------------- inline int CDispCollTree::Nodes_GetLevel(int iNode) { // node range [0...m_NodeCount) Assert(iNode >= 0); Assert(iNode < m_nodes.Count()); // level = 2^n + 1 if (iNode == 0) { return 1; } if (iNode < 5) { return 2; } if (iNode < 21) { return 3; } if (iNode < 85) { return 4; } if (iNode < 341) { return 5; } return -1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- inline int CDispCollTree::Nodes_GetIndexFromComponents(int x, int y) { int nIndex = 0; // Interleave bits from the x and y values to create the index int iShift; for (iShift = 0; x != 0; iShift += 2, x >>= 1) { nIndex |= (x & 1) << iShift; } for (iShift = 1; y != 0; iShift += 2, y >>= 1) { nIndex |= (y & 1) << iShift; } return nIndex; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- inline void CDispCollTree::CalcClosestBoxPoint(const Vector &vecPlaneNormal, const Vector &vecBoxStart, const Vector &vecBoxExtents, Vector &vecBoxPoint) { vecBoxPoint = vecBoxStart; (vecPlaneNormal[0] < 0.0f) ? vecBoxPoint[0] += vecBoxExtents[0] : vecBoxPoint[0] -= vecBoxExtents[0]; (vecPlaneNormal[1] < 0.0f) ? vecBoxPoint[1] += vecBoxExtents[1] : vecBoxPoint[1] -= vecBoxExtents[1]; (vecPlaneNormal[2] < 0.0f) ? vecBoxPoint[2] += vecBoxExtents[2] : vecBoxPoint[2] -= vecBoxExtents[2]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- inline void CDispCollTree::CalcClosestExtents(const Vector &vecPlaneNormal, const Vector &vecBoxExtents, Vector &vecBoxPoint) { (vecPlaneNormal[0] < 0.0f) ? vecBoxPoint[0] = vecBoxExtents[0] : vecBoxPoint[0] = -vecBoxExtents[0]; (vecPlaneNormal[1] < 0.0f) ? vecBoxPoint[1] = vecBoxExtents[1] : vecBoxPoint[1] = -vecBoxExtents[1]; (vecPlaneNormal[2] < 0.0f) ? vecBoxPoint[2] = vecBoxExtents[2] : vecBoxPoint[2] = -vecBoxExtents[2]; } //============================================================================= // Global Helper Functions CDispCollTree *DispCollTrees_Alloc(int count); void DispCollTrees_Free(CDispCollTree *pTrees); #endif // DISPCOLL_COMMON_H