Animated character collision response

Dboy85

18-11-2011 16:44:43

Hi,

I'm in the middle of a project for college and I'm using nxOgre. I'm stuck on implementing collision responses for animated characters. Can anyone give advice on how its implemented in Buggy Wires? I'm trying to use the callback class but I'm getting nowhere! Do I have to use the character controller for callbacks?
I had a look through the forum using the search but there is so much conflicting code implementation I think its just easier to ask here.

I'll be more than happy to post the finished project source code so it can be used as part of a tutorial.

Basically my scene is full of animated characters that collide just fine but I need to get call backs to implement the game logic like decrement the health and the like.

Any help is appreciated!

Cheers,

mistigrii33

18-11-2011 23:40:05

Hi,

I deal with the same problem some days ago (http://www.ogre3d.org/addonforums/viewtopic.php?f=6&t=14953). I've found a solution, but i'm not sure at all if this one is the good way to do it.

Open the CritterCharacterBase.h, and change

class CritterPublicClass CharacterBase : protected NxOgre::CharacterController, protected NxOgre::TimeListener, protected NxOgre::Callback
to
class CritterPublicClass CharacterBase : public NxOgre::CharacterController, protected NxOgre::TimeListener, public NxOgre::Callback


Now you can set your callBack like this :
mAnimatedCharacter1->setContactCallback(myCallbackObject); //object which will handle your callback, by overriding the function which are in NxOgreCallBack.h (onContact, ... depending of what you want)
mNxScene->setActorFlags(mAnimatedCharacter1,mAnimatedCharacter2, NxOgre::Enums::ContactPairFlags_All);

This means that you have to have mAnimatedCharacter1 and mAnimatedCharacter2 as Critter::AnimatedCharacter (or generally a Critter::Body, you can create a callback for a collision between Critter::AnimatedCharacter and Critter::Body) (which is now public (instead of protected) herited of NxOgre::CharacterController).

So now, you can create your own AnimatedCharacter (let's call it MyAnimatedCharacter) which herit from Critter::AnimatedCharacter and add it to Critter when your create it. Unfortunately, i haven't found other way than Critter::RenderSystem::createAnimatedCharacter to create an animatedCharacter. So, for that purpose, i wrote some functions on my own :

add to CritterRenderSystem.h

void addAnimatedCharacter(AnimatedCharacter* character);

add to CritterRenderSystem.cpp

void RenderSystem::addAnimatedCharacter(AnimatedCharacter* character)
{
mAnimatedCharacters.push_back(character);
}


Wherever you want, something like "CritterAddOn.h"


//code inspired by Critter::RenderSystem::createAnimatedCharacter
template<typename MyAnimatedCharacter>
static
Critter::AnimatedCharacter* createMyAnimatedCharacter( Ogre::SceneManager* mSceneMgr,
Critter::RenderSystem* mRenderSystem,
const Ogre::Vector3& position,
const Ogre::Radian& yaw,
Ogre::String& mesh_name,
const Critter::AnimatedCharacterDescription& desc)
{
Critter::Node* node = new Critter::Node(mSceneMgr, mRenderSystem);
node->createAndAttachEntity(mesh_name);
MyAnimatedCharacter* character = NxOgre::GC::safe_new5<MyAnimatedCharacter>(desc, position, yaw.valueRadians(), node, mRenderSystem, NXOGRE_GC_THIS);

mRenderSystem->addAnimatedCharacter(character);

return character;
}


You have to call this function this way to create your "MyAnimatedCharacter" for you to be able to use the callback code

MyAnimatedCharacter* mAnimatedCharacter1 = (MyAnimatedCharacter *) createMyAnimatedCharacter<MyAnimatedCharacter>(mSceneMgr, mRenderSystem, pos_joueur, Ogre::Radian(0), Ogre::String("myMesh.mesh"), joueurDesc);


and that's it ! Of course, don't forget to re-build your Critter, as we made some changes in it...

If someone can approve what i did is the only way to do that, this could be nice, as it's some kind of hack...

Dboy85

20-11-2011 18:38:14

Thank you for the in depth response. I'll try this and report back. I'm sure if it works for you it will work for me but surely there shouldn't be any need to edit the Critter files to get this to work. Either way I will post the final source for others to learn from if you don't mind me posting your code as part of it?

Cheers

Dboy85

20-11-2011 21:34:28

No joy as of yet, here is what I have tried to implement. Any ideas what to try next?

This is the Player class I set up to test it:


class MyCallBack : public NxOgre::Callback
{
public:
MyCallBack();
~MyCallBack();
void onContact(const NxOgre::ContactPair& pair)
{
int x = 0;// Test
}
};

class Player : public Character{

protected:
MyCallBack * mCallBack;

public:
Player (String name, SceneManager *sceneMgr,Critter::RenderSystem * renderS, Ogre::Vector3 position) :
mName(name)
{
mCallBack = new MyCallBack();
mSceneMgr = sceneMgr;
mRenderSystem = renderS;
mScene = mRenderSystem->getScene();
mNode = mRenderSystem->createNode();
mNode->createAndAttachEntity("sinbad.mesh");
mNode->setPosition(position);

Critter::AnimatedCharacterDescription desc;
...

mCharacter = mRenderSystem->createAnimatedCharacter(Ogre::Vector3(0,5,0), Ogre::Radian(0),mNode, desc);
mCharacter->setContactCallback(mCallBack);
}


And then in the main I do something like this:



mPlayer = new Player("Random_Name",mSceneMgr,mRenderSystem,Ogre::Vector3(0,0,0));
mPlayer_2 = new Player("Random_Name_2",mSceneMgr,mRenderSystem,Ogre::Vector3(20,0,20));
mScene->setActorFlags( (NxOgre::RigidBody* ) mPlayer , (NxOgre::RigidBody* ) mPlayer_2, NxOgre::Enums::ContactPairFlags_All);



I'm sure its something trivial but I've been staring at a blank screen so its eluding me.

mistigrii33

21-11-2011 21:56:38

What's the Character here ? class Player : public Character{ Is it an AnimatedCharacter ? If so, why does it herits from this and at the same time contains an AnimatedCharacter ? see your contructor : mNode = mRenderSystem->createNode();
mNode->createAndAttachEntity("sinbad.mesh");
mNode->setPosition(position);

Critter::AnimatedCharacterDescription desc;
...

mCharacter = mRenderSystem->createAnimatedCharacter(Ogre::Vector3(0,5,0), Ogre::Radian(0),mNode, desc);
mCharacter->setContactCallback(mCallBack);


Basically, what you have to do, for a "Player" object that CONTAINS an animatedCharacter (as in the constructor of your example)
mScene->setActorFlags( (NxOgre::RigidBody*) mPlayer->getAnimatedCharacter() , (NxOgre::RigidBody*) mPlayer_2->getAnimatedCharacter(), NxOgre::Enums::ContactPairFlags_All);So you have to create a new method, in your Player class, like this :
Critter::AnimatedCharacter Player::getAnimatedCharacter() { return mCharacter; }


if your "Player" herits from Critter::AnimatedCharacter, you can do:
mScene->setActorFlags( (NxOgre::RigidBody*) mPlayer, (NxOgre::RigidBody*) mPlayer_2, NxOgre::Enums::ContactPairFlags_All);

Edit : This call to setActorFlags can ONLY be valid if you change the CritterCharacterBase.h file as i said in my first post and recompile Critter (otherwise, an AnimatedCharacter can't be cast to a RigidBody* as it's a protected herits)

Dboy85

23-11-2011 12:45:04

The Player class inherits from Character which holds the Animated Character


Here is the Character class:


class Character
{

//==============================
// Variables
//==============================
protected:
Entity * mEntity;
NxOgre::Scene* mScene;
Critter::RenderSystem* mRenderSystem;
Critter::Node* mNode;
Critter::AnimatedCharacter* mCharacter;
Critter::CharacterInputHelper mCharacterHelper;

public:
//==============================
// Functions
//==============================
protected:
public:
// Updates the character (movement...)
virtual void update (Real elapsedTime, OIS::Keyboard *input) = 0;

// Return the animated character
Critter::AnimatedCharacter * getAnimChar()
{
return mCharacter;
}



And here is the Player class in a bit more detail:



enum ControllerShapeGroups
{
NonCollidable = 0, // Things that the character controller can go through such as Ghosts or tiny
// objects like small rocks.
Walls = 1, // Walls, Floors and other static geometry that can't be moved.
Objects = 2 // Boxes, Barrels and other dynamic parts of the scene that can be moved by pushing.
};

class MyCallBack : public NxOgre::Callback
{
public:
MyCallBack(){
}
~MyCallBack(){
}
void onContact(const NxOgre::ContactPair& pair)
{
int x = 0; // For debugging, this never gets called :(
}
};

class Player : public Character // Derived from a character class which contains the animated character
{
protected:
String mName;
Vector3 mVelocity;
MyCallBack * mCallBack;

public:
// Methods ---------------------------------------------------------------------------------
protected:

public:
Player (String name, SceneManager *sceneMgr,Critter::RenderSystem * renderS, Ogre::Vector3 position) :
mName(name)

{
mCallBack = new MyCallBack();
// Setup basic member references
mSceneMgr = sceneMgr;
mRenderSystem = renderS;
mScene = mRenderSystem->getScene();
// Briefly setup Sinbad, like in the previous tutorial.
mNode = mRenderSystem->createNode();
mNode->createAndAttachEntity("sinbad.mesh");
mNode->setPosition(position);

Critter::AnimatedCharacterDescription desc;
desc.mShape = NxOgre::SimpleCapsule(5+0.5+0.1,2);
desc.mCollisionMask = (Walls << 1) | (Objects << 1); // Maybe the mask isn't set right?
desc.mMaxGroundSpeed = 17.0f;
desc.setJumpVelocityFromMaxHeight(mScene->getGravity().y, 0.75f);
// Create the animated character using the original function in the rendersystem class
mCharacter = mRenderSystem->createAnimatedCharacter(Ogre::Vector3(0,5,0), Ogre::Radian(0),mNode, desc);
// Hook up the callback
mCharacter->setContactCallback(mCallBack);
}


And the main:



mPlayer = new Player("Name",mSceneMgr,mRenderSystem,Ogre::Vector3(0,0,0));
mPlayer_2 = new Player("Name_2",mSceneMgr,mRenderSystem,Ogre::Vector3(20,0,20));

// Frame listener to manage both character and camera updating and different camera modes.
mFrameListener = new SimpleListener(mWindow, mCamera); // Handle camera updating.
static_cast<SimpleListener *>(mFrameListener)->setCharacter (mPlayer); // Attach the player.
static_cast<SimpleListener *>(mFrameListener)->setExtendedCamera (exCamera); // Attach the extended camera.

// For collisions
mScene->setActorFlags( (NxOgre::RigidBody* ) mPlayer->getAnimChar() , (NxOgre::RigidBody* ) mPlayer_2->getAnimChar(), NxOgre::Enums::ContactPairFlags_All);



This all compiles fine but never enters the callback onContact function in the player class.

I changed the CharacterBase.h to:


class CritterPublicClass CharacterBase : public NxOgre::CharacterController, protected NxOgre::TimeListener, public NxOgre::Callback



I did not implemented this snippet you posted earlier though:


//code inspired by Critter::RenderSystem::createAnimatedCharacter
template<typename MyAnimatedCharacter>
static
Critter::AnimatedCharacter* createMyAnimatedCharacter( Ogre::SceneManager* mSceneMgr,
Critter::RenderSystem* mRenderSystem,
const Ogre::Vector3& position,
const Ogre::Radian& yaw,
Ogre::String& mesh_name,
const Critter::AnimatedCharacterDescription& desc)
{
Critter::Node* node = new Critter::Node(mSceneMgr, mRenderSystem);
node->createAndAttachEntity(mesh_name);
MyAnimatedCharacter* character = NxOgre::GC::safe_new5<MyAnimatedCharacter>(desc, position, yaw.valueRadians(), node, mRenderSystem, NXOGRE_GC_THIS);

mRenderSystem->addAnimatedCharacter(character);

return character;
}


I just used the rendersystem's function:

mCharacter = mRenderSystem->createAnimatedCharacter(Ogre::Vector3(0,5,0), Ogre::Radian(0),mNode, desc);


Sorry in advance about the length of the post, I want to be more concise about what I've done so far.

Cheers

Dboy85

23-11-2011 19:46:07

I think critter is not compiling correctly:

1>------ Build started: Project: Critter, Configuration: DebugStatic Win32 ------
1> Critter.cpp
1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppBuild.targets(1151,5): warning MSB8012: TargetPath(C:\Critter\build\vc10\\DebugStatic\Critter.lib) does not match the Library's OutputFile property value (C:\Critter\build\vc10\DebugStatic\CritterDebugStatic.lib). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %(Lib.OutputFile).
1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppBuild.targets(1153,5): warning MSB8012: TargetName(Critter) does not match the Library's OutputFile property value (CritterDebugStatic). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %(Lib.OutputFile).
1>NxOgreDebug.lib(NxOgreDebug.dll) : warning LNK4006: __NULL_IMPORT_DESCRIPTOR already defined in OgreMain_d.lib(OgreMain_d.dll); second definition ignored
1>NxOgreDebug.lib(NxOgreDebug.dll) : warning LNK4221: This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library
1> Critter.vcxproj -> C:\Critter\build\vc10\\DebugStatic\Critter.lib
1> [BuildBot] Copying dlls, libs and headers to SDK directory.
1> 1 File(s) copied
1> 23 File(s) copied
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========


EDIT 2 : Still doesn't work...help :(

Edit 3: I've set up a few bodys on the scene to test that my onContact code is correct and these are the results I've gotten.

Animated Character vs Animated Character = no Callback
Animated Character vs Body = onContact is successfully called.
Body vs Body = onContact is successfully called.

Here is the code snippet. I'm really confused now as the Animated character is registering but cant collide with another
animated character :? Maybe its the way my mCollisionmask is set in the description for the animated character?


// create bodies for debugging.
mBody = mRenderSystem->createBody(NxOgre::BoxDescription(5,5,5), NxOgre::Vec3(0,5,0), "cube.1m.mesh" , bodyDescription);
mBody->setContactCallback(this);
mBody_2 = mRenderSystem->createBody(NxOgre::BoxDescription(5,5,5), NxOgre::Vec3(10,10,0), "cube.1m.mesh", bodyDescription);
mBody_2->setContactCallback(this);

mPlayer_2 = new Player("Player_2",mSceneMgr,mRenderSystem,Ogre::Vector3(20,0,20));
mPlayer_2->getAnimChar()->setContactCallback(this);

mPlayer = new Player("Player",mSceneMgr,mRenderSystem,Ogre::Vector3(0,0,0));
mPlayer->getAnimChar()->setContactCallback(this);

// For collisions
mScene->setActorFlags( (NxOgre::RigidBody*) mPlayer->getAnimChar() , (NxOgre::RigidBody*) mPlayer_2->getAnimChar() , NxOgre::Enums::ContactPairFlags_All);// Fails to call onContact
mScene->setActorFlags( (NxOgre::RigidBody*) mBody , (NxOgre::RigidBody*) mBody_2 , NxOgre::Enums::ContactPairFlags_All);// Works
mScene->setActorFlags( (NxOgre::RigidBody*) mPlayer->getAnimChar() , (NxOgre::RigidBody*) mBody_2 , NxOgre::Enums::ContactPairFlags_All);// Works
mScene->setActorFlags( (NxOgre::RigidBody*) mPlayer_2->getAnimChar() , (NxOgre::RigidBody*) mBody_2 , NxOgre::Enums::ContactPairFlags_All); // Works
}

void MyClass::onContact(const NxOgre::ContactPair& pair)
{
int x =0;
}

Dboy85

24-11-2011 22:41:47

Any Ideas? Can anyone confirm that they have successfully managed to have Animated character vs Animated character callbacks?

mistigrii33

27-11-2011 16:06:34

I don't know where the problem come from then. I'd like to know if you solve it, or if anyone did ; i might need the solution in the future

betajaen

27-11-2011 16:46:47

Looking at the NxOgre source code, there isn't a callback class that feeds in Character events into a usable state. Your probably getting events from interaction between the Kinematic Actor and the Body itself, rather than the Character/Body. Technically, this is the same, but the PhysX core doesn't differentiate between the two, as the NxCharacter system is built on top of PhysX. Even so, I'm pretty sure PhysX doesn't handle Kinematic/Kinematic collisions - which is why your not getting events for character/character, using the normal event system.

How I remember it is, a PhysX callback for the characters is basically a virtual function of a class which you inherit, you make an instance of that class and feed it into the description of the NxCharacter when you create it.

What you need to do is in NxOgre, create an appropriate Callback class that inherits and implements the functions of a NxCharacterCallback, and feed the events captured into your application. Call it "PhysXCharacterCallback"

I suggest, looking at the line "controller_desc.callback = 0;" in "RigidBody::_createCharacterController" is a good place to start, put the pointer of your callback into that. I suggest creating the pointer and storing it in world, as you only need it once, and it can be shared between Characters. Sending your events will be a bit more difficult, but if you add some virtual functions to the proper NxOgre Callback for these functions. Just add the Callback class to the constructors and create functions for all of the Character creation functions, and store that pointer in the Character. Then in the PhysXCharacterCallback class, the functions should get the pointer for Callback, and just send the events directly into it. Then in your Application, create a class that inherits from Callback, and pass it into the methods that create your Character, and then events should start coming in.

I'm sorry this is vague, I don't have PhysX on my computer, or in a position to help or write code for it. But that is how I would write it.

Dboy85

27-11-2011 19:50:02

Thanks for the reply. I would have figured that this would be a common enough thing to deal with in a physics engine. In a lot of cases both the player and npc's are animated characters and that there would be an implementation of a callback system to use for both. I'll probably end up just using a primitive approach and just make the player an invisible entity instead and have the npc's as an animated character. I cant really risk putting anymore time into getting an implementation of this working but I will probably have a stab at it when there isn't a time constraint. In the mean time if anyone has a working implementation I would appreciate it if you would share it.

Thanks for the help

betajaen

27-11-2011 19:58:56

Well, it would be an hours work tops if you had a copy of some PhysX code to work from.

But another cheap way would be to have a body or trigger volume following the character around.