The multi-part decomposition pipeline takes a pre-encoded shape latent and a list of part names to return individual meshes for each part.
Workflow:
- Initialize
PartShapeDenoiserPipeline with your config and checkpoint paths. - Load an existing mesh and sample its surface using
sample_surface. - Convert the surface to a torch tensor and use
parts_pipe.encode_shape(surface) to obtain latents. - Call
parts_pipe.input_to_part_shape with a ShapeInput object containing your part prompts and latents.
Best Practices for Input Meshes:
- Ensure the mesh is canonically aligned (+Y up, +Z forward).
- Use a watertight, single-surface mesh. Avoid meshes with duplicated inner/outer shells.
import torch
import trimesh
from cube_part.pipelines import PartShapeDenoiserPipeline, ShapeInput
from cube_part.utils.mesh import load_mesh, sample_surface
parts_pipe = PartShapeDenoiserPipeline(
config_path="configs/shape_denoiser_multimesh.yaml",
checkpoint_path="weights/multi_part_dit.safetensors",
vae_checkpoint_path="weights/vae.safetensors",
extract_geometry_fn_name="extract_geometry_coarse_to_fine",
)
mesh, _, _ = load_mesh("examples/inputs/jellyfish_car.glb")
surface = sample_surface(mesh, num_samples=128_000)
surface = (
torch.from_numpy(surface).to(parts_pipe.device).unsqueeze(0).float()
)
latents, _ = parts_pipe.encode_shape(surface)
part_meshes = parts_pipe.input_to_part_shape(
ShapeInput(prompt=[["body", "wheels"]], latents=latents),
guidance_scale=7.5,
num_inference_steps=50,
)
for i, (vertices, faces) in enumerate(part_meshes):
if vertices is not None:
trimesh.Trimesh(vertices, faces).export(f"part_{i:02d}.glb")