mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
The BlenderBIM Add-on now supports loading IFCSQLite files!
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user