multi_phys
Jump to navigation
Jump to search
This sample entity creates two IPhysicsObject
s. The two objects are not constrained to each other, so can end up quite far apart.

Server
#include "cbase.h"
class CMultiPhys : public CBaseAnimating
{
public:
DECLARE_CLASS(CMultiPhys, CBaseAnimating);
DECLARE_SERVERCLASS();
void Spawn();
void SpawnTestObjects();
void UpdateOnRemove();
int VPhysicsGetObjectList( IPhysicsObject **pList, int listMax );
void VPhysicsUpdate( IPhysicsObject *pPhysics );
void ComputeWorldSpaceSurroundingBox( Vector *pMins, Vector *pMaxs );
bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace );
void PhysicsImpact( CBaseEntity *other, trace_t &trace );
void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity );
CUtlVector<IPhysicsObject*> m_physList;
// Have to choose a limit, 2 in this case. Remember to update the client if you raise this.
CNetworkArray(Vector,m_physPos,2);
CNetworkArray(QAngle,m_physAng,2);
};
IMPLEMENT_SERVERCLASS_ST(CMultiPhys, DTMultiPhys)
SendPropArray(SendPropVector(SENDINFO_ARRAY(m_physPos)),m_physPos),
SendPropArray(SendPropQAngles(SENDINFO_ARRAY(m_physAng)),m_physAng),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( multi_phys, CMultiPhys );
#define MODEL "models/props_junk/TrashBin01a.mdl"
#define MODEL2 "models/props_c17/FurnitureDresser001a.mdl"
void CMultiPhys::Spawn()
{
BaseClass::Spawn();
PrecacheModel(MODEL);
PrecacheModel(MODEL2);
SetModel(MODEL);
SpawnTestObjects();
VPhysicsSetObject(m_physList[0]);
SetMoveType( MOVETYPE_VPHYSICS );
SetSolid( SOLID_VPHYSICS );
AddSolidFlags(FSOLID_CUSTOMBOXTEST|FSOLID_CUSTOMRAYTEST);
CollisionProp()->SetSurroundingBoundsType(USE_GAME_CODE);
Teleport(&GetAbsOrigin(),&GetAbsAngles(),&GetAbsVelocity());
}
int CMultiPhys::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
{
int count = 0;
for (int i=0; i < m_physList.Count() && i < listMax; i++)
{
pList[i] = m_physList[i];
count++;
}
return count;
}
void CMultiPhys::VPhysicsUpdate( IPhysicsObject *pPhysics )
{
for (int i=0;i < m_physList.Count(); i++)
{
Vector pos;
QAngle ang;
m_physList[i]->GetPosition(&pos,&ang);
m_physPos.Set(i,pos);
m_physAng.Set(i,ang);
if (i==0)
{
SetAbsOrigin(pos);
SetAbsAngles(ang);
}
}
PhysicsTouchTriggers();
SetSimulationTime( gpGlobals->curtime ); // otherwise various game systems think the entity is inactive
}
// Called whenever bounding boxes intersect; *not* a VPhysics call
void CMultiPhys::PhysicsImpact( CBaseEntity *other, trace_t &trace )
{
for ( int i = 0; i < m_physList.Count(); i++ )
{
trace_t curTrace;
Vector position;
QAngle angles;
m_physList[i]->GetPosition( &position, &angles );
physcollision->TraceBox( trace.startpos, trace.endpos, Vector(-1), Vector(1) ,m_physList[i]->GetCollide(), position, angles, &curTrace );
if ( curTrace.fraction < trace.fraction )
BaseClass::PhysicsImpact(other,trace);
}
}
void CMultiPhys::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
{
BaseClass::Teleport(newPosition,newAngles,newVelocity);
for ( int i=1; i < m_physList.Count(); i++ ) // skip the first object, that's handled by CBaseEntity
{
Vector curPos;
QAngle curAng;
m_physList[i]->GetPosition(&curPos,&curAng);
// Redirect to current value if there isn't a new one
if (!newPosition)
newPosition = &curPos;
if (!newAngles)
newAngles = &curAng;
// In a real entity there would be something better than this
Vector OffsetPos = *newPosition + Vector(20*i, 0, 0);
newPosition = &OffsetPos;
// Move the physics object
m_physList[i]->SetPosition( *newPosition, *newAngles, true );
// Network the new values
m_physPos.Set(i,*newPosition);
m_physAng.Set(i,*newAngles);
// This doesn't need to be networked
if (newVelocity)
{
AngularImpulse angImp;
m_physList[i]->SetVelocity(newVelocity,&angImp);
}
}
}
#include "cbase.h"
#ifdef GAME_DLL
#include "multi_phys.h"
#include "physics_saverestore.h"
#else
#include "c_multi_phys.h"
#define CMultiPhys C_MultiPhys
#endif
#define MODEL "models/props_junk/TrashBin01a.mdl"
#define MODEL2 "models/props_c17/FurnitureDresser001a.mdl"
void CMultiPhys::SpawnTestObjects()
{
m_physList.AddToTail( PhysModelCreate( this, modelinfo->GetModelIndex(MODEL), vec3_origin, vec3_angle ) );
m_physList[0]->SetGameData(this);
m_physList[0]->SetGameIndex(0);
m_physList[0]->SetGameFlags(FVPHYSICS_MULTIOBJECT_ENTITY);
m_physList.AddToTail( PhysModelCreate( this, modelinfo->GetModelIndex(MODEL2), vec3_origin, vec3_angle ) );
m_physList[1]->SetGameData(this);
m_physList[1]->SetGameIndex(1);
m_physList[1]->SetGameFlags(FVPHYSICS_MULTIOBJECT_ENTITY);
}
// Generates the bounding box
void CMultiPhys::ComputeWorldSpaceSurroundingBox( Vector *pMins, Vector *pMaxs )
{
Vector Mins(GetAbsOrigin()),Maxs(Mins);
for (int i=0;i < m_physList.Count(); i++)
{
Vector curMin,curMax;
Vector curPos;
QAngle curAng;
m_physList[i]->GetPosition(&curPos,&curAng);
physcollision->CollideGetAABB(&curMin,&curMax,m_physList[i]->GetCollide(),curPos,curAng);
for (int j=0;j<3;j++)
{
Mins[j] = min(Mins[j],curMin[j]);
Maxs[j] = max(Maxs[j],curMax[j]);
}
}
*pMins = Mins;
*pMaxs = Maxs;
}
// Should a VPhysics collision happen?
bool CMultiPhys::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
{
for ( int i = 0; i < m_physList.Count(); i++ )
{
Vector position;
QAngle angles;
m_physList[i]->GetPosition( &position, &angles );
trace_t curTrace;
physcollision->TraceBox( ray, m_physList[i]->GetCollide(), position, angles, &curTrace );
if ( curTrace.fraction < trace.fraction )
{
curTrace.surface.surfaceProps = m_physList[i]->GetMaterialIndex();
trace = curTrace;
}
}
return trace.fraction < 1;
}
void CMultiPhys::UpdateOnRemove()
{
for ( int i=1; i < m_physList.Count(); i++ )
{
#ifdef GAME_DLL
g_pPhysSaveRestoreManager->ForgetModel( m_physList[i] );
#endif
physenv->DestroyObject( m_physList[i] );
}
BaseClass::UpdateOnRemove();
}
Client
#include "cbase.h"
class C_MultiPhys : public C_BaseAnimating
{
public:
DECLARE_CLASS(C_MultiPhys, C_BaseAnimating);
DECLARE_CLIENTCLASS();
C_MultiPhys();
void Spawn();
void SpawnTestObjects();
void ClientThink();
void UpdateOnRemove();
void GetRenderBounds( Vector& theMins, Vector& theMaxs );
void ComputeWorldSpaceSurroundingBox( Vector *pMins, Vector *pMaxs );
bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace );
int InternalDrawModel( int flags );
CUtlVector<IPhysicsObject*> m_physList;
Vector m_physPos[2];
QAngle m_physAng[2];
CInterpolatedVarArray<Vector,2> m_iv_physPos;
CInterpolatedVarArray<QAngle,2> m_iv_physAng;
private:
bool SpawnedPhys;
};
IMPLEMENT_CLIENTCLASS_DT(C_MultiPhys,DTMultiPhys,CMultiPhys)
RecvPropArray(RecvPropVector(RECVINFO(m_physPos[0])), m_physPos),
RecvPropArray(RecvPropQAngles(RECVINFO(m_physAng[0])), m_physAng),
END_RECV_TABLE()
LINK_ENTITY_TO_CLASS( multi_phys, C_MultiPhys );
C_MultiPhys::C_MultiPhys() :
m_iv_physPos("C_MultiPhys::m_iv_physPos"),
m_iv_physAng("C_MultiPhys::m_iv_physAng")
{
AddVar( m_physPos, &m_iv_physPos, LATCH_SIMULATION_VAR );
AddVar( m_physAng, &m_iv_physAng, LATCH_SIMULATION_VAR );
}
void C_MultiPhys::Spawn()
{
BaseClass::Spawn();
SetNextClientThink(CLIENT_THINK_ALWAYS);
}
void C_MultiPhys::ClientThink()
{
if (!SpawnedPhys && physenv) // The client physics environment might not exist when Spawn() is called!
{
SpawnTestObjects();
VPhysicsSetObject(m_physList[0]);
SpawnedPhys = true;
}
// Pos/Ang are interpolated every frame, so keep refreshing them
for (int i=0;i < m_physList.Count(); i++)
m_physList[i]->SetPosition(m_physPos[i],m_physAng[i],false);
SetNextClientThink(CLIENT_THINK_ALWAYS);
}
// FIXME: a little too large, despite WSSB being a tight fit
void C_MultiPhys::GetRenderBounds( Vector& theMins, Vector& theMaxs )
{
ComputeWorldSpaceSurroundingBox(&theMins,&theMaxs);
theMins -= GetAbsOrigin();
theMaxs -= GetAbsOrigin();
IRotateAABB( EntityToWorldTransform(), theMins, theMaxs, theMins, theMaxs );
}
// Quick visualisation of where the other models are. Actually rendering them is a whole new can of worms...
int C_MultiPhys::InternalDrawModel( int flags )
{
int ret = BaseClass::InternalDrawModel( flags );
IMaterial *pWireframe = materials->FindMaterial("shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER);
matrix3x4_t matrix;
static color32 debugColor = {0,255,255,0};
for (int i=1;i < m_physList.Count(); i++) // skip first object
{
m_physList[i]->GetPositionMatrix(&matrix);
engine->DebugDrawPhysCollide( m_physList[i]->GetCollide(), pWireframe, matrix, debugColor );
}
return ret;
}