Collider via collide-Property + Quickhull, OBJ-Pfad entfernt

col_*-Namenspräfix raus: Rollen steuern nur noch Custom Properties
(collide=1 sichtbar+Collider, "proxy" unsichtbar, signal wie gehabt).
Collider sind die konvexe Hülle der Vertices — konkave Meshes erlaubt.
glb ist der einzige Modell-Pfad; suzanne.obj → suzanne.glb.

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 f7bfdb4a0b
commit 9db3929750
12 changed files with 371 additions and 2497 deletions
+11 -15
View File
@@ -36,7 +36,7 @@ use winit::window::{CursorGrabMode, Window, WindowId};
use crate::engine::player;
use crate::engine::tga::Image;
use crate::engine::{assets, gltf, map, model, obj, tga};
use crate::engine::{assets, gltf, map, model, tga};
use crate::session::{FrameInput, Mode, Session};
use camera::Camera;
use gpu::Gpu;
@@ -64,7 +64,7 @@ pub fn run(mut session: Session) {
// 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/.obj unter assets/maps/props/. Autorinnen-
// Blender-Modelle: 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"));
@@ -81,9 +81,9 @@ pub fn run(mut session: Session) {
for (_, m) in &models { mesh.append(props::build(m, &tex_names)); }
report_map(&world, &tex_names, &mesh);
// Sim-Welt (Collision-Brushes + Spawn, dann col_*-Proxies der Modelle) in
// die Session einspielen — die Simulation gehört der Session, nicht dem
// Render-Frontend.
// 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);
for (_, m) in &models { session.load_props(m); }
@@ -134,25 +134,21 @@ pub fn run(mut session: Session) {
event_loop.run_app(&mut app).expect("winit: run");
}
/// Alle Blender-Modelle (`.glb` und `.obj`) eines Verzeichnisses laden
/// (sortiert → deterministische Reihenfolge). Fehlende Verzeichnisse sind
/// kein Fehler (kein Props-Ordner = keine Props); kaputte Dateien werden
/// gemeldet und übersprungen.
/// Alle Blender-Modelle (`.glb`) eines Verzeichnisses laden (sortiert →
/// deterministische Reihenfolge). Fehlende Verzeichnisse sind kein Fehler
/// (kein Props-Ordner = keine Props); kaputte Dateien werden gemeldet und
/// übersprungen.
fn load_models(dir: &str) -> Vec<(String, model::Model)> {
let Ok(entries) = std::fs::read_dir(dir) else { return Vec::new(); };
let mut paths: Vec<std::path::PathBuf> = entries.flatten()
.map(|e| e.path())
.filter(|p| matches!(p.extension().and_then(|s| s.to_str()), Some("obj" | "glb")))
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("glb"))
.collect();
paths.sort();
let mut models = Vec::new();
for p in paths {
let path = p.to_string_lossy();
let loaded = match p.extension().and_then(|s| s.to_str()) {
Some("glb") => gltf::load(&path),
_ => obj::load(&path),
};
match loaded {
match gltf::load(&path) {
Ok(m) => {
let name = p.file_stem().map_or_else(String::new, |s| s.to_string_lossy().into_owned());
let tris: usize = m.objects.iter().map(|o| o.tris.len()).sum();