diff --git a/package.json b/package.json
index d954635d0..19158d140 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
},
- "version": "1.4.1",
+ "version": "1.4.2",
"description": "CosmosJourneyer",
"name": "cosmos-journeyer",
"scripts": {
diff --git a/src/asset/spacestation/shipcarrier.glb b/src/asset/spacestation/shipcarrier.glb
index abf56d954..de92336a1 100644
Binary files a/src/asset/spacestation/shipcarrier.glb and b/src/asset/spacestation/shipcarrier.glb differ
diff --git a/src/ts/alphaTestis.ts b/src/ts/alphaTestis.ts
index 72b60b0e4..daa0689c3 100644
--- a/src/ts/alphaTestis.ts
+++ b/src/ts/alphaTestis.ts
@@ -34,6 +34,8 @@ import { StarModel } from "./stellarObjects/star/starModel";
import { RingsUniforms } from "./postProcesses/rings/ringsUniform";
import { getMoonSeed } from "./planets/common";
import { SystemSeed } from "./utils/systemSeed";
+import { SpaceStation } from "./spacestation/spaceStation";
+import { PhysicsViewer } from "@babylonjs/core/Debug/physicsViewer";
const engine = await CosmosJourneyer.CreateAsync();
@@ -80,8 +82,13 @@ const planet = StarSystemHelper.makeTelluricPlanet(starSystem, planetModel);
planet.model.ringsUniforms = new RingsUniforms(planet.model.rng);
planet.postProcesses.push(PostProcessType.RING);
-//const spacestation = new SpaceStation(starSystemView.scene, planet);
-//starSystemView.getStarSystem().addSpaceStation(spacestation);
+const spacestation = new SpaceStation(starSystemView.scene, planet);
+starSystemView.getStarSystem().addSpaceStation(spacestation);
+
+//physicsViewer.showBody(spacestation.aggregate.body);
+/*for(const landingpad of spacestation.landingPads) {
+ physicsViewer.showBody(landingpad.aggregate.body);
+}*/
const moonModel = new TelluricPlanetModel(getMoonSeed(planetModel, 0), planetModel);
moonModel.physicalProperties.mass = 2;
@@ -160,6 +167,13 @@ if (aresAtmosphere) {
document.addEventListener("keydown", (e) => {
if (engine.isPaused()) return;
+ if(e.key === "o") {
+ const landingPad = spacestation.handleDockingRequest();
+ if(landingPad !== null && starSystemView.scene.getActiveController() === spaceshipController) {
+ spaceshipController.spaceship.engageLandingOnPad(landingPad);
+ }
+ }
+
if (e.key === "x") {
let nbVertices = 0;
let nbInstances = 0;
diff --git a/src/ts/architecture/celestialBody.ts b/src/ts/architecture/celestialBody.ts
index adcc90839..b59b63f3d 100644
--- a/src/ts/architecture/celestialBody.ts
+++ b/src/ts/architecture/celestialBody.ts
@@ -48,4 +48,6 @@ export interface CelestialBodyModel extends OrbitalObjectModel {
* The radius of the celestial body
*/
readonly radius: number;
+
+ getNbSpaceStations(): number;
}
diff --git a/src/ts/architecture/orbitalObject.ts b/src/ts/architecture/orbitalObject.ts
index 3dc064630..bf12f8983 100644
--- a/src/ts/architecture/orbitalObject.ts
+++ b/src/ts/architecture/orbitalObject.ts
@@ -18,7 +18,6 @@
import { Transformable } from "./transformable";
import { BoundingSphere } from "./boundingSphere";
import { OrbitProperties } from "../orbit/orbitProperties";
-import { rotateVector3AroundInPlace } from "../utils/algebra";
import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math";
import { getRotationQuaternion, setRotationQuaternion, translate } from "../uberCore/transforms/basicTransform";
import { OrbitalObjectPhysicalProperties } from "./physicalProperties";
@@ -82,8 +81,8 @@ export class OrbitalObject {
// rotate the object around the barycenter of the orbit, around the normal to the orbital plane
const dtheta = (2 * Math.PI * deltaTime) / orbit.period;
- rotateVector3AroundInPlace(newPosition, barycenter, orbit.normalToPlane, dtheta);
-
+ const rotationQuaternion = Quaternion.RotationAxis(orbit.normalToPlane, dtheta);
+ newPosition.applyRotationQuaternionInPlace(rotationQuaternion);
newPosition.normalize().scaleInPlace(orbit.radius);
// enforce orbital plane
diff --git a/src/ts/assets.ts b/src/ts/assets.ts
index 7ff397b63..f139b2b53 100644
--- a/src/ts/assets.ts
+++ b/src/ts/assets.ts
@@ -73,6 +73,7 @@ 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 { Axis } from "@babylonjs/core/Maths/math.axis";
export class Assets {
static IS_READY = false;
diff --git a/src/ts/landingPad/landingPad.ts b/src/ts/landingPad/landingPad.ts
index 0a134071d..b3587626b 100644
--- a/src/ts/landingPad/landingPad.ts
+++ b/src/ts/landingPad/landingPad.ts
@@ -6,17 +6,28 @@ import { PhysicsShapeConvexHull } from "@babylonjs/core/Physics/v2/physicsShape"
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { CollisionMask } from "../settings";
import { Scene } from "@babylonjs/core/scene";
-import { Vector3 } from "@babylonjs/core/Maths/math";
+import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math";
import { Transformable } from "../architecture/transformable";
+import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
+import { PhysicsMotionType } from "@babylonjs/core";
+import { Axis } from "@babylonjs/core/Maths/math.axis";
export class LandingPad implements Transformable {
readonly instanceRoot: TransformNode;
- readonly aggregate: PhysicsAggregate;
- constructor(scene: Scene) {
- this.instanceRoot = Assets.CreateLandingPadInstance();
+ //readonly aggregate: PhysicsAggregate;
- this.aggregate = new PhysicsAggregate(
+ constructor(scene: Scene, existingMesh: AbstractMesh | null = null) {
+ if (existingMesh === null) {
+ this.instanceRoot = Assets.CreateLandingPadInstance();
+ } else {
+ this.instanceRoot = existingMesh;
+ }
+
+ // init rotation quaternion
+ this.instanceRoot.rotate(Axis.X, 0);
+
+ /*this.aggregate = new PhysicsAggregate(
this.instanceRoot,
PhysicsShapeType.CONTAINER,
{
@@ -26,6 +37,8 @@ export class LandingPad implements Transformable {
scene
);
+ this.aggregate.body.setMotionType(PhysicsMotionType.STATIC);
+
this.aggregate.body.setMassProperties({ inertia: Vector3.Zero(), mass: 0 });
for (const child of this.instanceRoot.getChildMeshes()) {
@@ -33,15 +46,15 @@ export class LandingPad implements Transformable {
childShape.filterMembershipMask = CollisionMask.LANDING_PADS;
this.aggregate.shape.addChildFromParent(this.instanceRoot, childShape, child);
}
- this.aggregate.body.disablePreStep = false;
+ this.aggregate.body.disablePreStep = false;*/
}
getTransform(): TransformNode {
- return this.aggregate.transformNode;
+ return this.instanceRoot;
}
dispose() {
- this.aggregate.dispose();
+ //this.aggregate.dispose();
this.instanceRoot.dispose();
}
}
diff --git a/src/ts/landingSimulator.ts b/src/ts/landingSimulator.ts
index ad1f43a40..2ede3f364 100644
--- a/src/ts/landingSimulator.ts
+++ b/src/ts/landingSimulator.ts
@@ -7,7 +7,7 @@ import { setMaxLinVel } from "./utils/havok";
import { UberScene } from "./uberCore/uberScene";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { Assets } from "./assets";
-import { roll, translate } from "./uberCore/transforms/basicTransform";
+import { roll, setRotationQuaternion, translate } from "./uberCore/transforms/basicTransform";
import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight";
import { Color4 } from "@babylonjs/core/Maths/math.color";
import "@babylonjs/core/Physics/physicsEngineComponent";
@@ -21,6 +21,10 @@ import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator"
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight";
import { PointLight } from "@babylonjs/core/Lights/pointLight";
+import { SpaceStation } from "./spacestation/spaceStation";
+import { Quaternion } from "@babylonjs/core/Maths/math";
+import { Axis } from "@babylonjs/core/Maths/math.axis";
+import { OrbitalObject } from "./architecture/orbitalObject";
const canvas = document.getElementById("renderer") as HTMLCanvasElement;
canvas.width = window.innerWidth;
@@ -53,19 +57,33 @@ shadowGenerator.addShadowCaster(spaceship.instanceRoot, true);
spaceship.getTransform().position = new Vector3(0, 0, -10);
roll(spaceship.getTransform(), Math.random() * 6.28);
-const landingPad = new LandingPad(scene);
+/*const landingPad = new LandingPad(scene);
landingPad.getTransform().position = new Vector3(0, -20, 0);
landingPad.instanceRoot.getChildMeshes().forEach((mesh) => {
mesh.receiveShadows = true;
-});
+});*/
+
+const physicsViewer = new PhysicsViewer();
+/*physicsViewer.showBody(spaceship.aggregate.body);
+physicsViewer.showBody(landingPad.aggregate.body);*/
+
+const spacestation = new SpaceStation(scene);
+setRotationQuaternion(spacestation.getTransform(), Quaternion.RotationAxis(Axis.X, Math.PI / 2));
+translate(spacestation.getTransform(), new Vector3(0, -100, 0));
+
+//physicsViewer.showBody(spacestation.aggregate.body);
+/*spacestation.landingPads.forEach(stationLandingPad => {
+ physicsViewer.showBody(stationLandingPad.aggregate.body);
+});*/
+
+/*spacestation.ringAggregates.forEach(ring => {
+ physicsViewer.showBody(ring.body);
+});*/
/*const ground = MeshBuilder.CreateGround("ground", { width: 50, height: 50 });
ground.position.y = -40;
ground.receiveShadows = true;*/
-/*const physicsViewer = new PhysicsViewer();
-physicsViewer.showBody(spaceship.aggregate.body);
-physicsViewer.showBody(landingPad.aggregate.body);*/
const defaultControls = new DefaultControls(scene);
defaultControls.speed *= 15;
@@ -74,12 +92,18 @@ scene.setActiveController(defaultControls);
translate(defaultControls.getTransform(), new Vector3(50, 0, 0));
-defaultControls.getTransform().lookAt(Vector3.Lerp(spaceship.getTransform().position, landingPad.getTransform().position, 0.5));
+//defaultControls.getTransform().lookAt(Vector3.Lerp(spaceship.getTransform().position, landingPad.getTransform().position, 0.5));
scene.onBeforeRenderObservable.add(() => {
const deltaTime = scene.deltaTime / 1000;
scene.getActiveController().update(deltaTime);
spaceship.update(deltaTime);
+
+ //OrbitalObject.UpdateRotation(spacestation, deltaTime);
+
+ spacestation.ringInstances.forEach(mesh => {
+ mesh.rotate(Axis.Y, 0.01 * deltaTime);
+ });
});
scene.executeWhenReady(() => {
@@ -88,9 +112,12 @@ scene.executeWhenReady(() => {
});
});
+const landingPad = spacestation.handleDockingRequest();
+if(landingPad === null) throw new Error("Docking request denied");
+
document.addEventListener("keydown", (event) => {
if (event.key === "o") {
- spaceship.engageLanding(landingPad);
+ spaceship.engageLandingOnPad(landingPad);
}
});
diff --git a/src/ts/mandelbulb/mandelbulbModel.ts b/src/ts/mandelbulb/mandelbulbModel.ts
index 9c778f5f3..1e8970a2c 100644
--- a/src/ts/mandelbulb/mandelbulbModel.ts
+++ b/src/ts/mandelbulb/mandelbulbModel.ts
@@ -90,4 +90,8 @@ export class MandelbulbModel implements PlanetModel {
getApparentRadius(): number {
return this.radius;
}
+
+ getNbSpaceStations(): number {
+ return 0;
+ }
}
diff --git a/src/ts/model/common.ts b/src/ts/model/common.ts
index 5c27377c0..90ca98638 100644
--- a/src/ts/model/common.ts
+++ b/src/ts/model/common.ts
@@ -35,7 +35,9 @@ export enum GENERATION_STEPS {
PRESSURE = 1100,
WATER_AMOUNT = 1200,
- TERRAIN = 1500
+ TERRAIN = 1500,
+
+ SPACE_STATION = 2000
}
export enum BODY_TYPE {
diff --git a/src/ts/planets/gasPlanet/gasPlanetModel.ts b/src/ts/planets/gasPlanet/gasPlanetModel.ts
index fa9285f2a..d5fdad5ca 100644
--- a/src/ts/planets/gasPlanet/gasPlanetModel.ts
+++ b/src/ts/planets/gasPlanet/gasPlanetModel.ts
@@ -97,4 +97,10 @@ export class GasPlanetModel implements PlanetModel {
getApparentRadius(): number {
return this.radius;
}
+
+ public getNbSpaceStations(): number {
+ if(uniformRandBool(0.2, this.rng, GENERATION_STEPS.SPACE_STATION)) return 1;
+ if(uniformRandBool(0.1, this.rng, GENERATION_STEPS.SPACE_STATION + 10)) return 2;
+ return 0;
+ }
}
diff --git a/src/ts/planets/telluricPlanet/telluricPlanetModel.ts b/src/ts/planets/telluricPlanet/telluricPlanetModel.ts
index f6bd3fcae..78e8485cf 100644
--- a/src/ts/planets/telluricPlanet/telluricPlanetModel.ts
+++ b/src/ts/planets/telluricPlanet/telluricPlanetModel.ts
@@ -153,4 +153,10 @@ export class TelluricPlanetModel implements PlanetModel {
getApparentRadius(): number {
return this.radius + this.physicalProperties.oceanLevel;
}
+
+ public getNbSpaceStations(): number {
+ if(uniformRandBool(0.2, this.rng, GENERATION_STEPS.SPACE_STATION)) return 1;
+ if(uniformRandBool(0.1, this.rng, GENERATION_STEPS.SPACE_STATION + 10)) return 2;
+ return 0;
+ }
}
diff --git a/src/ts/planets/telluricPlanet/terrain/chunks/planetChunk.ts b/src/ts/planets/telluricPlanet/terrain/chunks/planetChunk.ts
index 1d9b0e311..3492d434f 100644
--- a/src/ts/planets/telluricPlanet/terrain/chunks/planetChunk.ts
+++ b/src/ts/planets/telluricPlanet/terrain/chunks/planetChunk.ts
@@ -84,8 +84,8 @@ export class PlanetChunk implements Transformable, BoundingSphere {
this.mesh.parent = parentAggregate.transformNode;
- this.mesh.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
- this.mesh.occlusionType = AbstractMesh.OCCLUSION_TYPE_OPTIMISTIC;
+ //this.mesh.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
+ //this.mesh.occlusionType = AbstractMesh.OCCLUSION_TYPE_OPTIMISTIC;
this.parent = parentAggregate.transformNode;
this.parentAggregate = parentAggregate;
@@ -138,7 +138,8 @@ export class PlanetChunk implements Transformable, BoundingSphere {
this.aggregate = new PhysicsAggregate(this.mesh, PhysicsShapeType.MESH, { mass: 0 }, this.mesh.getScene());
this.aggregate.body.setMotionType(PhysicsMotionType.STATIC);
this.aggregate.body.disablePreStep = false;
- this.aggregate.shape.filterMembershipMask = CollisionMask.GROUND;
+ this.aggregate.shape.filterMembershipMask = CollisionMask.ENVIRONMENT;
+ this.aggregate.shape.filterCollideMask = CollisionMask.DYNAMIC_OBJECTS;
const constraint = new LockConstraint(Vector3.Zero(), this.getTransform().position.negate(), new Vector3(0, 1, 0), new Vector3(0, 1, 0), this.mesh.getScene());
this.parentAggregate.body.addConstraint(this.aggregate.body, constraint);
}
diff --git a/src/ts/settings.ts b/src/ts/settings.ts
index 42b854b29..90e4243c0 100644
--- a/src/ts/settings.ts
+++ b/src/ts/settings.ts
@@ -41,9 +41,8 @@ export const Settings = {
};
export const CollisionMask = {
- GROUND: 0b00000001,
- SPACESHIP: 0b00000010,
- LANDING_PADS: 0b00000100
+ ENVIRONMENT: 0b00000001,
+ DYNAMIC_OBJECTS: 0b00000010
};
const seedableRNG = seededSquirrelNoise(Settings.UNIVERSE_SEED);
diff --git a/src/ts/spacelegs/characterControls.ts b/src/ts/spacelegs/characterControls.ts
index 27e5075c6..71d095bf5 100644
--- a/src/ts/spacelegs/characterControls.ts
+++ b/src/ts/spacelegs/characterControls.ts
@@ -249,7 +249,7 @@ export class CharacterControls implements Controls {
setUpVector(character, up);
}
- (this.scene.getPhysicsEngine() as PhysicsEngineV2).raycastToRef(start, end, this.raycastResult, { collideWith: CollisionMask.GROUND });
+ (this.scene.getPhysicsEngine() as PhysicsEngineV2).raycastToRef(start, end, this.raycastResult, { collideWith: CollisionMask.ENVIRONMENT });
if (this.raycastResult.hasHit) {
const up = character.up;
const distance = Vector3.Dot(character.getAbsolutePosition().subtract(this.raycastResult.hitPointWorld), up);
diff --git a/src/ts/spaceship/shipControls.ts b/src/ts/spaceship/shipControls.ts
index 4af98deab..70bd29212 100644
--- a/src/ts/spaceship/shipControls.ts
+++ b/src/ts/spaceship/shipControls.ts
@@ -136,10 +136,6 @@ export class ShipControls implements Controls {
const deltaThrottle = keyboard.getZAxis() * deltaTime;
this.spaceship.getWarpDrive().increaseTargetThrottle(deltaThrottle);
}
-
- const warpSpeed = getForwardDirection(this.getTransform()).scale(this.spaceship.getWarpDrive().getWarpSpeed());
- //this.aggregate.body.setLinearVelocity(warpSpeed);
- translate(this.getTransform(), warpSpeed.scale(deltaTime));
}
}
diff --git a/src/ts/spaceship/spaceship.ts b/src/ts/spaceship/spaceship.ts
index 89d256e99..e08dc9472 100644
--- a/src/ts/spaceship/spaceship.ts
+++ b/src/ts/spaceship/spaceship.ts
@@ -30,20 +30,20 @@ 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,
- rotate,
+ getForwardDirection, getRotationQuaternion, getUpwardDirection, rotate,
setRotationQuaternion,
translate
} from "../uberCore/transforms/basicTransform";
import { TransformNode } from "@babylonjs/core/Meshes";
import { Assets } from "../assets";
import { PhysicsRaycastResult } from "@babylonjs/core/Physics/physicsRaycastResult";
-import { PhysicsEngineV2 } from "@babylonjs/core/Physics/v2";
import { CollisionMask } from "../settings";
import { Transformable } from "../architecture/transformable";
import { WarpTunnel } from "../utils/warpTunnel";
import { Quaternion } from "@babylonjs/core/Maths/math";
+import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
+import { LandingPad } from "../landingPad/landingPad";
+import { PhysicsEngineV2 } from "@babylonjs/core/Physics/v2";
enum ShipState {
FLYING,
@@ -80,6 +80,8 @@ export class Spaceship implements Transformable {
private readonly scene: Scene;
+ private targetLandingPad: LandingPad | null = null;
+
constructor(scene: Scene) {
this.instanceRoot = Assets.CreateSpaceShipInstance();
setRotationQuaternion(this.instanceRoot, Quaternion.Identity());
@@ -95,7 +97,8 @@ export class Spaceship implements Transformable {
);
for (const child of this.instanceRoot.getChildMeshes()) {
const childShape = new PhysicsShapeMesh(child as Mesh, scene);
- childShape.filterMembershipMask = CollisionMask.SPACESHIP;
+ childShape.filterMembershipMask = CollisionMask.DYNAMIC_OBJECTS;
+ childShape.filterCollideMask = CollisionMask.ENVIRONMENT;
this.aggregate.shape.addChildFromParent(this.instanceRoot, childShape, child);
}
this.aggregate.body.disablePreStep = false;
@@ -218,6 +221,13 @@ export class Spaceship implements Transformable {
console.log("landing on", this.landingTarget.getTransform().name);
}
+ public engageLandingOnPad(landingPad: LandingPad) {
+ console.log("Landing on pad", landingPad.getTransform().name);
+ this.aggregate.body.setMotionType(PhysicsMotionType.ANIMATED);
+ this.state = ShipState.LANDING;
+ this.targetLandingPad = landingPad;
+ }
+
private completeLanding() {
console.log("Landing sequence complete");
this.state = ShipState.LANDED;
@@ -225,45 +235,17 @@ export class Spaceship implements Transformable {
this.landingTarget = null;
}
- public update(deltaTime: number) {
- const warpSpeed = getForwardDirection(this.aggregate.transformNode).scale(this.warpDrive.getWarpSpeed());
-
- const currentForwardSpeed = Vector3.Dot(warpSpeed, this.aggregate.transformNode.getDirection(Axis.Z));
- this.warpDrive.update(currentForwardSpeed, this.closestObject.distance, this.closestObject.radius, deltaTime);
-
- // the warp throttle goes from 0.1 to 1 smoothly using an inverse function
- 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();
-
- if (this.closestWalkableObject !== null) {
- const gravityDir = this.closestWalkableObject.getTransform().getAbsolutePosition().subtract(this.getTransform().getAbsolutePosition()).normalize();
- this.aggregate.body.applyForce(gravityDir.scale(9.8), this.aggregate.body.getObjectCenterWorld());
- }
- }
-
- if (this.flightAssistEnabled) {
- this.aggregate.body.setAngularDamping(0.9);
- } else {
- this.aggregate.body.setAngularDamping(1);
+ private land(deltaTime: number) {
+ if(this.targetLandingPad !== null) {
+ this.landOnPad(this.targetLandingPad, deltaTime);
}
- if (this.state === ShipState.LANDING) {
- if (this.landingTarget === null) {
- throw new Error("Closest walkable object is null while landing");
- }
-
+ if(this.landingTarget !== null) {
const gravityDir = this.landingTarget.getTransform().getAbsolutePosition().subtract(this.getTransform().getAbsolutePosition()).normalize();
const start = this.getTransform().getAbsolutePosition().add(gravityDir.scale(-50e3));
const end = this.getTransform().getAbsolutePosition().add(gravityDir.scale(50e3));
- (this.scene.getPhysicsEngine() as PhysicsEngineV2).raycastToRef(start, end, this.raycastResult, { collideWith: CollisionMask.GROUND | CollisionMask.LANDING_PADS });
+ (this.scene.getPhysicsEngine() as PhysicsEngineV2).raycastToRef(start, end, this.raycastResult, { collideWith: CollisionMask.ENVIRONMENT });
if (this.raycastResult.hasHit) {
const landingSpotNormal = this.raycastResult.hitNormalWorld;
const extent = this.instanceRoot.getHierarchyBoundingVectors();
@@ -291,6 +273,64 @@ export class Spaceship implements Transformable {
}
}
+ private landOnPad(landingPad: LandingPad, deltaTime: number) {
+ const padUp = landingPad.getTransform().up;
+
+ const targetPosition = landingPad.getTransform().getAbsolutePosition();
+ targetPosition.addInPlace(padUp.scale(2));
+ const currentPosition = this.getTransform().getAbsolutePosition();
+
+ const distance = Vector3.Distance(targetPosition, currentPosition);
+
+ if(distance < 0.01) {
+ this.completeLanding();
+ return;
+ }
+
+ const targetOrientation = landingPad.getTransform().absoluteRotationQuaternion;
+ const currentOrientation = getRotationQuaternion(this.getTransform());
+
+ translate(this.getTransform(), targetPosition.subtract(currentPosition).normalize().scaleInPlace(Math.min(distance, 20 * deltaTime)));
+
+ this.getTransform().rotationQuaternion = Quaternion.Slerp(currentOrientation, targetOrientation, deltaTime);
+ }
+
+ public update(deltaTime: number) {
+ const warpSpeed = getForwardDirection(this.aggregate.transformNode).scale(this.warpDrive.getWarpSpeed());
+
+ const currentForwardSpeed = Vector3.Dot(warpSpeed, this.aggregate.transformNode.getDirection(Axis.Z));
+ this.warpDrive.update(currentForwardSpeed, this.closestObject.distance, this.closestObject.radius, deltaTime);
+
+ // the warp throttle goes from 0.1 to 1 smoothly using an inverse function
+ 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();
+
+ if (this.closestWalkableObject !== null) {
+ const gravityDir = this.closestWalkableObject.getTransform().getAbsolutePosition().subtract(this.getTransform().getAbsolutePosition()).normalize();
+ this.aggregate.body.applyForce(gravityDir.scale(9.8), this.aggregate.body.getObjectCenterWorld());
+ }
+ } else {
+ translate(this.getTransform(), warpSpeed.scale(deltaTime));
+ }
+
+ if (this.flightAssistEnabled) {
+ this.aggregate.body.setAngularDamping(0.9);
+ } else {
+ this.aggregate.body.setAngularDamping(1);
+ }
+
+ if (this.state === ShipState.LANDING) {
+ this.land(deltaTime);
+ }
+ }
+
public dispose() {
this.aggregate.dispose();
this.instanceRoot.dispose();
diff --git a/src/ts/spaceship/warpDrive.ts b/src/ts/spaceship/warpDrive.ts
index dc7fdcd95..b2eba3451 100644
--- a/src/ts/spaceship/warpDrive.ts
+++ b/src/ts/spaceship/warpDrive.ts
@@ -116,6 +116,8 @@ export class WarpDrive implements ReadonlyWarpDrive {
*/
private state = WARPDRIVE_STATE.DISABLED;
+ private static MIN_SPEED = 500;
+
constructor(enabledByDefault = false) {
this.state = enabledByDefault ? WARPDRIVE_STATE.ENABLED : WARPDRIVE_STATE.DISABLED;
}
@@ -159,12 +161,13 @@ export class WarpDrive implements ReadonlyWarpDrive {
/**
* Computes the target speed of the warp drive based on the distance to the closest body and the user throttle.
* @param closestObjectDistance The distance to the closest body in m.
+ * @param closestObjectRadius
* @returns The computed target speed in m/s.
*/
public updateTargetSpeed(closestObjectDistance: number, closestObjectRadius: number): number {
const speedThreshold = 10e3;
- const closeSpeed = (speedThreshold * 0.025 * (closestObjectDistance - closestObjectRadius)) / speedThreshold;
- const deepSpaceSpeed = speedThreshold * ((0.025 * (closestObjectDistance - closestObjectRadius)) / speedThreshold) ** 1.1;
+ const closeSpeed = (speedThreshold * 0.025 * Math.max(0, closestObjectDistance - closestObjectRadius)) / speedThreshold;
+ const deepSpaceSpeed = speedThreshold * ((0.025 * Math.max(0, closestObjectDistance - closestObjectRadius)) / speedThreshold) ** 1.1;
this.targetSpeed = Math.min(this.maxWarpSpeed, Math.max(closeSpeed, deepSpaceSpeed));
return this.targetThrottle * this.targetSpeed;
}
@@ -208,13 +211,14 @@ export class WarpDrive implements ReadonlyWarpDrive {
const deltaThrottle = this.internalThrottleAcceleration * deltaTime;
this.increaseInternalThrottle(deltaThrottle * sign);
- this.currentSpeed = this.internalThrottle * this.targetSpeed;
+ this.currentSpeed = Math.max(WarpDrive.MIN_SPEED, this.internalThrottle * this.targetSpeed);
}
/**
* Updates the warp drive based on the current speed of the ship, the distance to the closest body and the time elapsed since the last update.
* @param currentForwardSpeed The current speed of the warp drive projected on the forward direction of the ship.
- * @param closestObjectPosition The distance to the closest body in m.
+ * @param closestObjectDistance
+ * @param clostestObjectRadius
* @param deltaTime The time elapsed since the last update in seconds.
*/
public update(currentForwardSpeed: number, closestObjectDistance: number, clostestObjectRadius: number, deltaTime: number): void {
@@ -222,7 +226,7 @@ export class WarpDrive implements ReadonlyWarpDrive {
case WARPDRIVE_STATE.DESENGAGING:
this.targetSpeed *= 0.9;
this.updateWarpDriveSpeed(currentForwardSpeed, deltaTime);
- if (this.targetSpeed < 1e2 && this.currentSpeed < 1e2) this.disable();
+ if (this.targetSpeed <= WarpDrive.MIN_SPEED && this.currentSpeed <= WarpDrive.MIN_SPEED) this.disable();
break;
case WARPDRIVE_STATE.ENABLED:
this.updateTargetSpeed(closestObjectDistance, clostestObjectRadius);
diff --git a/src/ts/spacestation/spaceStation.ts b/src/ts/spacestation/spaceStation.ts
index 31727e654..a87a7faa8 100644
--- a/src/ts/spacestation/spaceStation.ts
+++ b/src/ts/spacestation/spaceStation.ts
@@ -22,28 +22,40 @@ import { Camera } from "@babylonjs/core/Cameras/camera";
import { SpaceStationModel } from "./spacestationModel";
import { PostProcessType } from "../postProcesses/postProcessTypes";
import { Assets } from "../assets";
-import { Axis } from "@babylonjs/core/Maths/math.axis";
import { OrbitalObject } from "../architecture/orbitalObject";
import { Cullable } from "../bodies/cullable";
import { TransformNode } from "@babylonjs/core/Meshes";
import { OrbitProperties } from "../orbit/orbitProperties";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { OrbitalObjectPhysicalProperties } from "../architecture/physicalProperties";
+import { PhysicsAggregate } from "@babylonjs/core/Physics/v2/physicsAggregate";
+import { PhysicsMotionType, PhysicsShapeType } from "@babylonjs/core";
+import { LandingPad } from "../landingPad/landingPad";
+import { PhysicsShapeConvexHull, PhysicsShapeMesh } from "@babylonjs/core/Physics/v2/physicsShape";
+import { Mesh } from "@babylonjs/core/Meshes/mesh";
+import { LockConstraint } from "@babylonjs/core/Physics/v2/physicsConstraint";
+import { CollisionMask } from "../settings";
+import { CelestialBody } from "../architecture/celestialBody";
export class SpaceStation implements OrbitalObject, Cullable {
readonly name: string;
readonly model: SpaceStationModel;
+ readonly aggregate: PhysicsAggregate;
+
readonly postProcesses: PostProcessType[] = [];
readonly instance: InstancedMesh;
readonly ringInstances: InstancedMesh[] = [];
+ readonly ringAggregates: PhysicsAggregate[] = [];
+
+ readonly landingPads: LandingPad[] = [];
readonly parent: OrbitalObject | null = null;
- constructor(scene: Scene, parentBody: OrbitalObject | null = null) {
+ constructor(scene: Scene, parentBody: CelestialBody | null = null) {
//TODO: do not hardcode name
this.name = "Spacestation";
@@ -55,16 +67,75 @@ export class SpaceStation implements OrbitalObject, Cullable {
this.parent = parentBody;
this.instance = Assets.CreateSpaceStationInstance();
- this.instance.parent = this.getTransform();
+
+ this.aggregate = new PhysicsAggregate(
+ this.getTransform(),
+ PhysicsShapeType.CONTAINER,
+ {
+ mass: 0,
+ restitution: 0.2
+ },
+ scene
+ );
+
+ this.aggregate.body.setMotionType(PhysicsMotionType.STATIC);
+ this.aggregate.shape.filterMembershipMask = CollisionMask.ENVIRONMENT;
+ this.aggregate.shape.filterCollideMask = CollisionMask.DYNAMIC_OBJECTS;
+
+ this.aggregate.body.setCollisionCallbackEnabled(true);
+ this.aggregate.body.getCollisionObservable().add(() => {
+ console.log("collision!");
+ });
+
+ this.aggregate.body.setMassProperties({ inertia: Vector3.Zero(), mass: 0 });
for (const mesh of this.instance.getChildMeshes()) {
- if (mesh.name.includes("ring")) {
+ if (mesh.name.toLowerCase().includes("landingpad")) {
+ const childShape = new PhysicsShapeConvexHull(mesh as Mesh, scene);
+ childShape.filterMembershipMask = CollisionMask.ENVIRONMENT;
+ childShape.filterCollideMask = CollisionMask.DYNAMIC_OBJECTS;
+ this.aggregate.shape.addChildFromParent(this.getTransform(), childShape, mesh);
+
+ const landingPad = new LandingPad(scene, mesh);
+ this.landingPads.push(landingPad);
+
+ /*const constraint = new LockConstraint(Vector3.Zero(), landingPad.getTransform().position.negate(), new Vector3(0, 1, 0), new Vector3(0, 1, 0), scene);
+ this.aggregate.body.addConstraint(landingPad.aggregate.body, constraint);*/
+
+ continue;
+ }
+
+ if (mesh.name.toLowerCase().includes("ring")) {
this.ringInstances.push(mesh as InstancedMesh);
+
+ const ringAggregate = new PhysicsAggregate(mesh, PhysicsShapeType.MESH, { mass: 0, restitution: 0.2 }, scene);
+ ringAggregate.body.disablePreStep = false;
+ this.ringAggregates.push(ringAggregate);
+
+ const constraint = new LockConstraint(Vector3.Zero(), mesh.position.negate(), new Vector3(0, 1, 0), new Vector3(0, 1, 0), scene);
+ this.aggregate.body.addConstraint(ringAggregate.body, constraint);
+
+ continue;
}
+
+ const childShape = new PhysicsShapeMesh(mesh as Mesh, scene);
+ childShape.filterMembershipMask = CollisionMask.ENVIRONMENT;
+ childShape.filterCollideMask = CollisionMask.DYNAMIC_OBJECTS;
+ this.aggregate.shape.addChildFromParent(this.getTransform(), childShape, mesh);
}
- this.getTransform().rotate(Axis.X, this.model.physicalProperties.axialTilt);
- this.getTransform().rotate(Axis.Y, this.model.physicalProperties.axialTilt);
+ this.aggregate.body.disablePreStep = false;
+
+ console.log("found", this.landingPads.length, "landing pads");
+ }
+
+ handleDockingRequest(): LandingPad | null {
+ const availableLandingPads = this.landingPads;
+ const nbPads = availableLandingPads.length;
+
+ if (nbPads === 0) return null;
+
+ return availableLandingPads[Math.floor(Math.random() * nbPads)];
}
getTransform(): TransformNode {
@@ -98,18 +169,6 @@ export class SpaceStation implements OrbitalObject, Cullable {
}
}
- public updateRotation(deltaTime: number): number {
- const dtheta = deltaTime / this.model.physicalProperties.rotationPeriod;
-
- if (this.ringInstances.length === 0) this.instance.rotate(Axis.Z, dtheta);
- else {
- for (const ring of this.ringInstances) {
- ring.rotate(Axis.Y, dtheta);
- }
- }
- return dtheta;
- }
-
public dispose(): void {
this.instance.dispose();
}
diff --git a/src/ts/spacestation/spacestationModel.ts b/src/ts/spacestation/spacestationModel.ts
index 059c29c43..4c553916c 100644
--- a/src/ts/spacestation/spacestationModel.ts
+++ b/src/ts/spacestation/spacestationModel.ts
@@ -16,13 +16,13 @@
// along with this program. If not, see .
import { seededSquirrelNoise } from "squirrel-noise";
-import { Settings } from "../settings";
import { GENERATION_STEPS } from "../model/common";
import { OrbitProperties } from "../orbit/orbitProperties";
import { getOrbitalPeriod } from "../orbit/orbit";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { OrbitalObjectModel } from "../architecture/orbitalObject";
import { OrbitalObjectPhysicalProperties } from "../architecture/physicalProperties";
+import { CelestialBodyModel } from "../architecture/celestialBody";
export class SpaceStationModel implements OrbitalObjectModel {
readonly seed: number;
@@ -32,15 +32,14 @@ export class SpaceStationModel implements OrbitalObjectModel {
readonly parentBody: OrbitalObjectModel | null;
readonly childrenBodies: OrbitalObjectModel[] = [];
- constructor(seed: number, parentBody?: OrbitalObjectModel) {
+ constructor(seed: number, parentBody?: CelestialBodyModel) {
this.seed = seed;
this.rng = seededSquirrelNoise(this.seed);
this.parentBody = parentBody ?? null;
this.childrenBodies = [];
- //TODO: do not hardcode
- const orbitRadius = 3 * Settings.EARTH_RADIUS;
+ const orbitRadius = 3 * (parentBody?.radius ?? 0);
this.orbit = {
radius: orbitRadius,
diff --git a/src/ts/starSystem/StarSystemView.ts b/src/ts/starSystem/StarSystemView.ts
index ec041eb74..f0ea3e7f2 100644
--- a/src/ts/starSystem/StarSystemView.ts
+++ b/src/ts/starSystem/StarSystemView.ts
@@ -131,8 +131,8 @@ export class StarSystemView {
ambientLight.intensity = 0.3;
this.scene.onBeforePhysicsObservable.add(() => {
- const deltaTime = engine.getDeltaTime() / 1000;
- this.update(deltaTime);
+ const deltaSeconds = engine.getDeltaTime() / 1000;
+ this.update(deltaSeconds * Settings.TIME_MULTIPLIER);
});
window.addEventListener("resize", () => {
@@ -156,8 +156,8 @@ export class StarSystemView {
const firstBody = this.getStarSystem().getBodies()[0];
if (firstBody === undefined) throw new Error("No bodies in star system");
- this.orbitRenderer.setOrbitalObjects(this.getStarSystem().getBodies());
- this.axisRenderer.setObjects(this.getStarSystem().getBodies());
+ this.orbitRenderer.setOrbitalObjects(this.getStarSystem().getOrbitalObjects());
+ this.axisRenderer.setObjects(this.getStarSystem().getOrbitalObjects());
const activeController = this.scene.getActiveController();
let controllerDistanceFactor = 5;
@@ -206,14 +206,18 @@ export class StarSystemView {
this.scene.setActiveController(this.spaceshipControls);
}
- update(deltaTime: number) {
+ /**
+ * Updates the system view. It updates the underlying star system, the UI, the chunk forge and the controls
+ * @param deltaSeconds the time elapsed since the last update in seconds
+ */
+ update(deltaSeconds: number) {
const starSystem = this.getStarSystem();
- Assets.ButterflyMaterial.update(starSystem.stellarObjects, this.scene.getActiveController().getTransform().getAbsolutePosition(), deltaTime);
- Assets.GrassMaterial.update(starSystem.stellarObjects, this.scene.getActiveController().getTransform().getAbsolutePosition(), deltaTime);
+ Assets.ButterflyMaterial.update(starSystem.stellarObjects, this.scene.getActiveController().getTransform().getAbsolutePosition(), deltaSeconds);
+ Assets.GrassMaterial.update(starSystem.stellarObjects, this.scene.getActiveController().getTransform().getAbsolutePosition(), deltaSeconds);
this.chunkForge.update();
- starSystem.update(deltaTime * Settings.TIME_MULTIPLIER, this.chunkForge);
+ starSystem.update(deltaSeconds, this.chunkForge);
if (this.spaceshipControls === null) throw new Error("Spaceship controls is null");
if (this.characterControls === null) throw new Error("Character controls is null");
@@ -225,14 +229,11 @@ export class StarSystemView {
this.spaceshipControls.spaceship.registerClosestObject(distance, radius);
const warpDrive = this.spaceshipControls.spaceship.getWarpDrive();
- const shipInternalThrottle = warpDrive.getInternalThrottle();
- const shipTargetThrottle = warpDrive.getTargetThrottle();
-
- const throttleString = warpDrive.isEnabled()
- ? `${parsePercentageFrom01(shipInternalThrottle)}/${parsePercentageFrom01(shipTargetThrottle)}`
- : `${parsePercentageFrom01(this.spaceshipControls.spaceship.getThrottle())}/100%`;
-
- (document.querySelector("#speedometer") as HTMLElement).innerHTML = `${throttleString} | ${parseSpeed(this.spaceshipControls.spaceship.getSpeed())}`;
+ if(warpDrive.isEnabled()) {
+ this.helmetOverlay.displaySpeed(warpDrive.getInternalThrottle(), warpDrive.getTargetThrottle(), this.spaceshipControls.spaceship.getSpeed());
+ } else {
+ this.helmetOverlay.displaySpeed(this.spaceshipControls.spaceship.getThrottle(), 100, this.spaceshipControls.spaceship.getSpeed());
+ }
this.characterControls.setClosestWalkableObject(nearestBody);
this.spaceshipControls.spaceship.setClosestWalkableObject(nearestBody);
diff --git a/src/ts/starSystem/starSystemController.ts b/src/ts/starSystem/starSystemController.ts
index 04dcde9ea..422269695 100644
--- a/src/ts/starSystem/starSystemController.ts
+++ b/src/ts/starSystem/starSystemController.ts
@@ -187,7 +187,7 @@ export class StarSystemController {
smallerDistance = -1;
for (const spacestation of this.spaceStations) {
- const distance = spacestation.getTransform().getAbsolutePosition().subtract(position).length() - spacestation.getBoundingRadius() * 50;
+ const distance = spacestation.getTransform().getAbsolutePosition().subtract(position).length() - spacestation.getBoundingRadius() * 10;
if (distance < smallerDistance && distance < 0) {
nearest = spacestation;
smallerDistance = distance;
@@ -331,75 +331,84 @@ export class StarSystemController {
}
/**
- * Updates the system and all its bodies forward in time by the given delta time
+ * Updates the system and all its orbital objects forward in time by the given delta time.
+ * The nearest object is kept in place and the other objects are updated accordingly.
* @param deltaTime The time elapsed since the last update
- * @param chunkForge
+ * @param chunkForge The chunk forge used to update the LOD of the telluric planets
*/
public update(deltaTime: number, chunkForge: ChunkForge): void {
const controller = this.scene.getActiveController();
this.computeNearestOrbitalObject(controller.getActiveCamera().globalPosition);
this.computeClosestToScreenCenterOrbitalObject();
- const nearestBody = this.getNearestOrbitalObject();
-
- const distanceOfNearestToCamera = Vector3.Distance(nearestBody.getTransform().getAbsolutePosition(), controller.getActiveCamera().globalPosition);
- const shouldCompensateTranslation = distanceOfNearestToCamera < nearestBody.getBoundingRadius() * (nearestBody instanceof SpaceStation ? 80 : 10);
- const shouldCompensateRotation = distanceOfNearestToCamera < nearestBody.getBoundingRadius() * 4;
- //nearestBody.updateInternalClock(deltaTime);
- const initialPosition = nearestBody.getTransform().getAbsolutePosition();
- const newPosition = OrbitalObject.GetNextOrbitalPosition(nearestBody, deltaTime);
- const nearestBodyDisplacement = newPosition.subtract(initialPosition);
- if (!shouldCompensateTranslation) translate(nearestBody.getTransform(), nearestBodyDisplacement);
+ // The nearest body might have to be treated separatly
+ // The first step is to find the nearest body
+ const nearestBody = this.getNearestOrbitalObject();
- const dthetaNearest = OrbitalObject.GetRotationAngle(nearestBody, deltaTime);
+ // Depending on the distance to the nearest body, we might have to compensate its translation and/or rotation
+ // If we are very close, we want both translation and rotation to be compensated, so that the body appears to be fixed
+ // When we are a bit further, we only need to compensate the translation as it would be unnatural not to see the body rotating
+ const distanceOfNearestToControls = Vector3.Distance(nearestBody.getTransform().getAbsolutePosition(), controller.getTransform().getAbsolutePosition());
+ const shouldCompensateTranslation = distanceOfNearestToControls < nearestBody.getBoundingRadius() * (nearestBody instanceof SpaceStation ? 80 : 10);
+ const shouldCompensateRotation = !(nearestBody instanceof SpaceStation) && distanceOfNearestToControls < nearestBody.getBoundingRadius() * 4;
+
+ // ROTATION COMPENSATION
+ // If we have to compensate the rotation of the nearest body, there are multiple things to take into account
+ // The orbital plane of the body can be described using its normal vector. When the body is not rotating, the normal vector will rotate in its stead.
+ // You can draw a simple example to understand this: have a simple planet and its moon, but the moon's rotation axis on itself is tilted heavily.
+ // Therefore, we have to rotate all the orbital planes accordingly.
+ // Using the same example as before, it is trivial to see the planet will have to rotate around its moon.
+ // Adding more bodies, we see that all bodies must rotate around the fixed moon.
+ // By doing so, their rotation axis on themselves except the fixed one must as well be rotated in the same way.
+ // Last but not least, the background starfield must be rotated in the opposite direction to give the impression the moon is rotating.
+ if (shouldCompensateRotation) {
+ const dthetaNearest = OrbitalObject.GetRotationAngle(nearestBody, deltaTime);
- // if we don't compensate the rotation of the nearest body, we must rotate it accordingly
- if (!shouldCompensateRotation) OrbitalObject.UpdateRotation(nearestBody, deltaTime);
+ for (const object of this.orbitalObjects) {
+ const orbit = object.getOrbitProperties();
- // As the nearest object is kept in place, we need to transfer its movement to other bodies
- for (const object of this.orbitalObjects) {
- const orbit = object.getOrbitProperties();
- const oldOrbitNormal = orbit.normalToPlane.clone();
- if (shouldCompensateRotation) {
// the normal to the orbit planes must be rotated as well (even the one of the nearest body)
const rotation = Quaternion.RotationAxis(nearestBody.getRotationAxis(), -dthetaNearest);
orbit.normalToPlane.applyRotationQuaternionInPlace(rotation);
- }
- if (object === nearestBody) continue;
- if (shouldCompensateTranslation) {
- // the body is translated so that the nearest body can stay in place
- translate(object.getTransform(), nearestBodyDisplacement.negate());
- }
+ if (object === nearestBody) continue;
- if (shouldCompensateRotation) {
- // if the nearest body does not rotate, all other bodies must revolve around it for consistency
+ // All other bodies must revolve around it for consistency (finally we can say the sun revolves around the earth!)
rotateAround(object.getTransform(), nearestBody.getTransform().getAbsolutePosition(), nearestBody.getRotationAxis(), -dthetaNearest);
-
- // we must as well rotate their rotation axis to keep consistency
- const newNormal = orbit.normalToPlane.clone();
- const angle = Math.acos(Vector3.Dot(oldOrbitNormal, newNormal));
- if (angle > 0.02) {
- // FIXME: when time goes very fast, this will get wrongfully executed
- const axis = Vector3.Cross(oldOrbitNormal, newNormal);
- const quaternion = Quaternion.RotationAxis(axis, angle);
- const newRotationAxis = object.getRotationAxis().applyRotationQuaternion(quaternion);
- setUpVector(object.getTransform(), newRotationAxis);
- }
}
- }
- if (shouldCompensateRotation) {
- // the starfield is rotated to give the impression the nearest body is rotating, which is not the case
+ // the starfield is rotated to give the impression the nearest body is rotating, which is only an illusion
const starfieldAdditionalRotation = Quaternion.RotationAxis(nearestBody.getRotationAxis(), dthetaNearest);
this.universeRotation.copyFrom(starfieldAdditionalRotation.multiply(this.universeRotation));
+ } else {
+ // if we don't compensate the rotation of the nearest body, we must simply update its rotation
+ OrbitalObject.UpdateRotation(nearestBody, deltaTime);
+ }
+
+ // TRANSLATION COMPENSATION
+ // Compensating the translation is much easier in comparison. We save the initial position of the nearest body and
+ // compute what would be its next position if it were to move normally.
+ // This gives us a translation vector that we can negate and apply to all other bodies.
+ const initialPosition = nearestBody.getTransform().getAbsolutePosition().clone();
+ const newPosition = OrbitalObject.GetNextOrbitalPosition(nearestBody, deltaTime);
+ const nearestBodyDisplacement = newPosition.subtract(initialPosition);
+ if (shouldCompensateTranslation) {
+ const negatedDisplacement = nearestBodyDisplacement.negate();
+ for (const object of this.orbitalObjects) {
+ if (object === nearestBody) continue;
+
+ // the body is translated so that the nearest body can stay in place
+ translate(object.getTransform(), negatedDisplacement);
+ }
+ } else {
+ // if we don't compensate the translation of the nearest body, we must simply update its position
+ translate(nearestBody.getTransform(), nearestBodyDisplacement);
}
// finally, all other objects are updated normally
for (const object of this.orbitalObjects) {
if (object === nearestBody) continue;
- //object.updateInternalClock(deltaTime);
OrbitalObject.UpdateOrbitalPosition(object, deltaTime);
OrbitalObject.UpdateRotation(object, deltaTime);
}
diff --git a/src/ts/starSystem/starSystemHelper.ts b/src/ts/starSystem/starSystemHelper.ts
index fc81f7f3b..d341b835c 100644
--- a/src/ts/starSystem/starSystemHelper.ts
+++ b/src/ts/starSystem/starSystemHelper.ts
@@ -34,6 +34,8 @@ import { getMoonSeed } from "../planets/common";
import { Planet } from "../architecture/planet";
import { StellarObject } from "../architecture/stellarObject";
import { BODY_TYPE } from "../model/common";
+import { SpaceStation } from "../spacestation/spaceStation";
+import { CelestialBody } from "../architecture/celestialBody";
export class StarSystemHelper {
public static makeStar(starsystem: StarSystemController, model?: number | StarModel): Star {
@@ -90,15 +92,20 @@ export class StarSystemHelper {
*/
public static makeStellarObject(starsystem: StarSystemController, seed: number = starsystem.model.getStarSeed(starsystem.stellarObjects.length)): StellarObject {
const stellarObjectType = starsystem.model.getBodyTypeOfStar(starsystem.stellarObjects.length);
- switch (stellarObjectType) {
- case BODY_TYPE.BLACK_HOLE:
- return StarSystemHelper.makeBlackHole(starsystem, seed);
- case BODY_TYPE.NEUTRON_STAR:
- return StarSystemHelper.makeNeutronStar(starsystem, seed);
- case BODY_TYPE.STAR:
- return StarSystemHelper.makeStar(starsystem, seed);
- default:
- throw new Error(`Unknown stellar object type ${stellarObjectType}`);
+ if(stellarObjectType === BODY_TYPE.BLACK_HOLE) {
+ const blackHole = StarSystemHelper.makeBlackHole(starsystem, seed);
+ StarSystemHelper.makeSpaceStations(starsystem, blackHole);
+ return blackHole;
+ } else if(stellarObjectType === BODY_TYPE.NEUTRON_STAR) {
+ const neutronStar = StarSystemHelper.makeNeutronStar(starsystem, seed);
+ StarSystemHelper.makeSpaceStations(starsystem, neutronStar);
+ return neutronStar;
+ } else if(stellarObjectType === BODY_TYPE.STAR) {
+ const star = StarSystemHelper.makeStar(starsystem, seed);
+ StarSystemHelper.makeSpaceStations(starsystem, star);
+ return star;
+ } else {
+ throw new Error(`Unknown stellar object type ${stellarObjectType}`);
}
}
@@ -123,6 +130,7 @@ export class StarSystemHelper {
): TelluricPlanet {
const planet = new TelluricPlanet(`${starsystem.model.getName()} ${romanNumeral(starsystem.planets.length + 1)}`, starsystem.scene, model, starsystem.stellarObjects[0]);
starsystem.addTelluricPlanet(planet);
+
return planet;
}
@@ -141,22 +149,29 @@ export class StarSystemHelper {
console.assert(n >= 0, `Cannot make a negative amount of planets : ${n}`);
for (let i = 0; i < n; i++) {
- switch (starsystem.model.getBodyTypeOfPlanet(starsystem.planets.length)) {
- case BODY_TYPE.TELLURIC_PLANET:
- StarSystemHelper.makeSatellites(starsystem, StarSystemHelper.makeTelluricPlanet(starsystem));
- break;
- case BODY_TYPE.GAS_PLANET:
- StarSystemHelper.makeSatellites(starsystem, StarSystemHelper.makeGasPlanet(starsystem));
- break;
- case BODY_TYPE.MANDELBULB:
- StarSystemHelper.makeMandelbulb(starsystem);
- break;
- default:
- throw new Error(`Unknown body type ${starsystem.model.getBodyTypeOfPlanet(starsystem.planets.length)}`);
+ const bodyType = starsystem.model.getBodyTypeOfPlanet(starsystem.planets.length);
+ if(bodyType === BODY_TYPE.TELLURIC_PLANET) {
+ const planet = StarSystemHelper.makeTelluricPlanet(starsystem);
+ StarSystemHelper.makeSatellites(starsystem, planet);
+ StarSystemHelper.makeSpaceStations(starsystem, planet);
+ } else if(bodyType === BODY_TYPE.GAS_PLANET) {
+ const planet = StarSystemHelper.makeGasPlanet(starsystem);
+ StarSystemHelper.makeSatellites(starsystem, planet);
+ StarSystemHelper.makeSpaceStations(starsystem, planet);
+ } else {
+ throw new Error(`Unknown body type ${bodyType}`);
}
}
}
+ public static makeSpaceStations(starsystem: StarSystemController, body: CelestialBody, n = body.model.getNbSpaceStations()): void {
+ console.assert(n >= 0, `Cannot make a negative amount of space stations : ${n}`);
+ for (let i = 0; i < n; i++) {
+ const spacestation = new SpaceStation(starsystem.scene, body);
+ starsystem.addSpaceStation(spacestation);
+ }
+ }
+
public static makeSatellite(
starsystem: StarSystemController,
planet: Planet,
diff --git a/src/ts/starSystem/starSystemModel.ts b/src/ts/starSystem/starSystemModel.ts
index d8a8ab48f..ec35e97da 100644
--- a/src/ts/starSystem/starSystemModel.ts
+++ b/src/ts/starSystem/starSystemModel.ts
@@ -83,7 +83,6 @@ export class StarSystemModel {
}
public getBodyTypeOfPlanet(index: number) {
- if (uniformRandBool(0.01, this.rng, GENERATION_STEPS.CHOOSE_PLANET_TYPE + (index + 20) * 500)) return BODY_TYPE.MANDELBULB;
if (uniformRandBool(0.5, this.rng, GENERATION_STEPS.CHOOSE_PLANET_TYPE + index)) return BODY_TYPE.TELLURIC_PLANET;
return BODY_TYPE.GAS_PLANET;
}
diff --git a/src/ts/stellarObjects/blackHole/blackHoleModel.ts b/src/ts/stellarObjects/blackHole/blackHoleModel.ts
index cf042bcc1..dbd54940b 100644
--- a/src/ts/stellarObjects/blackHole/blackHoleModel.ts
+++ b/src/ts/stellarObjects/blackHole/blackHoleModel.ts
@@ -18,7 +18,7 @@
import { seededSquirrelNoise } from "squirrel-noise";
import { getOrbitalPeriod } from "../../orbit/orbit";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
-import { normalRandom } from "extended-random";
+import { normalRandom, uniformRandBool } from "extended-random";
import { OrbitProperties } from "../../orbit/orbitProperties";
import { BODY_TYPE, GENERATION_STEPS } from "../../model/common";
import { BlackHolePhysicalProperties } from "../../architecture/physicalProperties";
@@ -71,4 +71,9 @@ export class BlackHoleModel implements StellarObjectModel {
accretionDiskRadius: 8000e3
};
}
+
+ public getNbSpaceStations(): number {
+ if(uniformRandBool(0.1, this.rng, GENERATION_STEPS.SPACE_STATION)) return 1;
+ return 0;
+ }
}
diff --git a/src/ts/stellarObjects/neutronStar/neutronStarModel.ts b/src/ts/stellarObjects/neutronStar/neutronStarModel.ts
index f9b52f67c..e09286dcd 100644
--- a/src/ts/stellarObjects/neutronStar/neutronStarModel.ts
+++ b/src/ts/stellarObjects/neutronStar/neutronStarModel.ts
@@ -84,4 +84,9 @@ export class NeutronStarModel implements StellarObjectModel {
this.ringsUniforms = null;
}
}
+
+ public getNbSpaceStations(): number {
+ if(uniformRandBool(0.00001, this.rng, GENERATION_STEPS.SPACE_STATION)) return 1;
+ return 0;
+ }
}
diff --git a/src/ts/stellarObjects/star/starModel.ts b/src/ts/stellarObjects/star/starModel.ts
index d86789d3a..1b4872d12 100644
--- a/src/ts/stellarObjects/star/starModel.ts
+++ b/src/ts/stellarObjects/star/starModel.ts
@@ -96,6 +96,11 @@ export class StarModel implements StellarObjectModel {
this.color.copyFrom(getRgbFromTemperature(temperature));
}
+ public getNbSpaceStations(): number {
+ if(uniformRandBool(0.001, this.rng, GENERATION_STEPS.SPACE_STATION)) return 1;
+ return 0;
+ }
+
static getStellarTypeFromTemperature(temperature: number) {
if (temperature < 3500) return STELLAR_TYPE.M;
else if (temperature < 5000) return STELLAR_TYPE.K;
diff --git a/src/ts/ui/helmetOverlay.ts b/src/ts/ui/helmetOverlay.ts
index 3eb641863..992c68ecb 100644
--- a/src/ts/ui/helmetOverlay.ts
+++ b/src/ts/ui/helmetOverlay.ts
@@ -17,6 +17,7 @@
import overlayHTML from "../../html/helmetOverlay.html";
import { OrbitalObject } from "../architecture/orbitalObject";
+import { parseSpeed } from "../utils/parseToStrings";
export class HelmetOverlay {
private parentNode: HTMLElement;
@@ -41,4 +42,9 @@ export class HelmetOverlay {
public update(currentBody: OrbitalObject) {
this.bodyNamePlate.innerText = currentBody.name;
}
+
+ displaySpeed(shipInternalThrottle: number, shipTargetThrottle: number, speed: number) {
+ const throttleString = `${shipInternalThrottle.toFixed(0)}% | ${shipTargetThrottle.toFixed(0)}%`;
+ (document.querySelector("#speedometer") as HTMLElement).innerText = `${throttleString} | ${parseSpeed(speed)}`;
+ }
}
diff --git a/src/ts/utils/algebra.ts b/src/ts/utils/algebra.ts
index 712170b02..f93a96dcf 100644
--- a/src/ts/utils/algebra.ts
+++ b/src/ts/utils/algebra.ts
@@ -46,11 +46,6 @@ export function getTransformationQuaternion(from: Vector3, to: Vector3): Quatern
return Quaternion.RotationAxis(rotationAxis, angle);
}
-export function rotateVector3AroundInPlace(vector: Vector3, center: Vector3, axis: Vector3, angle: number): Vector3 {
- const rotationQuaternion = Quaternion.RotationAxis(axis, angle);
- return vector.subtractInPlace(center).applyRotationQuaternionInPlace(rotationQuaternion).addInPlace(center);
-}
-
export function flattenVector3Array(vector3Array: Vector3[]): number[] {
const result: number[] = [];
for (const vector3 of vector3Array) {
diff --git a/src/ts/utils/warpTunnel.ts b/src/ts/utils/warpTunnel.ts
index 26da4fb92..8e6352bee 100644
--- a/src/ts/utils/warpTunnel.ts
+++ b/src/ts/utils/warpTunnel.ts
@@ -102,7 +102,7 @@ export class WarpTunnel implements Transformable {
particle.position.addInPlace(direction.scale(Math.random() * 10));
particle.position.addInPlace(this.anchor.getAbsolutePosition());
- particle.velocity.copyFrom(direction.scale(300));
+ particle.velocity.copyFrom(direction.scale(600));
particle.rotationQuaternion = rotationQuaternion;
diff --git a/tests/unit/algebra.test.ts b/tests/unit/algebra.test.ts
index c69012571..7e4db9b02 100644
--- a/tests/unit/algebra.test.ts
+++ b/tests/unit/algebra.test.ts
@@ -16,10 +16,8 @@
// along with this program. If not, see .
import { Scene } from "@babylonjs/core/scene";
-import { Vector3 } from "@babylonjs/core/Maths/math";
import { NullEngine } from "@babylonjs/core/Engines/nullEngine";
import { TransformNode } from "@babylonjs/core/Meshes";
-import { rotateVector3AroundInPlace } from "../../src/ts/utils/algebra";
const engine = new NullEngine();
const scene = new Scene(engine);
@@ -27,16 +25,4 @@ const scene = new Scene(engine);
describe("BasicTransform", () => {
const transform = new TransformNode("transform", scene);
it("exists", () => expect(transform).toBeDefined());
- transform.setAbsolutePosition(new Vector3(10, 0, 0));
-
- const pivotPoint = new Vector3(0, 5, 0);
- const rotationAxis = new Vector3(0, 1, 0);
-
- const finalPosition1 = rotateVector3AroundInPlace(transform.getAbsolutePosition(), pivotPoint, rotationAxis, Math.PI / 2);
- transform.rotateAround(pivotPoint, rotationAxis, Math.PI / 2);
- const finalPosition2 = transform.getAbsolutePosition();
-
- it("can rotate around a pivot", () => {
- expect(finalPosition1.equalsWithEpsilon(finalPosition2)).toBeTruthy();
- });
});
\ No newline at end of file