The BlenderBIM Add-on now supports loading IFCSQLite files!

This commit is contained in:
Dion Moult
2023-06-26 17:21:01 +10:00
parent 10e96239a1
commit 41a176cb6d
5 changed files with 67 additions and 4 deletions
+3
View File
@@ -168,6 +168,9 @@ def refresh_ui_data():
except AttributeError:
pass
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
tool.Ifc.get().clear_cache()
def purge_module_data():
from blenderbim.bim import modules
+5
View File
@@ -273,6 +273,9 @@ def convert_property_group_from_si(property_group, skip_props=()):
setattr(property_group, prop_name, prop_value)
# TODO this should move into ifcopenshell.util
class IfcHeaderExtractor:
def __init__(self, filepath: str):
self.filepath = filepath
@@ -284,6 +287,8 @@ class IfcHeaderExtractor:
return self.extract_ifc_spf(ifc_file)
elif extension.lower() == "ifczip":
return self.extract_ifc_zip()
elif extension.lower() == "ifcsqlite":
return {} # TODO
def extract_ifc_spf(self, ifc_file):
# https://www.steptools.com/stds/step/IS_final_p21e3.html#clause-8
+3 -1
View File
@@ -120,6 +120,8 @@ class IfcStore:
@staticmethod
def load_file(path):
if not os.path.isfile(path):
return
extension = path.split(".")[-1]
if extension.lower() == "ifczip":
with tempfile.TemporaryDirectory() as unzipped_path:
@@ -130,7 +132,7 @@ class IfcStore:
return
elif extension.lower() == "ifcxml":
IfcStore.file = ifcopenshell.file(ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(path))
elif extension.lower() == "ifc":
else:
IfcStore.file = ifcopenshell.open(path)
@staticmethod
+55 -2
View File
@@ -699,6 +699,9 @@ class IfcImporter:
self.create_generic_elements(self.elements)
def create_generic_elements(self, elements):
if isinstance(self.file, ifcopenshell.sqlite):
return self.create_generic_sqlite_elements(elements)
# Based on my experience in viewing BIM models, representations are prioritised as follows:
# 1. 3D Body, 2. 2D Body, 3. 2D Plans / annotations, 4. Point clouds, 5. No representation
# If an element has a representation that doesn't follow 1, 2, 3, or 4, it will not show by default.
@@ -719,6 +722,53 @@ class IfcImporter:
print("{} / {} elements processed ...".format(i, total))
self.create_product(element)
def create_generic_sqlite_elements(self, elements):
self.geometry_cache = self.file.get_geometry([e.id() for e in elements])
for geometry_id, geometry in self.geometry_cache["geometry"].items():
mesh_name = tool.Loader.get_mesh_name(type("Geometry", (), {"id": geometry_id}))
mesh = bpy.data.meshes.new(mesh_name)
verts = geometry["verts"]
mesh["has_cartesian_point_offset"] = False
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.update()
else:
e = geometry["edges"]
v = verts
vertices = [[v[i], v[i + 1], v[i + 2]] for i in range(0, len(v), 3)]
edges = [[e[i], e[i + 1]] for i in range(0, len(e), 2)]
mesh.from_pydata(vertices, edges, [])
mesh["ios_materials"] = geometry["materials"]
mesh["ios_material_ids"] = geometry["material_ids"]
self.meshes[mesh_name] = mesh
total = len(elements)
for i, element in enumerate(elements):
if i % 250 == 0:
print("{} / {} elements processed ...".format(i, total))
mesh = None
geometry_id = self.geometry_cache["shapes"][element.id()]["geometry"]
if geometry_id:
mesh_name = tool.Loader.get_mesh_name(type("Geometry", (), {"id": geometry_id}))
mesh = self.meshes.get(mesh_name)
self.create_product(element, mesh=mesh)
def create_products(self, products, settings=None):
if settings is None:
settings = self.settings
@@ -1574,7 +1624,10 @@ class IfcImporter:
return rel.RelatingGroup
def get_element_matrix(self, element):
result = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
if isinstance(element, ifcopenshell.sqlite_entity):
result = self.geometry_cache["shapes"][element.id()]["matrix"]
else:
result = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
result[0][3] *= self.unit_scale
result[1][3] *= self.unit_scale
result[2][3] *= self.unit_scale
@@ -1731,7 +1784,7 @@ class IfcImporter:
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("use_smooth", [0] * total_faces)
mesh.update()
else:
e = geometry.edges
@@ -525,7 +525,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
bl_options = {"REGISTER", "UNDO"}
bl_description = "Load an existing IFC project"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
is_advanced: bpy.props.BoolProperty(name="Enable Advanced Mode", default=False)
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)