def _readMovementSettings(self, configDataSec): movementMappings = dict() movementMappings[getattr( Keys, configDataSec.readString('keyMoveLeft', 'KEY_A'))] = Math.Vector3(-1, 0, 0) movementMappings[getattr( Keys, configDataSec.readString('keyMoveRight', 'KEY_D'))] = Math.Vector3(1, 0, 0) movementMappings[getattr( Keys, configDataSec.readString('keyMoveForward', 'KEY_W'))] = Math.Vector3(0, 0, 1) movementMappings[getattr( Keys, configDataSec.readString('keyMoveBackward', 'KEY_S'))] = Math.Vector3(0, 0, -1) linearSensitivity = configDataSec.readFloat('linearVelocity', 40.0) self._movementSensor = KeySensor(movementMappings, linearSensitivity) self._verticalMovementSensor = KeySensor({}, linearSensitivity) self._movementSensor.currentVelocity = Math.Vector3()
def __readCfg(self, dataSec): if dataSec is None: LOG_WARNING('Invalid section <arcadeMode/camera> in avatar_input_handler.xml') self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.01) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.01) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.01) bcfg['angleRange'] = readVec2(dataSec, 'angleRange', (0, 0), (180, 180), (10, 110)) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (100, 100), (2, 20)) bcfg['angleRange'][0] = math.radians(bcfg['angleRange'][0]) - math.pi * 0.5 bcfg['angleRange'][1] = math.radians(bcfg['angleRange'][1]) - math.pi * 0.5 ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['arcadeMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg from account_helpers.SettingsCore import g_settingsCore ucfg['horzInvert'] = g_settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = g_settingsCore.getSetting('mouseVertInvert') ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['startDist'] = readFloat(ds, 'startDist', 2, 500, 10) ucfg['startAngle'] = readFloat(ds, 'startAngle', 5, 180, 60) ucfg['startAngle'] = math.radians(ucfg['startAngle']) - math.pi * 0.5 self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['angleRange'] = bcfg['angleRange'] cfg['distRange'] = bcfg['distRange'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['startDist'] = ucfg['startDist'] cfg['startAngle'] = ucfg['startAngle'] enableShift = dataSec.readBool('shift', False) if enableShift: movementMappings = dict() movementMappings[Keys.KEY_A] = Math.Vector3(-1, 0, 0) movementMappings[Keys.KEY_D] = Math.Vector3(1, 0, 0) movementMappings[Keys.KEY_Q] = Math.Vector3(0, 1, 0) movementMappings[Keys.KEY_E] = Math.Vector3(0, -1, 0) movementMappings[Keys.KEY_W] = Math.Vector3(0, 0, 1) movementMappings[Keys.KEY_S] = Math.Vector3(0, 0, -1) shiftSensitivity = dataSec.readFloat('shiftSensitivity', 0.5) self.__shiftKeySensor = KeySensor(movementMappings, shiftSensitivity) self.__shiftKeySensor.reset(Math.Vector3()) return
class ArcadeCamera(ICamera): camera = property(lambda self: self.__cam) angles = property(lambda self: (self.__yaw, self.__pitch)) def __init__(self, dataSec): self.__shiftKeySensor = None self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__camDist = 0 self.__curSense = 0 self.__curScrollSense = 0 self.__pitch = 0.0 self.__yaw = 0.0 self.__cursorOffset = (0, 0) self.__postmortemMode = False self.__modelsToCollideWith = [] self.__defaultPivotPos = Math.Vector3(0, 0, 0) self.__onChangeControlMode = None self.__autoUpdateCallbackId = None self.__cameraUpdateCallbackId = None return def create(self, pivotPos, onChangeControlMode=None, postmortemMode=False): self.__onChangeControlMode = onChangeControlMode self.__postmortemMode = postmortemMode replayCtrl = BattleReplay.g_replayCtrl vehicleID = replayCtrl.playerVehicleID if replayCtrl.isPlaying else BigWorld.player( ).playerVehicleID targetMat = BigWorld.entity(vehicleID).matrix self.__camDist = self.__cfg['startDist'] self.__cam.pivotMaxDist = self.__camDist self.__cam.maxDistHalfLife = 0.0 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = 0.0 self.__cam.pivotPosition = self.__defaultPivotPos = pivotPos self.__pitch = self.__cfg['startAngle'] self.__yaw = Math.Matrix(targetMat).yaw matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(0, 0)) self.__cam.source = matrix def destroy(self): self.disable() self.__onChangeControlMode = None self.__cam = None self._writeUserPreferences() if self.__cameraUpdateCallbackId is not None: BigWorld.cancelCallback(self.__cameraUpdateCallbackId) self.__cameraUpdateCallbackId = None return def addModelsToCollideWith(self, models): self.__modelsToCollideWith = self.__modelsToCollideWith + models if BigWorld.camera() == self.__cam: self.__cam.wg_setModelsToCollideWith(self.__modelsToCollideWith) def removeModelsToCollideWith(self, models): for model in models: if self.__modelsToCollideWith.count(model) > 0: self.__modelsToCollideWith.remove(model) if BigWorld.camera() == self.__cam: self.__cam.wg_setModelsToCollideWith(self.__modelsToCollideWith) def focusOnPos(self, preferredPos): self.__calcStartAngles(self.__cam.target, preferredPos) def shiftCamPos(self, shift=None): matrixProduct = self.__cam.target shiftMat = getattr(matrixProduct, 'a', None) if shiftMat is None: return Math.Vector3() else: shiftMat = Math.Matrix(shiftMat) if shift is not None: camDirection = Math.Vector3(self.__cam.direction) camDirection.y = 0 camDirection.normalise() normal = Math.Vector3(camDirection) normal.x = camDirection.z normal.z = -camDirection.x shiftMat.translation += camDirection * shift.z + Math.Vector3( 0, 1, 0) * shift.y + normal * shift.x else: shiftMat.setIdentity() matrixProduct.a = shiftMat return shiftMat.translation def enable(self, preferredPos=None, closesDist=False, postmortemParams=None): camSource = None camDist = None camTarget = BigWorld.player().getOwnVehicleMatrix() camTargetTransOnly = Math.WGTranslationOnlyMP() camTargetTransOnly.source = camTarget camTarget = Math.MatrixProduct() shift = Math.Matrix() shift.setIdentity() camTarget.a = shift camTarget.b = camTargetTransOnly if not self.__postmortemMode: if preferredPos is not None: self.__calcStartAngles(camTarget, preferredPos) if closesDist: camDist = self.__cfg['distRange'][0] elif postmortemParams is not None: self.__yaw = postmortemParams[0][0] self.__pitch = postmortemParams[0][1] camDist = postmortemParams[1] camSource = Math.Matrix() camSource.setRotateYPR((self.__yaw, self.__pitch, 0)) else: camDist = self.__cfg['distRange'][1] replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: camDist = None vehicle = BigWorld.entity(replayCtrl.playerVehicleID) if vehicle is not None: camTarget = vehicle.matrix if camDist is not None: self.__camDist = camDist self.__cam.pivotMaxDist = camDist if camSource is not None: self.__cam.source = camSource self.__cam.target = camTarget self.__cam.sptHiding = True self.__cam.wg_setModelsToCollideWith(self.__modelsToCollideWith) BigWorld.camera(self.__cam) self.__cameraUpdate() return def disable(self): self.__cam.wg_setModelsToCollideWith([]) self.__cam.sptHiding = False BigWorld.camera(None) if self.__autoUpdateCallbackId is not None: BigWorld.cancelCallback(self.__autoUpdateCallbackId) self.__autoUpdateCallbackId = None if self.__cameraUpdateCallbackId is not None: BigWorld.cancelCallback(self.__cameraUpdateCallbackId) self.__cameraUpdateCallbackId = None if self.__shiftKeySensor is not None: self.__shiftKeySensor.reset(Math.Vector3()) return def update(self, dx, dy, dz, rotateMode=True, zoomMode=True): self.__curSense = self.__cfg['sensitivity'] self.__curScrollSense = self.__cfg['scrollSensitivity'] self.__update(dx, dy, dz, rotateMode, zoomMode, False) def autoUpdate(self, dx, dy, dz, rotateMode=True, zoomMode=True, onlyOnce=False): self.__curSense = self.__cfg['keySensitivity'] self.__curScrollSense = self.__cfg['keySensitivity'] if self.__autoUpdateCallbackId is not None: BigWorld.cancelCallback(self.__autoUpdateCallbackId) self.__autoUpdateCallbackId = None if onlyOnce: self.__update(dx, dy, dz, rotateMode, zoomMode, False) else: self.__autoUpdateCallbackId = BigWorld.callback( 0.001, partial(self.__update, dx, dy, dz, rotateMode, zoomMode, True)) return def restoreDefaultsState(self): self.__camDist = self.__cfg['startDist'] self.__cam.pivotMaxDist = self.__camDist self.__cam.maxDistHalfLife = 0.025 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = 0.025 self.__pitch = self.__cfg['startAngle'] self.__yaw = Math.Matrix(self.__cam.target).yaw matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(0, 0)) self.__cam.source = matrix def getUserConfigValue(self, name): return self.__userCfg.get(name) def setUserConfigValue(self, name, value): if name not in self.__userCfg: return self.__userCfg[name] = value if name not in ('keySensitivity', 'sensitivity', 'scrollSensitivity'): self.__cfg[name] = self.__userCfg[name] else: self.__cfg[name] = self.__baseCfg[name] * self.__userCfg[name] def cursorOffset(self, offset): self.__cursorOffset = offset def setPivotPosition(self, position): self.__cam.pivotPosition = self.__defaultPivotPos = position def getPivotPosition(self): return self.__defaultPivotPos def setCameraDistance(self, distance): distRange = self.__cfg['distRange'] self.__camDist = distance self.__camDist = _clamp(distRange[0], distRange[1], self.__camDist) self.__cam.pivotMaxDist = self.__camDist def getCameraDistance(self): return self.__camDist def setYawPitch(self, yaw, pitch): anglesRange = self.__cfg['angleRange'] self.__pitch = pitch self.__pitch = _clamp(anglesRange[0], anglesRange[1], self.__pitch) self.__yaw = yaw if self.__yaw > 2.0 * math.pi: self.__yaw -= 2.0 * math.pi elif self.__yaw < -2.0 * math.pi: self.__yaw += 2.0 * math.pi matrix = Math.Matrix() matrix.setRotateYPR((self.__yaw, self.__pitch, 0)) self.__cam.source = matrix def getYawPitch(self): return (self.__yaw, self.__pitch) def getPivotPos(self): camPivotPos = self.__cam.pivotPosition posOnEntity = Math.Matrix(self.__cam.target).applyPoint( Math.Vector3(0, camPivotPos[1], 0)) rotMat = Math.Matrix() rotMat.setRotateY(self.__yaw) pivotPos = posOnEntity + rotMat.applyPoint( Math.Vector3(camPivotPos[0], 0, camPivotPos[2])) return pivotPos def __calcStartAngles(self, targetMat, preferredPos): camDist = self.__camDist camPivotPos = self.__cam.pivotPosition posOnEntity = Math.Matrix(targetMat).applyPoint( Math.Vector3(0, camPivotPos[1], 0)) yaw = (preferredPos - posOnEntity).yaw rotMat = Math.Matrix() rotMat.setRotateY(yaw) pivotPos = posOnEntity + rotMat.applyPoint( Math.Vector3(camPivotPos[0], 0, camPivotPos[2])) dir = preferredPos - pivotPos posDir = preferredPos - Math.Matrix(targetMat).applyToOrigin() pitch = -posDir.pitch try: pitch = self.__calcPitchAngle(camDist, dir) dist = self.__sceneCheck(yaw, pitch, pivotPos, camDist) if dist != 0: pitch = self.__calcPitchAngle(dist, dir) except: LOG_CURRENT_EXCEPTION() self.__pitch = pitch self.__yaw = yaw matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(0, 0)) self.__cam.source = matrix def __getRotateAngle(self, vec2f): fov = BigWorld.projection().fov near = BigWorld.projection().nearPlane yLength = near * math.tan(fov * 0.5) return math.atan2(yLength * vec2f[1], near) def __sceneCheck(self, yaw, pitch, pivotPos, dist): dir = _vec3fFromYawPitch(yaw, -pitch) start = pivotPos - dir.scale(0.1) end = pivotPos - dir.scale(dist) colRes = BigWorld.wg_collideSegment(BigWorld.player().spaceID, start, end, 128) if colRes is not None: return Math.Vector3(colRes[0] - pivotPos).length else: return 0 def __calcPitchAngle(self, camDist, dir): alpha = self.__getRotateAngle(self.__cursorOffset) a = camDist b = dir.length A = 2.0 * a * math.cos(alpha) B = a * a - b * b D = A * A - 4.0 * B if D > 0.0: c1 = (A + math.sqrt(D)) * 0.5 c2 = (A - math.sqrt(D)) * 0.5 c = c1 if c1 > c2 else c2 beta = math.acos((a * a + b * b - c * c) / (2.0 * a * b)) eta = math.pi - beta return -dir.pitch - eta else: return -dir.pitch def __readCfg(self, dataSec): if dataSec is None: LOG_WARNING( 'Invalid section <arcadeMode/camera> in avatar_input_handler.xml' ) self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.01) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.01) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.01) bcfg['angleRange'] = readVec2(dataSec, 'angleRange', (0, 0), (180, 180), (10, 110)) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (100, 100), (2, 20)) bcfg['angleRange'][0] = math.radians( bcfg['angleRange'][0]) - math.pi * 0.5 bcfg['angleRange'][1] = math.radians( bcfg['angleRange'][1]) - math.pi * 0.5 ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['arcadeMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg from account_helpers.SettingsCore import g_settingsCore ucfg['horzInvert'] = g_settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = g_settingsCore.getSetting('mouseVertInvert') ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['startDist'] = readFloat(ds, 'startDist', 2, 500, 10) ucfg['startAngle'] = readFloat(ds, 'startAngle', 5, 180, 60) ucfg['startAngle'] = math.radians(ucfg['startAngle']) - math.pi * 0.5 self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['angleRange'] = bcfg['angleRange'] cfg['distRange'] = bcfg['distRange'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['startDist'] = ucfg['startDist'] cfg['startAngle'] = ucfg['startAngle'] enableShift = dataSec.readBool('shift', False) if enableShift: movementMappings = dict() movementMappings[Keys.KEY_A] = Math.Vector3(-1, 0, 0) movementMappings[Keys.KEY_D] = Math.Vector3(1, 0, 0) movementMappings[Keys.KEY_Q] = Math.Vector3(0, 1, 0) movementMappings[Keys.KEY_E] = Math.Vector3(0, -1, 0) movementMappings[Keys.KEY_W] = Math.Vector3(0, 0, 1) movementMappings[Keys.KEY_S] = Math.Vector3(0, 0, -1) shiftSensitivity = dataSec.readFloat('shiftSensitivity', 0.5) self.__shiftKeySensor = KeySensor(movementMappings, shiftSensitivity) self.__shiftKeySensor.reset(Math.Vector3()) return def _writeUserPreferences(self): ds = Settings.g_instance.userPrefs if not ds.has_key(Settings.KEY_CONTROL_MODE): ds.write(Settings.KEY_CONTROL_MODE, '') ucfg = self.__userCfg ds = ds[Settings.KEY_CONTROL_MODE] ds.writeBool('arcadeMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('arcadeMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('arcadeMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('arcadeMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('arcadeMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('arcadeMode/camera/startDist', ucfg['startDist']) ucfg['startAngle'] = math.degrees(ucfg['startAngle'] + math.pi * 0.5) ds.writeFloat('arcadeMode/camera/startAngle', ucfg['startAngle']) def calcYawPitchDelta(self, dx, dy): return (dx * self.__curSense * (-1 if self.__cfg['horzInvert'] else 1), dy * self.__curSense * (-1 if self.__cfg['vertInvert'] else 1)) def __updateAngles(self, dx, dy): cfg = self.__cfg anglesRange = cfg['angleRange'] offset = 0 yawDelta, pitchDelta = self.calcYawPitchDelta(dx, dy) self.__pitch -= pitchDelta self.__pitch = _clamp(anglesRange[0] + offset, anglesRange[1] + offset, self.__pitch) self.__yaw += yawDelta if self.__yaw > 2.0 * math.pi: self.__yaw -= 2.0 * math.pi elif self.__yaw < -2.0 * math.pi: self.__yaw += 2.0 * math.pi return (self.__yaw, self.__pitch, 0) def __update(self, dx, dy, dz, rotateMode=True, zoomMode=True, isCallback=False): if isCallback: self.__autoUpdateCallbackId = BigWorld.callback( 0.001, partial(self.__update, dx, dy, dz, rotateMode, zoomMode, True)) cfg = self.__cfg if zoomMode and dz != 0: distRange = cfg['distRange'] prevCam = self.__camDist self.__camDist -= dz * float(self.__curScrollSense) self.__camDist = _clamp(distRange[0], distRange[1], self.__camDist) self.__userCfg['startDist'] = self.__camDist if prevCam == self.__camDist and self.__onChangeControlMode is not None: if _almostZero(self.__camDist - distRange[0]): self.__onChangeControlMode() self.__cam.pivotMaxDist = self.__camDist if rotateMode: matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(dx, dy)) self.__cam.source = matrix return def __cameraUpdate(self): self.__cameraUpdateCallbackId = BigWorld.callback( 0.0, self.__cameraUpdate) if self.__cam.target is None: self.__cam.pivotPosition = self.__defaultPivotPos return else: if self.__shiftKeySensor is not None: self.__shiftKeySensor.update(1.0) if self.__shiftKeySensor.currentVelocity.lengthSquared > 0.0: currentShift = self.shiftCamPos( self.__shiftKeySensor.currentVelocity) self.__shiftKeySensor.currentVelocity = Math.Vector3() if currentShift.lengthSquared > 0.01: return targetPosition = Math.Matrix(self.__cam.target).translation BOTTOM_SHIFT = Math.Vector3(0, 2.0, 0) collData = BigWorld.wg_collideSegment( BigWorld.player().spaceID, targetPosition + BOTTOM_SHIFT, targetPosition + Math.Vector3(0, self.__defaultPivotPos.y, 0), 7) if collData is not None: collPoint = collData[0] collNormal = collData[1] if collNormal.dot(Math.Vector3(0, -1, 0)) > 0: self.__cam.pivotPosition = collPoint - Math.Vector3( 0, 0.3, 0) - targetPosition else: self.__cam.pivotPosition = self.__defaultPivotPos return def handleKeyEvent(self, isDown, key, mods, event=None): if self.__shiftKeySensor is None: return False elif BigWorld.isKeyDown(Keys.KEY_CAPSLOCK) and mods & 4: if key == Keys.KEY_C: self.shiftCamPos() return self.__shiftKeySensor.handleKeyEvent(key, isDown) else: self.__shiftKeySensor.reset(Math.Vector3()) return False
def __readCfg(self, dataSec): if dataSec is None: LOG_WARNING( 'Invalid section <arcadeMode/camera> in avatar_input_handler.xml' ) self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.01) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.01) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.01) bcfg['angleRange'] = readVec2(dataSec, 'angleRange', (0, 0), (180, 180), (10, 110)) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (100, 100), (2, 20)) bcfg['angleRange'][0] = math.radians( bcfg['angleRange'][0]) - math.pi * 0.5 bcfg['angleRange'][1] = math.radians( bcfg['angleRange'][1]) - math.pi * 0.5 ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['arcadeMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg from account_helpers.SettingsCore import g_settingsCore ucfg['horzInvert'] = g_settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = g_settingsCore.getSetting('mouseVertInvert') ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['startDist'] = readFloat(ds, 'startDist', 2, 500, 10) ucfg['startAngle'] = readFloat(ds, 'startAngle', 5, 180, 60) ucfg['startAngle'] = math.radians(ucfg['startAngle']) - math.pi * 0.5 self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['angleRange'] = bcfg['angleRange'] cfg['distRange'] = bcfg['distRange'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['startDist'] = ucfg['startDist'] cfg['startAngle'] = ucfg['startAngle'] enableShift = dataSec.readBool('shift', False) if enableShift: movementMappings = dict() movementMappings[Keys.KEY_A] = Math.Vector3(-1, 0, 0) movementMappings[Keys.KEY_D] = Math.Vector3(1, 0, 0) movementMappings[Keys.KEY_Q] = Math.Vector3(0, 1, 0) movementMappings[Keys.KEY_E] = Math.Vector3(0, -1, 0) movementMappings[Keys.KEY_W] = Math.Vector3(0, 0, 1) movementMappings[Keys.KEY_S] = Math.Vector3(0, 0, -1) shiftSensitivity = dataSec.readFloat('shiftSensitivity', 0.5) self.__shiftKeySensor = KeySensor(movementMappings, shiftSensitivity) self.__shiftKeySensor.reset(Math.Vector3()) return
def _readRotationSettings(self, configDataSec, rotationMappings): rotationSensitivity = configDataSec.readFloat('angularVelocity', 0.7) self._rotationSensor = KeySensor(rotationMappings, rotationSensitivity) self._rotationSensor.currentVelocity = Math.Vector3()
class ArcadeCamera(ICamera): camera = property(lambda self: self.__cam) angles = property(lambda self: (self.__yaw, self.__pitch)) def __init__(self, dataSec): self.__shiftKeySensor = None self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__camDist = 0 self.__curSense = 0 self.__curScrollSense = 0 self.__pitch = 0.0 self.__yaw = 0.0 self.__cursorOffset = (0, 0) self.__postmortemMode = False self.__modelsToCollideWith = [] self.__defaultPivotPos = Math.Vector3(0, 0, 0) self.__onChangeControlMode = None self.__autoUpdateCallbackId = None self.__cameraUpdateCallbackId = None return def create(self, pivotPos, onChangeControlMode = None, postmortemMode = False): self.__onChangeControlMode = onChangeControlMode self.__postmortemMode = postmortemMode replayCtrl = BattleReplay.g_replayCtrl vehicleID = replayCtrl.playerVehicleID if replayCtrl.isPlaying else BigWorld.player().playerVehicleID targetMat = BigWorld.entity(vehicleID).matrix self.__camDist = self.__cfg['startDist'] self.__cam.pivotMaxDist = self.__camDist self.__cam.maxDistHalfLife = 0.0 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = 0.0 self.__cam.pivotPosition = self.__defaultPivotPos = pivotPos self.__pitch = self.__cfg['startAngle'] self.__yaw = Math.Matrix(targetMat).yaw matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(0, 0)) self.__cam.source = matrix def destroy(self): self.disable() self.__onChangeControlMode = None self.__cam = None self._writeUserPreferences() if self.__cameraUpdateCallbackId is not None: BigWorld.cancelCallback(self.__cameraUpdateCallbackId) self.__cameraUpdateCallbackId = None return def addModelsToCollideWith(self, models): self.__modelsToCollideWith = self.__modelsToCollideWith + models if BigWorld.camera() == self.__cam: self.__cam.wg_setModelsToCollideWith(self.__modelsToCollideWith) def removeModelsToCollideWith(self, models): for model in models: if self.__modelsToCollideWith.count(model) > 0: self.__modelsToCollideWith.remove(model) if BigWorld.camera() == self.__cam: self.__cam.wg_setModelsToCollideWith(self.__modelsToCollideWith) def focusOnPos(self, preferredPos): self.__calcStartAngles(self.__cam.target, preferredPos) def shiftCamPos(self, shift = None): matrixProduct = self.__cam.target shiftMat = getattr(matrixProduct, 'a', None) if shiftMat is None: return Math.Vector3() else: shiftMat = Math.Matrix(shiftMat) if shift is not None: camDirection = Math.Vector3(self.__cam.direction) camDirection.y = 0 camDirection.normalise() normal = Math.Vector3(camDirection) normal.x = camDirection.z normal.z = -camDirection.x shiftMat.translation += camDirection * shift.z + Math.Vector3(0, 1, 0) * shift.y + normal * shift.x else: shiftMat.setIdentity() matrixProduct.a = shiftMat return shiftMat.translation def enable(self, preferredPos = None, closesDist = False, postmortemParams = None): camSource = None camDist = None camTarget = BigWorld.player().getOwnVehicleMatrix() camTargetTransOnly = Math.WGTranslationOnlyMP() camTargetTransOnly.source = camTarget camTarget = Math.MatrixProduct() shift = Math.Matrix() shift.setIdentity() camTarget.a = shift camTarget.b = camTargetTransOnly if not self.__postmortemMode: if preferredPos is not None: self.__calcStartAngles(camTarget, preferredPos) if closesDist: camDist = self.__cfg['distRange'][0] elif postmortemParams is not None: self.__yaw = postmortemParams[0][0] self.__pitch = postmortemParams[0][1] camDist = postmortemParams[1] camSource = Math.Matrix() camSource.setRotateYPR((self.__yaw, self.__pitch, 0)) else: camDist = self.__cfg['distRange'][1] replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: camDist = None vehicle = BigWorld.entity(replayCtrl.playerVehicleID) if vehicle is not None: camTarget = vehicle.matrix if camDist is not None: self.__camDist = camDist self.__cam.pivotMaxDist = camDist if camSource is not None: self.__cam.source = camSource self.__cam.target = camTarget self.__cam.sptHiding = True self.__cam.wg_setModelsToCollideWith(self.__modelsToCollideWith) BigWorld.camera(self.__cam) self.__cameraUpdate() return def disable(self): self.__cam.wg_setModelsToCollideWith([]) self.__cam.sptHiding = False BigWorld.camera(None) if self.__autoUpdateCallbackId is not None: BigWorld.cancelCallback(self.__autoUpdateCallbackId) self.__autoUpdateCallbackId = None if self.__cameraUpdateCallbackId is not None: BigWorld.cancelCallback(self.__cameraUpdateCallbackId) self.__cameraUpdateCallbackId = None if self.__shiftKeySensor is not None: self.__shiftKeySensor.reset(Math.Vector3()) return def update(self, dx, dy, dz, rotateMode = True, zoomMode = True): self.__curSense = self.__cfg['sensitivity'] self.__curScrollSense = self.__cfg['scrollSensitivity'] self.__update(dx, dy, dz, rotateMode, zoomMode, False) def autoUpdate(self, dx, dy, dz, rotateMode = True, zoomMode = True, onlyOnce = False): self.__curSense = self.__cfg['keySensitivity'] self.__curScrollSense = self.__cfg['keySensitivity'] if self.__autoUpdateCallbackId is not None: BigWorld.cancelCallback(self.__autoUpdateCallbackId) self.__autoUpdateCallbackId = None if onlyOnce: self.__update(dx, dy, dz, rotateMode, zoomMode, False) else: self.__autoUpdateCallbackId = BigWorld.callback(0.001, partial(self.__update, dx, dy, dz, rotateMode, zoomMode, True)) return def restoreDefaultsState(self): self.__camDist = self.__cfg['startDist'] self.__cam.pivotMaxDist = self.__camDist self.__cam.maxDistHalfLife = 0.025 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = 0.025 self.__pitch = self.__cfg['startAngle'] self.__yaw = Math.Matrix(self.__cam.target).yaw matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(0, 0)) self.__cam.source = matrix def getUserConfigValue(self, name): return self.__userCfg.get(name) def setUserConfigValue(self, name, value): if name not in self.__userCfg: return self.__userCfg[name] = value if name not in ('keySensitivity', 'sensitivity', 'scrollSensitivity'): self.__cfg[name] = self.__userCfg[name] else: self.__cfg[name] = self.__baseCfg[name] * self.__userCfg[name] def cursorOffset(self, offset): self.__cursorOffset = offset def setPivotPosition(self, position): self.__cam.pivotPosition = self.__defaultPivotPos = position def getPivotPosition(self): return self.__defaultPivotPos def setCameraDistance(self, distance): distRange = self.__cfg['distRange'] self.__camDist = distance self.__camDist = _clamp(distRange[0], distRange[1], self.__camDist) self.__cam.pivotMaxDist = self.__camDist def getCameraDistance(self): return self.__camDist def setYawPitch(self, yaw, pitch): anglesRange = self.__cfg['angleRange'] self.__pitch = pitch self.__pitch = _clamp(anglesRange[0], anglesRange[1], self.__pitch) self.__yaw = yaw if self.__yaw > 2.0 * math.pi: self.__yaw -= 2.0 * math.pi elif self.__yaw < -2.0 * math.pi: self.__yaw += 2.0 * math.pi matrix = Math.Matrix() matrix.setRotateYPR((self.__yaw, self.__pitch, 0)) self.__cam.source = matrix def getYawPitch(self): return (self.__yaw, self.__pitch) def getPivotPos(self): camPivotPos = self.__cam.pivotPosition posOnEntity = Math.Matrix(self.__cam.target).applyPoint(Math.Vector3(0, camPivotPos[1], 0)) rotMat = Math.Matrix() rotMat.setRotateY(self.__yaw) pivotPos = posOnEntity + rotMat.applyPoint(Math.Vector3(camPivotPos[0], 0, camPivotPos[2])) return pivotPos def __calcStartAngles(self, targetMat, preferredPos): camDist = self.__camDist camPivotPos = self.__cam.pivotPosition posOnEntity = Math.Matrix(targetMat).applyPoint(Math.Vector3(0, camPivotPos[1], 0)) yaw = (preferredPos - posOnEntity).yaw rotMat = Math.Matrix() rotMat.setRotateY(yaw) pivotPos = posOnEntity + rotMat.applyPoint(Math.Vector3(camPivotPos[0], 0, camPivotPos[2])) dir = preferredPos - pivotPos posDir = preferredPos - Math.Matrix(targetMat).applyToOrigin() pitch = -posDir.pitch try: pitch = self.__calcPitchAngle(camDist, dir) dist = self.__sceneCheck(yaw, pitch, pivotPos, camDist) if dist != 0: pitch = self.__calcPitchAngle(dist, dir) except: LOG_CURRENT_EXCEPTION() self.__pitch = pitch self.__yaw = yaw matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(0, 0)) self.__cam.source = matrix def __getRotateAngle(self, vec2f): fov = BigWorld.projection().fov near = BigWorld.projection().nearPlane yLength = near * math.tan(fov * 0.5) return math.atan2(yLength * vec2f[1], near) def __sceneCheck(self, yaw, pitch, pivotPos, dist): dir = _vec3fFromYawPitch(yaw, -pitch) start = pivotPos - dir.scale(0.1) end = pivotPos - dir.scale(dist) colRes = BigWorld.wg_collideSegment(BigWorld.player().spaceID, start, end, 128) if colRes is not None: return Math.Vector3(colRes[0] - pivotPos).length else: return 0 def __calcPitchAngle(self, camDist, dir): alpha = self.__getRotateAngle(self.__cursorOffset) a = camDist b = dir.length A = 2.0 * a * math.cos(alpha) B = a * a - b * b D = A * A - 4.0 * B if D > 0.0: c1 = (A + math.sqrt(D)) * 0.5 c2 = (A - math.sqrt(D)) * 0.5 c = c1 if c1 > c2 else c2 beta = math.acos((a * a + b * b - c * c) / (2.0 * a * b)) eta = math.pi - beta return -dir.pitch - eta else: return -dir.pitch def __readCfg(self, dataSec): if dataSec is None: LOG_WARNING('Invalid section <arcadeMode/camera> in avatar_input_handler.xml') self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.01) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.01) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.01) bcfg['angleRange'] = readVec2(dataSec, 'angleRange', (0, 0), (180, 180), (10, 110)) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (100, 100), (2, 20)) bcfg['angleRange'][0] = math.radians(bcfg['angleRange'][0]) - math.pi * 0.5 bcfg['angleRange'][1] = math.radians(bcfg['angleRange'][1]) - math.pi * 0.5 ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['arcadeMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg from account_helpers.SettingsCore import g_settingsCore ucfg['horzInvert'] = g_settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = g_settingsCore.getSetting('mouseVertInvert') ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['startDist'] = readFloat(ds, 'startDist', 2, 500, 10) ucfg['startAngle'] = readFloat(ds, 'startAngle', 5, 180, 60) ucfg['startAngle'] = math.radians(ucfg['startAngle']) - math.pi * 0.5 self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['angleRange'] = bcfg['angleRange'] cfg['distRange'] = bcfg['distRange'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['startDist'] = ucfg['startDist'] cfg['startAngle'] = ucfg['startAngle'] enableShift = dataSec.readBool('shift', False) if enableShift: movementMappings = dict() movementMappings[Keys.KEY_A] = Math.Vector3(-1, 0, 0) movementMappings[Keys.KEY_D] = Math.Vector3(1, 0, 0) movementMappings[Keys.KEY_Q] = Math.Vector3(0, 1, 0) movementMappings[Keys.KEY_E] = Math.Vector3(0, -1, 0) movementMappings[Keys.KEY_W] = Math.Vector3(0, 0, 1) movementMappings[Keys.KEY_S] = Math.Vector3(0, 0, -1) shiftSensitivity = dataSec.readFloat('shiftSensitivity', 0.5) self.__shiftKeySensor = KeySensor(movementMappings, shiftSensitivity) self.__shiftKeySensor.reset(Math.Vector3()) return def _writeUserPreferences(self): ds = Settings.g_instance.userPrefs if not ds.has_key(Settings.KEY_CONTROL_MODE): ds.write(Settings.KEY_CONTROL_MODE, '') ucfg = self.__userCfg ds = ds[Settings.KEY_CONTROL_MODE] ds.writeBool('arcadeMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('arcadeMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('arcadeMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('arcadeMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('arcadeMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('arcadeMode/camera/startDist', ucfg['startDist']) ucfg['startAngle'] = math.degrees(ucfg['startAngle'] + math.pi * 0.5) ds.writeFloat('arcadeMode/camera/startAngle', ucfg['startAngle']) def calcYawPitchDelta(self, dx, dy): return (dx * self.__curSense * (-1 if self.__cfg['horzInvert'] else 1), dy * self.__curSense * (-1 if self.__cfg['vertInvert'] else 1)) def __updateAngles(self, dx, dy): cfg = self.__cfg anglesRange = cfg['angleRange'] offset = 0 yawDelta, pitchDelta = self.calcYawPitchDelta(dx, dy) self.__pitch -= pitchDelta self.__pitch = _clamp(anglesRange[0] + offset, anglesRange[1] + offset, self.__pitch) self.__yaw += yawDelta if self.__yaw > 2.0 * math.pi: self.__yaw -= 2.0 * math.pi elif self.__yaw < -2.0 * math.pi: self.__yaw += 2.0 * math.pi return (self.__yaw, self.__pitch, 0) def __update(self, dx, dy, dz, rotateMode = True, zoomMode = True, isCallback = False): if isCallback: self.__autoUpdateCallbackId = BigWorld.callback(0.001, partial(self.__update, dx, dy, dz, rotateMode, zoomMode, True)) cfg = self.__cfg if zoomMode and dz != 0: distRange = cfg['distRange'] prevCam = self.__camDist self.__camDist -= dz * float(self.__curScrollSense) self.__camDist = _clamp(distRange[0], distRange[1], self.__camDist) self.__userCfg['startDist'] = self.__camDist if prevCam == self.__camDist and self.__onChangeControlMode is not None: if _almostZero(self.__camDist - distRange[0]): self.__onChangeControlMode() self.__cam.pivotMaxDist = self.__camDist if rotateMode: matrix = Math.Matrix() matrix.setRotateYPR(self.__updateAngles(dx, dy)) self.__cam.source = matrix return def __cameraUpdate(self): self.__cameraUpdateCallbackId = BigWorld.callback(0.0, self.__cameraUpdate) if self.__cam.target is None: self.__cam.pivotPosition = self.__defaultPivotPos return else: if self.__shiftKeySensor is not None: self.__shiftKeySensor.update(1.0) if self.__shiftKeySensor.currentVelocity.lengthSquared > 0.0: currentShift = self.shiftCamPos(self.__shiftKeySensor.currentVelocity) self.__shiftKeySensor.currentVelocity = Math.Vector3() if currentShift.lengthSquared > 0.01: return targetPosition = Math.Matrix(self.__cam.target).translation BOTTOM_SHIFT = Math.Vector3(0, 2.0, 0) collData = BigWorld.wg_collideSegment(BigWorld.player().spaceID, targetPosition + BOTTOM_SHIFT, targetPosition + Math.Vector3(0, self.__defaultPivotPos.y, 0), 7) if collData is not None: collPoint = collData[0] collNormal = collData[1] if collNormal.dot(Math.Vector3(0, -1, 0)) > 0: self.__cam.pivotPosition = collPoint - Math.Vector3(0, 0.3, 0) - targetPosition else: self.__cam.pivotPosition = self.__defaultPivotPos return def handleKeyEvent(self, isDown, key, mods, event = None): if self.__shiftKeySensor is None: return False elif BigWorld.isKeyDown(Keys.KEY_CAPSLOCK) and mods & 4: if key == Keys.KEY_C: self.shiftCamPos() return self.__shiftKeySensor.handleKeyEvent(key, isDown) else: self.__shiftKeySensor.reset(Math.Vector3()) return False