Blending Animations By (Blender3d) Keyframe? (Solved)

Hey, long time no see, I have been hard at work on my next project, but I have run into a stump lately, you see I have been animating my models by keyframe in blender3d and it works, but when I change animations it “snaps” to the next animation, which works but does not look really that good.

Sadly the next question requires some familiarly with Blender3d, What I want is to stop a animation then load the next animation but I want panda3d to transition and move the mesh from it’s stopped position to the first keyframe of the newly loaded animation as if it is moving to the next keyframe if it were in blender3d.

I heard of animation blending but I don’t know if it works by key frame or actual animation frame, anyway, thanks to anyone who is willing to help.

I think that Panda’s solution to this is indeed animation blending.

If I’m not much mistaken, this blending is based on the current animation frame, not on keyframes. However, if you want it to specifically move from keyframe to keyframe, then I think that it might be achievable by stopping the old animation on a keyframe and posing the new animation on a keyframe, then blending from one animation to the other. That should then produce the effect of transitioning from keyframe to keyframe.

If you want the new animation to then start playing, you might wait for the blend to finish and then simply play the new animation.

The steps involved might be something along the lines of the following:

  • Stop the old animation
  • Pose the new animation at the intended keyframe
  • Blend from the old animation to the new over a period of time
  • Once the blend is complete, start the new animation.

(A Sequence might be one way of implementing the above.)

So how would you stop on a keyframe? do I obtain it’s frame number within blender3d? or can panda3d grab it’s frame position/location within that animation’s timeline? (are key frames even recorded within the egg files?), if it is the former, there are problems that would cause major bloat within my code.

Like, I would have to create a map for keyframe locations within the frame timeline for each animation file, maybe I can do within txt files?

For the record, Panda doesn’t have a notion of “keyframes”. It encodes every frame individually, as though every frame were a keyframe.

Hmm… Perhaps it would help if you were to describe what it is that you’re trying to do, that keyframes specifically are required. (As opposed to just any animation frames.) Maybe we can come up with alternative approaches! (And honestly, I’m quite curious.)

If keyframes, specifically, are required, then indeed, it may call for your to record them yourself somehow, and then pose the relevant animations at the given times.

Thanks rdb, I was figuring that was the case.

Thaumaturge, I sure you know what stop motion animation is, maybe I worded it wrong but what I meant by keyframes are poses, like stop motion animation except blender3d makes it much more easier because you can do one pose then make another pose some frames away and blender3d will take care of movement for the frames in-between them.

Edit: I may be blowing this situation over proportion though, thinking about, Thaumaturge you have it half solved when you said to stop and pose from the last played frame of the animation that is going to be changed.

What I really need is a solution that somehow move the mesh from that pose to the pose of the 1st frame of the next animation without the help of any intervening animation.

(For the sake of clarity, let me note that I’m reasonably familiar with Blender and keyframe-based animation, as well as with exporting animations from Blender to Panda; what I’m perhaps still a little confused about is what you’re trying to do here that isn’t covered by the standard methods.)

So… you’re creating numerous single-keyframe animations, and want to blend between them procedurally? Is that right? But that seems to conflict with what you say about stopping an animation, so… I’m confused. ^^;

I think that what I suggested above should do exactly this: just use animation blending to lerp from one to the other.

Something like this (untested):

Setup:

self.myActor.enableBlend()

Later, when you want to lerp between anims. Presume for the sake of example that “self.myActor” has been playing “anim1”, and that you want to transition to “anim2”.

self.myActor.stop("anim1")
self.myActor.pose("anim2", 1) # I forget whether this should be 0 or 1... ^^;

# Set the initial control-values
# Simply put: full contribution from "anim1", and
# no contribution from "anim2".
self.myActor.setControlEffect("anim1", 1)
self.myActor.setControlEffect("anim2", 0)

# Some values used in the next section for the lerping of the animation
self.fromAnim = "anim1"
self.toAnim = "anim2"
self.animBlendVal = 0

Then, in a task somewhere:

# Skip all of this if we're not currently lerping
if self.fromAnim is not None:
    # Update the animation blending value
    self.animBlendVal += dt*self.animBlendSpeed

    # We don't want the value to exceed 1, so clamp it
    if self.animBlendVal > 1:
        self.animBlendVal = 1

    # Set the control-effects appropriately, based on the blend-value
    self.myActor.setControlEffect(self.fromAnim, 1 - self.animBlendVal)
    self.myActor.setControlEffect(self.toAnim, self.animBlendVal)

    # If the blend-value has hit 1, then we're done!
    if self.animBlendVal == 1:
        self.fromAnim = None

(There may well be a way to do this via a Sequence, instead of a task, if you prefer that approach.)

