OgreXml model and Animation blending

Hello!



I've only been using the OgreLoader for about two weeks, it seem nice enough, although now I've hit a snag with it and I'm stuck :frowning:



The problem is with blending between two animations, it does not look right.



Using the setAnimation() on the MeshController without a blend time & everything works fine, when a non-trivial blend time is entered (>0.1f) the mesh goes a bit loopy.



Video demostrating the problem: http://www.youtube.com/watch?v=Md5criAczQk



The two animations shown are a thrusting attack and guard. When the key is pressed to trigger a blend animation event the white on screen text appears, then fades away until the next key press.



The model is a skeleton animation, weighted bones and a single mesh.

All the animations have the first keyframe at the guard pose, the bind pose has the man standing arms outstreached.



Any thoughts on what is causing this?

JME 2 or JME 3?



Could show a snippet of animation changing code that you’re using?



I did a little update for blending single animations for ogrexml JME 2 about a week ago, I doubt that’s causing the problem, but check it out.

http://code.google.com/p/jmonkeyengine/source/detail?r=4893

JME 2 from svn (revision 4949)



This animation problem I've had as soon as I tried blending, which was around a week ago.



I'd seen your animation fix post when searching the forums for a solution, and in all honesty I don't really know what the code change meant.



Having reverted the MeshAnimationController back to it's state prior to the fix you mention, there is no problem with the animation, but again it looks an awful lot like there is also no blending (identical animation to when no blendtime is used).



Initialisation


// Load the model
Node character = loadModel();

// Grab the animation controller
animationController = (MeshAnimationController) character.getController( 0 );



Switch animations with blending


animationController.setAnimation( "CQ Guard", 0.5f );




If it'll help, I can give you a standalone test case and the ogrexml files to reproduce this problem.

Thanks
Chris
monkey_scratches_head said:

If it'll help, I can give you a standalone test case and the ogrexml files to reproduce this problem.

Sure, I don't know how my little code was causing the error, so maybe your test code could point me to it.  I thought for sure if an issue like this came up it would show up in the ogrexml ninja tests.  :|

@SomethingNew - Thanks for the quick replies!

SomethingNew wrote:
I don't know how my little code was causing the error
Before your fix, to me it looked like blending was not happening at all, while after your fix the blending was happening.

Of course, if the blending was not happening, then your fix is not the cause, rather it's something to do the underlying blending is (notice there was an 'if').
SomethingNew wrote:
show up in the ogrexml ninja tests.
I have tried recreating with the ninja model, but couldn't create a good example
monkey_scratches_head said:
SomethingNew wrote:
I don't know how my little code was causing the error

Before your fix, to me it looked like blending was not happening at all, while after your fix the blending was happening.

Of course, if the blending was not happening, then your fix is not the cause, rather it's something to do the underlying blending is (notice there was an 'if'). Ok hmmm...

monkey_scratches_head said:
SomethingNew wrote:
show up in the ogrexml ninja tests.

I have tried recreating with the ninja model, but couldn't create a good example

this might not be your problem, but what animation package do u use, does orgexml support bone scaling, if not, u might have introduce some bone scaling animations in that app, check your animation properties and ensure that there is no scaling on your bones, that is if it is not supported. I had this issue a while ago but I use md5

mcbeth said:

this might not be your problem, but what animation package do u use, does orgexml support bone scaling, if not, u might have introduce some bone scaling animations in that app, check your animation properties and ensure that there is no scaling on your bones, that is if it is not supported. I had this issue a while ago but I use md5

This is true, bone scaling is not supported in the ogrexml importer, but by the looks of you're animation xml, you're not scaling any bones.
SomethingNew wrote:
Mount.Hand.Left bone isn't being keyframed (or exported) in the animations xml file and so it's using the default pose position.
The Mount.Hand.Left bone is a mountbone, with no weighting on the mesh, so would not be affecting the mesh. I have also tried the keyframing every bone, and the result is unfortunately still the same :(
SomethingNew wrote:
debugging on the head bone and see why it's translating during the blending phase.  Tell me what you find!
Okay, looks like that's my next course of action!
SomethingNew wrote:
but by the looks of you're animation xml, you're not scaling any bones.
That is correct, there is no bone scaling in the animations.

I'm pretty sure the problem is somehow linked / caused by the animation blending, as only during the blend time is the animation wrong.
This assumption I make on the basis that outside of the blendtime everything animates correctly, i.e. changing animations without blending, and the animation times after the given blendtime.

As to why? And how to solve the problem ..that I'm currently stumpted  :?

well cool then, I suggested it because I knew that md5 didn't support scaling, so I didn't deliberately scale bones either, but especially if use blender…with all its hot keys …shit can happen…



u should also check your constraint setup…ik and what not, something in the bone setup might not be 'sitting' well with the impor, especially as u are attempting to mix multiple animations, and your rig is complicated

@mcbeth

Thanks for you input, I understood the point you made, a perfectly valid and sensible angle to check.



Just for clarity, I have checked the bones on each of the keyframes and they all have the standard scale of 1.

The constraints setup for the bones all look as expected …so now I'll really start on debugging the head bone movement!

Just a guess, maybe you forgot to apply object scale and rotation in blender?  In Blender 2.49, if in object mode and having an active object selected click Object->Transform Properties (N).  This will show you the transformations applied to an object in object mode (not edit mode).  All Rot should be 0 and all Scale should be 1.  If they aren't, in object mode with the active object selected press Ctrl-A->Scale and Rotation to ObData.  You may have to disable your armature modifier if you do this.

SomethingNew wrote:
All Rot should be 0 and all Scale should be 1
I've just checked Blender again, both the mesh object and armature object have all rotations at 0 and all scales at 1. (I've had fun before with hottbj exporter and that problem)

