Rotation for First Person Space Shooter

Hi @ll

I’ve got a problem related to the rotation functions.
Is there a way to pass a vector to panda’s rotation function so that I can rotate a model/object around that vector?

I’m trying to create a space shooter with a “Wing Commander” like movement.
Maybe someone knows another way of calculating the correct rotations than passing a vector(if that’s even possible) to the rotation function in order to get the proper results?

You can use Quaternions instead of Euler angles.


nodePath.setMat(Mat4.rotateMat(angle, axis))

where angle is a floating-point number in degrees, and axis is a Vec3. Note that this will also wipe out any translation or scale you have on NodePath, so you might want to apply those transforms to a parent node instead.


Thanks for the quick help! I’ll try that and see if it results in the expected behaviour… :wink:

I don’t know anything about Quaternions… yet
From what I’ve read in the past 10 minutes, that could be a solution, but I’ll stick with ordinary vector math, if it works…
Still, thank you, too.

nodePath.setMat(Mat4.rotateMat(angle, axis))

I’ve tryed that, but I get an error:

So I guess I’ll have to compose a Matrix called Mat4(or whatever name I put in there) first? If so, I don’t know how to use the composeMatrix function…

Just make sure you have imported:

from pandac.PandaModules import *


Thanks again…

It kind of works now.
But as you may guess there are still problems…
The command seems to let me rotate the model around my specified vector, but only as long as that vector is the same as one of the axles used for Hpr anyway.
I’m working on that rotation stuff for a week now and I’m starting to feel a little bit stupid.
Maybe I’m doing something wrong when I’m calculating the vectors that the model should rotate around (although the same method worked for calculating the vector for heading, which lets me move the model in the direction it’s facing.)

        self.x = self.boxd.getPos(self.basis)
        self.y = self.vectorRight.getPos(self.basis)
        self.axis = self.x-self.y
        self.boxd.setMat(Mat4.rotateMat(self.angle, self.axis))

explaination: self.vectorRight is a dummy model parented to the model I want to rotate (self.boxd). Its position is (0,20,0) (so pointing to the right).
rotating the parent means translating the child in order to preserve its position relative to the parent.

self.basis is another dummy model @Pos(0,0,0).
So it’s simple vector math and the result is a vector pointing in the positive X direction of the models axis.
So the setMat function should rotate the object around the real X axis of my object.

I’m confident that Mat4.rotateMat() works correctly. In fact, here is a stupid demo program that demonstrates it. Note that no matter which way you rotate the axis, the teapot always rotates about the green Y axis (which is aligned with the numeric axis value displayed onscreen).

So if you are not seeing this behavior, maybe your input to rotateMat() is wrong. You might want to print out the value of self.axis to confirm that it is what you think it is.


from direct.directbase.DirectStart import *
from direct.task.Task import Task
from direct.gui.DirectGui import *
from pandac.PandaModules import *

teapotModel = loader.loadModel('teapot.egg')

axisModel = loader.loadModel('zup-axis.egg')

base.disableMouse(), -30, 0)

dlight ='dlight'))

label = DirectLabel(text = '',
                    relief = None,
                    pos = (base.a2dLeft + 0.05, 0, 0.9),
                    text_align = TextNode.ALeft,
                    scale = 0.07)

keys = DirectLabel(text = 'Hold "x", "y", or "z" and move mouse to adjust the axis\nHold "a" and move mouse to adjust the angle',
                   relief = None,
                   pos = (base.a2dLeft + 0.05, 0, 0.7),
                   text_align = TextNode.ALeft,
                   scale = 0.07)

axis = Vec3(1, 0, 0)
angle = 0

xButton = KeyboardButton.asciiKey('x')
yButton = KeyboardButton.asciiKey('y')
zButton = KeyboardButton.asciiKey('z')
aButton = KeyboardButton.asciiKey('a')

def pointAxis(task):
    axisModel.lookAt(Point3(0, 0, 0) + axis)
    return Task.cont

def rotateTeapot(task):
    teapotModel.setMat(Mat4.rotateMat(angle, axis))
    return Task.cont

def updateLabel(task):
    label['text'] = "axis: (%s, %s, %s)\nangle: %s" % (axis[0], axis[1], axis[2], angle)
    return Task.cont

lastMouse = None

def watchMouse(task):
    global lastMouse
    if base.mouseWatcherNode.hasMouse():
        thisMouse = base.mouseWatcherNode.getMouseX()
        thisMouse = None

    if thisMouse != None and lastMouse != None:
        delta = thisMouse - lastMouse

        if base.mouseWatcherNode.isButtonDown(xButton):
        if base.mouseWatcherNode.isButtonDown(yButton):
        if base.mouseWatcherNode.isButtonDown(zButton):

        if base.mouseWatcherNode.isButtonDown(aButton):
            global angle
            angle += delta * 360

    lastMouse = thisMouse
    return Task.cont

taskMgr.add(pointAxis, 'pointAxis')
taskMgr.add(rotateTeapot, 'rotateTeapot')
taskMgr.add(updateLabel, 'updateLabel')
taskMgr.add(watchMouse, 'watchMouse')


