From ac7c57e288bac12806d5e10b60773f6c792c1aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Rom=C3=A1n=20N=C3=BA=C3=B1ez?= Date: Thu, 24 Sep 2026 13:14:02 +0200 Subject: [PATCH 1/3] Pin Blender for CI asset processing --- .gitea/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++-- scripts/install-blender.sh | 30 ++++++++++++++++++++++++++++++ src/blend_asset.rs | 26 ++++++++++++++++++++++---- src/blend_asset/export.py | 9 +++++++++ 4 files changed, 91 insertions(+), 6 deletions(-) create mode 100755 scripts/install-blender.sh diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index caa34e7..d3da77e 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -63,7 +63,6 @@ jobs: libxkbcommon-dev \ libxkbcommon-x11-0 \ mesa-vulkan-drivers \ - blender \ mold \ pkg-config @@ -136,16 +135,45 @@ jobs: llvm-19 \ mesa-vulkan-drivers \ mold \ - blender \ pkg-config sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100 sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100 echo "/usr/lib/llvm-19/bin" >> "$GITHUB_PATH" rm -f "$HOME/.cache/cargo-xwin/clang-cl" + - name: Install pinned Blender + run: | + scripts/install-blender.sh "$RUNNER_TEMP/blender" + echo "BLENDER=$RUNNER_TEMP/blender/blender" >> "$GITHUB_ENV" + - name: Preprocess assets run: cargo run --locked --bin process-assets --no-default-features --features debug_features -j 4 + - name: Verify processed battlefield asset + run: | + python3 - <<'PY' + import json + import struct + + path = "imported_assets/Default/stages/battlefield/bf2.blend" + with open(path, "rb") as asset: + header = asset.read(12) + if len(header) != 12: + raise SystemExit(f"processed asset is truncated: {path}") + magic, version, length = struct.unpack("<4sII", header) + if magic != b"glTF" or version != 2 or length != asset.seek(0, 2): + raise SystemExit(f"processed asset is not a valid GLB: {path}") + asset.seek(12) + json_length, chunk_type = struct.unpack("&2 + exit 1 +fi + +if [[ -x "$install_dir/blender" ]]; then + "$install_dir/blender" --version | head -n 1 + exit 0 +fi + +mkdir -p "$(dirname "$install_dir")" +tmp_dir="$(mktemp -d)" +trap 'rm -rf "$tmp_dir"' EXIT + +archive="blender-${version}-linux-x64.tar.xz" +url="https://download.blender.org/release/Blender5.2/${archive}" + +curl --fail --location --silent --show-error --output "$tmp_dir/$archive" "$url" +printf '%s %s\n' "$sha256" "$tmp_dir/$archive" | sha256sum --check --status +tar --extract --xz --file "$tmp_dir/$archive" --directory "$tmp_dir" +mv "$tmp_dir/blender-${version}-linux-x64" "$install_dir" + +"$install_dir/blender" --version | head -n 1 diff --git a/src/blend_asset.rs b/src/blend_asset.rs index 5d45aa1..a6b3913 100644 --- a/src/blend_asset.rs +++ b/src/blend_asset.rs @@ -84,10 +84,11 @@ impl AssetLoader for BlendSourceLoader { .arg(&blend_path) .arg("--python") .arg(&script) - .arg("--") - .arg(&output) - .arg(&actions_path) - .output() + .arg("--") + .arg(&output) + .arg(&actions_path) + .arg(&blend_path) + .output() .map_err(|error| io::Error::other(format!("failed to start Blender: {error}")))?; if !result.status.success() { return Err(io::Error::other(format!( @@ -106,6 +107,7 @@ impl AssetLoader for BlendSourceLoader { let mut dependencies = manifest.dependencies; let document: serde_json::Value = serde_json::from_slice(&fs::read(&output)?) .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; + validate_export(&document, &output)?; if let Some(images) = document.get("images").and_then(serde_json::Value::as_array) { for image in images { if let Some(uri) = image.get("uri").and_then(serde_json::Value::as_str) { @@ -142,6 +144,22 @@ impl AssetLoader for BlendSourceLoader { } } +fn validate_export(document: &serde_json::Value, output: &Path) -> io::Result<()> { + for key in ["scenes", "nodes", "meshes"] { + let count = document + .get(key) + .and_then(serde_json::Value::as_array) + .map_or(0, Vec::len); + if count == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("Blender export {} contains no {key}", output.display()), + )); + } + } + Ok(()) +} + async fn referenced_actions( load_context: &mut LoadContext<'_>, root: &Path, diff --git a/src/blend_asset/export.py b/src/blend_asset/export.py index 61efb59..2c5e3ff 100644 --- a/src/blend_asset/export.py +++ b/src/blend_asset/export.py @@ -7,6 +7,15 @@ import sys def main(): output = sys.argv[sys.argv.index("--") + 1] actions_path = sys.argv[sys.argv.index("--") + 2] + source_path = sys.argv[sys.argv.index("--") + 3] + loaded_path = os.path.realpath(bpy.data.filepath) + expected_path = os.path.realpath(source_path) + if loaded_path != expected_path: + raise RuntimeError( + f"Blender loaded {loaded_path!r} instead of {expected_path!r}" + ) + if not bpy.data.objects or not bpy.data.meshes: + raise RuntimeError("Blender loaded an empty scene") with open(actions_path) as file: actions_to_keep = set(json.load(file)) -- 2.54.0 From f05926e1befafde3e192cd8fc739dff5da1c76b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Rom=C3=A1n=20N=C3=BA=C3=B1ez?= Date: Thu, 24 Sep 2026 16:09:01 +0200 Subject: [PATCH 2/3] Install Blender XFixes runtime dependency --- .gitea/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index d3da77e..2f963a3 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -129,6 +129,7 @@ jobs: libusb-1.0-0-dev \ libwayland-dev \ libx11-dev \ + libxfixes3 \ libxkbcommon-dev \ libxkbcommon-x11-0 \ lld-19 \ -- 2.54.0 From 69186aaee5cb0adacc0fa22fe3878d31b9f9151a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Rom=C3=A1n=20N=C3=BA=C3=B1ez?= Date: Thu, 24 Sep 2026 16:14:35 +0200 Subject: [PATCH 3/3] Add libxi --- .gitea/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 2f963a3..0d391d5 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -130,8 +130,10 @@ jobs: libwayland-dev \ libx11-dev \ libxfixes3 \ + libxi6 \ libxkbcommon-dev \ libxkbcommon-x11-0 \ + blender \ lld-19 \ llvm-19 \ mesa-vulkan-drivers \ -- 2.54.0