Besides would a rotation / scale problem from Blender constantly cause a problem, not just when blending?


Thought I'd close off this thread with my findings.



After runtime investigation and s.o.p lines displaying the bone track affects on the bones rotation & translation during the blending, comparing them with the normal animation - the bouncing of the bones is caused by the compound multiplication of rotation.



The amount of work I've done looking into this single problem is only about 3 days, and in theory all the math behind the code should work, sadly for me and the way I'm creating animated models it does not.



…anyway my short term solution is a custom facade that adds a SmoothTransition to go between the keyframes of two seperate animations, while by no means perfect it'll do for the time being  :frowning:

So, is there a bug in the OgreXML animation system? Perhaps the rotations are not blended correctly, is slerp(rot, IDENTITY, weight) a correct way to reduce the "weight" of a rotation? I am really curious as to how other engines handle this.

Momoko_Fan wrote:
So, is there a bug in the OgreXML animation system?
I'm tempted to say yes, as the results are not what I'm expecting to see, but then I've no prior experience with blending animation in 3D engines.

On a brighter note, I've put together a test that demonstrating what appears to be the same problem using the Ninja model from the jmetest folder!
http://www.youtube.com/watch?v=KhzVhO7Hf6Q

and here's the test class

public class Main extends SimpleGame
{
    private static final Logger logger = Logger.getLogger(
            TestDotScene.class.getName() );

    private MeshAnimationController controllerNormal = null;

    private MeshAnimationController controllerBlending = null;

    public static void main( String[] args )
    {
        Main app = new Main();
        app.setConfigShowMode( ConfigShowMode.AlwaysShow );
        app.start();
    }

    @Override
    protected void simpleInitGame()
    {
        ResourceLocatorTool.addResourceLocator(
                ResourceLocatorTool.TYPE_TEXTURE,
                new ClasspathResourceLocator() );

        cam.setLocation( new Vector3f( 0f, 150f, -325f ) );
        cam.lookAt( new Vector3f( 0f, 100f, 0f ), Vector3f.UNIT_Y );

        Vector3f blendCentre = new Vector3f( 60f, 0f, 0f );
        Vector3f normalCentre = new Vector3f( -60f, 0f, 0f );

        controllerNormal = loadNinja( normalCentre );
        controllerBlending = loadNinja( blendCentre );

        // Slow the animation speeds
        float speed = 1f;
        controllerNormal.setSpeed( speed );
        controllerBlending.setSpeed( speed );

        animationNames = controllerNormal.getAnimationNames().toArray( new String[0] );

        // Change the background from black to gray
        display.getRenderer().setBackgroundColor( ColorRGBA.gray );

        // Add a floor
        Box boxNormal = new Box( "", normalCentre, 50f, 0.05f, 50 );
        MaterialState red = display.getRenderer().createMaterialState();
        red.setAmbient( ColorRGBA.red );
        boxNormal.setRenderState( red );
        boxNormal.updateRenderState();
        rootNode.attachChild( boxNormal );

        Box boxBlend = new Box( "", blendCentre, 50f, 0.05f, 50 );
        MaterialState blue = display.getRenderer().createMaterialState();
        blue.setAmbient( ColorRGBA.blue );
        boxBlend.setRenderState( blue );
        boxBlend.updateRenderState();
        rootNode.attachChild( boxBlend );

        // Simple key event mappings
        KeyBindingManager.getKeyBindingManager().set( "One", KeyInput.KEY_1 );
    }
    // Amount of time to blend - the amount of each animation being played is cycleMills
    private float blendTime = 0.5f;

    private float lockAfterBlending =  blendTime + 0.25f;

