Using PLSM2 with Newton        

Here are the steps needed to send terrain data from the PLSM2 plugin to the Newton physics engine. We'll be using the OgreNewt wrapper by Walaber for this, make sure you have the lastest version for Newton 1.5 (or better, the CVS version). You'll probably need the CVS version of PLSM2 and Ogre as well, as the SDKs don't contain some required functions at the time of writing this article.

Getting the geometry from PLSM2 to Newton

We'll send the terrain geometry to the physics engine when a tile is loaded, so first you need to register a listener (delegate) for tiles loading events if it's not already done (I'm using here a PLSM2Manager class to handle those events and other PLSM2 related things in my game) :

PLSM2Manager::PLSM2Manager(PagingLandScapeSceneManager* sceneManager)
{
  //snip

  loadTileDelegate=new PagingLandscapeDelegate();
  loadTileDelegate->bind(this,&PLSM2Manager::tileLoaded);
  sceneManager->setOption("addLoadTileListener",loadTileDelegate);
  
  //snip
}


Now, here is the tileLoaded method that will get called when a page is loaded. All it does is using the SceneManager::getOption() method with the PageGetTileVertexData_2 option to recover a reference to PLSM2's geometry data, then send it to another custom class handling the interaction with the physics engine (here PhysicsManager in my game). The 'renderLevel' variable indicates the LOD level we'll get from PLSM2. The LOD level on the screen is changing all the time (geomipmapping), but for physics we'll always use the same level, typically the highest (0 here) or objects will 'sink' into the ground.

If using Ogre 1.0.X define _OGRE_AZATOTH, otherwise don't.

void PLSM2Manager::tileLoaded(PagingLandscapeEvent* event)
{
  //recover PLSM2's tile object
  int pageX=event->mPagex;
  int pageZ=event->mPagez;
  int tileX=event->mTilex;
  int tileZ=event->mTilez;

  //ask reference to geometry to PLSM2
  vector<void*> params;
  int renderLevel=0;
  params.push_back(&pageX);
  params.push_back(&pageZ);
  params.push_back(&tileX);
  params.push_back(&tileZ);
  params.push_back(&renderLevel);
  sceneManager->getOption("PageGetTileVertexData_2",&params);

  //recover data at the end of the vector and send it to our physics class for the next step
  int* numVtx=((int*)params[5]);
  Vector3* vertices=((Vector3*)params[6]);
  IndexData* indexData=((IndexData*)params[7]);

  SceneNode *sn;
  sn = Ogre::Root::getSingleton().createSceneNode();
  sn->setPosition(event->mBox->getCenter());

  PhysicsManager::getInstance().loadTerrainGeometry(sn, vertices, *numVtx, indexData);

  //cleanup
  delete[] vertices;
  delete numVtx;
}


Now, we can send the vertices and indices to Newton via OgreNewt. We'll use the TreeCollision Newton objects for the terrain data, via a TreeCollision constructor provided by OgreNewt.

void PhysicsManager::loadTerrainGeometry(Ogre::SceneNode* tileNode, Ogre::Vector3* vertices, int numVertices, Ogre::IndexData* indexData)
{
  if (tilesBodies[tileNode]==NULL)
  {
    Collision* collision=new CollisionPrimitives::TreeCollision(world,numVertices,vertices,indexData,false);
    Body* body=new Body(world,collision);
    delete collision;  
    body->attachToNode(tileNode);
    tilesBodies[tileNode]=body;
  }
}


'tilesBodies' is a map<Ogre::SceneNode*,OgreNewt::Body*> I'm using to avoid loading the same physics data twice, and to delete the Body object once tiles are unloaded (see PLSM2's tileUnloaded message).

Improving performance with collision serialization

The above approach works, but is not very efficient. It takes about 25ms/tile in release mode on an Athlon64 3200+, so you can easily have an annoying lag of a few seconds when lots of new tiles are loaded at once. The solution is to use Newton's serialization mechanism, which 'bakes' collision objects to a file so Newton doesn't have to fully recompute them when they're created. It's about 50 times faster for me!

Here's how to save a TreeCollision to a file :

TreeCollisionSerializer* serializer=new TreeCollisionSerializer();
serializer->exportTreeCollision(collision,"SomeFileName.collision");


And how to recover it :

collision=new CollisionPrimitives::TreeCollision(world);
FILE* file=fopen("SomeFileName.collision","rb");
Ogre::DataStreamPtr ptr(new FileHandleDataStream(file));
serializer->importTreeCollision(ptr,collision);

See also

  • example code in forum thread about TreeCollisionSceneParser