Camera issue: ShowBase won't reference 'base' variable

Hi,
I want to reorient my 3D scene’s camera so that it is looking down at the grid (as a bird’s eye view) when the window opens. As of right now, when the program runs, the grid lines up with where the camera is currently, so it looks like a straight line. (When you hold the mouse down and move it, however, you can see the grid better.)

I have tried to use camera.setPos(0, -15, 5) and camera.setHpr(0, -15, 0), but my IDE PyCharm warns me that camera is unresolved, and base.camera doesn’t work either.

Here is my code:

import sys
import cv2
from direct.showbase.ShowBase import ShowBase
from panda3d_viewer import geometry
import numpy as np
from pandac.PandaModules import *

class Application(ShowBase):
    LightMask = BitMask32(1)
    def __init__(self):
        ShowBase.__init__(self)

        self._grid = self._make_grid()
        self.show_grid(self.config.GetBool('show-grid', True))

        self._floor = self._make_floor()
        self.show_floor(self.config.GetBool('show-floor', False))

        #self.disableMouse()

        camera.setPos(0, -15, 5)  # move the camera back
        camera.setHpr(0, -15, 0)  # make the camera look down

    def make_grid(num_ticks=10, step=1.0):
        ticks = np.arange(-num_ticks // 2, num_ticks // 2 + 1) * step
        vformat = GeomVertexFormat.get_v3()
        vdata = GeomVertexData('vdata', vformat, Geom.UHStatic)
        vdata.uncleanSetNumRows(len(ticks) * 4)
        vertex = GeomVertexWriter(vdata, 'vertex')

        for t in ticks:
            vertex.addData3(t, ticks[0], 0)
            vertex.addData3(t, ticks[-1], 0)
            vertex.addData3(ticks[0], t, 0)
            vertex.addData3(ticks[-1], t, 0)

        prim = GeomLines(Geom.UHStatic)
        prim.addNextVertices(len(ticks) * 4)

        geom = Geom(vdata)
        geom.addPrimitive(prim)
        return geom

    def _make_grid(self):
        model = GeomNode('grid')
        model.add_geom(geometry.make_grid())
        node = self.render.attach_new_node(model)
        node.set_light_off()
        node.set_render_mode_wireframe()
        node.set_antialias(AntialiasAttrib.MLine)
        node.hide(self.LightMask)
        return node


    def show_grid(self, show):
        if show:
            self._grid.show()
        else:
            self._grid.hide()

    def _make_floor(self):
        model = GeomNode('floor')
        model.add_geom(geometry.make_plane(size=(10, 10)))
        node = self.render.attach_new_node(model)
        node.set_color(Vec4(0.3, 0.3, 0.3, 1))
        material = Material()
        material.set_ambient(Vec4(0, 0, 0, 1))
        material.set_diffuse(Vec4(0.3, 0.3, 0.3, 1))
        material.set_specular(Vec3(1, 1, 1))
        material.set_roughness(0.8)
        node.set_material(material, 1)
        return node

    def make_plane(size=(1.0, 1.0)): # plane size as x,y
        vformat = GeomVertexFormat.get_v3n3t2()
        vdata = GeomVertexData('vdata', vformat, Geom.UHStatic)
        vdata.uncleanSetNumRows(4)

        vertex = GeomVertexWriter(vdata, 'vertex')
        normal = GeomVertexWriter(vdata, 'normal')
        tcoord = GeomVertexWriter(vdata, 'texcoord')

        quad = ((0, 0), (1, 0), (0, 1), (1, 1))

        for u, v in quad:
            vertex.addData3((u - 0.5) * size[0], (v - 0.5) * size[1], 0)
            normal.addData3(0, 0, 1)
            tcoord.addData2(u, v)

        prim = GeomTriangles(Geom.UHStatic)
        prim.addVertices(0, 1, 2)
        prim.addVertices(2, 1, 3)

        geom = Geom(vdata)
        geom.addPrimitive(prim)
        return geom

    # Render the floor
    def show_floor(self, show):
        if show:
            self._floor.show()
        else:
            self._floor.hide()

    def setup_camera(self, pos):
        self.setPos()


app = Application()
app.run()

You are wrong about the fact that this problem is related to Panda3D, it is a software problem that was developed by the third party.

Ah, maybe you should call the self.disableMouse() function.

You should not trust the PyCharm IDE, it may incorrectly define methods and variables of the ShowBase class. Just ignore these statements.

I’m inclined to agree: while PyCharm doesn’t recognise those references, I think that you’ll find that if you run the program (even from within PyCharm) they should work as expected.

(Indeed, I use PyCharm myself, and the above is my experience of it.)

That said, there is an argument for eschewing such global references. This can be done by accessing them not from “base”, but from your “Application” class-instance (as it is, after all, a sub-class of ShowBase).

Something like this:

class Application(ShowBase):
    def __init__(self):
        # All of your usual stuff here
        # (Plus "disableMouse", as serega recommended)
        
        self.camera.setPos(0, -15, 5)
        self.camera.setHpr(0, -15, 0)

# etc...

If you want to access these things from outside of the class, you should be able to do so by giving yourself access to your instance of the “Application” class.

This might be implemented either by passing it as a parameter into the relevant methods, or by creating a “common” Python-script that all relevant scripts import and that stores a reference to it.

The first option might look something like this:

class SomeOtherClass():
    def __init__(self):
        self.counter = 0

    def doSomething(self, applicationInstance):
        print ("Doing something!")
        self.counter += 1
        applicationInstance.camera.setX(5)

Which might be used by the Application class like so:

class Application(ShowBase):
    def __init__(self):
        # All of the usual stuff here

        obj = SomeOtherClass()
        obj.doSomething(self)

The second option might look something like this:

Common.py

applicationInstance = None

Application.py

import Common

class Application(ShowBase):
    def __init__(self):
        # All of the usual stuff here

        Common.applicationInstance = self

Some other Python file

import Common

class SomeOtherClass():
    def __init__(self):
        self.counter = 0

    def doSomething(self, applicationInstance):
        print ("Doing something!")
        self.counter += 1
        Common.applicationInstance.camera.setX(5)

However, I don’t think the publication is about links and access to them. I think the problem is obvious that the IDE does not recognize link types that are only available at runtime.

However, I drew attention to something else, how people suffer who resort to the help of third-party libraries, instead of learning native Panda3D. If you study Panda3D at least from the web manual, then limitless possibilities open up. Here is an example written in 20 minutes, taking into account the fact that I had part of the code.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import (LineSegs, NodePath, GeomVertexWriter, GeomVertexData, 
GeomPoints, Geom, GeomNode, GeomVertexFormat, TextNode,  AntialiasAttrib)

import random

def make_array(vertex_count):

    vdata = GeomVertexData('name', GeomVertexFormat.get_v3c4(), Geom.UHStatic)
    vdata.set_num_rows(4)

    vertex = GeomVertexWriter(vdata, 'vertex')
    color = GeomVertexWriter(vdata, 'color')

    geom_points = GeomPoints(Geom.UHDynamic)

    for i in range(vertex_count):

        vertex.add_data3(random.uniform(-1.0, 1.0),
                         random.uniform(-1.0, 1.0),
                         random.uniform(-1.0, 1.0))

        color.add_data4(random.uniform(0.0, 1.0),
                        random.uniform(0.0, 1.0),
                        random.uniform(0.0, 1.0), 1)

        geom_points.add_vertex(i)

    geom = Geom(vdata)
    geom.add_primitive(geom_points)

    geom_node = GeomNode('PointArray')
    geom_node.add_geom(geom)

    np = NodePath(geom_node)

    return np

def make_grid(size = 2, color = (1, 1, 1, 1)):

    size = int(size/2)

    seg = LineSegs()
    seg.set_color(color)

    for i in range((size*2)+1):
        seg.move_to(size-i, size, 0)
        seg.draw_to(size-i, -size, 0)
        seg.move_to(size, size-i, 0)
        seg.draw_to(-size, size-i, 0)

    np = NodePath(seg.create())
    np.set_light_off()
    np.set_antialias(AntialiasAttrib.MLine)

    return np

class Application(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        # Disable camera control by default.
        base.disable_mouse()

        # Adjust the camera's position and rotation.
        camera.set_pos(0, 0, 30)
        camera.set_hpr(0, -90, 0)

        # Create grid size 14, use only even numbers.
        grid = make_grid(14)
        grid.reparent_to(render)

        # Load plane.
        plane = loader.load_model("plane_1x1.bam")
        plane.set_color((0.7, 0.7, 0.7, 1))
        plane.set_scale(14)
        plane.set_light_off()
        plane.set_depth_offset(-1)
        plane.reparent_to(render)

        # Load model.
        model = loader.load_model("panda")
        model.set_scale(0.3)
        model.reparent_to(render)

        # Create array point.
        array = make_array(30000)
        array.reparent_to(render)
        array.set_render_mode_thickness(4)
        array.set_render_mode_perspective(True)
        array.set_pos(0, 0, 1)
        
        # Create info text.
        text_node = TextNode("label")
        text_node.set_text("Panda")
        text_node.set_align(TextNode.ACenter)

        text = NodePath(text_node.generate())
        text.set_pos(0, 0, 0.4)
        text.set_scale(0.3)
        text.reparent_to(aspect2d)

app = Application()
app.run()

plane_1x1.bam (925 Bytes)

Yes, but I think that it’s a point worth noting.

(And should also have the effect of allowing PyCharm to recognise the references.)

Hmm, in order to satisfy the IDE and specifically PyCharm, you need to duplicate these definitions additionally, which borders on absurdity. This is strange for me, my “IDE” - Nodepad++ is not so capricious.

But the warnings are harmless: PyCharm happily runs the program in question. And if it really bothers one, then one can just tell it to ignore them.

And note that if one takes the approach of referencing them from either the ShowBase-derived custom class or a “common script”, then I don’t think that PyCharm gives warnings at all–no need to duplicate definitions.

(Plus, it’s currently advised that one not use the global variables provided by ShowBase, and I think that I may have read somewhere that there’s intention to remove them in time.)