Root-Cause: Ghost-Preview und Platzierung nutzten zwei unterschiedliche
Pfade/Strukturen — der Ghost-Root trug den halben Mesh-Lift selbst
(Root=Y+half), das platzierte Objekt hebt nur das Kind-Mesh; die
Platzierung rastete X/Z und hob Y auf terrain_height_at des UNGERASTETEN
Punkts. Bei Raster-Snap und an Haengen wich die platzierte Position von
der Vorschau ab.
Fix — EIN Pfad (ObjectOps.placement_foot): Raster-Snap auf X/Z, dann
Y = Terrainhoehe am GERASTETEN Punkt (SvmapDocument.terrain_height_at).
Ghost-Root = Fusspunkt, Kind-Mesh um half_height gehoben — identische
Struktur wie map_view_builder. Objekt-Cursor trifft jetzt das GELAENDE
(TerrainField.raycast, gleicher Pfad wie Terrain-/Bauen-/Spawn-Werkzeug)
statt der flachen Basis-Ebene; Hoehen-Cache dekodiert einmal pro
Struktur-Aenderung (refresh_terrain_cache aus _after_structure_change).
Tests (test_library 40->94 Checks): Platzier-Fusspunkt (Rampe 0..20 m:
Fusspunkt == Terrainhoehe, Ghost-Pfad == Platzierungs-Pfad, Raster-Snap-Y
am gerasteten Punkt), Registry-Fusspunkt (alle 54 Objekte: Mesh-Unterseite
== pos, kein Schweben/Eingraben), Ghost-Preview == platziertes Objekt mit
simulierter Kamera (Treffer-Zelle == Cursor-Zelle, <5 cm). Editor-Suiten
501 Checks gruen, Builder-Export 21/0, GUI-Smoke 20 gruen.
Karte t_7567a06f. Hoehe wohnt direkt in object.pos.y — KEIN svmap-Format-
Feld noetig (V9 erlaubt freie y-Werte in bounds; Server-Loader geprueft).
Absenken stoppt am Terrain (nichts begraben), Anheben deckelt auf
bounds.max. Spiegeln am Objekt selbst (Position bleibt): X → −rot,
Z → 180°−rot selbstinvers. Alles über ActionHistory (Merge-Tags für
Lift-Serien), deutsche UI + Klartext-Status, dragt behält Terrain-Abstand.
test_ux.gd: 90 Checks (42 neu), Suite 5/5 grün, xvfb-Smoke UI-Bau ok.
- Bibliothek-Browser: 11 etablierte Objekte in deutschen Klapp-Gruppen
(Kisten & Barrieren / Bauwerke / Natur, neue IDs wandern nach Sonstiges)
- 128-px-Thumbnails einmalig gerendert (tools/gen_thumbs.gd, xvfb) und
committed; fehlendes PNG = sauberer Text-Fallback (LibraryBrowser)
- Snap to Ground: ein Klick setzt ALLE schwebenden Objekte auf Terrain-Höhe,
EIN Undo-Eintrag (ObjectOps.snap_to_ground)
- Raster-Snap (1 m): Toggle — rastet Platzieren und Ziehen auf ganze Meter
- Deutsche Namen nach LibraryBrowser verschoben (headless testbar)
- tests/test_library.gd: 47 Checks (Bibliothek, Gruppen, Thumbnails, Snap,
CRUD+Undo über alle Objekte, svmap-Roundtrip byte-identisch, Poly-Budget)
- Desktop-Smoke: --library-demo erzeugt Screenshots (lokal via xvfb verifiziert)
- UI-Layout: linke Spalte min 909→797 px, Statusleiste wieder vollständig sichtbar