mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 04:44:37 +00:00
Implement multicore importer using IfcOpenShell iterator with Revit material hack
This commit is contained in:
@@ -5,6 +5,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import mathutils
|
import mathutils
|
||||||
|
import multiprocessing
|
||||||
from .helper import SIUnitHelper
|
from .helper import SIUnitHelper
|
||||||
|
|
||||||
cwd = os.path.dirname(os.path.realpath(__file__)) + os.path.sep
|
cwd = os.path.dirname(os.path.realpath(__file__)) + os.path.sep
|
||||||
@@ -17,9 +18,11 @@ class IfcSchema():
|
|||||||
ifc_schema = IfcSchema()
|
ifc_schema = IfcSchema()
|
||||||
|
|
||||||
class MaterialCreator():
|
class MaterialCreator():
|
||||||
def __init__(self):
|
def __init__(self, ifc_import_settings):
|
||||||
self.mesh = None
|
self.mesh = None
|
||||||
self.materials = {}
|
self.materials = {}
|
||||||
|
self.parsed_meshes = []
|
||||||
|
self.ifc_import_settings = ifc_import_settings
|
||||||
|
|
||||||
def create(self, element, object, mesh):
|
def create(self, element, object, mesh):
|
||||||
self.object = object
|
self.object = object
|
||||||
@@ -27,13 +30,37 @@ class MaterialCreator():
|
|||||||
if not element.Representation:
|
if not element.Representation:
|
||||||
return
|
return
|
||||||
for item in element.Representation.Representations[0].Items:
|
for item in element.Representation.Representations[0].Items:
|
||||||
|
if item.is_a('IfcMappedItem'):
|
||||||
|
item = item.MappingSource.MappedRepresentation.Items[0]
|
||||||
if item.StyledByItem:
|
if item.StyledByItem:
|
||||||
styled_item = item.StyledByItem[0]
|
styled_item = item.StyledByItem[0]
|
||||||
material_name = str(styled_item.Name if styled_item.Name else styled_item.id())
|
if styled_item.Name:
|
||||||
|
material_name = styled_item.Name
|
||||||
|
# This is for a bug in Revit where Revit exports this in IFC4
|
||||||
|
elif styled_item.Styles[0] \
|
||||||
|
and styled_item.Styles[0].is_a('IfcPresentationStyleAssignment') \
|
||||||
|
and styled_item.Styles[0].Styles[0].Name:
|
||||||
|
material_name = styled_item.Styles[0].Styles[0].Name
|
||||||
|
elif styled_item.Styles[0] \
|
||||||
|
and styled_item.Styles[0].is_a('IfcPresentationStyle') \
|
||||||
|
and styled_item.Styles[0].Name:
|
||||||
|
material_name = styled_item.Styles[0].Name
|
||||||
|
else:
|
||||||
|
material_name = str(styled_item.id())
|
||||||
if material_name not in self.materials:
|
if material_name not in self.materials:
|
||||||
self.materials[material_name] = bpy.data.materials.new(material_name)
|
self.materials[material_name] = bpy.data.materials.new(material_name)
|
||||||
self.parse_styled_item(item.StyledByItem[0], self.materials[material_name])
|
self.parse_styled_item(item.StyledByItem[0], self.materials[material_name])
|
||||||
self.assign_material_to_mesh(self.materials[material_name], is_styled_item=True)
|
if self.ifc_import_settings.should_treat_styled_item_as_material:
|
||||||
|
# Revit workaround: since Revit exports all material
|
||||||
|
# assignments as individual object styled items. Treating
|
||||||
|
# them as reusable materials makes things much more
|
||||||
|
# efficient in Blender.
|
||||||
|
if self.mesh.name not in self.parsed_meshes:
|
||||||
|
self.parsed_meshes.append(self.mesh.name)
|
||||||
|
self.assign_material_to_mesh(self.materials[material_name])
|
||||||
|
else:
|
||||||
|
# Proper behaviour
|
||||||
|
self.assign_material_to_mesh(self.materials[material_name], is_styled_item=True)
|
||||||
return # styled items override material styles
|
return # styled items override material styles
|
||||||
for association in element.HasAssociations:
|
for association in element.HasAssociations:
|
||||||
if association.is_a('IfcRelAssociatesMaterial'):
|
if association.is_a('IfcRelAssociatesMaterial'):
|
||||||
@@ -66,7 +93,7 @@ class MaterialCreator():
|
|||||||
def parse_styled_item(self, styled_item, material):
|
def parse_styled_item(self, styled_item, material):
|
||||||
for style in styled_item.Styles:
|
for style in styled_item.Styles:
|
||||||
# Note IfcPresentationStyleAssignment is deprecated as of IFC4,
|
# Note IfcPresentationStyleAssignment is deprecated as of IFC4,
|
||||||
# but we still support it as it is widely used
|
# but we still support it as it is widely used, gee thanks Revit :(
|
||||||
if style.is_a('IfcPresentationStyleAssignment'):
|
if style.is_a('IfcPresentationStyleAssignment'):
|
||||||
style = style.Styles[0]
|
style = style.Styles[0]
|
||||||
if not style.is_a('IfcSurfaceStyle'):
|
if not style.is_a('IfcSurfaceStyle'):
|
||||||
@@ -112,7 +139,7 @@ class IfcImporter():
|
|||||||
self.time = 0
|
self.time = 0
|
||||||
self.unit_scale = 1
|
self.unit_scale = 1
|
||||||
|
|
||||||
self.material_creator = MaterialCreator()
|
self.material_creator = MaterialCreator(ifc_import_settings)
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.load_diff()
|
self.load_diff()
|
||||||
@@ -121,10 +148,80 @@ class IfcImporter():
|
|||||||
self.create_project()
|
self.create_project()
|
||||||
self.create_spatial_hierarchy()
|
self.create_spatial_hierarchy()
|
||||||
self.purge_diff()
|
self.purge_diff()
|
||||||
|
self.patch_ifc()
|
||||||
|
if self.ifc_import_settings.should_use_legacy:
|
||||||
|
# TODO: deprecate after we confirm stability of iterator based import
|
||||||
|
self.create_products_legacy()
|
||||||
|
else:
|
||||||
|
self.create_products()
|
||||||
|
|
||||||
|
def patch_ifc(self):
|
||||||
|
if self.ifc_import_settings.should_ignore_site_coordinates:
|
||||||
|
project = self.file.by_type('IfcProject')[0]
|
||||||
|
rel_aggregates = project.IsDecomposedBy
|
||||||
|
for rel_aggregate in rel_aggregates:
|
||||||
|
for site in rel_aggregate.RelatedObjects:
|
||||||
|
if not site.is_a('IfcSite'):
|
||||||
|
continue
|
||||||
|
site.ObjectPlacement.RelativePlacement.Location.Coordinates = (0., 0., 0.)
|
||||||
|
site.ObjectPlacement.RelativePlacement.Axis.DirectionRatios = (0., 0., 1.)
|
||||||
|
site.ObjectPlacement.RelativePlacement.RefDirection.DirectionRatios = (1., 0., 0.)
|
||||||
|
|
||||||
|
def create_products_legacy(self):
|
||||||
elements = self.file.by_type('IfcElement') + self.file.by_type('IfcSpace')
|
elements = self.file.by_type('IfcElement') + self.file.by_type('IfcSpace')
|
||||||
for element in elements:
|
for element in elements:
|
||||||
self.create_object(element)
|
self.create_object(element)
|
||||||
|
|
||||||
|
def create_products(self):
|
||||||
|
if self.ifc_import_settings.should_use_cpu_multiprocessing:
|
||||||
|
iterator = ifcopenshell.geom.iterator(self.settings, self.file, multiprocessing.cpu_count())
|
||||||
|
else:
|
||||||
|
iterator = ifcopenshell.geom.iterator(self.settings, self.file)
|
||||||
|
valid_file = iterator.initialize()
|
||||||
|
if not valid_file:
|
||||||
|
self.create_products_legacy() # Sometimes, this can still work, not 100% sure why yet
|
||||||
|
return False
|
||||||
|
while True:
|
||||||
|
self.create_product(iterator.get())
|
||||||
|
if not iterator.next():
|
||||||
|
break
|
||||||
|
|
||||||
|
def create_product(self, shape):
|
||||||
|
if shape is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
element = self.file.by_id(shape.guid)
|
||||||
|
|
||||||
|
if self.diff:
|
||||||
|
if element.GlobalId not in self.diff['added'] \
|
||||||
|
and element.GlobalId not in self.diff['changed'].keys():
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Creating object {}'.format(element))
|
||||||
|
|
||||||
|
# TODO: make names more meaningful
|
||||||
|
mesh_name = f'mesh-{shape.geometry.id}'
|
||||||
|
mesh = self.meshes.get(mesh_name)
|
||||||
|
if mesh is None:
|
||||||
|
mesh = self.create_mesh(element, shape)
|
||||||
|
self.meshes[mesh_name] = mesh
|
||||||
|
self.mesh_shapes[mesh_name] = shape
|
||||||
|
|
||||||
|
obj = bpy.data.objects.new(self.get_name(element), mesh)
|
||||||
|
|
||||||
|
m = shape.transformation.matrix.data
|
||||||
|
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]))
|
||||||
|
mat.transpose()
|
||||||
|
obj.matrix_world = mat
|
||||||
|
|
||||||
|
self.material_creator.create(element, obj, mesh)
|
||||||
|
self.add_element_attributes(element, obj)
|
||||||
|
self.add_element_document_relations(element, obj)
|
||||||
|
self.place_object_in_spatial_tree(element, obj)
|
||||||
|
|
||||||
def load_diff(self):
|
def load_diff(self):
|
||||||
if not self.ifc_import_settings.diff_file:
|
if not self.ifc_import_settings.diff_file:
|
||||||
return
|
return
|
||||||
@@ -181,6 +278,8 @@ class IfcImporter():
|
|||||||
return '{}/{}'.format(element.is_a(), element.Name)
|
return '{}/{}'.format(element.is_a(), element.Name)
|
||||||
|
|
||||||
def purge_diff(self):
|
def purge_diff(self):
|
||||||
|
if not self.diff:
|
||||||
|
return
|
||||||
objects_to_purge = []
|
objects_to_purge = []
|
||||||
for obj in bpy.data.objects:
|
for obj in bpy.data.objects:
|
||||||
if 'GlobalId' not in obj.BIMObjectProperties.attributes:
|
if 'GlobalId' not in obj.BIMObjectProperties.attributes:
|
||||||
@@ -370,4 +469,7 @@ class IfcImportSettings:
|
|||||||
self.input_file = None
|
self.input_file = None
|
||||||
self.should_ignore_site_coordinates = False
|
self.should_ignore_site_coordinates = False
|
||||||
self.should_import_curves = False
|
self.should_import_curves = False
|
||||||
|
self.should_treat_styled_item_as_material = False
|
||||||
|
self.should_use_cpu_multiprocessing = False
|
||||||
|
self.should_use_legacy = False
|
||||||
self.diff_file = None
|
self.diff_file = None
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ class ImportIFC(bpy.types.Operator, ImportHelper):
|
|||||||
ifc_import_settings.diff_file = bpy.context.scene.BIMProperties.diff_json_file
|
ifc_import_settings.diff_file = bpy.context.scene.BIMProperties.diff_json_file
|
||||||
ifc_import_settings.should_ignore_site_coordinates = bpy.context.scene.BIMProperties.import_should_ignore_site_coordinates
|
ifc_import_settings.should_ignore_site_coordinates = bpy.context.scene.BIMProperties.import_should_ignore_site_coordinates
|
||||||
ifc_import_settings.should_import_curves = bpy.context.scene.BIMProperties.import_should_import_curves
|
ifc_import_settings.should_import_curves = bpy.context.scene.BIMProperties.import_should_import_curves
|
||||||
|
ifc_import_settings.should_treat_styled_item_as_material = bpy.context.scene.BIMProperties.import_should_treat_styled_item_as_material
|
||||||
|
ifc_import_settings.should_use_cpu_multiprocessing = bpy.context.scene.BIMProperties.import_should_use_cpu_multiprocessing
|
||||||
|
ifc_import_settings.should_use_legacy = bpy.context.scene.BIMProperties.import_should_use_legacy
|
||||||
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||||
ifc_importer.execute()
|
ifc_importer.execute()
|
||||||
ifc_import_settings.logger.info('Import finished in {:.2f} seconds'.format(time.time() - start))
|
ifc_import_settings.logger.info('Import finished in {:.2f} seconds'.format(time.time() - start))
|
||||||
|
|||||||
@@ -211,6 +211,9 @@ class BIMProperties(PropertyGroup):
|
|||||||
export_should_use_presentation_style_assignment: BoolProperty(name="Export with Presentation Style Assignment", default=False)
|
export_should_use_presentation_style_assignment: BoolProperty(name="Export with Presentation Style Assignment", default=False)
|
||||||
import_should_ignore_site_coordinates: BoolProperty(name="Import Ignoring Site Coordinates", default=False)
|
import_should_ignore_site_coordinates: BoolProperty(name="Import Ignoring Site Coordinates", default=False)
|
||||||
import_should_import_curves: BoolProperty(name="Import Curves", default=False)
|
import_should_import_curves: BoolProperty(name="Import Curves", default=False)
|
||||||
|
import_should_treat_styled_item_as_material: BoolProperty(name="Import Treating Styled Item as Material", default=False)
|
||||||
|
import_should_use_legacy: BoolProperty(name="Import with Legacy Importer", default=False)
|
||||||
|
import_should_use_cpu_multiprocessing: BoolProperty(name="Import with CPU Multiprocessing", default=False)
|
||||||
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
||||||
pset_name: EnumProperty(items=getPsetNames, name="Pset Name", update=refreshPsetFiles)
|
pset_name: EnumProperty(items=getPsetNames, name="Pset Name", update=refreshPsetFiles)
|
||||||
pset_file: EnumProperty(items=getPsetFiles, name="Pset File")
|
pset_file: EnumProperty(items=getPsetFiles, name="Pset File")
|
||||||
|
|||||||
@@ -424,13 +424,23 @@ class BIM_PT_mvd(Panel):
|
|||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(bim_properties, "export_has_representations")
|
row.prop(bim_properties, "export_has_representations")
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(bim_properties, "import_should_import_curves")
|
||||||
|
|
||||||
|
layout.label(text="Experimental Modes:")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(bim_properties, "import_should_use_legacy")
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(bim_properties, "import_should_use_cpu_multiprocessing")
|
||||||
|
|
||||||
|
layout.label(text="Revit Workarounds:")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(bim_properties, "export_should_export_all_materials_as_styled_items")
|
row.prop(bim_properties, "export_should_export_all_materials_as_styled_items")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(bim_properties, "export_should_use_presentation_style_assignment")
|
row.prop(bim_properties, "export_should_use_presentation_style_assignment")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(bim_properties, "import_should_ignore_site_coordinates")
|
row.prop(bim_properties, "import_should_ignore_site_coordinates")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(bim_properties, "import_should_import_curves")
|
row.prop(bim_properties, "import_should_treat_styled_item_as_material")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user