def appendWord(self,word,tm = None, fadein = 0, fadeinType = 0): if word == '\n': self.startLine() return textMaker = tm or self.textMaker if not self.lines: self.startLine() active_line = self.lines[-1] unicodeText = isinstance(word, types.UnicodeType) if unicodeText: textMaker.setWtext(word) else: textMaker.setText(word) width = textMaker.getWidth() height = textMaker.getHeight() node = textMaker.generate() textpath = NodePath('text_path') textpath.attachNewNode(node) if self.wordwrap: if active_line.getTotalWidth() + width > self.wordwrap: self.startLine() active_line = self.lines[-1] active_line.append(textpath, width, height,self.spacing, fadein = fadein, fadeinType = fadeinType) active_line.setPos(0,0,-(self.currentHeight + active_line.getLineHeight()) )
def __build_Model(self, planet, model_type, rec): model_np = NodePath("model_{}".format(rec)) # Basic terrain model. ter_model, pts = self.__get_Sphere_Model(model_type, rec, planet.radius, "terrain") ter_model.NP.reparentTo(model_np) # Map planet topography. if "height_map" in planet.__dict__ and model_type in ("mid", "high"): self.__map_Topography(planet, ter_model, pts) # Map planet colours for low type models only. if model_type == "low" and "colour_map" in planet.__dict__: self.__map_Colours(planet, ter_model, rec, pts) # Atmosphere. if "atmos_ceiling" in planet.__dict__: a_radius = planet.radius + planet.atmos_ceiling am_type = model_type if model_type != "high" else "mid" atmos_model, a_pts = self.__get_Sphere_Model(am_type, min(rec,7), a_radius, "atmos") atmos_model.NP.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise)) atmos_model.NP.setAttrib(TransparencyAttrib.make(TransparencyAttrib.MAlpha)) atmos_model.NP.reparentTo(model_np) model_np.attachNewNode("planet_label") return model_np
class TestApplication: def __init__(self): self.setupPhysics() self.clock_ = ClockObject() def sim(self): while True: try: time.sleep(LOOP_DELAY) self.clock_.tick() self.physics_world_.doPhysics(self.clock_.getDt(), 5, 1.0/180.0) # printing location of first box print "Box 0: %s"%(str(self.boxes_[0].getPos())) except KeyboardInterrupt: print "Simulation finished" sys.exit() def setupPhysics(self): # setting up physics world and parent node path self.physics_world_ = BulletWorld() self.world_node_ = NodePath() self.physics_world_.setGravity(Vec3(0, 0, -9.81)) # setting up ground self.ground_ = self.world_node_.attachNewNode(BulletRigidBodyNode('Ground')) self.ground_.node().addShape(BulletPlaneShape(Vec3(0, 0, 1), 0)) self.ground_.setPos(0,0,0) self.ground_.setCollideMask(BitMask32.allOn()) self.physics_world_.attachRigidBody(self.ground_.node()) self.boxes_ = [] num_boxes = 20 side_length = 0.2 size = Vec3(side_length,side_length,side_length) start_pos = Vec3(-num_boxes*side_length,0,10) for i in range(0,20): self.addBox("name %i"%(i),size,start_pos + Vec3(i*2*side_length,0,0)) def addBox(self,name,size,pos): # Box (dynamic) box = self.world_node_.attachNewNode(BulletRigidBodyNode(name)) box.node().setMass(1.0) box.node().addShape(BulletBoxShape(size)) box.setPos(pos) box.setCollideMask(BitMask32.allOn()) self.physics_world_.attachRigidBody(box.node()) self.boxes_.append(box)
class Army(GameObject): def __init__(self,player,scale,x,y): self.my_id = base.army_count base.army_count += 1 self.name = "Army"+str(self.my_id) self.player = player self.scale = scale self.type = "army" self.node_path = NodePath("army"+str(self.my_id)+"_node_path") self.node_path.setPos(x,y,0) self.node_path.setTag("player","p"+str(player)) self.node_path.setScale(self.scale,self.scale,self.scale) self.model_list = ["../models/infantry_counter_grey.egg","../models/infantry_counter_red.egg","../models/infantry_counter_green.egg"] self.model = loader.loadModel(self.model_list[player]) self.model.reparentTo(self.node_path) self.node_col = self.node_path.attachNewNode(CollisionNode("tower"+str(self.my_id)+"_c_node")) self.node_col.setScale((2,2,1)) self.node_col.setPos(0,0,0) self.node_col.node().addSolid(CollisionSphere(0,0,0,1)) self.node_col.setTag("type","army") base.cTrav.addCollider(self.node_col,base.col_manager.col_handler) self.node_path.reparentTo(render) self.selected = False def change_player(self,player): self.model.remove() self.player = player self.model = loader.loadModel(self.model_list[player])
def drawBlocks(self): max_x = self.world.getMaxSize() print "max_x:", max_x, "nodes: ", for tmp_x in range(0, max_x - 1): tmp = self.world.blocks[tmp_x] x = tmp[0] y = tmp[1] z = tmp[2] tmpModel = self.loader.loadModel("models/box") newModel = NodePath("model") tmpModel.getChildren().reparentTo(newModel) newModel.reparentTo(self.render) newModel.setScale(1, 1, 1) newModel.setPos(x, y, z) self.render.flattenStrong() Node = NodePath(PandaNode("PhysicsNode")) Node.reparentTo(render) jetpackGuy = loader.loadModel("models/box") jetpackGuy.reparentTo(render) an = ActorNode("jetpack-guy-physics") anp = Node.attachNewNode(an) base.attachPhysicalNode(an) jetpackGuy.reparentTo(anp) self.render.analyze()
def renderQuadInto(self, xsize, ysize, colortex=None, cmode = GraphicsOutput.RTMBindOrCopy, auxtex = None): buffer = self.createBuffer("filter-stage", xsize, ysize, colortex, cmode, auxtex) if (buffer == None): return None cm = CardMaker("filter-stage-quad") cm.setFrameFullscreenQuad() quad = NodePath(cm.generate()) quad.setDepthTest(0) quad.setDepthWrite(0) quad.setColor(Vec4(1,0.5,0.5,1)) quadcamnode = Camera("filter-quad-cam") lens = OrthographicLens() lens.setFilmSize(2, 2) lens.setFilmOffset(0, 0) lens.setNearFar(-1000, 1000) quadcamnode.setLens(lens) quadcam = quad.attachNewNode(quadcamnode) buffer.getDisplayRegion(0).setCamera(quadcam) buffer.getDisplayRegion(0).setActive(1) return quad, buffer
def _updateDebugNode(self): """ Internal method to generate new debug geometry. """ mainNode = NodePath("DebugNodeInner") mainNode.setPos(self.position) lineNode = mainNode.attachNewNode("lines") inner = Globals.loader.loadModel("box") inner.setPos(-0.5, -0.5, 0.6) inner.flattenStrong() inner.reparentTo(mainNode) # Generate outer circles points1 = [] points2 = [] points3 = [] for i in range(self.visualizationNumSteps + 1): angle = float( i) / float(self.visualizationNumSteps) * math.pi * 2.0 points1.append(Vec3(0, math.sin(angle), math.cos(angle))) points2.append(Vec3(math.sin(angle), math.cos(angle), 0)) points3.append(Vec3(math.sin(angle), 0, math.cos(angle))) self._createDebugLine(points1, False).reparentTo(lineNode) self._createDebugLine(points2, False).reparentTo(lineNode) self._createDebugLine(points3, False).reparentTo(lineNode) lineNode.setScale(self.radius) mainNode.flattenStrong() self.debugNode.node().removeAllChildren() mainNode.reparentTo(self.debugNode)
def empty(prefix, points=False): path = NodePath(prefix + '_path') node = GeomNode(prefix + '_node') path.attachNewNode(node) gvd = GeomVertexData('gvd', GeomVertexFormat.getV3n3t2(), Geom.UHStatic) geom = Geom(gvd) gvw = GeomVertexWriter(gvd, b'vertex') gnw = GeomVertexWriter(gvd, b'normal') gtw = GeomVertexWriter(gvd, b'texcoord') node.addGeom(geom) if points: prim = GeomPoints(Geom.UHStatic) else: prim = GeomTriangles(Geom.UHStatic) return (gvw, gnw, gtw, prim, geom, path)
class TerrainPhysicsDemo2(): """Modified from ODE demo and Bullet demo.""" def __init__(self, world, spawnNP): self.running = False self.world = world rbc = RigidBodyCombiner("rbc") self.rbcnp = NodePath(rbc) self.rbcnp.reparentTo(render) self.NrObjectToDrop = 10 self.spawnNP = spawnNP self.objects = [] self.model = loader.loadModel('models/box.egg') self.model.flattenLight() self.shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5)) def start(self): """Drop Rectangles""" # add objects one by one if self.running: return self.running = True self.newObjects = 0 taskMgr.doMethodLater(0.5, self.runTask, "myDemo") def runTask(self, task): if self.demoContinue(): return task.done return task.again def demoContinue(self): if self.newObjects < self.NrObjectToDrop: node = BulletRigidBodyNode('Box') node.setMass(1.0) node.addShape(self.shape) np = self.rbcnp.attachNewNode(node) np.setPos(self.spawnNP.getPos(render)) np.setHpr(randint(-45, 45), randint(-45, 45), randint(-45, 45)) self.world.attachRigidBody(node) bNP = self.model.copyTo(np) #bNP.setPos(self.spawnNP.getPos()) #bNP.setColor(random(), random(), random(), 1) #bNP.setHpr(randint(-45, 45), randint(-45, 45), randint(-45, 45)) #self.setUntextured(bNP) #bNP.setTexureOff() #np.setScale(10) np.flattenStrong() self.objects.append(np) self.newObjects += 1 self.rbcnp.node().collect() if self.newObjects < self.NrObjectToDrop: return False else: self.running = False return True
def make_lights(): """Create one point light and an ambient light. Return: (NodePath): Lights nodepath. """ lights = NodePath("lights") # Create point lights. plight = PointLight("plight1") light = lights.attachNewNode(plight) light.setPos((3, -10, 2)) light.lookAt(0, 0, 0) # Create ambient light. alight = AmbientLight("alight") alight.setColor((0.75, 0.75, 0.75, 1.0)) lights.attachNewNode(alight) return lights
def createPlane(width,height): format=GeomVertexFormat.getV3() vdata=GeomVertexData("vertices", format, Geom.UHStatic) vertexWriter=GeomVertexWriter(vdata, "vertex") vertexWriter.addData3f(0,0,0) vertexWriter.addData3f(width,0,0) vertexWriter.addData3f(width,height,0) vertexWriter.addData3f(0,height,0) #step 2) make primitives and assign vertices to them tris=GeomTriangles(Geom.UHStatic) #have to add vertices one by one since they are not in order tris.addVertex(0) tris.addVertex(1) tris.addVertex(3) #indicates that we have finished adding vertices for the first triangle. tris.closePrimitive() #since the coordinates are in order we can use this convenience function. tris.addConsecutiveVertices(1,3) #add vertex 1, 2 and 3 tris.closePrimitive() #step 3) make a Geom object to hold the primitives squareGeom=Geom(vdata) squareGeom.addPrimitive(tris) #now put squareGeom in a GeomNode. You can now position your geometry in the scene graph. squareGN=GeomNode("square") squareGN.addGeom(squareGeom) terrainNode = NodePath("terrNode") terrainNode.reparentTo(render) terrainNode.attachNewNode(squareGN) terrainNode.setX(-width/2) texGrass = loader.loadTexture("textures/envir-ground.jpg") terrainNode.setTexture(texGrass)
def loadTSX(filename): builtTiles = [] animatedTiles = [] folder, file = path.split(filename) ts = untangle.parse(filename).tileset size = int(ts["tilewidth"]), int(ts["tileheight"]) cards = spriteSheetToCards(folder + "/" + ts.image['source'], size) for i in range(int(ts["tilecount"])): try: tile = ts.tile[i] animationData = tile.animation animatedTiles.append(tile) except (AttributeError, IndexError): tile = None if tile == None: tile = {"id":str(i), type:"none"} builtTiles.append([cards[i], tile]) for tile in animatedTiles: animation = SequenceNode("animation") animationNode = NodePath("animationNode") for frame in tile.animation.frame: frameNode = builtTiles[int(frame["tileid"])][0].node() animation.addChild(frameNode) frameDuration = int(tile.animation.frame[0]["duration"]) if frameDuration == 9999: fps = 0 else: fps = 1000/frameDuration animation.setFrameRate(fps) animation.loop(True) animationNode.attachNewNode(animation) builtTiles[int(tile["id"])][0] = animationNode return builtTiles
def renderQuadInto(self, mul=1, div=1, align=1, depthtex=None, colortex=None, auxtex0=None, auxtex1=None): """ Creates an offscreen buffer for an intermediate computation. Installs a quad into the buffer. Returns the fullscreen quad. The size of the buffer is initially equal to the size of the main window. The parameters 'mul', 'div', and 'align' can be used to adjust that size. """ texgroup = (depthtex, colortex, auxtex0, auxtex1) winx, winy = self.getScaledSize(mul, div, align) depthbits = bool(depthtex != None) buffer = self.createBuffer("filter-stage", winx, winy, texgroup, depthbits) if (buffer == None): return None cm = CardMaker("filter-stage-quad") cm.setFrameFullscreenQuad() quad = NodePath(cm.generate()) quad.setDepthTest(0) quad.setDepthWrite(0) quad.setColor(1, 0.5, 0.5, 1) quadcamnode = Camera("filter-quad-cam") lens = OrthographicLens() lens.setFilmSize(2, 2) lens.setFilmOffset(0, 0) lens.setNearFar(-1000, 1000) quadcamnode.setLens(lens) quadcam = quad.attachNewNode(quadcamnode) dr = buffer.makeDisplayRegion((0, 1, 0, 1)) dr.disableClears() dr.setCamera(quadcam) dr.setActive(True) dr.setScissorEnabled(False) # This clear stage is important if the buffer is padded, so that # any pixels accidentally sampled in the padded region won't # be reading from unititialised memory. buffer.setClearColor((0, 0, 0, 1)) buffer.setClearColorActive(True) self.buffers.append(buffer) self.sizes.append((mul, div, align)) return quad
def spriteSheetToCards(filename, tileSize): texture = loader.loadTexture( filename, minfilter=SamplerState.FT_nearest, magfilter=SamplerState.FT_nearest) cards = [] columns = int(texture.getXSize()/tileSize[0]) rows = int(texture.getYSize()/tileSize[1]) uvWidth = 1/columns uvHeight = 1/rows for y in range(rows): for x in range(columns): part = NodePath(filename + "_tile_" + str(x)+"_"+str(y)) part.attachNewNode(cardmaker.generate()) part.setTexture(textureStage, texture) part.setTexScale(textureStage, uvWidth, uvHeight) ox, oy = uvWidth*x, (1-uvHeight)-(uvHeight*y) part.setTexOffset(textureStage, ox, oy) part.setTransparency(5, 1) part.setHpr(0,-90,0) cards.append(part) return cards
class DistributedGardenBox(DistributedLawnDecor.DistributedLawnDecor): notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGardenPlot') def __init__(self, cr): DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) self.plantPath = NodePath('plantPath') self.plantPath.reparentTo(self) self.plotScale = 1.0 self.plantingGuiDoneEvent = 'plantingGuiDone' self.defaultModel = 'phase_5.5/models/estate/planterC' def announceGenerate(self): self.notify.debug('announceGenerate') DistributedLawnDecor.DistributedLawnDecor.announceGenerate(self) def doModelSetup(self): if self.typeIndex == GardenGlobals.BOX_THREE: self.defaultModel = 'phase_5.5/models/estate/planterA' elif self.typeIndex == GardenGlobals.BOX_TWO: self.defaultModel = 'phase_5.5/models/estate/planterC' else: self.defaultModel = 'phase_5.5/models/estate/planterD' self.collSphereOffset = 0.0 self.collSphereRadius = self.collSphereRadius * 1.41 self.plotScale = Vec3(1.0, 1.0, 1.0) def setupShadow(self): pass def loadModel(self): self.rotateNode = self.plantPath.attachNewNode('rotate') self.model = None self.model = loader.loadModel(self.defaultModel) self.model.setScale(self.plotScale) self.model.reparentTo(self.rotateNode) self.stick2Ground() return def handleEnterPlot(self, entry = None): pass def handleExitPlot(self, entry = None): DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) def setTypeIndex(self, typeIndex): self.typeIndex = typeIndex
def buildMap(tmx): rootNode = NodePath("root") for l, layer in enumerate(tmx.map.layer): layerNode = rootNode.attachNewNode(layer["name"]) unique = False if hasattr(layer, 'properties'): for property in layer.properties: unique_keys = ("unique", "seperate", "dynamic") if property.property["name"] in unique_keys: unique = True for y, row in enumerate(layer.data): for x, tile in enumerate(row): if not tile == 0: tileNode = tmx.tiles[tile-1][0] tileObject = tmx.tiles[tile-1][1] instance(layerNode, tileNode, pos=(x,-y,l), hpr=(0,0,0), unique=unique) if not unique: layerNode.flattenStrong() rootNode.setLightOff() return rootNode
def rebuildGeomNodesToColPolys (incomingNode,relativeTo=None): ''' Converts GeomNodes into CollisionPolys in a straight 1-to-1 conversion Returns a new NodePath containing the CollisionNodes ''' parent = NodePath('cGeomConversionParent') for c in incomingNode.findAllMatches('**/+GeomNode'): if relativeTo: xform=c.getMat(relativeTo).xformPoint else: xform=(c.getMat(incomingNode)*(incomingNode.getMat())).xformPoint gni = 0 geomNode = c.node() for g in range(geomNode.getNumGeoms()): geom = geomNode.getGeom(g).decompose() vdata = geom.getVertexData() vreader = GeomVertexReader(vdata, 'vertex') cChild = CollisionNode('cGeom-%s-gni%i' % (c.getName(), gni)) gni += 1 for p in range(geom.getNumPrimitives()): prim = geom.getPrimitive(p) for p2 in range(prim.getNumPrimitives()): s = prim.getPrimitiveStart(p2) e = prim.getPrimitiveEnd(p2) v = [] for vi in range (s, e): vreader.setRow(prim.getVertex(vi)) v.append (xform(vreader.getData3f())) colPoly = CollisionPolygon(*v) cChild.addSolid(colPoly) n=parent.attachNewNode (cChild) #n.show() return parent
def getBam(mesh, filename): scene_members = pandacore.getSceneMembers(mesh) rotateNode = GeomNode("rotater") rotatePath = NodePath(rotateNode) matrix = numpy.identity(4) if mesh.assetInfo.upaxis == collada.asset.UP_AXIS.X_UP: r = collada.scene.RotateTransform(0,1,0,90) matrix = r.matrix elif mesh.assetInfo.upaxis == collada.asset.UP_AXIS.Y_UP: r = collada.scene.RotateTransform(1,0,0,90) matrix = r.matrix rotatePath.setMat(Mat4(*matrix.T.flatten().tolist())) for geom, renderstate, mat4 in scene_members: node = GeomNode("primitive") node.addGeom(geom) if renderstate is not None: node.setGeomState(0, renderstate) geomPath = rotatePath.attachNewNode(node) geomPath.setMat(mat4) rotatePath.flattenStrong() wrappedNode = pandacore.centerAndScale(rotatePath) model_name = filename.replace('/', '_') wrappedNode.setName(model_name) bam_temp = tempfile.mktemp(suffix = model_name + '.bam') wrappedNode.writeBamFile(bam_temp) bam_f = open(bam_temp, 'rb') bam_data = bam_f.read() bam_f.close() os.remove(bam_temp) return bam_data
def rebuildGeomNodesToColPolys (incomingNode,relativeTo=None,filter=lambda n:True): ''' Converts GeomNodes into CollisionPolys in a straight 1-to-1 conversion Returns a new NodePath containing the CollisionNodes If the geometry is at all complex, running the result of this through colTree should improve performance. ''' parent = NodePath('cGeomConversionParent') for c in incomingNode.findAllMatches('**/+GeomNode'): if not filter(c): continue if relativeTo: xform=c.getMat(relativeTo).xformPoint else: xform=(c.getMat(incomingNode)*(incomingNode.getMat())).xformPoint geomNode = c.node() for g in range(geomNode.getNumGeoms()): geom = geomNode.getGeom(g).decompose() vdata = geom.getVertexData() vreader = GeomVertexReader(vdata, 'vertex') cChild = CollisionNode("") for p in range(geom.getNumPrimitives()): prim = geom.getPrimitive(p) for p2 in range(prim.getNumPrimitives()): s = prim.getPrimitiveStart(p2) e = prim.getPrimitiveEnd(p2) if e-s>2: v = [] for vi in range (s, e): vreader.setRow(prim.getVertex(vi)) v.append(Point3(xform(vreader.getData3f())) ) colPoly = CollisionPolygon(*v) cChild.addSolid(colPoly) n=parent.attachNewNode(cChild) return parent
def _updateDebugNode(self): """ Internal method to generate new debug geometry. """ debugNode = NodePath("PointLightDebugNode") debugNode.setPos(self.position) # Create the inner image cm = CardMaker("PointLightDebug") cm.setFrameFullscreenQuad() innerNode = NodePath(cm.generate()) innerNode.setTexture(Globals.loader.loadTexture("Data/GUI/Visualization/PointLight.png")) innerNode.setBillboardPointEye() innerNode.reparentTo(debugNode) # Create the outer lines lineNode = debugNode.attachNewNode("lines") # Generate outer circles points1 = [] points2 = [] points3 = [] for i in range(self.visualizationNumSteps + 1): angle = float( i) / float(self.visualizationNumSteps) * math.pi * 2.0 points1.append(Vec3(0, math.sin(angle), math.cos(angle))) points2.append(Vec3(math.sin(angle), math.cos(angle), 0)) points3.append(Vec3(math.sin(angle), 0, math.cos(angle))) self._createDebugLine(points1, False).reparentTo(lineNode) self._createDebugLine(points2, False).reparentTo(lineNode) self._createDebugLine(points3, False).reparentTo(lineNode) lineNode.setScale(self.radius) # Remove the old debug node self.debugNode.node().removeAllChildren() # Attach the new debug node debugNode.reparentTo(self.debugNode)
class FireflyDemo(ShowBase): def __init__(self): # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) self.setBackgroundColor((0, 0, 0, 0)) # Preliminary capabilities check. if not self.win.getGsg().getSupportsBasicShaders(): self.t = addTitle("Firefly Demo: Video driver reports that Cg " "shaders are not supported.") return if not self.win.getGsg().getSupportsDepthTexture(): self.t = addTitle("Firefly Demo: Video driver reports that depth " "textures are not supported.") return # This algorithm uses two offscreen buffers, one of which has # an auxiliary bitplane, and the offscreen buffers share a single # depth buffer. This is a heck of a complicated buffer setup. self.modelbuffer = self.makeFBO("model buffer", 1) self.lightbuffer = self.makeFBO("light buffer", 0) # Creation of a high-powered buffer can fail, if the graphics card # doesn't support the necessary OpenGL extensions. if self.modelbuffer is None or self.lightbuffer is None: self.t = addTitle("Toon Shader: Video driver does not support " "multiple render targets") return # Create four render textures: depth, normal, albedo, and final. # attach them to the various bitplanes of the offscreen buffers. self.texDepth = Texture() self.texDepth.setFormat(Texture.FDepthStencil) self.texAlbedo = Texture() self.texNormal = Texture() self.texFinal = Texture() self.modelbuffer.addRenderTexture(self.texDepth, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepthStencil) self.modelbuffer.addRenderTexture(self.texAlbedo, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) self.modelbuffer.addRenderTexture(self.texNormal, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0) self.lightbuffer.addRenderTexture(self.texFinal, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) # Set the near and far clipping planes. self.cam.node().getLens().setNear(50.0) self.cam.node().getLens().setFar(500.0) lens = self.cam.node().getLens() # This algorithm uses three cameras: one to render the models into the # model buffer, one to render the lights into the light buffer, and # one to render "plain" stuff (non-deferred shaded) stuff into the # light buffer. Each camera has a bitmask to identify it. self.modelMask = 1 self.lightMask = 2 self.plainMask = 4 self.modelcam = self.makeCamera(self.modelbuffer, lens=lens, scene=render, mask=self.modelMask) self.lightcam = self.makeCamera(self.lightbuffer, lens=lens, scene=render, mask=self.lightMask) self.plaincam = self.makeCamera(self.lightbuffer, lens=lens, scene=render, mask=self.plainMask) # Panda's main camera is not used. self.cam.node().setActive(0) # Take explicit control over the order in which the three # buffers are rendered. self.modelbuffer.setSort(1) self.lightbuffer.setSort(2) self.win.setSort(3) # Within the light buffer, control the order of the two cams. self.lightcam.node().getDisplayRegion(0).setSort(1) self.plaincam.node().getDisplayRegion(0).setSort(2) # By default, panda usually clears the screen before every # camera and before every window. Tell it not to do that. # Then, tell it specifically when to clear and what to clear. self.modelcam.node().getDisplayRegion(0).disableClears() self.lightcam.node().getDisplayRegion(0).disableClears() self.plaincam.node().getDisplayRegion(0).disableClears() self.cam.node().getDisplayRegion(0).disableClears() self.cam2d.node().getDisplayRegion(0).disableClears() self.modelbuffer.disableClears() self.win.disableClears() self.modelbuffer.setClearColorActive(1) self.modelbuffer.setClearDepthActive(1) self.lightbuffer.setClearColorActive(1) self.lightbuffer.setClearColor((0, 0, 0, 1)) # Miscellaneous stuff. self.disableMouse() self.camera.setPos(-9.112, -211.077, 46.951) self.camera.setHpr(0, -7.5, 2.4) random.seed() # Calculate the projection parameters for the final shader. # The math here is too complex to explain in an inline comment, # I've put in a full explanation into the HTML intro. proj = self.cam.node().getLens().getProjectionMat() proj_x = 0.5 * proj.getCell(3, 2) / proj.getCell(0, 0) proj_y = 0.5 * proj.getCell(3, 2) proj_z = 0.5 * proj.getCell(3, 2) / proj.getCell(2, 1) proj_w = -0.5 - 0.5 * proj.getCell(1, 2) # Configure the render state of the model camera. tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib( AlphaTestAttrib.make(RenderAttrib.MGreaterEqual, 0.5)) tempnode.setShader(loader.loadShader("model.sha")) tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MLessEqual)) self.modelcam.node().setInitialState(tempnode.getState()) # Configure the render state of the light camera. tempnode = NodePath(PandaNode("temp node")) tempnode.setShader(loader.loadShader("light.sha")) tempnode.setShaderInput("texnormal", self.texNormal) tempnode.setShaderInput("texalbedo", self.texAlbedo) tempnode.setShaderInput("texdepth", self.texDepth) tempnode.setShaderInput("proj", (proj_x, proj_y, proj_z, proj_w)) tempnode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOne)) tempnode.setAttrib( CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise)) # The next line causes problems on Linux. # tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MGreaterEqual)) tempnode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff)) self.lightcam.node().setInitialState(tempnode.getState()) # Configure the render state of the plain camera. rs = RenderState.makeEmpty() self.plaincam.node().setInitialState(rs) # Clear any render attribs on the root node. This is necessary # because by default, panda assigns some attribs to the root # node. These default attribs will override the # carefully-configured render attribs that we just attached # to the cameras. The simplest solution is to just clear # them all out. render.setState(RenderState.makeEmpty()) # My artist created a model in which some of the polygons # don't have textures. This confuses the shader I wrote. # This little hack guarantees that everything has a texture. white = loader.loadTexture("models/white.jpg") render.setTexture(white, 0) # Create two subroots, to help speed cull traversal. self.lightroot = NodePath(PandaNode("lightroot")) self.lightroot.reparentTo(render) self.modelroot = NodePath(PandaNode("modelroot")) self.modelroot.reparentTo(render) self.lightroot.hide(BitMask32(self.modelMask)) self.modelroot.hide(BitMask32(self.lightMask)) self.modelroot.hide(BitMask32(self.plainMask)) # Load the model of a forest. Make it visible to the model camera. # This is a big model, so we load it asynchronously while showing a # load text. We do this by passing in a callback function. self.loading = addTitle("Loading models...") self.forest = NodePath(PandaNode("Forest Root")) self.forest.reparentTo(render) self.forest.hide(BitMask32(self.lightMask | self.plainMask)) loader.loadModel([ "models/background", "models/foliage01", "models/foliage02", "models/foliage03", "models/foliage04", "models/foliage05", "models/foliage06", "models/foliage07", "models/foliage08", "models/foliage09"], callback=self.finishLoading) # Cause the final results to be rendered into the main window on a # card. self.card = self.lightbuffer.getTextureCard() self.card.setTexture(self.texFinal) self.card.reparentTo(render2d) # Panda contains a built-in viewer that lets you view the results of # your render-to-texture operations. This code configures the viewer. self.bufferViewer.setPosition("llcorner") self.bufferViewer.setCardSize(0, 0.40) self.bufferViewer.setLayout("vline") self.toggleCards() self.toggleCards() # Firefly parameters self.fireflies = [] self.sequences = [] self.scaleseqs = [] self.glowspheres = [] self.fireflysize = 1.0 self.spheremodel = loader.loadModel("misc/sphere") # Create the firefly model, a fuzzy dot dotSize = 1.0 cm = CardMaker("firefly") cm.setFrame(-dotSize, dotSize, -dotSize, dotSize) self.firefly = NodePath(cm.generate()) self.firefly.setTexture(loader.loadTexture("models/firefly.png")) self.firefly.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add, ColorBlendAttrib.O_incoming_alpha, ColorBlendAttrib.O_one)) # these allow you to change parameters in realtime self.accept("escape", sys.exit, [0]) self.accept("arrow_up", self.incFireflyCount, [1.1111111]) self.accept("arrow_down", self.decFireflyCount, [0.9000000]) self.accept("arrow_right", self.setFireflySize, [1.1111111]) self.accept("arrow_left", self.setFireflySize, [0.9000000]) self.accept("v", self.toggleCards) self.accept("V", self.toggleCards) def finishLoading(self, models): # This function is used as callback to loader.loadModel, and called # when all of the models have finished loading. # Attach the models to the scene graph. for model in models: model.reparentTo(self.forest) # Show the instructions. self.loading.destroy() self.title = addTitle("Panda3D: Tutorial - Fireflies using Deferred Shading") self.inst1 = addInstructions(0.06, "ESC: Quit") self.inst2 = addInstructions(0.12, "Up/Down: More / Fewer Fireflies (Count: unknown)") self.inst3 = addInstructions(0.18, "Right/Left: Bigger / Smaller Fireflies (Radius: unknown)") self.inst4 = addInstructions(0.24, "V: View the render-to-texture results") self.setFireflySize(25.0) while len(self.fireflies) < 5: self.addFirefly() self.updateReadout() self.nextadd = 0 taskMgr.add(self.spawnTask, "spawner") def makeFBO(self, name, auxrgba): # This routine creates an offscreen buffer. All the complicated # parameters are basically demanding capabilities from the offscreen # buffer - we demand that it be able to render to texture on every # bitplane, that it can support aux bitplanes, that it track # the size of the host window, that it can render to texture # cumulatively, and so forth. winprops = WindowProperties() props = FrameBufferProperties() props.setRgbColor(True) props.setRgbaBits(8, 8, 8, 8) props.setDepthBits(1) props.setAuxRgba(auxrgba) return self.graphicsEngine.makeOutput( self.pipe, "model buffer", -2, props, winprops, GraphicsPipe.BFSizeTrackHost | GraphicsPipe.BFCanBindEvery | GraphicsPipe.BFRttCumulative | GraphicsPipe.BFRefuseWindow, self.win.getGsg(), self.win) def addFirefly(self): pos1 = LPoint3(random.uniform(-50, 50), random.uniform(-100, 150), random.uniform(-10, 80)) dir = LVector3(random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)) dir.normalize() pos2 = pos1 + (dir * 20) fly = self.lightroot.attachNewNode(PandaNode("fly")) glow = fly.attachNewNode(PandaNode("glow")) dot = fly.attachNewNode(PandaNode("dot")) color_r = 1.0 color_g = random.uniform(0.8, 1.0) color_b = min(color_g, random.uniform(0.5, 1.0)) fly.setColor(color_r, color_g, color_b, 1.0) fly.setShaderInput("lightcolor", color_r, color_g, color_b, 1.0) int1 = fly.posInterval(random.uniform(7, 12), pos1, pos2) int2 = fly.posInterval(random.uniform(7, 12), pos2, pos1) si1 = fly.scaleInterval(random.uniform(0.8, 1.5), LPoint3(0.2, 0.2, 0.2), LPoint3(0.2, 0.2, 0.2)) si2 = fly.scaleInterval(random.uniform(1.5, 0.8), LPoint3(1.0, 1.0, 1.0), LPoint3(0.2, 0.2, 0.2)) si3 = fly.scaleInterval(random.uniform(1.0, 2.0), LPoint3(0.2, 0.2, 0.2), LPoint3(1.0, 1.0, 1.0)) siseq = Sequence(si1, si2, si3) siseq.loop() siseq.setT(random.uniform(0, 1000)) seq = Sequence(int1, int2) seq.loop() self.spheremodel.instanceTo(glow) self.firefly.instanceTo(dot) glow.setScale(self.fireflysize * 1.1) glow.hide(BitMask32(self.modelMask | self.plainMask)) dot.hide(BitMask32(self.modelMask | self.lightMask)) dot.setColor(color_r, color_g, color_b, 1.0) self.fireflies.append(fly) self.sequences.append(seq) self.glowspheres.append(glow) self.scaleseqs.append(siseq) def updateReadout(self): self.inst2.destroy() self.inst2 = addInstructions(0.12, "Up/Down: More / Fewer Fireflies (Currently: %d)" % len(self.fireflies)) self.inst3.destroy() self.inst3 = addInstructions(0.18, "Right/Left: Bigger / Smaller Fireflies (Radius: %d ft)" % self.fireflysize) def toggleCards(self): self.bufferViewer.toggleEnable() # When the cards are not visible, I also disable the color clear. # This color-clear is actually not necessary, the depth-clear is # sufficient for the purposes of the algorithm. if (self.bufferViewer.isEnabled()): self.modelbuffer.setClearColorActive(True) else: self.modelbuffer.setClearColorActive(False) def incFireflyCount(self, scale): n = int((len(self.fireflies) * scale) + 1) while (n > len(self.fireflies)): self.addFirefly() self.updateReadout() def decFireflyCount(self, scale): n = int(len(self.fireflies) * scale) if (n < 1): n = 1 while (len(self.fireflies) > n): self.glowspheres.pop() self.sequences.pop().finish() self.scaleseqs.pop().finish() self.fireflies.pop().removeNode() self.updateReadout() def setFireflySize(self, n): n = n * self.fireflysize self.fireflysize = n for x in self.glowspheres: x.setScale(self.fireflysize * 1.1) self.updateReadout() def spawnTask(self, task): if task.time > self.nextadd: self.nextadd = task.time + 1.0 if (len(self.fireflies) < 300): self.incFireflyCount(1.03) return Task.cont
def _updateDebugNode(self): """ Internal method to generate new debug geometry. """ debugNode = NodePath("SpotLightDebugNode") # Create the inner image cm = CardMaker("SpotLightDebug") cm.setFrameFullscreenQuad() innerNode = NodePath(cm.generate()) innerNode.setTexture(Globals.loader.loadTexture("Data/GUI/Visualization/SpotLight.png")) innerNode.setBillboardPointEye() innerNode.reparentTo(debugNode) innerNode.setPos(self.position) innerNode.setColorScale(1,1,0,1) # Create the outer lines lineNode = debugNode.attachNewNode("lines") currentNodeTransform = render.getTransform(self.ghostCameraNode).getMat() currentCamTransform = self.ghostLens.getProjectionMat() currentRelativeCamPos = self.ghostCameraNode.getPos(render) currentCamBounds = self.ghostLens.makeBounds() currentCamBounds.xform(self.ghostCameraNode.getMat(render)) p = lambda index: currentCamBounds.getPoint(index) # Make a circle at the bottom frustumBottomCenter = (p(0) + p(1) + p(2) + p(3)) * 0.25 upVector = (p(0) + p(1)) / 2 - frustumBottomCenter rightVector = (p(1) + p(2)) / 2 - frustumBottomCenter points = [] for idx in xrange(64): rad = idx / 64.0 * math.pi * 2.0 pos = upVector * math.sin(rad) + rightVector * math.cos(rad) pos += frustumBottomCenter points.append(pos) frustumLine = self._createDebugLine(points, True) frustumLine.setColorScale(1,1,0,1) frustumLine.reparentTo(lineNode) # Create frustum lines which connect the origin to the bottom circle pointArrays = [ [self.position, frustumBottomCenter + upVector], [self.position, frustumBottomCenter - upVector], [self.position, frustumBottomCenter + rightVector], [self.position, frustumBottomCenter - rightVector], ] for pointArray in pointArrays: frustumLine = self._createDebugLine(pointArray, False) frustumLine.setColorScale(1,1,0,1) frustumLine.reparentTo(lineNode) # Create line which is in the direction of the spot light startPoint = (p(0) + p(1) + p(2) + p(3)) * 0.25 endPoint = (p(4) + p(5) + p(6) + p(7)) * 0.25 line = self._createDebugLine([startPoint, endPoint], False) line.setColorScale(1,1,1,1) line.reparentTo(lineNode) # Remove the old debug node self.debugNode.node().removeAllChildren() # Attach the new debug node debugNode.reparentTo(self.debugNode)
def treeMe(parent, positions, uuids, geomCollide, center = None, side = None, radius = None, request_hash = b'Fake', pipe = None): """ Divide the space covered by all the objects into an oct tree and then replace cubes with 512 objects with spheres radius = (side**2 / 2)**.5 for some reason this massively improves performance even w/o the code for mouse over adding and removing subsets of nodes. """ num_points = len(positions) if not num_points: return None if type(center) == type(None): # must use equality due to id changing across interpreters center = np.mean(positions, axis=0) norms = np.linalg.norm(positions - center, axis = 1) radius = np.max(norms) * .75 # align points to center of oct node side = ((4/3) * radius**2) ** .5 if parent == None: l2Node = NodePath(CollisionNode('ObjectRoot for %s 0'%request_hash)) # TODO naming for search else: l2Node = parent.attachNewNode(CollisionNode('ObjectRoot for %s 0'%request_hash)) else: #l2Node = parent.attachNewNode(CollisionNode('%s.%s. %s'%(request_hash, center, int(parent.getName()[-2:]) + 1))) l2Node = parent.attachNewNode(CollisionNode(' %s'%(int(parent.getName()[-2:]) + 1))) #base_mask = np.zeros_like(uuids, dtype=np.bool_) # FIXME INSANE INTERACTION WITH SOMETHING panda related bitmasks = [] for _ in range(8): bitmasks.append(np.array([False] * len(uuids))) # must pass by value otherwise we have 8 of the same thing #bitmasks = [ np.zeros_like(uuids,dtype=np.bool_) for _ in range(8) ] # ICK there must be a better way of creating bitmasks partition = positions > center #the 8 conbinatorial cases for i in range(num_points): index = octit(partition[i]) bitmasks[index][i] = True next_leaves = [] for i in range(8): branch = bitmasks[i] new_center = center + TREE_LOGIC[i] * side * .5 #FIXME we pay a price here when we calculate the center of an empty node subSet = positions[branch] if len(subSet): next_leaves.append((l2Node, subSet, uuids[branch], geomCollide[branch], new_center, side * .5, radius * .5, request_hash)) #This method can also greatly accelerate the neighbor traversal because it reduces the total number of nodes needed if num_points < TREE_MAX_POINTS: leaf_max = np.max([len(leaf[1]) for leaf in next_leaves]) max_leaf = next_leaves[np.where([len(leaf[1]) for leaf in next_leaves] == leaf_max)[0][:1][0]] ml_center = np.mean(max_leaf[1],axis=0) if num_points < 4: c = np.mean(positions, axis=0) dists = [] for p1 in positions: for p2 in positions: if p1 is not p2: d = np.linalg.norm(np.array(p2) - np.array(p1)) dists.append(d) r = np.max(dists) + np.mean(geomCollide) * 2 #max dists is the diameter so this is safe #l2Node.setName('leaf %s.%s. %s'%(request_hash, c, int(parent.getName()[-2:]) + 1)) l2Node.node().addSolid(CollisionSphere(c[0],c[1],c[2],r)) l2Node.node().setIntoCollideMask(BitMask32.bit(BITMASK_COLL_MOUSE)) elif leaf_max > num_points * .90 and np.linalg.norm(ml_center - center) < radius * 2: # FIXME something is VERY wrong, we should NEVER be generating cases where there are leaves outside the radius!! for leaf in next_leaves: treeMe(*leaf) #[treeMe(*leaf) for leaf in next_leaves] #l2Node.setName('branch '+l2Node.getName()) l2Node.node().addSolid(CollisionSphere(center[0],center[1],center[2],radius * 2)) l2Node.node().setIntoCollideMask(BitMask32.bit(BITMASK_COLL_MOUSE)) # this does not collide if pipe: # extremely unlikely edge case print("hit an early pip") pipe.send(l2Node) pipe.close() return None else: return l2Node # just for kicks even though all this is in place else: #l2Node.setName('leaf '+l2Node.getName()) l2Node.node().addSolid(CollisionSphere(center[0],center[1],center[2],radius * 2)) l2Node.node().setIntoCollideMask(BitMask32.bit(BITMASK_COLL_MOUSE)) for p,uuid,geom in zip(positions,uuids,geomCollide): childNode = l2Node.attachNewNode(CollisionNode("%s"%uuid)) #XXX TODO childNode.node().addSolid(CollisionSphere(p[0],p[1],p[2],geom)) # we do it this way because it keeps framerates WAY higher dont know why childNode.node().setIntoCollideMask(BitMask32.bit(BITMASK_COLL_CLICK)) childNode.setTag('uuid',uuid) return l2Node else: # we are a containing node #l2Node.setName('branch '+l2Node.getName()) l2Node.node().addSolid(CollisionSphere(center[0],center[1],center[2],radius * 2)) l2Node.node().setIntoCollideMask(BitMask32.bit(BITMASK_COLL_MOUSE)) # this does not collide for leaf in next_leaves: treeMe(*leaf) #[treeMe(*leaf) for leaf in next_leaves] if pipe: pipe.send(l2Node) # TODO we are going to solve this by sending childs? no, NODE GRRR #for c in l2Node.getChildren(): #pipe.send(c) #l2Node.removeChild(c) #l2Node.removeAllChildren() #pipe.send(l2Node) #for s in to_send: #pipe.send(s) pipe.close() else: #embed() return l2Node # just for kicks even though all this is in place
def setupPhysics(self): # setting up physics world and parent node path self.physics_world_ = BulletWorld() self.world_node_ = self.render.attachNewNode('world') self.cam.reparentTo(self.world_node_) self.physics_world_.setGravity(Vec3(0, 0, -9.81)) self.debug_node_ = self.world_node_.attachNewNode(BulletDebugNode('Debug')) self.debug_node_.node().showWireframe(True) self.debug_node_.node().showConstraints(True) self.debug_node_.node().showBoundingBoxes(True) self.debug_node_.node().showNormals(True) self.physics_world_.setDebugNode(self.debug_node_.node()) self.debug_node_.hide() # setting up collision groups self.physics_world_.setGroupCollisionFlag(GAME_OBJECT_BITMASK.getLowestOnBit(),GAME_OBJECT_BITMASK.getLowestOnBit(),True) self.physics_world_.setGroupCollisionFlag(SECTOR_ENTERED_BITMASK.getLowestOnBit(),SECTOR_ENTERED_BITMASK.getLowestOnBit(),True) self.physics_world_.setGroupCollisionFlag(GAME_OBJECT_BITMASK.getLowestOnBit(),SECTOR_ENTERED_BITMASK.getLowestOnBit(),False) # setting up ground self.ground_ = self.world_node_.attachNewNode(BulletRigidBodyNode('Ground')) self.ground_.node().addShape(BulletPlaneShape(Vec3(0, 0, 1), 0)) self.ground_.setPos(0,0,0) self.ground_.setCollideMask(GAME_OBJECT_BITMASK) self.physics_world_.attachRigidBody(self.ground_.node()) # creating level objects self.setupLevelSectors() # creating controlled objects diameter = 0.4 sphere_visual = loader.loadModel('models/ball.egg') bounds = sphere_visual.getTightBounds() # start of model scaling extents = Vec3(bounds[1] - bounds[0]) scale_factor = diameter/max([extents.getX(),extents.getY(),extents.getZ()]) sphere_visual.clearModelNodes() sphere_visual.setScale(scale_factor,scale_factor,scale_factor) # end of model scaling sphere_visual.setTexture(loader.loadTexture('models/bowl.jpg'),1) sphere = NodePath(BulletRigidBodyNode('Sphere')) sphere.node().addShape(BulletSphereShape(0.5*diameter)) sphere.node().setMass(1.0) #sphere.node().setLinearFactor((1,0,1)) #sphere.node().setAngularFactor((0,1,0)) sphere.setCollideMask(GAME_OBJECT_BITMASK) sphere_visual.instanceTo(sphere) self.physics_world_.attachRigidBody(sphere.node()) self.controlled_objects_.append(sphere) sphere.reparentTo(self.world_node_) sphere.setPos(self.level_sectors_[0],Vec3(0,0,diameter+10)) sphere.setHpr(self.level_sectors_[0],Vec3(0,0,0)) # creating bullet ghost for detecting entry into other sectors player_ghost = sphere.attachNewNode(BulletGhostNode('player-ghost-node')) ghost_box_shape = BulletSphereShape(PLAYER_GHOST_DIAMETER/2) ghost_box_shape.setMargin(SECTOR_COLLISION_MARGIN) ghost_sphere_shape = BulletSphereShape(PLAYER_GHOST_DIAMETER*0.5) ghost_sphere_shape.setMargin(SECTOR_COLLISION_MARGIN) player_ghost.node().addShape(ghost_box_shape) player_ghost.setPos(sphere,Vec3(0,0,0)) player_ghost.node().setIntoCollideMask(SECTOR_ENTERED_BITMASK) self.physics_world_.attach(player_ghost.node()) # creating mobile box size = Vec3(0.2,0.2,0.4) mbox_visual = loader.loadModel('models/box.egg') bounds = mbox_visual.getTightBounds() extents = Vec3(bounds[1] - bounds[0]) scale_factor = 1/max([extents.getX(),extents.getY(),extents.getZ()]) mbox_visual.setScale(size.getX()*scale_factor,size.getY()*scale_factor,size.getZ()*scale_factor) mbox = NodePath(BulletRigidBodyNode('MobileBox')) mbox.node().addShape(BulletBoxShape(size/2)) mbox.node().setMass(1.0) #mbox.node().setLinearFactor((1,0,1)) #mbox.node().setAngularFactor((0,1,0)) mbox.setCollideMask(GAME_OBJECT_BITMASK) mbox_visual.instanceTo(mbox) self.physics_world_.attachRigidBody(mbox.node()) self.controlled_objects_.append(mbox) mbox.reparentTo(self.level_sectors_[0]) mbox.setPos(Vec3(1,0,size.getZ()+1)) # switching to sector 1 self.switchToSector(self.level_sectors_[0])
def addActor(self, name, position=(0,0,0), orientation=(0,0,0), scale=(1,1,1), collisionSphere=None): # Create the scene node path. actor_np = NodePath(ActorNode(name)) actor_np.setPosHpr(position, orientation) actor_np.reparentTo(self.render) # Attach model to the node path. base_path = os.path.dirname(__file__) model_path = os.path.join(base_path, 'models', name) model_path = Filename.fromOsSpecific(model_path) model_np = self.loader.loadModel(model_path) model_np.setScale(scale) model_np.reparentTo(actor_np) #======================================================================= # Make actor physics collidible. #======================================================================= # Attach collision solid to the node path. cn = CollisionNode(name+'PhysicsCollisionNode') cn.setIntoCollideMask(BitMask32.allOff()) cn.setFromCollideMask(BitMask32.bit(0)) cs = CollisionSphere(collisionSphere[0:3], collisionSphere[3]) cn.addSolid(cs) actor_cnp = actor_np.attachNewNode(cn) # Add actor to the physics collision detection system. handler = PhysicsCollisionHandler() handler.addCollider(actor_cnp, actor_np) # self.cTrav.addCollider(actor_cnp, handler) #======================================================================= # Make actor AI compatible. #======================================================================= ai_character = AICharacter(name, actor_np, mass=50, movt_force=10, max_force=30) self.AICharacter[name] = ai_character self.AIWorld.addAiChar(ai_character) #======================================================================= # Make actor events collidible. #======================================================================= # Attach collision solid to the node path. cn = CollisionNode(name+'EventCollisionNode') cn.setIntoCollideMask(BitMask32.allOff()) cn.setFromCollideMask(BitMask32.bit(1)) cs = CollisionSphere(collisionSphere[0:3], collisionSphere[3]) cn.addSolid(cs) actor_cnp = actor_np.attachNewNode(cn) actor_cnp.show() # Add actor to the collision event detection system. handler = CollisionHandlerEvent() handler.addInPattern('%fn-enters') handler.addOutPattern('%fn-exits') self.cTrav.addCollider(actor_cnp, handler) self.accept(name+'EventCollisionNode'+'-enters', self.onActorEnterEventFunc) self.accept(name+'EventCollisionNode'+'-exits', self.onActorExitEventFunc) self.actorNP[name] = actor_np # The most recently added actor becomes actor of interest. self.setActorOfInterest(name) self.taskMgr.add(self.actorOIStateUpdateTask,'actorStateUpdate',sort=50)
class Army(GameObject): def __init__(self,player,name,x,y,soldiers,general=None): global army_count self.my_id = army_count army_count += 1 print "ARMY ID IS",self.my_id self.name = name self.x = x self.y = y self.target_x = x self.target_y = y self.scale = counter_scale self.range = 50.0 self.selected = False self.general = general self.soldiers = soldiers self.state = "normal" self.speed = 25.0 self.battle = -1 self.stat_init = 0.0 self.stat_hit = 0.0 self.stat_block = 0.0 self.stat_delay = 0.0 self.player = player if self.player == 0: self.colour = (0.5,0.5,0.5,1) self.model = loader.loadModel("models/infantry_counter_grey.egg") if self.player == 1: self.colour = (1,0,0,1) self.model = loader.loadModel("models/infantry_counter_red.egg") elif self.player == 2: self.colour = (0,1,0,1) self.model = loader.loadModel("models/infantry_counter_green.egg") self.node_path = NodePath("army"+str(self.my_id)+"_node_path") self.model.reparentTo(self.node_path) self.node_path.setPos(x,y,0) self.node_path.setTag("player",str(player)) self.node_path.setScale(self.scale,self.scale,self.scale) self.node_col = self.node_path.attachNewNode(CollisionNode("army"+str(self.my_id)+"_c_node")) self.node_col.setScale((1,1,0.5)) self.node_col.setPos(0,0,0) self.node_col.node().addSolid(CollisionSphere(0,0,0,1)) self.node_col.setTag("type","army") base.cTrav.addCollider(self.node_col,base.col_manager.col_handler) #self.node_col.show() self.node_path.setTag("id",str(self.my_id)) self.army_fight_col = self.node_path.attachNewNode(CollisionNode("army"+str(self.my_id)+"_batcol_node")) self.army_fight_col.setScale((2,2,0.5)) self.army_fight_col.setPos(0,0,0) self.army_fight_col.node().addSolid(CollisionSphere(0,0,0,1)) self.army_fight_col.setColor(1,0,0,0.1) self.army_fight_col.setTag("player",str(player)) self.army_fight_col.setTag("state","normal") self.army_fight_col.setTag("type","army") base.cTrav.addCollider(self.army_fight_col,base.col_manager.col_handler) #self.army_fight_col.show() self.selection_ring = self.selection_ring_create(size = 1.2) self.selection_ring.reparentTo(self.node_path) self.selection_ring.hide() self.node_path.reparentTo(render) def turn_start(self): rate = 0.5 army_scl_up = self.model.scaleInterval(rate, Point3(1.5, 1.5, 1.5)) army_scl_down = self.model.scaleInterval(rate, Point3(1, 1, 1)) self.sq_army_bat = Sequence(army_scl_up,army_scl_down) self.sq_army_bat.loop() def turn_end(self): try: self.sq_army_bat.finish() except: print "no sequence to end" def die(self): if base.single_player == False and base.client == False: base.net_manager.server_messager("army_kill",[self.my_id]) self.state = "dead" self.army_fight_col.removeNode() self.node_col.removeNode() rate = 4 intvl_shrink = self.model.scaleInterval(rate, Point3(0, 0, 0)) func_destroy = Func(self.destroy) self.sq_die = Sequence(intvl_shrink,func_destroy) self.sq_die.start() base.battles[self.battle].recenter() base.battles[self.battle].shrink() if self.selected: i = base.col_manager.selecteds.index(self) del base.col_manager.selecteds[i] def stop(self): try: self.sq_army_move.pause() except: print "already stopped" def move_to_point(self,tx,ty): self.target_x = tx self.target_y = ty dist = float(TCalc.dist_to_point(self.node_path.getX(),self.node_path.getY(),tx,ty)) print dist #time = dist/speed #print dist/speed #print time*speed,dist try: self.sq_army_move.pause() self.intvl_army_move = None except: print "no sequence" if dist > 1: self.intvl_army_move = self.node_path.posInterval(dist/self.speed, Point3(self.target_x, self.target_y, 0),startPos=Point3(self.node_path.getX(), self.node_path.getY(), 0)) self.sq_army_move = Sequence(self.intvl_army_move) self.sq_army_move.start() else: try: self.sq_army_move.finish() except: print "no sequence" def set_target(self,sender,tx,ty): if self.state == "normal": if base.single_player == False: base.net_manager.army_move(self.my_id,tx,ty) else: self.move_to_point(tx,ty) def battle_shuffle(self,tx,ty): if base.single_player == False: base.net_manager.army_move(self.my_id,tx,ty) else: self.move_to_point(tx,ty)
def test_door_setup(self): parent_np = NodePath('parent_np') parent_np.setPosHpr(0, 10, .5, 180, 0, 0) door_origin = parent_np.attachNewNode('door_origin') door_origin.setPos(10, -25, .5) block = 4 color = Vec4(.842, .167, .361, 1) # Set up door_np nodes door_np = NodePath('door_np') left_hole = door_np.attachNewNode('door_0_hole_left') right_hole = door_np.attachNewNode('door_0_hole_right') left_door = door_np.attachNewNode('door_0_left') right_door = door_np.attachNewNode('door_0_right') door_flat = door_np.attachNewNode('door_0_flat') door_trigger = door_np.attachNewNode('door_0_trigger') DNADoor.setupDoor(door_np, parent_np, door_origin, self.store, block, color) # Check if the nodes attributes and parents are correct self.assertEqual(door_np.getPos(door_origin), Point3(0, 0, 0)) self.assertEqual(door_np.getHpr(door_origin), Point3(0, 0, 0)) self.assertEqual(door_np.getScale(door_origin), Point3(1, 1, 1)) def verify_color(color1, color2): self.assertAlmostEqual(color1.getX(), color2.getX(), places=4) self.assertAlmostEqual(color1.getY(), color2.getY(), places=4) self.assertAlmostEqual(color1.getZ(), color2.getZ(), places=4) self.assertAlmostEqual(color1.getW(), color2.getW(), places=4) verify_color(color, door_np.getColor()) self.assertEqual(left_hole.getParent(), door_flat) self.assertEqual(left_hole.getName(), 'doorFrameHoleLeft') self.assertEqual(right_hole.getParent(), door_flat) self.assertEqual(right_hole.getName(), 'doorFrameHoleRight') self.assertEqual(left_door.getParent(), parent_np) self.assertEqual(left_door.getName(), 'leftDoor') verify_color(color, right_door.getColor()) self.assertEqual(right_door.getParent(), parent_np) self.assertEqual(right_door.getName(), 'rightDoor') verify_color(color, right_door.getColor()) self.assertEqual(door_trigger.getParent(), parent_np) self.assertEqual(door_trigger.getName(), 'door_trigger_%d' % block) store_np = self.store.getDoorPosHprFromBlockNumber(block) self.assertFalse(store_np.isEmpty()) # Testing the pos is a pain because of decimal precision pos = store_np.getPos() self.assertAlmostEqual(pos.getX(), -10, places=2) self.assertAlmostEqual(pos.getY(), 35, places=2) self.assertAlmostEqual(pos.getZ(), 1, places=2) # Sometimes getH() returns -180 and others 180 # Test the modulus (better than abs I guess) self.assertEqual(store_np.getH() % 180, 0)
class Battle(GameObject): def __init__(self,counters,start=-1): global battle_count self.combatants = counters self.my_id = battle_count self.turn = 0 self.chance_range = 100 self.chance_success = 80 self.col_scale_orig = 2.0 self.col_scale = self.col_scale_orig self.col_scale_inc = 0.4 battle_count += 1 if start != -1: self.turn = start self.combatants[start].turn_start() coords = [(self.combatants[0].get_x(),self.combatants[0].get_y()),(self.combatants[1].get_x(),self.combatants[1].get_y())] self.x,self.y = TCalc.midpoint(coords) self.node_path = NodePath("battle"+str(self.my_id)+"_node_path") self.node_path.setPos(self.x,self.y,0) self.node_path.setTag("id",str(self.my_id)) self.node_path.setTag("type","battle") self.node_path.reparentTo(render) self.bat_col = self.node_path.attachNewNode(CollisionNode("battle"+str(self.my_id)+"_c_node")) self.bat_col.setScale((self.col_scale,self.col_scale,0.2)) self.bat_col.setPos(0,0,0) self.bat_col.node().addSolid(CollisionSphere(0,0,0,10)) self.bat_col.setTag("type","battle") self.bat_col.show() self.battle_speed = 1.0 for a in self.combatants: #a.turn_start() a.stop() a.state = "battle" a.army_fight_col.setTag("state","battle") a.battle = self.my_id if base.client == False: taskMgr.add(self.battle_init_rolls, "battle"+str(self.my_id)+"_task_start") def recenter(self): new_x = 0.0 new_y = 0.0 x_list = [] y_list = [] counter = 0 try: for a in self.combatants: if a.state != "dead": x_list.append(a.node_path.getX()) y_list.append(a.node_path.getY()) counter += 1 new_x += a.node_path.getX() new_y += a.node_path.getY() new_x /= len(x_list) new_y /= len(y_list) self.node_path.setPos(new_x,new_y,0) except: pass def shrink(self): if len(self.combatants) <= 10: self.col_scale -= self.col_scale_orig*self.col_scale_inc self.bat_col.setScale((self.col_scale,self.col_scale,0.2)) def battle_init_rolls(self,task): init_rolls = [] highest_roll = -1 counter = 0 leader = 0 for a in self.combatants: roll = random.randint(0,self.chance_range)+a.stat_init print counter,"rolled",roll init_rolls.append(roll) if roll > highest_roll: highest_roll = roll leader = counter counter += 1 self.turn = leader print leader,"wins!" self.combatants[self.turn].turn_start() a1 = self.combatants[0] a2 = self.combatants[1] if base.client == False: if base.single_player == False: base.net_manager.server_messager("battle_start",[a1.my_id,a1.node_path.getX(),a1.node_path.getY(), a2.my_id,a2.node_path.getX(),a2.node_path.getY(), self.turn]) taskMgr.doMethodLater(1,self.battle_loop,"battle"+str(self.my_id)+"_task_loop") self.get_odds() return task.done def get_odds(self): side1 = [] side2 = [] counter = 0 for a in self.combatants: if a.player == 1 and a.state != "dead": side1.append(a) counter += 1 elif a.player == 2 and a.state != "dead": side2.append(a) counter += 1 self.odds = (100/counter)*len(side1) if base.col_manager.selected_node == self.node_path: base.vis_manager.statbar.refresh_battle(self.odds) def target_recheck(self): army = self.combatants[self.turn] target_id = random.randint(0,len(self.combatants)-1) target = self.combatants[target_id] while target == army or target.player == army.player or target.state == "dead": target_id = random.randint(0,len(self.combatants)-1) target = self.combatants[target_id] print "recheck target - aquired",target_id return army,target,target_id def turn_change(self,new_turn): self.combatants[self.turn].turn_end() self.combatants[new_turn].turn_start() self.turn = new_turn def battle_loop(self,task): towerBuff = 0 battle_end = False army,target,target_id = self.target_recheck() task.delayTime = self.battle_speed+army.stat_delay roll = random.randint(0,self.chance_range)+army.stat_hit #Make towers pown arse for t in base.towers: if t.player == army.player: distanceToTower = base.calculator.dist_to_point(army.get_x(),army.get_y(),t.get_x(),t.get_y()) if distanceToTower < 80: towerBuff = random.randint(0, 40) roll+=towerBuff if roll >= self.chance_success: roll = random.randint(0,self.chance_range)+target.stat_block if roll >= self.chance_success: result = "block" else: result = "hit" target.state = "dead" battle_end = True for a in self.combatants: if a.player != army.player and a.state != "dead": battle_end = False else: result = "fail" if base.client == False and base.single_player == False: randomness = 80 army.battle_shuffle(self.node_path.getX()+random.randint(0,randomness)-randomness/2,self.node_path.getY()+random.randint(0,randomness)-randomness/2) if base.single_player == False: base.net_manager.server_messager("battle_clash",[self.my_id,army.my_id,target.my_id,result,towerBuff]) self.clash(army,target,result,towerBuff) army.turn_end() last_turn = self.turn if battle_end: if base.single_player == False: base.net_manager.server_messager("battle_end",[self.my_id]) self.end() return task.done else: self.get_odds() if self.turn < len(self.combatants)-1: self.turn += 1 else: self.turn = 0 while self.combatants[self.turn].state == "dead": if self.turn < len(self.combatants)-1: self.turn += 1 else: self.turn = 0 if base.single_player == False: base.net_manager.server_messager("battle_turn",[self.my_id,self.turn]) self.combatants[self.turn].turn_start() return task.again def end(self): if base.col_manager.selected_node == self.node_path: base.vis_manager.statbar.reset_statbar() for a in self.combatants: a.turn_end() if a.state != "dead": a.state = "normal" a.battle = -1 a.army_fight_col.setTag("state","normal") self.destroy() def clash(self,a1,a2,result,buff): if buff != 0: buffText = "^+" +str(buff) TimVisuals.BattleText(self.node_path,buffText,self.x,self.y + 10,(1,1,0,1)) if result == "block": TimVisuals.BattleText(self.node_path,"BLOCK!",self.x,self.y,a2.colour) elif result == "hit": TimVisuals.BattleText(self.node_path,"HIT AND KILL!",self.x,self.y,a1.colour) a2.state = "dead" a2.die() else: TimVisuals.BattleText(self.node_path,"Attack Failed!",self.x,self.y,a1.colour) def add_army(self,army): army.stop() army.state = "battle" army.army_fight_col.setTag("state","battle") army.battle = self.my_id self.combatants.append(army) if len(self.combatants) <= 10: self.col_scale += self.col_scale_orig*self.col_scale_inc self.bat_col.setScale((self.col_scale,self.col_scale,0.2)) self.get_odds() self.recenter() def destroy(self): self.node_path.removeNode()
class Tower(GameObject): def __init__(self,player,name,x,y,income): global tower_count self.my_id = tower_count tower_count += 1 self.player = player self.name = name self.x = x self.y = y self.state = "normal" self.build_progress = 0.0 self.build_speed = 1.0 self.gold_inc = 1.0 if base.player == self.player: base.ecn_manager.gold_inc += self.gold_inc base.vis_manager.update() self.model_list = ["models/farmhouse_grey.egg","models/farmhouse_red.egg","models/farmhouse_green.egg"] self.model_list = ["models/tower_grey.egg","models/tower_red.egg","models/tower_green.egg"] self.model = loader.loadModel(self.model_list[self.player]) # if self.player == 0: # self.model = loader.loadModel("models/tower_grey.egg") # elif self.player == 1: # self.model = loader.loadModel("models/tower_red.egg") # elif self.player == 2: # self.model = loader.loadModel("models/tower_green.egg") self.node_path = NodePath("tower"+str(self.my_id)+"_node_path") self.model.reparentTo(self.node_path) self.node_path.setPos(x,y,0) self.node_path.setTag("player",str(player)) self.node_path.setScale(tower_scale,tower_scale,tower_scale) self.node_path.setColor(1,1,1,0.1) self.node_col = self.node_path.attachNewNode(CollisionNode("tower"+str(self.my_id)+"_c_node")) self.node_col.setScale((2,2,1)) self.node_col.setPos(0,0,0) self.node_col.node().addSolid(CollisionSphere(0,0,0,1)) self.node_col.setTag("type","tower") base.cTrav.addCollider(self.node_col,base.col_manager.col_handler) #self.node_col.show() self.node_path.setTag("id",str(self.my_id)) self.tower_fight_col = self.node_path.attachNewNode(CollisionNode("tower"+str(self.my_id)+"_batcol_node")) self.tower_fight_col.setScale((2,2,0.5)) self.tower_fight_col.setPos(0,0,0) self.tower_fight_col.node().addSolid(CollisionSphere(0,0,0,1)) self.tower_fight_col.setColor(1,0,0,0.1) self.tower_fight_col.setTag("player",str(player)) self.tower_fight_col.setTag("state","normal") #self.tower_fight_col.show() base.cTrav.addCollider(self.tower_fight_col,base.col_manager.col_handler) self.selection_ring = self.selection_ring_create(size = 1.5) self.selection_ring.reparentTo(self.node_path) self.selection_ring.hide() self.node_path.reparentTo(render) def capture_check(self): has_guard = False for a in base.armies: if a.player == self.player and (a.state == "normal" or a.state == "battle"): if base.calculator.dist_to_point(self.get_x(),self.get_y(),a.get_x(),a.get_y()) < 80: has_guard = True if has_guard == True: return False else: return True def change_owner(self,new_owner): if self.player == base.player: base.ecn_manager.gold_inc -= self.gold_inc elif new_owner == base.player: base.ecn_manager.gold_inc += self.gold_inc base.vis_manager.update() self.model.remove() self.model = loader.loadModel(self.model_list[new_owner]) self.node_path.setTag("player",str(new_owner)) self.tower_fight_col.setTag("player",str(new_owner)) self.player = new_owner self.model.reparentTo(self.node_path) def build_cancel(self): if self.player == base.player: base.ecn_manager.gold += base.ecn_manager.cost_army_gold base.vis_manager.update() if base.single_player == False and base.client == False: base.net_manager.server_messager("build_cancel",[self.my_id]) taskMgr.remove("task_tower"+str(self.my_id)+"_build") self.build_progress = 0.0 if base.vis_manager.statbar.focus == self: base.vis_manager.statbar.show_tower(self.my_id) def build_start(self): if self.player == base.player: base.ecn_manager.gold -= base.ecn_manager.cost_army_gold base.vis_manager.update() print "Started Building" if base.single_player == False and base.client == False: base.net_manager.server_messager("build_start",[self.my_id,self.player,"army"]) self.build_progress = self.build_speed taskMgr.add(self.task_build,"task_tower"+str(self.my_id)+"_build") if base.vis_manager.statbar.focus == self: base.vis_manager.statbar.show_tower(self.my_id) def build_start_request(self): base.net_manager.client_messager("build_start_request",[self.my_id,self.player,"army"]) def build_cancel_request(self): base.net_manager.client_messager("build_cancel_request",[self.my_id]) def task_build(self,task): if self.build_progress < 100.00: self.build_progress += self.build_speed if base.vis_manager.statbar.focus == self: base.vis_manager.statbar.bar_build.set_value(self.build_progress) return Task.again else: self.build_progress = 0.0 if base.vis_manager.statbar.focus == self: base.vis_manager.statbar.show_tower(self.my_id) if base.single_player == False and base.client == False: base.net_manager.server_messager("build_complete",[self.my_id,self.player,"army"]) if base.client == False: self.create_counter() return Task.done def create_counter(self): new_army = Army(self.player,"Infantry",self.node_path.getX(),self.node_path.getY(),1) base.armies.append(new_army) new_army.state = "new" new_army.army_fight_col.setTag("state","new") intvl_exit = new_army.node_path.posInterval(2, Point3(self.x, self.y-24, 0),startPos=Point3(self.x, self.y, 0)) def army_ready(): new_army.state = "normal" new_army.army_fight_col.setTag("state","normal") func_armyready = Func(army_ready) sq_army_move = Sequence(intvl_exit,func_armyready) sq_army_move.start()
class DistributedTreasure(DistributedObject): notify = directNotify.newCategory('DistributedTreasure') def __init__(self, cr): DistributedObject.__init__(self, cr) self.grabSoundPath = None self.rejectSoundPath = 'phase_4/audio/sfx/ring_miss.mp3' self.dropShadow = None self.treasureTrack = None self.nodePath = None self.modelPath = None self.modelChildString = None self.sphereRadius = 2.0 self.scale = 1.0 self.zOffset = 0.0 self.playSoundForRemoteToons = True self.fly = True self.shadow = True self.billboard = False self.av = None return def announceGenerate(self): DistributedObject.announceGenerate(self) self.loadModel(self.modelPath, self.modelChildString) self.startAnimation() self.nodePath.reparentTo(render) self.accept(self.uniqueName('entertreasureSphere'), self.handleEnterSphere) def loadModel(self, mdlPath, childString = None): self.grabSound = base.loadSfx(self.grabSoundPath) self.rejectSound = base.loadSfx(self.rejectSoundPath) if self.nodePath == None: self.makeNodePath() else: self.treasure.getChildren().detach() model = loader.loadModel(mdlPath) if childString: model = model.find('**/' + childString) model.instanceTo(self.treasure) return def makeNodePath(self): self.nodePath = NodePath('treasure') if self.billboard: self.nodePath.setBillboardPointEye() self.nodePath.setScale(0.9 * self.scale) self.treasure = self.nodePath.attachNewNode('treasure') if self.shadow: if not self.dropShadow: self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow.bam') self.dropShadow.setColor(0, 0, 0, 0.5) self.dropShadow.setPos(0, 0, 0.025) self.dropShadow.setScale(0.4 * self.scale) self.dropShadow.flattenLight() self.dropShadow.reparentTo(self.nodePath) collSphere = CollisionSphere(0, 0, 0, self.sphereRadius) collSphere.setTangible(0) collNode = CollisionNode(self.uniqueName('treasureSphere')) collNode.setIntoCollideMask(CIGlobals.WallBitmask) collNode.addSolid(collSphere) self.collNodePath = self.nodePath.attachNewNode(collNode) self.collNodePath.stash() def handleEnterSphere(self, collEntry = None): localAvId = base.localAvatar.doId if not self.fly: self.setGrab(localAvId) self.d_requestGrab() def setPosition(self, x, y, z): if not self.nodePath: self.makeNodePath() self.nodePath.reparentTo(render) self.nodePath.setPos(x, y, z + self.zOffset) self.collNodePath.unstash() def setReject(self): self.cleanupTrack() base.playSfx(self.rejectSound, node=self.nodePath) self.treasureTrack = Sequence(LerpColorScaleInterval(self.nodePath, 0.8, colorScale=VBase4(0, 0, 0, 0), startColorScale=VBase4(1, 1, 1, 1), blendType='easeIn'), LerpColorScaleInterval(self.nodePath, 0.2, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(0, 0, 0, 0), blendType='easeOut', name='treasureFlyTrack')) self.treasureTrack.start() def setGrab(self, avId): self.collNodePath.stash() self.avId = avId if self.cr.doId2do.has_key(avId): self.av = self.cr.doId2do[avId] else: self.nodePath.detachNode() return if self.playSoundForRemoteToons or self.avId == base.localAvatar.doId: base.playSfx(self.grabSound, node=self.nodePath) if not self.fly: self.nodePath.detachNode() return self.cleanupTrack() avatarGoneName = self.av.uniqueName('disable') self.accept(avatarGoneName, self.handleUnexpectedExit) flyTime = 1.0 track = Sequence(LerpPosInterval(self.nodePath, flyTime, pos=Point3(0, 0, 3), startPos=self.nodePath.getPos(self.av), blendType='easeInOut'), Func(self.nodePath.detachNode), Func(self.ignore, avatarGoneName)) if self.shadow: self.treasureTrack = Sequence(HideInterval(self.dropShadow), track, ShowInterval(self.dropShadow), name='treasureFlyTrack') else: self.treasureTrack = Sequence(track, name='treasureFlyTrack') self.nodePath.reparentTo(self.av) self.treasureTrack.start() def handleUnexpectedExit(self): self.notify.warning('%s disconnected while collecting treasure.' % str(self.avId)) self.cleanupTrack() def d_requestGrab(self): self.sendUpdate('requestGrab', []) def startAnimation(self): pass def disable(self): self.ignoreAll() self.nodePath.detachNode() DistributedObject.disable(self) def cleanupTrack(self): if self.treasureTrack: self.treasureTrack.finish() self.treasureTrack = None return def delete(self): self.cleanupTrack() DistributedObject.delete(self) self.nodePath.removeNode()