multi part actor issues

I’m working on a megaman game, which needs multipart actors. The actors won’t load on their bones properly. It says:

:Actorwarning: UARM.L not found!
:Actorwarning: UARM.R not found!

even though these parts are found in all the egg files. Maybe its a scripting issue? Here is a link to the script and all the eggs. I haven’t found any sample programs using multipart actors. I’m using blender by the way.

http://ambyra.googlepages.com/mml.zip

Do you really need multipart actors, in which you can actually mix-and-match geometry from different files, or do you only need to have independent controls of the animation on the different parts? If the latter, it’s much easier to use the subpart feature of Actor.

If you really do need multipart actors, try searching the forums for examples. As to your immediate problem, it sounds like you haven’t exposed the joints in question (they must be exposed from the base part, so that the nested part can be parented to the exposed nodes).

David

I do need the multi-part; megaman will change his arms for different weapons (like armored core for playstation). I tried to expose the joint but panda can’t find it or something, even after I just made it?!

MM=Actor(
    {"MM_BODY":"MM_BODY.egg",
    "MM_BUSTER":"MM_BUSTER.egg"},

    {"MM_BODY":{"rest":"MM_BODY-rest.egg"},
     "MM_BUSTER":{"rest":"MM_BUSTER-rest.egg"}
     })

mmjoint = MM.exposeJoint(None, 'MM_BODY','UARM.L')
MM.attach("MM_BODY", "MM_BUSTER", "mmjoint")
MM.actorInterval('rest').loop()

MM.reparentTo(render)

plight = PointLight('plight')
plnp = render.attachNewNode(plight)
plnp.setPos(0, -20, 0)
render.setLight(plnp)


run()

And I get this for an error:

DirectStart: Starting the game.
Warning: DirectNotify: category 'Interval' already exists
:Actorwarning: mmjoint not found!
:ActorIntervalwarning: Animations 'rest' on aBUSTER have an inconsistent number of frames.

Panda displays it like this, with the arm centered on the body.

The name of the joint is ‘UARM.L’, not “mmjoint”. You could also see this with MM.ls(), after you have made the call to exposeJoint(). The call to MM.attach() is looking for a node with the name you specify, and the node is created with the same name as the joint it exposes.

There are other ways to achieve a similar effect, than using multipart actors. One common technique is to model your actor with all possible arms, and then hide or stash the arms that he isn’t using at any given time. Swapping out geometry can work too, but you have to be prepared to learn a lot about the multipart actor system: it isn’t necessarily straightforward.

David

Thanks for your interest David. I’ve been looking for a sample program that accomplishes this, but everyone seems to find a way around this task. Unfortunately for me, I have two heads to be swapped out, two sets of feet, two sets of left arms, and four right arms. So, 2 x 2 x 2 x 4 = 1,000,000 models… haha, not possible. Actually, I’m not really sure how to apply the exposed joint to the model. I just get shot down with UARM.L not found.

I have all the models made up and rigged already. It would be a shame for them to go to waste. If I could be pointed in the right direction I would really appreciate it!

I did try to point you in the right direction. Did you try mm.ls() to see the name of the joint node after you had exposed it? Was it, in fact, named “UARM.L”? Did you then change “mmjoint” to “UARM.L” in your attachJoint() call?

If you did these things and it still didn’t work, what is your current situation?

As to the model multiplication, I think you misunderstood my point. You don’t need to create a different model for each possible combination of heads, feet, legs, and arms. Just create one model that has two heads, two sets of feet, two sets of left arms, and four right arms. Then hide all the parts that you aren’t using at any given time.

I’m not saying that you need to do it that way, but I am saying it’s a particularly easy and effective way to solve this particular problem, and it’s much simpler than the multipart actor solution.

I’m still willing to help you use the multipart actor solution. Multipart actors do work, but they’re complex to set up, there are lots of places where it’s easy to do something wrong, and it’s not obvious what you have done wrong. It’s also hard to help diagnose remotely–you have to be able to look inside your model and see what it looks like on the inside. It might also help for you to read the Python code in Actor.py to understand what the Actor calls are supposed to be doing–it will make it easier for you to diagnose what’s going wrong.

David

