Problem of rotation of a 3d object

Hello,
As part of a class project, my group and I would like to rotate a planet on a fixed point with keyboard keys.
We know how to code in python and we have looked at the 3d panda tutorials but we are stuck on the rotation.
The planet rotates on itself (Z axis) and we can move it but it doesn’t rotate with respect to X and Y.
Can you give us a clue, a path to explore or a solution that could help us?

from math import *

from panda3d.core import loadPrcFile, loadPrcFileData, AmbientLight, VBase4, Vec4, Vec3, DirectionalLight

loadPrcFile(“config/conf.prc”)

from direct.showbase.ShowBase import ShowBase

“”"

obj2egg pythonPrograms/ModelsProps/Arbre1.obj pythonPrograms/ModelsProps/Arbre1.egg

“”"

loadPrcFileData("", “load-file-type p3assimp”) #Pour pouvoir importer correctement des fichiers .obj

keyMap = {

    "up" : False,

    "down" : False,

    "left" : False,

    "right" : False,

    "rotate" : False}

#callback fonction to update the keyMap

def updateKeyMap(key, state):

keyMap[key] = state

#Pour démarrer l’écran d’affichage

class Game(ShowBase):

def __init__(self):

    super().__init__()



    self.cam.setPos(0,-20,0)



    #self.disableMouse() # Disables the default camera control



    self.planete = self.loader.load_model("ModelsProps/Main2.glb", )

    self.planete.setPos(0, 0, -6)

    self.planete.setHpr(0,0,0)

    self.planete.setScale(0.70)

    self.planete.reparent_to(self.render) # Connexion à la racine render du graph



    dt = globalClock.getDt()

    print(dt)

    ambientLight = AmbientLight("ambient light")

    ambientLight.setColor(Vec4(0.2, 0.2, 0.2, 1))

    self.ambientLightNodePath = render.attachNewNode(ambientLight)

    render.setLight(self.ambientLightNodePath)

    

    mainLight = DirectionalLight("main light")

    mainLight.setColor(Vec4(1, 233/255, 145/255, 1))

    self.mainLightNodePath = render.attachNewNode(mainLight)

    self.mainLightNodePath.setHpr(45, -45, 0)

    render.setLight(self.mainLightNodePath)

    

    SideLight = DirectionalLight("Side light 1")

    SideLight.setColor(Vec4(117/255, 7/255, 74/255, 1))

    self.SideLightNodePath = render.attachNewNode(SideLight)

    self.SideLightNodePath.setPos(-30, -30, -30)

    self.SideLightNodePath.setHpr(225, 45, 0)

    render.setLight(self.SideLightNodePath)

    self.speed = 4

    self.angle = 0



    #self.taskMgr.add(self.tourne, "tourne")

    self.taskMgr.add(self.update, "update")

      

    self.accept("z", updateKeyMap, ["up", True])

    self.accept("z-up", updateKeyMap, ["up", False])

    self.accept("s", updateKeyMap, ["down", True])

    self.accept("s-up", updateKeyMap, ["down", False])

    self.accept("q", updateKeyMap, ["left", True])

    self.accept("q-up", updateKeyMap, ["left", False])

    self.accept("d", updateKeyMap, ["right", True])

    self.accept("d-up", updateKeyMap, ["right", False])

    self.accept("space", updateKeyMap, ["rotate", True])

    self.accept("space-up", updateKeyMap, ["rotate", False])



def update(self, task):

    dt = globalClock.getDt()

    pos = self.planete.getPos()

    if keyMap["left"]:

        pos.x -= self.speed * dt

    if keyMap["right"]:

        pos.x += self.speed * dt

    if keyMap["up"]:

        pos.z += self.speed * dt

    if keyMap["down"]:

        pos.z -= self.speed * dt

    if keyMap["rotate"]:

        self.angle += 1

        self.planete.setH(self.angle)

    self.planete.setPos(pos)

    return task.cont

game = Game()

“”"

for i in range (-360,361):

print("rang", i, ":", game.sinDroit(i))

“”"

game.run()

this can be done via some trigonometry, I do believe, but I would suggest instead making use of node-parenting.

You see, when one node (let’s call it “A”) is a child of another node (let’s call it “B”), then it moved (and scaled, rotated, etc.) as that other node is moved (and scaled, rotated, etc.). Or, more accurately, it simply retains its position (and scale, rotation, etc.) relative to that other node.

That is, if node B rotates, its local coordinates (what you might call its “perspective”) rotates with it. If node A starts off located “directly in front of” node B, it remains “directly in front of” node B–even as B rotates and the direction that corresponds to “directly in front of it” changes accordingly.

