Explorar el Código

organize some more, in preparation for better multi-window support

David Rose hace 24 años
padre
commit
38bb65887c
Se han modificado 1 ficheros con 246 adiciones y 107 borrados
  1. 246 107
      direct/src/showbase/ShowBase.py

+ 246 - 107
direct/src/showbase/ShowBase.py

@@ -65,62 +65,178 @@ class ShowBase:
         fsmRedefine = self.config.GetBool('fsm-redefine', 0)
         State.FsmRedefine = fsmRedefine
 
-        self.renderTop = NodePath(NamedNode('renderTop'))
-        self.render = self.renderTop.attachNewNode('render')
-
-        # Set a default "off color" (i.e. use poly color) for color transitions
-        self.render.setColorOff()
-
         self.hidden = NodePath(NamedNode('hidden'))
-        
-        self.dataRoot = NodePath(NamedNode('dataRoot'), DataRelation.getClassType())
-        # Cache the node so we do not ask for it every frame
-        self.dataRootNode = self.dataRoot.node()
-        self.dataUnused = NodePath(NamedNode('dataUnused'), DataRelation.getClassType())
-        self.pipe = makeGraphicsPipe()
-        chanConfig = makeGraphicsWindow(self.pipe, self.render.arc())
-        self.win = chanConfig.getWin()
-
-        # Now that we've assigned a window, assign an exitfunc.
-        self.oldexitfunc = getattr(sys, 'exitfunc', None)
-        sys.exitfunc = self.exitfunc
 
-        # cameraList is a list of camera group nodes.  There may
-        # be more than one display region/camera node beneath each
-        # one.
-        self.cameraList = []
-        for i in range(chanConfig.getNumGroups()):
-            self.cameraList.append(self.render.attachNewNode(
-                chanConfig.getGroupNode(i)))
-        # this is how we know which display region cameras belong to which
-        # camera group.  display region i belongs to group self.groupList[i]
-        self.groupList = []
-        for i in range(chanConfig.getNumDrs()):
-            self.groupList.append(chanConfig.getGroupMembership(i))
-        self.camera = self.cameraList[0]
+        self.setupRender()
+        self.setupRender2d()
+        self.setupDataGraph()        
 
         # This is a placeholder for a CollisionTraverser.  If someone
         # stores a CollisionTraverser pointer here, we'll traverse it
         # in the igloop task.
         self.cTrav = 0
 
-        # This is a list of cams associated with the display region's cameras
+        # base.win is the main, or only window; base.winList is a list of
+        # *all* windows.  Similarly with base.pipeList and base.camList.
+        self.win = None
+        self.winList = []
+        self.pipe = None
+        self.pipeList = []
+        self.cam = None
         self.camList = []
