Animated mesh + body part collision

nasirus1983

25-11-2008 12:59:49

Hi!

At begining of my post I would like to sorry for my english :)

Ok, what is my problem ? For some time I've been reading Ogre and NxOgre tutorials and make most of them.
But I would like to do an animated mesh, which would collide with objects on the scene within for example legs and hands.

I know that I have to make my own Body class as extension of Actor.
Probably using flour.exe I have to generate nxs file from mesh. But I have no idea what next. How to part body for colliding parts like legs, hands and how to integrate this with playing animation when for example my character will collide with stairs.

I'm not expect that you will post me the whole code :) But I'll appreciate if you give me some hints and skeletal of my classes (I'm guessing that I would not be one class.

Currenty I know that I have to make own Body class - in that class probably will be something what will check collisions by leg or hand. But when and where to call animation for situation when for example character is going upstairs or downstairs ?

Please help :D :D

Prophet

25-11-2008 15:25:12

One approach would be to create triggers at the stairs, making the character using the stair-walk-animation. Make a check a few frames back and see if he has fallen in the y-axis, or get the direction of the stairs and see if (s)he is going in the up or down-direction.
I guess.

nasirus1983

25-11-2008 17:28:15

Yes, but first I must program my character with collisions, callback's etc.

Please tell me, collision model and/or callbacks should be in my body class. Or maybe this must be other class which extends another one ?

Where I should start ?

nargil

25-11-2008 17:45:54

Basicly its better to make

class cMyCharacter : public NxOgre::Body
{
};


than class cMyCharacter
{
public/private:
NxOgre::Body* mBody;
}


because when getting raycasts you can static_cast the raycast return value to cMyCharacter class and access values like mHitPoits

nargil

25-11-2008 17:48:48

Basicly its better to make

class cMyCharacter : public NxOgre::Body
{
};


than class cMyCharacter
{
public/private:
NxOgre::Body* mBody;
}


because when getting raycasts/callbacks you can static_cast the raycast return value to cMyCharacter class and access values like mHitPoits

else you had to do a public global/static object std::map<NxOgre::Body*, cMyCharacter> and update it when you create a character/Body and in collisions use:
mMapObject[raycast_return_val]->mHitPoints
which I don't like it, because it needs more ram and a global variable

nasirus1983

26-11-2008 09:53:48

But as is written in NxBody.h :

"Body is an example of an Actor using a NodeRenderable. This is merely an example on how to uses visualisation in NxOgre, it shouldn't be used for serious things."

So I think that extending:

class cMyCharacter : public NxOgre::Body
{
};

isn't a good idea. On nxOgre.org site Betajaen writes:
Creating your own class based off the NxOgre Actor is almost essential in serious applications

So for sure I must inherit Actor class. My problem is where to implement collisions, callbacks etc and how to do this (ofcourse I do not expect full code :))

mcaden

26-11-2008 12:41:12

It would've only taken you a little bit of reading.



http://nxogre.org/Inheriting_Actor_and_ ... type_class

Then find my fixes in the 1.0'22T1 and 1.0'22T2 threads

nargil

26-11-2008 14:11:38

Yeah, but creating onw body classes is something added in 1.0.22'Tx, and from what I've seen the NxOgre 1.5 code doesn't use templates again.

mcaden

26-11-2008 15:07:54

he said he was including it

nasirus1983

27-11-2008 08:04:25

Ok, but how it's connected with my problem and my question ?
Is anybody here, who could help me some ? I'm interested in concrete answers not discussion about nothing.

NoodlesOnMyBack

27-11-2008 18:15:50

