feat: Solar System mode — 'Travel to' the planets (Wave 4)

New '◉ Solar System' mode: leaves the Earth OSINT globe and renders a
heliocentric orrery — Sun + 8 planets as spheres textured with real (CC BY 4.0,
self-hosted) imagery, at their TRUE current positions (Schlyter series, incl.
Uranus/Neptune), with orbits. A 'Travel to…' panel flies the camera to any body;
'Return to Earth' restores the full OSINT view (globe, all layers, clock, camera,
frustum, lighting — all saved/restored). Planets exaggerated + distances
compressed for a readable orrery; Saturn ring; camera headlight for even lighting;
far plane widened to 8e9 (default 5e8 would clip the whole system).

Textures harvested from Solar System Scope (CC BY 4.0), self-hosted in textures/
(WebGL taints cross-origin), credited in-app + README + textures/TEXTURES_CREDIT.md.

Components verified in-browser (ellipsoids render, positions correct, mode
enter/exit, UI, textures). Full-orrery screenshot pending a real GPU frame — the
headless preview can't complete async worker geometry (backgrounded tab clamps).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-20 19:28:43 +10:00
parent b3499381bf
commit 07993c77ff
16 changed files with 378 additions and 1 deletions

41
SPEC4.md Normal file
View File

@ -0,0 +1,41 @@
# GODSIGH — Wave 4 Spec: Solar System mode ("Travel to…")
**Author:** Claude Fable 5, 2026-07-14, on John's request. Built by Fable in the same session.
**Prereq:** Waves 13.2 live at partly.party/godsigh. This is additive — a new *mode*, not a layer.
## 0. Mission
A "Travel to…" experience: leave Earth and fly out to a Sun-centred solar system rendered from free/public data — the 8 planets (+ Sun, Moon) as spheres textured with real imagery, at their true current positions, with orbits, and smooth camera flights between them. Everything computed or harvested from free/CC/public-domain sources (the "harvest it all" ethos, applied to the sky).
## 1. Ground truth — verified 2026-07-14 (CORS-probed)
- **Planet textures:** solarsystemscope.com `/textures/download/2k_*.jpg` (CC BY 4.0) — 200. No ACAO ⇒ **self-host** (WebGL taints cross-origin textures). Download the 2k set into `textures/`.
- **Real surface tiles (Phase 2):** NASA Moon Trek WMTS (`trek.nasa.gov/tiles/Moon/…`) and USGS/OpenPlanetaryMap Mars tiles — both 200 with `ACAO: *`. Usable as Cesium imagery providers.
- **Positions:** computed (Schlyter low-precision series, already proven in `js/layers/celestial.js`).
## 2. Architecture decision (locked)
Cesium's globe is Earth-at-origin, so a Sun-centred scene can't move the globe. Therefore **Solar System mode HIDES the Earth globe** (`scene.globe.show = false`, hide all Earth OSINT layers) and renders a **heliocentric orrery** with entities; exiting restores the Earth OSINT view. The detailed Earth globe stays the "home" view; the orrery is a distinct mode.
- **Bodies:** Sun + Mercury·Venus·Earth·Mars·Jupiter·Saturn·Uranus·Neptune (+ Moon near Earth) as `ellipsoid` entities with `ImageMaterialProperty` (self-hosted textures). Saturn gets a ring (`2k_saturn_ring_alpha.png` on a flat disc / ring geometry). Real axial tilt applied via entity orientation.
- **Positions:** heliocentric xyz from Schlyter (extend the celestial ELEM table with Uranus & Neptune; Earth = sunRect). Compressed distance scale (tune in-browser) + exaggerated body sizes so it reads (true scale is unviewable — Sun↔Neptune is 30 AU vs Sun radius 0.005 AU). Directions/relative order stay true.
- **Orbits:** faint polyline ellipses per planet (sample the orbit over one period).
- **Travel-to:** a mode panel with a destination per body; clicking `camera.flyTo` the body with a sensible offset. "Return to Earth" exits the mode.
- **Skybox/Sun light:** keep Cesium starfield; a point of light at the Sun optional.
## 3. Phases
- **P1 (core, must-ship):** mode toggle + orrery (all 8 planets + Sun + Moon textured, true positions, orbits) + travel-to camera flights + return-to-Earth. Self-hosted 2k textures. Attribution in-app + README.
- **P2 (stretch):** "Land" on **Mars** and the **Moon** with real NASA/USGS tile imagery. Cleanest path that avoids fragile globe-ellipsoid swapping: when travelling to Mars/Moon, load an 8k real-imagery texture (still real, harvested) OR, if a runtime globe swap proves stable, a true tile-imagery explorable body. Attempt the swap; fall back to hi-res texture if fragile.
- **P3 (later, noted):** OSINT-on-other-worlds — Mars rover positions (NASA public APIs), Apollo landing sites, active orbiters.
## 4. Files
- `textures/` — self-hosted CC planet maps (2k set) + `TEXTURES_CREDIT.md` (CC BY 4.0 attribution to solarsystemscope.com).
- `js/solarsystem.js` — the mode: build bodies, orbits, travel-to menu, enter/exit.
- HUD: a "◉ Solar System" toggle (top of the HUD, near the Data/Photo buttons) that enters/exits the mode; a destination panel while inside.
- `main.js` — wire the mode (hide/restore globe + Earth layers + timeline; the mode owns its own render/positions).
## 5. Verify (browser, per phase)
Zero console errors; textures load (self-hosted, same-origin); planets at plausible positions (inner planets near Sun, gas giants far, correct order); travel-to flies smoothly and arrives framed; return-to-Earth restores every layer + the globe exactly. Screenshot the orrery and an arrival. Subpath-safe (`textures/…`, relative). Commit per phase; deploy = rsync `js/` + `textures/` (no nginx change — all static/CORS-open).
## 6. Attribution (license compliance)
Planet textures © solarsystemscope.com, CC BY 4.0 — credit in-app footer + README + `textures/TEXTURES_CREDIT.md`. Mars/Moon tiles: NASA/USGS/OpenPlanetaryMap (public domain / open) — credit when P2 lands.
## 7. Out of scope (Wave 4)
Comets, asteroids, moons beyond Earth's, true-scale mode, VR. Roadmap.

