From de5dda5f1578b93152457d7d3786337f47641562 Mon Sep 17 00:00:00 2001 From: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Date: Fri, 25 Sep 2026 19:42:17 -0400 Subject: [PATCH] chore(gates): raise the asset-sheet reference bar and commit its renderer A gallery review found three of the four asset references were among the least-designed assets in the gallery: lod-decimate-chain's primitive rocket, custom-normals-shade's jerry can and vertex-weight-limit's cylinder-stack arm. The gate asks "is the candidate the least-designed object in the lineup?", so a weak lineup lets weak candidates through. The references are now the hydrant, the survey drone, the stone well, and the wheelbarrow and apothecary shelf from the showcase. The panel renderer lived only in uncommitted scratch scripts, so no sheet could be reproduced. scripts/asset_sheet.py (host) and asset_sheet_panel.py (Blender side) commit it, read the reference set from CLAUDE.md so the tool cannot disagree with the gate, and print each isolated asset's floors for the calibration table. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> --- CLAUDE.md | 2 +- docs/VISUAL-STYLE.md | 17 +- .../asset-sheets/lod-decimate-chain.webp | Bin 0 -> 50150 bytes scripts/asset_sheet.py | 159 +++++++++++++++++ scripts/asset_sheet_panel.py | 165 ++++++++++++++++++ 5 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 docs/gallery/asset-sheets/lod-decimate-chain.webp create mode 100644 scripts/asset_sheet.py create mode 100644 scripts/asset_sheet_panel.py diff --git a/CLAUDE.md b/CLAUDE.md index 3ddf6f4c..a458c0aa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -159,7 +159,7 @@ Stage with **explicit paths only** — never `git add -A` or `git add .`. Cursor - `docs/VISUAL-STYLE.md` is the **binding** render standard; deviations are defects. - **Framing gate:** Layer 1 framing is measured, not eyeballed — the example's `--output` render path calls the shared helper `examples/gallery_framing.py` (`check_framing`, exit 10 on violation) before writing the still: hero fill 0.70–0.90 of frame in at least one axis, every element that matters clearing all four edges by ≥ 0.02. The check-only path never invokes it, so smoke runtimes are unaffected. The helper measures; call sites enforce. Bleed compositions call `measure_framing_deviation` and assert their own cap at the call site — `check_framing` has no `deviation=` flag. - **Contact-sheet gate:** composite the candidate hero beside the pinned calibration set — currently `armature-bend`, `damped-track-aim`, `bmesh-gear` — commit the composite under `docs/gallery/contact-sheets/`, link it in the PR body, and report per-criterion verdicts (stage darkness, wedge warmth, subject fill, saturation, thumbnail legibility) including mean luminance versus the calibration images. A claim without the committed composite is not acceptable evidence. **This list is the canonical home of the pinned set** — update it here when a new example outclasses a member; `docs/new-example-prompt.md` points here rather than naming members. The longer "calibration references" list in `docs/VISUAL-STYLE.md` is a style reference, not this contact-sheet set. Tooling: `python scripts/render_hero.py --blender PATH --only NAME` renders the README `--output` command and writes the hero and preview webps (the one committed PNG-to-webp path); `python scripts/contact_sheet.py NAME` builds the composite in the committed format and prints each tile's luma, stage, wedge warmth and saturation; `python scripts/measure_hero_drift.py` checks committed heroes against what the code renders now. -- **Asset-sheet gate (asset-type examples — game props/kits):** composite the hero asset rendered alone (neutral three-quarter view, plain studio lighting, no staging tricks, no labels, no comparison props) beside the pinned asset-quality reference set — currently `collision-hull-proxy`, `custom-normals-shade`, `vertex-weight-limit`, `lod-decimate-chain` — rendered the same way; commit under `docs/gallery/asset-sheets/`, link it in the PR body, and report a verdict. The asset ships only if it is not identifiable as the least-designed object in that lineup — a strong scene can carry a weak model; this gate removes the scene. **This list is the canonical home of the reference set** — update it here when a new asset outclasses a member. The measurable floors behind the gate (naming, material variation, edge treatment) live in `examples/gallery_asset_quality.py` — render path only, same call pattern as `gallery_framing`, exit 11 on violation — with the calibration table and dropped-floor evidence in `docs/VISUAL-STYLE.md` § Asset quality. +- **Asset-sheet gate (asset-type examples — game props/kits):** composite the hero asset rendered alone (neutral three-quarter view, plain studio lighting, no staging tricks, no labels, no comparison props) beside the pinned asset-quality reference set — currently `collision-hull-proxy`, `socket-attach-points`, `vertex-color-ao`, `wheelbarrow`, `apothecary-shelf` — rendered the same way; commit under `docs/gallery/asset-sheets/`, link it in the PR body, and report a verdict. The asset ships only if it is not identifiable as the least-designed object in that lineup — a strong scene can carry a weak model; this gate removes the scene. **This list is the canonical home of the reference set** — update it here when a new asset outclasses a member. A reference may be an example or a showcase piece. Tooling: `python scripts/asset_sheet.py --blender PATH NAME` renders the candidate and this set (read from this line) through `scripts/asset_sheet_panel.py` and writes the composite. The measurable floors behind the gate (naming, material variation, edge treatment) live in `examples/gallery_asset_quality.py` — render path only, same call pattern as `gallery_framing`, exit 11 on violation — with the calibration table and dropped-floor evidence in `docs/VISUAL-STYLE.md` § Asset quality. - **Falsification:** every check must be proven to fail once — break the contract, observe the non-zero exit, restore — with the probe and the measured error reported in the PR body. An assertion that cannot fail witnesses nothing. - **After gallery regeneration** (`python scripts/build_gallery.py`), read the **generated HTML** character by character — the `` text and witnesses callouts in `docs/gallery/index.html` and `docs/gallery//index.html` — not just `examples/gallery.json`. Precedent: the `teaches.split(".")[0]` bug truncated 14/21 card alts at dotted API paths like `bmesh.ops` while the source JSON looked fine (fixed in PR #68). - **Playwright gallery captures:** gallery `` tags lazy-load, so force them first (`document.querySelectorAll('img').forEach(i => i.loading = 'eager')`, then wait). **Scroll the target card into view and take a viewport capture** — `scrollIntoView({block:'center', behavior:'instant'})`, short wait, `browser_take_screenshot` with `fullPage` omitted. A `fullPage` capture is NOT a workaround: on a tall gallery page it renders every card image blank even when the images are verified loaded (`complete === true`, `naturalWidth === 1280`, `opacity === 1`) — measured on the 45-card grid at 1425x4516. Verify load state via `browser_evaluate` rather than trusting the pixels. diff --git a/docs/VISUAL-STYLE.md b/docs/VISUAL-STYLE.md index 8bce0848..69e0df80 100644 --- a/docs/VISUAL-STYLE.md +++ b/docs/VISUAL-STYLE.md @@ -130,14 +130,23 @@ violation; never imported by a check-only path): (`bmesh-gear` measures 0.667); raw boxes (1.0) fail. Calibrated empirically against the gallery's own assets, exactly as the -0.02 margin floor was: +0.02 margin floor was. The four new reference rows were measured on the +isolated asset by `scripts/asset_sheet_panel.py` (the sheet renderer prints +the floors per panel); compactness was not re-measured for them. The three +former references passed every floor and were still demoted by eye in the +2026-09 gallery review — the floors cannot tell a primitive-built rocket +from a designed wheelbarrow, which is the Status section below in practice: | asset | parts | materials | edge90 | compactness (info only) | | --- | --- | --- | --- | --- | | collision-hull-proxy (reference) | 16 | 6 | 0.044 | 65.9 | -| custom-normals-shade (reference) | 39 | 2 | 0.288 | 31.0 | -| vertex-weight-limit (reference) | 1 | 4 | 0.150 | 34.8 | -| lod-decimate-chain (reference) | 3 | 4 | 0.019 | 79.1 | +| socket-attach-points (reference) | 21 | 32 | 0.027 | — | +| vertex-color-ao (reference) | 11 | 11 | 0.171 | — | +| wheelbarrow (reference, showcase) | 1 | 2 | 0.216 | — | +| apothecary-shelf (reference, showcase) | 1 | 6 | 0.055 | — | +| custom-normals-shade (former reference) | 39 | 2 | 0.288 | 31.0 | +| vertex-weight-limit (former reference) | 1 | 4 | 0.150 | 34.8 | +| lod-decimate-chain (former reference) | 3 | 4 | 0.019 | 79.1 | | depsgraph-export (weak) | 2 | 1 | 1.000 | 23.9 | | text-version-stamp (weak) | 1 | 1 | 1.000 | 405.3 | | bmesh-gear (simple, honest) | 1 | 1 | 0.667 | 18.3 | diff --git a/docs/gallery/asset-sheets/lod-decimate-chain.webp b/docs/gallery/asset-sheets/lod-decimate-chain.webp new file mode 100644 index 0000000000000000000000000000000000000000..419cd04731fabd314819d4b86d20a13413facfd2 GIT binary patch literal 50150 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 j_w!w6D}32o) list[str]: + """The pinned reference set, parsed from its canonical home in CLAUDE.md.""" + text = (REPO / "CLAUDE.md").read_text(encoding="utf-8") + m = _REF_LINE.search(text) + if not m: + raise SystemExit("could not find the asset-sheet reference set in CLAUDE.md") + return re.findall(r"`([a-z0-9-]+)`", m.group(1)) + + +def render_panel(blender: str, entry: dict, png: Path) -> bool: + inc, exc = SELECT[entry["name"]] + with tempfile.TemporaryDirectory() as tmp: + cmd = render_args(entry, Path(tmp) / "stage.png") + if cmd is None: + print(f"{entry['name']}: no --output command in README", flush=True) + return False + script = next((REPO / entry["dir"]).glob("*.py")) + env = dict(os.environ, BDT_SHEET_SCRIPT=str(script), BDT_SHEET_SELECT=inc, + BDT_SHEET_EXCLUDE=exc or "", BDT_SHEET_OUT=str(png)) + proc = subprocess.run( + [blender, "--background", "--factory-startup", "--python", + str(REPO / "scripts" / "asset_sheet_panel.py"), "--", *cmd], + capture_output=True, text=True, encoding="utf-8", errors="replace", + cwd=REPO, env=env, + ) + for line in proc.stdout.splitlines(): + if line.startswith("sheet:"): + print(f"{entry['name']}: {line}", flush=True) + if proc.returncode != 0 or not png.is_file(): + tail = (proc.stdout + proc.stderr).strip().splitlines()[-3:] + print(f"{entry['name']}: panel failed, exit {proc.returncode}: {' | '.join(tail)}", + flush=True) + return False + return True + + +def main(argv=None) -> int: + p = argparse.ArgumentParser(description=__doc__.split("\n")[0]) + p.add_argument("--blender", required=True, help="Blender binary to render with") + p.add_argument("name", help="candidate entry (examples/ or showcase/ gallery name)") + p.add_argument("--out", help="composite path (default docs/gallery/asset-sheets/NAME.webp)") + p.add_argument("--panels", help="directory to keep and reuse per-asset panel PNGs") + args = p.parse_args(argv) + + by_name = {e["name"]: e for e in entries()} + refs = reference_set() + lineup = [args.name] + [r for r in refs if r != args.name] + missing = [n for n in lineup if n not in by_name or n not in SELECT] + if missing: + print(f"no gallery entry or SELECT row for: {', '.join(missing)}", file=sys.stderr) + return 2 + + blender = str(Path(args.blender).resolve()) + version = subprocess.run([blender, "--version"], capture_output=True, text=True, + encoding="utf-8", errors="replace").stdout.splitlines()[0] + print(f"# renderer: {blender}\n# version: {version}\n# references: {', '.join(refs)}", + flush=True) + + keep = Path(args.panels) if args.panels else None + with tempfile.TemporaryDirectory() as tmp: + pdir = keep or Path(tmp) + pdir.mkdir(parents=True, exist_ok=True) + panels = [] + for n in lineup: + png = pdir / f"{n}.png" + # the candidate is always re-rendered; references may be reused + if not (keep and png.is_file() and n != args.name): + if not render_panel(blender, by_name[n], png): + return 3 + panels.append(Image.open(png).convert("RGB").resize(PANEL, Image.LANCZOS)) + + cols = 3 + rows = -(-len(panels) // cols) + sheet = Image.new("RGB", (PANEL[0] * cols + GUTTER * (cols - 1), + PANEL[1] * rows + GUTTER * (rows - 1)), (18, 18, 20)) + for i, im in enumerate(panels): + sheet.paste(im, ((i % cols) * (PANEL[0] + GUTTER), (i // cols) * (PANEL[1] + GUTTER))) + out = Path(args.out) if args.out else REPO / "docs/gallery/asset-sheets" / f"{args.name}.webp" + out.parent.mkdir(parents=True, exist_ok=True) + sheet.save(out, "WEBP", quality=QUALITY) + print(f"wrote {out.relative_to(REPO) if out.is_relative_to(REPO) else out} " + f"({out.stat().st_size} B): {' | '.join(lineup)}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/asset_sheet_panel.py b/scripts/asset_sheet_panel.py new file mode 100644 index 00000000..68886b58 --- /dev/null +++ b/scripts/asset_sheet_panel.py @@ -0,0 +1,165 @@ +"""Blender side of ``scripts/asset_sheet.py``: one neutral asset-sheet panel. + +Stages an example (or showcase piece) through its own documented ``--output`` +path, captures the scene that was rendered, keeps only the hero asset's mesh +objects, then re-stages them alone: grey sweep, plain three-light studio, +three-quarter view fitted to the asset's real extent, no labels, no comparison +props. Renders one 640x360 panel. Isolating the asset from its staging is the +point of the gate — a strong scene can carry a weak model. + +Not run directly. The host script passes everything through the environment +so the argv after ``--`` stays exactly the example's documented flags: + + BDT_SHEET_SCRIPT path to the example's .py + BDT_SHEET_SELECT regex matched against mesh object names (the asset) + BDT_SHEET_EXCLUDE optional regex of names to drop from that match + BDT_SHEET_OUT panel PNG to write +""" +import importlib.util +import math +import os +import re +import sys + +import bmesh +import bpy +from mathutils import Vector + +RES_X, RES_Y = 640, 360 + +script = os.environ["BDT_SHEET_SCRIPT"] +inc_re = os.environ["BDT_SHEET_SELECT"] +exc_re = os.environ.get("BDT_SHEET_EXCLUDE") or None +out_png = os.path.abspath(os.environ["BDT_SHEET_OUT"]) + +# examples import their shared helpers through a __file__-relative shim; the +# showcase ones do the same, so the module only needs to load from its path +spec = importlib.util.spec_from_file_location("sheet_mod", script) +mod = importlib.util.module_from_spec(spec) +spec.loader.exec_module(mod) + +captured = [] + + +@bpy.app.handlers.persistent +def _pre(scene, depsgraph=None): + captured.append(scene.name) + + +bpy.app.handlers.render_pre.append(_pre) +code = mod.main() +bpy.app.handlers.render_pre.remove(_pre) +if code not in (None, 0): + print(f"sheet: staging run exited {code}", file=sys.stderr) + sys.exit(3) + +sc = bpy.data.scenes[captured[-1]] if captured else bpy.context.scene +asset = [ob for ob in sc.objects + if ob.type == "MESH" and re.search(inc_re, ob.name) + and not (exc_re and re.search(exc_re, ob.name))] +print(f"sheet: asset objects {[ob.name for ob in asset]}") +if not asset: + print(f"sheet: no mesh object matches {inc_re!r}", file=sys.stderr) + sys.exit(4) + +# The measurable floors of the isolated asset, as information for the +# calibration table in docs/VISUAL-STYLE.md § Asset quality. The gate itself +# runs on each example's own render path; nothing is enforced here. +sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "examples")) +import gallery_asset_quality as aq # noqa: E402 + +parts, _ = aq.measure_parts(asset) +mats = aq.measure_materials(asset)[0] +edge90 = aq.measure_edge90(asset)[0] +print(f"sheet: floors parts={parts} materials={mats} edge90={edge90:.3f}") + +# Restage in place. Deleting the scene would wipe the datablocks the asset +# needs, so unparent the asset (world transform kept) and remove the rest. +for ob in asset: + mw = ob.matrix_world.copy() + ob.parent = None + ob.matrix_world = mw +keep = set(asset) +for ob in list(sc.objects): + if ob not in keep: + bpy.data.objects.remove(ob, do_unlink=True) +bpy.context.view_layer.update() + +mn, mx = Vector((1e9,) * 3), Vector((-1e9,) * 3) +for ob in asset: + for c in ob.bound_box: + w = ob.matrix_world @ Vector(c) + mn = Vector(map(min, mn, w)) + mx = Vector(map(max, mx, w)) +center = (mn + mx) / 2 +dim = max((mx - mn).length, 0.1) + +sweep = bpy.data.materials.new("Sweep") +sweep.use_nodes = True +bsdf = sweep.node_tree.nodes["Principled BSDF"] +bsdf.inputs["Base Color"].default_value = (0.09, 0.09, 0.095, 1.0) +bsdf.inputs["Roughness"].default_value = 0.8 +floor_me = bpy.data.meshes.new("SweepFloor") +bm = bmesh.new() +try: + bmesh.ops.create_grid(bm, x_segments=1, y_segments=1, size=30) + bmesh.ops.translate(bm, verts=bm.verts, vec=(0.0, 0.0, mn.z)) + bm.to_mesh(floor_me) +finally: + bm.free() +floor_me.materials.append(sweep) +sc.collection.objects.link(bpy.data.objects.new("SweepFloor", floor_me)) + +world = bpy.data.worlds.new("SheetWorld") +world.use_nodes = True +world.node_tree.nodes["Background"].inputs["Color"].default_value = (0.035, 0.035, 0.04, 1.0) +sc.world = world + + +def aim(ob, point): + ob.rotation_euler = (point - ob.location).normalized().to_track_quat("-Z", "Y").to_euler() + + +def area(name, offset, energy, size, col): + ld = bpy.data.lights.new(name, "AREA") + ld.energy, ld.size, ld.color = energy * (dim / 3) ** 2, size * dim, col + ob = bpy.data.objects.new(name, ld) + ob.location = center + Vector(offset) * dim * 1.5 + sc.collection.objects.link(ob) + aim(ob, center) + + +area("Key", (-0.5, -0.7, 1.0), 500, 1.2, (1.0, 1.0, 1.0)) +area("Fill", (0.9, -0.4, 0.5), 180, 1.6, (0.9, 0.95, 1.0)) +area("Rim", (0.3, 0.9, 0.8), 280, 1.0, (1.0, 1.0, 1.0)) + +cam_data = bpy.data.cameras.new("SheetCam") +cam_data.lens, cam_data.sensor_width = 50, 36.0 +cam = bpy.data.objects.new("SheetCam", cam_data) +# Fit the true extent at the real FOV rather than a bbox-diagonal guess, so +# tall and wide assets land at comparable scale across panels. Horizontal +# extent is the worst case: a 3/4 view can present either face. +hfov = 2.0 * math.atan(cam_data.sensor_width / (2.0 * cam_data.lens)) +vfov = 2.0 * math.atan(cam_data.sensor_width * (RES_Y / RES_X) / (2.0 * cam_data.lens)) +span = mx - mn +dist = max(math.hypot(span.x, span.y) / (2.0 * math.tan(hfov / 2.0)), + max(span.z, 1e-3) / (2.0 * math.tan(vfov / 2.0))) * 1.18 +cam.location = center + Vector((-0.55, -0.77, 0.38)).normalized() * dist +sc.collection.objects.link(cam) +aim(cam, center) +sc.camera = cam + +sc.render.engine = "BLENDER_EEVEE" if bpy.app.version >= (5, 0, 0) else "BLENDER_EEVEE_NEXT" +sc.eevee.taa_render_samples = 32 +sc.render.resolution_x, sc.render.resolution_y = RES_X, RES_Y +sc.render.resolution_percentage = 100 +sc.render.image_settings.file_format = "PNG" +sc.render.filepath = out_png +# Standard, as every gallery render: AgX would wash the panel toward grey +sc.view_settings.view_transform = "Standard" +sc.view_settings.look = "None" +sc.view_settings.exposure = 0.0 +sc.render.use_compositing = False +bpy.ops.render.render(write_still=True, scene=sc.name) +print(f"sheet: panel written {out_png}")