35 payloads move to tooling/scripts/blender/ and stay outside package scope. They run under Blender's bundled Python, which cannot see the repo venv, so they physically cannot import tooling.core — holding them to the D-263 contract would either fail the gate forever or force the contract to be weakened for everyone, and the second is how a gate stops meaning anything. Count verified by import rather than filename: 33 import bpy/bmesh directly, and the two that do not are still payloads per their own usage lines. garment-fit/make_logo.py is the one genuine non-payload and stays for T-1290. The bash wrapper is retired rather than kept. Keeping it would have put the install-resolution logic in two places, which is the duplication T-1286 had just finished collapsing three copies of. domains/blender/service.py owns the decisions — resolve_blender (native beats flatpak, ordering preserved), resolve_payload, absolutise — and only run_payload performs. test_blender.py pins all of them without launching Blender, which matters here more than usual: the thing being launched is a 200 MB GUI application that writes GLBs. `reach blender run` takes a registered payload name OR a path to any script, because the wrapper served both — the spikes and the glb-gen skill hand it one-off scripts of their own. An unknown name enumerates all 35 and exits 2. The exclusion now defends itself. check_carve_out_stays_carved fails if `scripts` is added to PACKAGE_ROOTS, if the payload directory empties (an empty exclusion proves nothing), or if an __init__.py appears there (which would make the payloads importable — the coupling the carve-out exists to prevent). All three arms mutation-proved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
3.3 KiB
Bash
Executable File
99 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Wrapper for the Blender GLB post-processor.
|
|
#
|
|
# Usage:
|
|
# postprocess <input.glb> <output.glb> [options]
|
|
# postprocess .tmp/glb-gen/furniture/desks/scifi_desk.glb client-tmp/models/furniture/scifi_desk.glb
|
|
# postprocess .tmp/glb-gen/furniture/desks/scifi_desk.glb # auto-output to .tmp/glb-gen/postproc/...
|
|
#
|
|
# Options are passed through to the Blender script:
|
|
# --target-width FLOAT Target width in world units (default: 1.0)
|
|
# --target-height FLOAT Target height in world units (default: auto)
|
|
# --color-threshold FLOAT Dominant color detection threshold (default: 0.25)
|
|
# --material-name NAME Material slot name (default: mat_primary)
|
|
#
|
|
# Output:
|
|
# <output.glb> Post-processed model (texture preserved)
|
|
# <output_mask.png> Recolor mask sidecar (if texture found)
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
BLENDER_SCRIPT="$SCRIPT_DIR/postprocess_glb.py"
|
|
|
|
# Find project root via git
|
|
PROJECT_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)"
|
|
|
|
if [ $# -lt 1 ]; then
|
|
echo "Usage: postprocess <input.glb> [output.glb] [--target-width N] [--color-threshold N]"
|
|
echo ""
|
|
echo "If output is omitted, writes to .tmp/glb-gen/postproc/ mirroring the input path."
|
|
exit 1
|
|
fi
|
|
|
|
INPUT_REL="$1"
|
|
shift
|
|
|
|
# Resolve to absolute
|
|
if [[ "$INPUT_REL" = /* ]]; then
|
|
INPUT_ABS="$INPUT_REL"
|
|
else
|
|
INPUT_ABS="$(cd "$PROJECT_ROOT" && pwd)/$INPUT_REL"
|
|
fi
|
|
|
|
if [ ! -f "$INPUT_ABS" ]; then
|
|
echo "ERROR: Input file not found: $INPUT_ABS" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Determine output path
|
|
if [ $# -ge 1 ] && [[ "$1" != --* ]]; then
|
|
OUTPUT_REL="$1"
|
|
shift
|
|
if [[ "$OUTPUT_REL" = /* ]]; then
|
|
OUTPUT_ABS="$OUTPUT_REL"
|
|
else
|
|
OUTPUT_ABS="$(cd "$PROJECT_ROOT" && pwd)/$OUTPUT_REL"
|
|
fi
|
|
else
|
|
# Auto-output: mirror input path under .tmp/glb-gen/postproc/
|
|
# e.g. .tmp/glb-gen/furniture/desks/foo.glb → .tmp/glb-gen/postproc/furniture/desks/foo.glb
|
|
REL_TO_TMP="${INPUT_REL#.tmp/glb-gen/}"
|
|
OUTPUT_ABS="$(cd "$PROJECT_ROOT" && pwd)/.tmp/glb-gen/postproc/$REL_TO_TMP"
|
|
fi
|
|
|
|
mkdir -p "$(dirname "$OUTPUT_ABS")"
|
|
|
|
echo "Post-processing: $(basename "$INPUT_ABS")"
|
|
echo " Input: $INPUT_REL"
|
|
echo " Output: ${OUTPUT_ABS#$(cd "$PROJECT_ROOT" && pwd)/}"
|
|
|
|
LOG="${OUTPUT_ABS%.glb}_blender.log"
|
|
|
|
# Blender does not reliably set a non-zero exit code on an uncaught Python
|
|
# exception (--python-exit-code is not set), so failures must be detected
|
|
# from the log content and from whether the output file actually appeared —
|
|
# not from Blender's own exit status alone. Capture the full log first
|
|
# (temporarily disabling -e so a non-zero Blender exit doesn't abort before
|
|
# we can inspect it), then filter for the console summary.
|
|
set +e
|
|
reach blender run "$BLENDER_SCRIPT" \
|
|
-- "$INPUT_ABS" "$OUTPUT_ABS" "$@" > "$LOG" 2>&1
|
|
BLENDER_EXIT=$?
|
|
set -e
|
|
|
|
grep -iE "^ |^=|warning|error|traceback|dominant|mask" "$LOG" || true
|
|
|
|
if [ "$BLENDER_EXIT" -ne 0 ] || [ ! -f "$OUTPUT_ABS" ]; then
|
|
echo "ERROR: Blender post-process failed (exit $BLENDER_EXIT, output present: $([ -f "$OUTPUT_ABS" ] && echo yes || echo no))" >&2
|
|
echo " Full log: ${LOG#$(cd "$PROJECT_ROOT" && pwd)/}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
MASK="${OUTPUT_ABS%.glb}_mask.png"
|
|
if [ -f "$MASK" ]; then
|
|
echo " Mask: ${MASK#$(cd "$PROJECT_ROOT" && pwd)/}"
|
|
fi
|
|
|
|
echo "Done."
|