Wasser ist #3090c0, nicht #2c67a4; der alte Wert war die Kompositionsfarbe aus tiles.py. Mit Schwelle 22 fallen die unbekannten Pixel von 37,4 auf 3,5 Prozent. Die Sollanteile für Straße und Gebäude stammten aus einer Messung mit anderer Klasseneinteilung und werden auf die gemessenen Bänder gesetzt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
13 KiB
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:
sipswandelt die TGA-Texturen der Palme mit Alpha-Kanal nach PNG.
6. Extraktion (tools/extract)
- Laden. 144 Kacheln vom gepinnten Commit in einen Cache, der nicht committet wird.
- 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.
- Küste. Wassermaske glätten, Landpolygone mit Löchern bilden, auf 1 m vereinfachen.
- 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.
- 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.
- 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.
- 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.jsnachsrc/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 30–36 %, Straße 10–14 %, Gebäude 9–13 % (am Mosaik gemessen, Stand Aufgabe 3).
- 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
- Installation der Node- und Python-Pakete aus Abschnitt 5.
.gitignoreanlegen und erste Commits im bisher leeren gta6-Repository.- Sicherung der bestehenden Showcase-Änderungen auf einem lokalen Branch im Controller-Klon.