mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 06:39:13 +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:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
|
||||||
|
tool.Ifc.get().clear_cache()
|
||||||
|
|
||||||
|
|
||||||
def purge_module_data():
|
def purge_module_data():
|
||||||
from blenderbim.bim import modules
|
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)
|
setattr(property_group, prop_name, prop_value)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO this should move into ifcopenshell.util
|
||||||
|
|
||||||
|
|
||||||
class IfcHeaderExtractor:
|
class IfcHeaderExtractor:
|
||||||
def __init__(self, filepath: str):
|
def __init__(self, filepath: str):
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
@@ -284,6 +287,8 @@ class IfcHeaderExtractor:
|
|||||||
return self.extract_ifc_spf(ifc_file)
|
return self.extract_ifc_spf(ifc_file)
|
||||||
elif extension.lower() == "ifczip":
|
elif extension.lower() == "ifczip":
|
||||||
return self.extract_ifc_zip()
|
return self.extract_ifc_zip()
|
||||||
|
elif extension.lower() == "ifcsqlite":
|
||||||
|
return {} # TODO
|
||||||
|
|
||||||
def extract_ifc_spf(self, ifc_file):
|
def extract_ifc_spf(self, ifc_file):
|
||||||
# https://www.steptools.com/stds/step/IS_final_p21e3.html#clause-8
|
# https://www.steptools.com/stds/step/IS_final_p21e3.html#clause-8
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ class IfcStore:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_file(path):
|
def load_file(path):
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
return
|
||||||
extension = path.split(".")[-1]
|
extension = path.split(".")[-1]
|
||||||
if extension.lower() == "ifczip":
|
if extension.lower() == "ifczip":
|
||||||
with tempfile.TemporaryDirectory() as unzipped_path:
|
with tempfile.TemporaryDirectory() as unzipped_path:
|
||||||
@@ -130,7 +132,7 @@ class IfcStore:
|
|||||||
return
|
return
|
||||||
elif extension.lower() == "ifcxml":
|
elif extension.lower() == "ifcxml":
|
||||||
IfcStore.file = ifcopenshell.file(ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(path))
|
IfcStore.file = ifcopenshell.file(ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(path))
|
||||||
elif extension.lower() == "ifc":
|
else:
|
||||||
IfcStore.file = ifcopenshell.open(path)
|
IfcStore.file = ifcopenshell.open(path)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -699,6 +699,9 @@ class IfcImporter:
|
|||||||
self.create_generic_elements(self.elements)
|
self.create_generic_elements(self.elements)
|
||||||
|
|
||||||
def 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:
|
# 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
|
# 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.
|
# 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))
|
print("{} / {} elements processed ...".format(i, total))
|
||||||
self.create_product(element)
|
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):
|
def create_products(self, products, settings=None):
|
||||||
if settings is None:
|
if settings is None:
|
||||||
settings = self.settings
|
settings = self.settings
|
||||||
@@ -1574,7 +1624,10 @@ class IfcImporter:
|
|||||||
return rel.RelatingGroup
|
return rel.RelatingGroup
|
||||||
|
|
||||||
def get_element_matrix(self, element):
|
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[0][3] *= self.unit_scale
|
||||||
result[1][3] *= self.unit_scale
|
result[1][3] *= self.unit_scale
|
||||||
result[2][3] *= self.unit_scale
|
result[2][3] *= self.unit_scale
|
||||||
@@ -1731,7 +1784,7 @@ class IfcImporter:
|
|||||||
mesh.polygons.add(num_loops)
|
mesh.polygons.add(num_loops)
|
||||||
mesh.polygons.foreach_set("loop_start", loop_start)
|
mesh.polygons.foreach_set("loop_start", loop_start)
|
||||||
mesh.polygons.foreach_set("loop_total", loop_total)
|
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()
|
mesh.update()
|
||||||
else:
|
else:
|
||||||
e = geometry.edges
|
e = geometry.edges
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Load an existing IFC project"
|
bl_description = "Load an existing IFC project"
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
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)
|
is_advanced: bpy.props.BoolProperty(name="Enable Advanced Mode", default=False)
|
||||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user