Multi phys: Difference between revisions
		
		
		
		
		
		Jump to navigation
		Jump to search
		
				
		 Bug:Pushing lighter objects will sometimes lead to the player taking 5 crush damage. This is probably due to problems with the calculation of mass when there are two dissimilar models (by default a big wooden wardrobe and an empty plastic wheelie bin) in the entity. In practice, avoid simulating completely separate physics objects with one entity.  [todo tested in ?]
Bug:Pushing lighter objects will sometimes lead to the player taking 5 crush damage. This is probably due to problems with the calculation of mass when there are two dissimilar models (by default a big wooden wardrobe and an empty plastic wheelie bin) in the entity. In practice, avoid simulating completely separate physics objects with one entity.  [todo tested in ?]
		
	
| TomEdwards (talk | contribs) m (+cat) | TomEdwards (talk | contribs)   (fixed not removing clientside phys objects) | ||
| Line 7: | Line 7: | ||
| <source lang=cpp> | <source lang=cpp> | ||
| #include "cbase.h" | #include "cbase.h" | ||
| class CMultiPhys : public CBaseAnimating | class CMultiPhys : public CBaseAnimating | ||
| Line 150: | Line 149: | ||
| 		} | 		} | ||
| 	} | 	} | ||
| } | } | ||
| </source> | </source> | ||
| Line 169: | Line 158: | ||
| #ifdef GAME_DLL | #ifdef GAME_DLL | ||
| 	#include "multi_phys.h" | 	#include "multi_phys.h" | ||
| 	#include "physics_saverestore.h" | |||
| #else | #else | ||
| 	#include "c_multi_phys.h" | 	#include "c_multi_phys.h" | ||
| Line 236: | Line 226: | ||
| 	} | 	} | ||
| 	return trace.fraction < 1; | 	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(); | |||
| } | } | ||
| </source> | </source> | ||
| Line 257: | Line 259: | ||
| 	void ClientThink(); | 	void ClientThink(); | ||
| 	void UpdateOnRemove(); | |||
| 	void GetRenderBounds( Vector& theMins, Vector& theMaxs ); | 	void GetRenderBounds( Vector& theMins, Vector& theMaxs ); | ||
Revision as of 03:36, 23 June 2011
This sample entity creates two IPhysicsObjects. The two objects are not constrained to each other, so can end up quite far apart. 
 Bug:Pushing lighter objects will sometimes lead to the player taking 5 crush damage. This is probably due to problems with the calculation of mass when there are two dissimilar models (by default a big wooden wardrobe and an empty plastic wheelie bin) in the entity. In practice, avoid simulating completely separate physics objects with one entity.  [todo tested in ?]
Bug:Pushing lighter objects will sometimes lead to the player taking 5 crush damage. This is probably due to problems with the calculation of mass when there are two dissimilar models (by default a big wooden wardrobe and an empty plastic wheelie bin) in the entity. In practice, avoid simulating completely separate physics objects with one entity.  [todo tested in ?]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;
}