class CamManager(DirectObject.DirectObject): """1st or 3d person camera, or disable """ def __init__(self, game): self.game = game self.char = self.game.char self.win = self.game.gui.win self.hotkeys = self.game.gui.hotkeys self.node = NodePath('char') self.Ccentr = NodePath('Ccentr') self.Ccentr.reparentTo(self.node) self.Ccentr.setZ(1) self.third_dist = -6 self.sleep = 0.001 self.camera = self.game.gui.camera self.char.reparentTo(self.node) taskMgr.setupTaskChain('cam_move', numThreads = 1, frameSync = False, threadPriority = TPUrgent, timeslicePriority = False) def set_enable(self, value, third = False): self.enable = value self.third = third self.node.reparentTo(self.game.world.root_node) self.node.setPos(self.game.world.avatar.getPos()) if self.enable: self.camera.reparentTo(self.Ccentr) if self.third: self.camera.setPos(0, self.third_dist, 0) #self.char.show() else: self.camera.setPos(0, 0, 0) #self.char.hide() self.camera.lookAt(self.Ccentr) taskMgr.add(self.mouse_update, 'mouse-task') else: self.camera.reparentTo(self.game.world.root_node) self.camera.setPos(self.game.world.root_node, self.game.world.avatar.getPos(self.game.world.root_node)) self.node.detachNode() #@profile_decorator def mouse_update(self, task): """ this task updates the mouse """ if not self.enable: return task.done md = base.win.getPointer(0) x = md.getX() y = md.getY() if self.win.movePointer(0, self.win.getXSize()/2, self.win.getYSize()/2): self.node.setH(self.node.getH() - (x - self.win.getXSize()/2)*0.1) self.Ccentr.setP(self.Ccentr.getP() - (y - self.win.getYSize()/2)*0.1) time.sleep(self.sleep) return task.cont def point_dist(self, p1, p2): return math.sqrt((p1[0]-p2[0])**2+(p1[1]-p2[1])**2+(p1[2]-p2[2])**2)
class Player: def __init__(self, x, y, z, id): self.health = 100 radius = .15 height = 1 shape = BulletCylinderShape(radius, height, ZUp) self.playerNode = BulletCharacterControllerNode(shape, 0.4, str(id)) self.playerNode.setMaxJumpHeight(2.0) self.playerNode.setJumpSpeed(4.0) self.playerNP = base.render.attachNewNode(self.playerNode) self.playerNP.setPos(x, y, z) self.playerModel = Actor('models/soldier.egg', {"idle": "models/soldier_ani_idle.egg", "walk": "models/soldier_ani_walk.egg", "pistol": "models/soldier_ani_pistol.egg", "death": "models/soldier_ani_death.egg",}) self.playerModel.makeSubpart("legs", ["mixamorig:LeftUpLeg", "mixamorig:RightUpLeg"]) self.playerModel.makeSubpart("hips", ["mixamorig:Hips"], ["mixamorig:LeftUpLeg", "mixamorig:RightUpLeg", "mixamorig:Spine"]) self.playerModel.makeSubpart("upperBody", ["mixamorig:Spine"]) self.playerModel.pose("idle", 0, partName="hips") self.playerModel.setH(90) self.playerModel.setScale(.06) self.playerModel.setZ(-.45) self.playerModel.flattenLight() # self.playerModel.setLightOff()self.playerSpine self.playerModel.reparentTo(self.playerNP) self.playerSpine = self.playerModel.controlJoint(None, 'modelRoot', 'mixamorig:Spine') self.hand = self.playerModel.exposeJoint(None, 'modelRoot', 'mixamorig:RightHand') self.spineExpose = self.playerModel.exposeJoint(None, 'modelRoot', 'mixamorig:Spine') self.playerSpine.setH(-7) # player weapon self.weapon = Ak47(self.hand) # player animation self.xSpeed = 0 self.ySpeed = 0 self.animation = Animation(self) self.model = NodePath("MySpineNode") def bendBody(self): self.model.setPos(self.spineExpose, 0, 0, 0) obj = RayCollider.getObjectHit() self.model.lookAt(obj) self.playerSpine.setP(self.model.getP())
class LocalPlayer(object): def __init__(self, mapobj, showbase): self.x = None self.y = None self.win = showbase.win self.map = mapobj self.mouseWatcherNode = showbase.mouseWatcherNode self.accept = showbase.accept self.camera = showbase.camera self.setup_input() self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(showbase.render) self.up = Vec3(0, 1, 0) incarn = self.map.world.get_incarn() walker_color_dict = { "barrel_color": [219.0 / 255, 16.0 / 255, 50.0 / 255], "visor_color": [157.0 / 255, 14.0 / 255, 48.0 / 255], "body_primary_color": [44.0 / 255, 31.0 / 255, 54.0 / 255], "body_secondary_color": [80.0 / 255, 44.0 / 255, 62.0 / 255], } self.walker = self.map.world.attach(Walker(incarn, colordict=walker_color_dict, player=True)) taskMgr.add(self.move, "move") def setup_input(self): self.key_map = { "cam_forward": 0, "cam_left": 0, "cam_backward": 0, "cam_right": 0, "left": 0, "right": 0, "forward": 0, "backward": 0, "rotateLeft": 0, "rotateRight": 0, "walkForward": 0, "crouch": 0, "fire": 0, "missile": 0, "grenade_fire": 0, "grenade": 0, "print_cam": 0, } self.accept("escape", sys.exit) self.accept("p", self.drop_blocks) for key, cmd in KeyMaps.flycam_input_settings: self.accept(key, self.set_key, [cmd, 1]) self.accept(key + "-up", self.set_key, [cmd, 0]) def move(self, task): dt = globalClock.getDt() if self.mouseWatcherNode.hasMouse(): oldx = self.x oldy = self.y md = self.win.getPointer(0) self.x = md.getX() self.y = md.getY() centerx = self.win.getProperties().getXSize() / 2 centery = self.win.getProperties().getYSize() / 2 self.win.movePointer(0, centerx, centery) if oldx is not None: self.floater.setPos(self.camera, 0, 0, 0) self.floater.setHpr(self.camera, 0, 0, 0) self.floater.setH(self.floater, (centerx - self.x) * 10 * dt) p = self.floater.getP() self.floater.setP(self.floater, (centery - self.y) * 10 * dt) self.floater.setZ(self.floater, -1) angle = self.up.angleDeg(self.floater.getPos() - self.camera.getPos()) if 10 > angle or angle > 170: self.floater.setPos(self.camera, 0, 0, 0) self.floater.setP(p) self.floater.setZ(self.floater, -1) self.camera.lookAt(self.floater.getPos(), self.up) else: self.x = None self.y = None if self.key_map["cam_forward"]: self.camera.setZ(self.camera, -25 * dt) if self.key_map["cam_backward"]: self.camera.setZ(self.camera, 25 * dt) if self.key_map["cam_left"]: self.camera.setX(self.camera, -25 * dt) if self.key_map["cam_right"]: self.camera.setX(self.camera, 25 * dt) if self.key_map["print_cam"]: print "CAMERA: Pos - %s, Hpr - %s" % (self.camera.get_pos(), self.camera.get_hpr()) self.key_map["print_cam"] = 0 self.walker.handle_command("forward", self.key_map["forward"]) self.walker.handle_command("left", self.key_map["left"]) self.walker.handle_command("backward", self.key_map["backward"]) self.walker.handle_command("right", self.key_map["right"]) self.walker.handle_command("crouch", self.key_map["crouch"]) self.walker.handle_command("fire", self.key_map["fire"]) if self.key_map["fire"]: self.key_map["fire"] = 0 self.walker.handle_command("missile", self.key_map["missile"]) if self.key_map["missile"]: self.key_map["missile"] = 0 self.walker.handle_command("grenade_fire", self.key_map["grenade_fire"]) if self.key_map["grenade_fire"]: self.key_map["grenade_fire"] = 0 self.walker.handle_command("grenade", self.key_map["grenade"]) if self.key_map["grenade"]: self.key_map["grenade"] = 0 return task.cont def set_key(self, key, value): self.key_map[key] = value def drop_blocks(self): block = self.map.world.attach(FreeSolid(Block((1, 1, 1), (1, 0, 0, 1), 0.01, (0, 40, 0), (0, 0, 0)), 0.01)) for i in range(10): rand_pos = (random.randint(-25, 25), 40, random.randint(-25, 25)) block = self.map.world.attach(FreeSolid(Block((1, 1, 1), (1, 0, 0, 1), 0.01, rand_pos, (0, 0, 0)), 0.01))
class Vehicle(ActorNode): def __init__(self, parent=None): ''' Create a new Vehicle node. Physics should be initialized before any instances of Vehicle are created. arguments: parent -- A PandaNode for the vehicle to attach to. Default is None, in which case the Vehicle should be added to the scene graph via NodePath.attachNewNode(). ''' ActorNode.__init__(self, 'VehiclePhysics') base.physicsMgr.attachPhysicalNode(self) self.getPhysicsObject().setMass(MASS) if parent: self.myPath = parent.attachNewNode(self) else: self.myPath = NodePath(self) # Load vehicle model and place in the transparent bin. vehicleModel = loader.loadModel(MODEL_PATH) hull = vehicleModel.find('**/Hull') hull.setBin('transparent', 30) pwnsEnclosure = vehicleModel.find('**/Pwns_Enclosure') pwnsEnclosure.setBin('transparent', 30) self.myPath.setPos(0, 0, -0.0) selectable = self.myPath.attachNewNode(SelectableNode('vehicle sel')) vehicleModel.reparentTo(selectable) # ==== Initialize Physics ==== # thrusterForceNode = ForceNode('ThrusterForce') self.myPath.attachNewNode(thrusterForceNode) self.linearForce = LinearVectorForce(0, 0, 0) self.linearForce.setMassDependent(1) self.angularForce = AngularVectorForce(0, 0, 0) thrusterForceNode.addForce(self.linearForce) thrusterForceNode.addForce(self.angularForce) self.getPhysical(0).addLinearForce(self.linearForce) self.getPhysical(0).addAngularForce(self.angularForce) self.previousXY = (self.myPath.getX(), self.myPath.getY()) self.tm = ThrusterManager() # Add self.updatePhysics to the task manager and run this task as # frequently as possible. self.updatePhysicsTask = taskMgr.add(self.updatePhysics, 'UpdatePhysics') # ==== Initialize Cameras ==== # lens = PerspectiveLens() lens.setNearFar(0.05, 100.0) #Use either FocalLength or Fov. Fov ~40 is about what actual forward cameras are #lens.setFocalLength(0.8) lens.setFov(70, 70) camera = Camera("Forward_left", lens).getPath() camera.reparentTo(vehicleModel.find('**/Forward_Camera')) camera.setX(camera.getX() - 0.1) # Forward cameras 20cm apart camera.setY( camera.getY() + 0.05) # Move in front of torpedo tubes to avoid abstruction camera.setHpr(0, 0, 0) camera = Camera("Forward_right", lens).getPath() camera.reparentTo(vehicleModel.find('**/Forward_Camera')) camera.setX(camera.getX() + 0.1) # Forward cameras 20cm apart camera.setY( camera.getY() + 0.05) # Move in front of torpedo tubes to avoid abstruction camera.setHpr(0, 0, 0) lens = PerspectiveLens() lens.setNearFar(0.05, 100.0) lens.setFocalLength(0.8) camera = Camera("Downward", lens).getPath() camera.reparentTo(vehicleModel.find('**/Downward_Camera')) camera.setHpr(0, -90, 0) #Layout link (to access hydrophone information) self.layout = None #Hydrophone variables self.start_time = time() self.last_hydro_update = time() def setLayout(self, layout): #Add a link to the layout to allow for referencing other objects #This is necessary for the hydrophone addition self.layout = layout def getDepth(self): ''' Returns the depth of the vehicle, in meters. ''' return -0.15 - self.myPath.getZ() def getHeading(self): ''' Returns the heading of the vehicle, in clockwise degrees. ''' # Panda uses counter-clockwise degrees, with the range (-180, 180]. heading = self.myPath.getH() if heading < 0: return -heading elif heading > 0: return 360 - heading else: return 0 def updatePhysics(self, task): ''' Use the motor PWM values calculated by the controller to apply forces to the simulated vehicle. This runs at every frame, so it needs to complete quickly. ''' outputs = shm.kalman.get() self.tm.update(outputs) passive_wrench = vehicle.passive_forces(outputs, self.tm) passive_forces, passive_torques = passive_wrench[:3], \ passive_wrench[3:] # Get motor thrusts thrusts = np.array(self.tm.get_thrusts()) # Add passive forces and torques to that produced by thrusters, # converting them to sub space first. force = self.tm.total_thrust(thrusts) + \ self.tm.orientation.conjugate() * passive_forces torque = self.tm.total_torque(thrusts) + \ self.tm.orientation.conjugate() * passive_torques # Finally apply forces and torques to the model # we also need to account for panda3d's strange coordinate system # (x and y are flipped and z points up (instead of down)) self.linearForce.setVector(force_subspace[1], \ force_subspace[0], \ -force_subspace[2]) # We're supposed to use axis angle here, but I'm being sneaky # and using the torque vector directly, i.e. non normalized axis angle # with the hopes that this LRotationf constructor will figure it out self.angularForce.setQuat(\ LRotationf(LVector3f(torque_subspace[1], \ torque_subspace[0], \ -torque_subspace[2]), 1)) # Update shared variables for controller outputs.heading = self.getHeading() outputs.pitch = self.myPath.getP() outputs.roll = self.myPath.getR() # This velocity is in world space # We need to put it into THRUST CONVENTION SPACE # which we assume kalman outputs in... velocity = self.getPhysicsObject().getVelocity() # Bring the velocity into THRUST CONVENTION SPACE # Don't forget to account for panda's coordinate system velocity = self.tm.heading_quat.conjugate() * \ np.array((velocity.getY(), velocity.getX(), -velocity.getZ())) outputs.velx = velocity[0] outputs.vely = velocity[1] outputs.depth_rate = velocity[2] outputs.depth = self.getDepth() outputs.north = self.myPath.getY() outputs.east = self.myPath.getX() dX = self.myPath.getX() - self.previousXY[0] dY = self.myPath.getY() - self.previousXY[1] # Forward and sway are in THRUST CONVENTION SPACE # don't forget to account for panda's coordinate system dF, dS, dD = self.tm.heading_quat.conjugate() * np.array((dY, dX, 0.0)) outputs.forward += dF outputs.sway += dS # Output some quaternions, accounting for Panda's coordinate system outputs.q0, outputs.q2, outputs.q1, outputs.q3 = self.myPath.getQuat() outputs.q3 *= -1.0 shm.kalman.set(outputs) svHeadingInt.set(self.getHeading()) svDepth.set(self.getDepth()) #XXX: Approximate altitude assuming that the pool is 12 feet deep svAltitude.set(3.6 - self.getDepth()) svDvlDmgNorth.set(self.myPath.getY()) svDvlDmgEast.set(self.myPath.getX()) self.previousXY = (self.myPath.getX(), self.myPath.getY()) #update self.output_hydro_data() return Task.cont def output_hydro_data(self): #Update simulated hydrophone values pingers = [] #Get all pingers from the layout for element in self.layout.elements: if element.getTypeName() == "Pinger": pingers.append(element) HYDRO_TICK_PERIOD = 1 if time() - self.last_hydro_update > HYDRO_TICK_PERIOD: dt = time() - self.last_hydro_update self.last_hydro_update = time() if shm.hydrophones_settings.dsp_mode.get() == 1: #Search mode #Incr search count shm.hydrophones_results.search_count.set( shm.hydrophones_results.search_count.get() + 1) #Generate proper "hydrophone bins" marks sb = 0 for p in pingers: f = p.pinger_frequency dc = (f - (shm.hydrophones_settings.searchCenter.get() - shm.hydrophones_settings.searchDelta.get()) ) / shm.hydrophones_settings.searchStep.get() + 0.5 sb |= 1 << int(dc) shm.hydrophones_results.search_bins.set(sb) else: #Track Mode #Incr ping count shm.hydrophones_results.ping_count.set( shm.hydrophones_results.ping_count.get() + 1) #Determine which pinger we are actively tracking (within 0.7khz of target) targetp = None for p in pingers: if abs(shm.hydrophones_settings.trackFrequency.get() - p.pinger_frequency) < 700: targetp = p if targetp is not None: shm.hydrophones_results.intensity.set( int(shm.hydrophones_settings.trackMagThresh.get() + 1e4 * random())) shm.hydrophones_results.ping_time.set(int(dt * 1000)) pp = targetp.path.getPos() vv = vector.Vector(self.myPath.getY(), self.myPath.getX()) pv = vector.Vector(pp.getY(), pp.getX()) #heading dv = pv - vv ang = vector.GetAuvAngle(dv) hdiff = helpers.heading_diff(self.getHeading(), ang) shm.hydrophones_results.heading.set(hdiff) #elevation dh = self.myPath.getZ() - pp.getZ() dist = vector.Length(dv) elev = math.degrees(math.atan2(dist, dh)) elev = min(elev, 90) shm.hydrophones_results.elevation.set(elev) #phase calculations dy = self.myPath.getY() - pp.getY() dx = self.myPath.getX() - pp.getX() yang = math.degrees(math.atan2(dist, dy)) xang = math.degrees(math.atan2(dist, dx)) shm.hydrophones_results.phaseX.set((90.0 - xang) / 90.0) shm.hydrophones_results.phaseY.set((90.0 - yang) / 90.0) shm.hydrophones_results.phaseZ.set((90.0 - elev) / 90.0) else: shm.hydrophones_results.heading.set(0) shm.hydrophones_results.elevation.set(0) shm.hydrophones_results.intensity.set(0) shm.hydrophones_results.ping_time.set(0) shm.hydrophones_results.uptime.set(int(time() - self.start_time)) def __del__(self): ''' Remove update tasks from the panda task manager. ''' taskMgr.remove(self.updatePhysicsTask) ActorNode.__del__(self)
class CameraHandler(): """Hold the different cameras""" def __init__(self, _engine, _mode): self.mode = _mode self.engine = _engine self.camDummy = None player = self.engine.GameObjects["player"].bulletBody if self.mode == "TPA": self.initTPAMode(player) else: self.initTPSMode(player) def update(self, dt): if self.mode == "TPA": self.followPlayerTPA(dt) else: self.followPlayerTPS(dt) def stop(self): base.camera.reparentTo(render) base.camera.setPos(0,0,0) # # Third person shooter mode # def initTPSMode(self, player): """Sets the cam mode to a third person shooter mode, so the cam will be up and behind the player as well as always look in the direction the player faces""" # Setup the camera so that its on the player self.camDummy = NodePath(PandaNode("camDummy")) self.camDummy.reparentTo(player.movementParent) self.camDummy.setPos(0, 0, 1.8) base.camera.reparentTo(self.camDummy) base.camera.setPos(0, -8, 2) def followPlayerTPS(self, dt): player = self.engine.GameObjects["player"].bulletBody ih = self.engine.inputHandler if base.win.movePointer(0, ih.winXhalf, ih.winYhalf) \ and base.mouseWatcherNode.hasMouse(): cam = self.camDummy.getP() - (ih.mouseY - ih.winYhalf) * ih.mouseSpeedY if cam <-80: cam = -80 elif cam > 90: cam = 90 self.camDummy.setP(cam) base.camera.lookAt(self.camDummy) # # Third person adventure mode # def initTPAMode(self, player): """Sets the cam mode to a third person adventure mode, so the cam will be up and behind the player and will be lazily move behind the player.""" # create a new dummy node that the cam will look at self.camDummy = NodePath(PandaNode("camDummy")) self.camDummy.reparentTo(render) # Setup the camera so that its on the player base.camera.reparentTo(self.camDummy) base.camera.setPos(player.getX(), player.getY() - 6.0, player.getZ() + 4.0) def followPlayerTPA(self, dt): player = self.engine.GameObjects["player"].bulletBody base.camera.lookAt(player.getPos()) ih = self.engine.inputHandler if base.win.movePointer(0, ih.winXhalf, ih.winYhalf) \ and base.mouseWatcherNode.hasMouse(): omega = (ih.mouseX - ih.winXhalf)*-ih.mouseSpeedX * dt * 0.25 if omega != 0.0: base.camera.setX(base.camera, omega) camvec = player.getPos() - base.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() minCamDist = 2.0 maxCamDist = 8.0 if camdist > maxCamDist: base.camera.setPos(base.camera.getPos() + camvec*(camdist-maxCamDist)) camdist = maxCamDist if camdist < minCamDist: base.camera.setPos(base.camera.getPos() - camvec*(minCamDist-camdist)) camdist = minCamDist if base.camera.getZ() > self.camDummy.getZ() + 2: base.camera.setZ(self.camDummy.getZ() + 2) elif base.camera.getZ() < self.camDummy.getZ() + 1: base.camera.setZ(self.camDummy.getZ() + 1) self.camDummy.setPos(player.getPos()) self.camDummy.setZ(player.getZ() + 2.0) base.camera.lookAt(self.camDummy)
class CogdoFlyingCameraManager: def __init__(self, cam, parent, player, level): self._toon = player.toon self._camera = cam self._parent = parent self._player = player self._level = level self._enabled = False def enable(self): if self._enabled: return self._toon.detachCamera() self._prevToonY = 0.0 levelBounds = self._level.getBounds() l = Globals.Camera.LevelBoundsFactor self._bounds = ((levelBounds[0][0] * l[0], levelBounds[0][1] * l[0]), (levelBounds[1][0] * l[1], levelBounds[1][1] * l[1]), (levelBounds[2][0] * l[2], levelBounds[2][1] * l[2])) self._lookAtZ = self._toon.getHeight( ) + Globals.Camera.LookAtToonHeightOffset self._camParent = NodePath('CamParent') self._camParent.reparentTo(self._parent) self._camParent.setPos(self._toon, 0, 0, 0) self._camParent.setHpr(180, Globals.Camera.Angle, 0) self._camera.reparentTo(self._camParent) self._camera.setPos(0, Globals.Camera.Distance, 0) self._camera.lookAt(self._toon, 0, 0, self._lookAtZ) self._cameraLookAtNP = NodePath('CameraLookAt') self._cameraLookAtNP.reparentTo(self._camera.getParent()) self._cameraLookAtNP.setPosHpr(self._camera.getPos(), self._camera.getHpr()) self._levelBounds = self._level.getBounds() self._enabled = True self._frozen = False self._initCollisions() def _initCollisions(self): self._camCollRay = CollisionRay() camCollNode = CollisionNode('CameraToonRay') camCollNode.addSolid(self._camCollRay) camCollNode.setFromCollideMask(OTPGlobals.WallBitmask | OTPGlobals.CameraBitmask | ToontownGlobals.FloorEventBitmask | ToontownGlobals.CeilingBitmask) camCollNode.setIntoCollideMask(0) self._camCollNP = self._camera.attachNewNode(camCollNode) self._camCollNP.show() self._collOffset = Vec3(0, 0, 0.5) self._collHandler = CollisionHandlerQueue() self._collTrav = CollisionTraverser() self._collTrav.addCollider(self._camCollNP, self._collHandler) self._betweenCamAndToon = {} self._transNP = NodePath('trans') self._transNP.reparentTo(render) self._transNP.setTransparency(True) self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon) self._transNP.setBin('fixed', 10000) def _destroyCollisions(self): self._collTrav.removeCollider(self._camCollNP) self._camCollNP.removeNode() del self._camCollNP del self._camCollRay del self._collHandler del self._collOffset del self._betweenCamAndToon self._transNP.removeNode() del self._transNP def freeze(self): self._frozen = True def unfreeze(self): self._frozen = False def disable(self): if not self._enabled: return self._destroyCollisions() self._camera.wrtReparentTo(render) self._cameraLookAtNP.removeNode() del self._cameraLookAtNP self._camParent.removeNode() del self._camParent del self._prevToonY del self._lookAtZ del self._bounds del self._frozen self._enabled = False def update(self, dt=0.0): self._updateCam(dt) self._updateCollisions() def _updateCam(self, dt): toonPos = self._toon.getPos() camPos = self._camParent.getPos() x = camPos[0] z = camPos[2] toonWorldX = self._toon.getX(render) maxX = Globals.Camera.MaxSpinX toonWorldX = clamp(toonWorldX, -1.0 * maxX, maxX) spinAngle = Globals.Camera.MaxSpinAngle * toonWorldX * toonWorldX / ( maxX * maxX) newH = 180.0 + spinAngle self._camParent.setH(newH) spinAngle = spinAngle * (pi / 180.0) distBehindToon = Globals.Camera.SpinRadius * cos(spinAngle) distToRightOfToon = Globals.Camera.SpinRadius * sin(spinAngle) d = self._camParent.getX() - clamp(toonPos[0], *self._bounds[0]) if abs(d) > Globals.Camera.LeewayX: if d > Globals.Camera.LeewayX: x = toonPos[0] + Globals.Camera.LeewayX else: x = toonPos[0] - Globals.Camera.LeewayX x = self._toon.getX(render) + distToRightOfToon boundToonZ = min(toonPos[2], self._bounds[2][1]) d = z - boundToonZ if d > Globals.Camera.MinLeewayZ: if self._player.velocity[2] >= 0 and toonPos[ 1] != self._prevToonY or self._player.velocity[2] > 0: z = boundToonZ + d * INVERSE_E**(dt * Globals.Camera.CatchUpRateZ) elif d > Globals.Camera.MaxLeewayZ: z = boundToonZ + Globals.Camera.MaxLeewayZ elif d < -Globals.Camera.MinLeewayZ: z = boundToonZ - Globals.Camera.MinLeewayZ if self._frozen: y = camPos[1] else: y = self._toon.getY(render) - distBehindToon self._camParent.setPos(x, smooth(camPos[1], y), smooth(camPos[2], z)) if toonPos[2] < self._bounds[2][1]: h = self._cameraLookAtNP.getH() if d >= Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._toon, 0, 0, self._lookAtZ) elif d <= -Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._camParent, 0, 0, self._lookAtZ) self._cameraLookAtNP.setHpr(h, self._cameraLookAtNP.getP(), 0) self._camera.setHpr( smooth(self._camera.getHpr(), self._cameraLookAtNP.getHpr())) self._prevToonY = toonPos[1] def _updateCollisions(self): pos = self._toon.getPos(self._camera) + self._collOffset self._camCollRay.setOrigin(pos) direction = -Vec3(pos) direction.normalize() self._camCollRay.setDirection(direction) self._collTrav.traverse(render) nodesInBetween = {} if self._collHandler.getNumEntries() > 0: self._collHandler.sortEntries() for entry in self._collHandler.getEntries(): name = entry.getIntoNode().getName() if name.find('col_') >= 0: np = entry.getIntoNodePath().getParent() if np not in nodesInBetween: nodesInBetween[np] = np.getParent() for np in nodesInBetween.keys(): if np in self._betweenCamAndToon: del self._betweenCamAndToon[np] else: np.setTransparency(True) np.wrtReparentTo(self._transNP) if np.getName().find('lightFixture') >= 0: if not np.find('**/*floor_mesh').isEmpty(): np.find('**/*floor_mesh').hide() elif np.getName().find('platform') >= 0: if not np.find('**/*Floor').isEmpty(): np.find('**/*Floor').hide() for np, parent in self._betweenCamAndToon.items(): np.wrtReparentTo(parent) np.setTransparency(False) if np.getName().find('lightFixture') >= 0: if not np.find('**/*floor_mesh').isEmpty(): np.find('**/*floor_mesh').show() elif np.getName().find('platform') >= 0: if not np.find('**/*Floor').isEmpty(): np.find('**/*Floor').show() self._betweenCamAndToon = nodesInBetween
class CameraControl(DirectObject, HasKeybinds): """ adds controls to a given camera, usually base.camera""" def __init__(self,camera=None): #camera setup self.camera = camera if self.camera == None: self.camera = base.camera #XXX note, when moving cam target we need to make sure the camera doesnt move too... cameraBase = GeomNode('cameraBase') #utility node for pan targetGeom = makeCameraTarget() cameraBase.addGeom(targetGeom) self.cameraBase = render.attachNewNode(cameraBase) #self.cameraBase.setTwoSided(True) #backface culling issue with my tristrip fail self.cameraTarget = NodePath('cameraTarget') #utility node for rot, zoom, reattach self.cameraTarget.reparentTo(self.cameraBase) #self.cameraTarget.reparentTo(render) self.camera.reparentTo(self.cameraTarget) self.track = self.camera.attachNewNode('track') #hack for pointing vector self.track.setPos(LVecBase3f(0,50,0)) #nn = GeomNode('helper') #ng = makeCameraTarget() #nn.addGeom(targetGeom) #self.track.attachNewNode(nn) #keybind setup self.__ends__=defaultdict(list) #self.accept("escape", sys.exit) #no, exit_cleanup will handle this... for function_name, key in keybinds['view'].items(): #self.accept(key,taskMgr.add,(getattr(self,function),function+'Task')) self.accept(key, self.makeTask, [function_name]) # TODO split out functions that don't require tasks keytest=key.split('-')[-1] #print(keytest) if keytest in {'mouse1','mouse2','mouse3'}: self.addEndTask(keytest,function_name) self.accept(keytest+'-up', self.endTask, [keytest,function_name]) #gains #TODO tweak me! self.XGAIN = .01 self.YGAIN = .01 #window setup self.getWindowSize() self.accept('window-event', self.getWindowSize) #self.accept('mouse1') #mouse 1 by itself does selection? #self.accpet('mouse3') #pan #self.accpet('mouse2') #--camera moves relatvie to arbitrary origin-- #pan in plane #zoom #this needs to be on a log scale, linear is balls #rotate #--camera in place-- #roll camera in place #yaw #pitch #look at selection/origin/center of mass of #--camera lense changes-- #fov (for perspective) #perspective/orthographic #--worldcraft-- #z mode wasd + mouse to orient for zooming #--selection functions we need to leave space for-- #drop origin if we don't have something selected #click select #drag select, logial intersec #right click for menu self.__ch__=None self.__cp__=None self.__cr__=None self.__cth__=None self.__ctp__=None pass def getWindowSize(self,wat=None): self.__winx__ = base.win.getXSize() self.__winy__ = base.win.getYSize() #print(self.__winx__,self.__winy__) def makeTask(self, function_name): """ ye old task spawner """ if hasattr(self, function_name): if base.mouseWatcherNode.hasMouse(): x,y = base.mouseWatcherNode.getMouse() setattr(self, '__%sTask_s__'%function_name, (x,y)) #this should be faster taskMgr.add(getattr(self,function_name), function_name+'Task') else: raise KeyError('Check your keybinds, there is no function by that name here!') def addEndTask(self,key,function_name): self.__ends__[key].append(function_name) def endTask(self, key, function): for func in self.__ends__[key]: taskMgr.remove(func+'Task') setattr(self, '__%sTask_s__'%func, None) #this should be faster self.__ch__=None #FIXME this seems hackish self.__cp__=None self.__cr__=None self.__cth__=None self.__ctp__=None def getMouseDdDt(self, name): #XXX deprecated """ use gain to adjust pixels per degree this should probably be normalized to screen size actually? or no... but to what? """ if base.mouseWatcherNode.hasMouse(): x,z = base.mouseWatcherNode.getMouse() sx,sz = getattr(self,'__%s_start__'%name) print(x,sx) print(z,sz) if z != sz or x != sx: #watch out for aliasing here... norm = (((x - sx) * self.XGAIN)**2 + ((z - sz) * self.YGAIN)**2)**.5 #norm = ((x - sx) * self.X_GAIN), ((z - sz) * self.Y_GAIN) setattr(self, '__%s_start__'%name, (x,z)) return norm else: #mouse has not moved return 0 def getMouseDdDf(self,name): if base.mouseWatcherNode.hasMouse(): x,y = base.mouseWatcherNode.getMouse() sx,sy = getattr(self,'__%s_s__'%(name)) dx = (x - sx) * self.XGAIN * self.__winx__ dy = (y - sy) * self.YGAIN * self.__winy__ return dx, dy def getMouseCross(self,name): #FIXME may need to do this incrementally as we started with... if base.mouseWatcherNode.hasMouse(): x,y = base.mouseWatcherNode.getMouse() sx,sy = getattr(self,'__%s_s__'%(name)) dx = (x - sx) * self.XGAIN * self.__winx__ dy = (y - sy) * self.YGAIN * self.__winy__ norm = (dx**2 + dy**2)**.5 cross = x * sy - y * sx return cross * norm @event_callback def home(self, task): self.camera.lookAt(self.cameraBase) taskMgr.remove(task.getName()) return task.cont @event_callback def pan(self, task): """ I don't like it, it's weird! """ invert = -1 magic_number = 15 magic_number = 20 if base.mouseWatcherNode.hasMouse(): x,y = base.mouseWatcherNode.getMouse() sx,sy = getattr(self,'__%s_s__'%(task.getName())) dx = (x - sx) * self.XGAIN * self.__winx__ * magic_number * invert dy = (y - sy) * self.YGAIN * self.__winy__ * magic_number * invert #cx,cy,cz = self.camera.getPos() self.camera.setPos(self.camera,dx,0,dy) setattr(self, '__%s_s__'%task.getName(), (x,y)) #reset each frame to compensate for moving from own position #nx,ny,nz = self.camera.getPos() #dx2, dy2, dz2 = nx-cx, ny-cy, nz-cz #self.camera.setPos(cx,cz,cy) #self.cameraBase.setPos(self.cameraBase,dx2,dy2,dz2) #a hack to move cameraBase as if it were the camera #self.cameraTarget.setPos(self.cameraBase,dx2,dy2,dz2) #a hack to move cameraBase as if it were the camera return task.cont @event_callback def zoom_in_slow(self, task, speed = 10): return self.zoom_in(task, speed) #hehe this will work because it just passes the task :) @event_callback def zoom_out_slow(self, task, speed = 10): return self.zoom_out(task, speed) @event_callback def zoom_in_fast(self, task, speed = 1000): return self.zoom_in(task, speed) #hehe this will work because it just passes the task :) @event_callback def zoom_out_fast(self, task, speed = 1000): return self.zoom_out(task, speed) @event_callback def zoom_in(self, task, speed = 100): #FIXME zoom_in and zoom_out still get custom xys even thought they don't use them! self.camera.setPos(self.camera,0,speed,0) taskMgr.remove(task.getName()) return task.cont @event_callback def zoom_out(self, task, speed = 100): self.camera.setPos(self.camera,0,-speed,0) taskMgr.remove(task.getName()) #we do it this way instead of addOnce because we want to add all the tasks in one go return task.cont @event_callback def rotate(self, task): #FIXME disregard orientation acqurie proper mouse movements! dx,dy = self.getMouseDdDf(task.getName()) if self.__cth__ == None: self.__cth__ = self.cameraTarget.getH() if self.__ctp__ == None: self.__ctp__ = self.cameraTarget.getP() self.cameraTarget.setH(self.__cth__ - dx * 10) self.cameraTarget.setP(self.__ctp__ + dy * 10) return task.cont #if we are in camera mode @event_callback def pitch(self, task): dx,dy = self.getMouseDdDf(task.getName()) print('got pitch',dy) return task.cont @event_callback def look(self, task): #AKA heading in hpr dx,dy = self.getMouseDdDf(task.getName()) if self.__ch__ == None: self.__ch__ = self.camera.getH() if self.__cp__ == None: self.__cp__ = self.camera.getP() self.camera.setH(self.__ch__ - dx) self.camera.setP(self.__cp__ + dy) #FIXME when we're clicking this might should be inverted? return task.cont @event_callback def roll(self, task): """ ALWAYS roll with respect to axis of rotation""" if self.__cr__ == None: self.__cr__ = self.cameraTarget.getR() #cross product idiot cross = self.getMouseCross(task.getName()) self.cameraTarget.setR(self.__cr__ - cross * 10 ) return task.cont
class CameraControl(DirectObject, HasKeybinds): """ adds controls to a given camera, usually base.camera""" def __init__(self, camera=None): #camera setup self.camera = camera if self.camera == None: self.camera = base.camera #XXX note, when moving cam target we need to make sure the camera doesnt move too... cameraBase = GeomNode('cameraBase') #utility node for pan targetGeom = makeCameraTarget() cameraBase.addGeom(targetGeom) self.cameraBase = render.attachNewNode(cameraBase) #self.cameraBase.setTwoSided(True) #backface culling issue with my tristrip fail self.cameraTarget = NodePath( 'cameraTarget') #utility node for rot, zoom, reattach self.cameraTarget.reparentTo(self.cameraBase) #self.cameraTarget.reparentTo(render) self.camera.reparentTo(self.cameraTarget) self.track = self.camera.attachNewNode( 'track') #hack for pointing vector self.track.setPos(LVecBase3f(0, 50, 0)) #nn = GeomNode('helper') #ng = makeCameraTarget() #nn.addGeom(targetGeom) #self.track.attachNewNode(nn) #keybind setup self.__ends__ = defaultdict(list) #self.accept("escape", sys.exit) #no, exit_cleanup will handle this... for function_name, key in keybinds['view'].items(): #self.accept(key,taskMgr.add,(getattr(self,function),function+'Task')) self.accept( key, self.makeTask, [function_name ]) # TODO split out functions that don't require tasks keytest = key.split('-')[-1] #print(keytest) if keytest in {'mouse1', 'mouse2', 'mouse3'}: self.addEndTask(keytest, function_name) self.accept(keytest + '-up', self.endTask, [keytest, function_name]) #gains #TODO tweak me! self.XGAIN = .01 self.YGAIN = .01 #window setup self.getWindowSize() self.accept('window-event', self.getWindowSize) #self.accept('mouse1') #mouse 1 by itself does selection? #self.accpet('mouse3') #pan #self.accpet('mouse2') #--camera moves relatvie to arbitrary origin-- #pan in plane #zoom #this needs to be on a log scale, linear is balls #rotate #--camera in place-- #roll camera in place #yaw #pitch #look at selection/origin/center of mass of #--camera lense changes-- #fov (for perspective) #perspective/orthographic #--worldcraft-- #z mode wasd + mouse to orient for zooming #--selection functions we need to leave space for-- #drop origin if we don't have something selected #click select #drag select, logial intersec #right click for menu self.__ch__ = None self.__cp__ = None self.__cr__ = None self.__cth__ = None self.__ctp__ = None pass def getWindowSize(self, wat=None): self.__winx__ = base.win.getXSize() self.__winy__ = base.win.getYSize() #print(self.__winx__,self.__winy__) def makeTask(self, function_name): """ ye old task spawner """ if hasattr(self, function_name): if base.mouseWatcherNode.hasMouse(): x, y = base.mouseWatcherNode.getMouse() setattr(self, '__%sTask_s__' % function_name, (x, y)) #this should be faster taskMgr.add(getattr(self, function_name), function_name + 'Task') else: raise KeyError( 'Check your keybinds, there is no function by that name here!') def addEndTask(self, key, function_name): self.__ends__[key].append(function_name) def endTask(self, key, function): for func in self.__ends__[key]: taskMgr.remove(func + 'Task') setattr(self, '__%sTask_s__' % func, None) #this should be faster self.__ch__ = None #FIXME this seems hackish self.__cp__ = None self.__cr__ = None self.__cth__ = None self.__ctp__ = None def getMouseDdDt(self, name): #XXX deprecated """ use gain to adjust pixels per degree this should probably be normalized to screen size actually? or no... but to what? """ if base.mouseWatcherNode.hasMouse(): x, z = base.mouseWatcherNode.getMouse() sx, sz = getattr(self, '__%s_start__' % name) print(x, sx) print(z, sz) if z != sz or x != sx: #watch out for aliasing here... norm = (((x - sx) * self.XGAIN)**2 + ((z - sz) * self.YGAIN)**2)**.5 #norm = ((x - sx) * self.X_GAIN), ((z - sz) * self.Y_GAIN) setattr(self, '__%s_start__' % name, (x, z)) return norm else: #mouse has not moved return 0 def getMouseDdDf(self, name): if base.mouseWatcherNode.hasMouse(): x, y = base.mouseWatcherNode.getMouse() sx, sy = getattr(self, '__%s_s__' % (name)) dx = (x - sx) * self.XGAIN * self.__winx__ dy = (y - sy) * self.YGAIN * self.__winy__ return dx, dy def getMouseCross( self, name ): #FIXME may need to do this incrementally as we started with... if base.mouseWatcherNode.hasMouse(): x, y = base.mouseWatcherNode.getMouse() sx, sy = getattr(self, '__%s_s__' % (name)) dx = (x - sx) * self.XGAIN * self.__winx__ dy = (y - sy) * self.YGAIN * self.__winy__ norm = (dx**2 + dy**2)**.5 cross = x * sy - y * sx return cross * norm @event_callback def home(self, task): self.camera.lookAt(self.cameraBase) taskMgr.remove(task.getName()) return task.cont @event_callback def pan(self, task): """ I don't like it, it's weird! """ invert = -1 magic_number = 15 magic_number = 20 if base.mouseWatcherNode.hasMouse(): x, y = base.mouseWatcherNode.getMouse() sx, sy = getattr(self, '__%s_s__' % (task.getName())) dx = (x - sx) * self.XGAIN * self.__winx__ * magic_number * invert dy = (y - sy) * self.YGAIN * self.__winy__ * magic_number * invert #cx,cy,cz = self.camera.getPos() self.camera.setPos(self.camera, dx, 0, dy) setattr( self, '__%s_s__' % task.getName(), (x, y) ) #reset each frame to compensate for moving from own position #nx,ny,nz = self.camera.getPos() #dx2, dy2, dz2 = nx-cx, ny-cy, nz-cz #self.camera.setPos(cx,cz,cy) #self.cameraBase.setPos(self.cameraBase,dx2,dy2,dz2) #a hack to move cameraBase as if it were the camera #self.cameraTarget.setPos(self.cameraBase,dx2,dy2,dz2) #a hack to move cameraBase as if it were the camera return task.cont @event_callback def zoom_in_slow(self, task, speed=10): return self.zoom_in( task, speed) #hehe this will work because it just passes the task :) @event_callback def zoom_out_slow(self, task, speed=10): return self.zoom_out(task, speed) @event_callback def zoom_in_fast(self, task, speed=1000): return self.zoom_in( task, speed) #hehe this will work because it just passes the task :) @event_callback def zoom_out_fast(self, task, speed=1000): return self.zoom_out(task, speed) @event_callback def zoom_in( self, task, speed=100 ): #FIXME zoom_in and zoom_out still get custom xys even thought they don't use them! self.camera.setPos(self.camera, 0, speed, 0) taskMgr.remove(task.getName()) return task.cont @event_callback def zoom_out(self, task, speed=100): self.camera.setPos(self.camera, 0, -speed, 0) taskMgr.remove( task.getName() ) #we do it this way instead of addOnce because we want to add all the tasks in one go return task.cont @event_callback def rotate(self, task ): #FIXME disregard orientation acqurie proper mouse movements! dx, dy = self.getMouseDdDf(task.getName()) if self.__cth__ == None: self.__cth__ = self.cameraTarget.getH() if self.__ctp__ == None: self.__ctp__ = self.cameraTarget.getP() self.cameraTarget.setH(self.__cth__ - dx * 10) self.cameraTarget.setP(self.__ctp__ + dy * 10) return task.cont #if we are in camera mode @event_callback def pitch(self, task): dx, dy = self.getMouseDdDf(task.getName()) print('got pitch', dy) return task.cont @event_callback def look(self, task): #AKA heading in hpr dx, dy = self.getMouseDdDf(task.getName()) if self.__ch__ == None: self.__ch__ = self.camera.getH() if self.__cp__ == None: self.__cp__ = self.camera.getP() self.camera.setH(self.__ch__ - dx) self.camera.setP( self.__cp__ + dy) #FIXME when we're clicking this might should be inverted? return task.cont @event_callback def roll(self, task): """ ALWAYS roll with respect to axis of rotation""" if self.__cr__ == None: self.__cr__ = self.cameraTarget.getR() #cross product idiot cross = self.getMouseCross(task.getName()) self.cameraTarget.setR(self.__cr__ - cross * 10) return task.cont
class LocalPlayer(object): def __init__(self, mapobj, showbase): self.x = None self.y = None self.win = showbase.win self.map = mapobj self.mouseWatcherNode = showbase.mouseWatcherNode self.accept = showbase.accept self.camera = showbase.camera self.setup_input() self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(showbase.render) self.up = Vec3(0, 1, 0) incarn = self.map.world.get_incarn() walker_color_dict = { "barrel_color": [219.0 / 255, 16.0 / 255, 50.0 / 255], "visor_color": [157.0 / 255, 14.0 / 255, 48.0 / 255], "body_primary_color": [44.0 / 255, 31.0 / 255, 54.0 / 255], "body_secondary_color": [80.0 / 255, 44.0 / 255, 62.0 / 255] } self.walker = self.map.world.attach( Walker(incarn, colordict=walker_color_dict, player=True)) taskMgr.add(self.move, 'move') def setup_input(self): self.key_map = { 'cam_forward': 0, 'cam_left': 0, 'cam_backward': 0, 'cam_right': 0, 'left': 0, 'right': 0, 'forward': 0, 'backward': 0, 'rotateLeft': 0, 'rotateRight': 0, 'walkForward': 0, 'crouch': 0, 'fire': 0, 'missile': 0, 'grenade_fire': 0, 'grenade': 0, 'print_cam': 0 } self.accept('escape', sys.exit) self.accept('p', self.drop_blocks) for key, cmd in KeyMaps.flycam_input_settings: self.accept(key, self.set_key, [cmd, 1]) self.accept(key + "-up", self.set_key, [cmd, 0]) def move(self, task): dt = globalClock.getDt() if self.mouseWatcherNode.hasMouse(): oldx = self.x oldy = self.y md = self.win.getPointer(0) self.x = md.getX() self.y = md.getY() centerx = self.win.getProperties().getXSize() / 2 centery = self.win.getProperties().getYSize() / 2 self.win.movePointer(0, centerx, centery) if (oldx is not None): self.floater.setPos(self.camera, 0, 0, 0) self.floater.setHpr(self.camera, 0, 0, 0) self.floater.setH(self.floater, (centerx - self.x) * 10 * dt) p = self.floater.getP() self.floater.setP(self.floater, (centery - self.y) * 10 * dt) self.floater.setZ(self.floater, -1) angle = self.up.angleDeg(self.floater.getPos() - self.camera.getPos()) if 10 > angle or angle > 170: self.floater.setPos(self.camera, 0, 0, 0) self.floater.setP(p) self.floater.setZ(self.floater, -1) self.camera.lookAt(self.floater.getPos(), self.up) else: self.x = None self.y = None if (self.key_map['cam_forward']): self.camera.setZ(self.camera, -25 * dt) if (self.key_map['cam_backward']): self.camera.setZ(self.camera, 25 * dt) if (self.key_map['cam_left']): self.camera.setX(self.camera, -25 * dt) if (self.key_map['cam_right']): self.camera.setX(self.camera, 25 * dt) if (self.key_map['print_cam']): print "CAMERA: Pos - %s, Hpr - %s" % (self.camera.get_pos(), self.camera.get_hpr()) self.key_map['print_cam'] = 0 self.walker.handle_command('forward', self.key_map['forward']) self.walker.handle_command('left', self.key_map['left']) self.walker.handle_command('backward', self.key_map['backward']) self.walker.handle_command('right', self.key_map['right']) self.walker.handle_command('crouch', self.key_map['crouch']) self.walker.handle_command('fire', self.key_map['fire']) if self.key_map['fire']: self.key_map['fire'] = 0 self.walker.handle_command('missile', self.key_map['missile']) if self.key_map['missile']: self.key_map['missile'] = 0 self.walker.handle_command('grenade_fire', self.key_map['grenade_fire']) if self.key_map['grenade_fire']: self.key_map['grenade_fire'] = 0 self.walker.handle_command('grenade', self.key_map['grenade']) if self.key_map['grenade']: self.key_map['grenade'] = 0 return task.cont def set_key(self, key, value): self.key_map[key] = value def drop_blocks(self): block = self.map.world.attach( FreeSolid( Block((1, 1, 1), (1, 0, 0, 1), 0.01, (0, 40, 0), (0, 0, 0)), 0.01)) for i in range(10): rand_pos = (random.randint(-25, 25), 40, random.randint(-25, 25)) block = self.map.world.attach( FreeSolid( Block((1, 1, 1), (1, 0, 0, 1), 0.01, rand_pos, (0, 0, 0)), 0.01))
class Player(object): """ Player is the main actor in the fps game """ FORWARD = Vec3(0,2,0) BACK = Vec3(0,-1,0) LEFT = Vec3(-1,0,0) RIGHT = Vec3(1,0,0) FLYUP = Vec3(0,0,1) FLYDN = Vec3(0,0,-1) STOP = Vec3(0) PORTAL_CYCLE = { 'blue' : 'orange', 'orange' : 'blue', } def __init__(self, base, fps, osd): self.base = base self.fps = fps self.osd = osd self.speed = RUN_SPEED self.walk = self.STOP self.readyToJump = False self.intoPortal = None self.mass = Mass() self.origin = self.fps.level.settings.origin self.bporigin = (999,999,999) self.oporigin = (999,999,999) self.current_target = None self.canPortal = [] self.canSetTarget = True self.selectedCubes = [] self.editorTextureStage = TextureStage('editor') self.editorSelectedTexture = loader.loadTexture('models/tex/selected.png') self.selectingForMulti = False # Init functions self.loadModel() self.makePortals() self.setUpCamera() if self.fps.editor_mode: self.createMouseCollisions() self.speed = self.speed * 5 self.attachEditorControls() self.attachEditorTasks() else: self.createCollisions() self.attachStandardControls() self.attachStandardTasks() def loadModel(self): """ make the nodepath for player """ self.node = NodePath('player') self.node.reparentTo(render) self.node.setPos(*self.origin) self.node.setScale(0.05) self.mass.pos = VBase3(self.node.getX(), self.node.getY(), self.node.getZ()) def makePortals(self): # The BLUE CUBE bpor = loader.loadModel("cube_nocol") bpor.setTag('noportals', '1') bpor.reparentTo(render) bpor.setPos(*self.bporigin) bpor.setScale(0.3,0.02,0.5) # The BLUE CUBE's camera bbuffer = self.base.win.makeTextureBuffer("B Buffer", 512, 512) bbuffer.setSort(-100) bcamera = self.base.makeCamera(bbuffer) bcamera.node().getLens().setAspectRatio(0.3/0.5) bcamera.node().getLens().setFov(15) bcamera.reparentTo(bpor) bcamera.node().setScene(render) # The ORANGE CUBE opor = loader.loadModel("cube_nocol") opor.setTag('noportals', '1') opor.reparentTo(render) opor.setPos(*self.oporigin) opor.setScale(0.3,0.02,0.5) # The ORANGE CUBE's camera obuffer = self.base.win.makeTextureBuffer("O Buffer", 512, 512) obuffer.setSort(-100) ocamera = self.base.makeCamera(obuffer) ocamera.node().getLens().setAspectRatio(0.3/0.5) ocamera.node().getLens().setFov(15) ocamera.reparentTo(opor) ocamera.node().setScene(render) # Assign the textures bpor.setTexture(obuffer.getTexture()) opor.setTexture(bbuffer.getTexture()) # Store the portals and theirs cameras self.bluePortal = bpor self.bluePortal.setHpr(0,90,0) self.orangePortal = opor self.orangePortal.setHpr(0,-90,0) self.bcamera = bcamera self.ocamera = ocamera def setUpCamera(self): """ puts camera at the players node """ pl = self.base.cam.node().getLens() pl.setFov(70) self.base.cam.node().setLens(pl) self.base.camera.reparentTo(self.node) self.base.camLens.setFov(100) if self.fps.editor_mode: self.node.lookAt(self.fps.level.cubes_hash.keys()[0]) def createCollisions(self): self.createPlayerCollisions() self.createMouseCollisions() self.createPortalCollisions() def createPlayerCollisions(self): """ create a collision solid and ray for the player """ cn = CollisionNode('player') cn.setFromCollideMask(COLLISIONMASKS['geometry']) cn.setIntoCollideMask(COLLISIONMASKS['portals'] | COLLISIONMASKS['exit'] | COLLISIONMASKS['lava']) cn.addSolid(CollisionSphere(0,0,0,3)) solid = self.node.attachNewNode(cn) # TODO : find a way to remove that, it's the cause of the little # "push me left" effect we see sometime when exiting a portal self.base.cTrav.addCollider(solid,self.base.pusher) self.base.pusher.addCollider(solid,self.node, self.base.drive.node()) # init players floor collisions ray = CollisionRay() ray.setOrigin(0,0,-.2) ray.setDirection(0,0,-1) cn = CollisionNode('playerRay') cn.setFromCollideMask(COLLISIONMASKS['player']) cn.setIntoCollideMask(BitMask32.allOff()) cn.addSolid(ray) solid = self.node.attachNewNode(cn) self.nodeGroundHandler = CollisionHandlerQueue() self.base.cTrav.addCollider(solid, self.nodeGroundHandler) # init players ceil collisions ray = CollisionRay() ray.setOrigin(0,0,.2) ray.setDirection(0,0,1) cn = CollisionNode('playerUpRay') cn.setFromCollideMask(COLLISIONMASKS['player']) cn.setIntoCollideMask(BitMask32.allOff()) cn.addSolid(ray) solid = self.node.attachNewNode(cn) self.ceilGroundHandler = CollisionHandlerQueue() self.base.cTrav.addCollider(solid, self.ceilGroundHandler) def createMouseCollisions(self): # Fire the portals firingNode = CollisionNode('mouseRay') firingNP = self.base.camera.attachNewNode(firingNode) firingNode.setFromCollideMask(COLLISIONMASKS['geometry']) firingNode.setIntoCollideMask(BitMask32.allOff()) firingRay = CollisionRay() firingRay.setOrigin(0,0,0) firingRay.setDirection(0,1,0) firingNode.addSolid(firingRay) self.firingHandler = CollisionHandlerQueue() self.base.cTrav.addCollider(firingNP, self.firingHandler) def createPortalCollisions(self): # Enter the portals cn = CollisionNode('bluePortal') cn.setFromCollideMask(COLLISIONMASKS['portals']) cn.setIntoCollideMask(BitMask32.allOff()) np = self.bluePortal.attachNewNode(cn) cn.addSolid(CollisionSphere(0,0,0,2)) h = CollisionHandlerEvent() h.addInPattern('%fn-into-%in') h.addOutPattern('%fn-outof-%in') self.base.cTrav.addCollider(np, h) cn = CollisionNode('orangePortal') cn.setFromCollideMask(COLLISIONMASKS['portals']) cn.setIntoCollideMask(BitMask32.allOff()) np = self.orangePortal.attachNewNode(cn) cn.addSolid(CollisionSphere(0,0,0,2)) h = CollisionHandlerEvent() h.addInPattern('%fn-into-%in') h.addOutPattern('%fn-outof-%in') self.base.cTrav.addCollider(np, h) def attachCommonControls(self): self.base.accept( "z" if AZERTY else "w" , self.addWalk,[self.FORWARD]) self.base.accept( "z-up" if AZERTY else "w-up" , self.addWalk,[-self.FORWARD] ) self.base.accept( "s" , self.addWalk,[self.BACK] ) self.base.accept( "s-up" , self.addWalk,[-self.BACK] ) self.base.accept( "q" if AZERTY else "a" , self.addWalk,[self.LEFT]) self.base.accept( "q-up" if AZERTY else "a-up" , self.addWalk,[-self.LEFT] ) self.base.accept( "d" , self.addWalk,[self.RIGHT] ) self.base.accept( "d-up" , self.addWalk,[-self.RIGHT] ) self.base.accept( "r-up" , self.resetPosition ) self.base.accept( "p-up" , self.showPosition ) self.base.accept( "b-up" , self.deBug ) def attachStandardControls(self): self.attachCommonControls() self.base.accept( "space" , self.__setattr__,["readyToJump",True]) self.base.accept( "space-up" , self.__setattr__,["readyToJump",False]) self.base.accept( "c-up" , self.__setattr__,["intoPortal",None] ) self.base.accept( "e-up" , self.erasePortals ) self.base.accept( "mouse1" , self.fireBlue ) self.base.accept( "mouse3" , self.fireOrange ) # Events self.base.accept( "bluePortal-into-player" , self.enterPortal, ["blue"] ) self.base.accept( "orangePortal-into-player" , self.enterPortal, ["orange"] ) self.base.accept( "bluePortal-outof-player" , self.exitPortal, ["blue"] ) self.base.accept( "orangePortal-outof-player" , self.exitPortal, ["orange"] ) self.base.accept( "levelExit-into-player" , self.levelExit) self.base.accept( "lava-into-player" , self.fallIntoLava) def attachStandardTasks(self): taskMgr.add(self.mouseUpdate, 'mouse-task') taskMgr.add(self.moveUpdate, 'move-task') taskMgr.add(self.jumpUpdate, 'jump-task') def attachEditorControls(self): self.attachCommonControls() self.base.accept( "space" , self.__setattr__, ['selectingForMulti', 1]) self.base.accept( "space-up" , self.__setattr__, ['selectingForMulti', 0]) self.base.accept( "shift-space" , self.__setattr__, ['selectingForMulti', 2]) self.base.accept( "shift-space-up" , self.__setattr__, ['selectingForMulti', 0]) self.base.accept( "c-up" , self.clearMultiSelectedCubes) self.base.accept( "mouse1" , self.selectCubeForCopy, [1]) self.base.accept( "wheel_up" , self.selectCubeForChange, [1] ) self.base.accept( "wheel_down" , self.selectCubeForChange, [-1] ) self.base.accept( "mouse3" , self.selectCubeForDelete ) self.base.accept("f11", self.saveLevel) self.base.accept("x", self.selectCubeForRectangle) self.base.accept("shift-x", self.selectCubeForRectangle, [True]) self.base.accept("l", self.addLightHere) self.base.accept("u", self.fps.level.undo, [1]) for i in range(1,10): self.base.accept( "%i-up" % (i,), self.selectCubeForCopy, [i]) for key, vec in [("a" if AZERTY else "q", self.FLYUP),("w" if AZERTY else "z", self.FLYDN)]: self.base.accept(key, self.addWalk, [vec]) self.base.accept(key + "-up", self.addWalk, [-vec]) def attachEditorTasks(self): taskMgr.add(self.mouseUpdate, 'mouse-task') taskMgr.add(self.moveInEditor, 'move-task') def deBug(self): import pdb pdb.set_trace() def showPosition(self): print self.node.getPos() print self.mass def fallIntoLava(self, *args, **kwargs): # TODO : sound and message + little delay self.erasePortals() self.resetPosition() def resetPosition(self, *args, **kwargs): self.node.setHpr(VBase3(0,0,0)) self.mass.pos = VBase3(*self.origin) self.mass.vel = VBase3(0,0,0) self.mass.force = VBase3(0,0,0) self.node.setPos(self.mass.pos) def erasePortals(self): self.bluePortal.setPos(*self.bporigin) self.orangePortal.setPos(*self.oporigin) self.bluePortal.detachNode() self.orangePortal.detachNode() self.intoPortal = None self.canPortal = [] #@oldpostracker def mouseUpdate(self,task): """ this task updates the mouse """ md = self.base.win.getPointer(0) x = md.getX() y = md.getY() if self.base.win.movePointer(0, self.base.win.getXSize()/2, self.base.win.getYSize()/2): self.node.setH(self.node.getH() - (x - self.base.win.getXSize()/2)*0.1) if self.fps.editor_mode: self.node.setP(self.node.getP() - (y - self.base.win.getYSize()/2)*0.1) else: self.base.camera.setP(self.base.camera.getP() - (y - self.base.win.getYSize()/2)*0.1) self.canSetTarget = True self.bcamera.lookAt(self.bluePortal, self.node.getPos(self.orangePortal)) self.ocamera.lookAt(self.orangePortal, self.node.getPos(self.bluePortal)) #self.canPortal = ['blue','orange'] if self.fps.editor_mode: cube, point, normal = self.selectCube() self.osd.updateTargetPosition(cube) if self.selectingForMulti: self.selectCubeForMulti() return task.cont def addWalk(self, vec): self.walk += vec def moveUpdate(self,task): """ this task makes the player move """ # move where the keys set it self.node.setPos(self.node, self.walk*globalClock.getDt()*self.speed) return task.cont #@oldpostracker def jumpUpdate(self,task): """ this task simulates gravity and makes the player jump """ # get the highest Z from the down casting ray highestZ = -100 lowestZ = 100 for i in range(self.nodeGroundHandler.getNumEntries()): entry = self.nodeGroundHandler.getEntry(i) z = entry.getSurfacePoint(render).getZ() if z > highestZ and entry.getIntoNode().getName() in ( "CollisionStuff", "Plane", "Cube" ): highestZ = z for i in range(self.ceilGroundHandler.getNumEntries()): entry = self.ceilGroundHandler.getEntry(i) z = entry.getSurfacePoint(render).getZ() if z < lowestZ and entry.getIntoNode().getName() in ( "CollisionStuff", "Plane", "Cube" ): lowestZ = z # gravity effects and jumps self.mass.simulate(globalClock.getDt()) self.node.setZ(self.mass.pos.getZ()) if highestZ > self.node.getZ()-PLAYER_TO_FLOOR_TOLERANCE: self.mass.zero() self.mass.pos.setZ(highestZ+PLAYER_TO_FLOOR_TOLERANCE) self.node.setZ(highestZ+PLAYER_TO_FLOOR_TOLERANCE) if lowestZ < self.node.getZ()+PLAYER_TO_FLOOR_TOLERANCE: self.mass.zero() self.mass.pos.setZ(lowestZ-PLAYER_TO_FLOOR_TOLERANCE) self.node.setZ(lowestZ-PLAYER_TO_FLOOR_TOLERANCE) if self.readyToJump and self.node.getZ() < highestZ + PLAYER_TO_FLOOR_TOLERANCE_FOR_REJUMP: self.mass.jump(JUMP_FORCE) return task.cont def firePortal(self, name, node): def hasTagValue(node, tag, value): if node.getTag(tag) == value: return True for pnum in range(node.getNumParents()): return hasTagValue(node.getParent(pnum), tag, value) return False self.firingHandler.sortEntries() if self.firingHandler.getNumEntries() > 0: closest = self.firingHandler.getEntry(0) if hasTagValue(closest.getIntoNode(), 'noportals', '1'): return point = closest.getSurfacePoint(render) normal = closest.getSurfaceNormal(render) node.setPos(point) node.lookAt(point + normal) node.reparentTo(render) dest = self.PORTAL_CYCLE[name] if dest not in self.canPortal: self.canPortal.append(dest) def fireBlue(self, *arg, **kwargs): self.firePortal("blue", self.bluePortal) def fireOrange(self, *arg, **kwargs): self.firePortal("orange", self.orangePortal) #@oldpostracker def enterPortal(self, color, collision): if self.intoPortal is None and color in self.canPortal: self.intoPortal = color portal = {"orange": self.bluePortal, "blue": self.orangePortal}.get(color) otherportal = {"orange": self.orangePortal, "blue": self.bluePortal}.get(color) # Handle horizontal portals : if portal.getH() == 0: self.node.setP(0) self.node.setR(0) elif otherportal.getH() == 0: self.node.setH(portal.getH()) self.node.setP(0) self.node.setR(0) else: # New HPR is relative to 'new' portal but it the 'same' value # as the old HPR seen from the 'other' portal oldh_fromportal = self.node.getH(otherportal) self.node.setHpr(Vec3(0,0,0)) self.node.setH(portal, 180-oldh_fromportal) newh_fromportal = self.node.getH(portal) self.node.setPos(portal, self.walk * 10.) self.mass.pos = self.node.getPos() # Make half a turn (only if we straffing without walking) if self.walk.getY() == 0 and self.walk.getX() != 0: self.node.setH(self.node, 180) self.node.setPos(self.node, self.walk * 10) #@oldpostracker def exitPortal(self, color, collision): # When you entered the blue portal, you have to exit the orange one if self.intoPortal != color: self.intoPortal = None def levelExit(self, event): if self.fps.level.settings.next_level: self.fps.level.loadlevel(self.fps.level.settings.next_level) self.origin = self.fps.level.settings.origin self.resetPosition() self.erasePortals() self.walk = self.STOP else: print "You won !" sys.exit(0) # EDITOR MODE def selectCube(self): self.firingHandler.sortEntries() if self.firingHandler.getNumEntries() > 0: closest = self.firingHandler.getEntry(0) return closest.getIntoNodePath().getParent().getParent(), closest.getSurfacePoint(render), closest.getSurfaceNormal(render) # render/cube.egg/-PandaNode/-GeomNode else: return None, None, None def clearMultiSelectedCubes(self): for c in self.selectedCubes: c.clearTexture(self.editorTextureStage) self.selectedCubes = [] def selectCubeForMulti(self): cube, point, normal = self.selectCube() if cube: if self.selectingForMulti == 1: cube.setTexture(self.editorTextureStage, self.editorSelectedTexture) if cube not in self.selectedCubes: self.selectedCubes.append(cube) elif cube in self.selectedCubes: cube.clearTexture(self.editorTextureStage) self.selectedCubes.remove(cube) def selectCubeForCopy(self, qty = 1): cube, point, normal = self.selectCube() if not (cube and point and normal): return if self.selectedCubes: for c in self.selectedCubes: self.fps.level.copyCube(c, normal, qty) self.clearMultiSelectedCubes() else: self.fps.level.copyCube(cube, normal, qty) def selectCubeForDelete(self): cube, point, normal = self.selectCube() if not (cube and point and normal): return if self.selectedCubes: for c in self.selectedCubes: self.fps.level.deleteCube(c) self.clearMultiSelectedCubes() else: self.fps.level.deleteCube(cube) def selectCubeForChange(self, step = 1): cube, point, normal = self.selectCube() if not (cube and point and normal): return if self.selectedCubes: for c in self.selectedCubes: self.fps.level.changeCube(c, step) else: self.fps.level.changeCube(cube, step) def selectCubeForRectangle(self, makeRoom = False): cube, point, normal = self.selectCube() if makeRoom: self.fps.level.createRoom(cube, self.node) # creates a room from the selected cube to the player(camera) position else: self.fps.level.createRectangle(cube, self.node) # creates a rectangle from the selected cube to the player(camera) position def saveLevel(self): camerapos = [self.node.getX(), self.node.getY(), self.node.getZ()] levelname = self.fps.levelname self.fps.level.savelevel(levelname, camerapos) def addLightHere(self): camerapos = [self.node.getX(), self.node.getY(), self.node.getZ()] self.fps.level.addLightHere(camerapos) def moveInEditor(self,task): self.node.setPos(self.node, self.walk*globalClock.getDt()*self.speed) self.osd.updatePosition(self.node) return task.cont
class CogdoFlyingCameraManager: def __init__(self, cam, parent, player, level): self._toon = player.toon self._camera = cam self._parent = parent self._player = player self._level = level self._enabled = False def enable(self): if self._enabled: return self._toon.detachCamera() self._prevToonY = 0.0 levelBounds = self._level.getBounds() l = Globals.Camera.LevelBoundsFactor self._bounds = ((levelBounds[0][0] * l[0], levelBounds[0][1] * l[0]), (levelBounds[1][0] * l[1], levelBounds[1][1] * l[1]), (levelBounds[2][0] * l[2], levelBounds[2][1] * l[2])) self._lookAtZ = self._toon.getHeight() + Globals.Camera.LookAtToonHeightOffset self._camParent = NodePath('CamParent') self._camParent.reparentTo(self._parent) self._camParent.setPos(self._toon, 0, 0, 0) self._camParent.setHpr(180, Globals.Camera.Angle, 0) self._camera.reparentTo(self._camParent) self._camera.setPos(0, Globals.Camera.Distance, 0) self._camera.lookAt(self._toon, 0, 0, self._lookAtZ) self._cameraLookAtNP = NodePath('CameraLookAt') self._cameraLookAtNP.reparentTo(self._camera.getParent()) self._cameraLookAtNP.setPosHpr(self._camera.getPos(), self._camera.getHpr()) self._levelBounds = self._level.getBounds() self._enabled = True self._frozen = False self._initCollisions() def _initCollisions(self): self._camCollRay = CollisionRay() camCollNode = CollisionNode('CameraToonRay') camCollNode.addSolid(self._camCollRay) camCollNode.setFromCollideMask(OTPGlobals.WallBitmask | OTPGlobals.CameraBitmask | ToontownGlobals.FloorEventBitmask | ToontownGlobals.CeilingBitmask) camCollNode.setIntoCollideMask(0) self._camCollNP = self._camera.attachNewNode(camCollNode) self._camCollNP.show() self._collOffset = Vec3(0, 0, 0.5) self._collHandler = CollisionHandlerQueue() self._collTrav = CollisionTraverser() self._collTrav.addCollider(self._camCollNP, self._collHandler) self._betweenCamAndToon = {} self._transNP = NodePath('trans') self._transNP.reparentTo(render) self._transNP.setTransparency(True) self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon) self._transNP.setBin('fixed', 10000) def _destroyCollisions(self): self._collTrav.removeCollider(self._camCollNP) self._camCollNP.removeNode() del self._camCollNP del self._camCollRay del self._collHandler del self._collOffset del self._betweenCamAndToon self._transNP.removeNode() del self._transNP def freeze(self): self._frozen = True def unfreeze(self): self._frozen = False def disable(self): if not self._enabled: return self._destroyCollisions() self._camera.wrtReparentTo(render) self._cameraLookAtNP.removeNode() del self._cameraLookAtNP self._camParent.removeNode() del self._camParent del self._prevToonY del self._lookAtZ del self._bounds del self._frozen self._enabled = False def update(self, dt = 0.0): self._updateCam(dt) self._updateCollisions() def _updateCam(self, dt): toonPos = self._toon.getPos() camPos = self._camParent.getPos() x = camPos[0] z = camPos[2] toonWorldX = self._toon.getX(render) maxX = Globals.Camera.MaxSpinX toonWorldX = clamp(toonWorldX, -1.0 * maxX, maxX) spinAngle = Globals.Camera.MaxSpinAngle * toonWorldX * toonWorldX / (maxX * maxX) newH = 180.0 + spinAngle self._camParent.setH(newH) spinAngle = spinAngle * (pi / 180.0) distBehindToon = Globals.Camera.SpinRadius * cos(spinAngle) distToRightOfToon = Globals.Camera.SpinRadius * sin(spinAngle) d = self._camParent.getX() - clamp(toonPos[0], *self._bounds[0]) if abs(d) > Globals.Camera.LeewayX: if d > Globals.Camera.LeewayX: x = toonPos[0] + Globals.Camera.LeewayX else: x = toonPos[0] - Globals.Camera.LeewayX x = self._toon.getX(render) + distToRightOfToon boundToonZ = min(toonPos[2], self._bounds[2][1]) d = z - boundToonZ if d > Globals.Camera.MinLeewayZ: if self._player.velocity[2] >= 0 and toonPos[1] != self._prevToonY or self._player.velocity[2] > 0: z = boundToonZ + d * INVERSE_E ** (dt * Globals.Camera.CatchUpRateZ) elif d > Globals.Camera.MaxLeewayZ: z = boundToonZ + Globals.Camera.MaxLeewayZ elif d < -Globals.Camera.MinLeewayZ: z = boundToonZ - Globals.Camera.MinLeewayZ if self._frozen: y = camPos[1] else: y = self._toon.getY(render) - distBehindToon self._camParent.setPos(x, smooth(camPos[1], y), smooth(camPos[2], z)) if toonPos[2] < self._bounds[2][1]: h = self._cameraLookAtNP.getH() if d >= Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._toon, 0, 0, self._lookAtZ) elif d <= -Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._camParent, 0, 0, self._lookAtZ) self._cameraLookAtNP.setHpr(h, self._cameraLookAtNP.getP(), 0) self._camera.setHpr(smooth(self._camera.getHpr(), self._cameraLookAtNP.getHpr())) self._prevToonY = toonPos[1] def _updateCollisions(self): pos = self._toon.getPos(self._camera) + self._collOffset self._camCollRay.setOrigin(pos) direction = -Vec3(pos) direction.normalize() self._camCollRay.setDirection(direction) self._collTrav.traverse(render) nodesInBetween = {} if self._collHandler.getNumEntries() > 0: self._collHandler.sortEntries() for entry in self._collHandler.getEntries(): name = entry.getIntoNode().getName() if name.find('col_') >= 0: np = entry.getIntoNodePath().getParent() if np not in nodesInBetween: nodesInBetween[np] = np.getParent() for np in nodesInBetween.keys(): if np in self._betweenCamAndToon: del self._betweenCamAndToon[np] else: np.setTransparency(True) np.wrtReparentTo(self._transNP) if np.getName().find('lightFixture') >= 0: np.find('**/*floor_mesh').hide() elif np.getName().find('platform') >= 0: np.find('**/*Floor').hide() for np, parent in self._betweenCamAndToon.items(): np.wrtReparentTo(parent) np.setTransparency(False) if np.getName().find('lightFixture') >= 0: np.find('**/*floor_mesh').show() elif np.getName().find('platform') >= 0: np.find('**/*Floor').show() self._betweenCamAndToon = nodesInBetween