Move common code to create_mesh_from_shape

This commit is contained in:
Andrej730
2025-02-12 18:07:58 +05:00
parent 299b5bcf6b
commit ba2456ad36
5 changed files with 76 additions and 124 deletions
+4 -15
View File
@@ -589,6 +589,7 @@ class IfcImporter:
obj.hide_select = True obj.hide_select = True
def create_generic_sqlite_elements(self, elements: set[ifcopenshell.entity_instance]) -> None: def create_generic_sqlite_elements(self, elements: set[ifcopenshell.entity_instance]) -> None:
assert isinstance(self.file, ifcopenshell.sql.sqlite)
self.geometry_cache = self.file.get_geometry([e.id() for e in elements]) self.geometry_cache = self.file.get_geometry([e.id() for e in elements])
for geometry_id, geometry in self.geometry_cache["geometry"].items(): for geometry_id, geometry in self.geometry_cache["geometry"].items():
mesh_name = tool.Loader.get_mesh_name_from_shape(type("Geometry", (), {"id": geometry_id})) mesh_name = tool.Loader.get_mesh_name_from_shape(type("Geometry", (), {"id": geometry_id}))
@@ -598,21 +599,9 @@ class IfcImporter:
mesh["has_cartesian_point_offset"] = False mesh["has_cartesian_point_offset"] = False
if geometry["faces"]: if geometry["faces"]:
num_vertices = len(verts) // 3 mesh = tool.Loader.create_mesh_from_shape(
total_faces = len(geometry["faces"]) mesh=mesh, faces=geometry["faces"].reshape(-1, 3), verts=verts.reshape(-1, 3)
loop_start = range(0, total_faces, 3) )
num_loops = total_faces // 3
loop_total = [3] * num_loops
num_vertex_indices = len(geometry["faces"])
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", verts)
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", geometry["faces"])
mesh.polygons.add(num_loops)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.update()
else: else:
e = geometry["edges"] e = geometry["edges"]
v = verts v = verts
@@ -1848,21 +1848,7 @@ class LoadLinkedProject(bpy.types.Operator):
material_index = np.array([(material_to_slot[i] if i != -1 else 0) for i in material_ids], dtype="I") material_index = np.array([(material_to_slot[i] if i != -1 else 0) for i in material_ids], dtype="I")
num_vertices = len(verts) // 3 mesh = tool.Loader.create_mesh_from_shape(geometry, mesh)
total_faces = len(faces)
loop_start = range(0, total_faces, 3)
num_loops = total_faces // 3
loop_total = [3] * num_loops
num_vertex_indices = len(faces)
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", verts)
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", faces)
mesh.polygons.add(num_loops)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
mesh.polygons.foreach_set("material_index", material_index) mesh.polygons.foreach_set("material_index", material_index)
mesh.update() mesh.update()
@@ -1890,29 +1876,13 @@ class LoadLinkedProject(bpy.types.Operator):
num_vertices = len(verts) // 3 num_vertices = len(verts) // 3
if not num_vertices: if not num_vertices:
return return
total_faces = len(faces)
loop_start = range(0, total_faces, 3)
num_loops = total_faces // 3
loop_total = [3] * num_loops
num_vertex_indices = len(faces)
mesh = bpy.data.meshes.new("Mesh") mesh = tool.Loader.create_mesh_from_shape(verts=verts.reshape(-1, 3), faces=faces.reshape(-1, 3))
for material in materials: for material in materials:
mesh.materials.append(material) mesh.materials.append(material)
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", verts)
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", faces)
mesh.polygons.add(num_loops)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
if material_ids.size > 0 and len(mesh.polygons) == len(material_ids): if material_ids.size > 0 and len(mesh.polygons) == len(material_ids):
mesh.polygons.foreach_set("material_index", material_ids) mesh.polygons.foreach_set("material_index", material_ids)
mesh.update() mesh.update()
obj = bpy.data.objects.new("Chunk", mesh) obj = bpy.data.objects.new("Chunk", mesh)
+1 -21
View File
@@ -1188,27 +1188,7 @@ def create_mesh_from_shape(
settings.set("keep-bounding-boxes", True) settings.set("keep-bounding-boxes", True)
shape = ifcopenshell.geom.create_shape(settings, element) shape = ifcopenshell.geom.create_shape(settings, element)
geometry = shape.geometry if element.is_a("IfcRoot") else shape geometry = shape.geometry if element.is_a("IfcRoot") else shape
faces = geometry.faces return tool.Loader.create_mesh_from_shape(geometry)
verts = geometry.verts
mesh = bpy.data.meshes.new("myBeautifulMesh")
num_vertices = len(verts) // 3
total_faces = len(faces)
loop_start = range(0, total_faces, 3)
num_loops = total_faces // 3
loop_total = [3] * num_loops
num_vertex_indices = len(faces)
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", verts)
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", faces)
mesh.polygons.add(num_loops)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.update()
return mesh
def get_bmesh_from_mesh(mesh: bpy.types.Mesh) -> bmesh.types.BMesh: def get_bmesh_from_mesh(mesh: bpy.types.Mesh) -> bmesh.types.BMesh:
+68 -36
View File
@@ -972,10 +972,7 @@ class Loader(bonsai.core.tool.Loader):
if verts is None: if verts is None:
verts = ifcopenshell.util.shape.get_vertices(geometry) verts = ifcopenshell.util.shape.get_vertices(geometry)
faces = ifcopenshell.util.shape.get_faces(geometry) faces = ifcopenshell.util.shape.get_faces(geometry)
total_faces: int if faces.shape[0] > 0:
if total_faces := faces.shape[0]:
num_vertices: int = verts.shape[0]
# See bug 3546 # See bug 3546
# ios_edges holds true edges that aren't triangulated. # ios_edges holds true edges that aren't triangulated.
# #
@@ -984,38 +981,7 @@ class Loader(bonsai.core.tool.Loader):
ios_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(geometry).tolist() ios_item_ids = ifcopenshell.util.shape.get_faces_representation_item_ids(geometry).tolist()
mesh["ios_item_ids"] = ios_item_ids mesh["ios_item_ids"] = ios_item_ids
mesh.vertices.add(num_vertices) mesh = tool.Loader.create_mesh_from_shape(mesh=mesh, verts=verts, faces=faces)
mesh.vertices.foreach_set("co", verts.ravel().astype("f"))
is_triangulated = True
num_vertex_indices = faces.size
if is_triangulated:
loop_start = np.arange(0, num_vertex_indices, 3, dtype="I")
loop_total = np.full(total_faces, 3, dtype="I")
use_smooth = np.zeros(num_vertex_indices, dtype="?")
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", faces.ravel().astype("I"))
mesh.polygons.add(total_faces)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.polygons.foreach_set("use_smooth", use_smooth)
else:
# TODO: optimize using correct numpy array types.
faces_array = np.array(geometry.faces, dtype=object)
loop_total = np.array(tuple(len(face) for face in faces_array), dtype="I")
loop_start = np.cumsum((0,) + loop_total)[:-1]
vertex_index = np.concatenate(faces_array)
use_smooth = np.zeros(num_vertex_indices, dtype="?")
mesh.loops.add(len(vertex_index))
mesh.loops.foreach_set("vertex_index", vertex_index)
mesh.polygons.add(len(loop_start))
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.polygons.foreach_set("use_smooth", use_smooth)
mesh.update()
rep_str: str = geometry.id rep_str: str = geometry.id
if load_indexed_maps and "openings" not in rep_str: if load_indexed_maps and "openings" not in rep_str:
@@ -1043,6 +1009,72 @@ class Loader(bonsai.core.tool.Loader):
mesh["ios_material_ids"] = ifcopenshell.util.shape.get_faces_material_style_ids(geometry).tolist() mesh["ios_material_ids"] = ifcopenshell.util.shape.get_faces_material_style_ids(geometry).tolist()
return mesh return mesh
@classmethod
def create_mesh_from_shape(
cls,
geometry: Optional[ifcopenshell.geom.ShapeType] = None,
mesh: Optional[bpy.types.Mesh] = None,
*,
verts: Optional[npt.NDArray[np.float64]] = None,
faces: Optional[npt.NDArray[np.int32]] = None,
) -> bpy.types.Mesh:
"""
Either geometry or verts+faces should be provided.
:param verts: Numpy array of shape (n, 3).
:param faces: Numpy array of shape (m, 3).
"""
assert geometry is not None or (verts is not None and faces is not None), (
"Either geometry or verts+faces should be provided.\n"
f"Current geometry: {geometry}\n"
f"Current verts: {verts}\n"
f"Current faces: {faces}"
)
if mesh is None:
mesh = bpy.data.meshes.new("temp")
if verts is None or faces is None:
verts = ifcopenshell.util.shape.get_vertices(geometry)
faces = ifcopenshell.util.shape.get_faces(geometry)
total_faces: int = faces.shape[0]
num_vertices: int = verts.shape[0]
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", verts.ravel().astype("f"))
is_triangulated = True
num_vertex_indices = faces.size
if is_triangulated:
loop_start = np.arange(0, num_vertex_indices, 3, dtype="I")
loop_total = np.full(total_faces, 3, dtype="I")
use_smooth = np.zeros(num_vertex_indices, dtype="?")
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", faces.ravel().astype("I"))
mesh.polygons.add(total_faces)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.polygons.foreach_set("use_smooth", use_smooth)
else:
# TODO: optimize using correct numpy array types.
faces_array = np.array(geometry.faces, dtype=object)
loop_total = np.array(tuple(len(face) for face in faces_array), dtype="I")
loop_start = np.cumsum((0,) + loop_total)[:-1]
vertex_index = np.concatenate(faces_array)
use_smooth = np.zeros(num_vertex_indices, dtype="?")
mesh.loops.add(len(vertex_index))
mesh.loops.foreach_set("vertex_index", vertex_index)
mesh.polygons.add(len(loop_start))
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.polygons.foreach_set("use_smooth", use_smooth)
mesh.update()
return mesh
@classmethod @classmethod
def setup_active_bsdd_classification(cls) -> None: def setup_active_bsdd_classification(cls) -> None:
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
+1 -20
View File
@@ -758,26 +758,7 @@ class Spatial(bonsai.core.tool.Spatial):
@classmethod @classmethod
def create_mesh_from_shape(cls, shape: ifcopenshell.geom.ShapeElementType) -> bpy.types.Mesh: def create_mesh_from_shape(cls, shape: ifcopenshell.geom.ShapeElementType) -> bpy.types.Mesh:
geometry = shape.geometry geometry = shape.geometry
mesh = bpy.data.meshes.new("tmp") return tool.Loader.create_mesh_from_shape(geometry)
verts = geometry.verts
if geometry.faces:
num_vertices = len(verts) // 3
total_faces = len(geometry.faces)
loop_start = range(0, total_faces, 3)
num_loops = total_faces // 3
loop_total = [3] * num_loops
num_vertex_indices = len(geometry.faces)
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", verts)
mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", geometry.faces)
mesh.polygons.add(num_loops)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)
mesh.polygons.foreach_set("use_smooth", [0] * total_faces)
mesh.update()
return mesh
@classmethod @classmethod
def get_x_y_z_h_mat_from_obj(cls, obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]: def get_x_y_z_h_mat_from_obj(cls, obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]: