#!/usr/bin/env python3 """ Convert the TRELLIS.2 shape-SLAT VAE encoder checkpoint (shape_enc_next_dc_f16c32_fp16.safetensors) to a GGUF file for trellis2.cpp. This is FlexiDualGridVaeEncoder (a SparseUnetVaeEncoder): the mirror of the shape/tex decoders. It ingests the res-1024 flexible-dual-grid of the input mesh as a 6-channel sparse tensor -- 3 dual-vertex offsets + 3 per-axis intersection flags (concatenated in FlexiDualGridVaeEncoder.forward) -- and downsamples 16x (four SparseResBlockS2C3d Spatial2Channel steps) to a 32-channel latent on the res-64 grid. The texture pipeline uses this latent as concat_cond for the tex flow, and the Spatial2Channel steps record the per-level subdivision the tex decoder replays to rebuild the res-1024 voxel set. model_channels [64,128,256,512,1024], num_blocks [0,4,8,16,4], SparseConvNeXtBlock3d blocks, SparseResBlockS2C3d down-blocks, input_layer 6->64, to_latent 1024->2*32 (mean/logvar; we take mean). Conv weights are FlexGEMM [Co,kD,kH,kW,Ci] -> reshaped to [Co, kD*kH*kW, Ci]. Usage: python convert_shape_enc_to_gguf.py --output ggufs/shape_enc_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-shape-enc" KV_PREFIX = "trellis2.shape_enc." 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("