From 0db816b55d6386cb4308c250ea1d2a237fe09c66 Mon Sep 17 00:00:00 2001 From: m3ultra Date: Mon, 20 Jul 2026 03:53:35 +1000 Subject: [PATCH] vertex baker: export contract + vertex normals; validator accepts COLOR_0 asset class - _export_pbr vertex branch returns (trimesh, pre_simplified) per contract - materialize vertex_normals pre-export (glTF NORMAL, same trick as _raw_trimesh) - gltf_validation: COLOR_0-without-UVs primitives are a legitimate color-bearing static asset (glTF default material x COLOR_0); texture checks skip for them Full gate now EXIT=0: 71.96s e2e, 26.5GB peak (from A0 216.6s / 75.4GB). Co-Authored-By: Claude Fable 5 --- scripts/generate_asset.py | 4 ++-- trellis2/gltf_validation.py | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/scripts/generate_asset.py b/scripts/generate_asset.py index 3660e4c..f91987f 100755 --- a/scripts/generate_asset.py +++ b/scripts/generate_asset.py @@ -242,9 +242,9 @@ def _export_pbr(mesh, path: Path, *, baker: str, target: Optional[int], texture_ rgba = np.concatenate([rgb, np.ones((len(v_gltf), 1))], 1) out.visual = trimesh.visual.ColorVisuals( out, vertex_colors=(rgba * 255).astype(np.uint8)) # LINEAR + _ = out.vertex_normals # materialize so glTF gets NORMAL (see _raw_trimesh) out.export(str(path)) - return {"baker": "vertex", "pre_simplified": pre_simplified, - "triangles": int(len(f_np)), "vertices": int(len(v_np))} + return out, pre_simplified if baker == "metal": available, reason = _metal_baker_available() diff --git a/trellis2/gltf_validation.py b/trellis2/gltf_validation.py index 22d7fda..92721b6 100644 --- a/trellis2/gltf_validation.py +++ b/trellis2/gltf_validation.py @@ -83,8 +83,13 @@ def inspect_glb(path: Path, *, require_pbr: bool) -> dict[str, Any]: if int(primitive.get("mode", 4)) != 4: raise ValueError("only TRIANGLES primitives are supported") attributes = primitive.get("attributes", {}) - required = ["POSITION", "NORMAL"] + (["TEXCOORD_0"] if require_pbr else []) + required = ["POSITION", "NORMAL"] missing = [name for name in required if name not in attributes] + # a color-bearing static asset may carry UVs (texture bake) OR + # per-vertex colors (vertex bake) — either satisfies require_pbr + if require_pbr and "TEXCOORD_0" not in attributes \ + and "COLOR_0" not in attributes: + missing.append("TEXCOORD_0|COLOR_0") if missing: raise ValueError(f"primitive is missing attributes: {', '.join(missing)}") position = document["accessors"][int(attributes["POSITION"])] @@ -103,7 +108,9 @@ def inspect_glb(path: Path, *, require_pbr: bool) -> dict[str, Any]: raise ValueError("invalid triangle index count") triangles += index_count // 3 - if require_pbr: + _vertex_colored = ("COLOR_0" in attributes + and "TEXCOORD_0" not in attributes) + if require_pbr and not _vertex_colored: if "material" not in primitive: raise ValueError("PBR primitive has no material") material = document["materials"][int(primitive["material"])]