在Ogre中使用Newton物理引擎SDK

龙佐
2023-12-01

Using Ogre with the Newton Game Dynamics physics SDK

Introduction

This tutorial is intended to explain how to setup a simple project using Ogre version 1.0.0, to work with the Newton Game Dynamics physics SDK, currently version 1.31.

Requirements:

  1. For this tutorial I will assume you have Ogre 1.1.x (Dagon) installed on your machine, and compiling and running properly. There are plenty of other tutorials for that! Note that if you have version 1.0.x of Ogre, this tutorial will work as well, with one small change (noted below).
  2. I will also assume you have installed the Newton Game Dynamics (from here on referred to as "Newton") on your system as well. If you haven't, however, just go to http://www.newtondynamics.com and download the SDK. It's free.
  3. Finally, I will also be using my "OgreNewt" library to connect Ogre and Newton. OgreNewt is available as a part of "ogreaddons" in the Ogre CVS, but is also available from my website walaber.com. As we go through the tutorial I will also explain the basics of Newton, and how OgreNewt implements itself.
  4. Now might be a good idea to download the Media pack for this tutorial.

Getting Started

The first thing we need to do, is get OgreNewt compiled, and up and running. I will be assuming you have a directory tree setup like below:

directory structure:

<some_dir>/OGRE/ogrenew                <- ogre install dir
<some_dir>/OGRE/ogreaddons/OgreNewt/   <- OgreNewt install dir.
<some_dir>/NewtonSDK                   <- Newton SDK install dir
<some_dir>/tinyxml                     <- Tinyxml library (used only for demo08, not in OgreNewt itself)

If your directory tree is different, you will need to modify the OgreNewt project files to get it to compile properly.

Okay, so open up VC++, and load the "OgreNewt" solution. You should see 9 total projects:

  1. OgreNewt_Main - this is the library itself, where all classes are defined.
  2. Demo01_TheBasics - an extremely simple demo, where you can throw objects at a solid object, and watch them interact.
  3. Demo02_Joints - another simple demo, showing TreeCollision bodies, and simple Joints.
  4. Demo03_CollisionCallbacks - how to use advanced collision callbacks to control object behavior through the example of a conveyor belt.
  5. Demo04_Raycasting - using raycasting with rigid bodies
  6. Demo05_SimpleVehicle - how to use the Newton vehicle system
  7. Demo06_SimpleBuoyancy - how to use the Newton buoyancy system
  8. Demo07_CustomJoints - simple example of how to make your own joints with OgreNewt.
  9. Demo08_RagdollExample - an example of how to make skinned ragdolls using OgreNewt.

Assuming your directory structure is set up like I have shown above, you should be able to compile the solution right away with no problems. Do so now. Note that you will have to have tinyxml compiled properly (use the STL version) to get demo08 to compile properly.

Now you can navigate to the "OgreNewt/Bin/Debug" or "OgreNewt/Bin/Release" directories, and there should be the executables for the demos there. Copy over the standard Ogre .dll files, as well as the newton.dll file from your Newton SDK directory, and try running the demos.

DEMO CONTROLS:

  1. MOUSE to look around
  2. ARROW KEYS to move / strafe
  3. SPACE BAR to throw objects
  4. ESC to quit.

Okay, if you've made it this far, you're ready to try and make your own simple OgreNewt application. I'll be using the standard ExampleApplication and ExampleFrameListener classes used in most Ogre tutorials. if you've never made an Ogre application with these classes, you might want to read through a few general Ogre tutorials to get up to speed first.

Okay... so first let's set up our Application class. Create a new project and add a new header file called "OgreNewtApplication.h". Open it up and set up the basic class outline like below:


OgreNewtApplication.h

#ifndef _OGRENEWTAPPLICATION_
#define _OGRENEWTAPPLICATION_

#include "ExampleApplication.h"

class OgreNewtApplication :
	public ExampleApplication
{
public:
	OgreNewtApplication(void);
	~OgreNewtApplication(void);

	void createScene();

	void createFrameListener(void);
};

This should look familiar... it's just a standard inherited ExampleApplication class. First we need to set up the various include directories for our project. in the C/C++ section of the project settings, make sure the following directories are in the "include directories" list:

include directories

ogrenew/ogremain/include
ogrenew/samples/common/include
ogreaddons/OgreNewt/OgreNewt_Main/inc
ogreaddons/OgreNewt/demos/Include
NewtonSDK/sdk

library directories

ogrenew/ogremain/lib/debug <or release>
ogreaddons/OgreNewt_Main/lib/debug <or release>
NewtonSDK/dll/

Now we need to add the various pieces necessary to make a Newton application. first, let's include the OgreNewt main header:

