Runtime model bones interpolation

Hello people!

Sometime ago I posted a topic here when I was going to start my project of sign language animation.

Now I’ve started it and I’ve used 3dStudio to build the models and the Processing API to animate it.
But Processing is very poor and how it cannot even handle with bones, I’m now intending to move do panda3D.

One the thing I’ve done in processing is a lib that, at runtime, operates an interpolation to morph an OBJ model into another OBJ models shape. Please, see details at AnimObj.

So, my first goal on Panda3D is to make the same thing.
In manual pages I’ve seen how extract a joint and procedurally control it. So I could extract all the joints off the model and perform the interpolation according another given model at runtime.

(obs: with Processing I was interpolating the mesh vertexes; with Panda3D I want to interpolate the bones, expecting get less interpolation bizarre effects : )

But I’m wondering, and I would like help in this point, if Panda3D has already in same manner this feature.

The main idea is get something like:

model = new AnimObj("model1");
// render model
otherModel = new AnimObj("model2");
model.setNextPose(otherModel);
model.startAnim();

where, for example, model1 is a closed hand model and model2 is a opened hand model; so we have first the closed hand rendered and we must get an animation of a hand opening.

Very thank you
Bye

ps: you can check what I’ve achieved until now with Processing in this video demo.

panda3d.org/manual/index.php … erpolation

:slight_smile: all you need to do is activate it, and panda will do all the magic for you.

I think what he is describing is actually animation blending, to have a bunch of 1-frame animations and blend smoothly between between them.
You can achieve this with code something like below:

actor = Actor()
anims = {'walk': 'walk_filename', 'run': 'run_filename'}
actor.loadAnims(anims)
actor.setBlend(animBlend=True)
actor.loop('walk')
actor.loop('run')
actor.setControlEffect('walk', 1.0)
actor.setControlEffect('run', 0.0)

Now you can adjust the weights of the animations over time with setControlEffect, for example with a task. It is good practice to only call actor.loop(‘anim’) when you want to start blending into that animation, and call actor.stop(‘anim’) when the control effect is reduced to 0.

Let me point out that, if this is indeed good practice, it is only because doing so may make it easier to remember which animations you have left playing.

There is no technical reason to insist that animations be stopped when they are set to 0. There is no performance cost to keeping an animation in the playing state when its control effect is set to 0–setting the control effect to 0 effectively disables all computation associated with that animation.

(This is unlike, say, transparent objects; rendering transparent objects does introduce a real and ongoing cost, even when you have set their opacity to 0.0. It is good practice for technical reasons to hide() an object whose opacity has been faded to 0.)

David

Ah, my mistake. I thought I had read somewhere that the animation would still be calculated.
I suppose it might also be useful if you wanted to ensure that the animation started blending from the first frame.

Humm… I’ve read this page before and unfortunatly I think it’s not my case

Correct me if i’m wrong, BUT this function (or blending) creates such interpolations for the animations Panda are running. But in my case, I don’t have any animation in my models… I want to create an animation at run time!

Let me explain my needs… my “opened hand” and “closed hand” are just examples, but there are over one hundred hand shapes… and, like in a game, just at tun time I’ll choose what shape a hand must take, so the animation is set at runtime and it can be from any hand shape to another shape else.

Pre-build animations with all possible combinations is what I really do not want.

So, another way to question is: how to program an animation based on a start model that morphs itself until become like a target model, where both start and target model are given at run time.

Thanks a lot

An “animation” can also be a pose, e.g. the hand in the closed position can be a one-frame animation, and the hand in the open position can be another one-frame animation. With this model, you can animate between open and closed by lerping the controlEffect for the closed animation down while you lerp the controlEffect for the open animation up.

It’s a little bit clumsy, but it sounds like this is the effect you’re asking for.

David

Humm… I’ve just tried the following:

    self.eve = Actor("models/opened", {'opened' : "models/opened", 'closed' : "models/closed"})
    self.eve.setBlend(animBlend=True)
    self.eve.reparentTo(render)          #Put it in the scene

// another place...
      self.eve.loop("closed");

And nothing happens…
Is it not what are you telling me?

PS: And the line “actor.loadAnims(anims)” got me in an error.

You still need to use setControlEffect to give the animation a weight.
The “actor.loadAnims(anims)” line is not needed in your case since you are loading the animations when creating the Actor.

Ouch… I’ve just tried in a couple of ways, such as:

or…

and nothing still happens… (my hand stands opened)

And reading the Blending manual page it doesn’t seem be the case…

Works fine for me. Are you sure your animation files loaded correctly? Were there any error messages on the console?

David

No, I don’t have any errors on console

I think it’s loaded OK because if I do

    self.eve = Actor("models/opened")
    self.eve.reparentTo(render)          

or

    self.eve = Actor("models/closed")
    self.eve.reparentTo(render)          

things work like supposed.

Well, that means it’s not correct, actually. You need to understand the difference between a “model” and an “animation”. A model file contains polygons and vertices, which are assigned to an invisible skeleton. An animation file is just a table of joint positions.

In the line:

self.eve = Actor("models/opened", {'opened' : "models/opened", 'closed' : "models/closed"}) 

