Joint rotations

I know this is a popular topic, and I’ve searched extensively but even with all of that information, I still cannot get my joint control work as intended.

My goal is to control one of the joints in world coordinates, so I can set it to the same orientation as the world. Now, I know that joints are stored as local transforms and that I can make Panda work for me by creating a nodepath hierarchy based on those joints. That way, if I set the rotation based on the world, it should do the appropriate transforms for me.

Except I can’t quite get it to work.

I have a basic animated model, and its joint hierarchy looks like this:

player
   <skeleton> 
     Base  hpr 1.66133 89.8603 178.339 trans -0.000564083 -0.175523 1.39111
       Spine  hpr 0 7.651 0 trans 0 0.619687 -0.14133
         UpperArm.L  hpr -178.126 -23.3147 -177.6 trans 0.383515 0.355083 0.0989534
           LowerArm.L  hpr -1.99828 -48.0996 -2.74372 trans 0 0.510121 0
             Hand.L  hpr 1.11921 -2.31335 -1.16677 trans 0 0.492569 0
         UpperArm.R  hpr 178.246 13.4539 178.735 trans -0.383515 0.355083 0.0989534
           LowerArm.R  hpr 0.960175 -35.608 1.75383 trans 0 0.510121 0
             Hand.R  hpr -7.90664 -8.76821 -12.6235 trans 0 0.492569 0
       UpperLeg.L  hpr -179.838 30.4395 -179.63 trans 0.115283 0.132332 -0.00205376
         LowerLeg.L  hpr 2.20218 30.4601 -1.73607 trans 0 0.73409 0
       UpperLeg.R  hpr -179.97 3.52013 179.011 trans -0.115283 0.132332 -0.00205375
         LowerLeg.R  hpr -2.83956 42.9173 1.88821 trans 0 0.73409 0
     Leg.L  hpr -5.077e-006 -69.7964 -180 trans -0.0957884 0.186403 0.0922465
       Toe.L  hpr 1.54104e-005 -88.6037 180 trans 0 0 -0.213355
     Leg.R  hpr -180 -54.9409 -1.63765e-006 trans 0.0946603 -0.635872 0.190287
       Toe.R  hpr 180 -56.0208 1.66145e-006 trans 0 0 -0.213355

So I am trying to control the Spine joint, in order that I can set its Hpr in global coordinates and have it automatically work back up the chain for me. From what I have found in the forums and what I understand, I should be doing something like this:

# expose the Base joint
self.jointBase = self.playerActor.exposeJoint (None, 'modelRoot', 'Base')        

# create new nodepath for the spine joint (that we will control)
self.jointSpine = self.jointBase.attachNewNode ('Spine')

# expose the spine so we can get its transform
Spine = self.playerActor.exposeJoint (None, 'modelRoot', 'Spine')

# copy the joint transform over to the nodepath
self.jointSpine.setTransform (Spine.getTransform(self.jointBase))        

# associate this nodepath with this joint
self.playerActor.controlJoint (self.jointSpine, 'modelRoot', 'Spine')

However, while working with self.playerSpine, it does not appear to be resolving correctly when set to world orientation.

I fear I am missing something really elementary here, so I wanted to ask and see if anyone knows what I am missing. :slight_smile:

i havent played aroudn with joint manipulation so far. but shouldnt the setHpr call be just fine.i mean you usualy can specify a nodepath as first argument to make the transform relative to the node’s coordinate system.

something like self.jointSpine.setHpr( render, 321 , 123, 413 )
… it’s just a guess of mine, havent tried it on joints. works fine for common nodepaths thought.

You can replace these steps:

# expose the spine so we can get its transform
Spine = self.playerActor.exposeJoint (None, 'modelRoot', 'Spine')

# copy the joint transform over to the nodepath
self.jointSpine.setTransform (Spine.getTransform(self.jointBase))       

# associate this nodepath with this joint
self.playerActor.controlJoint (self.jointSpine, 'modelRoot', 'Spine') 

with this:

# associate the nodepath with the joint
self.playerActor.controlJoint (self.jointSpine, 'modelRoot', 'Spine')

since the controlJoint() call will simultaneously associate the joint and also load its current transform into the NodePath. This is probably more reliable too (it’s possible that exposeJoint won’t immediately load its joint’s transform into the target node).

Other than that, I don’t see anything wrong. What kind of results do you get?

