The bread slice is the whole game's canvas, so it's built first and properly: - Field: a 128x128 scalar field with bilinear sample, a mass-conserving brush, self-levelling relax, and the masked statistics the judge will read. - Slice: extruded loaf silhouette (domed top), planar-projected UVs so the sim grid lines up with the bread, per-texel silhouette mask, and four fields (browning/dryness/spread/damage) packed into one RGBA8 texture per frame. - Custom shader: browning ramp with blotchy char, crumb/crust/inclusions, spread wetting + opacity + thickness relief, gloss, gouge exposure, and flat data views for the judge screen. - bread.ts / spreads.ts: the archetypes and the rheology the difficulty curve will live in. - scripts/gen-assets.sh: reproducible fixed-seed asset generation through MODELBEAST (flux_local -> bg_remove_local -> hunyuan3d_mlx). All on-device. Legibility work that mattered: butter rendered invisible at first. Fixes were a gamma-style wetting darkening (a multiply *brightens* pale crumb), a normal perturbation taken in UV units rather than raw texel deltas (~30x too small), and pulling the lighting back under 1.0 so ACES stops desaturating yellow to cream. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100 lines
5.3 KiB
Bash
Executable File
100 lines
5.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# TOASTSIM asset generation — 100% local, 100% free, via MODELBEAST on the M3 Ultra.
|
|
#
|
|
# ./scripts/gen-assets.sh 3d # product shot -> cutout -> GLB (slow: ~5 min each, gpu lane is serial)
|
|
# ./scripts/gen-assets.sh 2d # flux images (judge portraits, backdrops, ui)
|
|
# ./scripts/gen-assets.sh all
|
|
#
|
|
# Seeds are fixed so every asset is re-generatable. Style token is shared for coherence.
|
|
set -uo pipefail
|
|
|
|
MB_DIR="$HOME/Documents/MODELBEAST"
|
|
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
OUT_MODELS="$REPO/public/assets/models"
|
|
OUT_IMG="$REPO/public/assets/img"
|
|
mkdir -p "$OUT_MODELS" "$OUT_IMG"
|
|
|
|
cd "$MB_DIR" || exit 1
|
|
# shellcheck disable=SC1091
|
|
source data/agent.env
|
|
|
|
STYLE="chunky stylized claymation kitchen prop, soft matte plastic look, warm colors"
|
|
SHOT="single object centered on plain light grey background, soft studio lighting, 3/4 view, chunky stylized game asset"
|
|
|
|
# run_flux <seed> <prompt> [w] [h] -> asset id on stdout
|
|
run_flux() {
|
|
local seed="$1" prompt="$2" w="${3:-1024}" h="${4:-1024}"
|
|
./mb run flux_local -p prompt="$prompt" -p model=flux2-klein-4b -p steps=4 \
|
|
-p seed="$seed" -p width="$w" -p height="$h" --wait 2>/dev/null \
|
|
| awk '/image/{print $1}' | tail -1
|
|
}
|
|
|
|
# gen_3d <name> <seed> <prompt>
|
|
gen_3d() {
|
|
local name="$1" seed="$2" prompt="$3"
|
|
if [ -f "$OUT_MODELS/$name.glb" ]; then echo " = $name.glb exists, skipping"; return; fi
|
|
echo "→ [$name] flux..."
|
|
local img cut
|
|
img=$(run_flux "$seed" "$prompt, $STYLE, $SHOT")
|
|
[ -z "$img" ] && { echo " ! $name: flux failed"; return 1; }
|
|
echo "→ [$name] cutout ($img)..."
|
|
cut=$(./mb run bg_remove_local --asset "$img" -p resolution=2048 --wait 2>/dev/null | awk '/image/{print $1}' | tail -1)
|
|
[ -z "$cut" ] && { echo " ! $name: bg_remove failed"; return 1; }
|
|
echo "→ [$name] mesh ($cut) — ~5 min..."
|
|
local tmp; tmp=$(mktemp -d)
|
|
./mb run hunyuan3d_mlx --asset "$cut" -p octree_resolution=256 -p texture_size=1024 \
|
|
-p max_num_view=6 --wait --download "$tmp" >/dev/null 2>&1
|
|
local glb; glb=$(find "$tmp" -iname '*.glb' | head -1)
|
|
if [ -n "$glb" ]; then mv "$glb" "$OUT_MODELS/$name.glb"; echo " ✓ $name.glb"; else echo " ! $name: no glb"; fi
|
|
rm -rf "$tmp"
|
|
}
|
|
|
|
# gen_2d <name> <seed> <prompt> [w] [h] [cutout]
|
|
gen_2d() {
|
|
local name="$1" seed="$2" prompt="$3" w="${4:-1024}" h="${5:-1024}" cutout="${6:-no}"
|
|
if [ -f "$OUT_IMG/$name.png" ]; then echo " = $name.png exists, skipping"; return; fi
|
|
echo "→ [$name] flux..."
|
|
local img; img=$(run_flux "$seed" "$prompt" "$w" "$h")
|
|
[ -z "$img" ] && { echo " ! $name: flux failed"; return 1; }
|
|
if [ "$cutout" = "cut" ]; then
|
|
echo "→ [$name] cutout..."
|
|
local c; c=$(./mb run bg_remove_local --asset "$img" -p resolution=2048 --wait 2>/dev/null | awk '/image/{print $1}' | tail -1)
|
|
[ -n "$c" ] && img="$c"
|
|
fi
|
|
local tmp; tmp=$(mktemp -d)
|
|
./mb get "$img" -o "$tmp" >/dev/null 2>&1
|
|
local f; f=$(find "$tmp" -iname '*.png' -o -iname '*.jpg' | head -1)
|
|
[ -n "$f" ] && { mv "$f" "$OUT_IMG/$name.png"; echo " ✓ $name.png"; }
|
|
rm -rf "$tmp"
|
|
}
|
|
|
|
# ---- the judge: same person every time, seed fixed, only the expression phrase varies ----
|
|
JUDGE="portrait of a stern elderly australian food inspector, deep wrinkles, bushy grey eyebrows, thin wire glasses, grey moustache, wearing a brown cardigan over a shirt and tie, holding a clipboard, plain light grey background, head and shoulders, claymation stop-motion character, soft matte clay texture, warm studio light"
|
|
|
|
do_3d() {
|
|
gen_3d toaster 11 "a retro two-slot pop-up toaster, cream and chrome, with a browning dial"
|
|
gen_3d butter_dish 23 "a ceramic butter dish with a thick block of yellow butter on it, lid off"
|
|
gen_3d pb_jar 31 "an open glass jar of peanut butter, chunky brown spread visible, cream label"
|
|
gen_3d mitey_jar 37 "a small squat glass jar of dark brown yeast extract spread with a bright red and yellow label reading MITEY, lid off"
|
|
gen_3d plate 43 "a simple round white ceramic side plate, empty, seen at an angle"
|
|
}
|
|
|
|
do_2d() {
|
|
gen_2d judge_neutral 101 "$JUDGE, neutral unimpressed expression" 768 768 cut
|
|
gen_2d judge_intrigued 101 "$JUDGE, one eyebrow raised, mildly intrigued expression" 768 768 cut
|
|
gen_2d judge_impressed 101 "$JUDGE, small approving smile, impressed expression" 768 768 cut
|
|
gen_2d judge_disappointed 101 "$JUDGE, deeply disappointed frowning expression, looking down at clipboard" 768 768 cut
|
|
gen_2d judge_horrified 101 "$JUDGE, horrified appalled expression, eyes wide, recoiling" 768 768 cut
|
|
gen_2d bench_wood 202 "seamless top-down texture of a warm honey-toned wooden kitchen benchtop, subtle grain and knife marks, claymation soft matte look, flat even lighting, no objects" 1024 1024
|
|
gen_2d kitchen_backdrop 205 "blurry cosy 1970s australian kitchen interior background, warm cream tiles, soft bokeh, claymation set, no people, no text" 1280 768
|
|
gen_2d ticket_paper 210 "seamless texture of a plain cream paper order docket, faint blue lines, slightly crumpled, flat even lighting, no text" 512 512
|
|
gen_2d title_art 220 "a single slice of golden toast with a pat of butter melting on it, dramatic hero shot, claymation stop-motion, soft matte clay, warm rim light, plain dark background" 1024 768 cut
|
|
}
|
|
|
|
case "${1:-all}" in
|
|
3d) do_3d ;;
|
|
2d) do_2d ;;
|
|
*) do_3d; do_2d ;;
|
|
esac
|
|
echo "done."
|