#!/bin/bash # Renders the Neon Coast car with Singe and makes demos/neonCoast/art/car.png from it: one frame per # steering angle, side by side, each with true alpha from a black and a white render of the same # view. Runs headless; nothing appears on screen. # # util/neonCoastCar.sh set -euo pipefail REPO=$(cd "$(dirname "$0")/.." && pwd) BIN=$(ls "$REPO"/.builddir/Singe-v*-Linux-x86_64 | head -1) SCRATCH=${SCRATCH:-/tmp/neonCoastCar} rm -rf "$SCRATCH" mkdir -p "$SCRATCH/run/car" cp "$REPO/util/neonCoastCar.singe" "$SCRATCH/run/car/" # With "traffic" it makes art/traffic.png instead: the car straight from behind in other paints. SHEET=car if [ "${1:-}" = "traffic" ]; then SHEET=traffic printf 'CAR_TRAFFIC = true\ndofile(singeGetScriptPath():match("(.*[/\\\\])") .. "neonCoastCar.singe")\n' > "$SCRATCH/run/car/traffic.singe" fi SCRIPT=car/neonCoastCar.singe if [ "$SHEET" = "traffic" ]; then SCRIPT=car/traffic.singe fi cd "$SCRATCH/run" SDL_VIDEODRIVER=offscreen SDL_AUDIO_DRIVER=dummy timeout -k 10 120 "$BIN" -k -s -w -C 1024x768 -x 1024 -y 768 --deterministic -d "$SCRATCH/data" "$SCRIPT" > "$SCRATCH/run.log" 2>&1 || true python3 - "$SCRATCH/data" "$REPO/demos/neonCoast/art/$SHEET.png" <<'PY' import glob, os, sys import numpy as np from PIL import Image data, out = sys.argv[1], sys.argv[2] shots = {os.path.basename(p)[:-4]: p for p in glob.glob(data + "/**/shot*.png", recursive=True)} takes = sorted({int(k[4:-1]) for k in shots}) frames = [] for a in takes: b = np.asarray(Image.open(shots["shot%02db" % a]).convert("RGB")).astype(np.float64) w = np.asarray(Image.open(shots["shot%02dw" % a]).convert("RGB")).astype(np.float64) # Over black a pixel is c * alpha; over white it is c * alpha + (1 - alpha) * white, white being # whatever the background actually came out as. white = (w[:4, :4] - b[:4, :4]).mean() alpha = np.clip(1 - (w - b).mean(axis=2) / white, 0, 1) colour = np.where(alpha[..., None] > 0.004, b / np.maximum(alpha[..., None], 0.004), 0) frames.append(np.dstack([np.clip(colour, 0, 255), alpha * 255])) # One box round every frame, so the car does not jump when the frame changes. mask = np.zeros(frames[0].shape[:2], bool) for f in frames: mask |= f[..., 3] > 8 ys, xs = np.where(mask) x0, x1, y0, y1 = xs.min() - 2, xs.max() + 3, ys.min() - 2, ys.max() + 3 width = 288 height = int((y1 - y0) * width / (x1 - x0)) sheet = Image.new("RGBA", (width * len(frames), height), (0, 0, 0, 0)) for i, f in enumerate(frames): im = Image.fromarray(f[y0:y1, x0:x1].astype(np.uint8), "RGBA").resize((width, height), Image.LANCZOS) sheet.paste(im, (i * width, 0)) sheet.save(out) print(os.path.basename(out), sheet.size, "frames", len(takes)) PY