mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-10 06:00:51 +00:00
#2224 Coordination mode now allows interspersed (much faster) object merging during model loading to crunch down large models, with a new by type merge mode
This commit is contained in:
@@ -249,20 +249,14 @@ class IfcImporter:
|
|||||||
self.profile_code("Create type products")
|
self.profile_code("Create type products")
|
||||||
self.place_objects_in_collections()
|
self.place_objects_in_collections()
|
||||||
self.profile_code("Place objects in collections")
|
self.profile_code("Place objects in collections")
|
||||||
if self.ifc_import_settings.should_merge_by_class:
|
|
||||||
self.merge_by_class()
|
|
||||||
self.profile_code("Merging by class")
|
|
||||||
elif self.ifc_import_settings.should_merge_by_material:
|
|
||||||
self.merge_by_material()
|
|
||||||
self.profile_code("Merging by material")
|
|
||||||
if self.ifc_import_settings.should_merge_materials_by_colour or len(self.material_creator.materials) > 300:
|
|
||||||
self.merge_materials_by_colour()
|
|
||||||
self.profile_code("Merging by colour")
|
|
||||||
self.add_project_to_scene()
|
self.add_project_to_scene()
|
||||||
self.profile_code("Add project to scene")
|
self.profile_code("Add project to scene")
|
||||||
if self.ifc_import_settings.should_clean_mesh and len(self.file.by_type("IfcElement")) < 1000:
|
if self.ifc_import_settings.should_clean_mesh and len(self.file.by_type("IfcElement")) < 1000:
|
||||||
self.clean_mesh()
|
self.clean_mesh()
|
||||||
self.profile_code("Mesh cleaning")
|
self.profile_code("Mesh cleaning")
|
||||||
|
if self.ifc_import_settings.should_merge_materials_by_colour or len(self.material_creator.materials) > 300:
|
||||||
|
self.merge_materials_by_colour()
|
||||||
|
self.profile_code("Merging by colour")
|
||||||
self.set_default_context()
|
self.set_default_context()
|
||||||
self.profile_code("Setting default context")
|
self.profile_code("Setting default context")
|
||||||
self.update_progress(100)
|
self.update_progress(100)
|
||||||
@@ -632,8 +626,14 @@ class IfcImporter:
|
|||||||
for element in self.native_elements:
|
for element in self.native_elements:
|
||||||
progress += 1
|
progress += 1
|
||||||
if progress % 250 == 0:
|
if progress % 250 == 0:
|
||||||
print("{} / {} elements processed in {:.2f}s ...".format(progress, total, time.time() - checkpoint))
|
percent = round(progress / total * 100)
|
||||||
|
print(
|
||||||
|
"{} / {} ({}%) elements processed in {:.2f}s ...".format(
|
||||||
|
progress, total, percent, time.time() - checkpoint
|
||||||
|
)
|
||||||
|
)
|
||||||
checkpoint = time.time()
|
checkpoint = time.time()
|
||||||
|
self.incrementally_merge_objects()
|
||||||
native_data = self.native_data[element.GlobalId]
|
native_data = self.native_data[element.GlobalId]
|
||||||
representation = native_data["representation"]
|
representation = native_data["representation"]
|
||||||
if not representation:
|
if not representation:
|
||||||
@@ -702,13 +702,17 @@ class IfcImporter:
|
|||||||
while True:
|
while True:
|
||||||
progress += 1
|
progress += 1
|
||||||
if progress % 250 == 0:
|
if progress % 250 == 0:
|
||||||
|
percent_created = round(progress / total * 100)
|
||||||
|
percent_preprocessed = iterator.progress()
|
||||||
|
percent_average = (percent_created + percent_preprocessed) / 2
|
||||||
print(
|
print(
|
||||||
"{} / {} ({}% preprocessed) elements processed in {:.2f}s ...".format(
|
"{} / {} ({}% created, {}% preprocessed) elements processed in {:.2f}s ...".format(
|
||||||
progress, total, iterator.progress(), time.time() - checkpoint
|
progress, total, percent_created, percent_preprocessed, time.time() - checkpoint
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
checkpoint = time.time()
|
checkpoint = time.time()
|
||||||
self.update_progress((iterator.progress() / 100 * progress_range) + start_progress)
|
self.update_progress((percent_average / 100 * progress_range) + start_progress)
|
||||||
|
self.incrementally_merge_objects()
|
||||||
shape = iterator.get()
|
shape = iterator.get()
|
||||||
if shape:
|
if shape:
|
||||||
product = self.file.by_id(shape.guid)
|
product = self.file.by_id(shape.guid)
|
||||||
@@ -727,6 +731,19 @@ class IfcImporter:
|
|||||||
print("Done creating geometry")
|
print("Done creating geometry")
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
def incrementally_merge_objects(self):
|
||||||
|
if not self.ifc_import_settings.is_coordinating:
|
||||||
|
return
|
||||||
|
if self.ifc_import_settings.merge_mode == "IFC_CLASS":
|
||||||
|
self.merge_by_class()
|
||||||
|
self.profile_code("Merging by class")
|
||||||
|
elif self.ifc_import_settings.merge_mode == "IFC_TYPE":
|
||||||
|
self.merge_by_type()
|
||||||
|
self.profile_code("Merging by type")
|
||||||
|
elif self.ifc_import_settings.merge_mode == "MATERIAL":
|
||||||
|
self.merge_by_material()
|
||||||
|
self.profile_code("Merging by material")
|
||||||
|
|
||||||
def create_structural_items(self):
|
def create_structural_items(self):
|
||||||
# Create structural collections
|
# Create structural collections
|
||||||
self.structural_member_collection = bpy.data.collections.new("Members")
|
self.structural_member_collection = bpy.data.collections.new("Members")
|
||||||
@@ -1121,33 +1138,74 @@ class IfcImporter:
|
|||||||
|
|
||||||
def merge_by_class(self):
|
def merge_by_class(self):
|
||||||
merge_set = {}
|
merge_set = {}
|
||||||
for obj in self.added_data.values():
|
id_set = {}
|
||||||
if not isinstance(obj, bpy.types.Object):
|
for ifc_definition_id, obj in self.added_data.items():
|
||||||
|
if not isinstance(obj, bpy.types.Object) or not obj.data:
|
||||||
continue
|
continue
|
||||||
if "/" not in obj.name or "IfcRelAggregates" in obj.users_collection[0].name:
|
element = self.file.by_id(ifc_definition_id)
|
||||||
|
if not element.is_a("IfcElement"):
|
||||||
continue
|
continue
|
||||||
merge_set.setdefault(obj.name.split("/")[0], []).append(obj)
|
merge_set.setdefault(element.is_a(), []).append(obj)
|
||||||
self.merge_objects(merge_set)
|
id_set.setdefault(element.is_a(), []).append(ifc_definition_id)
|
||||||
|
self.merge_objects(merge_set, id_set)
|
||||||
|
|
||||||
|
def merge_by_type(self):
|
||||||
|
merge_set = {}
|
||||||
|
id_set = {}
|
||||||
|
for ifc_definition_id, obj in self.added_data.items():
|
||||||
|
if not isinstance(obj, bpy.types.Object) or not obj.data:
|
||||||
|
continue
|
||||||
|
element = self.file.by_id(ifc_definition_id)
|
||||||
|
if not element.is_a("IfcElement"):
|
||||||
|
continue
|
||||||
|
element_type = ifcopenshell.util.element.get_type(element)
|
||||||
|
if not element_type:
|
||||||
|
continue
|
||||||
|
merge_key = str(element_type.id()) + "-" + element_type.Name or "Unnamed"
|
||||||
|
merge_set.setdefault(merge_key, []).append(obj)
|
||||||
|
id_set.setdefault(merge_key, []).append(ifc_definition_id)
|
||||||
|
self.merge_objects(merge_set, id_set)
|
||||||
|
|
||||||
def merge_by_material(self):
|
def merge_by_material(self):
|
||||||
merge_set = {}
|
merge_set = {}
|
||||||
for obj in self.added_data.values():
|
id_set = {}
|
||||||
if not isinstance(obj, bpy.types.Object):
|
for ifc_definition_id, obj in self.added_data.items():
|
||||||
|
if not isinstance(obj, bpy.types.Object) or not obj.data:
|
||||||
continue
|
continue
|
||||||
if "/" not in obj.name or "IfcRelAggregates" in obj.users_collection[0].name:
|
element = self.file.by_id(ifc_definition_id)
|
||||||
|
if not element.is_a("IfcElement"):
|
||||||
continue
|
continue
|
||||||
if not obj.material_slots:
|
merge_key = obj.material_slots[0].name if obj.material_slots else "no-material"
|
||||||
merge_set.setdefault("no-material", []).append(obj)
|
merge_set.setdefault(merge_key, []).append(obj)
|
||||||
else:
|
id_set.setdefault(merge_key, []).append(ifc_definition_id)
|
||||||
merge_set.setdefault(obj.material_slots[0].name, []).append(obj)
|
self.merge_objects(merge_set, id_set)
|
||||||
self.merge_objects(merge_set)
|
|
||||||
|
|
||||||
def merge_objects(self, merge_set):
|
def merge_objects(self, merge_set, id_set):
|
||||||
for ifc_class, objs in merge_set.items():
|
total_objs = sum([len(o) for o in merge_set.values()])
|
||||||
context_override = {}
|
cumulative_total = 0
|
||||||
context_override["object"] = context_override["active_object"] = objs[0]
|
merge_set = {k: v for k, v in sorted(merge_set.items(), key=lambda i: len(i[1]), reverse=True)}
|
||||||
context_override["selected_objects"] = context_override["selected_editable_objects"] = objs
|
for group_name, objs in merge_set.items():
|
||||||
bpy.ops.object.join(context_override)
|
total_group_objs = len(objs)
|
||||||
|
if total_group_objs < 10:
|
||||||
|
continue
|
||||||
|
merge_potential = total_objs - cumulative_total
|
||||||
|
if merge_potential < 250:
|
||||||
|
print("Merge target achieved")
|
||||||
|
return
|
||||||
|
cumulative_total += total_group_objs
|
||||||
|
print(f"Merging {total_group_objs} objects, {merge_potential} potentially remaining -", group_name)
|
||||||
|
try:
|
||||||
|
target = objs[0]
|
||||||
|
target.data = target.data.copy()
|
||||||
|
context_override = {}
|
||||||
|
context_override["object"] = context_override["active_object"] = target
|
||||||
|
context_override["selected_objects"] = context_override["selected_editable_objects"] = objs
|
||||||
|
bpy.ops.object.join(context_override)
|
||||||
|
target.data.name += "-merge"
|
||||||
|
for ifc_definition_id in id_set[group_name][1:]:
|
||||||
|
del self.added_data[ifc_definition_id]
|
||||||
|
except:
|
||||||
|
print("Merge failed")
|
||||||
|
|
||||||
def merge_materials_by_colour(self):
|
def merge_materials_by_colour(self):
|
||||||
cleaned_materials = {}
|
cleaned_materials = {}
|
||||||
@@ -1926,8 +1984,7 @@ class IfcImportSettings:
|
|||||||
self.input_file = None
|
self.input_file = None
|
||||||
self.diff_file = None
|
self.diff_file = None
|
||||||
self.should_use_cpu_multiprocessing = True
|
self.should_use_cpu_multiprocessing = True
|
||||||
self.should_merge_by_class = False
|
self.merge_mode = None
|
||||||
self.should_merge_by_material = False
|
|
||||||
self.should_merge_materials_by_colour = False
|
self.should_merge_materials_by_colour = False
|
||||||
self.should_use_native_meshes = False
|
self.should_use_native_meshes = False
|
||||||
self.should_clean_mesh = True
|
self.should_clean_mesh = True
|
||||||
@@ -1956,8 +2013,7 @@ class IfcImportSettings:
|
|||||||
settings.diff_file = scene_diff.diff_json_file
|
settings.diff_file = scene_diff.diff_json_file
|
||||||
settings.collection_mode = props.collection_mode
|
settings.collection_mode = props.collection_mode
|
||||||
settings.should_use_cpu_multiprocessing = props.should_use_cpu_multiprocessing
|
settings.should_use_cpu_multiprocessing = props.should_use_cpu_multiprocessing
|
||||||
settings.should_merge_by_class = props.should_merge_by_class
|
settings.merge_mode = props.merge_mode
|
||||||
settings.should_merge_by_material = props.should_merge_by_material
|
|
||||||
settings.should_merge_materials_by_colour = props.should_merge_materials_by_colour
|
settings.should_merge_materials_by_colour = props.should_merge_materials_by_colour
|
||||||
settings.should_use_native_meshes = props.should_use_native_meshes
|
settings.should_use_native_meshes = props.should_use_native_meshes
|
||||||
settings.should_clean_mesh = props.should_clean_mesh
|
settings.should_clean_mesh = props.should_clean_mesh
|
||||||
|
|||||||
@@ -135,12 +135,20 @@ class BIMProjectProperties(PropertyGroup):
|
|||||||
active_filter_category_index: IntProperty(name="Active Filter Category Index")
|
active_filter_category_index: IntProperty(name="Active Filter Category Index")
|
||||||
filter_query: StringProperty(name="Filter Query")
|
filter_query: StringProperty(name="Filter Query")
|
||||||
should_filter_spatial_elements: BoolProperty(name="Filter Spatial Elements", default=False)
|
should_filter_spatial_elements: BoolProperty(name="Filter Spatial Elements", default=False)
|
||||||
should_use_cpu_multiprocessing: BoolProperty(name="Import with CPU Multiprocessing", default=True)
|
should_use_cpu_multiprocessing: BoolProperty(name="CPU Multiprocessing", default=True)
|
||||||
should_merge_by_class: BoolProperty(name="Import and Merge by Class", default=False)
|
merge_mode: bpy.props.EnumProperty(
|
||||||
should_merge_by_material: BoolProperty(name="Import and Merge by Material", default=False)
|
items=[
|
||||||
should_merge_materials_by_colour: BoolProperty(name="Import and Merge Materials by Colour", default=False)
|
("NONE", "None", "No objects are merged"),
|
||||||
should_clean_mesh: BoolProperty(name="Import and Clean Mesh", default=True)
|
("IFC_CLASS", "IFC Class", "One object per IFC class"),
|
||||||
|
("IFC_TYPE", "IFC Type", "One object per IFC construction type"),
|
||||||
|
("MATERIAL", "Material", "One object per material"),
|
||||||
|
],
|
||||||
|
name="Merge Mode",
|
||||||
|
default="NONE",
|
||||||
|
)
|
||||||
|
should_merge_materials_by_colour: BoolProperty(name="Merge Materials by Colour", default=False)
|
||||||
should_use_native_meshes: BoolProperty(name="Native Meshes", default=False)
|
should_use_native_meshes: BoolProperty(name="Native Meshes", default=False)
|
||||||
|
should_clean_mesh: BoolProperty(name="Clean Meshes", default=True)
|
||||||
should_cache: BoolProperty(name="Cache", default=False)
|
should_cache: BoolProperty(name="Cache", default=False)
|
||||||
is_coordinating: BoolProperty(name="For Coordination Only", default=False)
|
is_coordinating: BoolProperty(name="For Coordination Only", default=False)
|
||||||
deflection_tolerance: FloatProperty(name="Deflection Tolerance", default=0.001)
|
deflection_tolerance: FloatProperty(name="Deflection Tolerance", default=0.001)
|
||||||
|
|||||||
@@ -74,16 +74,18 @@ class BIM_PT_project(Panel):
|
|||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "should_use_cpu_multiprocessing")
|
row.prop(pprops, "should_use_cpu_multiprocessing")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "should_merge_by_class")
|
row.prop(pprops, "should_clean_mesh")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "should_cache")
|
row.prop(pprops, "should_cache")
|
||||||
row.prop(pprops, "should_merge_by_material")
|
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "should_use_native_meshes")
|
row.prop(pprops, "should_use_native_meshes")
|
||||||
|
row = self.layout.row()
|
||||||
row.prop(pprops, "should_merge_materials_by_colour")
|
row.prop(pprops, "should_merge_materials_by_colour")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "is_coordinating")
|
row.prop(pprops, "is_coordinating")
|
||||||
row.prop(pprops, "should_clean_mesh")
|
if pprops.is_coordinating:
|
||||||
|
row = self.layout.row()
|
||||||
|
row.prop(pprops, "merge_mode")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "deflection_tolerance")
|
row.prop(pprops, "deflection_tolerance")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
|
|||||||
Reference in New Issue
Block a user