-        for camera in self.cameraList:
-            self.camList.append( camera.find('**/+Camera') )
-        # Set the default camera
-        self.cam = self.camera.find('**/+Camera')
+        self.camNode = None
+        self.camLens = None
+
+        # base.camera is a little different; rather than referring to
+        # base.cameraList[0], it is instead the parent node of all
+        # cameras in base.cameraList.  That way, multiple cameras can
+        # easily be dragged around by moving the one node.
+        self.camera = self.render.attachNewNode('camera')
+        self.cameraList = []
+        self.groupList = []
+        self.camera2d = self.render2d.attachNewNode('camera2d')
+
+        # Now that we've set up the window structures, assign an exitfunc.
+        self.oldexitfunc = getattr(sys, 'exitfunc', None)
+        sys.exitfunc = self.exitfunc
+
+        # Open the default rendering window.
+        if self.config.GetBool('open-default-window', 1):
+            self.openWindow()
+            self.setupMouse(self.win)
+            self.makeCamera2d(self.win, -1, 1, -1, 1)
+
+        self.loader = Loader.Loader(self)
+        self.eventMgr = eventMgr
+        self.messenger = messenger
+        self.taskMgr = taskMgr
+
+        # Particle manager
+        self.particleMgr = particleMgr
+        self.particleMgr.setFrameStepping(1)
+        self.particleMgrEnabled = 0
+
+        # Physics manager
+        self.physicsMgr = physicsMgr
+        integrator = LinearEulerIntegrator()
+        self.physicsMgr.attachLinearIntegrator(integrator)
+        self.physicsMgrEnabled = 0
+        self.physicsMgrAngular = 0
+
+        self.createAudioManager()
+        self.createStats()
+
+        # Transition effects (fade, iris, etc)
+        self.transitions = Transitions.Transitions(self.loader)
+
+        self.AppHasAudioFocus = 1
+
+        __builtin__.base = self
+        __builtin__.render2d = self.render2d
+        __builtin__.aspect2d = self.aspect2d
+        __builtin__.render = self.render
+        __builtin__.hidden = self.hidden
+        __builtin__.camera = self.camera
+        __builtin__.loader = self.loader
+        __builtin__.taskMgr = self.taskMgr
+        __builtin__.eventMgr = self.eventMgr
+        __builtin__.messenger = self.messenger
+        __builtin__.config = self.config
+        __builtin__.run = self.run
+        __builtin__.ostream = Notify.out()
+        __builtin__.directNotify = directNotify
+
+        # Tk
+        if self.wantTk:
+            import TkGlobal
+        if self.wantDIRECT:
+            import DirectSession
+            direct.enable()
+        else:
+            __builtin__.direct = self.direct = None
+
+        self.restart()
+
+    def exitfunc(self):
+        """exitfunc(self)
+
+        This should be assigned to sys.exitfunc to be called just
+        before Python shutdown.  It guarantees that the Panda window
+        is closed cleanly, so that we free system resources, restore
+        the desktop and keyboard functionality, etc.
+        """
+        for win in self.winList:
+            win.closeWindow()
+        del self.win
+        del self.winList
+        del self.pipe
+
+        if self.oldexitfunc:
+            self.oldexitfunc()
+
+    def openWindow(self):
+        """openWindow(self)
+
+        Invokes ChanConfig to create a window and adds it to the list
+        of windows that are to be updated every frame.
+
+        """
+        pipe = makeGraphicsPipe()
+        chanConfig = makeGraphicsWindow(pipe, self.render.arc())
+        win = chanConfig.getWin()
+
+        if self.pipe == None:
+            self.pipe = pipe
+        if self.win == None:
+            self.win = win
+
+        self.pipeList.append(pipe)
+        self.winList.append(win)
 
-        # If you need to get a handle to the camera node itself, use
-        # self.camNode.
-        self.camNode = self.cam.node()
-        # If you need to adjust camera parameters, like fov or
-        # near/far clipping planes, use self.camLens
-        self.camLens = self.camNode.getLens()
+        self.getCameras(chanConfig)
 
-        # Set up a 2-d layer for drawing things behind Gui labels.
-        self.render2d = NodePath(setupPanda2d(self.win, "render2d"))
 
+    def setupRender(self):
+        """setupRender(self)
+
+        Creates the render scene graph, the primary scene graph for
+        rendering 3-d geometry.
+        """
+        self.renderTop = NodePath(NamedNode('renderTop'))
+        self.render = self.renderTop.attachNewNode('render')
+
+        # Set a default "off color" (i.e. use poly color) for color transitions
+        self.render.setColorOff()
+
+    def setupRender2d(self):
+        """setupRender2d(self)
+
+        Creates the render2d scene graph, the primary scene graph for
+        2-d objects and gui elements that are superimposed over the
+        3-d geometry in the window.
+        """
+
+        self.render2dTop = NodePath(NamedNode('render2dTop'))
+        self.render2d = self.render2dTop.attachNewNode('render2d')
+
+        # Set up some overrides to turn off certain properties which
+        # we probably won't need for 2-d objects.
+
+        # It's particularly important to turn off the depth test,
+        # since we'll be keeping the same depth buffer already filled
+        # by the previously-drawn 3-d scene--we don't want to pay for
+        # a clear operation, but we also don't want to collide with
+        # that depth buffer.
+        dt = DepthTestTransition(DepthTestProperty.MNone)
+        dw = DepthWriteTransition.off()
+        lt = LightTransition.allOff()
+        self.render2d.arc().setTransition(dt, 1)
+        self.render2d.arc().setTransition(dw, 1)
+        self.render2d.arc().setTransition(lt, 1)
+
+        self.render2d.setMaterialOff(1)
+        self.render2d.setTwoSided(1, 1)
+        
         # The normal 2-d layer has an aspect ratio that matches the
         # window, but its coordinate system is square.  This means
         # anything we parent to render2d gets stretched.  For things
