From a6662416a1b554c903c6bc9d5c390028082daf8a Mon Sep 17 00:00:00 2001 From: KNSO Date: Sat, 12 Sep 2026 13:07:21 +0200 Subject: [PATCH] =?UTF-8?q?Design-Spec=20f=C3=BCr=20den=20spielbaren=20Vic?= =?UTF-8?q?e-City-Ausschnitt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ausschnitt A (X -1000..2000, Y -500..2500) aus der Community-Karte YANIS v15; Kartendaten von map.gtadb.org unter CC BY 4.0. Die Spec beschreibt Rechtslage, Koordinaten, Projektstruktur, die siebenstufige Extraktion, die Laufzeit mit Chunk-Streaming, Aussehen, Fehlerbehandlung, Tests und Budgets. Co-Authored-By: Claude Opus 5 --- .claude/launch.json | 12 + .gitignore | 16 ++ .../2026-09-12-leonida-vice-slice-design.md | 215 ++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 .claude/launch.json create mode 100644 .gitignore create mode 100644 docs/superpowers/specs/2026-09-12-leonida-vice-slice-design.md diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..b6aded5 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "three-player-controller", + "runtimeExecutable": "bash", + "runtimeArgs": ["-lc", "cd three-player-controller && npm run dev"], + "port": 5173, + "url": "http://localhost:5173" + } + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d562a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Große Quell-Archive; die benötigten Assets werden entpackt eingebunden +*.zip + +# Node +node_modules/ +dist/ + +# Python +.venv/ +__pycache__/ +*.pyc + +# Generierte Zwischenstände +tools/extract/cache/ + +.DS_Store diff --git a/docs/superpowers/specs/2026-09-12-leonida-vice-slice-design.md b/docs/superpowers/specs/2026-09-12-leonida-vice-slice-design.md new file mode 100644 index 0000000..733adcf --- /dev/null +++ b/docs/superpowers/specs/2026-09-12-leonida-vice-slice-design.md @@ -0,0 +1,215 @@ +# Leonida: spielbarer Ausschnitt Vice City (Vertikalschnitt) + +- **Datum:** 2026-09-12 +- **Status:** Design freigegeben, Umsetzungsplan ausstehend +- **Umfang:** erster von vier Teilprojekten. Ein 3 × 3 km großer Ausschnitt, durch alle Ebenen hindurch spielbar. + +## 1. Ziel und Abgrenzung + +Ein provisorisches, aber spielbares 3D-Blockout von Vice City und Vice Beach aus der +Community-Karte „State of Leonida" (YANIS v15). Zu Fuß und mit dem Auto begehbar, mit +Küste, Straßen, Brücken, extrudierten Gebäuden, Palmen und Straßenmobiliar. + +**Ausdrücklich nicht im Umfang:** die vollen 20 × 20 km, Geländehöhen aus dem Farbcode, +Überführungen und Tunnel als getrennte Ebenen, Dachformen, KI-Verkehr, Innenräume, +Mobilgeräte. + +## 2. Rechtliches und Quellen + +| Quelle | Inhalt | Lizenz | +|---|---|---| +| github.com/rolux/gtadb.org @ `4093e8c874e480d47c38b37fe7a404f8b0c5275e` | Kacheln YANIS v15, 2.577 Landmarks | CC BY 4.0 | +| github.com/rolux/gtamaplib-vc | 107 Wahrzeichen als Quader mit Höhen | Unlicense (gemeinfrei) | +| polyhaven.com | Texturen und Requisiten | CC0 | +| opengameart.org „Palm tree v2" (Yughues/Nobiax) | Palme, 1.032 Dreiecke | CC0 | +| Quaternius | Figuren und Animationen (UAL) | CC0 | +| Sketchfab „Low-poly cyberpunk SUV" (Boris731) | Fahrzeug | CC BY | + +**Nicht verwenden:** Kacheln und Bilder von map.stateofleonida.net (deren `robots.txt` +enthält einen ausdrücklichen Rechtevorbehalt nach Art. 4 der EU-Richtlinie 2019/790, eine +Lizenz fehlt), YANIS v16 (beruht auf dem Cyberleek-Leak, gegen den Take-Two per DMCA +vorgeht), Rockstar-Trailerbilder als Texturen (nur als optische Vorlage). + +**IP-Hinweis:** Alles leitet sich von GTA VI ab. Lokal zum Basteln unproblematisch, eine +Veröffentlichung wäre rechtlich riskant. Das ist eine Entscheidung des Nutzers. + +Namensnennung ist Pflicht (CC BY) und steht dauerhaft im HUD. + +## 3. Koordinaten und Ausschnitt + +- 1 Spieleinheit = 1 Meter. gtadb-Weltursprung liegt in der Innenstadt, +Y ist Norden. +- Kachelschema `5,{zeile},{spalte}.jpg`, Zoom 5 = exakt 1 px/m, 256 px je Kachel. + Spalte = ⌊(X + 16384)/256⌋, Zeile = ⌊(16384 − Y)/256⌋. +- **Ausschnitt A:** X −1000 … 2000, Y −500 … 2500. + Kacheln: Spalten 60–71, Zeilen 54–65 = **144 Kacheln, 1,18 MB**. Mosaik 3072 × 3072 px, + Überhang wird auf die exakten Grenzen beschnitten. +- three.js: `x = X`, `z = −Y`. Norden liegt bei −Z. Der Ausschnitt bleibt unter 2,5 km vom + Ursprung, eine Ursprungsverschiebung ist nicht nötig. +- Gemessene Flächenanteile (Grundlage für die Testgrenzen): Wasser 32,3 %, Stadtfläche + 31,1 %, Gebäude 9,2 %, Straße 8,3 %, Land 6,5 %, Spekulativ-Rot 2,1 %, Grün 1,9 %, + Sand 0,9 %. 58 von 144 Kacheln enthalten Gebäudegrundrisse, 10 sind Stadtfläche ohne. + +## 4. Projektstruktur + +``` +gta6/ +├── package.json three 0.184.0, three-mesh-bvh, three-player-controller (file:), vite, vitest +├── vite.config.js resolve.dedupe ['three','three-mesh-bvh'], optimizeDeps.exclude ['three-mesh-bvh'] +├── index.html +├── src/ +│ ├── main.js Renderer, Kamera, Player, Schleife +│ ├── world/ Chunk-Streaming und Geometrie-Baustein (reines JS, auch in Node lauffähig) +│ ├── characters/ Figurenauswahl und Retargeting, aus showcase.js herausgelöst +│ └── ui/ Debug-Panel, Loader, Namensnennung +├── public/ +│ ├── data/vice-a/ manifest.json und Chunk-Dateien (generiert, committet) +│ ├── textures/ Poly-Haven-Texturen, 1k +│ └── models/ Palme, Requisiten, Figuren, SUV +├── tools/ +│ ├── extract/ Python 3.12 (uv): Kacheln → Vektoren +│ └── assets/ Downloads, TGA→PNG, Vereinfachung +├── three-player-controller/ Git-Submodul, gepinnt auf Upstream ac39682 +└── docs/superpowers/specs/ +``` + +**Warum die Vite-Konfiguration nötig ist:** `dist/index.mjs` des Controllers importiert +`three` und `three-mesh-bvh` als externe Pakete. Da der Klon ein eigenes `node_modules` +besitzt, lädt eine App ohne `resolve.dedupe` **zwei three.js-Kopien**, was `instanceof` +bricht. Der BVH-Worker wird per dynamischem Import aus `three-mesh-bvh/src/workers/` +geladen und darf deshalb nicht vorgebündelt werden. + +**Git:** `.gitignore` für die beiden ZIPs (160 MB), `node_modules`, `.venv` und den +Kachel-Cache. Der Controller kommt als Submodul auf dem unveränderten Upstream-Stand; +die vorhandenen lokalen Showcase-Änderungen werden vorher auf einem lokalen Branch im +Klon gesichert. + +## 5. Abhängigkeiten + +- **Node 26:** three 0.184.0, three-mesh-bvh, three-player-controller (file:), vite, + lil-gui, vitest, @gltf-transform/cli 4.5.0 (nur zur Asset-Aufbereitung, braucht Node ≥ 20). +- **Python 3.12 über uv** (bereits installiert): numpy, scipy, scikit-image, shapely, + networkx, pillow, pytest. Alle mit Apple-Silicon-Wheels. +- **Bewusst nicht:** `skan` (zieht numba, pandas, matplotlib nach), `buildingregulariser` + (zieht geopandas und GDAL nach), OpenCV, GDAL/rasterio (das Mosaik ist klein genug). +- Bordmittel: `sips` wandelt die TGA-Texturen der Palme mit Alpha-Kanal nach PNG. + +## 6. Extraktion (`tools/extract`) + +1. **Laden.** 144 Kacheln vom gepinnten Commit in einen Cache, der nicht committet wird. +2. **Klassifizieren.** Medianfilter gegen JPEG-Rauschen, dann jedes Pixel zur nächsten + Legendenfarbe im Lab-Raum. Palette einmalig an den Farbfeldern der Legende kalibriert. + Klassen: Wasser, Land, Sand, Park, Stadtfläche, Gebäude, Straße, Gasse, Weg, Bahn, + Spekulativ-Rot. Beschriftung, Icons und Kanten gelten als „unbekannt" und werden vom + nächsten bekannten Nachbarpixel aufgefüllt. +3. **Küste.** Wassermaske glätten, Landpolygone mit Löchern bilden, auf 1 m vereinfachen. +4. **Straßen.** Kein Graph: die gezeichneten **Straßenflächen** werden übernommen (Maske + schließen, das überbrückt Straßennamen, dann polygonisieren). Kreuzungen stimmen + dadurch automatisch. Mittellinien entstehen zusätzlich per Skelett, nur zum Platzieren + von Laternen. Straßenflächen über Wasser werden als **Brücken** markiert. +5. **Gebäude.** Zusammenhängende Flächen → Konturen → Polygone. Deckt ein Polygon ≥ 85 % + seines gedrehten Hüllrechtecks, wird es zum Rechteck, sonst auf 1 m vereinfacht. + Unter 12 m² verwerfen. Rot = spekulativ markieren. +6. **Höhen.** Die 54 Wahrzeichen aus gtamaplib-vc (26–248 m, Median 111 m) werden dem + Grundriss unter ihrem Punkt zugeordnet und liefern die echte Höhe. Alle übrigen + Gebäude bekommen Startwerte je Viertel, per Seed reproduzierbar, kleine Grundrisse + niedriger: Downtown 20–60 m, Ocean Beach 8–25 m, sonst 6–15 m. Die Zuordnung eines Gebäudes zu einem Viertel erfolgt über das + nächstgelegene gtadb-Landmark. Die 10 leeren + Stadtkacheln bekommen generierte Flachbauten, als solche markiert. +7. **Palmenpunkte.** Deterministisch, mit festem Seed: auf Parkflächen mit mindestens + 6 m Abstand zu Gebäuden und Straßen, sowie beidseitig im Abstand von 12 m entlang + jeder Straßenmittellinie, die höchstens 40 m von einer Sandfläche entfernt liegt + (das ist die Promenade). + +**Ausgabe** je 250-m-Chunk eine JSON-Datei mit Koordinaten in Metern relativ zur +Chunk-Ecke: `land[]`, `roads[]` (mit `class`, `bridge`), `centerlines[]`, `buildings[]` +(`poly`, `height`, `source` = skyline | district | generated, `speculative`), `palms[]`. +Dazu `manifest.json` mit Grenzen, Chunk-Liste, Quell-Commit, Parametern, Seed und +Namensnennung. Zusätzlich ein **Debug-PNG je Chunk** mit den Vektoren über der Karte. + +## 7. Laufzeit + +**Streaming.** 12 × 12 Chunks à 250 m. Laden ab 400 m Entfernung, Entladen ab 600 m; der +Abstand verhindert Flackern. Pro Frame höchstens ein Chunk-Bau und eine Collider-Anmeldung, +bis zu vier Abmeldungen (Muster aus `example/3dtilesScene.js`). Typisch 9–16 aktive Chunks. +Das ist zwingend, weil `CollisionWorld.query` **jeden** Collider in jedem Frame durchgeht. + +**Geometrie je Chunk**, trianguliert mit dem in three.js enthaltenen Earcut: +- Land: 1 m dicke Platte, Oberkante y = 0, mit Seitenwänden an der Küste +- Straßen: flache Flächen 3 cm über dem Boden, ohne eigenen Collider +- Brücken: 0,6 m dicke Platten mit Collider +- Gebäude: Wände und Flachdach, je Material zu einem Mesh zusammengefasst +- Wasser: eine Fläche für den ganzen Ausschnitt bei y = −0,6 + +**Kollision.** Genau ein Collider je Chunk (Land, Brücken, Gebäude, dazu Stammzylinder von +Palmen und Laternen), BVH im Worker über `addCollider({ useWorker: true })`. Meeresboden +bei y = −2,5, damit man waten kann statt zu fallen. Requisiten-Meshes selbst bleiben ohne +Kollision. + +**Start und Sicherheit.** Bis `player.collisionWorld.get(id).ready` für den Start-Chunk +true meldet, schwebt der Spieler mit `setSkipCapsuleCollision(true)`; nach spätestens 15 s +wird regulär gestartet und protokolliert. Unter y = −30 setzt `reset()` an die letzte +sichere Position zurück. + +**Kamera.** Near 0,1 m, Far 2500 m, Nebel verdeckt die Chunk-Grenze, Schatten folgen dem +Spieler. + +## 8. Aussehen und Inhalte + +- **Materialien:** Welt-Texturkoordinaten (waagerecht aus x/z, an Wänden aus Kantenlänge + und Höhe), damit Texturen nahtlos über Chunk-Grenzen laufen. Rund sechs Poly-Haven- + Materialien in 1k mit je drei Maps: Asphalt, Beton, `white_stucco`, Sand, Gras, Dach. + Asphalt bekommen Highway und Road, Beton bekommen Gasse, Weg und Brücke. + Gebäude werden über Vertex-Farben pastellig getönt (Art déco), ohne Zusatztexturen. + Ziel: 4–6 Draw Calls je Chunk. +- **Wasser:** getönte Fläche mit beim Start erzeugter, kachelbarer Wellen-Normalmap, + Spiegelung über eine Umgebungsmap statt eines Reflexionspasses. +- **Palmen:** Nobiax-Palme, einmalig zu GLB gewandelt (TGA → PNG, Alpha-Test, Wedel + doppelseitig), zur Laufzeit als InstancedMesh, Höhe zufällig 10–18 m. Das Modell ist + Y-oben, Fuß bei 0, 440 Einheiten hoch; die Zielhöhe wird über einen Skalierungsfaktor + gesetzt. +- **Requisiten** (Poly Haven, vor dem Einbau auf ≈ 3k Dreiecke vereinfacht, Texturen 512): + `street_lamp_02` (20.338), `painted_wooden_bench` (630), `metal_trash_can` (13.960), + `fire_hydrant` (86.316), `concrete_road_barrier_02` (42.578). +- **Autos:** vier SUVs an Straßenpunkten nahe dem Start, Konfiguration aus der Showcase + übernommen. Ihre Chunks bleiben geladen, weil jedes Fahrzeug in jedem Frame Physik rechnet. +- **Figuren:** Modul aus `showcase.js` nach `src/characters/`, inklusive Delta-Retargeting + und ans Mesh angepasster Hitbox. Start ist die Promenade von Vice Beach bei X 1699, Y 743 (three.js: x = 1699, z = -743), + abgeleitet aus dem Schwerpunkt der 259 Ocean-Beach-Landmarks im Ausschnitt. +- **Debug-Panel:** Figurenauswahl, Chunk-Grenzen, Hitbox, Kartenbild auf dem Boden, + Sichtweite, Höhenfaktor, Sprung zu einem der 54 Wahrzeichen. +- **Namensnennung im HUD:** Kartendaten und Landmarks von map.gtadb.org (CC BY 4.0, + YANIS v15), SUV von Boris731 (CC BY), Poly Haven, Nobiax und Quaternius (CC0). + +## 9. Fehlerbehandlung + +| Fall | Verhalten | +|---|---| +| Kachel-Download scheitert | drei Versuche mit Wartezeit, dann Abbruch mit Liste der fehlenden Kacheln | +| über 5 % Pixel bleiben „unbekannt" | Abbruch mit Diagnosebild, Palette neu kalibrieren | +| ungültige Polygone | `buffer(0)`, Reste verwerfen und zählen | +| Wahrzeichen ohne Grundriss darunter | protokollieren, Viertel-Höhe verwenden | +| Chunk lädt nicht | zwei Wiederholungen, dann flache Platzhalterfläche und Meldung | +| BVH-Worker fällt aus | Controller rechnet synchron weiter (eingebauter Rückfall) | +| Textur fehlt | einfarbiges Ersatzmaterial | +| Start-Chunk nicht fertig | nach 15 s regulär starten, protokollieren | +| Spieler unter y = −30 | `reset()` auf letzte sichere Position | + +## 10. Tests und Budgets + +- **pytest:** künstliche Masken (Quadrat → Rechteck, Straßenkreuz als ein Polygon, Insel + mit Lagune behält das Loch, Wahrzeichen findet Grundriss, gleicher Seed → gleiche Höhen), + Schema-Prüfung der Chunk-Dateien, Plausibilitätsgrenzen am echten Ausschnitt: + Wasser 32 % ± 3, Straße 8 % ± 2, Gebäude 9 % ± 2. +- **vitest:** Geometrie-Baustein — Dächer nach oben, Wände nach außen, UV-Bereiche, + Koordinaten innerhalb der Chunk-Grenzen, Dreieckszahlen bei bekannter Eingabe. Dazu ein + Node-Durchlauf über alle 144 Chunks: keine NaN, Budget eingehalten. +- **Abnahme im Spiel:** am Strand starten, über eine Dammstraße fahren, ins Wasser waten, + einen Wolkenkratzer erreichen, den Ausschnitt ohne Ruckler durchqueren. +- **Budgets:** mindestens 55 FPS, unter 150 Draw Calls, Chunk-Bau unter 20 ms im + Hauptthread. + +## 11. Zustimmungen, die vor der Umsetzung nötig sind + +1. Installation der Node- und Python-Pakete aus Abschnitt 5. +2. `.gitignore` anlegen und erste Commits im bisher leeren gta6-Repository. +3. Sicherung der bestehenden Showcase-Änderungen auf einem lokalen Branch im Controller-Klon.