diff --git a/src/ifcblender/io_import_scene_ifc/__init__.py b/src/ifcblender/io_import_scene_ifc/__init__.py index 2375d7bc6b..5310e829a5 100644 --- a/src/ifcblender/io_import_scene_ifc/__init__.py +++ b/src/ifcblender/io_import_scene_ifc/__init__.py @@ -41,10 +41,17 @@ if "bpy" in locals(): if "ifcopenshell" in locals(): importlib.reload(ifcopenshell) -import bpy -import mathutils -from bpy.props import StringProperty, IntProperty, BoolProperty +from bpy.props import ( + BoolProperty, + IntProperty, + StringProperty, +) from bpy_extras.io_utils import ImportHelper +from collections import defaultdict +import bpy +import logging +import mathutils +import os major, minor = bpy.app.version[0:2] transpose_matrices = minor >= 62 @@ -62,6 +69,25 @@ bpy.types.Object.ifc_type = StringProperty( name="IFC Entity Type", description="The STEP Datatype keyword") +def _get_parent(instance): + """This is based on ifcopenshell.app.geom""" + if instance.is_a("IfcOpeningElement"): + # We skip opening elements as they are nameless. + # We use this function to get usable collections. + return _get_parent(instance.VoidsElements[0].RelatingBuildingElement) + if instance.is_a("IfcElement"): + fills = instance.FillsVoids + if len(fills): + return fills[0].RelatingOpeningElement + containments = instance.ContainedInStructure + if len(containments): + return containments[0].RelatingStructure + if instance.is_a("IfcObjectDefinition"): + decompositions = instance.Decomposes + if len(decompositions): + return decompositions[0].RelatingObject + + def import_ifc(filename, use_names, process_relations, blender_booleans): from . import ifcopenshell @@ -69,22 +95,60 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): print(f"Reading {bpy.path.basename(filename)}...") settings = ifcopenshell_geom.settings() settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, blender_booleans) - iterator = ifcopenshell_geom.iterator(settings, filename) + assert os.path.exists(filename), filename + ifc_file = ifcopenshell.open(filename) + iterator = ifcopenshell_geom.iterator(settings, ifc_file) valid_file = iterator.initialize() if not valid_file: return False print("Done reading file") - id_to_object = {} + id_to_object = defaultdict(list) id_to_parent = {} id_to_matrix = {} openings = [] old_progress = -1 print("Creating geometry...") - collection = bpy.data.collections.new(f"{bpy.path.basename(filename)}") - bpy.context.scene.collection.children.link(collection) + root_collection = bpy.data.collections.new(f"{bpy.path.basename(filename)}") + bpy.context.scene.collection.children.link(root_collection) + + collections = { + 0: root_collection + } + def get_collection(cid): + if cid == 0: + return root_collection + + collection = collections.get(cid) + if collection is None: + try: + ifc_object = ifc_file.by_id(cid) + except Exception as exc: + logging.exception(exc) + ifc_object = None + + if ifc_object is not None: + # FIXME: I am really unsure if that is correct way to get parent object + ifc_parent_object = _get_parent(ifc_object) + parent_id = ifc_parent_object.id() if ifc_parent_object is not None else 0 + parent_collection = get_collection(parent_id) + name = ifc_object.Name or f'{ifc_object.is_a()}[{cid}]' + else: + parent_collection = get_collection(0) + name = f'unresolved_{cid}' + + collection = bpy.data.collections.new(name) + parent_collection.children.link(collection) + collections[cid] = collection + + + return collection + if process_relations: rel_collection = bpy.data.collections.new("Relations") collection.children.link(rel_collection) + + project_meshes = dict() + while True: ob = iterator.get() @@ -98,9 +162,9 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): # MESH CREATION # Depending on version, geometry.id will be either int or str mesh_name = 'mesh-%r' % ob.geometry.id - if mesh_name in bpy.data.meshes: - me = bpy.data.meshes[mesh_name] - else: + + me = project_meshes.get(mesh_name) + if me is None: verts = [[v[i], v[i + 1], v[i + 2]] for i in range(0, len(v), 3)] faces = [[f[i], f[i + 1], f[i + 2]] @@ -163,7 +227,8 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): id_to_matrix[ob.id] = mat else: bob.matrix_world = mat - collection.objects.link(bob) + + get_collection(ob.parent_id).objects.link(bob) bpy.context.view_layer.objects.active = bob bpy.ops.object.mode_set(mode='EDIT') @@ -178,8 +243,6 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): bob.hide_viewport = bob.hide_render = True bob.display_type = 'WIRE' - if ob.id not in id_to_object: - id_to_object[ob.id] = [] id_to_object[ob.id].append(bob) if ob.parent_id > 0: @@ -201,45 +264,43 @@ def import_ifc(filename, use_names, process_relations, blender_booleans): if process_relations: print("Processing relations...") + while len(id_to_parent_temp): + id, parent_id = id_to_parent_temp.popitem() - while len(id_to_parent_temp) and process_relations: - id, parent_id = id_to_parent_temp.popitem() - - if parent_id in id_to_object: - bob = id_to_object[parent_id][0] - else: - parent_ob = iterator.getObject(parent_id) - if parent_ob.id == -1: - bob = None + if parent_id in id_to_object: + bob = id_to_object[parent_id][0] else: - m = parent_ob.transformation.matrix.data - nm = parent_ob.name if len(parent_ob.name) and use_names \ - else parent_ob.guid - bob = bpy.data.objects.new(nm, None) + parent_ob = iterator.getObject(parent_id) + if parent_ob.id == -1: + bob = None + else: + m = parent_ob.transformation.matrix.data + nm = parent_ob.name if len(parent_ob.name) and use_names \ + else parent_ob.guid + bob = bpy.data.objects.new(nm, None) - mat = mathutils.Matrix(( - [m[0], m[1], m[2], 0], - [m[3], m[4], m[5], 0], - [m[6], m[7], m[8], 0], - [m[9], m[10], m[11], 1])) - if transpose_matrices: - mat.transpose() - id_to_matrix[parent_ob.id] = mat + mat = mathutils.Matrix(( + [m[0], m[1], m[2], 0], + [m[3], m[4], m[5], 0], + [m[6], m[7], m[8], 0], + [m[9], m[10], m[11], 1])) + if transpose_matrices: + mat.transpose() + id_to_matrix[parent_ob.id] = mat - rel_collection.objects.link(bob) + rel_collection.objects.link(bob) - bob.ifc_id = parent_ob.id - bob.ifc_name, bob.ifc_type, bob.ifc_guid = \ - parent_ob.name, parent_ob.type, parent_ob.guid + bob.ifc_id = parent_ob.id + bob.ifc_name, bob.ifc_type, bob.ifc_guid = \ + parent_ob.name, parent_ob.type, parent_ob.guid - if parent_ob.parent_id > 0: - id_to_parent[parent_id] = parent_ob.parent_id - id_to_parent_temp[parent_id] = parent_ob.parent_id - if parent_id not in id_to_object: id_to_object[parent_id] = [] - id_to_object[parent_id].append(bob) - if bob: - for ob in id_to_object[id]: - ob.parent = bob + if parent_ob.parent_id > 0: + id_to_parent[parent_id] = parent_ob.parent_id + id_to_parent_temp[parent_id] = parent_ob.parent_id + id_to_object[parent_id].append(bob) + if bob: + for ob in id_to_object[id]: + ob.parent = bob id_to_matrix_temp = dict(id_to_matrix)