does directbase's run mess up the cam's heading?


I came closer to the problem why I could see only background: the camera’s H-value was messed up when starting as browser applet, but it was fine when starting from win7 command-line.

I have made

from direct.directbase.DirectStart import *

Then during execution I have output the current cam’s heading:

text.setText("currH: %.3f" %

where text is a simple aspect2d node on screen for output.

Starting from console, the heading is 30. But starting as browser plugin, the heading is -33776020680764318000000000.0! Where does that come from?

This could be fixed by resetting the heading INSIDE the mouse-handler task:

      #if abs( > 1000):

So it looks like the run() messes up my heading, although base.disableMouse().


Probably better to put initialization like this within a main() function instead of putting it at the module level. When running in a p3d file, the startup order is a little bit different, and perhaps some of the initialization is not ready at the time the module is first imported.

Also, run() is not necessary and is supposed to be a no-op when you call it within a p3d file. Does it in fact have an effect at all? What happens if you remove it?



many thanks for your information! I assumed the run () is necessary to init and start the task system. You see I’m just creeping out of the egg shell with Panda3d. But it is such an incredibly awesome framework, I wonder why there are so few better known projects using it. It just perfectly fits all needs for my minecraft derivate, I currently read into the package system.

You are my idol as you allways have a suitable answer to any question. Are you a developer of panda3d?


Hello again,

actually it should have been a quick test, I usually don’t put runtime execution code on module level.

To avoid complications, I have put the init code into main_init function, to be called by a task. Not very nice, but way from module level. I hope the globals don’t matter.

But now I can not even correct the p3d-settings at runtime. Yesterday I’ve had a state, where I could still see the edge of geometry at start, but when I moved it completely disappered.

Now, I cannot see geometry at all. But from command-line it works well. Also, in browser-plugin the motion cannot be changed by a, s, d, w, shift or space.

Here’s the code + model:

and here the plug-in on a 5-minute-quick-hack website:

For quick view directly the code. It’s a mess and inefficient, I know. :slight_smile: But I’d like command-line and plugin to behave equally, at first.

from panda3d.core import Texture, GeomNode
from panda3d.core import GeomVertexFormat, GeomVertexData
from panda3d.core import Geom, GeomTriangles, GeomTristrips, GeomVertexWriter
from panda3d.core import Vec3, Vec4, Point3
from pandac.PandaModules import TextNode
from direct.directbase.DirectStart import *
from direct.showbase.DirectObject import DirectObject
from direct.task import Task

from random import choice
import time
import math

Q = 1   #: The ultimate cube edge length
LVU = Vec3(0, 0, 0)
LVO = Vec3(0, 0, Q)
RVU = Vec3(Q, 0, 0)
RVO = Vec3(Q, 0, Q)
LHU = Vec3(0, Q, 0)
LHO = Vec3(0, Q, Q)
RHU = Vec3(Q, Q, 0)
RHO = Vec3(Q, Q, Q)
v3_bg_color = Vec3(0.8, 0.8, 1)
d_map = {}
f_factor_h = -30.0
f_factor_p = 30.0
facx = 0.1
facy = 0.1

class Cube(object):
    v3_center_offs = Vec3(0.5*Q, 0.5*Q, 0.5*Q)    #: Offset vom node pos to center

    def __init__(self, chunk, v3_pos):
        self.chunk = chunk
        self.v3_pos = v3_pos
        self.v3_center = v3_pos + Cube.v3_center_offs
        self.d_faces = {}

    def create_faces(self):
        for i_face_orient in xrange(6):
            face = Face(cube=self, i_typ=i_face_orient)
            self.d_faces[i_face_orient] = face

class Face(object):
    LEFT = 0
    RIGHT = 1
    BACK = 2
    FRONT = 3
    BOTTOM = 4
    TOP = 5

    def __init__(self, cube, i_typ):
        """Object to handle a visible face of a cube

        @param cube: reference to the cube the face belongs to
        @type cube: Cube
        @param typ: the face type of the cube
        @type typ: the ID of the represented face (Face.LEFT, Face.RIGHT, ...)
        self.cube = cube
        self.i_typ = i_typ
        self.is_drawn = False

    def make_face(self):
        v3 = self.cube.v3_pos
        id = 1
        chunk = self.cube.chunk
        if self.i_typ == Face.FRONT:
            v1, v2, color = v3 + RHU, v3 + LHO, 0.4

        if self.i_typ == Face.BACK:
            v1, v2, color = v3 + LVU, v3 + RVO,  0.8

        if self.i_typ == Face.RIGHT:
            v1, v2, color = v3 + RVU, v3 + RHO, 1.0

        if self.i_typ == Face.LEFT:
            v1, v2, color = v3 + LHU, v3 + LVO, 0.3

        if self.i_typ == Face.TOP:
            v1, v2, color = v3 + RHO, v3 + LVO, 0.7

        if self.i_typ == Face.BOTTOM:
            v1, v2, color = v3 + LHU, v3 + RVU, 0.6

        x1, y1, z1 = v1
        x2, y2, z2 = v2
        if x1 != x2:
            _v0 = chunk.vertex.getWriteRow()
            chunk.vertex.addData3f(x1, y1, z1)
            chunk.vertex.addData3f(x2, y1, z1)
            chunk.vertex.addData3f(x2, y2, z2)
            chunk.vertex.addData3f(x1, y2, z2)

            _v0 = chunk.vertex.getWriteRow()
            chunk.vertex.addData3f(x1, y1, z1)
            chunk.vertex.addData3f(x2, y2, z1)
            chunk.vertex.addData3f(x2, y2, z2)
            chunk.vertex.addData3f(x1, y1, z2)

        chunk.color.addData4f(color, color, color, 1.0)
        chunk.color.addData4f(color, color, color, 1.0)
        chunk.color.addData4f(color, color, color, 1.0)
        chunk.color.addData4f(color, color, color, 1.0)

        if id == 1:
            chunk.texcoord.addData2f(0.0, 1.0)
            chunk.texcoord.addData2f(0.0, 0.0)
            chunk.texcoord.addData2f(0.5, 0.0)
            chunk.texcoord.addData2f(0.5, 1.0)
        elif id == 2:
            chunk.texcoord.addData2f(0.5, 1.0)
            chunk.texcoord.addData2f(0.5, 0.0)
            chunk.texcoord.addData2f(1.0, 0.0)
            chunk.texcoord.addData2f(1.0, 1.0)

        chunk.tristrips.addVertices(_v0, _v0 + 1, _v0 + 3, _v0 + 2)
        self.is_drawn = True

class Chunk(object):
    format = GeomVertexFormat.getV3c4t2()

    def __init__(self, v3_pos=Vec3(0, 0, 0), name="Chunk"):
        self.d_cubes = {} = name
        self.vertex_data = GeomVertexData(name, self.format, Geom.UHStatic)
        self.tristrips = GeomTristrips(Geom.UHStatic)

        self.vertex = GeomVertexWriter(self.vertex_data, 'vertex')
        self.color = GeomVertexWriter(self.vertex_data, 'color')
        self.texcoord = GeomVertexWriter(self.vertex_data, 'texcoord')

    def get_geom_node(self):
        geom_node = GeomNode(
        mesh = Geom(self.vertex_data)
        return geom_node

    def add_cube(self, cube):
        self.d_cubes[cube.v3_pos] = cube
        return cube

    def draw_cubes(self):
        for cube in self.d_cubes.itervalues():

class World(DirectObject):
    def __init__(self):
        self.d_keys = {"a":False, "s":False, "d":False,
                       "w":False, "shift":False, "space":False}
        self.accept( "a" , self.a_down_callback )
        self.accept( "d" , self.d_down_callback )
        self.accept( "w" , self.w_down_callback )
        self.accept( "s" , self.s_down_callback )
        self.accept( "shift" , self.lshift_down_callback )
        self.accept( "space" , self.space_down_callback )
        self.accept( "a-up" , self.a_up_callback )
        self.accept( "d-up" , self.d_up_callback )
        self.accept( "w-up" , self.w_up_callback )
        self.accept( "s-up" , self.s_up_callback )
        self.accept( "shift-up" , self.lshift_up_callback )
        self.accept( "space-up" , self.space_up_callback )
        self.accept( "mouse1", self.mouse_left_callback )
        self.accept( "escape", self.quit_callback )

    def translate_camera_along_vec(self, scale=None):
        """Projects cam coord-system onto world coord. system before translation

        @keyword vel: vector indicating the desired translation relative to the camera. If omitted, vel is created from x, y, z keyword params
        @type vel: LVecBase3f | None
        @keyword x: x portion of the desired translation relativ to the camera, ignored if keyword param is given and non-zero
        @type x: Real number
        @keyword y: y portion of the desired translation relativ to the camera, ignored if keyword param is given and non-zero
        @type y: Real number
        @keyword z: z portion of the desired translation relativ to the camera, ignored if keyword param is given and non-zero
        @type z: Real number
        @keyword scale: if given, then a copy of vec is normalized and multiplied by scale, so only vec's direction is relevant, not its total length
        @type scale: Float for true division
        @return: the camera gets translated
        @rvalue: None
        if self.d_keys["a"]: v3_vel[0] = -0.4
        if self.d_keys["d"]: v3_vel[0] = 0.4
        if self.d_keys["s"]: v3_vel[1] = -0.5
        if self.d_keys["w"]: v3_vel[1] = 0.5
        if self.d_keys["shift"]: v3_vel[2] = -0.2
        if self.d_keys["space"]: v3_vel[2] = 0.2
        x, y, z = v3_vel, v3_vel)
        if x >= 0:
            v3_vel[0] = max(0, x-0.08)
        if x < 0:
            v3_vel[0] = min(0, x+0.08)
        if y >= 0:
            v3_vel[1] = max(0, y-0.06)
        if y < 0:
            v3_vel[1] = min(0, y+0.06)
        if z >= 0:
            v3_vel[2] = max(0, z-0.05)
        if z < 0:
            v3_vel[2] = min(0, z+0.05)

        #print dir(

    def a_down_callback(self): self.d_keys["a"] = True
    def s_down_callback(self): self.d_keys["s"] = True
    def d_down_callback(self): self.d_keys["d"] = True
    def w_down_callback(self): self.d_keys["w"] = True
    def lshift_down_callback(self): self.d_keys["shift"] = True
    def space_down_callback(self): self.d_keys["space"] = True
    def a_up_callback(self): self.d_keys["a"] = False
    def s_up_callback(self): self.d_keys["s"] = False
    def d_up_callback(self): self.d_keys["d"] = False
    def w_up_callback(self): self.d_keys["w"] = False
    def lshift_up_callback(self): self.d_keys["shift"] = False
    def space_up_callback(self): self.d_keys["space"] = False

    def arrow_up_callback(self):
        print "up arrow pressed", 5)

    def arrow_down_callback(self):
        print "down arrow pressed", -5)

    def mouse_left_callback(self):
        print "left mouse button clicked:"

    def quit_callback(self):
        raise SystemExit()

