Thank you very much David! This solved a particularly puzzling problem I was having with my procedural character animation.
I’m posting to this (very) old thread to add one bit of information that might be useful to others. In some applications, it may be important to initialize the node created by controlJoint() to the initial transform specified for the joint in the model file, even in cases where one needs to be able to access its global transform.
For example, my application of this is for a custom physics-based procedural simulation of a character’s hair. The joints belonging to hair chains in the character model are controlled procedurally, while all other joints are controlled by prerecorded animations. The intent is that the hair will respond in a physically reasonable manner, regardless of the combination of the animation playing (that may include motion for the head) and the state of motion (rotation + translational acceleration) of the character.
To keep things simple, the simulation code knows nothing about orientation, computing only cartesian positions of nodes (in the coordinate system of the character’s head) connected to each other by rigid rods, interfaced by linear “ball-joint” bending springs. This information is copied into the controlled joints at each frame, by walking the hair chains and using lookAt(). The local roll of each joint is kept locked to its original value from the model file, since in the absence of torsion the hair chains should not twist.
For initialization, the simulation needs the original positions of the joints in the hair chains with respect to the character’s head. Panda can do the coordinate transformations easily, provided that one can get the global transform of a controlled joint.
Thus, we modify the approach slightly:
grandparent = actor.exposeJoint(None, "modelRoot", "joint_grandparent")
parent = actor.controlJoint(None, "modelRoot", "joint_parent")
parent.reparentTo(grandparent)
child = actor.controlJoint(None, "modelRoot", "joint_child")
child.reparentTo(parent)
We let controlJoint() create the new node and apply its default initialization in the usual way (copying the initial transformation from the joint in the model file), instead of creating the new node ourselves. Then we reparent the created node as suggested in this thread, in order to be able to read its global transform. At least in Panda3D 1.8.1, this does what is intended.
This does have the side effect that one cannot have an exposed joint further down the controlled joint chain. If that is attempted, the net transform read from the downstream exposed joint will be incorrect. (My interpretation is that this is because Panda expects the NodePaths related to controlled joints are not parented to anything.)
As for why one would need to do that - for example, in my particular application, each hair chain has a terminator joint whose only purpose is to indicate the initial position of the endpoint of the last hair segment in the chain. The terminator affects no vertices in the model, is never animated, and there is no need to really control it procedurally. However, its initial position must be read to initialize the simulation correctly.
This particular problem was solved by using controlJoint() also on the terminator joint, and calling releaseJoint() on it after the initialization is done.