David

Weirdness!

Maybe a picture can explain better what I am seeing. :slight_smile:

It seems that if I just do the controlJoint without first copying the xform from the Spine over, the joint is loaded with a null transform. (Making him a very short biped! Rotation still looks fine, as the joint is Hpr 0,0,0 local.)

So I have to first expose the Spine joint, as in my original code example. I am using Panda 1.6.2, BTW.

The spine joint’s local rotation is 0,0,0, and the base joint is 0,90,-180. I would expect that if I did jointSpine.setHpr (render, 0, 0, 0), then what would actually be set on the bone is 0, 0, 0 based on the transform bubbling up the hierarchy. But instead it’s set to 0,90,0… It’s acting like that it cuts it off at the base joint, which is incorrect. It should also take into consideration the base joint’s rotation (0, 90, -180) when doing the transform-to-world coordinate change. Does the base joint need to be parented to the actor somehow in order to get it to do this?

In case my setup code is causing problems, here is what is called immediately before the joint code:

        # build player
        self.playerNP = base.render.attachNewNode (ActorNode ('Player'))
        self.playerNP.setPos (0, 0, 100)
        self.playerNP.setH (0)
        
        # load actor and attach to player
        self.playerActor = Actor()
        self.playerActor.loadModel ('mechman')
        self.playerActor.loadAnims ({'idle' : 'mechman-idle'})
        self.playerActor.loadAnims ({'walk' : 'mechman-walk'})
        self.playerActor.reparentTo (self.playerNP)
        self.playerActor.setBlend(frameBlend = True)

        # blend default anims
        self.playerActor.enableBlend()
        self.playerActor.setControlEffect ('idle', 0.2)
        self.playerActor.setControlEffect ('walk', 0.8)
        self.playerActor.loop ('idle')

I appreciate you taking a look at this. :slight_smile:

PS: Image is wrong, that should be spineJoint.setHpr (render, 0, 0, 0).

Still no dice. I tried this:

        
        #  setup base joint
        self.jointBase = self.playerActor.attachNewNode ('newBase')
        Base = self.playerActor.exposeJoint (None, 'modelRoot', 'Base')
        self.jointBase.setTransform (Base.getTransform(self.playerActor))
        Base.remove()        
        self.playerActor.controlJoint (self.jointBase, 'modelRoot', 'Base') 
        
        # setup spine joint
        self.jointSpine = self.jointBase.attachNewNode ('newSpine')
        Spine = self.playerActor.exposeJoint (None, 'modelRoot', 'Spine')
        self.jointSpine.setTransform (Spine.getTransform(self.jointBase))        
        Spine.remove()
        self.playerActor.controlJoint (self.jointSpine, 'modelRoot', 'Spine')

And the rotation is still being set incorrectly for the Spine joint.

It’s even worse than I thought, as my original example included setHpr (render, 0, 0, 0), but that isn’t right. I was actually just setting the heading and pitch to zero. The rotation of the joint ends up being 0, 90, 180.

# before
ActorNode Player T:(pos 0 0 100)
  PandaNode mech
    Character __Actor_modelRoot
      GeomNode  (1 geoms: S:(TextureAttrib))
  PandaNode newBase T:q(pos -0.000564083 -0.175523 1.39111 hpr 1.66129 89.8603 178.339)
    PandaNode newSpine T:q(pos 0 0.61969 -0.141333 hpr 0 7.651 1.30822e-005)
    
# after jointSpine.setHpr (render, 0, 0, 0)
ActorNode Player T:(pos 0 0 100)
  PandaNode mech
    Character __Actor_modelRoot
      GeomNode  (1 geoms: S:(TextureAttrib))
  PandaNode newBase T:q(pos -0.000564083 -0.175523 1.39111 hpr 1.66129 89.8603 178.339)
    PandaNode newSpine T:(pos 0 0.61969 -0.141333 hpr 0.0141252 89.8604 179.99)

I mean I guess I could just zero out the bones in my modeling program and re-export, but that won’t save me when I start to animate the hands and legs via controlJoints. I’m going to have to deal with some joints being at different respective orientations to each other.

I know I can just do the corrections manually, but I’d rather have Panda do it for and from what I gather, by setting up a joint hierarchy in Panda it should be possible…

Hmm. Can you post your model so I can play with it too?

David