Dave, I think I found where the problem is using MM.ls() like you said. I missed it yesterday X) Thanks for your help.

I did a MM.exposeJoint() on the left and right bones of the body, and the corresponding bones of the arms. MM.ls() showed four Pandanodes, which is right. So, the method I can’t get to work is MM.attach, which returns a UARM.L, UARM.R not found! Why aren’t the nodes visible to the attach method?

I added the other arm to make sure it wasn’t the model’s problem. Both arms fail to connect properly. This is what I got:

MM=Actor(
    {'MM_BUSTER':'MM_BUSTER.egg',
	'MM_BODY':'MM_BODY.egg',
	'MM_ARM':'MM_ARM.egg'},

    {'MM_BODY':{'rest':'MM_BODY-rest.egg'},
     'MM_BUSTER':{'rest':'MM_BUSTER-rest.egg'},
     'MM_ARM':{'rest':'MM_ARM-rest.egg'}
	 }
	 )


MM.exposeJoint(None, 'MM_BODY','UARM.L')
MM.exposeJoint(None, 'MM_BODY','UARM.R')

MM.exposeJoint(None, 'MM_BUSTER', 'UARM.L')
MM.exposeJoint(None, 'MM_ARM','UARM.R')

MM.attach('MM_BUSTER', 'MM_BODY', 'UARM.L')
MM.attach('MM_ARM','MM_BODY','UARM.R')

MM.actorInterval('rest').loop()

MM.reparentTo(render)

MM.ls()
run()

MM.ls() gives:

PandaNode aBUSTER
  Character __Actor_MM_BUSTER
    GeomNode  (1 geoms: MaterialAttrib TextureAttrib)
  Character __Actor_MM_BODY
    GeomNode  (1 geoms: MaterialAttrib TextureAttrib)
  Character __Actor_MM_ARM
    GeomNode  (2 geoms: MaterialAttrib TextureAttrib)
  PandaNode UARM.L T:m(pos 0 -0.166667 4.08333 hpr -90.0002 23.1435 -156.856 scale 2.42851) E:(CharacterJointEffect)
  PandaNode UARM.R T:m(pos 0 -0.166667 4.08333 hpr 90.0003 23.1435 156.856 scale 2.42851 2.42851 2.4285) E:(CharacterJointEffect)
  PandaNode UARM.L T:m(pos 0 0 1.58333 hpr 0 -90 0) E:(CharacterJointEffect)
  PandaNode UARM.R T:m(pos 0.050804 -0.041667 0.167213 hpr 0 -90 0) E:(CharacterJointEffect)
:util(warning): Adjusting global clock's real time by -0.736932 seconds.
DirectStart: Starting the game.
Warning: DirectNotify: category 'Interval' already exists
:Actorwarning: UARM.L not found!
:Actorwarning: UARM.R not found!

Looking at the code for Actor.attach(), it appears that it is searching for the exposed node underneath the part node. But your exposed nodes are all at the root! This is probably due to the mergeLODBundles feature, an optimization which was added to Actor after the multipart feature. I suspect you will need to turn off this optimization in order to successfully use multipart.

Try passing mergeLODBundles = False to the Actor constructor.

David

I passed it this way with no change, although it is probably necessary anyway.

MM=Actor(
    {'MM_BUSTER':'MM_BUSTER.egg',
	'MM_BODY':'MM_BODY.egg',
	'MM_ARM':'MM_ARM.egg'},

    {'MM_BUSTER':{'rest':'MM_BUSTER-rest.egg'},
	'MM_BODY':{'rest':'MM_BODY-rest.egg'},
	'MM_ARM':{'rest':'MM_ARM-rest.egg'}
	 },mergeLODBundles = False
	 )

The description mentions something about LODBundles at the bottom, haha, so it seems like this is a known issue.

init
def init(self, models=None, anims=None, other=None, copy=1, lodNode=None, flattenable=1, setFinal=0, mergeLODBundles=None)

