diff --git a/README.md b/README.md index c4a185f..46bf032 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,7 @@ Alternativ ohne Fenster/GPU in der Konsole: `cargo run -- --cli` Ein anderes Welt-Verzeichnis laden (Default `assets/maps/props/`): `cargo run -- --maps ` (Pfad relativ zum aktuellen Verzeichnis). + +Assets (Ink-Dialoge, Blender-Welten) baut `cargo build` bei Bedarf selbst, +sofern die Werkzeuge da sind (`inklecate` liegt bei, Blender über `$BLENDER` +oder `PATH`). Ohne sie werden die eingecheckten `.ink.json` / `.glb` benutzt. diff --git a/artist.md b/artist.md index fb9d84f..093ba01 100644 --- a/artist.md +++ b/artist.md @@ -5,20 +5,22 @@ Start in der Konsole gemeldet (`[model]`, `[audio]`, `[tex]`, …). ## 3D / Blender -- **Format:** glTF Binary (`.glb`) nach `assets/maps/props/` — alle Dateien - dort werden beim Start geladen (anderer Ordner: `cargo run -- --maps `). -- **Beim Export anhaken:** „Include → Custom Properties" (sonst fehlen die - Properties unten) **und** „Include → Textures" / „Data → Images" - (sonst fehlen die Texturen). +- **Ablage:** die `.blend` in ihren Map-Ordner legen — + `assets/maps//.blend`. `cargo build` (oder + `./tools/build-models.sh`) exportiert sie zu `.glb` daneben; von + Hand exportieren musst du nichts. Die Welt sind alle `.glb` unter + `assets/maps/props/`; ein anderer Ordner per `cargo run -- --maps `. - **Maßstab:** 1 Blender-Meter = 1 Engine-Meter. Augenhöhe 1,6 m, Türen/Durchgänge ≥ 2 m hoch und ≥ 0,8 m breit denken. -- **Texturen:** Bild im Material als Base Color zuweisen — es wird beim - Export in die `.glb` eingebettet und mitgeladen (kein `assets/textures/` - mehr). Format **PNG** (Export „Images: PNG" oder „Automatic"). Fehlt das - Bild, erscheint die Platzhalter-Textur. +- **Texturen:** Bild im Material als Base Color zuweisen (PNG) — es wird + beim Export in die `.glb` eingebettet. Damit die Datei self-contained + bleibt: „File → External Data → Pack Resources". Fehlt das Bild, + erscheint die Platzhalter-Textur. - **Kacheln / Skalieren:** über einen **Mapping-Node** vor dem Image-Texture-Node (Location/Scale). Die Engine backt das (`KHR_texture_transform`) beim Laden in die UVs ein. +- **Custom Properties** (unten) kommen automatisch mit; Modifier werden + beim Export angewandt. ### Custom Properties auf Mesh-Objekten @@ -72,8 +74,10 @@ Start in der Konsole gemeldet (`[model]`, `[audio]`, `[tex]`, …). ## Testen -- `cargo run` — Konsole zeigt beim Start, was ankam: Objekte, Dreiecke, - Empties und **jede Custom Property**. Fehlt eine → Export-Haken prüfen. +- `cargo run` — baut geänderte `.blend` neu und zeigt beim Start, was + ankam: Objekte, Dreiecke, Empties und **jede Custom Property**. Fehlt + eine, sitzt sie in Blender nicht am Objekt (Object Properties → Custom + Properties), nicht am Mesh-Data. - Im Spiel (Konsole = das Terminal, aus dem gestartet wurde): `noclip` zum freien Fliegen, `kv` zeigt alle Variablen, `signal ` feuert jedes Signal von Hand, `reload` lädt signals.toml neu. diff --git a/assets/Models/cinema/cinema/Cinemaroom.blend b/assets/maps/cinema/cinema.blend similarity index 68% rename from assets/Models/cinema/cinema/Cinemaroom.blend rename to assets/maps/cinema/cinema.blend index b4379aa..736efdd 100644 Binary files a/assets/Models/cinema/cinema/Cinemaroom.blend and b/assets/maps/cinema/cinema.blend differ diff --git a/assets/maps/cinema/cinema.glb b/assets/maps/cinema/cinema.glb index c304525..778fd65 100644 Binary files a/assets/maps/cinema/cinema.glb and b/assets/maps/cinema/cinema.glb differ diff --git a/build.rs b/build.rs index c66fae2..ceeddbb 100644 --- a/build.rs +++ b/build.rs @@ -1,59 +1,82 @@ -//! Build-Hook: kompiliert geänderte Ink-Dialoge automatisch beim Cargo-Build. +//! Build-Hook: baut geänderte Assets automatisch beim Cargo-Build. //! -//! Reines „Wann"-Stück — das eigentliche Bauen macht `tools/build-dialogues.sh` -//! (und bleibt von Hand aufrufbar). Hier melden wir Cargo nur, wann es uns -//! erneut ausführen soll, und stoßen das Skript an. +//! Reines „Wann"-Stück — das eigentliche Bauen machen die Skripte unter +//! `tools/` (und bleiben von Hand aufrufbar). Hier melden wir Cargo nur, wann +//! es uns erneut ausführen soll, und stoßen die Skripte an: +//! +//! * `build-dialogues.sh` — `.ink` → `.ink.json` (inklecate) +//! * `build-models.sh` — `assets/maps/**/*.blend` → `.glb` (Blender) //! //! Cargo führt build.rs ausschließlich dann erneut aus, wenn eine der unten //! via `rerun-if-changed` gemeldeten Dateien sich ändert. Unveränderte -//! Rebuilds kosten also nichts; nur nach einer `.ink`-Änderung läuft -//! inklecate (inkrementell nur für die betroffene Datei). +//! Rebuilds kosten also nichts; nur nach einer Quell-Änderung läuft das +//! jeweilige Werkzeug (beide Skripte bauen intern inkrementell). -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; fn main() { - let root = env!("CARGO_MANIFEST_DIR"); - let script = Path::new(root).join("tools/build-dialogues.sh"); - let ink_dir = Path::new(root).join("assets/interactions"); + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - // Wann soll Cargo build.rs erneut ausführen? + // --- Ink-Dialoge ------------------------------------------------------ println!("cargo:rerun-if-changed=tools/build-dialogues.sh"); println!("cargo:rerun-if-changed=tools/inklecate"); // Verzeichnis-mtime fängt neu hinzugekommene/gelöschte .ink-Dateien. println!("cargo:rerun-if-changed=assets/interactions"); - // Inhaltsänderungen einzelner .ink-Dateien einzeln melden. - if let Ok(entries) = std::fs::read_dir(&ink_dir) { - for entry in entries.flatten() { - let p = entry.path(); - if p.extension().and_then(|s| s.to_str()) == Some("ink") { - println!("cargo:rerun-if-changed={}", p.display()); - } + watch_ext(&root.join("assets/interactions"), "ink"); + run_script(&root, "tools/build-dialogues.sh", "Dialog"); + + // --- Blender-Welten ------------------------------------------------- + println!("cargo:rerun-if-changed=tools/build-models.sh"); + // Ordner-mtime fängt neue/gelöschte Maps; die .blend selbst einzeln. + // Die erzeugten .glb werden NICHT beobachtet (das Skript schreibt sie). + watch_dirs_and_ext(&root.join("assets/maps"), "blend"); + run_script(&root, "tools/build-models.sh", "Modell"); +} + +/// Jede Datei mit Endung `ext` direkt in `dir` einzeln bei Cargo anmelden. +fn watch_ext(dir: &Path, ext: &str) { + let Ok(entries) = std::fs::read_dir(dir) else { return }; + for entry in entries.flatten() { + let p = entry.path(); + if p.extension().and_then(|s| s.to_str()) == Some(ext) { + println!("cargo:rerun-if-changed={}", p.display()); } } +} - // Skript ausführen. Ausgabe einsammeln, damit ein Dialog-Fehler sichtbar - // wird (Cargo blendet Build-Script-stdout sonst aus). +/// Wie [`watch_ext`], aber rekursiv — und jedes Unterverzeichnis wird selbst +/// beobachtet (fängt neu angelegte/entfernte Maps). +fn watch_dirs_and_ext(dir: &Path, ext: &str) { + let Ok(entries) = std::fs::read_dir(dir) else { return }; + println!("cargo:rerun-if-changed={}", dir.display()); + for entry in entries.flatten() { + let p = entry.path(); + if p.is_dir() { + watch_dirs_and_ext(&p, ext); + } else if p.extension().and_then(|s| s.to_str()) == Some(ext) { + println!("cargo:rerun-if-changed={}", p.display()); + } + } +} + +/// Ein `tools/`-Skript laufen lassen. Ausgabe einsammeln (Cargo blendet +/// Build-Script-stdout sonst aus); Fehlschlag ist fatal, ein nicht startbares +/// bash nur eine Warnung — die committeten Artefakte reichen dann. +fn run_script(root: &Path, rel: &str, label: &str) { + let script = root.join(rel); match Command::new("bash").arg(&script).output() { - Ok(out) if out.status.success() => { /* still bei Erfolg */ } + Ok(out) if out.status.success() => { /* still bei Erfolg — Detail via Hand-Aufruf */ } Ok(out) => { let stdout = String::from_utf8_lossy(&out.stdout); let stderr = String::from_utf8_lossy(&out.stderr); for line in stdout.lines().chain(stderr.lines()) { println!("cargo:warning={line}"); } - panic!( - "Dialog-Build fehlgeschlagen — siehe Warnungen oben. \ - Einzeln debuggen: ./tools/build-dialogues.sh" - ); + panic!("{label}-Build fehlgeschlagen — siehe Warnungen oben. Einzeln debuggen: ./{rel}"); } Err(e) => { - // bash/Skript nicht startbar (z.B. fremdes OS): den Build nicht - // blockieren, nur warnen — die committeten .ink.json reichen. - println!( - "cargo:warning=Dialog-Build übersprungen ({e}). \ - Bei Bedarf von Hand: ./tools/build-dialogues.sh" - ); + println!("cargo:warning={label}-Build übersprungen ({e}). Bei Bedarf von Hand: ./{rel}"); } } } diff --git a/tools/README.md b/tools/README.md index 837223b..3aa5d14 100644 --- a/tools/README.md +++ b/tools/README.md @@ -27,3 +27,29 @@ Das Skript sucht den Ink-Compiler in dieser Reihenfolge: 1. `$INKLECATE` (Pfad zum Binary) 2. `tools/inklecate` (mitgeliefert) 3. `inklecate` im `PATH` + +## build-models.sh + +Exportiert die Blender-Welten zu den `.glb`, die das Spiel lädt. + +```sh +./tools/build-models.sh # nur Geändertes neu bauen +./tools/build-models.sh -f # alles neu bauen +``` + +Workflow für die Autorin: `.blend` in den jeweiligen Map-Ordner legen — +`assets/maps//.blend` — Skript laufen lassen, fertig. Die +`.glb` landen direkt daneben (`assets/maps//.glb`) und werden +mit ins Repo eingecheckt, damit ein frischer Clone ohne Blender spielbar +ist. Ein `.glb` ohne `.blend` daneben (z.B. `props/testscene.glb`) bleibt +unangetastet. + +**Automatisch beim Build:** `cargo build`/`cargo run` ruft dieses Skript +über `build.rs` selbst auf, sobald sich eine `.blend` geändert hat. + +Blender wird über `$BLENDER` oder den `PATH` gefunden. Fehlt es, wird der +Schritt übersprungen (kein Fehler — die committeten `.glb` reichen). +Bundeln geht nicht, dafür ist Blender zu groß. + +Tipp: in Blender „File → External Data → Pack Resources", dann liegen die +Texturen in der `.blend` und der Export ist self-contained. diff --git a/tools/build-models.sh b/tools/build-models.sh new file mode 100755 index 0000000..50e851f --- /dev/null +++ b/tools/build-models.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# +# Exportiert alle Blender-Welten unter assets/maps/ zu den .glb, die das +# Spiel lädt. +# +# Für die Autorin: Leg deine .blend in den jeweiligen Map-Ordner +# assets/maps//.blend +# und führ danach dieses Skript aus: +# ./tools/build-models.sh +# Fertig — die .glb landen direkt daneben (assets/maps//.glb). +# +# Standardmäßig wird nur exportiert, was sich seit dem letzten Mal geändert +# hat. Mit -f / --force wird alles neu gebaut. +# +# Tipp: in Blender „File -> External Data -> Pack Resources", dann sind die +# Texturen in der .blend und der Export ist self-contained. + +set -euo pipefail + +# Repo-Wurzel relativ zum Skript bestimmen — egal, von wo aufgerufen. +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +root="$(cd "$script_dir/.." && pwd)" +maps_dir="$root/assets/maps" + +force=0 +case "${1:-}" in + -f|--force) force=1 ;; + "") ;; + *) echo "Unbekannte Option: $1 (nur -f/--force)" >&2; exit 2 ;; +esac + +# blender finden: $BLENDER -> PATH. Bundeln geht nicht (zu groß) — fehlt es, +# ist das kein Fehler: die committeten .glb reichen zum Bauen und Spielen. +if [ -n "${BLENDER:-}" ] && [ -x "$BLENDER" ]; then + blender="$BLENDER" +elif command -v blender >/dev/null 2>&1; then + blender="$(command -v blender)" +else + echo " übersprungen blender nicht gefunden (\$BLENDER oder PATH) — .glb bleiben wie committet" >&2 + exit 0 +fi + +if [ ! -d "$maps_dir" ]; then + echo "Kein Map-Ordner: $maps_dir" >&2 + exit 1 +fi + +# Der Export als Python-Einzeiler. filepath ohne Endung — export_format='GLB' +# hängt .glb an. export_extras trägt collide/signal/role mit. +gltf_expr=' +import bpy, sys +out = sys.argv[-1] +bpy.ops.export_scene.gltf( + filepath=out, + export_format="GLB", + export_extras=True, + export_yup=True, + export_apply=True, + export_image_format="AUTO", +)' + +built=0 skipped=0 failed=0 +while IFS= read -r -d '' src; do + out="${src%.blend}.glb" + rel="${src#"$root"/}" + + # Inkrementell: überspringen, wenn .glb existiert und neuer ist. + if [ "$force" -eq 0 ] && [ -f "$out" ] && [ "$out" -nt "$src" ]; then + printf ' aktuell %s\n' "$rel" + skipped=$((skipped + 1)) + continue + fi + + if log="$("$blender" -b --factory-startup "$src" \ + --python-expr "$gltf_expr" -- "${out%.glb}" 2>&1)"; then + printf ' gebaut %s\n' "$rel" + built=$((built + 1)) + else + printf ' FEHLER %s\n' "$rel" + printf '%s\n' "$log" | sed 's/^/ /' + failed=$((failed + 1)) + fi +done < <(find "$maps_dir" -type f -name '*.blend' -print0 | sort -z) + +echo "—— $built gebaut, $skipped aktuell, $failed fehlgeschlagen ——" +[ "$failed" -eq 0 ]