animation looping

animation looping

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

12 帖子 / 0 全新
最新文章
如需更全面地了解编译器优化,请参阅优化注意事项

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 Detectionthen integrate the Api/Playback/Ease Curvesand 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/2is 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 gethkpPhysicsData* physicsData from method 1 (in order to access the motor)orhkRootLevelContainer* 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 :pbtw, 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_snapMaxLinearVelocitym_snapMaxAngularVelocitym_snapMaxLinearDistancem_snapMaxAngularDistancebtw, 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

发表评论

登录添加评论。还不是成员?立即加入