diff --git a/src/app/Config.ts b/src/app/Config.ts index b771e110..4195b7e2 100644 --- a/src/app/Config.ts +++ b/src/app/Config.ts @@ -10,6 +10,7 @@ const Config = { MaxCameraDistance: 4000, SlippyMapTransitionDuration: 400, MinFreeCameraHeight: 10, + FreeCameraBikeHeight: 2, CameraZoomSmoothing: 0.4, CameraZoomSpeed: 0.0005, CameraZoomTrackpadFactor: 4, @@ -21,6 +22,10 @@ const Config = { GroundCameraSpeedFast: 1200, FreeCameraSpeed: 400, FreeCameraSpeedFast: 1200, + BikeCameraSpeed: 10, + BikeCameraSpeedFast: 50, + BikeCameraNear: 1, + CameraNear: 10, FreeCameraRotationSensitivity: 0.00002, FreeCameraYawSpeed: 0.8, FreeCameraPitchSpeed: 0.8, diff --git a/src/app/controls/FreeControlsNavigator.ts b/src/app/controls/FreeControlsNavigator.ts index 78c142d8..fc53acca 100644 --- a/src/app/controls/FreeControlsNavigator.ts +++ b/src/app/controls/FreeControlsNavigator.ts @@ -2,13 +2,14 @@ import ControlsNavigator from "./ControlsNavigator"; import Vec3 from "~/lib/math/Vec3"; import Config from "../Config"; import MathUtils from "~/lib/math/MathUtils"; -import {ControlsState} from "../systems/ControlsSystem"; +import ControlsSystem, {ControlsState} from "../systems/ControlsSystem"; import PerspectiveCamera from "~/lib/core/PerspectiveCamera"; import TerrainHeightProvider from "~/app/terrain/TerrainHeightProvider"; export default class FreeControlsNavigator extends ControlsNavigator { private readonly terrainHeightProvider: TerrainHeightProvider; private readonly camera: PerspectiveCamera; + private readonly parent: ControlsSystem private pitch: number = MathUtils.toRad(45); private yaw: number = MathUtils.toRad(0); private forwardKeyPressed: boolean = false; @@ -21,16 +22,19 @@ export default class FreeControlsNavigator extends ControlsNavigator { private yawMinusKeyPressed: boolean = false; private yawPlusKeyPressed: boolean = false; private pointerLocked: boolean = false; + private bikeMode: boolean = false; public constructor( element: HTMLElement, camera: PerspectiveCamera, - terrainHeightProvider: TerrainHeightProvider + terrainHeightProvider: TerrainHeightProvider, + parent: ControlsSystem ) { super(element); this.camera = camera; this.terrainHeightProvider = terrainHeightProvider; + this.parent = parent; this.addEventListeners(); } @@ -107,6 +111,9 @@ export default class FreeControlsNavigator extends ControlsNavigator { case 'KeyF': this.pitchPlusKeyPressed = true; break; + case 'KeyB': + this.parent.switchBikeMode(); + break; } } @@ -216,11 +223,18 @@ export default class FreeControlsNavigator extends ControlsNavigator { }; } + public setBikeMode(bikeMode: boolean): void { + this.bikeMode = bikeMode; + } + public update(deltaTime: number): void { const mat = this.camera.matrixWorld.values; const forwardDir = Vec3.normalize(new Vec3(mat[8], mat[9], mat[10])); const rightDir = Vec3.normalize(new Vec3(mat[0], mat[1], mat[2])); - const speed = this.fastMovementKeyPressed ? Config.FreeCameraSpeedFast : Config.FreeCameraSpeed; + const fastSpeed = this.bikeMode ? Config.BikeCameraSpeedFast : Config.FreeCameraSpeedFast; + const slowSpeed = this.bikeMode ? Config.BikeCameraSpeed : Config.FreeCameraSpeed; + + const speed = this.fastMovementKeyPressed ? fastSpeed : slowSpeed; let movementDelta = new Vec3(); if (this.forwardKeyPressed) { @@ -242,7 +256,12 @@ export default class FreeControlsNavigator extends ControlsNavigator { this.camera.position.z += movementDelta.z; const heightmapValue = this.getHeightmapValueAtPosition(this.camera.position.x, this.camera.position.z); - this.camera.position.y = Math.max(this.camera.position.y, heightmapValue + Config.MinFreeCameraHeight); + if (this.bikeMode) { + this.camera.position.y = heightmapValue + Config.FreeCameraBikeHeight; + } else { + this.camera.position.y = Math.max(this.camera.position.y, heightmapValue + Config.MinFreeCameraHeight); + } + this.camera.updateMatrix(); diff --git a/src/app/controls/GroundControlsNavigator.ts b/src/app/controls/GroundControlsNavigator.ts index d5f9756a..9bf65d36 100644 --- a/src/app/controls/GroundControlsNavigator.ts +++ b/src/app/controls/GroundControlsNavigator.ts @@ -4,7 +4,7 @@ import Vec3 from "~/lib/math/Vec3"; import Config from "../Config"; import MathUtils from "~/lib/math/MathUtils"; import CursorStyleSystem from "../systems/CursorStyleSystem"; -import {ControlsState} from "../systems/ControlsSystem"; +import ControlsSystem, {ControlsState} from "../systems/ControlsSystem"; import PerspectiveCamera from "~/lib/core/PerspectiveCamera"; import TerrainHeightProvider from "~/app/terrain/TerrainHeightProvider"; import FreeControlsNavigator from "~/app/controls/FreeControlsNavigator"; @@ -21,6 +21,7 @@ export default class GroundControlsNavigator extends ControlsNavigator { private readonly camera: PerspectiveCamera; private readonly cursorStyleSystem: CursorStyleSystem; private readonly terrainHeightProvider: TerrainHeightProvider; + private readonly parent: ControlsSystem; public target: Vec3 = new Vec3(); private direction: Vec3 = new Vec3(); public distance: number = 0; @@ -48,18 +49,21 @@ export default class GroundControlsNavigator extends ControlsNavigator { public switchToSlippy: boolean = false; private transitionYawFrom: number = 0; private transitionPitchFrom: number = 0; + private bikeMode: boolean = false; public constructor( element: HTMLElement, camera: PerspectiveCamera, cursorStyleSystem: CursorStyleSystem, - terrainHeightProvider: TerrainHeightProvider + terrainHeightProvider: TerrainHeightProvider, + parent: ControlsSystem ) { super(element); this.camera = camera; this.cursorStyleSystem = cursorStyleSystem; this.terrainHeightProvider = terrainHeightProvider; + this.parent = parent; this.addEventListeners(); } @@ -207,6 +211,9 @@ export default class GroundControlsNavigator extends ControlsNavigator { case 'KeyF': this.pitchMinusKeyPressed = true; break; + case 'KeyB': + this.parent.switchBikeMode(); + break; } } @@ -335,8 +342,13 @@ export default class GroundControlsNavigator extends ControlsNavigator { } } + public setBikeMode(bikeMode: boolean): void { + this.bikeMode = bikeMode; + this.updateCameraProjectionMatrix(); + } + private updateCameraProjectionMatrix(): void { - this.camera.near = 10; + this.camera.near = this.bikeMode ? Config.BikeCameraNear : Config.CameraNear; this.camera.far = 100000; this.camera.updateProjectionMatrix(); } diff --git a/src/app/systems/ControlsSystem.ts b/src/app/systems/ControlsSystem.ts index 5b7db3dc..0f0181d0 100644 --- a/src/app/systems/ControlsSystem.ts +++ b/src/app/systems/ControlsSystem.ts @@ -32,6 +32,8 @@ export enum NavigationMode { export default class ControlsSystem extends System { private readonly element: HTMLElement; public mode: NavigationMode = NavigationMode.Ground; + public bikeMode: boolean = false; + private switchBikeModeOnUpdate: boolean = false; private camera: PerspectiveCamera; private tick: number = 0; public target: Vec3 = new Vec3(); @@ -72,8 +74,8 @@ export default class ControlsSystem extends System { const cursorStyleSystem = this.systemManager.getSystem(CursorStyleSystem); const terrainHeightProvider = this.systemManager.getSystem(TerrainSystem).terrainHeightProvider; - this.groundNavigator = new GroundControlsNavigator(this.element, this.camera, cursorStyleSystem, terrainHeightProvider); - this.freeNavigator = new FreeControlsNavigator(this.element, this.camera, terrainHeightProvider); + this.groundNavigator = new GroundControlsNavigator(this.element, this.camera, cursorStyleSystem, terrainHeightProvider, this); + this.freeNavigator = new FreeControlsNavigator(this.element, this.camera, terrainHeightProvider, this); this.slippyNavigator = new SlippyControlsNavigator(this.element, this.camera, cursorStyleSystem, terrainHeightProvider); this.activeNavigator = this.slippyNavigator; @@ -182,6 +184,10 @@ export default class ControlsSystem extends System { } } + public switchBikeMode(): void { + this.switchBikeModeOnUpdate = true; + } + private mouseDownEvent(e: MouseEvent): void { e.preventDefault(); @@ -250,6 +256,21 @@ export default class ControlsSystem extends System { this.activeNavigator.update(deltaTime); + if (this.switchBikeModeOnUpdate) { + this.switchBikeModeOnUpdate = false; + + this.bikeMode = !this.bikeMode; + + this.freeNavigator.setBikeMode(this.bikeMode); + this.groundNavigator.setBikeMode(this.bikeMode); + + this.mode = NavigationMode.Free; + this.activeNavigator = this.freeNavigator; + this.groundNavigator.disable(); + this.freeNavigator.enable(); + this.freeNavigator.syncWithCamera(this.groundNavigator); + } + if (this.groundNavigator.switchToSlippy) { this.groundNavigator.switchToSlippy = false; diff --git a/src/app/ui/components/InfoModalPanel/InfoModalPanel.tsx b/src/app/ui/components/InfoModalPanel/InfoModalPanel.tsx index 23363fc7..23f955e4 100644 --- a/src/app/ui/components/InfoModalPanel/InfoModalPanel.tsx +++ b/src/app/ui/components/InfoModalPanel/InfoModalPanel.tsx @@ -29,6 +29,9 @@ const KeysConfig: {keys: JSX.Element; desc: string}[] = [ }, { keys: , desc: 'Toggle between ground camera mode (default) and free camera mode' + }, { + keys: , + desc: 'Toggle between bike and free camera mode' }, { keys: <> + , desc: 'Purge all loaded tiles'