#!/usr/bin/env python3 """ Convert the TRELLIS.2 shape-SLAT VAE decoder checkpoint (shape_dec_next_dc_f16c32_fp16.safetensors) to a GGUF file for trellis2.cpp. This is FlexiDualGridVaeDecoder (a SparseUnetVaeDecoder): a sparse ConvNeXt U-Net decoder that turns the 32-channel structured latent on active voxels into 7 channels per voxel at 16x the input resolution (dual-vertex offset, per-axis intersection flags, quad split weight) for flexible-dual-grid mesh extraction. Architecture (config shape_dec_next_dc_f16c32_fp16.json): model_channels [1024, 512, 256, 128, 64], num_blocks [4, 16, 8, 4, 0], SparseConvNeXtBlock3d blocks, SparseResBlockC2S3d up-blocks, out 7. Checkpoint conv weights are stored in FlexGEMM layout [Co, kD, kH, kW, Ci] (permuted at module init, then saved). ggml tensors are 4-D, so the kernel axes merge: we reshape to [Co, kD*kH*kW, Ci] (identical bytes) and the C++ side slices per kernel offset into [Ci, Co] GEMM operands. Usage: python convert_shape_dec_to_gguf.py --output shape_dec.gguf --ftype 1 ftype: 0 = f32 (lossless upcast, validation), 1 = f16 (default). """ import argparse import json import os import struct import sys 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-shape-dec" KV_PREFIX = "trellis2.shape_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("