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 HitscanWeapon(Weapon): def __init__(self, mask, damage, knockback, range=None): Weapon.__init__(self, mask, range, damage, knockback) if range is None: self.ray = CollisionRay(0, 0, 0, 0, 1, 0) else: self.ray = CollisionSegment(0, 0, 0, 1, 0, 0) rayNode = CollisionNode("playerRay") rayNode.addSolid(self.ray) rayNode.setFromCollideMask(mask) rayNode.setIntoCollideMask(0) self.rayNodePath = render.attachNewNode(rayNode) self.rayQueue = CollisionHandlerQueue() self.traverser = CollisionTraverser() self.traverser.addCollider(self.rayNodePath, self.rayQueue) def performRayCast(self, origin, direction): if isinstance(self.ray, CollisionRay): self.ray.setOrigin(origin) self.ray.setDirection(direction) else: self.ray.setPointA(origin) self.ray.setPointB(origin + direction * self.range) self.traverser.traverse(render) if self.rayQueue.getNumEntries() > 0: self.rayQueue.sortEntries() rayHit = self.rayQueue.getEntry(0) return True, rayHit else: return False, None def fire(self, owner, dt): Weapon.fire(self, owner, dt) rayDir = owner.weaponNP.getQuat(render).getForward() hit, hitEntry = self.performRayCast(owner.weaponNP.getPos(render), rayDir) if hit: hitNP = hitEntry.getIntoNodePath() if hitNP.hasPythonTag(TAG_OWNER): subject = hitNP.getPythonTag(TAG_OWNER) subject.alterHealth(-self.damage, rayDir * self.knockback, self.flinchValue) def cleanup(self): self.traverser.removeCollider(self.rayNodePath) self.traverser = None if self.rayNodePath is not None: self.rayNodePath.removeNode() self.rayNodePath = None Weapon.cleanup(self)
class SmartCamera: UPDATE_TASK_NAME = 'update_smartcamera' notify = directNotify.newCategory('SmartCamera') def __init__(self): self.cTrav = CollisionTraverser('cam_traverser') base.pushCTrav(self.cTrav) self.cTrav.setRespectPrevTransform(1) self.default_pos = None self.parent = None self.initialized = False self.started = False self.camFloorRayNode = None self.ccRay2 = None self.ccRay2Node = None self.ccRay2NodePath = None self.ccRay2BitMask = None self.ccRay2MoveNodePath = None self.camFloorCollisionBroadcaster = None self.notify.debug('SmartCamera initialized!') return def lerpCameraFov(self, fov, time): taskMgr.remove('cam-fov-lerp-play') oldFov = base.camLens.getHfov() if abs(fov - oldFov) > 0.1: def setCamFov(fov): base.camLens.setMinFov(fov / (4.0 / 3.0)) self.camLerpInterval = LerpFunctionInterval(setCamFov, fromData=oldFov, toData=fov, duration=time, name='cam-fov-lerp') self.camLerpInterval.start() def setCameraFov(self, fov): self.fov = fov if not (self.isPageDown or self.isPageUp): base.camLens.setMinFov(self.fov / (4.0 / 3.0)) def initCameraPositions(self): camHeight = max(base.localAvatar.getHeight(), 3.0) nrCamHeight = base.localAvatar.getHeight() heightScaleFactor = camHeight * 0.3333333333 defLookAt = Point3(0.0, 1.5, camHeight) self.firstPersonCamPos = Point3(0.0, 0.7, nrCamHeight * 5.0) scXoffset = 3.0 scPosition = (Point3(scXoffset - 1, -10.0, camHeight + 5.0), Point3(scXoffset, 2.0, camHeight)) self.cameraPositions = [ (Point3(0.0, -9.0 * heightScaleFactor, camHeight), defLookAt, Point3(0.0, camHeight, camHeight * 4.0), Point3(0.0, camHeight, camHeight * -1.0), 0), ( Point3(0.0, 0.7, camHeight), defLookAt, Point3(0.0, camHeight, camHeight * 1.33), Point3(0.0, camHeight, camHeight * 0.66), 1), ( Point3(5.7 * heightScaleFactor, 7.65 * heightScaleFactor, camHeight + 2.0), Point3(0.0, 1.0, camHeight), Point3(0.0, 1.0, camHeight * 4.0), Point3(0.0, 1.0, camHeight * -1.0), 0), ( Point3(0.0, 8.65 * heightScaleFactor, camHeight), Point3(0.0, 1.0, camHeight), Point3(0.0, 1.0, camHeight * 4.0), Point3(0.0, 1.0, camHeight * -1.0), 0), ( Point3(0.0, -24.0 * heightScaleFactor, camHeight + 4.0), defLookAt, Point3(0.0, 1.5, camHeight * 4.0), Point3(0.0, 1.5, camHeight * -1.0), 0), ( Point3(0.0, -12.0 * heightScaleFactor, camHeight + 4.0), defLookAt, Point3(0.0, 1.5, camHeight * 4.0), Point3(0.0, 1.5, camHeight * -1.0), 0)] def pageUp(self): if not base.localAvatar.avatarMovementEnabled: return if not self.isPageUp: self.isPageDown = 0 self.isPageUp = 1 self.lerpCameraFov(70, 0.6) self.setCameraPositionByIndex(self.cameraIndex) else: self.clearPageUpDown() def pageDown(self): if not base.localAvatar.avatarMovementEnabled: return if not self.isPageDown: self.isPageUp = 0 self.isPageDown = 1 self.lerpCameraFov(70, 0.6) self.setCameraPositionByIndex(self.cameraIndex) else: self.clearPageUpDown() def clearPageUpDown(self): if self.isPageDown or self.isPageUp: self.lerpCameraFov(self.fov, 0.6) self.isPageDown = 0 self.isPageUp = 0 self.setCameraPositionByIndex(self.cameraIndex) def nextCameraPos(self, forward): if not base.localAvatar.avatarMovementEnabled: return self.__cameraHasBeenMoved = 1 if forward: self.cameraIndex += 1 if self.cameraIndex > len(self.cameraPositions) - 1: self.cameraIndex = 0 else: self.cameraIndex -= 1 if self.cameraIndex < 0: self.cameraIndex = len(self.cameraPositions) - 1 self.setCameraPositionByIndex(self.cameraIndex) def setCameraPositionByIndex(self, index): self.notify.debug('switching to camera position %s' % index) self.setCameraSettings(self.cameraPositions[index]) def setCameraSettings(self, camSettings): self.setIdealCameraPos(camSettings[0]) if self.isPageUp and self.isPageDown or not self.isPageUp and not self.isPageDown: self.__cameraHasBeenMoved = 1 self.setLookAtPoint(camSettings[1]) else: if self.isPageUp: self.__cameraHasBeenMoved = 1 self.setLookAtPoint(camSettings[2]) else: if self.isPageDown: self.__cameraHasBeenMoved = 1 self.setLookAtPoint(camSettings[3]) else: self.notify.error('This case should be impossible.') self.__disableSmartCam = camSettings[4] if self.__disableSmartCam: self.putCameraFloorRayOnAvatar() self.cameraZOffset = 0.0 def set_default_pos(self, pos): self.default_pos = pos def get_default_pos(self): return self.default_pos def set_parent(self, parent): self.parent = parent def get_parent(self): return self.parent def getVisibilityPoint(self): return Point3(0.0, 0.0, base.localAvatar.getHeight()) def setLookAtPoint(self, la): self.__curLookAt = Point3(la) def getLookAtPoint(self): return Point3(self.__curLookAt) def setIdealCameraPos(self, pos): self.__idealCameraPos = Point3(pos) self.updateSmartCameraCollisionLineSegment() def getIdealCameraPos(self): return Point3(self.__idealCameraPos) def getCompromiseCameraPos(self): if self.__idealCameraObstructed == 0: compromisePos = self.getIdealCameraPos() else: visPnt = self.getVisibilityPoint() idealPos = self.getIdealCameraPos() distance = Vec3(idealPos - visPnt).length() ratio = self.closestObstructionDistance / distance compromisePos = idealPos * ratio + visPnt * (1 - ratio) liftMult = 1.0 - ratio * ratio compromisePos = Point3(compromisePos[0], compromisePos[1], compromisePos[2] + base.localAvatar.getHeight() * 0.4 * liftMult) compromisePos.setZ(compromisePos[2] + self.cameraZOffset) return compromisePos def updateSmartCameraCollisionLineSegment(self): pointB = self.getIdealCameraPos() pointA = self.getVisibilityPoint() vectorAB = Vec3(pointB - pointA) lengthAB = vectorAB.length() if lengthAB > 0.001: self.ccLine.setPointA(pointA) self.ccLine.setPointB(pointB) def initializeSmartCamera(self): self.__idealCameraObstructed = 0 self.closestObstructionDistance = 0.0 self.cameraIndex = 0 self.cameraPositions = [] self.auxCameraPositions = [] self.cameraZOffset = 0.0 self.setGeom(render) self.__onLevelGround = 0 self.__camCollCanMove = 0 self.__disableSmartCam = 0 self.initializeSmartCameraCollisions() self._smartCamEnabled = False self.isPageUp = 0 self.isPageDown = 0 self.fov = CIGlobals.DefaultCameraFov def enterFirstPerson(self): self.stop_smartcamera() if hasattr(self.get_parent(), 'toon_head'): head = self.get_parent().toon_head camera.reparentTo(head) camera.setPos(0, -0.35, 0) camera.setHpr(0, 0, 0) def exitFirstPerson(self): self.initialize_smartcamera() self.initialize_smartcamera_collisions() self.start_smartcamera() def putCameraFloorRayOnAvatar(self): self.camFloorRayNode.setPos(base.localAvatar, 0, 0, 5) def putCameraFloorRayOnCamera(self): self.camFloorRayNode.setPos(self.ccSphereNodePath, 0, 0, 0) def recalcCameraSphere(self): nearPlaneDist = base.camLens.getNear() hFov = base.camLens.getHfov() vFov = base.camLens.getVfov() hOff = nearPlaneDist * math.tan(deg2Rad(hFov / 2.0)) vOff = nearPlaneDist * math.tan(deg2Rad(vFov / 2.0)) camPnts = [Point3(hOff, nearPlaneDist, vOff), Point3(-hOff, nearPlaneDist, vOff), Point3(hOff, nearPlaneDist, -vOff), Point3(-hOff, nearPlaneDist, -vOff), Point3(0.0, 0.0, 0.0)] avgPnt = Point3(0.0, 0.0, 0.0) for camPnt in camPnts: avgPnt = avgPnt + camPnt avgPnt = avgPnt / len(camPnts) sphereRadius = 0.0 for camPnt in camPnts: dist = Vec3(camPnt - avgPnt).length() if dist > sphereRadius: sphereRadius = dist avgPnt = Point3(avgPnt) self.ccSphereNodePath.setPos(avgPnt) self.ccSphereNodePath2.setPos(avgPnt) self.ccSphere.setRadius(sphereRadius) def setGeom(self, geom): self.__geom = geom def initializeSmartCameraCollisions(self): if self.initialized: return self.ccTrav = CollisionTraverser('LocalAvatar.ccTrav') self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) self.ccLineNode = CollisionNode('ccLineNode') self.ccLineNode.addSolid(self.ccLine) self.ccLineNodePath = base.localAvatar.attachNewNode(self.ccLineNode) self.ccLineBitMask = CIGlobals.CameraBitmask self.ccLineNode.setFromCollideMask(self.ccLineBitMask) self.ccLineNode.setIntoCollideMask(BitMask32.allOff()) self.camCollisionQueue = CollisionHandlerQueue() self.ccTrav.addCollider(self.ccLineNodePath, self.camCollisionQueue) self.ccSphere = CollisionSphere(0, 0, 0, 1) self.ccSphereNode = CollisionNode('ccSphereNode') self.ccSphereNode.addSolid(self.ccSphere) self.ccSphereNodePath = base.camera.attachNewNode(self.ccSphereNode) self.ccSphereNode.setFromCollideMask(CIGlobals.CameraBitmask) self.ccSphereNode.setIntoCollideMask(BitMask32.allOff()) self.camPusher = CollisionHandlerPusher() self.camPusher.addCollider(self.ccSphereNodePath, base.camera) self.camPusher.setCenter(base.localAvatar) self.ccPusherTrav = CollisionTraverser('LocalAvatar.ccPusherTrav') self.ccSphere2 = self.ccSphere self.ccSphereNode2 = CollisionNode('ccSphereNode2') self.ccSphereNode2.addSolid(self.ccSphere2) self.ccSphereNodePath2 = base.camera.attachNewNode(self.ccSphereNode2) self.ccSphereNode2.setFromCollideMask(CIGlobals.CameraBitmask) self.ccSphereNode2.setIntoCollideMask(BitMask32.allOff()) self.camPusher2 = CollisionHandlerPusher() self.ccPusherTrav.addCollider(self.ccSphereNodePath2, self.camPusher2) self.camPusher2.addCollider(self.ccSphereNodePath2, base.camera) self.camPusher2.setCenter(base.localAvatar) self.camFloorRayNode = base.localAvatar.attachNewNode('camFloorRayNode') self.ccRay = CollisionRay(0.0, 0.0, 0.0, 0.0, 0.0, -1.0) self.ccRayNode = CollisionNode('ccRayNode') self.ccRayNode.addSolid(self.ccRay) self.ccRayNodePath = self.camFloorRayNode.attachNewNode(self.ccRayNode) self.ccRayBitMask = CIGlobals.FloorBitmask self.ccRayNode.setFromCollideMask(self.ccRayBitMask) self.ccRayNode.setIntoCollideMask(BitMask32.allOff()) self.ccTravFloor = CollisionTraverser('LocalAvatar.ccTravFloor') self.camFloorCollisionQueue = CollisionHandlerQueue() self.ccTravFloor.addCollider(self.ccRayNodePath, self.camFloorCollisionQueue) self.ccTravOnFloor = CollisionTraverser('LocalAvatar.ccTravOnFloor') self.ccRay2 = CollisionRay(0.0, 0.0, 0.0, 0.0, 0.0, -1.0) self.ccRay2Node = CollisionNode('ccRay2Node') self.ccRay2Node.addSolid(self.ccRay2) self.ccRay2NodePath = self.camFloorRayNode.attachNewNode(self.ccRay2Node) self.ccRay2BitMask = CIGlobals.FloorBitmask self.ccRay2Node.setFromCollideMask(self.ccRay2BitMask) self.ccRay2Node.setIntoCollideMask(BitMask32.allOff()) self.ccRay2MoveNodePath = hidden.attachNewNode('ccRay2MoveNode') self.camFloorCollisionBroadcaster = CollisionHandlerFloor() self.camFloorCollisionBroadcaster.setInPattern('on-floor') self.camFloorCollisionBroadcaster.setOutPattern('off-floor') self.camFloorCollisionBroadcaster.addCollider(self.ccRay2NodePath, self.ccRay2MoveNodePath) self.cTrav.addCollider(self.ccRay2NodePath, self.camFloorCollisionBroadcaster) self.initialized = True def deleteSmartCameraCollisions(self): del self.ccTrav del self.ccLine del self.ccLineNode self.ccLineNodePath.removeNode() del self.ccLineNodePath del self.camCollisionQueue del self.ccRay del self.ccRayNode self.ccRayNodePath.removeNode() del self.ccRayNodePath del self.ccRay2 del self.ccRay2Node self.ccRay2NodePath.removeNode() del self.ccRay2NodePath self.ccRay2MoveNodePath.removeNode() del self.ccRay2MoveNodePath del self.ccTravOnFloor del self.ccTravFloor del self.camFloorCollisionQueue del self.camFloorCollisionBroadcaster del self.ccSphere del self.ccSphereNode self.ccSphereNodePath.removeNode() del self.ccSphereNodePath del self.camPusher del self.ccPusherTrav del self.ccSphere2 del self.ccSphereNode2 self.ccSphereNodePath2.removeNode() del self.ccSphereNodePath2 del self.camPusher2 self.initialized = False def startUpdateSmartCamera(self): if self.started: return self.__floorDetected = 0 self.__cameraHasBeenMoved = 1 self.recalcCameraSphere() self.__instantaneousCamPos = camera.getPos() self.cTrav.addCollider(self.ccSphereNodePath, self.camPusher) self.ccTravOnFloor.addCollider(self.ccRay2NodePath, self.camFloorCollisionBroadcaster) self.__disableSmartCam = 0 self.__lastPosWrtRender = camera.getPos(render) + 1 self.__lastHprWrtRender = camera.getHpr(render) + 1 taskName = base.localAvatar.taskName('updateSmartCamera') taskMgr.remove(taskName) taskMgr.add(self.updateSmartCamera, taskName, priority=47) self.started = True def stopUpdateSmartCamera(self): self.cTrav.removeCollider(self.ccSphereNodePath) self.ccTravOnFloor.removeCollider(self.ccRay2NodePath) taskName = base.localAvatar.taskName('updateSmartCamera') taskMgr.remove(taskName) camera.setPos(self.getIdealCameraPos()) self.started = False def updateSmartCamera(self, task): if not self.__camCollCanMove and not self.__cameraHasBeenMoved: if self.__lastPosWrtRender == camera.getPos(render): if self.__lastHprWrtRender == camera.getHpr(render): return Task.cont self.__cameraHasBeenMoved = 0 self.__lastPosWrtRender = camera.getPos(render) self.__lastHprWrtRender = camera.getHpr(render) self.__idealCameraObstructed = 0 if not self.__disableSmartCam: self.ccTrav.traverse(self.__geom) if self.camCollisionQueue.getNumEntries() > 0: try: self.camCollisionQueue.sortEntries() self.handleCameraObstruction(self.camCollisionQueue.getEntry(0)) except AssertionError: pass if not self.__onLevelGround: self.handleCameraFloorInteraction() if not self.__idealCameraObstructed: self.nudgeCamera() if not self.__disableSmartCam: self.ccPusherTrav.traverse(self.__geom) self.putCameraFloorRayOnCamera() self.ccTravOnFloor.traverse(self.__geom) return Task.cont def positionCameraWithPusher(self, pos, lookAt): camera.setPos(pos) self.ccPusherTrav.traverse(self.__geom) camera.lookAt(lookAt) def nudgeCamera(self): CLOSE_ENOUGH = 0.1 curCamPos = self.__instantaneousCamPos curCamHpr = camera.getHpr() targetCamPos = self.getCompromiseCameraPos() targetCamLookAt = self.getLookAtPoint() posDone = 0 if Vec3(curCamPos - targetCamPos).length() <= CLOSE_ENOUGH: camera.setPos(targetCamPos) posDone = 1 camera.setPos(targetCamPos) camera.lookAt(targetCamLookAt) targetCamHpr = camera.getHpr() hprDone = 0 if Vec3(curCamHpr - targetCamHpr).length() <= CLOSE_ENOUGH: hprDone = 1 if posDone and hprDone: return lerpRatio = 0.15 lerpRatio = 1 - pow(1 - lerpRatio, globalClock.getDt() * 30.0) self.__instantaneousCamPos = targetCamPos * lerpRatio + curCamPos * (1 - lerpRatio) if self.__disableSmartCam or not self.__idealCameraObstructed: newHpr = targetCamHpr * lerpRatio + curCamHpr * (1 - lerpRatio) else: newHpr = targetCamHpr camera.setPos(self.__instantaneousCamPos) camera.setHpr(newHpr) def popCameraToDest(self): newCamPos = self.getCompromiseCameraPos() newCamLookAt = self.getLookAtPoint() self.positionCameraWithPusher(newCamPos, newCamLookAt) self.__instantaneousCamPos = camera.getPos() def handleCameraObstruction(self, camObstrCollisionEntry): collisionPoint = camObstrCollisionEntry.getSurfacePoint(self.ccLineNodePath) collisionVec = Vec3(collisionPoint - self.ccLine.getPointA()) distance = collisionVec.length() self.__idealCameraObstructed = 1 self.closestObstructionDistance = distance self.popCameraToDest() def handleCameraFloorInteraction(self): self.putCameraFloorRayOnCamera() self.ccTravFloor.traverse(self.__geom) if self.__onLevelGround: return if self.camFloorCollisionQueue.getNumEntries() == 0: return self.camFloorCollisionQueue.sortEntries() camObstrCollisionEntry = self.camFloorCollisionQueue.getEntry(0) camHeightFromFloor = camObstrCollisionEntry.getSurfacePoint(self.ccRayNodePath)[2] self.cameraZOffset = camera.getPos()[2] + camHeightFromFloor if self.cameraZOffset < 0: self.cameraZOffset = 0 if self.__floorDetected == 0: self.__floorDetected = 1 self.popCameraToDest()
class MyApp(ShowBase): def __init__(self): ShowBase.__init__(self) self.seeNode = self.render.attachNewNode("see") self.cam.reparentTo(self.seeNode) self.cam.setPos(0, 0, 5) self.fpscamera = fpscontroller.FpsController(self, self.seeNode) self.fpscamera.setFlyMode(True) self.prevPos = self.fpscamera.getPos() self.prevInto = None self.info = self.genLabelText("Position: <unknown>", 4) self.makeInstructions() self.initCollisions() self.leftColor = LVecBase4i(224, 224, 64, 255) self.rightColor = LVecBase4i(64, 224, 224, 255) self.isDrawing = False self.toggleDrawing() self.accept("escape", sys.exit) # Escape quits self.accept("enter", self.toggleDrawing) def initCollisions(self): # Initialize the collision traverser. self.cTrav = CollisionTraverser() self.cTrav.showCollisions(self.render) # self.cQueue = CollisionHandlerQueue() # Initialize the Pusher collision handler. # self.pusher = CollisionHandlerPusher() self.pusher = CollisionHandlerFloor() ### player print DirectNotifyGlobal.directNotify.getCategories() # Create a collsion node for this object. playerNode = CollisionNode("player") playerNode.addSolid(CollisionSphere(0, 0, 0, 1)) # playerNode.setFromCollideMask(BitMask32.bit(0)) # playerNode.setIntoCollideMask(BitMask32.allOn()) # Attach the collision node to the object's model. self.playerC = self.fpscamera.player.attachNewNode(playerNode) # Set the object's collision node to render as visible. self.playerC.show() # Add the 'player' collision node to the Pusher collision handler. # self.pusher.addCollider(self.playerC, self.fpscamera.player) # self.pusher.addCollider(playerC, self.fpscamera.player) # self.cTrav.addCollider(self.playerC, self.cQueue) def toggleDrawing(self): self.isDrawing = not self.isDrawing if self.isDrawing: self.drawText.setText("Enter: Turn off drawing") self.fpscamera.setFlyMode(True) self.prevPos = None self.cTrav.removeCollider(self.playerC) self.pusher.removeCollider(self.playerC) self.removeTask("updatePhysics") self.addTask(self.drawHere, "drawHere") self.geomNode = GeomNode("geomNode") self.geomNodePath = self.render.attachNewNode(self.geomNode) self.geomNodePath.setTwoSided(True) # apparently p3tinydisplay needs this self.geomNodePath.setColorOff() # Create a collision node for this object. self.floorCollNode = CollisionNode("geom") # self.floorCollNode.setFromCollideMask(BitMask32.bit(0)) # self.floorCollNode.setIntoCollideMask(BitMask32.allOn()) # Attach the collision node to the object's model. floorC = self.geomNodePath.attachNewNode(self.floorCollNode) # Set the object's collision node to render as visible. floorC.show() # self.pusher.addCollider(floorC, self.geomNodePath) self.newVertexData() self.newGeom() else: self.drawText.setText("Enter: Turn on drawing") self.removeTask("drawHere") if self.prevPos: self.completePath() self.fpscamera.setFlyMode(True) self.drive.setPos(self.fpscamera.getPos()) self.cTrav.addCollider(self.playerC, self.pusher) self.pusher.addCollider(self.playerC, self.fpscamera.player) self.taskMgr.add(self.updatePhysics, "updatePhysics") def newVertexData(self): fmt = GeomVertexFormat.getV3c4() # fmt = GeomVertexFormat.getV3n3c4() self.vertexData = GeomVertexData("path", fmt, Geom.UHStatic) self.vertexWriter = GeomVertexWriter(self.vertexData, "vertex") # self.normalWriter = GeomVertexWriter(self.vertexData, 'normal') self.colorWriter = GeomVertexWriter(self.vertexData, "color") def newGeom(self): self.triStrips = GeomTristrips(Geom.UHDynamic) self.geom = Geom(self.vertexData) self.geom.addPrimitive(self.triStrips) def makeInstructions(self): OnscreenText(text="Draw Path by Walking", style=1, fg=(1, 1, 0, 1), pos=(0.5, -0.95), scale=0.07) self.drawText = self.genLabelText("", 0) self.genLabelText("Walk (W/S/A/D), Jump=Space, Look=PgUp/PgDn", 1) self.genLabelText(" (hint, go backwards with S to see your path immediately)", 2) self.genLabelText("ESC: Quit", 3) def genLabelText(self, text, i): return OnscreenText(text=text, pos=(-1.3, 0.95 - 0.05 * i), fg=(1, 1, 0, 1), align=TextNode.ALeft, scale=0.05) def drawHere(self, task): pos = self.fpscamera.getPos() self.info.setText( "Position: {0}, {1}, {2} at {3} by {4}".format( int(pos.x * 100) / 100.0, int(pos.y * 100) / 100.0, int(pos.z) / 100.0, self.fpscamera.getHeading(), self.fpscamera.getLookAngle(), ) ) prevPos = self.prevPos if not prevPos: self.prevPos = pos elif (pos - prevPos).length() > 1: self.drawQuadTo(prevPos, pos, 2) row = self.vertexWriter.getWriteRow() numPrims = self.triStrips.getNumPrimitives() if numPrims == 0: primVerts = row else: primVerts = row - self.triStrips.getPrimitiveEnd(numPrims - 1) if primVerts >= 4: self.triStrips.closePrimitive() if row >= 256: print "Packing and starting anew" newGeom = True self.geom.unifyInPlace(row, False) else: newGeom = False self.completePath() if newGeom: self.newVertexData() self.newGeom() if not newGeom: self.triStrips.addConsecutiveVertices(row - 2, 2) else: self.drawQuadTo(prevPos, pos, 2) self.leftColor[1] += 63 self.rightColor[2] += 37 self.prevPos = pos return task.cont def drawLineTo(self, pos, color): self.vertexWriter.addData3f(pos.x, pos.y, pos.z) # self.normalWriter.addData3f(0, 0, 1) self.colorWriter.addData4i(color) self.triStrips.addNextVertices(1) def drawQuadTo(self, a, b, width): """ a (to) b are vectors defining a line bisecting a new quad. """ into = b - a if abs(into.x) + abs(into.y) < 1: if not self.prevInto: return into = self.prevInto print into else: into.normalize() # the perpendicular of (a,b) is (-b,a); we want the path to be "flat" in Z=space if self.vertexWriter.getWriteRow() == 0: self.drawQuadRow(a, into, width) self.drawQuadRow(b, into, width) self.prevInto = into def drawQuadRow(self, a, into, width): """ a defines a point, with 'into' being the normalized direction. """ # the perpendicular of (a,b) is (-b,a); we want the path to be "flat" in Z=space aLeft = Vec3(a.x - into.y * width, a.y + into.x * width, a.z) aRight = Vec3(a.x + into.y * width, a.y - into.x * width, a.z) row = self.vertexWriter.getWriteRow() self.vertexWriter.addData3f(aLeft) self.vertexWriter.addData3f(aRight) # self.normalWriter.addData3f(Vec3(0, 0, 1)) # self.normalWriter.addData3f(Vec3(0, 0, 1)) self.colorWriter.addData4i(self.leftColor) self.colorWriter.addData4i(self.rightColor) self.triStrips.addConsecutiveVertices(row, 2) def completePath(self): self.geomNode.addGeom(self.geom) if self.triStrips.getNumPrimitives() == 0: return floorMesh = CollisionFloorMesh() tris = self.triStrips.decompose() p = 0 vertexReader = GeomVertexReader(self.vertexData, "vertex") for i in range(tris.getNumPrimitives()): v0 = tris.getPrimitiveStart(i) ve = tris.getPrimitiveEnd(i) if v0 < ve: vertexReader.setRow(tris.getVertex(v0)) floorMesh.addVertex(Point3(vertexReader.getData3f())) vertexReader.setRow(tris.getVertex(v0 + 1)) floorMesh.addVertex(Point3(vertexReader.getData3f())) vertexReader.setRow(tris.getVertex(v0 + 2)) floorMesh.addVertex(Point3(vertexReader.getData3f())) floorMesh.addTriangle(p, p + 1, p + 2) p += 3 self.floorCollNode.addSolid(floorMesh) def updatePhysics(self, task): pos = self.fpscamera.getPos() self.info.setText( "Position: {0}, {1}, {2}".format(int(pos.x * 100) / 100.0, int(pos.y * 100) / 100.0, int(pos.z) / 100.0) ) return task.cont
class LevelEditor(DirectObject): notify = directNotify.newCategory("Foundry") DocActions = [ KeyBind.FileSave, KeyBind.FileSaveAll, KeyBind.FileSaveAs, KeyBind.FileClose, KeyBind.FileCloseAll, KeyBind.Undo, KeyBind.Redo, KeyBind.ViewQuads, KeyBind.View3D, KeyBind.ViewXY, KeyBind.ViewYZ, KeyBind.ViewXZ, KeyBind.Toggle2DGrid, KeyBind.Toggle3DGrid, KeyBind.ToggleGridSnap, KeyBind.IncGridSize, KeyBind.DecGridSize ] def __init__(self): DirectObject.__init__(self) if ConfigVariableBool("want-pstats", False): PStatClient.connect() self.docTitle = "" self.viewportName = "" self.renderRequested = False ################################################################### # Minimal emulation of ShowBase glue code. Note we're not using # ShowBase because there's too much going on in there that assumes # too much (one camera, one lens, one aspect2d, lots of bloat). self.graphicsEngine = GraphicsEngine.getGlobalPtr() self.pipe = GraphicsPipeSelection.getGlobalPtr().makeDefaultPipe() if not self.pipe: self.notify.error("No graphics pipe is available!") return self.globalClock = ClockObject.getGlobalClock() # Since we have already started up a TaskManager, and probably # a number of tasks; and since the TaskManager had to use the # TrueClock to tell time until this moment, make sure the # globalClock object is exactly in sync with the TrueClock. trueClock = TrueClock.getGlobalPtr() self.globalClock.setRealTime(trueClock.getShortTime()) self.globalClock.tick() builtins.globalClock = self.globalClock self.loader = CogInvasionLoader(self) self.graphicsEngine.setDefaultLoader(self.loader.loader) builtins.loader = self.loader self.taskMgr = taskMgr builtins.taskMgr = self.taskMgr self.dgTrav = DataGraphTraverser() self.dataRoot = NodePath("data") self.hidden = NodePath("hidden") self.aspect2d = NodePath("aspect2d") builtins.aspect2d = self.aspect2d # Messages that are sent regardless of the active document. self.messenger = messenger builtins.messenger = self.messenger builtins.base = self builtins.hidden = self.hidden ################################################################### self.clickTrav = CollisionTraverser() # All open documents. self.documents = [] # The focused document. self.document = None TextNode.setDefaultFont( loader.loadFont("resources/models/fonts/consolas.ttf")) self.initialize() def requestRender(self): self.renderRequested = True def setWindowSubTitle(self, sub): title = LEGlobals.AppName if sub: title += " - " + sub self.qtWindow.setWindowTitle(title) def __docLoop(self): if self.document: self.document.step() def __gbcLoop(self): TransformState.garbageCollect() RenderState.garbageCollect() def __dataLoop(self): self.dgTrav.traverse(self.dataRoot.node()) def __igLoop(self): if self.renderRequested: self.graphicsEngine.renderFrame() #self.graphicsEngine.syncFrame() #self.graphicsEngine.readyFlip() self.graphicsEngine.flipFrame() #self.graphicsEngine.openWindows() self.renderRequested = False else: #self.graphicsEngine.flipFrame() self.globalClock.tick() PStatClient.mainTick() throwNewFrame() def openDocument(self, filename): doc = Document() self.documents.append(doc) doc.page.showMaximized() doc.open(filename) doc.updateTabText() if len(self.documents) == 1: self.enableDocActions() def setActiveDoc(self, doc): if self.document: self.document.deactivated() self.document = doc if not doc: return doc.activated() doc.updateTabText() base.document = doc def reqCloseDocument(self, doc): return self.qtWindow.closeDoc(doc) def closeDocument(self, doc): doc.close() if doc == base.document: self.qtWindow.docArea.activateNextSubWindow() self.documents.remove(doc) if len(self.documents) == 0: self.disableDocActions() def enableDocActions(self): for act in self.DocActions: self.menuMgr.enableAction(act) def disableDocActions(self): for act in self.DocActions: self.menuMgr.disableAction(act) def clickTraverse(self, np, handler, travRoot=None): self.clickTrav.addCollider(np, handler) if not travRoot: travRoot = self.render self.clickTrav.traverse(travRoot) self.clickTrav.removeCollider(np) def snapToGrid(self, point): if GridSettings.GridSnap: return LEUtils.snapToGrid(GridSettings.DefaultStep, point) return point def initialize(self): self.loader.mountMultifiles() self.loader.mountMultifile("resources/mod.mf") self.fgd = FgdParse('resources/phase_14/etc/cio.fgd') self.qtApp = LevelEditorApp() self.qtWindow = self.qtApp.window self.menuMgr = MenuManager() self.menuMgr.addMenuItems() ToolManager.addToolActions() SelectionManager.addModeActions() self.qtWindow.initialize() # Open a blank document #self.openDocument(None) self.adjustGridText() self.brushMgr = BrushManager() self.modelBrowser = ModelBrowser(None) self.materialBrowser = MaterialBrowser(None) self.menuMgr.connect(KeyBind.ToggleGridSnap, self.__gridSnap) self.menuMgr.connect(KeyBind.IncGridSize, self.__incGridSize) self.menuMgr.connect(KeyBind.DecGridSize, self.__decGridSize) self.menuMgr.connect(KeyBind.Toggle2DGrid, self.__toggleGrid) self.menuMgr.connect(KeyBind.Toggle3DGrid, self.__toggleGrid3D) self.menuMgr.connect(KeyBind.ViewQuads, self.__viewQuads) self.menuMgr.connect(KeyBind.View3D, self.__view3D) self.menuMgr.connect(KeyBind.ViewXY, self.__viewXY) self.menuMgr.connect(KeyBind.ViewXZ, self.__viewXZ) self.menuMgr.connect(KeyBind.ViewYZ, self.__viewYZ) self.menuMgr.action(KeyBind.ToggleGridSnap).setChecked( GridSettings.GridSnap) self.menuMgr.action(KeyBind.Toggle2DGrid).setChecked( GridSettings.EnableGrid) self.menuMgr.action(KeyBind.Toggle3DGrid).setChecked( GridSettings.EnableGrid3D) self.brushMgr.addBrushes() def __viewQuads(self): self.document.page.arrangeInQuadLayout() def __view3D(self): self.document.page.focusOnViewport(VIEWPORT_3D) def __viewXY(self): self.document.page.focusOnViewport(VIEWPORT_2D_TOP) def __viewYZ(self): self.document.page.focusOnViewport(VIEWPORT_2D_SIDE) def __viewXZ(self): self.document.page.focusOnViewport(VIEWPORT_2D_FRONT) def __gridSnap(self): GridSettings.GridSnap = not GridSettings.GridSnap self.adjustGridText() def __toggleGrid(self): GridSettings.EnableGrid = not GridSettings.EnableGrid self.adjustGridText() self.document.update2DViews() def __toggleGrid3D(self): GridSettings.EnableGrid3D = not GridSettings.EnableGrid3D self.adjustGridText() self.document.update3DViews() def __incGridSize(self): GridSettings.DefaultStep *= 2 GridSettings.DefaultStep = min(256, GridSettings.DefaultStep) self.adjustGridText() self.document.updateAllViews() def __decGridSize(self): GridSettings.DefaultStep //= 2 GridSettings.DefaultStep = max(1, GridSettings.DefaultStep) self.adjustGridText() self.document.updateAllViews() def adjustGridText(self): text = "Snap: %s Grid: %i" % ("On" if GridSettings.GridSnap else "Off", GridSettings.DefaultStep) self.qtApp.window.gridSnapLabel.setText(text) def run(self): self.running = True while self.running: fr = globalClock.getAverageFrameRate() dt = globalClock.getDt() self.qtWindow.fpsLabel.setText("%.2f ms / %i fps" % (dt * 1000, fr)) self.qtApp.processEvents() self.__gbcLoop() self.__dataLoop() self.__docLoop() self.taskMgr.step() self.__igLoop()
class LevelEditor(HostBase): notify = directNotify.newCategory("Foundry") DocActions = [ KeyBind.FileSave, KeyBind.FileSaveAll, KeyBind.FileSaveAs, KeyBind.FileClose, KeyBind.FileCloseAll, KeyBind.Undo, KeyBind.Redo, KeyBind.ViewQuads, KeyBind.View3D, KeyBind.ViewXY, KeyBind.ViewYZ, KeyBind.ViewXZ, KeyBind.Toggle2DGrid, KeyBind.Toggle3DGrid, KeyBind.ToggleGridSnap, KeyBind.IncGridSize, KeyBind.DecGridSize ] def __init__(self): self.gotLocalConfig = False HostBase.__init__(self) self.docTitle = "" self.viewportName = "" self.renderRequested = False ################################################################### # Minimal emulation of ShowBase glue code. Note we're not using # ShowBase because there's too much going on in there that assumes # too much (one camera, one lens, one aspect2d, lots of bloat). self.graphicsEngine = GraphicsEngine.getGlobalPtr() self.pipe = GraphicsPipeSelection.getGlobalPtr().makeDefaultPipe() if not self.pipe: self.notify.error("No graphics pipe is available!") return self.dgTrav = DataGraphTraverser() self.dataRoot = NodePath("data") self.hidden = NodePath("hidden") self.aspect2d = NodePath("aspect2d") builtins.aspect2d = self.aspect2d builtins.hidden = self.hidden ################################################################### self.clickTrav = CollisionTraverser() # All open documents. self.documents = [] # The focused document. self.document = None TextNode.setDefaultFont( loader.loadFont("resources/models/fonts/consolas.ttf")) def requestRender(self): self.renderRequested = True def setWindowSubTitle(self, sub): title = LEGlobals.AppName if sub: title += " - " + sub self.qtWindow.setWindowTitle(title) def __docLoop(self): if self.document: self.document.step() def __gbcLoop(self): TransformState.garbageCollect() RenderState.garbageCollect() def __dataLoop(self): self.dgTrav.traverse(self.dataRoot.node()) def __igLoop(self): if self.renderRequested: self.graphicsEngine.renderFrame() #self.graphicsEngine.syncFrame() #self.graphicsEngine.readyFlip() self.graphicsEngine.flipFrame() #self.graphicsEngine.openWindows() self.renderRequested = False else: #self.graphicsEngine.flipFrame() self.globalClock.tick() PStatClient.mainTick() throwNewFrame() def openDocument(self, filename): doc = Document() self.documents.append(doc) doc.page.showMaximized() doc.open(filename) doc.updateTabText() if len(self.documents) == 1: self.enableDocActions() self.materialPanel.show() def setActiveDoc(self, doc): if self.document: self.document.deactivated() self.document = doc if not doc: return doc.activated() doc.updateTabText() base.document = doc def reqCloseDocument(self, doc): return self.qtWindow.closeDoc(doc) def closeDocument(self, doc): doc.close() if doc == base.document: self.qtWindow.docArea.activateNextSubWindow() self.documents.remove(doc) if len(self.documents) == 0: self.disableDocActions() self.materialPanel.hide() def enableDocActions(self): for act in self.DocActions: self.menuMgr.enableAction(act) def disableDocActions(self): for act in self.DocActions: self.menuMgr.disableAction(act) def clickTraverse(self, np, handler, travRoot=None): self.clickTrav.addCollider(np, handler) if not travRoot: travRoot = self.render self.clickTrav.traverse(travRoot) self.clickTrav.removeCollider(np) def snapToGrid(self, point): if GridSettings.GridSnap: return LEUtils.snapToGrid(GridSettings.DefaultStep, point) return point def loadDefaultConfig(self): HostBase.loadDefaultConfig(self) data = """ bounds-type box """ self.loadDefaultPRCData("leveleditor", data) def loadLocalConfig(self): HostBase.loadLocalConfig(self) ret = self.loadLocalPRCFile("leveleditor") if ret: self.gotLocalConfig = True def loadFGDFiles(self): """Reads the .fgd files specified in the config file""" self.fgd = Fgd() numVals = LEConfig.fgd_files.getNumUniqueValues() if numVals == 0: QtWidgets.QMessageBox.critical( None, LEGlobals.AppName, "No FGD files specified in local config!", QtWidgets.QMessageBox.Ok) sys.exit(1) for i in range(numVals): fgdFilename = LEConfig.fgd_files.getUniqueValue(i) fgd = FgdParse(fgdFilename) self.fgd.add_include(fgd) def startup(self): HostBase.startup(self) #self.loader.mountMultifiles() #self.loader.mountMultifile("resources/mod.mf") self.qtApp = LevelEditorApp() self.qtWindow = self.qtApp.window if not self.gotLocalConfig: QtWidgets.QMessageBox.critical( None, LEGlobals.AppName, "Local config file not found. Please make sure that " "etc/leveleditor.prc exists on your working directory or " "search path.") sys.exit(1) self.loadFGDFiles() self.menuMgr = MenuManager() self.menuMgr.addMenuItems() ToolManager.addToolActions() SelectionManager.addModeActions() self.qtWindow.initialize() # Open a blank document #self.openDocument(None) self.adjustGridText() self.brushMgr = BrushManager() self.modelBrowser = ModelBrowser(None) self.materialBrowser = MaterialBrowser(None) self.materialPanel = ActiveMaterialPanel() self.menuMgr.connect(KeyBind.ToggleGridSnap, self.__gridSnap) self.menuMgr.connect(KeyBind.IncGridSize, self.__incGridSize) self.menuMgr.connect(KeyBind.DecGridSize, self.__decGridSize) self.menuMgr.connect(KeyBind.Toggle2DGrid, self.__toggleGrid) self.menuMgr.connect(KeyBind.Toggle3DGrid, self.__toggleGrid3D) self.menuMgr.connect(KeyBind.ViewQuads, self.__viewQuads) self.menuMgr.connect(KeyBind.View3D, self.__view3D) self.menuMgr.connect(KeyBind.ViewXY, self.__viewXY) self.menuMgr.connect(KeyBind.ViewXZ, self.__viewXZ) self.menuMgr.connect(KeyBind.ViewYZ, self.__viewYZ) self.menuMgr.action(KeyBind.ToggleGridSnap).setChecked( GridSettings.GridSnap) self.menuMgr.action(KeyBind.Toggle2DGrid).setChecked( GridSettings.EnableGrid) self.menuMgr.action(KeyBind.Toggle3DGrid).setChecked( GridSettings.EnableGrid3D) self.brushMgr.addBrushes() # Set the default active material here MaterialPool.setActiveMaterial( MaterialPool.getMaterial(LEConfig.default_material.getValue())) def __viewQuads(self): self.document.page.arrangeInQuadLayout() def __view3D(self): self.document.page.focusOnViewport(VIEWPORT_3D) def __viewXY(self): self.document.page.focusOnViewport(VIEWPORT_2D_TOP) def __viewYZ(self): self.document.page.focusOnViewport(VIEWPORT_2D_SIDE) def __viewXZ(self): self.document.page.focusOnViewport(VIEWPORT_2D_FRONT) def __gridSnap(self): GridSettings.GridSnap = not GridSettings.GridSnap self.adjustGridText() def __toggleGrid(self): GridSettings.EnableGrid = not GridSettings.EnableGrid self.adjustGridText() self.document.update2DViews() def __toggleGrid3D(self): GridSettings.EnableGrid3D = not GridSettings.EnableGrid3D self.adjustGridText() self.document.update3DViews() def __incGridSize(self): GridSettings.DefaultStep *= 2 GridSettings.DefaultStep = min(256, GridSettings.DefaultStep) self.adjustGridText() self.document.updateAllViews() def __decGridSize(self): GridSettings.DefaultStep //= 2 GridSettings.DefaultStep = max(1, GridSettings.DefaultStep) self.adjustGridText() self.document.updateAllViews() def adjustGridText(self): text = "Snap: %s Grid: %i" % ("On" if GridSettings.GridSnap else "Off", GridSettings.DefaultStep) self.qtApp.window.gridSnapLabel.setText(text) def preRunFrame(self): fr = globalClock.getAverageFrameRate() dt = globalClock.getDt() self.qtWindow.fpsLabel.setText("%.2f ms / %i fps" % (dt * 1000, fr)) self.qtApp.processEvents() self.__gbcLoop() self.__dataLoop() self.__docLoop() def postRunFrame(self): self.__igLoop()
class CellManager(object): """Creates a collision ray and collision traverser to use for selecting the current cell.""" def __init__(self, game): self.game = game self.cells = {} self.cells_by_collider = {} self.cell_picker_world = NodePath('cell_picker_world') self.ray = CollisionRay() self.ray.setDirection(LVector3.down()) cnode = CollisionNode('cell_raycast_cnode') self.ray_nodepath = self.cell_picker_world.attachNewNode(cnode) self.ray_nodepath.node().addSolid(self.ray) self.ray_nodepath.node().setIntoCollideMask( 0) # not for colliding into self.ray_nodepath.node().setFromCollideMask(1) self.traverser = CollisionTraverser('traverser') self.last_known_cell = None def add_cell(self, collider, name): """Add a new cell.""" cell = Cell(self, name, collider) self.cells[name] = cell self.cells_by_collider[collider.node()] = cell def get_cell(self, pos): """Given a position, return the nearest cell below that position. If no cell is found, returns None.""" self.ray.setOrigin(pos) queue = CollisionHandlerQueue() self.traverser.addCollider(self.ray_nodepath, queue) self.traverser.traverse(self.cell_picker_world) self.traverser.removeCollider(self.ray_nodepath) queue.sortEntries() if not queue.getNumEntries(): return None entry = queue.getEntry(0) cnode = entry.getIntoNode() try: return self.cells_by_collider[cnode] except KeyError: raise Warning('collision ray collided with something ' 'other than a cell: %s' % cnode) def get_dist_to_cell(self, pos): """Given a position, return the distance to the nearest cell below that position. If no cell is found, returns None.""" self.ray.setOrigin(pos) queue = CollisionHandlerQueue() self.traverser.addCollider(self.ray_nodepath, queue) self.traverser.traverse(self.cell_picker_world) self.traverser.removeCollider(self.ray_nodepath) queue.sortEntries() if not queue.getNumEntries(): return None entry = queue.getEntry(0) return (entry.getSurfacePoint(self.cell_picker_world) - pos).length() def load_cells_from_model(self, modelpath): """Loads cells from an EGG file. Cells must be named in the format "cell#" to be loaded by this function.""" cell_model = self.game.loader.loadModel(modelpath) for collider in cell_model.findAllMatches('**/+GeomNode'): name = collider.getName() if name.startswith('cell'): self.add_cell(collider, name[4:]) cell_model.removeNode() def load_portals_from_model(self, modelpath): """Loads portals from an EGG file. Portals must be named in the format "portal_#to#_*" to be loaded by this function, whereby the first # is the from cell, the second # is the into cell, and * can be anything.""" portal_model = loader.loadModel(modelpath) portal_nodepaths = portal_model.findAllMatches('**/+PortalNode') for portal_nodepath in portal_nodepaths: name = portal_nodepath.getName() if name.startswith('portal_'): from_cell_id, into_cell_id = name.split('_')[1].split('to') try: from_cell = self.cells[from_cell_id] except KeyError: print('could not load portal "%s" because cell "%s"' 'does not exist' % (name, from_cell_id)) continue try: into_cell = self.cells[into_cell_id] except KeyError: print('could not load portal "%s" because cell "%s"' 'does not exist' % (name, into_cell_id)) continue from_cell.add_portal(portal_nodepath, into_cell) portal_model.removeNode() def update(self): """Show the cell the camera is currently in and hides the rest. If the camera is not in a cell, use the last known cell that the camera was in. If the camera has not yet been in a cell, then all cells will be hidden.""" camera_pos = self.game.camera.getPos(self.game.render) for cell in self.cells: self.cells[cell].nodepath.hide() current_cell = self.get_cell(camera_pos) if current_cell is None: if self.last_known_cell is None: return self.last_known_cell.nodepath.show() else: self.last_known_cell = current_cell current_cell.nodepath.show()
class MyApp(ShowBase): def __init__(self): ShowBase.__init__(self) self.seeNode = self.render.attachNewNode('see') self.cam.reparentTo(self.seeNode) self.cam.setPos(0, 0, 5) self.fpscamera = fpscontroller.FpsController(self, self.seeNode) self.fpscamera.setFlyMode(True) self.prevPos = self.fpscamera.getPos() self.prevInto = None self.info = self.genLabelText("Position: <unknown>", 4) self.makeInstructions() self.initCollisions() self.leftColor = LVecBase4i(224, 224, 64, 255) self.rightColor = LVecBase4i(64, 224, 224, 255) self.isDrawing = False self.toggleDrawing() self.accept("escape", sys.exit) #Escape quits self.accept("enter", self.toggleDrawing) def initCollisions(self): # Initialize the collision traverser. self.cTrav = CollisionTraverser() self.cTrav.showCollisions(self.render) # self.cQueue = CollisionHandlerQueue() # Initialize the Pusher collision handler. #self.pusher = CollisionHandlerPusher() self.pusher = CollisionHandlerFloor() ### player print DirectNotifyGlobal.directNotify.getCategories() # Create a collsion node for this object. playerNode = CollisionNode('player') playerNode.addSolid(CollisionSphere(0, 0, 0, 1)) # playerNode.setFromCollideMask(BitMask32.bit(0)) # playerNode.setIntoCollideMask(BitMask32.allOn()) # Attach the collision node to the object's model. self.playerC = self.fpscamera.player.attachNewNode(playerNode) # Set the object's collision node to render as visible. self.playerC.show() # Add the 'player' collision node to the Pusher collision handler. #self.pusher.addCollider(self.playerC, self.fpscamera.player) #self.pusher.addCollider(playerC, self.fpscamera.player) # self.cTrav.addCollider(self.playerC, self.cQueue) def toggleDrawing(self): self.isDrawing = not self.isDrawing if self.isDrawing: self.drawText.setText("Enter: Turn off drawing") self.fpscamera.setFlyMode(True) self.prevPos = None self.cTrav.removeCollider(self.playerC) self.pusher.removeCollider(self.playerC) self.removeTask('updatePhysics') self.addTask(self.drawHere, 'drawHere') self.geomNode = GeomNode('geomNode') self.geomNodePath = self.render.attachNewNode(self.geomNode) self.geomNodePath.setTwoSided(True) # apparently p3tinydisplay needs this self.geomNodePath.setColorOff() # Create a collision node for this object. self.floorCollNode = CollisionNode('geom') # self.floorCollNode.setFromCollideMask(BitMask32.bit(0)) # self.floorCollNode.setIntoCollideMask(BitMask32.allOn()) # Attach the collision node to the object's model. floorC = self.geomNodePath.attachNewNode(self.floorCollNode) # Set the object's collision node to render as visible. floorC.show() #self.pusher.addCollider(floorC, self.geomNodePath) self.newVertexData() self.newGeom() else: self.drawText.setText("Enter: Turn on drawing") self.removeTask('drawHere') if self.prevPos: self.completePath() self.fpscamera.setFlyMode(True) self.drive.setPos(self.fpscamera.getPos()) self.cTrav.addCollider(self.playerC, self.pusher) self.pusher.addCollider(self.playerC, self.fpscamera.player) self.taskMgr.add(self.updatePhysics, 'updatePhysics') def newVertexData(self): fmt = GeomVertexFormat.getV3c4() # fmt = GeomVertexFormat.getV3n3c4() self.vertexData = GeomVertexData("path", fmt, Geom.UHStatic) self.vertexWriter = GeomVertexWriter(self.vertexData, 'vertex') # self.normalWriter = GeomVertexWriter(self.vertexData, 'normal') self.colorWriter = GeomVertexWriter(self.vertexData, 'color') def newGeom(self): self.triStrips = GeomTristrips(Geom.UHDynamic) self.geom = Geom(self.vertexData) self.geom.addPrimitive(self.triStrips) def makeInstructions(self): OnscreenText(text="Draw Path by Walking", style=1, fg=(1, 1, 0, 1), pos=(0.5, -0.95), scale=.07) self.drawText = self.genLabelText("", 0) self.genLabelText("Walk (W/S/A/D), Jump=Space, Look=PgUp/PgDn", 1) self.genLabelText( " (hint, go backwards with S to see your path immediately)", 2) self.genLabelText("ESC: Quit", 3) def genLabelText(self, text, i): return OnscreenText(text=text, pos=(-1.3, .95 - .05 * i), fg=(1, 1, 0, 1), align=TextNode.ALeft, scale=.05) def drawHere(self, task): pos = self.fpscamera.getPos() self.info.setText("Position: {0}, {1}, {2} at {3} by {4}".format( int(pos.x * 100) / 100., int(pos.y * 100) / 100., int(pos.z) / 100., self.fpscamera.getHeading(), self.fpscamera.getLookAngle())) prevPos = self.prevPos if not prevPos: self.prevPos = pos elif (pos - prevPos).length() > 1: self.drawQuadTo(prevPos, pos, 2) row = self.vertexWriter.getWriteRow() numPrims = self.triStrips.getNumPrimitives() if numPrims == 0: primVerts = row else: primVerts = row - self.triStrips.getPrimitiveEnd(numPrims - 1) if primVerts >= 4: self.triStrips.closePrimitive() if row >= 256: print "Packing and starting anew" newGeom = True self.geom.unifyInPlace(row, False) else: newGeom = False self.completePath() if newGeom: self.newVertexData() self.newGeom() if not newGeom: self.triStrips.addConsecutiveVertices(row - 2, 2) else: self.drawQuadTo(prevPos, pos, 2) self.leftColor[1] += 63 self.rightColor[2] += 37 self.prevPos = pos return task.cont def drawLineTo(self, pos, color): self.vertexWriter.addData3f(pos.x, pos.y, pos.z) # self.normalWriter.addData3f(0, 0, 1) self.colorWriter.addData4i(color) self.triStrips.addNextVertices(1) def drawQuadTo(self, a, b, width): """ a (to) b are vectors defining a line bisecting a new quad. """ into = (b - a) if abs(into.x) + abs(into.y) < 1: if not self.prevInto: return into = self.prevInto print into else: into.normalize() # the perpendicular of (a,b) is (-b,a); we want the path to be "flat" in Z=space if self.vertexWriter.getWriteRow() == 0: self.drawQuadRow(a, into, width) self.drawQuadRow(b, into, width) self.prevInto = into def drawQuadRow(self, a, into, width): """ a defines a point, with 'into' being the normalized direction. """ # the perpendicular of (a,b) is (-b,a); we want the path to be "flat" in Z=space aLeft = Vec3(a.x - into.y * width, a.y + into.x * width, a.z) aRight = Vec3(a.x + into.y * width, a.y - into.x * width, a.z) row = self.vertexWriter.getWriteRow() self.vertexWriter.addData3f(aLeft) self.vertexWriter.addData3f(aRight) # self.normalWriter.addData3f(Vec3(0, 0, 1)) # self.normalWriter.addData3f(Vec3(0, 0, 1)) self.colorWriter.addData4i(self.leftColor) self.colorWriter.addData4i(self.rightColor) self.triStrips.addConsecutiveVertices(row, 2) def completePath(self): self.geomNode.addGeom(self.geom) if self.triStrips.getNumPrimitives() == 0: return floorMesh = CollisionFloorMesh() tris = self.triStrips.decompose() p = 0 vertexReader = GeomVertexReader(self.vertexData, 'vertex') for i in range(tris.getNumPrimitives()): v0 = tris.getPrimitiveStart(i) ve = tris.getPrimitiveEnd(i) if v0 < ve: vertexReader.setRow(tris.getVertex(v0)) floorMesh.addVertex(Point3(vertexReader.getData3f())) vertexReader.setRow(tris.getVertex(v0 + 1)) floorMesh.addVertex(Point3(vertexReader.getData3f())) vertexReader.setRow(tris.getVertex(v0 + 2)) floorMesh.addVertex(Point3(vertexReader.getData3f())) floorMesh.addTriangle(p, p + 1, p + 2) p += 3 self.floorCollNode.addSolid(floorMesh) def updatePhysics(self, task): pos = self.fpscamera.getPos() self.info.setText("Position: {0}, {1}, {2}".format( int(pos.x * 100) / 100., int(pos.y * 100) / 100., int(pos.z) / 100.)) return task.cont
class CellManager(object): """Creates a collision ray and collision traverser to use for selecting the current cell.""" def __init__(self, game): self.game = game self.cells = {} self.cells_by_collider = {} self.cell_picker_world = NodePath('cell_picker_world') self.ray = CollisionRay() self.ray.setDirection(LVector3.down()) cnode = CollisionNode('cell_raycast_cnode') self.ray_nodepath = self.cell_picker_world.attachNewNode(cnode) self.ray_nodepath.node().addSolid(self.ray) self.ray_nodepath.node().setIntoCollideMask(0) # not for colliding into self.ray_nodepath.node().setFromCollideMask(1) self.traverser = CollisionTraverser('traverser') self.last_known_cell = None def add_cell(self, collider, name): """Add a new cell.""" cell = Cell(self, name, collider) self.cells[name] = cell self.cells_by_collider[collider.node()] = cell def get_cell(self, pos): """Given a position, return the nearest cell below that position. If no cell is found, returns None.""" self.ray.setOrigin(pos) queue = CollisionHandlerQueue() self.traverser.addCollider(self.ray_nodepath, queue) self.traverser.traverse(self.cell_picker_world) self.traverser.removeCollider(self.ray_nodepath) queue.sortEntries() if not queue.getNumEntries(): return None entry = queue.getEntry(0) cnode = entry.getIntoNode() try: return self.cells_by_collider[cnode] except KeyError: raise Warning('collision ray collided with something ' 'other than a cell: %s' % cnode) def get_dist_to_cell(self, pos): """Given a position, return the distance to the nearest cell below that position. If no cell is found, returns None.""" self.ray.setOrigin(pos) queue = CollisionHandlerQueue() self.traverser.addCollider(self.ray_nodepath, queue) self.traverser.traverse(self.cell_picker_world) self.traverser.removeCollider(self.ray_nodepath) queue.sortEntries() if not queue.getNumEntries(): return None entry = queue.getEntry(0) return (entry.getSurfacePoint(self.cell_picker_world) - pos).length() def load_cells_from_model(self, modelpath): """Loads cells from an EGG file. Cells must be named in the format "cell#" to be loaded by this function.""" cell_model = self.game.loader.loadModel(modelpath) for collider in cell_model.findAllMatches('**/+GeomNode'): name = collider.getName() if name.startswith('cell'): self.add_cell(collider, name[4:]) cell_model.removeNode() def load_portals_from_model(self, modelpath): """Loads portals from an EGG file. Portals must be named in the format "portal_#to#_*" to be loaded by this function, whereby the first # is the from cell, the second # is the into cell, and * can be anything.""" portal_model = loader.loadModel(modelpath) portal_nodepaths = portal_model.findAllMatches('**/+PortalNode') for portal_nodepath in portal_nodepaths: name = portal_nodepath.getName() if name.startswith('portal_'): from_cell_id, into_cell_id = name.split('_')[1].split('to') try: from_cell = self.cells[from_cell_id] except KeyError: print ('could not load portal "%s" because cell "%s"' 'does not exist' % (name, from_cell_id)) continue try: into_cell = self.cells[into_cell_id] except KeyError: print ('could not load portal "%s" because cell "%s"' 'does not exist' % (name, into_cell_id)) continue from_cell.add_portal(portal_nodepath, into_cell) portal_model.removeNode() def update(self): """Show the cell the camera is currently in and hides the rest. If the camera is not in a cell, use the last known cell that the camera was in. If the camera has not yet been in a cell, then all cells will be hidden.""" camera_pos = self.game.camera.getPos(self.game.render) for cell in self.cells: self.cells[cell].nodepath.hide() current_cell = self.get_cell(camera_pos) if current_cell is None: if self.last_known_cell is None: return self.last_known_cell.nodepath.show() else: self.last_known_cell = current_cell current_cell.nodepath.show()
class CollisionChecker(object): """ A fast collision checker that allows maximum 32 collision pairs author: weiwei date: 20201214osaka """ def __init__(self, name="auto"): self.ctrav = CollisionTraverser() self.chan = CollisionHandlerQueue() self.np = NodePath(name) self.bitmask_list = [BitMask32(2**n) for n in range(31)] self._bitmask_ext = BitMask32( 2**31) # 31 is prepared for cd with external non-active objects self.all_cdelements = [ ] # a list of cdlnks or cdobjs for quick accessing the cd elements (cdlnks/cdobjs) def add_cdlnks(self, jlcobj, lnk_idlist): """ The collision node of the given links will be attached to self.np, but their collision bitmask will be cleared When the a robot_s is treated as an obstacle by another robot_s, the IntoCollideMask of its all_cdelements will be set to BitMask32(2**31), so that the other robot_s can compare its active_cdelements with the all_cdelements. :param jlcobj: :param lnk_idlist: :return: author: weiwei date: 20201216toyonaka """ for id in lnk_idlist: if jlcobj.lnks[id]['cdprimit_childid'] == -1: # first time add cdnp = jlcobj.lnks[id]['collision_model'].copy_cdnp_to( self.np, clearmask=True) self.ctrav.addCollider(cdnp, self.chan) self.all_cdelements.append(jlcobj.lnks[id]) jlcobj.lnks[id]['cdprimit_childid'] = len( self.all_cdelements) - 1 else: raise ValueError("The link is already added!") def set_active_cdlnks(self, activelist): """ The specified collision links will be used for collision detection with external obstacles :param activelist: essentially a from list like [jlchain.lnk0, jlchain.lnk1...] the correspondent tolist will be set online in cd functions TODO use all elements in self.all_cdnlks if None :return: author: weiwei date: 20201216toyonaka """ for cdlnk in activelist: if cdlnk['cdprimit_childid'] == -1: raise ValueError( "The link needs to be added to collider using the add_cdlnks function first!" ) cdnp = self.np.getChild(cdlnk['cdprimit_childid']) cdnp.node().setFromCollideMask(self._bitmask_ext) def set_cdpair(self, fromlist, intolist): """ The given collision pair will be used for self collision detection :param fromlist: [[bool, cdprimit_cache], ...] :param intolist: [[bool, cdprimit_cache], ...] :return: author: weiwei date: 20201215 """ if len(self.bitmask_list) == 0: raise ValueError("Too many collision pairs! Maximum: 29") allocated_bitmask = self.bitmask_list.pop() for cdlnk in fromlist: if cdlnk['cdprimit_childid'] == -1: raise ValueError( "The link needs to be added to collider using the addjlcobj function first!" ) cdnp = self.np.getChild(cdlnk['cdprimit_childid']) current_from_cdmask = cdnp.node().getFromCollideMask() new_from_cdmask = current_from_cdmask | allocated_bitmask cdnp.node().setFromCollideMask(new_from_cdmask) for cdlnk in intolist: if cdlnk['cdprimit_childid'] == -1: raise ValueError( "The link needs to be added to collider using the addjlcobj function first!" ) cdnp = self.np.getChild(cdlnk['cdprimit_childid']) current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask | allocated_bitmask cdnp.node().setIntoCollideMask(new_into_cdmask) def add_cdobj(self, objcm, rel_pos, rel_rotmat, into_list): """ :return: cdobj_info, a dictionary that mimics a joint link; Besides that, there is an additional 'into_list' key to hold into_list to easily toggle off the bitmasks. """ cdobj_info = {} cdobj_info['collision_model'] = objcm # for reversed lookup cdobj_info['gl_pos'] = objcm.get_pos() cdobj_info['gl_rotmat'] = objcm.get_rotmat() cdobj_info['rel_pos'] = rel_pos cdobj_info['rel_rotmat'] = rel_rotmat cdobj_info['into_list'] = into_list cdnp = objcm.copy_cdnp_to(self.np, clearmask=True) cdnp.node().setFromCollideMask(self._bitmask_ext) # set active self.ctrav.addCollider(cdnp, self.chan) self.all_cdelements.append(cdobj_info) cdobj_info['cdprimit_childid'] = len(self.all_cdelements) - 1 self.set_cdpair([cdobj_info], into_list) return cdobj_info def delete_cdobj(self, cdobj_info): """ :param cdobj_info: an lnk-like object generated by self.add_objinhnd :param objcm: :return: """ self.all_cdelements.remove(cdobj_info) cdnp_to_delete = self.np.getChild(cdobj_info['cdprimit_childid']) self.ctrav.removeCollider(cdnp_to_delete) this_cdmask = cdnp_to_delete.node().getFromCollideMask() for cdlnk in cdobj_info['into_list']: cdnp = self.np.getChild(cdlnk['cdprimit_childid']) current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask & ~this_cdmask cdnp.node().setIntoCollideMask(new_into_cdmask) cdnp_to_delete.detachNode() self.bitmask_list.append(this_cdmask) def is_collided(self, obstacle_list=[], otherrobot_list=[], toggle_contact_points=False): """ :param obstacle_list: staticgeometricmodel :param otherrobot_list: :return: """ for cdelement in self.all_cdelements: pos = cdelement['gl_pos'] rotmat = cdelement['gl_rotmat'] cdnp = self.np.getChild(cdelement['cdprimit_childid']) cdnp.setPosQuat(da.npv3_to_pdv3(pos), da.npmat3_to_pdquat(rotmat)) # print(da.npv3mat3_to_pdmat4(pos, rotmat)) # print("From", cdnp.node().getFromCollideMask()) # print("Into", cdnp.node().getIntoCollideMask()) # print("xxxx colliders xxxx") # for collider in self.ctrav.getColliders(): # print(collider.getMat()) # print("From", collider.node().getFromCollideMask()) # print("Into", collider.node().getIntoCollideMask()) # attach obstacles obstacle_parent_list = [] for obstacle in obstacle_list: obstacle_parent_list.append(obstacle.objpdnp.getParent()) obstacle.objpdnp.reparentTo(self.np) # attach other robots for robot in otherrobot_list: for cdnp in robot.cc.np.getChildren(): current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask | self._bitmask_ext cdnp.node().setIntoCollideMask(new_into_cdmask) robot.cc.np.reparentTo(self.np) # collision check self.ctrav.traverse(self.np) # clear obstacles for i, obstacle in enumerate(obstacle_list): obstacle.objpdnp.reparentTo(obstacle_parent_list[i]) # clear other robots for robot in otherrobot_list: for cdnp in robot.cc.np.getChildren(): current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask & ~self._bitmask_ext cdnp.node().setIntoCollideMask(new_into_cdmask) robot.cc.np.detachNode() if self.chan.getNumEntries() > 0: collision_result = True else: collision_result = False if toggle_contact_points: contact_points = [ da.pdv3_to_npv3(cd_entry.getSurfacePoint(base.render)) for cd_entry in self.chan.getEntries() ] return collision_result, contact_points else: return collision_result def show_cdprimit(self): """ Copy the current nodepath to base.render to show collision states TODO: maintain a list to allow unshow :return: author: weiwei date: 20220404 """ # print("call show_cdprimit") snp_cpy = self.np.copyTo(base.render) for cdelement in self.all_cdelements: pos = cdelement['gl_pos'] rotmat = cdelement['gl_rotmat'] cdnp = snp_cpy.getChild(cdelement['cdprimit_childid']) cdnp.setPosQuat(da.npv3_to_pdv3(pos), da.npmat3_to_pdquat(rotmat)) cdnp.show() def disable(self): """ clear pairs and nodepath :return: """ for cdelement in self.all_cdelements: cdelement['cdprimit_childid'] = -1 self.all_cdelements = [] for child in self.np.getChildren(): child.removeNode() self.bitmask_list = list(range(31))
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