    private float blendingAnimationLock;

    String[] animationNames;

    @Override
    public void simpleUpdate()
    {
        super.simpleUpdate();

        // Is there currently a blending underway?
        if ( blendingAnimationLock > 0f )
        {
            blendingAnimationLock -= tpf;
        }
        else
        {
            // Test for the key input
            processInput();
        }
    }

    private void processInput()
    {
        if ( KeyBindingManager.getKeyBindingManager().isValidCommand( "One", false ) )
        {
            controllerNormal.setAnimation( animationNames[4] );
            controllerBlending.setAnimation( animationNames[4], blendTime );
            blendingAnimationLock = lockAfterBlending;
        }
    }

    private MeshAnimationController loadNinja( Vector3f location )
    {
        OgreLoader loader = new OgreLoader();
        MaterialLoader matLoader = new MaterialLoader();
        String matUrlString = "/jmetest/data/model/ogrexml/Example.material";
        String ninjaMeshUrlString =
                "/jmetest/data/model/ogrexml/ninja.mesh.xml";
        Node model = null;

        try
        {
            URL matURL = ResourceLocatorTool.locateResource(
                    ResourceLocatorTool.TYPE_TEXTURE, matUrlString );
            URL meshURL = ResourceLocatorTool.locateResource(
                    ResourceLocatorTool.TYPE_MODEL, ninjaMeshUrlString );

            if ( meshURL == null )
            {
                throw new IllegalStateException(
                        "Required runtime resource missing: "
                        + ninjaMeshUrlString );
            }
            if ( matURL == null )
            {
                throw new IllegalStateException(
                        "Required runtime resource missing: " + matUrlString );
            }
            try
            {
                ResourceLocatorTool.addResourceLocator(
                        ResourceLocatorTool.TYPE_TEXTURE,
                        new RelativeResourceLocator( matURL ) );
                // This causes relative references in the .material file to
                // resolve to the same dir as the material file.
                // Don't have to set up a relative locator for TYPE_MODEL
                // here, because OgreLoader.loadModel() takes care of that.
            }
            catch ( URISyntaxException use )
            {
                // Since we're generating the URI from a URL we know to be
                // good, we won't get here.  This is just to satisfy the
                // compiler.
                throw new RuntimeException( use );
            }
            matLoader.load( matURL.openStream() );
            if ( matLoader.getMaterials().size() > 0 )
            {
                loader.setMaterials( matLoader.getMaterials() );
            }

            model = (Node) loader.loadModel( meshURL );
        }
        catch ( IOException ex )
        {
            logger.log( Level.SEVERE, null, ex );
        }
        catch ( ModelFormatException mfe )
        {
            logger.log( Level.SEVERE, null, mfe );
        }

        // Attach the model to the scene
        model.setLocalTranslation( location );
        rootNode.attachChild( model );

        MeshAnimationController controller = (MeshAnimationController) model.getController( 0 );

        return controller;
    }
}


Momoko_Fan wrote:
is slerp(rot, IDENTITY, weight) a correct way to reduce the "weight" of a  rotation?
Me, no idea. I've googled it, and ended up just getting myself into a muddle.

Now would really be a good time for someone with the old 3D maths skills to chime in  :?

I think I fixed this bug (jME3)… It was the odd way of how I blended the animations I think. I tried the test in jME3 and couldn't notice the problem as much even with the blending the way it is so to be honest I am unsure.

Momoko_Fan wrote:
I think I fixed this bug (jME3)
Ah, a fine reason to migrate to jME3 then!  :D

Hi all,

sorry to up an issue that is 1 year old, but I was looking into that and I found that using nlerp instead of slerp works better. I don’t know if slerp method is bugged, but I just tried nlerp and it removes those glitches! I just added the following nlerp method in Quaternion.java and replaced slerp both in BoneTrack.setTime and in Bone.blendAnimTransforms.



[java]

/**

  • Sets the values of this quaternion to the nlerp from itself to q2 by blend.
  • @param q2
  • @param blend

    */

    public void nlerp(Quaternion q2, float blend)

    {

    float dot = dot(q2);

    float blendI = 1.0f - blend;



    if (dot < 0.0f)

    {

    x = blendI * x - blend * q2.x;

    y = blendI * y - blend * q2.y;

    z = blendI * z - blend * q2.z;

    w = blendI * w - blend * q2.w;

    }

    else

    {

    x = blendI * x + blend * q2.x;

    y = blendI * y + blend * q2.y;

    z = blendI * z + blend * q2.z;

    w = blendI * w + blend * q2.w;

    }



    normalizeLocal();

    }

    [/java]
1 Like

Hey,

mhh you mean in JME2?

If it’s in jME3, could you tell me what’s the issue?