Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add camera lag to spaceship #307

Merged
merged 4 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/ts/playground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { createHyperspaceTunnelDemo } from "./playgrounds/hyperspaceTunnel";
import { createDebugAssetsScene } from "./playgrounds/debugAssets";
import { createSpaceStationScene } from "./playgrounds/spaceStation";
import { createXrScene } from "./playgrounds/xr";
import { createFlightDemoScene } from "./playgrounds/flightDemo";

const canvas = document.getElementById("renderer") as HTMLCanvasElement;
canvas.width = window.innerWidth;
Expand Down Expand Up @@ -61,6 +62,9 @@ switch (requestedScene) {
case "xr":
scene = await createXrScene(engine);
break;
case "flightDemo":
scene = await createFlightDemoScene(engine);
break;
default:
scene = await createAutomaticLandingScene(engine);
}
Expand Down
98 changes: 98 additions & 0 deletions src/ts/playgrounds/flightDemo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// This file is part of Cosmos Journeyer
//
// Copyright (C) 2024 Barthélemy Paléologue <barth.paleologue@cosmosjourneyer.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import { AbstractEngine } from "@babylonjs/core/Engines/abstractEngine";
import { Scene } from "@babylonjs/core/scene";
import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import {
AssetsManager,
Color3,
DirectionalLight,
MeshBuilder,
PBRMetallicRoughnessMaterial,
SolidParticle,
SolidParticleSystem
} from "@babylonjs/core";
import { enablePhysics } from "./utils";
import { Objects } from "../assets/objects";
import { Textures } from "../assets/textures";
import { Sounds } from "../assets/sounds";
import { ShipControls } from "../spaceship/shipControls";
import { SpaceShipControlsInputs } from "../spaceship/spaceShipControlsInputs";

