diff --git a/.eslintrc.json b/.eslintrc.json
index a760820a1..6cc7a9715 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -13,10 +13,59 @@
"plugins": ["@typescript-eslint"],
"rules": {
"import/no-cycle": "error",
- "import/no-unresolved": "off",
+ "import/no-unresolved": "warn",
"@typescript-eslint/switch-exhaustiveness-check": "error",
- "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-inferrable-types": "warn",
"eqeqeq": "error",
- "camelcase": "warn"
+ "@typescript-eslint/naming-convention": [
+ "error",
+ {
+ "selector": "enumMember",
+ "format": ["UPPER_CASE"]
+ },
+ {
+ "selector": "memberLike",
+ "modifiers": ["public", "static"],
+ "format": ["PascalCase", "UPPER_CASE"],
+ "leadingUnderscore": "forbid"
+ },
+ {
+ "selector": "memberLike",
+ "modifiers": ["private", "static"],
+ "format": ["PascalCase", "UPPER_CASE"],
+ "leadingUnderscore": "forbid"
+ },
+ {
+ "selector": "typeLike",
+ "format": ["PascalCase"]
+ },
+ {
+ "selector": "variable",
+ "modifiers": ["exported", "const", "global"],
+ "format": ["PascalCase"],
+ "leadingUnderscore": "forbid"
+ },
+ {
+ "selector": "function",
+ "format": ["camelCase", "snake_case"],
+ "leadingUnderscore": "forbid"
+ },
+ {
+ "selector": "function",
+ "modifiers": ["exported", "global"],
+ "format": ["camelCase"],
+ "leadingUnderscore": "forbid"
+ },
+ {
+ "selector": "interface",
+ "format": ["PascalCase"],
+ "leadingUnderscore": "forbid"
+ },
+ {
+ "selector": "class",
+ "format": ["PascalCase"],
+ "leadingUnderscore": "forbid"
+ }
+ ]
}
}
diff --git a/package.json b/package.json
index 07fbfe420..624c92b78 100644
--- a/package.json
+++ b/package.json
@@ -50,7 +50,7 @@
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
},
- "version": "1.6.4",
+ "version": "1.7.0",
"description": "CosmosJourneyer",
"name": "cosmos-journeyer",
"scripts": {
diff --git a/src/asset/sound/318688__limitsnap_creations__rocket-thrust-effect.mp3 b/src/asset/sound/318688__limitsnap_creations__rocket-thrust-effect.mp3
new file mode 100644
index 000000000..dfed2fecb
Binary files /dev/null and b/src/asset/sound/318688__limitsnap_creations__rocket-thrust-effect.mp3 differ
diff --git a/src/asset/spaceship/wanderer.glb b/src/asset/spaceship/wanderer.glb
new file mode 100644
index 000000000..203681483
Binary files /dev/null and b/src/asset/spaceship/wanderer.glb differ
diff --git a/src/html/mainMenu.html b/src/html/mainMenu.html
index 709535d2b..263150fde 100644
--- a/src/html/mainMenu.html
+++ b/src/html/mainMenu.html
@@ -1,7 +1,7 @@
diff --git a/src/shaders/starfieldFragment.glsl b/src/shaders/starfieldFragment.glsl
index d62f5c60e..02073300b 100644
--- a/src/shaders/starfieldFragment.glsl
+++ b/src/shaders/starfieldFragment.glsl
@@ -48,7 +48,7 @@ void main() {
vec2 starfieldUV = vec2(0.0);
// if the pixel is at the far plane
- if (depth == 1.0) {
+ if (screenColor == vec4(0.0) && depth == 1.0) {
// get the starfield color
// get spherical coordinates uv for the starfield texture
starfieldUV = vec2(
diff --git a/src/styles/mainMenu.scss b/src/styles/mainMenu.scss
index 592c3a2ef..4d9fe2913 100644
--- a/src/styles/mainMenu.scss
+++ b/src/styles/mainMenu.scss
@@ -39,6 +39,16 @@
font-size: 1.5em;
padding: 10px;
transition: 0.2s ease-in-out;
+
+ a {
+ color: white;
+ transition: 0.1s;
+ text-decoration: underline solid rgba(255, 255, 255, 0.5);
+
+ &:hover {
+ text-decoration: underline solid white;
+ }
+ }
}
.rightPanel {
diff --git a/src/ts/assets.ts b/src/ts/assets.ts
index e4701bf70..ff6ffb375 100644
--- a/src/ts/assets.ts
+++ b/src/ts/assets.ts
@@ -46,6 +46,7 @@ import spaceship from "../asset/spaceship/spaceship2.glb";
import shipCarrier from "../asset/spacestation/shipcarrier.glb";
import banana from "../asset/banana/banana.glb";
import endeavorSpaceship from "../asset/spaceship/endeavour.glb";
+import wanderer from "../asset/spaceship/wanderer.glb";
import character from "../asset/character/character.glb";
import rock from "../asset/rock.glb";
import landingPad from "../asset/landingpad.glb";
@@ -65,6 +66,8 @@ import disableWarpDriveSound from "../asset/sound/204418__nhumphrey__large-engin
import acceleratingWarpDriveSound from "../asset/sound/539503__timbre__endless-acceleration.mp3";
import deceleratingWarpDriveSound from "../asset/sound/539503__timbre_endless-deceleration.mp3";
+import thrusterSound from "../asset/sound/318688__limitsnap_creations__rocket-thrust-effect.mp3";
+
import starMapBackgroundMusic from "../asset/sound/455855__andrewkn__wandering.mp3";
import { Texture } from "@babylonjs/core/Materials/Textures/texture";
@@ -84,7 +87,6 @@ import { createButterfly } from "./proceduralAssets/butterfly/butterfly";
import { createGrassBlade } from "./proceduralAssets/grass/grassBlade";
import { ButterflyMaterial } from "./proceduralAssets/butterfly/butterflyMaterial";
import { GrassMaterial } from "./proceduralAssets/grass/grassMaterial";
-import { Observable } from "@babylonjs/core/Misc/observable";
export class Assets {
static IS_READY = false;
@@ -115,6 +117,7 @@ export class Assets {
private static SPACESHIP: Mesh;
private static ENDEAVOR_SPACESHIP: Mesh;
+ private static WANDERER: Mesh;
private static SPACE_STATION: Mesh;
private static BANANA: Mesh;
private static CHARACTER: Mesh;
@@ -149,6 +152,8 @@ export class Assets {
public static ACCELERATING_WARP_DRIVE_SOUND: Sound;
public static DECELERATING_WARP_DRIVE_SOUND: Sound;
+ public static THRUSTER_SOUND: Sound;
+
public static STAR_MAP_BACKGROUND_MUSIC: Sound;
public static MAIN_MENU_BACKGROUND_MUSIC: Sound;
@@ -205,6 +210,17 @@ export class Assets {
console.log("Endeavor Spaceship loaded");
};
+ const wandererTask = Assets.MANAGER.addMeshTask("wandererTask", "", "", wanderer);
+ wandererTask.onSuccess = function (task: MeshAssetTask) {
+ Assets.WANDERER = task.loadedMeshes[0] as Mesh;
+
+ for (const mesh of Assets.WANDERER.getChildMeshes()) {
+ mesh.isVisible = false;
+ }
+
+ console.log("Wanderer loaded");
+ };
+
const spacestationTask = Assets.MANAGER.addMeshTask("spacestationTask", "", "", shipCarrier);
spacestationTask.onSuccess = function (task: MeshAssetTask) {
Assets.SPACE_STATION = task.loadedMeshes[0] as Mesh;
@@ -401,6 +417,18 @@ export class Assets {
console.log("Decelerating warp drive sound loaded");
};
+ const thrusterSoundTask = Assets.MANAGER.addBinaryFileTask("thrusterSoundTask", thrusterSound);
+ thrusterSoundTask.onSuccess = function (task) {
+ Assets.THRUSTER_SOUND = new Sound("ThrusterSound", task.data, scene);
+ Assets.THRUSTER_SOUND.updateOptions({
+ playbackRate: 1.0,
+ volume: 0.5,
+ loop: true
+ });
+
+ console.log("Thruster sound loaded");
+ };
+
const starMapBackgroundMusicTask = Assets.MANAGER.addBinaryFileTask("starMapBackgroundMusicTask", starMapBackgroundMusic);
starMapBackgroundMusicTask.onSuccess = function (task) {
Assets.STAR_MAP_BACKGROUND_MUSIC = new Sound("StarMapBackgroundMusic", task.data, scene, null, {
@@ -454,6 +482,10 @@ export class Assets {
return instance;
}
+ static CreateWandererInstance(): InstancedMesh {
+ return Assets.WANDERER.instantiateHierarchy(null, { doNotInstantiate: false }) as InstancedMesh;
+ }
+
static CreateSpaceStationInstance(): InstancedMesh {
return Assets.SPACE_STATION.instantiateHierarchy(null, { doNotInstantiate: false }) as InstancedMesh;
}
diff --git a/src/ts/audio/audioManager.ts b/src/ts/audio/audioManager.ts
new file mode 100644
index 000000000..5fa6438f7
--- /dev/null
+++ b/src/ts/audio/audioManager.ts
@@ -0,0 +1,24 @@
+import { AudioInstance } from "../utils/audioInstance";
+
+
+export class AudioManager {
+ private static ENABLED_MASK = 0b1111;
+
+ private static readonly SOUNDS: AudioInstance[] = [];
+
+ public static RegisterSound(sound: AudioInstance) {
+ this.SOUNDS.push(sound);
+ }
+
+ public static SetMask(mask: number) {
+ this.ENABLED_MASK = mask;
+ }
+
+ public static Update(deltaSeconds: number) {
+ this.SOUNDS.forEach(sound => {
+ const isSoundEnabled = (sound.mask & this.ENABLED_MASK) !== 0;
+ sound.setMaskFactor(isSoundEnabled ? 1 : 0);
+ sound.update(deltaSeconds);
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/ts/audio/audioMasks.ts b/src/ts/audio/audioMasks.ts
new file mode 100644
index 000000000..31a651199
--- /dev/null
+++ b/src/ts/audio/audioMasks.ts
@@ -0,0 +1,4 @@
+export const AudioMasks = {
+ STAR_SYSTEM_VIEW: 0b0001,
+ STAR_MAP_VIEW: 0b0010,
+}
\ No newline at end of file
diff --git a/src/ts/cosmosJourneyer.ts b/src/ts/cosmosJourneyer.ts
index 5584f9f1f..6b6f45fe3 100644
--- a/src/ts/cosmosJourneyer.ts
+++ b/src/ts/cosmosJourneyer.ts
@@ -23,7 +23,6 @@ import { Tools } from "@babylonjs/core/Misc/tools";
import { VideoRecorder } from "@babylonjs/core/Misc/videoRecorder";
import "@babylonjs/core/Misc/screenshotTools";
import { StarMap } from "./starmap/starMap";
-import { Scene } from "@babylonjs/core/scene";
import "@babylonjs/core/Physics/physicsEngineComponent";
import HavokPhysics from "@babylonjs/havok";
@@ -44,6 +43,8 @@ import { UniverseCoordinates } from "./saveFile/universeCoordinates";
import { View } from "./utils/view";
import { updateInputDevices } from "./inputs/devices";
import { Assets } from "./assets";
+import { AudioManager } from "./audio/audioManager";
+import { AudioMasks } from "./audio/audioMasks";
enum EngineState {
UNINITIALIZED,
@@ -84,7 +85,7 @@ export class CosmosJourneyer {
const activeControls = this.starSystemView.scene.getActiveController();
if (activeControls instanceof ShipControls) {
activeControls.spaceship.enableWarpDrive();
- activeControls.thirdPersonCamera.radius = 30;
+ activeControls.thirdPersonCamera.radius = ShipControls.BASE_CAMERA_RADIUS;
}
});
@@ -92,6 +93,7 @@ export class CosmosJourneyer {
this.starMap.detachControl();
this.starSystemView.attachControl();
this.activeView = this.starSystemView;
+ AudioManager.SetMask(AudioMasks.STAR_SYSTEM_VIEW);
this.mainMenu = new MainMenu(starSystemView);
this.mainMenu.onStartObservable.add(() => {
@@ -100,6 +102,7 @@ export class CosmosJourneyer {
this.starSystemView.getSpaceshipControls().spaceship.enableWarpDrive();
this.starSystemView.showUI();
this.starSystemView.ui.setEnabled(true);
+ this.starSystemView.ui.setTarget(this.starSystemView.getStarSystem().getClosestToScreenCenterOrbitalObject());
});
this.mainMenu.onLoadSaveObservable.add((saveData: SaveFileData) => {
@@ -178,14 +181,14 @@ export class CosmosJourneyer {
const havokInstance = await HavokPhysics();
console.log(`Havok initialized`);
- // Init starmap view
- const starMap = new StarMap(engine);
-
// Init star system view
const starSystemView = new StarSystemView(engine, havokInstance);
await starSystemView.initAssets();
+ // Init starmap view
+ const starMap = new StarMap(engine);
+
return new CosmosJourneyer(engine, starSystemView, starMap);
}
@@ -218,6 +221,7 @@ export class CosmosJourneyer {
this.engine.runRenderLoop(() => {
updateInputDevices();
+ AudioManager.Update(this.engine.getDeltaTime() / 1000);
if (this.isPaused()) return;
this.activeView.render();
@@ -231,8 +235,7 @@ export class CosmosJourneyer {
public toggleStarMap(): void {
if (this.activeView === this.starSystemView) {
this.starSystemView.unZoom(() => {
- this.starSystemView.stopBackgroundSounds();
- this.starMap.startBackgroundMusic();
+ AudioManager.SetMask(AudioMasks.STAR_MAP_VIEW);
this.activeView.detachControl();
this.starMap.attachControl();
@@ -242,9 +245,10 @@ export class CosmosJourneyer {
starMap.focusOnCurrentSystem();
});
} else {
- this.starMap.stopBackgroundMusic();
this.activeView.detachControl();
this.starSystemView.attachControl();
+
+ AudioManager.SetMask(AudioMasks.STAR_SYSTEM_VIEW);
this.activeView = this.starSystemView;
this.starSystemView.showUI();
}
diff --git a/src/ts/mainMenu/mainMenu.ts b/src/ts/mainMenu/mainMenu.ts
index e594ad700..17ebc4417 100644
--- a/src/ts/mainMenu/mainMenu.ts
+++ b/src/ts/mainMenu/mainMenu.ts
@@ -131,7 +131,10 @@ Math.trunc((Math.random() * 2 - 1) * 1000),
const version = document.getElementById("version");
if (version === null) throw new Error("#version does not exist!");
- version.textContent = `Alpha ${packageInfo.version}`;
+ // children a elements has the version number as textContent
+ const childLink = version.querySelector("a");
+ if (childLink === null) throw new Error("version link does not exist!");
+ childLink.textContent = `Alpha ${packageInfo.version}`;
this.version = version;
document.querySelectorAll("#menuItems li").forEach((li) => {
diff --git a/src/ts/planets/telluricPlanet/terrain/chunks/chunkTree.ts b/src/ts/planets/telluricPlanet/terrain/chunks/chunkTree.ts
index e747f6dc1..64e9e67ba 100644
--- a/src/ts/planets/telluricPlanet/terrain/chunks/chunkTree.ts
+++ b/src/ts/planets/telluricPlanet/terrain/chunks/chunkTree.ts
@@ -36,7 +36,7 @@ import { ChunkForge } from "./chunkForge";
/**
* A quadTree is defined recursively
*/
-type quadTree = quadTree[] | PlanetChunk;
+type QuadTree = QuadTree[] | PlanetChunk;
/**
* A ChunkTree is a structure designed to manage LOD using a quadtree
@@ -45,7 +45,7 @@ export class ChunkTree {
readonly minDepth: number; // minimum depth of the tree
readonly maxDepth: number; // maximum depth of the tree
- private tree: quadTree = [];
+ private tree: QuadTree = [];
private readonly rootChunkLength: number;
@@ -105,7 +105,7 @@ export class ChunkTree {
* @param tree the tree to explore
* @param f the function to apply on every chunk
*/
- public executeOnEveryChunk(f: (chunk: PlanetChunk) => void, tree: quadTree = this.tree): void {
+ public executeOnEveryChunk(f: (chunk: PlanetChunk) => void, tree: QuadTree = this.tree): void {
if (tree instanceof PlanetChunk) f(tree);
else for (const stem of tree) this.executeOnEveryChunk(f, stem);
}
@@ -115,12 +115,12 @@ export class ChunkTree {
* @param tree The tree to delete
* @param newChunks
*/
- private requestDeletion(tree: quadTree, newChunks: PlanetChunk[]): void {
+ private requestDeletion(tree: QuadTree, newChunks: PlanetChunk[]): void {
const chunksToDelete = this.getChunkList(tree);
this.deleteSemaphores.push(new DeleteSemaphore(newChunks, chunksToDelete));
}
- public getChunkList(tree: quadTree): PlanetChunk[] {
+ public getChunkList(tree: QuadTree): PlanetChunk[] {
const result: PlanetChunk[] = [];
this.executeOnEveryChunk((chunk) => result.push(chunk), tree);
return result;
@@ -140,20 +140,20 @@ export class ChunkTree {
this.tree = this.updateLODRecursively(observerPosition, chunkForge);
}
- private getAverageHeight(tree: quadTree): number {
+ private getAverageHeight(tree: QuadTree): number {
if (tree instanceof PlanetChunk) return tree.getAverageHeight();
else if (tree.length > 0) return 0.25 * (this.getAverageHeight(tree[0]) + this.getAverageHeight(tree[1]) + this.getAverageHeight(tree[2]) + this.getAverageHeight(tree[3]));
else return 0;
}
- private getMinAverageHeight(tree: quadTree): number {
+ private getMinAverageHeight(tree: QuadTree): number {
if (tree instanceof PlanetChunk) return tree.getAverageHeight();
else if (tree.length > 0)
return Math.min(this.getMinAverageHeight(tree[0]), this.getMinAverageHeight(tree[1]), this.getMinAverageHeight(tree[2]), this.getMinAverageHeight(tree[3]));
else return 0;
}
- private getMaxAverageHeight(tree: quadTree): number {
+ private getMaxAverageHeight(tree: QuadTree): number {
if (tree instanceof PlanetChunk) return tree.getAverageHeight();
else if (tree.length > 0)
return Math.max(this.getMaxAverageHeight(tree[0]), this.getMaxAverageHeight(tree[1]), this.getMaxAverageHeight(tree[2]), this.getMaxAverageHeight(tree[3]));
@@ -168,7 +168,7 @@ export class ChunkTree {
* @param walked The position of the current root relative to the absolute root
* @returns The updated tree
*/
- private updateLODRecursively(observerPositionW: Vector3, chunkForge: ChunkForge, tree: quadTree = this.tree, walked: number[] = []): quadTree {
+ private updateLODRecursively(observerPositionW: Vector3, chunkForge: ChunkForge, tree: QuadTree = this.tree, walked: number[] = []): QuadTree {
if (walked.length === this.maxDepth) return tree;
const nodeRelativePosition = getChunkSphereSpacePositionFromPath(walked, this.direction, this.rootChunkLength / 2, getRotationQuaternion(this.parent));
diff --git a/src/ts/spaceship/abstractThruster.ts b/src/ts/spaceship/abstractThruster.ts
index 98c4299ef..7cd3839c6 100644
--- a/src/ts/spaceship/abstractThruster.ts
+++ b/src/ts/spaceship/abstractThruster.ts
@@ -19,10 +19,8 @@ import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
import { Vector3 } from "@babylonjs/core/Maths/math";
import { Color3 } from "@babylonjs/core/Maths/math.color";
import { AbstractMesh, MeshBuilder } from "@babylonjs/core/Meshes";
-import { DirectionnalParticleSystem } from "../utils/particleSystem";
import { PhysicsAggregate } from "@babylonjs/core/Physics/v2/physicsAggregate";
-import { getDownwardDirection } from "../uberCore/transforms/basicTransform";
-import { LocalDirection } from "../uberCore/localDirections";
+import { SolidPlume } from "../utils/solidPlume";
export abstract class AbstractThruster {
readonly mesh: AbstractMesh;
@@ -33,21 +31,15 @@ export abstract class AbstractThruster {
readonly localNozzleDown: Vector3;
- readonly plume: DirectionnalParticleSystem;
+ readonly plume: SolidPlume;
readonly parentAggregate: PhysicsAggregate;
- readonly leverage: number;
-
- protected abstract maxAuthority: number;
-
- constructor(mesh: AbstractMesh, direction: Vector3, parentAggregate: PhysicsAggregate) {
+ protected constructor(mesh: AbstractMesh, direction: Vector3, parentAggregate: PhysicsAggregate) {
this.mesh = mesh;
- this.leverage = this.mesh.position.length();
-
this.localNozzleDown = direction;
- this.plume = new DirectionnalParticleSystem(mesh, this.localNozzleDown);
+ this.plume = new SolidPlume(mesh, mesh.getScene());
this.parentAggregate = parentAggregate;
@@ -66,46 +58,10 @@ export abstract class AbstractThruster {
return this.throttle;
}
- public setMaxAuthority(maxAuthority: number): void {
- this.maxAuthority = maxAuthority;
- }
-
- public getMaxAuthority(): number {
- return this.maxAuthority;
- }
-
- /**
- * Returns the theoretical authority of the thruster in the given direction between 0 and 1 (independent of throttle)
- * @param direction The direction (in local space)
- * @returns
- */
- public getAuthority01(direction: Vector3): number {
- return Math.max(0, Vector3.Dot(this.localNozzleDown.negate(), direction));
- }
-
- public getAuthorityAroundAxisNormalized(rotationAxis: Vector3): number {
- const thrusterPosition = this.mesh.position;
- const thrusterPositionOnAxis = rotationAxis.scale(Vector3.Dot(thrusterPosition, rotationAxis));
+ public update(deltaSeconds: number): void {
+ this.plume.update(deltaSeconds);
- const thrusterPositionToAxisNormalized = thrusterPosition.subtract(thrusterPositionOnAxis).normalize();
-
- const thrusterRotationAxis = Vector3.Cross(this.localNozzleDown.negate(), thrusterPositionToAxisNormalized);
- return Vector3.Dot(thrusterRotationAxis, rotationAxis);
- }
-
- public getRollAuthorityNormalized(): number {
- return this.getAuthorityAroundAxisNormalized(LocalDirection.FORWARD);
- }
-
- public getPitchAuthorityNormalized(): number {
- return this.getAuthorityAroundAxisNormalized(LocalDirection.RIGHT);
- }
-
- public update(): void {
- this.plume.emitRate = this.throttle * 1000;
- this.plume.setDirection(getDownwardDirection(this.parentAggregate.transformNode));
- //const parentAcceleration = this.parentAggregate.body.getL
- //this.plume.applyAcceleration(this.parent.acceleration.negate());
+ this.plume.setThrottle(this.throttle);
if (this.throttle > 0) {
this.helperMesh.scaling = new Vector3(0.8, 0.8, 0.8);
@@ -113,6 +69,4 @@ export abstract class AbstractThruster {
this.helperMesh.scaling = new Vector3(0.5, 0.5, 0.5);
}
}
-
- public abstract applyForce(): void;
}
diff --git a/src/ts/spaceship/mainThruster.ts b/src/ts/spaceship/mainThruster.ts
index abd119f24..bec1fa7a0 100644
--- a/src/ts/spaceship/mainThruster.ts
+++ b/src/ts/spaceship/mainThruster.ts
@@ -19,7 +19,6 @@ import { Vector3 } from "@babylonjs/core/Maths/math";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { AbstractThruster } from "./abstractThruster";
import { PhysicsAggregate } from "@babylonjs/core/Physics/v2/physicsAggregate";
-import { getDownwardDirection } from "../uberCore/transforms/basicTransform";
export class MainThruster extends AbstractThruster {
protected readonly maxAuthority = 3e3;
@@ -35,12 +34,4 @@ export class MainThruster extends AbstractThruster {
public updateThrottle(delta: number): void {
this.throttle = Math.max(Math.min(1, this.throttle + delta), 0);
}
-
- public applyForce(): void {
- const thrustDirection = getDownwardDirection(this.mesh);
- const force = thrustDirection.scale(this.maxAuthority * this.throttle);
-
- // make the ship spin (apply force at the position of the thruster then apply the same force at the center of mass in the opposite direction)
- this.parentAggregate.body.applyForce(force, this.parentAggregate.body.getObjectCenterWorld());
- }
}
diff --git a/src/ts/spaceship/rcsThruster.ts b/src/ts/spaceship/rcsThruster.ts
deleted file mode 100644
index cdccc6699..000000000
--- a/src/ts/spaceship/rcsThruster.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-// This file is part of Cosmos Journeyer
-//
-// Copyright (C) 2024 Barthélemy Paléologue
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-import { Vector3 } from "@babylonjs/core/Maths/math.vector";
-import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
-import { AbstractThruster } from "./abstractThruster";
-import { PhysicsAggregate } from "@babylonjs/core/Physics/v2/physicsAggregate";
-import { getDownwardDirection } from "../uberCore/transforms/basicTransform";
-
-export class RCSThruster extends AbstractThruster {
- protected override maxAuthority = 30;
-
- constructor(mesh: AbstractMesh, direction: Vector3, parentAggregate: PhysicsAggregate) {
- super(mesh, direction, parentAggregate);
-
- this.plume.maxSize = 0.3;
- this.plume.minSize = 0.3;
-
- this.plume.minLifeTime = 0.2;
- this.plume.maxLifeTime = 0.2;
- }
-
- public activate(): void {
- this.throttle = 1;
- }
-
- public setThrottle(throttle: number): void {
- if (throttle < 0 || throttle > 1) throw new Error("Throttle must be between 0 and 1");
- this.throttle = throttle;
- }
-
- public deactivate(): void {
- this.throttle = 0;
- }
-
- public applyForce(): void {
- // the nozzle is directed upward
- const thrustDirection = getDownwardDirection(this.mesh);
- const force = thrustDirection.scale(this.maxAuthority * this.throttle);
-
- this.parentAggregate.body.applyForce(force, this.mesh.getAbsolutePosition());
- }
-}
diff --git a/src/ts/spaceship/shipControls.ts b/src/ts/spaceship/shipControls.ts
index 1d5e87c25..7c804b0ad 100644
--- a/src/ts/spaceship/shipControls.ts
+++ b/src/ts/spaceship/shipControls.ts
@@ -17,7 +17,6 @@
import { Scene } from "@babylonjs/core/scene";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
-import { LocalDirection } from "../uberCore/localDirections";
import { getUpwardDirection, pitch, roll } from "../uberCore/transforms/basicTransform";
import { TransformNode } from "@babylonjs/core/Meshes";
import { Controls } from "../uberCore/controls";
@@ -38,6 +37,8 @@ export class ShipControls implements Controls {
private isCameraShaking = false;
+ static BASE_CAMERA_RADIUS = 60;
+
private baseFov: number;
private targetFov: number;
@@ -46,9 +47,9 @@ export class ShipControls implements Controls {
this.firstPersonCamera = new FreeCamera("firstPersonCamera", Vector3.Zero(), scene);
this.firstPersonCamera.parent = this.getTransform();
- this.firstPersonCamera.position = new Vector3(0, 1, 0);
+ this.firstPersonCamera.position = new Vector3(0, 1.2, 3.5);
- this.thirdPersonCamera = new ArcRotateCamera("thirdPersonCamera", -3.14 / 2, 3.14 / 2, 30, Vector3.Zero(), scene);
+ this.thirdPersonCamera = new ArcRotateCamera("thirdPersonCamera", -3.14 / 2, 3.14 / 2.2, ShipControls.BASE_CAMERA_RADIUS, Vector3.Zero(), scene);
this.thirdPersonCamera.parent = this.getTransform();
this.thirdPersonCamera.lowerRadiusLimit = 10;
this.thirdPersonCamera.upperRadiusLimit = 500;
@@ -63,6 +64,17 @@ export class ShipControls implements Controls {
this.spaceship.toggleWarpDrive();
});
+ SpaceShipControlsInputs.map.landing.on("complete", () => {
+ if (this.spaceship.getClosestWalkableObject() !== null) {
+ this.spaceship.engageLanding(null);
+ }
+ });
+
+ SpaceShipControlsInputs.map.throttleToZero.on("complete", () => {
+ this.spaceship.setMainEngineThrottle(0);
+ this.spaceship.getWarpDrive().increaseTargetThrottle(-this.spaceship.getWarpDrive().getTargetThrottle());
+ });
+
this.baseFov = this.thirdPersonCamera.fov;
this.targetFov = this.baseFov;
@@ -97,51 +109,25 @@ export class ShipControls implements Controls {
this.spaceship.update(deltaTime);
let [inputRoll, inputPitch] = SpaceShipControlsInputs.map.rollPitch.value;
- if(SpaceShipControlsInputs.map.ignorePointer.value > 0) {
+ if (SpaceShipControlsInputs.map.ignorePointer.value > 0) {
inputRoll *= 0;
inputPitch *= 0;
}
if (this.spaceship.getWarpDrive().isDisabled()) {
- for (const thruster of this.spaceship.mainThrusters) {
- thruster.updateThrottle(2 * deltaTime * SpaceShipControlsInputs.map.throttle.value * thruster.getAuthority01(LocalDirection.FORWARD));
- thruster.updateThrottle(2 * deltaTime * -SpaceShipControlsInputs.map.throttle.value * thruster.getAuthority01(LocalDirection.BACKWARD));
-
- thruster.updateThrottle(2 * deltaTime * SpaceShipControlsInputs.map.upDown.value * thruster.getAuthority01(LocalDirection.UP));
- thruster.updateThrottle(2 * deltaTime * -SpaceShipControlsInputs.map.upDown.value * thruster.getAuthority01(LocalDirection.DOWN));
-
- /*thruster.updateThrottle(2 * deltaTime * input.getXAxis() * thruster.getAuthority01(LocalDirection.LEFT));
- thruster.updateThrottle(2 * deltaTime * -input.getXAxis() * thruster.getAuthority01(LocalDirection.RIGHT));*/
- }
+ this.spaceship.increaseMainEngineThrottle(deltaTime * SpaceShipControlsInputs.map.throttle.value);
this.spaceship.aggregate.body.applyForce(
- getUpwardDirection(this.getTransform()).scale(9.8 * 10 * SpaceShipControlsInputs.map.upDown.value),
- this.spaceship.aggregate.body.getObjectCenterWorld()
+ getUpwardDirection(this.getTransform()).scale(9.8 * 10 * SpaceShipControlsInputs.map.upDown.value),
+ this.spaceship.aggregate.body.getObjectCenterWorld()
);
-
- if (SpaceShipControlsInputs.map.landing.state === "complete") {
- if (this.spaceship.getClosestWalkableObject() !== null) {
- this.spaceship.engageLanding(null);
- }
- }
-
- for (const rcsThruster of this.spaceship.rcsThrusters) {
- let throttle = 0;
-
- // rcs rotation contribution
- if (inputRoll < 0 && rcsThruster.getRollAuthorityNormalized() > 0.2) throttle = Math.max(throttle, Math.abs(inputRoll));
- else if (inputRoll > 0 && rcsThruster.getRollAuthorityNormalized() < -0.2) throttle = Math.max(throttle, Math.abs(inputRoll));
-
- if (inputPitch < 0 && rcsThruster.getPitchAuthorityNormalized() > 0.2) throttle = Math.max(throttle, Math.abs(inputPitch));
- else if (inputPitch > 0 && rcsThruster.getPitchAuthorityNormalized() < -0.2) throttle = Math.max(throttle, Math.abs(inputPitch));
-
- rcsThruster.setThrottle(throttle);
- }
} else {
+ this.spaceship.getWarpDrive().increaseTargetThrottle(deltaTime * SpaceShipControlsInputs.map.throttle.value);
+ }
+
+ if (!this.spaceship.isLanded()) {
roll(this.getTransform(), inputRoll * deltaTime);
pitch(this.getTransform(), inputPitch * deltaTime);
-
- this.spaceship.getWarpDrive().increaseTargetThrottle(deltaTime * SpaceShipControlsInputs.map.throttle.value);
}
// camera shake
diff --git a/src/ts/spaceship/spaceShipControlsInputs.ts b/src/ts/spaceship/spaceShipControlsInputs.ts
index bc70d2e04..e5a49875e 100644
--- a/src/ts/spaceship/spaceShipControlsInputs.ts
+++ b/src/ts/spaceship/spaceShipControlsInputs.ts
@@ -10,7 +10,7 @@ const keyboard = InputDevices.KEYBOARD;
const pointer = InputDevices.POINTER;
const landingAction = new Action({
- bindings: [keyboard.getControl("Space"), gamepad.getControl("A")]
+ bindings: [keyboard.getControl("KeyL"), gamepad.getControl("A")]
});
const landingInteraction = new PressInteraction(landingAction);
@@ -85,6 +85,12 @@ const ignorePointer = new Action({
bindings: [keyboard.getControl("AltLeft")]
});
+const throttleToZero = new Action({
+ bindings: [keyboard.getControl("KeyX")]
+});
+
+const throttleToZeroInteraction = new PressInteraction(throttleToZero);
+
export const SpaceShipControlsInputs = new InputMap<{
landing: PressInteraction,
upDown: Action,
@@ -92,7 +98,8 @@ export const SpaceShipControlsInputs = new InputMap<{
rollPitch: Action<[number, number]>,
toggleFlightAssist: PressInteraction,
toggleWarpDrive: PressInteraction,
- ignorePointer: Action
+ ignorePointer: Action,
+ throttleToZero: PressInteraction
}>("SpaceShipInputs", {
landing: landingInteraction,
upDown: upDownAction,
@@ -100,7 +107,8 @@ export const SpaceShipControlsInputs = new InputMap<{
rollPitch: rollPitch,
toggleFlightAssist: toggleFlightAssistInteraction,
toggleWarpDrive: toggleWarpDriveInteraction,
- ignorePointer: ignorePointer
+ ignorePointer: ignorePointer,
+ throttleToZero: throttleToZeroInteraction
});
InputMaps.push(SpaceShipControlsInputs);
\ No newline at end of file
diff --git a/src/ts/spaceship/spaceship.ts b/src/ts/spaceship/spaceship.ts
index 211cfbd05..d857fd2b4 100644
--- a/src/ts/spaceship/spaceship.ts
+++ b/src/ts/spaceship/spaceship.ts
@@ -18,9 +18,7 @@
import { Scene } from "@babylonjs/core/scene";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
-import { MainThruster } from "./mainThruster";
import { WarpDrive } from "./warpDrive";
-import { RCSThruster } from "./rcsThruster";
import { PhysicsAggregate } from "@babylonjs/core/Physics/v2/physicsAggregate";
import { IPhysicsCollisionEvent, PhysicsMotionType, PhysicsShapeType } from "@babylonjs/core/Physics/v2/IPhysicsEnginePlugin";
import { PhysicsShapeMesh } from "@babylonjs/core/Physics/v2/physicsShape";
@@ -41,6 +39,9 @@ import { LandingPad } from "../landingPad/landingPad";
import { PhysicsEngineV2 } from "@babylonjs/core/Physics/v2";
import { HyperSpaceTunnel } from "../utils/hyperSpaceTunnel";
import { AudioInstance } from "../utils/audioInstance";
+import { AudioManager } from "../audio/audioManager";
+import { MainThruster } from "./mainThruster";
+import { AudioMasks } from "../audio/audioMasks";
enum ShipState {
FLYING,
@@ -56,11 +57,11 @@ export class Spaceship implements Transformable {
private flightAssistEnabled = true;
- readonly mainThrusters: MainThruster[] = [];
- readonly rcsThrusters: RCSThruster[] = [];
-
private readonly warpDrive = new WarpDrive(false);
+ private mainEngineThrottle = 0;
+ private mainEngineTargetSpeed = 0;
+
private closestWalkableObject: Transformable | null = null;
private landingTarget: Transformable | null = null;
@@ -80,16 +81,19 @@ export class Spaceship implements Transformable {
private targetLandingPad: LandingPad | null = null;
+ private mainThrusters: MainThruster[] = [];
+
readonly enableWarpDriveSound: AudioInstance;
readonly disableWarpDriveSound: AudioInstance;
readonly acceleratingWarpDriveSound: AudioInstance;
readonly deceleratingWarpDriveSound: AudioInstance;
+ readonly thrusterSound: AudioInstance;
readonly onWarpDriveEnabled = new Observable();
readonly onWarpDriveDisabled = new Observable();
constructor(scene: Scene) {
- this.instanceRoot = Assets.CreateSpaceShipInstance();
+ this.instanceRoot = Assets.CreateWandererInstance();
setRotationQuaternion(this.instanceRoot, Quaternion.Identity());
this.aggregate = new PhysicsAggregate(
@@ -102,6 +106,11 @@ export class Spaceship implements Transformable {
scene
);
for (const child of this.instanceRoot.getChildMeshes()) {
+ if(child.name.includes("mainThruster")) {
+ const mainThruster = new MainThruster(child, getForwardDirection(this.instanceRoot).negate(), this.aggregate);
+ this.mainThrusters.push(mainThruster);
+ continue;
+ }
const childShape = new PhysicsShapeMesh(child as Mesh, scene);
childShape.filterMembershipMask = CollisionMask.DYNAMIC_OBJECTS;
childShape.filterCollideMask = CollisionMask.ENVIRONMENT;
@@ -116,31 +125,28 @@ export class Spaceship implements Transformable {
console.log("Collision");
if (collisionEvent.impulse < 0.8) return;
console.log(collisionEvent);
- //Assets.OuchSound.play();
});
- for (const child of this.instanceRoot.getChildMeshes()) {
- if (child.name.includes("mainThruster")) {
- console.log("Found main thruster");
- this.addMainThruster(child);
- } else if (child.name.includes("rcsThruster")) {
- console.log("Found rcs thruster");
- this.addRCSThruster(child);
- }
- }
-
this.warpTunnel = new WarpTunnel(this.getTransform(), scene);
this.hyperSpaceTunnel = new HyperSpaceTunnel(this.getTransform().getDirection(Axis.Z), scene);
this.hyperSpaceTunnel.setParent(this.getTransform());
this.hyperSpaceTunnel.setEnabled(false);
- this.enableWarpDriveSound = new AudioInstance(Assets.ENABLE_WARP_DRIVE_SOUND, Vector3.Zero(), this.getTransform());
- this.disableWarpDriveSound = new AudioInstance(Assets.DISABLE_WARP_DRIVE_SOUND, Vector3.Zero(), this.getTransform());
- this.acceleratingWarpDriveSound = new AudioInstance(Assets.ACCELERATING_WARP_DRIVE_SOUND, Vector3.Zero(), this.getTransform());
- this.deceleratingWarpDriveSound = new AudioInstance(Assets.DECELERATING_WARP_DRIVE_SOUND, Vector3.Zero(), this.getTransform());
+ this.enableWarpDriveSound = new AudioInstance(Assets.ENABLE_WARP_DRIVE_SOUND, AudioMasks.STAR_SYSTEM_VIEW, 1, true, this.getTransform());
+ this.disableWarpDriveSound = new AudioInstance(Assets.DISABLE_WARP_DRIVE_SOUND, AudioMasks.STAR_SYSTEM_VIEW, 1, true, this.getTransform());
+ this.acceleratingWarpDriveSound = new AudioInstance(Assets.ACCELERATING_WARP_DRIVE_SOUND, AudioMasks.STAR_SYSTEM_VIEW, 0, false, this.getTransform());
+ this.deceleratingWarpDriveSound = new AudioInstance(Assets.DECELERATING_WARP_DRIVE_SOUND, AudioMasks.STAR_SYSTEM_VIEW, 0, false, this.getTransform());
+ this.thrusterSound = new AudioInstance(Assets.THRUSTER_SOUND, AudioMasks.STAR_SYSTEM_VIEW, 0, false, this.getTransform());
+
+ AudioManager.RegisterSound(this.enableWarpDriveSound);
+ AudioManager.RegisterSound(this.disableWarpDriveSound);
+ AudioManager.RegisterSound(this.acceleratingWarpDriveSound);
+ AudioManager.RegisterSound(this.deceleratingWarpDriveSound);
+ AudioManager.RegisterSound(this.thrusterSound);
- this.acceleratingWarpDriveSound.setTargetVolume(0);
- this.deceleratingWarpDriveSound.setTargetVolume(0);
+ this.thrusterSound.sound.play();
+ this.acceleratingWarpDriveSound.sound.play();
+ this.deceleratingWarpDriveSound.sound.play();
this.scene = scene;
}
@@ -153,20 +159,6 @@ export class Spaceship implements Transformable {
return this.aggregate.transformNode;
}
- private addMainThruster(mesh: AbstractMesh) {
- const direction = mesh.getDirection(new Vector3(0, 1, 0));
- this.mainThrusters.push(new MainThruster(mesh, direction, this.aggregate));
- }
-
- private addRCSThruster(mesh: AbstractMesh) {
- const direction = mesh.getDirection(new Vector3(0, 1, 0));
- const thruster = new RCSThruster(mesh, direction, this.aggregate);
- this.rcsThrusters.push(thruster);
-
- //FIXME: this is temporary to balance rc thrust
- thruster.setMaxAuthority(thruster.getMaxAuthority() / thruster.leverage);
- }
-
public setEnabled(enabled: boolean, havokPlugin: HavokPlugin) {
this.instanceRoot.setEnabled(enabled);
setEnabledBody(this.aggregate.body, enabled, havokPlugin);
@@ -177,16 +169,13 @@ export class Spaceship implements Transformable {
}
public enableWarpDrive() {
- for (const thruster of this.mainThrusters) thruster.setThrottle(0);
- for (const thruster of this.rcsThrusters) thruster.deactivate();
this.warpDrive.enable();
this.aggregate.body.setMotionType(PhysicsMotionType.ANIMATED);
this.aggregate.body.setLinearVelocity(Vector3.Zero());
this.aggregate.body.setAngularVelocity(Vector3.Zero());
- if(!this.acceleratingWarpDriveSound.sound.isPlaying) this.acceleratingWarpDriveSound.sound.play();
- if(!this.deceleratingWarpDriveSound.sound.isPlaying) this.deceleratingWarpDriveSound.sound.play();
+ this.thrusterSound.setTargetVolume(0);
this.enableWarpDriveSound.sound.play();
this.onWarpDriveEnabled.notifyObservers();
@@ -205,6 +194,10 @@ export class Spaceship implements Transformable {
else this.disableWarpDrive();
}
+ public setMainEngineThrottle(throttle: number) {
+ this.mainEngineThrottle = throttle;
+ }
+
/**
* Returns a readonly interface to the warp drive of the ship.
* @returns A readonly interface to the warp drive of the ship.
@@ -232,7 +225,11 @@ export class Spaceship implements Transformable {
}
public getThrottle(): number {
- return this.warpDrive.isEnabled() ? this.warpDrive.getTargetThrottle() : this.mainThrusters[0].getThrottle();
+ return this.warpDrive.isEnabled() ? this.warpDrive.getTargetThrottle() : this.mainEngineThrottle;
+ }
+
+ public increaseMainEngineThrottle(delta: number) {
+ this.mainEngineThrottle = Math.max(-1, Math.min(1, this.mainEngineThrottle + delta));
}
public getClosestWalkableObject(): Transformable | null {
@@ -264,6 +261,10 @@ export class Spaceship implements Transformable {
this.landingTarget = null;
}
+ public isLanded(): boolean {
+ return this.state === ShipState.LANDED;
+ }
+
private land(deltaTime: number) {
if (this.targetLandingPad !== null) {
this.landOnPad(this.targetLandingPad, deltaTime);
@@ -328,6 +329,8 @@ export class Spaceship implements Transformable {
}
public update(deltaTime: number) {
+ this.mainEngineTargetSpeed = this.mainEngineThrottle * 500;
+
const warpSpeed = getForwardDirection(this.aggregate.transformNode).scale(this.warpDrive.getWarpSpeed());
const currentForwardSpeed = Vector3.Dot(warpSpeed, this.aggregate.transformNode.getDirection(Axis.Z));
@@ -337,12 +340,30 @@ export class Spaceship implements Transformable {
if (this.warpDrive.isEnabled()) this.warpTunnel.setThrottle(1 - 1 / (1.1 * (1 + 1e-7 * this.warpDrive.getWarpSpeed())));
else this.warpTunnel.setThrottle(0);
- for (const thruster of this.mainThrusters) thruster.update();
- for (const thruster of this.rcsThrusters) thruster.update();
-
if (this.warpDrive.isDisabled()) {
- for (const thruster of this.mainThrusters) thruster.applyForce();
- for (const thruster of this.rcsThrusters) thruster.applyForce();
+ const linearVelocity = this.aggregate.body.getLinearVelocity();
+ const forwardDirection = getForwardDirection(this.getTransform());
+ const forwardSpeed = Vector3.Dot(linearVelocity, forwardDirection);
+
+ const otherSpeed = linearVelocity.subtract(forwardDirection.scale(forwardSpeed));
+
+ if(this.mainEngineThrottle !== 0) this.thrusterSound.setTargetVolume(1);
+ else this.thrusterSound.setTargetVolume(0);
+
+ if(forwardSpeed < this.mainEngineTargetSpeed) {
+ this.aggregate.body.applyForce(forwardDirection.scale(3000), this.aggregate.body.getObjectCenterWorld());
+ this.mainThrusters.forEach(thruster => {
+ thruster.setThrottle(this.mainEngineThrottle);
+ });
+ } else {
+ this.aggregate.body.applyForce(forwardDirection.scale(-3000), this.aggregate.body.getObjectCenterWorld());
+ this.mainThrusters.forEach(thruster => {
+ thruster.setThrottle(0);
+ });
+ }
+
+ // damp other speed
+ this.aggregate.body.applyForce(otherSpeed.scale(-10), this.aggregate.body.getObjectCenterWorld());
if (this.closestWalkableObject !== null) {
const gravityDir = this.closestWalkableObject.getTransform().getAbsolutePosition().subtract(this.getTransform().getAbsolutePosition()).normalize();
@@ -352,8 +373,14 @@ export class Spaceship implements Transformable {
this.acceleratingWarpDriveSound.setTargetVolume(0);
this.deceleratingWarpDriveSound.setTargetVolume(0);
} else {
+ this.mainThrusters.forEach(thruster => {
+ thruster.setThrottle(0);
+ });
+
translate(this.getTransform(), warpSpeed.scale(deltaTime));
+ this.thrusterSound.setTargetVolume(0);
+
if (currentForwardSpeed < this.warpDrive.getWarpSpeed()) {
this.acceleratingWarpDriveSound.setTargetVolume(1);
this.deceleratingWarpDriveSound.setTargetVolume(0);
@@ -363,10 +390,9 @@ export class Spaceship implements Transformable {
}
}
- this.enableWarpDriveSound.update(deltaTime);
- this.disableWarpDriveSound.update(deltaTime);
- this.acceleratingWarpDriveSound.update(deltaTime);
- this.deceleratingWarpDriveSound.update(deltaTime);
+ this.mainThrusters.forEach(thruster => {
+ thruster.update(deltaTime);
+ });
if (this.flightAssistEnabled) {
this.aggregate.body.setAngularDamping(0.9);
diff --git a/src/ts/spaceship/warpDrive.ts b/src/ts/spaceship/warpDrive.ts
index bcd72c470..49a71ad6a 100644
--- a/src/ts/spaceship/warpDrive.ts
+++ b/src/ts/spaceship/warpDrive.ts
@@ -116,7 +116,7 @@ export class WarpDrive implements ReadonlyWarpDrive {
*/
private state = WarpDriveState.DISABLED;
- private static MIN_SPEED = 500;
+ private static MIN_SPEED = 2000;
constructor(enabledByDefault = false) {
this.state = enabledByDefault ? WarpDriveState.ENABLED : WarpDriveState.DISABLED;
diff --git a/src/ts/spaceshipExtended/thruster.ts b/src/ts/spaceshipExtended/thruster.ts
index 76ed31c37..9f542af03 100644
--- a/src/ts/spaceshipExtended/thruster.ts
+++ b/src/ts/spaceshipExtended/thruster.ts
@@ -20,13 +20,13 @@ import { Mesh, TransformNode } from "@babylonjs/core/Meshes";
import { PhysicsAggregate } from "@babylonjs/core/Physics/v2/physicsAggregate";
import { PhysicsShapeBox } from "@babylonjs/core/Physics/v2/physicsShape";
import { Scene } from "@babylonjs/core/scene";
-import { DirectionnalParticleSystem } from "../utils/particleSystem";
+import { DirectionalParticleSystem } from "../utils/particleSystem";
export class Thruster {
private mesh: Mesh;
private torque: Vector3;
- private particleSystem: DirectionnalParticleSystem;
+ private particleSystem: DirectionalParticleSystem;
private throttle = 0;
@@ -34,7 +34,7 @@ export class Thruster {
constructor(mesh: Mesh) {
this.mesh = mesh;
- this.particleSystem = new DirectionnalParticleSystem(mesh, this.getThrustDirection().negateInPlace());
+ this.particleSystem = new DirectionalParticleSystem(mesh, this.getThrustDirection().negateInPlace());
const minY = this.mesh.getBoundingInfo().boundingBox.extendSize.y;
this.particleSystem.minEmitBox = new Vector3(-0.8, -minY, -0.8);
diff --git a/src/ts/spacestation/spaceStation.ts b/src/ts/spacestation/spaceStation.ts
index 2a17f7b8c..c92202142 100644
--- a/src/ts/spacestation/spaceStation.ts
+++ b/src/ts/spacestation/spaceStation.ts
@@ -155,7 +155,7 @@ export class SpaceStation implements OrbitalObject, Cullable {
}
public getBoundingRadius(): number {
- return 1e3;
+ return 2e3;
}
getTypeName(): string {
diff --git a/src/ts/starSystem/starSystemModel.ts b/src/ts/starSystem/starSystemModel.ts
index c890ff503..5109144a6 100644
--- a/src/ts/starSystem/starSystemModel.ts
+++ b/src/ts/starSystem/starSystemModel.ts
@@ -23,12 +23,12 @@ import { generateName } from "../utils/nameGenerator";
import { SystemSeed } from "../utils/systemSeed";
enum GenerationSteps {
- Name,
- NbStars = 20,
- GenerateStars = 21,
- NbPlanets = 30,
- GeneratePlanets = 200,
- ChoosePlanetType = 200
+ NAME,
+ NB_STARS = 20,
+ GENERATE_STARS = 21,
+ NB_PLANETS = 30,
+ GENERATE_PLANETS = 200,
+ CHOOSE_PLANET_TYPE = 200
}
export class StarSystemModel {
@@ -41,7 +41,7 @@ export class StarSystemModel {
this.seed = seed;
this.rng = seededSquirrelNoise(this.seed.hash);
- this.name = generateName(this.rng, GenerationSteps.Name);
+ this.name = generateName(this.rng, GenerationSteps.NAME);
}
setName(name: string) {
@@ -59,12 +59,12 @@ export class StarSystemModel {
getNbPlanets(): number {
if (this.getBodyTypeOfStar(0) === BodyType.BLACK_HOLE) return 0; //Fixme: will not apply when more than one star
- return randRangeInt(0, 7, this.rng, GenerationSteps.NbPlanets);
+ return randRangeInt(0, 7, this.rng, GenerationSteps.NB_PLANETS);
}
public getStarSeed(index: number) {
if (index > this.getNbStars()) throw new Error("Star out of bound! " + index);
- return centeredRand(this.rng, GenerationSteps.GenerateStars + index) * Settings.SEED_HALF_RANGE;
+ return centeredRand(this.rng, GenerationSteps.GENERATE_STARS + index) * Settings.SEED_HALF_RANGE;
}
/**
@@ -76,18 +76,18 @@ export class StarSystemModel {
if (index > this.getNbStars()) throw new Error("Star out of bound! " + index);
// percentages are taken from https://physics.stackexchange.com/questions/442154/how-common-are-neutron-stars
- if (uniformRandBool(0.0006, this.rng, GenerationSteps.GenerateStars + index)) return BodyType.BLACK_HOLE;
- if (uniformRandBool(0.0026, this.rng, GenerationSteps.GenerateStars + index)) return BodyType.NEUTRON_STAR;
+ if (uniformRandBool(0.0006, this.rng, GenerationSteps.GENERATE_STARS + index)) return BodyType.BLACK_HOLE;
+ if (uniformRandBool(0.0026, this.rng, GenerationSteps.GENERATE_STARS + index)) return BodyType.NEUTRON_STAR;
return BodyType.STAR;
}
public getBodyTypeOfPlanet(index: number) {
- if (uniformRandBool(0.5, this.rng, GenerationSteps.ChoosePlanetType + index)) return BodyType.TELLURIC_PLANET;
+ if (uniformRandBool(0.5, this.rng, GenerationSteps.CHOOSE_PLANET_TYPE + index)) return BodyType.TELLURIC_PLANET;
return BodyType.GAS_PLANET;
}
public getPlanetSeed(index: number) {
- return centeredRand(this.rng, GenerationSteps.GeneratePlanets + index) * Settings.SEED_HALF_RANGE;
+ return centeredRand(this.rng, GenerationSteps.GENERATE_PLANETS + index) * Settings.SEED_HALF_RANGE;
}
}
diff --git a/src/ts/starSystem/starSystemView.ts b/src/ts/starSystem/starSystemView.ts
index 3de9a0cf4..8ea1016ba 100644
--- a/src/ts/starSystem/starSystemView.ts
+++ b/src/ts/starSystem/starSystemView.ts
@@ -83,7 +83,7 @@ export class StarSystemView implements View {
StarSystemView.UN_ZOOM_ANIMATION.setKeys([
{
frame: 0,
- value: 30
+ value: ShipControls.BASE_CAMERA_RADIUS
},
{
frame: 30,
@@ -225,6 +225,7 @@ export class StarSystemView implements View {
Assets.GRASS_MATERIAL.update(starSystem.stellarObjects, this.scene.getActiveController().getTransform().getAbsolutePosition(), deltaSeconds);
this.chunkForge.update();
+
starSystem.update(deltaSeconds, this.chunkForge);
if (this.spaceshipControls === null) throw new Error("Spaceship controls is null");
@@ -299,8 +300,7 @@ export class StarSystemView implements View {
shipControls.spaceship.warpTunnel.setThrottle(0);
shipControls.spaceship.setEnabled(false, this.havokPlugin);
- shipControls.spaceship.acceleratingWarpDriveSound.setTargetVolume(0);
- shipControls.spaceship.deceleratingWarpDriveSound.setTargetVolume(0);
+ this.stopBackgroundSounds();
}
switchToDefaultControls() {
@@ -311,8 +311,7 @@ export class StarSystemView implements View {
characterControls.getTransform().setEnabled(false);
shipControls.spaceship.warpTunnel.setThrottle(0);
shipControls.spaceship.setEnabled(false, this.havokPlugin);
- shipControls.spaceship.acceleratingWarpDriveSound.setTargetVolume(0);
- shipControls.spaceship.deceleratingWarpDriveSound.setTargetVolume(0);
+ this.stopBackgroundSounds();
this.scene.setActiveController(defaultControls);
setRotationQuaternion(defaultControls.getTransform(), getRotationQuaternion(shipControls.getTransform()).clone());
@@ -320,8 +319,9 @@ export class StarSystemView implements View {
}
stopBackgroundSounds() {
- this.spaceshipControls?.spaceship.acceleratingWarpDriveSound.muteInstantly();
- this.spaceshipControls?.spaceship.deceleratingWarpDriveSound.muteInstantly();
+ this.spaceshipControls?.spaceship.acceleratingWarpDriveSound.setTargetVolume(0);
+ this.spaceshipControls?.spaceship.deceleratingWarpDriveSound.setTargetVolume(0);
+ this.spaceshipControls?.spaceship.thrusterSound.setTargetVolume(0);
}
/**
diff --git a/src/ts/starmap/starMap.ts b/src/ts/starmap/starMap.ts
index f3ffb2272..f4ef6e888 100644
--- a/src/ts/starmap/starMap.ts
+++ b/src/ts/starmap/starMap.ts
@@ -52,11 +52,16 @@ import { NeutronStarModel } from "../stellarObjects/neutronStar/neutronStarModel
import { View } from "../utils/view";
import { Assets } from "../assets";
import { syncCamera } from "../utils/cameraSyncing";
+import { AudioInstance } from "../utils/audioInstance";
+import { AudioManager } from "../audio/audioManager";
+import { AudioMasks } from "../audio/audioMasks";
export class StarMap implements View {
readonly scene: Scene;
private readonly controls: DefaultControls;
+ private readonly backgroundMusic: AudioInstance;
+
private rotationAnimation: TransformRotationAnimation | null = null;
private translationAnimation: TransformTranslationAnimation | null = null;
@@ -124,6 +129,10 @@ export class StarMap implements View {
this.controls.getActiveCamera().attachControl();
+ this.backgroundMusic = new AudioInstance(Assets.STAR_MAP_BACKGROUND_MUSIC, AudioMasks.STAR_MAP_VIEW, 1, false, null);
+ AudioManager.RegisterSound(this.backgroundMusic);
+ this.backgroundMusic.sound.play();
+
this.starMapUI = new StarMapUI(engine);
this.starMapUI.warpButton.onPointerClickObservable.add(() => {
@@ -509,14 +518,6 @@ export class StarMap implements View {
this.starMapUI.setHoveredStarSystemMesh(null);
}
- startBackgroundMusic() {
- Assets.STAR_MAP_BACKGROUND_MUSIC.play();
- }
-
- stopBackgroundMusic() {
- Assets.STAR_MAP_BACKGROUND_MUSIC.stop();
- }
-
public focusOnCurrentSystem(skipAnimation = false) {
console.log("focus on current system");
if (this.currentSystemSeed === null) return console.warn("No current system seed!");
diff --git a/src/ts/ui/objectOverlay.ts b/src/ts/ui/objectOverlay.ts
index 8412943fc..f71358ed1 100644
--- a/src/ts/ui/objectOverlay.ts
+++ b/src/ts/ui/objectOverlay.ts
@@ -36,7 +36,7 @@ export class ObjectOverlay {
readonly etaText: TextBlock;
readonly object: OrbitalObject;
- private lastDistance: number = 0;
+ private lastDistance = 0;
static readonly WIDTH = 300;
diff --git a/src/ts/ui/systemUI.ts b/src/ts/ui/systemUI.ts
index 3c6f72504..4654db05b 100644
--- a/src/ts/ui/systemUI.ts
+++ b/src/ts/ui/systemUI.ts
@@ -23,7 +23,6 @@ import { OrbitalObject } from "../architecture/orbitalObject";
import { Engine } from "@babylonjs/core/Engines/engine";
import { FreeCamera } from "@babylonjs/core/Cameras/freeCamera";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
-import { Assets } from "../assets";
export class SystemUI {
readonly scene: Scene;
diff --git a/src/ts/utils/audioInstance.ts b/src/ts/utils/audioInstance.ts
index 499a49520..312e8bafb 100644
--- a/src/ts/utils/audioInstance.ts
+++ b/src/ts/utils/audioInstance.ts
@@ -6,7 +6,7 @@ import { TransformNode } from "@babylonjs/core/Meshes";
export class AudioInstance {
readonly sound: Sound;
- private targetVolume = 1;
+ private targetVolume;
private targetPlaybackSpeed = 1;
private blendSpeed = 0.5;
@@ -20,19 +20,34 @@ export class AudioInstance {
private parent: TransformNode | null;
- constructor(baseSound: Sound, localPosition: Vector3, parent: TransformNode | null) {
+ private maskFactor = 1;
+
+ private targetMaskFactor = 1;
+
+ private readonly isPonctual: boolean;
+
+ readonly mask: number;
+
+ constructor(baseSound: Sound, mask: number, initialTargetVolume: number, isPonctual: boolean, parent: TransformNode | null) {
const clonedSound = baseSound.clone();
if (clonedSound === null) throw new Error("Cloned sound was null!");
this.sound = clonedSound;
+ this.mask = mask;
+
+ this.targetVolume = initialTargetVolume;
+
this.volumeMultiplier = baseSound.getVolume();
this.playbackSpeedMultiplier = baseSound.getPlaybackRate();
this.sound.updateOptions({
- playbackRate: this.playbackSpeedMultiplier
+ playbackRate: this.playbackSpeedMultiplier * this.targetPlaybackSpeed,
+ volume: this.targetVolume * this.volumeMultiplier * this.maskFactor
});
- this.localPosition = localPosition;
+ this.isPonctual = isPonctual;
+
+ this.localPosition = Vector3.Zero();
this.parent = parent;
}
@@ -53,26 +68,31 @@ export class AudioInstance {
this.blendSpeed = speed;
}
- muteInstantly() {
- this.sound.setVolume(0);
+ setMaskFactor(factor: number) {
+ this.targetMaskFactor = factor;
}
update(deltaSeconds: number) {
- if (this.blendSpeed === 0) {
- this.sound.setVolume(this.targetVolume * this.volumeMultiplier);
- this.sound.setPlaybackRate(this.targetPlaybackSpeed);
- } else {
- this.sound.setVolume(moveTowards(this.sound.getVolume(), this.targetVolume * this.volumeMultiplier * this.spatialVolumeMultiplier, this.blendSpeed * deltaSeconds));
- this.sound.setPlaybackRate(moveTowards(this.sound.getPlaybackRate(), this.targetPlaybackSpeed * this.playbackSpeedMultiplier, this.blendSpeed * deltaSeconds));
+ this.maskFactor = moveTowards(this.maskFactor, this.targetMaskFactor, this.blendSpeed * deltaSeconds);
+ if (this.maskFactor === 0) {
+ this.sound.stop();
+ } else if (!this.sound.isPlaying && !this.isPonctual) {
+ this.sound.play();
}
- if (this.parent !== null) {
+ this.sound.setVolume(
+ moveTowards(this.sound.getVolume(), this.targetVolume * this.volumeMultiplier * this.spatialVolumeMultiplier * this.maskFactor, this.blendSpeed * deltaSeconds)
+ );
+ this.sound.setPlaybackRate(moveTowards(this.sound.getPlaybackRate(), this.targetPlaybackSpeed * this.playbackSpeedMultiplier, this.blendSpeed * deltaSeconds));
+
+
+ /*if (this.parent !== null) {
const worldPosition = Vector3.TransformCoordinates(this.localPosition, this.parent.getWorldMatrix());
const camera = this.parent.getScene().activeCamera;
- if(camera === null) throw new Error("No active camera");
+ if (camera === null) throw new Error("No active camera");
const worldCameraPosition = Vector3.TransformCoordinates(camera.position, this.parent.getWorldMatrix());
const distance = Vector3.Distance(worldCameraPosition, worldPosition);
this.spatialVolumeMultiplier = 1 / (1 + 0.01 * distance);
- }
+ }*/
}
}
diff --git a/src/ts/utils/parseToStrings.ts b/src/ts/utils/parseToStrings.ts
index 0e8854563..7ff8e8d47 100644
--- a/src/ts/utils/parseToStrings.ts
+++ b/src/ts/utils/parseToStrings.ts
@@ -63,7 +63,7 @@ export function parsePercentageFrom01(percentage01: number): string {
return `${(percentage01 * 100).toFixed(0)}%`;
}
-export const alphabet = "abcdefghijklmnopqrstuvwxyz";
+export const Alphabet = "abcdefghijklmnopqrstuvwxyz";
export function starName(baseName: string, index: number): string {
- return `${baseName} ${alphabet[index].toUpperCase()}`;
+ return `${baseName} ${Alphabet[index].toUpperCase()}`;
}
diff --git a/src/ts/utils/particleSystem.ts b/src/ts/utils/particleSystem.ts
index f7f2e7268..ac38aea69 100644
--- a/src/ts/utils/particleSystem.ts
+++ b/src/ts/utils/particleSystem.ts
@@ -26,7 +26,7 @@ function randomNumber(min: number, max: number): number {
return Math.random() * (max - min) + min;
}
-export class DirectionnalParticleSystem extends ParticleSystem {
+export class DirectionalParticleSystem extends ParticleSystem {
private direction: Vector3;
readonly emitter: AbstractMesh;
@@ -47,14 +47,13 @@ export class DirectionnalParticleSystem extends ParticleSystem {
this.particleTexture.hasAlpha = true;
this.emitter = mesh;
- this.minSize = 0.6;
- this.maxSize = 0.7;
+ this.minSize = 1.6;
+ this.maxSize = 1.7;
this.minLifeTime = 0.5;
this.maxLifeTime = 0.6;
this.minEmitPower = 0;
this.maxEmitPower = 0;
this.updateSpeed = 0.005;
- this.forceDepthWrite = true;
this.color1 = new Color4(0.5, 0.5, 0.5, 1);
this.color2 = new Color4(0.5, 0.5, 0.5, 1);
this.colorDead = new Color4(0, 0, 0, 0);
diff --git a/src/ts/utils/solidPlume.ts b/src/ts/utils/solidPlume.ts
new file mode 100644
index 000000000..a5f1a2f6f
--- /dev/null
+++ b/src/ts/utils/solidPlume.ts
@@ -0,0 +1,149 @@
+import { SolidParticleSystem } from "@babylonjs/core/Particles/solidParticleSystem";
+import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
+import { Scene } from "@babylonjs/core/scene";
+import { Axis } from "@babylonjs/core/Maths/math.axis";
+import { Vector3 } from "@babylonjs/core/Maths/math.vector";
+import { SolidParticle } from "@babylonjs/core/Particles/solidParticle";
+import { Scalar } from "@babylonjs/core/Maths/math.scalar";
+import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
+import { Color3, Color4 } from "@babylonjs/core/Maths/math.color";
+import { TransformNode } from "@babylonjs/core/Meshes";
+
+export class SolidPlume {
+ static TUNNEL_LENGTH = 3;
+ static MAX_NB_PARTICLES = 3000;
+
+ targetNbParticles = 0;
+ nbParticles = 0;
+
+ direction = Axis.Z;
+
+ recycledParticles: SolidParticle[] = [];
+
+ SPS: SolidParticleSystem;
+
+ private nbSubTimeSteps = 5;
+ private subTimeStep = 0;
+
+ constructor(engineAnchor: TransformNode, scene: Scene) {
+ this.SPS = new SolidParticleSystem("SPS", scene);
+ const poly = MeshBuilder.CreatePolyhedron("p", { type: 1 });
+ this.SPS.addShape(poly, SolidPlume.MAX_NB_PARTICLES);
+
+ // trying to get rid of polygon
+ // poly.setEnabled(false);
+ // poly.visibility = 0;
+ poly.dispose(); //dispose of original model poly
+
+ this.SPS.buildMesh(); // finally builds and displays the SPS mesh
+ this.SPS.isAlwaysVisible = true;
+
+ const scaling = new Vector3(0.01, 0.01, 0.1);
+
+ // initiate particles function
+ this.SPS.initParticles = () => {
+ for (let p = 0; p < this.SPS.nbParticles; p++) {
+ const particle = this.SPS.particles[p];
+ this.initParticle(particle);
+ if (this.nbParticles >= this.targetNbParticles) {
+ // particle.alive = false;
+ particle.isVisible = false;
+ this.recycledParticles.push(particle);
+ // console.log('set particle', particle.name, 'as not alive');
+ } else {
+ this.nbParticles++;
+ }
+ particle.position.z = Scalar.RandomRange(0, SolidPlume.TUNNEL_LENGTH);
+ }
+ };
+
+ //Update SPS mesh
+ this.SPS.initParticles();
+ this.SPS.setParticles();
+
+ const mat = new StandardMaterial("mat", scene);
+ mat.emissiveColor = new Color3(1, 1, 1);
+ mat.disableLighting = true;
+ this.SPS.mesh.material = mat;
+
+ this.SPS.updateParticle = (particle) => {
+ // console.log('particle', particle.name, 'alive', particle.alive);
+ if (!particle.alive) return particle;
+ if (!particle.isVisible) return particle;
+ particle.position.addInPlace(particle.velocity.scale(10 * this.subTimeStep));
+
+ const localZ = particle.position.z;
+
+ // if particle has gone too far, recycle or reset depending on current need
+ if (localZ > SolidPlume.TUNNEL_LENGTH) {
+ if (this.nbParticles <= this.targetNbParticles) {
+ this.initParticle(particle);
+ } else {
+ this.recycledParticles.push(particle);
+ particle.isVisible = false;
+ //particle.alive = false;
+ this.nbParticles--;
+
+ return particle;
+ }
+ }
+
+ // update color and scaling
+
+ const progression = Scalar.RangeToPercent(localZ, 0, SolidPlume.TUNNEL_LENGTH);
+
+ if (progression < 0.5) {
+ const t = progression / 0.5;
+ particle.color = Color4.Lerp(new Color4(0, 0, 1, 1), new Color4(0, 1, 1, 1), t);
+ particle.scaling = scaling; //Vector3.Lerp(Vector3.Zero(), scaling, Math.min(t * 2, 1));
+ } else {
+ const t = (progression - 0.5) / 0.5;
+ particle.color = Color4.Lerp(new Color4(0, 1, 1), new Color4(1, 0, 1), t);
+ particle.scaling = Vector3.Lerp(scaling, Vector3.Zero(), t * t);
+ }
+ // console.log('particle scaling is', particle.scaling.toString());
+
+ return particle;
+ };
+
+ this.SPS.mesh.parent = engineAnchor;
+ this.SPS.mesh.scaling.z = -1;
+ }
+
+ private initParticle(particle: SolidParticle) {
+ const r = Math.sqrt(Math.random());
+ const theta = Math.random() * 2 * Math.PI;
+
+ const position = new Vector3(r * Math.cos(theta), r * Math.sin(theta), 0);
+
+ particle.position.copyFrom(position);
+
+ particle.velocity.copyFrom(this.direction.scale(2));
+
+ // particle.scaling = scaling;
+ particle.scaling = new Vector3(0, 0, 0);
+ }
+
+ setThrottle(throttle: number) {
+ this.targetNbParticles = Math.floor(SolidPlume.MAX_NB_PARTICLES * throttle);
+ }
+
+ update(deltaSeconds: number) {
+ this.subTimeStep = deltaSeconds / this.nbSubTimeSteps;
+ for (let i = 0; i < this.nbSubTimeSteps; i++) {
+ // if there aren't enough particles, instantiate more
+ for (let j = 0; j < 10; j++) {
+ if (this.nbParticles < this.targetNbParticles && this.recycledParticles.length > 0) {
+ const particle = this.recycledParticles.shift() as SolidParticle;
+ // particle.alive = true;
+ particle.isVisible = true;
+ // console.log('make particle', particle.name, 'alive');
+ this.initParticle(particle);
+ this.nbParticles++;
+ }
+ }
+
+ this.SPS.setParticles();
+ }
+ }
+}
diff --git a/src/ts/utils/warpTunnel.ts b/src/ts/utils/warpTunnel.ts
index 5b3c46046..dd9068563 100644
--- a/src/ts/utils/warpTunnel.ts
+++ b/src/ts/utils/warpTunnel.ts
@@ -39,7 +39,7 @@ export class WarpTunnel implements Transformable {
readonly solidParticleSystem: SolidParticleSystem;
- private throttle: number = 0;
+ private throttle = 0;
static TUNNEL_LENGTH = 300;