import argparse import os import re os.environ.setdefault("SPCONV_ALGO", "native") os.environ.setdefault("ATTN_BACKEND", "flash_attn") os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") import numpy as np from trellis.pipelines import ( DVDTextToVoxelPipeline, TrellisTextTo3DPipeline, export_cubified_voxels, run_text_stage2_from_dvd_voxels, ) from trellis.utils import postprocessing_utils def slugify(text: str, max_length: int = 48) -> str: slug = re.sub(r"[^a-zA-Z0-9]+", "_", text.lower()).strip("_") return (slug[:max_length].strip("_") or "text_prompt") def parse_args(): parser = argparse.ArgumentParser(description="DVD text-to-voxel generation followed by TRELLIS stage 2.") parser.add_argument("--prompt", required=True, help="Input text prompt.") parser.add_argument("--name", default=None, help="Output asset name. Defaults to a slug of the prompt.") parser.add_argument("--dvd-config", default="ckpts/dvd_text.json", help="DVD text model config JSON.") parser.add_argument("--dvd-checkpoint", default="ckpts/dvd_text.safetensors", help="DVD text safetensors checkpoint.") parser.add_argument("--trellis-model", default="microsoft/TRELLIS-text-large", help="TRELLIS text pipeline.") parser.add_argument("--output-dir", default="example_results", help="Directory for generated assets.") parser.add_argument("--seed", type=int, default=42) parser.add_argument("--device", default="cuda") parser.add_argument("--dvd-steps", type=int, default=256) parser.add_argument("--stage2-steps", type=int, default=25) parser.add_argument("--stage2-cfg", type=float, default=5.0) parser.add_argument("--skip-stage2", action="store_true", help="Only generate and save DVD voxels.") return parser.parse_args() def main(): args = parse_args() os.makedirs(args.output_dir, exist_ok=True) name = args.name or slugify(args.prompt) dvd = DVDTextToVoxelPipeline.from_files( args.dvd_config, args.dvd_checkpoint, device=args.device, ) voxels = dvd.sample_voxels( args.prompt, seed=args.seed, steps=args.dvd_steps, ) voxel_coords_path = os.path.join(args.output_dir, f"voxel64_{name}_dis.npy") np.save(voxel_coords_path, voxels.coords_without_batch.numpy()) export_cubified_voxels(voxels, os.path.join(args.output_dir, f"{name}_dvd_voxels.glb")) print(f"Saved DVD coords: {voxel_coords_path}") if args.skip_stage2: return trellis = TrellisTextTo3DPipeline.from_pretrained(args.trellis_model) trellis.to(args.device) outputs = run_text_stage2_from_dvd_voxels( trellis, args.prompt, voxels, seed=args.seed, slat_sampler_params={ "steps": args.stage2_steps, "cfg_strength": args.stage2_cfg, }, ) glb = postprocessing_utils.to_glb(outputs["gaussian"][0], outputs["mesh"][0]) glb_path = os.path.join(args.output_dir, f"{name}.glb") glb.export(glb_path) print(f"Saved GLB: {glb_path}") if __name__ == "__main__": main()