--- name: placing-editor-models description: Use when a model built in the three-d-stage editor (GLB export, often sent with a .mtl) should be placed 1:1 at a real-world spot in the Leonida world — "setz das an die Stelle aus dem Satellitenbild", a yard, kiosk or building next to a park or street in a Planquadrat — or when an entry in src/world/places.js has to move, look different or collide differently. --- # Editor-Modelle in Leonida platzieren ## Überblick Der Ablauf bleibt der des Hood Yard: Brainstorming mit der Lagefrage am Vergleichsbild → kurze Spec → Plan → Umsetzung mit Subagenten und Reviews → Browserprüfung → Gesamt-Review → Abschluss. Vorlage: `docs/superpowers/specs/2026-09-14-hood-yard-design.md` und `docs/superpowers/plans/2026-09-14-hood-yard.md`. Neu gebaut werden muss nichts. Orte-Schicht (`src/world/places.js`), Aufbereitung (`tools/assets/places.mjs`), Bump-Erweiterung, Tests und das Vorwärmen in `main.js` gibt es schon. Ein neuer Ort besteht aus: - einem `PLACES`-Eintrag, optional mit `replaces` (Weltpunkte), wenn an der Stelle schon ein Gebäudekasten aus den Chunk-Daten steht, - einer aufbereiteten `.glb`, - je einer Zeile in `SOLID_BY_PLACE` und `BUMP_BY_PLACE` (`tests/placesAsset.test.js`). Die eigentliche Arbeit sind die Lage, die Materialrollen und die Prüfung im Spiel. **Pflicht-Skills in dieser Reihenfolge:** superpowers:brainstorming, superpowers:writing-plans, superpowers:subagent-driven-development, superpowers:finishing-a-development-branch. ## Schon entschieden, nicht neu fragen | Punkt | Festlegung | |---|---| | Maßstab | 1:1, three-d-stage baut in Metern; `PLACES` hat kein `scale` | | Lage | `at` = Welt-x/-y (Minimap), `rotation` in Grad gegen den Uhrzeigersinn, Norden oben | | Höhe | Mit Bodenplatte (flaches Teil über die ganze Grundfläche): `lift = 0,06 − t`, t = Höhe, auf der die übrigen Gruppen stehen (Hood Yard: 0,34, obwohl `ground` wegen Bordsteinen bis 0,5 reicht → −0,28). Bei 0 cm flackert der Boden, ein Podest wird zur Stolperkante. Ohne Bodenplatte: `lift = 0` | | Radien | Vorladen 600 m, einsetzen 250 m, herausnehmen 450 m; `tests/placesSite.test.js` prüft den Chunk-Abstand | | Aufbereitung | nur `tools/assets/places.mjs`: die gltf-transform-CLI verwirft `EXT_materials_bump`. Ergebnis < 30 MB, die Quelle kommt nie ins Repo | | Kollision | über Materialnamen (`SOLID_MATERIALS`); Pflanzen und Kleinteile durchlässig, Stämme fest | | Arbeitsweise | eigener Arbeitsbaum, wenn im Hauptordner fremde Änderungen liegen | ## Ablauf 1. **Modell vermessen**, die `.mtl` ignorieren: ```bash node .claude/skills/placing-editor-models/scripts/measure-model.mjs ~/Downloads/.glb /model.json ``` - Dreiecke zählt das Skript je Knoten; die Summe über die Meshes unterschätzt bei three-d-stage stark. - Außerdem zeigt es Bump-Materialien, Außenmaße und Baugruppen. - **Bodenplatte suchen:** eine flache Gruppe, die x und z der Außenmaße abdeckt. Gibt es keine, ist `lift = 0`. - **t bestimmen:** t ist das kleinste y-Minimum der übrigen Gruppen, also die Höhe, auf der sie stehen. Nicht das y-Maximum der Plattengruppe, darin stecken oft Bordsteine und Wege. Dann gilt `lift = 0,06 − t`. - Die Wurzel-Verschiebung ist nur ein Hinweis, nicht die Plattendicke. 2. **Stelle holen:** Planquadrat → Weltbox (`parseChunkName`, `chunkOrigin` in `src/world/coords.js`), dann ein Fenster von etwa 150 × 50 m: ```bash node .claude/skills/placing-editor-models/scripts/site-data.mjs /site.json ``` Straßenränder, Flächen und Laternen daraus ablesen. - **Ersetzt der Ort ein Gebäude:** Steht in `site.json` unter `buildings` ein Grundriss an der Stelle, den der Ort ersetzen soll, dessen Mitte als `replaces: [[x, y]]` in den `PLACES`-Eintrag. Der `ChunkStreamer` lässt ihn dann weg (`withoutReplacedBuildings`). Den Ort nicht um den Kasten herum verschieben, nicht `corrections.toml` vorschlagen. Vorlage: `docs/superpowers/specs/2026-09-14-tribuene-design.md`. 3. **Satellitenbild einhängen:** - **Anker:** ein Punkt, der im Bild und in den Chunk-Daten eindeutig ist (Feldecke, Kreuzung), möglichst nah am Ziel. Echte Stadt und Spielkarte weichen voneinander ab. - **Maßstab:** Meter pro Pixel aus Autos (~4,5 m) oder Feldmarkierungen schätzen, mit ±15 % Unsicherheit rechnen. 4. **Vergleichsbild** rendern und per `SendUserFile` schicken: ```bash tools/extract/.venv/bin/python .claude/skills/placing-editor-models/scripts/render-site.py --site site.json --model model.json --at X Y --satellite --anchor-px PX PY --anchor-world AX AY --mpp 0.15 --out lage.png ``` - Dann genau **eine** Frage mit `AskUserQuestion`: „wie im Bild“ oder die zwei naheliegenden Verschiebungen. - Ist die echte Anlage länger oder kürzer als das Modell, die Ausrichtung (Mitte, Anfang, Übergang zwischen Bauteilen) ausdrücklich zur Wahl stellen. 5. **Materialrollen festlegen.** `NEAR_MATERIALS` und `SIMPLIFY_RATIOS` (`places.mjs`) sowie `SOLID_MATERIALS` (`places.js`) sind global und nach Namen geordnet. - Neue Namen gegen die Listen abgleichen. Hat derselbe Name eine andere Rolle (etwa `grass` als begehbare Fläche statt als Halme), dem Nutzer vorlegen und nicht still teilen. - Anteile beim Vereinfachen, nach gezeichneten Dreiecken: Bäume mit mehr als 300 000 → 0,15; Gras und Büsche → 0,5; Kleinteile → 0,25; Gebäude, Boden und Zäune nie. 6. **Spec und Plan** kurz halten, nur was neu ist: - **Spec:** Befund (Tabelle aus Schritt 1), Lage-Tabelle mit Abständen zu Straße, Feldern und Laternen, Materialrollen, Budget. - **Plan-Tasks:** - Materiallisten - Aufbereitung und Asset-Test - `PLACES`-Eintrag und Lage-Test - Browserprüfung 7. **Aufbereiten:** `node tools/assets/places.mjs `. Aus dem Bericht die `bounds` in `PLACES` übernehmen; `gpuMB` und die Dreieckszahl mit dem Nutzer abgleichen, wenn sie deutlich über dem Hood Yard liegen (231 MB, 658 000). 8. **Tests:** - `tests/placesSite.test.js` prüft jeden `PLACES`-Eintrag von selbst, bei `replaces` auch, ob der ersetzte Grundriss noch an der Stelle liegt. - `tests/placesAsset.test.js` läuft über `describe.each(PLACES)`. Für den neuen Ort je eine Zeile ergänzen: `SOLID_BY_PLACE` (feste Materialien seiner Datei) und `BUMP_BY_PLACE` (Materialien mit Bump, sonst `[]`). 9. **Browserprüfung** nach `browser-checks.md`: - Lage prüfen. - Kollision: Kapsel steht auf der Platte, eine Wand stoppt sie, Pflanzen sind durchlässig. - Drei Blicke als Bilder an den Nutzer. - Kosten mit `gl.finish` messen, mit und ohne Ort. - Vorwärmen: Texturzähler am Startpunkt ablesen. 10. **Gesamt-Review, Abnahme durch den Nutzer, Branch abschließen.** ## Fallstricke | Symptom | Ursache | Gegenmittel | |---|---|---| | Dreieckszahl viel kleiner als gezeichnet | three-d-stage benutzt Meshes mehrfach | je Knoten zählen (`measure-model.mjs`) | | Wellblech, Relief oder Rillen flach | gltf-transform-CLI verwirft `EXT_materials_bump` | nur `places.mjs` | | Figur läuft durch Wände, steht bei y 0,75 statt auf der Platte | meshopt-Positionen sind quantisiert, der Collider-Bau klemmt sie auf ±1 | Orte immer über `prepareModel`/`PlaceLayer` einsetzen; im Browser prüfen, dass feste Meshes Float32 haben | | Boden flackert im Ort | Plattenoberkante auf y 0 | `lift` für +6 cm | | Laterne oder Palme steht im Ort | Requisiten entstehen je Chunk, Laternen bis 16 m neben der Mittellinie, auch aus Nachbarchunks | Site-Test, Lage verschieben | | Ort schwebt nach einem Minimap-Sprung kurz über dem Meer | der Streamer baut einen Chunk pro Frame | hingenommen (Ruling Hood Yard) | | Asset-Test rot, „verteilt SOLID_MATERIALS …“ oder eine fehlende Zeile | Ort ohne Zeile in `SOLID_BY_PLACE`/`BUMP_BY_PLACE` | Zeile für den Ort ergänzen (Schritt 8) | | Site-Test „in keinem Gebäude“ rot | der Ort soll ein vorhandenes Gebäude ersetzen | `replaces` mit der Mitte des Grundrisses setzen (Schritt 2), statt den Ort zu verschieben | | `preview_start` findet den Server nicht | liest nur `.claude/launch.json` des Hauptordners | Nutzer fragen, Arbeitsbaum kurz verlassen, Eintrag setzen, zurück, am Ende entfernen | | Befehle im Arbeitsbaum verweigert | Isolationswache mag keine Variablen als Befehl oder Skriptpfade mit Leerzeichen | Pfade ausschreiben; statt `review-package` drei Befehle: `git log --oneline A..B >`, `git diff --stat A..B >>`, `git diff -U10 A..B >>` in eine Datei | | Draw-Call- und Dreieckszahlen zu optimistisch | `renderer.info` wird nach den Schattenstufen zurückgesetzt | `gl.finish`-Messung (`browser-checks.md` §8) | | Ruckler beim ersten Erscheinen | Vorwärmen lief nicht | am Startpunkt muss `renderer.info.memory.textures` die Texturen des Orts schon enthalten | | Seite friert nach einem Browser-Hook ein | Ausnahme im `onBeforeRender`-Hook beendet die Zeichenschleife | `window.__probe ??= {}`, nach Vite-Reload neu einrichten | Werkzeuge: `scripts/` (Vermessung, Kartendaten, Vergleichsbild, Screenshot-Dekodierung); Browser-Ausschnitte: `browser-checks.md`.