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'