The first parameter, “models/opened”, must be a model file, that contains polygons. The second parameter names only animation files (and they must contain animation tables that bind onto the first-named model file). It’s normally a different filename than the model file.

If you load an animation file only, you normally won’t see anything at all.

David

Ha! Nice… I think I’m getting…

So, the ideia is exporting the different poses (hand shapes) as single-frame animations and so make that I was trying before…!

I’ll return as soon as I try this…

very thank you!

=D

Hello!

Finally I’ve come back.

I think now I’m in the write way, but still not there.

When I use the loop command (with or without the blending controls) I got bad effects…

Maybe it can be some problem in exporting 3d studio to panda…
To do that we’ve followed this page: panda3d.org/manual/index.php … Studio_Max.

I’m sending all the code and the models, so it’s possible for you check both: tinyurl.com/2vyq7z9

The main code part is:

    anims =  {'closed' : "models/closed", 'opened' : "models/opened"}
    self.eve = Actor("models/hand")
    self.eve.loadAnims(anims) 
    self.eve.setBlend(animBlend=True)
    self.eve.reparentTo(render)

    self.eve.loop('closed')
    self.eve.loop('opened')
    self.eve.setControlEffect('closed', 1.0)
    self.eve.setControlEffect('opened', 0.0)

Another problem is if I write only “self.eve = Acotr()” instead “…Actor(“models/hand”)” // teedee has done it in his code example

I got the follow error:

leonardo@leonardo-laptop:~/panda/jonah$ python jonah.py 
DirectStart: Starting the game.
Known pipe types:
  glxGraphicsPipe
(all display modules loaded.)
Traceback (most recent call last):
  File "jonah.py", line 43, in <module>
    w = World()        #Create an instance of our class
  File "jonah.py", line 24, in __init__
    self.eve.loop('closed')
  File "/usr/share/panda3d/direct/actor/Actor.py", line 1555, in loop
    for control in self.getAnimControls(animName, partName):
  File "/usr/share/panda3d/direct/actor/Actor.py", line 1849, in getAnimControls
    allowAsyncBind = allowAsyncBind)
  File "/usr/share/panda3d/direct/actor/Actor.py", line 2341, in __bindAnimToPart
    bundle = self.__commonBundleHandles[subpartDef.truePartName].getBundle()
KeyError: 'modelRoot'

Ah, yes I left out the line to load the model.
You can do

self.eve = Actor()
self.eve.loadModel('models/hand')

or

self.eve = Actor('models/hand')

It has the same results.

I will take a look at the files and see if I can spot anything wrong.

I can tell you that the model itself appears to be set up properly, and that the problem is with the animation files. Specifically it is the joints at the tips of each finger. They are being scaled and moved improperly. Is there something special about them that is different than the rest of the joints? In most 3d packages there will be an IK controller at the end of a chain of bones, maybe this is what the problem is. If so you could add another regular bone to each finger and not use this IK controller for weighting geometry to.
Here is the modification I made to the script to visualize the problem:

import direct.directbase.DirectStart
from panda3d.core import AmbientLight,DirectionalLight
from panda3d.core import Vec3,Vec4
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
import sys



joints = [
"Bip01 R Hand",
"Bip01 R Finger12",
"Bip01 R Finger02",
"Bip01 R Finger4",
"Bip01 R Finger42",
"Bip01 R Finger41",
"Bip01 R Finger0",
"Bip01 R Finger01",
"Bip01 R Finger32",
"Bip01 R Finger22",
"Bip01 R Finger3",
"Bip01 R Finger31",
"Bip01 R Finger2",
"Bip01 R Finger21",
"Bip01 R Finger1",
"Bip01 R Finger11",
]



class World(DirectObject):
  def __init__(self):

    #setup key input
    self.accept('escape', sys.exit)

    #base.disableMouse()                  #Disable mouse-based camera-control
    camera.setPos(1000,-1500, 1200)              #Position the camera

    anims =  {'closed' : "models/closed", 'opened' : "models/opened"}
    self.eve = Actor("models/hand")
    self.eve.loadAnims(anims)
    self.eve.setBlend(animBlend=True)
    self.eve.reparentTo(render)
    self.eve.setRenderModeWireframe()

    for joint in joints:
        bone = self.eve.exposeJoint(None, 'modelRoot', joint)
        smiley = loader.loadModel('smiley')
        smiley.reparentTo(bone)
        smiley.setColor(Vec4(1, 0, 0, 1), 1)

    self.eve.loop('closed')
    self.eve.loop('opened')
    self.eve.setControlEffect('closed', 0.0)
    self.eve.setControlEffect('opened', 1.0)


    self.setupLights()                           #Put in some default lighting


  def setupLights(self):                    #Sets up some default lighting
    ambientLight = AmbientLight( "ambientLight" )
    ambientLight.setColor( Vec4(.4, .4, .35, 1) )
    directionalLight = DirectionalLight( "directionalLight" )
    directionalLight.setDirection( Vec3( 0, 8, -2.5 ) )
    directionalLight.setColor( Vec4( 0.9, 0.8, 0.9, 1 ) )
    render.setLight(render.attachNewNode( directionalLight ) )
    render.setLight(render.attachNewNode( ambientLight ) )

#main
w = World()        #Create an instance of our class
run()              #Run the simulation