Added SOI display

- Added SOI checkbox in the editor
- Added management of SOI displays during rendering in `system.ts`
- Replaced body sprites with spheres
This commit is contained in:
Krafpy
2022-07-12 17:35:18 +02:00
parent 82720783e5
commit e0f592cdba
12 changed files with 150 additions and 25 deletions

View File

@@ -11,6 +11,7 @@ solarSystem:
satFarSize: 0.04 # size of satellites sprites
satDispRadii: 10 # minimum display distance of satellites (in radii of the scaled semi major axis)
mouseFocusDst: 25 # minimum distance to between body on screen and mouse to set focus (in pixels)
soiOpacity: 0.3 # the opacity of SOI spheres
orbit:
satSampPoints: 1000 # sample points for satellites' orbits

View File

@@ -21,6 +21,9 @@ export function initEditor(controls, system, config, canvas) {
};
systemTime.input(updateSystemTime);
updateSystemTime();
const soiCheckbox = document.getElementById("soi-checkbox");
soiCheckbox.onchange = () => system.showSOIs = soiCheckbox.checked;
soiCheckbox.checked = false;
const sequenceSelector = new SequenceSelector("sequence-selector");
sequenceSelector.disable();
const originSelector = new BodySelector("origin-selector", system);

1
dist/main/main.js vendored
View File

@@ -25,6 +25,7 @@ async function main() {
requestAnimationFrame(loop);
controls.update();
system.updateSatellitesDisplay(controls);
system.updateSOIsDisplay(controls);
renderer.render(scene, camera);
};
requestAnimationFrame(loop);

View File

