import ifcopenshell import ifcopenshell.api import ifcopenshell.geom import multiprocessing class BlenderImporter: def __init__(self): self.file = ifcopenshell.open('/home/dion/untitled.ifc') self.cache_path = "cache.h5" self.should_use_cpu_multiprocessing = True self.deflection_tolerance = 0.001 self.angular_tolerance = 0.5 def execute(self): self.created_guids = set() self.settings = ifcopenshell.geom.settings() self.settings.set_deflection_tolerance(self.deflection_tolerance) self.settings.set_angular_tolerance(self.angular_tolerance) self.settings.set(self.settings.STRICT_TOLERANCE, True) self.settings_2d = ifcopenshell.geom.settings() self.settings_2d.set(self.settings_2d.INCLUDE_CURVES, True) self.settings_2d.set(self.settings.STRICT_TOLERANCE, True) self.process_element_filter() self.process_context_filter() self.create_elements(self.elements) def process_element_filter(self): # Blender users can typically customise a filtered set of elements to import. # For simplicity, let's select everything: self.elements = set(self.file.by_type("IfcProduct")) def process_context_filter(self): # Facetation is to accommodate broken Revit files # See https://forums.buildingsmart.org/t/suggestions-on-how-to-improve-clarity-of-representation-context-usage-in-documentation/3663/6?u=moult self.body_contexts = [ c.id() for c in self.file.by_type("IfcGeometricRepresentationSubContext") if c.ContextIdentifier in ["Body", "Facetation"] ] # Ideally, all representations should be in a subcontext, but some BIM programs don't do this correctly self.body_contexts.extend( [ c.id() for c in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False) if c.ContextType == "Model" ] ) if self.body_contexts: self.settings.set_context_ids(self.body_contexts) # Annotation is to accommodate broken Revit files # See https://github.com/Autodesk/revit-ifc/issues/187 self.plan_contexts = [ c.id() for c in self.file.by_type("IfcGeometricRepresentationContext") if c.ContextType in ["Plan", "Annotation"] ] if self.plan_contexts: self.settings_2d.set_context_ids(self.plan_contexts) def create_elements(self, elements): # Based on my experience in viewing BIM models, representations are prioritised as follows: # 1. 3D Body, 2. 2D Plans, 3. Point clouds, 4. No representation # If an element has a representation that doesn't follow 1, 2, or 3, it will not show by default. # The user can load them later if they want to view them. products = self.create_products(elements) elements -= products products = self.create_products(elements, is_curve=True) elements -= products # Creating point clouds and non geometric objects are specific to Blender so no code is shown. pass def create_products(self, products, is_curve=False): results = set() if not products: return results settings = self.settings_2d if is_curve else self.settings if self.should_use_cpu_multiprocessing: iterator = ifcopenshell.geom.iterator( settings, self.file, multiprocessing.cpu_count(), include=products ) else: iterator = ifcopenshell.geom.iterator(settings, self.file, include=products) cache = self.get_cache() if cache: iterator.set_cache(cache) valid_file = iterator.initialize() if not valid_file: return results total = 0 while True: total += 1 if total % 250 == 0: print(f"{total} elements processed") shape = iterator.get() if shape: product = self.file.by_id(shape.guid) if self.body_contexts: print("Created", product) self.created_guids.add(shape.guid) results.add(product) else: if shape.context not in ["Body", "Facetation"] and shape.guid in self.created_guids: # We only load a single context, and we prioritise the Body context. See #1290. pass else: print("Created", product) self.created_guids.add(shape.guid) results.add(product) if not iterator.next(): break print("Done creating geometry") return results def get_cache(self): cache_settings = ifcopenshell.geom.settings() cache_settings.set(cache_settings.STRICT_TOLERANCE, True) try: return ifcopenshell.geom.serializers.hdf5(self.cache_path, cache_settings) except: return BlenderImporter().execute()