瀏覽代碼

*** empty log message ***

Mark Mine 24 年之前
父節點
當前提交
33c8db149d
共有 1 個文件被更改,包括 281 次插入11 次删除
  1. 281 11
      direct/src/leveleditor/LevelEditor.py

+ 281 - 11
direct/src/leveleditor/LevelEditor.py

@@ -19,6 +19,8 @@ import sys
 import whrandom
 import whrandom
 import __builtin__
 import __builtin__
 
 
+visualizeZones = base.config.GetBool("visualize-zones", 0)
+
 # Colors used by all color menus
 # Colors used by all color menus
 DEFAULT_COLORS = [
 DEFAULT_COLORS = [
     Vec4(1,1,1,1),
     Vec4(1,1,1,1),
@@ -501,6 +503,10 @@ class LevelEditor(NodePath, PandaObject):
         base.cam.node().setNear(1.0)
         base.cam.node().setNear(1.0)
         base.cam.node().setFar(3000)
         base.cam.node().setFar(3000)
         direct.camera.setPos(0,-10,10)
         direct.camera.setPos(0,-10,10)
+        # Initialize drive mode
+        self.configureDriveModeCollisionData()
+        # Init visibility variables
+        self.__zoneId = None
         # Hide (disable) grid initially
         # Hide (disable) grid initially
         self.showGrid(0)
         self.showGrid(0)
         # Create variable for vis groups panel
         # Create variable for vis groups panel
@@ -626,9 +632,10 @@ class LevelEditor(NodePath, PandaObject):
         # Destory old toplevel node path and DNA
         # Destory old toplevel node path and DNA
         # First the toplevel DNA
         # First the toplevel DNA
         self.DNAData.remove(self.DNAToplevel)
         self.DNAData.remove(self.DNAToplevel)
-        # Then the toplevel Node Path
-        self.NPToplevel.reparentTo(hidden)
-        self.NPToplevel.removeNode()
+        if self.NPToplevel.hasArcs():
+            # Then the toplevel Node Path
+            self.NPToplevel.reparentTo(hidden)
+            self.NPToplevel.removeNode()
 
 
     def createToplevel(self, dnaNode, nodePath = None):
     def createToplevel(self, dnaNode, nodePath = None):
         # When you create a new level, data is added to this node
         # When you create a new level, data is added to this node
@@ -659,6 +666,16 @@ class LevelEditor(NodePath, PandaObject):
 
 
     def useDirectFly(self):
     def useDirectFly(self):
         """ Disable player camera controls/enable direct camera control """
         """ Disable player camera controls/enable direct camera control """
+        # Turn off collision traversal
+        self.traversalOff()
+        # Turn on collisions
+        self.collisionsOff()
+        # Turn on visiblity
+        self.visibilityOff()
+        # Reset cam
+        base.camera.iPos(base.cam)
+        base.cam.iPosHpr()
+        # Renable mouse
         self.enableMouse()
         self.enableMouse()
         direct.enable()
         direct.enable()
 
 
@@ -677,10 +694,17 @@ class LevelEditor(NodePath, PandaObject):
         """ Disable direct camera manipulation and enable player drive mode """
         """ Disable direct camera manipulation and enable player drive mode """
         direct.minimumConfiguration()
         direct.minimumConfiguration()
         direct.manipulationControl.disableManipulation()
         direct.manipulationControl.disableManipulation()
+        # Update vis data
+        self.initVisibilityData()
+        # Switch to drive mode
         base.useDrive()
         base.useDrive()
+        # Move cam up and back
+        base.cam.setPos(0,-5,4)
+        # And move down and forward to compensate
+        base.camera.setPos(base.camera, 0, 5, -4)
         # Make sure we're where we want to be
         # Make sure we're where we want to be
         pos = direct.camera.getPos()
         pos = direct.camera.getPos()
-        pos.setZ(4.0)
+        pos.setZ(0.0)
         hpr = direct.camera.getHpr()
         hpr = direct.camera.getHpr()
         hpr.set(hpr[0], 0.0, 0.0)
         hpr.set(hpr[0], 0.0, 0.0)
         # Fine tune the drive mode
         # Fine tune the drive mode
@@ -688,7 +712,232 @@ class LevelEditor(NodePath, PandaObject):
         base.mouseInterface.node().setHpr(hpr)
         base.mouseInterface.node().setHpr(hpr)
         base.mouseInterface.node().setForwardSpeed(20.0)
         base.mouseInterface.node().setForwardSpeed(20.0)
         base.mouseInterface.node().setReverseSpeed(20.0)
         base.mouseInterface.node().setReverseSpeed(20.0)
+        # Turn on collisions
+        if self.panel.fColl.get():
+            self.collisionsOn()
+        # Turn on visiblity
+        if self.panel.fVis.get():
+            self.visibilityOn()
+        # Turn on collision traversal
+        if self.panel.fColl.get() or self.panel.fVis.get():
+            self.traversalOn()
+
+    def configureDriveModeCollisionData(self):
+        """initializeCollisions(self)
+        Set up the local avatar for collisions
+        """
+        # Set up the collision sphere
+        # This is a sphere on the ground to detect barrier collisions
+        self.cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.5)
+        self.cSphereNode = CollisionNode('cSphereNode')
+        self.cSphereNode.addSolid(self.cSphere)
+        self.cSphereNodePath = camera.attachNewNode(self.cSphereNode)
+        self.cSphereNodePath.hide()
+        self.cSphereBitMask = BitMask32.bit(0)
+        self.cSphereNode.setFromCollideMask(self.cSphereBitMask)
+        self.cSphereNode.setIntoCollideMask(BitMask32.allOff())
+
+        # Set up the collison ray
+        # This is a ray cast from your head down to detect floor polygons
+        self.cRay = CollisionRay(0.0, 0.0, 6.0, 0.0, 0.0, -1.0)
+        self.cRayNode = CollisionNode('cRayNode')
+        self.cRayNode.addSolid(self.cRay)
+        self.cRayNodePath = camera.attachNewNode(self.cRayNode)
+        self.cRayNodePath.hide()
+        self.cRayBitMask = BitMask32.bit(1)
+        self.cRayNode.setFromCollideMask(self.cRayBitMask)
+        self.cRayNode.setIntoCollideMask(BitMask32.allOff())
+
+        # set up wall collision mechanism
+        self.pusher = CollisionHandlerPusher()
+        self.pusher.setInPattern("enter%in")
+        self.pusher.setOutPattern("exit%in")
+
+        # set up floor collision mechanism
+        self.lifter = CollisionHandlerFloor()
+        self.lifter.setInPattern("on-floor")
+        self.lifter.setOutPattern("off-floor")
+        self.floorOffset = 0.1
+        self.lifter.setOffset(self.floorOffset)
+
+        # Limit our rate-of-fall with the lifter.
+        # If this is too low, we actually "fall" off steep stairs
+        # and float above them as we go down. I increased this
+        # from 8.0 to 16.0 to prevent this
+        self.lifter.setMaxVelocity(16.0)
+
+        # set up the collision traverser
+        self.cTrav = CollisionTraverser()
+
+        # activate the collider with the traverser and pusher
+        self.pusher.addCollider(self.cSphereNode, base.drive.node())
+        self.lifter.addCollider(self.cRayNode, base.drive.node())
+        # A map of zone ID's to a list of nodes that are visible from
+        # that zone.
+        self.nodeDict = {}
+        # A map of zone ID's to the particular node that corresponds
+        # to that zone.
+        self.zoneDict = {}
+        # A list of all visible nodes
+        self.nodeList = []
+        # Flag for bootstrapping visibility
+        self.fVisInit = 0
+
+    def traversalOn(self):
+        base.cTrav = self.cTrav
+
+    def traversalOff(self):
+        base.cTrav = 0
+
+    def collisionsOff(self):
+        self.cTrav.removeCollider(self.cSphereNode)
+
+    def collisionsOn(self):
+        self.collisionsOff()
+        self.cTrav.addCollider(self.cSphereNode, self.pusher)
+
+    def toggleCollisions(self):
+        if self.panel.fColl.get():
+            print 'ON'
+            self.collisionsOn()
+            self.traversalOn()
+        else:
+            self.collisionsOff()
+            if (not (self.panel.fColl.get() or self.panel.fVis.get())):
+                self.traversalOff()
+
+    def initVisibilityData(self):
+        # First make sure everything is shown
+        self.showAllVisibles()
+        # A map of zone ID's to a list of nodes that are visible from
+        # that zone.
+        self.nodeDict = {}
+        # A map of zone ID's to the particular node that corresponds
+        # to that zone.
+        self.zoneDict = {}
+        # A list of all visible nodes
+        self.nodeList = []
+        # NOTE: this should change to find the groupnodes in
+        # the dna storage instead of searching through the tree
+        for i in range(DNASTORE.getNumDNAVisGroups()):
+            groupFullName = DNASTORE.getDNAVisGroupName(i)
+            groupName = self.extractGroupName(groupFullName)
+            zoneId = int(groupName)
+            self.nodeDict[zoneId] = []
+            self.zoneDict[zoneId] = self.NPToplevel.find("**/" + groupName)
+            
+            # TODO: we only need to look from the top of the hood
+            # down one level to find the vis groups as an optimization
+            groupNode = self.NPToplevel.find("**/" + groupFullName)
+            if groupNode.isEmpty():
+                print "Could not find visgroup"
+            self.nodeList.append(groupNode)
+            for j in range(DNASTORE.getNumVisiblesInDNAVisGroup(i)):
+                visName = DNASTORE.getVisibleName(i, j)
+                visNode = self.NPToplevel.find("**/" + visName)
+                self.nodeDict[zoneId].append(visNode)
+        # Rename the floor polys to have the same name as the
+        # visgroup they are in... This makes visibility possible.
+        self.renameFloorPolys(self.nodeList)
+        # Init vis flag
+        self.fVisInit = 1
+
+    def extractGroupName(self, groupFullName):
+        # The Idea here is that group names may have extra flags associated
+        # with them that tell more information about what is special about
+        # the particular vis zone. A normal vis zone might just be "13001",
+        # but a special one might be "14356:safe_zone" or
+        # "345:safe_zone:exit_zone"... These are hypotheticals. The main
+        # idea is that there are colon separated flags after the initial
+        # zone name.
+        return(string.split(groupFullName, ":", 1)[0])
+    
+    def renameFloorPolys(self, nodeList):
+        for i in nodeList:
+            # Get all the collision nodes in the vis group
+            collNodePaths = i.findAllMatches("**/+CollisionNode")
+            numCollNodePaths = collNodePaths.getNumPaths()
+            visGroupName = i.node().getName()
+            for j in range(numCollNodePaths):
+                collNodePath = collNodePaths.getPath(j)
+                bitMask = collNodePath.node().getIntoCollideMask()
+                if bitMask.getBit(1):
+                    # Bit 1 is the floor collision bit. This renames
+                    # all floor collision polys to the same name as their
+                    # visgroup.
+                    collNodePath.node().setName(visGroupName)
+
+    def hideAllVisibles(self):
+        for i in self.nodeList:
+            i.hide()
+
+    def showAllVisibles(self):
+        for i in self.nodeList:
+            i.show()
+
+    def visibilityOn(self):
+        self.visibilityOff()
+        # Accept event
+        self.accept("on-floor", self.enterZone)
+        # Add collider
+        self.cTrav.addCollider(self.cRayNode, self.lifter)
+        # Reset lifter
+        self.lifter.clear()
+        # Reset flag
+        self.fVisInit = 1
+
+    def visibilityOff(self):
+        self.ignore("on-floor")
+        self.cTrav.removeCollider(self.cRayNode)
+        self.showAllVisibles()
+
+    def toggleVisibility(self):
+        if self.panel.fVis.get():
+            self.visibilityOn()
+            self.traversalOn()
+        else:
+            self.visibilityOff()
+            if (not (self.panel.fColl.get() or self.panel.fVis.get())):
+                self.traversalOff()
 
 
+    def enterZone(self, newZone):
+        """
+        Puts the toon in the indicated zone.  newZone may either be a
+        CollisionEntry object as determined by a floor polygon, or an
+        integer zone id.  It may also be None, to indicate no zone.
+        """
+        # First entry into a zone, hide everything
+        if self.fVisInit:
+            self.hideAllVisibles()
+            self.fVisInit = 0
+        # Get zone id
+        if isinstance(newZone, CollisionEntry):
+            # Get the name of the collide node
+            newZoneId = int(newZone.getIntoNode().getName())
+        else:
+            newZoneId = newZone
+        # Ensure we have vis data
+        assert(self.nodeDict)
+        # Hide the old zone (if there is one)
+        if self.__zoneId != None:
+            for i in self.nodeDict[self.__zoneId]:
+                i.hide()
+        # Show the new zone
+        if newZoneId != None:
+            for i in self.nodeDict[newZoneId]:
+                i.show()
+        # Make sure we changed zones
+        if newZoneId != self.__zoneId:
+            if visualizeZones:
+                # Set a color override on our zone to make it obvious what
+                # zone we're in.
+                if self.__zoneId != None:
+                    self.zoneDict[self.__zoneId].clearColor()
+                if newZoneId != None:
+                    self.zoneDict[newZoneId].setColor(0, 0, 1, 1, 100)
+            # The new zone is now old
+            self.__zoneId = newZoneId        
+        
     def enableMouse(self):
     def enableMouse(self):
         """ Enable Pie Menu interaction (and disable player camera control) """
         """ Enable Pie Menu interaction (and disable player camera control) """
         # Turn off player camera control
         # Turn off player camera control
@@ -4463,6 +4712,13 @@ class LevelEditorPanel(Pmw.MegaToplevel):
         buttonFrame4 = Frame(hull)
         buttonFrame4 = Frame(hull)
         self.driveMode = IntVar()
         self.driveMode = IntVar()
         self.driveMode.set(1)
         self.driveMode.set(1)
+        self.directModeButton = Radiobutton(
+            buttonFrame4,
+            text = 'DIRECT Fly',
+            value = 1,
+            variable = self.driveMode,
+            command = self.levelEditor.useDirectFly)
+        self.directModeButton.pack(side = LEFT, fill = X, expand = 1)
         self.driveModeButton = Radiobutton(
         self.driveModeButton = Radiobutton(
             buttonFrame4,
             buttonFrame4,
             text = 'Drive Mode',
             text = 'Drive Mode',
@@ -4470,14 +4726,28 @@ class LevelEditorPanel(Pmw.MegaToplevel):
             variable = self.driveMode,
             variable = self.driveMode,
             command = self.levelEditor.useDriveMode)
             command = self.levelEditor.useDriveMode)
         self.driveModeButton.pack(side = LEFT, fill = X, expand = 1)
         self.driveModeButton.pack(side = LEFT, fill = X, expand = 1)
-        self.directModeButton = Radiobutton(
+        
+        self.fColl = IntVar()
+        self.fColl.set(1)
+        direct.collButton = Checkbutton(
             buttonFrame4,
             buttonFrame4,
-            text = 'DIRECT Fly',
-            value = 1,
-            variable = self.driveMode,
-            command = self.levelEditor.useDirectFly)
-        self.directModeButton.pack(side = LEFT, fill = X, expand = 1)
-        buttonFrame4.pack(fill = X, padx = 60)
+            text = 'Collide',
+            width = 6,
+            variable = self.fColl,
+            command = self.levelEditor.toggleCollisions)
+        direct.collButton.pack(side = LEFT, expand = 1, fill = X)
+
+        self.fVis = IntVar()
+        self.fVis.set(1)
+        direct.visButton = Checkbutton(
+            buttonFrame4,
+            text = 'Visibility',
+            width = 6,
+            variable = self.fVis,
+            command = self.levelEditor.toggleVisibility)
+        direct.visButton.pack(side = LEFT, expand = 1, fill = X)
+
+        buttonFrame4.pack(fill = X, padx = 15)
         
         
         # Make sure input variables processed 
         # Make sure input variables processed 
         self.initialiseoptions(LevelEditorPanel)
         self.initialiseoptions(LevelEditorPanel)