export async function createFlightDemoScene(engine: AbstractEngine): Promise<Scene> {
const scene = new Scene(engine);
scene.useRightHandedSystem = true;
scene.defaultCursor = "crosshair";

await enablePhysics(scene);

const assetsManager = new AssetsManager(scene);
Sounds.EnqueueTasks(assetsManager, scene);
Objects.EnqueueTasks(assetsManager, scene);
Textures.EnqueueTasks(assetsManager, scene);
await assetsManager.loadAsync();

const ship = ShipControls.CreateDefault(scene);

const camera = ship.getActiveCamera();
camera.minZ = 0.1;
camera.attachControl();

scene.activeCamera = camera;

SpaceShipControlsInputs.setEnabled(true);

const hemi = new HemisphericLight("hemi", Vector3.Up(), scene);
hemi.intensity = 1.0;

// Shape to follow
const box = MeshBuilder.CreateBox("box", { size: 50 }, scene);

//create solid particle system of stationery grey boxes to show movement of box and camera
const boxesSPS = new SolidParticleSystem("boxes", scene, { updatable: false });

const randRange = (min: number, max: number) => Math.random() * (max - min) + min;

const range = 5e3;

//add 400 boxes
boxesSPS.addShape(box, 10_000, {
positionFunction: (particle: SolidParticle) => {
particle.position = new Vector3(randRange(-1, 1), randRange(-1, 1), randRange(-1, 1)).scaleInPlace(range);
}
});

const mesh = boxesSPS.buildMesh();

const material = new PBRMetallicRoughnessMaterial("material", scene);
material.baseColor = new Color3(0.5, 0.5, 0.5);
material.metallic = 0.5;
material.roughness = 0.5;

mesh.material = material;

box.setEnabled(false);

scene.onBeforeRenderObservable.add(() => {
const deltaSeconds = engine.getDeltaTime() / 1000;
ship.update(deltaSeconds);
});

return scene;
}
44 changes: 32 additions & 12 deletions src/ts/spaceship/shipControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import { Scene } from "@babylonjs/core/scene";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math.vector";
import {
getForwardDirection,
getRightDirection,
Expand Down Expand Up @@ -45,7 +45,7 @@ import { CameraShakeAnimation } from "../uberCore/transforms/animations/cameraSh
import { Tools } from "@babylonjs/core/Misc/tools";
import { quickAnimation } from "../uberCore/transforms/animations/quickAnimation";
import { Observable } from "@babylonjs/core/Misc/observable";
import { lerpSmooth } from "../utils/math";
import { lerpSmooth, slerpSmoothToRef } from "../utils/math";
import { HasBoundingSphere } from "../architecture/hasBoundingSphere";
import { canEngageWarpDrive } from "./warpDriveUtils";

Expand All @@ -57,6 +57,8 @@ export class ShipControls implements Controls {
readonly thirdPersonCameraDefaultBeta = 3.14 / 2.2;
readonly thirdPersonCamera: ArcRotateCamera;

readonly thirdPersonCameraTransform: TransformNode;

readonly firstPersonCamera: FreeCamera;

private readonly cameraShakeAnimation: CameraShakeAnimation;
Expand Down Expand Up @@ -84,6 +86,8 @@ export class ShipControls implements Controls {
this.firstPersonCamera.parent = this.getTransform();
this.firstPersonCamera.position = new Vector3(0, 1.5, 8);

this.thirdPersonCameraTransform = new TransformNode("thirdPersonCameraTransform", scene);

this.thirdPersonCamera = new ArcRotateCamera(
"shipThirdPersonCamera",
this.thirdPersonCameraDefaultAlpha,
Expand All @@ -92,7 +96,7 @@ export class ShipControls implements Controls {
Vector3.Zero(),
scene
);
this.thirdPersonCamera.parent = this.getTransform();
this.thirdPersonCamera.parent = this.thirdPersonCameraTransform;
this.thirdPersonCamera.lowerRadiusLimit =
(1.2 *
Math.max(
Expand Down Expand Up @@ -303,19 +307,23 @@ export class ShipControls implements Controls {
const shipUp = getUpwardDirection(this.getTransform());
const shipRight = getRightDirection(this.getTransform());

const angularVelocity = spaceship.aggregate.body.getAngularVelocity();

const angularImpulse = Vector3.Zero();

const currentRoll = angularImpulse.dot(shipForward);
const authority = 0.7;

const currentRoll = angularVelocity.dot(shipForward);
const targetRoll = this.spaceship.maxRollSpeed * inputRoll;
angularImpulse.addInPlace(shipForward.scale(0.5 * (targetRoll - currentRoll)));
angularImpulse.addInPlace(shipForward.scale(authority * (targetRoll - currentRoll)));

const currentYaw = angularImpulse.dot(shipUp);
const currentYaw = angularVelocity.dot(shipUp);
const targetYaw = -this.spaceship.maxYawSpeed * inputRoll;
angularImpulse.addInPlace(shipUp.scale(0.5 * (targetYaw - currentYaw)));
angularImpulse.addInPlace(shipUp.scale(authority * (targetYaw - currentYaw)));

const currentPitch = angularImpulse.dot(shipRight);
const currentPitch = angularVelocity.dot(shipRight);
const targetPitch = -this.spaceship.maxPitchSpeed * inputPitch;
angularImpulse.addInPlace(shipRight.scale(0.5 * (targetPitch - currentPitch)));
angularImpulse.addInPlace(shipRight.scale(authority * (targetPitch - currentPitch)));

spaceship.aggregate.body.applyAngularImpulse(angularImpulse);
}
Expand All @@ -330,11 +338,23 @@ export class ShipControls implements Controls {
pitch(this.getTransform(), this.spaceship.maxPitchSpeed * this.rotationInertia.y * deltaSeconds);
}

this.targetFov = Tools.ToRadians(60 + 10 * spaceship.getThrottle());
this.thirdPersonCameraTransform.position =
this.getTransform().parent === null
? this.getTransform().position
: this.getTransform().getAbsolutePosition();
this.thirdPersonCameraTransform.rotationQuaternion = slerpSmoothToRef(
this.getTransform().absoluteRotationQuaternion,
this.thirdPersonCameraTransform.rotationQuaternion ?? Quaternion.Identity(),
0.3,
deltaSeconds,
this.thirdPersonCameraTransform.rotationQuaternion ?? Quaternion.Identity()
);
this.thirdPersonCameraTransform.computeWorldMatrix(true);

this.targetFov = Tools.ToRadians(60 + 10 * spaceship.getThrottle());
this.thirdPersonCamera.fov = lerpSmooth(this.thirdPersonCamera.fov, this.targetFov, 0.08, deltaSeconds);

this.getActiveCamera().getViewMatrix();
this.getActiveCamera().getViewMatrix(true);

return this.getTransform().getAbsolutePosition();
}
Expand All @@ -347,7 +367,7 @@ export class ShipControls implements Controls {

setSpaceship(ship: Spaceship) {
this.spaceship = ship;
this.thirdPersonCamera.parent = this.getTransform();
this.thirdPersonCamera.parent = this.thirdPersonCameraTransform;
this.firstPersonCamera.parent = this.getTransform();

this.spaceship.onFuelScoopStart.add(() => {
Expand Down
13 changes: 8 additions & 5 deletions src/ts/starSystem/starSystemController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
StellarObject
} from "../architecture/orbitalObject";
import { OrbitalObjectUtils } from "../architecture/orbitalObjectUtils";
import { ShipControls } from "../spaceship/shipControls";

export type PlanetarySystem = {
readonly planets: Planet[];
Expand Down Expand Up @@ -706,12 +707,12 @@ export class StarSystemController {
}

public applyFloatingOrigin() {
const controller = this.scene.getActiveControls();
if (controller.getTransform().getAbsolutePosition().length() > Settings.FLOATING_ORIGIN_THRESHOLD) {
const displacementTranslation = controller.getTransform().getAbsolutePosition().negate();
const controls = this.scene.getActiveControls();
if (controls.getTransform().getAbsolutePosition().length() > Settings.FLOATING_ORIGIN_THRESHOLD) {
const displacementTranslation = controls.getTransform().getAbsolutePosition().negate();
this.translateEverythingNow(displacementTranslation);
if (controller.getTransform().parent === null) {
translate(controller.getTransform(), displacementTranslation);
if (controls.getTransform().parent === null) {
translate(controls.getTransform(), displacementTranslation);
}
}
}
Expand All @@ -738,6 +739,8 @@ export class StarSystemController {
if (stellarObject instanceof Star) stellarObject.updateMaterial(deltaSeconds);
}

this.scene.activeCamera?.getViewMatrix(true);

postProcessManager.setCelestialBody(nearestBody);
postProcessManager.update(deltaSeconds);
}
Expand Down
27 changes: 24 additions & 3 deletions src/ts/utils/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import { Quaternion } from "@babylonjs/core/Maths/math.vector";

export function clamp(value: number, min: number, max: number) {
return Math.min(Math.max(value, min), max);
}
Expand Down Expand Up @@ -47,13 +49,32 @@ export function remap(value: number, from1: number, to1: number, from2: number,
* Frame-rate independent lerp based on Freya Holmer's tweet.
* @param a The start value
* @param b The target value
* @param halfLife The half-life of the lerp (in seconds)
* @param halfLifeSeconds The half-life of the lerp (in seconds)
* @param deltaSeconds The time delta (in seconds)
* @returns The interpolated value
* @see https://x.com/FreyaHolmer/status/1757836988495847568
*/
export function lerpSmooth(a: number, b: number, halfLife: number, deltaSeconds: number) {
return b + (a - b) * 2 ** (-deltaSeconds / halfLife);
export function lerpSmooth(a: number, b: number, halfLifeSeconds: number, deltaSeconds: number) {
return b + (a - b) * 2 ** (-deltaSeconds / halfLifeSeconds);
}

/**
* Frame-rate independent slerp based on frame-rate independent lerp.
* @param a The start quaternion
* @param b The target quaternion
* @param halfLifeSeconds The half-life of the slerp (in seconds)
* @param deltaSeconds The time delta (in seconds)
* @param ref The quaternion to store the result in
* @returns The interpolated quaternion
*/
export function slerpSmoothToRef(
a: Quaternion,
b: Quaternion,
halfLifeSeconds: number,
deltaSeconds: number,
ref: Quaternion
) {
return Quaternion.SlerpToRef(a, b, 2 ** (-deltaSeconds / halfLifeSeconds), ref);
}

export function gcd(a: number, b: number): number {
Expand Down
2 changes: 1 addition & 1 deletion src/ts/utils/solidPlume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class SolidPlume {

readonly direction = Axis.Z;

private particleSpeed = 10;
private particleSpeed = 50;

readonly recycledParticles: SolidParticle[] = [];

Expand Down
Loading