You can think of this as being something like a human arm, with “node B” being the upper arm and “node A” being the lower arm, attached at the elbow: as the upper arm is lifted (i.e. rotated), the lower arm is lifts by it–and in doing so moves along a curve around the origin of the upper arm.

Does that help?

Hello,
Thanks a lot for your answer ! We are still beginners and your tips are very helpful. Indeed I tried earlier with trigonometry but it turned out really complicated and we couldn’t think it throught. I tried to create 2 Nodes, « rotationX » is the parent of « rotationY » and Y is the parent of « planete ». When rotating on P through rotationX it works perfectly, but it alters the direction of rotationY, messing up the axis R as well. Do you have a solution for this problem ? Here is ( part of ) our code ( in a class ) so far :

self.rotationX = render.attachNewNode(“X”)
self.rotationY = render.attachNewNode(“Y”)
self.rotationY.reparent_to(self.rotationX)

self.planete = self.loader.load_model(“ModelsProps/Main2.glb”, )
self.planete.reparent_to(self.rotationY)

self.keyMap = {
“up” : False,
“down” : False,
“left” : False,
“right” : False}

self.accept(“z”, self.updateKeyMap, [“up”, True])
self.accept(“z-up”, self.updateKeyMap, [“up”, False])
self.accept(“s”, self.updateKeyMap, [“down”, True])
self.accept(“s-up”, self.updateKeyMap, [“down”, False])
self.accept(“q”, self.updateKeyMap, [“left”, True])
self.accept(“q-up”, self.updateKeyMap, [“left”, False])
self.accept(“d”, self.updateKeyMap, [“right”, True])
self.accept(“d-up”, self.updateKeyMap, [“right”, False])

self.taskMgr.add(self.update, “update”)

def update(self, task):

    dt = globalClock.getDt()
    speed = 20 

    if self.keyMap["up"]:
        self.rotationX.setHpr(self.rotationX.getHpr() + Vec3( 0, speed*dt, 0))
        
    if self.keyMap["down"]:
        self.rotationX.setHpr(self.rotationX.getHpr() + Vec3( 0, -speed*dt, 0))

    if self.keyMap["left"]:
        self.rotationY.setHpr(self.rotationY.getHpr() + Vec3( 0, 0, speed*dt))

    if self.keyMap["right"]:
        self.rotationY.setHpr(self.rotationY.getHpr() + Vec3( 0, 0, -speed*dt))

    return task.cont

Hmm… Is the “R” rotation supposed to work much like the “P” rotation? That is, is your goal to allow the user to rotate the planet freely by pressing the keys?

If so, then things might become a little more complicated. I think that the following should work:

In addition to rotation relative to the node’s parent, as I’ve described above, Panda has a feature that allows one to rotate an object relative to some other, arbitrary node (with Panda doing the maths for this in the background, presumably).

In this case, what might then work is to rotate the “rotationY” NodePath relative not to its parent (i.e. rotationX), but to the parent of its parent (i.e. render). This should essentially “bypass” the effect of its parent.

As to how this is coded, one simply passes into the “setHpr” call an optional first parameter, that being the NodePath relative to which you want to the rotation to take place. Like so:

myNodePath.setHpr(someOtherNodePath, h, p, r)

Now, you’re adding to the node’s existing HPR-values, and these existing values are naturally given by default relative to the parent of the node in question–which is not what we want in this case, since we’re operating relative to another node.

However, Panda also allows us to get values relative to some other node in the same way as we set them: by passing in an optional first paremeter. Like so:

myHpr = myNodePath.getHpr(someOtherNode)

So, in your case you would end up with something like this:

self.rotationY.setHpr(render, self.rotationY.getHpr(render) + Vec3(0, 0, speed * dt))

i.e. “Set rotationY’s new rotation relative to render to be equal to rotationY’s current rotation relative to render plus some value.”

Note that this can also be applied to transformations other than rotation–so, for two examples, you can get a relative position with something like “myNodePath.getPos(someOtherNodePath)”, or set a relative scale with something like “myNodePath.setScale(someOtherNodePath, newScaleValue)”.

By the way, you needn’t set all of “H”, “P”, and “R” (or get all of “H”, “P” and “R”) when you’re only working with one of them: Panda has methods for working with individual components. (And again, this applies to other transformations too, as I recall.)

So, for example, you can set just “R” by calling “myNodePath.setR(newRValue)”, where “newRValue” is a single float or int, or get just “H” by calling “myNodePath.getH()”.

And as before, these can be made relative to some other NodePath by passing that NodePath into the method as an optional first parameter.

So, in your case you might have something like this:

self.rotationY.setR(render, self.rotationY.getR(render) + speed*dt)