Hi guys~! I'm using easeIn/Out to blend between animations anddriveToPose to update my ragdoll. wonder how do you set the ragdoll animations to loop for a fix number of time? do u do it through code or the Havok Content Tools? And how can you detect if the animation has reach to the end, so I can set another animation after that. Regards, Hiro
animation looping
animation looping
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.
hi hiro,
someone correct me if I'm wrong ... but
i think for the current time of animation controll if you have multiple animations... you must to have an Animation sate machine. on each animation state you can get the time elapsed and set up the total time of each animation... for a simple animation i believe that you must to use hkaAnimationControl... GDC 2005 Demo have an good example of this... if you wish.. an animation can run only one time on an determinated
time or for example for walk/run the animation may be consecutive.
I hope this help you...
Michael Guerra
an Colombian
Hey Hiro,
Michael is correct here - to do sequences of animations what you really need is some sort of state machine.
He also mentions the hkaAnimationControl, which is where I'd start on this question.
Take a look at the Animation/Api/Playback/Control demo for examples of how to use it.
That should give you some idea of how to do loop counting and so on.
Then if I were you I'd read up about state machines, animation blend trees and so on.
You might get some good resources if you search for university-level animation courses, or look on aigamedev.com.
Cormac
Thanks to Michael and Cormac!
Think I've got everything I want, to control the animations~
but I've discovered something else before I go onto the state machine.
I started building my ragdoll based on the demo Api/Ragdoll/Penetration Detection
then integrate the Api/Playback/Ease Curves
and down the end, I use...
m_ragdollRigidBodyController-driveToPose( delta_time, ragdollPose.accessSyncedPoseLocalSpace().begin(), m_worldFromModel, HK_NULL );
for ( int i = 0; i m_ragdollInstance-getNumBones(); i++) {
hkpRigidBody* rb = m_ragdollInstance-getRigidBodyOfBone(i);
// Initialize with quality type and collision filter
if (rb != HK_NULL) {
_setBodyKeyframed( rb );
}
}
m_detectRagdollPenetration-resetBoneStates();
I use the driveToPose alone at first.
But no good results shown. Like the character is an hunchback (image 1).
And then I use the set keyframe, the ragdoll poses correctly (image 2), but I lost the flexibility when collided into a wall or hit by a bullet.
and I think the documentation says to only use set keyframe for initialising.
image 1/2
is the demo, Api/Ragdoll/Blend Test the one I should be look into? will modifying the motors helps the driveToPose?
Since the ragdoll loading is so different and involves many steps to construct things...
(confuses me quite a lot here...)
//method 1 : the method I'm currently using
hkStringBuf assetFile("res/ragdoll/ef2.hkx"); getFilePath(assetFile);
hkRootLevelContainer* dataContainerRig = m_loader-load( assetFile.cString() );
//method 2 :the load method in Api/Ragdoll/Blend Test
hkpPhysicsData* physicsData = HK_NULL;
hkpPhysicsSystem* physicsSystem = HK_NULL;
{
hkStringBuf filename( "res/ragdoll/ef2.hkx" ); getFilePath(filename);
hkIstream infile( filename.cString() ); HK_ASSERT( 0x215d080c, infile.isOk() );
hkResource* res = HK_NULL;
physicsData = hkpHavokSnapshot::load( infile.getStreamReader(), &res );
if ( physicsData )
{
physicsSystem = physicsData-getPhysicsSystems()[0];
m_loader-m_loadedData.pushBack(res);
}
}
although I can still get the m_ragdollInstance in the end, but I have no idea how to get
hkpPhysicsData* physicsData from method 1 (in order to access the motor)
or
hkRootLevelContainer* dataContainerRig from method 2 (in order to get the low-res/hi-res mapping)
If I exclude the method 2,
Is there something I can use or call to modify a m_ragdollInstance's motor (or any attributes necessory)?
Regards, Hiro
Hi Hiro,
Take a look at the Animation/Api/Ragdoll/ConstraintMatching demo, it uses a rigid body controller.
This kind of controller works by directly setting the velocities of the ragdoll's rigid bodies, so it doesn't actually use constraint motors.
The hkaRagdollPoweredConstraintController is the one that drives joints using motors, but I think that can be tricky to tune and get behaving nicely (it's an inherently more difficult way to control an articulated body).
That demo has some loading code too.
There are a lot of different ways to go about loading assets, which can get confusing.
hkSerializeUtil is a useful one - here's some example code for loading a ragdoll instance and some physics data:
[The important things to note are hkSerializeUtil::load() and container->findObjectByType()]
// Load the file
hkSerializeUtil::ErrorDetails loadError;
hkResource* loadedData = hkSerializeUtil::load(filename, &loadError);
if ( !loadedData )
{
HK_ASSERT2(0x0badd00d, loadedData != HK_NULL, "Could not load file. The error is:n" << loadError.defaultMessage.cString() );
}
// Get the top level object in the file, which we know is a hkRootLevelContainer
hkRootLevelContainer* container = loadedData->getContents();
HK_ASSERT2(0x0badd00d, container != HK_NULL, "Could not load root level obejct" );
// Grab the ragdoll instance
hkaRagdollInstance* ragdollInstance = HK_NULL;
ragdollInstance = static_cast( container->findObjectByType( hkaRagdollInstanceClass.getName() ));
HK_ASSERT2(0, ragdollInstance, "Couldn't load ragdoll setup");
// do something with ragdollInstance
// and if there's more than one, something like this:
while (ragdollInstance)
{
ragdollInstance = static_cast( container->findObjectByType( hkaRagdollInstanceClass.getName(), ragdollInstance ));
// do something with ragdollInstance
}
// then if you're loading physics data:
hkpPhysicsData* physicsData = static_cast( container->findObjectByType( hkpPhysicsDataClass.getName() ) );
HK_ASSERT2(0xa6451544, physicsData != HK_NULL, "Could not find physics data in root level object" );
HK_ASSERT2(0xa6451535, physicsData->getWorldCinfo() != HK_NULL, "No physics cinfo in loaded file - cannot create a hkpWorld" );
// Create a world and add the physics systems to it
hkpWorld* physicsWorld;
{
physicsWorld = new hkpWorld( *physicsData->getWorldCinfo() );
physicsWorld->lock();
// Register all collision agents
hkpAgentRegisterUtil::registerAllAgents( physicsWorld->getCollisionDispatcher() );
// Add all the physics systems to the world
for ( int i = 0; i < physicsData->getPhysicsSystems().getSize(); ++i )
{
physicsWorld->addPhysicsSystem( physicsData->getPhysicsSystems()[i] );
//for( int j=0; j < physicsData->getPhysicsSystems()[i]->getRigidBodies().getSize(); ++j )
//{
// physicsWorld->addEntity( physicsData->getPhysicsSystems()[i]->getRigidBodies()[j] );
// //physicsData->getPhysicsSystems()[i]->getRigidBodies()[j]->getCollisionFilterInfo()
//}
}
physicsWorld->unlock();
}
As for the first problem - well, setting the bodies to keyframed is definitely not the way.
You might be having some problem with converting between model space and local space poses.
Is everything set up ok with the skeleton mapper?
Cormac
opps, no progress for the day :p
btw, I was trying to set the motors becausehkaRagdollRigidBodyController (driveToPose) didn't work for me...
I'm still messing with different things, hitting breakpoints...
but please have a look and see if there's anything wrong with my old code.
here is the cleaned up version.
This is how I construct a ragdoll
hkStringBuf assetFile("res/ragdoll/ef2.hkx"); getFilePath(assetFile);
hkRootLevelContainer* dataContainerRig = m_loader->load( assetFile.cString() );
HK_ASSERT2(0x27343437, dataContainerRig != HK_NULL , "Could not load data asset");
m_ragdollInstance = reinterpret_cast( dataContainerRig->findObjectByType( hkaRagdollInstanceClass.getName() ) );
HK_ASSERT2(0, m_ragdollInstance, "Couldn't load ragdoll setup");
// Assign the ragdoll skeleton
m_ragdollSkeleton = const_cast(m_ragdollInstance->getSkeleton());
// This routine iterates through the bodies pointed to by the constraints and stabilizes their inertias.
// This makes both ragdoll controllers lees sensitive to angular effects and hence more effective
const hkArray& constraints = m_ragdollInstance->getConstraintArray();
hkpInertiaTensorComputer::optimizeInertiasOfConstraintTree( constraints.begin(), constraints.getSize(), m_ragdollInstance->getRigidBodyOfBone(0) );
{ // Find the two mappings
// Iterate over all hkaSkeletonMapper objects
void* objectFound = dataContainerRig->findObjectByType( hkaSkeletonMapperClass.getName() );
while ( objectFound ) {
hkaSkeletonMapper* mapper = reinterpret_cast( objectFound );
if ( mapper->m_mapping.m_skeletonA == m_ragdollInstance->getSkeleton() ) {
m_animationFromRagdoll = mapper;
m_skeleton = const_cast(mapper->m_mapping.m_skeletonB.val());
}
if ( mapper->m_mapping.m_skeletonB == m_ragdollInstance->getSkeleton() ) {
m_ragdollFromAnimation = mapper;
m_skeleton = const_cast(mapper->m_mapping.m_skeletonA.val());
}
// Find the next object of this type
objectFound = dataContainerRig->findObjectByType( hkaSkeletonMapperClass.getName(), objectFound );
}
HK_ASSERT2( 0, m_animationFromRagdoll, "Couldn't load high-to-ragdoll mapping" );
HK_ASSERT2( 0, m_ragdollFromAnimation, "Couldn't load ragdoll-to-high mapping" );
}
m_worldFromModel.setIdentity();
m_worldFromModel.m_rotation.set( 0,0,1,0 );
// Create the skeleton
m_skeletonInstance = new hkaAnimatedSkeleton( m_skeleton );
m_skeletonInstance->setReferencePoseWeightThreshold( 0.1f );
// Get the animation and the binding
anime_controller.loadAnimation( "ef2_poses_stand.hkx" );
anime_controller.loadAnimation( "ef2_poses_test1.hkx" );
anime_controller.loadAnimation( "ef2_poses_test2.hkx", 1, 0 );
anime_controller.loadAnimation( "ef2_poses_test3.hkx", 1, 0 );
// Set the initial pose of the character
{
hkaPose animationPose( m_skeleton );
hkaPose ragdollPose( m_ragdollSkeleton );
// Sample the character's current animated pose
m_skeletonInstance->sampleAndCombineAnimations( animationPose.accessUnsyncedPoseLocalSpace().begin(), animationPose.getFloatSlotValues().begin() );
// Convert the animation pose to a ragdoll pose
m_ragdollFromAnimation->mapPose( animationPose.getSyncedPoseModelSpace().begin(), m_ragdollSkeleton->m_referencePose.begin(), ragdollPose.accessUnsyncedPoseModelSpace().begin(), hkaSkeletonMapper::CURRENT_POSE );
// Set the pose of the ragdoll
m_ragdollInstance->setPoseModelSpace( ragdollPose.getSyncedPoseModelSpace().begin(), m_worldFromModel );
m_ragdollInstance->addToWorld( ogreHavokTool::havokUtilities->m_world, true );
{ // set AABB from current init pose in world space, so that it encapsulates all bones
hkaPose ragdollPose( m_ragdollInstance->getSkeleton() );
m_ragdollInstance->getPoseWorldSpace( ragdollPose.accessUnsyncedPoseModelSpace().begin() );
hkAabb ragdollAabb;
ragdollPose.getModelSpaceAabb(ragdollAabb);
m_ragdollPhantom = new hkpAabbPhantom( ragdollAabb ,hkpGroupFilter::calcFilterInfo( LAYER_RAYCAST,0 ) );
ogreHavokTool::world()->addPhantom(m_ragdollPhantom);
}
{ // create detection object and raycast interface
// create raycast object
m_ragdollRaycastInterface = new PenetrationDemoRagdollRaycastInterface( ogreHavokTool::havokUtilities->m_world );
// setup DetectRagdollPenetration object
hkaDetectRagdollPenetration::Setup setup;
setup.m_ragdollSkeleton = m_ragdollInstance->getSkeleton();
setup.m_raycastInterface = m_ragdollRaycastInterface;
setup.m_ragdollPhantom = m_ragdollPhantom;
m_detectRagdollPenetration = new hkaDetectRagdollPenetration(setup);
}
}
// Create the new controllers
m_ragdollRigidBodyController = new hkaRagdollRigidBodyController( m_ragdollInstance );
and here's how I step and do the set-pose or drive-pose
hkaPose animationPose( m_skeleton );
hkaPose ragdollPose( m_ragdollSkeleton );
{
hkaPose upsampledPose( m_skeleton );
// Sample the character's current animated pose
m_skeletonInstance->sampleAndCombineAnimations( animationPose.accessUnsyncedPoseLocalSpace().begin(), HK_NULL );
// Convert the animation pose to a ragdoll pose
m_ragdollFromAnimation->mapPose( animationPose.getSyncedPoseModelSpace().begin(), m_ragdollSkeleton->m_referencePose.begin(), ragdollPose.accessUnsyncedPoseModelSpace().begin(), hkaSkeletonMapper::CURRENT_POSE );
// Map the ragdoll pose back to an animation pose
m_animationFromRagdoll->mapPose( ragdollPose.getSyncedPoseModelSpace().begin(), m_skeleton->m_referencePose.begin(), upsampledPose.accessUnsyncedPoseModelSpace().begin(), hkaSkeletonMapper::CURRENT_POSE );
}
anime_controller.ctrlAnimations( delta_time );
m_skeletonInstance->stepDeltaTime( delta_time );
int method = 3;
if ( method == 1 ) { //gives me the hunchback but good collision
m_ragdollRigidBodyController->driveToPose( delta_time, ragdollPose.accessSyncedPoseLocalSpace().begin(), m_worldFromModel, HK_NULL );
} else if ( method == 2 ) { //good pose but penetrats walls and not flexable when collide
m_ragdollInstance->setPoseModelSpace( ragdollPose.getSyncedPoseModelSpace().begin(), m_worldFromModel );
} else { //good pose but penetrats walls and not flexable when collide
m_ragdollRigidBodyController->driveToPose( delta_time, ragdollPose.accessSyncedPoseLocalSpace().begin(), m_worldFromModel, HK_NULL );
for ( int i = 0; i < m_ragdollInstance->getNumBones(); i++) {
hkpRigidBody* rb = m_ragdollInstance->getRigidBodyOfBone(i);
// Initialize with quality type and collision filter
if (rb != HK_NULL) { _setBodyKeyframed( rb ); }
}
m_detectRagdollPenetration->resetBoneStates();
}
(after line 18) note that method 1 and 2 uses the same hkaPose, but the method 2 actually gives me good pose result. So I assumed my hkaSkeletonMapper was set up alright.
Regards, Hiro
Hi guys!
Sorry for the long delay.
After tried out few different loadings and stepings, I've managed to solve the issue!
It was the constrain of the ragdoll and the rigbody overlaps.
like, my character has a bulky shoulder and the antennas at the side of its head. I've chosen a convex shell as it's rigbody. And they collapse with other body parts.
hope this will help someone to solve their problems :)
as for my progress.
I've created a state machine with a phantom.
the phantom can be moved around, and the character's animation can be set due to its state.
And, now trying to ease the ragdoll's position towards the phantom's position.
however, the m_rigidBodyController->driveToPose...
the rotation seemed to be doing it's job nicely (the rigbodies and the animation do match)
but the translation is kinda slow.
for example,
if the ragdoll's initial position is (0, 0, 0) and the phantom is (30, 0, 0), the ragdoll will moves really slow towards the phantom's position.
And if I rotate the ragdoll, the ragdoll's hand and leg will be like, left behine. cause they can't keep up fast enough.
maybe a PoweredConstraint would help with this issue?
Regards, Hiro
Hey Hiro,
I didn't think of that, but of course if the bodies overlap then they'll be driven apart and end up in strange positions.
What you need to do is set up a collision filter.
It's actually good practice to have ragdoll bodies overlap, since that makes it difficult for scene geometry to get stuck between the ragdoll body parts.
With a collision filter you can make it so that adjacent bodies in the ragdoll won't collide with each other, but will collide with everything else.
Check out the Physics manual, Chapter 3 section 1 on collision filtering.
So you can set up a group filter, where there's a layer for ragdolls. Within that layer there's one group for each ragdoll. You divide each ragdoll into two sub-groups 0 and 1, and set no collisions between subgroups 0 and 1.
Then for instance the upper arm will be in group 1, and the forearm and chest will be in group 0, and so on.
[Hopefully this will make more sense when you've read the documentation].
I think you're right to be using the ragdoll rigid body controller rather than the powered ragdoll controller.
The rigid body controller works by setting the velocities so it gives much stiffer results, and can drive the bodies to their model space positions. The powered constraint controller only acts by setting forces at the joints so it tends to be floppier and won't keep the character upright.
If you want to tune the parameters, access hkaRagdollRigidBodyController::m_controlDataPalette.
Have a look at Source/Animation/Ragdoll/Controller/RigidBody/hkaKeyFrameHierarchyUtility.h for descriptions of the control parameters.
Does your character have a very large scale? You seem to be making some sort of gundam giant robot, but the typical values of the ragdoll controller are probably set with human characters in mind :)
Cormac
Hi Cormac~~
wow, instant solutoins arrived after a shower~~
yes, I've dealt with the filtering already (yay~)
good to hear that I'm on the right track.
about the speed tuning.
I think that could be it!!!
it reminds me, that the 3dmax will ask me if I want to use the new scale (or unit? either way) when I open between my *.max and demo's *.max.
and if I load a demo's hkx into my test project, it is very small, compare to my other objects...
btw, in chapter 2.5.1.4 The Character Controller Behavior -> Default Dynamic Behaviour.
it mentioned about the walk speed and jump speed (which I had no clue where to access'em at the time).
was that talking about the m_controlDataPalette?
I'll work on that first thing in the morning and come back to share some results, thanks for the tips!!
so exsited! but got to go to bed or else my girlfriend will kill me... (light sleeper :p)
Ah, I've went to the Zelda's 25th symphony. (4th row!! lucky us!) it was marvelous! can't wait for the OST!
Regards, Hiro
The character controller and ragdoll controller are two separate things.
m_controlDataPalette is for the ragdoll controller, allowing you to set parameters like gains for how quickly the pose error of the ragdoll will be corrected.
The character controller is a high-level object that represents your character's movement about a scene.
If you want to set parameters like walking speed and jump height then you can do that while setting up the character controller state machine.
I think we've had a couple of threads here mentioning this in the past few months.
cheers, and good luck with it
Cormac
Morning Cormac!
i see, that makes sense!
so, after modified these 4 parameters to 1000 times, everything worked sweet!
m_snapMaxLinearVelocity
m_snapMaxAngularVelocity
m_snapMaxLinearDistance
m_snapMaxAngularDistance
btw, in a normal tps or fps game where a character's lower and upper body uses different set of animation.
A walking animation for the legs, and a gun holding animation for upper body and it can be pan around.
wonder what's the normal approach.
do I simply provide few sets' of animations and set'em depending onthe rigbody's name?
Regards, Hiro
Hey Hiro,
I'll just say - be careful with the scale of physics objects.
Because of the limitations of floating point math, physics calculations can be very sensitive to large variations in scale. Havok works best for objects that are between about 10cm and 10m - 'tennis ball to truck' scales.
It depends on the size of the rest of the objects in your game world, but you might consider scaling everything down (as long as that won't leave you with tiny objects).
As for the animation question, that would normally be done at the skeleton level.
Say you have two animations, one running and one holding/shooting a gun.
Then you have a set of bone weights for each animation: for the running animation the bones of the lower body are set to a weight of 1 and the upper body to zero; and vice versa for the shooting animation.
Then when you blend these animations together on the skeleton you get running in the lower body bones and shooting in the upper body bones.
hkaAnimationControl allows you to set per-bone weights.
Alternatively, you might be able to just have partial animations that work on a subset of the skeleton, and use hkaAnimationBinding to define those sets. This is a bit more complex than using full-body animations with per-bone weights, since there's more housekeeping to do and exporting partial animations might be trickier.
Then you use the ragdoll controller as usual to drive your ragdoll to the skeleton's pose.
Cormac


