Collision Handling Tutorial

rewb0rn

07-11-2007 17:07:42

As promised I wrote a tutorial on how to handle collisions the easy way. You find it here. Please give me some feedback, especially point out any mistakes with my English :)

kungfoomasta

07-11-2007 18:18:57

Thanks for writing the tutorial!

I have a question about the code, this part:


//we have 2 collidable objects from our object system
//PATH1 if (g1->getUserObject())
if (!static_cast<CollisionTestedObject*>(g1->getUserObject())->Collide(true, Contact))
return false;

//PATH2 if (g2->getUserObject())
if (!static_cast<CollisionTestedObject*>(g2->getUserObject())->Collide(false, Contact))
return false;


If the 2 colliding geometries have an associated user object, won't PATH1 always be executed? I guess I don't understand how "getUserObject" works. PATH2 looks like it will never be executed.

rewb0rn

07-11-2007 18:31:26

well if both geos have a user object, path1 will always be executed, thats true. if path1 returns false, the collision function of the collisionListener class returns also false, so path2 will not be executed, you are right thats my mistake. (its only a problem when path2 would do something else then just setting new contact parameters, because these dont matter anyway when the function returns false)
i will change this to:
bool Return = true;

if (g1->getUserObject())
if (!static_cast<CollisionTestedObject*>(g1->getUserObject())->Collide(true, Contact))
Return = false;

if (g2->getUserObject())
if (!static_cast<CollisionTestedObject*>(g2->getUserObject())->Collide(false, Contact))
Return = false;

return Return;


when i wrote this part of the code, i only used the collide methods to set new contact parameters, so in that spirit the code was correct, i just forgot to update it later..

getUserObject() returns a pointer to the user object or 0.

kungfoomasta

07-11-2007 18:37:53

I guess I'm just trying to get a grasp on the function. So we want to return true if the collision was handled, and false otherwise?

if g1->getUserObject is NULL and g2->getUserObject is NULL, true will be returned. (should this be false?)

The main question is, under what conditions will "Collide" return false?

rewb0rn

07-11-2007 18:46:26

no we return true if we want ode to react on this collision, if not we return false. so in case 1 you would have a normal rigid body behavior, in case 2 the objects would just move through each other with no further action, except anything you implemented in the objects collide function. i do this with my water e.g. on a collision the collide function returns false, so ode will not make the object that collides with the water bounce back. in the collide function of the water though i add some force to the object so it would swim when its not to heavy.

edit: there still was some bug, check the new code (edited above)

kungfoomasta

07-11-2007 19:03:41

Thanks for clearing that up. So we have 3 main cases:

1. No user defined collision modifications, Ode handles collision normally.
2. User defined collision modifications, Ode handles collision normally.
3. User defined collision modifications, Ode does not handle collision.

Basically we can alter properties before the collision occurs, and if we return false, the collision will not be handled by Ode. This makes a lot of sense now! :D

rewb0rn

07-11-2007 19:07:40

yep,
as i said there is some overhead, regard the following example:

we have a water class. this will add some force to the object it collides with but returns false in its own collide method, so ode does not handle the collision. now object 2, a crate for instance, sets the contact parameters as it normally would do, but we dont need them anyway, so thats all for nothing. but i think the concept is worth the loss of performance.

And in case of use there is probably some possibility to avoid some overhead, e.g. by dividing the collide function in a bool precheckcollide() and a void postcheckcollide() part, maybe i will test this in my application and then add it to the tutorial, but no promise ;)

kungfoomasta

07-11-2007 19:59:22

I think its fine the way it is. You will have classes overriding the Collide method, so in the case of a crate, it can immediately return true, where water will make modifications, and then return false.

This isn't really related, but how you would know when the player has gotten out of the water, instead of underneath the water plane?

Aquatix

07-11-2007 20:05:15

Wow, that's amazing, great tutorial! Thnx!
Haven't read it all though, but it looks really "just the right thing"!

P.S. a simple spell-check said: no errors :lol:

