NodePath Trouble

I’m having trouble making my class inherit from NodePath and reparenting it to render.

class Planet(NodePath):
    def __init__(self):
        NodePath.__init__(self)
        self.Terrain = loader.loadModel("Models/testplanet.egg")
        self.Terrain.reparentTo(self)
        self.reparentTo(render)

This is a simplified version however when I create a Planet class I get the following error.

Commandline: ppython C:\Chaneciya\sphereicalworldtest.py
Workingdirectory: 
Timeout: 0 ms

Known pipe types:
  wglGraphicsPipe
(3 aux display modules not yet loaded.)
:display:gsg:glgsg(warning): BlendEquation advertised as supported by OpenGL runtime, but could not get pointers to extension function.
:display:gsg:glgsg(warning): BlendColor advertised as supported by OpenGL runtime, but could not get pointers to extension function.
:util(warning): Adjusting global clock's real time by 0.277725 seconds.
Assertion failed: !is_empty() at line 335 of panda/src/pgraph/nodePath.cxx
DirectStart: Starting the game.
Warning: DirectNotify: category 'Interval' already exists
Traceback (most recent call last):
  File "C:\Chaneciya\sphereicalworldtest.py", line 107, in ?
    G = Game()
  File "C:\Chaneciya\sphereicalworldtest.py", line 94, in __init__
    self.TestPlanet = Planet()
  File "C:\Chaneciya\sphereicalworldtest.py", line 14, in __init__
    self.reparentTo(render)
AssertionError: !is_empty() at line 335 of panda/src/pgraph/nodePath.cxx

Process "Panda Python" terminated, ExitCode: 00000001

I’ve tried searching the forums however I had no luck. Can anyone point me in the right direction?

Well, there error message says that your NodePath is empty.

In your code you create a new Instance of your class Planet, and then init the base class NodePath. Until now you have exactly what the error message say, an empty NodePath.

In the next lines you load another NodePath (loader.loadModel…) assign the new NodePath to an attribute of your class Planet (self.Terrain). This does not modify the state of your original NodePath. It’s a new attribute that your derived class introduces. So you is still an empty NodePath without a PandaNode.

In other words: NodePath is just a pointer/handle/name-it-yourself to a node. Nodes can be just basic PandaNode instances, GeomNodes, ForceNodes, PlaneNode, and various other. NodePath is volatile (you don’t have to keep them), while PandaNodes are the “real” elements of the scene graph, and pernament (until you remove them again, using removeNode( ) <== node, not node path!).

So the problem is to create a non-empty NodePath, that is a NodePath with a PandaNode. A.F.A.I.K. this can be done only at the creation of the NodePath. Here are two ways to do so:

Using a PandaNode as node:

class Planet( NodePath ):
    def __init__( self, name ):
        NodePath.__init__( self, name )
        self.terrain = loader.loadModel( 'planet.bam' )
        self.terrain.reparentTo( self )

planet = Planet( 'Uranus' )
planet.reparentTo( render )
planet.setPos( 0, 10, 0 )

Using a GeomNode as node:

class Foo( NodePath ):
    def __init__( self ):
        np = loader.loadModel( 'foo.bam' )
        node = np.node( )
        NodePath.__init__( self, node )

foo = Foo( )
foo.reparentTo( render )
foo.setPos( 0, 10, 0 )

By the way: If deriving from a base class I would recommend to have the derived object behave as much as possible as a object it is derived from, plus some extra functionality. This means the Planet instance should not reparent itself to render or anything else upon creation. There is an object method (inherited from NodePath) for this purpose: Planet.reparentTo( np ).

enn0x