Mushrooms: three varieties for the prep bench, plus gen-assets machine fixes

- button_mushroom / portobello / king_oyster in the ingredient cast:
  slippery skins (0.55-0.8, the tomato treatment), soft flesh, portobello
  gills layered and weeping; new 'mushroom' cap-and-stem silhouette.
- Verified via harness: dinner knife pressed on a button skates (1 slip,
  cut lands 0.157 off-aim; tomato baseline 2 slips / 0.218), The Best
  Thing bites clean through under the same press, sawing properly cuts
  center with zero slips.
- gen-assets.sh: find MODELBEAST at ~/MODELBEAST, token from
  backnforth/.env, MB_HOST to the m3ultra queue; every mb stage retries
  10x with 60s pause because m4pro's venvs are gone (ponytail-marked,
  remove when m4pro is healed).
- launch.json: port 5273 (5173 taken by another session's server).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-17 18:20:10 +10:00
parent faf196657e
commit ee1eb8ebc3
12 changed files with 98 additions and 22 deletions

View File

@ -4,8 +4,8 @@
{
"name": "toastsim",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"port": 5173
"runtimeArgs": ["run", "dev", "--", "--port", "5273"],
"port": 5273
}
]
}

5
package-lock.json generated
View File

@ -1,13 +1,12 @@
{
"name": "toastsim",
"version": "1.0.0",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "toastsim",
"version": "1.0.0",
"license": "ISC",
"version": "0.1.0",
"dependencies": {
"@dimforge/rapier3d-compat": "^0.19.3",
"three": "^0.185.1"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -9,6 +9,7 @@
set -uo pipefail
MB_DIR="$HOME/Documents/MODELBEAST"
[ -d "$MB_DIR" ] || MB_DIR="$HOME/MODELBEAST"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUT_MODELS="$REPO/public/assets/models"
OUT_IMG="$REPO/public/assets/img"
@ -16,17 +17,39 @@ mkdir -p "$OUT_MODELS" "$OUT_IMG"
cd "$MB_DIR" || exit 1
# shellcheck disable=SC1091
source data/agent.env
if [ -f data/agent.env ]; then source data/agent.env
else set -a; source "$HOME/Documents/backnforth/.env"; set +a; fi
export MB_HOST="${MB_HOST:-http://100.89.131.57:8777}"
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"
# ponytail: m4pro node has dead venvs (2026-07-17) — jobs routed there fail in ~1s,
# so every stage retries up to 10x with a 60s pause until a healthy node takes it.
# Remove the loops once m4pro is fixed or pulled from the pool.
MB_TRIES=10 MB_PAUSE=60
# 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
local seed="$1" prompt="$2" w="${3:-1024}" h="${4:-1024}" i out
for i in $(seq 1 "$MB_TRIES"); do
out=$(./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)
[ -n "$out" ] && { echo "$out"; return; }
sleep "$MB_PAUSE"
done
}
# run_cutout <asset_id> -> asset id on stdout
run_cutout() {
local asset="$1" i out
for i in $(seq 1 "$MB_TRIES"); do
out=$(./mb run bg_remove_local --asset "$asset" -p resolution=2048 --wait 2>/dev/null \
| awk '/image/{print $1}' | tail -1)
[ -n "$out" ] && { echo "$out"; return; }
sleep "$MB_PAUSE"
done
}
# gen_3d <name> <seed> <prompt> — each stage retries once (transient rsync/infra errors happen)
@ -42,24 +65,21 @@ gen_3d() {
fi
[ -z "$img" ] && { echo " ! $name: flux failed twice"; 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)
if [ -z "$cut" ]; then
echo " ~ $name: bg_remove failed, retrying once..."
cut=$(./mb run bg_remove_local --asset "$img" -p resolution=2048 --wait 2>/dev/null | awk '/image/{print $1}' | tail -1)
fi
[ -z "$cut" ] && { echo " ! $name: bg_remove failed twice"; return 1; }
cut=$(run_cutout "$img")
[ -z "$cut" ] && { echo " ! $name: bg_remove failed after $MB_TRIES tries"; return 1; }
echo "→ [$name] mesh ($cut) — ~5 min..."
local try glb
for try in 1 2; do
for try in $(seq 1 "$MB_TRIES"); do
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
glb=$(find "$tmp" -iname '*.glb' | head -1)
if [ -n "$glb" ]; then mv "$glb" "$OUT_MODELS/$name.glb"; echo "$name.glb"; rm -rf "$tmp"; return 0; fi
rm -rf "$tmp"
[ "$try" = 1 ] && echo " ~ $name: no glb, retrying mesh once..."
echo " ~ $name: no glb (try $try/$MB_TRIES), pausing ${MB_PAUSE}s..."
sleep "$MB_PAUSE"
done
echo " ! $name: no glb after retry"
echo " ! $name: no glb after $MB_TRIES tries"
return 1
}
@ -72,7 +92,7 @@ gen_2d() {
[ -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)
local c; c=$(run_cutout "$img")
[ -n "$c" ] && img="$c"
fi
local tmp; tmp=$(mktemp -d)

View File

@ -169,6 +169,25 @@ export class PrepView implements View {
mesh.scale.set(1, 0.85, 1);
break;
}
case 'mushroom': {
// Cap on a stem; group extent stays ~size so the shared y-offset works.
mesh = new THREE.Mesh(new THREE.SphereGeometry(r, 24, 16), skin);
mesh.scale.set(1, 0.6, 1);
mesh.position.y = ing.size * 0.15;
const stemMat = new THREE.MeshStandardMaterial({
color: new THREE.Color().setRGB(...ing.colors.flesh, THREE.SRGBColorSpace),
roughness: 0.7,
});
const stem = new THREE.Mesh(
new THREE.CylinderGeometry(r * 0.35, r * 0.42, ing.size * 0.6, 16),
stemMat,
);
stem.position.y = -ing.size * 0.15;
stem.castShadow = true;
stem.receiveShadow = true;
g.add(stem);
break;
}
default:
mesh = new THREE.Mesh(new THREE.SphereGeometry(r, 28, 20), skin);
}

View File

@ -7,7 +7,7 @@
* 1 world unit 11cm, same convention as the loaf.
*/
export type IngredientShape = 'sphere' | 'ovoid' | 'block' | 'wedge' | 'bulb';
export type IngredientShape = 'sphere' | 'ovoid' | 'block' | 'wedge' | 'bulb' | 'mushroom';
export interface Ingredient {
id: string;
@ -39,7 +39,10 @@ export type IngredientId =
| 'parmesan'
| 'brie'
| 'cucumber'
| 'avocado';
| 'avocado'
| 'button_mushroom'
| 'portobello'
| 'king_oyster';
export const INGREDIENTS: Record<IngredientId, Ingredient> = {
tomato: {
@ -136,4 +139,39 @@ export const INGREDIENTS: Record<IngredientId, Ingredient> = {
seeds: { count: 1, pocket: 'core' },
colors: { skin: [0.16, 0.2, 0.1], flesh: [0.78, 0.85, 0.5] },
},
// The mushrooms: dry-slick skin that skates under a pressed blade, soft
// flesh once the edge bites. Three varieties, three different fights.
button_mushroom: {
id: 'button_mushroom',
name: 'button mushroom',
shape: 'mushroom',
size: 0.3,
// Tiny and rolls away from the blade — the slip is the whole challenge.
skin: { resistance: 0.15, slippery: 0.75 },
flesh: { resistance: 0.15, moisture: 0.35 },
roastable: true,
colors: { skin: [0.92, 0.88, 0.8], flesh: [0.97, 0.95, 0.9] },
},
portobello: {
id: 'portobello',
name: 'portobello',
shape: 'mushroom',
size: 0.85,
skin: { resistance: 0.2, slippery: 0.55 },
// Gills crumble when mistreated, and it weeps dark juice.
flesh: { resistance: 0.2, moisture: 0.45, layered: true },
roastable: true,
colors: { skin: [0.45, 0.32, 0.22], flesh: [0.82, 0.76, 0.66] },
},
king_oyster: {
id: 'king_oyster',
name: 'king oyster',
shape: 'ovoid',
size: 1.0,
// Mostly one dense smooth stem — cuts like a slippery little log.
skin: { resistance: 0.2, slippery: 0.8 },
flesh: { resistance: 0.35, moisture: 0.3 },
roastable: true,
colors: { skin: [0.9, 0.87, 0.78], flesh: [0.96, 0.94, 0.88] },
},
};