2D Rigid Body class

Started by
0 comments, last by szinkopa 19 years, 9 months ago
I created a Rigid Body class for use in (e.g.) a 2D physics engine. I hope it does not include any (read: too much) errors, but please criticize (note: I did not write 'flame' ;-) ) everything you do not like as then the class is likely to become better. There are two kinds of integrators available: Euler and Backwards Euler. I would also have liked to include RK methods, but as far as my understanding goes, I would have to use some kind of force object first and a function to accumulate the forces everytime I need them. Maybe someone can give me a hint on what's the right direction - how would you implement RK2/RK4? (Or how have you implemented one of the Runge-Kutta methods?) Anyhow, time for the source: [RigidBody.h]

//RigidBody.h

#if !defined(RIGID_BODY_H)
#define RIGID_BODY_H

//include some kind of vector class, have 'real' typedef'ed
#include "Vector2.h"
typedef float real;

class RigidBody
{
public:
	RigidBody(void);
	RigidBody(const real Mass, const real Inertia,
		const Vector2& Position, const Vector2& Velocity,
		const real Orientation, const real Omega,
		const bool Translate, const bool Rotate);
	~RigidBody();

	//compute force and torque from an external force
	//Point is point of force application, in world coordinates
	//(function does not check if Point actually lies inside the rigid body)
	void AddForce(const Vector2& Force, const Vector2& Point);

	//same function as AddForce(Force, Point), only force is added to center of mass (-> no torque)
	void AddForce(const Vector2& Force);

	//integrate forward a timestep (using m_Force and m_Torque, setting them to 0 afterwards)
	//using various integrators
    //explicit euler integrator: y(n+1) = y(n) + y'(n) * dt
	void IntegrateEuler(const real Timestep);
	//implicit (backward) euler integrator: y(n+1) = y(n) + y'(n+1) * dt
	void IntegrateEuler2(const real Timestep);

#if 0
	void IntegrateRK2(const real Timestep);
	void IntegrateRK4(const real Timestep);
#endif //#if 0

	//set functions
	void SetMass(const real Mass);
	void SetInvMass(const real InvMass);
	void SetInertia(const real Inertia);
	void SetInvInertia(const real InvInertia);

	void SetPosition(const Vector2& Position);
	void SetVelocity(const Vector2& Velocity);

	void SetOrientation(const real Orientation);
	void SetOmega(const real Omega);

	void SetForce(const Vector2& Force);
	void SetTorque(const real Torque);

	void SetTranslate(const bool Translate);
	void SetRotate(const bool Rotate);

	//get functions
	const real GetMass(void) const;
	const real GetInvMass(void) const;
	const real GetInertia(void) const;
	const real GetInvInertia(void) const;

	const Vector2& GetPosition(void) const;
	const Vector2& GetVelocity(void) const;

	const real GetOrientation(void) const;
	const real GetOmega(void) const;

	const Vector2& GetForce(void) const;
	const real GetTorque(void) const;

	const bool GetTranslate(void) const;
	const bool GetRotate(void) const;

private:
	//properties
	//inverse of mass
	real	m_InvMass;
	//inverse of moment of inertia
	real	m_InvInertia;

	//linear
	//position (at center of mass)
	Vector2 m_Position;
	//velocity
	Vector2 m_Velocity;

	//angular
	//orientation (capital omega 'O')
	real m_Orientation;
	//angular velocity (small omega 'w')
	real m_Omega;

	//force and torque to apply
	Vector2 m_Force;
	real m_Torque;

	//integrate linear and/or angular properties?
	bool m_Translate;
	bool m_Rotate;

};

#endif //!defined(RIGID_BODY_H)
[RigidBody.cpp]

//RigidBody.cpp

#include "RigidBody.h"

RigidBody::RigidBody(void) :	m_InvMass(0.0f),
								m_InvInertia(0.0f),

								m_Position(0.0f, 0.0f),
								m_Velocity(0.0f, 0.0f),

								m_Orientation(0.0f),
								m_Omega(0.0f),

								m_Force(0.0f, 0.0f),
								m_Torque(0.0f),

								m_Translate(true),
								m_Rotate(true)
{

}

