Integrating the Newton Dynamics Engine

From C4 Engine Wiki

Jump to: navigation, search
Image:warning.jpg Under Construction

This article is still under construction and may contain sections that are incomplete.

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.


Thanks,

Steve Montgomery (aka FlashJackson)

Contents

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 the Newton.dll in the same directory as your C4 executeable. Then you need to put Newton.lib and Newton.h either in the Game Code directory 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 the Newton.h and Newton.lib files into.
Step 3. Click on the linker folder and in the General pane, add ..\..\..\newton under Additional Library Directories.
Step 4. Click on the Input pane under the Linker folder. Add Newton.lib to Additional dependencies.
Step 5. Make sure you have the Newton.dll file in the same directory as your C4.exe file.
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 the Game::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 the Update() 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 the Init() 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 a PhysicsBodyController is attached, and if so it calls the controller's SetForce() 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.

Personal tools