//----------------------------------------------------------------------------- // Computes the surrounding collision bounds from the current sequence box //----------------------------------------------------------------------------- void CCollisionProperty::ComputeRotationExpandedSequenceBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) { CBaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); if ( !pAnim ) { ComputeOBBBounds( pVecWorldMins, pVecWorldMaxs ); return; } Vector mins, maxs; pAnim->ExtractBbox( pAnim->GetSequence(), mins, maxs ); float flRadius = MAX( MAX( FloatMakePositive( mins.x ), FloatMakePositive( maxs.x ) ), MAX( FloatMakePositive( mins.y ), FloatMakePositive( maxs.y ) ) ); mins.x = mins.y = -flRadius; maxs.x = maxs.y = flRadius; // Add bloat to account for gesture sequences Vector vecBloat( 6, 6, 0 ); mins -= vecBloat; maxs += vecBloat; // NOTE: This is necessary because the server doesn't know how to blend // animations together. Therefore, we have to just pick a box that can // surround all of our potential sequences. This should be something we // should be able to compute @ tool time instead, however. VectorMin( mins, m_vecSurroundingMins, mins ); VectorMax( maxs, m_vecSurroundingMaxs, maxs ); VectorAdd( mins, GetCollisionOrigin(), *pVecWorldMins ); VectorAdd( maxs, GetCollisionOrigin(), *pVecWorldMaxs ); }
//----------------------------------------------------------------------------- // Transforms an AABB measured in entity space to a box that surrounds it in world space //----------------------------------------------------------------------------- void CCollisionProperty::CollisionAABBToWorldAABB( const Vector &entityMins, const Vector &entityMaxs, Vector *pWorldMins, Vector *pWorldMaxs ) const { if ( !IsBoundsDefinedInEntitySpace() || (GetCollisionAngles() == vec3_angle) ) { VectorAdd( entityMins, GetCollisionOrigin(), *pWorldMins ); VectorAdd( entityMaxs, GetCollisionOrigin(), *pWorldMaxs ); } else { TransformAABB( CollisionToWorldTransform(), entityMins, entityMaxs, *pWorldMins, *pWorldMaxs ); } }
//----------------------------------------------------------------------------- // Updates the spatial partition //----------------------------------------------------------------------------- void CCollisionProperty::UpdatePartition( ) { if ( m_pOuter->IsEFlagSet( EFL_DIRTY_SPATIAL_PARTITION ) ) { m_pOuter->RemoveEFlags( EFL_DIRTY_SPATIAL_PARTITION ); #ifndef CLIENT_DLL Assert( m_pOuter->entindex() != 0 ); // Don't bother with deleted things if ( !m_pOuter->edict() ) return; if ( GetPartitionHandle() == PARTITION_INVALID_HANDLE ) { CreatePartitionHandle(); UpdateServerPartitionMask(); } #else if ( GetPartitionHandle() == PARTITION_INVALID_HANDLE ) return; #endif // We don't need to bother if it's not a trigger or solid if ( IsSolid() || IsSolidFlagSet( FSOLID_TRIGGER ) || m_pOuter->IsEFlagSet( EFL_USE_PARTITION_WHEN_NOT_SOLID ) ) { // Bloat a little bit... if ( BoundingRadius() != 0.0f ) { Vector vecSurroundMins, vecSurroundMaxs; WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs ); vecSurroundMins -= Vector( 1, 1, 1 ); vecSurroundMaxs += Vector( 1, 1, 1 ); partition->ElementMoved( GetPartitionHandle(), vecSurroundMins, vecSurroundMaxs ); } else { partition->ElementMoved( GetPartitionHandle(), GetCollisionOrigin(), GetCollisionOrigin() ); } } } }
//----------------------------------------------------------------------------- // Compute the largest dot product of the OBB and the specified direction vector //----------------------------------------------------------------------------- float CCollisionProperty::ComputeSupportMap( const Vector &vecDirection ) const { Vector vecCollisionDir; WorldDirectionToCollisionSpace( vecDirection, &vecCollisionDir ); float flResult = DotProduct( GetCollisionOrigin(), vecDirection ); flResult += (( vecCollisionDir.x >= 0.0f ) ? m_vecMaxs.Get().x : m_vecMins.Get().x) * vecCollisionDir.x; flResult += (( vecCollisionDir.y >= 0.0f ) ? m_vecMaxs.Get().y : m_vecMins.Get().y) * vecCollisionDir.y; flResult += (( vecCollisionDir.z >= 0.0f ) ? m_vecMaxs.Get().z : m_vecMins.Get().z) * vecCollisionDir.z; return flResult; }
const Vector &CECollisionProperty::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 point in OBB space to world space //----------------------------------------------------------------------------- const Vector &CECollisionProperty::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; }
//----------------------------------------------------------------------------- // Expand trigger bounds.. //----------------------------------------------------------------------------- void CCollisionProperty::ComputeVPhysicsSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) { bool bSetBounds = false; IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject(); if ( pPhysicsObject ) { if ( pPhysicsObject->GetCollide() ) { physcollision->CollideGetAABB( pVecWorldMins, pVecWorldMaxs, pPhysicsObject->GetCollide(), GetCollisionOrigin(), GetCollisionAngles() ); bSetBounds = true; } else if ( pPhysicsObject->GetSphereRadius( ) ) { float flRadius = pPhysicsObject->GetSphereRadius( ); Vector vecExtents( flRadius, flRadius, flRadius ); VectorSubtract( GetCollisionOrigin(), vecExtents, *pVecWorldMins ); VectorAdd( GetCollisionOrigin(), vecExtents, *pVecWorldMaxs ); bSetBounds = true; } } if ( !bSetBounds ) { *pVecWorldMins = GetCollisionOrigin(); *pVecWorldMaxs = *pVecWorldMins; } // Also, lets expand for the trigger bounds also if ( IsSolidFlagSet( FSOLID_USE_TRIGGER_BOUNDS ) ) { Vector vecWorldTriggerMins, vecWorldTriggerMaxs; WorldSpaceTriggerBounds( &vecWorldTriggerMins, &vecWorldTriggerMaxs ); VectorMin( vecWorldTriggerMins, *pVecWorldMins, *pVecWorldMins ); VectorMax( vecWorldTriggerMaxs, *pVecWorldMaxs, *pVecWorldMaxs ); } }
const matrix3x4_t& CCollisionProperty::CollisionToWorldTransform() const { static matrix3x4_t s_matTemp[4]; static int s_nIndex = 0; matrix3x4_t &matResult = s_matTemp[s_nIndex]; s_nIndex = (s_nIndex+1) & 0x3; if ( IsBoundsDefinedInEntitySpace() ) { return m_pOuter->EntityToWorldTransform(); } SetIdentityMatrix( matResult ); MatrixSetColumn( GetCollisionOrigin(), 3, matResult ); return matResult; }
//----------------------------------------------------------------------------- // Computes the surrounding collision bounds based on whatever algorithm we want... //----------------------------------------------------------------------------- void CCollisionProperty::WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs ) { const Vector &vecAbsOrigin = GetCollisionOrigin(); if ( GetOuter()->IsEFlagSet( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS )) { GetOuter()->RemoveEFlags( EFL_DIRTY_SURROUNDING_COLLISION_BOUNDS ); ComputeSurroundingBox( pVecMins, pVecMaxs ); VectorSubtract( *pVecMins, vecAbsOrigin, m_vecSurroundingMins ); VectorSubtract( *pVecMaxs, vecAbsOrigin, m_vecSurroundingMaxs ); ASSERT_COORD( m_vecSurroundingMins ); ASSERT_COORD( m_vecSurroundingMaxs ); } else { VectorAdd( m_vecSurroundingMins, vecAbsOrigin, *pVecMins ); VectorAdd( m_vecSurroundingMaxs, vecAbsOrigin, *pVecMaxs ); } }
//----------------------------------------------------------------------------- // Computes the surrounding collision bounds based on whatever algorithm we want... //----------------------------------------------------------------------------- void CCollisionProperty::ComputeSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) { if (( GetSolid() == SOLID_CUSTOM ) && (m_nSurroundType != USE_GAME_CODE )) { // NOTE: This can only happen in transition periods, say during network // reception on the client. We expect USE_GAME_CODE to be used with SOLID_CUSTOM *pVecWorldMins = GetCollisionOrigin(); *pVecWorldMaxs = *pVecWorldMins; return; } switch( m_nSurroundType ) { case USE_OBB_COLLISION_BOUNDS: { Assert( GetSolid() != SOLID_CUSTOM ); bool bUseVPhysics = false; if ( ( GetSolid() == SOLID_VPHYSICS ) && ( GetOuter()->GetMoveType() == MOVETYPE_VPHYSICS ) ) { // UNDONE: This may not be necessary any more. IPhysicsObject *pPhysics = GetOuter()->VPhysicsGetObject(); bUseVPhysics = pPhysics && pPhysics->IsAsleep(); } ComputeCollisionSurroundingBox( bUseVPhysics, pVecWorldMins, pVecWorldMaxs ); } break; case USE_BEST_COLLISION_BOUNDS: Assert( GetSolid() != SOLID_CUSTOM ); ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), pVecWorldMins, pVecWorldMaxs ); break; case USE_COLLISION_BOUNDS_NEVER_VPHYSICS: Assert( GetSolid() != SOLID_CUSTOM ); ComputeCollisionSurroundingBox( false, pVecWorldMins, pVecWorldMaxs ); break; case USE_HITBOXES: ComputeHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs ); break; case USE_ROTATION_EXPANDED_BOUNDS: ComputeRotationExpandedBounds( pVecWorldMins, pVecWorldMaxs ); break; case USE_SPECIFIED_BOUNDS: VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMins, *pVecWorldMins ); VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMaxs, *pVecWorldMaxs ); break; case USE_GAME_CODE: GetOuter()->ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs ); Assert( pVecWorldMins->x <= pVecWorldMaxs->x ); Assert( pVecWorldMins->y <= pVecWorldMaxs->y ); Assert( pVecWorldMins->z <= pVecWorldMaxs->z ); return; } #ifdef DEBUG /* // For debugging purposes, make sure the bounds actually does surround the thing. // Otherwise the optimization we were using isn't really all that great, is it? Vector vecTestMins, vecTestMaxs; ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), &vecTestMins, &vecTestMaxs ); // Now that we have the basics, let's expand for hitboxes if appropriate Vector vecWorldHitboxMins, vecWorldHitboxMaxs; if ( ComputeHitboxSurroundingBox( &vecWorldHitboxMins, &vecWorldHitboxMaxs ) ) { VectorMin( vecWorldHitboxMaxs, vecTestMins, vecTestMins ); VectorMax( vecWorldHitboxMaxs, vecTestMaxs, vecTestMaxs ); } Assert( vecTestMins.x >= pVecWorldMins->x && vecTestMins.y >= pVecWorldMins->y && vecTestMins.z >= pVecWorldMins->z ); Assert( vecTestMaxs.x <= pVecWorldMaxs->x && vecTestMaxs.y <= pVecWorldMaxs->y && vecTestMaxs.z <= pVecWorldMaxs->z ); */ #endif }
void CCollisionProperty::ComputeSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) { if (( GetSolid() == SOLID_CUSTOM ) && (m_nSurroundType != USE_GAME_CODE )) { // NOTE: This can only happen in transition periods, say during network // reception on the client. We expect USE_GAME_CODE to be used with SOLID_CUSTOM *pVecWorldMins = GetCollisionOrigin(); *pVecWorldMaxs = *pVecWorldMins; return; } switch( m_nSurroundType ) { case USE_OBB_COLLISION_BOUNDS: Assert( GetSolid() != SOLID_CUSTOM ); ComputeOBBBounds( pVecWorldMins, pVecWorldMaxs ); break; case USE_BEST_COLLISION_BOUNDS: Assert( GetSolid() != SOLID_CUSTOM ); ComputeCollisionSurroundingBox( (GetSolid() == SOLID_VPHYSICS), pVecWorldMins, pVecWorldMaxs ); break; case USE_ROTATION_EXPANDED_SEQUENCE_BOUNDS: ComputeRotationExpandedSequenceBounds( pVecWorldMins, pVecWorldMaxs ); break; case USE_COLLISION_BOUNDS_NEVER_VPHYSICS: Assert( GetSolid() != SOLID_CUSTOM ); ComputeCollisionSurroundingBox( false, pVecWorldMins, pVecWorldMaxs ); break; case USE_HITBOXES: ComputeHitboxSurroundingBox( pVecWorldMins, pVecWorldMaxs ); break; case USE_ROTATION_EXPANDED_BOUNDS: ComputeRotationExpandedBounds( pVecWorldMins, pVecWorldMaxs ); break; case USE_SPECIFIED_BOUNDS: VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMins, *pVecWorldMins ); VectorAdd( GetCollisionOrigin(), m_vecSpecifiedSurroundingMaxs, *pVecWorldMaxs ); break; case USE_GAME_CODE: GetOuter()->ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs ); Assert( pVecWorldMins->x <= pVecWorldMaxs->x ); Assert( pVecWorldMins->y <= pVecWorldMaxs->y ); Assert( pVecWorldMins->z <= pVecWorldMaxs->z ); return; } //#ifdef DEBUG #ifdef CLIENT_DLL if ( cl_show_bounds_errors.GetBool() && ( m_nSurroundType == USE_ROTATION_EXPANDED_SEQUENCE_BOUNDS ) ) { // For debugging purposes, make sure the bounds actually does surround the thing. // Otherwise the optimization we were using isn't really all that great, is it? Vector vecTestMins, vecTestMaxs; if ( GetOuter()->GetBaseAnimating() ) { GetOuter()->GetBaseAnimating()->InvalidateBoneCache(); } ComputeHitboxSurroundingBox( &vecTestMins, &vecTestMaxs ); Assert( vecTestMins.x >= pVecWorldMins->x && vecTestMins.y >= pVecWorldMins->y && vecTestMins.z >= pVecWorldMins->z ); Assert( vecTestMaxs.x <= pVecWorldMaxs->x && vecTestMaxs.y <= pVecWorldMaxs->y && vecTestMaxs.z <= pVecWorldMaxs->z ); if ( vecTestMins.x < pVecWorldMins->x || vecTestMins.y < pVecWorldMins->y || vecTestMins.z < pVecWorldMins->z || vecTestMaxs.x > pVecWorldMaxs->x || vecTestMaxs.y > pVecWorldMaxs->y || vecTestMaxs.z > pVecWorldMaxs->z ) { const char *pSeqName = "<unknown seq>"; C_BaseAnimating *pAnim = GetOuter()->GetBaseAnimating(); if ( pAnim ) { int nSequence = pAnim->GetSequence(); pSeqName = pAnim->GetSequenceName( nSequence ); } Warning( "*** Bounds problem, index %d Eng %s, Seqeuence %s ", GetOuter()->entindex(), GetOuter()->GetClassname(), pSeqName ); Vector vecDelta = *pVecWorldMins - vecTestMins; Vector vecDelta2 = vecTestMaxs - *pVecWorldMaxs; if ( vecDelta.x > 0.0f || vecDelta2.x > 0.0f || vecDelta.y > 0.0f || vecDelta2.y > 0.0f ) { Msg( "Outside X/Y by %.2f ", MAX( MAX( vecDelta.x, vecDelta2.x ), MAX( vecDelta.y, vecDelta2.y ) ) ); } if ( vecDelta.z > 0.0f || vecDelta2.z > 0.0f ) { Msg( "Outside Z by (below) %.2f, (above) %.2f ", MAX( vecDelta.z, 0.0f ), MAX( vecDelta2.z, 0.0f ) ); } Msg( "\n" ); char pTemp[MAX_PATH]; Q_snprintf( pTemp, sizeof(pTemp), "%s [seq: %s]", GetOuter()->GetClassname(), pSeqName ); debugoverlay->AddBoxOverlay( vec3_origin, vecTestMins, vecTestMaxs, vec3_angle, 255, 0, 0, 0, 2 ); debugoverlay->AddBoxOverlay( vec3_origin, *pVecWorldMins, *pVecWorldMaxs, vec3_angle, 0, 0, 255, 0, 2 ); debugoverlay->AddTextOverlay( ( vecTestMins + vecTestMaxs ) * 0.5f, 2, pTemp ); } } #endif //#endif }