From 1ccf2d413e08f2b18ca4ec60cea5831c97cdd891 Mon Sep 17 00:00:00 2001 From: Yousef Rafat <81116377+yousef-rafat@users.noreply.github.com> Date: Wed, 20 May 2026 20:18:54 +0300 Subject: [PATCH] .. --- comfy_extras/nodes_mesh_postprocess.py | 96 -------------------------- comfy_extras/nodes_save_3d.py | 34 +++++---- 2 files changed, 21 insertions(+), 109 deletions(-) diff --git a/comfy_extras/nodes_mesh_postprocess.py b/comfy_extras/nodes_mesh_postprocess.py index a1f3c1a68..74eb9e0c1 100644 --- a/comfy_extras/nodes_mesh_postprocess.py +++ b/comfy_extras/nodes_mesh_postprocess.py @@ -284,80 +284,6 @@ def fill_holes_fn(vertices, faces, max_perimeter=0.03): return v, f - -def make_double_sided(vertices, faces, colors=None, normals=None, z_offset=1e-4): - """ - Creates double-sided mesh using PER-FACE normals for offset. - This avoids pole singularities completely. - """ - is_batched = vertices.ndim == 3 - - if is_batched: - v_list, f_list, c_list = [], [], [] - for i in range(vertices.shape[0]): - # Compute face normals for this mesh - v0 = vertices[i][faces[i][:, 0]] - v1 = vertices[i][faces[i][:, 1]] - v2 = vertices[i][faces[i][:, 2]] - fn = torch.cross(v1 - v0, v2 - v0, dim=-1) - fn = fn / (torch.norm(fn, dim=-1, keepdim=True) + 1e-8) - - # Offset each face's vertices along its face normal - front = torch.stack([v0, v1, v2], dim=1) + fn.unsqueeze(1) * z_offset - back = torch.stack([v0, v1, v2], dim=1) - fn.unsqueeze(1) * z_offset - - front = front.reshape(-1, 3) - back = back.reshape(-1, 3) - - f_front = torch.arange(faces[i].shape[0] * 3, device=vertices.device).reshape(-1, 3) - f_back = f_front + faces[i].shape[0] * 3 - f_back = f_back[:, [0, 2, 1]] # flip winding for back faces - - v_list.append(torch.cat([front, back], dim=0)) - f_list.append(torch.cat([f_front, f_back], dim=0)) - - if colors is not None: - c_faces = colors[i][faces[i]] - c_front = c_faces.reshape(-1, colors[i].shape[-1]) - c_back = c_front.clone() - c_list.append(torch.cat([c_front, c_back], dim=0)) - - out_v = torch.stack(v_list) - out_f = torch.stack(f_list) - if colors is not None: - return out_v, out_f, torch.stack(c_list) - return out_v, out_f - - # --- Unbatched --- - v0 = vertices[faces[:, 0]] - v1 = vertices[faces[:, 1]] - v2 = vertices[faces[:, 2]] - fn = torch.cross(v1 - v0, v2 - v0, dim=-1) - fn = fn / (torch.norm(fn, dim=-1, keepdim=True) + 1e-8) - - # Offset each face's vertices along its face normal - front = torch.stack([v0, v1, v2], dim=1) + fn.unsqueeze(1) * z_offset - back = torch.stack([v0, v1, v2], dim=1) - fn.unsqueeze(1) * z_offset - - front = front.reshape(-1, 3) - back = back.reshape(-1, 3) - - f_front = torch.arange(faces.shape[0] * 3, device=vertices.device).reshape(-1, 3) - f_back = f_front + faces.shape[0] * 3 - f_back = f_back[:, [0, 2, 1]] # flip winding for back faces - - v_dup = torch.cat([front, back], dim=0) - f_dup = torch.cat([f_front, f_back], dim=0) - - if colors is not None: - c_faces = colors[faces] - c_front = c_faces.reshape(-1, colors.shape[-1]) - c_back = c_front.clone() - c_dup = torch.cat([c_front, c_back], dim=0) - return v_dup, f_dup, c_dup - - return v_dup, f_dup, None - def _cleanup_mesh(verts, faces, min_angle_deg=0.5, max_aspect=100.0): if faces.numel() == 0: return verts, faces @@ -905,32 +831,10 @@ class FillHoles(IO.ComfyNode): return v, f, c return _process_mesh_batch(mesh, _fn) - -class MakeDoubleSided(IO.ComfyNode): - @classmethod - def define_schema(cls): - return IO.Schema( - node_id="MakeDoubleSided", - display_name="Make Double Sided", - category="latent/3d", - description="Duplicates faces with flipped normals so the mesh renders from both sides.", - inputs=[IO.Mesh.Input("mesh")], - outputs=[IO.Mesh.Output("mesh")], - ) - - @classmethod - def execute(cls, mesh): - def _fn(v, f, c): - return make_double_sided(v, f, c) - return _process_mesh_batch(mesh, _fn) - - - class PostProcessMeshExtension(ComfyExtension): @override async def get_node_list(self) -> list[type[IO.ComfyNode]]: return [ - MakeDoubleSided, FillHoles, DecimateMesh, PaintMesh diff --git a/comfy_extras/nodes_save_3d.py b/comfy_extras/nodes_save_3d.py index c03524246..6657c4490 100644 --- a/comfy_extras/nodes_save_3d.py +++ b/comfy_extras/nodes_save_3d.py @@ -234,6 +234,12 @@ def save_glb(vertices, faces, filepath, metadata=None, textures = [] samplers = [] materials = [] + pbr = { + "metallicFactor": 0.0, + "roughnessFactor": 0.5, + "baseColorFactor": [0.22, 0.22, 0.22, 1.0], + } + if texture_png_bytes is not None and "TEXCOORD_0" in primitive_attributes: buffer_views.append({ "buffer": 0, @@ -243,15 +249,13 @@ def save_glb(vertices, faces, filepath, metadata=None, images.append({"bufferView": len(buffer_views) - 1, "mimeType": "image/png"}) samplers.append({"magFilter": 9729, "minFilter": 9729, "wrapS": 33071, "wrapT": 33071}) textures.append({"source": 0, "sampler": 0}) - materials.append({ - "pbrMetallicRoughness": { - "baseColorTexture": {"index": 0, "texCoord": 0}, - "metallicFactor": 0.0, - "roughnessFactor": 1.0, - }, - "doubleSided": True, - }) - primitive["material"] = 0 + pbr["baseColorTexture"] = {"index": 0, "texCoord": 0} + + materials.append({ + "pbrMetallicRoughness": pbr, + "doubleSided": True, + }) + primitive["material"] = 0 gltf = { "asset": {"version": "2.0", "generator": "ComfyUI"}, @@ -373,10 +377,14 @@ class SaveGLB(IO.ComfyNode): continue tex_img = Image.fromarray(texture_np[i], mode="RGB") if texture_np is not None else None f = f"{filename}_{counter:05}_.glb" - save_glb(vertices_i, faces_i, os.path.join(full_output_folder, f), metadata, - uvs=uvs_i, - vertex_colors=v_colors, - texture_image=tex_img) + save_glb( + vertices_i, faces_i, + os.path.join(full_output_folder, f), + metadata, + uvs=uvs_i, + vertex_colors=v_colors, + texture_image=tex_img, + ) results.append({ "filename": f, "subfolder": subfolder,