added some workflows for automated testing

This commit is contained in:
PozzettiAndrea
2026-02-26 05:29:06 +00:00
parent 283cfbc441
commit f33a2ad7ef
18 changed files with 367368 additions and 6 deletions
+26322
View File
File diff suppressed because it is too large Load Diff
+35108
View File
File diff suppressed because it is too large Load Diff
+19422
View File
File diff suppressed because it is too large Load Diff
+25912
View File
File diff suppressed because it is too large Load Diff
+19422
View File
File diff suppressed because it is too large Load Diff
+25912
View File
File diff suppressed because it is too large Load Diff
+72672
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+111089
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+8 -6
View File
@@ -59,18 +59,20 @@ class LoadMesh:
@classmethod
def get_mesh_files(cls):
"""Get list of available mesh files in input/3d and input folders."""
"""Get list of available mesh files in input/3d (recursively) and input folders."""
mesh_files = []
if COMFYUI_INPUT_FOLDER is not None:
# Scan input/3d first
# Scan input/3d recursively
input_3d = os.path.join(COMFYUI_INPUT_FOLDER, "3d")
if os.path.exists(input_3d):
for file in os.listdir(input_3d):
if any(file.lower().endswith(ext) for ext in cls.SUPPORTED_EXTENSIONS):
mesh_files.append(f"3d/{file}")
for root, _dirs, files in os.walk(input_3d):
for file in files:
if any(file.lower().endswith(ext) for ext in cls.SUPPORTED_EXTENSIONS):
rel = os.path.relpath(os.path.join(root, file), COMFYUI_INPUT_FOLDER)
mesh_files.append(rel)
# Then scan input root
# Then scan input root (non-recursive, top-level files only)
for file in os.listdir(COMFYUI_INPUT_FOLDER):
file_path = os.path.join(COMFYUI_INPUT_FOLDER, file)
if os.path.isfile(file_path):
+4
View File
@@ -6,6 +6,7 @@ Smoothing nodes
"""
from .smooth import NODE_CLASS_MAPPINGS as SMOOTH_MAPS, NODE_DISPLAY_NAME_MAPPINGS as SMOOTH_DISP
from .sharpen import NODE_CLASS_MAPPINGS as SHARPEN_MAPS, NODE_DISPLAY_NAME_MAPPINGS as SHARPEN_DISP
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
@@ -13,4 +14,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(SMOOTH_MAPS)
NODE_DISPLAY_NAME_MAPPINGS.update(SMOOTH_DISP)
NODE_CLASS_MAPPINGS.update(SHARPEN_MAPS)
NODE_DISPLAY_NAME_MAPPINGS.update(SHARPEN_DISP)
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+294
View File
@@ -0,0 +1,294 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2025 ComfyUI-GeometryPack Contributors
"""
Sharpen Mesh Node - Edge-recovering sharpening via pymeshlab.
Available backends:
- two_step: Two-phase bilateral normal filtering. Smooths face normals
(respecting dihedral angle thresholds), then repositions vertices to
match. Sharpens creases while keeping faces flat. Best for CAD-like
geometry from marching cubes, scanning, or neural SDF extraction.
- unsharp_mask: Geometric unsharp masking. Subtracts a smoothed version
from the original to amplify ridges and valleys.
"""
import logging
import numpy as np
import trimesh as trimesh_module
log = logging.getLogger("geometrypack")
def _pymeshlab_two_step_sharpen(mesh, smooth_steps, normal_threshold,
normal_iterations, fit_iterations, selected_only):
"""Two-step bilateral normal sharpening via PyMeshLab."""
try:
import pymeshlab
except (ImportError, OSError):
return None, "pymeshlab is not installed. Install with: pip install pymeshlab"
ms = pymeshlab.MeshSet()
pml_mesh = pymeshlab.Mesh(
vertex_matrix=np.asarray(mesh.vertices, dtype=np.float64),
face_matrix=np.asarray(mesh.faces, dtype=np.int32),
)
ms.add_mesh(pml_mesh)
try:
ms.apply_coord_two_steps_smoothing(
stepsmoothnum=smooth_steps,
normalthr=normal_threshold,
stepnormalnum=normal_iterations,
stepfitnum=fit_iterations,
selected=selected_only,
)
except AttributeError:
try:
ms.two_steps_smooth(
stepsmoothnum=smooth_steps,
normalthr=normal_threshold,
stepnormalnum=normal_iterations,
stepfitnum=fit_iterations,
selected=selected_only,
)
except AttributeError:
return None, (
"PyMeshLab two-step smoothing filter not available. "
"Ensure pymeshlab >= 2023.12 is installed."
)
out = ms.current_mesh()
result = trimesh_module.Trimesh(
vertices=out.vertex_matrix(),
faces=out.face_matrix(),
process=False,
)
return result, ""
def _pymeshlab_unsharp_mask_sharpen(mesh, weight, weight_original, iterations):
"""Geometric unsharp mask sharpening via PyMeshLab."""
try:
import pymeshlab
except (ImportError, OSError):
return None, "pymeshlab is not installed. Install with: pip install pymeshlab"
ms = pymeshlab.MeshSet()
pml_mesh = pymeshlab.Mesh(
vertex_matrix=np.asarray(mesh.vertices, dtype=np.float64),
face_matrix=np.asarray(mesh.faces, dtype=np.int32),
)
ms.add_mesh(pml_mesh)
try:
ms.apply_coord_unsharp_mask(
weight=weight,
weightorig=weight_original,
iterations=iterations,
)
except AttributeError:
try:
ms.coord_unsharp_mask(
weight=weight,
weightorig=weight_original,
iterations=iterations,
)
except AttributeError:
return None, (
"PyMeshLab coordinate unsharp mask filter not available. "
"Ensure pymeshlab >= 2023.12 is installed."
)
out = ms.current_mesh()
result = trimesh_module.Trimesh(
vertices=out.vertex_matrix(),
faces=out.face_matrix(),
process=False,
)
return result, ""
class SharpenMeshNode:
"""
Sharpen Mesh - Edge-recovering sharpening algorithms.
Available backends:
- two_step: Bilateral normal filtering then vertex repositioning.
Sharpens creases while flattening faces. Best for CAD-like geometry.
The normal_threshold is the key parameter: edges with dihedral angle
sharper than this are preserved as features.
- unsharp_mask: Geometric unsharp masking. Subtracts smoothed from
original to amplify ridges and valleys. Good for general enhancement.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"trimesh": ("TRIMESH",),
"backend": ([
"two_step",
"unsharp_mask",
], {
"default": "two_step",
"tooltip": (
"Sharpening algorithm. "
"two_step=bilateral normal filtering (recommended for CAD-like edges), "
"unsharp_mask=geometric unsharp masking (general enhancement)"
),
}),
},
"optional": {
"smooth_steps": ("INT", {
"default": 3,
"min": 1,
"max": 50,
"step": 1,
"tooltip": (
"Number of two-step smoothing passes. "
"More steps = stronger sharpening effect."
),
"visible_when": {"backend": ["two_step"]},
}),
"normal_threshold": ("FLOAT", {
"default": 60.0,
"min": 0.0,
"max": 180.0,
"step": 0.5,
"tooltip": (
"Dihedral angle threshold in degrees. "
"Edges sharper than this angle are preserved as features. "
"Lower = more aggressive (more edges treated as creases). "
"60 is a good default for most CAD models."
),
"visible_when": {"backend": ["two_step"]},
}),
"weight": ("FLOAT", {
"default": 0.3,
"min": 0.0,
"max": 3.0,
"step": 0.01,
"tooltip": (
"Unsharp mask weight controlling sharpening strength. "
"Higher = more pronounced sharpening."
),
"visible_when": {"backend": ["unsharp_mask"]},
}),
"iterations": ("INT", {
"default": 5,
"min": 1,
"max": 50,
"step": 1,
"tooltip": (
"Smoothing iterations for the reference smooth mesh. "
"More iterations = larger-scale sharpening."
),
"visible_when": {"backend": ["unsharp_mask"]},
}),
},
}
RETURN_TYPES = ("TRIMESH", "STRING")
RETURN_NAMES = ("sharpened_mesh", "info")
FUNCTION = "sharpen"
CATEGORY = "geompack/smoothing"
OUTPUT_NODE = True
def sharpen(
self,
trimesh,
backend,
smooth_steps=3,
normal_threshold=60.0,
weight=0.3,
iterations=5,
):
"""Apply mesh sharpening based on selected backend."""
# Sanitize hidden widget values
smooth_steps = int(smooth_steps) if smooth_steps not in (None, "") else 3
normal_threshold = float(normal_threshold) if normal_threshold not in (None, "") else 60.0
weight = float(weight) if weight not in (None, "") else 0.3
iterations = int(iterations) if iterations not in (None, "") else 5
initial_vertices = len(trimesh.vertices)
initial_faces = len(trimesh.faces)
log.info("Sharpen backend: %s", backend)
log.info("Input: %s vertices, %s faces",
f"{initial_vertices:,}", f"{initial_faces:,}")
if backend == "two_step":
log.info("Parameters: smooth_steps=%d, normal_threshold=%.1f",
smooth_steps, normal_threshold)
sharpened, error = _pymeshlab_two_step_sharpen(
trimesh, smooth_steps, normal_threshold,
normal_iterations=20, fit_iterations=20,
selected_only=False,
)
elif backend == "unsharp_mask":
log.info("Parameters: weight=%.3f, iterations=%d", weight, iterations)
sharpened, error = _pymeshlab_unsharp_mask_sharpen(
trimesh, weight, weight_original=1.0, iterations=iterations,
)
else:
raise ValueError(f"Unknown backend: {backend}")
if sharpened is None:
raise ValueError(f"Sharpening failed ({backend}): {error}")
# Copy metadata
if hasattr(trimesh, "metadata") and trimesh.metadata:
sharpened.metadata = trimesh.metadata.copy()
sharpened.metadata["sharpening"] = {
"algorithm": backend,
"original_vertices": initial_vertices,
"original_faces": initial_faces,
}
# Compute displacement stats
disp = np.linalg.norm(
np.asarray(sharpened.vertices) - np.asarray(trimesh.vertices), axis=1
)
avg_disp = float(np.mean(disp))
max_disp = float(np.max(disp))
log.info("Output: %d vertices, %d faces",
len(sharpened.vertices), len(sharpened.faces))
log.info("Avg vertex displacement: %.6f, max: %.6f", avg_disp, max_disp)
# Build backend-specific param block
if backend == "two_step":
param_text = (
f"Smooth Steps: {smooth_steps}\n"
f"Normal Threshold: {normal_threshold}\u00b0"
)
elif backend == "unsharp_mask":
param_text = (
f"Weight: {weight}\n"
f"Iterations: {iterations}"
)
else:
param_text = ""
info = f"""Sharpen Mesh Results ({backend}):
{param_text}
Vertices: {initial_vertices:,} (unchanged)
Faces: {initial_faces:,} (unchanged)
Displacement:
Average: {avg_disp:.6f}
Maximum: {max_disp:.6f}
"""
return {"ui": {"text": [info]}, "result": (sharpened, info)}
NODE_CLASS_MAPPINGS = {
"GeomPackSharpenMesh": SharpenMeshNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"GeomPackSharpenMesh": "Sharpen Mesh",
}
+48
View File
@@ -0,0 +1,48 @@
# Nodes Without Workflow Coverage
35 of 74 nodes (~47%) have no workflow JSON. Checked against `workflows/*.json`.
## IO (5 missing)
- [ ] `GeomPackLoadMeshPath` — Load Mesh (Path)
- [ ] `GeomPackLoadMeshGlob` — Load Mesh Batch (Glob)
- [ ] `GeomPackSaveMesh` — Save Mesh
- [ ] `GeomPackSaveMeshBatch` — Save Meshes to Folder
- [ ] `GeomPackGetMeshFilename` — Get Mesh Filename
## Analysis (4 missing)
- [ ] `GeomPackConnectedComponents` — Connected Components
- [ ] `GeomPackOpenEdges` — Open Edges
- [ ] `GeomPackDegenerateFaces` — Degenerate Faces
- [ ] `ScrambleIntField` — Scramble Int Field
## Conversion (1 missing)
- [ ] `GeomPackSubsamplePointCloud` — Subsample Point Cloud
## Transforms (1 missing)
- [ ] `GeomPackNormalizeMeshToBBox` — Normalize to BBox
## Visualization (3 missing)
- [ ] `GeomPackBackdraftView` — Backdraft View
- [ ] `GeomPackPreviewGaussian` — Preview Gaussian
- [ ] `GeomPackPreviewMeshAnalysis` — Preview Mesh (Analysis)
## Repair (6 missing)
- [ ] `GeomPackAddNormalsToPointCloud` — Add Normals to PointCloud
- [ ] `GeomPackVisualizeNormalField` — Visualize Normal Field
- [ ] `GeomPackMergeVertices` — Merge Vertices
- [ ] `GeomPackFixSelfIntersectionsByRemoval` — Fix Self Intersections (Removal)
- [ ] `GeomPackRemoveDegenerateFaces` — Remove Degenerate Faces
- [ ] `GeomPackMeshFix` — MeshFix
## Texture Remeshing (1 missing)
- [ ] `GeomPackDepthNormalsToMesh` — Depth + Normals to Mesh
## Combine (2 missing)
- [ ] `GeomPackCombineMeshesBatch` — Combine Meshes (Batch)
- [ ] `GeomPackSplitByField` — Split By Field
## Boolean (1 missing)
- [ ] `GeomPackBooleanBlender` — Boolean Blender
## ParaView (1 missing)
- [ ] `GeomPackParaViewFilter` — ParaView Filter
+1
View File
@@ -0,0 +1 @@
{"id":"b2f31842-cb16-4652-a0bb-ef347acd2c27","revision":0,"last_node_id":3,"last_link_id":3,"nodes":[{"id":2,"type":"GeomPackLoadMesh","pos":[-475.3018486006804,-89.92481471968625],"size":[270,78],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"file_path","name":"file_path","type":"COMBO","widget":{"name":"file_path"},"link":null}],"outputs":[{"localized_name":"mesh","name":"mesh","type":"TRIMESH","links":[1,2]},{"localized_name":"texture","name":"texture","type":"IMAGE","links":null}],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackLoadMesh"},"widgets_values":["3d/spot_the_cow.obj"]},{"id":1,"type":"GeomPackDecimateMesh","pos":[-341.89427205099804,42.18256877884877],"size":[214.34375,222],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"trimesh","name":"trimesh","type":"TRIMESH","link":1},{"localized_name":"backend","name":"backend","type":"COMBO","widget":{"name":"backend"},"link":null},{"localized_name":"target_face_count","name":"target_face_count","shape":7,"type":"INT","widget":{"name":"target_face_count"},"link":null},{"localized_name":"target_reduction","name":"target_reduction","shape":7,"type":"FLOAT","widget":{"name":"target_reduction"},"link":null},{"localized_name":"quality_threshold","name":"quality_threshold","shape":7,"type":"FLOAT","widget":{"name":"quality_threshold"},"link":null},{"localized_name":"preserve_boundary","name":"preserve_boundary","shape":7,"type":"COMBO","widget":{"name":"preserve_boundary"},"link":null},{"localized_name":"preserve_normal","name":"preserve_normal","shape":7,"type":"COMBO","widget":{"name":"preserve_normal"},"link":null},{"localized_name":"preserve_topology","name":"preserve_topology","shape":7,"type":"COMBO","widget":{"name":"preserve_topology"},"link":null},{"localized_name":"planar_quadric","name":"planar_quadric","shape":7,"type":"COMBO","widget":{"name":"planar_quadric"},"link":null},{"localized_name":"aggressiveness","name":"aggressiveness","shape":7,"type":"INT","widget":{"name":"aggressiveness"},"link":null},{"localized_name":"cluster_threshold","name":"cluster_threshold","shape":7,"type":"FLOAT","widget":{"name":"cluster_threshold"},"link":null},{"localized_name":"cost_strategy","name":"cost_strategy","shape":7,"type":"COMBO","widget":{"name":"cost_strategy"},"link":null},{"localized_name":"feature_angle","name":"feature_angle","shape":7,"type":"FLOAT","widget":{"name":"feature_angle"},"link":null}],"outputs":[{"localized_name":"decimated_mesh","name":"decimated_mesh","type":"TRIMESH","links":[3]},{"localized_name":"info","name":"info","type":"STRING","links":null}],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackDecimateMesh"},"widgets_values":["quadric_edge_collapse",1004,0.5,0.3,"true","true","true","false",7,1,"lindstrom_turk",15]},{"id":3,"type":"GeomPackPreviewMeshDual","pos":[-76.10573591330262,-130.49691411299682],"size":[768,834],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"mesh_1","name":"mesh_1","type":"TRIMESH","link":2},{"localized_name":"mesh_2","name":"mesh_2","type":"TRIMESH","link":3},{"localized_name":"layout","name":"layout","shape":7,"type":"COMBO","widget":{"name":"layout"},"link":null},{"localized_name":"mode","name":"mode","shape":7,"type":"COMBO","widget":{"name":"mode"},"link":null},{"localized_name":"opacity_1","name":"opacity_1","shape":7,"type":"FLOAT","widget":{"name":"opacity_1"},"link":null},{"localized_name":"opacity_2","name":"opacity_2","shape":7,"type":"FLOAT","widget":{"name":"opacity_2"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackPreviewMeshDual"},"widgets_values":["side_by_side","fields",1,1,"{\"show_edges\":true,\"camera_state\":\"{\\\"position\\\":[-1.7780235403811753,1.6766117516965702,-3.559892704773735],\\\"focalPoint\\\":[0,0,0],\\\"viewUp\\\":[0.10922451112503215,0.9191915766556414,0.3783607426449175]}\",\"selected_field\":\"\"}"]}],"links":[[1,2,0,1,0,"TRIMESH"],[2,2,0,3,0,"TRIMESH"],[3,1,0,3,1,"TRIMESH"]],"groups":[],"config":{},"extra":{"workflowRendererVersion":"LG","ds":{"scale":0.564473930053778,"offset":[1015.1636836243922,437.7471233117878]}},"version":0.4}
+1
View File
@@ -0,0 +1 @@
{"id":"3a030446-090e-4dec-aa56-ff2245e5c752","revision":0,"last_node_id":5,"last_link_id":8,"nodes":[{"id":3,"type":"GeomPackLoadMesh","pos":[-122.94664697897677,-247.07890552869162],"size":[270,78],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"file_path","name":"file_path","type":"COMBO","widget":{"name":"file_path"},"link":null}],"outputs":[{"localized_name":"mesh","name":"mesh","type":"TRIMESH","links":[1,7]},{"localized_name":"texture","name":"texture","type":"IMAGE","links":null}],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackLoadMesh"},"widgets_values":["3d/cow.off"]},{"id":1,"type":"GeomPackPreviewMeshDual","pos":[360.9494657084009,-269.09765927991236],"size":[768,834],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"mesh_1","name":"mesh_1","type":"TRIMESH","link":1},{"localized_name":"mesh_2","name":"mesh_2","type":"TRIMESH","link":8},{"localized_name":"layout","name":"layout","shape":7,"type":"COMBO","widget":{"name":"layout"},"link":null},{"localized_name":"mode","name":"mode","shape":7,"type":"COMBO","widget":{"name":"mode"},"link":null},{"localized_name":"opacity_1","name":"opacity_1","shape":7,"type":"FLOAT","widget":{"name":"opacity_1"},"link":null},{"localized_name":"opacity_2","name":"opacity_2","shape":7,"type":"FLOAT","widget":{"name":"opacity_2"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackPreviewMeshDual"},"widgets_values":["side_by_side","fields",1,1,"{\"show_edges\":true,\"camera_state\":\"{\\\"position\\\":[-1.264590737186599,0.4315551722479547,-0.95689822713445],\\\"focalPoint\\\":[0,0,0],\\\"viewUp\\\":[0.3414289225328609,0.9395050612781046,-0.027505102996978413]}\",\"selected_field\":\"\"}"]},{"id":5,"type":"GeomPackSmoothMesh","pos":[19.308970746690356,8.031904442981274],"size":[210,150],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"trimesh","name":"trimesh","type":"TRIMESH","link":7},{"localized_name":"backend","name":"backend","type":"COMBO","widget":{"name":"backend"},"link":null},{"localized_name":"iterations","name":"iterations","shape":7,"type":"INT","widget":{"name":"iterations"},"link":null},{"localized_name":"lambda_","name":"lambda_","shape":7,"type":"FLOAT","widget":{"name":"lambda_"},"link":null},{"localized_name":"mu","name":"mu","shape":7,"type":"FLOAT","widget":{"name":"mu"},"link":null},{"localized_name":"cotangent_weight","name":"cotangent_weight","shape":7,"type":"COMBO","widget":{"name":"cotangent_weight"},"link":null}],"outputs":[{"localized_name":"smoothed_mesh","name":"smoothed_mesh","type":"TRIMESH","links":[8]},{"localized_name":"info","name":"info","type":"STRING","links":null}],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackSmoothMesh"},"widgets_values":["taubin",5,1,-0.8,"true"]}],"links":[[1,3,0,1,0,"TRIMESH"],[7,3,0,5,0,"TRIMESH"],[8,5,0,1,1,"TRIMESH"]],"groups":[],"config":{},"extra":{"workflowRendererVersion":"LG","ds":{"scale":0.7513148009015788,"offset":[360.4697500785045,392.5990955570182]}},"version":0.4}
+1
View File
@@ -0,0 +1 @@
{"id":"1420e2f4-15c5-4bb4-b298-69fb0d5f6356","revision":0,"last_node_id":8,"last_link_id":8,"nodes":[{"id":6,"type":"GeomPackLoadMesh","pos":[-719.4819699886815,-132.5789047560572],"size":[270,78],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"file_path","name":"file_path","type":"COMBO","widget":{"name":"file_path"},"link":null}],"outputs":[{"localized_name":"mesh","name":"mesh","type":"TRIMESH","links":[6]},{"localized_name":"texture","name":"texture","type":"IMAGE","links":null}],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackLoadMesh"},"widgets_values":["3d/noisy/Fandisk0.3/Original.obj"]},{"id":4,"type":"GeomPackLoadMesh","pos":[-765.4619884518163,129.58774960185292],"size":[270,78],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"file_path","name":"file_path","type":"COMBO","widget":{"name":"file_path"},"link":null}],"outputs":[{"localized_name":"mesh","name":"mesh","type":"TRIMESH","links":[5,7]},{"localized_name":"texture","name":"texture","type":"IMAGE","links":null}],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackLoadMesh"},"widgets_values":["3d/noisy/Fandisk0.3/Noisy.obj"]},{"id":7,"type":"GeomPackSharpenMesh","pos":[-424.4191210268283,273.2249762472692],"size":[210,126],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"trimesh","name":"trimesh","type":"TRIMESH","link":5},{"localized_name":"backend","name":"backend","type":"COMBO","widget":{"name":"backend"},"link":null},{"localized_name":"smooth_steps","name":"smooth_steps","shape":7,"type":"INT","widget":{"name":"smooth_steps"},"link":null},{"localized_name":"normal_threshold","name":"normal_threshold","shape":7,"type":"FLOAT","widget":{"name":"normal_threshold"},"link":null},{"localized_name":"weight","name":"weight","shape":7,"type":"FLOAT","widget":{"name":"weight"},"link":null},{"localized_name":"iterations","name":"iterations","shape":7,"type":"INT","widget":{"name":"iterations"},"link":null}],"outputs":[{"localized_name":"sharpened_mesh","name":"sharpened_mesh","type":"TRIMESH","links":[8]},{"localized_name":"info","name":"info","type":"STRING","links":null}],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackSharpenMesh"},"widgets_values":["two_step",2,60,0,5]},{"id":8,"type":"GeomPackPreviewMeshMulti","pos":[-93.23315435742478,-231.02465627340337],"size":[768,702],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"mesh_1","name":"mesh_1","type":"TRIMESH","link":6},{"localized_name":"mesh_2","name":"mesh_2","shape":7,"type":"TRIMESH","link":7},{"localized_name":"mesh_3","name":"mesh_3","shape":7,"type":"TRIMESH","link":8},{"localized_name":"mesh_4","name":"mesh_4","shape":7,"type":"TRIMESH","link":null},{"localized_name":"mode","name":"mode","shape":7,"type":"COMBO","widget":{"name":"mode"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfyui-geometrypack","ver":"283cfbc4414b0b9343c6562793f7b42f80dd32dd","Node name for S&R":"GeomPackPreviewMeshMulti"},"widgets_values":["fields","{\"show_edges\":true,\"camera_state\":\"{\\\"position\\\":[11.988365833801835,17.82450062324679,9.744545997398912],\\\"focalPoint\\\":[2.4225937947630882,15.191427230834961,-1.2984154149889946],\\\"viewUp\\\":[-0.5732642974885914,-0.5317644442147023,0.6233735806833175]}\",\"selected_field\":\"\"}"]}],"links":[[5,4,0,7,0,"TRIMESH"],[6,6,0,8,0,"TRIMESH"],[7,4,0,8,1,"TRIMESH"],[8,7,0,8,2,"TRIMESH"]],"groups":[],"config":{},"extra":{"workflowRendererVersion":"LG","ds":{"scale":0.6209213230591556,"offset":[948.190060342013,447.314044719686]}},"version":0.4}