Remesh blender_sharp: expose remove-disconnected cull, fix ranges, clearer tooltips
blender_sharp = Blender's Remesh modifier in SHARP mode (octree dual-contouring, watertight output, reconstructs sharp edges). - Expose use_remove_disconnected + threshold as remove_disconnected (default true, matching Blender) + disconnected_threshold. Previously unset, so Blender's defaults silently culled small disconnected pieces; now it's controllable. Verified: true culls a floating cube (1 component), false keeps it (2 components). - sharpness max 5.0 -> 2.0 (Blender's normal slider range; higher just over-sharpens). - octree_depth max 10 -> 12 (Blender's real max), scale max 1.0 -> 0.99 (real max). - Rewrite octree_depth/scale/sharpness tooltips with concrete meaning (power-of-2 face growth, octree fit, sharpness behavior). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
fd4e322174
commit
7a8f881e64
@@ -25,9 +25,11 @@ class RemeshBlenderSharpNode(io.ComfyNode):
|
||||
is_output_node=True,
|
||||
inputs=[
|
||||
io.Custom("TRIMESH").Input("trimesh"),
|
||||
io.Int.Input("octree_depth", default=6, min=1, max=10, step=1, tooltip="Resolution. Higher = more detail, more faces."),
|
||||
io.Float.Input("scale", default=0.9, min=0.0, max=1.0, step=0.05, display_mode="number", tooltip="Ratio of output size to input bounding box."),
|
||||
io.Float.Input("sharpness", default=1.0, min=0.0, max=5.0, step=0.1, display_mode="number", tooltip="Edge sharpness for Sharp mode."),
|
||||
io.Int.Input("octree_depth", default=6, min=1, max=12, step=1, tooltip="Octree resolution -- the detail knob. Power of 2: each +1 roughly QUADRUPLES face count and halves voxel size. 6 is a sane start; 8-9 is high detail; 10+ can be very heavy."),
|
||||
io.Float.Input("scale", default=0.9, min=0.0, max=0.99, step=0.05, display_mode="number", tooltip="Octree fit relative to the bounding box (0-0.99). Higher = grid hugs the mesh tighter = finer effective resolution; too close to 1.0 can clip the outer shell. 0.9 default."),
|
||||
io.Float.Input("sharpness", default=1.0, min=0.0, max=2.0, step=0.1, display_mode="number", tooltip="How aggressively dual-contouring snaps to sharp edges/corners. Higher = crisper edges but can spike on noisy input; lower = rounder. 0-2 is Blender's normal slider range (1.0 default); capped at 2 here since higher just over-sharpens."),
|
||||
io.Combo.Input("remove_disconnected", options=["true", "false"], default="true", tooltip="Delete small disconnected (floating) pieces after remeshing. ON by default (matches Blender)."),
|
||||
io.Float.Input("disconnected_threshold", default=1.0, min=0.0, max=1.0, step=0.05, display_mode="number", tooltip="Size cutoff for removal, relative to the largest component. Higher = remove more aggressively (1.0 ~ keep only the main body); lower = keep more; 0 = keep everything. Only used when remove_disconnected is on."),
|
||||
],
|
||||
outputs=[
|
||||
io.Custom("TRIMESH").Output(display_name="remeshed_mesh"),
|
||||
@@ -36,12 +38,14 @@ class RemeshBlenderSharpNode(io.ComfyNode):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, trimesh, octree_depth=6, scale=0.9, sharpness=1.0):
|
||||
def execute(cls, trimesh, octree_depth=6, scale=0.9, sharpness=1.0,
|
||||
remove_disconnected="true", disconnected_threshold=1.0):
|
||||
import bpy
|
||||
|
||||
log.info("Backend: blender_sharp")
|
||||
log.info("Input: %d vertices, %d faces", len(trimesh.vertices), len(trimesh.faces))
|
||||
log.info("Parameters: octree_depth=%d, scale=%s, sharpness=%s", octree_depth, scale, sharpness)
|
||||
log.info("Parameters: octree_depth=%d, scale=%s, sharpness=%s, remove_disconnected=%s, threshold=%s",
|
||||
octree_depth, scale, sharpness, remove_disconnected, disconnected_threshold)
|
||||
|
||||
obj, mesh = _bpy_setup_object(
|
||||
np.asarray(trimesh.vertices, dtype=np.float32),
|
||||
@@ -52,6 +56,8 @@ class RemeshBlenderSharpNode(io.ComfyNode):
|
||||
mod.octree_depth = octree_depth
|
||||
mod.scale = scale
|
||||
mod.sharpness = sharpness
|
||||
mod.use_remove_disconnected = (remove_disconnected == "true")
|
||||
mod.threshold = disconnected_threshold
|
||||
bpy.ops.object.modifier_apply(modifier="Remesh")
|
||||
result = _bpy_extract_and_cleanup(obj)
|
||||
|
||||
@@ -63,6 +69,7 @@ class RemeshBlenderSharpNode(io.ComfyNode):
|
||||
remeshed_mesh.metadata = trimesh.metadata.copy()
|
||||
remeshed_mesh.metadata['remeshing'] = {
|
||||
'algorithm': 'blender_sharp', 'octree_depth': octree_depth, 'scale': scale, 'sharpness': sharpness,
|
||||
'remove_disconnected': remove_disconnected == "true", 'disconnected_threshold': disconnected_threshold,
|
||||
}
|
||||
|
||||
log.info("Output: %d vertices, %d faces", len(remeshed_mesh.vertices), len(remeshed_mesh.faces))
|
||||
|
||||
@@ -131,9 +131,11 @@ class RemeshNode(io.ComfyNode):
|
||||
io.Float.Input("voxel_size", default=1, min=0.001, max=1.0, step=0.01, display_mode="number", tooltip="Voxel size. Smaller = more detail. Output is always watertight."),
|
||||
]),
|
||||
io.DynamicCombo.Option("blender_sharp", [
|
||||
io.Int.Input("octree_depth", default=6, min=1, max=10, step=1, tooltip="Resolution. Higher = more detail, more faces."),
|
||||
io.Float.Input("scale", default=0.9, min=0.0, max=1.0, step=0.05, display_mode="number", tooltip="Ratio of output size to input bounding box."),
|
||||
io.Float.Input("sharpness", default=1.0, min=0.0, max=5.0, step=0.1, display_mode="number", tooltip="Edge sharpness."),
|
||||
io.Int.Input("octree_depth", default=6, min=1, max=12, step=1, tooltip="Octree resolution -- the detail knob. Power of 2: each +1 roughly QUADRUPLES face count and halves voxel size. 6 is a sane start; 8-9 is high detail; 10+ can be very heavy."),
|
||||
io.Float.Input("scale", default=0.9, min=0.0, max=0.99, step=0.05, display_mode="number", tooltip="Octree fit relative to the bounding box (0-0.99). Higher = grid hugs the mesh tighter = finer effective resolution; too close to 1.0 can clip the outer shell. 0.9 default."),
|
||||
io.Float.Input("sharpness", default=1.0, min=0.0, max=2.0, step=0.1, display_mode="number", tooltip="How aggressively dual-contouring snaps to sharp edges/corners. Higher = crisper edges but can spike on noisy input; lower = rounder. 0-2 is Blender's normal slider range (1.0 default); capped at 2 here since higher just over-sharpens."),
|
||||
io.Combo.Input("remove_disconnected", options=["true", "false"], default="true", tooltip="Delete small disconnected (floating) pieces after remeshing. ON by default (matches Blender)."),
|
||||
io.Float.Input("disconnected_threshold", default=1.0, min=0.0, max=1.0, step=0.05, display_mode="number", tooltip="Size cutoff for removal, relative to the largest component. Higher = remove more aggressively (1.0 ~ keep only the main body); lower = keep more; 0 = keep everything. Only used when remove_disconnected is on."),
|
||||
]),
|
||||
io.DynamicCombo.Option("blender_blocks", [
|
||||
io.Int.Input("octree_depth", default=6, min=1, max=10, step=1, tooltip="Resolution. Higher = more detail, more faces."),
|
||||
|
||||
Reference in New Issue
Block a user