Note that, depending on the logic that you put in place and the functionality that you intend, you may want to be careful about ending up with lots of animations trying to blend with each other, as this may or may not be what you want.

1 Like

Sorry for the confusion and trouble I caused, I suspected your expertise on blender3d, it’s just my wording I feared trying to describe the question I want to get out. It is kinda difficult for me to put into words, I attempted to clarify but was unable to then, but I,m trying ask to if panda3d can move the mesh as if it were animated between 2 different frames like blender does between two keyframes.

As said blender takes care of movement between those keyframes, I was wondering if Panda3d can do the same, but from your syntax examples you gave me it seems like it can, I don’t know at the moment since I skimmed through your post since I,m tried, but I reread it when I wake up and test some your examples later today, thank you for that, also sorry for the confusion.

Not a problem! And I’m not sure that I’d go so far as to call my experience “expertise”! Let’s just say that I’m familiar with it, and leave it at that.

Rest well, and I hope that your dev’ing goes well in the new day!

Regarding movement, let me note for safety’s sake that, if I’m not much mistaken, animations in Panda won’t change the position of your Actor’s node. They may change its apparent position if the keyframes include position offsets, but not its underlying position. Depending on what you’re doing, this can have occasional implications for your code, as culling or position-calculations might be affected (or not affected as expected).

Thanks, I tried my interpretation of your code, but the character mostly stayed still but when the animation did play, it snapped to the other animation (not the effect I wanted), I don’t know it was my interpretation or not, but I’ll keep trying though.

Edit: So I rechecked and and edit and move some variables around and it almost works but animations barely play or don’t play at all, I’ll keep testing though, also, you said there may be a way with a sequence, what about from a play loop to another?

Edit2: Hey, I just studying what the pose() command does, and If I,am correct it basically moves the mesh to position of the chosen animation and frame much like a keyframe does, so I was wondering if it is possible to blend poses?

Maybe by grabbing the pose of the last frame of the animation played and grabbing the pose of the 0 frame of the animation that will be played and blending them together will cause movement between those 2 poses and give the effect I desired.

Hum, so you want to blend between two animations that are playing, but starting only on a keyframe, is that right…?

Unless you mean that the animations don’t play after the blend–that is, that you blend from “anim1” to “anim2”, then play “anim2”–and “anim2” doesn’t play.

If so, then that may suggest that your control-effect values have “anim2” contributing little or nothing to the final result, as though it has a control-effect value of 0.

If you’re still having this problem, could you perhaps put together a simple test-program using your code, maybe with a simple animated model (e.g. a box that has a “spinning” animation and a “stretching” animation)? That way I can see what’s happening for myself.

… That’s pretty much what I had in mind with my code above. ^^; Note that I have it posing the new animation, and stopping the old one, so that it moves from static frame to static frame, without the animations playing.

It does occur to me that perhaps blending doesn’t work properly with a stopped animation–perhaps, instead of stopping the previous animation as I have in my code above, it will work if you instead pose the previous animation.

If you don’t mind, could you describe please what the intention of this feature is? What effect are you trying to achieve here? Why are you blending these animations? I still feel like I have a poor grasp of what you’re trying to do, and that makes it more difficult to come up with a workable solution, I fear… Sorry about that! :/

I’ll try to explain, so let’s say you had character with a standing animation but you also had a walking animation that started with the left foot out for that character, using a simple loop animation from one to another would change to the character from standing directly with his left foot out because that how the walking animation was created to start from.

Hence the term snap, now what I want panda3d to do instead of directly changing the position, is have panda from the standing position move the character’s left foot out into the walking position, so from there the walking animation can begin.

But moving pose to pose should give that result, so I don’t know why it moves only half to quarter way, but I could be from the “blend()” command as I was doing research and someone said it plays all the animation involved at the same time, so maybe that is the issue

Aaah, I see!

In that case, does it really matter that you go from exact keyframe to exact keyframe? I’ve implemented something similar myself, I believe, and I’ve found it to be fine just blending between animations as they play.

Hmm… That would seem to imply that you have at least one animation running aside from the target animation that has a non-zero control-effect value.

Could you post the relevant code from your project, please? Perhaps then I’ll see where the problem lies!

yeah, give me a second.

    def animation(self, task):
        global toAnim
        global fromAnim
        global abls
        global animVal
        if not fromAnim is None:
            if not abls:
                toAnim = str(self.pmodel.getCurrentAnim()) 
                self.pmodel.stop(fromAnim)
                self.pmodel.pose(toAnim, 0)
                self.pmodel.setControlEffect(fromAnim, 1)
                self.pmodel.setControlEffect(toAnim, 0)
                animVal  = 0
                abls = True
            animVal += 0.1
            if animVal > 1:
                animVal = 1
            self.pmodel.setControlEffect(fromAnim, animVal)
            self.pmodel.setControlEffect(toAnim, animVal)    
            if animVal is 1:
                self.pmodel.pose(toAnim, 0)
                self.pmodel.loop(toAnim)
                abls = False
                animVal = 0
                fromAnim = None

        return Task.cont