#include <OgreNewt.h>

okay, simple enough. now add the following line to a "private:" section of the class:

OgreNewt::World* mWorld;

if you have autocomplete set up in your IDE, you should see a bunch of classes and namespaces pop up when you type "OgreNewt::". Let's now take a minute and talk about the Newton SDK, and how it works.

Newton SDK in a Nutshell

This section is a quick introduction to the Newton SDK, and how it works. it is by no means a substitute to the SDK documentation, which is included with the SDK. My OgreNewt library is pretty much a 1:1 conversion of the Newton functions into a class-based environment, so most of the function descriptions in the Newton documentation apply directly to OgreNewt.

Anyway, Newton has a few basic elements used to describe the physics world... they are:

WORLD (OgreNewt::World) 
This is the "space" in which all objects exist. all other objects require a World to be created, and are contained within that world. in most applications, you will only need 1 World object, inside which you will place all other objects. however the system allows for multiple worlds to co-exist if desired. this tutorial will only use 1 world.
RIGID BODY (OgreNewt::Body) 
This is the basic object in the physics world. it represents a solid (rigid) body, which can interact with other bodies in the scene. Bodies can have mass, size, and shape. Basically everything you want to be affected by physics calculations needs a Body.
COLLISION (OgreNewt::Collision) 
This is an object that represents a certain shape. Rigid Bodies (above) require a Collision object to define their shape. You can re-use Collision objects. so for example if you want 100 different crates in your scene (that are all the same size), you would create 1 Collision object, and use it when creating all 100 seperate Body objects. Newton supports several different ways to describe collision. they are:
  • PRIMITIVE SHAPES : these include: Boxes, Ellipsoids, Cylinders, Capsules, Cones, and Chamfer Cylinders (closed donuts).
  • CONVEX HULLS : Convex hulls are a more general primitive type. they take a series of points in space, and create the smallest possible convex shape based on all of those points. in most cases, you would use the vertices that makeup a model for the points. this results in a primitive shape that looks something like your 3D model wrapped up in wrapping paper.
  • TREE COLLISIONS : the name may not be too obvious, but "TreeCollision" objects are just general polygon collision. for example if you have a large city made of polygons, you can create a collision object from it. note that TreeCollision objects are special in that they CANNOT be used for active rigid bodies (aka movable bodies). all Bodies created from a TreeCollision will automatically have infinite mass, and therefore be completely immobile. they are best used for "background" objects that never need to move. for dynamic, moving objects, you must use convex hulls or the general primitives.
  • Note that you can combine any of the above collisions to create "CompoundCollision" objects to define more complex shapes.
  • On this page of Newton Wiki are pictures of collision primitives.

 

JOINT (OgreNewt::Joint) 
Joints are connections between 2 Bodies that affect the way they interact. for example you can connect 2 bodies together with a hinge joint to create a door.
MATERIAL (OgreNewt::MaterialID && OgreNewt::MaterialPair) 
Materials are how Newton lets you adjust the interaction between bodies when they collide. This can be as simple as adjusting the friction, or much more complex. The material system is pretty simple. First you create "MaterialID" objects to represent each material you might need in your system. Examples of common materials might be: wood_mat, metal_mat, plastic_mat, or player_mat, etc.

Then you build what is called a "MaterialPair". A material pair is a description of what happens when 2 materials collide with each other. For example, you might create a material pair for collisions between iron and steel. Then you will know any time an "iron" and "steel" object come in contact. You can then create sparks, or sound effects, etc.


Okay, those are the big, basic objects that are used in Newton. Note that there are also special objects for creating ragdolls and vehicles as well. See the Newton documentation for more information on them. At this time, OgreNewt has no special implementation for these objects.

The Newton World

okay, so back to our OgreNewtApplication class. as you can see, we've now added a pointer to an OgreNewt::World, which will be the main World in which we place our rigid bodies. now create a new OgreNewtApplication.cpp source file, and add the basic elements necessary. you should end up with something looking like this:

OgreNewtApplication.cpp

#include "./ogrenewtapplication.h"

OgreNewtApplication::OgreNewtApplication(void)
{
}

OgreNewtApplication::~OgreNewtApplication(void)
{
}

void OgreNewtApplication::createScene()
{

}

void OgreNewtApplication::createFrameListener()
{

}

now, let's create the Newton world in the constructor for the application class. add this line:

mWorld = new OgreNewt::World();

this line creates the OgreNewt::World object. you need to create this object before creating any other Newton objects, as they will all require you pass a pointer to a World object. you might also want to add a line to delete the world in the destructor:

delete mWorld;

