merge perihelion/m-selene → main: moon maps + tidal lock, gate green (fable)

This commit is contained in:
monsterrobotparty 2026-07-16 10:24:47 +10:00
commit 8bfce067d5
11 changed files with 42 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
assets/textures/1k_io.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@ -1,7 +1,9 @@
# Texture credits
All planet, moon, Sun, ring and starfield maps in this directory are from the
**Solar System Scope** texture pack and are used under **CC BY 4.0**.
## `2k_*` planet, Sun, ring & starfield maps — Solar System Scope (CC BY 4.0)
The **`2k_`** planet, Moon, Sun, ring and starfield maps in this directory are from
the **Solar System Scope** texture pack and are used under **CC BY 4.0**.
- Source: https://www.solarsystemscope.com/textures/
- License: Creative Commons Attribution 4.0 International (CC BY 4.0)
@ -28,6 +30,29 @@ Downloaded 2026-07-16 at 2k resolution:
Attribution also appears in the app's HUD footer, as CC BY requires.
## `1k_*` moon maps — USGS Astrogeology / NASA (public domain)
Added wave 2 (PERIHELION · SELENE). Global equirectangular (simple-cylindrical)
mosaics from the **USGS Astrogeology Science Center** (`planetarymaps.usgs.gov`),
built from NASA mission imagery. USGS/NASA-produced planetary data are in the
**U.S. public domain** (no copyright asserted). Each was downloaded as a GeoTIFF
and downscaled to 1024×512 JPEG with `sips` (Europa re-encoded at q70 to stay
under budget). Downloaded 2026-07-16.
| File | Body | Mission / instrument | Source (USGS Astrogeology) | License |
|------|------|----------------------|----------------------------|---------|
| 1k_io.jpg | Io | Galileo SSI + Voyager, color-merge | `.../mosaic/Io_Galileo_SSI_Global_Mosaic_ClrMerge_1km.tif` | Public domain |
| 1k_europa.jpg | Europa | Voyager + Galileo SSI, global 500 m | `.../mosaic/Europa_Voyager_GalileoSSI_global_mosaic_500m.tif` | Public domain |
| 1k_ganymede.jpg | Ganymede | Voyager + Galileo SSI, global color 1435 m | `.../mosaic/Ganymede_Voyager_GalileoSSI_Global_ClrMosaic_1435m.tif` | Public domain |
| 1k_callisto.jpg | Callisto | Voyager + Galileo SSI, global 1 km | `.../mosaic/Callisto_Voyager_GalileoSSI_global_mosaic_1km.tif` | Public domain |
| 1k_titan.jpg | Titan | Cassini ISS, global mosaic (PIA19658), 4 km | `.../mosaic/Titan_ISS_P19658_Mosaic_Global_4km.tif` | Public domain |
| 1k_triton.jpg | Triton | Voyager 2, global color mosaic (gap-filled) 600 m | `.../mosaic/Triton_Voyager2_ClrMosaic_GlobalFill_600m.tif` | Public domain |
| 1k_charon.jpg | Charon | New Horizons LORRI/MVIC, global mosaic 300 m (Jul 2017) | `.../mosaic/Charon_NewHorizons_Global_Mosaic_300m_Jul2017_8bit.tif` | Public domain |
| 1k_phobos.jpg | Phobos | Mars Express HRSC-SRC, global mosaic 16 ppd | `.../mosaic/Phobos_ME_SRC_Mosaic_Global_16ppd.tif` | Public domain |
Host base URL: `https://planetarymaps.usgs.gov/mosaic/` (served from the USGS
`asc-pds-services` bucket). USGS terms: <https://www.usgs.gov/information-policies-and-instructions/copyrights-and-credits>.
**Truth boundary (brief §7b):** these are the *real data* textures — real worlds
get real maps. The stylized grimoire portraits in `../art/grimoire/` are the
*aesthetic* layer (generated art), kept visually and directorially separate.

View File