def main(task):
    global text, w, v3_vel, chunk

    chunk = Chunk()

    print "define cubes"
    for x in xrange(n):
        print "x:", x+1, "von", n
        for y in xrange(n):
            z_max = int(8*(math.sin(x*facx)+math.cos(y*facy)))
            for z in xrange(z_max+1):
                d_map[(x, y, z)] = 1

    print "create cubes"
    for pos, typ in d_map.iteritems():
        c = chunk.add_cube(Cube(chunk=chunk, v3_pos=Vec3(*pos)))

    print "draw cubes"
    chunk_np = render.attachNewNode(chunk.get_geom_node())
    chunk_np.setPos(0, 0, 0)
    print "cubes drawn, start copying the chunk"
    i = 1
    for x in xrange(i):
        for y in xrange(i):
            if x or y:
                np = render.attachNewNode("copy")
                copy = chunk_np.copyTo(np)
                copy.setPos(x*n, y*n, 0)

    print "finished creating", 0, 0)

    v3_vel = Vec3(0, 0, 0)

    w = World(), 0, 0)

    text = TextNode('node name')
    textNodePath = aspect2d.attachNewNode(text)

    if abs( > 1000:
        text.setText("heading reset")

    return Task.done

def findCenter():
   # Querry the screen size
   #   and calculate the center
   props =
   winX = props.getXSize()
   winY = props.getYSize()
   return [winX / 2, winY / 2]

def mouseTask(task):
   # variable to save center coordinates
   center    = findCenter()

      # Get mouse coordinates from mouse watcher
      move = [x,y]
      text.setText("curr hpr: %s" % + " %s" % ( #(, f_factor_h*x))
      if x or y:
 + f_factor_h*x)
          f_p = + y * f_factor_p

      # Reset the cursor to the center of the screen, center[0], center[1])

   except Exception, s:
      # If mouse goes outside window, catch the
      #    exception here and pass gracefully.
      print "Exception in mouseTask:", s

   return Task.cont

# Add the task and Run
taskMgr.add(main, 'maintask_init')
taskMgr.add(mouseTask, 'myTask')

Thanks, BR

Hmm, “main” is a magic name. If your toplevel module has a function called main(), then the p3d system will automatically call it, with a single parameter (the AppRunner object), when it starts executing.

Here you have a function called main() that accepts one parameter and is intended to be added to the task manager. I’m not sure what will happen when the p3d system tries to call it automatically, and then the task system runs it too.

Probably better to either rename the function, or not to add it to the task manager.


Hi Dave,

yes, that’s a point. I’m still not very familiar with the side-effects of the program running as website plugin.
I have renamed the function to init_main, but it doesn’t help in making the geometry visible in the browser plugin. Do you have any idea where it is lost? I think I’ll have to do a deep step-by-step analysis this weekend.


Hmm, well, it seems to work when I try it, mostly. Your code has some issues with assuming it always has the mouse focus; if the application starts up without window focus, it gets very confused and I can’t control anything. If it has window focus initially, though, it appears to be pretty well-behaved. Both of these cases appear to be true whether I run it directly from the Python code, or from a p3d file on the command line.

I haven’t tried it in a browser, but I suspect it’s similar. Note that initial keyboard focus isn’t likely in a browser. Also note that support for movePointer() in a browser is not universal. It should work on Windows, though (but in won’t work on a Mac, because that’s just the way Macs are).


Morning David,

I have now tested on win7/64-bit Google Chrome and it has the same behaviour + does not show the geometry.

For both, Firefox and Chrome: the p-rotation works fine right from the start. The heading stays at that incredibly large negative value (3.38e+025) until I hit any of the movement keys (a, s, d, w, space, shift), then h is reset to 0. Strangely the heading is reset to 0 every time it is negative and I hit a movement key. When it is positive, hitting a movement key has no effect on the heading. This is a completely different behaviour from the shell execution. And still I need to find out where the geometry was left behind. :slight_smile:

Thanks for your hint on resetting the pointer. So you propose me to keep track of the current mouse pos and work with the difference instead of absolute values after a reset?


I suspect the differences between in-browser and in-Python might be more due to subtle differences in frame rate or something like that, than to any fundamental difference in execution order. The code seems rather sensitive to environmental differences.

I don’t know if there’s a reliable way to do mouselook mode on a Mac in a browser. The OSX operating system doesn’t really like to let an application take complete control over the mouse. It kind of begrudgingly allows it in application mode, but when you’re running in a browser the browser is the application, not the Panda app.



removing the mouse handler action made the geometry visible and the camera navigable. I have uploaded the working p3d so it can be tested on my website, if anyone wants to have a try: