diff --git a/src/blenderbim/blenderbim/bim/handler.py b/src/blenderbim/blenderbim/bim/handler.py index 9fff5d4836..f478a80338 100644 --- a/src/blenderbim/blenderbim/bim/handler.py +++ b/src/blenderbim/blenderbim/bim/handler.py @@ -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 diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py index d0b19c6d1c..5b73dadd2a 100644 --- a/src/blenderbim/blenderbim/bim/helper.py +++ b/src/blenderbim/blenderbim/bim/helper.py @@ -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 diff --git a/src/blenderbim/blenderbim/bim/ifc.py b/src/blenderbim/blenderbim/bim/ifc.py index fa8cbfa8df..7e815a416f 100644 --- a/src/blenderbim/blenderbim/bim/ifc.py +++ b/src/blenderbim/blenderbim/bim/ifc.py @@ -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 diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 6d20f07313..733d2d4958 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -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 diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index ab6446af3f..53b17c739b 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -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)