feat(qa): reference-vs-render iteration loop; screenshots are demo-only

Tools/visual_loop.py:
- --verify gates every capture against its expectation in Tools/visual_targets.json
  (missing, blank, tone-less or wrong-size shots fail), so 'each screenshot shows
  what was expected' is checked rather than assumed.
- --compare measures luminance, RMS contrast, edge density, palette distance and a
  3x3 luma grid against the reference photograph, with a 0-1 similarity score.
- --iteration N writes Art/reference/iterations/N/{metrics.json,feedback.md}; the
  feedback names the skill to consult (blender-modeler, archviz, materials, lookdev,
  camera-cinematography), so the next modelling pass starts from a measurement.
- Iterations are gitignored until the owner confirms a shot; only confirmed shots
  are promoted into Screenshots/.

Screenshots/ holds demo-map captures only now: render_screenshots.sh renders just
DemoMap, collects the shots and runs the verification gate (--verify); the review
staging map is no longer rendered for review and its PNGs are removed. The README
is rewritten around the four demo shots with per-shot expected content and a
verified column, and no longer claims the review staging set.

Tests: 67 pass (13 new) covering metrics on synthetic images, comparison scoring,
the feedback rules and the target spec (including that every target is a demo shot
and that iterations are gitignored).
This commit is contained in:
zwitschi committed 2026-11-08 19:52:25 +01:00
1 parent ad5bb25009
commit bd1f8a9c2e
25 files changed
+688 -121

No files matched your search

+32 -34
View File
@@ -1,56 +1,54 @@
#!/usr/bin/env bash
# Render in-game screenshots for visual review into <repo>/Screenshots/ (gitignored).
# Render the demo-map screenshots used for visual review into <repo>/Screenshots/demo/.
#
# Usage: Tools/render_screenshots.sh
# Rebuilds /Game/Test/ReviewMap and /Game/Hollywood/DemoMap (shot lists), runs
# the game on each map (the GigLineShotActor walks the cameras, captures, quits),
# and copies the PNGs into Screenshots/. PNGs are never committed.
# Usage: Tools/render_screenshots.sh [--verify]
#
# Only the **demo map** is captured: the review staging map exists to hold the
# hero/camera list, not to produce review screenshots. Everything here is what a
# player actually sees when the demo runs.
#
# --verify run Tools/visual_loop.py --verify afterwards and fail if a shot
# does not show the content its expectation describes
#
# The PNGs are committed (see Screenshots/README.md): a reviewer must see exactly
# what the build rendered.
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENGINE="${UE_ENGINE_DIR:-/run/media/system/DATA/Engines/UE_5.8.4}"
PROJ="$REPO/Game/GigLine/GigLine.uproject"
VERIFY="no"
[ "${1:-}" = "--verify" ] && VERIFY="yes"
echo "== 1/4 build review + demo shot maps =="
echo "== 1/3 stage the demo shot cameras =="
cd "$ENGINE"
./Engine/Binaries/Linux/UnrealEditor-Cmd "$PROJ" \
-run=pythonscript -script="$REPO/Tools/ue_build_review_map.py" \
-nullrhi -unattended -nopause -stdout
cat /tmp/ue-review-map-result.txt | tail -3
tail -3 /tmp/ue-review-map-result.txt 2>/dev/null || true
run_capture() {
local map="$1" label="$2"
echo "== capture: $label =="
./Engine/Binaries/Linux/UnrealEditor-Cmd "$PROJ" "$map" \
-game -windowed -ResX=1920 -ResY=1080 -nopause -nosplash -stdout
}
echo "== 2/3 capture the demo map =="
./Engine/Binaries/Linux/UnrealEditor-Cmd "$PROJ" /Game/Hollywood/DemoMap \
-game -windowed -ResX=1920 -ResY=1080 -nopause -nosplash -stdout
echo "== 2/4 run game on review map (hero/scene shots) =="
run_capture "/Game/Test/ReviewMap" "review"
echo "== 3/4 run game on demo map (walkable-demo shots) =="
run_capture "/Game/Hollywood/DemoMap" "demo"
echo "== 4/4 collect screenshots =="
echo "== 3/3 collect the demo screenshots =="
mkdir -p "$REPO/Screenshots/demo"
rm -f "$REPO"/Screenshots/review-*.png "$REPO"/Screenshots/demo/review-*.png
rm -f "$REPO"/Screenshots/demo/review-*.png
found=0
for src in \
"$REPO/Game/GigLine/Saved/Screenshots"/*/review-*.png \
"$REPO/Game/GigLine/Saved/Screenshots"/*/*/review-*.png \
"$HOME/.config/Epic/GigLine/Saved/Screenshots"/*/review-*.png; do
[ -e "$src" ] || continue
name=$(basename "$src")
if [[ "$name" == review-?("")-demo-* || "$name" == review-*-demo-* || "$name" == review-0?-demo-* ]] \
|| [[ "$name" == *demo-* ]]; then
cp -f "$src" "$REPO/Screenshots/demo/$name"
else
cp -f "$src" "$REPO/Screenshots/$name"
fi
found=1
case "$(basename "$src")" in
*demo-*) cp -f "$src" "$REPO/Screenshots/demo/"; found=1 ;;
esac
done
[ "$found" = "1" ] || { echo "ERROR: no review-*.png found"; exit 1; }
echo "--- Screenshots/ ---"
ls -1 "$REPO/Screenshots"
echo "--- Screenshots/demo/ ---"
ls -1 "$REPO/Screenshots/demo"
[ "$found" = "1" ] || { echo "ERROR: no demo screenshots found"; exit 1; }
ls -1 "$REPO/Screenshots/demo"
if [ "$VERIFY" = "yes" ]; then
echo "== verify: does each shot show what it should? =="
cd "$REPO"
python3 Tools/visual_loop.py --verify
fi