@@ -1,11 +1,13 @@
import { OrbitingBody, CelestialBody } from "./body.js";
import { createLine, createOrbitPoints, createSprite } from "../utilities/geometry.js";
import * as Geometry from "../utilities/geometry.js";
export class SolarSystem {
constructor(sun, bodies, config) {
this.config = config;
this._orbiting = new Map();
this._objects = new Map();
this._orbits = new Map();
this._sois = new Map();
this.showSOIs = false;
this.sun = new CelestialBody(sun);
for (const data of bodies) {
const { orbiting } = data;
@@ -56,31 +58,43 @@ export class SolarSystem {
const textureLoader = new THREE.TextureLoader();
return new Promise((resolve, _) => {
const loaded = (texture) => {
const material = new THREE.SpriteMaterial({
const spriteMaterial = new THREE.SpriteMaterial({
map: texture
});
const { scale } = this.config.rendering;
const { satSampPoints, planetSampPoints, orbitLineWidth } = this.config.orbit;
const { planetFarSize, satFarSize } = this.config.solarSystem;
const sunSprite = createSprite(material, this.sun.color, true, scale * this.sun.radius * 2);
const { soiOpacity } = this.config.solarSystem;
const sunSprite = Geometry.createSprite(spriteMaterial, this.sun.color, true, scale * this.sun.radius * 2);
const sunGroup = new THREE.Group();
sunGroup.add(sunSprite);
this._objects.set(0, sunGroup);
for (const body of this.orbiting) {
const { radius, orbit, color, attractor } = body;
const { radius, soi, orbit, color, attractor } = body;
const parentGroup = this._objects.get(attractor.id);
const bodyGroup = new THREE.Group();
const samplePts = attractor.id == 0 ? planetSampPoints : satSampPoints;
const spriteSize = attractor.id == 0 ? planetFarSize : satFarSize;
const orbitPoints = createOrbitPoints(orbit, samplePts, scale);
const ellipse = createLine(orbitPoints, canvas, {
const orbitPoints = Geometry.createOrbitPoints(orbit, samplePts, scale);
const ellipse = Geometry.createLine(orbitPoints, canvas, {
color: color,
linewidth: orbitLineWidth,
});
parentGroup.add(ellipse);
this._orbits.set(body.id, ellipse);
bodyGroup.add(createSprite(material, color, true, scale * radius * 2));
bodyGroup.add(createSprite(material, color, false, spriteSize));
const soiMaterial = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: soiOpacity
});
const soiLines = Geometry.createWireframeSphere(soi, scale, soiMaterial);
bodyGroup.add(soiLines);
this._sois.set(body.id, soiLines);
const bodyMaterial = new THREE.MeshBasicMaterial({
color: color
});
bodyGroup.add(Geometry.createSphere(radius, scale, bodyMaterial));
bodyGroup.add(Geometry.createSprite(spriteMaterial, color, false, spriteSize));
parentGroup.add(bodyGroup);
this._objects.set(body.id, bodyGroup);
}
@@ -115,4 +129,33 @@ export class SolarSystem {
}
}
}
updateSOIsDisplay(camController) {
if (!this.showSOIs) {
for (const sphere of this._sois.values()) {
sphere.visible = false;
}
}
else {
const camPos = camController.camera.position;
const { scale } = this.config.rendering;
for (const body of this.orbiting) {
const group = this._objects.get(body.id);
const objPos = new THREE.Vector3();
group.getWorldPosition(objPos);
const dstToCam = camPos.distanceTo(objPos);
const sphere = this._sois.get(body.id);
sphere.visible = dstToCam > body.soi * scale;
}
for (const body of this.orbiting) {
const soi = this._sois.get(body.id);
const attrSoi = this._sois.get(body.attractor.id);
if (attrSoi === null || attrSoi === void 0 ? void 0 : attrSoi.visible) {
soi.visible = false;
}
else {
soi.visible && (soi.visible = true);
}
}
}
}
}

View File

@@ -1,9 +1,14 @@
export function createSphere(radius, scale, color) {
export function createSphere(radius, scale, material) {
const geometry = new THREE.SphereGeometry(radius * scale, 50, 50);
const material = new THREE.MeshBasicMaterial({ color: color });
const mesh = new THREE.Mesh(geometry, material);
const mesh = new THREE.Mesh(geometry, material.clone());
return mesh;
}
export function createWireframeSphere(radius, scale, material) {
const geometry = new THREE.SphereGeometry(radius * scale, 15, 15);
const wireframe = new THREE.WireframeGeometry(geometry);
const lines = new THREE.LineSegments(wireframe, material.clone());
return lines;
}
export function createSprite(material, color, sizeAttenuation, scale) {
const sprite = new THREE.Sprite(material.clone());
sprite.center.set(0.5, 0.5);

View File

@@ -217,6 +217,10 @@
<strong>Double click:</strong> focus on body -
<strong>Mouse wheel:</strong> zoom
</p>
<div id="soi-toggle-box">
<input type="checkbox" id="soi-checkbox" name="soi-checkbox">
<label for="soi-checkbox">Show SOIs</label>
</div>
</div>
</div>
<!-- End of the 3D display panel -->

View File

@@ -25,7 +25,13 @@ export function initEditor(controls: CameraController, system: SolarSystem, conf
};
systemTime.input(updateSystemTime);
updateSystemTime();
// SOI toggle
const soiCheckbox = document.getElementById("soi-checkbox") as HTMLInputElement;
soiCheckbox.onchange = () => system.showSOIs = soiCheckbox.checked;
soiCheckbox.checked = false; // default
// Sequence generation panel
const sequenceSelector = new SequenceSelector("sequence-selector");
sequenceSelector.disable();

View File

@@ -40,6 +40,7 @@ async function main(){
requestAnimationFrame(loop);
controls.update();
system.updateSatellitesDisplay(controls);
system.updateSOIsDisplay(controls);
renderer.render(scene, camera);
}
requestAnimationFrame(loop);

View File

@@ -1,12 +1,14 @@
import { OrbitingBody, CelestialBody } from "./body.js";
import { CameraController } from "./camera.js";
import { /*createSphere,*/ createLine, createOrbitPoints, createSprite } from "../utilities/geometry.js";
import * as Geometry from "../utilities/geometry.js";
export class SolarSystem {
readonly sun!: CelestialBody;
private readonly _orbiting: Map<number, OrbitingBody> = new Map();
private readonly _objects: Map<number, THREE.Group> = new Map();
private readonly _orbits: Map<number, THREE.Object3D> = new Map();
private readonly _sois: Map<number, THREE.Object3D> = new Map();
public showSOIs: boolean = false;
constructor(sun: ICelestialBody, bodies: IOrbitingBody[], public readonly config: Config) {
this.sun = new CelestialBody(sun);
@@ -68,36 +70,37 @@ export class SolarSystem {
* Creates the 3D objects of all the celestial bodies.
* @returns The promise to wait for the loading to complete.
*/
public fillSceneObjects(scene: THREE.Scene, canvas: HTMLCanvasElement) {
public fillSceneObjects(scene: THREE.Scene, canvas: HTMLCanvasElement) {
const textureLoader = new THREE.TextureLoader();
return new Promise(
(resolve, _) => {
const loaded = (texture: THREE.Texture) => {
const material = new THREE.SpriteMaterial({
const spriteMaterial = new THREE.SpriteMaterial({
map: texture
});
const {scale} = this.config.rendering;
const {satSampPoints, planetSampPoints, orbitLineWidth} = this.config.orbit;
const {planetFarSize, satFarSize} = this.config.solarSystem;
const {soiOpacity} = this.config.solarSystem;
const sunSprite = createSprite(material, this.sun.color, true, scale * this.sun.radius * 2);
const sunSprite = Geometry.createSprite(spriteMaterial, this.sun.color, true, scale * this.sun.radius * 2);
const sunGroup = new THREE.Group();
sunGroup.add(sunSprite);
this._objects.set(0, sunGroup);
for(const body of this.orbiting){
const {radius, orbit, color, attractor} = body;
const {radius, soi, orbit, color, attractor} = body;
const parentGroup = <THREE.Group>this._objects.get(attractor.id);
const bodyGroup = new THREE.Group();
const samplePts = attractor.id == 0 ? planetSampPoints : satSampPoints;
const spriteSize = attractor.id == 0 ? planetFarSize : satFarSize;
const orbitPoints = createOrbitPoints(orbit, samplePts, scale);
const ellipse = createLine(orbitPoints, canvas, {
const orbitPoints = Geometry.createOrbitPoints(orbit, samplePts, scale);
const ellipse = Geometry.createLine(orbitPoints, canvas, {
color: color,
linewidth: orbitLineWidth,
});
@@ -105,9 +108,23 @@ export class SolarSystem {
this._orbits.set(body.id, ellipse);
//bodyGroup.add(createSphere(radius, scale, color));
bodyGroup.add(createSprite(material, color, true, scale * radius * 2));
bodyGroup.add(createSprite(material, color, false, spriteSize));
// Create the SOI sphere
const soiMaterial = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: soiOpacity
});
const soiLines = Geometry.createWireframeSphere(soi, scale, soiMaterial)
bodyGroup.add(soiLines);
this._sois.set(body.id, soiLines);
// Create body sphere and sprites
const bodyMaterial = new THREE.MeshBasicMaterial({
color: color
});
bodyGroup.add(Geometry.createSphere(radius, scale, bodyMaterial));
bodyGroup.add(Geometry.createSprite(spriteMaterial, color, false, spriteSize));
parentGroup.add(bodyGroup);
this._objects.set(body.id, bodyGroup);
@@ -158,4 +175,39 @@ export class SolarSystem {
}
}
}
/**
* Updates how SOIs are displayed based on camera position.
*/
public updateSOIsDisplay(camController: CameraController){
if(!this.showSOIs){
for(const sphere of this._sois.values()){
sphere.visible = false;
}
} else {
const camPos = camController.camera.position;
const {scale} = this.config.rendering;
for(const body of this.orbiting){
const group = <THREE.Group>this._objects.get(body.id);
const objPos = new THREE.Vector3();
group.getWorldPosition(objPos);
const dstToCam = camPos.distanceTo(objPos);
const sphere = <THREE.Object3D>this._sois.get(body.id);
sphere.visible = dstToCam > body.soi * scale;
}
for(const body of this.orbiting){
const soi = <THREE.Object3D>this._sois.get(body.id);
const attrSoi = this._sois.get(body.attractor.id);
if(attrSoi?.visible){
soi.visible = false;
} else {
soi.visible &&= true;
}
}
}
}
}

View File

@@ -1,12 +1,18 @@
import { Orbit } from "../objects/orbit";
export function createSphere(radius: number, scale: number, color: number) {
export function createSphere(radius: number, scale: number, material: THREE.MeshBasicMaterial) {
const geometry = new THREE.SphereGeometry(radius * scale, 50, 50);
const material = new THREE.MeshBasicMaterial({color: color});
const mesh = new THREE.Mesh(geometry, material);
const mesh = new THREE.Mesh(geometry, material.clone());
return mesh;
}
export function createWireframeSphere(radius: number, scale: number, material: THREE.Material){
const geometry = new THREE.SphereGeometry(radius * scale, 15, 15);
const wireframe = new THREE.WireframeGeometry(geometry);
const lines = new THREE.LineSegments(wireframe, material.clone());
return lines;
}
export function createSprite(material: THREE.SpriteMaterial, color: number, sizeAttenuation: boolean, scale: number){
const sprite = new THREE.Sprite(material.clone());
sprite.center.set(0.5, 0.5);

2
src/types.d.ts vendored
View File

@@ -40,6 +40,7 @@ interface SystemDrawSettings {
readonly satFarSize: number;
readonly satDispRadii: number;
readonly mouseFocusDst: number;
readonly soiOpacity: number;
}
interface OrbitSettings {
@@ -89,7 +90,6 @@ interface WorkersSettings {
interface TrajectorySearchSettings {
readonly splitLimit: number;
//readonly crossoverProba: number;
readonly minCrossProba: number;
readonly maxCrossProba: number;
readonly crossProbaIncr: number;

View File

@@ -354,6 +354,9 @@ input[type="text"]::placeholder
{
font-size: 0.75em;
color: rgba(255, 255, 255, 0.459);
display: flex;
justify-content: space-between;
align-items: center;
}
.control-group p