View File

@ -253,3 +253,57 @@ html, body {
/* Keep Cesium's info box on-theme without fighting it */
.cesium-infoBox { font-family: ui-monospace, monospace; }
/* ---- Solar System mode ---- */
#ss-btn {
position: absolute;
top: 14px;
right: 14px;
z-index: 12;
padding: 8px 14px;
font: 12px ui-monospace, "SF Mono", Menlo, monospace;
color: var(--text);
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
cursor: pointer;
letter-spacing: 0.5px;
transition: all 0.15s;
}
#ss-btn:hover { color: #fff; border-color: rgba(255,255,255,0.25); }
#ss-btn.active { color: #04121a; background: var(--accent); border-color: var(--accent); font-weight: 700; }
#ss-panel {
position: absolute;
top: 14px;
left: 14px;
width: 240px;
z-index: 12;
padding: 14px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
font: 12px ui-monospace, Menlo, monospace;
color: var(--text);
}
#ss-panel .ss-title { font-weight: 700; letter-spacing: 1px; color: var(--accent); font-size: 11px; margin-bottom: 10px; }
#ss-panel .ss-dests { display: flex; flex-direction: column; gap: 4px; }
#ss-panel .ss-dest {
text-align: left;
padding: 7px 10px;
font: inherit;
color: var(--text);
background: rgba(255,255,255,0.03);
border: 1px solid var(--border);
border-radius: 8px;
cursor: pointer;
transition: all 0.12s;
}
#ss-panel .ss-dest:hover { background: rgba(53,224,255,0.12); border-color: var(--accent); color: #fff; }
#ss-panel .ss-back { margin-top: 8px; color: var(--dim); }
#ss-panel .ss-foot { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); color: var(--dim); font-size: 9.5px; line-height: 1.4; }
#ss-panel .ss-foot a { color: var(--dim); }

View File

@ -22,6 +22,7 @@ import { REGISTRY } from './registry.js';
import { createGeoJsonLayer } from './layers/geojson-layer.js';
import { ADSB_LAYERS } from './adsb-registry.js';
import { createAdsbLayer } from './layers/adsb-layer.js';
import initSolarSystem from './solarsystem.js';
const Cesium = window.Cesium;
@ -263,4 +264,7 @@ loadLayers().then(() => {
startHashSync();
});
window.__godsigh = { viewer, ctx, activeLayers }; // debug handle
// Solar System mode ("Travel to…") — a separate mode, not a layer.
const solarSystem = initSolarSystem(ctx);
window.__godsigh = { viewer, ctx, activeLayers, solarSystem }; // debug handle

