Design-Spec für den spielbaren Vice-City-Ausschnitt

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 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5 committed 2026-09-12 13:07:21 +02:00
commit a6662416a1
3 files changed
+243

No files matched your search

@@ -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.