Hausputz: UV-Doppel-Flip raus, tga::load → Result, hide_object entfernt

UVs laufen jetzt durchgehend in glTF-/tga-Konvention (der Flip-Hin-und-
Zurück stammte vom gelöschten OBJ-Pfad). Kaputte Welt-Texturen fallen auf
den Platzhalter statt zu panicken. Player::eye() statt vierfacher
Augenpunkt-Rechnung. hide_object war ein irl3d-Demo-Relikt ohne
Konsument.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 20:23:10 +02:00
parent 31e1942870
commit 01f299aa4f
11 changed files with 46 additions and 48 deletions
+16 -9
View File
@@ -101,7 +101,11 @@ pub fn run(mut session: Session) {
// FONT_CGA, CURSORS, ORN). Decode (CPU) bleibt in run(); Fonts, Cursor und
// Ornament sind weiß-auf-schwarz und bekommen ihre Alpha-Maske aus der
// Luminanz.
let load_keyed = |p: &str| ui::key_luminance(&tga::load(&assets::path(p)));
// UI-Texturen sind Projekt-Assets (kein Autorinnen-Content) — fehlen sie,
// ist die Installation kaputt, da darf es scheitern.
let load_keyed = |p: &str| {
ui::key_luminance(&tga::load(&assets::path(p)).expect("UI-Textur (Projekt-Asset)"))
};
// Font-Atlanten zuerst keyen, daraus die Glyph-Breiten messen (Font),
// dann dieselben Bilder in die GPU-Texturliste übernehmen.
let ega_img = load_keyed("assets/textures/fonts/oldschool-ega-8x14.tga");
@@ -196,13 +200,17 @@ fn load_sound(name: &str) -> Option<audio::Sound> {
}
}
/// Welt-Textur laden. Autorinnen-Content: fehlt oder klemmt die Datei, wird
/// gemeldet und der Platzhalter genommen — der selbst ist Projekt-Asset und
/// darf hart scheitern.
fn load_texture(name: &str) -> Image {
let path = assets::path(&format!("assets/textures/{name}.tga"));
if std::path::Path::new(&path).exists() {
tga::load(&path)
} else {
eprintln!("[tex] {name}: keine Datei unter {path} — Platzhalter");
tga::load(&assets::path(&format!("assets/textures/{}.tga", props::FALLBACK_TEXTURE)))
match tga::load(&assets::path(&format!("assets/textures/{name}.tga"))) {
Ok(img) => img,
Err(e) => {
eprintln!("[tex] {e} — Platzhalter");
tga::load(&assets::path(&format!("assets/textures/{}.tga", props::FALLBACK_TEXTURE)))
.expect("Platzhalter-Textur (Projekt-Asset)")
}
}
}
@@ -487,8 +495,7 @@ impl App {
// Klick-Ziele (`signal`-Property). Steuert den Interakt-Cursor und
// löst beim Klick unten das `use` aus.
let aim: Option<String> = if play {
let p = self.session.player.pos;
let eye = [p[0], p[1] + player::EYE_HEIGHT, p[2]];
let eye = self.session.player.eye();
let dir = self.session.player.look_dir();
self.session.pick(eye, dir, crate::session::INTERACT_REACH).map(str::to_string)
} else {
+8 -9
View File
@@ -8,11 +8,10 @@
//! `assets/textures/{name}.tga` (macht der Aufrufer). Faces ohne Material
//! (`""`) laufen unter [`FALLBACK_TEXTURE`].
//!
//! UVs: das Modell trägt den V-Ursprung unten links, unsere `tga::Image`s
//! oben links — V wird hier gespiegelt (Achsen-Konvention ist Sache des
//! Konsumenten, siehe engine::tga). Nur `visible`-Objekte werden
//! ausgegeben; Collider-/Zonen-Volumen sind Sache von engine::collision
//! und engine::trigger.
//! UVs kommen unverändert durch (Modell und `tga::Image` teilen den
//! Oben-links-Ursprung). Nur `visible`-Objekte werden ausgegeben;
//! Collider-/Zonen-Volumen sind Sache von engine::collision und
//! engine::trigger.
use crate::engine::model::Model;
use crate::render::scene::{Batch, Mesh, Vertex};
@@ -46,7 +45,7 @@ pub fn build(model: &Model, tex_names: &[String]) -> Mesh {
if !o.visible { continue; }
let base = verts.len() as u32;
for (p, uv) in o.verts.iter().zip(&o.uvs) {
verts.push(Vertex { pos: *p, uv: [uv[0], 1.0 - uv[1]] }); // V-Flip
verts.push(Vertex { pos: *p, uv: *uv });
}
for (t, &m) in o.tris.iter().zip(&o.tri_mats) {
let name = resolve(&model.materials[m]);
@@ -93,7 +92,7 @@ mod tests {
}
#[test]
fn builds_only_visible_objects_with_v_flip() {
fn builds_only_visible_objects() {
let m = sample();
let names = texture_names(&m);
assert_eq!(names, vec!["carpet".to_string()]);
@@ -103,8 +102,8 @@ mod tests {
assert_eq!(mesh.verts.len(), 3);
assert_eq!(mesh.indices, vec![0, 1, 2]);
assert_eq!(mesh.batches.len(), 1);
// uv (1,1) → V-Flip → (1,0).
assert_eq!(mesh.verts[2].uv, [1.0, 0.0]);
// UVs unverändert (geteilte Oben-links-Konvention).
assert_eq!(mesh.verts[2].uv, [1.0, 1.0]);
}
#[test]