Thanks for your quick reply again, and for the effort of creating this code…
I really appreciate your help, that actually brought me further.

Imagine a ship where forward is the forward direction of the y axis (In the game it won’t be a model, but rather the camera itself that will be moved/rotated.)
Further imagine you’re sitting in this ship and fly it.
Let’s imagine the ship first turns 90 degrees aroung its X axis(which in the initial state represents the worlds X axis, now referred to as X’[Y’,Z’])
Then your ship should be pointing with it’s nose down (=ships Y axis points down on the Z’ axis(-Y=Z’), Z axis on Y’ axis, and X=X’).
If you now rotate 90 degrees around the ships Z axis, the result should be:
Z=Y’, -X=Z’, -Y=X’ (the nose should be pointing in the oppsite direction of its X axis, when it was initialised(when X=X’ was true).

BUT the actual result is:
X=Y’, -Y=Z’, -Z=X’

Of course that is for the usual setHpr function.
The rotateMat function works differently, and like your example shows, this function can correctly rotate around one single vector.
But the thing is, I’ll actually have to rotate around three different vectors, in order to achieve the proper behaviour. But as you already told me, the setMat function overrides any changes made to the Object Matrix so far.
So I can’t really rotate around all three object axles.
But it’s my fault, I should have been more precise about what I need

Anyway, your help so far gave me some hints where to look in the API manual for possible candidate functions for what I intend to do(although they’re undocumented and I don’t how to use them yet).
If I don’t find a way to let the Engine do the work I’ll have to do the whole vector math and matrix calculations on my own…
Which is probably a bad idea, considering I’ll have to create a Matrix and do the calculations for each individual object (which may be slow) and for each player in Multiplayer (which may be even slower). :frowning:

If I finally get my problem solved, I’ll post the source for a demo program where you can freely move in 3D space and rotate your view perspectively correct, so no one shall ever have the same problem as me again.

Ah, I see. You want to accumulate rotations on a node. That is, when your node already has a rotation (h, p, r), you want to apply a new rotation (h’, p’, r’), and end up with some net rotation (h’’, p’’, r’’).

This is quite simple. The easiest way is to rotate the node with respect to itself:

node.setHpr(node, h', p', r')


Damn it, it’s true, the easiest solution is the best!

Really, David, I really really really really thank you a lot!
Thanks a million!

It works now, and as promised, I post the code…

(See below)

import direct.directbase.DirectStart
from direct.showbase import DirectObject
import math, sys

class main(DirectObject.DirectObject):
    def __init__(self):
        #set key bindings
        self.accept('mouse1', self.forw)
        self.accept('escape', sys.exit)
        self.accept('w-repeat', self.up)
        self.accept('w', self.up)
        self.accept('a-repeat', self.left)
        self.accept('a', self.left)
        self.accept('s-repeat', self.down)
        self.accept('s', self.down)
        self.accept('d-repeat', self.right)
        self.accept('d', self.right)
        self.accept('q-repeat', self.rleft)
        self.accept('q', self.rleft)
        self.accept('e-repeat', self.rright)
        self.accept('e', self.rright)
        self.accept('y', self.forw)
        self.accept('y-repeat', self.forw)
        #Loading Model as 0 Point for Vector calculation
        self.basis = loader.loadModel("models/box")
        #Load a Model as a reference point in Front of the camera
        #needed for vector calculation
        self.null = loader.loadModel('models/box')
        #Load a model to construct the environement
        self.beta = loader.loadModel('models/box')
        #creating the environment through instancing and positioning 
        #the same model in circles around the world axles
        x = 0
        for i in range(100):
            self.placeholder = render.attachNewNode("Placeholder")
            self.placeholder.setPos(100*math.sin(anglerad), 100.0*math.cos(anglerad),0)
            x = x +200
        x = 0
        for i in range(100):
            self.placeholder2 = render.attachNewNode("Placeholder2")
            self.placeholder2.setPos(100*math.sin(anglerad),0, 100.0*math.cos(anglerad))
            x = x +200
        x = 0
        for i in range(100):
            self.placeholder3 = render.attachNewNode("Placeholder2")
            self.placeholder3.setPos(0,100*math.sin(anglerad), 100.0*math.cos(anglerad))
            x = x +200
        #Disable Mouse control over camera
    #Setting up the new HPR relative to the current HPR
    #the commented print statements are for debugging
    def rleft(self):, -3)
        #print '(R)roll r/L: ',
    def rright(self):, +3)
        #print '(R)roll R/l: ',
    def left(self):, +3)
        #print '(H)rot L/r: ',
    def down(self):, -3)
        #print '(P)rot u/D: ',
    def up(self):, +3)
        #print '(P)rot U/d: ',
    def right(self):, -3 )
        #print '(H)rot l/R: ',
    #calculating forward movement
    def forw(self):
        #getting two position vectors from basis to camera and 
        #from basis to the point in front of the camera
        x =
        y = self.null.getPos(self.basis)
        #calculating the pointing vector
        g = x-y
        #aplly translation