Added the node "Fill Holes Nicely with Meshlib"
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
| Date | Description |
|
||||
| --- | --- |
|
||||
| **2026-04-20** | Added node "Fill Holes Nicely with Meshlib" |
|
||||
| **2026-04-06** | Added new "DINO-Lock" functionality<br>New fill_holes in Sparse Generator<br>Thanks to Easymode on Discord |
|
||||
| **2026-04-05** | Added node "Extract Images from Video"<br>Can be used with "Sparse Generator with ReconViaGen" |
|
||||
| **2026-04-04** | Added node "Sparse Generator with ReconViaGen" |
|
||||
|
||||
@@ -1355,7 +1355,7 @@ class Trellis2MeshWithVoxelAdvancedGenerator:
|
||||
"hole_iterations": ("INT",{"default":1,"min":1,"max":9,"step":1}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"hole_fill_algorithm": (["morphological_closing","flood_fill","remove_small_holes"],{"default":"flood_fill"}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
"keep_only_shell": ("BOOLEAN",{"default":True}),
|
||||
@@ -1496,7 +1496,7 @@ class Trellis2MeshWithVoxelMultiViewGenerator:
|
||||
"hole_iterations": ("INT",{"default":1,"min":1,"max":9,"step":1}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"hole_fill_algorithm": (["morphological_closing","flood_fill","remove_small_holes"],{"default":"flood_fill"}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
"keep_only_shell": ("BOOLEAN",{"default":True}),
|
||||
@@ -2278,7 +2278,7 @@ class Trellis2MeshTexturing:
|
||||
"inpainting": (["telea","ns"],{"default":"telea"}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
},
|
||||
}
|
||||
@@ -2354,7 +2354,7 @@ class Trellis2MeshTexturingMultiView:
|
||||
"inpainting": (["telea","ns"],{"default":"telea"}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
},
|
||||
"optional": {
|
||||
@@ -2623,7 +2623,7 @@ class Trellis2MeshRefiner:
|
||||
"sampler": (["euler", "heun", "rk4", "rk5"], {"default": "euler"}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
},
|
||||
}
|
||||
@@ -3051,7 +3051,7 @@ class Trellis2FillHolesWithMeshlib:
|
||||
|
||||
last_reported_percent = -1 # Initialize at -1 to ensure 0% triggers an update
|
||||
|
||||
for i, e in enumerate(hole_edges):
|
||||
for i, e in enumerate(hole_edges):
|
||||
params = mrmeshpy.FillHoleParams()
|
||||
params.metric = mrmeshpy.getUniversalMetric(mesh)
|
||||
mrmeshpy.fillHole(mesh, e, params)
|
||||
@@ -3090,6 +3090,82 @@ class Trellis2FillHolesWithMeshlib:
|
||||
|
||||
return (mesh_copy, holes_filled)
|
||||
|
||||
class Trellis2FillHolesNicelyWithMeshlib:
|
||||
"""Fill all holes in a mesh"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"mesh": ("MESHWITHVOXEL",),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MESHWITHVOXEL", "INT")
|
||||
RETURN_NAMES = ("mesh", "holes_filled")
|
||||
FUNCTION = "process"
|
||||
CATEGORY = "Trellis2Wrapper"
|
||||
DESCRIPTION = "Fill all holes in a mesh using optimal triangulation."
|
||||
|
||||
def process(self, mesh):
|
||||
import meshlib.mrmeshpy as mrmeshpy
|
||||
|
||||
mesh_copy = copy.deepcopy(mesh)
|
||||
mesh = mrmeshnumpy.meshFromFacesVerts(mesh_copy.faces.detach().clone().cpu().numpy(), mesh_copy.vertices.detach().clone().cpu().numpy())
|
||||
|
||||
hole_edges = mesh.topology.findHoleRepresentiveEdges()
|
||||
holes_filled = 0
|
||||
|
||||
nb_holes = len(hole_edges)
|
||||
print(f"{nb_holes} holes found")
|
||||
|
||||
if nb_holes > 0:
|
||||
progress_bar = tqdm(total=nb_holes, desc="Filling holes")
|
||||
pbar = ProgressBar(nb_holes)
|
||||
|
||||
last_reported_percent = -1 # Initialize at -1 to ensure 0% triggers an update
|
||||
|
||||
for i, e in enumerate(hole_edges):
|
||||
params = mrmeshpy.FillHoleNicelySettings()
|
||||
params.triangulateParams.metric = mrmeshpy.getMinAreaMetric(mesh)
|
||||
params.smoothCurvature = False
|
||||
# Fill hole represented by `e`
|
||||
mrmeshpy.fillHoleNicely(mesh, e, params)
|
||||
|
||||
# Calculate current progress
|
||||
current_step = i + 1
|
||||
current_percent = int((current_step / nb_holes) * 100)
|
||||
|
||||
# Only update the UI if the percentage has moved up
|
||||
if current_percent > last_reported_percent:
|
||||
# Calculate how many holes have been filled since the last UI update
|
||||
# This handles cases where 1% might represent multiple holes
|
||||
if last_reported_percent == -1:
|
||||
# First update
|
||||
progress_bar.update(current_step)
|
||||
pbar.update(current_step)
|
||||
else:
|
||||
# Update by the difference since the last check
|
||||
last_step = int((last_reported_percent * nb_holes) / 100)
|
||||
diff = current_step - last_step
|
||||
progress_bar.update(diff)
|
||||
pbar.update(diff)
|
||||
|
||||
last_reported_percent = current_percent
|
||||
|
||||
progress_bar.close()
|
||||
|
||||
new_vertices = mrmeshnumpy.getNumpyVerts(mesh)
|
||||
new_faces = mrmeshnumpy.getNumpyFaces(mesh.topology)
|
||||
|
||||
del mesh
|
||||
gc.collect()
|
||||
|
||||
mesh_copy.vertices = torch.from_numpy(new_vertices).float().to(mesh_copy.device)
|
||||
mesh_copy.faces = torch.from_numpy(new_faces).int().to(mesh_copy.device)
|
||||
|
||||
return (mesh_copy, holes_filled)
|
||||
|
||||
class Trellis2SmoothNormals:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -3581,7 +3657,7 @@ class Trellis2MeshWithVoxelCascadeGenerator:
|
||||
"hole_iterations": ("INT",{"default":1,"min":1,"max":9,"step":1}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"hole_fill_algorithm": (["morphological_closing","flood_fill","remove_small_holes"],{"default":"flood_fill"}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
"keep_only_shell": ("BOOLEAN",{"default":True}),
|
||||
@@ -3763,7 +3839,7 @@ class Trellis2SparseGenerator:
|
||||
"hole_iterations": ("INT",{"default":1,"min":1,"max":9,"step":1}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"hole_fill_algorithm": (["morphological_closing","flood_fill","remove_small_holes"],{"default":"flood_fill"}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
"keep_only_shell": ("BOOLEAN",{"default":True}),
|
||||
@@ -3851,7 +3927,7 @@ class Trellis2ShapeGenerator:
|
||||
"shape_guidance_interval_end": ("FLOAT",{"default":1.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
},
|
||||
}
|
||||
@@ -3936,7 +4012,7 @@ class Trellis2ShapeCascadeGenerator:
|
||||
"shape_guidance_interval_end": ("FLOAT",{"default":1.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
},
|
||||
}
|
||||
@@ -4071,7 +4147,7 @@ class Trellis2TexSlatGenerator:
|
||||
"texture_guidance_interval_end": ("FLOAT",{"default":0.90,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
},
|
||||
}
|
||||
@@ -5022,7 +5098,7 @@ class Trellis2SparseGeneratorWithReconViaGen:
|
||||
"sparse_structure_guidance_interval_end": ("FLOAT",{"default":1.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"verbose": ("BOOLEAN",{"default":False}),
|
||||
"dino_lock": ("FLOAT",{"default":0.00,"min":0.00,"max":1.00,"step":0.01}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":9,"step":1}),
|
||||
"dino_substeps": ("INT",{"default":4,"min":1,"max":99,"step":1}),
|
||||
"dino_foundation_cap": ("FLOAT",{"default":1.00,"min":0.01,"max":1.00,"step":0.01}),
|
||||
"fill_holes":("BOOLEAN",{"default":True}),
|
||||
"hole_iterations": ("INT",{"default":1,"min":1,"max":9,"step":1}),
|
||||
@@ -5594,6 +5670,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Trellis2SparseGeneratorWithReconViaGen": Trellis2SparseGeneratorWithReconViaGen,
|
||||
"Trellis2ExtractImagesFromVideo": Trellis2ExtractImagesFromVideo,
|
||||
"Trellis2MaxTokensCalculator": Trellis2MaxTokensCalculator,
|
||||
"Trellis2FillHolesNicelyWithMeshlib": Trellis2FillHolesNicelyWithMeshlib,
|
||||
}
|
||||
|
||||
|
||||
@@ -5656,4 +5733,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Trellis2SparseGeneratorWithReconViaGen": "Trellis2 - Sparse Generator with ReconViaGen",
|
||||
"Trellis2ExtractImagesFromVideo": "Trellis2 - Extract Images from Video",
|
||||
"Trellis2MaxTokensCalculator": "Trellis2 - Max Tokens Calculator",
|
||||
"Trellis2FillHolesNicelyWithMeshlib": "Trellis2 - Fill Holes Nicely With Meshlib",
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "trellis2"
|
||||
description = "ComfyUI Wrapper for Microsoft Trellis.2 - Native and Compact Structured Latents for 3D Generation"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
license = {file = "LICENSE"}
|
||||
# classifiers = [
|
||||
# # For OS-independent nodes (works on all operating systems)
|
||||
|
||||
@@ -3169,13 +3169,16 @@ class Trellis2ImageTo3DPipeline(Pipeline):
|
||||
if num_tokens < max_num_tokens:
|
||||
if hr_resolution != resolution:
|
||||
print(f"Due to the limited number of tokens, the resolution is reduced to {hr_resolution}.")
|
||||
print(f"Num tokens {num_tokens}")
|
||||
break
|
||||
hr_resolution -= 128
|
||||
if hr_resolution < 1024 and resolution >= 1024:
|
||||
hr_resolution = 1024
|
||||
print(f"Num tokens {num_tokens}")
|
||||
break
|
||||
if hr_resolution < 512:
|
||||
hr_resolution = 512
|
||||
print(f"Num tokens {num_tokens}")
|
||||
break
|
||||
|
||||
coords_dev = coords.to(self.device)
|
||||
|
||||
Reference in New Issue
Block a user