Multipart actors expect a dictionary of parts and a dictionary of animation dictionaries (partName:(animName:animPath{}){}) as below:
a = Actor(
part dictionary {“head”:“char/dogMM/dogMM_Shorts-head-mod”, “torso”:“char/dogMM/dogMM_Shorts-torso-mod”, “legs”:“char/dogMM/dogMM_Shorts-legs-mod”}, dictionary of anim dictionaries {“head”:{“walk”:“char/dogMM/dogMM_Shorts-head-walk”, “run”:“char/dogMM/dogMM_Shorts-head-run”}, “torso”:{“walk”:“char/dogMM/dogMM_Shorts-torso-walk”, “run”:“char/dogMM/dogMM_Shorts-torso-run”}, “legs”:{“walk”:“char/dogMM/dogMM_Shorts-legs-walk”, “run”:“char/dogMM/dogMM_Shorts-legs-run”} })
In addition multipart actor parts need to be connected together in a meaningful fashion:
a.attach(“head”, “torso”, “joint-head”) a.attach(“torso”, “legs”, “joint-hips”)

ADD LOD COMMENT HERE!

Hmm. Instead of using the run-time exposeJoint() call, try using egg-optchar to expose the joints. I think it does it a little differently, and it may result in the joint node being parented to the partBundle, the way that Actor.attach() expects it to be.

The egg-optchar program is run on the command line. Do something like:

egg-optchar -d opt -expose UARM.L -expose UARM.R MM_*.egg

which will write the modified MM_*.egg files to a directory called “opt”; you can then load them out of this directory. (There are other good reasons to run egg-optchar anyway; in particular, it will be able to remove unused joints, resulting in a performance optimization.)

David

HAHAHAHAH!

Dave, this looks even funnier when animated… Of course its a disaster, but at least its not complaining that it can’t find the nodes anymore! Seriously though, everything is flipped around, and scaled (why is it scaling the arms by 2.42851?), and attached to the wrong node. But this is a solid starting point.

Also MM_*.egg doesn’t work, the names must be entered separately. I’ll write a procedure up nice when I get the model working properly.

This is the mm.ls() output, which is finally right (THANKS!). I think I did the attach() wrong making all the pieces in the wrong place. I’ll write when things look good. Thanks so much for your help!

PandaNode aBUSTER
  Character __Actor_MM_BUSTER
    GeomNode  (1 geoms: MaterialAttrib)
    ModelNode UARM.L T:m(pos 0 0 1.58333 hpr 0 -90 0) E:(CharacterJointEffect)
      Character __Actor_MM_BODY
        GeomNode  (1 geoms: MaterialAttrib)
        ModelNode UARM.L T:m(pos 0 -0.166667 4.08333 hpr -90.0002 23.1435 -156.856 scale 2.42851) E:(CharacterJointEffect)
        ModelNode UARM.R T:m(pos 0 -0.166667 4.08333 hpr 90.0003 23.1435 156.856 scale 2.42851 2.42851 2.42851) E:(CharacterJointEffect)
          Character __Actor_MM_ARM
            GeomNode  (1 geoms: MaterialAttrib)
            ModelNode UARM.R T:m(pos 0.050804 -0.041667 0.167213 hpr 0 -90 0) E:(CharacterJointEffect)
  PandaNode UARM.L T:m(pos 0 -0.166667 4.08333 hpr -90.0002 23.1435 -156.856 scale 2.42851) E:(CharacterJointEffect)
  PandaNode UARM.R T:m(pos 0 -0.166667 4.08333 hpr 90.0003 23.1435 156.856 scale 2.42851 2.42851 2.42851) E:(CharacterJointEffect)
  PandaNode UARM.L T:m(pos 0 0 1.58333 hpr 0 -90 0) E:(CharacterJointEffect)
  PandaNode UARM.R T:m(pos 0.050804 -0.041667 0.167213 hpr 0 -90 0) E:(CharacterJointEffect)

Okay, I found something out. mergeLODBundles = False is bad when the eggs are optimized already. That’s what caused the flippage. Next, I have to figure out why the arms are scaled and then attached to the wrong parts of the bones. This might be something that can be fixed with blender.

panda3d replaces the exposed bone node with the corresponding node of the actor to be attached. The bone isn’t added on to the end. Also, in blender, before exporting ALL scales, rotations, displacements need to be applied. Otherwise the attached bone will inherit the scale of the root or main model. This means that the attached models will blow up or shrink down, depending on the relative scale.