def __init__(self, dataSec, aim): CallbackDelayer.__init__(self) TimeDeltaMeter.__init__(self) self.__shiftKeySensor = None self.__movementOscillator = None self.__impulseOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) self.__cam = None self.__aim = None self.__onChangeControlMode = None self.__aimingSystem = None self.__curSense = 0 self.__curScrollSense = 0 self.__postmortemMode = False self.__vehiclesToCollideWith = set() self.__focalPointDist = 1.0 self.__autoUpdateDxDyDz = Vector3(0.0) self.__defaultAimOffset = (0.0, 0.0) if aim is None: return else: self.__aim = weakref.proxy(aim) self.__cam = BigWorld.HomingCamera() aimOffset = self.__aim.offset() self.__cam.aimPointClipCoords = Vector2(aimOffset) self.__defaultAimOffset = (aimOffset[0], aimOffset[1]) return
def __init__(self, dataSec, defaultOffset = None, binoculars = None): CallbackDelayer.__init__(self) self.__impulseOscillator = None self.__movementOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) if binoculars is None: return else: self.__cam = BigWorld.FreeCamera() self.__zoom = self.__cfg['zoom'] self.__curSense = 0 self.__curScrollSense = 0 self.__waitVehicleCallbackId = None self.__onChangeControlMode = None self.__aimingSystem = SniperAimingSystem(dataSec) self.__binoculars = binoculars self.__defaultAimOffset = defaultOffset or Vector2() self.__crosshairMatrix = createCrosshairMatrix(offsetFromNearPlane=self.__dynamicCfg['aimMarkerDistance']) self.__prevTime = BigWorld.time() self.__autoUpdateDxDyDz = Vector3(0, 0, 0) if BattleReplay.g_replayCtrl.isPlaying: BattleReplay.g_replayCtrl.setDataCallback('applyZoom', self.__applyZoom) return
def __init__(self, dataSec, aim, binoculars): CallbackDelayer.__init__(self) self.__impulseOscillator = None self.__movementOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) if aim is None or binoculars is None: return else: self.__cam = BigWorld.FreeCamera() self.__zoom = self.__cfg['zoom'] self.__curSense = 0 self.__curScrollSense = 0 self.__waitVehicleCallbackId = None self.__onChangeControlMode = None self.__aimingSystem = SniperAimingSystem(dataSec) self.__aim = weakref.proxy(aim) self.__binoculars = binoculars self.__defaultAimOffset = self.__aim.offset() self.__defaultAimOffset = (self.__defaultAimOffset[0], self.__defaultAimOffset[1]) self.__crosshairMatrix = createCrosshairMatrix(offsetFromNearPlane=self.__dynamicCfg['aimMarkerDistance']) self.__prevTime = BigWorld.time() self.__autoUpdateDxDyDz = Vector3(0, 0, 0) return
def __init__(self, dataSec, defaultOffset = None): CallbackDelayer.__init__(self) TimeDeltaMeter.__init__(self) self.__shiftKeySensor = None self.__movementOscillator = None self.__impulseOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) self.__onChangeControlMode = None self.__aimingSystem = None self.__curSense = 0 self.__curScrollSense = 0 self.__postmortemMode = False self.__vehiclesToCollideWith = set() self.__focalPointDist = 1.0 self.__autoUpdateDxDyDz = Vector3(0.0) self.__updatedByKeyboard = False if defaultOffset is not None: self.__defaultAimOffset = defaultOffset self.__cam = BigWorld.HomingCamera(self.__adCfg['enable']) if self.__adCfg['enable']: self.__cam.initAdvancedCollider(self.__adCfg['fovRatio'], self.__adCfg['rollbackSpeed'], self.__adCfg['minimalCameraDistance'], self.__adCfg['speedThreshold'], self.__adCfg['minimalVolume']) for group_name in VOLUME_GROUPS_NAMES: self.__cam.addVolumeGroup(self.__adCfg['volumeGroups'][group_name]) self.__cam.aimPointClipCoords = defaultOffset else: self.__defaultAimOffset = Vector2() self.__cam = None return
def __init__(self, dataSec, defaultOffset = None): CallbackDelayer.__init__(self) TimeDeltaMeter.__init__(self) self.__shiftKeySensor = None self.__movementOscillator = None self.__impulseOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) self.__onChangeControlMode = None self.__aimingSystem = None self.__curSense = 0 self.__curScrollSense = 0 self.__postmortemMode = False self.__vehiclesToCollideWith = set() self.__focalPointDist = 1.0 self.__autoUpdateDxDyDz = Vector3(0.0) self.__updatedByKeyboard = False if defaultOffset is not None: self.__defaultAimOffset = defaultOffset self.__cam = BigWorld.HomingCamera() self.__cam.aimPointClipCoords = defaultOffset else: self.__defaultAimOffset = Vector2() self.__cam = None return
def __init__(self, dataSec, aim): CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__curSense = self.__cfg['sensitivity'] self.__onChangeControlMode = None self.__aimingSystem = StrategicAimingSystem(self.__cfg['distRange'][0], StrategicCamera._CAMERA_YAW) self.__prevTime = BigWorld.time() self.__aimOffsetFunc = None if aim is None: self.__aimOffsetFunc = lambda x = None: (0, 0) else: self.__aimOffsetFunc = aim.offset self.__autoUpdatePosition = False self.__dxdydz = Vector3(0, 0, 0) self.__needReset = 0
class ArcadeCamera(ICamera, CallbackDelayer, TimeDeltaMeter): REASONS_AFFECT_CAMERA_DIRECTLY = (ImpulseReason.MY_SHOT, ImpulseReason.OTHER_SHOT, ImpulseReason.VEHICLE_EXPLOSION, ImpulseReason.HE_EXPLOSION) _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): ArcadeCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return ArcadeCamera._DYNAMIC_ENABLED _FILTER_LENGTH = 5 _DEFAULT_MAX_ACCELERATION_DURATION = 1.5 _FOCAL_POINT_CHANGE_SPEED = 100.0 _FOCAL_POINT_MIN_DIFF = 5.0 _MIN_REL_SPEED_ACC_SMOOTHING = 0.7 def getReasonsAffectCameraDirectly(self): return ArcadeCamera.REASONS_AFFECT_CAMERA_DIRECTLY def __getVehicleMProv(self): refinedMProv = self.__aimingSystem.vehicleMProv return refinedMProv.b.source def __setVehicleMProv(self, vehicleMProv): prevAimRel = self.__cam.aimPointProvider.a if self.__cam.aimPointProvider is not None else None refinedVehicleMProv = self.__refineVehicleMProv(vehicleMProv) self.__aimingSystem.vehicleMProv = refinedVehicleMProv self.__setupCameraProviders(refinedVehicleMProv) self.__cam.speedTreeTarget = self.__aimingSystem.vehicleMProv self.__aimingSystem.update(0.0) if prevAimRel is not None: self.__cam.aimPointProvider.a = prevAimRel baseTranslation = Matrix(refinedVehicleMProv).translation relativePosition = self.__aimingSystem.matrix.translation - baseTranslation self.__setCameraPosition(relativePosition) return camera = property(lambda self: self.__cam) angles = property(lambda self: (self.__aimingSystem.yaw, self.__aimingSystem.pitch)) aimingSystem = property(lambda self: self.__aimingSystem) vehicleMProv = property(__getVehicleMProv, __setVehicleMProv) def __init__(self, dataSec, aim): CallbackDelayer.__init__(self) TimeDeltaMeter.__init__(self) self.__shiftKeySensor = None self.__movementOscillator = None self.__impulseOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) self.__cam = None self.__aim = None self.__onChangeControlMode = None self.__aimingSystem = None self.__curSense = 0 self.__curScrollSense = 0 self.__postmortemMode = False self.__vehiclesToCollideWith = set() self.__focalPointDist = 1.0 self.__autoUpdateDxDyDz = Vector3(0.0) self.__defaultAimOffset = (0.0, 0.0) if aim is None: return else: self.__aim = weakref.proxy(aim) self.__cam = BigWorld.HomingCamera() aimOffset = self.__aim.offset() self.__cam.aimPointClipCoords = Vector2(aimOffset) self.__defaultAimOffset = (aimOffset[0], aimOffset[1]) return def create(self, pivotPos, onChangeControlMode = None, postmortemMode = False): self.__onChangeControlMode = onChangeControlMode self.__postmortemMode = postmortemMode targetMat = self.getTargetMProv() self.__aimingSystem = ArcadeAimingSystem(self.__refineVehicleMProv(targetMat), pivotPos.y, pivotPos.z, self.__calcAimMatrix(), self.__cfg['angleRange'], not postmortemMode) self.setCameraDistance(self.__cfg['startDist']) self.__aimingSystem.pitch = self.__cfg['startAngle'] self.__aimingSystem.yaw = Math.Matrix(targetMat).yaw self.__updateAngles(0, 0) cameraPosProvider = Math.Vector4Translation(self.__aimingSystem.matrix) self.__cam.cameraPositionProvider = cameraPosProvider def getTargetMProv(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.playerVehicleID != 0: vehicleID = replayCtrl.playerVehicleID else: vehicleID = BigWorld.player().playerVehicleID return BigWorld.entity(vehicleID).matrix def reinitMatrix(self): self.__setVehicleMProv(self.getTargetMProv()) def setToVehicleDirection(self): matrix = Math.Matrix(self.getTargetMProv()) self.setYawPitch(matrix.yaw, matrix.pitch) def destroy(self): CallbackDelayer.destroy(self) self.disable() self.__onChangeControlMode = None self.__cam = None self.__aim = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None return def getPivotSettings(self): return self.__aimingSystem.getPivotSettings() def setPivotSettings(self, heightAboveBase, focusRadius): self.__aimingSystem.setPivotSettings(heightAboveBase, focusRadius) def addVehicleToCollideWith(self, vehicle): self.__vehiclesToCollideWith.add(vehicle) self.__setModelsToCollideWith(self.__vehiclesToCollideWith) def removeVehicleToCollideWith(self, vehicle): lengthBefore = len(self.__vehiclesToCollideWith) self.__vehiclesToCollideWith = set([ v for v in self.__vehiclesToCollideWith if v.id != vehicle.id ]) if lengthBefore != len(self.__vehiclesToCollideWith): self.__setModelsToCollideWith(self.__vehiclesToCollideWith) def clearVehicleToCollideWith(self): self.__vehiclesToCollideWith = set() self.__setModelsToCollideWith(self.__vehiclesToCollideWith) def __setModelsToCollideWith(self, vehicles): colliders = [] for vehicle in vehicles: vehicleAppearance = vehicle.appearance compound = vehicleAppearance.compoundModel colliders.append((compound.node(TankPartNames.HULL), compound.bounds, compound.getPartGeometryLink(TankPartIndexes.HULL))) if not vehicle.isTurretDetached: colliders.append((compound.node(TankPartNames.TURRET), compound.bounds, compound.getPartGeometryLink(TankPartIndexes.TURRET))) self.__cam.setDynamicColliders(colliders) self.__aimingSystem.setDynamicColliders(colliders) def focusOnPos(self, preferredPos): self.__aimingSystem.focusOnPos(preferredPos) def shiftCamPos(self, shift = None): matrixProduct = self.__aimingSystem.vehicleMProv shiftMat = matrixProduct.a if shift is not None: camDirection = Math.Vector3(math.sin(self.angles[0]), 0, math.cos(self.angles[0])) 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() return def enable(self, preferredPos = None, closesDist = False, postmortemParams = None, turretYaw = None, gunPitch = None): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isRecording: replayCtrl.setAimClipPosition(Vector2(self.__aim.offset())) self.measureDeltaTime() camDist = None vehicle = BigWorld.player().getVehicleAttached() initialVehicleMatrix = BigWorld.player().getOwnVehicleMatrix() if vehicle is None else vehicle.matrix vehicleMProv = initialVehicleMatrix if not self.__postmortemMode: if closesDist: camDist = self.__cfg['distRange'][0] elif postmortemParams is not None: self.__aimingSystem.yaw = postmortemParams[0][0] self.__aimingSystem.pitch = postmortemParams[0][1] camDist = postmortemParams[1] 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: vehicleMProv = vehicle.matrix if camDist is not None: self.setCameraDistance(camDist) else: self.__inputInertia.teleport(self.__calcRelativeDist()) self.vehicleMProv = vehicleMProv self.__setModelsToCollideWith(self.__vehiclesToCollideWith) BigWorld.camera(self.__cam) self.__aimingSystem.enable(preferredPos, turretYaw, gunPitch) self.__aimingSystem.aimMatrix = self.__calcAimMatrix() self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) from gui import g_guiResetters g_guiResetters.add(self.__onRecreateDevice) return def __refineVehicleMProv(self, vehicleMProv): vehicleTranslationOnly = Math.WGTranslationOnlyMP() vehicleTranslationOnly.source = vehicleMProv refinedMatrixProvider = Math.MatrixProduct() refinedMatrixProvider.a = mathUtils.createIdentityMatrix() refinedMatrixProvider.b = vehicleTranslationOnly return refinedMatrixProvider def __setupCameraProviders(self, vehicleMProv): vehiclePos = Math.Vector4Translation(vehicleMProv) cameraPositionProvider = Math.Vector4Combiner() cameraPositionProvider.fn = 'ADD' cameraPositionProvider.a = Vector4(0, 0, 0, 0) cameraPositionProvider.b = vehiclePos cameraAimPointProvider = Math.Vector4Combiner() cameraAimPointProvider.fn = 'ADD' cameraAimPointProvider.a = Vector4(0, 0, 1, 0) cameraAimPointProvider.b = vehiclePos self.__cam.cameraPositionProvider = cameraPositionProvider self.__cam.aimPointProvider = cameraAimPointProvider self.__cam.pivotPositionProvider = self.__aimingSystem.positionAboveVehicleProv def __setCameraPosition(self, relativeToVehiclePosition): self.__cam.cameraPositionProvider.a = Vector4(relativeToVehiclePosition.x, relativeToVehiclePosition.y, relativeToVehiclePosition.z, 1.0) def __setCameraAimPoint(self, relativeToVehiclePosition): self.__cam.aimPointProvider.a = Vector4(relativeToVehiclePosition.x, relativeToVehiclePosition.y, relativeToVehiclePosition.z, 1.0) def disable(self): from gui import g_guiResetters if self.__onRecreateDevice in g_guiResetters: g_guiResetters.remove(self.__onRecreateDevice) self.__setModelsToCollideWith([]) self.__cam.speedTreeTarget = None BigWorld.camera(None) if self.__shiftKeySensor is not None: self.__shiftKeySensor.reset(Math.Vector3()) self.stopCallback(self.__cameraUpdate) self.__movementOscillator.reset() self.__impulseOscillator.reset() self.__noiseOscillator.reset() self.__accelerationSmoother.reset() self.__autoUpdateDxDyDz.set(0) self.__inputInertia.teleport(self.__calcRelativeDist()) FovExtended.instance().resetFov() return def update(self, dx, dy, dz, rotateMode = True, zoomMode = True, updatedByKeyboard = False): self.__curSense = self.__cfg['keySensitivity'] if updatedByKeyboard else self.__cfg['sensitivity'] self.__curScrollSense = self.__cfg['keySensitivity'] if updatedByKeyboard else self.__cfg['scrollSensitivity'] if updatedByKeyboard: self.__autoUpdateDxDyDz.set(dx, dy, dz) else: self.__autoUpdateDxDyDz.set(0) self.__update(dx, dy, dz, rotateMode, zoomMode, False) def restoreDefaultsState(self): LOG_ERROR('ArcadeCamera::restoreDefaultState is obsolete!') def getConfigValue(self, name): return self.__cfg.get(name) def getUserConfigValue(self, name): return self.__userCfg.get(name) def setUserConfigValue(self, name, value): if name not in self.__userCfg: return else: self.__userCfg[name] = value if name not in ('keySensitivity', 'sensitivity', 'scrollSensitivity'): self.__cfg[name] = self.__userCfg[name] if name == 'fovMultMinMaxDist' and getattr(self, '_ArcadeCamera__aimingSystem', None) is not None: self.__inputInertia.teleport(self.__calcRelativeDist(), value) else: self.__cfg[name] = self.__baseCfg[name] * self.__userCfg[name] return def setCameraDistance(self, distance): distRange = self.__cfg['distRange'] clampedDist = mathUtils.clamp(distRange[0], distRange[1], distance) self.__aimingSystem.distanceFromFocus = clampedDist self.__inputInertia.teleport(self.__calcRelativeDist()) def getCameraDistance(self): return self.__aimingSystem.distanceFromFocus def setYawPitch(self, yaw, pitch): self.__aimingSystem.yaw = yaw self.__aimingSystem.pitch = pitch matrix = Math.Matrix() matrix.setRotateYPR((self.__aimingSystem.yaw, self.__aimingSystem.pitch, 0)) 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): yawDelta, pitchDelta = self.calcYawPitchDelta(dx, dy) self.__aimingSystem.handleMovement(yawDelta, -pitchDelta) return (self.__aimingSystem.yaw, self.__aimingSystem.pitch, 0) def __update(self, dx, dy, dz, rotateMode = True, zoomMode = True, isCallback = False): prevPos = self.__inputInertia.calcWorldPos(self.__aimingSystem.matrix) distChanged = False if zoomMode and dz != 0: prevDist = self.__aimingSystem.distanceFromFocus distDelta = dz * float(self.__curScrollSense) distMinMax = self.__cfg['distRange'] newDist = mathUtils.clamp(distMinMax.min, distMinMax.max, prevDist - distDelta) if abs(newDist - prevDist) > 0.001: self.__aimingSystem.distanceFromFocus = newDist self.__userCfg['startDist'] = newDist self.__inputInertia.glideFov(self.__calcRelativeDist()) self.__aimingSystem.aimMatrix = self.__calcAimMatrix() distChanged = True changeControlMode = prevDist == newDist and mathUtils.almostZero(newDist - distMinMax.min) if changeControlMode and self.__onChangeControlMode is not None: self.__onChangeControlMode() return if rotateMode: self.__updateAngles(dx, dy) if ENABLE_INPUT_ROTATION_INERTIA and not distChanged: self.__aimingSystem.update(0.0) if ENABLE_INPUT_ROTATION_INERTIA or distChanged: worldDeltaPos = prevPos - self.__aimingSystem.matrix.translation matInv = Matrix(self.__aimingSystem.matrix) matInv.invert() self.__inputInertia.glide(matInv.applyVector(worldDeltaPos)) return def __cameraUpdate(self): if not (self.__autoUpdateDxDyDz.x == 0.0 and self.__autoUpdateDxDyDz.y == 0.0 and self.__autoUpdateDxDyDz.z == 0.0): self.__update(self.__autoUpdateDxDyDz.x, self.__autoUpdateDxDyDz.y, self.__autoUpdateDxDyDz.z) inertDt = deltaTime = self.measureDeltaTime() replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: repSpeed = replayCtrl.playbackSpeed if repSpeed == 0.0: inertDt = 0.01 deltaTime = 0.0 else: inertDt = deltaTime = deltaTime / repSpeed self.__aimingSystem.update(deltaTime) virginShotPoint = self.__aimingSystem.getThirdPersonShotPoint() delta = self.__inputInertia.positionDelta sign = delta.dot(Vector3(0, 0, 1)) self.__inputInertia.update(inertDt) delta = (delta - self.__inputInertia.positionDelta).length if delta != 0.0: self.__cam.setScrollDelta(math.copysign(delta, sign)) FovExtended.instance().setFovByMultiplier(self.__inputInertia.fovZoomMultiplier) unshakenPos = self.__inputInertia.calcWorldPos(self.__aimingSystem.idealMatrix if self.__cam.advancedColliderEnabled else self.__aimingSystem.matrix) vehMatrix = Math.Matrix(self.__aimingSystem.vehicleMProv) vehiclePos = vehMatrix.translation fromVehicleToUnshakedPos = unshakenPos - vehiclePos deviationBasis = mathUtils.createRotationMatrix(Vector3(self.__aimingSystem.yaw, 0, 0)) impulseDeviation, movementDeviation, oscillationsZoomMultiplier = self.__updateOscillators(deltaTime) relCamPosMatrix = mathUtils.createTranslationMatrix(impulseDeviation + movementDeviation) relCamPosMatrix.postMultiply(deviationBasis) relCamPosMatrix.translation += fromVehicleToUnshakedPos upRotMat = mathUtils.createRotationMatrix(Vector3(0, 0, -impulseDeviation.x * self.__dynamicCfg['sideImpulseToRollRatio'] - self.__noiseOscillator.deviation.z)) upRotMat.postMultiply(relCamPosMatrix) self.__cam.up = upRotMat.applyVector(Vector3(0, 1, 0)) relTranslation = relCamPosMatrix.translation self.__setCameraPosition(relTranslation) shotPoint = self.__calcFocalPoint(virginShotPoint, deltaTime) vehToShotPoint = shotPoint - vehiclePos self.__setCameraAimPoint(vehToShotPoint) replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: aimOffset = self.__calcAimOffset(oscillationsZoomMultiplier) if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__cam.aimPointClipCoords = aimOffset self.__aim.offset(aimOffset) if self.__shiftKeySensor is not None: self.__shiftKeySensor.update(1.0) if self.__shiftKeySensor.currentVelocity.lengthSquared > 0.0: self.shiftCamPos(self.__shiftKeySensor.currentVelocity) self.__shiftKeySensor.currentVelocity = Math.Vector3() return 0.0 def __calcFocalPoint(self, shotPoint, deltaTime): aimStartPoint = self.__aimingSystem.matrix.translation aimDir = shotPoint - aimStartPoint distToShotPoint = aimDir.length if distToShotPoint < 0.001: return shotPoint aimDir /= distToShotPoint absDiff = abs(distToShotPoint - self.__focalPointDist) if absDiff < ArcadeCamera._FOCAL_POINT_MIN_DIFF or absDiff <= ArcadeCamera._FOCAL_POINT_CHANGE_SPEED * deltaTime: self.__focalPointDist = distToShotPoint return shotPoint changeDir = (distToShotPoint - self.__focalPointDist) / absDiff self.__focalPointDist += changeDir * ArcadeCamera._FOCAL_POINT_CHANGE_SPEED * deltaTime return aimStartPoint + aimDir * self.__focalPointDist def __calcAimOffset(self, oscillationsZoomMultiplier): fov = BigWorld.projection().fov aspect = cameras.getScreenAspectRatio() yTan = math.tan(fov * 0.5) xTan = yTan * aspect defaultX = self.__defaultAimOffset[0] defaultY = self.__defaultAimOffset[1] yawFromImpulse = self.__impulseOscillator.deviation.x * self.__dynamicCfg['sideImpulseToYawRatio'] xImpulseDeviationTan = math.tan(-(yawFromImpulse + self.__noiseOscillator.deviation.x) * oscillationsZoomMultiplier) pitchFromImpulse = self.__impulseOscillator.deviation.z * self.__dynamicCfg['frontImpulseToPitchRatio'] yImpulseDeviationTan = math.tan((pitchFromImpulse + self.__noiseOscillator.deviation.y) * oscillationsZoomMultiplier) totalOffset = Vector2((defaultX * xTan + xImpulseDeviationTan) / (xTan * (1 - defaultX * xTan * xImpulseDeviationTan)), (defaultY * yTan + yImpulseDeviationTan) / (yTan * (1 - defaultY * yTan * yImpulseDeviationTan))) return totalOffset def __calcRelativeDist(self): distRange = self.__cfg['distRange'] curDist = self.__aimingSystem.distanceFromFocus relDist = (curDist - distRange[0]) / (distRange[1] - distRange[0]) return relDist def __calcCurOscillatorAcceleration(self, deltaTime): vehicle = BigWorld.player().getVehicleAttached() if vehicle is None: return Vector3(0, 0, 0) else: vehFilter = vehicle.filter curVelocity = getattr(vehFilter, 'velocity', Vector3(0.0)) relativeSpeed = curVelocity.length / vehicle.typeDescriptor.physics['speedLimits'][0] if relativeSpeed >= ArcadeCamera._MIN_REL_SPEED_ACC_SMOOTHING: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg['accelerationThreshold'] else: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg['accelerationMax'] acceleration = self.__accelerationSmoother.update(vehicle, deltaTime) yawMat = mathUtils.createRotationMatrix((-self.__aimingSystem.yaw, 0, 0)) acceleration = yawMat.applyVector(-acceleration) oscillatorAcceleration = Vector3(acceleration.x, acceleration.y, acceleration.z) return oscillatorAcceleration * self.__dynamicCfg['accelerationSensitivity'] def __updateOscillators(self, deltaTime): if not ArcadeCamera.isCameraDynamic(): self.__impulseOscillator.reset() self.__movementOscillator.reset() self.__noiseOscillator.reset() return (Vector3(0), Vector3(0), 1.0) oscillatorAcceleration = self.__calcCurOscillatorAcceleration(deltaTime) self.__movementOscillator.externalForce += oscillatorAcceleration self.__impulseOscillator.update(deltaTime) self.__movementOscillator.update(deltaTime) self.__noiseOscillator.update(deltaTime) self.__impulseOscillator.externalForce.set(0, 0, 0) self.__movementOscillator.externalForce.set(0, 0, 0) self.__noiseOscillator.externalForce.set(0, 0, 0) relDist = self.__calcRelativeDist() zoomMultiplier = mathUtils.lerp(1.0, self.__dynamicCfg['zoomExposure'], relDist) impulseDeviation = Vector3(self.__impulseOscillator.deviation) impulseDeviation.set(impulseDeviation.x * zoomMultiplier, impulseDeviation.y * zoomMultiplier, impulseDeviation.z * zoomMultiplier) movementDeviation = Vector3(self.__movementOscillator.deviation) movementDeviation.set(movementDeviation.x * zoomMultiplier, movementDeviation.y * zoomMultiplier, movementDeviation.z * zoomMultiplier) return (impulseDeviation, movementDeviation, zoomMultiplier) def applyImpulse(self, position, impulse, reason = ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse(impulse, reason) yawMat = mathUtils.createRotationMatrix((-self.__aimingSystem.yaw, 0, 0)) impulseLocal = yawMat.applyVector(adjustedImpulse) self.__impulseOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason = ImpulseReason.ME_HIT): applicationPosition = self.__cam.position if reason == ImpulseReason.SPLASH: applicationPosition = Matrix(self.vehicleMProv).translation impulse = applicationPosition - position distance = impulse.length if distance < 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.OTHER_SHOT and distance <= self.__dynamicCfg['maxShotImpulseDistance']: impulse *= impulseValue / distance elif reason == ImpulseReason.SPLASH or reason == ImpulseReason.HE_EXPLOSION: impulse *= impulseValue / distance elif reason == ImpulseReason.VEHICLE_EXPLOSION and distance <= self.__dynamicCfg['maxExplosionImpulseDistance']: impulse *= impulseValue / distance else: return self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3(noiseMagnitude) self.__noiseOscillator.applyImpulse(noiseImpulse) 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 reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection('gui/avatar_input_handler.xml/arcadeMode/camera/') self.__readCfg(cameraSec) def __calcAimMatrix(self): endMult = self.__inputInertia.endZoomMultiplier fov = FovExtended.instance().actualDefaultVerticalFov * endMult offset = self.__defaultAimOffset return cameras.getAimMatrix(-offset[0], -offset[1], fov) 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)) distRangeVec = readVec2(dataSec, 'distRange', (1, 1), (100, 100), (2, 20)) bcfg['distRange'] = MinMax(distRangeVec.x, distRangeVec.y) bcfg['minStartDist'] = readFloat(dataSec, 'minStartDist', bcfg['distRange'][0], bcfg['distRange'][1], bcfg['distRange'][0]) bcfg['optimalStartDist'] = readFloat(dataSec, 'optimalStartDist', bcfg['distRange'][0], bcfg['distRange'][1], bcfg['distRange'][0]) bcfg['angleRange'][0] = math.radians(bcfg['angleRange'][0]) - math.pi * 0.5 bcfg['angleRange'][1] = math.radians(bcfg['angleRange'][1]) - math.pi * 0.5 bcfg['fovMultMinMaxDist'] = MinMax(readFloat(dataSec, 'fovMultMinDist', 0.1, 100, 1.0), readFloat(dataSec, 'fovMultMaxDist', 0.1, 100, 1.0)) 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.settings_core.SettingsCore import g_settingsCore ucfg['horzInvert'] = g_settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = g_settingsCore.getSetting('mouseVertInvert') ucfg['sniperModeByShift'] = g_settingsCore.getSetting('sniperModeByShift') 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', bcfg['distRange'][0], 500, bcfg['optimalStartDist']) if ucfg['startDist'] < bcfg['minStartDist']: ucfg['startDist'] = bcfg['optimalStartDist'] ucfg['startAngle'] = readFloat(ds, 'startAngle', 5, 180, 60) ucfg['startAngle'] = math.radians(ucfg['startAngle']) - math.pi * 0.5 ucfg['fovMultMinMaxDist'] = MinMax(readFloat(ds, 'fovMultMinDist', 0.1, 100, bcfg['fovMultMinMaxDist'].min), readFloat(ds, 'fovMultMaxDist', 0.1, 100, bcfg['fovMultMinMaxDist'].max)) 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['minStartDist'] = bcfg['minStartDist'] 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'] cfg['fovMultMinMaxDist'] = ucfg['fovMultMinMaxDist'] cfg['sniperModeByShift'] = ucfg['sniperModeByShift'] 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()) dynamicsSection = dataSec['dynamics'] self.__impulseOscillator = createOscillatorFromSection(dynamicsSection['impulseOscillator'], False) self.__movementOscillator = createOscillatorFromSection(dynamicsSection['movementOscillator'], False) self.__movementOscillator = CompoundOscillator(self.__movementOscillator, Oscillator(1.0, Vector3(50), Vector3(20), Vector3(0.01, 0.0, 0.01))) self.__noiseOscillator = createOscillatorFromSection(dynamicsSection['randomNoiseOscillatorSpherical']) self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__dynamicCfg['accelerationSensitivity'] = readFloat(dynamicsSection, 'accelerationSensitivity', -1000, 1000, 0.1) self.__dynamicCfg['frontImpulseToPitchRatio'] = math.radians(readFloat(dynamicsSection, 'frontImpulseToPitchRatio', -1000, 1000, 0.1)) self.__dynamicCfg['sideImpulseToRollRatio'] = math.radians(readFloat(dynamicsSection, 'sideImpulseToRollRatio', -1000, 1000, 0.1)) self.__dynamicCfg['sideImpulseToYawRatio'] = math.radians(readFloat(dynamicsSection, 'sideImpulseToYawRatio', -1000, 1000, 0.1)) accelerationThreshold = readFloat(dynamicsSection, 'accelerationThreshold', 0.0, 1000.0, 0.1) self.__dynamicCfg['accelerationThreshold'] = accelerationThreshold self.__dynamicCfg['accelerationMax'] = readFloat(dynamicsSection, 'accelerationMax', 0.0, 1000.0, 0.1) self.__dynamicCfg['maxShotImpulseDistance'] = readFloat(dynamicsSection, 'maxShotImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['maxExplosionImpulseDistance'] = readFloat(dynamicsSection, 'maxExplosionImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['zoomExposure'] = readFloat(dynamicsSection, 'zoomExposure', 0.0, 1000.0, 0.25) accelerationFilter = mathUtils.RangeFilter(self.__dynamicCfg['accelerationThreshold'], self.__dynamicCfg['accelerationMax'], 100, mathUtils.SMAFilter(ArcadeCamera._FILTER_LENGTH)) maxAccelerationDuration = readFloat(dynamicsSection, 'maxAccelerationDuration', 0.0, 10000.0, ArcadeCamera._DEFAULT_MAX_ACCELERATION_DURATION) self.__accelerationSmoother = AccelerationSmoother(accelerationFilter, maxAccelerationDuration) self.__inputInertia = _InputInertia(self.__cfg['fovMultMinMaxDist'], 0.0) advancedCollision = dataSec['advancedCollision'] if advancedCollision is None: LOG_ERROR('<advancedCollision> dataSection is not found!') else: BigWorld.setAdvancedColliderConstants(AdvancedColliderConstants.fromSection(advancedCollision), cfg['distRange'][0]) 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']) ds.writeFloat('arcadeMode/camera/fovMultMinDist', ucfg['fovMultMinMaxDist'].min) ds.writeFloat('arcadeMode/camera/fovMultMaxDist', ucfg['fovMultMinMaxDist'].max) ucfg['startAngle'] = math.degrees(ucfg['startAngle'] + math.pi * 0.5) ds.writeFloat('arcadeMode/camera/startAngle', ucfg['startAngle']) def __onRecreateDevice(self): self.__aimingSystem.aimMatrix = self.__calcAimMatrix()
class StrategicCamera(ICamera, CallbackDelayer): _CAMERA_YAW = 0.0 _DYNAMIC_ENABLED = True ABSOLUTE_VERTICAL_FOV = math.radians(60.0) @staticmethod def enableDynamicCamera(enable): StrategicCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return StrategicCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) def __init__(self, dataSec, aim): CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__curSense = self.__cfg['sensitivity'] self.__onChangeControlMode = None self.__aimingSystem = StrategicAimingSystem(self.__cfg['distRange'][0], StrategicCamera._CAMERA_YAW) self.__prevTime = BigWorld.time() self.__aimOffsetFunc = None if aim is None: self.__aimOffsetFunc = lambda x = None: (0, 0) else: self.__aimOffsetFunc = aim.offset self.__autoUpdatePosition = False self.__dxdydz = Vector3(0, 0, 0) self.__needReset = 0 def create(self, onChangeControlMode = None): self.__onChangeControlMode = onChangeControlMode self.__camDist = self.__cfg['camDist'] self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) def destroy(self): CallbackDelayer.destroy(self) self.disable() self.__onChangeControlMode = None self.__cam = None self._writeUserPreferences() self.__aimingSystem.destroy() self.__aimingSystem = None def enable(self, targetPos, saveDist): self.__prevTime = BigWorld.time() self.__aimingSystem.enable(targetPos) srcMat = mathUtils.createRotationMatrix((0.0, -math.pi * 0.499, 0.0)) self.__cam.source = srcMat if not saveDist: self.__camDist = self.__cfg['camDist'] self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) camTarget = Math.MatrixProduct() camTarget.b = self.__aimingSystem.matrix self.__cam.target = camTarget BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo(self.__aimingSystem.matrix.translation) BigWorld.player().positionControl.followCamera(True) FovExtended.instance().enabled = False BigWorld.projection().fov = StrategicCamera.ABSOLUTE_VERTICAL_FOV self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) self.__needReset = 1 def disable(self): self.__aimingSystem.disable() self.stopCallback(self.__cameraUpdate) BigWorld.camera(None) BigWorld.player().positionControl.followCamera(False) self.__positionOscillator.reset() FovExtended.instance().resetFov() FovExtended.instance().enabled = True def teleport(self, pos): self.moveToPosition(pos) def setMaxDist(self): distRange = self.__cfg['distRange'] if len(distRange) > 1: self.__camDist = distRange[1] def restoreDefaultsState(self): LOG_ERROR('StrategiCamera::restoreDefaultState is obsolete!') return vPos = BigWorld.player().getOwnVehiclePosition() self.__camDist = self.__cfg['camDist'] self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = 0.01 self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) self.__cam.target = trgMat BigWorld.player().positionControl.moveTo(self.__totalMove) 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 update(self, dx, dy, dz, updatedByKeyboard = False): self.__curSense = self.__cfg['keySensitivity'] if updatedByKeyboard else self.__cfg['sensitivity'] self.__autoUpdatePosition = updatedByKeyboard self.__dxdydz = Vector3(dx, dy, dz) def moveToPosition(self, pos): self.__aimingSystem.enable(pos) self.update(0.0, 0.0, 0.0) def calcVisibleAreaRatio(self): points = [Math.Vector2(1, 1), Math.Vector2(1, -1), Math.Vector2(-1, -1), Math.Vector2(-1, 1)] dirsPos = [ getWorldRayAndPoint(point.x, point.y) for point in points ] planeXZ = Plane(Math.Vector3(0, 1, 0), 0) collisionPoints = [] for dir, begPos in dirsPos: endPos = begPos + dir * 1000 testResult = BigWorld.wg_collideSegment(BigWorld.player().spaceID, begPos, endPos, 3) collPoint = Math.Vector3(0, 0, 0) if collPoint is not None: collPoint = testResult[0] else: collPoint = planeXZ.intersectSegment(begPos, endPos) collisionPoints.append(collPoint) x0 = abs(collisionPoints[1].x - collisionPoints[2].x) x1 = abs(collisionPoints[0].x - collisionPoints[3].x) z0 = abs(collisionPoints[0].z - collisionPoints[1].z) z1 = abs(collisionPoints[3].z - collisionPoints[2].z) bb = BigWorld.player().arena.arenaType.boundingBox arenaBottomLeft = bb[0] arenaUpperRight = bb[1] arenaX = arenaUpperRight[0] - arenaBottomLeft[0] arenaZ = arenaUpperRight[1] - arenaBottomLeft[1] return ((x0 + x1) * 0.5 / arenaX, (z0 + z1) * 0.5 / arenaZ) def applyImpulse(self, position, impulse, reason = ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse(impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() cameraYawMat = mathUtils.createRotationMatrix(Vector3(-StrategicCamera._CAMERA_YAW, 0.0, 0.0)) impulseLocal = cameraYawMat.applyVector(impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason = ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) 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.writeFloat('strategicMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('strategicMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('strategicMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('strategicMode/camera/camDist', self.__cfg['camDist']) def __cameraUpdate(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: aimOffset = self.__calcAimOffset() if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__aimOffsetFunc(aimOffset) shotDescr = BigWorld.player().vehicleTypeDescriptor.shot BigWorld.wg_trajectory_drawer().setParams(shotDescr['maxDistance'], Math.Vector3(0, -shotDescr['gravity'], 0), self.__aimOffsetFunc()) curTime = BigWorld.time() deltaTime = curTime - self.__prevTime self.__prevTime = curTime if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos(replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) distRange = self.__cfg['distRange'] self.__camDist -= self.__dxdydz.z * float(self.__curSense) maxPivotHeight = distRange[1] - distRange[0] self.__camDist = mathUtils.clamp(0, maxPivotHeight, self.__camDist) self.__cfg['camDist'] = self.__camDist self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) if self.__dxdydz.z != 0 and self.__onChangeControlMode is not None and mathUtils.almostZero(self.__camDist - maxPivotHeight): self.__onChangeControlMode() self.__updateOscillator(deltaTime) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) return 0.0 def __calcAimOffset(self): aimWorldPos = self.__aimingSystem.matrix.applyPoint(Vector3(0, -self.__aimingSystem.height, 0)) aimOffset = cameras.projectPoint(aimWorldPos) return Vector2(mathUtils.clamp(-0.95, 0.95, aimOffset.x), mathUtils.clamp(-0.95, 0.95, aimOffset.y)) def __updateOscillator(self, deltaTime): if StrategicCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = mathUtils.createTranslationMatrix(self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection('gui/avatar_input_handler.xml/strategicMode/camera/') self.__readCfg(cameraSec) def __readCfg(self, dataSec): if not dataSec: LOG_WARNING('Invalid section <strategicMode/camera> in avatar_input_handler.xml') self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10, 0.025) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (10000, 10000), (2, 30)) ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['strategicMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg from account_helpers.settings_core.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['camDist'] = readFloat(ds, 'camDist', 0.0, 60.0, 0) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['camDist'] = ucfg['camDist'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] dynamicsSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__positionOscillator = createOscillatorFromSection(dynamicsSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection(dynamicsSection['randomNoiseOscillatorFlat'], False)
class SniperCamera(ICamera, CallbackDelayer): _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): SniperCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return SniperCamera._DYNAMIC_ENABLED _FILTER_LENGTH = 5 _DEFAULT_MAX_ACCELERATION_DURATION = 1.5 _MIN_REL_SPEED_ACC_SMOOTHING = 0.7 camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) def __init__(self, dataSec, aim, binoculars): CallbackDelayer.__init__(self) self.__impulseOscillator = None self.__movementOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) if aim is None or binoculars is None: return else: self.__cam = BigWorld.FreeCamera() self.__zoom = self.__cfg['zoom'] self.__curSense = 0 self.__curScrollSense = 0 self.__waitVehicleCallbackId = None self.__onChangeControlMode = None self.__aimingSystem = SniperAimingSystem(dataSec) self.__aim = weakref.proxy(aim) self.__binoculars = binoculars self.__defaultAimOffset = self.__aim.offset() self.__defaultAimOffset = (self.__defaultAimOffset[0], self.__defaultAimOffset[1]) self.__crosshairMatrix = createCrosshairMatrix(offsetFromNearPlane=self.__dynamicCfg['aimMarkerDistance']) self.__prevTime = BigWorld.time() self.__autoUpdateDxDyDz = Vector3(0, 0, 0) return def create(self, onChangeControlMode = None): self.__onChangeControlMode = onChangeControlMode def destroy(self): self.disable() self.__onChangeControlMode = None self.__cam = None self._writeUserPreferences() self.__aimingSystem.destroy() self.__aimingSystem = None self.__aim = None CallbackDelayer.destroy(self) return def enable(self, targetPos, saveZoom): self.__prevTime = BigWorld.time() player = BigWorld.player() if saveZoom: self.__zoom = self.__cfg['zoom'] else: self.__cfg['zoom'] = self.__zoom = self.__cfg['zooms'][0] self.__applyZoom(self.__zoom) self.__setupCamera(targetPos) vehicle = BigWorld.entity(player.playerVehicleID) if self.__waitVehicleCallbackId is not None: BigWorld.cancelCallback(self.__waitVehicleCallbackId) if vehicle is None: self.__whiteVehicleCallbackId = BigWorld.callback(0.1, self.__waitVehicle) else: self.__showVehicle(False) BigWorld.camera(self.__cam) if self.__cameraUpdate(False) >= 0.0: self.delayCallback(0.0, self.__cameraUpdate) return def disable(self): BigWorld.camera(None) if self.__waitVehicleCallbackId is not None: BigWorld.cancelCallback(self.__waitVehicleCallbackId) self.__waitVehicleCallbackId = None self.__showVehicle(True) self.stopCallback(self.__cameraUpdate) self.__aimingSystem.disable() self.__movementOscillator.reset() self.__impulseOscillator.reset() self.__noiseOscillator.reset() self.__accelerationSmoother.reset() self.__autoUpdateDxDyDz.set(0) FovExtended.instance().resetFov() return 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 update(self, dx, dy, dz, updatedByKeyboard = False): self.__curSense = self.__cfg['keySensitivity'] if updatedByKeyboard else self.__cfg['sensitivity'] self.__curScrollSense = self.__cfg['keySensitivity'] if updatedByKeyboard else self.__cfg['scrollSensitivity'] self.__curSense *= 1.0 / self.__zoom if updatedByKeyboard: self.__autoUpdateDxDyDz.set(dx, dy, dz) else: self.__autoUpdateDxDyDz.set(0, 0, 0) self.__rotateAndZoom(dx, dy, dz) def onRecreateDevice(self): self.__applyZoom(self.__zoom) def applyImpulse(self, position, impulse, reason = ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse(impulse, reason) camMatrix = Matrix(self.__cam.matrix) impulseLocal = camMatrix.applyVector(adjustedImpulse) impulseAsYPR = Vector3(impulseLocal.x, -impulseLocal.y + impulseLocal.z, 0) rollPart = self.__dynamicCfg['impulsePartToRoll'] impulseAsYPR.z = -rollPart * impulseAsYPR.x impulseAsYPR.x *= 1 - rollPart self.__impulseOscillator.applyImpulse(impulseAsYPR) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason = ImpulseReason.ME_HIT): impulse = self.__cam.position - position distance = impulse.length if distance < 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.OTHER_SHOT and distance <= self.__dynamicCfg['maxShotImpulseDistance']: impulse *= impulseValue / distance elif reason == ImpulseReason.SPLASH: impulse *= impulseValue / distance elif reason == ImpulseReason.VEHICLE_EXPLOSION and distance <= self.__dynamicCfg['maxExplosionImpulseDistance']: impulse *= impulseValue / distance else: return self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3(noiseMagnitude) self.__noiseOscillator.applyImpulse(noiseImpulse) def __rotateAndZoom(self, dx, dy, dz): self.__aimingSystem.handleMovement(*self.__calcYawPitchDelta(dx, dy)) self.__setupZoom(dz) 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 __showVehicle(self, show): vehicle = BigWorld.entity(BigWorld.player().playerVehicleID) if vehicle is not None: vehicle.show(show) return def __setupCamera(self, targetPos): self.__aimingSystem.enable(targetPos) def __waitVehicle(self): vehicle = BigWorld.entity(BigWorld.player().playerVehicleID) if vehicle is not None and vehicle.isStarted: self.__waitVehicleCallbackId = None else: self.__waitVehicleCallbackId = BigWorld.callback(0.1, self.__waitVehicle) return self.__showVehicle(False) return def __applyZoom(self, zoomFactor): FovExtended.instance().setFovByMultiplier(1 / zoomFactor) def __setupZoom(self, dz): if dz == 0: return else: zooms = self.__cfg['zooms'] prevZoom = self.__zoom if self.__zoom == zooms[0] and dz < 0 and self.__onChangeControlMode is not None: self.__onChangeControlMode(True) if dz > 0: for elem in zooms: if self.__zoom < elem: self.__zoom = elem self.__cfg['zoom'] = self.__zoom break elif dz < 0: for i in range(len(zooms) - 1, -1, -1): if self.__zoom > zooms[i]: self.__zoom = zooms[i] self.__cfg['zoom'] = self.__zoom break if prevZoom != self.__zoom: self.__applyZoom(self.__zoom) return def __cameraUpdate(self, allowModeChange = True): curTime = BigWorld.time() deltaTime = curTime - self.__prevTime self.__prevTime = curTime if not self.__autoUpdateDxDyDz.x == self.__autoUpdateDxDyDz.y == self.__autoUpdateDxDyDz.z == 0.0: self.__rotateAndZoom(self.__autoUpdateDxDyDz.x, self.__autoUpdateDxDyDz.y, self.__autoUpdateDxDyDz.z) self.__aimingSystem.update(deltaTime) localTransform, impulseTransform = self.__updateOscillators(deltaTime) aimMatrix = cameras.getAimMatrix(*self.__defaultAimOffset) camMat = Matrix(aimMatrix) rodMat = mathUtils.createTranslationMatrix(-self.__dynamicCfg['pivotShift']) antiRodMat = mathUtils.createTranslationMatrix(self.__dynamicCfg['pivotShift']) camMat.postMultiply(rodMat) camMat.postMultiply(localTransform) camMat.postMultiply(antiRodMat) camMat.postMultiply(self.__aimingSystem.matrix) camMat.invert() self.__cam.set(camMat) replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() binocularsOffset = aimOffset else: aimOffset = self.__calcAimOffset(impulseTransform) binocularsOffset = self.__calcAimOffset() if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__aim.offset(aimOffset) self.__binoculars.setMaskCenter(binocularsOffset.x, binocularsOffset.y) player = BigWorld.player() if allowModeChange and (self.__isPositionUnderwater(self.__aimingSystem.matrix.translation) or player.isGunLocked): self.__onChangeControlMode(False) return -1 return 0.0 def __calcAimOffset(self, aimLocalTransform = None): worldCrosshair = Matrix(self.__crosshairMatrix) aimingSystemMatrix = self.__aimingSystem.matrix if aimLocalTransform is not None: worldCrosshair.postMultiply(aimLocalTransform) worldCrosshair.postMultiply(aimingSystemMatrix) aimOffset = cameras.projectPoint(worldCrosshair.translation) return Vector2(mathUtils.clamp(-0.95, 0.95, aimOffset.x), mathUtils.clamp(-0.95, 0.95, aimOffset.y)) def __calcCurOscillatorAcceleration(self, deltaTime): vehicle = BigWorld.player().vehicle if vehicle is None: return Vector3(0, 0, 0) else: curVelocity = vehicle.filter.velocity relativeSpeed = curVelocity.length / vehicle.typeDescriptor.physics['speedLimits'][0] if relativeSpeed >= SniperCamera._MIN_REL_SPEED_ACC_SMOOTHING: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg['accelerationThreshold'] else: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg['accelerationMax'] acceleration = self.__accelerationSmoother.update(vehicle, deltaTime) camMat = Matrix(self.__cam.matrix) acceleration = camMat.applyVector(-acceleration) accelSensitivity = self.__dynamicCfg['accelerationSensitivity'] acceleration.x *= accelSensitivity.x acceleration.y *= accelSensitivity.y acceleration.z *= accelSensitivity.z oscillatorAcceleration = Vector3(0, -acceleration.y + acceleration.z, -acceleration.x) return oscillatorAcceleration def __updateOscillators(self, deltaTime): if not SniperCamera.isCameraDynamic(): self.__impulseOscillator.reset() self.__movementOscillator.reset() self.__noiseOscillator.reset() return (mathUtils.createRotationMatrix(Vector3(0, 0, 0)), mathUtils.createRotationMatrix(Vector3(0, 0, 0))) oscillatorAcceleration = self.__calcCurOscillatorAcceleration(deltaTime) self.__movementOscillator.externalForce += oscillatorAcceleration self.__impulseOscillator.update(deltaTime) self.__movementOscillator.update(deltaTime) self.__noiseOscillator.update(deltaTime) noiseDeviation = Vector3(self.__noiseOscillator.deviation) deviation = self.__impulseOscillator.deviation + self.__movementOscillator.deviation + noiseDeviation oscVelocity = self.__impulseOscillator.velocity + self.__movementOscillator.velocity + self.__noiseOscillator.velocity if abs(deviation.x) < 1e-05 and abs(oscVelocity.x) < 0.0001: deviation.x = 0 if abs(deviation.y) < 1e-05 and abs(oscVelocity.y) < 0.0001: deviation.y = 0 if abs(deviation.z) < 1e-05 and abs(oscVelocity.z) < 0.0001: deviation.z = 0 curZoomIdx = 0 zooms = self.__cfg['zooms'] for idx in xrange(len(zooms)): if self.__zoom == zooms[idx]: curZoomIdx = idx break zoomExposure = self.__zoom * self.__dynamicCfg['zoomExposure'][curZoomIdx] deviation /= zoomExposure impulseDeviation = (self.__impulseOscillator.deviation + noiseDeviation) / zoomExposure self.__impulseOscillator.externalForce.set(0, 0, 0) self.__movementOscillator.externalForce.set(0, 0, 0) self.__noiseOscillator.externalForce.set(0, 0, 0) return (mathUtils.createRotationMatrix(Vector3(deviation.x, deviation.y, deviation.z)), mathUtils.createRotationMatrix(impulseDeviation)) def __isPositionUnderwater(self, position): return BigWorld.wg_collideWater(position, position + Vector3(0, 1, 0), False) > -1.0 def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection('gui/avatar_input_handler.xml/sniperMode/camera/') self.__readCfg(cameraSec) def __readCfg(self, dataSec): if not dataSec: LOG_WARNING('Invalid section <sniperMode/camera> in avatar_input_handler.xml') self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.005) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.005) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.005) zooms = readVec3(dataSec, 'zooms', (0, 0, 0), (10, 10, 10), (2, 4, 8)) bcfg['zooms'] = [zooms.x, zooms.y, zooms.z] ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['sniperMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg from account_helpers.settings_core.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['zoom'] = readFloat(ds, 'zoom', 0.0, 10.0, bcfg['zooms'][0]) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['zooms'] = bcfg['zooms'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['zoom'] = ucfg['zoom'] dynamicsSection = dataSec['dynamics'] self.__impulseOscillator = createOscillatorFromSection(dynamicsSection['impulseOscillator']) self.__movementOscillator = createOscillatorFromSection(dynamicsSection['movementOscillator']) self.__noiseOscillator = createOscillatorFromSection(dynamicsSection['randomNoiseOscillatorSpherical']) self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__dynamicCfg['accelerationSensitivity'] = readVec3(dynamicsSection, 'accelerationSensitivity', (-1000, -1000, -1000), (1000, 1000, 1000), (0.5, 0.5, 0.5)) accelerationThreshold = readFloat(dynamicsSection, 'accelerationThreshold', 0.0, 1000.0, 0.1) self.__dynamicCfg['accelerationThreshold'] = accelerationThreshold self.__dynamicCfg['accelerationMax'] = readFloat(dynamicsSection, 'accelerationMax', 0.0, 1000.0, 0.1) self.__dynamicCfg['maxShotImpulseDistance'] = readFloat(dynamicsSection, 'maxShotImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['maxExplosionImpulseDistance'] = readFloat(dynamicsSection, 'maxExplosionImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['impulsePartToRoll'] = readFloat(dynamicsSection, 'impulsePartToRoll', 0.0, 1000.0, 0.3) self.__dynamicCfg['pivotShift'] = Vector3(0, readFloat(dynamicsSection, 'pivotShift', -1000, 1000, -0.5), 0) self.__dynamicCfg['aimMarkerDistance'] = readFloat(dynamicsSection, 'aimMarkerDistance', -1000, 1000, 1.0) self.__dynamicCfg['zoomExposure'] = readVec3(dynamicsSection, 'zoomExposure', (0.1, 0.1, 0.1), (10, 10, 10), (0.5, 0.5, 0.5)) accelerationFilter = mathUtils.RangeFilter(self.__dynamicCfg['accelerationThreshold'], self.__dynamicCfg['accelerationMax'], 100, mathUtils.SMAFilter(SniperCamera._FILTER_LENGTH)) maxAccelerationDuration = readFloat(dynamicsSection, 'maxAccelerationDuration', 0.0, 10000.0, SniperCamera._DEFAULT_MAX_ACCELERATION_DURATION) self.__accelerationSmoother = AccelerationSmoother(accelerationFilter, maxAccelerationDuration) 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('sniperMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('sniperMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('sniperMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('sniperMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('sniperMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('sniperMode/camera/zoom', self.__cfg['zoom'])
class ArtyCamera(CameraWithSettings, CallbackDelayer): _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): ArtyCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return ArtyCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) __aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec): super(ArtyCamera, self).__init__() CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__switchers = CameraSwitcherCollection(cameraSwitchers=[ CameraSwitcher(switchType=SwitchTypes.FROM_TRANSITION_DIST_AS_MAX, switchToName=CTRL_MODE_NAME.STRATEGIC, switchToPos=0.0) ], isEnabled=True) self.__dynamicCfg = CameraDynamicConfig() self._readConfigs(dataSec) self.__cam = BigWorld.CursorCamera() self.__curSense = self._cfg['sensitivity'] self.__onChangeControlMode = None self.__camDist = 0.0 self.__desiredCamDist = 0.0 self.__aimingSystem = None self.__prevTime = 0.0 self.__prevAimPoint = Vector3() self.__dxdydz = Vector3(0.0, 0.0, 0.0) self.__autoUpdatePosition = False self.__needReset = 0 self.__sourceMatrix = None self.__targetMatrix = None self.__rotation = 0.0 self.__positionHysteresis = None self.__timeHysteresis = None self.__transitionEnabled = True self.__scrollSmoother = SPGScrollSmoother(0.3) self.__collisionDist = 0.0 self.__camViewPoint = Vector3() return @staticmethod def _getConfigsKey(): return ArtyCamera.__name__ def create(self, onChangeControlMode=None): super(ArtyCamera, self).create() self.__onChangeControlMode = onChangeControlMode aimingSystemClass = ArtyAimingSystemRemote if BigWorld.player( ).isObserver() else ArtyAimingSystem self.__aimingSystem = aimingSystemClass() self.__camDist = self._cfg['camDist'] self.__desiredCamDist = self.__camDist self.__positionHysteresis = PositionHysteresis( self._cfg['hPositionThresholdSq']) self.__timeHysteresis = TimeHysteresis(self._cfg['hTimeThreshold']) self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Vector3(0.0, 0.0, 0.0) self.__sourceMatrix = Matrix() self.__targetMatrix = Matrix() self.__rotation = 0.0 self.__enableSwitchers() self.__scrollSmoother.setTime(self._cfg['scrollSmoothingTime']) def destroy(self): self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None CallbackDelayer.destroy(self) CameraWithSettings.destroy(self) return def enable(self, targetPos, saveDist, switchToPos=None, switchToPlace=None): BigWorld.wg_trajectory_drawer().setStrategicMode(False) self.__prevTime = 0.0 if switchToPlace == SwitchToPlaces.TO_TRANSITION_DIST: self.__camDist = math_utils.clamp(self._cfg['distRange'][0], self._cfg['distRange'][1], self._cfg['transitionDist']) elif switchToPlace == SwitchToPlaces.TO_RELATIVE_POS and switchToPos is not None: minDist, maxDist = self._cfg['distRange'] self.__camDist = (maxDist - minDist) * switchToPos + minDist elif switchToPlace == SwitchToPlaces.TO_NEAR_POS and switchToPos is not None: minDist, maxDist = self._cfg['distRange'] self.__camDist = math_utils.clamp(minDist, maxDist, switchToPos) elif self.settingsCore.getSetting(SPGAim.AUTO_CHANGE_AIM_MODE): self.__camDist = math_utils.clamp(self._cfg['distRange'][0], self._cfg['transitionDist'], self.__camDist) self.__desiredCamDist = self.__camDist self.__scrollSmoother.start(self.__desiredCamDist) self.__enableSwitchers() self.__aimingSystem.enable(targetPos) self.__positionHysteresis.update(Vector3(0.0, 0.0, 0.0)) self.__timeHysteresis.update(BigWorld.timeExact()) camTarget = MatrixProduct() self.__rotation = max(self.__aimingSystem.direction.pitch, self._cfg['minimalPitch']) cameraDirection = self.__getCameraDirection() self.__targetMatrix.translation = self.aimingSystem.aimPoint - cameraDirection.scale( self.__camDist) self.__cam.target = camTarget self.__cam.target.b = self.__targetMatrix self.__sourceMatrix = math_utils.createRotationMatrix( (cameraDirection.yaw, -cameraDirection.pitch, 0.0)) self.__cam.source = self.__sourceMatrix self.__cam.forceUpdate() BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo(self.__aimingSystem.hitPoint) BigWorld.player().positionControl.followCamera(False) self.__cameraUpdate() self.delayCallback(0.01, self.__cameraUpdate) self.__needReset = 1 return def disable(self): self.__scrollSmoother.stop() if self.__aimingSystem is not None: self.__aimingSystem.disable() self.stopCallback(self.__cameraUpdate) self.__switchers.clear() self.__positionOscillator.reset() return def teleport(self, pos): self.__aimingSystem.updateTargetPos(pos) self.update(0.0, 0.0, 0.0) def getDistRatio(self): minDist, maxDist = self._cfg['distRange'] return (self.__desiredCamDist - minDist) / (maxDist - minDist) def getCurrentCamDist(self): return self.__desiredCamDist def getCamDistRange(self): return self._cfg['distRange'] def getCamTransitionDist(self): return self._cfg['transitionDist'] def update(self, dx, dy, dz, updateByKeyboard=False): self.__curSense = self._cfg[ 'keySensitivity'] if updateByKeyboard else self._cfg['sensitivity'] self.__autoUpdatePosition = updateByKeyboard self.__dxdydz = Vector3(dx if not self._cfg['horzInvert'] else -dx, dy if not self._cfg['vertInvert'] else -dy, dz) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() impulseLocal = self.__sourceMatrix.applyVector( impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) 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('artyMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('artyMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('artyMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('artyMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('artyMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('artyMode/camera/camDist', self._cfg['camDist']) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = math_utils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) def __calculateAimOffset(self, aimWorldPos): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: proj = BigWorld.projection() aimLocalPos = Matrix(self.__cam.matrix).applyPoint(aimWorldPos) if aimLocalPos.z < 0: aimLocalPos.z = max(0.0, proj.nearPlane - _OFFSET_FROM_NEAR_PLANE) aimWorldPos = Matrix( self.__cam.invViewMatrix).applyPoint(aimLocalPos) aimOffset = cameras.projectPoint(aimWorldPos) aimOffset = Vector2(math_utils.clamp(-0.95, 0.95, aimOffset.x), math_utils.clamp(-0.95, 0.95, aimOffset.y)) if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) return aimOffset def __handleMovement(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos( replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) return def __getCameraDirection(self): direction = Vector3() direction.setPitchYaw(self.__rotation, self.__aimingSystem.direction.yaw) direction.normalise() return direction def __getDesiredCameraDistance(self): distRange = self.__switchersDistRange() self.__desiredCamDist -= self.__dxdydz.z * self.__curSense self.__desiredCamDist = math_utils.clamp(distRange[0], distRange[1], self.__desiredCamDist) self.__desiredCamDist = self.__aimingSystem.overrideCamDist( self.__desiredCamDist) self._cfg['camDist'] = self.__camDist self.__scrollSmoother.moveTo(self.__desiredCamDist, distRange) return self.__desiredCamDist def __updateTrajectoryDrawer(self): shotDescr = BigWorld.player().getVehicleDescriptor().shot BigWorld.wg_trajectory_drawer().setParams( shotDescr.maxDistance, Vector3(0, -shotDescr.gravity, 0), self.__aimOffset) def __updateTime(self): curTime = BigWorld.timeExact() deltaTime = curTime - self.__prevTime self.__prevTime = curTime return deltaTime def __choosePitchLevel(self, aimPoint): useHighPitch = (aimPoint - self.__aimingSystem.hitPoint ).lengthSquared > self._cfg['highPitchThresholdSq'] if useHighPitch: useHighPitch = self.__positionHysteresis.check( aimPoint) or self.__timeHysteresis.check(self.__prevTime) else: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return useHighPitch def __getCollisionTestOrigin(self, aimPoint, direction): distRange = self.__switchersDistRange() vehiclePosition = BigWorld.player().getVehicleAttached().position collisionTestOrigin = Vector3(vehiclePosition) if direction.x * direction.x > direction.z * direction.z and not math_utils.almostZero( direction.x): collisionTestOrigin.y = direction.y / direction.x * ( vehiclePosition.x - aimPoint.x) + aimPoint.y elif not math_utils.almostZero(direction.z): collisionTestOrigin.y = direction.y / direction.z * ( vehiclePosition.z - aimPoint.z) + aimPoint.y else: collisionTestOrigin = aimPoint - direction.scale( (distRange[1] - distRange[0]) / 2.0) LOG_WARNING( "Can't find point on direction ray. Using half point as collision test origin" ) return collisionTestOrigin def __resolveCollisions(self, aimPoint, distance, direction): distRange = self.__switchersDistRange() collisionDist = 0.0 collisionTestOrigin = self.__getCollisionTestOrigin( aimPoint, direction) sign = copysign( 1.0, distance * distance - (aimPoint - collisionTestOrigin).lengthSquared) srcPoint = collisionTestOrigin endPoint = aimPoint - direction.scale( distance + sign * (distRange[0] + _SEARCH_COLLISION_DEPTH)) collision = collideDynamicAndStatic( srcPoint, endPoint, (BigWorld.player().playerVehicleID, )) if collision: collisionDistance = (aimPoint - collision[0]).length collisionDist = collisionDistance - sign * distRange[0] if sign * (collisionDistance - distance) < distRange[0]: distance = collisionDist if math_utils.almostZero(self.__rotation): srcPoint = aimPoint - direction.scale(distance) endPoint = aimPoint collision = collideDynamicAndStatic( srcPoint, endPoint, (BigWorld.player().playerVehicleID, )) if collision: self.__aimingSystem.aimPoint = collision[0] if collision[1]: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return collisionDist def __calculateIdealState(self, deltaTime): aimPoint = self.__aimingSystem.aimPoint direction = self.__aimingSystem.direction impactPitch = max(direction.pitch, self._cfg['minimalPitch']) self.__rotation = max(self.__rotation, impactPitch) distRange = self.__switchersDistRange() distanceCurSq = ( aimPoint - BigWorld.player().getVehicleAttached().position).lengthSquared distanceMinSq = distRange[0]**2.0 forcedPitch = impactPitch if distanceCurSq < distanceMinSq: forcedPitch = atan2(sqrt(distanceMinSq - distanceCurSq), sqrt(distanceCurSq)) angularShift = self._cfg['angularSpeed'] * deltaTime angularShift = angularShift if self.__choosePitchLevel( aimPoint) else -angularShift minPitch = max(forcedPitch, impactPitch) maxPitch = max(forcedPitch, self._cfg['maximalPitch']) self.__rotation = math_utils.clamp(minPitch, maxPitch, self.__rotation + angularShift) desiredDistance = self.__getDesiredCameraDistance() cameraDirection = self.__getCameraDirection() collisionDist = self.__resolveCollisions(aimPoint, desiredDistance, cameraDirection) collisionDist = max(distRange[0], collisionDist) desiredDistance = self.__scrollSmoother.update(deltaTime) rotation = Vector3(cameraDirection.yaw, -cameraDirection.pitch, 0.0) return (rotation, desiredDistance, collisionDist) def __interpolateStates(self, deltaTime, rotation, desiredDistance, collisionDist): lerpParam = math_utils.clamp( 0.0, 1.0, deltaTime * self._cfg['interpolationSpeed']) self.__sourceMatrix = slerp(self.__sourceMatrix, rotation, lerpParam) camDirection = Vector3() camDirection.setPitchYaw(-self.__sourceMatrix.pitch, self.__sourceMatrix.yaw) camDirection.normalise() self.__camViewPoint = math_utils.lerp(self.__camViewPoint, self.__aimingSystem.aimPoint, lerpParam) self.__collisionDist = math_utils.lerp(self.__collisionDist, collisionDist, lerpParam) desiredDistance = max(desiredDistance, self.__collisionDist) self.__targetMatrix.translation = self.__camViewPoint - camDirection.scale( desiredDistance) return (self.__sourceMatrix, self.__targetMatrix) def __cameraUpdate(self): deltaTime = self.__updateTime() self.__aimOffset = self.__calculateAimOffset( self.__aimingSystem.aimPoint) self.__handleMovement() aimPoint = Vector3(self.__aimingSystem.aimPoint) self.__updateTrajectoryDrawer() rotation, desiredDistance, collisionDist = self.__calculateIdealState( deltaTime) self.__interpolateStates(deltaTime, math_utils.createRotationMatrix(rotation), desiredDistance, collisionDist) self.__camDist = aimPoint - self.__targetMatrix.translation self.__camDist = self.__camDist.length self.__cam.source = self.__sourceMatrix self.__cam.target.b = self.__targetMatrix self.__cam.pivotPosition = Vector3(0, 0, 0) if aimPoint.distSqrTo(self.__prevAimPoint) > 0.010000000000000002: BigWorld.player().positionControl.moveTo(aimPoint) self.__prevAimPoint = aimPoint self.__updateOscillator(deltaTime) self.__aimingSystem.update(deltaTime) if self.__onChangeControlMode is not None and self.__switchers.needToSwitch( self.__dxdydz.z, self.__desiredCamDist, self._cfg['distRange'][0], self._cfg['distRange'][1], self._cfg['transitionDist']): self.__onChangeControlMode(*self.__switchers.getSwitchParams()) if not self.__transitionEnabled and self.__desiredCamDist - TRANSITION_DIST_HYSTERESIS <= self._cfg[ 'transitionDist']: self.__transitionEnabled = True self.__enableSwitchers(False) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) return 0.01 def __updateOscillator(self, deltaTime): if ArtyCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = math_utils.createTranslationMatrix( self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def __switchersDistRange(self): distRange = self._cfg['distRange'] if self.__switchers.isEnabled(): distRange = self.__switchers.getDistLimitationForSwitch( distRange[0], distRange[1], self._cfg['transitionDist']) return distRange def _readConfigs(self, dataSec): if not dataSec: LOG_WARNING( 'Invalid section <artyMode/camera> in avatar_input_handler.xml' ) super(ArtyCamera, self)._readConfigs(dataSec) dynamicSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicSection) self.__positionOscillator = createOscillatorFromSection( dynamicSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection( dynamicSection['randomNoiseOscillatorFlat']) def _readBaseCfg(self, dataSec): bcfg = self._baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10.0, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10.0, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10.0, 0.025) bcfg['angularSpeed'] = readFloat(dataSec, 'angularSpeed', pi / 720.0, pi / 0.5, pi / 360.0) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1.0, 1.0), (10000.0, 10000.0), (2.0, 30.0)) bcfg['transitionDist'] = readFloat(dataSec, 'transitionDist', 1.0, 10000.0, 60.0) bcfg['minimalPitch'] = readFloat(dataSec, 'minimalPitch', pi / 36.0, pi / 3.0, pi / 18.0) bcfg['maximalPitch'] = readFloat(dataSec, 'maximalPitch', pi / 6.0, pi / 3.0, pi / 3.5) bcfg['interpolationSpeed'] = readFloat(dataSec, 'interpolationSpeed', 0.0, 10.0, 5.0) bcfg['highPitchThreshold'] = readFloat(dataSec, 'highPitchThreshold', 0.1, 10.0, 3.0) bcfg['hTimeThreshold'] = readFloat(dataSec, 'hysteresis/timeThreshold', 0.0, 10.0, 0.5) bcfg['hPositionThreshold'] = readFloat(dataSec, 'hysteresis/positionThreshold', 0.0, 100.0, 7.0) bcfg['scrollSmoothingTime'] = readFloat(dataSec, 'scrollSmoothingTime', 0.0, 1.0, 0.3) def _readUserCfg(self): ucfg = self._userCfg dataSec = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if dataSec is not None: dataSec = dataSec['artyMode/camera'] ucfg['horzInvert'] = False ucfg['vertInvert'] = False ucfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['camDist'] = readFloat(dataSec, 'camDist', 0.0, 60.0, 0.0) return def _makeCfg(self): bcfg = self._baseCfg ucfg = self._userCfg cfg = self._cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['transitionDist'] = bcfg['transitionDist'] cfg['angularSpeed'] = bcfg['angularSpeed'] cfg['minimalPitch'] = bcfg['minimalPitch'] cfg['maximalPitch'] = bcfg['maximalPitch'] cfg['interpolationSpeed'] = bcfg['interpolationSpeed'] cfg['highPitchThresholdSq'] = bcfg['highPitchThreshold'] * bcfg[ 'highPitchThreshold'] cfg['hTimeThreshold'] = bcfg['hTimeThreshold'] cfg['hPositionThresholdSq'] = bcfg['hPositionThreshold'] * bcfg[ 'hPositionThreshold'] cfg['scrollSmoothingTime'] = bcfg['scrollSmoothingTime'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['camDist'] = ucfg['camDist'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] def _handleSettingsChange(self, diff): super(ArtyCamera, self)._handleSettingsChange(diff) if SPGAim.AUTO_CHANGE_AIM_MODE in diff: self.__enableSwitchers() if SPGAim.SCROLL_SMOOTHING_ENABLED in diff: self.__scrollSmoother.setIsEnabled( self.settingsCore.getSetting(SPGAim.SCROLL_SMOOTHING_ENABLED)) def _updateSettingsFromServer(self): super(ArtyCamera, self)._updateSettingsFromServer() if self.settingsCore.isReady: self.__enableSwitchers() self.__scrollSmoother.setIsEnabled( self.settingsCore.getSetting(SPGAim.SCROLL_SMOOTHING_ENABLED)) def __enableSwitchers(self, updateTransitionEnabled=True): if updateTransitionEnabled and self.__desiredCamDist - TRANSITION_DIST_HYSTERESIS >= self._cfg[ 'transitionDist']: self.__transitionEnabled = False if self.settingsCore.isReady: self.__switchers.setIsEnabled( self.settingsCore.getSetting(SPGAim.AUTO_CHANGE_AIM_MODE) and self.__transitionEnabled)
class SniperCamera(ICamera, CallbackDelayer): _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): SniperCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return SniperCamera._DYNAMIC_ENABLED _FILTER_LENGTH = 5 _DEFAULT_MAX_ACCELERATION_DURATION = 1.5 _MIN_REL_SPEED_ACC_SMOOTHING = 0.7 camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) settingsCore = dependency.descriptor(ISettingsCore) __aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) __zoomFactor = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.ZOOM_FACTOR) def __init__(self, dataSec, defaultOffset=None, binoculars=None): CallbackDelayer.__init__(self) self.__impulseOscillator = None self.__movementOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) if binoculars is None: return else: self.__cam = BigWorld.FreeCamera() self.__zoom = self.__cfg['zoom'] self.__curSense = 0 self.__curScrollSense = 0 self.__waitVehicleCallbackId = None self.__onChangeControlMode = None self.__aimingSystem = None self.__binoculars = binoculars self.__defaultAimOffset = defaultOffset or Vector2() self.__crosshairMatrix = createCrosshairMatrix( offsetFromNearPlane=self.__dynamicCfg['aimMarkerDistance']) self.__prevTime = BigWorld.time() self.__autoUpdateDxDyDz = Vector3(0, 0, 0) if BattleReplay.g_replayCtrl.isPlaying: BattleReplay.g_replayCtrl.setDataCallback( 'applyZoom', self.__applySerializedZoom) return def __onSettingsChanged(self, diff): if 'increasedZoom' in diff: self.__cfg['increasedZoom'] = diff['increasedZoom'] if not self.__cfg['increasedZoom']: self.__cfg['zoom'] = self.__zoom = self.__cfg['zooms'][:3][-1] if self.camera is BigWorld.camera(): self.delayCallback(0.0, self.__applyZoom, self.__cfg['zoom']) if 'fov' in diff and self.camera is BigWorld.camera(): self.delayCallback(0.01, self.__applyZoom, self.__cfg['zoom']) def create(self, onChangeControlMode=None): self.__onChangeControlMode = onChangeControlMode self.settingsCore.onSettingsChanged += self.__onSettingsChanged aimingSystemClass = SniperAimingSystemRemote if BigWorld.player( ).isObserver() else SniperAimingSystem self.__aimingSystem = aimingSystemClass() def destroy(self): self.settingsCore.onSettingsChanged -= self.__onSettingsChanged self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None CallbackDelayer.destroy(self) return def enable(self, targetPos, saveZoom): self.__prevTime = BigWorld.time() player = BigWorld.player() if saveZoom: self.__zoom = self.__cfg['zoom'] else: self.__cfg['zoom'] = self.__zoom = self.__cfg['zooms'][0] self.__applyZoom(self.__zoom) self.__setupCamera(targetPos) vehicle = player.getVehicleAttached() if self.__waitVehicleCallbackId is not None: BigWorld.cancelCallback(self.__waitVehicleCallbackId) if vehicle is None: self.__waitVehicleCallbackId = BigWorld.callback( 0.1, self.__waitVehicle) else: self.__showVehicle(False) BigWorld.camera(self.__cam) if self.__cameraUpdate(False) >= 0.0: self.delayCallback(0.0, self.__cameraUpdate) return def disable(self): BigWorld.camera(None) if self.__waitVehicleCallbackId is not None: BigWorld.cancelCallback(self.__waitVehicleCallbackId) self.__waitVehicleCallbackId = None self.__showVehicle(True) self.stopCallback(self.__cameraUpdate) if self.__aimingSystem is not None: self.__aimingSystem.disable() self.__movementOscillator.reset() self.__impulseOscillator.reset() self.__noiseOscillator.reset() self.__accelerationSmoother.reset() self.__autoUpdateDxDyDz.set(0) FovExtended.instance().resetFov() return def getConfigValue(self, name): return self.__cfg.get(name) 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 update(self, dx, dy, dz, updatedByKeyboard=False): self.__curSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'sensitivity'] self.__curScrollSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'scrollSensitivity'] self.__curSense *= 1.0 / self.__zoom if updatedByKeyboard: self.__autoUpdateDxDyDz.set(dx, dy, dz) else: self.__autoUpdateDxDyDz.set(0, 0, 0) self.__rotateAndZoom(dx, dy, dz) def onRecreateDevice(self): self.__applyZoom(self.__zoom) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) camMatrix = Matrix(self.__cam.matrix) impulseLocal = camMatrix.applyVector(adjustedImpulse) impulseAsYPR = Vector3(impulseLocal.x, -impulseLocal.y + impulseLocal.z, 0) rollPart = self.__dynamicCfg['impulsePartToRoll'] impulseAsYPR.z = -rollPart * impulseAsYPR.x impulseAsYPR.x *= 1 - rollPart self.__impulseOscillator.applyImpulse(impulseAsYPR) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): impulse = self.__cam.position - position distance = impulse.length if distance < 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.OTHER_SHOT and distance <= self.__dynamicCfg[ 'maxShotImpulseDistance']: impulse *= impulseValue / distance elif reason == ImpulseReason.SPLASH or reason == ImpulseReason.HE_EXPLOSION: impulse *= impulseValue / distance elif reason == ImpulseReason.VEHICLE_EXPLOSION and distance <= self.__dynamicCfg[ 'maxExplosionImpulseDistance']: impulse *= impulseValue / distance else: return self.applyImpulse(position, impulse, reason) def setMaxZoom(self): zooms = self.__getZooms() self.__zoom = zooms[-1] self.__applyZoom(self.__zoom) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3(noiseMagnitude) self.__noiseOscillator.applyImpulse(noiseImpulse) def __rotateAndZoom(self, dx, dy, dz): self.__aimingSystem.handleMovement(*self.__calcYawPitchDelta(dx, dy)) self.__setupZoom(dz) 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 __showVehicle(self, show): player = BigWorld.player() vehicle = player.getVehicleAttached() if vehicle is not None: LOG_DEBUG('__showVehicle: ', vehicle.id, show) vehicle.show(show) return def __setupCamera(self, targetPos): vehicleTypeDescriptor = BigWorld.player().vehicleTypeDescriptor playerGunMatFunction = AimingSystems.getPlayerGunMat if vehicleTypeDescriptor is not None and vehicleTypeDescriptor.isYawHullAimingAvailable: playerGunMatFunction = AimingSystems.getCenteredPlayerGunMat self.__aimingSystem.enable(targetPos, playerGunMatFunction) return def __waitVehicle(self): player = BigWorld.player() vehicle = player.getVehicleAttached() if vehicle is not None and vehicle.isStarted: self.__waitVehicleCallbackId = None else: self.__waitVehicleCallbackId = BigWorld.callback( 0.1, self.__waitVehicle) return self.__showVehicle(False) return def __applySerializedZoom(self, zoomFactor): if BattleReplay.g_replayCtrl.isPlaying: if BattleReplay.g_replayCtrl.isControllingCamera: self.__applyZoom(zoomFactor) def __applyZoom(self, zoomFactor): self.__zoomFactor = zoomFactor if BattleReplay.g_replayCtrl.isRecording: BattleReplay.g_replayCtrl.serializeCallbackData( 'applyZoom', (zoomFactor, )) FovExtended.instance().setFovByMultiplier(1 / zoomFactor) def __getZooms(self): zooms = self.__cfg['zooms'] if not self.__cfg['increasedZoom']: zooms = zooms[:3] return zooms def __setupZoom(self, dz): if dz == 0: return else: zooms = self.__getZooms() prevZoom = self.__zoom if self.__zoom == zooms[ 0] and dz < 0 and self.__onChangeControlMode is not None: self.__onChangeControlMode(True) if dz > 0: for elem in zooms: if self.__zoom < elem: self.__zoom = elem self.__cfg['zoom'] = self.__zoom break elif dz < 0: for i in range(len(zooms) - 1, -1, -1): if self.__zoom > zooms[i]: self.__zoom = zooms[i] self.__cfg['zoom'] = self.__zoom break if prevZoom != self.__zoom: self.__applyZoom(self.__zoom) return def __cameraUpdate(self, allowModeChange=True): curTime = BigWorld.time() deltaTime = curTime - self.__prevTime self.__prevTime = curTime if not self.__autoUpdateDxDyDz.x == self.__autoUpdateDxDyDz.y == self.__autoUpdateDxDyDz.z == 0.0: self.__rotateAndZoom(self.__autoUpdateDxDyDz.x, self.__autoUpdateDxDyDz.y, self.__autoUpdateDxDyDz.z) self.__aimingSystem.update(deltaTime) localTransform, impulseTransform = self.__updateOscillators(deltaTime) zoom = self.__aimingSystem.overrideZoom(self.__zoom) aimMatrix = cameras.getAimMatrix(self.__defaultAimOffset.x, self.__defaultAimOffset.y) camMat = Matrix(aimMatrix) rodMat = mathUtils.createTranslationMatrix( -self.__dynamicCfg['pivotShift']) antiRodMat = mathUtils.createTranslationMatrix( self.__dynamicCfg['pivotShift']) camMat.postMultiply(rodMat) camMat.postMultiply(localTransform) camMat.postMultiply(antiRodMat) camMat.postMultiply(self.__aimingSystem.matrix) camMat.invert() self.__cam.set(camMat) if zoom != self.__zoom: self.__zoom = zoom self.__applyZoom(self.__zoom) replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() binocularsOffset = aimOffset if not BigWorld.player().isForcedGuiControlMode() and GUI.mcursor( ).inFocus: GUI.mcursor().position = aimOffset else: aimOffset = self.__calcAimOffset(impulseTransform) binocularsOffset = self.__calcAimOffset() if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__aimOffset = aimOffset self.__binoculars.setMaskCenter(binocularsOffset.x, binocularsOffset.y) player = BigWorld.player() if allowModeChange and (self.__isPositionUnderwater( self.__aimingSystem.matrix.translation) or player.isGunLocked): self.__onChangeControlMode(False) return -1 return 0.0 def __calcAimOffset(self, aimLocalTransform=None): aimingSystemMatrix = self.__aimingSystem.matrix worldCrosshair = Matrix(self.__crosshairMatrix) if aimLocalTransform is not None: worldCrosshair.postMultiply(aimLocalTransform) worldCrosshair.postMultiply(aimingSystemMatrix) aimOffset = cameras.projectPoint(worldCrosshair.translation) return Vector2(mathUtils.clamp(-0.95, 0.95, aimOffset.x), mathUtils.clamp(-0.95, 0.95, aimOffset.y)) def __calcCurOscillatorAcceleration(self, deltaTime): vehicle = BigWorld.player().vehicle if vehicle is None or not vehicle.isAlive(): return Vector3(0, 0, 0) else: curVelocity = vehicle.filter.velocity relativeSpeed = curVelocity.length / vehicle.typeDescriptor.physics[ 'speedLimits'][0] if relativeSpeed >= SniperCamera._MIN_REL_SPEED_ACC_SMOOTHING: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationThreshold'] else: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationMax'] acceleration = self.__accelerationSmoother.update( vehicle, deltaTime) camMat = Matrix(self.__cam.matrix) acceleration = camMat.applyVector(-acceleration) accelSensitivity = self.__dynamicCfg['accelerationSensitivity'] acceleration.x *= accelSensitivity.x acceleration.y *= accelSensitivity.y acceleration.z *= accelSensitivity.z oscillatorAcceleration = Vector3(0, -acceleration.y + acceleration.z, -acceleration.x) return oscillatorAcceleration def __updateOscillators(self, deltaTime): if not SniperCamera.isCameraDynamic(): self.__impulseOscillator.reset() self.__movementOscillator.reset() self.__noiseOscillator.reset() return (mathUtils.createRotationMatrix(Vector3(0, 0, 0)), mathUtils.createRotationMatrix(Vector3(0, 0, 0))) oscillatorAcceleration = self.__calcCurOscillatorAcceleration( deltaTime) self.__movementOscillator.externalForce += oscillatorAcceleration self.__impulseOscillator.update(deltaTime) self.__movementOscillator.update(deltaTime) self.__noiseOscillator.update(deltaTime) noiseDeviation = Vector3(self.__noiseOscillator.deviation) deviation = self.__impulseOscillator.deviation + self.__movementOscillator.deviation + noiseDeviation oscVelocity = self.__impulseOscillator.velocity + self.__movementOscillator.velocity + self.__noiseOscillator.velocity if abs(deviation.x) < 1e-05 and abs(oscVelocity.x) < 0.0001: deviation.x = 0 if abs(deviation.y) < 1e-05 and abs(oscVelocity.y) < 0.0001: deviation.y = 0 if abs(deviation.z) < 1e-05 and abs(oscVelocity.z) < 0.0001: deviation.z = 0 curZoomIdx = 0 zooms = self.__cfg['zooms'] for idx in xrange(len(zooms)): if self.__zoom == zooms[idx]: curZoomIdx = idx break zoomExposure = self.__zoom * self.__dynamicCfg['zoomExposure'][ curZoomIdx] deviation /= zoomExposure impulseDeviation = (self.__impulseOscillator.deviation + noiseDeviation) / zoomExposure self.__impulseOscillator.externalForce = Vector3(0) self.__movementOscillator.externalForce = Vector3(0) self.__noiseOscillator.externalForce = Vector3(0) return (mathUtils.createRotationMatrix( Vector3(deviation.x, deviation.y, deviation.z)), mathUtils.createRotationMatrix(impulseDeviation)) def __isPositionUnderwater(self, position): return BigWorld.wg_collideWater(position, position + Vector3(0, 1, 0), False) > -1.0 def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection( 'gui/avatar_input_handler.xml/sniperMode/camera/') self.__readCfg(cameraSec) def __readCfg(self, dataSec): if not dataSec: LOG_WARNING( 'Invalid section <sniperMode/camera> in avatar_input_handler.xml' ) self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.005) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.005) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.005) zooms = dataSec.readString('zooms', '2 4 8 16 25') bcfg['zooms'] = [float(x) for x in zooms.split()] ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['sniperMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg ucfg['horzInvert'] = self.settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = self.settingsCore.getSetting('mouseVertInvert') ucfg['increasedZoom'] = self.settingsCore.getSetting('increasedZoom') maxZoom = bcfg['zooms'][-1] if ucfg['increasedZoom'] else bcfg[ 'zooms'][:3][-1] ucfg['zoom'] = readFloat(ds, 'zoom', 0.0, maxZoom, bcfg['zooms'][0]) ucfg['sniperModeByShift'] = self.settingsCore.getSetting( 'sniperModeByShift') 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) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['zooms'] = bcfg['zooms'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['zoom'] = ucfg['zoom'] cfg['increasedZoom'] = ucfg['increasedZoom'] cfg['sniperModeByShift'] = ucfg['sniperModeByShift'] dynamicsSection = dataSec['dynamics'] self.__impulseOscillator = createOscillatorFromSection( dynamicsSection['impulseOscillator']) self.__movementOscillator = createOscillatorFromSection( dynamicsSection['movementOscillator']) self.__noiseOscillator = createOscillatorFromSection( dynamicsSection['randomNoiseOscillatorSpherical']) self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__dynamicCfg['accelerationSensitivity'] = readVec3( dynamicsSection, 'accelerationSensitivity', (-1000, -1000, -1000), (1000, 1000, 1000), (0.5, 0.5, 0.5)) accelerationThreshold = readFloat(dynamicsSection, 'accelerationThreshold', 0.0, 1000.0, 0.1) self.__dynamicCfg['accelerationThreshold'] = accelerationThreshold self.__dynamicCfg['accelerationMax'] = readFloat( dynamicsSection, 'accelerationMax', 0.0, 1000.0, 0.1) self.__dynamicCfg['maxShotImpulseDistance'] = readFloat( dynamicsSection, 'maxShotImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['maxExplosionImpulseDistance'] = readFloat( dynamicsSection, 'maxExplosionImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['impulsePartToRoll'] = readFloat( dynamicsSection, 'impulsePartToRoll', 0.0, 1000.0, 0.3) self.__dynamicCfg['pivotShift'] = Vector3( 0, readFloat(dynamicsSection, 'pivotShift', -1000, 1000, -0.5), 0) self.__dynamicCfg['aimMarkerDistance'] = readFloat( dynamicsSection, 'aimMarkerDistance', -1000, 1000, 1.0) rawZoomExposure = dynamicsSection.readString('zoomExposure', '0.6 0.5 0.4 0.3 0.2') self.__dynamicCfg['zoomExposure'] = [ float(x) for x in rawZoomExposure.split() ] accelerationFilter = mathUtils.RangeFilter( self.__dynamicCfg['accelerationThreshold'], self.__dynamicCfg['accelerationMax'], 100, mathUtils.SMAFilter(SniperCamera._FILTER_LENGTH)) maxAccelerationDuration = readFloat( dynamicsSection, 'maxAccelerationDuration', 0.0, 10000.0, SniperCamera._DEFAULT_MAX_ACCELERATION_DURATION) self.__accelerationSmoother = AccelerationSmoother( accelerationFilter, maxAccelerationDuration) 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('sniperMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('sniperMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('sniperMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('sniperMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('sniperMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('sniperMode/camera/zoom', self.__cfg['zoom'])
class StrategicCamera(ICamera, CallbackDelayer): _CAMERA_YAW = 0.0 _DYNAMIC_ENABLED = True ABSOLUTE_VERTICAL_FOV = math.radians(60.0) _SMOOTHING_PIVOT_DELTA_FACTOR = 6.0 @staticmethod def enableDynamicCamera(enable): StrategicCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return StrategicCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) settingsCore = dependency.descriptor(ISettingsCore) __aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec): CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__curSense = self.__cfg['sensitivity'] self.__onChangeControlMode = None self.__aimingSystem = None self.__prevTime = BigWorld.time() self.__autoUpdatePosition = False self.__dxdydz = Vector3(0, 0, 0) self.__needReset = 0 self.__smoothingPivotDelta = 0 return def create(self, onChangeControlMode=None): self.__onChangeControlMode = onChangeControlMode self.__camDist = self.__cfg['camDist'] self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) aimingSystemClass = StrategicAimingSystemRemote if BigWorld.player( ).isObserver() else StrategicAimingSystem self.__aimingSystem = aimingSystemClass(self.__cfg['distRange'][0], StrategicCamera._CAMERA_YAW) def destroy(self): CallbackDelayer.destroy(self) self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None return def enable(self, targetPos, saveDist): self.__prevTime = BigWorld.time() self.__aimingSystem.enable(targetPos) srcMat = mathUtils.createRotationMatrix((0.0, -math.pi * 0.499, 0.0)) self.__cam.source = srcMat if not saveDist: self.__camDist = self.__cfg['camDist'] self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) camTarget = Math.MatrixProduct() camTarget.b = self.__aimingSystem.matrix self.__cam.target = camTarget self.__cam.wg_applyParams() BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo( self.__aimingSystem.matrix.translation) BigWorld.player().positionControl.followCamera(True) FovExtended.instance().enabled = False BigWorld.projection().fov = StrategicCamera.ABSOLUTE_VERTICAL_FOV self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) self.__needReset = 1 def disable(self): if self.__aimingSystem is not None: self.__aimingSystem.disable() self.stopCallback(self.__cameraUpdate) BigWorld.camera(None) positionControl = BigWorld.player().positionControl if positionControl is not None: positionControl.followCamera(False) self.__positionOscillator.reset() FovExtended.instance().resetFov() FovExtended.instance().enabled = True return def teleport(self, pos): self.moveToPosition(pos) def setMaxDist(self): distRange = self.__cfg['distRange'] if len(distRange) > 1: self.__camDist = distRange[1] def restoreDefaultsState(self): LOG_ERROR('StrategiCamera::restoreDefaultState is obsolete!') def getConfigValue(self, name): return self.__cfg.get(name) 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 update(self, dx, dy, dz, updatedByKeyboard=False): self.__curSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'sensitivity'] self.__autoUpdatePosition = updatedByKeyboard self.__dxdydz = Vector3(dx, dy, dz) def moveToPosition(self, pos): self.__aimingSystem.enable(pos) self.update(0.0, 0.0, 0.0) def calcVisibleAreaRatio(self): points = [ Math.Vector2(1, 1), Math.Vector2(1, -1), Math.Vector2(-1, -1), Math.Vector2(-1, 1) ] dirsPos = [getWorldRayAndPoint(point.x, point.y) for point in points] planeXZ = Plane(Math.Vector3(0, 1, 0), 0) collisionPoints = [] for dir, begPos in dirsPos: endPos = begPos + dir * 1000 testResult = BigWorld.wg_collideSegment(BigWorld.player().spaceID, begPos, endPos, 3) collPoint = Math.Vector3(0, 0, 0) if collPoint is not None: collPoint = testResult[0] else: collPoint = planeXZ.intersectSegment(begPos, endPos) collisionPoints.append(collPoint) x0 = abs(collisionPoints[1].x - collisionPoints[2].x) x1 = abs(collisionPoints[0].x - collisionPoints[3].x) z0 = abs(collisionPoints[0].z - collisionPoints[1].z) z1 = abs(collisionPoints[3].z - collisionPoints[2].z) bb = BigWorld.player().arena.arenaType.boundingBox arenaBottomLeft = bb[0] arenaUpperRight = bb[1] arenaX = arenaUpperRight[0] - arenaBottomLeft[0] arenaZ = arenaUpperRight[1] - arenaBottomLeft[1] return ((x0 + x1) * 0.5 / arenaX, (z0 + z1) * 0.5 / arenaZ) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() cameraYawMat = mathUtils.createRotationMatrix( Vector3(-StrategicCamera._CAMERA_YAW, 0.0, 0.0)) impulseLocal = cameraYawMat.applyVector(impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) 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.writeFloat('strategicMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('strategicMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('strategicMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('strategicMode/camera/camDist', self.__cfg['camDist']) def __cameraUpdate(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: aimOffset = self.__calcAimOffset() if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__aimOffset = aimOffset shotDescr = BigWorld.player().getVehicleDescriptor().shot BigWorld.wg_trajectory_drawer().setParams( shotDescr.maxDistance, Math.Vector3(0, -shotDescr.gravity, 0), aimOffset) curTime = BigWorld.time() deltaTime = curTime - self.__prevTime self.__prevTime = curTime self.__aimingSystem.update(deltaTime) if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos( replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) self.__calcSmoothingPivotDelta(deltaTime) self.__camDist -= self.__dxdydz.z * float(self.__curSense) self.__camDist = self.__aimingSystem.overrideCamDist(self.__camDist) distRange = self.__cfg['distRange'] maxPivotHeight = distRange[1] - distRange[0] self.__camDist = mathUtils.clamp(0, maxPivotHeight, self.__camDist) self.__cfg['camDist'] = self.__camDist camDistWithSmoothing = self.__camDist + self.__smoothingPivotDelta - self.__aimingSystem.heightFromPlane self.__cam.pivotPosition = Math.Vector3(0.0, camDistWithSmoothing, 0.0) if self.__dxdydz.z != 0 and self.__onChangeControlMode is not None and mathUtils.almostZero( self.__camDist - maxPivotHeight): self.__onChangeControlMode() self.__updateOscillator(deltaTime) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) return 0.0 def __calcSmoothingPivotDelta(self, deltaTime): heightsDy = self.__aimingSystem.heightFromPlane - self.__smoothingPivotDelta smoothingPivotDeltaDy = StrategicCamera._SMOOTHING_PIVOT_DELTA_FACTOR * heightsDy * deltaTime self.__smoothingPivotDelta += smoothingPivotDeltaDy def __calcAimOffset(self): aimWorldPos = self.__aimingSystem.matrix.applyPoint( Vector3(0, -self.__aimingSystem.height, 0)) aimOffset = cameras.projectPoint(aimWorldPos) return Vector2(mathUtils.clamp(-0.95, 0.95, aimOffset.x), mathUtils.clamp(-0.95, 0.95, aimOffset.y)) def __updateOscillator(self, deltaTime): if StrategicCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = mathUtils.createTranslationMatrix( self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection( 'gui/avatar_input_handler.xml/strategicMode/camera/') self.__readCfg(cameraSec) def __readCfg(self, dataSec): if not dataSec or dataSec['strategic']: LOG_WARNING( 'Invalid section <strategicMode/camera> in avatar_input_handler.xml' ) self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10, 0.025) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (10000, 10000), (2, 30)) ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['strategicMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg ucfg['horzInvert'] = self.settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = self.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['camDist'] = readFloat(ds, 'camDist', 0.0, 60.0, 0) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['camDist'] = ucfg['camDist'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] dynamicsSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__positionOscillator = createOscillatorFromSection( dynamicsSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection( dynamicsSection['randomNoiseOscillatorFlat'], False) return
class ArcadeCamera(ICamera, CallbackDelayer, TimeDeltaMeter): REASONS_AFFECT_CAMERA_DIRECTLY = (ImpulseReason.MY_SHOT, ImpulseReason.OTHER_SHOT, ImpulseReason.VEHICLE_EXPLOSION, ImpulseReason.HE_EXPLOSION) _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): ArcadeCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return ArcadeCamera._DYNAMIC_ENABLED _FILTER_LENGTH = 5 _DEFAULT_MAX_ACCELERATION_DURATION = 1.5 _FOCAL_POINT_CHANGE_SPEED = 100.0 _FOCAL_POINT_MIN_DIFF = 5.0 _MIN_REL_SPEED_ACC_SMOOTHING = 0.7 def getReasonsAffectCameraDirectly(self): return ArcadeCamera.REASONS_AFFECT_CAMERA_DIRECTLY def __getVehicleMProv(self): refinedMProv = self.__aimingSystem.vehicleMProv return refinedMProv.b.source def __setVehicleMProv(self, vehicleMProv): prevAimRel = self.__cam.aimPointProvider.a if self.__cam.aimPointProvider is not None else None refinedVehicleMProv = self.__refineVehicleMProv(vehicleMProv) self.__aimingSystem.vehicleMProv = refinedVehicleMProv self.__setupCameraProviders(refinedVehicleMProv) self.__cam.speedTreeTarget = self.__aimingSystem.vehicleMProv self.__aimingSystem.update(0.0) if prevAimRel is not None: self.__cam.aimPointProvider.a = prevAimRel baseTranslation = Matrix(refinedVehicleMProv).translation relativePosition = self.__aimingSystem.matrix.translation - baseTranslation self.__setCameraPosition(relativePosition) return camera = property(lambda self: self.__cam) angles = property(lambda self: (self.__aimingSystem.yaw, self.__aimingSystem.pitch)) aimingSystem = property(lambda self: self.__aimingSystem) vehicleMProv = property(__getVehicleMProv, __setVehicleMProv) __aimOffset = aim_global_binding.bind( aim_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec, defaultOffset=None): CallbackDelayer.__init__(self) TimeDeltaMeter.__init__(self) self.__shiftKeySensor = None self.__movementOscillator = None self.__impulseOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) self.__onChangeControlMode = None self.__aimingSystem = None self.__curSense = 0 self.__curScrollSense = 0 self.__postmortemMode = False self.__vehiclesToCollideWith = set() self.__focalPointDist = 1.0 self.__autoUpdateDxDyDz = Vector3(0.0) self.__updatedByKeyboard = False if defaultOffset is not None: self.__defaultAimOffset = defaultOffset self.__cam = BigWorld.HomingCamera(self.__adCfg['enable']) if self.__adCfg['enable']: self.__cam.initAdvancedCollider( self.__adCfg['fovRatio'], self.__adCfg['rollbackSpeed'], self.__adCfg['minimalCameraDistance'], self.__adCfg['speedThreshold'], self.__adCfg['minimalVolume']) for group_name in VOLUME_GROUPS_NAMES: self.__cam.addVolumeGroup( self.__adCfg['volumeGroups'][group_name]) self.__cam.aimPointClipCoords = defaultOffset else: self.__defaultAimOffset = Vector2() self.__cam = None return def create(self, pivotPos, onChangeControlMode=None, postmortemMode=False): self.__onChangeControlMode = onChangeControlMode self.__postmortemMode = postmortemMode targetMat = self.getTargetMProv() self.__aimingSystem = ArcadeAimingSystem( self.__refineVehicleMProv(targetMat), pivotPos.y, pivotPos.z, self.__calcAimMatrix(), self.__cfg['angleRange'], not postmortemMode) self.setCameraDistance(self.__cfg['startDist']) self.__aimingSystem.pitch = self.__cfg['startAngle'] self.__aimingSystem.yaw = Math.Matrix(targetMat).yaw self.__updateAngles(0, 0) cameraPosProvider = Math.Vector4Translation(self.__aimingSystem.matrix) self.__cam.cameraPositionProvider = cameraPosProvider def getTargetMProv(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.playerVehicleID != 0: vehicleID = replayCtrl.playerVehicleID else: vehicleID = BigWorld.player().playerVehicleID return BigWorld.entity(vehicleID).matrix def reinitMatrix(self): self.__setVehicleMProv(self.getTargetMProv()) def setToVehicleDirection(self): matrix = Math.Matrix(self.getTargetMProv()) self.setYawPitch(matrix.yaw, matrix.pitch) def destroy(self): CallbackDelayer.destroy(self) self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None return def getPivotSettings(self): return self.__aimingSystem.getPivotSettings() def setPivotSettings(self, heightAboveBase, focusRadius): self.__aimingSystem.setPivotSettings(heightAboveBase, focusRadius) def addVehicleToCollideWith(self, vehicle): self.__vehiclesToCollideWith.add(vehicle) self.__setModelsToCollideWith(self.__vehiclesToCollideWith) def removeVehicleToCollideWith(self, vehicle): lengthBefore = len(self.__vehiclesToCollideWith) self.__vehiclesToCollideWith = set( [v for v in self.__vehiclesToCollideWith if v.id != vehicle.id]) if lengthBefore != len(self.__vehiclesToCollideWith): self.__setModelsToCollideWith(self.__vehiclesToCollideWith) def clearVehicleToCollideWith(self): self.__vehiclesToCollideWith = set() self.__setModelsToCollideWith(self.__vehiclesToCollideWith) def __setModelsToCollideWith(self, vehicles): colliders = [] for vehicle in vehicles: vehicleAppearance = vehicle.appearance compound = vehicleAppearance.compoundModel if not vehicle.isTurretDetached: colliders.append( (compound.node(TankPartNames.TURRET), compound.getBoundsForPart(TankPartIndexes.TURRET), compound.getPartGeometryLink(TankPartIndexes.TURRET))) colliders.append( (compound.node(TankPartNames.HULL), compound.getBoundsForPart(TankPartIndexes.HULL), compound.getPartGeometryLink(TankPartIndexes.HULL))) self.__cam.setDynamicColliders(colliders) self.__aimingSystem.setDynamicColliders(colliders) def focusOnPos(self, preferredPos): self.__aimingSystem.focusOnPos(preferredPos) def shiftCamPos(self, shift=None): matrixProduct = self.__aimingSystem.vehicleMProv shiftMat = matrixProduct.a if shift is not None: camDirection = Math.Vector3(math.sin(self.angles[0]), 0, math.cos(self.angles[0])) 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() return def enable(self, preferredPos=None, closesDist=False, postmortemParams=None, turretYaw=None, gunPitch=None): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isRecording: replayCtrl.setAimClipPosition(self.__aimOffset) self.measureDeltaTime() camDist = None vehicle = BigWorld.player().getVehicleAttached() initialVehicleMatrix = BigWorld.player().getOwnVehicleMatrix( ) if vehicle is None else vehicle.matrix vehicleMProv = initialVehicleMatrix if not self.__postmortemMode: if closesDist: camDist = self.__cfg['distRange'][0] elif postmortemParams is not None: self.__aimingSystem.yaw = postmortemParams[0][0] self.__aimingSystem.pitch = postmortemParams[0][1] camDist = postmortemParams[1] 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: vehicleMProv = vehicle.matrix if camDist is not None: self.setCameraDistance(camDist) else: self.__inputInertia.teleport(self.__calcRelativeDist()) self.vehicleMProv = vehicleMProv self.__setModelsToCollideWith(self.__vehiclesToCollideWith) BigWorld.camera(self.__cam) self.__aimingSystem.enable(preferredPos, turretYaw, gunPitch) self.__aimingSystem.aimMatrix = self.__calcAimMatrix() self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) from gui import g_guiResetters g_guiResetters.add(self.__onRecreateDevice) return def __refineVehicleMProv(self, vehicleMProv): vehicleTranslationOnly = Math.WGTranslationOnlyMP() vehicleTranslationOnly.source = vehicleMProv refinedMatrixProvider = Math.MatrixProduct() refinedMatrixProvider.a = mathUtils.createIdentityMatrix() refinedMatrixProvider.b = vehicleTranslationOnly return refinedMatrixProvider def __setupCameraProviders(self, vehicleMProv): vehiclePos = Math.Vector4Translation(vehicleMProv) cameraPositionProvider = Math.Vector4Combiner() cameraPositionProvider.fn = 'ADD' cameraPositionProvider.a = Vector4(0, 0, 0, 0) cameraPositionProvider.b = vehiclePos cameraAimPointProvider = Math.Vector4Combiner() cameraAimPointProvider.fn = 'ADD' cameraAimPointProvider.a = Vector4(0, 0, 1, 0) cameraAimPointProvider.b = vehiclePos self.__cam.cameraPositionProvider = cameraPositionProvider self.__cam.aimPointProvider = cameraAimPointProvider self.__cam.pivotPositionProvider = self.__aimingSystem.positionAboveVehicleProv def __setCameraPosition(self, relativeToVehiclePosition): self.__cam.cameraPositionProvider.a = Vector4( relativeToVehiclePosition.x, relativeToVehiclePosition.y, relativeToVehiclePosition.z, 1.0) def __setCameraAimPoint(self, relativeToVehiclePosition): self.__cam.aimPointProvider.a = Vector4(relativeToVehiclePosition.x, relativeToVehiclePosition.y, relativeToVehiclePosition.z, 1.0) def disable(self): from gui import g_guiResetters if self.__onRecreateDevice in g_guiResetters: g_guiResetters.remove(self.__onRecreateDevice) self.__setModelsToCollideWith([]) self.__cam.speedTreeTarget = None BigWorld.camera(None) if self.__shiftKeySensor is not None: self.__shiftKeySensor.reset(Math.Vector3()) self.stopCallback(self.__cameraUpdate) self.__movementOscillator.reset() self.__impulseOscillator.reset() self.__noiseOscillator.reset() self.__accelerationSmoother.reset() self.__autoUpdateDxDyDz.set(0) self.__updatedByKeyboard = False self.__inputInertia.teleport(self.__calcRelativeDist()) FovExtended.instance().resetFov() return def update(self, dx, dy, dz, rotateMode=True, zoomMode=True, updatedByKeyboard=False): self.__curSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'sensitivity'] self.__curScrollSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'scrollSensitivity'] self.__updatedByKeyboard = updatedByKeyboard if updatedByKeyboard: self.__autoUpdateDxDyDz.set(dx, dy, dz) else: self.__autoUpdateDxDyDz.set(0) self.__update(dx, dy, dz, rotateMode, zoomMode, False) def restoreDefaultsState(self): LOG_ERROR('ArcadeCamera::restoreDefaultState is obsolete!') def getConfigValue(self, name): return self.__cfg.get(name) def getUserConfigValue(self, name): return self.__userCfg.get(name) def setUserConfigValue(self, name, value): if name not in self.__userCfg: return else: self.__userCfg[name] = value if name not in ('keySensitivity', 'sensitivity', 'scrollSensitivity'): self.__cfg[name] = self.__userCfg[name] if name == 'fovMultMinMaxDist' and getattr( self, '_ArcadeCamera__aimingSystem', None) is not None: self.__inputInertia.teleport(self.__calcRelativeDist(), value) else: self.__cfg[name] = self.__baseCfg[name] * self.__userCfg[name] return def setCameraDistance(self, distance): distRange = self.__cfg['distRange'] clampedDist = mathUtils.clamp(distRange[0], distRange[1], distance) self.__aimingSystem.distanceFromFocus = clampedDist self.__inputInertia.teleport(self.__calcRelativeDist()) def getCameraDistance(self): return self.__aimingSystem.distanceFromFocus def setYawPitch(self, yaw, pitch): self.__aimingSystem.yaw = yaw self.__aimingSystem.pitch = pitch matrix = Math.Matrix() matrix.setRotateYPR( (self.__aimingSystem.yaw, self.__aimingSystem.pitch, 0)) 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): yawDelta, pitchDelta = self.calcYawPitchDelta(dx, dy) self.__aimingSystem.handleMovement(yawDelta, -pitchDelta) return (self.__aimingSystem.yaw, self.__aimingSystem.pitch, 0) def __update(self, dx, dy, dz, rotateMode=True, zoomMode=True, isCallback=False): prevPos = self.__inputInertia.calcWorldPos(self.__aimingSystem.matrix) distChanged = False if zoomMode and dz != 0: prevDist = self.__aimingSystem.distanceFromFocus distDelta = dz * float(self.__curScrollSense) distMinMax = self.__cfg['distRange'] newDist = mathUtils.clamp(distMinMax.min, distMinMax.max, prevDist - distDelta) floatEps = 0.001 if abs(newDist - prevDist) > floatEps: self.__aimingSystem.distanceFromFocus = newDist self.__userCfg['startDist'] = newDist self.__inputInertia.glideFov(self.__calcRelativeDist()) self.__aimingSystem.aimMatrix = self.__calcAimMatrix() distChanged = True if not self.__updatedByKeyboard: if abs(newDist - prevDist) < floatEps and mathUtils.almostZero( newDist - distMinMax.min): if self.__onChangeControlMode is not None: self.__onChangeControlMode() return if rotateMode: self.__updateAngles(dx, dy) if ENABLE_INPUT_ROTATION_INERTIA and not distChanged: self.__aimingSystem.update(0.0) if ENABLE_INPUT_ROTATION_INERTIA or distChanged: worldDeltaPos = prevPos - self.__aimingSystem.matrix.translation matInv = Matrix(self.__aimingSystem.matrix) matInv.invert() self.__inputInertia.glide(matInv.applyVector(worldDeltaPos)) return def __cameraUpdate(self): if not (self.__autoUpdateDxDyDz.x == 0.0 and self.__autoUpdateDxDyDz.y == 0.0 and self.__autoUpdateDxDyDz.z == 0.0): self.__update(self.__autoUpdateDxDyDz.x, self.__autoUpdateDxDyDz.y, self.__autoUpdateDxDyDz.z) inertDt = deltaTime = self.measureDeltaTime() replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: repSpeed = replayCtrl.playbackSpeed if repSpeed == 0.0: inertDt = 0.01 deltaTime = 0.0 else: inertDt = deltaTime = deltaTime / repSpeed self.__aimingSystem.update(deltaTime) virginShotPoint = self.__aimingSystem.getThirdPersonShotPoint() delta = self.__inputInertia.positionDelta sign = delta.dot(Vector3(0, 0, 1)) self.__inputInertia.update(inertDt) delta = (delta - self.__inputInertia.positionDelta).length if delta != 0.0: self.__cam.setScrollDelta(math.copysign(delta, sign)) FovExtended.instance().setFovByMultiplier( self.__inputInertia.fovZoomMultiplier) unshakenPos = self.__inputInertia.calcWorldPos( self.__aimingSystem.idealMatrix if self. __adCfg['enable'] else self.__aimingSystem.matrix) vehMatrix = Math.Matrix(self.__aimingSystem.vehicleMProv) vehiclePos = vehMatrix.translation fromVehicleToUnshakedPos = unshakenPos - vehiclePos deviationBasis = mathUtils.createRotationMatrix( Vector3(self.__aimingSystem.yaw, 0, 0)) impulseDeviation, movementDeviation, oscillationsZoomMultiplier = self.__updateOscillators( deltaTime) relCamPosMatrix = mathUtils.createTranslationMatrix(impulseDeviation + movementDeviation) relCamPosMatrix.postMultiply(deviationBasis) relCamPosMatrix.translation += fromVehicleToUnshakedPos upRotMat = mathUtils.createRotationMatrix( Vector3( 0, 0, -impulseDeviation.x * self.__dynamicCfg['sideImpulseToRollRatio'] - self.__noiseOscillator.deviation.z)) upRotMat.postMultiply(relCamPosMatrix) self.__cam.up = upRotMat.applyVector(Vector3(0, 1, 0)) relTranslation = relCamPosMatrix.translation self.__setCameraPosition(relTranslation) shotPoint = self.__calcFocalPoint(virginShotPoint, deltaTime) vehToShotPoint = shotPoint - vehiclePos self.__setCameraAimPoint(vehToShotPoint) replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() if not BigWorld.player().isForcedGuiControlMode() and GUI.mcursor( ).inFocus: GUI.mcursor().position = aimOffset else: aimOffset = self.__calcAimOffset(oscillationsZoomMultiplier) if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__cam.aimPointClipCoords = aimOffset self.__aimOffset = aimOffset if self.__shiftKeySensor is not None: self.__shiftKeySensor.update(1.0) if self.__shiftKeySensor.currentVelocity.lengthSquared > 0.0: self.shiftCamPos(self.__shiftKeySensor.currentVelocity) self.__shiftKeySensor.currentVelocity = Math.Vector3() return 0.0 def __calcFocalPoint(self, shotPoint, deltaTime): aimStartPoint = self.__aimingSystem.matrix.translation aimDir = shotPoint - aimStartPoint distToShotPoint = aimDir.length if distToShotPoint < 0.001: return shotPoint aimDir /= distToShotPoint absDiff = abs(distToShotPoint - self.__focalPointDist) if absDiff < ArcadeCamera._FOCAL_POINT_MIN_DIFF or absDiff <= ArcadeCamera._FOCAL_POINT_CHANGE_SPEED * deltaTime: self.__focalPointDist = distToShotPoint return shotPoint changeDir = (distToShotPoint - self.__focalPointDist) / absDiff self.__focalPointDist += changeDir * ArcadeCamera._FOCAL_POINT_CHANGE_SPEED * deltaTime return aimStartPoint + aimDir * self.__focalPointDist def __calcAimOffset(self, oscillationsZoomMultiplier): fov = BigWorld.projection().fov aspect = cameras.getScreenAspectRatio() yTan = math.tan(fov * 0.5) xTan = yTan * aspect defaultX = self.__defaultAimOffset[0] defaultY = self.__defaultAimOffset[1] yawFromImpulse = self.__impulseOscillator.deviation.x * self.__dynamicCfg[ 'sideImpulseToYawRatio'] xImpulseDeviationTan = math.tan( -(yawFromImpulse + self.__noiseOscillator.deviation.x) * oscillationsZoomMultiplier) pitchFromImpulse = self.__impulseOscillator.deviation.z * self.__dynamicCfg[ 'frontImpulseToPitchRatio'] yImpulseDeviationTan = math.tan( (pitchFromImpulse + self.__noiseOscillator.deviation.y) * oscillationsZoomMultiplier) totalOffset = Vector2( (defaultX * xTan + xImpulseDeviationTan) / (xTan * (1 - defaultX * xTan * xImpulseDeviationTan)), (defaultY * yTan + yImpulseDeviationTan) / (yTan * (1 - defaultY * yTan * yImpulseDeviationTan))) return totalOffset def __calcRelativeDist(self): distRange = self.__cfg['distRange'] curDist = self.__aimingSystem.distanceFromFocus relDist = (curDist - distRange[0]) / (distRange[1] - distRange[0]) return relDist def __calcCurOscillatorAcceleration(self, deltaTime): vehicle = BigWorld.player().getVehicleAttached() if vehicle is None: return Vector3(0, 0, 0) else: vehFilter = vehicle.filter curVelocity = getattr(vehFilter, 'velocity', Vector3(0.0)) relativeSpeed = curVelocity.length / vehicle.typeDescriptor.physics[ 'speedLimits'][0] if relativeSpeed >= ArcadeCamera._MIN_REL_SPEED_ACC_SMOOTHING: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationThreshold'] else: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationMax'] acceleration = self.__accelerationSmoother.update( vehicle, deltaTime) yawMat = mathUtils.createRotationMatrix( (-self.__aimingSystem.yaw, 0, 0)) acceleration = yawMat.applyVector(-acceleration) oscillatorAcceleration = Vector3(acceleration.x, acceleration.y, acceleration.z) return oscillatorAcceleration * self.__dynamicCfg[ 'accelerationSensitivity'] def __updateOscillators(self, deltaTime): if not ArcadeCamera.isCameraDynamic(): self.__impulseOscillator.reset() self.__movementOscillator.reset() self.__noiseOscillator.reset() return (Vector3(0), Vector3(0), 1.0) oscillatorAcceleration = self.__calcCurOscillatorAcceleration( deltaTime) self.__movementOscillator.externalForce += oscillatorAcceleration self.__impulseOscillator.update(deltaTime) self.__movementOscillator.update(deltaTime) self.__noiseOscillator.update(deltaTime) self.__impulseOscillator.externalForce = Vector3(0) self.__movementOscillator.externalForce = Vector3(0) self.__noiseOscillator.externalForce = Vector3(0) relDist = self.__calcRelativeDist() zoomMultiplier = mathUtils.lerp(1.0, self.__dynamicCfg['zoomExposure'], relDist) impulseDeviation = Vector3(self.__impulseOscillator.deviation) impulseDeviation.set(impulseDeviation.x * zoomMultiplier, impulseDeviation.y * zoomMultiplier, impulseDeviation.z * zoomMultiplier) movementDeviation = Vector3(self.__movementOscillator.deviation) movementDeviation.set(movementDeviation.x * zoomMultiplier, movementDeviation.y * zoomMultiplier, movementDeviation.z * zoomMultiplier) return (impulseDeviation, movementDeviation, zoomMultiplier) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) yawMat = mathUtils.createRotationMatrix( (-self.__aimingSystem.yaw, 0, 0)) impulseLocal = yawMat.applyVector(adjustedImpulse) self.__impulseOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): applicationPosition = self.__cam.position if reason == ImpulseReason.SPLASH: applicationPosition = Matrix(self.vehicleMProv).translation impulse = applicationPosition - position distance = impulse.length if distance < 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.OTHER_SHOT and distance <= self.__dynamicCfg[ 'maxShotImpulseDistance']: impulse *= impulseValue / distance elif reason == ImpulseReason.SPLASH or reason == ImpulseReason.HE_EXPLOSION: impulse *= impulseValue / distance elif reason == ImpulseReason.VEHICLE_EXPLOSION and distance <= self.__dynamicCfg[ 'maxExplosionImpulseDistance']: impulse *= impulseValue / distance else: return self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3(noiseMagnitude) self.__noiseOscillator.applyImpulse(noiseImpulse) 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 reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection( 'gui/avatar_input_handler.xml/arcadeMode/camera/') self.__readCfg(cameraSec) def __calcAimMatrix(self): endMult = self.__inputInertia.endZoomMultiplier fov = FovExtended.instance().actualDefaultVerticalFov * endMult offset = self.__defaultAimOffset return cameras.getAimMatrix(-offset[0], -offset[1], fov) 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)) distRangeVec = readVec2(dataSec, 'distRange', (1, 1), (100, 100), (2, 20)) bcfg['distRange'] = MinMax(distRangeVec.x, distRangeVec.y) bcfg['minStartDist'] = readFloat(dataSec, 'minStartDist', bcfg['distRange'][0], bcfg['distRange'][1], bcfg['distRange'][0]) bcfg['optimalStartDist'] = readFloat(dataSec, 'optimalStartDist', bcfg['distRange'][0], bcfg['distRange'][1], bcfg['distRange'][0]) bcfg['angleRange'][0] = math.radians( bcfg['angleRange'][0]) - math.pi * 0.5 bcfg['angleRange'][1] = math.radians( bcfg['angleRange'][1]) - math.pi * 0.5 bcfg['fovMultMinMaxDist'] = MinMax( readFloat(dataSec, 'fovMultMinDist', 0.1, 100, 1.0), readFloat(dataSec, 'fovMultMaxDist', 0.1, 100, 1.0)) 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.settings_core.SettingsCore import g_settingsCore ucfg['horzInvert'] = g_settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = g_settingsCore.getSetting('mouseVertInvert') ucfg['sniperModeByShift'] = g_settingsCore.getSetting( 'sniperModeByShift') 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', bcfg['distRange'][0], 500, bcfg['optimalStartDist']) if ucfg['startDist'] < bcfg['minStartDist']: ucfg['startDist'] = bcfg['optimalStartDist'] ucfg['startAngle'] = readFloat(ds, 'startAngle', 5, 180, 60) ucfg['startAngle'] = math.radians(ucfg['startAngle']) - math.pi * 0.5 ucfg['fovMultMinMaxDist'] = MinMax( readFloat(ds, 'fovMultMinDist', 0.1, 100, bcfg['fovMultMinMaxDist'].min), readFloat(ds, 'fovMultMaxDist', 0.1, 100, bcfg['fovMultMinMaxDist'].max)) 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['minStartDist'] = bcfg['minStartDist'] 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'] cfg['fovMultMinMaxDist'] = ucfg['fovMultMinMaxDist'] cfg['sniperModeByShift'] = ucfg['sniperModeByShift'] 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()) dynamicsSection = dataSec['dynamics'] self.__impulseOscillator = createOscillatorFromSection( dynamicsSection['impulseOscillator'], False) self.__movementOscillator = createOscillatorFromSection( dynamicsSection['movementOscillator'], False) self.__movementOscillator = Math.PyCompoundOscillator( self.__movementOscillator, Math.PyOscillator(1.0, Vector3(50), Vector3(20), Vector3(0.01, 0.0, 0.01))) self.__noiseOscillator = createOscillatorFromSection( dynamicsSection['randomNoiseOscillatorSpherical']) self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__dynamicCfg['accelerationSensitivity'] = readFloat( dynamicsSection, 'accelerationSensitivity', -1000, 1000, 0.1) self.__dynamicCfg['frontImpulseToPitchRatio'] = math.radians( readFloat(dynamicsSection, 'frontImpulseToPitchRatio', -1000, 1000, 0.1)) self.__dynamicCfg['sideImpulseToRollRatio'] = math.radians( readFloat(dynamicsSection, 'sideImpulseToRollRatio', -1000, 1000, 0.1)) self.__dynamicCfg['sideImpulseToYawRatio'] = math.radians( readFloat(dynamicsSection, 'sideImpulseToYawRatio', -1000, 1000, 0.1)) accelerationThreshold = readFloat(dynamicsSection, 'accelerationThreshold', 0.0, 1000.0, 0.1) self.__dynamicCfg['accelerationThreshold'] = accelerationThreshold self.__dynamicCfg['accelerationMax'] = readFloat( dynamicsSection, 'accelerationMax', 0.0, 1000.0, 0.1) self.__dynamicCfg['maxShotImpulseDistance'] = readFloat( dynamicsSection, 'maxShotImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['maxExplosionImpulseDistance'] = readFloat( dynamicsSection, 'maxExplosionImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['zoomExposure'] = readFloat(dynamicsSection, 'zoomExposure', 0.0, 1000.0, 0.25) accelerationFilter = mathUtils.RangeFilter( self.__dynamicCfg['accelerationThreshold'], self.__dynamicCfg['accelerationMax'], 100, mathUtils.SMAFilter(ArcadeCamera._FILTER_LENGTH)) maxAccelerationDuration = readFloat( dynamicsSection, 'maxAccelerationDuration', 0.0, 10000.0, ArcadeCamera._DEFAULT_MAX_ACCELERATION_DURATION) self.__accelerationSmoother = AccelerationSmoother( accelerationFilter, maxAccelerationDuration) self.__inputInertia = _InputInertia(self.__cfg['fovMultMinMaxDist'], 0.0) advancedCollider = dataSec['advancedCollider'] self.__adCfg = dict() cfg = self.__adCfg if advancedCollider is None: LOG_ERROR('<advancedCollider> dataSection is not found!') cfg['enable'] = False else: cfg['enable'] = advancedCollider.readBool('enable', False) cfg['fovRatio'] = advancedCollider.readFloat('fovRatio', 2.0) cfg['rollbackSpeed'] = advancedCollider.readFloat( 'rollbackSpeed', 1.0) cfg['minimalCameraDistance'] = self.__cfg['distRange'][0] cfg['speedThreshold'] = advancedCollider.readFloat( 'speedThreshold', 0.1) cfg['minimalVolume'] = advancedCollider.readFloat( 'minimalVolume', 200.0) cfg['volumeGroups'] = dict() for group in VOLUME_GROUPS_NAMES: groups = advancedCollider['volumeGroups'] cfg['volumeGroups'][group] = CollisionVolumeGroup.fromSection( groups[group]) 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']) ds.writeFloat('arcadeMode/camera/fovMultMinDist', ucfg['fovMultMinMaxDist'].min) ds.writeFloat('arcadeMode/camera/fovMultMaxDist', ucfg['fovMultMinMaxDist'].max) ucfg['startAngle'] = math.degrees(ucfg['startAngle'] + math.pi * 0.5) ds.writeFloat('arcadeMode/camera/startAngle', ucfg['startAngle']) def __onRecreateDevice(self): self.__aimingSystem.aimMatrix = self.__calcAimMatrix()
class ArtyCamera(ICamera, CallbackDelayer): _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): ArtyCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return ArtyCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) settingsCore = dependency.descriptor(ISettingsCore) __aimOffset = aih_global_binding.bindRW(aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec): CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__curSense = self.__cfg['sensitivity'] self.__onChangeControlMode = None self.__camDist = 0.0 self.__desiredCamDist = 0.0 self.__aimingSystem = None self.__prevTime = 0.0 self.__dxdydz = Vector3(0.0, 0.0, 0.0) self.__autoUpdatePosition = False self.__needReset = 0 self.__sourceMatrix = None self.__targetMatrix = None self.__rotation = 0.0 self.__positionHysteresis = None self.__timeHysteresis = None return def create(self, onChangeControlMode=None): self.__onChangeControlMode = onChangeControlMode aimingSystemClass = ArtyAimingSystemRemote if BigWorld.player().isObserver() else ArtyAimingSystem self.__aimingSystem = aimingSystemClass() self.__camDist = self.__cfg['camDist'] self.__desiredCamDist = self.__camDist self.__positionHysteresis = PositionHysteresis(self.__cfg['hPositionThresholdSq']) self.__timeHysteresis = TimeHysteresis(self.__cfg['hTimeThreshold']) self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Vector3(0.0, 0.0, 0.0) self.__sourceMatrix = Matrix() self.__targetMatrix = Matrix() self.__rotation = 0.0 def destroy(self): CallbackDelayer.destroy(self) self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None return def enable(self, targetPos, saveDist): self.__prevTime = 0.0 self.__aimingSystem.enable(targetPos) self.__positionHysteresis.update(Vector3(0.0, 0.0, 0.0)) self.__timeHysteresis.update(BigWorld.timeExact()) camTarget = MatrixProduct() self.__cam.target = camTarget self.__cam.source = self.__sourceMatrix self.__cam.wg_applyParams() BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo(self.__aimingSystem.hitPoint) BigWorld.player().positionControl.followCamera(False) self.__rotation = 0.0 self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) self.__needReset = 1 def disable(self): if self.__aimingSystem is not None: self.__aimingSystem.disable() self.stopCallback(self.__cameraUpdate) self.__positionOscillator.reset() return def teleport(self, pos): self.__aimingSystem.updateTargetPos(pos) self.update(0.0, 0.0, 0.0) def getConfigValue(self, name): return self.__cfg.get(name) 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 update(self, dx, dy, dz, updateByKeyboard=False): self.__curSense = self.__cfg['keySensitivity'] if updateByKeyboard else self.__cfg['sensitivity'] self.__autoUpdatePosition = updateByKeyboard self.__dxdydz = Vector3(dx if not self.__cfg['horzInvert'] else -dx, dy if not self.__cfg['vertInvert'] else -dy, dz) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse(impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() impulseLocal = self.__sourceMatrix.applyVector(impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) 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('artyMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('artyMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('artyMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('artyMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('artyMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('artyMode/camera/camDist', self.__cfg['camDist']) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) def __calculateAimOffset(self, aimWorldPos): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: aimOffset = cameras.projectPoint(aimWorldPos) aimOffset = Vector2(mathUtils.clamp(-0.95, 0.95, aimOffset.x), mathUtils.clamp(-0.95, 0.95, aimOffset.y)) if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) return aimOffset def __handleMovement(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos(replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) return def __getCameraDirection(self): direction = Vector3() direction.setPitchYaw(self.__rotation, self.__aimingSystem.direction.yaw) direction.normalise() return direction def __getDesiredCameraDistance(self): distRange = self.__cfg['distRange'] self.__desiredCamDist -= self.__dxdydz.z * self.__curSense self.__desiredCamDist = mathUtils.clamp(distRange[0], distRange[1], self.__desiredCamDist) self.__desiredCamDist = self.__aimingSystem.overrideCamDist(self.__desiredCamDist) self.__cfg['camDist'] = self.__camDist return self.__desiredCamDist def __updateTrajectoryDrawer(self): shotDescr = BigWorld.player().getVehicleDescriptor().shot BigWorld.wg_trajectory_drawer().setParams(shotDescr.maxDistance, Vector3(0, -shotDescr.gravity, 0), self.__aimOffset) def __updateTime(self): curTime = BigWorld.timeExact() deltaTime = curTime - self.__prevTime self.__prevTime = curTime return deltaTime def __choosePitchLevel(self, aimPoint): useHighPitch = (aimPoint - self.__aimingSystem.hitPoint).lengthSquared > self.__cfg['highPitchThresholdSq'] if useHighPitch: useHighPitch = self.__positionHysteresis.check(aimPoint) or self.__timeHysteresis.check(self.__prevTime) else: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return useHighPitch def __getCollisionTestOrigin(self, aimPoint, direction): distRange = self.__cfg['distRange'] vehiclePosition = BigWorld.player().getVehicleAttached().position collisionTestOrigin = Vector3(vehiclePosition) if direction.x * direction.x > direction.z * direction.z and not mathUtils.almostZero(direction.x): collisionTestOrigin.y = direction.y / direction.x * (vehiclePosition.x - aimPoint.x) + aimPoint.y elif not mathUtils.almostZero(direction.z): collisionTestOrigin.y = direction.y / direction.z * (vehiclePosition.z - aimPoint.z) + aimPoint.y else: collisionTestOrigin = aimPoint - direction.scale((distRange[1] - distRange[0]) / 2.0) LOG_WARNING("Can't find point on direction ray. Using half point as collision test origin") return collisionTestOrigin def __resolveCollisions(self, aimPoint, distance, direction): distRange = self.__cfg['distRange'] collisionTestOrigin = self.__getCollisionTestOrigin(aimPoint, direction) sign = copysign(1.0, distance * distance - (aimPoint - collisionTestOrigin).lengthSquared) srcPoint = collisionTestOrigin endPoint = aimPoint - direction.scale(distance + sign * distRange[0]) collision = collideDynamicAndStatic(srcPoint, endPoint, (BigWorld.player().playerVehicleID,)) if collision: collisionDistance = (aimPoint - collision[0]).length if sign * (collisionDistance - distance) < distRange[0]: distance = collisionDistance - sign * distRange[0] if mathUtils.almostZero(self.__rotation): srcPoint = aimPoint - direction.scale(distance) endPoint = aimPoint collision = collideDynamicAndStatic(srcPoint, endPoint, (BigWorld.player().playerVehicleID,)) if collision: self.__aimingSystem.aimPoint = collision[0] if collision[1]: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return distance def __calculateIdealState(self, deltaTime): aimPoint = self.__aimingSystem.aimPoint direction = self.__aimingSystem.direction impactPitch = max(direction.pitch, self.__cfg['minimalPitch']) self.__rotation = max(self.__rotation, impactPitch) distRange = self.__cfg['distRange'] distanceCurSq = (aimPoint - BigWorld.player().getVehicleAttached().position).lengthSquared distanceMinSq = distRange[0] ** 2.0 forcedPitch = impactPitch if distanceCurSq < distanceMinSq: forcedPitch = atan2(sqrt(distanceMinSq - distanceCurSq), sqrt(distanceCurSq)) angularShift = self.__cfg['angularSpeed'] * deltaTime angularShift = angularShift if self.__choosePitchLevel(aimPoint) else -angularShift minPitch = max(forcedPitch, impactPitch) maxPitch = max(forcedPitch, self.__cfg['maximalPitch']) self.__rotation = mathUtils.clamp(minPitch, maxPitch, self.__rotation + angularShift) desiredDistance = self.__getDesiredCameraDistance() cameraDirection = self.__getCameraDirection() desiredDistance = self.__resolveCollisions(aimPoint, desiredDistance, cameraDirection) desiredDistance = mathUtils.clamp(distRange[0], distRange[1], desiredDistance) translation = aimPoint - cameraDirection.scale(desiredDistance) rotation = Vector3(cameraDirection.yaw, -cameraDirection.pitch, 0.0) return (translation, rotation) def __interpolateStates(self, deltaTime, translation, rotation): lerpParam = mathUtils.clamp(0.0, 1.0, deltaTime * self.__cfg['interpolationSpeed']) self.__sourceMatrix = slerp(self.__sourceMatrix, rotation, lerpParam) self.__targetMatrix.translation = mathUtils.lerp(self.__targetMatrix.translation, translation, lerpParam) return (self.__sourceMatrix, self.__targetMatrix) def __cameraUpdate(self): deltaTime = self.__updateTime() self.__handleMovement() aimPoint = Vector3(self.__aimingSystem.aimPoint) self.__aimOffset = self.__calculateAimOffset(aimPoint) self.__updateTrajectoryDrawer() translation, rotation = self.__calculateIdealState(deltaTime) self.__interpolateStates(deltaTime, translation, mathUtils.createRotationMatrix(rotation)) self.__camDist = aimPoint - self.__targetMatrix.translation self.__camDist = self.__camDist.length self.__cam.source = self.__sourceMatrix self.__cam.target.b = self.__targetMatrix self.__cam.pivotPosition = Vector3(0, 0, 0) BigWorld.player().positionControl.moveTo(aimPoint) self.__updateOscillator(deltaTime) self.__aimingSystem.update(deltaTime) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) def __updateOscillator(self, deltaTime): if ArtyCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = mathUtils.createTranslationMatrix(self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def __readCfg(self, dataSec): if not dataSec: LOG_WARNING('Invalid section <artyMode/camera> in avatar_input_handler.xml') self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10.0, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10.0, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10.0, 0.025) bcfg['angularSpeed'] = readFloat(dataSec, 'angularSpeed', pi / 720.0, pi / 0.5, pi / 360.0) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1.0, 1.0), (10000.0, 10000.0), (2.0, 30.0)) bcfg['minimalPitch'] = readFloat(dataSec, 'minimalPitch', pi / 36.0, pi / 3.0, pi / 18.0) bcfg['maximalPitch'] = readFloat(dataSec, 'maximalPitch', pi / 6.0, pi / 3.0, pi / 3.5) bcfg['interpolationSpeed'] = readFloat(dataSec, 'interpolationSpeed', 0.0, 10.0, 5.0) bcfg['highPitchThreshold'] = readFloat(dataSec, 'highPitchThreshold', 0.1, 10.0, 3.0) bcfg['hTimeThreshold'] = readFloat(dataSec, 'hysteresis/timeThreshold', 0.0, 10.0, 0.5) bcfg['hPositionThreshold'] = readFloat(dataSec, 'hysteresis/positionThreshold', 0.0, 100.0, 7.0) ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['artyMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg ucfg['horzInvert'] = self.settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = self.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['camDist'] = readFloat(ds, 'camDist', 0.0, 60.0, 0.0) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['angularSpeed'] = bcfg['angularSpeed'] cfg['minimalPitch'] = bcfg['minimalPitch'] cfg['maximalPitch'] = bcfg['maximalPitch'] cfg['interpolationSpeed'] = bcfg['interpolationSpeed'] cfg['highPitchThresholdSq'] = bcfg['highPitchThreshold'] * bcfg['highPitchThreshold'] cfg['hTimeThreshold'] = bcfg['hTimeThreshold'] cfg['hPositionThresholdSq'] = bcfg['hPositionThreshold'] * bcfg['hPositionThreshold'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['camDist'] = ucfg['camDist'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] dynamicSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicSection) self.__positionOscillator = createOscillatorFromSection(dynamicSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection(dynamicSection['randomNoiseOscillatorFlat'], False) return
class StrategicCamera(CameraWithSettings, CallbackDelayer): _DYNAMIC_ENABLED = True ABSOLUTE_VERTICAL_FOV = math.radians(60.0) _SMOOTHING_PIVOT_DELTA_FACTOR = 6.0 @staticmethod def enableDynamicCamera(enable): StrategicCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return StrategicCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) __aimOffset = aih_global_binding.bindRW(aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec): super(StrategicCamera, self).__init__() CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__activeDistRangeSettings = None self.__dynamicCfg = CameraDynamicConfig() self.__cameraYaw = 0.0 self.__switchers = CameraSwitcherCollection(cameraSwitchers=[CameraSwitcher(switchType=SwitchTypes.FROM_TRANSITION_DIST_AS_MIN, switchToName=CTRL_MODE_NAME.ARTY, switchToPos=1.0)], isEnabled=True) self._readConfigs(dataSec) self.__cam = BigWorld.CursorCamera() self.__cam.isHangar = False self.__curSense = self._cfg['sensitivity'] self.__onChangeControlMode = None self.__aimingSystem = None self.__prevTime = BigWorld.time() self.__autoUpdatePosition = False self.__dxdydz = Vector3(0, 0, 0) self.__needReset = 0 self.__smoothingPivotDelta = 0 self.__transitionEnabled = True self.__camDist = 0.0 self.__scrollSmoother = SPGScrollSmoother(0.3) self.__saveDist = False return @staticmethod def _getConfigsKey(): return StrategicCamera.__name__ def create(self, onChangeControlMode=None): aimingSystemClass = StrategicAimingSystemRemote if BigWorld.player().isObserver() else StrategicAimingSystem self.__aimingSystem = aimingSystemClass(self._cfg['distRange'][0], self.__cameraYaw) super(StrategicCamera, self).create() self.__onChangeControlMode = onChangeControlMode self.__camDist = self._cfg['camDist'] self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) self.__scrollSmoother.setTime(self._cfg['scrollSmoothingTime']) self.__enableSwitchers() def destroy(self): self.__saveDist = False self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None CallbackDelayer.destroy(self) CameraWithSettings.destroy(self) return def enable(self, targetPos, saveDist, switchToPos=None, switchToPlace=None): self.__prevTime = BigWorld.time() self.__aimingSystem.enable(targetPos) self.__activeDistRangeSettings = self.__getActiveDistRangeForArena() if self.__activeDistRangeSettings is not None: self.__aimingSystem.height = self.__getDistRange()[0] minDist, maxDist = self.__getDistRange() maxPivotHeight = maxDist - minDist self.__updateCameraYaw() if switchToPlace == SwitchToPlaces.TO_TRANSITION_DIST: self.__camDist = self.__getTransitionCamDist() elif switchToPlace == SwitchToPlaces.TO_RELATIVE_POS and switchToPos is not None: self.__camDist = maxPivotHeight * switchToPos elif switchToPlace == SwitchToPlaces.TO_NEAR_POS and switchToPos is not None: switchToPos = math_utils.clamp(minDist, maxDist, switchToPos) self.__camDist = switchToPos - minDist elif self.settingsCore.getSetting(settings_constants.SPGAim.AUTO_CHANGE_AIM_MODE): self.__camDist = math_utils.clamp(self.__getTransitionCamDist(), maxPivotHeight, self.__camDist) self.__saveDist = saveDist self.__camDist = math_utils.clamp(0, maxPivotHeight, self.__camDist) self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) self.__scrollSmoother.start(self.__camDist) self.__enableSwitchers() camTarget = Math.MatrixProduct() camTarget.b = self.__aimingSystem.matrix self.__cam.target = camTarget self.__cam.forceUpdate() BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo(self.__aimingSystem.matrix.translation) BigWorld.player().positionControl.followCamera(True) FovExtended.instance().enabled = False BigWorld.projection().fov = StrategicCamera.ABSOLUTE_VERTICAL_FOV self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) self.__needReset = 1 return def disable(self): self.__scrollSmoother.stop() if self.__aimingSystem is not None: self.__aimingSystem.disable() self.__switchers.clear() self.stopCallback(self.__cameraUpdate) positionControl = BigWorld.player().positionControl if positionControl is not None: positionControl.followCamera(False) self.__positionOscillator.reset() FovExtended.instance().resetFov() FovExtended.instance().enabled = True return def teleport(self, pos): self.moveToPosition(pos) def setMaxDist(self): distRange = self.__getDistRange() if len(distRange) > 1: self.__camDist = distRange[1] def getDistRatio(self): distRange = self.__getDistRange() maxPivotHeight = distRange[1] - distRange[0] return self.__camDist / maxPivotHeight def getCurrentCamDist(self): return self.__camDist + self.__getDistRange()[0] def getCamDistRange(self): return self.__getDistRange() def getCamTransitionDist(self): return self._cfg['transitionDist'] def update(self, dx, dy, dz, updatedByKeyboard=False): self.__curSense = self._cfg['keySensitivity'] if updatedByKeyboard else self._cfg['sensitivity'] standardMaxDist = self._cfg['distRange'][1] if self.__camDist > standardMaxDist: self.__curSense *= self.__getCameraScrollMultiplier() + (self.__camDist - self._cfg['distRange'][1]) * self.__getCameraAcceleration() self.__autoUpdatePosition = updatedByKeyboard self.__dxdydz = Vector3(dx, dy, dz) def moveToPosition(self, pos): self.__aimingSystem.enable(pos) self.update(0.0, 0.0, 0.0) def calcVisibleAreaRatio(self): points = [Math.Vector2(1, 1), Math.Vector2(1, -1), Math.Vector2(-1, -1), Math.Vector2(-1, 1)] dirsPos = [ getWorldRayAndPoint(point.x, point.y) for point in points ] planeXZ = Plane(Math.Vector3(0, 1, 0), 0) collisionPoints = [] for direction, begPos in dirsPos: endPos = begPos + direction * 1000 testResult = BigWorld.wg_collideSegment(BigWorld.player().spaceID, begPos, endPos, 3) collPoint = Math.Vector3(0, 0, 0) if collPoint is not None: collPoint = testResult.closestPoint else: collPoint = planeXZ.intersectSegment(begPos, endPos) collisionPoints.append(collPoint) x0 = abs(collisionPoints[1].x - collisionPoints[2].x) x1 = abs(collisionPoints[0].x - collisionPoints[3].x) z0 = abs(collisionPoints[0].z - collisionPoints[1].z) z1 = abs(collisionPoints[3].z - collisionPoints[2].z) bb = BigWorld.player().arena.arenaType.boundingBox arenaBottomLeft = bb[0] arenaUpperRight = bb[1] arenaX = arenaUpperRight[0] - arenaBottomLeft[0] arenaZ = arenaUpperRight[1] - arenaBottomLeft[1] return ((x0 + x1) * 0.5 / arenaX, (z0 + z1) * 0.5 / arenaZ) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse(impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() cameraYawMat = math_utils.createRotationMatrix(Vector3(-self.__cameraYaw, 0.0, 0.0)) impulseLocal = cameraYawMat.applyVector(impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = math_utils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) 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.writeFloat('strategicMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('strategicMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('strategicMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('strategicMode/camera/camDist', self._cfg['camDist']) def __cameraUpdate(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: aimOffset = self.__calcAimOffset() if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__aimOffset = aimOffset self.__updateCameraYaw() shotDescr = BigWorld.player().getVehicleDescriptor().shot BigWorld.wg_trajectory_drawer().setParams(shotDescr.maxDistance, Math.Vector3(0, -shotDescr.gravity, 0), aimOffset) curTime = BigWorld.time() deltaTime = curTime - self.__prevTime self.__prevTime = curTime self.__aimingSystem.update(deltaTime) if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos(replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) self.__calcSmoothingPivotDelta(deltaTime) self.__camDist -= self.__dxdydz.z * float(self.__curSense) self.__camDist = self.__aimingSystem.overrideCamDist(self.__camDist) distRange = self.__getDistRange() maxPivotHeight = distRange[1] - distRange[0] transitionDist = self._cfg['transitionDist'] - distRange[0] if self.__switchers.isEnabled(): self.__camDist = math_utils.clamp(transitionDist, maxPivotHeight, self.__camDist) scrollLimits = (transitionDist, maxPivotHeight) else: self.__camDist = math_utils.clamp(0, maxPivotHeight, self.__camDist) scrollLimits = (0, maxPivotHeight) if self.__saveDist: self._cfg['camDist'] = self.__camDist self.__scrollSmoother.moveTo(self.__camDist, scrollLimits) currentCamDist = self.__scrollSmoother.update(deltaTime) camDistWithSmoothing = currentCamDist + self.__smoothingPivotDelta - self.__aimingSystem.heightFromPlane self.__cam.pivotPosition = Math.Vector3(0.0, camDistWithSmoothing, 0.0) if self.__onChangeControlMode is not None and self.__switchers.needToSwitch(self.__dxdydz.z, self.__camDist, 0, maxPivotHeight, transitionDist): self.__onChangeControlMode(*self.__switchers.getSwitchParams()) if not self.__transitionEnabled and self.__camDist + TRANSITION_DIST_HYSTERESIS >= transitionDist: self.__transitionEnabled = True self.__enableSwitchers(False) self.__updateOscillator(deltaTime) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) return 0.0 def _handleSettingsChange(self, diff): if settings_constants.SPGAim.SPG_STRATEGIC_CAM_MODE in diff: self.__aimingSystem.setParallaxModeEnabled(diff[settings_constants.SPGAim.SPG_STRATEGIC_CAM_MODE] == 1) if settings_constants.SPGAim.AUTO_CHANGE_AIM_MODE in diff: self.__enableSwitchers() if settings_constants.SPGAim.SCROLL_SMOOTHING_ENABLED in diff: self.__scrollSmoother.setIsEnabled(self.settingsCore.getSetting(settings_constants.SPGAim.SCROLL_SMOOTHING_ENABLED)) def _updateSettingsFromServer(self): if self.settingsCore.isReady: if self.__aimingSystem is not None: self.__aimingSystem.setParallaxModeEnabled(self.settingsCore.getSetting(settings_constants.SPGAim.SPG_STRATEGIC_CAM_MODE) == 1) self.__enableSwitchers() self.__scrollSmoother.setIsEnabled(self.settingsCore.getSetting(settings_constants.SPGAim.SCROLL_SMOOTHING_ENABLED)) return def __calcSmoothingPivotDelta(self, deltaTime): heightsDy = self.__aimingSystem.heightFromPlane - self.__smoothingPivotDelta smoothFactor = math_utils.clamp(0, 1, StrategicCamera._SMOOTHING_PIVOT_DELTA_FACTOR * deltaTime) smoothingPivotDeltaDy = smoothFactor * heightsDy self.__smoothingPivotDelta += smoothingPivotDeltaDy def __calcAimOffset(self): aimWorldPos = self.__aimingSystem.matrix.applyPoint(Vector3(0, -self.__aimingSystem.height, 0)) aimOffset = cameras.projectPoint(aimWorldPos) return Vector2(math_utils.clamp(-0.95, 0.95, aimOffset.x), math_utils.clamp(-0.95, 0.95, aimOffset.y)) def __updateOscillator(self, deltaTime): if StrategicCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = math_utils.createTranslationMatrix(self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def __updateCameraYaw(self): altModeEnabled = self.settingsCore.getSetting(settings_constants.SPGAim.SPG_STRATEGIC_CAM_MODE) == 1 pitch = -math.pi * 0.499 if not altModeEnabled else math.radians(-88.0) self.__cameraYaw = round(self.aimingSystem.getCamYaw(), _CAM_YAW_ROUND) srcMat = math_utils.createRotationMatrix((self.__cameraYaw, pitch, 0.0)) self.__cam.source = srcMat def __getTransitionCamDist(self): minDist, maxDist = self.__getDistRange() maxPivotHeight = maxDist - minDist transitionDist = self._cfg['transitionDist'] - minDist return math_utils.clamp(0, maxPivotHeight, transitionDist) def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection('gui/avatar_input_handler.xml/strategicMode/camera/') self._reloadConfigs(cameraSec) def _readConfigs(self, dataSec): if not dataSec or dataSec['strategic']: LOG_WARNING('Invalid section <strategicMode/camera> in avatar_input_handler.xml') super(StrategicCamera, self)._readConfigs(dataSec) dynamicsSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__positionOscillator = createOscillatorFromSection(dynamicsSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection(dynamicsSection['randomNoiseOscillatorFlat'], False) def _readBaseCfg(self, dataSec): bcfg = self._baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10, 0.025) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (10000, 10000), (2, 30)) bcfg['transitionDist'] = readFloat(dataSec, 'transitionDist', 1.0, 10000.0, 60.0) bcfg['distRangeForArenaSize'] = self.__readDynamicDistRangeData(dataSec) bcfg['scrollSmoothingTime'] = readFloat(dataSec, 'scrollSmoothingTime', 0.0, 1.0, 0.3) def _readUserCfg(self): ucfg = self._userCfg dataSec = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if dataSec is not None: dataSec = dataSec['strategicMode/camera'] ucfg['horzInvert'] = False ucfg['vertInvert'] = False ucfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['camDist'] = readFloat(dataSec, 'camDist', 0.0, 60.0, 0) return def _makeCfg(self): bcfg = self._baseCfg ucfg = self._userCfg cfg = self._cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['transitionDist'] = bcfg['transitionDist'] cfg['distRangeForArenaSize'] = bcfg['distRangeForArenaSize'] cfg['scrollSmoothingTime'] = bcfg['scrollSmoothingTime'] cfg['camDist'] = ucfg['camDist'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] def __readDynamicDistRangeData(self, dataSec): section = dataSec['dynamicDistRanges'] dynamicDistRanges = [] if section is None: return dynamicDistRanges else: value = section['dynamicDistRange'] minArenaSize = readFloat(value, 'minArenaSize', 0.1, 2000, 2000.0) distRange = readVec2(value, 'distRangeOverride', (1, 1), (2000, 2000), (40, 300)) acceleration = readFloat(value, 'acceleration', 0.0, 100.0, 0.0) scrollMultiplier = readFloat(value, 'scrollMultiplier', 0.0, 100.0, 1.0) dynamicDistRanges.append(_DistRangeSetting(minArenaSize, distRange, scrollMultiplier, acceleration)) return dynamicDistRanges def __getActiveDistRangeForArena(self): bb = BigWorld.player().arena.arenaType.boundingBox arenaBottomLeft = bb[0] arenaUpperRight = bb[1] arenaX = arenaUpperRight[0] - arenaBottomLeft[0] arenaZ = arenaUpperRight[1] - arenaBottomLeft[1] arenaSize = min(arenaX, arenaZ) availableDistRanges = self._cfg['distRangeForArenaSize'] currentDistRange = None choosenArenaMinSize = 0 for pt in availableDistRanges: if arenaSize >= pt.minArenaSize and pt.minArenaSize > choosenArenaMinSize: choosenArenaMinSize = pt.minArenaSize currentDistRange = pt return currentDistRange def __getDistRange(self): return self._cfg['distRange'] if not self.__activeDistRangeSettings else self.__activeDistRangeSettings.distRange def __getCameraAcceleration(self): return 0.0 if not self.__activeDistRangeSettings else self.__activeDistRangeSettings.acceleration def __getCameraScrollMultiplier(self): return 1.0 if not self.__activeDistRangeSettings else self.__activeDistRangeSettings.scrollMultiplier def __enableSwitchers(self, updateTransitionEnabled=True): minDist, _ = self.__getDistRange() if updateTransitionEnabled and self.__camDist + minDist + TRANSITION_DIST_HYSTERESIS <= self._cfg['transitionDist']: self.__transitionEnabled = False if self.settingsCore.isReady: self.__switchers.setIsEnabled(self.settingsCore.getSetting(settings_constants.SPGAim.AUTO_CHANGE_AIM_MODE) and self.__transitionEnabled)