@@ -133,15 +249,61 @@ class ShowBase:
         self.aspect2d = self.render2d.attachNewNode(PGTop("aspect2d"))
         self.aspect2d.setScale(1.0 / self.aspectRatio, 1.0, 1.0)
 
-        # And let's enforce that aspect ratio on the camera.
-        self.camLens.setAspectRatio(self.aspectRatio)
-
         # It's important to know the bounds of the aspect2d screen.
         self.a2dTop = 1.0
         self.a2dBottom = -1.0
         self.a2dLeft = -self.aspectRatio
         self.a2dRight = self.aspectRatio
 
+    def makeCamera2d(self, win, left, right, bottom, top):
+        """makeCamera2d(self)
+
+        Makes a new camera2d associated with the indicated window, and
+        assigns it to render the indicated subrectangle of render2d.
+        """
+
+        # First, we need to make a new layer on the window.
+        chan = win.getChannel(0)
+        layer = chan.makeLayer()
+
+        # And make a display region to cover the whole layer.
+        dr = layer.makeDisplayRegion()
+
+        # Now make a new Camera node.
+        cam2dNode = Camera('cam2d')
+        lens = OrthographicLens()
+        lens.setFilmSize(right - left, top - bottom)
+        lens.setFilmOffset((right + left) / 2.0, (top + bottom) / 2.0)
+        lens.setNearFar(-1000, 1000)
+        cam2dNode.setLens(lens)
+        cam2dNode.setScene(self.render2d.getTopNode())
+        dr.setCamera(cam2dNode)
+
+        camera2d = self.camera2d.attachNewNode(cam2dNode)
+        return camera2d
+
+
+    def setupDataGraph(self):
+        """setupDataGraph(self)
+
+        Creates the data graph and populates it with the basic input
+        devices.
+        """
+        
+        self.dataRoot = NodePath(NamedNode('dataRoot'), DataRelation.getClassType())
+        # Cache the node so we do not ask for it every frame
+        self.dataRootNode = self.dataRoot.node()
+        self.dataUnused = NodePath(NamedNode('dataUnused'), DataRelation.getClassType())
+
+
+    def setupMouse(self, win):
+        """setupMouse(self, win)
+
+        Creates the structures necessary to monitor the mouse input,
+        using the indicated window.  This should only be called once
+        per application.
+        """
+        
         # We create both a MouseAndKeyboard object and a MouseWatcher object
         # for the window.  The MouseAndKeyboard generates mouse events and
         # mouse button/keyboard events; the MouseWatcher passes them through
@@ -151,7 +313,7 @@ class ShowBase:
         # button, like the driveInterface, should be parented to
         # mouseWatcher, while objects that want events in all cases, like the
         # chat interface, should be parented to mak.
-        self.mak = self.dataRoot.attachNewNode(MouseAndKeyboard(self.win, 0, 'mak'))
+        self.mak = self.dataRoot.attachNewNode(MouseAndKeyboard(win, 0, 'mak'))
         self.mouseWatcherNode = MouseWatcher('mouseWatcher')
         self.mouseWatcher = self.mak.attachNewNode(self.mouseWatcherNode)
         mb = self.mouseWatcherNode.getModifierButtons()
@@ -185,75 +347,51 @@ class ShowBase:
 
         self.buttonThrower = self.mouseWatcher.attachNewNode(ButtonThrower())
 