268
js/solarsystem.js Normal file
View File

@ -0,0 +1,268 @@
// Solar System mode (Wave 4) — "Travel to…". Leaves the Earth OSINT globe and
// renders a heliocentric orrery: the Sun + 8 planets as spheres textured with
// real (CC BY 4.0, self-hosted) imagery, at their TRUE current positions
// (Schlyter series), with orbits. Click a destination to fly there.
//
// Cesium's globe is Earth-at-origin, so the mode HIDES the globe + all Earth
// layers and places the Sun at the scene origin; exiting restores everything.
export default function initSolarSystem(ctx) {
const { viewer, Cesium, lib } = ctx;
const scene = viewer.scene;
// ---- astronomy (Schlyter low-precision, heliocentric ecliptic AU) ----
const D2R = Math.PI / 180;
const rev = (x) => x - Math.floor(x / 360) * 360;
const ELEM = {
Mercury: (d) => ({ N: 48.3313 + 3.24587e-5 * d, i: 7.0047 + 5.00e-8 * d, w: 29.1241 + 1.01444e-5 * d, a: 0.387098, e: 0.205635 + 5.59e-10 * d, M: 168.6562 + 4.0923344368 * d }),
Venus: (d) => ({ N: 76.6799 + 2.46590e-5 * d, i: 3.3946 + 2.75e-8 * d, w: 54.8910 + 1.38374e-5 * d, a: 0.723330, e: 0.006773 - 1.302e-9 * d, M: 48.0052 + 1.6021302244 * d }),
Mars: (d) => ({ N: 49.5574 + 2.11081e-5 * d, i: 1.8497 - 1.78e-8 * d, w: 286.5016 + 2.92961e-5 * d, a: 1.523688, e: 0.093405 + 2.516e-9 * d, M: 18.6021 + 0.5240207766 * d }),
Jupiter: (d) => ({ N: 100.4542 + 2.76854e-5 * d, i: 1.3030 - 1.557e-7 * d, w: 273.8777 + 1.64505e-5 * d, a: 5.20256, e: 0.048498 + 4.469e-9 * d, M: 19.8950 + 0.0830853001 * d }),
Saturn: (d) => ({ N: 113.6634 + 2.38980e-5 * d, i: 2.4886 - 1.081e-7 * d, w: 339.3939 + 2.97661e-5 * d, a: 9.55475, e: 0.055546 - 9.499e-9 * d, M: 316.9670 + 0.0334442282 * d }),
Uranus: (d) => ({ N: 74.0005 + 1.3978e-5 * d, i: 0.7733 + 1.9e-8 * d, w: 96.6612 + 3.0565e-5 * d, a: 19.18171 - 1.55e-8 * d, e: 0.047318 + 7.45e-9 * d, M: 142.5905 + 0.011725806 * d }),
Neptune: (d) => ({ N: 131.7806 + 3.0173e-5 * d, i: 1.7700 - 2.55e-7 * d, w: 272.8461 - 6.027e-6 * d, a: 30.05826 + 3.313e-8 * d, e: 0.008606 + 2.15e-9 * d, M: 260.2471 + 0.005995147 * d }),
};
const dayNumber = (t) => Cesium.JulianDate.toDate(t).getTime() / 86400000 - 10956.0;
function helio(name, d) {
const el = ELEM[name](d);
const N = el.N * D2R, i = el.i * D2R, w = el.w * D2R, e = el.e, M = rev(el.M) * D2R;
let E = M + e * Math.sin(M) * (1 + e * Math.cos(M));
for (let k = 0; k < 6; k++) E = E - (E - e * Math.sin(E) - M) / (1 - e * Math.cos(E));
const xv = el.a * (Math.cos(E) - e), yv = el.a * Math.sqrt(1 - e * e) * Math.sin(E);
const v = Math.atan2(yv, xv), r = Math.hypot(xv, yv), u = v + w;
return {
x: r * (Math.cos(N) * Math.cos(u) - Math.sin(N) * Math.sin(u) * Math.cos(i)),
y: r * (Math.sin(N) * Math.cos(u) + Math.cos(N) * Math.sin(u) * Math.cos(i)),
z: r * Math.sin(u) * Math.sin(i),
};
}
function earthHelio(d) {
const w = 282.9404 + 4.70935e-5 * d, e = 0.016709 - 1.151e-9 * d, M = rev(356.0470 + 0.9856002585 * d) * D2R;
let E = M + e * Math.sin(M) * (1 + e * Math.cos(M));
const xv = Math.cos(E) - e, yv = Math.sqrt(1 - e * e) * Math.sin(E);
const v = Math.atan2(yv, xv), r = Math.hypot(xv, yv), lon = v + w * D2R;
return { x: -r * Math.cos(lon), y: -r * Math.sin(lon), z: 0 }; // Earth = (Sun geocentric)
}
// ---- scaling (tuned for a readable orrery, not true scale) ----
const AU_UNIT = 4.0e7; // ~Earth orbit display radius
const distScale = (au) => AU_UNIT * Math.pow(au, 0.72); // gentle outer compression
const BODY_UNIT = 3.4e6; // exaggerated so bodies read at orrery scale
const bodyRadius = (km, isSun) => isSun ? 1.6e7 : BODY_UNIT * Math.pow(km / 6371, 0.4);
function scaledPos(vec) { // ecliptic AU {x,y,z} → scene Cartesian3
const au = Math.hypot(vec.x, vec.y, vec.z) || 1e-6;
const s = distScale(au) / au;
return new Cesium.Cartesian3(vec.x * s, vec.y * s, vec.z * s);
}
const BODIES = [
{ key: 'sun', name: '☉ Sun', tex: 'textures/2k_sun.jpg', km: 696000, sun: true },
{ key: 'mercury', name: '☿ Mercury', tex: 'textures/2k_mercury.jpg', km: 2439.7 },
{ key: 'venus', name: '♀ Venus', tex: 'textures/2k_venus_atmosphere.jpg', km: 6051.8 },
{ key: 'earth', name: '🜨 Earth', tex: 'textures/2k_earth_daymap.jpg', km: 6371 },
{ key: 'mars', name: '♂ Mars', tex: 'textures/2k_mars.jpg', km: 3389.5 },
{ key: 'jupiter', name: '♃ Jupiter', tex: 'textures/2k_jupiter.jpg', km: 69911 },
{ key: 'saturn', name: '♄ Saturn', tex: 'textures/2k_saturn.jpg', km: 58232, ring: [1.3, 2.3] },
{ key: 'uranus', name: '⛢ Uranus', tex: 'textures/2k_uranus.jpg', km: 25362 },
{ key: 'neptune', name: '♆ Neptune', tex: 'textures/2k_neptune.jpg', km: 24622 },
];
const ds = new Cesium.CustomDataSource('solarsystem');
ds.show = false;
viewer.dataSources.add(ds);
const bodyEntities = {}; // key → entity (for travel-to framing)
function posOf(key, d) {
if (key === 'sun') return new Cesium.Cartesian3(0, 0, 0);
if (key === 'earth') return scaledPos(earthHelio(d));
return scaledPos(helio(key.charAt(0).toUpperCase() + key.slice(1), d));
}
function build(d) {
ds.entities.removeAll();
for (const b of BODIES) {
const pos = posOf(b.key, d);
const r = bodyRadius(b.km, b.sun);
const ent = ds.entities.add({
name: b.name,
position: pos,
ellipsoid: {
radii: new Cesium.Cartesian3(r, r, r),
material: new Cesium.ImageMaterialProperty({ image: b.tex, color: Cesium.Color.WHITE }),
slicePartitions: 32, stackPartitions: 32,
},
label: {
text: b.name, font: '13px "Segoe UI", system-ui, sans-serif',
fillColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.fromCssColorString('#05080b'),
outlineWidth: 3, style: Cesium.LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, pixelOffset: new Cesium.Cartesian2(0, -8),
translucencyByDistance: new Cesium.NearFarScalar(1e8, 1.0, 4e9, 0.35),
disableDepthTestDistance: 50000,
},
});
bodyEntities[b.key] = ent;
if (b.ring) {
ds.entities.add({
position: pos,
ellipse: {
semiMajorAxis: r * b.ring[1], semiMinorAxis: r * b.ring[1],
height: 0,
material: new Cesium.ImageMaterialProperty({ image: 'textures/2k_saturn_ring_alpha.png', transparent: true }),
},
});
}
// Orbit (skip the Sun): trace one full revolution by sweeping the anomaly.
if (b.key !== 'sun') {
const pts = [];
for (let a = 0; a <= 360; a += 3) {
const v = b.key === 'earth' ? earthHelioAt(d, a) : helioAt(b.key, d, a);
pts.push(scaledPos(v));
}
ds.entities.add({
polyline: { positions: pts, width: 2, material: new Cesium.ColorMaterialProperty(lib.cz('#6a86c0', 0.7)) },
});
}
}
}
// Orbit sampling: recompute a body's heliocentric position at mean-anomaly `deg`.
function helioAt(key, d, deg) {
const name = key.charAt(0).toUpperCase() + key.slice(1);
const el = ELEM[name](d);
const N = el.N * D2R, i = el.i * D2R, w = el.w * D2R, e = el.e, M = deg * D2R;
let E = M + e * Math.sin(M) * (1 + e * Math.cos(M));
for (let k = 0; k < 6; k++) E = E - (E - e * Math.sin(E) - M) / (1 - e * Math.cos(E));
const xv = el.a * (Math.cos(E) - e), yv = el.a * Math.sqrt(1 - e * e) * Math.sin(E);
const v = Math.atan2(yv, xv), r = Math.hypot(xv, yv), u = v + w;
return { x: r * (Math.cos(N) * Math.cos(u) - Math.sin(N) * Math.sin(u) * Math.cos(i)), y: r * (Math.sin(N) * Math.cos(u) + Math.cos(N) * Math.sin(u) * Math.cos(i)), z: r * Math.sin(u) * Math.sin(i) };
}
function earthHelioAt(d, deg) {
const w = 282.9404 + 4.70935e-5 * d, e = 0.016709 - 1.151e-9 * d, M = deg * D2R;
let E = M + e * Math.sin(M) * (1 + e * Math.cos(M));
const xv = Math.cos(E) - e, yv = Math.sqrt(1 - e * e) * Math.sin(E);
const v = Math.atan2(yv, xv), r = Math.hypot(xv, yv), lon = v + w * D2R;
return { x: -r * Math.cos(lon), y: -r * Math.sin(lon), z: 0 };
}
// ---- mode enter / exit ----
let active = false;
const saved = { dataSources: [], primitives: [], globe: true, atmo: true, sky: true, animate: true, light: null, far: 5e8, collide: true };
function enter() {
if (active) return;
active = true;
const d = dayNumber(viewer.clock.currentTime);
build(d);
// Hide every Earth layer: entity data sources + billboard primitives.
saved.dataSources = [];
for (let i = 0; i < viewer.dataSources.length; i++) {
const s = viewer.dataSources.get(i);
if (s === ds) continue;
saved.dataSources.push([s, s.show]); s.show = false;
}
saved.primitives = [];
for (let i = 0; i < scene.primitives.length; i++) {
const p = scene.primitives.get(i);
if (p && typeof p.length === 'number' && 'show' in p) { saved.primitives.push([p, p.show]); p.show = false; }
}
saved.globe = scene.globe.show; scene.globe.show = false;
saved.atmo = scene.globe.showGroundAtmosphere; scene.globe.showGroundAtmosphere = false;
saved.sky = scene.skyAtmosphere.show; scene.skyAtmosphere.show = false;
saved.animate = viewer.clock.shouldAnimate; viewer.clock.shouldAnimate = false; // freeze the snapshot
// The scene spans ~1e9 m — well past Cesium's default 5e8 far plane, which
// would clip every body. Widen it (and drop the surface-collision limit so
// you can fly far out); restored on exit.
saved.far = scene.camera.frustum.far; scene.camera.frustum.far = 8e9;
saved.collide = scene.screenSpaceCameraController.enableCollisionDetection;
scene.screenSpaceCameraController.enableCollisionDetection = false;
scene.screenSpaceCameraController.maximumZoomDistance = 6e9;
// Even "planetarium" lighting: a headlight from the camera so no half-dark planets.
saved.light = scene.light;
scene.light = new Cesium.DirectionalLight({ direction: scene.camera.directionWC.clone(), intensity: 2.2 });
headlight = scene.preRender.addEventListener((s) => {
scene.light.direction = Cesium.Cartesian3.clone(s.camera.directionWC, scene.light.direction);
});
ds.show = true;
document.getElementById('hud').style.display = 'none';
panel.style.display = 'block';
document.getElementById('ss-btn').classList.add('active');
// Fly out to a clean top-down-ish view of the system (orthonormal dir/up —
// Cesium needs those, and lookAt at the geocentre is degenerate).
viewer.camera.flyTo({
destination: new Cesium.Cartesian3(0.7e8, 0.3e8, 7.5e8),
orientation: { direction: new Cesium.Cartesian3(0, 0, -1), up: new Cesium.Cartesian3(0, 1, 0) },
duration: 2.5,
});
}
let headlight = null;
function exit() {
if (!active) return;
active = false;
ds.show = false;
for (const [s, show] of saved.dataSources) s.show = show;
for (const [p, show] of saved.primitives) p.show = show;
scene.globe.show = saved.globe;
scene.globe.showGroundAtmosphere = saved.atmo;
scene.skyAtmosphere.show = saved.sky;
viewer.clock.shouldAnimate = saved.animate;
if (headlight) { headlight(); headlight = null; }
if (saved.light) scene.light = saved.light;
scene.camera.frustum.far = saved.far;
scene.screenSpaceCameraController.enableCollisionDetection = saved.collide;
scene.screenSpaceCameraController.maximumZoomDistance = Infinity;
document.getElementById('hud').style.display = '';
panel.style.display = 'none';
document.getElementById('ss-btn').classList.remove('active');
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(ctx.CONFIG.camera.lon, ctx.CONFIG.camera.lat, ctx.CONFIG.camera.height),
duration: 2.0,
});
}
function travelTo(key) {
const ent = bodyEntities[key];
if (!ent) return;
if (key === 'earth') { // Earth = home; offer to drop back to the OSINT globe
exit();
return;
}
viewer.flyTo(ent, { duration: 2.5, offset: new Cesium.HeadingPitchRange(0, -0.3, bodyRadius(BODIES.find((b) => b.key === key).km) * 6) });
}
// ---- UI: enter button + destination panel ----
const btn = document.createElement('button');
btn.id = 'ss-btn';
btn.type = 'button';
btn.textContent = '◉ Solar System';
btn.title = 'Leave Earth and travel the solar system';
btn.addEventListener('click', () => (active ? exit() : enter()));
document.body.appendChild(btn);
const panel = document.createElement('aside');
panel.id = 'ss-panel';
panel.style.display = 'none';
panel.innerHTML = `<div class="ss-title">TRAVEL TO</div><div class="ss-dests"></div>
<div class="ss-foot">Positions computed live · textures © <a href="https://www.solarsystemscope.com/" target="_blank" rel="noopener">Solar System Scope</a> CC BY 4.0</div>`;
const dests = panel.querySelector('.ss-dests');
for (const b of BODIES) {
const d = document.createElement('button');
d.className = 'ss-dest';
d.textContent = b.name;
d.addEventListener('click', () => travelTo(b.key));
dests.appendChild(d);
}
const back = document.createElement('button');
back.className = 'ss-dest ss-back';
back.textContent = '↩ Return to Earth (OSINT)';
back.addEventListener('click', exit);
panel.appendChild(back);
document.body.appendChild(panel);
return { enter, exit, isActive: () => active };
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

BIN
textures/2k_jupiter.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

BIN
textures/2k_mars.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 KiB

BIN
textures/2k_mercury.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

BIN
textures/2k_moon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
textures/2k_neptune.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
textures/2k_saturn.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
textures/2k_sun.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 KiB

BIN
textures/2k_uranus.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

View File

@ -0,0 +1,10 @@
# Planet & body textures
Equirectangular surface/cloud maps for the Solar System mode.
**Source:** [Solar System Scope](https://www.solarsystemscope.com/textures/) — © INOVE / Solar System Scope.
**License:** Creative Commons Attribution 4.0 International (CC BY 4.0).
Files: `2k_{sun,mercury,venus_atmosphere,earth_daymap,moon,mars,jupiter,saturn,uranus,neptune}.jpg`, `2k_saturn_ring_alpha.png`.
Attribution is also shown in-app (Solar System mode footer) and in the project README.