Map-Loader entfernt: glTF ist das einzige 3D-Format

engine/map.rs + render/brush.rs raus, Welt kommt komplett aus .glb.
Spawn via Empty mit role=spawn statt info_player_start; testscene.glb
(Boden, Wände, Spawn) ersetzt test.map.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 20:37:18 +02:00
co-authored by Claude Fable 5
parent 186ee9a817
commit 56d92cdb46
13 changed files with 94 additions and 898 deletions
+12 -30
View File
@@ -14,7 +14,6 @@
//! Schicht-Regel wie `cli`: Geschwister von `engine`/`session`, konsumiert
//! deren Schnittstellen — nie umgekehrt.
mod brush;
mod camera;
mod font;
mod gpu;
@@ -36,7 +35,7 @@ use winit::window::{CursorGrabMode, Window, WindowId};
use crate::engine::player;
use crate::engine::tga::Image;
use crate::engine::{assets, gltf, map, model, tga};
use crate::engine::{assets, gltf, model, tga};
use crate::session::{FrameInput, Mode, Session};
use camera::Camera;
use gpu::Gpu;
@@ -62,29 +61,26 @@ pub fn run(mut session: Session) {
// CPU-Assets dekodieren (Pull über die engine-Decoder). Das Hochladen
// auf die GPU macht später `Gpu` — Decode (CPU) und Upload (GPU) bleiben
// getrennt.
let world = map::load(&assets::path("assets/maps/test.map"));
// Blender-Modelle: alle .glb unter assets/maps/props/. Autorinnen-
// Content — ein kaputtes File wird gemeldet und übersprungen, nie fatal.
// getrennt. Die Welt sind alle .glb unter assets/maps/props/ —
// Autorinnen-Content, ein kaputtes File wird gemeldet und übersprungen,
// nie fatal.
let models = load_models(&assets::path("assets/maps/props"));
// Eine geteilte Texturliste für Brush- und Modell-Geometrie, damit beide
// Meshes denselben Index-Raum benutzen und verschmelzen können.
let mut tex_names = brush::texture_names(&world);
// Eine geteilte Texturliste über alle Modelle, damit ihre Meshes
// denselben Index-Raum benutzen und zu einem Buffer verschmelzen können.
let mut tex_names: Vec<String> = Vec::new();
for (_, m) in &models { tex_names.extend(props::texture_names(m)); }
tex_names.sort_unstable();
tex_names.dedup();
let images: Vec<Image> = tex_names.iter().map(|n| load_texture(n)).collect();
let dims: Vec<(u32, u32)> = images.iter().map(|i| (i.width, i.height)).collect();
let mut mesh = brush::build(&world, &tex_names, &dims);
let mut mesh = Mesh::default();
for (_, m) in &models { mesh.append(props::build(m, &tex_names)); }
report_map(&world, &tex_names, &mesh);
println!("[scene] {} Modelle, Texturen {:?} → {} Vertices, {} Dreiecke",
models.len(), tex_names, mesh.verts.len(), mesh.indices.len() / 3);
// Sim-Welt (Collision-Brushes + Spawn, dann Collider/Trigger der
// Modelle) in die Session einspielen — die Simulation gehört der
// Session, nicht dem Render-Frontend.
session.load_world(&world);
// Sim-Welt (Collider, Trigger, Spawn-Empty) in die Session einspielen —
// die Simulation gehört der Session, nicht dem Render-Frontend.
for (_, m) in &models { session.load_props(m); }
// UI-Texturen, Reihenfolge = die Index-Konstanten in ui (WHITE, FONT_EGA,
@@ -184,20 +180,6 @@ fn load_texture(name: &str) -> Image {
}
}
/// Kurzer Lade-Report: Map-Inhalt und resultierende Geometriegröße.
fn report_map(world: &map::Map, tex_names: &[String], mesh: &Mesh) {
let brushes: usize = world.entities.iter().map(|e| e.brushes.len()).sum();
let faces: usize = world.entities.iter()
.flat_map(|e| &e.brushes)
.map(|b| b.faces.len())
.sum();
println!(
"[map] {} Entities, {} Brushes, {} Faces, Texturen {:?} → {} Vertices, {} Dreiecke",
world.entities.len(), brushes, faces, tex_names,
mesh.verts.len(), mesh.indices.len() / 3,
);
}
/// Aktueller Eingabezustand. Tasten als gehaltene Flags (nicht Events),
/// damit die Bewegung pro Frame mit dt skaliert; Maus-Delta wird zwischen
/// den Frames akkumuliert und nach dem Anwenden genullt.