#!/usr/bin/env python3 """ Reference PBR texturing, end-to-end, on OUR generated mesh -- the golden target the C++/ggml texture port validates against, and a quick eyeball of the texture NN stages. Runs the real Trellis2TexturingPipeline NN stages (shape encoder -> tex SLAT flow -> tex decoder) with sparse ops monkeypatched to pure torch (ref_common), so it needs NO custom CUDA kernels except o-voxel's CPU mesh->dual-grid. For the eyeball we skip the CUDA-only UV bake (nvdiffrast/cumesh/flexgemm) and instead trilinear-sample the decoded 6-channel PBR voxels at each mesh vertex (base_color / metallic / roughness), then dump a coloured mesh to render. python scripts/ref_texture.py --mesh --image \ --resolution 512 --out dumps/tex_vcolor.bin """ import argparse, json, os, struct, sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import o_voxel # noqa: E402 (import the REAL o-voxel BEFORE ref_common so its import o_voxel.convert # stub guard skips it; we need mesh_to_flexible_dual_grid) import ref_common # noqa: E402 ref_common.setup() import numpy as np # noqa: E402 import torch # noqa: E402 from PIL import Image # noqa: E402 import trimesh # noqa: E402 from safetensors.torch import load_file # noqa: E402 from trellis2.models.sc_vaes.fdg_vae import FlexiDualGridVaeEncoder # noqa: E402 from trellis2.models.sc_vaes.sparse_unet_vae import SparseUnetVaeDecoder # noqa: E402 from trellis2.models.structured_latent_flow import SLatFlowModel # noqa: E402 from trellis2.pipelines.samplers import FlowEulerGuidanceIntervalSampler # noqa: E402 from trellis2.pipelines import Trellis2TexturingPipeline # noqa: E402 from trellis2.modules.image_feature_extractor import DinoV3FeatureExtractor # noqa: E402 def load_t2mesh(path): b = open(path, "rb").read() assert b[:8] == b"T2MESH01", b[:8] nv, nt = struct.unpack(" PBR voxels ...", flush=True) pbr = pipe.decode_tex_slat(tex_slat) # SparseTensor, 6ch, already *0.5+0.5 print(f"pbr voxels: {pbr.feats.shape} coords {pbr.coords.shape} " f"range [{pbr.feats.min():.3f},{pbr.feats.max():.3f}]", flush=True) # per-vertex trilinear sample of the PBR voxels (mesh is now normalized to [-.5,.5]) Vt = torch.from_numpy(mesh.vertices).float().to(dev) qvox = (Vt + 0.5) * R feats = pbr.feats.float() coords_xyz = pbr.coords[:, 1:].to(dev) vals, w = trilinear_sample_sparse(feats, coords_xyz, qvox) vals = vals.clamp(0, 1).cpu().numpy() hit = (w > 1e-4).float().mean().item() print(f"per-vertex sample hit-rate {hit*100:.1f}%", flush=True) base_color = vals[:, 0:3] metallic = vals[:, 3:4]; roughness = vals[:, 4:5] Vout = mesh.vertices.astype("