-edit-
Just was looking at the "overhead" thingy. It does seem to slow things a bit. But, you are right in the sense, that it's not too noticable, provided everything can be setup properly. Something that ccomes straight to attention - why doubles at all? Aren't floats sufficient? (especially for the params)
But everything else seems to be perfect.

rewb0rn

07-11-2007 21:12:45

good point, dont know why i came to use double originally. fixed.
by now i am quite sure, that dividing the collide method in two parts will erase most of the unnecessary computation, hope i get to implement that soon.

Aquatix

07-11-2007 21:29:11

That's good news. Faster code = better code :wink:

F-Wölkchen

25-03-2008 19:02:55

Hello.

Can somebody post the whole code please?
:)
Florian

rewb0rn

25-03-2008 19:53:03

The code in the tutorial is fully functional and should be sufficient. The improvements discussed above are not all implemented yet but that does not touch the functionality of the code.

F-Wölkchen

26-03-2008 19:53:44

Hello.
But it still doesn't work :(

Although I set up a collisionsListner the boxes fell through the plane.
Here's my Code:


class Program : public ExampleApplication
{
public:
Program()
{

}

~Program()
{

}

class Collision : public OgreOde::CollisionListener {
public:
bool collision(OgreOde::Contact* contact)
{
OgreOde::Geometry * const g1 = contact->getFirstGeometry();
OgreOde::Geometry * const g2 = contact->getSecondGeometry();

if (g1 && g2)
{
const OgreOde::Body * const b1 = g1->getBody();
const OgreOde::Body * const b2 = g2->getBody();
if (b1 && b2 && OgreOde::Joint::areConnected(b1, b2))
return false;
}

contact->setBouncyness(0.1);
contact->setCoulombFriction(OgreOde::Utility::Infinity);


return true;
}
};
protected:

virtual void createCamera(void)
{
mCamera = mSceneMgr->createCamera("PlayerCam");
mCamera->setPosition(Vector3(0,10,500));
mCamera->lookAt(Vector3(0,0,0));
mCamera->setNearClipDistance(1);
}

virtual void createViewports(void)
{
Viewport* vp = mWindow->addViewport(mCamera);
vp->setBackgroundColour(ColourValue(0,0,0));
mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
}




void createScene(void)
{
mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));

mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_MODULATIVE);
mSceneMgr->setShadowColour(ColourValue(0.5,0.5,0.5));

Light *light = mSceneMgr->createLight("Light1");
light->setType(Light::LT_POINT);
light->setPosition(Vector3(0, 150, 250));
light->setDiffuseColour(0.5, 0.5, 0.5);
light->setSpecularColour(1.0, 1.0, 1.0);
light->setCastShadows(true);

mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);




//---------------Welt wird definiert---------------
mWorld = new OgreOde::World(mSceneMgr);
mWorld->setGravity(Ogre::Vector3(0,-9.80665,0));
mWorld->setCFM(10e-5);
mWorld->setERP(0.8);
mWorld->setAutoSleep(true);
mWorld->setAutoSleepAverageSamplesCount(10);
mWorld->setContactCorrectionVelocity(1.0);
mSpace = mWorld->getDefaultSpace();

mWorld->setCollisionListener(new Program::Collision());

//mWorld->setCollisionListener(this);
//---------------Welt wird definiert---------------


const Ogre::Real _time_step = 0.5;
const Ogre::Real time_scale = Ogre::Real(1.7);
const Ogre::Real max_frame_time = Ogre::Real(1.0 / 4);
mStepper = new OgreOde::StepHandler(mWorld, OgreOde::StepHandler::BasicStep,_time_step, max_frame_time,time_scale);



