Issue with controljoint rotating a bone from a wrong axis with a .glb model

So I was having a lot of fun starting a videogame project and learning panda3d. I loaded a free rigged tank model from a .glb file I found in sketchfab using the Actor class. It looked gorgeous. So I tried animating it.

I started with the turret and rotated it with setHpr until I found the correct axis. It was so easy so I tried spinning the wheels within their axis. Which is something I did first within blender in pose mode and had no issue. But no matter how I tried the wheels allways rotated from the root skeleton bone as seen in the next picture:

This code fragment initializes my model, the next fragment is where the issue occurs

class T72Target:
    def __init__(self, render, pos, model_base, speed_multiplier=1.0):
        self.width = 7.5   
        self.length = 4.0  
        self.height = 2.4  

        self.node = render.attachNewNode("T72_Target")

        self.model = Actor(model_base)
        self.model.reparentTo(self.node)


        self.model.setHpr(0, 0, 0) 
        self.model.setZ(0) 
        self.model.setScale(.0095)
        
        self.turret_bone = self.model.controlJoint(None, "modelRoot", "C_Turret")
        self.barrel_bone = self.model.controlJoint(None, "modelRoot", "C_Gun")

      
        self.c_node = CollisionNode('target_collision')
        c_box = CollisionBox(Vec3(0, 0, self.height / 2), self.width / 2, self.length / 2, self.height / 2)
        self.c_node.addSolid(c_box)
        
        self.c_node.setIntoCollideMask(BitMask32.bit(1))
        self.c_np = self.node.attachNewNode(self.c_node)
        self.c_np.setPythonTag("target_object", self)
        
        self.node.setPos(pos)
        self.speed = random.choice([-15.0, -1.0, 1.0, 15.0]) * speed_multiplier
        self.is_destroyed = False

        #Here is where I seach the wheel bones, no issue yet   
        self.LT_Wheels = [self.model.controlJoint(None, "modelRoot", "L_Wheel_0{n}".format(n=i)) for i in range(1,8)]
        
        self.RT_Wheels = [self.model.controlJoint(None, "modelRoot", "R_Wheel_0{n}".format(n=i)) for i in range(1,8)]

But when I try to rotate each individual wheel, no matter how many. They rotate along the root:

#These are the methods where the issue happened
   def rotate_left_track(self, speed, dt):
          
          instant_speed = speed  * dt
          #Here
          for wheel in self.LT_Wheels:
              wheel.setH(wheel.getH() + instant_speed)
  
  def rotate_rigth_track(self, speed, dt):
          
          instant_speed = speed  * dt

          #and here, no matter the axis, H P or R

          for wheel in self.RT_Wheels:
              wheel.setH(wheel.getH() + instant_speed)

#here are the rest

    def rotate_turret(self, speed, dt):
        r = self.turret_bone.get_r()
        self.turret_bone.set_r(r+speed*dt)

    def update(self, dt):
        if self.is_destroyed:
            return
        current_pos = self.node.getPos()
        current_pos.x += self.speed * dt
        if abs(current_pos.x) > 400:
            self.speed *= -1
        self.node.setPos(current_pos)
        
        self.rotate_turret( 15, dt)
        
        #This is where the issue happens
        self.rotate_left_track(self.speed , dt)
        self.rotate_rigth_track(self.speed , dt)

    def destroy(self):
        self.is_destroyed = True
        self.node.removeNode()

I don’t want to risk breaking the share your own stuff guideline so I won’t include the link to the model from sketchfab (Unless it’s ok ).

Here is the bone hierarchy:

