Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1675 lines
53 KiB

//========= Copyright © Valve Corporation, All rights reserved. ============//
#ifndef SERIALIZE_HDR
#define SERIALIZE_HDR
#include "resourcefile/resourcestream.h"
#include "mathlib/aabb.h"
#include "bitvec.h"
#include "rubikon/serializehelpers.h"
#include "rubikon/constants.h"
#include "tier1/checksum_crc.h"
#include "mathlib/transform.h"
//---------------------------------------------------------------------------------------
// Sphere serialization
//---------------------------------------------------------------------------------------
schema struct RnSphere_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnSphere_t );
AABB_t GetBbox() const;
AABB_t GetBbox( const CTransform& xform ) const;
int GetTriangulation( Vector *pVerts = NULL ) const;
void GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices ) const;
void GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, int nSides, int nSlices ) const;
float GetVolume() const;
Vector ComputeOrthographicAreas() const;
Vector m_vCenter;
float32 m_flRadius;
};
inline RnSphere_t operator*( const RnSphere_t& sphere, float flScale )
{
RnSphere_t out;
out.m_vCenter = sphere.m_vCenter * flScale;
out.m_flRadius = sphere.m_flRadius * flScale;
return out;
}
inline RnSphere_t operator*( float flScale, const RnSphere_t& sphere )
{
RnSphere_t out;
out.m_vCenter = sphere.m_vCenter * flScale;
out.m_flRadius = sphere.m_flRadius * flScale;
return out;
}
//---------------------------------------------------------------------------------------
// Capsule serialization
//---------------------------------------------------------------------------------------
schema struct RnCapsule_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnCapsule_t );
AABB_t GetBbox() const;
AABB_t GetBbox( const CTransform& xform ) const;
int GetTriangulation( Vector *pVerts = NULL ) const;
void GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices ) const;
void GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, int nSides, int nSlices ) const;
float GetVolume() const;
Vector ComputeOrthographicAreas() const;
Vector m_vCenter[ 2 ];
float32 m_flRadius;
};
inline RnCapsule_t operator*( const RnCapsule_t& capsule, float flScale )
{
RnCapsule_t out;
out.m_vCenter[ 0 ] = capsule.m_vCenter[ 0 ] * flScale;
out.m_vCenter[ 1 ] = capsule.m_vCenter[ 1 ] * flScale;
out.m_flRadius = capsule.m_flRadius * flScale;
return out;
}
inline RnCapsule_t operator*( float flScale, const RnCapsule_t& capsule )
{
RnCapsule_t out;
out.m_vCenter[ 0 ] = capsule.m_vCenter[ 0 ] * flScale;
out.m_vCenter[ 1 ] = capsule.m_vCenter[ 1 ] * flScale;
out.m_flRadius = capsule.m_flRadius * flScale;
return out;
}
//--------------------------------------------------------------------------------------------------
// Ray
//--------------------------------------------------------------------------------------------------
struct RnRay_t
{
RnRay_t( void ) { }
RnRay_t( const Vector& vStart, const Vector& vEnd )
{
vOrigin = vStart;
vDelta = vEnd - vStart;
// Pre-compute inverse
vDeltaInv.x = vDelta.x != 0.0f ? 1.0f / vDelta.x : FLT_MAX;
vDeltaInv.y = vDelta.y != 0.0f ? 1.0f / vDelta.y : FLT_MAX;
vDeltaInv.z = vDelta.z != 0.0f ? 1.0f / vDelta.z : FLT_MAX;
}
VectorAligned vOrigin;
VectorAligned vDelta;
VectorAligned vDeltaInv;
};
//---------------------------------------------------------------------------------------
// Hull serialization
//---------------------------------------------------------------------------------------
schema struct RnPlane_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnPlane_t );
Vector m_vNormal; // The plane normal
float32 m_flOffset; // The plane offset such that P: n*x - d = 0
// Construction
FORCEINLINE RnPlane_t( void ) { }
FORCEINLINE RnPlane_t( const Vector& n, float d ) { m_vNormal = n; m_flOffset = d; }
FORCEINLINE RnPlane_t( const Vector& n, const Vector& p ) { m_vNormal = n; m_flOffset = DotProduct( n, p ); }
// Utilities
FORCEINLINE float Distance( const Vector &vPoint ) const { return DotProduct( m_vNormal, vPoint ) - m_flOffset; }
FORCEINLINE bool IsValid( void ) const { return m_vNormal != vec3_origin; }
FORCEINLINE bool operator == ( const RnPlane_t &other )const { return m_vNormal == other.m_vNormal && m_flOffset == other.m_flOffset; }
};
schema struct RnHalfEdge_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnHalfEdge_t );
uint8 m_nNext; // Next edge index in CCW circular list around face
uint8 m_nTwin; // Twin edge
uint8 m_nOrigin; // Origin vertex index of edge
uint8 m_nFace; // Face index
};
schema struct RnFace_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnFace_t );
uint8 m_nEdge; // Start edge index for CCW circular list around face
};
schema struct RnHull_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnHull_t );
Vector m_vCentroid; // Hull centroid
float m_flMaxAngularRadius; // Angular radius for CCD
CResourceArray< Vector > m_Vertices; // Hull vertices (x1, y1, z1, x2, y2, z2, ...)
CResourceArray< RnPlane_t > m_Planes; // Hull face planes with outward pointing normals (n1, -d1, n2, -d2, ...)
CResourceArray< RnHalfEdge_t > m_Edges; // Hull half edges order such that each edge e is followed by its twin e' (e1, e1', e2, e2', ...)
CResourceArray< RnFace_t > m_Faces; // Hull faces
Vector m_vOrthographicAreas; // fraction 0..1 of coverage along YZ,ZX,XY sides of AABB
matrix3x4_t m_MassProperties; // inertia tensor (in 3x3 part, always PSD) and center of mass (translation)
float m_flVolume;
float m_flMaxMotionRadius;
float m_flMinMotionThickness;
AABB_t m_Bounds;
FORCEINLINE int GetVertexCount( void ) const { return m_Vertices.Count(); }
FORCEINLINE const Vector GetVertex( int nVertex ) const { return m_Vertices[ nVertex ]; }
FORCEINLINE int GetPlaneCount( void ) const { return m_Planes.Count(); }
FORCEINLINE const RnPlane_t& GetPlane( int nFace ) const { return m_Planes[ nFace ]; }
FORCEINLINE const Vector& GetPlaneNormal( int nFace ) const { return m_Planes[ nFace ].m_vNormal; }
FORCEINLINE int GetEdgeCount( void ) const { return m_Edges.Count(); }
FORCEINLINE const RnHalfEdge_t* GetEdge( int nEdge ) const { return &m_Edges[ nEdge ]; }
FORCEINLINE int GetFaceCount( void ) const { return m_Faces.Count(); }
FORCEINLINE const RnFace_t* GetFace( int nFace ) const { return &m_Faces[ nFace ]; }
FORCEINLINE const Vector ComputeFaceCentroid( int nFace )const;
int GetMemory( void ) const;
void Transform( const matrix3x4_t& transform );
AABB_t GetBbox( void ) const;
AABB_t GetBbox( const CTransform& xform ) const;
int GetTriangulation( Vector *pVerts = NULL ) const;
void GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, float flScale = 1.0f ) const;
float GetVolume( void ) const;
FORCEINLINE const Vector &GetCentroid( )const { return m_vCentroid; }
uintp GetRuntimeSize( void ) const;
void Validate( void ) const;
};
inline void ShallowCopy( RnHull_t &dest, const RnHull_t &source )
{
V_memcpy( &dest, &source, sizeof( RnHull_t ) );
dest.m_Vertices = source.m_Vertices;
dest.m_Planes = source.m_Planes;
dest.m_Edges = source.m_Edges;
dest.m_Faces = source.m_Faces;
}
//--------------------------------------------------------------------------------------------------
// Helpers (for stack allocation)
//--------------------------------------------------------------------------------------------------
inline RnHalfEdge_t MakeEdge( uint8 nNext, uint8 nTwin, uint8 nOrigin, uint8 nFace )
{
RnHalfEdge_t e;
e.m_nNext = nNext;
e.m_nTwin = nTwin;
e.m_nOrigin = nOrigin;
e.m_nFace = nFace;
return e;
}
struct RnHullTriangle_t : public RnHull_t
{
void Init( const Vector& v1, const Vector& v2, const Vector& v3 )
{
// Centroid
m_vCentroid = ( v1 + v2 + v3 ) / 3.0f;
// Vertices
Vector TriangleVertices;
m_TriangleVertices[ 0 ] = v1;
m_TriangleVertices[ 1 ] = v2;
m_TriangleVertices[ 2 ] = v3;
m_Vertices.WriteDirect( 3, m_TriangleVertices );
// Planes
Vector n = CrossProduct( v2 - v1, v3 - v1 );
VectorNormalize( n );
m_TrianglePlanes[ 0 ] = RnPlane_t( n, v1 );
m_TrianglePlanes[ 1 ] = RnPlane_t( -n, v1 );
m_Planes.WriteDirect( 2, m_TrianglePlanes );
// Edges (remember that each edge *must* be followed by its twin!)
m_TriangleEdges[ 0 ] = MakeEdge( 2, 1, 0, 0 ); // Face 0 - Edge 0
m_TriangleEdges[ 1 ] = MakeEdge( 5, 0, 1, 1 ); // Face 1 - Edge 0
m_TriangleEdges[ 2 ] = MakeEdge( 4, 3, 1, 0 ); // Face 0 - Edge 1
m_TriangleEdges[ 3 ] = MakeEdge( 1, 2, 2, 1 ); // Face 1 - Edge 1
m_TriangleEdges[ 4 ] = MakeEdge( 0, 5, 2, 0 ); // Face 0 - Edge 2
m_TriangleEdges[ 5 ] = MakeEdge( 3, 4, 0, 1 ); // Face 1 - Edge 2
m_Edges.WriteDirect( 6, m_TriangleEdges );
// Faces
m_TriangleFaces[ 0 ].m_nEdge = 0;
m_TriangleFaces[ 1 ].m_nEdge = 1;
m_Faces.WriteDirect( 2, m_TriangleFaces );
// Bounds
m_Bounds.m_vMinBounds = VectorMin( v1, VectorMin( v2, v3 ) );
m_Bounds.m_vMaxBounds = VectorMax( v1, VectorMax( v2, v3 ) );
}
Vector m_TriangleVertices[ 3 ];
RnPlane_t m_TrianglePlanes[ 2 ];
RnHalfEdge_t m_TriangleEdges[ 6 ];
RnFace_t m_TriangleFaces[ 2 ];
};
struct RnHullBox_t : public RnHull_t
{
void Init( const Vector& vMin, const Vector& vMax )
{
// Centroid
m_vCentroid = 0.5f * ( vMin + vMax );
// Vertices
Vector vExtent = vMax - m_vCentroid;
float ex = vExtent.x;
float ey = vExtent.y;
float ez = vExtent.z;
m_BoxVertices[ 0 ] = m_vCentroid + Vector( ex, ey, ez );
m_BoxVertices[ 1 ] = m_vCentroid + Vector( -ex, ey, ez );
m_BoxVertices[ 2 ] = m_vCentroid + Vector( -ex, -ey, ez );
m_BoxVertices[ 3 ] = m_vCentroid + Vector( ex, -ey, ez );
m_BoxVertices[ 4 ] = m_vCentroid + Vector( ex, ey, -ez );
m_BoxVertices[ 5 ] = m_vCentroid + Vector( -ex, ey, -ez );
m_BoxVertices[ 6 ] = m_vCentroid + Vector( -ex, -ey, -ez );
m_BoxVertices[ 7 ] = m_vCentroid + Vector( ex, -ey, -ez );
m_Vertices.WriteDirect( 8, m_BoxVertices );
// Planes
Vector vAxisX( 1, 0, 0 );
Vector vAxisY( 0, 1, 0 );
Vector vAxisZ( 0, 0, 1 );
m_BoxPlanes[ 0 ] = RnPlane_t( -vAxisX, vMin );
m_BoxPlanes[ 1 ] = RnPlane_t( vAxisX, vMax );
m_BoxPlanes[ 2 ] = RnPlane_t( -vAxisY, vMin );
m_BoxPlanes[ 3 ] = RnPlane_t( vAxisY, vMax );
m_BoxPlanes[ 4 ] = RnPlane_t( -vAxisZ, vMin );
m_BoxPlanes[ 5 ] = RnPlane_t( vAxisZ, vMax );
m_Planes.WriteDirect( 6, m_BoxPlanes );
// Edges (remember that each edge *must* be followed by its twin!)
m_BoxEdges[ 0 ] = MakeEdge( 2, 1, 2, 0 );
m_BoxEdges[ 1 ] = MakeEdge( 17, 0, 1, 5 );
m_BoxEdges[ 2 ] = MakeEdge( 4, 3, 1, 0 );
m_BoxEdges[ 3 ] = MakeEdge( 20, 2, 5, 3 );
m_BoxEdges[ 4 ] = MakeEdge( 6, 5, 5, 0 );
m_BoxEdges[ 5 ] = MakeEdge( 23, 4, 6, 4 );
m_BoxEdges[ 6 ] = MakeEdge( 0, 7, 6, 0 );
m_BoxEdges[ 7 ] = MakeEdge( 18, 6, 2, 2 );
m_BoxEdges[ 8 ] = MakeEdge( 10, 9, 0, 1 );
m_BoxEdges[ 9 ] = MakeEdge( 21, 8, 3, 5 );
m_BoxEdges[ 10 ] = MakeEdge( 12, 11, 3, 1 );
m_BoxEdges[ 11 ] = MakeEdge( 16, 10, 7, 2 );
m_BoxEdges[ 12 ] = MakeEdge( 14, 13, 7, 1 );
m_BoxEdges[ 13 ] = MakeEdge( 19, 12, 4, 4 );
m_BoxEdges[ 14 ] = MakeEdge( 8, 15, 4, 1 );
m_BoxEdges[ 15 ] = MakeEdge( 22, 14, 0, 3 );
m_BoxEdges[ 16 ] = MakeEdge( 7, 17, 3, 2 );
m_BoxEdges[ 17 ] = MakeEdge( 9, 16, 2, 5 );
m_BoxEdges[ 18 ] = MakeEdge( 11, 19, 6, 2 );
m_BoxEdges[ 19 ] = MakeEdge( 5, 18, 7, 4 );
m_BoxEdges[ 20 ] = MakeEdge( 15, 21, 1, 3 );
m_BoxEdges[ 21 ] = MakeEdge( 1, 20, 0, 5 );
m_BoxEdges[ 22 ] = MakeEdge( 3, 23, 4, 3 );
m_BoxEdges[ 23 ] = MakeEdge( 13, 22, 5, 4 );
m_Edges.WriteDirect( 24, m_BoxEdges );
// Faces
m_BoxFaces[ 0 ].m_nEdge = 0;
m_BoxFaces[ 1 ].m_nEdge = 8;
m_BoxFaces[ 2 ].m_nEdge = 16;
m_BoxFaces[ 3 ].m_nEdge = 20;
m_BoxFaces[ 4 ].m_nEdge = 19;
m_BoxFaces[ 5 ].m_nEdge = 21;
m_Faces.WriteDirect( 6, m_BoxFaces );
// Bounds
m_Bounds.m_vMinBounds = vMin;
m_Bounds.m_vMaxBounds = vMax;
}
Vector m_BoxVertices[ 8 ];
RnPlane_t m_BoxPlanes[ 6 ];
RnHalfEdge_t m_BoxEdges[ 24 ];
RnFace_t m_BoxFaces[ 6 ];
};
//--------------------------------------------------------------------------------------------------
// Mesh serialization
//--------------------------------------------------------------------------------------------------
#define RN_TYPE_SPLIT_X 0
#define RN_TYPE_SPLIT_Y 1
#define RN_TYPE_SPLIT_Z 2
#define RN_TYPE_LEAF 3
schema struct RnTriangle_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnTriangle_t );
int32 m_nIndex[ 3 ];
};
// TODO: this wants to be ALIGN32, but it's currently stored in CUtlVector and CResourceArray, which do not support this.
schema struct ALIGN16 RnNode_t // node needs to not stride over cache line boundary and min/max vectors need to be aligned for easy SIMD loading
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnNode_t );
Vector m_vMin; // The node AABB
uint32 m_nChildren; // The 2nd child offset and the node type/split axis
Vector m_vMax; // The node AABB
uint32 m_nTriangleOffset; // If leaf node this is the offset into the associated triangle array
// Traversal
FORCEINLINE bool IsLeaf( void ) const { return GetType() == RN_TYPE_LEAF; }
FORCEINLINE RnNode_t* GetLeftChild( void ) { return this + 1; }
FORCEINLINE const RnNode_t* GetLeftChild( void ) const { return this + 1; }
FORCEINLINE RnNode_t* GetRightChild( void ) { return this + GetChildOffset(); }
FORCEINLINE const RnNode_t* GetRightChild( void ) const { return this + GetChildOffset(); }
FORCEINLINE uint GetAxis( void ) const { AssertDbg( !IsLeaf() ); return m_nChildren >> 30; }
FORCEINLINE uint GetChildOffset( void) const { AssertDbg( !IsLeaf() ); return m_nChildren & 0x3FFFFFFF; }
FORCEINLINE uint GetType( void ) const { return m_nChildren >> 30; }
FORCEINLINE uint GetTriangleCount( void ) const { AssertDbg( IsLeaf() ); return m_nChildren & 0x3FFFFFFF; }
FORCEINLINE uint GetTriangleOffset( void ) const { AssertDbg( IsLeaf() ); return m_nTriangleOffset; }
FORCEINLINE void SetTriangleOffset( uint32 nTriangleOffset ) { AssertDbg( IsLeaf( ) ); m_nTriangleOffset = nTriangleOffset; }
// Construction
void SetLeaf( uint nOffset, uint nCount )
{
m_nTriangleOffset = nOffset;
m_nChildren = ( RN_TYPE_LEAF << 30 ) | nCount;
}
void SetNode( uint nAxis, uint nChildOffset )
{
AssertDbg( nAxis < 3 );
m_nChildren = ( nAxis << 30 ) | nChildOffset;
}
// Statistics
int GetHeight( void ) const;
} ALIGN16_POST;
schema struct RnMesh_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnMesh_t );
Vector m_vMin; // The mesh AABB
Vector m_vMax; // The mesh AABB
CResourceArray< RnNode_t > m_Nodes; // The nodes of the loose kd-tree to accelerate ray casts and volume queries against this mesh.
CResourceArray< Vector > m_Vertices; // The mesh vertices in the space of the parent shape.
CResourceArray< RnTriangle_t > m_Triangles; // The mesh triangles with additional topology information similar to the half-edge data structure.
CResourceArray< uint8 > m_Materials; // The per-triangle material indices for this mesh. Can be empty if all triangles share the same material.
Vector m_vOrthographicAreas; // fraction 0..1 of coverage along YZ,ZX,XY sides of AABB
FORCEINLINE RnNode_t* GetRoot( void ) { return &m_Nodes[ 0 ]; }
FORCEINLINE const RnNode_t* GetRoot( void ) const { return &m_Nodes[ 0 ]; }
FORCEINLINE int GetVertexCount( void ) const { return m_Vertices.Count(); }
FORCEINLINE const Vector &GetVertex( int nVertex ) const { return m_Vertices[ nVertex ]; }
FORCEINLINE int GetTriangleCount( void ) const { return m_Triangles.Count(); }
FORCEINLINE const RnTriangle_t* GetTriangle( int nTriangle ) const { return &m_Triangles[ nTriangle ]; }
FORCEINLINE int GetMaterialCount( void ) const { return m_Materials.Count(); }
FORCEINLINE uint8 GetMaterial( int nMaterial ) const { return m_Materials[ nMaterial ]; }
FORCEINLINE const Vector ComputeTriangleUnitNormal( const Vector &vScale, int nIndex )const;
FORCEINLINE const Vector ComputeTriangleCentroid( int nIndex )const;
FORCEINLINE const Vector ComputeTriangleIncenter( int nIndex )const; // the center of inscribed circle
// Statistics
int GetHeight( void ) const;
int GetMemory( void ) const;
AABB_t GetBbox( void ) const;
int GetTriangulation( Vector *pVerts = NULL ) const;
void GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, const Vector &vScale = Vector( 1, 1, 1 ) ) const;
float GetVolume( void ) const { return 0; }
uintp GetRuntimeSize( void ) const ;
void Validate( void ) const;
template < typename Functor >
void CastBox( const Functor &callback, const RnRay_t& localRay, const Vector& vExtent, float flMaxFraction ) const;
};
//--------------------------------------------------------------------------------------------------
// Shape serialization
//--------------------------------------------------------------------------------------------------
schema struct RnShapeDesc_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnShapeDesc_t );
uint32 m_nCollisionAttributeIndex;
uint32 m_nSurfacePropertyIndex;
};
schema struct RnSphereDesc_t : public RnShapeDesc_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnSphereDesc_t );
RnSphere_t m_Sphere;
};
schema struct RnCapsuleDesc_t : public RnShapeDesc_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnCapsuleDesc_t );
RnCapsule_t m_Capsule;
};
schema struct RnHullDesc_t : public RnShapeDesc_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnHullDesc_t );
RnHull_t m_Hull;
};
schema struct RnMeshDesc_t : public RnShapeDesc_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnMeshDesc_t );
RnMesh_t m_Mesh;
};
//--------------------------------------------------------------------------------------------------
template <typename Array>
uintp GetRuntimeSizeOf( const Array & arr )
{
return sizeof( arr[0] ) * uintp( arr.Count() );
}
//--------------------------------------------------------------------------------------------------
inline uintp RnHull_t::GetRuntimeSize( void ) const
{
return sizeof( *this ) +
GetRuntimeSizeOf( m_Vertices ) +
GetRuntimeSizeOf( m_Planes ) +
GetRuntimeSizeOf( m_Edges ) +
GetRuntimeSizeOf( m_Faces );
}
//--------------------------------------------------------------------------------------------------
inline uintp RnMesh_t::GetRuntimeSize( void ) const
{
return ( ( sizeof( *this ) + 15 ) & ~15 ) +
GetRuntimeSizeOf( m_Nodes ) +
GetRuntimeSizeOf( m_Vertices ) +
GetRuntimeSizeOf( m_Triangles ) +
GetRuntimeSizeOf( m_Materials );
}
//--------------------------------------------------------------------------------------------------
inline void RnHull_t::Validate( void ) const
{
#ifdef DBGFLAG_ASSERT
Assert( m_flMaxAngularRadius > 0 && m_flMaxAngularRadius < 1e5f && m_vCentroid.Length() < 1e4f && uint( m_Vertices.Count() ) < 256 && uint( m_Planes.Count() ) < 256 && uint( m_Edges.Count() ) < 256 && uint( m_Faces.Count() ) < 256 );
for( int i = 0; i < m_Faces.Count(); ++i )
{
Assert( m_Faces[i].m_nEdge < ( uint )m_Edges.Count() );
}
for( int i = 0; i < m_Edges.Count(); ++i )
{
Assert( m_Edges[i].m_nNext < ( uint )m_Edges.Count() );
Assert( m_Edges[i].m_nTwin < ( uint )m_Edges.Count() );
Assert( m_Edges[i].m_nOrigin < ( uint )m_Vertices.Count() );
Assert( m_Edges[i].m_nFace < ( uint )m_Faces.Count() );
}
#endif
}
//--------------------------------------------------------------------------------------------------
inline void RnMesh_t::Validate( void ) const
{
#ifdef DBGFLAG_ASSERT
Assert( m_vMin.Length() < 1e5f && m_vMax.Length() < 1e5f ); // check saneness
//for( int i = 0; i < m_Nodes.Count(); ++i )
{
}
for( int i = 0; i < m_Vertices.Count(); ++i )
{
Assert( m_Vertices[i].Length() < 1e5f );
}
#endif
}
//--------------------------------------------------------------------------------------------------
// Joint serialization
//--------------------------------------------------------------------------------------------------
struct RnJointDesc_t
{
// Bodies
uint32 m_nBody1;
uint32 m_nBody2;
// Joint frames
Vector m_vOrigin1;
Quaternion m_qBasis1;
Vector m_vOrigin2;
Quaternion m_qBasis2;
// Breakable
float m_flMaxForce;
float m_flMaxTorque;
};
struct RnSphericalDesc_t : public RnJointDesc_t
{
// Angular motor (3D)
Vector m_vTargetVelocity;
float m_flMaxTorque;
};
struct RnUniversalDesc_t : public RnJointDesc_t
{
// Limit
float m_flConeAngle;
};
struct RnRevoluteDesc_t : public RnJointDesc_t
{
// Limit
float m_flMinAngle;
float m_flMaxAngle;
// Angular motor (1D)
float m_flTargetVelocity;
float m_flMaxTorque;
};
struct RnPrismaticDesc_t : public RnJointDesc_t
{
// Limit
float m_flMinDistance;
float m_flMaxDistance;
// Linear motor (1D)
float m_flTargetVelocity;
float m_flMaxForce;
};
struct RnRagdollDesc_t : public RnJointDesc_t
{
// Conical limit with elliptical base
float32 m_flRadiusY;
float32 m_flRadiusZ;
// Angular motor (3D)
Vector m_vTargetVelocity;
float32 m_flMaxTorque;
};
struct rnWeldDesc_t : public RnJointDesc_t
{
// Spring parameters
float m_flFrequency;
float m_flDampingRatio;
};
struct rnPulleyDesc_t : public RnJointDesc_t
{
};
struct rnSpringDesc_t : public RnJointDesc_t
{
// Spring parameters
float32 m_flFrequency;
float32 m_flDampingRatio;
};
schema struct RnSoftbodyParticle_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnSoftbodyParticle_t );
float32 m_flMassInv;
};
schema struct RnSoftbodySpring_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnSoftbodySpring_t );
uint16 m_nParticle[2];
float32 m_flLength;
};
schema struct RnSoftbodyCapsule_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnSoftbodyCapsule_t );
Vector m_vCenter[2];
float32 m_flRadius;
uint16 m_nParticle[2];
};
schema struct ALIGN16 RnBlendVertex_t
{
TYPEMETA( MNoScatter )
DECLARE_SCHEMA_DATA_CLASS( RnBlendVertex_t );
uint16 m_nWeight0;
uint16 m_nIndex0;
uint16 m_nWeight1;
uint16 m_nIndex1;
uint16 m_nWeight2;
uint16 m_nIndex2;
uint16 m_nFlags;
uint16 m_nTargetIndex;
};
inline Vector RnSphere_t::ComputeOrthographicAreas()const
{
return Vector( M_PI/4, M_PI/4, M_PI/4 );
}
// compute the area of 2D capsule with the given 2 centers and the radius
inline float Compute2DCapsuleArea( float x0, float y0, float x1, float y1, float flRadius )
{
// it's really simple: the 2D pill, if you don't think of it as 2 circles and a rectangle, but think of it as 2 half-circles and a rectangle,
// has no overlapping parts and is easy to compute the area of
float flAxisLength = sqrtf( Sqr( x0 - x1 ) + Sqr( y0 - y1 ) );
return M_PI * flRadius * flRadius + flAxisLength * 2 * flRadius;
}
inline float Compute2DCapsuleOrthographicArea( float x0, float y0, float x1, float y1, float flRadius )
{
float flBboxArea = ( fabsf( x0 - x1 ) + 2 * flRadius ) * ( fabsf( y0 - y1 ) + 2 * flRadius ); // the area of bounding rectangle
return Compute2DCapsuleArea( x0, y0, x1, y1, flRadius ) / flBboxArea;
}
inline Vector RnCapsule_t::ComputeOrthographicAreas()const
{
return Vector(
Compute2DCapsuleOrthographicArea( m_vCenter[0].x, m_vCenter[0].y, m_vCenter[1].x, m_vCenter[1].y, m_flRadius ),
Compute2DCapsuleOrthographicArea( m_vCenter[0].y, m_vCenter[0].z, m_vCenter[1].y, m_vCenter[1].z, m_flRadius ),
Compute2DCapsuleOrthographicArea( m_vCenter[0].z, m_vCenter[0].x, m_vCenter[1].z, m_vCenter[1].x, m_flRadius )
);
}
inline bool IsTriangulationValid( int nVertexCount, const CUtlVector<uint32> &indices )
{
if( nVertexCount == 0 )
{
return indices.IsEmpty(); // triangulation is valid iff both indices and vertices are empty
}
// check that indices are referring to all the vertices, and no more
CVarBitVecT<uint32> used( nVertexCount );
for( int i = 0; i < indices.Count(); ++i )
{
if( indices[i] < ( uint )nVertexCount )
{
used.Set( indices[i] );
}
else
{
return false;
}
}
uint nUnusedVerts = 0;
for( int i = 0; i < nVertexCount; ++i )
{
if( !used[ i ] )
{
nUnusedVerts++;
Warning( "%d,", i );
}
}
if ( nUnusedVerts )
{
Warning( " fully %u verts are unused\n", nUnusedVerts );
}
//bool bIsAllSet = used.IsAllSet();
return true; // even if we don't use some verts, the triangulation is still valid; it's just not optimal
}
inline bool IsTriangulationValid( const CUtlVector<Vector> &vertices, const CUtlVector<uint32> &indices )
{
return IsTriangulationValid( vertices.Count(), indices );
}
//--------------------------------------------------------------------------------------------------
// RnHull_t
//--------------------------------------------------------------------------------------------------
inline void RnHull_t::Transform( const matrix3x4_t& transform )
{
m_vCentroid = VectorTransform( m_vCentroid, transform );
for ( int nVertex = 0; nVertex < m_Vertices.Count(); ++nVertex )
{
m_Vertices[ nVertex ] = VectorTransform( m_Vertices[ nVertex ], transform );
}
Vector vOrigin = transform.GetOrigin();
for ( int nPlane = 0; nPlane < m_Planes.Count(); ++nPlane )
{
Vector vNormal = VectorRotate( m_Planes[ nPlane ].m_vNormal, transform );
float flOffset = m_Planes[ nPlane ].m_flOffset + DotProduct( vNormal, vOrigin );
m_Planes[ nPlane ].m_vNormal = vNormal;
m_Planes[ nPlane ].m_flOffset = flOffset;
}
}
//--------------------------------------------------------------------------------------------------
// RnNode_t
//--------------------------------------------------------------------------------------------------
inline int RnNode_t::GetHeight( void ) const
{
if ( IsLeaf() )
{
return 0;
}
const RnNode_t* pLeftChild = GetLeftChild();
int LeftHeight = pLeftChild->GetHeight();
const RnNode_t* pRightChild = GetRightChild();
int RightHeight = pRightChild->GetHeight();
return 1 + MAX( LeftHeight, RightHeight );
}
//--------------------------------------------------------------------------------------------------
// RnMesh_t
//--------------------------------------------------------------------------------------------------
inline int RnMesh_t::GetHeight( void ) const
{
const RnNode_t* Root = GetRoot();
if ( Root == NULL )
{
return 0;
}
return Root->GetHeight();
}
//--------------------------------------------------------------------------------------------------
inline int RnHull_t::GetMemory( void ) const
{
int nMemory = 0;
nMemory += sizeof( RnHull_t );
nMemory += m_Vertices.Count() * sizeof( m_Vertices[0] );
nMemory += m_Planes.Count() * sizeof( m_Planes[0] );
nMemory += m_Edges.Count() * sizeof( m_Edges[0] );
nMemory += m_Faces.Count() * sizeof( m_Faces[0] );
return nMemory;
}
//--------------------------------------------------------------------------------------------------
inline int RnMesh_t::GetMemory( void ) const
{
int nMemory = 0;
nMemory += sizeof( RnMesh_t );
nMemory += m_Nodes.Count() * sizeof( RnNode_t );
nMemory += m_Vertices.Count() * sizeof( Vector );
nMemory += m_Triangles.Count() * sizeof( RnTriangle_t );
nMemory += m_Materials.Count() * sizeof( uint8 );
return nMemory;
}
//--------------------------------------------------------------------------------------------------
inline AABB_t RnSphere_t::GetBbox()const
{
AABB_t b;
b.m_vMinBounds.Init( m_vCenter.x - m_flRadius, m_vCenter.y - m_flRadius, m_vCenter.z - m_flRadius );
b.m_vMaxBounds.Init( m_vCenter.x + m_flRadius, m_vCenter.y + m_flRadius, m_vCenter.z + m_flRadius );
return b;
}
//--------------------------------------------------------------------------------------------------
inline AABB_t RnSphere_t::GetBbox( const CTransform& xform ) const
{
Vector vCenter = TransformPoint( xform, m_vCenter );
Vector vExtent( m_flRadius, m_flRadius, m_flRadius );
Vector vMin = vCenter - vExtent;
Vector vMax = vCenter + vExtent;
return AABB_t( vMin, vMax );
}
//--------------------------------------------------------------------------------------------------
inline int RnSphere_t::GetTriangulation( Vector *pVerts ) const
{
if( pVerts )
{
float flSin[5] = {0,1,0,-1,0};
float flCos[5] = {1,0,-1,0,1};
Vector *pOut = pVerts;
for( int i = 0; i < 4; ++i )
{
float s0 = flSin[i] * m_flRadius, c0 = flCos[i] * m_flRadius, s1 = flSin[i+1] * m_flRadius, c1 = flCos[i+1] * m_flRadius;
*pOut++ = m_vCenter + Vector( -m_flRadius, 0, 0 );
*pOut++ = m_vCenter + Vector( 0, c0, s0 );
*pOut++ = m_vCenter + Vector( 0, c1, s1 );
*pOut++ = m_vCenter + Vector( m_flRadius, 0, 0 );
*pOut++ = m_vCenter + Vector( 0, c1, s1 );
*pOut++ = m_vCenter + Vector( 0, c0, s0 );
}
Assert( 24 == pOut - pVerts );
}
return 24; // approximation with octahedron
}
//--------------------------------------------------------------------------------------------------
inline void RnSphere_t::GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices )const
{
GetTriangulation( vertices, indices, 12, 5 );
}
//--------------------------------------------------------------------------------------------------
inline void RnSphere_t::GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, int nSides, int nSlices )const
{
int nIndexBase = indices.AddMultipleToTail( nSides * 2 * nSlices * 3 );
uint32 *pOut = indices.Base() + nIndexBase;
int nVertBase = vertices.AddMultipleToTail( nSides * nSlices + 2 );
Vector *pVerts = vertices.Base() + nVertBase;
V_memset( pVerts, 0, ( nSides * nSlices + 2 ) * sizeof( Vector ) );
pVerts[ nSides * nSlices + 0 ] = Vector( -m_flRadius, 0, 0 );
pVerts[ nSides * nSlices + 1 ] = Vector( m_flRadius, 0, 0 );
for( int i = 0; i < nSides; ++i )
{
float theta = ( -2 * M_PI * i ) / nSides;
float s0 = m_flRadius * sinf( theta ), c0 = m_flRadius * cosf( theta );
int i1 = ( i + 1 ) % nSides;
// sides
for( int j = 0; ; ++j )
{
float psi = ( j + 1 ) * M_PI / ( nSlices + 1 ) - M_PI / 2, x0 = sinf( psi ), y0 = cosf( psi );
pVerts[ nSlices * i + j ] = Vector( x0 * m_flRadius, c0 * y0, s0 * y0 );
if( j + 1 < nSlices )
{
*pOut++ = nVertBase + nSlices * i1 + j ;
*pOut++ = nVertBase + nSlices * i + j ;
*pOut++ = nVertBase + nSlices * i1 + j + 1;
*pOut++ = nVertBase + nSlices * i1 + j + 1;
*pOut++ = nVertBase + nSlices * i + j ;
*pOut++ = nVertBase + nSlices * i + j + 1;
Assert( nVertBase + nSlices * i + j + 1 < vertices.Count() );
}
else
{
break;
}
}
// the last slice - only triangles
// the end of caps
if( 1 )
{
*pOut++ = nVertBase + nSlices * nSides + 0;
*pOut++ = nVertBase + nSlices * i ;
*pOut++ = nVertBase + nSlices * i1 ;
}
if( 1 )
{
*pOut++ = nVertBase + nSlices * nSides + 1;
*pOut++ = nVertBase + nSlices * i1 + nSlices - 1;
*pOut++ = nVertBase + nSlices * i + nSlices - 1;
}
}
for( int i = 0; i < nSides * nSlices + 2; ++i )
{
pVerts[i] += m_vCenter;
}
//indices.SetCount( pOut - indices.Base() );
Assert( pOut == indices.end() );
AssertDbg( IsTriangulationValid( vertices, indices ) );
}
//--------------------------------------------------------------------------------------------------
inline AABB_t RnCapsule_t::GetBbox() const
{
AABB_t b;
b.m_vMinBounds.Init( Min( m_vCenter[0].x, m_vCenter[1].x ) - m_flRadius, Min( m_vCenter[0].y, m_vCenter[1].y ) - m_flRadius, Min( m_vCenter[0].z, m_vCenter[1].z ) - m_flRadius );
b.m_vMaxBounds.Init( Max( m_vCenter[0].x, m_vCenter[1].x ) + m_flRadius, Max( m_vCenter[0].y, m_vCenter[1].y ) + m_flRadius, Max( m_vCenter[0].z, m_vCenter[1].z ) + m_flRadius );
return b;
}
//--------------------------------------------------------------------------------------------------
inline AABB_t RnCapsule_t::GetBbox( const CTransform& xform ) const
{
Vector vCenter1 = TransformPoint( xform, m_vCenter[ 0 ] );
Vector vCenter2 = TransformPoint( xform, m_vCenter[ 1 ] );
Vector vExtent( m_flRadius, m_flRadius, m_flRadius );
AABB_t aabb1( vCenter1 - vExtent, vCenter1 + vExtent );
AABB_t aabb2( vCenter2 - vExtent, vCenter2 + vExtent );
return aabb1 + aabb2;
}
//--------------------------------------------------------------------------------------------------
inline int RnCapsule_t::GetTriangulation( Vector *pVerts ) const
{
int nSides = 12, nSlices = 2;
if( pVerts )
{
Vector vHeight = m_vCenter[1] - m_vCenter[0];
float flHeight = vHeight.Length();
Vector vAxisX;
if( flHeight > 1e-5f )
{
vAxisX = vHeight / flHeight;
}
else
{
vAxisX = vHeight = Vector( 1,0,0 );
flHeight = 1;
}
Vector vAxisY = VectorPerpendicularToVector( vAxisX ), vAxisZ = CrossProduct( vAxisX, vAxisY );
Vector *pOut = pVerts;
float s0 = 0, c0 = m_flRadius;
for( int i = 0; i < nSides; ++i )
{
float theta = (2 * M_PI * ( i + 1 ) ) / nSides;
float s1 = m_flRadius * sinf( theta ), c1 = m_flRadius * cosf( theta );
// cylinder sides
*pOut++ = Vector( 0, c1, s1 );
*pOut++ = Vector( 0, c0, s0 );
*pOut++ = Vector( flHeight, c1, s1 );
*pOut++ = Vector( flHeight, c1, s1 );
*pOut++ = Vector( 0, c0, s0 );
*pOut++ = Vector( flHeight, c0, s0 );
// caps - quads
float x0 = 0, x1 = 0, y0 = 1, y1 = 0;
for( int j = 0; j < nSlices; ++j )
{
float psi = ( ( j + 1 ) * M_PI / 2 ) / ( nSlices + 1 );
x1 = sinf( psi );
y1 = cosf( psi );
// top cap
*pOut++ = Vector( flHeight + x0 * m_flRadius, c1 * y0, s1 * y0 );
*pOut++ = Vector( flHeight + x0 * m_flRadius, c0 * y0, s0 * y0 );
*pOut++ = Vector( flHeight + x1 * m_flRadius, c1 * y1, s1 * y1 );
*pOut++ = Vector( flHeight + x1 * m_flRadius, c1 * y1, s1 * y1 );
*pOut++ = Vector( flHeight + x0 * m_flRadius, c0 * y0, s0 * y0 );
*pOut++ = Vector( flHeight + x1 * m_flRadius, c0 * y1, s0 * y1 );
// bottom cap
*pOut++ = Vector( -x0 * m_flRadius, c1 * y0, s1 * y0 );
*pOut++ = Vector( -x1 * m_flRadius, c1 * y1, s1 * y1 );
*pOut++ = Vector( -x0 * m_flRadius, c0 * y0, s0 * y0 );
*pOut++ = Vector( -x1 * m_flRadius, c1 * y1, s1 * y1 );
*pOut++ = Vector( -x1 * m_flRadius, c0 * y1, s0 * y1 );
*pOut++ = Vector( -x0 * m_flRadius, c0 * y0, s0 * y0 );
x0 = x1;
y0 = y1;
}
// the end of caps
*pOut++ = Vector( -m_flRadius, 0, 0 );
*pOut++ = Vector( -x1 * m_flRadius, c0 * y1, s0 * y1 );
*pOut++ = Vector( -x1 * m_flRadius, c1 * y1, s1 * y1 );
*pOut++ = Vector( flHeight + m_flRadius, 0, 0 );
*pOut++ = Vector( flHeight + x1 * m_flRadius, c1 * y1, s1 * y1 );
*pOut++ = Vector( flHeight + x1 * m_flRadius, c0 * y1, s0 * y1 );
s0 = s1;
c0 = c1;
}
for( Vector *p = pVerts; p < pOut; ++p )
{
*p = m_vCenter[0] + vAxisX * p->x + vAxisY * p->y + vAxisZ * p->z;
}
Assert( nSides * ( 2 + 2 * ( 2 * nSlices + 1 ) ) * 3 == pOut - pVerts );
}
return nSides * ( 2 + 2 * ( 2 * nSlices + 1 ) ) * 3; // each side is 4 tris, each 3 verts
}
//--------------------------------------------------------------------------------------------------
inline void RnCapsule_t::GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices )const
{
GetTriangulation( vertices, indices, 12, 2 );
}
//--------------------------------------------------------------------------------------------------
inline void RnCapsule_t::GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, int nSides, int nSlices )const
{
int nVertBase = vertices.Count();
int nIndexBase = indices.AddMultipleToTail( nSides * ( 2 + 2 * ( 2 * nSlices + 1 ) ) * 3 ); // each side is 4 tris, each 3 verts
uint32 *pOut = &indices[ nIndexBase ];
int nSideVerts = 2 + 2 * nSlices;
vertices.AddMultipleToTail( nSides * nSideVerts + 2 );
Vector *pVerts = &vertices[nVertBase];
V_memset( pVerts, 0, nSides * nSideVerts * sizeof( Vector ) );
Vector vHeight = m_vCenter[1] - m_vCenter[0];
float flHeight = vHeight.Length();
Vector vAxisX;
if( flHeight > 1e-5f )
{
vAxisX = vHeight / flHeight;
}
else
{
vAxisX = vHeight = Vector( 1,0,0 );
flHeight = 1;
}
Vector vAxisY = VectorPerpendicularToVector( vAxisX ), vAxisZ = CrossProduct( vAxisX, vAxisY );
pVerts[ nSides * nSideVerts + 0 ] = Vector( -m_flRadius, 0, 0 );
pVerts[ nSides * nSideVerts + 1 ] = Vector( flHeight + m_flRadius, 0, 0 );
for( int i = 0; i < nSides; ++i )
{
float theta = ( -2 * M_PI * i ) / nSides;
float s0 = m_flRadius * sinf( theta ), c0 = m_flRadius * cosf( theta );
int i1 = ( i + 1 ) % nSides;
// cylinder sides
*pOut++ = nVertBase + nSideVerts * i1;
*pOut++ = nVertBase + nSideVerts * i;
*pOut++ = nVertBase + nSideVerts * i1 + 1;
*pOut++ = nVertBase + nSideVerts * i1 + 1;
*pOut++ = nVertBase + nSideVerts * i;
*pOut++ = nVertBase + nSideVerts * i + 1;
// caps - quads
for( int j = 0; ; ++j )
{
int j1 = j + 1;
float psi = ( j * M_PI / 2 ) / ( nSlices + 1 ), x0 = sinf( psi ), y0 = cosf( psi );
pVerts[nSideVerts * i + j * 2 + 0 ] = Vector( - x0 * m_flRadius, c0 * y0, s0 * y0 );
pVerts[nSideVerts * i + j * 2 + 1 ] = Vector( flHeight + x0 * m_flRadius, c0 * y0, s0 * y0 );
if( j < nSlices)
{
// top cap
*pOut++ = nVertBase + nSideVerts * i1 + j * 2 + 1;
*pOut++ = nVertBase + nSideVerts * i + j * 2 + 1;
*pOut++ = nVertBase + nSideVerts * i1 + j1 * 2 + 1;
*pOut++ = nVertBase + nSideVerts * i1 + j1 * 2 + 1;
*pOut++ = nVertBase + nSideVerts * i + j * 2 + 1;
*pOut++ = nVertBase + nSideVerts * i + j1 * 2 + 1;
// bottom cap
*pOut++ = nVertBase + nSideVerts * i1 + j * 2 + 0;
*pOut++ = nVertBase + nSideVerts * i1 + j1 * 2 + 0;
*pOut++ = nVertBase + nSideVerts * i + j * 2 + 0;
*pOut++ = nVertBase + nSideVerts * i1 + j1 * 2 + 0;
*pOut++ = nVertBase + nSideVerts * i + j1 * 2 + 0;
*pOut++ = nVertBase + nSideVerts * i + j * 2 + 0;
}
else
{
// the last slice - only triangles
// the end of caps
*pOut++ = nVertBase + nSides * nSideVerts + 0;
*pOut++ = nVertBase + nSideVerts * i + j * 2 + 0;
*pOut++ = nVertBase + nSideVerts * i1 + j * 2 + 0;
*pOut++ = nVertBase + nSides * nSideVerts + 1;
*pOut++ = nVertBase + nSideVerts * i1 + j * 2 + 1;
*pOut++ = nVertBase + nSideVerts * i + j * 2 + 1;
break;
}
}
}
for( int i = 0; i < nSides * nSideVerts + 2; ++i )
{
pVerts[i] = m_vCenter[0] + vAxisX * pVerts[i].x + vAxisY * pVerts[i].y + vAxisZ * pVerts[i].z;
}
Assert( pOut == indices.end() );
AssertDbg( IsTriangulationValid( vertices, indices ) );
}
//--------------------------------------------------------------------------------------------------
inline AABB_t RnHull_t::GetBbox() const
{
// note: we should store the bbox in RnHull if it's a frequent operation to compute its bbox
AABB_t b;
b.MakeInvalid();
for( int nVert = 0; nVert < m_Vertices.Count(); ++nVert )
{
b |= m_Vertices[nVert];
}
return b;
}
//--------------------------------------------------------------------------------------------------
inline AABB_t RnHull_t::GetBbox( const CTransform& xform ) const
{
// note: if we store the bbox in RnHull we could just transform it (which potentially might grow it)
AABB_t b;
b.MakeInvalid();
for ( int nVert = 0; nVert < m_Vertices.Count(); ++nVert )
{
b |= TransformPoint( xform, m_Vertices[ nVert ] );
}
return b;
}
//--------------------------------------------------------------------------------------------------
inline int RnHull_t::GetTriangulation( Vector *pVerts ) const
{
int nVertsOut = 0;
for( int nFace = 0; nFace < m_Faces.Count(); ++nFace )
{
uint nStartEdge = m_Faces[nFace].m_nEdge, nStartVert = m_Edges[nStartEdge].m_nOrigin;
const Vector &vStartEdge = m_Vertices[ nStartVert ];
for( uint nEdge = m_Edges[nStartEdge].m_nNext; nEdge != nStartEdge; )
{
uint nNextEdge = m_Edges[nEdge].m_nNext; // go to next edge
if( nNextEdge == nStartEdge )
{
break; // if next edge is the starting edge, we're done
}
if( pVerts )
{
pVerts[ nVertsOut + 0 ] = vStartEdge;
pVerts[ nVertsOut + 1 ] = m_Vertices[ m_Edges[nEdge].m_nOrigin ];
pVerts[ nVertsOut + 2 ] = m_Vertices[ m_Edges[nNextEdge].m_nOrigin ];
}
nVertsOut += 3;
nEdge = nNextEdge;
}
}
return nVertsOut;
}
//------------------------------------------------------------------------------------------------------------------------------------------------------
inline void RnHull_t::GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, float flScale )const
{
int nVertBase = vertices.Count();
vertices.AddMultipleToTail( GetVertexCount() );
for( int i = 0; i < GetVertexCount(); ++i )
{
vertices[ i + nVertBase ] = GetVertex( i ) * flScale;
}
for ( int i = 0; i < GetFaceCount(); ++i )
{
const RnFace_t* pFace = GetFace( i );
const RnHalfEdge_t* pEdge1 = GetEdge( pFace->m_nEdge );
const RnHalfEdge_t* pEdge2 = GetEdge( pEdge1->m_nNext );
const RnHalfEdge_t* pEdge3 = GetEdge( pEdge2->m_nNext );
AssertDbg( pEdge1 != pEdge3 );
int v1 = nVertBase + pEdge1->m_nOrigin;
do
{
int v2 = nVertBase + pEdge2->m_nOrigin;
int v3 = nVertBase + pEdge3->m_nOrigin;
indices.AddToTail( v1 );
indices.AddToTail( v2 );
indices.AddToTail( v3 );
pEdge2 = pEdge3;
pEdge3 = GetEdge( pEdge3->m_nNext );
}
while ( pEdge1 != pEdge3 );
}
AssertDbg( IsTriangulationValid( vertices, indices ) );
}
//--------------------------------------------------------------------------------------------------
inline AABB_t RnMesh_t::GetBbox()const
{
AABB_t b;
b.m_vMinBounds = m_vMin;
b.m_vMaxBounds = m_vMax;
return b;
}
//------------------------------------------------------------------------------------------------------------------------------------------------------
inline int RnMesh_t::GetTriangulation( Vector *pVerts ) const
{
if( pVerts )
{
for( int nTriangleIndex = 0; nTriangleIndex < m_Triangles.Count(); ++nTriangleIndex )
{
const RnTriangle_t &tri = m_Triangles[nTriangleIndex];
for( int nVertInTri = 0; nVertInTri < 3; ++nVertInTri )
{
pVerts[ nTriangleIndex * 3 + nVertInTri ] = m_Vertices[ tri.m_nIndex[ nVertInTri ] ];
}
}
}
return m_Triangles.Count() * 3;
}
//------------------------------------------------------------------------------------------------------------------------------------------------------
inline void RnMesh_t::GetTriangulation( CUtlVector<Vector> &vertices, CUtlVector<uint32> &indices, const Vector &vScale ) const
{
int nVertBase = vertices.Count();
vertices.AddMultipleToTail( GetVertexCount() );
for ( int i = 0; i < GetVertexCount(); ++i )
{
Vector vertex = vScale * GetVertex( i );
vertices[ nVertBase + i ] = vertex;
}
indices.EnsureCapacity( 3 * GetTriangleCount() );
for ( int i = 0; i < GetTriangleCount(); ++i )
{
const RnTriangle_t* pTriangle = GetTriangle( i );
indices.AddToTail( nVertBase + pTriangle->m_nIndex[ 0 ] );
indices.AddToTail( nVertBase + pTriangle->m_nIndex[ 1 ] );
indices.AddToTail( nVertBase + pTriangle->m_nIndex[ 2 ] );
}
AssertDbg( IsTriangulationValid( vertices, indices ) );
}
//------------------------------------------------------------------------------------------------------------------------------------------------------
inline float RnSphere_t::GetVolume()const
{
return m_flRadius * m_flRadius * m_flRadius * M_PI * ( 4.0f / 3.0f ); // http://en.wikipedia.org/wiki/Sphere#Volume_of_a_sphere
}
//------------------------------------------------------------------------------------------------------------------------------------------------------
inline float RnCapsule_t::GetVolume()const
{
return m_flRadius * m_flRadius * M_PI * ( m_flRadius * ( 4.0f / 3.0f ) + ( m_vCenter[0] - m_vCenter[1] ).Length() ); // http://en.wikipedia.org/wiki/Sphere#Volume_of_a_sphere
}
//------------------------------------------------------------------------------------------------------------------------------------------------------
inline float RnHull_t::GetVolume()const
{
float flVolume = 0;
for( int nFace = 0; nFace < m_Faces.Count(); ++nFace )
{
uint nStartEdge = m_Faces[nFace].m_nEdge, nStartVert = m_Edges[nStartEdge].m_nOrigin;
const Vector &vStartEdge = m_Vertices[ nStartVert ];
for( uint nEdge = m_Edges[nStartEdge].m_nNext; nEdge != nStartEdge; )
{
uint nNextEdge = m_Edges[nEdge].m_nNext; // go to next edge
if( nNextEdge == nStartEdge )
{
break; // if next edge is the starting edge, we're done
}
Vector v1 = m_Vertices[ m_Edges[nEdge].m_nOrigin ];
Vector v2 = m_Vertices[ m_Edges[nNextEdge].m_nOrigin ];
float flCenterZHalf = ( vStartEdge.z + v1.z + v2.z ) * ( 1.0f / 6.0f ); // half-height of the prism, to account for cross product being double the area of base
float flSignedDoubleBaseArea = CrossProductZ( v1 - vStartEdge, v2 - vStartEdge );
flVolume += flCenterZHalf * flSignedDoubleBaseArea;
nEdge = nNextEdge;
}
}
Assert( flVolume > 0 );
return flVolume;
}
FORCEINLINE const Vector RnMesh_t::ComputeTriangleUnitNormal( const Vector &vScale, int nTriIndex )const
{
const RnTriangle_t* pTriangle = GetTriangle( nTriIndex );
const Vector& vVertex1 = GetVertex( pTriangle->m_nIndex[ 0 ] );
const Vector& vVertex2 = GetVertex( pTriangle->m_nIndex[ 1 ] );
const Vector& vVertex3 = GetVertex( pTriangle->m_nIndex[ 2 ] );
Vector vEdge1 = ScaleVector( vScale, vVertex2 - vVertex1 );
Vector vEdge2 = ScaleVector( vScale, vVertex3 - vVertex1 );
Vector vNormal = CrossProduct( vEdge1, vEdge2 );
return vNormal / vNormal.Length(); // valid RnMesh should never have degenerate triangles
}
FORCEINLINE const Vector RnMesh_t::ComputeTriangleCentroid( int nTriIndex )const
{
const RnTriangle_t* pTriangle = GetTriangle( nTriIndex );
const Vector& vVertex1 = GetVertex( pTriangle->m_nIndex[ 0 ] );
const Vector& vVertex2 = GetVertex( pTriangle->m_nIndex[ 1 ] );
const Vector& vVertex3 = GetVertex( pTriangle->m_nIndex[ 2 ] );
return ( vVertex1 + vVertex2 + vVertex3 ) * ( 1.0f / 3.0f );
}
FORCEINLINE const Vector RnMesh_t::ComputeTriangleIncenter( int nTriIndex )const
{
const RnTriangle_t* pTriangle = GetTriangle( nTriIndex );
const Vector& a = GetVertex( pTriangle->m_nIndex[ 0 ] );
const Vector& b = GetVertex( pTriangle->m_nIndex[ 1 ] );
const Vector& c = GetVertex( pTriangle->m_nIndex[ 2 ] );
float la = ( b - c ).Length(), lb = ( a - c ).Length(), lc = ( b - a ).Length();
return ( a * la + b * lb + c * lc ) / ( la + lb + lc );
}
FORCEINLINE const Vector RnHull_t::ComputeFaceCentroid( int nFace )const
{
Vector vSum( 0,0,0 );
uint8 nFirstEdge = m_Faces[nFace].m_nEdge, nEdge = nFirstEdge;
float flNumEdges = 0;
do
{
AssertDbg( m_Edges[nEdge].m_nFace == uint( nFace ) );
vSum += m_Vertices[ m_Edges[nEdge].m_nOrigin ];
nEdge = m_Edges[ nEdge ].m_nNext;
flNumEdges += 1.0f;
} while ( nEdge != nFirstEdge );
return vSum / flNumEdges;
}
//--------------------------------------------------------------------------------------------------
// SIMD clipping
//--------------------------------------------------------------------------------------------------
FORCEINLINE fltx4 HMaxSIMD( fltx4 a )
{
fltx4 b = RotateLeft( a );
fltx4 c = MaxSIMD( a, b );
fltx4 d = RotateLeft2( c );
return MaxSIMD( c, d );
}
//-------------------------------------------------------------------------------------------------
FORCEINLINE fltx4 HMinSIMD( fltx4 a )
{
fltx4 b = RotateLeft( a );
fltx4 c = MinSIMD( a, b );
fltx4 d = RotateLeft2( c );
return MinSIMD( c, d );
}
//-------------------------------------------------------------------------------------------------
FORCEINLINE int ClipRaySIMD( const RnRay_t& ray, const Vector& vMin, const Vector& vMax, float &flBestTime )
{
fltx4 f4Min = LoadAlignedSIMD( &vMin.x ), f4Max = LoadAlignedSIMD( &vMax.x );
fltx4 f4Origin = LoadAlignedSIMD( &ray.vOrigin ), f4DeltaInv = LoadAlignedSIMD( &ray.vDeltaInv );
fltx4 t1 = ( f4Min - f4Origin ) * f4DeltaInv; // can be MADD
fltx4 t2 = ( f4Max - f4Origin ) * f4DeltaInv; // can be MADD
fltx4 f4TMin = MinSIMD( t1, t2 ), f4TMax = MaxSIMD( t1, t2 );
fltx4 f4MinT = HMaxSIMD( SetWSIMD( f4TMin, Four_Zeros ) );
fltx4 f4MaxT = HMinSIMD( SetWSIMD( f4TMax, ReplicateX4( flBestTime ) ) );
// flMinT = max( 0, f4TMin.xyz )
// flMaxT = min( 1, f4TMin.xyz )
// return ( flMinT > flMaxT || flMinT > flBestTime )
return _mm_comigt_ss( f4MinT, f4MaxT );
}
//-------------------------------------------------------------------------------------------------
FORCEINLINE int ClipRaySIMD( const RnRay_t& ray, const fltx4 &f4Min , const fltx4& f4Max, float &flBestTime )
{
fltx4 f4Origin = LoadAlignedSIMD( &ray.vOrigin ), f4DeltaInv = LoadAlignedSIMD( &ray.vDeltaInv );
fltx4 t1 = ( f4Min - f4Origin ) * f4DeltaInv; // can be MADD
fltx4 t2 = ( f4Max - f4Origin ) * f4DeltaInv; // can be MADD
fltx4 f4TMin = MinSIMD( t1, t2 ), f4TMax = MaxSIMD( t1, t2 );
fltx4 f4MinT = HMaxSIMD( SetWSIMD( f4TMin, Four_Zeros ) );
fltx4 f4MaxT = HMinSIMD( SetWSIMD( f4TMax, ReplicateX4( flBestTime ) ) );
// flMinT = max( 0, f4TMin.xyz )
// flMaxT = min( 1, f4TMin.xyz )
// return ( flMinT > flMaxT || flMinT > flBestTime )
return _mm_comigt_ss( f4MinT, f4MaxT );
}
//-------------------------------------------------------------------------------------------------
FORCEINLINE int ClipRaySIMD( const RnRay_t& ray, const fltx4& f4Min, const fltx4& f4Max )
{
fltx4 f4Origin = LoadAlignedSIMD( &ray.vOrigin ), f4DeltaInv = LoadAlignedSIMD( &ray.vDeltaInv );
fltx4 t1 = ( f4Min - f4Origin ) * f4DeltaInv; // can be MADD
fltx4 t2 = ( f4Max - f4Origin ) * f4DeltaInv; // can be MADD
fltx4 f4TMin = MinSIMD( t1, t2 ), f4TMax = MaxSIMD( t1, t2 );
fltx4 f4MinT = HMaxSIMD( SetWSIMD( f4TMin, Four_Zeros ) );
fltx4 f4MaxT = HMinSIMD( SetWSIMD( f4TMax, Four_Ones ) );
// flMinT = max( 0, f4TMin.xyz )
// flMaxT = min( 1, f4TMin.xyz )
// return ( flMinT > flMaxT || flMinT > flBestTime )
return _mm_comigt_ss( f4MinT, f4MaxT );
}
//-------------------------------------------------------------------------------------------------
template < typename Functor >
void RnMesh_t::CastBox( const Functor &callback, const RnRay_t &ray, const Vector& vLocalExtent, float flMaxFraction ) const
{
int nCount = 0;
const RnNode_t* stack[ STACK_SIZE ];
stack[ nCount++ ] = GetRoot();
fltx4 f4LocalExtent = LoadUnalignedSIMD( &vLocalExtent );
while ( nCount > 0 )
{
const RnNode_t* pNode = stack[ --nCount ];
if( ClipRaySIMD( ray, LoadAlignedSIMD( &pNode->m_vMin ) - f4LocalExtent, LoadAlignedSIMD( &pNode->m_vMax ) + f4LocalExtent ) )
{
continue;
}
if ( !pNode->IsLeaf() )
{
// Determine traversal order (front -> back)
// The near and far child are determined by the direction of
// the ray with respect to the dimension of the split plane.
const RnNode_t *pLeft = pNode->GetLeftChild(), *pRight = pNode->GetRightChild();
if ( ray.vDelta[ pNode->GetAxis() ] > 0.0f )
{
stack[ nCount++ ] = pRight;
stack[ nCount++ ] = pLeft;
}
else
{
stack[ nCount++ ] = pLeft;
stack[ nCount++ ] = pRight;
}
AssertDbg( nCount < STACK_SIZE - 1 ); // there should always be space for at least 1 more entry in the stack
}
else
{
uint32 nTriangleCount = pNode->GetTriangleCount();
uint32 nTriangleOffset = pNode->GetTriangleOffset();
for ( uint32 nTriangle = 0; nTriangle < nTriangleCount; ++nTriangle )
{
// Get the triangle in world coordinates
const RnTriangle_t* pTriangle = GetTriangle( nTriangleOffset + nTriangle );
const Vector &vVertex1 = GetVertex( pTriangle->m_nIndex[ 0 ] );
const Vector &vVertex2 = GetVertex( pTriangle->m_nIndex[ 1 ] );
const Vector &vVertex3 = GetVertex( pTriangle->m_nIndex[ 2 ] );
callback( vVertex1, vVertex2, vVertex3 );
}
}
}
}
template <typename Array>
inline bool ArePlainArraysEqual( const Array &a, const Array &b )
{
return a.Count( ) == b.Count( ) && 0 == V_memcmp( a.Base( ), b.Base( ), a.Count( ) * sizeof( a[ 0 ] ) );
}
template <typename Array>
inline void CRC64_ProcessArray( CRC64_t *pCrc, const Array &array )
{
CRC64_ProcessBuffer( pCrc, array.Base( ), array.Count( ) * sizeof( array[ 0 ] ) );
}
class CRnEqualFunctor
{
public:
bool operator() ( const RnHull_t *pLeft, const RnHull_t *pRight )const
{
return ArePlainArraysEqual( pLeft->m_Vertices, pRight->m_Vertices )
&& ArePlainArraysEqual( pLeft->m_Planes, pRight->m_Planes )
&& ArePlainArraysEqual( pLeft->m_Edges, pRight->m_Edges )
&& ArePlainArraysEqual( pLeft->m_Faces, pRight->m_Faces );
}
bool operator() ( const RnMesh_t *pLeft, const RnMesh_t *pRight )const
{
return ArePlainArraysEqual( pLeft->m_Vertices, pRight->m_Vertices )
&& ArePlainArraysEqual( pLeft->m_Nodes, pRight->m_Nodes )
&& ArePlainArraysEqual( pLeft->m_Triangles, pRight->m_Triangles )
&& ArePlainArraysEqual( pLeft->m_Materials, pRight->m_Materials );
}
};
class CRnHashFunctor
{
public:
uint operator()( const RnHull_t *pHull )const
{
CRC64_t nCrc;
CRC64_Init( &nCrc );
CRC64_ProcessArray( &nCrc, pHull->m_Vertices );
CRC64_ProcessArray( &nCrc, pHull->m_Planes );
CRC64_ProcessArray( &nCrc, pHull->m_Edges );
CRC64_ProcessArray( &nCrc, pHull->m_Faces );
CRC64_Final( &nCrc );
return nCrc;
}
uint operator()( const RnMesh_t *pHull )const
{
CRC64_t nCrc;
CRC64_Init( &nCrc );
CRC64_ProcessArray( &nCrc, pHull->m_Vertices );
CRC64_ProcessArray( &nCrc, pHull->m_Nodes );
CRC64_ProcessArray( &nCrc, pHull->m_Triangles );
CRC64_ProcessArray( &nCrc, pHull->m_Materials );
CRC64_Final( &nCrc );
return nCrc;
}
};
#endif