mGround = new OgreOde::InfinitePlaneGeometry(Plane(Ogre::Vector3(0,1,0),0), mWorld, mWorld->getDefaultSpace());
// Use a load of meshes to represent the floor
int i = 0;
StaticGeometry* s;
s = mSceneMgr->createStaticGeometry("StaticFloor");
s->setRegionDimensions(Ogre::Vector3(160.0, 100.0, 160.0));
// Set the region origin so the center is at 0 world
s->setOrigin(Ogre::Vector3::ZERO);
for (Real z = -80.0;z <= 80.0;z += 20.0)
{
for (Real x = -80.0;x <= 80.0;x += 20.0)
{
String name = String("Ground") + StringConverter::toString(i++);
Entity* entity = mSceneMgr->createEntity(name, "plane.mesh");
entity->setQueryFlags (1<<4);
entity->setUserObject(mGround);
entity->setCastShadows(false);
s->addEntity(entity, Ogre::Vector3(x,0,z));
}
}
s->build();


}

void chooseSceneManager(void)
{
mSceneMgr = mRoot->createSceneManager( ST_GENERIC, "ExampleGrandTurismo" );
}

void createFrameListener(void)
{
mFrameListener= new EventListener(mWindow, mCamera, mSceneMgr);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);

mFrameListener= new ExampleFrameListener(mWindow, mCamera);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
}



};
//=======================================Hauptklasse=============================================


What's wrong?

Florian

rewb0rn

27-03-2008 12:36:22

You dont have bodies in you application I wonder what should move in there. Also this has nothing to do with the tutorial.

marc_antoine

13-05-2009 22:51:22

rewb0rn, full code listing would eb nice, i have myself having a hard tiem tryin to understand it, since i'm srtating with ODE.. would it be too much to ask?


thanks..
:)

nfalcon

06-10-2009 21:55:20

Hey All,

I wasn't sure if OgreOde is dead or not, but I figured I could resurrect certain portions that might not link anymore with the latest version of Ode.
I was able to get it compiling and created a world with gravity and have been able to see objects fall, but I have been having issues with the collision tutorial.

1.) No matter what I change a geometries boxMass to, it always falls at the same speed.
2.) When objects collide, they go through each other.

Now I have it set up where there is a RigidBody class and the Collision hierarchy described in the OgreOde Collision Handling page.
I have derived a class from both of these which are the two entity's that I would like see collide with each other.

First entity is the player and is constructed as followed:
Player::Player(Entity* entity, const String& name, World* world, Space* space) : EntityBase(entity, name, world, space)
{
// Setup basic node structure to handle 3rd person cameras
mSightNode = EntityBase::mMainNode->createChildSceneNode(EntityBase::mName + "_sight", Vector3(0, 0, 100));
mCameraNode = EntityBase::mMainNode->createChildSceneNode(EntityBase::mName + "_camera", Vector3(0, 50, -200));

RigidBody::setBoxPhysics(0.5, Vector3(10.0f, 10.0f, 10.0f));
RigidBody::mGeometry->setUserObject(static_cast<CollisionBase*>(this));
}


RigidBody::setBoxPhysics
inline void setBoxPhysics(const Ogre::Real mass, const Ogre::Vector3& size)
{
mOdeNode->setScale(size.x * 0.1, size.y * 0.1, size.z * 0.1);
OgreOde::BoxMass boxMass(mass, size);

mGeometry = (OgreOde::Geometry*)new OgreOde::BoxGeometry(size, mWorld, mSpace);
mBody->setMass(boxMass);
mGeometry->setBody(mBody);

mOdeNode->attachObject(mBody);
}


The second is a test object and is as followed:
Dummy(Ogre::Entity* entity, const Ogre::String& name, OgreOde::World* world, OgreOde::Space* space) : EntityBase(entity, name, world, space)
{
RigidBody::mGeometry = (OgreOde::Geometry*)new OgreOde::BoxGeometry(Ogre::Vector3(10.0f, 10.0f, 10.0f), mWorld, mSpace);
RigidBody::mGeometry->setUserObject(static_cast<CollisionBase*>(this));
mEntity->setUserObject(RigidBody::mGeometry);
}


Maybe I am not understanding how this should actually work, but these two object just pass right through each other, and the collision function seems to be returning true at the start of the application, even when the two entities are not touching.

Can anyone see anything I might blatantly be doing wrong?

Thanks!