#show baute bisher scene.add, bevor player.addCollider lief: wirft
addCollider (z. B. toter Worker), blieb die Wurzel ohne active-Eintrag
in der Szene stehen — sichtbar, aber nie wieder entfernbar — und die
Ausnahme entkam synchron aus update() in die Spielschleife.
Der Collider-Handle wird jetzt vor scene.add in einem try/catch
gebaut; schlägt er fehl, bleibt die Szene unverändert, der Ort landet
in this.failed (kein erneuter Versuch) und #show überspringt ihn beim
nächsten Aufruf.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PlaceLayer nimmt jetzt ein optionales warmUp(root) entgegen und ruft es
nach prepareModel, aber vor dem Einsetzen auf. main.js nutzt das, um
Texturen hochzuladen und CSM-Shader zu übersetzen, solange der Ort noch
nicht in der Szene ist — gemessen kostete das sonst rund 130 ms auf
einen Schlag im ersten Bild, in dem der Hof erscheint. Schlägt das
Vorwärmen fehl, wird nur gewarnt; der Ort wird trotzdem eingesetzt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Prototyp am echten Modell: 653,6 MB -> 16,2 MB, 2,9 Mio. -> 658 000 Dreiecke,
Bump-Maps erhalten. Radien geändert: Der Hof erscheint erst ab 250 m und
verschwindet ab 450 m, sonst stünde er vom Startpunkt aus über ungeladenem
Boden. Vereinfachung je Material statt eines gemeinsamen Anteils.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eigenes Modell aus three-d-stage 1:1 an die Stelle der kleinen Gebäude
zwischen Baseball- und Fußballfeld: Aufbereitung mit gltf-transform unter
Erhalt von EXT_materials_bump, Orte-Schicht mit Laden je nach Abstand,
Kollision über Materialnamen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Acht Aufgaben, Code und Liste aus dem gegen die echte Vorlage geprüften
Prototyp; in Plan-Reihenfolge in einer Repository-Kopie nachgespielt.
Spec: Start und Autos ziehen von der Buchstabenkontur auf die
wiederaufgebaute Straße am Park, Nummern im Berichtsbeispiel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Straßenenden über das Skelett samt Maske, Korridor statt Versatzgrenze,
Weiterführen über Kreuzungen, Ziel auf der anderen Seite, keine gemalten
Sackgassen, Kartenrand; Gebäudevorlage ab 100 m² im 150-m-Fenster;
randvoll vor dem Wachsen; Kontrastregel der Suche; Probelauf mit Kennzahlen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Alle Beschriftungen der Vorlage (Schriftzüge, Marker, Schilder, Symbole,
Straßennamen, Gitternetz) werden vor der Vektorisierung aus dem Klassenbild
entfernt und darunter wiederaufgebaut: Straßen laufen durch, Flächen
schließen sich, angebissene Gebäude werden geschlossen, verdeckte Blockteile
bekommen passende, als ergänzt markierte Gebäude. Ersetzt task_999fed4a.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Himmel: 4k-HDRI von Poly Haven (CC0, tools/assets/hdri.mjs) statt Sky-Shader. Kuppel zeigt das Foto
ab 12°, darunter weiches Band und Dunst in Nebelfarbe; Umgebungslicht und Spiegelungen per PMREM;
Sonne aus dem Bild gekappt und als Licht übernommen (77°, Stärke 2,5)
- Schatten: CSM mit vier Stufen (25/100/350/1000 m, je 2048²), Schutznetz für beleuchtete Materialien,
Normal-Bias 1,5 Texel je Stufe gegen Streifen auf doppelseitigen Materialien (Figur, Autos)
- Startfigur wirft Schatten, Palmen und Laternen empfangen Schatten, Wasser als ruhige See
- Debug-Panel: Sonne, Himmelslicht, Himmelshelligkeit; Namensnennung um das HDRI ergänzt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(−511, 254) ist der Bezugspunkt aus tools/extract/data/districts.json, der
Mittelpunkt der westlichen Hochhäuser, und liegt in B9. Der höchste Turm,
das Southeast Financial Center, steht in C12. Das Anzeigebeispiel
x 412 / y 1830 liegt in F3, nicht in C10. Im Browser bei Task 4 der
Minimap aufgefallen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sieben Aufgaben: Korrekturdatei lesen und prüfen, Korrekturen je Ebene
anwenden, Laternen in die Pipeline, einzelner Chunk vergrößert, Einhängen
in run.py mit Vorher/Nachher-Probe, Browser liest chunk.lamps.
Vor dem Commit in einer Kopie komplett durchgespielt, inklusive echtem
Pipeline-Lauf (22 s, 2.591 Laternen) und Mutationsproben der neuen Tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beim Abgleich mit Pipeline und Browsercode aufgefallen: add_road hätte
keine Brücke hinzufügen können (Landprüfung am Mittelpunkt statt an den
Enden); set_area zu Wasser ließ Sand und Parks stehen; unbekannte
Feldnamen wären still ignoriert worden; Drehrichtung von add_building,
Abschneiden der Mittellinien und die Laternenregeln waren unbestimmt.
Dazu die exakte Winkelumrechnung für die Laternen, damit sie nach dem
Umzug nicht gespiegelt stehen, und eine neue Regel gegen Laternen in
Gebäuden, die die Browser-Version nicht hatte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dauerhafte Korrekturschicht in der Pipeline, damit der Nutzer die Welt
im Chat Chunk für Chunk korrigieren kann: Stelle über Chunk-Name und
Weltkoordinate von der Minimap, Korrektur in tools/extract/data/
corrections.toml. Aktionen für Gebäude, Straßen, Flächen und Küste und
Requisiten; Formfehler brechen ab, Nichttreffer werden gemeldet und
übersprungen. Die Laternen ziehen dafür in die Pipeline, womit sich
nebenbei ihre Lücke an Chunk-Grenzen schließt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- chunk_name: Reject float/bool indices like JavaScript (Number.isInteger)
- parse_chunk_name: Use fullmatch instead of match($) to reject strings with trailing newlines
- Add invalidIndices fixture cases to catch these edge cases in both languages
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Vier Aufgaben: Chunk-Namen in JavaScript und Python mit gemeinsamem
Beispielsatz; Kartenbild, Begehbarkeitsmaske und map.json aus
overview.py; reine Kartenmathematik mit Tests; Kartenkomponente und
Einhängen mit Browser-Abnahme.
Die Spec bekommt dabei drei Präzisierungen, die sich erst beim Lesen des
Codes ergeben haben: die Namensnennung muss über der Karte liegen
(Lizenzpflicht), die Veraltungsprüfung erkennt nicht jede Änderung, und
die Eingabesperre stoppt Mausereignisse auf der Karte erst an deren
Wurzel, weil ein Stopp in der Capture-Phase auch den eigenen Canvas
taub machen würde.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Nutzer will Chunks im Chat für die Feinkalibrierung ansprechen. Das
Raster wird auf der Karte sichtbar, jeder Chunk heißt als Planquadrat
A1 bis L12 — A1 ist die Nordwest-Ecke, gegen chunkOrigin geprüft. Die
Umrechnung steht in JavaScript und Python mit demselben Beispielsatz,
dazu eine Anzeige von Chunk-Name und Weltkoordinate unter dem Mauszeiger.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine parallele Sitzung arbeitet in .claude/worktrees/ innerhalb dieses
Repositorys. vitest sammelte deren Testdateien mit ein: 136 statt 71
Tests, und unser Lauf wäre fehlgeschlagen, sobald dort ein Test rot ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Downtown aus der Luft: Boden, Stadtfläche, Park, Strand und Straße lagen 1-2 cm
auseinander (geometry.js: SURFACE_OFFSET=0.02 für alle drei Flächen gleich,
ROAD_OFFSET=0.03), bei 150-300 m Kameraabstand unter der Auflösung eines
24-Bit-Tiefenpuffers (~1,3 cm bei 150 m, ~5 cm bei 300 m, gemessen mit dem alten
Nahwert 0.1) — Strand/Park/Stadtfläche lagen zusätzlich exakt aufeinander und
fighteten unabhängig von der Entfernung. Die Brückenoberkante lag exakt auf der
Bodenoberkante (beide y=0).
Vier Änderungen, freigegebener Entwurf:
- Kamera-Nahwert 0.1 -> 0.5 m (src/main.js): fünffache Tiefenauflösung auf jede
Entfernung, far bleibt 2500.
- src/world/materials.js: POLYGON_OFFSETS/POLYGON_OFFSET_ORDER, eine feste,
streng zunehmend negative Stufe je Schicht (Boden 0, Stadt -1, Park -2, Strand -3,
Straße -4, für Faktor und Units gleich) — wirkt im Tiefenpuffer selbst, hält die
Rangfolge deshalb auf jede Entfernung, unabhängig vom geometrischen Abstand.
applyPolygonOffsets() ist eine separate, reine Funktion, damit sie ohne
createMaterials()/Texturladen testbar ist und beweisbar nur diese fünf Materialien
trifft (Brücken/Gebäude/Wasser/Meeresboden bleiben unberührt).
- Strand, Park und Stadtfläche bekommen dabei je eine eigene Stufe, statt sich wie
bisher (SURFACE_OFFSET) eine zu teilen.
- src/world/geometry.js: Brückendeck-Oberkante jetzt BRIDGE_OFFSET (=SURFACE_OFFSET,
0.02 m) statt 0 — derselbe Größenordnungsentscheid wie bei den Flächen, klein genug
für einen unbemerkten Schritt, groß genug für die Tiefenpuffer-Auflösung bei 300 m.
TDD: Rangfolge/Kein-Teilen-Regeln sind rein und zuerst als fehlschlagende Tests
geschrieben (tests/materials.test.js, 5 neue), ebenso die Brückenanhebung
(tests/geometry.test.js, 1 neu) — beide zunächst rot, dann grün. 65 -> 71 Tests,
alle grün.
Browser-Verifikation (Downtown, Chunk 2_9, gleiche Kameraposition vorher/nachher):
das zuvor durchgängig zackige/zerstückelte Straßen-/Flächenbild ist jetzt glatt und
durchgängig lesbar. Ego-Perspektive dicht an einer Gebäudewand zeigt mit near=0.5
kein sichtbares Durchsehen. Details, Screenshot-Pfade und offene Punkte (Brücken-
Nahaufnahme nicht separat bestätigt) im Bericht:
.superpowers/sdd/2026-09-12-leonida-vice-slice/schichten-report.md
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Große Kartenansicht auf Taste M über dem laufenden Spiel: eigene Position
mit Blickrichtung, Fahrzeuge, Zoomen, Verschieben, Klick zum Hinspringen.
Kartenbild vorab aus overview.py mit 1 px/m, Landung neben statt in
Gebäuden über eine Begehbarkeitsmaske. Eingaben werden in der
Capture-Phase abgefangen statt über unbindEvents(), das die ganze Figur
anhalten würde.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
727 von 2.286 rohen Grundrissen (31,8 %, aber nur 1,8 % der bebauten Fläche) sind
unter 4 m an der kurzen Seite ihres gedrehten Hüllrechtecks — extrudiert werden
daraus Wände, keine Häuser. Eine Mindestbreite allein reicht nicht: einzelne Formen
bleiben bei >= 4 m Breite trotzdem extrem lang und dünn (z.B. 4,27 m x 153 m,
Verhältnis 35,9). MAX_ASPECT_RATIO=12 ergänzt die Mindestbreite MIN_BUILDING_WIDTH=4,
an einer echten Lücke in den gemessenen Verhältnissen festgemacht (größte plausible
große Grundfläche: Verhältnis 10,8 bei 7.196 m²; nächste eindeutig wandartige Form:
Verhältnis 12,0 bei 60,5 m²). Zusammen fallen 766 Grundrisse (33,5 %, 36.986 m² =
3,3 % der Fläche) weg.
Bestehende Fixture in test_small_staircase_building_is_not_dropped vergrößert (4x4-
auf 6x6-Block): ihre kurze Seite lag zufällig bei ~3,7 m, unter der neuen Schwelle,
und hätte den Test sonst für den falschen Grund bestehen lassen (Ruling 74 statt
Ruling 28).
Pipeline neu durchgelaufen: eindeutige Gebäude 2.282 -> 1.520 (-33,4 %), Dreiecke
über alle 144 Chunks 327.181 -> 307.678 (-6,0 %, vollständig aus der Gebäude-Ebene),
public/data/vice-a 2,73 M -> 2,50 M.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
street_lamp_02.glb ist nur 1,657 m hoch (per gltf-transform inspect gemessen:
bboxMax.y 1.26478 - bboxMin.y -0.39258) und ihr Fuß liegt unter dem Ursprung.
#buildLamps setzte das Modell bisher unskaliert auf y=0 — der Fuß steckt im Boden,
und 1,66 m ist ohnehin zu klein neben 10-18 m hohen Palmen und 10-28 m breiten
Straßen dieser Karte. Poly Havens eigene Angabe ("1.7m tall") bestätigt, dass die
Rohgröße korrekt gemessen ist — der Fehler liegt in der Verwendung dieser Rohgröße
in diesem Maßstab, nicht in der Messung selbst.
lampFit(minY, maxY, targetHeight) leitet Skalierung und Fuß-Anhebung aus der
gemessenen bbox her; STREET_LAMP_HEIGHT = 4.0 m als benannte Zielgröße (Mitte des
für dekorative Straßenlaternen üblichen 3-6-m-Bereichs). load() wendet beides auf
die Modellwurzel an, bevor die Teil-Matrizen (Mast, Glas) eingesammelt werden, damit
#buildLamps() unverändert bleiben kann.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
playerCapsule.position ist beim Fahren nicht die Weltposition, sondern der lokale
Sitzplatz-Offset (syncMountedPlayer hängt die Kapsel an vehicleGroup und überschreibt
ihre .position mit driverSeatPosition * scale). main.js trieb Streaming, isReadyAt,
das Auffangnetz, lastSafe und den Schattenwurf trotzdem ausnahmslos damit an — die
Welt hörte beim Fahren auf nachzuladen.
src/player.js bündelt "wo ist der Spieler gerade" in playerPosition()/
playerIsGrounded(): Fahrzeugposition (vehicleGroup.position) und Radkontakt, wenn
eines aktiv ist, sonst Kapsel und das vorhandene getIsOnGround(). Zusätzlich über
getControllerMode() abgesichert, nicht nur getActiveVehicle() — Letzteres wird beim
Aussteigen vom Controller nie zurückgesetzt (nur destroy() tut das), im Browser
nachgewiesen: nach einem erzwungenen Ausstieg lieferte getActiveVehicle() weiterhin
das verlassene Fahrzeug, obwohl controllerMode schon wieder 0 war.
Im Browser mit von Hand angetriebenem tick()/player.update() verifiziert (die
automatisierte Vorschau drosselt requestAnimationFrame auf ~2 Hz): Fahrzeug 227
Einheiten bewegt, playerPosition() folgte exakt, vier neue Chunks luden nach
(Netzwerk-Log bestätigt), sichtbar andere Umgebung nach der Fahrt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
window.__shotWithReadout war nur zur Beleg-Aufnahme für Ruling 68
gedacht (Höhe-Regler nach Figurenwechsel) und gehört nicht in den
ausgelieferten Code.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>