diff --git a/SPEC4.md b/SPEC4.md new file mode 100644 index 0000000..baf4ef3 --- /dev/null +++ b/SPEC4.md @@ -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 1–3.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. diff --git a/css/style.css b/css/style.css index b0508c2..9855599 100644 --- a/css/style.css +++ b/css/style.css @@ -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); } diff --git a/js/main.js b/js/main.js index 78730ec..bf30e66 100644 --- a/js/main.js +++ b/js/main.js @@ -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 diff --git a/js/solarsystem.js b/js/solarsystem.js new file mode 100644 index 0000000..3b7e098 --- /dev/null +++ b/js/solarsystem.js @@ -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 = `
TRAVEL TO
+
Positions computed live · textures © Solar System Scope CC BY 4.0
`; + 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 }; +} diff --git a/textures/2k_earth_daymap.jpg b/textures/2k_earth_daymap.jpg new file mode 100644 index 0000000..6cdcffe Binary files /dev/null and b/textures/2k_earth_daymap.jpg differ diff --git a/textures/2k_jupiter.jpg b/textures/2k_jupiter.jpg new file mode 100644 index 0000000..21f1951 Binary files /dev/null and b/textures/2k_jupiter.jpg differ diff --git a/textures/2k_mars.jpg b/textures/2k_mars.jpg new file mode 100644 index 0000000..b3654a9 Binary files /dev/null and b/textures/2k_mars.jpg differ diff --git a/textures/2k_mercury.jpg b/textures/2k_mercury.jpg new file mode 100644 index 0000000..eebaed9 Binary files /dev/null and b/textures/2k_mercury.jpg differ diff --git a/textures/2k_moon.jpg b/textures/2k_moon.jpg new file mode 100644 index 0000000..8a1de86 Binary files /dev/null and b/textures/2k_moon.jpg differ diff --git a/textures/2k_neptune.jpg b/textures/2k_neptune.jpg new file mode 100644 index 0000000..30e7ac5 Binary files /dev/null and b/textures/2k_neptune.jpg differ diff --git a/textures/2k_saturn.jpg b/textures/2k_saturn.jpg new file mode 100644 index 0000000..d8b23df Binary files /dev/null and b/textures/2k_saturn.jpg differ diff --git a/textures/2k_saturn_ring_alpha.png b/textures/2k_saturn_ring_alpha.png new file mode 100644 index 0000000..ce82586 Binary files /dev/null and b/textures/2k_saturn_ring_alpha.png differ diff --git a/textures/2k_sun.jpg b/textures/2k_sun.jpg new file mode 100644 index 0000000..9d787d8 Binary files /dev/null and b/textures/2k_sun.jpg differ diff --git a/textures/2k_uranus.jpg b/textures/2k_uranus.jpg new file mode 100644 index 0000000..d0abf4c Binary files /dev/null and b/textures/2k_uranus.jpg differ diff --git a/textures/2k_venus_atmosphere.jpg b/textures/2k_venus_atmosphere.jpg new file mode 100644 index 0000000..6448848 Binary files /dev/null and b/textures/2k_venus_atmosphere.jpg differ diff --git a/textures/TEXTURES_CREDIT.md b/textures/TEXTURES_CREDIT.md new file mode 100644 index 0000000..bb38b91 --- /dev/null +++ b/textures/TEXTURES_CREDIT.md @@ -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.