|
@@ -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)
|