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:
+60 -28
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,6 +981,65 @@ 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 = tool.Loader.create_mesh_from_shape(mesh=mesh, verts=verts, faces=faces)
rep_str: str = geometry.id
if load_indexed_maps and "openings" not in rep_str:
rep_id = rep_str.split("-", 1)[0]
rep = tool.Ifc.get().by_id(int(rep_id))
# For now, not necessary to load maps in Item mode
if rep.is_a("IfcShapeRepresentation"):
tool.Loader.load_indexed_colour_map(rep, mesh)
tool.Blender.Attribute.fill_attribute(mesh, "ios_item_ids", "FACE", "INT", ios_item_ids)
tool.Blender.Attribute.fill_attribute(mesh, "ios_material_ids", "FACE", "INT", geometry.material_ids)
else:
edges = ifcopenshell.util.shape.get_edges(geometry)
mesh.from_pydata(verts.tolist(), edges.tolist(), [])
# TODO: remove error handling after we update build in Bonsai.
try:
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(geometry).tolist()
except AttributeError:
edges_item_ids = []
mesh["ios_edges_item_ids"] = edges_item_ids
tool.Blender.Attribute.fill_attribute(mesh, "ios_edges_item_ids", "EDGE", "INT", edges_item_ids)
tool.Blender.Attribute.fill_attribute(mesh, "ios_material_ids", "EDGE", "INT", geometry.material_ids)
mesh["ios_materials"] = [m.instance_id() for m in ifcopenshell.util.shape.get_shape_material_styles(geometry)]
mesh["ios_material_ids"] = ifcopenshell.util.shape.get_faces_material_style_ids(geometry).tolist()
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.add(num_vertices)
mesh.vertices.foreach_set("co", verts.ravel().astype("f")) mesh.vertices.foreach_set("co", verts.ravel().astype("f"))
@@ -1017,30 +1073,6 @@ class Loader(bonsai.core.tool.Loader):
mesh.update() mesh.update()
rep_str: str = geometry.id
if load_indexed_maps and "openings" not in rep_str:
rep_id = rep_str.split("-", 1)[0]
rep = tool.Ifc.get().by_id(int(rep_id))
# For now, not necessary to load maps in Item mode
if rep.is_a("IfcShapeRepresentation"):
tool.Loader.load_indexed_colour_map(rep, mesh)
tool.Blender.Attribute.fill_attribute(mesh, "ios_item_ids", "FACE", "INT", ios_item_ids)
tool.Blender.Attribute.fill_attribute(mesh, "ios_material_ids", "FACE", "INT", geometry.material_ids)
else:
edges = ifcopenshell.util.shape.get_edges(geometry)
mesh.from_pydata(verts.tolist(), edges.tolist(), [])
# TODO: remove error handling after we update build in Bonsai.
try:
edges_item_ids = ifcopenshell.util.shape.get_edges_representation_item_ids(geometry).tolist()
except AttributeError:
edges_item_ids = []
mesh["ios_edges_item_ids"] = edges_item_ids
tool.Blender.Attribute.fill_attribute(mesh, "ios_edges_item_ids", "EDGE", "INT", edges_item_ids)
tool.Blender.Attribute.fill_attribute(mesh, "ios_material_ids", "EDGE", "INT", geometry.material_ids)
mesh["ios_materials"] = [m.instance_id() for m in ifcopenshell.util.shape.get_shape_material_styles(geometry)]
mesh["ios_material_ids"] = ifcopenshell.util.shape.get_faces_material_style_ids(geometry).tolist()
return mesh return mesh
@classmethod @classmethod
+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]: