From 2251679479299a85496053da1a1c2ef3b5c3a52a Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 13 Dec 2024 15:37:59 +0500 Subject: [PATCH] Skip parts without polygons converting mesh to tessellation #5870 As IFC doesn't allow mixed geometry types anyway. --- src/bonsai/bonsai/bim/module/root/operator.py | 6 ++++ src/bonsai/bonsai/tool/geometry.py | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/bonsai/bonsai/bim/module/root/operator.py b/src/bonsai/bonsai/bim/module/root/operator.py index acc0133782..9d7a081b66 100644 --- a/src/bonsai/bonsai/bim/module/root/operator.py +++ b/src/bonsai/bonsai/bim/module/root/operator.py @@ -217,6 +217,12 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator): should_add_representation = False export_mesh_to_tesselation = True + if tool.Geometry.mesh_has_loose_geometry(obj.data): + self.report( + {"WARNING"}, + f"Mesh '{obj.data.name}' has loose geometry, loose geometry was be ignored to save mesh to IFC as a tessellation.", + ) + element = core.assign_class( tool.Ifc, tool.Collector, diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index e396495091..ac5d78f768 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -1727,6 +1727,10 @@ class Geometry(bonsai.core.tool.Geometry): items = [] meshes = cls.split_by_loose_parts(obj) for mesh in meshes: + # Skip parts that won't work for tessellation. + if not mesh.polygons: + bpy.data.meshes.remove(mesh) + continue verts = [v.co / unit_scale for v in mesh.vertices] faces = [p.vertices[:] for p in mesh.polygons] item = builder.mesh(verts, faces) @@ -1750,3 +1754,28 @@ class Geometry(bonsai.core.tool.Geometry): ifcopenshell.api.style.assign_item_style(tool.Ifc.get(), item=item, style=style) bpy.data.meshes.remove(mesh) return builder.get_representation(ifc_context, items) + + @classmethod + def mesh_has_loose_geometry(cls, mesh: bpy.types.Mesh) -> bool: + """Check if mesh has loose geometry (edges without faces, verts without edges).""" + bm = tool.Blender.get_bmesh_for_mesh(mesh) + + # Most of the time it will return `False`, + # so checking verts for being manifold + # should be the fastest way to proceed in those cases. + non_manifold_edges = set() + for vert in bm.verts: + if not vert.is_manifold: + # Not all non-manifold verts mean loose geometry + # e.g. a vert shared by 2 planes. + if not vert.link_faces: + return True + non_manifold_edges.update(vert.link_edges) + + if not non_manifold_edges: + return False + + for edge in non_manifold_edges: + if not edge.link_faces: + return True + return False