set loop start frame of AnimBundleNode?

Hey,
we are creating an AnimBundleNode from our own animation format and I was wondering if it’s possible to set the loop start frame for the AnimBundleNode, AnimBundle, or lower level objects, or do I need to go the ugly way and return a list with the animbundlenode and startframe in a list instead and set it in the Actor?

Actor is just a thin wrapper around AnimBundleNode and related low-level classes, so everything Actor can do, you can do directly on the low-level classes.

The way to start animations using the low-level classes is to use the AnimControl object that you get from binding the PartBundle (the model) to the AnimBundle (the animation). With the AnimControl, you have the full play/stop/loop/pose interface.

In particular, you can loop from a particular frame using one of these two calls, depending on what you want precisely:

control.loop(True, startFrame, endFrame)

To start playing at startFrame, then play till endFrame and loop back to startFrame again continuously.

control.pose(startFrame)
control.loop(False)

To start playing at startFrame, then play till the end of the animation and loop back to the beginning of the animation then loop continuously.

David

The problem is I have a function which creates AnimBundleNode from an animation file, which has the loop start frame. I would like to set the loop start frame on the Anmation object (AnimBundleNode), because the animations might be reused on other models, so I don’t want to keep a list of the start frames, which can get ugly.

I don’t understand. There is no concept of a “loop start frame” saved within Panda’s AnimBundle object. You always specify the start frame whenever you start an animation looping. If you don’t specify a start frame at that time, the animation starts at frame 0.

If you want to bake in a particular frame to start the loop on, I suppose you could unroll the animation when you build the AnimBundle, so that the intended start frame is frame 0.

David

I don’t know if AnimBundleNode can store such info, I’m asking. Our format does.

What I mean by loop start frame is the frame the animation will start looping: the first time the animation will start at frame 0, 2nd time and onwards from a specified frame.

Nope, no concept like that in Panda.

Well I’m in a bit difficult situation, the animation format has that, there are hundereds of animations already made and counting and the tools use that format.
Maybe I could ask for a feature request?

I can think of only one ugly solution:

  1. have the animation loader function return a list containing the AnimBundleNode and loop start frame, and when assigning it to the Actor, tell it to play() the animation once completely, and then loop() from the start frame to the end frame. Actually play()-ing once from 0 frame to loop start frame and then loop()-ing would be the same).

How can I do that though? I would need to know animation play() finished before loop()-ing it again with the startFrame specified. Having a task to check if it’s finished each frame might be too much, and using a Sequence won’t work either. Well i could have a Func in the Sequence to loop it, but maybe there is a cleaner way.

I can think of several ways to do that.
(1) A Sequence with a Func in the end, as you say.
(2) With tasks, e.g. anim.play(), then a do-later task to call anim.loop() the appropriate number of seconds later.
(3) A threaded task that calls anim.play(), Thread.sleep(), anim.loop(). A bit risky unless you’re comfortable with threads, but easy to understand.
(4) An FSM with states named “StartPlay” and “Looping”. When you enter StartPlay, it calls anim.play(), then spawns a do-later task to transition to Looping after the appropriate number of seconds. When you enter Looping, it calls anim.loop(). This is really the same as (2) with an FSM to organize it.

David

Okay, thanks. Option 2 is also a simple and good one too.

Actually it didn’t work so perfectly.

The problem is, like you know from my other thread I sometimes need to get an animation from another Actor, assign it to this Actor and sync their animation frames. This will work OK if the animation on the original Actor has reached the loop-start-frame and is already looping, but if it’s still playing (hasn’t reached that frame), then if I change the current frame to match the other Actor’s, then the doMethodLater() won’t know about that and Sequence probably doesn’t allow to change the current frame of it’s component intervals.

The only ugly solution I can think of is to keep reference to all these doMethodLater tasks and each time when assigning the animation, add some extra delay somehow, if the animation hasn’t reached the loop frame (so keep a reference to that too).
Might you have a nicer solution?

