It's free to join Gamasutra!|Have a question? Want to know who runs this site? Here you go.|Targeting the game development market with your product or service? Get info on advertising here.||For altering your contact information or changing email subscription preferences.
Registered members can log in here.Back to the home page.

Search articles, jobs, buyers guide, and more.

By Miguel Gomez
Gamasutra
July 2, 1999

Letters to the Editor:
Write a letter
View all letters


Features

The RIGID_BODY_STATE type

Contents

Introduction

The Vector type

The MATRIX type

The BASIS type

The COORDINATE FRAME type

The RIGID_BODY STATE type

Bibliography

The name says it all. The RIGID_BODY_STATE keeps track of the dynamic variables of the body. It is created by deriving from COORDINATE_FRAME and including the linear velocity, the angular velocity, the inertia tensor and its inverse (see Listing 5).

Listing 5

// Describe the dynamic state of a rigid
// body

class RIGID_BODY_STATE : public COORD_FRAME

{

public:

VECTOR V; //linear velocity, meters/sec

VECTOR W; //angular velocity, radians/sec

MATRIX I; //inertia tensor in world space,kg m m

MATRIX I_inv; //inverse inertia tensor in world space

public:

RIGID_BODY_STATE()

{}

RIGID_BODY_STATE( const VECTOR& v, const VECTOR& w )

: V (v),

W (w)

{}

const VECTOR& velocity() const { return V; }

void velocity( const VECTOR& v ) { V = v; }

const VECTOR& angularVelocity() const { return W; }

void angularVelocity(const VECTOR& v) {W = v;}

const MATRIX& inertiaTensor() const { return I; }

const MATRIX& inverseInertiaTensor() const { return I_inv; }

//calculate the inertia tensor and its
//inverse from the current orientation
//and the principal moments of inertia

void calculateInertiaTensor( const VECTOR& ip );

};

The RIGID_BODY type

Finally, we create the structure that allows use to fully describe and manipulate a rigid body in 3D space. The object hierarchy of the RIGID_BODY class allows us to perform all the fundamental operations: positioning, translation, rotation, transformation, and linear and angular acceleration. In order to perform collision detection, you also need to store the previous state of the body. The current and previous states will allow you to interpolate between states to check for overlaps with the world or other bodies. This is why we separated the state information from the actual rigid body class.

In Listing 6, you’ll notice that RIGID_BODY is a class instead of a structure. Up to this point I’ve broken the object-oriented rule of data hiding because I’m lazy and I like to access members directly. The RIGID_BODY will be our interface with the rest of the world, so data hiding will do us good at this point.

Listing 6.

// Describe a rigid body

//

class RIGID_BODY : public RIGID_BODY_STATE

{

public:

//previous dynamic state

RIGID_BODY_STATE PrevState;

//mass and rotational inertia

SCALAR M; //mass, kg

VECTOR Ip; //principal moments of inertia, kg m m

public:

RIGID_BODY()

: M(1), Ip(1,1,1)

{}

RIGID_BODY( const SCALAR& m, const VECTOR& ip )

: M(m), Ip(ip)

{}

//physical properties

void mass( const SCALAR& m ) { M = m; }

SCALAR mass() const { return M; }

void inertiaMoments( const VECTOR& ip ) { Ip = ip; }

const VECTOR& inertiaMoments() const { return Ip; }

};

What have we gained?

So what have we gained by creating all these structures and operators? Here is an example of C++ code that will move a body under a force and a torque according to the Newton-Euler equations of rigid body motion:

void RIGID_BODY::move( const SCALAR dt )

{

VECTOR F, T;

this->Translate( this->V * dt );

this->Rotate( this->W * dt );

ComputeForceAndTorque( F, T );

this->V += (1/this->M)* F * dt;

this->W += this->I_inv * (T - W.cross(I*W)) * dt;

}

The equivalent C code is less elegant:

void RIGID_BODY_Move( RIGID_BODY* pBody, SCALAR dt )

{

VECTOR F, T, t, v;

//easier to hand-inline this one

pBody->State.Frame.O.x += pBody->State.V.x * dt;

pBody->State.Frame.O.y += pBody->State.V.y * dt;

pBody->State.Frame.O.z += pBody->State.V.z * dt;

vmul( t, pBody->State.W, dt );//macro

RotateBasis( pBody->State.Frame.Basis, &t );

ComputeForceAndTorque( &F, &T );

s = 1/ pBody->M;

pBody->State.V.x += s * F.x * dt;

pBody->State.V.y += s * F.y * dt;

pBody->State.V.z += s * F.z * dt;

matrix_mul( t, pBody->State.I, pBody->State.W );//t = I*W

vcross( v, pBody->State.W, t ); //v = Wx(I*W)

vsub( t, T, v ); //t = T - Wx(I*W)

matrix_mul( v, pBody->State.I_inv, t );//v = I_inv*(T - Wx(I*W))

vmul( t, v, dt ); //t = I_inv * (T - Wx(I*W)) * dt

pBody->State.W.x += t.x;

pBody->State.W.y += t.y;

pBody->State.W.z += t.z;

}

Using a good set of data structures, we’ve gained simplicity while preserving efficiency. With a little time spent up front on design, the entire physics and collision detection code base will be smaller, faster and easier to debug.

Miguel Gomez is a Senior Software Engineer at Looking Glass Studios, Redmond. Since receiving a BS in Physics from the University of Washington in 1995, he has programmed physics and collision detection for PGA Tour Golf '96" Hyperblade, Baseball 3D, 1998 Edition, Destruction Derby 64, and most recently Wild Waters, a 3-D kayaking simulation title for the Nintendo 64. And if it weren’t for the computer gaming industry, he’d be unemployed.

Bibliography

[1] Tai L. Chow, Classical Mechanics

[2] Harry F. Davis, Arthur David Snider, Introduction to Vector Analysis, 6th Edition

[3] Brian W. Kernighan, Dennis M. Ritchie, The C Programming Language, 2nd Edition

[4] Serge Lang, Linear Algebra, 3rd Edition

[5] Paul Pedriana, High Performance Game Programming in C++, Conference Proceedings 1998 Computer Game Developer’s Conference

[6] Bjarne Stroustrup, The C++ Programming Language, 2nd Edition


[Back to] Introduction


join | contact us | advertise | write | my profile
news | features | companies | jobs | resumes | education | product guide | projects | store



Copyright © 2003 CMP Media LLC

privacy policy
| terms of service