@ -62,18 +62,18 @@ export const BODIES = {
parent: 'earth', moonOrbit: { aKm: 384400, periodDays: 27.322 } },
phobos: { name: 'Phobos', type: 'moon', radiusKm: 11.27, color: '#8a7c6e',
texture: null, parent: 'mars', moonOrbit: { aKm: 9378, periodDays: 0.319 } },
texture: '1k_phobos.jpg', parent: 'mars', moonOrbit: { aKm: 9378, periodDays: 0.319 } },
deimos: { name: 'Deimos', type: 'moon', radiusKm: 6.2, color: '#9a8c7a',
texture: null, parent: 'mars', moonOrbit: { aKm: 23459, periodDays: 1.262 } },
io: { name: 'Io', type: 'moon', radiusKm: 1821.6, color: '#e8d24b',
texture: null, parent: 'jupiter', moonOrbit: { aKm: 421700, periodDays: 1.769 } },
texture: '1k_io.jpg', parent: 'jupiter', moonOrbit: { aKm: 421700, periodDays: 1.769 } },
europa: { name: 'Europa', type: 'moon', radiusKm: 1560.8, color: '#c9b79c',
texture: null, parent: 'jupiter', moonOrbit: { aKm: 671034, periodDays: 3.551 } },
texture: '1k_europa.jpg', parent: 'jupiter', moonOrbit: { aKm: 671034, periodDays: 3.551 } },
ganymede: { name: 'Ganymede', type: 'moon', radiusKm: 2634.1, color: '#9b8e7e',
texture: null, parent: 'jupiter', moonOrbit: { aKm: 1070412, periodDays: 7.155 } },
texture: '1k_ganymede.jpg', parent: 'jupiter', moonOrbit: { aKm: 1070412, periodDays: 7.155 } },
callisto: { name: 'Callisto', type: 'moon', radiusKm: 2410.3, color: '#6b5f52',
texture: null, parent: 'jupiter', moonOrbit: { aKm: 1882709, periodDays: 16.689 } },
texture: '1k_callisto.jpg', parent: 'jupiter', moonOrbit: { aKm: 1882709, periodDays: 16.689 } },
mimas: { name: 'Mimas', type: 'moon', radiusKm: 198.2, color: '#c8c8c8',
texture: null, parent: 'saturn', moonOrbit: { aKm: 185539, periodDays: 0.942 } },
@ -86,7 +86,7 @@ export const BODIES = {
rhea: { name: 'Rhea', type: 'moon', radiusKm: 763.8, color: '#c4c4c4',
texture: null, parent: 'saturn', moonOrbit: { aKm: 527068, periodDays: 4.518 } },
titan: { name: 'Titan', type: 'moon', radiusKm: 2574.7, color: '#d9a441',
texture: null, parent: 'saturn', moonOrbit: { aKm: 1221870, periodDays: 15.945 } },
texture: '1k_titan.jpg', parent: 'saturn', moonOrbit: { aKm: 1221870, periodDays: 15.945 } },
iapetus: { name: 'Iapetus', type: 'moon', radiusKm: 734.5, color: '#8a7a5e',
texture: null, parent: 'saturn', moonOrbit: { aKm: 3560851, periodDays: 79.33 } },
@ -103,7 +103,7 @@ export const BODIES = {
// Triton is retrograde — encoded via the negative-period convention (brief §8).
triton: { name: 'Triton', type: 'moon', radiusKm: 1353.4, color: '#d6cfc4',
texture: null, parent: 'neptune', moonOrbit: { aKm: 354759, periodDays: -5.877 } },
texture: '1k_triton.jpg', parent: 'neptune', moonOrbit: { aKm: 354759, periodDays: -5.877 } },
// ---- Pluto system (small-body epoch elements; texture-less spheres) ----
pluto: {
@ -114,7 +114,7 @@ export const BODIES = {
w: 113.76329, ma: 14.86012204, epoch: 2451545.0 },
},
charon: { name: 'Charon', type: 'moon', radiusKm: 606.0, color: '#b3a894',
texture: null, parent: 'pluto', moonOrbit: { aKm: 19591, periodDays: 6.387 } },
texture: '1k_charon.jpg', parent: 'pluto', moonOrbit: { aKm: 19591, periodDays: 6.387 } },
};
// Ordered planet ids (draw / menu order).

View File

@ -6,7 +6,8 @@
// focus, to keep the wide view clean (brief §5).
export default function create(ctx) {
const { THREE, scene, CONFIG, ui, scale, bodies, focus, toLocal, effectiveE, makeLabel, registerPick } = ctx;
const { THREE, scene, CONFIG, lib, ui, scale, bodies, focus, toLocal, effectiveE,
makeLabel, registerPick, loadTextureInto } = ctx;
const entries = [];
for (const id in bodies) {
@ -16,6 +17,7 @@ export default function create(ctx) {
const mesh = new THREE.Mesh(new THREE.SphereGeometry(1, 24, 16), mat);
mesh.visible = true;
scene.add(mesh);
loadTextureInto(mat, b.texture); // NASA/USGS map when present; flat color otherwise (§7 fallback)
registerPick(mesh, id);
const lbl = makeLabel(mesh, b.name, 'body-label moon');
lbl.obj.visible = false; // CSS2DRenderer honors obj.visible (not div.style.display)
@ -34,6 +36,10 @@ export default function create(ctx) {
if (!abs) continue;
toLocal(abs, e.mesh.position);
e.mesh.scale.setScalar(scale.drawRadius(e.b.radiusKm, effectiveE(e.b.parent)));
// tidal lock: spin at the SAME signed orbital rate main.js positions by, so
// one face stays toward the parent (negative period → retrograde for free).
const theta = 2 * Math.PI * ((jd - lib.J2000_JD) / e.b.moonOrbit.periodDays);
e.mesh.rotation.y = theta + Math.PI;
const near = focus.id === e.b.parent || focus.id === e.id;
e.lbl.obj.visible = show && near;
e.lbl.div.classList.toggle('focused', e.id === focus.id);