Now there's one more thing we want to add to the Application class while we're here: a frame listener. in most demos, they have you create a FrameListener class inherited from ExampleFrameListener. We'll do the same in this demo, but also add a special FrameListener just for updating and debugging the Newton world. First, setup the standard ExampleFrameListener class, in your createFrameListener() function like so:

OgreNewtApplication.cpp

void OgreNewtApplication::createFrameListener()
{
	mFrameListener = new ExampleFrameListener( mWindow, mCamera );
	mRoot->addFrameListener( mFrameListener );
}


now we'll add a frame listener to update the physics, and provide physics debugging features. Luckily, I've included such a frame listener in the OgreNewt library, called "BasicFrameListener". add a member pointer variable in the Application class like this:

OgreNewt::BasicFrameListener* mOgreNewtListener;

and then, add these lines to the createFrameListener() function:

mOgreNewtListener = new OgreNewt::BasicFrameListener( mWindow, mCamera, mSceneMgr, mWorld, 120 );
mRoot->addFrameListener( mOgreNewtListener );

the OgreNewt::BasicFrameListener is a simple frame listener that updates the Newton world each frame, with a simple time-slicing method, which means you can control the rate at which the physics update. the last paramater you pass to the constructor is the desired update fps for the physics system. by adding this FrameListener to your application, the physics will automatically update. Also, the frame listener has a simple debug system implemented, which I'll discuss later. note that for more complex applications, it would probably be better to implement your own frame listener for the physics, which gives more control.


Okay, so everything is all set up. now let's build a simple Newton scene. For this demo, we're going to make a very simple scene, which creates a basic floow (out of a box), and drops several primitive objects onto the ground.

so let's get started. first let's make a simple ground. first we'll make a standard visual object, like any other in Ogre, and then add the physics Rigid Body "behind it". Now would be a good time to download the MESH PACK for this tutorial, and unzip the .mesh files somewhere your Ogre program will find them. okay, first add the mesh as usual:

Ogre::Vector3 size(10.0,1.0,10.0);
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
Ogre::Entity* ent = mSceneMgr->createEntity( "floorobj", "box.mesh" );
node->attachObject( ent );
node->setScale( size );

okay, now let's make the Rigid Body that represents this object. it's actually pretty simple: first we make a collision object that represents the shape:

OgreNewt::Collision* col = new OgreNewt::CollisionPrimitives::Box( mWorld, size );

then, we make a Rigid Body object from this collision object.

OgreNewt::Body* floorbody = new OgreNewt::Body( mWorld, col );

notice that we also must pass a pointer to our Newton World object as well when making these objects. now our body is created, finally we need to "connect" the Rigid Body, to the visual 3D object attached to the SceneNode. what does this mean? well, Newton has a really interesting system of callbacks, which means that if you setup your scene properly, Newton will automatically update the position and rotation of objects for you! for example, let's say you have 50 different objects in a scene. when you ask Newton to update the physics, it calculates the new position and rotation of all of the objects in the scene, based on physical laws. with many physics engines, you then need to perform some kind of loop, which goes through all of the physics bodies in the scene, gets their orientation, and applies this to their "visual" object, to match them up. However Newton has a callback system built in, which does this for you. this is ultimately very cool because it's automated, and also more efficient, because Newton only calls the callback for bodies that are moving, bodies that have not moved since the last update are properly ignored. Anyway, the way this is implemented in OgreNewt is through the "attachToNode()" member function of the OgreNewt::Body class. basically this function says "this SceneNode has some meshes attached to it, which are a visual representation of this Rigid Body. when this Rigid Body moves, please also move this SceneNode". it's as simple as that. here's how it's done:

floorbody->attachToNode( node );


finally, we want to set the initial position of our floor. let's place it in the center of the world, down 5 units on the Y axis, like so:

floorbody->setPositionOrientation( Ogre::Vector3(0,-5,0), Ogre::Quaternion::IDENTITY );
delete col;

this member function manually sets the position and orientation of a RigidBody, as well as the SceneNode attached to it (if any). You'll notice we also delete the Collision object, as we don't need it anymore.

now our floor is complete. next, let's make a few other primitive shapes to fall onto the floor! First let's make a simple cylinder-shaped body. The first few steps are exactly like above:

// CYLINDER BODY
// cylinder with a radius of 0.5, height of 1.3
size = Ogre::Vector3( 1.3, 0.5, 0.5 );
node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
ent = mSceneMgr->createEntity("cylinder_body", "cylinder.mesh" );
node->attachObject( ent );
node->setScale( size );

// rigid body.
col = new OgreNewt::CollisionPrimitives::Cylinder( mWorld, 0.5, 1.3 );
OgreNewt::Body* bod = new OgreNewt::Body( mWorld, col );
bod->attachToNode( node );

