SkinnedModel Animations seem to be in the wrong space?

I read an article where there were pros and cons for both. FBX is good for later editing and glTF if you just want to render it and / or send the animation over the internet.

How does the animation blending work? Are you blending keyframes of both animations?

I have a current clip, and previous clip.
When I start a clip, if one is already running, that get’s made the prev clip.
While getting the transforms, I get both sets, and blend them.
Simple as that really.

1 Like

Not used to the terminology in animation what is a clip? The set of keyframes? So you do calculate both animations (interpolation between keyframes) and then interpolating the results again with linear interpolation?

Good to see something happening in this topic / area (animation) in MonoGame. :slight_smile:

I played around with animation in XNA but when trying to port it over to MonoGame the content pipeline was lacking some classes and I couldn’t easily figure out how to implement the stuff myself. Maybe I will revisit the animation stuff.

Here is an older video showing some test animation.

1 Like

Nice :smiley:

I don’t have my code in front of me, but my clip is the collection of frames, the frames are the bone transforms for each bone for that clip.

So, when transitioning, I lerp based on a blend factor, beween the previous bone and the current bone in the two animation clips.

It will be a post on my Patreon page at some point :stuck_out_tongue: I have already posted on how I am doing the pipeline extension, it’s based on one I did in my old XNA engine, which, as I say was based on the XNA Skinned mesh sample, only I use it to also pull a lot more data out of the mesh :slight_smile:

2 Likes

That’s soft-shadowed right? - looks really good.
I think I might be doing it in very similar way to Charles (?)
Initializer:

idleTransforms  = new Matrix[bones_count];
walkTransforms  = new Matrix[bones_count];
blendTransforms = new Matrix[bones_count];
// COPY BONE TRANSFORMS
hero[IDLE].CopyAbsoluteBoneTransformsTo(idleTransforms);
hero[WALK].CopyAbsoluteBoneTransformsTo(walkTransforms);
// START AN ANIMATION 
AnimationClip idleClip = skinDataIdle.AnimationClips["Take 001"];  // "Take 001"
AnimationClip walkClip = skinDataWalk.AnimationClips["Take 001"];
idleAnimPlayer.StartClip(idleClip);
walkAnimPlayer.StartClip(walkClip);

Update:

playspeed = speed * 6f + 0.8f;
TimeSpan elapsedTime = TimeSpan.FromSeconds(gameTime.ElapsedGameTime.TotalSeconds * playspeed);
idleAnimPlayer.Update(elapsedTime, true, Matrix.Identity);
walkAnimPlayer.Update(elapsedTime, true, Matrix.Identity);

DrawPrep [(3 animations(idle, walk, run) depending on speed]:

// START SKINNEDMESH ANIMATION BLEND
Matrix[] bones1;
Matrix[] bones2;
float percent;
if (speed <= 0.2f) {
    bones1 = idleAnimPlayer.GetSkinTransforms();
    bones2 = walkAnimPlayer.GetSkinTransforms();
    percent = 1f / 0.2f * speed;                  // first 20%
}
else {
    bones1 = walkAnimPlayer.GetSkinTransforms();
    bones2 = runAnimPlayer.GetSkinTransforms();
    percent = 1f / 0.8f * (speed - 0.2f);         // remaining 80%
}
int i = 0;
while (i < bones1.Length) {
    blendTransforms[i] = Matrix.Lerp(bones1[i], bones2[i], percent);
    i++;
}            

Draw :
(this may look slightly different than usual because I’m using a customized version of skinnedEffect - so I extract a few shader/material values out of the default effect settings attached to each mesh):

// RENDER CHARACTER
foreach (ModelMesh mesh in hero[IDLE].Meshes) {
    foreach (ModelMeshPart part in mesh.MeshParts) {
        SkinnedEffect oldEffect = (SkinnedEffect)part.Effect;
        heroFX.alpha = oldEffect.Alpha;
        if (heroFX.alpha < 0.6f) continue;                    
        heroFX.preferPerPixelLighting = true;
        heroFX.SetDiffuseCol(Color.White.ToVector4());
        heroFX.SetSpecularCol(new Vector3(0.2f, 0.3f, 0.05f));
        heroFX.SetSpecularPow(256);
        heroFX.SetBoneTransforms(blendTransforms);
        heroFX.world = mtx_hero_rotate * Matrix.CreateTranslation(hero_pos);
        heroFX.SetDrawParams(cam, oldEffect.Texture);
        gpu.SetVertexBuffer(part.VertexBuffer);
         gpu.Indices = part.IndexBuffer;
         gpu.DrawIndexedPrimitives(PrimitiveType.TriangleList, part.VertexOffset, part.StartIndex, part.PrimitiveCount);
    }    
}
2 Likes

Similar yes, in principle, that is what I am doing :smiley:

1 Like

Thank you. Yes, I had soft shadows on for a point light in this case if I remember right. Maybe I can record newer videos when I revisit the project sometime.

2 Likes