#!/usr/bin/env python3 """ Convert the TRELLIS.2 texture-SLAT VAE decoder checkpoint (tex_dec_next_dc_f16c32_fp16.safetensors) to a GGUF file for trellis2.cpp. This is a SparseUnetVaeDecoder — the SAME sparse ConvNeXt U-Net as the shape decoder (FlexiDualGridVaeDecoder) except: * out_channels = 6 (PBR: base_color[3], metallic[1], roughness[1], alpha[1]), no dual-grid geometry head; * pred_subdiv = False -> NO `to_subdiv` layers. The decoder does not predict which children to keep; it replays the subdivision structure recorded by the shape encoder's Spatial2Channel steps (threaded through the SparseTensor spatial cache). So the tex decoder reconstructs exactly the encoder's res-1024 voxel set; the C++ side must supply that per-level subdivision. Same architecture/weight layout as the shape decoder otherwise: model_channels [1024,512,256,128,64], num_blocks [4,16,8,4,0], SparseConvNeXtBlock3d blocks, SparseResBlockC2S3d up-blocks. Conv weights are FlexGEMM [Co,kD,kH,kW,Ci] -> reshaped to [Co, kD*kH*kW, Ci]. Usage: python convert_tex_dec_to_gguf.py --output ggufs/tex_dec_f16.gguf --ftype 1 """ import argparse import json import os import struct import numpy as np GGUF_MAGIC = b"GGUF" GGUF_VERSION = 3 GGUF_ALIGNMENT = 32 GGML_TYPE_F32 = 0 GGML_TYPE_F16 = 1 GGUF_VT_UINT32 = 4 GGUF_VT_FLOAT32 = 6 GGUF_VT_STRING = 8 ARCH = "trellis2-tex-dec" KV_PREFIX = "trellis2.tex_dec." def _gguf_str(s): b = s.encode("utf-8") return struct.pack(" [Co, 27, Ci] Co, kD, kH, kW, Ci = shape arr = np.ascontiguousarray(arr).reshape(Co, kD * kH * kW, Ci) shape = arr.shape gtype = GGML_TYPE_F32 if args.ftype == 1 and len(shape) >= 2: gtype = GGML_TYPE_F16 raw = (arr.astype(" 0 else [1] tensors.append((name, gtype, dims, raw)) counts[gtype] += 1 print(f"tensors: {len(tensors)} (f32={counts[GGML_TYPE_F32]}, f16={counts[GGML_TYPE_F16]})") header = bytearray() header += GGUF_MAGIC header += struct.pack("