Skip to content

Commit ba2456a

Browse files
committed
Move common code to create_mesh_from_shape
1 parent 299b5bc commit ba2456a

5 files changed

Lines changed: 76 additions & 124 deletions

File tree

src/bonsai/bonsai/bim/import_ifc.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ def create_generic_elements(
589589
obj.hide_select = True
590590

591591
def create_generic_sqlite_elements(self, elements: set[ifcopenshell.entity_instance]) -> None:
592+
assert isinstance(self.file, ifcopenshell.sql.sqlite)
592593
self.geometry_cache = self.file.get_geometry([e.id() for e in elements])
593594
for geometry_id, geometry in self.geometry_cache["geometry"].items():
594595
mesh_name = tool.Loader.get_mesh_name_from_shape(type("Geometry", (), {"id": geometry_id}))
@@ -598,21 +599,9 @@ def create_generic_sqlite_elements(self, elements: set[ifcopenshell.entity_insta
598599
mesh["has_cartesian_point_offset"] = False
599600

600601
if geometry["faces"]:
601-
num_vertices = len(verts) // 3
602-
total_faces = len(geometry["faces"])
603-
loop_start = range(0, total_faces, 3)
604-
num_loops = total_faces // 3
605-
loop_total = [3] * num_loops
606-
num_vertex_indices = len(geometry["faces"])
607-
608-
mesh.vertices.add(num_vertices)
609-
mesh.vertices.foreach_set("co", verts)
610-
mesh.loops.add(num_vertex_indices)
611-
mesh.loops.foreach_set("vertex_index", geometry["faces"])
612-
mesh.polygons.add(num_loops)
613-
mesh.polygons.foreach_set("loop_start", loop_start)
614-
mesh.polygons.foreach_set("loop_total", loop_total)
615-
mesh.update()
602+
mesh = tool.Loader.create_mesh_from_shape(
603+
mesh=mesh, faces=geometry["faces"].reshape(-1, 3), verts=verts.reshape(-1, 3)
604+
)
616605
else:
617606
e = geometry["edges"]
618607
v = verts

src/bonsai/bonsai/bim/module/project/operator.py

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,21 +1848,7 @@ def process_occurrence(self, shape: ShapeElementType) -> None:
18481848

18491849
material_index = np.array([(material_to_slot[i] if i != -1 else 0) for i in material_ids], dtype="I")
18501850

1851-
num_vertices = len(verts) // 3
1852-
total_faces = len(faces)
1853-
loop_start = range(0, total_faces, 3)
1854-
num_loops = total_faces // 3
1855-
loop_total = [3] * num_loops
1856-
num_vertex_indices = len(faces)
1857-
1858-
mesh.vertices.add(num_vertices)
1859-
mesh.vertices.foreach_set("co", verts)
1860-
mesh.loops.add(num_vertex_indices)
1861-
mesh.loops.foreach_set("vertex_index", faces)
1862-
mesh.polygons.add(num_loops)
1863-
mesh.polygons.foreach_set("loop_start", loop_start)
1864-
mesh.polygons.foreach_set("loop_total", loop_total)
1865-
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
1851+
mesh = tool.Loader.create_mesh_from_shape(geometry, mesh)
18661852
mesh.polygons.foreach_set("material_index", material_index)
18671853
mesh.update()
18681854

@@ -1890,29 +1876,13 @@ def create_object(
18901876
num_vertices = len(verts) // 3
18911877
if not num_vertices:
18921878
return
1893-
total_faces = len(faces)
1894-
loop_start = range(0, total_faces, 3)
1895-
num_loops = total_faces // 3
1896-
loop_total = [3] * num_loops
1897-
num_vertex_indices = len(faces)
18981879

1899-
mesh = bpy.data.meshes.new("Mesh")
1880+
mesh = tool.Loader.create_mesh_from_shape(verts=verts.reshape(-1, 3), faces=faces.reshape(-1, 3))
19001881

19011882
for material in materials:
19021883
mesh.materials.append(material)
1903-
1904-
mesh.vertices.add(num_vertices)
1905-
mesh.vertices.foreach_set("co", verts)
1906-
mesh.loops.add(num_vertex_indices)
1907-
mesh.loops.foreach_set("vertex_index", faces)
1908-
mesh.polygons.add(num_loops)
1909-
mesh.polygons.foreach_set("loop_start", loop_start)
1910-
mesh.polygons.foreach_set("loop_total", loop_total)
1911-
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
1912-
19131884
if material_ids.size > 0 and len(mesh.polygons) == len(material_ids):
19141885
mesh.polygons.foreach_set("material_index", material_ids)
1915-
19161886
mesh.update()
19171887

19181888
obj = bpy.data.objects.new("Chunk", mesh)

src/bonsai/bonsai/bim/module/qto/calculator.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,27 +1188,7 @@ def create_mesh_from_shape(
11881188
settings.set("keep-bounding-boxes", True)
11891189
shape = ifcopenshell.geom.create_shape(settings, element)
11901190
geometry = shape.geometry if element.is_a("IfcRoot") else shape
1191-
faces = geometry.faces
1192-
verts = geometry.verts
1193-
1194-
mesh = bpy.data.meshes.new("myBeautifulMesh")
1195-
1196-
num_vertices = len(verts) // 3
1197-
total_faces = len(faces)
1198-
loop_start = range(0, total_faces, 3)
1199-
num_loops = total_faces // 3
1200-
loop_total = [3] * num_loops
1201-
num_vertex_indices = len(faces)
1202-
1203-
mesh.vertices.add(num_vertices)
1204-
mesh.vertices.foreach_set("co", verts)
1205-
mesh.loops.add(num_vertex_indices)
1206-
mesh.loops.foreach_set("vertex_index", faces)
1207-
mesh.polygons.add(num_loops)
1208-
mesh.polygons.foreach_set("loop_start", loop_start)
1209-
mesh.polygons.foreach_set("loop_total", loop_total)
1210-
mesh.update()
1211-
return mesh
1191+
return tool.Loader.create_mesh_from_shape(geometry)
12121192

12131193

12141194
def get_bmesh_from_mesh(mesh: bpy.types.Mesh) -> bmesh.types.BMesh:

src/bonsai/bonsai/tool/loader.py

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -972,10 +972,7 @@ def convert_geometry_to_mesh(
972972
if verts is None:
973973
verts = ifcopenshell.util.shape.get_vertices(geometry)
974974
faces = ifcopenshell.util.shape.get_faces(geometry)
975-
total_faces: int
976-
if total_faces := faces.shape[0]:
977-
num_vertices: int = verts.shape[0]
978-
975+
if faces.shape[0] > 0:
979976
# See bug 3546
980977
# ios_edges holds true edges that aren't triangulated.
981978
#
@@ -984,38 +981,7 @@ def convert_geometry_to_mesh(
984981
ios_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(geometry).tolist()
985982
mesh["ios_item_ids"] = ios_item_ids
986983

987-
mesh.vertices.add(num_vertices)
988-
mesh.vertices.foreach_set("co", verts.ravel().astype("f"))
989-
990-
is_triangulated = True
991-
num_vertex_indices = faces.size
992-
if is_triangulated:
993-
loop_start = np.arange(0, num_vertex_indices, 3, dtype="I")
994-
loop_total = np.full(total_faces, 3, dtype="I")
995-
use_smooth = np.zeros(num_vertex_indices, dtype="?")
996-
997-
mesh.loops.add(num_vertex_indices)
998-
mesh.loops.foreach_set("vertex_index", faces.ravel().astype("I"))
999-
mesh.polygons.add(total_faces)
1000-
mesh.polygons.foreach_set("loop_start", loop_start)
1001-
mesh.polygons.foreach_set("loop_total", loop_total)
1002-
mesh.polygons.foreach_set("use_smooth", use_smooth)
1003-
else:
1004-
# TODO: optimize using correct numpy array types.
1005-
faces_array = np.array(geometry.faces, dtype=object)
1006-
loop_total = np.array(tuple(len(face) for face in faces_array), dtype="I")
1007-
loop_start = np.cumsum((0,) + loop_total)[:-1]
1008-
vertex_index = np.concatenate(faces_array)
1009-
use_smooth = np.zeros(num_vertex_indices, dtype="?")
1010-
1011-
mesh.loops.add(len(vertex_index))
1012-
mesh.loops.foreach_set("vertex_index", vertex_index)
1013-
mesh.polygons.add(len(loop_start))
1014-
mesh.polygons.foreach_set("loop_start", loop_start)
1015-
mesh.polygons.foreach_set("loop_total", loop_total)
1016-
mesh.polygons.foreach_set("use_smooth", use_smooth)
1017-
1018-
mesh.update()
984+
mesh = tool.Loader.create_mesh_from_shape(mesh=mesh, verts=verts, faces=faces)
1019985

1020986
rep_str: str = geometry.id
1021987
if load_indexed_maps and "openings" not in rep_str:
@@ -1043,6 +1009,72 @@ def convert_geometry_to_mesh(
10431009
mesh["ios_material_ids"] = ifcopenshell.util.shape.get_faces_material_style_ids(geometry).tolist()
10441010
return mesh
10451011

1012+
@classmethod
1013+
def create_mesh_from_shape(
1014+
cls,
1015+
geometry: Optional[ifcopenshell.geom.ShapeType] = None,
1016+
mesh: Optional[bpy.types.Mesh] = None,
1017+
*,
1018+
verts: Optional[npt.NDArray[np.float64]] = None,
1019+
faces: Optional[npt.NDArray[np.int32]] = None,
1020+
) -> bpy.types.Mesh:
1021+
"""
1022+
Either geometry or verts+faces should be provided.
1023+
1024+
:param verts: Numpy array of shape (n, 3).
1025+
:param faces: Numpy array of shape (m, 3).
1026+
"""
1027+
assert geometry is not None or (verts is not None and faces is not None), (
1028+
"Either geometry or verts+faces should be provided.\n"
1029+
f"Current geometry: {geometry}\n"
1030+
f"Current verts: {verts}\n"
1031+
f"Current faces: {faces}"
1032+
)
1033+
1034+
if mesh is None:
1035+
mesh = bpy.data.meshes.new("temp")
1036+
1037+
if verts is None or faces is None:
1038+
verts = ifcopenshell.util.shape.get_vertices(geometry)
1039+
faces = ifcopenshell.util.shape.get_faces(geometry)
1040+
1041+
total_faces: int = faces.shape[0]
1042+
num_vertices: int = verts.shape[0]
1043+
mesh.vertices.add(num_vertices)
1044+
mesh.vertices.foreach_set("co", verts.ravel().astype("f"))
1045+
1046+
is_triangulated = True
1047+
num_vertex_indices = faces.size
1048+
if is_triangulated:
1049+
loop_start = np.arange(0, num_vertex_indices, 3, dtype="I")
1050+
loop_total = np.full(total_faces, 3, dtype="I")
1051+
use_smooth = np.zeros(num_vertex_indices, dtype="?")
1052+
1053+
mesh.loops.add(num_vertex_indices)
1054+
mesh.loops.foreach_set("vertex_index", faces.ravel().astype("I"))
1055+
mesh.polygons.add(total_faces)
1056+
mesh.polygons.foreach_set("loop_start", loop_start)
1057+
mesh.polygons.foreach_set("loop_total", loop_total)
1058+
mesh.polygons.foreach_set("use_smooth", use_smooth)
1059+
else:
1060+
# TODO: optimize using correct numpy array types.
1061+
faces_array = np.array(geometry.faces, dtype=object)
1062+
loop_total = np.array(tuple(len(face) for face in faces_array), dtype="I")
1063+
loop_start = np.cumsum((0,) + loop_total)[:-1]
1064+
vertex_index = np.concatenate(faces_array)
1065+
use_smooth = np.zeros(num_vertex_indices, dtype="?")
1066+
1067+
mesh.loops.add(len(vertex_index))
1068+
mesh.loops.foreach_set("vertex_index", vertex_index)
1069+
mesh.polygons.add(len(loop_start))
1070+
mesh.polygons.foreach_set("loop_start", loop_start)
1071+
mesh.polygons.foreach_set("loop_total", loop_total)
1072+
mesh.polygons.foreach_set("use_smooth", use_smooth)
1073+
1074+
mesh.update()
1075+
1076+
return mesh
1077+
10461078
@classmethod
10471079
def setup_active_bsdd_classification(cls) -> None:
10481080
ifc_file = tool.Ifc.get()

src/bonsai/bonsai/tool/spatial.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -758,26 +758,7 @@ def get_gross_mesh_from_element(cls, visible_element: ifcopenshell.entity_instan
758758
@classmethod
759759
def create_mesh_from_shape(cls, shape: ifcopenshell.geom.ShapeElementType) -> bpy.types.Mesh:
760760
geometry = shape.geometry
761-
mesh = bpy.data.meshes.new("tmp")
762-
verts = geometry.verts
763-
if geometry.faces:
764-
num_vertices = len(verts) // 3
765-
total_faces = len(geometry.faces)
766-
loop_start = range(0, total_faces, 3)
767-
num_loops = total_faces // 3
768-
loop_total = [3] * num_loops
769-
num_vertex_indices = len(geometry.faces)
770-
771-
mesh.vertices.add(num_vertices)
772-
mesh.vertices.foreach_set("co", verts)
773-
mesh.loops.add(num_vertex_indices)
774-
mesh.loops.foreach_set("vertex_index", geometry.faces)
775-
mesh.polygons.add(num_loops)
776-
mesh.polygons.foreach_set("loop_start", loop_start)
777-
mesh.polygons.foreach_set("loop_total", loop_total)
778-
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
779-
mesh.update()
780-
return mesh
761+
return tool.Loader.create_mesh_from_shape(geometry)
781762

782763
@classmethod
783764
def get_x_y_z_h_mat_from_obj(cls, obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]:

0 commit comments

Comments
 (0)