From 2fe2c349fd8ef4e6fdda22f2f125ab8284c257d3 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 23 Feb 2023 22:17:34 +1100 Subject: [PATCH] Fix #2743. obj2ifc scripts updated to support multiple OBJ files. --- src/blenderbim/scripts/obj2ifc-meshlab.py | 68 ++++++++++++---------- src/blenderbim/scripts/obj2ifc.py | 69 ++++++++++++++--------- 2 files changed, 80 insertions(+), 57 deletions(-) diff --git a/src/blenderbim/scripts/obj2ifc-meshlab.py b/src/blenderbim/scripts/obj2ifc-meshlab.py index b41ebd3930..c0130fb68a 100644 --- a/src/blenderbim/scripts/obj2ifc-meshlab.py +++ b/src/blenderbim/scripts/obj2ifc-meshlab.py @@ -27,13 +27,20 @@ from pathlib import Path import numpy as np + class Obj2Ifc: - def __init__(self, path): - self.path = path + def __init__(self, paths, outfile): + self.paths = paths + self.outfile = outfile + self.basename = Path(outfile).stem def execute(self, version="IFC4"): - self.basename = Path(self.path).stem self.create_ifc_file(version) + for path in self.paths: + self.path = path + self._execute() + + def _execute(self): mesh_set = pymeshlab.MeshSet() mesh_set.load_new_mesh(self.path) @@ -45,25 +52,20 @@ class Obj2Ifc: faces = mesh.face_matrix() vertices = mesh.vertex_matrix() - ifc_faces = np.zeros([len(faces)], dtype=object) for i, face in enumerate(faces): - ifc_faces[i] = ( - self.file.createIfcFace( - [ - self.file.createIfcFaceOuterBound( - self.file.createIfcPolyLoop( - [ - self.file.createIfcCartesianPoint( - self.get_coordinates(vertices[index].tolist()) - ) - for index in face - ] - ), - True, - ) - ] - ) + ifc_faces[i] = self.file.createIfcFace( + [ + self.file.createIfcFaceOuterBound( + self.file.createIfcPolyLoop( + [ + self.file.createIfcCartesianPoint(self.get_coordinates(vertices[index].tolist())) + for index in face + ] + ), + True, + ) + ] ) representation = self.file.createIfcProductDefinitionShape( None, @@ -79,15 +81,14 @@ class Obj2Ifc: ) product = self.file.create_entity( "IfcBuildingElementProxy", - **{ - "GlobalId": ifcopenshell.guid.new(), - "Name": mesh.label() or self.basename, - "ObjectPlacement": self.placement, - "Representation": representation, - } + GlobalId=ifcopenshell.guid.new(), + Name=mesh.label() or self.basename, + Representation=representation, ) ifcopenshell.api.run("spatial.assign_container", self.file, product=product, relating_structure=self.storey) - self.file.write(self.path + ".ifc") + product.ObjectPlacement = self.placement + + self.file.write(self.outfile) def get_coordinates(self, coordinates): if self.format == "obj": @@ -108,7 +109,7 @@ class Obj2Ifc: ifcopenshell.api.owner.settings.get_application = lambda ifc: application project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject", name=self.basename) - lengthunit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", name="METRE") + lengthunit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT") ifcopenshell.api.run("unit.assign_unit", self.file, units=[lengthunit]) model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model") self.context = ifcopenshell.api.run( @@ -128,19 +129,24 @@ class Obj2Ifc: ifcopenshell.api.run("aggregate.assign_object", self.file, product=building, relating_object=site) ifcopenshell.api.run("aggregate.assign_object", self.file, product=self.storey, relating_object=building) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=site) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=building) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=self.storey) + self.origin = self.file.createIfcAxis2Placement3D( self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)), self.file.createIfcDirection((0.0, 0.0, 1.0)), self.file.createIfcDirection((1.0, 0.0, 0.0)), ) - self.placement = self.file.createIfcLocalPlacement(None, self.origin) + self.placement = self.file.createIfcLocalPlacement(self.storey.ObjectPlacement, self.origin) self.history = ifcopenshell.api.run("owner.create_owner_history", self.file) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Converts an OBJ to an IFC") - parser.add_argument("obj", type=str, help="The OBJ file") + parser.add_argument("obj", type=str, nargs="+", help="One or more OBJ files") + parser.add_argument("-o", "--output", type=str, help="The output file to save the patched IFC", default="out.ifc") args = parser.parse_args() - obj2ifc = Obj2Ifc(args.obj) + obj2ifc = Obj2Ifc(args.obj, args.output) obj2ifc.execute() diff --git a/src/blenderbim/scripts/obj2ifc.py b/src/blenderbim/scripts/obj2ifc.py index 0e7210d613..e26a83898a 100644 --- a/src/blenderbim/scripts/obj2ifc.py +++ b/src/blenderbim/scripts/obj2ifc.py @@ -27,30 +27,39 @@ from pathlib import Path import numpy as np + class Obj2Ifc: - def __init__(self, path): - self.path = path + def __init__(self, paths, outfile): + self.paths = paths + self.outfile = outfile + self.basename = Path(outfile).stem def execute(self, version="IFC4"): - self.basename = Path(self.path).stem self.create_ifc_file(version) + for path in self.paths: + self.path = path + self._execute() + + def _execute(self): + self.scene = pywavefront.Wavefront(self.path, create_materials=True, collect_faces=True) for mesh in self.scene.mesh_list: - + faces = mesh.faces ifc_faces = np.zeros([len(faces)], dtype=object) for i, face in enumerate(faces): - ifc_faces[i] = ( - self.file.createIfcFace( - [ - self.file.createIfcFaceOuterBound( - self.file.createIfcPolyLoop( - [self.file.createIfcCartesianPoint(self.scene.vertices[index]) for index in face] - ), - True, - ) - ] - ) + ifc_faces[i] = self.file.createIfcFace( + [ + self.file.createIfcFaceOuterBound( + self.file.createIfcPolyLoop( + [ + self.file.createIfcCartesianPoint(self.get_coordinates(self.scene.vertices[index])) + for index in face + ] + ), + True, + ) + ] ) representation = self.file.createIfcProductDefinitionShape( None, @@ -66,15 +75,18 @@ class Obj2Ifc: ) product = self.file.create_entity( "IfcBuildingElementProxy", - **{ - "GlobalId": ifcopenshell.guid.new(), - "Name": mesh.name or self.basename, - "ObjectPlacement": self.placement, - "Representation": representation, - } + GlobalId=ifcopenshell.guid.new(), + Name=mesh.name or self.basename, + Representation=representation, ) ifcopenshell.api.run("spatial.assign_container", self.file, product=product, relating_structure=self.storey) - self.file.write(self.path.replace(".obj", ".ifc")) + product.ObjectPlacement = self.placement + + self.file.write(self.outfile) + + def get_coordinates(self, coordinates): + # OBJ swaps Y and Z axis + return [coordinates[0], -coordinates[2], coordinates[1]] def create_ifc_file(self, version): self.file = ifcopenshell.api.run("project.create_file", version=version) @@ -90,7 +102,7 @@ class Obj2Ifc: ifcopenshell.api.owner.settings.get_application = lambda ifc: application project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject", name=self.basename) - lengthunit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", name="METRE") + lengthunit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT") ifcopenshell.api.run("unit.assign_unit", self.file, units=[lengthunit]) model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model") self.context = ifcopenshell.api.run( @@ -110,19 +122,24 @@ class Obj2Ifc: ifcopenshell.api.run("aggregate.assign_object", self.file, product=building, relating_object=site) ifcopenshell.api.run("aggregate.assign_object", self.file, product=self.storey, relating_object=building) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=site) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=building) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=self.storey) + self.origin = self.file.createIfcAxis2Placement3D( self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)), self.file.createIfcDirection((0.0, 0.0, 1.0)), self.file.createIfcDirection((1.0, 0.0, 0.0)), ) - self.placement = self.file.createIfcLocalPlacement(None, self.origin) + self.placement = self.file.createIfcLocalPlacement(self.storey.ObjectPlacement, self.origin) self.history = ifcopenshell.api.run("owner.create_owner_history", self.file) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Converts an OBJ to an IFC") - parser.add_argument("obj", type=str, help="The OBJ file") + parser.add_argument("obj", type=str, nargs="+", help="One or more OBJ files") + parser.add_argument("-o", "--output", type=str, help="The output file to save the patched IFC", default="out.ifc") args = parser.parse_args() - obj2ifc = Obj2Ifc(args.obj) + obj2ifc = Obj2Ifc(args.obj, args.output) obj2ifc.execute()