diff --git a/src/ifcblenderexport/blenderbim/bim/export_ifc.py b/src/ifcblenderexport/blenderbim/bim/export_ifc.py index 4bd658cfcf..af20ccd357 100644 --- a/src/ifcblenderexport/blenderbim/bim/export_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/export_ifc.py @@ -1608,13 +1608,18 @@ class IfcExporter(): placement_rel_to = None else: placement_rel_to = relating_object.ObjectPlacement + related_objects = [] for node in element_tree: element = self.ifc_parser.spatial_structure_elements[node['reference']] + + placement = self.file.createIfcLocalPlacement( + placement_rel_to, self.get_relative_placement(element, placement_rel_to)) + self.cast_attributes(element['class'], element['attributes']) element['attributes'].update({ 'OwnerHistory': self.owner_history, # TODO: unhardcode - 'ObjectPlacement': self.file.createIfcLocalPlacement(placement_rel_to, self.origin), + 'ObjectPlacement': placement, 'Representation': self.get_product_shape(element) }) element['ifc'] = self.file.create_entity(element['class'], **element['attributes']) @@ -1625,6 +1630,44 @@ class IfcExporter(): ifcopenshell.guid.new(), self.owner_history, None, None, relating_object, related_objects) + def get_relative_placement(self, element, placement_rel_to): + if placement_rel_to: + relating_object_matrix = self.get_local_placement(placement_rel_to) + else: + relating_object_matrix = Matrix() + z = Vector(element['up_axis']) + x = Vector(element['forward_axis']) + o = Vector(element['location']) + object_matrix = self.a2p(o, z, x) + relative_placement_matrix = relating_object_matrix.inverted() @ object_matrix + return self.create_ifc_axis_2_placement_3d( + relative_placement_matrix.translation, + self.get_axis(relative_placement_matrix, 2), + self.get_axis(relative_placement_matrix, 0)) + + def get_axis(self, matrix, axis): + return matrix.col[axis].to_3d().normalized() + + def get_local_placement(self, plc): + if plc.PlacementRelTo is None: + parent = Matrix() + else: + parent = self.get_local_placement(plc.PlacementRelTo) + return parent @ self.get_axis2placement(plc.RelativePlacement) + + def a2p(self, o, z, x): + y = z.cross(x) + r = Matrix((x, y, z, o)) + r.resize_4x4() + r.transpose() + return r + + def get_axis2placement(self, plc): + z = Vector(plc.Axis.DirectionRatios if plc.Axis else (0,0,1)) + x = Vector(plc.RefDirection.DirectionRatios if plc.RefDirection else (1,0,0)) + o = plc.Location.Coordinates + return self.a2p(o,z,x) + def create_groups(self): for group in self.ifc_parser.groups: group['ifc'] = self.file.create_entity(group['class'], **group['attributes']) @@ -1778,12 +1821,11 @@ class IfcExporter(): placement_rel_to = None if product['has_scale']: - placement = self.file.createIfcLocalPlacement(placement_rel_to, self.origin) + # Omission of the relative placement here is not as per implementer agreements + placement = self.file.createIfcLocalPlacement(None, self.origin) else: placement = self.file.createIfcLocalPlacement(placement_rel_to, - self.create_ifc_axis_2_placement_3d(product['location'], - product['up_axis'], - product['forward_axis'])) + self.get_relative_placement(product, placement_rel_to)) self.cast_attributes(product['class'], product['attributes']) diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index ca3e80a8a8..eab95da487 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -520,6 +520,8 @@ class IfcImporter(): mat.transpose() obj.matrix_world = mat self.material_creator.create(element, obj, mesh) + elif hasattr(element, 'ObjectPlacement'): + obj.matrix_world = self.get_element_matrix(element) self.add_element_attributes(element, obj) self.add_element_classifications(element, obj) @@ -1133,31 +1135,32 @@ class IfcImporter(): return element.Identification return self.get_referenced_source_name(element.ReferencedSource) - def get_element_matrix(self, element, mesh_name): + def get_element_matrix(self, element, mesh_name=None): element_matrix = self.get_local_placement(element.ObjectPlacement) - # Blender supports reusing a mesh with a different transformation - # applied at the object level. In contrast, IFC supports reusing a mesh - # with a different transformation applied at the mesh level _as well as_ - # the object level. For this reason, if the end-goal is to re-use mesh - # data, we must combine IFC's mesh-level transformation into Blender's - # object level transformation. + if mesh_name: + # Blender supports reusing a mesh with a different transformation + # applied at the object level. In contrast, IFC supports reusing a mesh + # with a different transformation applied at the mesh level _as well as_ + # the object level. For this reason, if the end-goal is to re-use mesh + # data, we must combine IFC's mesh-level transformation into Blender's + # object level transformation. - # The first step to do this is to _undo_ the mesh-level transformation - # from whatever shared mesh we are using, as it is not necessarily the - # same as the current mesh. - shared_shape_transformation = self.get_representation_cartesian_transformation( - self.file.by_id(self.mesh_shapes[mesh_name].product.id())) - if shared_shape_transformation: - shared_transform = self.get_cartesiantransformationoperator(shared_shape_transformation) - shared_transform.invert() - element_matrix = element_matrix @ shared_transform + # The first step to do this is to _undo_ the mesh-level transformation + # from whatever shared mesh we are using, as it is not necessarily the + # same as the current mesh. + shared_shape_transformation = self.get_representation_cartesian_transformation( + self.file.by_id(self.mesh_shapes[mesh_name].product.id())) + if shared_shape_transformation: + shared_transform = self.get_cartesiantransformationoperator(shared_shape_transformation) + shared_transform.invert() + element_matrix = element_matrix @ shared_transform - # The next step is to apply the current element's mesh level - # transformation to our current element's object transformation - transformation = self.get_representation_cartesian_transformation(element) - if transformation: - element_matrix = self.get_cartesiantransformationoperator(transformation) @ element_matrix + # The next step is to apply the current element's mesh level + # transformation to our current element's object transformation + transformation = self.get_representation_cartesian_transformation(element) + if transformation: + element_matrix = self.get_cartesiantransformationoperator(transformation) @ element_matrix element_matrix[0][3] *= self.unit_scale element_matrix[1][3] *= self.unit_scale