|
|
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "physics_prop_statue.h"
#include "baseanimating.h"
#include "studio.h"
#include "bone_setup.h"
#include "EntityFreezing.h"
//#include "particle_parse.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Networking
//-----------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS( physics_prop_statue, CStatueProp );
IMPLEMENT_SERVERCLASS_ST( CStatueProp, DT_StatueProp ) SendPropEHandle( SENDINFO( m_hInitBaseAnimating ) ), SendPropBool( SENDINFO( m_bShatter ) ), SendPropInt( SENDINFO( m_nShatterFlags ), 3 ), SendPropVector( SENDINFO( m_vShatterPosition ) ), SendPropVector( SENDINFO( m_vShatterForce ) ), END_SEND_TABLE()
BEGIN_DATADESC( CStatueProp ) DEFINE_FIELD( m_hInitBaseAnimating, FIELD_EHANDLE ), DEFINE_FIELD( m_bShatter, FIELD_BOOLEAN ), DEFINE_FIELD( m_nShatterFlags, FIELD_INTEGER ), DEFINE_FIELD( m_vShatterPosition, FIELD_VECTOR ), DEFINE_FIELD( m_vShatterForce, FIELD_VECTOR ),
DEFINE_THINKFUNC( CollisionPartnerThink ), END_DATADESC()
ConVarRef *s_vcollide_wireframe = NULL;
CStatueProp::CStatueProp( void ) { static ConVarRef vcollide_wireframe( "vcollide_wireframe" ); s_vcollide_wireframe = &vcollide_wireframe; m_pInitOBBs = NULL; }
void CStatueProp::Spawn( void ) { // Make it breakable
SetBreakableModel( MAKE_STRING( "ConcreteChunks" ) ); SetBreakableCount( 6 ); SetHealth( 5 );
BaseClass::Spawn();
m_flFrozen = 1.0f; }
void CStatueProp::Precache( void ) { }
bool CStatueProp::CreateVPhysics( void ) { if ( m_pInitOBBs ) { return CreateVPhysicsFromOBBs( m_hInitBaseAnimating ); } else { if ( !CreateVPhysicsFromHitBoxes( m_hInitBaseAnimating ) ) { // Init model didn't work out, so just use our own
return CreateVPhysicsFromHitBoxes( this ); }
return true; } }
void CStatueProp::VPhysicsUpdate( IPhysicsObject *pPhysics ) { BaseClass::VPhysicsUpdate( pPhysics );
if ( s_vcollide_wireframe->GetBool() ) { const CPhysCollide *pCollide = pPhysics->GetCollide();
Vector vecOrigin; QAngle angAngles;
pPhysics->GetPosition( &vecOrigin, &angAngles );
if ( pCollide ) { Vector *outVerts; int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts ); int triCount = vertCount / 3; int vert = 0;
VMatrix tmp = SetupMatrixOrgAngles( vecOrigin, angAngles ); int i; for ( i = 0; i < vertCount; i++ ) { outVerts[i] = tmp.VMul4x3( outVerts[i] ); }
for ( i = 0; i < triCount; i++ ) { NDebugOverlay::Line( outVerts[ vert + 0 ], outVerts[ vert + 1 ], 0, 255, 255, false, 0.0f ); NDebugOverlay::Line( outVerts[ vert + 1 ], outVerts[ vert + 2 ], 0, 255, 255, false, 0.0f ); NDebugOverlay::Line( outVerts[ vert + 2 ], outVerts[ vert + 0 ], 0, 255, 255, false, 0.0f ); vert += 3; }
physcollision->DestroyDebugMesh( vertCount, outVerts ); } } }
void CStatueProp::ComputeWorldSpaceSurroundingBox( Vector *pMins, Vector *pMaxs ) { CBaseAnimating *pBaseAnimating = m_hInitBaseAnimating;
if ( pBaseAnimating ) { pBaseAnimating->CollisionProp()->WorldSpaceSurroundingBounds( pMins, pMaxs ); return; }
CollisionProp()->WorldSpaceSurroundingBounds( pMins, pMaxs ); }
bool CStatueProp::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) { IPhysicsObject *pPhysObject = VPhysicsGetObject();
if ( pPhysObject ) { Vector vecPosition; QAngle vecAngles; pPhysObject->GetPosition( &vecPosition, &vecAngles ); const CPhysCollide *pScaledCollide = pPhysObject->GetCollide(); physcollision->TraceBox( ray, pScaledCollide, vecPosition, vecAngles, &tr );
return tr.DidHit(); }
return false; }
int CStatueProp::OnTakeDamage( const CTakeDamageInfo &info ) { return BaseClass::OnTakeDamage( info ); }
void CStatueProp::Event_Killed( const CTakeDamageInfo &info ) { IPhysicsObject *pPhysics = VPhysicsGetObject();
if ( pPhysics && !pPhysics->IsMoveable() ) { pPhysics->EnableMotion( true ); VPhysicsTakeDamage( info ); } m_nShatterFlags = 0; // If you have some flags to network for the shatter effect, put them here!
m_vShatterPosition = info.GetDamagePosition(); m_vShatterForce = info.GetDamageForce(); m_bShatter = true;
// Skip over breaking code!
//Break( info.GetInflictor(), info );
//BaseClass::Event_Killed( info );
// FIXME: Short delay before we actually remove so that the client statue gets a network update before we need it
// This isn't a reliable way to do this and needs to be rethought.
AddSolidFlags( FSOLID_NOT_SOLID );
SetNextThink( gpGlobals->curtime + 0.2f ); SetThink( &CBaseEntity::SUB_Remove ); }
void CStatueProp::Freeze( float flFreezeAmount, CBaseEntity *pFreezer, Ray_t *pFreezeRay ) { // Can't freeze a statue
TakeDamage( CTakeDamageInfo( pFreezer, pFreezer, 1, DMG_GENERIC ) ); }
void CStatueProp::CollisionPartnerThink( void ) { CBaseAnimating *pBaseAnimating = m_hInitBaseAnimating; if ( !pBaseAnimating ) { // Our partner died, I have no reason to live!
UTIL_Remove( this ); }
if ( GetHealth() <= 0 ) { // Reset health here in case it was tweaked by the model parse
SetHealth( 5 ); m_takedamage = DAMAGE_YES; }
SetNextThink( gpGlobals->curtime + 1.0f ); }
bool CStatueProp::CreateVPhysicsFromHitBoxes( CBaseAnimating *pInitBaseAnimating ) { if ( !pInitBaseAnimating ) return false;
// Use the current animation sequence and cycle
CopyAnimationDataFrom( pInitBaseAnimating );
// Copy over any render color
color24 colorRender = pInitBaseAnimating->GetRenderColor(); SetRenderColor( colorRender.r, colorRender.g, colorRender.b ); SetRenderAlpha( pInitBaseAnimating->GetRenderAlpha() );
// Get hitbox data
CStudioHdr *pStudioHdr = GetModelPtr(); if ( !pStudioHdr ) return false;
mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet ); if ( !set ) return false;
Vector position; QAngle angles;
// Make enough pointers to convexes for each hitbox
CPhysConvex **ppConvex = new CPhysConvex*[ set->numhitboxes ];
float flTotalVolume = 0.0f; float flTotalSurfaceArea = 0.0f;
for ( int i = 0; i < set->numhitboxes; i++ ) { // Get the hitbox info
mstudiobbox_t *pbox = set->pHitbox( i ); GetBonePosition( pbox->bone, position, angles );
// Accumulate volume and area
Vector flDimentions = pbox->bbmax - pbox->bbmin; flTotalVolume += flDimentions.x * flDimentions.y * flDimentions.z; flTotalSurfaceArea += 2.0f * ( flDimentions.x * flDimentions.y + flDimentions.x * flDimentions.z + flDimentions.y * flDimentions.z );
// Get angled min and max extents
Vector vecMins, vecMaxs; VectorRotate( pbox->bbmin, angles, vecMins ); VectorRotate( pbox->bbmax, angles, vecMaxs );
// Get the corners in world space
Vector vecMinCorner = position + vecMins; Vector vecMaxCorner = position + vecMaxs;
// Get the normals of the hitbox in world space
Vector vecForward, vecRight, vecUp; AngleVectors( angles, &vecForward, &vecRight, &vecUp ); vecRight = -vecRight;
// Convert corners and normals to local space
Vector vecCornerLocal[ 2 ]; Vector vecNormalLocal[ 3 ];
matrix3x4_t matToWorld = EntityToWorldTransform(); VectorITransform( vecMaxCorner, matToWorld, vecCornerLocal[ 0 ] ); VectorITransform( vecMinCorner, matToWorld, vecCornerLocal[ 1 ] ); VectorIRotate( vecForward, matToWorld, vecNormalLocal[ 0 ] ); VectorIRotate( vecRight, matToWorld, vecNormalLocal[ 1 ] ); VectorIRotate( vecUp, matToWorld, vecNormalLocal[ 2 ] );
// Create 6 planes from the local oriented hit box data
float pPlanes[ 4 * 6 ];
for ( int iPlane = 0; iPlane < 6; ++iPlane ) { int iPlaneMod2 = iPlane % 2; int iPlaneDiv2 = iPlane / 2; bool bOdd = ( iPlaneMod2 == 1 );
// Plane Normal
pPlanes[ iPlane * 4 + 0 ] = vecNormalLocal[ iPlaneDiv2 ].x * ( bOdd ? -1.0f : 1.0f ); pPlanes[ iPlane * 4 + 1 ] = vecNormalLocal[ iPlaneDiv2 ].y * ( bOdd ? -1.0f : 1.0f ); pPlanes[ iPlane * 4 + 2 ] = vecNormalLocal[ iPlaneDiv2 ].z * ( bOdd ? -1.0f : 1.0f );
// Plane D
pPlanes[ iPlane * 4 + 3 ] = ( vecCornerLocal[ iPlaneMod2 ].x * vecNormalLocal[ iPlaneDiv2 ].x + vecCornerLocal[ iPlaneMod2 ].y * vecNormalLocal[ iPlaneDiv2 ].y + vecCornerLocal[ iPlaneMod2 ].z * vecNormalLocal[ iPlaneDiv2 ].z ) * ( bOdd ? -1.0f : 1.0f ); }
// Create convex from the intersection of these planes
ppConvex[ i ] = physcollision->ConvexFromPlanes( pPlanes, 6, 0.0f ); }
// Make a single collide out of the group of convex boxes
CPhysCollide *pPhysCollide = physcollision->ConvertConvexToCollide( ppConvex, set->numhitboxes );
delete[] ppConvex;
// Create the physics object
objectparams_t params = g_PhysDefaultObjectParams; params.pGameData = static_cast<void *>( this );
int nMaterialIndex = physprops->GetSurfaceIndex( "ice" ); // use ice material
IPhysicsObject* p = physenv->CreatePolyObject( pPhysCollide, nMaterialIndex, GetAbsOrigin(), GetAbsAngles(), ¶ms ); Assert( p != NULL );
// Set velocity
Vector vecInitialVelocity = pInitBaseAnimating->GetAbsVelocity(); p->SetVelocity( &vecInitialVelocity, NULL );
// Compute mass
float flMass; float flDensity, flThickness; physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, &flThickness, NULL, NULL );
// Make it more hollow
flThickness = MIN ( 1.0f, flThickness + 0.5f );
if ( flThickness > 0.0f ) { flMass = flTotalSurfaceArea * flThickness * CUBIC_METERS_PER_CUBIC_INCH * flDensity; } else { // density is in kg/m^3, volume is in in^3
flMass = flTotalVolume * CUBIC_METERS_PER_CUBIC_INCH * flDensity; }
// Mass is somewhere between the original and if it was all ice
p->SetMass( flMass );
// Yes, gravity
p->EnableGravity( true );
// Use this as our vphysics
VPhysicsSetObject( p );
SetSolid( SOLID_VPHYSICS ); AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
SetMoveType( MOVETYPE_VPHYSICS );
if ( pInitBaseAnimating != this ) { // Transfer children from the init base animating
TransferChildren( pInitBaseAnimating, this );
CBaseEntity *pChild = FirstMoveChild();
while ( pChild ) { CEntityFreezing *pFreezing = dynamic_cast<CEntityFreezing*>( pChild ); if ( pFreezing ) { pFreezing->FinishFreezing(); }
pChild = pChild->NextMovePeer(); } }
return true; }
bool CStatueProp::CreateVPhysicsFromOBBs( CBaseAnimating *pInitBaseAnimating ) { // Make enough pointers to convexes for each hitbox
CPhysConvex **ppConvex = new CPhysConvex*[ m_pInitOBBs->Count() ];
float flTotalVolume = 0.0f; float flTotalSurfaceArea = 0.0f;
for ( int i = 0; i < m_pInitOBBs->Count(); i++ ) { const outer_collision_obb_t *pOBB = &((*m_pInitOBBs)[ i ]);
// Accumulate volume and area
Vector flDimentions = pOBB->vecMaxs - pOBB->vecMins; flTotalVolume += flDimentions.x * flDimentions.y * flDimentions.z; flTotalSurfaceArea += 2.0f * ( flDimentions.x * flDimentions.y + flDimentions.x * flDimentions.z + flDimentions.y * flDimentions.z );
// Get angled min and max extents
Vector vecMins, vecMaxs; VectorRotate( pOBB->vecMins, pOBB->angAngles, vecMins ); VectorRotate( pOBB->vecMaxs, pOBB->angAngles, vecMaxs );
// Get the corners in world space
Vector vecMinCorner = pOBB->vecPos + vecMins; Vector vecMaxCorner = pOBB->vecPos + vecMaxs;
// Get the normals of the hitbox in world space
Vector vecForward, vecRight, vecUp; AngleVectors( pOBB->angAngles, &vecForward, &vecRight, &vecUp ); vecRight = -vecRight;
// Convert corners and normals to local space
Vector vecCornerLocal[ 2 ]; Vector vecNormalLocal[ 3 ];
matrix3x4_t matToWorld = EntityToWorldTransform(); VectorITransform( vecMaxCorner, matToWorld, vecCornerLocal[ 0 ] ); VectorITransform( vecMinCorner, matToWorld, vecCornerLocal[ 1 ] ); VectorIRotate( vecForward, matToWorld, vecNormalLocal[ 0 ] ); VectorIRotate( vecRight, matToWorld, vecNormalLocal[ 1 ] ); VectorIRotate( vecUp, matToWorld, vecNormalLocal[ 2 ] );
// Create 6 planes from the local oriented hit box data
float pPlanes[ 4 * 6 ];
for ( int iPlane = 0; iPlane < 6; ++iPlane ) { int iPlaneMod2 = iPlane % 2; int iPlaneDiv2 = iPlane / 2; bool bOdd = ( iPlaneMod2 == 1 );
// Plane Normal
pPlanes[ iPlane * 4 + 0 ] = vecNormalLocal[ iPlaneDiv2 ].x * ( bOdd ? -1.0f : 1.0f ); pPlanes[ iPlane * 4 + 1 ] = vecNormalLocal[ iPlaneDiv2 ].y * ( bOdd ? -1.0f : 1.0f ); pPlanes[ iPlane * 4 + 2 ] = vecNormalLocal[ iPlaneDiv2 ].z * ( bOdd ? -1.0f : 1.0f );
// Plane D
pPlanes[ iPlane * 4 + 3 ] = ( vecCornerLocal[ iPlaneMod2 ].x * vecNormalLocal[ iPlaneDiv2 ].x + vecCornerLocal[ iPlaneMod2 ].y * vecNormalLocal[ iPlaneDiv2 ].y + vecCornerLocal[ iPlaneMod2 ].z * vecNormalLocal[ iPlaneDiv2 ].z ) * ( bOdd ? -1.0f : 1.0f ); }
// Create convex from the intersection of these planes
ppConvex[ i ] = physcollision->ConvexFromPlanes( pPlanes, 6, 0.0f ); }
// Make a single collide out of the group of convex boxes
CPhysCollide *pPhysCollide = physcollision->ConvertConvexToCollide( ppConvex, m_pInitOBBs->Count() );
delete[] ppConvex;
// Create the physics object
objectparams_t params = g_PhysDefaultObjectParams; params.pGameData = static_cast<void *>( this );
int nMaterialIndex = physprops->GetSurfaceIndex( "ice" ); // use ice material
IPhysicsObject* p = physenv->CreatePolyObject( pPhysCollide, nMaterialIndex, GetAbsOrigin(), GetAbsAngles(), ¶ms ); Assert( p != NULL );
// Set velocity
Vector vecInitialVelocity = pInitBaseAnimating->GetAbsVelocity(); p->SetVelocity( &vecInitialVelocity, NULL );
// Compute mass
float flMass; float flDensity, flThickness; physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, &flThickness, NULL, NULL );
// Make it more hollow
flThickness = MIN ( 1.0f, flThickness + 0.5f );
if ( flThickness > 0.0f ) { flMass = flTotalSurfaceArea * flThickness * CUBIC_METERS_PER_CUBIC_INCH * flDensity; } else { // density is in kg/m^3, volume is in in^3
flMass = flTotalVolume * CUBIC_METERS_PER_CUBIC_INCH * flDensity; }
// Mass is somewhere between the original and if it was all ice
p->SetMass( flMass );
// Yes, gravity
p->EnableGravity( true );
// Use this as our vphysics
VPhysicsSetObject( p );
SetSolid( SOLID_VPHYSICS ); AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
SetMoveType( MOVETYPE_VPHYSICS );
m_pInitOBBs = NULL;
return true; }
CBaseEntity *CreateServerStatue( CBaseAnimating *pAnimating, int collisionGroup ) { CStatueProp *pStatue = static_cast<CStatueProp *>( CreateEntityByName( "physics_prop_statue" ) );
if ( pStatue ) { pStatue->m_hInitBaseAnimating = pAnimating; pStatue->SetModelName( pAnimating->GetModelName() ); pStatue->SetAbsOrigin( pAnimating->GetAbsOrigin() ); pStatue->SetAbsAngles( pAnimating->GetAbsAngles() ); DispatchSpawn( pStatue ); pStatue->Activate(); }
return pStatue; }
CBaseEntity *CreateServerStatueFromOBBs( const CUtlVector<outer_collision_obb_t> &vecSphereOrigins, CBaseAnimating *pAnimating ) { Assert( vecSphereOrigins.Count() > 0 );
if ( vecSphereOrigins.Count() <= 0 ) return NULL;
CStatueProp *pStatue = static_cast<CStatueProp *>( CreateEntityByName( "physics_prop_statue" ) );
if ( pStatue ) { pStatue->m_pInitOBBs = &vecSphereOrigins;
pStatue->m_hInitBaseAnimating = pAnimating; pStatue->SetModelName( pAnimating->GetModelName() ); pStatue->SetAbsOrigin( pAnimating->GetAbsOrigin() ); pStatue->SetAbsAngles( pAnimating->GetAbsAngles() ); DispatchSpawn( pStatue ); pStatue->Activate();
pStatue->AddEffects( EF_NODRAW ); pStatue->CollisionProp()->SetSurroundingBoundsType( USE_GAME_CODE ); pStatue->AddSolidFlags( ( pAnimating->GetSolidFlags() & FSOLID_CUSTOMBOXTEST ) | ( pAnimating->GetSolidFlags() & FSOLID_CUSTOMRAYTEST ) );
pAnimating->SetParent( pStatue );
// You'll need to keep track of the child for collision rules
pStatue->SetThink( &CStatueProp::CollisionPartnerThink ); pStatue->SetNextThink( gpGlobals->curtime + 1.0f ); }
return pStatue; }
|