RigidBody::RigidBody(
	const real Mass,
	const real Inertia,
	
	const Vector2& Position,
	const Vector2& Velocity,
	
	const real Orientation,
	const real Omega,
	
	const bool Translate,
	const bool Rotate) :		m_InvMass(0.0f),
								m_InvInertia(0.0f),

								m_Position(Position),
								m_Velocity(Velocity),

								m_Orientation(Orientation),
								m_Omega(Omega),

								m_Force(0.0f, 0.0f),
								m_Torque(0.0f),

								m_Translate(Translate),
								m_Rotate(Rotate)
{
	//only compute 1/mass if mass > 0 !
	//otherwise m_InvMass has already been set to 0.0f
	if(Mass > 0.0f)
	{
        m_InvMass = 1.0f/Mass;
	}

	//only compute 1/inertia if inertia > 0 !
	//otherwise m_InvInertia has already been set to 0.0f
	if(Inertia > 0.0f)
	{
        m_InvInertia = 1.0f/Inertia;
	}
}


RigidBody::~RigidBody()
{

}

void RigidBody::AddForce(const Vector2& Force, const Vector2& Point)
{
	//add force to total force
	m_Force += Force;

	//get radius from center of mass to force application point
	//(move m_Position to (0,0) )
	Vector2 r(Point - m_Position);

    //make radius perpendicular (to radius itself)
	Vector2 r_perp(-(r.y), r.x);

    //compute torque (t = r_perp * force) and add to total torque
	m_Torque += r_perp.dot(Force);
}

void RigidBody::AddForce(const Vector2& Force)
{
	//add force to total force
	m_Force += Force;
}

void RigidBody::IntegrateEuler(const real Timestep)
{
	//see IntegrateEuler2 for details

	//linear
	if(m_Translate)
	{
		m_Position += m_Velocity * Timestep;
		m_Velocity += (m_Force * m_InvMass) * Timestep;
	}
	m_Force.Set(0.0f, 0.0f);

	//angular
	if(m_Rotate)
	{
		m_Orientation += m_Omega * Timestep;
		m_Omega += (m_Torque * m_InvInertia) * Timestep;
	}
	m_Torque = 0.0f;
}

void RigidBody::IntegrateEuler2(const real Timestep)
{
	//note: this is not a 'true' (explicit) euler integrator, as then
	//integrating would have to be done this way (i.e. in that specific order):
	//	pos	+= v * dt
	//	v	+= a * dt
	//...and similar with rotations
	//when reversing the order, it isn't explicit euler anymore, it is
	//'backwards' (or implicit) euler - and it's general considered to be better,
	//so use this one

	//linear
	if(m_Translate)
	{
		//integrate velocity and position
		m_Velocity += (m_Force * m_InvMass) * Timestep;
		m_Position += m_Velocity * Timestep;
	}
	m_Force.Set(0.0f, 0.0f);

	//angular
	if(m_Rotate)
	{
		//integrate angular velocity and orientation
		m_Omega += (m_Torque * m_InvInertia) * Timestep;
		m_Orientation += m_Omega * Timestep;
	}
	m_Torque = 0.0f;
}

//set functions
void RigidBody::SetMass(const real Mass)
{
	if(Mass > 0.0f)
	{
		m_InvMass = 1/Mass;
	}
	else
	{
		m_InvMass = 0.0f;
	}
}

void RigidBody::SetInvMass(const real InvMass)
{
	m_InvMass = InvMass;
}

void RigidBody::SetInertia(const real Inertia)
{
	if(Inertia > 0.0f)
	{
		m_InvInertia = 1/Inertia;
	}
	else
	{
		m_InvInertia = 0.0f;
	}
}

void RigidBody::SetInvInertia(const real InvInertia)
{
	m_InvInertia = InvInertia;
}

void RigidBody::SetPosition(const Vector2& Position)
{
	m_Position = Position;
}

void RigidBody::SetVelocity(const Vector2& Velocity)
{
	m_Velocity = Velocity;
}

void RigidBody::SetOrientation(const real Orientation)
{
	m_Orientation = Orientation;
}

void RigidBody::SetOmega(const real Omega)
{
	m_Omega = Omega;
}

void RigidBody::SetForce(const Vector2& Force)
{
	m_Force = Force;
}

void RigidBody::SetTorque(const real Torque)
{
	m_Torque = Torque;
}

void RigidBody::SetTranslate(const bool Translate)
{
	m_Translate = Translate;
}

void RigidBody::SetRotate(const bool Rotate)
{
	m_Rotate = Rotate;
}


//get functions
const real RigidBody::GetMass(void) const
{
	if(m_InvMass > 0)
	{
		return (1/m_InvMass);
	}
	else
	{
		return 0.0f;
	}
}

const real RigidBody::GetInvMass(void) const
{
	return m_InvMass;
}

const real RigidBody::GetInertia(void) const
{
	if(m_InvInertia > 0)
	{
		return (1/m_InvInertia);
	}
	else
	{
		return 0.0f;
	}
}

const real RigidBody::GetInvInertia(void) const
{
	return m_InvInertia;
}

const Vector2& RigidBody::GetPosition(void) const
{
	return m_Position;
}

const Vector2& RigidBody::GetVelocity(void) const
{
	return m_Velocity;
}

const real RigidBody::GetOrientation(void) const
{
	return m_Orientation;
}

const real RigidBody::GetOmega(void) const
{
	return m_Omega;
}

const Vector2& RigidBody::GetForce(void) const
{
	return m_Force;
}

const real RigidBody::GetTorque(void) const
{
	return m_Torque;
}

const bool RigidBody::GetTranslate(void) const
{
	return m_Translate;
}

const bool RigidBody::GetRotate(void) const
{
	return m_Rotate;
}
The comments (read: documentation) are not that great, I know. Please let me hear your opinion(s)! I would also like to thank oliii at this point for the great 2D collision tutorial(s) he wrote! Good Job! Thanks for your time, brupp
Advertisement
Maybe AddPosition(), AddVelocity(), etc would be useful, even if you implement RK2, or RK4

If you implement a higher order integration method, and there is some kind of interaction (for example spring), then you should not integrate the integration into this class. It should be implemented somewhere else, because you have integrate the bodies simultanously.

How to implement RK2:

You need a class, which access all the bodies on the scene. This class has a SolveRK2(TimeStep) method. RK2 has 2 steps. The first is a simple Euler integration, but the result is not written to the body, but into some temporary variable (K1,L1,M1,N1). To be able to integrate you need the forces acting on the current body you integrate. So your RigidBody class should have an ApplyForcesAndTorques() virtual method, which can be overridden in the derived classes. Or you can surely do it with function pointers too. The main point is that the integrator has to be able to apply force and torques on a body. So the pseudocode:

SolveRK2(TimeStep){//1st step is an Euler integrationfor each body on the scene{   //save the initial state   currentBody->saveState()   //evaluate forces, torques   currentbody->applyforcesAndTorques()   //store the result   K1[bodyIndex]=TimeStep*currentbody->getVelocity()   L1[bodyIndex]=TimeStep*currentbody->getAcceleration()   M1[bodyIndex]=TimeStep*currentbody->getOmega()   N1[bodyIndex]=TimeStep*currentbody->getAngularAcceleration()}//2nd stepfor each body on the scene{   //new initial state   currentBody->addPosition(0.5*K1[bodyIndex])   currentBody->addVelocity(0.5*L1[bodyIndex])   currentBody->addOrientation(0.5*M1[bodyIndex])   currentBody->addOmega(0.5*N1[bodyIndex])   //evaluate forces, torques   currentbody->applyforcesAndTorques()   //store the result   K2[bodyIndex]=TimeStep*currentbody->getVelocity()   L2[bodyIndex]=TimeStep*currentbody->getAcceleration()   M2[bodyIndex]=TimeStep*currentbody->getOmega()   N2[bodyIndex]=TimeStep*currentbody->getAngularAcceleration()}//apply changesfor each body on the scene{   //restore the initial state   currentBody->restoreState()   //apply change   currentbody->setPosition(K2[bodyindex])   currentbody->setVelocity(L2[bodyindex])   currentbody->setOrientation(M2[bodyindex])   currentbody->setOmega(N2[bodyindex])}}



bodyindex is the index of the current body

I hope I haven't done mistake.

EDIT: The RigidBody's ApplyForcesAndTorques() methos should apply all the forces and torques that act on the body.
EDIT: saveState(), restoreState() calls

This topic is closed to new replies.

Advertisement