So when ever a animation is called, I have “fromAnim” equal “str(self.pmodel.getCurrentAnim())” just before the call and the call itself is just “toAnim” equaling a string that is the name of said animation that is being called.

Sorry, it is not the best interpretation, as I tried to squeeze your concept into a single task as best as I can, I was also playing with it too, so its kind of different now, sorry.

Ah! I see one problem at least, I believe!

When you set the control-effect values, you’re setting them both to the same value. That means that both of them end contributing the same amount to the animation, and you only ever see an equal blend–never just one or the other, or shading between them.

(For one thing, note that, if I’m not much mistaken, the control-values don’t specify a percentage of the animation to show, but rather a weight. Thus, if two animations have the same control-values–regardless of what that value might be (possibly excluding zero; I’m not sure)–they will contribute equal amounts to the final result.)

What you have at the moment is something like this:
animVal = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0
fromAnim control value = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0
toAnim control value = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0

What you want, I think, is something like this:
animVal = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0
fromAnim control value = 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0
toAnim control value = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0

Note that “fromAnim” counts down, while “toAnim” counts up. This means that the control-value–the weight–of “fromAnim” decreases, as the control-value of “toAnim” increases.

Thus, while it makes sense to just use “animVal” for the “toAnim”, I think that you would want to use 1 - “animVal” for the “fromAnim”.

Note, by the way, that your current code doesn’t take into account the frame-rate of your program. If that’s intentional, then fair enough! If not, then you might want to incorporate the time since the last frame (“dt”) into the changing of “animVal”.

Also, if I may ask, is “fromAnim” being set to “None” anywhere? You’re checking for that case, but I don’t see any code so assigning it.

1 Like

thanks, I take a another whack at it, then come back with the result soon.

Edit: so I applied your changes and it almost works, yes it moves from end frame to start frame of the next animation like I want however, the character either no longer animates or animates about 3 to 5 frames of the animation, I’ll look more into this issue.

Edit2: I got it to work, after tweaking the code on my part, a little glitchy mostly likely due to my code, you basically solved and answered my question, thank you.


Edit3: Hey, I didn’t this forum has a mark post as the solution feature until now, I just did.

1 Like

I’m glad that you got it working, and I’m glad if I’ve been of service! :slight_smile:

As to the glitchiness, from what I recall of my own work on a similar feature, there are a number of potential cases that can impact how the feature works. Indeed, my implementation is not entirely uncomplex!

1 Like

after 6 hours, I was able to fix the glitch, but thanks, also for future reference, I’ll post the updated task below…

    def animation(self, task):
        global toAnim
        global fromAnim
        global blAnim
        global animVal
        global playAnim
        global plAnim
        global hitdur
        global jamAnim
        global loading
        
        if not loading:
            if not jamAnim is None:
                blAnim[0] = jamAnim
                blAnim[1] = None
                jamAnim = None
        else:        
            if jamAnim is None:
                jamAnim = blAnim[0]

        if toAnim is None:
            if not blAnim[0] is blAnim[1]:
                fromAnim = str(self.pmodel.getCurrentAnim())
                toAnim = blAnim[0]
                self.pmodel.stop(fromAnim)
                self.pmodel.pose(toAnim, 1)
                self.pmodel.setControlEffect(fromAnim, 1)
                self.pmodel.setControlEffect(toAnim, 0)
                animVal  = 0
                blAnim[1] = blAnim[0]
        if not toAnim is None:    
            animVal += (globalClock.getDt() * 10)
            if animVal > 1:
                animVal = 1
            self.pmodel.setControlEffect(fromAnim, 1 - animVal)
            self.pmodel.setControlEffect(toAnim, animVal)    
            if animVal is 1:
                if playAnim:
                    self.pmodel.play(toAnim)
                    plAnim = True
                    hitdur = 0
                else:
                    self.pmodel.loop(toAnim)
                    plAnim = False
                animVal = 0
                fromAnim = None
                toAnim = None

        return Task.cont
# just place "blAnim[0]" anywhere the animation loop is placed, and add the
# the name of the animation you want to loop as a string to equal blAnim[0]
# if you want to play, simply add "playAmin" as true after the blAnim[0]
# variable, but to that end you must add it after each loop() animation
# as False.

That way people wanting to do this can create code based off this. NOTE: to those looking at this, this is not perfect, so I don’t recommend using it directly unless you are comfortable with it.

1 Like