// initial position
bod->setPositionOrientation( Ogre::Vector3(-2,3,2), Ogre::Quaternion::IDENTITY );
delete col;

okay, all of that should look familiar, it's very similar to the Floor code. however, we want this body to be "dynamic", in the sense that it should be affected by gravity, and fall down, hitting other objects, and generally impressing us. To do so, we need to define a few other properties of the Rigid Body, namely Mass and Moment of Inertia. Mass is simple, you can use any units you see fit, although personally I recommend using Kilograms as a standard. Inertia might not be so intuitive, as it's a value that represents an objects resistance to rotation around a specific axis. many factors can affect this value, and getting truly accurate values is beyond me. however, there are some handy formulas available on the internet for calculating the moment of inertia for certain primitive shapes. what's even more handy, is that I've included these functions right inside OgreNewt. there just happens to be a function for cylinders, so we'll use it to calculate the inertia values for us, based on the mass and size of the object. here's how we set it up:

Ogre::Real mass = 10.0;
Ogre::Vector3 inertia = OgreNewt::MomentOfInertia::CalcCylinderSolid( mass, 0.5, 1.3 );

bod->setMassMatrix( mass, inertia );

okay. now the object has mass! however if you were to compile and run your program right now (go ahead and try if you want!), the body would not fall to the ground. this is because we haven't applied any forces to the object, to set it in motion. in Physics simulations, the most common force is gravity. so how do we add gravity with Newton?

Well, in a similar fashion to the callback that positions and orients visual objects for you, Newton also provides a callback for applying forces to a body. this way, you can create different general callbacks, which apply some kind of force to a body, and by simply setting the callback for the body, you can adjust the forces that act upon it. I have yet to fully implement the details of adding custom callbacks into OgreNewt, but it's not too difficult to implement if you read the documentation for Newton, and have a look at the OgreNewt source code. however I have included a "basicForceTorqueCallback" right into the OgreNewt::Body class, which applies a constant gravitational (-Y) force of 9.8units/sec^2 to the body. to set this callback, simply do the following:

bod->setStandardForceCallback();

and voila! your Rigid Body now has gravity. okay, that's the basic setup for making a rigid body in this tutorial. let's now add a few more bodies to the scene, following the same steps we have seen above:

// CONE BODY
// cone with a radius of 0.8, height of 1.0
size = Ogre::Vector3( 1.0, 0.8, 0.8 );
node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
ent = mSceneMgr->createEntity("cone_body", "cone.mesh" );
node->attachObject( ent );
node->setScale( size ); 
 
// rigid body.
col = new OgreNewt::CollisionPrimitives::Cone( mWorld, 0.8, 1.0 );
bod = new OgreNewt::Body( mWorld, col );
bod->attachToNode( node );  

// initial position
bod->setPositionOrientation( Ogre::Vector3(2,3,2), Ogre::Quaternion::IDENTITY );
delete col;

mass = 10.0;
inertia = OgreNewt::MomentOfInertia::CalcCylinderSolid( mass, 0.5, 1.3 );
bod->setMassMatrix( mass, inertia );
bod->setStandardForceCallback();


// BOX BODY
// standard 1x1x1 cube.
size = Ogre::Vector3( 1, 1, 1 );
node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
ent = mSceneMgr->createEntity("box_body", "box.mesh" );
node->attachObject( ent );
node->setScale( size );

// rigid body.
col = new OgreNewt::CollisionPrimitives::Box( mWorld, size );
bod = new OgreNewt::Body( mWorld, col );
bod->attachToNode( node );

// initial position
bod->setPositionOrientation( Ogre::Vector3(0,3,-2),Ogre::Quaternion::IDENTITY );
delete col;

mass = 10.0;
inertia = OgreNewt::MomentOfInertia::CalcBoxSolid( mass, size );
bod->setMassMatrix( mass, inertia );
bod->setStandardForceCallback();

Okay, that's it! don't forget to also add a light to your scene, and possibly set a good starting point for the camera. Now compile and run your project, you should have 3 objects that fall and bounce on the ground we made.

This should be enough to get you started with Ogre and Newton, using my OgreNewt library... although I think the main ideas in the tutorial would work for your own implementation of Newton in Ogre as well. Good luck, and good physics!

  • finally, you can download a copy of this entire project, including all media, and the MSVC++ (7.1) project files here

 

Edit by Persoontje: If you get an error like this:

Unhandled exception at 0x00423d76 in newtontest.exe: 0xC0000005: Access violation reading location 0xfeef003e In the line: m_debugnode->detachAllObjects();

You need to deinit the debugger. To do this, add

OgreNewt::Debugger::getSingleton().deInit();

to the destructor of your application class.

 类似资料: