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