diff --git a/comfy/ldm/hunyuan3dv2_1/vae.py b/comfy/ldm/hunyuan3dv2_1/vae.py index 35986e917..219bc430e 100644 --- a/comfy/ldm/hunyuan3dv2_1/vae.py +++ b/comfy/ldm/hunyuan3dv2_1/vae.py @@ -5,7 +5,6 @@ import torch from torch import Tensor import math -import trimesh import numpy as np from skimage import measure from dataclasses import dataclass @@ -607,8 +606,8 @@ class PointCrossAttention(nn.Module): @dataclass class Latent2MeshOutput(): # mesh for vertices and faces - mesh_v: None - mesh_f: None + vertices: None + faces: None class SufraceExtractor(): def compute_box_stat(self, bounds, octree_resolution: int): @@ -644,7 +643,7 @@ class SufraceExtractor(): vertices, faces = self.run(grid_logits[i], **kwds) vertices = vertices.astype(np.float32) faces = np.ascontiguousarray(faces) - outputs.append(Latent2MeshOutput(mesh_v = vertices, mesh_f = faces)) + outputs.append(Latent2MeshOutput(vertices = vertices, faces = faces)) except Exception: import traceback @@ -687,24 +686,6 @@ class VanillaVolumeDecoder(): return grid_logits -def export_to_trimesh(mesh_output): - import trimesh - - if isinstance(mesh_output, list): - outputs = [] - for mesh in mesh_output: - if mesh is None: - outputs.append(None) - else: - mesh.mesh_f = mesh.mesh_f[:, ::-1] - mesh_output = trimesh.Trimesh(mesh.mesh_v, mesh.mesh_f) - outputs.append(mesh_output) - return outputs - else: - mesh_output.mesh_f = mesh_output.mesh_f[:, ::-1] - mesh_output = trimesh.Trimesh(mesh_output.mesh_v, mesh_output.mesh_f) - return mesh_output - def normalize_mesh(mesh, scale = 0.9999): """Normalize mesh to fit in [-scale, scale]. Translate mesh so its center is [0,0,0]""" @@ -767,6 +748,8 @@ def sharp_sample_pointcloud(mesh, num = 16384): def load_surface_sharpedge(mesh, num_points=4096, num_sharp_points=4096, sharpedge_flag = True, device = "cuda"): """Load a surface with optional sharp-edge annotations from a trimesh mesh.""" + import trimesh + try: mesh_full = trimesh.util.concatenate(mesh.dump()) except Exception: @@ -837,6 +820,7 @@ class SharpEdgeSurfaceLoader: @staticmethod def _load_mesh(mesh_input): + import trimesh if isinstance(mesh_input, str): mesh = trimesh.load(mesh_input, force="mesh", merge_primitives = True) diff --git a/comfy_extras/nodes_hunyuan3d.py b/comfy_extras/nodes_hunyuan3d.py index 1034f7fbc..e91b48a99 100644 --- a/comfy_extras/nodes_hunyuan3d.py +++ b/comfy_extras/nodes_hunyuan3d.py @@ -110,17 +110,15 @@ class VAEDecodeHunyuan3D: }), "num_chunks": ("INT", { "default": 8000, "min": 1000, "max": 500000, - "visible_if": {"version": "2.0"} }), "octree_resolution": ("INT", { "default": 256, "min": 16, "max": 512, - "visible_if": {"version": "2.0"} }), } } - RETURN_TYPES = ("VOXEL", "SDF_FUNCTION") - RETURN_NAMES = ("voxel", "sdf") + RETURN_TYPES = ("VOXEL", "MESH") + RETURN_NAMES = ("voxel", "mesh") FUNCTION = "decode" CATEGORY = "latent/3d" @@ -137,6 +135,12 @@ class VAEDecodeHunyuan3D: mesh = vae.decode(samples["samples"], to_mesh = True, num_chunks = num_chunks, octree_resolution = octree_resolution) + + # ensure batch dim + if mesh.verticies.ndim == 2: + mesh.verticies = mesh.verticies[np.newaxis, ...] + mesh.faces = mesh.faces[np.newaxis, ...] + return (None, mesh) def voxel_to_mesh(voxels, threshold=0.5, device=None): @@ -495,7 +499,7 @@ class VoxelToMesh: return (MESH(torch.stack(vertices), torch.stack(faces)), ) -def save_glb(vertices, faces, filepath, metadata=None, numpy_ready = False): +def save_glb(vertices, faces, filepath, metadata=None): """ Save PyTorch tensor vertices and faces as a GLB file without external dependencies. @@ -506,7 +510,7 @@ def save_glb(vertices, faces, filepath, metadata=None, numpy_ready = False): """ # Convert tensors to numpy arrays - if not numpy_ready: + if isinstance(vertices, torch.tensor) and isinstance(faces, torch.tensor): vertices_np = vertices.cpu().numpy().astype(np.float32) faces_np = faces.cpu().numpy().astype(np.uint32) else: