Blender-Import-Pipeline: glTF/OBJ, col_*-Proxies, Trigger

Blender wird das Haupt-3D-Tool: zwei Loader (engine/gltf.rs für .glb als
Hauptpfad, engine/obj.rs aus irl3d eingebettet) liefern ein neutrales
engine/model.rs::Model. Loader sind reine Dekoder; das Frontend zieht sie
und schiebt das Ergebnis per Session::load_props in den State, der
Renderer baut sein Mesh (render/props.rs, verschmolzen mit der
Brush-Welt über eine geteilte Texturliste).

glTF trägt Blender-Custom-Properties (node.extras → Object::props) und
Empties (Nodes ohne Mesh) als Entity-Kanal. Namenskonvention: `col_*` =
unsichtbarer Collision-Proxy, aus dem collision::add_model konvexe
Ebenen-Sets baut (konvex-only, daher kein Default-Collider auf
Sichtgeometrie).

Trigger (engine/trigger.rs) hängen an der Custom Property `signal`:
mit Collider = Point-and-Click-Ziel (Session::pick raycastet aus der
Bildmitte, Treffer läuft durch den `use <name>`-Trichter), ohne Collider
= unsichtbare Betretens-Zone, die einmal je Eintritt feuert. Gefeuert
wird nur über Session::exec — der Eingabe-Trichter bleibt der einzige
Weg in den State.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 15:20:00 +02:00
parent 67c4a366c7
commit f7bfdb4a0b
14 changed files with 1465 additions and 43 deletions
+6 -3
View File
@@ -30,8 +30,7 @@ pub(crate) const ORN: usize = 4;
pub(crate) const CUR_MOUSE: usize = 0; // Maus, normal
pub(crate) const CUR_MOUSE_INTERACT: usize = 1; // Maus, über Klickziel
pub(crate) const CUR_HUD: usize = 2; // First-Person-Pointer, normal
// Die interagierbare HUD-Variante (3) kommt mit dem Welt-Raycast.
#[allow(dead_code)] pub(crate) const CUR_HUD_INTERACT: usize = 3;
pub(crate) const CUR_HUD_INTERACT: usize = 3; // dito, über einem Klick-Ziel
const CURSOR_PX: f32 = 16.0;
const ORN_CORNER: f32 = 8.0; // Eckgröße im Ornament-Atlas (und im Panel)
@@ -61,6 +60,9 @@ pub(crate) struct Cursor {
/// Maus gefangen (Flycam aktiv) → First-Person-Pointer mittig statt
/// Maus-Cursor an `pos`.
pub(crate) grabbed: bool,
/// Fadenkreuz zielt auf ein Klick-Ziel (`Session::pick`) → Interakt-
/// Variante des HUD-Pointers.
pub(crate) hud_interact: bool,
}
/// Akkumulierte Overlay-Geometrie eines Frames, fertig für den Sprite-Pass.
@@ -237,7 +239,8 @@ pub(crate) fn layout(internal: [f32; 2], fonts: &Fonts, session: &Session, cur:
// Cursor zuletzt → über allem. Gefangen → HUD-Pointer mittig; sonst
// Maus-Cursor, „interagierbar" wenn er über einem Klickziel steht.
if cur.grabbed {
ui.cursor(CUR_HUD, [internal[0] * 0.5, internal[1] * 0.5], [1.0; 4]);
let idx = if cur.hud_interact { CUR_HUD_INTERACT } else { CUR_HUD };
ui.cursor(idx, [internal[0] * 0.5, internal[1] * 0.5], [1.0; 4]);
} else {
let idx = if hover.is_some() { CUR_MOUSE_INTERACT } else { CUR_MOUSE };
ui.cursor(idx, cur.pos, [1.0; 4]);