CharacterJointBundle T72B_Model {
  PartGroup <skeleton> {
    LMatrix4f Skeleton {
      LMatrix4f Root {
        LMatrix4f C_Cog {
          LMatrix4f C_Driver_Hatch
          LMatrix4f C_Turret {
            LMatrix4f C_Commander_Hatch
            LMatrix4f C_Gun {
              LMatrix4f C_Gun_01
            }
            LMatrix4f C_Hatch
          }
          LMatrix4f L_Wheel_01
          LMatrix4f L_Wheel_09
          LMatrix4f R_Wheel_01
          LMatrix4f R_Wheel_09
        }
        LMatrix4f L_Wheel_02_Track {
          LMatrix4f L_Wheel_02
        }
        LMatrix4f L_Wheel_03_Track {
          LMatrix4f L_Wheel_03
        }
        LMatrix4f L_Wheel_04_Track {
          LMatrix4f L_Wheel_04
        }
        LMatrix4f L_Wheel_05_Track {
          LMatrix4f L_Wheel_05
        }
        LMatrix4f L_Wheel_06_Track {
          LMatrix4f L_Wheel_06
        }
        LMatrix4f L_Wheel_07_Track {
          LMatrix4f L_Wheel_07
        }
        LMatrix4f R_Wheel_02_Track {
          LMatrix4f R_Wheel_02
        }
        LMatrix4f R_Wheel_03_Track {
          LMatrix4f R_Wheel_03
        }
        LMatrix4f R_Wheel_04_Track {
          LMatrix4f R_Wheel_04
        }
        LMatrix4f R_Wheel_05_Track {
          LMatrix4f R_Wheel_05
        }
        LMatrix4f R_Wheel_06_Track {
          LMatrix4f R_Wheel_06
        }
        LMatrix4f R_Wheel_07_Track {
          LMatrix4f R_Wheel_07
        }
      }
    }
  }
  PartGroup morph {
  }
}

Hmm… Bone-control is something that I’ll admit that I’ve had trouble with, so I’m a little hesitant to offer a suggestion.

However, have you tried rotating your wheel-joints relative to themselves?

That might get them rotating around their own axes, in keeping with what I imagine you want to have happen. (Rather than rotating relative to their parents, as is the default.)

Something like this:
wheel.setH(wheel, instant_speed)

Note that there are now two parameters to the call to “setH”: the second is the usual parameter, the angle to apply, while the first is the new parameter, being the object relative to which the rotation should be performed. In this case, the call tells the object to rotate relative to itself.

(Note also that I’ve removed the call to “wheel.getH()”. This is because, when an object is transformed (e.g. rotated) relative to itself, its current transformation is inherently included. After all, moving a given distance relative to yourself means moving that distance from your current position.)

Thanks a lot for your response. Yes, I’ve tried that but the result is the same, for some reason it looks as if the transform matrix were applied in the wrong order. First the traslation and then the rotation.

Hmm… I doubt that it’s an order-of-operations issue.

Given what you’ve shown, and short of seeing the model for myself, I’d guess then that the issue lies with the location of the wheel-joint’s origin.

That is, the joint that you’re controlling may have an origin-point that isn’t located at its centre–perhaps being located at the origin of the overall model.

It might be worth testing this by printing out both the position of your model and the position of one of the joints, and seeing whether they’re perhaps the same (or close together)…

[edit]
Or, unlikely but possible, that the wheel-joint doesn’t actually align with the axis of the visible wheel, but instead runs from some point within the model to the location of the wheel.

While I haven’t tested it, a quick forum-search turned up the following old thread that provides code for visualising a joint-hierarchy. Perhaps it might be worth running it on your model, and seeing what the joints actually look like.

That script was useful, at first it looked like it didn’t work, but I noticed a rainbow point exactly where the root is suposed to be:


So it is very weird that all the origin points are set together at the root. In blender the skeleton looks like this:

No idea why this happens, nor how to solve it… yet

Hmm, interesting.

Looking at your screenshot from Blender, I note with interest that the bones appear to be all disconnected from their parents. (Note that they’re linked one bone to another not by intermediate bones, but by dotted lines.)

This does appear to confuse that script to which I linked a little. Although conversely, in a quick test it did appear to put the bones in more or less the right places. But on the other hand again, that was a very simple test.

Still, I ran a quick test with a test-model that has a similar setup, and rotation seemed to work there as expected.

Maybe it’s an issue with GLB loading? Have you tried importing the model into Blender and then exporting it as a Panda “EGG” file?