-        # Set up gui mouse watcher
+
+        # Tell the gui system about our new mouse watcher.
         self.aspect2d.node().setMouseWatcher(self.mouseWatcherNode)
         self.mouseWatcherNode.addRegion(PGMouseWatcherBackground())
 
-        self.loader = Loader.Loader(self)
-        self.eventMgr = eventMgr
-        self.messenger = messenger
-        self.taskMgr = taskMgr
-
-        # Particle manager
-        self.particleMgr = particleMgr
-        self.particleMgr.setFrameStepping(1)
-        self.particleMgrEnabled = 0
-
-        # Physics manager
-        self.physicsMgr = physicsMgr
-        integrator = LinearEulerIntegrator()
-        self.physicsMgr.attachLinearIntegrator(integrator)
-        self.physicsMgrEnabled = 0
-        self.physicsMgrAngular = 0
-
-        self.createAudioManager()
-        self.createStats()
 
-        # Transition effects (fade, iris, etc)
-        self.transitions = Transitions.Transitions(self.loader)
+    def getCameras(self, chanConfig):
+        """getCameras(self, chanConfig)
 
-        self.AppHasAudioFocus = 1
+        Extracts the camera(s) out of the ChanConfig record, parents
+        them all to base.camera, and adds them to base.cameraList.
+        """
 
-        __builtin__.base = self
-        __builtin__.render2d = self.render2d
-        __builtin__.aspect2d = self.aspect2d
-        __builtin__.render = self.render
-        __builtin__.hidden = self.hidden
-        __builtin__.camera = self.camera
-        __builtin__.loader = self.loader
-        __builtin__.taskMgr = self.taskMgr
-        __builtin__.eventMgr = self.eventMgr
-        __builtin__.messenger = self.messenger
-        __builtin__.config = self.config
-        __builtin__.run = self.run
-        __builtin__.ostream = Notify.out()
-        __builtin__.directNotify = directNotify
+        # cameraList is a list of camera group nodes.  There may
+        # be more than one display region/camera node beneath each
+        # one.
+        for i in range(chanConfig.getNumGroups()):
+            camera = self.camera.attachNewNode(chanConfig.getGroupNode(i))
+            cam = camera.find('**/+Camera')
+            lens = cam.node().getLens()
 
-        # Tk
-        if self.wantTk:
-            import TkGlobal
-        if self.wantDIRECT:
-            import DirectSession
-            direct.enable()
-        else:
-            __builtin__.direct = self.direct = None
+            # Enforce our expected aspect ratio, overriding whatever
+            # nonsense ChanConfig put in there.
+            lens.setAspectRatio(self.aspectRatio)
+            
+            self.cameraList.append(camera)
+            self.camList.append(cam)
+            
+        # this is how we know which display region cameras belong to which
+        # camera group.  display region i belongs to group self.groupList[i]
+        for i in range(chanConfig.getNumDrs()):
+            self.groupList.append(chanConfig.getGroupMembership(i))
 
-        self.restart()
+        # Set the default camera
+        if self.cam == None:
+            self.cam = self.camList[0]
 
-    def exitfunc(self):
-        """exitfunc(self)
+            # If you need to get a handle to the camera node itself,
+            # use self.camNode.
+            self.camNode = self.cam.node()
 
-        This should be assigned to sys.exitfunc to be called just
-        before Python shutdown.  It guarantees that the Panda window
-        is closed cleanly, so that we free system resources, restore
-        the desktop and keyboard functionality, etc.
-        """
-        self.win.closeWindow()
-        del self.win
-        del self.pipe
+            # If you need to adjust camera parameters, like fov or
+            # near/far clipping planes, use self.camLens.
+            self.camLens = self.camNode.getLens()
 
-        if self.oldexitfunc:
-            self.oldexitfunc()
 
     def getAlt(self):
         return base.mouseWatcherNode.getModifierButtons().isDown(
@@ -376,7 +514,8 @@ class ShowBase:
         if self.cTrav:
             self.cTrav.traverse(self.render)
         # Finally, render the frame.
-        self.win.update()
+        for win in self.winList:
+            win.update()
         globalClock.tick()
         return Task.cont