I believe that you need some ragdolls physics for your skeletal animated meshes.
You have to create a "skin pose" for your model (the classic T position with hands extended) and then put the actors (basic shapes, capsules,cubes and spheres) in place over the bone's position and orientation.
Then you joint them togheter, and when the character is animated, you set each actor to be kinematic, so the skeletal animation 'drives' the actors (you have to set the orientation and position here too.
If the player dies for example, you deactivate the kinematic flag for the body parts and set all ogre bones to be manually positioned.here you do the oposite, meaning that the actors will drive the ogre skeleton bones having the classic ragdolls physics.
There was some code about this somewhere here in the forums, i think from Spacedude.
Other choice will be to see the Scythe Editor code, its a PhysX/Newton editor wich is open source and using Ogre.

nasirus1983

29-11-2008 21:40:45

I do not want the ragdoll (I guess). I have animated mesh and only what I need is to set collision for him. For example when my mesh is approach to stairs I want to detect that and turn on stairs walking animation. When my mesh approach for exmaple low wall I want turn on jump animation and so on or when my mesh approach to high wall to turn on stand (idle) animation. Can you help me with that. Maybe some pseudo code ?
Please help, it's my project for studies - which have some top restrictions.

NoodlesOnMyBack

30-11-2008 08:56:57

What most people do in that case is to have a character controller, wich i did my own (the source code is in this forum somewhere), this is just a basic shape like a capsule, later on if you want more accurate collision detection you use ragdolls on top of that, but the basic "checks" as ground or object touching, you do it with the character controller of PhysX and using its callbacks, i set the callbacks to the same Character Controller class, so when i touch something they are triggered, then i get what kind of object i had hitted, since i use a component based design i can check here if what im toucking its a gun, a stair or whathever.:


NxControllerAction CCharacter::onShapeHit(const NxControllerShapeHit& hit)
{
if(1 && hit.shape)
{
NxCollisionGroup group = hit.shape->getGroup();
if(group!=GROUP_COLLIDABLE_NON_PUSHABLE)
{
NxActor& actor = hit.shape->getActor();
if(actor.isDynamic())
{
NxOgre::VoidPointer* vp = static_cast<NxOgre::VoidPointer*>(actor.userData);
NxOgre::Actor* userDataActor = vp->toActor();
//CActor* tmpActor = dynamic_cast<CActor*>(userDataActor); //->toActor();
PhysicsActor* tmpActor = dynamic_cast<PhysicsActor*>(userDataActor); //->toActor();

Ogre::String name = tmpActor->getOwnerObject()->getID();



Note that PhysicsActor its a custom made body type.
Look for the full Character class in the forums, its not meant to be the best way, it just worked for me.

nasirus1983

30-11-2008 09:59:43

Is the same class responsible for playing animations ?

NoodlesOnMyBack

30-11-2008 19:53:52

If you want too, yes, but its better to have all components separated from each other, so lets say the character is touching a stair, you can make some methods in CCharacter like isMoving() and isOnStairs(), then you you can use them from your CAnimation class.

nasirus1983

12-12-2008 19:47:00

Ok. I think my knowledge is a little disordered. So I'll try get help in an other way.
I've made class responsible for my character.

MyCharacter.h

#include <Ogre.h>
#include <NxOgre.h>
#include <OgreBaseListener.h>

using namespace Ogre;
using namespace NxOgre;

class MyCharacter : public OgreBaseListener
{
public:
MyCharacter(RenderWindow*, Camera*, SceneManager*, World*, Scene*);
~MyCharacter();
void createCharacter();
void createPhysicCharacter();
Entity* getEntity();
Actor* getActor();
protected:
Entity* mEntity;
private:
Scene* mNxScene;
World* mNxWordl;
SceneNode* mSceneNode;
SceneManager* mSceneMgr;
NodeRenderableParams nrp;
Actor* myCharacterBody;

AnimationState* mAnimState;
ActorParams actorParams;

//static const Ogre::Real physics_scale = 2.0;

////////////////// from OgreBaseListener //////////////////////////
bool processUnbufferedKeyInput(const FrameEvent& evt);
bool keyPressed(const OIS::KeyEvent&);
bool keyReleased(const OIS::KeyEvent&);
};


MyCharacter.cpp

#include <Ogre.h>
#include <NxOgre.h>
#include <MyCharacter.h>

using namespace Ogre;
using namespace NxOgre;

#define MESH_NAME "robot.mesh"
#define ENTITY_NAME "MyMesh_Entity"
#define NODE_NAME "MyMesh_node"
#define BODY_NAME "MyMesh_body"

MyCharacter::MyCharacter(RenderWindow *win, Camera* cam, SceneManager* sceneMgr, World* nxWorld, Scene* nxScene) : OgreBaseListener(win, cam, sceneMgr, nxWorld, nxScene)
{
mSceneMgr = sceneMgr;
mNxScene = nxScene;
mNxWordl = nxWorld;
}

MyCharacter::~MyCharacter(){}

void MyCharacter::createCharacter()
{
static const Ogre::Real mesh_scale = 0.02;

mEntity = mSceneMgr->createEntity(ENTITY_NAME, MESH_NAME);
mSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(NODE_NAME);
mSceneNode->attachObject(mEntity);
mSceneNode->scale(Vector3::UNIT_SCALE * mesh_scale);
mSceneNode->setPosition(Vector3(0,0,0));

createPhysicCharacter();
}

void MyCharacter::createPhysicCharacter()
{
static const Ogre::Real physics_scale = 2.0;

nrp.setToDefault();
nrp.mIdentifierUsage = NxOgre::NodeRenderableParams::IU_Use;
nrp.mIdentifier = mSceneNode->getName();

//ShapeParams sp;
//sp.setToDefault();

actorParams.setToDefault();
actorParams.mMass = 80;

myCharacterBody = mNxScene->createBody<Body>(BODY_NAME, new NxOgre::Capsule(physics_scale,1), mSceneNode->getPosition(), nrp, actorParams);
}

Entity* MyCharacter::getEntity()
{
return mEntity;
}

Actor* MyCharacter::getActor()
{
return myCharacterBody;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool MyCharacter::processUnbufferedKeyInput(const FrameEvent& evt)
{

if (mKeyboard->isKeyDown(OIS::KC_W))
{
Vector3 vecW = myCharacterBody->getGlobalPositionAsOgreVector3();
vecW.z -= 1;
myCharacterBody->setGlobalPosition(vecW);
}
if (mKeyboard->isKeyDown(OIS::KC_S))
{
Vector3 vecS = myCharacterBody->getGlobalPositionAsOgreVector3();
vecS.z += 1;
myCharacterBody->setGlobalPosition(vecS);
}
if (mKeyboard->isKeyDown(OIS::KC_A))
{
Vector3 vecA = myCharacterBody->getGlobalPositionAsOgreVector3();
vecA.x -= 1;
myCharacterBody->setGlobalPosition(vecA);
}
if (mKeyboard->isKeyDown(OIS::KC_D))
{
Vector3 vecD = myCharacterBody->getGlobalPositionAsOgreVector3();
vecD.x += 1;
myCharacterBody->setGlobalPosition(vecD);
}

if(mKeyboard->isKeyDown(OIS::KC_LEFT))
mTranslateVector.x = -mMoveScale; // Move camera left

if(mKeyboard->isKeyDown(OIS::KC_RIGHT))
mTranslateVector.x = mMoveScale; // Move camera RIGHT

if(mKeyboard->isKeyDown(OIS::KC_UP))
mTranslateVector.z = -mMoveScale; // Move camera forward

if(mKeyboard->isKeyDown(OIS::KC_DOWN))
mTranslateVector.z = mMoveScale; // Move camera backward

if(mKeyboard->isKeyDown(OIS::KC_PGUP))
mTranslateVector.y = mMoveScale; // Move camera up

if(mKeyboard->isKeyDown(OIS::KC_PGDOWN))
mTranslateVector.y = -mMoveScale; // Move camera down

if(mKeyboard->isKeyDown(OIS::KC_RIGHT))
mCamera->yaw(-mRotScale);

if(mKeyboard->isKeyDown(OIS::KC_LEFT))
mCamera->yaw(mRotScale);

if( mKeyboard->isKeyDown(OIS::KC_ESCAPE) || mKeyboard->isKeyDown(OIS::KC_Q) )
return false;

if( mKeyboard->isKeyDown(OIS::KC_F) && mTimeUntilNextToggle <= 0 )
{
mStatsOn = !mStatsOn;
showDebugOverlay(mStatsOn);
mTimeUntilNextToggle = 1;
}

if( mKeyboard->isKeyDown(OIS::KC_T) && mTimeUntilNextToggle <= 0 )
{
switch(mFiltering)
{
case TFO_BILINEAR:
mFiltering = TFO_TRILINEAR;
mAniso = 1;
break;
case TFO_TRILINEAR:
mFiltering = TFO_ANISOTROPIC;
mAniso = 8;
break;
case TFO_ANISOTROPIC:
mFiltering = TFO_BILINEAR;
mAniso = 1;
break;
default: break;
}

MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);

showDebugOverlay(mStatsOn);
mTimeUntilNextToggle = 1;
}

if(mKeyboard->isKeyDown(OIS::KC_SYSRQ) && mTimeUntilNextToggle <= 0)
{
std::ostringstream ss;
ss << "screenshot_" << ++mNumScreenShots << ".png";
mWindow->writeContentsToFile(ss.str());
mTimeUntilNextToggle = 0.5;
mDebugText = "Saved: " + ss.str();
}

if(mKeyboard->isKeyDown(OIS::KC_R) && mTimeUntilNextToggle <=0)
{
mSceneDetailIndex = (mSceneDetailIndex+1)%3 ;
switch(mSceneDetailIndex) {
case 0 : mCamera->setPolygonMode(PM_SOLID); break;
case 1 : mCamera->setPolygonMode(PM_WIREFRAME); break;
case 2 : mCamera->setPolygonMode(PM_POINTS); break;
}
mTimeUntilNextToggle = 0.5;
}

static bool displayCameraDetails = false;
if(mKeyboard->isKeyDown(OIS::KC_P) && mTimeUntilNextToggle <= 0)
{
displayCameraDetails = !displayCameraDetails;
mTimeUntilNextToggle = 0.5;
if (!displayCameraDetails)
mDebugText = "";
}

if(displayCameraDetails)
mDebugText = "P: " + StringConverter::toString(mCamera->getDerivedPosition()) +
" " + "O: " + StringConverter::toString(mCamera->getDerivedOrientation());
return true;
}

bool MyCharacter::keyPressed(const OIS::KeyEvent& evt)
{
switch(evt.key)
{
case OIS::KC_SPACE:
myCharacterBody->addForce(Vector3(0,0.1,0), NxForceMode::NX_SMOOTH_VELOCITY_CHANGE);
break;
}
return true;
}

bool MyCharacter::keyReleased(const OIS::KeyEvent &evt)
{
switch(evt.key)
{
case OIS::KC_SPACE:
myCharacterBody->addForce(Vector3(0,0,0));
break;
}
return true;
}


I even do not mention that movement doesn't work - but that I'll maybe do later.
My question is: where and how implement collisions and playing animation for that event. For example my character collides with stars so I turning on stairs walking animation and causes that animation is playing and my character is really going upstairs or downstairs.

Please help.

nasirus1983

17-12-2008 14:20:46

anyone ?

lonwolf

18-12-2008 19:00:56

Or you can fire some rays (or just a wisely positioned one somewhere around the legs e.g.) to check if you're hitting something that has an animation "tied to it".

Like, you create all the stairs in the scene naming them "i_o_stair"+ some other chars. The point is to check if the name of the body (scenenode) that was hit start with "i_o" (from interaction"able" object) then continue in the string to find what object you hit: "_stair" and load "stair_up_animation". same thing for going down with a second ray fired from above. And you can do various tests with 2 rays (or mainly 1) than adding loads of contact callbacks.

It's cheap and tricky to position and name your objects, but from my point of view it's more efficient (considering you are doing some basic tests - very simple in a specific time_step - not all possible tests every frame!).

So you have 2 approaches, callbacks or rays :) it's up to you how you want your character_class to load them

nasirus1983

20-12-2008 21:54:03

Can you propose me any example within code (skeletal code, pseudo code) looking at my class ?

lonwolf

20-12-2008 22:23:40

stricly related to specific animations:

here's an implementation (might not be the best, or the easy, or whatever, but it's what i have in mind - i'm using something similar with paged landscape loading - own implementation).

1) you need an Update() function, that it's called whenever you moved 2 meters or lets say 2 or 1 time per second. it's up to you. It would be something like this:

* setup a ray: pos = mCharacterNode->getPosition();
fire a ray(pos.x, 0.2, poz.z,mCharacterNode->getOrientation() * Vector3::UNIT_X);
*iterate thru the results:
*if iterator->movable != NULL and itr->movable->getName().substr(0,9) == "i_o_stair" (and the distance is shorter than 0.5 -< optional, not rewuired) and CharacterState is different than CH_ON_STAIR
*load the climb up animation and save your characters state in CH_ON_STAIR (initialy it was CH_WALKING) like so:

enum CharacterState
{
CH_WALKING, CH_ON_STAIR, CH_IDLE, CH_ATTACKING //etc
};

or fire the ray from (pos.x + mNode->getOrientation()*Vector3(1,0,0), pos.y+2,pos.z, mNode->getOrientation() * Vector3(1,-1,0).

This is just a sample code to give you the main idea, if i was to optimise it i'd use half of these functions :D Depends on how you want it done - what directions to shoot a ray/ or 2, and how to name your objects.

i have a similar system implemented in progressive loading or chunks of terrain, but using Ogre Raycasting system, and in its final version (the algorithm) isn't killing more than 0.1 fps in release. It isn't noticeable and it's doing calculations in real time, and hiding/destroying terrain that is too far away, and showing/creating new terrain, with no position calculations, just some rays fired in 1 polygon planes and using their names to check if i'm in a new zone. Using 5 variables to save your char's position and state is inefficient when u can use the scenenodes, actors, entities names (since the names are useless to begin with, they can come in handy in such a system).

it's hard to write a pseudo algorithm, but it should look like the one i just wrote, with minor / major modifications regarding distances, directions, etc.