I wish Panda would have something like this for actor animations (loopStartFrame), it seems such a common task.

As you say, keep around the doMethodLater tasks, and stop them when you sync the actor to another one.

This is where option (4) becomes attractive, because an FSM makes it easy to keep track of these kinds of unexpected state changes, and do the right thing internally. Simply create an FSM class that handles all of your animation, and always do animation requests via that FSM. (This FSM object can be the same object as your fundamental actor itself, unless you’re already using the FSM interface for some other purpose in that object–just add FSM to its inheritance list.)

Now you can add a new state to the FSM–“Sync”. When you enter Sync state, it will do whatever it needs to do to track the other actor’s frame. The doMethodLater task will have been automatically stopped by virtue of exiting the “StartPlay” task (you’ll create an exitStartPlay() method that kills the doMethodLater task).

Is it? I’ve never heard of it before. To me it seems like an odd thing to do, but I’ll take your word for it. I’m certainly in favor of adding new features that reflect the way people actually want to use Panda. Does anyone else reading this thread have an opinion on this kind of a feature?

David

Yes, I think it will be useful to others.
The main point is you’re animation might start with something you don’t want to loop, but you want to loop the rest of the animation from some frame. You can of course split your animation to multiple ones, might I think it can get messy, you will end up with multiple files which you use for a single action. And of course if you’re doing something like this, it won’t be very easy to sync animations.

I certainly can’t think of any negative consequences, it would probably just be another argument to loop(), or/and setting it later with a function like setLoopStartFrame()/getLoopStartFrame().

But it’s so limited. All you can do is assign a fixed lead-in animation to each looping animation. You can’t use this technique for transitioning between different looping animations, or for using different lead-in animations in different circumstances, or even for tail-out animations.

Even if all you want is an avatar that transitions from standing to walking to running and then back down to walking and standing, you can’t make use of a single lead-in per each looping animation.

For any kind of serious animation management, you’re pretty much going to need an FSM to manage your animation state. (And the kind of things you’ll want to manage are generally highly application-specific, so it’s not clear how Panda could facilitate this animation engine concept more easily than just what the generic FSM class already provides.)

What, specifically, does having a built-in lead-in animation allow you to do easily without having to manage it with an FSM?

David

I understand what you mean. Of course this isn’t useful for all cases, like most things.
The situation I’m talking about are things like interactive cutscenes. Fahrenheit by Quantic Dream is a good example. You don’t want to have one or few huge animations for your scene, because the animation which will be played depends on your choices. But you also don’t want to split your animations, which can go over hundred, to even more files. Now just put the advantages I mentioned here.

An actual example: when the NPC finishes talking and waits for you to choose what to say/do. The idle waiting animation is not the same for after everything he says.

Hm, looks like I can’t sync two animations properly.

from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.actor.Actor import Actor

base.cam.setPos(0,-34,6)

panda1 = Actor('panda')
panda1.reparentTo(render)
panda1.setX(-5)

panda2 = Actor('panda')
panda2.reparentTo(render)
panda2.setX(5)

panda1.loadAnims({'anim': 'panda-walk'})
panda1.loop('anim')

panda2.loadAnims({'anim': 'panda-walk'})

def func():
	frame = panda1.getCurrentFrame()
	panda2.loop('anim', fromFrame = frame, restart = True)
base.accept('enter', func)

run()
panda2.loop('anim', fromFrame = frame, restart = True) 

This is not the way to loop the entire animation beginning at “frame”. What this means is to continuously loop from “frame” and to the end of the animation, then repeating at “frame”.

Replace this with these two lines, which is what you really want to do:

panda2.pose('anim', frame)
panda2.loop('anim', restart = False)

This means to position the animation to “frame”, and then from there to loop over the entire animation, beginning at the current frame (which is still “frame”).

David

Okay, I asked few others of their opinion and the general view is it would be helpful for some game genres, but useless as most modellers wouldn’t allow you to export such information.