diff --git a/src/ts/blackHoleDemo.ts b/src/ts/blackHoleDemo.ts index 88a444618..eb008d9a0 100644 --- a/src/ts/blackHoleDemo.ts +++ b/src/ts/blackHoleDemo.ts @@ -14,6 +14,8 @@ import { StarSystemHelper } from "./starSystem/starSystemHelper"; import { Mouse } from "./inputs/mouse"; import { Keyboard } from "./inputs/keyboard"; import { Gamepad } from "./inputs/gamepad"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { SystemSeed } from "./utils/systemSeed"; const engine = new CosmosJourneyer(); @@ -62,7 +64,7 @@ engine.registerStarSystemUpdateCallback(() => { (document.querySelector("#speedometer") as HTMLElement).innerHTML = `${throttleString} | ${parseSpeed(spaceshipController.getSpeed())}`; }); -const starSystemSeed = randRange(-1, 1, (step: number) => Math.random(), 0); +const starSystemSeed = new SystemSeed(Vector3.Zero(), 0); const starSystem = new StarSystemController(starSystemSeed, scene); starSystemView.setStarSystem(starSystem, false); diff --git a/src/ts/cosmosJourneyer.ts b/src/ts/cosmosJourneyer.ts index a25373dcf..9e0a78a1b 100644 --- a/src/ts/cosmosJourneyer.ts +++ b/src/ts/cosmosJourneyer.ts @@ -17,6 +17,7 @@ import { Observable } from "@babylonjs/core/Misc/observable"; import { PauseMenu } from "./ui/pauseMenu"; import { StarSystemView } from "./starSystem/StarSystemView"; import { EngineFactory } from "@babylonjs/core/Engines/engineFactory"; +import { SystemSeed } from "./utils/systemSeed"; enum EngineState { RUNNING, @@ -51,7 +52,8 @@ export class CosmosJourneyer { this.pauseMenu.onScreenshot.add(() => this.takeScreenshot()); this.pauseMenu.onShare.add(() => { const seed = this.getStarSystemView().getStarSystem().model.seed; - const url = new URL(`https://barthpaleologue.github.io/CosmosJourneyer/random.html?seed=${seed}`); + const payload = `starMapX=${seed.starSectorCoordinates.x}&starMapY=${seed.starSectorCoordinates.y}&starMapZ=${seed.starSectorCoordinates.z}&index=${seed.index}`; + const url = new URL(`https://barthpaleologue.github.io/CosmosJourneyer/random.html?${payload}`); navigator.clipboard.writeText(url.toString()).then(() => console.log("Copied to clipboard")); }); @@ -110,7 +112,7 @@ export class CosmosJourneyer { // Init starmap view this.starMap = new StarMap(this.engine); - this.starMap.onWarpObservable.add((seed: number) => { + this.starMap.onWarpObservable.add((seed: SystemSeed) => { this.getStarSystemView().setStarSystem(new StarSystemController(seed, this.getStarSystemView().scene), true); this.getStarSystemView().init(); this.toggleStarMap(); diff --git a/src/ts/index.ts b/src/ts/index.ts index ab1fbcafa..b3b8c113b 100644 --- a/src/ts/index.ts +++ b/src/ts/index.ts @@ -13,12 +13,7 @@ import { ShipControls } from "./spaceship/shipControls"; import { PostProcessType } from "./postProcesses/postProcessTypes"; import { TelluricPlanemoModel } from "./planemos/telluricPlanemo/telluricPlanemoModel"; import { GasPlanetModel } from "./planemos/gasPlanet/gasPlanetModel"; -import { - getForwardDirection, - getRotationQuaternion, - setRotationQuaternion, - translate -} from "./uberCore/transforms/basicTransform"; +import { getForwardDirection, getRotationQuaternion, setRotationQuaternion, translate } from "./uberCore/transforms/basicTransform"; import { parsePercentageFrom01, parseSpeed } from "./utils/parseToStrings"; import { StarSystemHelper } from "./starSystem/starSystemHelper"; @@ -31,6 +26,7 @@ import { getMoonSeed } from "./planemos/common"; import { Gamepad } from "./inputs/gamepad"; import { CharacterControls } from "./spacelegs/characterControls"; +import { SystemSeed } from "./utils/systemSeed"; const engine = new CosmosJourneyer(); @@ -104,7 +100,7 @@ engine.onToggleStarMapObservable.add((isStarMapOpen) => { console.log(`Time is going ${Settings.TIME_MULTIPLIER} time${Settings.TIME_MULTIPLIER > 1 ? "s" : ""} faster than in reality`); -const starSystemSeed = 0; +const starSystemSeed = new SystemSeed(Vector3.Zero(), 0); const starSystem = new StarSystemController(starSystemSeed, starSystemView.scene); starSystem.model.setName("Alpha Testis"); @@ -218,8 +214,8 @@ if (aresAtmosphere) { document.addEventListener("keydown", (e) => { if (engine.isPaused()) return; - if(e.key === "y") { - if(starSystemView.scene.getActiveController() === spaceshipController) { + if (e.key === "y") { + if (starSystemView.scene.getActiveController() === spaceshipController) { console.log("disembark"); characterController.getTransform().setEnabled(true); @@ -230,7 +226,7 @@ document.addEventListener("keydown", (e) => { starSystemView.scene.setActiveController(characterController); starSystemView.getStarSystem().postProcessManager.rebuild(); - } else if(starSystemView.scene.getActiveController() === characterController) { + } else if (starSystemView.scene.getActiveController() === characterController) { console.log("embark"); characterController.getTransform().setEnabled(false); diff --git a/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts b/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts index c751d333c..bc35977bd 100644 --- a/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts +++ b/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts @@ -161,7 +161,7 @@ export class ChunkTree { const distanceThreshold = Settings.CHUNK_RENDER_DISTANCE_MULTIPLIER * (this.rootChunkLength / 2 ** walked.length); - if ((distanceToNodeSquared < distanceThreshold ** 2) || walked.length < this.minDepth) { + if (distanceToNodeSquared < distanceThreshold ** 2 || walked.length < this.minDepth) { // if the node is near the camera or if we are loading minimal LOD if (tree instanceof Array && tree.length === 4) { return [ diff --git a/src/ts/postProcesses/atmosphericScatteringPostProcess.ts b/src/ts/postProcesses/atmosphericScatteringPostProcess.ts index 8fdb0f38f..81db8e088 100644 --- a/src/ts/postProcesses/atmosphericScatteringPostProcess.ts +++ b/src/ts/postProcesses/atmosphericScatteringPostProcess.ts @@ -10,8 +10,6 @@ import { TelluricPlanemo } from "../planemos/telluricPlanemo/telluricPlanemo"; import { GasPlanet } from "../planemos/gasPlanet/gasPlanet"; import { ObjectPostProcess } from "./objectPostProcess"; import { UniformEnumType, ShaderSamplers, ShaderUniforms, SamplerEnumType } from "../uberCore/postProcesses/types"; -import { StellarObject } from "../stellarObjects/stellarObject"; -import { BaseObject } from "../bodies/common"; import { Transformable } from "../uberCore/transforms/basicTransform"; export interface AtmosphereUniforms { diff --git a/src/ts/postProcesses/objectPostProcess.ts b/src/ts/postProcesses/objectPostProcess.ts index 9a5a556cd..d0f8b4167 100644 --- a/src/ts/postProcesses/objectPostProcess.ts +++ b/src/ts/postProcesses/objectPostProcess.ts @@ -1,5 +1,4 @@ import { PostProcess } from "@babylonjs/core/PostProcesses/postProcess"; -import { BaseObject } from "../bodies/common"; import { Transformable } from "../uberCore/transforms/basicTransform"; export interface UpdatablePostProcess extends PostProcess { diff --git a/src/ts/postProcesses/shadowPostProcess.ts b/src/ts/postProcesses/shadowPostProcess.ts index d3178fafb..70a03d3d4 100644 --- a/src/ts/postProcesses/shadowPostProcess.ts +++ b/src/ts/postProcesses/shadowPostProcess.ts @@ -9,7 +9,6 @@ import { Effect } from "@babylonjs/core/Materials/effect"; import { SamplerEnumType, ShaderSamplers, ShaderUniforms, UniformEnumType } from "../uberCore/postProcesses/types"; import { PostProcessType } from "./postProcessTypes"; import { RingsUniforms } from "./rings/ringsUniform"; -import { RingsPostProcess } from "./rings/ringsPostProcess"; import { Assets } from "../assets"; export type ShadowUniforms = { diff --git a/src/ts/randomizer.ts b/src/ts/randomizer.ts index f02eeddcc..a580739c0 100644 --- a/src/ts/randomizer.ts +++ b/src/ts/randomizer.ts @@ -2,7 +2,7 @@ import "../styles/index.scss"; import { StarSystemController } from "./starSystem/starSystemController"; -import { randRange } from "extended-random"; +import { centeredRand, randRange } from "extended-random"; import { Settings } from "./settings"; import { DefaultControls } from "./defaultController/defaultControls"; import { positionNearObject } from "./utils/positionNearObject"; @@ -15,6 +15,8 @@ import { parsePercentageFrom01, parseSpeed } from "./utils/parseToStrings"; import { Mouse } from "./inputs/mouse"; import { Keyboard } from "./inputs/keyboard"; import { Gamepad } from "./inputs/gamepad"; +import { SystemSeed } from "./utils/systemSeed"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; const engine = new CosmosJourneyer(); @@ -72,11 +74,25 @@ engine.onToggleStarMapObservable.add((isStarMapOpen) => { //check if url contains a seed const urlParams = new URLSearchParams(window.location.search); -const seed = urlParams.get("seed"); +const urlStarMapX = urlParams.get("starMapX"); +const urlStarMapY = urlParams.get("starMapY"); +const urlStarMapZ = urlParams.get("starMapZ"); +const urlIndex = urlParams.get("index"); +const urlBodyIndex = urlParams.get("bodyIndex"); -const starSystem = new StarSystemController(seed ? Number(seed) : randRange(-1, 1, (step: number) => Math.random(), 0) * Number.MAX_SAFE_INTEGER, scene); +const starMapX = urlStarMapX !== null ? Number(urlStarMapX) : Math.trunc((Math.random() * 2 - 1) * Number.MAX_SAFE_INTEGER * 0.1); +const starMapY = urlStarMapY !== null ? Number(urlStarMapY) : Math.trunc((Math.random() * 2 - 1) * Number.MAX_SAFE_INTEGER * 0.1); +const starMapZ = urlStarMapZ !== null ? Number(urlStarMapZ) : Math.trunc((Math.random() * 2 - 1) * Number.MAX_SAFE_INTEGER * 0.1); +const index = urlIndex !== null ? Number(urlIndex) : 0; +const bodyIndex = urlBodyIndex !== null ? Number(urlBodyIndex) : 0; + +const seed = new SystemSeed(new Vector3(starMapX, starMapY, starMapZ), index); + +const starSystem = new StarSystemController(seed, scene); starSystemView.setStarSystem(starSystem, true); +engine.getStarMap().setCurrentStarSystem(seed); + document.addEventListener("keydown", (e) => { if (engine.isPaused()) return; if (e.key === "g") { @@ -99,7 +115,8 @@ document.addEventListener("keydown", (e) => { engine.init(); const nbRadius = starSystem.model.getBodyTypeOfStar(0) === BODY_TYPE.BLACK_HOLE ? 8 : 3; -positionNearObject(scene.getActiveController(), starSystem.planets.length > 0 ? starSystem.getBodies()[1] : starSystem.stellarObjects[0], starSystem, nbRadius); +if (bodyIndex >= starSystem.getBodies().length) throw new Error(`Body index (${bodyIndex}) out of bound (0 - ${starSystem.getBodies().length - 1})!`); +positionNearObject(scene.getActiveController(), starSystem.planets.length > 0 ? starSystem.getBodies()[bodyIndex] : starSystem.stellarObjects[0], starSystem, nbRadius); engine.getStarSystemView().bodyEditor.setVisibility(EditorVisibility.NAVBAR); diff --git a/src/ts/settings.ts b/src/ts/settings.ts index c9a47f3f9..6268b6302 100644 --- a/src/ts/settings.ts +++ b/src/ts/settings.ts @@ -1,3 +1,6 @@ +import { seededSquirrelNoise } from "squirrel-noise"; +import { makeNoise3D } from "fast-simplex-noise"; + export const Settings = { UNIVERSE_SEED: Math.PI, EARTH_RADIUS: 1000e3, // target is 6000e3 @@ -20,5 +23,12 @@ export const Settings = { export const CollisionMask = { GROUND: 0b00000001, - SPACESHIP: 0b00000010, -} \ No newline at end of file + SPACESHIP: 0b00000010 +}; + +const seedableRNG = seededSquirrelNoise(Settings.UNIVERSE_SEED); +let step = 0; +const perlinRNG = makeNoise3D(() => { + return seedableRNG(step++); +}); +export const UniverseDensity = (x: number, y: number, z: number) => (1.0 - Math.abs(perlinRNG(x * 0.2, y * 0.2, z * 0.2))) ** 8; diff --git a/src/ts/spaceship/shipControls.ts b/src/ts/spaceship/shipControls.ts index 6dbeba0a3..260ae0e37 100644 --- a/src/ts/spaceship/shipControls.ts +++ b/src/ts/spaceship/shipControls.ts @@ -13,15 +13,7 @@ import { Observable } from "@babylonjs/core/Misc/observable"; import { Axis } from "@babylonjs/core/Maths/math.axis"; import { HavokPlugin } from "@babylonjs/core/Physics/v2/Plugins/havokPlugin"; import { setEnabledBody } from "../utils/havok"; -import { - getForwardDirection, - getUpwardDirection, - pitch, - roll, - rotate, - Transformable, - translate -} from "../uberCore/transforms/basicTransform"; +import { getForwardDirection, getUpwardDirection, pitch, roll, rotate, Transformable, translate } from "../uberCore/transforms/basicTransform"; import { TransformNode } from "@babylonjs/core/Meshes"; import { Controls } from "../uberCore/controls"; import { Assets } from "../assets"; @@ -322,14 +314,13 @@ export class ShipControls implements Controls { console.log(500 * deltaTime * Math.sign(distance), distance); translate(this.getTransform(), gravityDir.scale(Math.min(500 * deltaTime * Math.sign(distance), distance))); - const currentUp = getUpwardDirection(this.getTransform()); const targetUp = landingSpotNormal; const axis = Vector3.Cross(currentUp, targetUp); const theta = Math.acos(Vector3.Dot(currentUp, targetUp)); rotate(this.getTransform(), axis, Math.min(0.1 * deltaTime, theta)); - if(Math.abs(distance) < 0.3 && Math.abs(theta) < 0.01) { + if (Math.abs(distance) < 0.3 && Math.abs(theta) < 0.01) { this.state = ShipState.LANDED; this.aggregate.body.setMotionType(PhysicsMotionType.STATIC); } diff --git a/src/ts/spaceship/warpDrive.ts b/src/ts/spaceship/warpDrive.ts index 73627224d..6f674cdfc 100644 --- a/src/ts/spaceship/warpDrive.ts +++ b/src/ts/spaceship/warpDrive.ts @@ -1,4 +1,3 @@ -import { Vector3 } from "@babylonjs/havok"; import { Settings } from "../settings"; import { clamp } from "../utils/math"; diff --git a/src/ts/starSystem/starSystemController.ts b/src/ts/starSystem/starSystemController.ts index 625410bc6..9cf09631e 100644 --- a/src/ts/starSystem/starSystemController.ts +++ b/src/ts/starSystem/starSystemController.ts @@ -20,6 +20,7 @@ import { Star } from "../stellarObjects/star/star"; import { BlackHole } from "../stellarObjects/blackHole/blackHole"; import { NeutronStar } from "../stellarObjects/neutronStar/neutronStar"; import { ChunkForge } from "../planemos/telluricPlanemo/terrain/chunks/chunkForge"; +import { SystemSeed } from "../utils/systemSeed"; export class StarSystemController { readonly scene: UberScene; @@ -73,7 +74,7 @@ export class StarSystemController { private closestToScreenCenterOrbitalObject: AbstractObject | null = null; - constructor(model: StarSystemModel | number, scene: UberScene) { + constructor(model: StarSystemModel | SystemSeed, scene: UberScene) { this.scene = scene; this.postProcessManager = new PostProcessManager(this.scene); diff --git a/src/ts/starSystem/starSystemModel.ts b/src/ts/starSystem/starSystemModel.ts index aadf93c21..e4cdad7fb 100644 --- a/src/ts/starSystem/starSystemModel.ts +++ b/src/ts/starSystem/starSystemModel.ts @@ -3,6 +3,7 @@ import { centeredRand, randRangeInt, uniformRandBool } from "extended-random"; import { Settings } from "../settings"; import { BODY_TYPE } from "../model/common"; import { generateName } from "../utils/nameGenerator"; +import { SystemSeed } from "../utils/systemSeed"; enum GENERATION_STEPS { NAME, @@ -14,14 +15,14 @@ enum GENERATION_STEPS { } export class StarSystemModel { - readonly seed: number; + readonly seed: SystemSeed; readonly rng: (step: number) => number; private name: string; - constructor(seed: number) { + constructor(seed: SystemSeed) { this.seed = seed; - this.rng = seededSquirrelNoise(this.seed); + this.rng = seededSquirrelNoise(this.seed.hash); this.name = generateName(this.rng, GENERATION_STEPS.NAME); } diff --git a/src/ts/starmap/starMap.ts b/src/ts/starmap/starMap.ts index 0c81aba98..1585a7b4c 100644 --- a/src/ts/starmap/starMap.ts +++ b/src/ts/starmap/starMap.ts @@ -4,7 +4,7 @@ import starTexture from "../../asset/textures/starParticle.png"; import blackHoleTexture from "../../asset/textures/blackholeParticleSmall.png"; import { StarSystemModel } from "../starSystem/starSystemModel"; -import { BuildData, Cell, Vector3ToString } from "./cell"; +import { BuildData, StarSector, Vector3ToString } from "./starSector"; import { StarMapUI } from "./starMapUI"; import { getStellarTypeString } from "../stellarObjects/common"; import { BODY_TYPE } from "../model/common"; @@ -25,19 +25,17 @@ import "@babylonjs/core/Animations/animatable"; import "@babylonjs/core/Culling/ray"; import { TransformRotationAnimation } from "../uberCore/transforms/animations/rotation"; import { TransformTranslationAnimation } from "../uberCore/transforms/animations/translation"; -import { makeNoise3D } from "fast-simplex-noise"; -import { seededSquirrelNoise } from "squirrel-noise"; -import { Settings } from "../settings"; -import { getForwardDirection } from "../uberCore/transforms/basicTransform"; +import { getForwardDirection, translate } from "../uberCore/transforms/basicTransform"; import { ThickLines } from "../utils/thickLines"; import { Observable } from "@babylonjs/core/Misc/observable"; import { Keyboard } from "../inputs/keyboard"; import { StarModel } from "../stellarObjects/star/starModel"; import { BlackHoleModel } from "../stellarObjects/blackHole/blackHoleModel"; +import { SystemSeed } from "../utils/systemSeed"; export class StarMap { readonly scene: Scene; - private readonly controller: DefaultControls; + private readonly controls: DefaultControls; private rotationAnimation: TransformRotationAnimation | null = null; private translationAnimation: TransformTranslationAnimation | null = null; @@ -47,7 +45,7 @@ export class StarMap { */ private readonly starMapCenterPosition: Vector3; - private readonly allowedCellRelativeCoordinates: Vector3[] = []; + private readonly allowedStarSectorRelativeCoordinates: Vector3[] = []; private readonly starTemplate: Mesh; private readonly blackHoleTemplate: Mesh; @@ -63,23 +61,23 @@ export class StarMap { private readonly starMapUI: StarMapUI; - private selectedSystemSeed: number | null = null; - private currentSystemSeed: number | null = null; + private selectedSystemSeed: SystemSeed | null = null; + private currentSystemSeed: SystemSeed | null = null; - private readonly loadedCells: Map = new Map(); + private readonly loadedStarSectors: Map = new Map(); - private readonly seedToInstanceMap: Map = new Map(); - private readonly instanceToSeedMap: Map = new Map(); + private readonly seedToInstanceMap: Map = new Map(); + private readonly instanceToSeedMap: Map = new Map(); private travelLine: ThickLines; private readonly thickLines: ThickLines[]; - public readonly onWarpObservable: Observable = new Observable(); + public readonly onWarpObservable: Observable = new Observable(); /** - * The position of the cell the player is currently in (relative to the global node). + * The position of the star sector the player is currently in (relative to the global node). */ - private currentCellPosition = Vector3.Zero(); + private currentStarSectorPosition = Vector3.Zero(); private cameraPositionToCenter = Vector3.Zero(); @@ -94,8 +92,6 @@ export class StarMap { private static readonly SHIMMER_ANIMATION = new Animation("shimmer", "instancedBuffers.color.a", 60, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE); private static readonly SHIMMER_DURATION = 1000; - private readonly densityRNG; - constructor(engine: Engine) { this.scene = new Scene(engine); this.scene.clearColor = new Color4(0, 0, 0, 1); @@ -103,23 +99,27 @@ export class StarMap { this.scene.skipPointerMovePicking = false; this.scene.useRightHandedSystem = true; - this.controller = new DefaultControls(this.scene); - this.controller.speed /= 5; - this.controller.getActiveCamera().minZ = 0.01; + this.controls = new DefaultControls(this.scene); + this.controls.speed /= 5; + this.controls.getActiveCamera().minZ = 0.01; - this.controller.getActiveCamera().attachControl(); + this.controls.getActiveCamera().attachControl(); - this.controller.addInput(new Keyboard()); + this.controls.addInput(new Keyboard()); this.starMapUI = new StarMapUI(this.scene); this.starMapUI.warpButton.onPointerClickObservable.add(() => { this.currentSystemSeed = this.selectedSystemSeed; - if (this.currentSystemSeed !== null) this.starMapUI.setCurrentStarSystemMesh(this.seedToInstanceMap.get(this.currentSystemSeed) as InstancedMesh); + if (this.currentSystemSeed !== null) this.starMapUI.setCurrentStarSystemMesh(this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh); this.dispatchWarpCallbacks(); }); - const pipeline = new DefaultRenderingPipeline("pipeline", false, this.scene, [this.controller.getActiveCamera()]); + document.addEventListener("keydown", (e) => { + if (e.key === "f") this.focusOnCurrentSystem(); + }); + + const pipeline = new DefaultRenderingPipeline("pipeline", false, this.scene, [this.controls.getActiveCamera()]); pipeline.fxaaEnabled = true; pipeline.bloomEnabled = true; pipeline.bloomThreshold = 0.0; @@ -128,12 +128,7 @@ export class StarMap { pipeline.imageProcessing.exposure = 1.1; pipeline.imageProcessing.contrast = 1.0; - const urlParams = new URLSearchParams(window.location.search); - const initialStarMapX = Number(urlParams.get("smx")); - const initialStarMapY = Number(urlParams.get("smy")); - const initialStarMapZ = Number(urlParams.get("smz")); - - this.starMapCenterPosition = new Vector3(initialStarMapX ?? 0, initialStarMapY ?? 0, initialStarMapZ ?? 0); + this.starMapCenterPosition = Vector3.Zero(); this.starTemplate = MeshBuilder.CreatePlane("star", { size: 0.15 }, this.scene); this.starTemplate.billboardMode = Mesh.BILLBOARDMODE_ALL; @@ -205,52 +200,50 @@ export class StarMap { this.travelLine = new ThickLines("travelLine", { points: [], thickness: 0.01, color: Color3.Red() }, this.scene); this.thickLines = [this.travelLine]; - // then generate missing cells // TODO: make this in parallel + // then generate missing star sectors for (let x = -StarMap.RENDER_RADIUS; x <= StarMap.RENDER_RADIUS; x++) { for (let y = -StarMap.RENDER_RADIUS; y <= StarMap.RENDER_RADIUS; y++) { for (let z = -StarMap.RENDER_RADIUS; z <= StarMap.RENDER_RADIUS; z++) { if (x * x + y * y + z * z > StarMap.RENDER_RADIUS * StarMap.RENDER_RADIUS) continue; - this.allowedCellRelativeCoordinates.push(new Vector3(x, y, z)); + this.allowedStarSectorRelativeCoordinates.push(new Vector3(x, y, z)); } } } - const seedableRNG = seededSquirrelNoise(Settings.UNIVERSE_SEED); - let step = 0; - const perlinRNG = makeNoise3D(() => { - return seedableRNG(step++); - }); - this.densityRNG = (x: number, y: number, z: number) => (1.0 - Math.abs(perlinRNG(x * 0.2, y * 0.2, z * 0.2))) ** 8; - this.scene.onBeforeRenderObservable.add(() => { const deltaTime = this.scene.getEngine().getDeltaTime() / 1000; if (this.rotationAnimation !== null) this.rotationAnimation.update(deltaTime); if (this.translationAnimation !== null) this.translationAnimation.update(deltaTime); - this.controller.update(deltaTime); + this.controls.update(deltaTime); - this.cameraPositionToCenter = this.controller.getActiveCamera().globalPosition.subtract(this.starMapCenterPosition); + this.acknowledgeCameraMovement(); - this.currentCellPosition = new Vector3( - Math.round(this.cameraPositionToCenter.x / Cell.SIZE), - Math.round(this.cameraPositionToCenter.y / Cell.SIZE), - Math.round(this.cameraPositionToCenter.z / Cell.SIZE) - ); - - this.updateCells(); - - if (this.controller.getActiveCamera().globalPosition.length() > StarMap.FLOATING_ORIGIN_MAX_DISTANCE) { - this.translateCameraBackToOrigin(); - } + this.updateStarSectors(); this.thickLines.forEach((bondingLine) => bondingLine.update()); }); } + private acknowledgeCameraMovement() { + // floating origin + if (this.controls.getActiveCamera().globalPosition.length() > StarMap.FLOATING_ORIGIN_MAX_DISTANCE) { + this.translateCameraBackToOrigin(); + } + + this.cameraPositionToCenter = this.controls.getActiveCamera().globalPosition.subtract(this.starMapCenterPosition); + this.currentStarSectorPosition = new Vector3( + Math.round(this.cameraPositionToCenter.x / StarSector.SIZE), + Math.round(this.cameraPositionToCenter.y / StarSector.SIZE), + Math.round(this.cameraPositionToCenter.z / StarSector.SIZE) + ); + } + public translateCameraBackToOrigin() { - const translationToOrigin = this.controller.getTransform().getAbsolutePosition().negate(); - this.controller.getTransform().position = Vector3.Zero(); + const translationToOrigin = this.controls.getTransform().getAbsolutePosition().negate(); + this.controls.getTransform().position = Vector3.Zero(); + this.controls.getActiveCamera().getViewMatrix(true); this.starMapCenterPosition.addInPlace(translationToOrigin); for (const mesh of this.scene.meshes) mesh.position.addInPlace(translationToOrigin); } @@ -261,187 +254,224 @@ export class StarMap { } /** - * Register a cell at the given position, it will be added to the generation queue - * @param position The position of the cell + * Register a star sector at the given position, it will be added to the generation queue + * @param position The position of the sector + * @param generateNow */ - private registerCell(position: Vector3) { - const cell = new Cell(position, this.densityRNG(position.x, position.y, position.z)); - this.loadedCells.set(cell.getKey(), cell); - this.starBuildStack.push(...cell.generate()); + private registerStarSector(position: Vector3, generateNow = false): StarSector { + const starSector = new StarSector(position); + this.loadedStarSectors.set(starSector.getKey(), starSector); + + if (!generateNow) this.starBuildStack.push(...starSector.generate()); + else { + const data = starSector.generate(); + for (const d of data) this.createInstance(d); + } + + return starSector; + } + + public setCurrentStarSystem(starSystemSeed: SystemSeed) { + this.currentSystemSeed = starSystemSeed; + this.selectedSystemSeed = starSystemSeed; + + if (this.loadedStarSectors.has(Vector3ToString(starSystemSeed.starSectorCoordinates))) { + this.starMapUI.setCurrentStarSystemMesh(this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh); + this.focusOnCurrentSystem(); + return; + } + + this.registerStarSector(starSystemSeed.starSectorCoordinates, true); + this.starMapUI.setCurrentStarSystemMesh(this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh); + + const translation = starSystemSeed.starSectorCoordinates.subtract(this.currentStarSectorPosition).scaleInPlace(StarSector.SIZE); + translate(this.controls.getTransform(), translation); + this.controls.getActiveCamera().getViewMatrix(true); + this.acknowledgeCameraMovement(); + + this.focusOnCurrentSystem(true); } - private updateCells() { - // first remove all cells that are too far - const currentSystemInstance = this.currentSystemSeed === null ? null : (this.seedToInstanceMap.get(this.currentSystemSeed) as InstancedMesh); - const selectedSystemInstance = this.selectedSystemSeed === null ? null : (this.seedToInstanceMap.get(this.selectedSystemSeed) as InstancedMesh); - for (const cell of this.loadedCells.values()) { - if (currentSystemInstance !== null && cell.starInstances.concat(cell.blackHoleInstances).includes(currentSystemInstance)) continue; // don't remove cells that contain the current system - if (selectedSystemInstance !== null && cell.starInstances.concat(cell.blackHoleInstances).includes(selectedSystemInstance)) continue; // don't remove cells that contain the selected system + private updateStarSectors() { + // first remove all star sectors that are too far + const currentSystemInstance = this.currentSystemSeed === null ? null : (this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh); + const selectedSystemInstance = this.selectedSystemSeed === null ? null : (this.seedToInstanceMap.get(this.selectedSystemSeed.toString()) as InstancedMesh); + for (const starSector of this.loadedStarSectors.values()) { + if (currentSystemInstance !== null && starSector.starInstances.concat(starSector.blackHoleInstances).includes(currentSystemInstance)) continue; // don't remove star sector that contains the current system + if (selectedSystemInstance !== null && starSector.starInstances.concat(starSector.blackHoleInstances).includes(selectedSystemInstance)) continue; // don't remove star sector that contains the selected system - const position = cell.position; + const position = starSector.position; if (position.subtract(this.cameraPositionToCenter).length() > StarMap.RENDER_RADIUS + 1) { - for (const starInstance of cell.starInstances) this.fadeOutThenRecycle(starInstance, this.recycledStars); - for (const blackHoleInstance of cell.blackHoleInstances) this.fadeOutThenRecycle(blackHoleInstance, this.recycledBlackHoles); + for (const starInstance of starSector.starInstances) this.fadeOutThenRecycle(starInstance, this.recycledStars); + for (const blackHoleInstance of starSector.blackHoleInstances) this.fadeOutThenRecycle(blackHoleInstance, this.recycledBlackHoles); - this.loadedCells.delete(cell.getKey()); + this.loadedStarSectors.delete(starSector.getKey()); } } - // then generate missing cells - for (const relativeCoordinate of this.allowedCellRelativeCoordinates) { - const position = this.currentCellPosition.add(relativeCoordinate); - const cellKey = Vector3ToString(position); + // then generate missing sectors + for (const relativeCoordinate of this.allowedStarSectorRelativeCoordinates) { + const position = this.currentStarSectorPosition.add(relativeCoordinate); + const sectorKey = Vector3ToString(position); - if (this.loadedCells.has(cellKey)) continue; // already generated + if (this.loadedStarSectors.has(sectorKey)) continue; // already generated - // don't generate cells that are not in the frustum - const bb = Cell.getBoundingBox(position, this.starMapCenterPosition); - if (!this.controller.getActiveCamera().isInFrustum(bb)) continue; + // don't generate star sectors that are not in the frustum + const bb = StarSector.getBoundingBox(position, this.starMapCenterPosition); + if (!this.controls.getActiveCamera().isInFrustum(bb)) continue; - this.registerCell(position); + this.registerStarSector(position); } - this.buildNextStars(Math.min(2000, StarMap.GENERATION_CADENCE * this.controller.speed)); + this.buildNextStars(Math.min(2000, StarMap.GENERATION_CADENCE * this.controls.speed)); - this.starMapUI.update(this.controller.getActiveCamera()); + this.starMapUI.update(this.controls.getActiveCamera()); } private buildNextStars(n: number): void { for (let i = 0; i < n; i++) { - if (this.starBuildStack.length === 0) return; - - const data = this.starBuildStack.pop() as BuildData; + const data = this.starBuildStack.pop(); + if (data === undefined) return; - if (!this.loadedCells.has(data.cellString)) { - // if cell was removed in the meantime we build another star + if (!this.loadedStarSectors.has(data.sectorString)) { + // if star sector was removed in the meantime we build another star n++; continue; } - const starSystemSeed = data.seed; - const starSystemModel = new StarSystemModel(starSystemSeed); - - const starSeed = starSystemModel.getStarSeed(0); - const isStarBlackHole = starSystemModel.getBodyTypeOfStar(0) === BODY_TYPE.BLACK_HOLE; - - const starModel = !isStarBlackHole ? new StarModel(starSeed) : new BlackHoleModel(starSeed); - - let instance: InstancedMesh | null = null; - let recycled = false; - - if (!isStarBlackHole) { - if (this.recycledStars.length > 0) { - instance = this.recycledStars[0]; - this.recycledStars.shift(); - recycled = true; - } else instance = this.starTemplate.createInstance(data.name); - } else { - if (this.recycledBlackHoles.length > 0) { - instance = this.recycledBlackHoles[0]; - this.recycledBlackHoles.shift(); - recycled = true; - } else instance = this.blackHoleTemplate.createInstance(data.name); - } - - const initializedInstance = instance; - - this.seedToInstanceMap.set(starSystemSeed, initializedInstance); - this.instanceToSeedMap.set(initializedInstance, starSystemSeed); + this.createInstance(data); + } + } - initializedInstance.scaling = Vector3.One().scaleInPlace(data.scale); - initializedInstance.position = data.position.add(this.starMapCenterPosition); + private createInstance(data: BuildData) { + const starSystemSeed = data.seed; + const starSystemModel = new StarSystemModel(starSystemSeed); + + const starSeed = starSystemModel.getStarSeed(0); + const isStarBlackHole = starSystemModel.getBodyTypeOfStar(0) === BODY_TYPE.BLACK_HOLE; + + const starModel = !isStarBlackHole ? new StarModel(starSeed) : new BlackHoleModel(starSeed); + + let instance: InstancedMesh | null = null; + let recycled = false; + + if (!isStarBlackHole) { + if (this.recycledStars.length > 0) { + instance = this.recycledStars[0]; + this.recycledStars.shift(); + recycled = true; + } else instance = this.starTemplate.createInstance(data.name); + } else { + if (this.recycledBlackHoles.length > 0) { + instance = this.recycledBlackHoles[0]; + this.recycledBlackHoles.shift(); + recycled = true; + } else instance = this.blackHoleTemplate.createInstance(data.name); + } - if (starModel instanceof StarModel) { - const starColor = starModel.surfaceColor; - initializedInstance.instancedBuffers.color = new Color4(starColor.x, starColor.y, starColor.z, 0.0); - } else { - initializedInstance.instancedBuffers.color = new Color4(1.0, 0.6, 0.3, 0.0); - } + const initializedInstance = instance; - if (!recycled) { - initializedInstance.isPickable = true; - initializedInstance.actionManager = new ActionManager(this.scene); - - initializedInstance.actionManager.registerAction( - new ExecuteCodeAction(ActionManager.OnPointerOverTrigger, () => { - this.starMapUI.setHoveredStarSystemMesh(initializedInstance); - }) - ); - - initializedInstance.actionManager.registerAction( - new ExecuteCodeAction(ActionManager.OnPointerOutTrigger, () => { - this.starMapUI.setHoveredStarSystemMesh(null); - }) - ); - } else { - initializedInstance.setEnabled(true); - initializedInstance.actionManager?.unregisterAction(initializedInstance.actionManager.actions[2]); - } + this.seedToInstanceMap.set(starSystemSeed.toString(), initializedInstance); + this.instanceToSeedMap.set(initializedInstance, starSystemSeed.toString()); - initializedInstance.actionManager?.registerAction( - new ExecuteCodeAction(ActionManager.OnPickTrigger, () => { - let text = ""; - if (this.currentSystemSeed !== null) { - const currentInstance = this.seedToInstanceMap.get(this.currentSystemSeed) as InstancedMesh; - const distance = 10 * currentInstance.getAbsolutePosition().subtract(initializedInstance.getAbsolutePosition()).length(); - text += `Distance: ${distance.toFixed(2)}ly\n`; - } - text += `Planets: ${starSystemModel.getNbPlanets()}\n`; - text += `Type: ${getStellarTypeString(starModel.stellarType)}\n`; - text += `Seed: ${starSystemModel.seed}`; + initializedInstance.scaling = Vector3.One().scaleInPlace(data.scale); + initializedInstance.position = data.position.add(this.starMapCenterPosition); - this.starMapUI.attachUIToMesh(initializedInstance); - this.starMapUI.setSelectedSystem({ name: starSystemModel.getName(), text }); + if (starModel instanceof StarModel) { + const starColor = starModel.surfaceColor; + initializedInstance.instancedBuffers.color = new Color4(starColor.x, starColor.y, starColor.z, 0.0); + } else { + initializedInstance.instancedBuffers.color = new Color4(1.0, 0.6, 0.3, 0.0); + } - this.selectedSystemSeed = starSystemSeed; + if (!recycled) { + initializedInstance.isPickable = true; + initializedInstance.actionManager = new ActionManager(this.scene); - this.focusCameraOnStar(initializedInstance); + initializedInstance.actionManager.registerAction( + new ExecuteCodeAction(ActionManager.OnPointerOverTrigger, () => { + this.starMapUI.setHoveredStarSystemMesh(initializedInstance); + }) + ); - if (this.currentSystemSeed !== null) { - this.travelLine.setPoints([this.seedToInstanceMap.get(this.currentSystemSeed) as InstancedMesh, initializedInstance]); - } + initializedInstance.actionManager.registerAction( + new ExecuteCodeAction(ActionManager.OnPointerOutTrigger, () => { + this.starMapUI.setHoveredStarSystemMesh(null); }) ); + } else { + initializedInstance.setEnabled(true); + initializedInstance.actionManager?.unregisterAction(initializedInstance.actionManager.actions[2]); + } - this.fadeIn(initializedInstance); + initializedInstance.actionManager?.registerAction( + new ExecuteCodeAction(ActionManager.OnPickTrigger, () => { + let text = ""; + if (this.currentSystemSeed !== null) { + const currentInstance = this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh; + const distance = 15 * currentInstance.getAbsolutePosition().subtract(initializedInstance.getAbsolutePosition()).length(); + text += `Distance: ${distance.toFixed(2)}ly\n`; + } + text += `Type: ${getStellarTypeString(starModel.stellarType)}\n`; + text += `Planets: ${starSystemModel.getNbPlanets()}\n`; - if (isStarBlackHole) this.loadedCells.get(data.cellString)?.blackHoleInstances.push(initializedInstance); - else this.loadedCells.get(data.cellString)?.starInstances.push(initializedInstance); - } + this.starMapUI.attachUIToMesh(initializedInstance); + this.starMapUI.setSelectedSystem({ name: starSystemModel.getName(), text }); + + this.selectedSystemSeed = starSystemSeed; + + this.focusCameraOnStar(initializedInstance); + + if (this.currentSystemSeed !== null) { + this.travelLine.setPoints([this.seedToInstanceMap.get(this.currentSystemSeed.toString()) as InstancedMesh, initializedInstance]); + } + }) + ); + + this.fadeIn(initializedInstance); + + if (isStarBlackHole) this.loadedStarSectors.get(data.sectorString)?.blackHoleInstances.push(initializedInstance); + else this.loadedStarSectors.get(data.sectorString)?.starInstances.push(initializedInstance); } - private focusCameraOnStar(starInstance: InstancedMesh) { - const cameraDir = getForwardDirection(this.controller.getTransform()); - const starDir = starInstance.position.subtract(this.controller.getTransform().getAbsolutePosition()).normalize(); + private focusCameraOnStar(starInstance: InstancedMesh, skipAnimation = false) { + const cameraDir = getForwardDirection(this.controls.getTransform()); + const starDir = starInstance.position.subtract(this.controls.getTransform().getAbsolutePosition()).normalize(); const rotationAngle = Math.acos(Vector3.Dot(cameraDir, starDir)); // if the rotation axis has a length different from 1, it means the cross product was made between very close vectors : no rotation is needed - if (rotationAngle > 0.02) { + if (skipAnimation) { + this.controls.getTransform().lookAt(starInstance.position); + this.controls.getTransform().computeWorldMatrix(true); + } else if (rotationAngle > 0.02) { const rotationAxis = Vector3.Cross(cameraDir, starDir).normalize(); - this.rotationAnimation = new TransformRotationAnimation(this.controller.getTransform(), rotationAxis, rotationAngle, 1); + this.rotationAnimation = new TransformRotationAnimation(this.controls.getTransform(), rotationAxis, rotationAngle, 1); } - const distance = starInstance.position.subtract(this.controller.getTransform().getAbsolutePosition()).length(); - const targetPosition = this.controller + const distance = starInstance.position.subtract(this.controls.getTransform().getAbsolutePosition()).length(); + const targetPosition = this.controls .getTransform() .getAbsolutePosition() .add(starDir.scaleInPlace(distance - 0.8)); // if the transform is already in the right position, do not animate - if (targetPosition.subtract(this.controller.getTransform().getAbsolutePosition()).lengthSquared() > 0.1) { - this.translationAnimation = new TransformTranslationAnimation(this.controller.getTransform(), targetPosition, 1); + if (skipAnimation) this.controls.getTransform().position = targetPosition; + else if (targetPosition.subtract(this.controls.getTransform().getAbsolutePosition()).lengthSquared() > 0.1) { + this.translationAnimation = new TransformTranslationAnimation(this.controls.getTransform(), targetPosition, 1); } this.starMapUI.setHoveredStarSystemMesh(null); } - public focusOnCurrentSystem() { + public focusOnCurrentSystem(skipAnimation = false) { + console.log("focus on current system"); if (this.currentSystemSeed === null) return console.warn("No current system seed!"); - const instance = this.seedToInstanceMap.get(this.currentSystemSeed) as InstancedMesh; + const instance = this.seedToInstanceMap.get(this.currentSystemSeed.toString()); + if (instance === undefined) throw new Error("The current system has no instance!"); - this.focusCameraOnStar(instance); + this.focusCameraOnStar(instance, skipAnimation); } private fadeIn(instance: InstancedMesh) { @@ -459,7 +489,8 @@ export class StarMap { if (this.starMapUI.getCurrentHoveredMesh() === instance) this.starMapUI.setHoveredStarSystemMesh(null); instance.setEnabled(false); - const seed = this.instanceToSeedMap.get(instance) as number; + const seed = this.instanceToSeedMap.get(instance); + if (seed === undefined) throw new Error("No seed for instance!"); this.seedToInstanceMap.delete(seed); this.instanceToSeedMap.delete(instance); diff --git a/src/ts/starmap/cell.ts b/src/ts/starmap/starSector.ts similarity index 62% rename from src/ts/starmap/cell.ts rename to src/ts/starmap/starSector.ts index 7728680af..7e6a07b28 100644 --- a/src/ts/starmap/cell.ts +++ b/src/ts/starmap/starSector.ts @@ -1,10 +1,11 @@ import { hashVec3 } from "../utils/hashVec3"; import { seededSquirrelNoise } from "squirrel-noise"; import { centeredRand } from "extended-random"; -import { Settings } from "../settings"; +import { UniverseDensity } from "../settings"; import { Matrix, Vector3 } from "@babylonjs/core/Maths/math.vector"; import { InstancedMesh } from "@babylonjs/core/Meshes/instancedMesh"; import { BoundingBox } from "@babylonjs/core/Culling/boundingBox"; +import { SystemSeed } from "../utils/systemSeed"; export function Vector3ToString(v: Vector3): string { return `${v.x},${v.y},${v.z}`; @@ -17,53 +18,57 @@ export function StringToVector3(s: string): Vector3 { export type BuildData = { name: string; - seed: number; - cellString: string; + seed: SystemSeed; + sectorString: string; scale: number; position: Vector3; }; -export class Cell { +export class StarSector { /** - * The star instances of the cell + * The star instances of the sector */ readonly starInstances: InstancedMesh[] = []; readonly blackHoleInstances: InstancedMesh[] = []; /** - * The position of the cell relative to the center of the starmap + * The position of the sector relative to the center of the starmap */ readonly position: Vector3; /** - * The size of all cells + * The size of all sectors */ static readonly SIZE = 1; readonly density; + readonly nbStars: number; + /** - * The random number generator of the cell + * The random number generator of the sector */ readonly rng: (step: number) => number; - constructor(positionInStarMap: Vector3, density: number) { + constructor(positionInStarMap: Vector3) { this.position = positionInStarMap; this.rng = seededSquirrelNoise(hashVec3(positionInStarMap)); - this.density = density; + this.density = UniverseDensity(positionInStarMap.x, positionInStarMap.y, positionInStarMap.z); + + this.nbStars = 40 * this.density * this.rng(0); } generate(): BuildData[] { - const cellString = Vector3ToString(this.position); - const nbStars = 40 * this.density * this.rng(0); - const data = []; - for (let i = 0; i < nbStars; i++) { + const sectorString = this.getKey(); + const data: BuildData[] = []; + for (let i = 0; i < this.nbStars; i++) { + const systemSeed = new SystemSeed(this.position, i); data.push({ name: `starInstance|${this.position.x}|${this.position.y}|${this.position.z}|${i}`, - seed: centeredRand(this.rng, 1 + i) * Settings.SEED_HALF_RANGE, - cellString: cellString, + seed: systemSeed, + sectorString: sectorString, scale: 0.5 + this.rng(100 * i) / 2, position: new Vector3(centeredRand(this.rng, 10 * i + 1) / 2, centeredRand(this.rng, 10 * i + 2) / 2, centeredRand(this.rng, 10 * i + 3) / 2).addInPlace( this.position @@ -74,8 +79,8 @@ export class Cell { } /** - * Returns a string that uniquely identifies this cell (its position relative to the global node) - * @returns a string that uniquely identifies this cell + * Returns a string that uniquely identifies this sector (its position relative to the global node) + * @returns a string that uniquely identifies this sector */ getKey(): string { return Vector3ToString(this.position); @@ -83,8 +88,8 @@ export class Cell { static getBoundingBox(position: Vector3, globalNodePosition: Vector3): BoundingBox { return new BoundingBox( - new Vector3(-1, -1, -1).scaleInPlace(Cell.SIZE / 2), - new Vector3(1, 1, 1).scaleInPlace(Cell.SIZE / 2), + new Vector3(-1, -1, -1).scaleInPlace(StarSector.SIZE / 2), + new Vector3(1, 1, 1).scaleInPlace(StarSector.SIZE / 2), Matrix.Translation(position.x + globalNodePosition.x, position.y + globalNodePosition.y, position.z + globalNodePosition.z) ); } diff --git a/src/ts/uberCore/transforms/animations/rotation.ts b/src/ts/uberCore/transforms/animations/rotation.ts index dcbddeb45..3925fcb08 100644 --- a/src/ts/uberCore/transforms/animations/rotation.ts +++ b/src/ts/uberCore/transforms/animations/rotation.ts @@ -2,10 +2,11 @@ import { Vector3 } from "@babylonjs/core/Maths/math.vector"; import { easeInOutInterpolation } from "./interpolations"; import { TransformNode } from "@babylonjs/core/Meshes"; import { rotate } from "../basicTransform"; +import { clamp } from "../../../utils/math"; export class TransformRotationAnimation { private clock = 0; - private duration: number; + private readonly duration: number; private thetaAcc = 0; private readonly totalTheta; private readonly axis; @@ -23,7 +24,9 @@ export class TransformRotationAnimation { this.clock += deltaTime; - const dtheta = this.totalTheta * easeInOutInterpolation(this.clock / this.duration) - this.thetaAcc; + const t = clamp(this.clock / this.duration, 0, 1); + + const dtheta = this.totalTheta * easeInOutInterpolation(t) - this.thetaAcc; this.thetaAcc += dtheta; rotate(this.transform, this.axis, dtheta); diff --git a/src/ts/uberCore/transforms/animations/translation.ts b/src/ts/uberCore/transforms/animations/translation.ts index 9953f1796..6d71eb398 100644 --- a/src/ts/uberCore/transforms/animations/translation.ts +++ b/src/ts/uberCore/transforms/animations/translation.ts @@ -2,6 +2,7 @@ import { Vector3 } from "@babylonjs/core/Maths/math.vector"; import { easeInOutInterpolation } from "./interpolations"; import { TransformNode } from "@babylonjs/core/Meshes"; import { translate } from "../basicTransform"; +import { clamp } from "../../../utils/math"; export class TransformTranslationAnimation { private clock = 0; @@ -23,7 +24,9 @@ export class TransformTranslationAnimation { this.clock += deltaTime; - const dDistance = this.totalDistance * easeInOutInterpolation(this.clock / this.duration) - this.distanceAcc; + const t = clamp(this.clock / this.duration, 0, 1); + + const dDistance = this.totalDistance * easeInOutInterpolation(t) - this.distanceAcc; this.distanceAcc += dDistance; translate(this.transform, this.direction.scale(dDistance)); diff --git a/src/ts/uberCore/uberScene.ts b/src/ts/uberCore/uberScene.ts index 3ea3df969..840e19873 100644 --- a/src/ts/uberCore/uberScene.ts +++ b/src/ts/uberCore/uberScene.ts @@ -27,7 +27,7 @@ export class UberScene extends Scene { } public setActiveCamera(camera: Camera) { - if(this.activeCamera !== null) this.activeCamera.detachControl(); + if (this.activeCamera !== null) this.activeCamera.detachControl(); this.activeCamera = camera; camera.attachControl(true); diff --git a/src/ts/utils/systemSeed.ts b/src/ts/utils/systemSeed.ts new file mode 100644 index 000000000..b5db9e924 --- /dev/null +++ b/src/ts/utils/systemSeed.ts @@ -0,0 +1,28 @@ +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { seededSquirrelNoise } from "squirrel-noise"; +import { hashVec3 } from "./hashVec3"; +import { centeredRand } from "extended-random"; +import { Settings } from "../settings"; + +export class SystemSeed { + readonly starSectorCoordinates: Vector3; + readonly index: number; + + readonly hash: number; + + constructor(starSectorCoordinates: Vector3, index: number) { + this.starSectorCoordinates = starSectorCoordinates; + this.index = index; + + if (!Number.isSafeInteger(this.starSectorCoordinates.x)) throw new Error("x coordinate of star sector is not a safe integer"); + if (!Number.isSafeInteger(this.starSectorCoordinates.y)) throw new Error("y coordinate of star sector is not a safe integer"); + if (!Number.isSafeInteger(this.starSectorCoordinates.z)) throw new Error("z coordinate of star sector is not a safe integer"); + + const cellRNG = seededSquirrelNoise(hashVec3(starSectorCoordinates)); + this.hash = centeredRand(cellRNG, 1 + index) * Settings.SEED_HALF_RANGE; + } + + toString(): string { + return `${this.starSectorCoordinates.x},${this.starSectorCoordinates.y},${this.starSectorCoordinates.z},${this.index}`; + } +} diff --git a/src/ts/xr.ts b/src/ts/xr.ts index 374184249..f6d5225ac 100644 --- a/src/ts/xr.ts +++ b/src/ts/xr.ts @@ -54,32 +54,32 @@ camera.attachControl(canvas, true); const xr = await scene.createDefaultXRExperienceAsync(); if (!xr.baseExperience) { - // no xr support - throw new Error("No XR support"); + // no xr support + throw new Error("No XR support"); } else { - // all good, ready to go - console.log("XR support"); + // all good, ready to go + console.log("XR support"); } const webXRInput = xr.input; // if using the experience helper, otherwise, an instance of WebXRInput webXRInput.onControllerAddedObservable.add((xrController) => { - console.log("Controller added"); - xrController.onMotionControllerInitObservable.add((motionController) => { - console.log("Motion controller initialized"); - - const mainComponent = motionController.getMainComponent(); - - mainComponent.onButtonStateChangedObservable.add((component) => { - if (component.changes.pressed) { - if (component.changes.pressed.current) { - console.log("Pressed"); - } - if (component.pressed) { - console.log("Pressed"); - } - } + console.log("Controller added"); + xrController.onMotionControllerInitObservable.add((motionController) => { + console.log("Motion controller initialized"); + + const mainComponent = motionController.getMainComponent(); + + mainComponent.onButtonStateChangedObservable.add((component) => { + if (component.changes.pressed) { + if (component.changes.pressed.current) { + console.log("Pressed"); + } + if (component.pressed) { + console.log("Pressed"); + } + } + }); }); - }); }); const xrCamera = xr.baseExperience.camera; @@ -117,34 +117,34 @@ FlatCloudsPostProcess.CreateAsync("clouds", planet, planet.model.cloudsUniforms, const chunkForge = new ChunkForge(Settings.VERTEX_RESOLUTION); scene.onBeforeRenderObservable.add(() => { - const deltaTime = engine.getDeltaTime() / 1000; + const deltaTime = engine.getDeltaTime() / 1000; - if (scene.activeCamera === null) throw new Error("Active camera is null"); + if (scene.activeCamera === null) throw new Error("Active camera is null"); - if (camera.globalPosition.length() > 0) { - translate(planet.getTransform(), camera.globalPosition.negate()); - translate(star.getTransform(), camera.globalPosition.negate()); - camera.position.set(0, 0, 0); - } + if (camera.globalPosition.length() > 0) { + translate(planet.getTransform(), camera.globalPosition.negate()); + translate(star.getTransform(), camera.globalPosition.negate()); + camera.position.set(0, 0, 0); + } - planet.updateLOD(scene.activeCamera.globalPosition, chunkForge); - planet.updateMaterial(camera, [star], deltaTime); + planet.updateLOD(scene.activeCamera.globalPosition, chunkForge); + planet.updateMaterial(camera, [star], deltaTime); - chunkForge.update(); + chunkForge.update(); - star.updateMaterial(); + star.updateMaterial(); - //ocean.update(deltaTime); + //ocean.update(deltaTime); }); scene.executeWhenReady(() => { - engine.runRenderLoop(() => { - scene.render(); - }); + engine.runRenderLoop(() => { + scene.render(); + }); }); window.addEventListener("resize", () => { - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - engine.resize(true); + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + engine.resize(true); });