Integrating the Newton Dynamics Engine
From C4 Engine Wiki
| I think I have added everything I meant to, but I am leaving this open for editing in case I missed something or someone else has something to add. If you do change this tutorial and it is a signficant change, all I ask is that you PM me (flashjackon) and let me know what you did.
I changed some of the code because there were typos and bugs. The code that is posted is tested and should work. I also added a link to the end of the tutorial that will allow you to download my source files directly. These files only contain the source you see in this tutorial. No C4 game or engine stuff is included, so you will still have to manually create the hooks into the engine outlined below.
Steve Montgomery (aka FlashJackson) |
Introduction
In this tutorial, I will show you how to do a very basic integration of the Newton Dynamics engine into C4. When complete, any geometric object in the C4 world can be either a static or dynamic physics body. Objects are made dynamic by attaching the PhysicsBodyController() to them in the world editor. The blaster is modified to be able to exert impulse force on any physics object it hits, allowing the player to move physics objects by shooting them.
Newton also supports constraints, materials, ragdolls, etc. Those are not implemented yet. As I add them to my code, I will update this tutorial to include the new functionality.
One more note. This is a Windows implementation, though a Mac version of Newton is available.
Credits
I had so much help getting this to work, it is hard for me to remember what is my original work and what I copied from other people's suggestions. To follow the major parts of the development of this code and understand who did what, look at this thread: Newton GDK Thread. I also had a lot of help here: Controller Tutorial Thread
Getting the Newton SDK
The first step is to download and install the Newton SDK. You can get it here: NewtonSDK.
Once you have installed the SDK, there will be three subdirectories in the main installation directory:
-
DOC— All documentation is stored in here. The tutorials are OK at best, but the Newton help file is very handy. You might want to make a shortcut to it.
-
SAMPLES— All of the samples are organized into a Visual C++ solution. These are handy to peruse when trying to understand how to use a particular Newton function.
-
SDK— There are three files you want to get from this directory. First of all, you need to put theNewton.dllin the same directory as your C4 executeable. Then you need to putNewton.libandNewton.heither in theGame Codedirectory under the C4 install or create a new directory for them (I made a subdirectory called Newton under the C4 isntall directory).
You may want to play around with the samples that come with the Newton SDK to get a feel for what the engine is capable of.
Setting up the compiler
Once you have the Newton SDK installed, you need to setup the compiler so that you can compile the code I will give you below.
I know everyone probably knows this, but I am going to suggest it anyways. Copy your C4 installation to a backup location before you start integrating this code. It is very important to have a clean install to go back to if something goes wrong.
- Step 1. Put your mouse over the Game project in the solution manager and right click. Select properties.
- Step 2. Click on the C++ folder to open its properties pane. Under additional directories add
..\..\..\newton(or substitute with the directory you put theNewton.handNewton.libfiles into. - Step 3. Click on the linker folder and in the General pane, add
..\..\..\newtonunder Additional Library Directories. - Step 4. Click on the Input pane under the Linker folder. Add
Newton.libto Additional dependencies. - Step 5. Make sure you have the
Newton.dllfile in the same directory as yourC4.exefile. - Step 6. Make sure you include the newton.h file in any source files that implement this code.
PhysicsMgr and PhysicsBodyController classes
To integrate the Newton SDK into the C4 engine, we are going to create two classes, along with some support functions.
The PhysicsMgr class provides the interface between Newton and C4.
class PhysicsMgr
{
private:
static void Cleanup();
void BuildCollisionMesh(World *theWorld);
void AddObjects(World *theWorld);
// static void DebugGraphics(const NewtonBody* body, int vertexCount, const float* FaceArray, int faceId);
public:
PhysicsMgr();
~PhysicsMgr();
void Init(void);
void Update();
void ShowDebug(void);
void Destroy(void);
};
You can put this definition wherever you want, but I put it in a seperate file (SJMPhysics.h). Make sure it is in the C4 namespace and that you include the Newton.h in all files that access Newton functions.
-
Init()— This function is called by the game after a world is loaded. It will create the Newton world and register all static and dynamic objects.
-
Update()— This function is called in theGame::Render()loop function. It steps the simulation forward by the amount of time elapsed since the last frame and updates all dynamic body transforms to reflect the simulation
-
ShowDebug()— This function can optionally be called right after theUpdate()function is called. If the physics debug mode is set to 1, the collision frames for all objects will be drawn. If it is set to 2, the center of mass (COM) will be drawn for all dynamic objects. I should note that the COM display is only accurate if the body hasn't been rotated by the simulation. It is invalid after that and I have not debugged why yet.
-
Destroy()— This function is called when the world is unloaded at the end of the game. It destroys the Newton world.
Here is the class for the PhysicsBodyController:
enum
{
kControllerPhysicsBody = 'pbod'
};
class PhysicsBodyController : public Controller
{
private:
float mass; // in kG
NewtonBody *myBody;
NewtonWorld *myWorld;
PhysicsBodyController(const PhysicsBodyController& PhysicsBodyController);
Controller *Replicate(void) const;
public:
PhysicsBodyController();
PhysicsBodyController(float rate);
~PhysicsBodyController();
float GetMass(void) const
{
return (mass);
}
void SetMass(float m)
{
mass = m;
}
void SetForce(Vector3D& force, Vector3D& posit);
void SetBody(NewtonBody *theBody, NewtonWorld *theWorld)
{
myBody = theBody;
myWorld = theWorld;
}
static bool ValidNode(const Node *node);
// Serialization functions
unsigned long GetPackSize(unsigned long packFlags) const;
void Pack(Packer& data, unsigned long packFlags) const;
void Unpack(Unpacker& data, unsigned long unpackFlags);
// User interface functions
long GetSettingCount(void) const;
Setting *GetSetting(long index) const;
void SetSetting(const Setting *setting);
void Preprocess(void);
// The function that moves the target node
void Move(void);
};
For reference, you should look at the controller tutorial on the Wiki. Most of these functions are explained there. The special physics related functions are:
-
SetBody()— This function is called when a physics body is created in theInit()function. It registers the Newton body with the controller so the controller will have a reference into the Newton world if it needs to do something with the body it is controlling.
-
SetForce()— This function is called to apply an impulse force on a body. This is used now to allow C4 weapons to interact with objects without having to integrate Newton into the weapon system. Basically, when a projectile hits something, it checks to see if aPhysicsBodyControlleris attached, and if so it calls the controller'sSetForce()function, passing the impact information.
Implementing the PhysicsBodyController class
Here is the entire source code for the PhysicsBodyController class:
#include "MGGame.h"
#include "Newton.h"
#include "SJMPhysicsBodyController.h" // This is where I put my class definition
using namespace C4;
PhysicsBodyController::PhysicsBodyController() : Controller(kControllerPhysicsBody)
{
mass=1.0f;
}
PhysicsBodyController::PhysicsBodyController(const PhysicsBodyController& PhysicsBodyController) : Controller(PhysicsBodyController)
{
mass = PhysicsBodyController.mass;
}
PhysicsBodyController::PhysicsBodyController(float m) : Controller(kControllerPhysicsBody)
{
mass = m;
}
PhysicsBodyController::~PhysicsBodyController()
{
}
Controller *PhysicsBodyController::Replicate(void) const
{
return (new PhysicsBodyController(*this));
}
bool PhysicsBodyController::ValidNode(const Node *node)
{
return (node->GetNodeType() == kNodeGeometry);
}
unsigned long PhysicsBodyController::GetPackSize(unsigned long packFlags) const
{
unsigned long size = Controller::GetPackSize(packFlags);
// Add enough space to hold a float for the mass
return (size + sizeof(float));
}
void PhysicsBodyController::Pack(Packer& data, unsigned long packFlags) const
{
Controller::Pack(data, packFlags);
// Write the mass
data << mass;
}
void PhysicsBodyController::Unpack(Unpacker& data, unsigned long unpackFlags)
{
Controller::Unpack(data, unpackFlags);
// Read the mass
data >> mass;
}
long PhysicsBodyController::GetSettingCount(void) const
{
// There's only one setting
return (1);
}
Setting *PhysicsBodyController::GetSetting(long index) const
{
// Is it asking for the first setting?
if (index == 0)
{
// Yes, return a new text setting and set its value in revolutions per second
return (new TextSetting('mass', Text::FloatToString(mass),
"Body Mass in kG", 64.0F, 7, &EditableTextElement::FloatNumberKeyFilter));
}
return (nullptr);
}
void PhysicsBodyController::SetSetting(const Setting *setting)
{
// Are we setting the spin rate?
if (setting->GetSettingIdentifier() == 'mass')
{
// Yes, grab the value from the setting and convert it back to radians per millisecond
const char *text = static_cast<const TextSetting *>(setting)->GetText();
mass = Text::StringToFloat(text);
}
}
void PhysicsBodyController::Move(void)
{
Controller::Move();
}
void PhysicsBodyController::Preprocess()
{
Controller::Preprocess();
}
void PhysicsBodyController::SetForce(Vector3D& force, Vector3D& posit)
{
Vector3D impulse = force;
NewtonAddBodyImpulse(myBody,&impulse.x,&posit.x);
}
Implementing the PhysicsMgr class
Here is the source code for the entire PhysicsMgr class:
#include "MGGame.h"
#include "SJMPhysics.h"
#include "C4Primitives.h"
using namespace C4;
static NewtonWorld* nWorld;
int debugDisplay;
ColorRGBA debuglineColor;
static void* PhysicsAlloc (int sizeInBytes);
static void PhysicsFree (void *ptr, int sizeInBytes);
//*************************************************************************
// Utility Functions
//*************************************************************************
// memory allocation for Newton
void* PhysicsAlloc (int sizeInBytes)
{
return (new char[sizeInBytes]);
}
// memory de-allocation for Newton
void PhysicsFree (void *ptr, int sizeInBytes)
{
delete[] (char *) ptr;
}
//*************************************************************************
// Debug Functions
//*************************************************************************
void DebugGraphics(const NewtonBody* body, int vertexCount, const float* FaceArray, int faceId)
{
Renderable colTreeRender(kRenderLineLoop);
List<Renderable> colTree;
Point3D *p = new Point3D[vertexCount];
if(debugDisplay>0)
{
// first show the outline
p[0].x=0.0f;
int i;
for (i=0;i<vertexCount;i++)
{
p[i].x =FaceArray[i*3+0];
p[i].y =FaceArray[i*3+1];
p[i].z =FaceArray[i*3+2];
}
colTreeRender.SetTransformable(0);
colTreeRender.SetVertexCount(vertexCount);
colTreeRender.SetAttributeArray(kArrayVertex, p);
colTreeRender.SetAttributeArray(kArrayColor0, &debuglineColor);
colTree.Append(&colTreeRender);
TheGraphicsMgr->DrawRenderList(&colTree);
colTree.RemoveAll();
}
delete []p;
}
void Debug_ShowBodyCollision(const NewtonBody* body)
{
if(NewtonBodyGetUserData(body))
debuglineColor.Set(1.0F, 0.0F, 0.0F,1.0F);
else
debuglineColor.Set(0.0F, 1.0F, 0.0F,1.0F);
NewtonBodyForEachPolygonDo(body, DebugGraphics);
// now show the object center
if(debugDisplay&2)
{
Renderable colTreeRender(kRenderLines);
List<Renderable> colTree;
Node *node;
node = (Node*) NewtonBodyGetUserData(body);
if(node)
{
Point3D loc=node->GetWorldPosition();
Point3D p[2];
Point3D com;
NewtonBodyGetCentreOfMass(body,&com[0]);
p[0]=loc+com;
p[1]=loc+com;
p[1].x+=.5;
colTreeRender.SetTransformable(0);
colTreeRender.SetVertexCount(2);
colTree.Append(&colTreeRender);
colTreeRender.SetAttributeArray(kArrayVertex, p);
TheGraphicsMgr->DrawRenderList(&colTree);
colTree.RemoveAll();
p[1]=loc+com;
p[1].z+=.5;
colTreeRender.SetTransformable(0);
colTreeRender.SetVertexCount(2);
colTree.Append(&colTreeRender);
colTreeRender.SetAttributeArray(kArrayVertex, p);
TheGraphicsMgr->DrawRenderList(&colTree);
colTree.RemoveAll();
node->Invalidate();
}
}
}
//*************************************************************************
// Physics Callback Functions
//*************************************************************************
// add force and torque to rigid body
void PhysicsApplyForceAndTorque (const NewtonBody* body)
{
dFloat Ixx;
dFloat Iyy;
dFloat Izz;
dFloat mass;
// right now this only simulates gravity
NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz);
Vector3D force (0.0f, 0.0f,mass * -09.8f);
NewtonBodySetForce (body, &force[0]);
}
// set the transformation` of a rigid body
void PhysicsSetTransform (const NewtonBody* body, const dFloat* matrix)
{
Node *node;
node = (Node*) NewtonBodyGetUserData(body);
if(node)
{
Transform4D& mat = *((Transform4D*)matrix);
node->SetNodeTransform(mat);
node->Invalidate();
}
}
// rigid body destructor
void PhysicsBodyDestructor (const NewtonBody* body)
{
// destroy the graphic object
// delete primitive;
}
//*************************************************************************
// Physics Manager Class
//*************************************************************************
void DebugMgr(const char *params)
{
debugDisplay = Text::StringToFloat(params);
}
PhysicsMgr::PhysicsMgr()
{
debugDisplay = 0;
Command *pdebugCommand = new Command("pdebug", &DebugMgr);
TheSystem->AddCommand(pdebugCommand);
}
PhysicsMgr::~PhysicsMgr()
{
}
void PhysicsMgr::ShowDebug(void)
{
if(!debugDisplay)
return;
NewtonWorldForEachBodyDo(nWorld,Debug_ShowBodyCollision);
}
void PhysicsMgr::Cleanup()
{
NewtonDestroy(nWorld);
}
void PhysicsMgr::BuildCollisionMesh(World *theWorld)
{
NewtonCollision *collision= NewtonCreateTreeCollision(nWorld,NULL);
NewtonTreeCollisionBeginBuild(collision);
Node *root = theWorld->GetRootNode(); // start at the root
Node *node = root;
while (node)
{
if( (node->GetNodeType() == kNodeGeometry ) && !(node->GetController()))
{
if(1)
{
Geometry *geometry = static_cast<Geometry *>(node);
const GeometryLevel *level = geometry->GetObject()->GetGeometryLevel(0);
long vertexCount = level->GetVertexCount();
Point3D *vertex = level->GetArrayPoint3D(kArrayVertex);
long triangleCount = level->GetArrayDescriptor(kArrayFace)->elementCount;
Triangle *triangle = level->GetArrayTriangle(kArrayFace);
const GeometryObject *object = geometry->GetObject();
GeometrySpace space = object->GetGeometrySpace();
if (space != kGeometrySpaceWorld)
{
const Transform4D& transform = geometry->GetWorldTransform();
for (long i = 0; i < vertexCount; i++)
{
vertex[i] = transform * vertex[i];
}
}
for(natural i=0; i<triangleCount; i++)
{
long i1 = triangle->index[0];
long i2 = triangle->index[1];
long i3 = triangle->index[2];
float point[3][3];
point[0][0] = vertex[i1].x;
point[0][1] = vertex[i1].y;
point[0][2] = vertex[i1].z;
point[1][0] = vertex[i2].x;
point[1][1] = vertex[i2].y;
point[1][2] = vertex[i2].z;
point[2][0] = vertex[i3].x;
point[2][1] = vertex[i3].y;
point[2][2] = vertex[i3].z;
NewtonTreeCollisionAddFace(collision, 3, &point[0][0], sizeof(float)*3, 1);
triangle++;
}
if (space != kGeometrySpaceWorld)
{
const Transform4D& transform = geometry->GetInverseWorldTransform();
for (long i = 0; i < vertexCount; i++)
{
vertex[i] = transform * vertex[i];
}
}
}
}
node = root->GetNextNode(node);
}
// finish
NewtonTreeCollisionEndBuild(collision,1);
NewtonCreateBody(nWorld,collision);
NewtonReleaseCollision(nWorld,collision);
}
void PhysicsMgr::AddObjects(World *theWorld)
{
NewtonCollision *collision;
Node *root = theWorld->GetRootNode(); // start at the root
Node *node = root;
while (node)
{
if(node->GetNodeType() == kNodeGeometry && node->GetController())
{
if(node->GetController()->GetControllerType() == kControllerPhysicsBody)
{
Vector3D massOffset(0,0,0);
Point3D objCenter = node->GetBoundingSphere()->GetCenter();// * node->GetInverseWorldTransform() ;
Geometry *geometry = static_cast<Geometry *>(node);
const GeometryLevel *level = geometry->GetObject()->GetGeometryLevel(0);
long vertexCount = level->GetVertexCount();
Point3D *vertex = level->GetArrayPoint3D(kArrayVertex);
const GeometryObject *object = geometry->GetObject();
GeometrySpace space = object->GetGeometrySpace();
// assign the right primitve collision model
GeometryType geotype=geometry->GetGeometryType();
if(geotype == kGeometryPrimitive)
{
// we have a primitive, so calculate a collision primitive to match
Vector3D size;
Vector2D size2D;
float radius;
float length;
PrimitiveType primtype = static_cast<PrimitiveGeometry*>(geometry)->GetPrimitiveType();
Transform4D offsetTransform;
Point3D translation;
switch(primtype)
{
case kPrimitiveBox:
size=static_cast<BoxGeometryObject*>(geometry->GetObject())->GetBoxSize();
translation.Set(size.x/2.0f, size.y/2.0f,size.z/2.0f);
offsetTransform.SetIdentity();
offsetTransform.SetTranslation(translation);
massOffset.x=size.x/2.0f;
massOffset.y=size.y/2.0f;
massOffset.z=size.z/2.0f;
collision=NewtonCreateBox(nWorld,size.x,size.y,size.z,&offsetTransform(0,0));
break;
case kPrimitiveSphere:
size = static_cast<SphereGeometryObject*>(geometry->GetObject())->GetSphereSize();
collision = NewtonCreateSphere(nWorld,size.x,size.y,size.z,NULL);
break;
case kPrimitiveCylinder:
radius=static_cast<CylinderGeometryObject*>(geometry->GetObject())->GetCylinderSize().x;
length=static_cast<CylinderGeometryObject*>(geometry->GetObject())->GetCylinderHeight();
translation.Set(0.0f, 0.0f,length/2.0f);
offsetTransform.SetIdentity();
offsetTransform.SetRotationAboutY(K::pi / 2.0F); // cylinders are off center
offsetTransform.SetTranslation(translation);
massOffset.x=0;
massOffset.y=0;
massOffset.z=length/2.0f;;
collision=NewtonCreateCylinder(nWorld,radius,length,&offsetTransform(0,0));
break;
case kPrimitiveCone:
radius=static_cast<ConeGeometryObject*>(geometry->GetObject())->GetConeSize().x;
length=static_cast<ConeGeometryObject*>(geometry->GetObject())->GetConeHeight();
translation.Set(0.0f, 0.0f,length/2.0f);
offsetTransform.SetIdentity();
offsetTransform.SetRotationAboutY(K::pi / -2.0F); // cylinders are off center
offsetTransform.SetTranslation(translation);
massOffset.x=0;
massOffset.y=0;
massOffset.z=length/4.0f;
collision=NewtonCreateCone(nWorld,radius,length,&offsetTransform(0,0));
break;
default:
float rad = geometry->GetBoundingSphere()->GetRadius()*1.5;
collision = NewtonCreateSphere(nWorld,rad,rad,rad,NULL);
}
}
else
{
// for now, make a big box. Ultimately, this is where we
// will calculate a convex hull
float rad = geometry->GetBoundingSphere()->GetRadius()*1.5;
collision = NewtonCreateSphere(nWorld,rad,rad,rad,NULL);
}
NewtonBody *body=NewtonCreateBody(nWorld,collision);
NewtonReleaseCollision(nWorld,collision);
NewtonBodySetUserData(body, static_cast <void*>(node));
NewtonBodySetDestructorCallback (body, PhysicsBodyDestructor);
// set the transform call back function
NewtonBodySetTransformCallback (body, PhysicsSetTransform);
// set the force and torque call back function
NewtonBodySetForceAndTorqueCallback (body, PhysicsApplyForceAndTorque);
// set the mass matrix from the controller's mass setting
const float mass=Text::StringToFloat(static_cast<const TextSetting *>(node->GetController()->GetSetting(0))->GetText());
NewtonBodySetMassMatrix (body, mass, 1.0f, 1.0f, 1.0f);
const Geometry *geom = static_cast<const Geometry *>(node);
GeometryObject *geomObject = geom->GetObject();
geomObject ->SetGeometryFlags(geomObject ->GetGeometryFlags() | kGeometryDynamic);
Transform4D matrix;
matrix=node->GetWorldTransform();
NewtonBodySetMatrix (body, &matrix(0,0));
NewtonBodySetCentreOfMass(body,&massOffset.x);
static_cast <PhysicsBodyController*>(node->GetController())->SetBody(body,nWorld);
}
}
node = root->GetNextNode(node);
}
// finish
}
void PhysicsMgr::Init(void)
{
// create the newton world
nWorld = NewtonCreate (PhysicsAlloc, PhysicsFree);
// set the linear solver model for faster speed
NewtonSetSolverModel (nWorld, 8);
// set the adpative friction model for faster speed
NewtonSetFrictionModel (nWorld, 1);
// Set the termination function... we'll force cleanup when the world is unloaded.
atexit(NULL);
BuildCollisionMesh(TheWorldMgr->GetWorld());
AddObjects(TheWorldMgr->GetWorld());
}
// This function should be called from the render function in the game module
void PhysicsMgr::Update(void)
{
float dt=TheTimeMgr->GetFloatDeltaTime();
NewtonUpdate(nWorld,dt / 1000.0F);
}
// This function should be called whenever the world is unloaded.
void PhysicsMgr::Destroy(void)
{
NewtonDestroy(nWorld);
}
Breaking it down
The PhysicsMgr class is responsible for giving the C4 engine its interface to the Newton Dynamics SDK. It has three main functions used for that purpose: Init(), Update(), and Destroy()
Init()
void PhysicsMgr::Init(void)
{
// create the newton world
nWorld = NewtonCreate(PhysicsAlloc, PhysicsFree);
// set the linear solver model for faster speed
NewtonSetSolverModel(nWorld, 8);
// set the adpative friction model for faster speed
NewtonSetFrictionModel(nWorld, 1);
// Set the termination function... we'll force cleanup when the world is unloaded.
atexit(NULL);
BuildCollisionMesh(TheWorldMgr->GetWorld());
AddObjects(TheWorldMgr->GetWorld());
}
The Init() function is called by the game code to create a physics world and use the node tree to construct the static collision geometry and dynamic physics objects that do into that world.
The first thing Init() does is to create the newton physics world (nWorld). nWorld is a pointer to the data structure created internally by Newton. Since Newton has the ability to support multiple worlds, this pointer is used as a reference for many of the construction and registration function calls we will make to create and simulate the physics world.
The next thing the function does is to set the solver and friction models. Currently, these models are configured for the best execution time for game speed. Different models can be used to improve simulation accuracy at the expense of speed.
Since Newton provides a C-style interface, call-back functions are utilitized frequently to provide hooks into the main program for functions that Newton requires. The atexit() function is provided by Newton to register a callback function that will be called on program exit. In the demos, this callback (called Cleanup()) is used to destroy the Newton world. However, in our case, we may initialize and destroy the Newton world many times per program execution as the player loads and exits games. Therefore we set the callback function to NULL. Cleanup is performed int the Destroy() function. It should be noted that atexit() does not appear to be documented in the Newton SDK help file and does not have the typical Newton prefix. It is the only function in the API I have found that is that way.
The next two function calls are to internal class methods: BuildCollisionObjects() and AddObjects(). These methods actually traverse the node tree and registers all static and dynamic geomtery. BuildCollisionObjects() traverses the tree looking for geometry without a PhysicsBodyController attached and registers that geometry as static bodies. AddObjects() traverse the tree looking for geometry with the PhysicsBodyController attached and registers this geometry as individual dynamic bodies.
It should be noted that registering collision geometry this way is very time consuming and unsuitable for large levels. The first optimization I am going to work on will be a method to construct the collision mesh dynamically in the world editor and save it to a data file. Then, when the world is loaded, that file will be opened and the collision data will be read in. Newton provides serialization routines for this purpose.
Update()
void PhysicsMgr::Update(void)
{
float dt=TheTimeMgr->GetFloatDeltaTime();
NewtonUpdate(nWorld,dt / 1000.0F);
}
The Update() function should be called in the Games rendering loop. It measures the time since the last frame and pass that to Newton via the NewtonUpdate() function call. C4 measures time in milliseconds, and Newton expects the timestep in seconds so a conversion is done in the function call (dividing dT by 1000.0f).
Destroy()
void PhysicsMgr::Destroy(void)
{
NewtonDestroy(nWorld);
}
The Destroy() function should be called when the game world is unloaded. When called, this function will call NewtonDestroy(nWorld) which will release all structures and other memory that has been allocated to nWorld by Newton.
Hooks into existing game code
MGGame.h
Add this code immediately after the other includes:
#include "SJMPhysics.h" #include "SJMPhysicsBodyController.h"
MGGame.CPP
Add this code to the top of the file, right after the namespace declaration:
// SJMHACK PhysicsMgr ThePhysicsMgr; // ENDHACK
Add this code to Game::StartSinglePlayer() right underneath TheWorldMgr->LoadWorld(name);
// STEVE HACK - Iterate through the nodes and find the geometry nodes ThePhysicsMgr.Init(); // END STEVE HACK
Add this code to the end of the GameWorld::Render() function:
// SJMHack ThePhysicsMgr.ShowDebug(); ThePhysicsMgr.Update(); // ENDHACK
Add this code underneath the other controller registrations in the Game::Game():
// SJM - Physics engine hooks static ControllerRegistration physicsbodyControllerReg(kControllerPhysicsBody, "Pbod", &PhysicsBodyController::ValidNode); Controller::RegisterController(&physicsbodyControllerReg);
Add this code to Game::ConstructController() underneath the last case statement:
// SJM - Phyics Hooks
case kControllerPhysicsBody:
return (new PhysicsBodyController);
// ENDSJM
Lastly, add this code to the Game::ExitCurrentGame() function:
//SJM ThePhysicsMgr.Destroy(); // ENDSJM
MGWeapons.cpp
Add this code to the beginning of the ShotController::ProcessGeometryProperties() function:
// SJM Hack;
const CollisionData *data = GetCollisionData();
const Geometry *geometry = data->geometry;
if (geometry->GetController())
{
if(geometry->GetController()->GetControllerType() == kControllerPhysicsBody)
{
Point3D zerovec = geometry->GetWorldPosition();
// OK we have a physics node, lets apply some force to it.
PhysicsBodyController *physicsController = static_cast<PhysicsBodyController *>(geometry->GetController());
Vector3D force = GetCollisionVelocity();
physicsController->SetForce(force * shotImpulse * 5.0F, zerovec);
}
}
// ENDSJM
Creating Newton Objects from C4 Nodes
Now to the fun stuff: actually making stuff move
Static Geometry
To do that we first need static collision geometry to constrain our moving objects in the world. This is done in BuildCollisionMesh(TheWorld->GetWorld()).
The first thing we need to know how to do is traverse the C4 node tree and find all geometry objects without a PhysicsBodyController.
Node *root = theWorld->GetRootNode(); // start at the root
Node *node = root;
while (node)
{
if(node->GetNodeType() == kNodeGeometry && !node->GetController())
{
if(node->GetController()->GetControllerType() == kControllerPhysicsBody)
{
....
}
}
node = root->GetNextNode()
}
The idea here is to find a node, make sure it is geometric, and see if it has a PhysicsBodyController attached. If it meets all of these requirements, then the node needs to be registered as static collision geometry with Newton. For the purposes of static geometry, we are going to create one big collision structure called a collision tree as follows:
NewtonCollision *collision= NewtonCreateTreeCollision(nWorld,NULL); NewtonTreeCollisionBeginBuild(collision);
Then, for each node that is static geometry, we are going to register the triangles that make up that node's geometry with the collision tree:
Step 1: Get the triangles to register and the triangle count:
Geometry *geometry = static_cast<Geometry *>(node);
const GeometryLevel *level = geometry->GetObject()->GetGeometryLevel(0);
long vertexCount = level->GetVertexCount();
Point3D *vertex = level->GetArrayPoint3D(kArrayVertex);
long triangleCount = level->GetArrayDescriptor(kArrayFace)->elementCount;
Triangle *triangle = level->GetArrayTriangle(kArrayFace);
This code shows some important principles. Firstly, there are many types of nodes, Geometry being one of them. Since we know from the earlier test that this node is geometric in type, we can cast it to a <Geometry> node and get access to the additional information we need, such as the vertex count, the vertex array, the triangle count, and the triangle array.
VERY IMPORTANT - we are not working with a copy of the node's data in this code. When we modify the vertices later, we will be modifying the same ones C4 uses to render the object. ALWAYS make sure you undo your changes before giving control back to C4.
Step 2: Convert vertices to World Space
const GeometryObject *object = geometry->GetObject();
GeometrySpace space = object->GetGeometrySpace();
if (space != kGeometrySpaceWorld)
{
const Transform4D& transform = geometry->GetWorldTransform();
for (long i = 0; i < vertexCount; i++)
{
vertex[i] = transform * vertex[i];
}
}
For Newton, we want to register our static collision faces in world space. This makes sense because world space is intended for static objects that don't move. So if we find something we consider static that is in object space (which will be the default for objects created in the world editor) we have to convert the vertices of that object to world space. This is done by multiplying the vertex by node->GetWorldTransform (note that in this case geometry is used instead of node).
Step 3: Register triangles
for(natural i=0; i<triangleCount; i++)
{
long i1 = triangle->index[0];
long i2 = triangle->index[1];
long i3 = triangle->index[2];
float point[3][3];
point[0][0] = vertex[i1].x;
point[0][1] = vertex[i1].y;
point[0][2] = vertex[i1].z;
point[1][0] = vertex[i2].x;
point[1][1] = vertex[i2].y;
point[1][2] = vertex[i2].z;
point[2][0] = vertex[i3].x;
point[2][1] = vertex[i3].y;
point[2][2] = vertex[i3].z;
NewtonTreeCollisionAddFace(collision, 3, &point[0][0], sizeof(float)*3, 1);
triangle++;
}
This code can be kind of confusing at first. Think about the fact that all of the triangles in the object are going to share some combinations of the vertices stored in the object. So to find out which vertex a particular triangle uses, use triangle->index[x] where x can be 0 through 2.
A float array is used to store the points of the face so that we can pass Newton an ordered list of the points (that would be impossible to do with the vertices we got from the object).
Once we have the point array filled with the three vertices for the triangle, a call to NewtonTreeCollisionAddFace() registers the face with the collision object we created in step 1.
Step 4: Undo the changes to the node
if (space != kGeometrySpaceWorld)
{
const Transform4D& transform = geometry->GetInverseWorldTransform();
for (long i = 0; i < vertexCount; i++)
{
vertex[i] = transform * vertex[i];
}
}
This is absolutely required. If we don't do this, we will have translated all object-space objects to world space and they will not render properly.
Lastly, once we have traversed the entire node tree and build our entire collision tree, we end the build and register the body as follows:
NewtonTreeCollisionEndBuild(collision,1);
NewtonCreateBody(nWorld,collision);
NewtonReleaseCollision(nWorld,collision);
Notice that no transforms are required because this geometry is already in world space.
Dynamic Geomtery
Dynamic geometry objects are registered in AddObjects()
This code works a lot like the collision geometry code except that it tests for a PhysicsBodyController being attached before it processes a node. That is the indication to the code that the world designer intends this node to behave with physics properties.
Since the node iteration code and data extraction code are essentially the same as earlier, I am not going to go over them here. One difference I will note is that an object that is dyanmic must be in object space to work correctly. There is currently no error checking for that, so it is possible for the world designer to assign a PhysicsBodyController to a world-space node. The transforms will be wrong and it won't work, do just don't do it.
The current implementation is designed to work best with primitive geometric shapes (boxes, cylinders, etc..) I will update it in the future to also support arbitrary convex collision geometry from Collada for models, but that isn't there right now. So let's focus on the primitives.
In C4, the only primitive that is drawn with 0,0,0 being its true center is the sphere. The box has 0,0,0 on corner, the cylinder is zero'd at the center of one end, and so on. Furthermore, the primitive shapes created by Newton and C4 are for the most part 90 degrees rotated from each other on the Y axis.
So let's examine the code that makes a Newton primitive match up with a C4 primitive so we can use it in the world:
if(geotype == kGeometryPrimitive)
{
// we have a primitive, so calculate a collision primitive to match
Vector3D size;
PrimitiveType primtype = static_cast<PrimitiveGeometry*>(geometry)->GetPrimitiveType();
Transform4D offsetTransform;
Point3D translation;
switch(primtype)
{
case kPrimitiveBox:
size=static_cast<BoxGeometryObject*>(geometry->GetObject())->GetBoxSize();
translation.Set(size.x/2.0f, size.y/2.0f,size.z/2.0f);
offsetTransform.SetIdentity();
offsetTransform.SetTranslation(translation);
massOffset.x=size.x/2.0f;
massOffset.y=size.y/2.0f;
massOffset.z=size.z/2.0f;
collision=NewtonCreateBox(nWorld,size.x,size.y,size.z,&offsetTransform(0,0));
break;
.....
In this code, we check the primitive type of the C4 node. I am only showing the code for the box, but the complete source above shows everything. Notice that before we deal with the individual primitives, offsetTransform and translation are declared. Remember how I said earlier that Newton doesn't always create primitives with the same offset and rotation as C4? Well, these two guys are going to help us fix that.
The first thing we do is to cast the geometry object to a BoxGeometryObject so that we can get the size of the box. With the size in hand, the translation is set to be exactly 1/2 the size of the box in all directions. That will offset the 0,0,0 center of the box so that the C4 box and the Newton box line up.
The translation is then used to set the transform. In this case, no rotiation is needed, so the transform is basically set to identity with a translation. If this were a cylinder, I would have rotated the transform pi/2 in the Y axis.
The massOffset vector is a necassary hack to deal with the fact that Newton always expects 0,0,0 to reflect the center of gravity. However, since we know that boxes have 0,0,0 on their corner, that is not the case. If we left the center of mass alone, the boxes would simulate with the COG on a corner. Believe me, it is ugly to watch. So the way we fix it is to calculate a COG offset that will move the COG to the actual center of the box. Hence, massOffset.
Now that we have a box collision object with its initial offset transform set and a properly calculated massOffset vector in hand, we can register the shape as a Newton body and get it ready for simualtion.
const Geometry *geom = static_cast<const Geometry *>(node); GeometryObject *geomObject = geom->GetObject(); geomObject ->SetGeometryFlags(geomObject ->GetGeometryFlags() | kGeometryDynamic);
This code is executed to make sure that C4 knows that the geometry is dynamic. If the flag is not set, then lighting and shadows won't work as the object moves. Its ugly, so don't forgot to set kGeometryDynamic
Transform4D matrix; matrix=node->GetWorldTransform(); NewtonBodySetMatrix (body, &matrix(0,0)); NewtonBodySetCentreOfMass(body,&massOffset.x); static_cast <PhysicsBodyController*>(node->GetController())->SetBody(body,nWorld);
Lastly, we make the dynamic body. Although we built the collision body in object space, we have to register its tansform in world space so that Newton can properly place the object in its world. The rest of the code should explain itself.
Physics Callback Functions
Debug Graphics
Newton uses a special set of iterating callbacks to support debug renderings. The process is started with a call to NewtonWorldForEachBodyDo(nWorld,Debug_ShowBodyCollision) where Debug_ShowBodyCollision is a function that will be called once for each body in the world.
void Debug_ShowBodyCollision(const NewtonBody* body)
{
if(NewtonBodyGetUserData(body))
debuglineColor.Set(1.0F, 0.0F, 0.0F,1.0F);
else
debuglineColor.Set(0.0F, 1.0F, 0.0F,1.0F);
NewtonBodyForEachPolygonDo(body, DebugGraphics);
// .... Show Body-wise debug information here
}
The second iterator (NewtonBodyForEachPolygonDobody, DebugGraphics);) is called in the Debug_ShowBodyCollision callback. This iterator will call the DebugGraphics callback for every polygon in the body.
The real work is done in DebugGraphics:
Renderable colTreeRender(kRenderLineLoop);
List<Renderable> colTree;
Point3D *p=new Point3D[vertexCount];
if(debugDisplay>0)
{
// first show the outline
p[0].x=0.0f;
int i;
for (i=0;i<vertexCount;i++)
{
p[i].x =FaceArray[i*3+0];
p[i].y =FaceArray[i*3+1];
p[i].z =FaceArray[i*3+2];
}
colTreeRender.SetTransformable(0);
colTreeRender.SetVertexCount(vertexCount);
colTreeRender.SetAttributeArray(kArrayVertex, p);
colTreeRender.SetAttributeArray(kArrayColor0, &debuglineColor);
colTree.Append(&colTreeRender);
TheGraphicsMgr->DrawRenderList(&colTree);
colTree.RemoveAll();
}
delete []p;
Since the vertices passed by Newton are already in world space, no transforms are required. We just create an array of points, populate it with the vertices from Newton, and use the renderer to draw a line loop. Simple, right??
PhysicsApplyForceAndTorque
This callback is registered with Newton for each dynamic body. Right now, it simply applies gravity to the body.
void PhysicsApplyForceAndTorque (const NewtonBody* body)
{
dFloat Ixx;
dFloat Iyy;
dFloat Izz;
dFloat mass;
// right now this only simulates gravity
NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz);
Vector3D force (0.0f, 0.0f,mass * -09.8f);
NewtonBodySetForce (body, &force[0]);
}
PhysicsSetTransform
This callback is also registered with Newton for each dynamic body and is called once per simulation for all active bodies. It passes the body pointer and a transform matrix for the body. The callback function uses this information to update the transform matrix for the corresponding C4 object.
Note that we are storing a pointer to the C4 node with each Newton body. This pointer is retrieved by calling NewtonBodyGetUserData() . Forgive the c-style casting. It is safe in this case, I promise.
All in all, this code is relatively simple and self-explanatory. Send me a PM or post a message if you have any problems with it.
void PhysicsSetTransform (const NewtonBody* body, const dFloat* matrix)
{
Node *node;
node = (Node*) NewtonBodyGetUserData(body);
if(node)
{
Transform4D& mat = *((Transform4D*)matrix);
node->SetNodeTransform(mat);
node->Invalidate();
}
}
Utility Functions
When you create the Newton world using NewtonCreate (PhysicsAlloc, PhysicsFree) you can specify memory allocation functions you want Newton to use or pass NULL pointers. If you pass NULL, Newton uses its internal functions. I am not sure what the trade-offs are but I decided it would probably be better to use C4 scoped functions that I could watch in case there were problems. I borrowed the Alloc and Free functions from a post Eric had made on the forums.
// This function should be called from the render function in the game module
void PhysicsMgr::Update(void)
{
float dt=TheTimeMgr->GetFloatDeltaTime();
NewtonUpdate(nWorld,dt / 1000.0F);
}
// This function should be called whenever the world is unloaded.
void PhysicsMgr::Destroy(void)
{
NewtonDestroy(nWorld);
}
Source Files
You can download my source files here: SJM Physics Source Files. Use them any way you want. You have my blessing.

