Fix #2743. obj2ifc scripts updated to support multiple OBJ files.

This commit is contained in:
Dion Moult
2023-02-23 22:17:34 +11:00
parent d396d32a8f
commit 2fe2c349fd
2 changed files with 80 additions and 57 deletions
+37 -31
View File
@@ -27,13 +27,20 @@ from pathlib import Path
import numpy as np import numpy as np
class Obj2Ifc: class Obj2Ifc:
def __init__(self, path): def __init__(self, paths, outfile):
self.path = path self.paths = paths
self.outfile = outfile
self.basename = Path(outfile).stem
def execute(self, version="IFC4"): def execute(self, version="IFC4"):
self.basename = Path(self.path).stem
self.create_ifc_file(version) self.create_ifc_file(version)
for path in self.paths:
self.path = path
self._execute()
def _execute(self):
mesh_set = pymeshlab.MeshSet() mesh_set = pymeshlab.MeshSet()
mesh_set.load_new_mesh(self.path) mesh_set.load_new_mesh(self.path)
@@ -45,25 +52,20 @@ class Obj2Ifc:
faces = mesh.face_matrix() faces = mesh.face_matrix()
vertices = mesh.vertex_matrix() vertices = mesh.vertex_matrix()
ifc_faces = np.zeros([len(faces)], dtype=object) ifc_faces = np.zeros([len(faces)], dtype=object)
for i, face in enumerate(faces): for i, face in enumerate(faces):
ifc_faces[i] = ( ifc_faces[i] = self.file.createIfcFace(
self.file.createIfcFace( [
[ self.file.createIfcFaceOuterBound(
self.file.createIfcFaceOuterBound( self.file.createIfcPolyLoop(
self.file.createIfcPolyLoop( [
[ self.file.createIfcCartesianPoint(self.get_coordinates(vertices[index].tolist()))
self.file.createIfcCartesianPoint( for index in face
self.get_coordinates(vertices[index].tolist()) ]
) ),
for index in face True,
] )
), ]
True,
)
]
)
) )
representation = self.file.createIfcProductDefinitionShape( representation = self.file.createIfcProductDefinitionShape(
None, None,
@@ -79,15 +81,14 @@ class Obj2Ifc:
) )
product = self.file.create_entity( product = self.file.create_entity(
"IfcBuildingElementProxy", "IfcBuildingElementProxy",
**{ GlobalId=ifcopenshell.guid.new(),
"GlobalId": ifcopenshell.guid.new(), Name=mesh.label() or self.basename,
"Name": mesh.label() or self.basename, Representation=representation,
"ObjectPlacement": self.placement,
"Representation": representation,
}
) )
ifcopenshell.api.run("spatial.assign_container", self.file, product=product, relating_structure=self.storey) 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): def get_coordinates(self, coordinates):
if self.format == "obj": if self.format == "obj":
@@ -108,7 +109,7 @@ class Obj2Ifc:
ifcopenshell.api.owner.settings.get_application = lambda ifc: application ifcopenshell.api.owner.settings.get_application = lambda ifc: application
project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject", name=self.basename) 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]) ifcopenshell.api.run("unit.assign_unit", self.file, units=[lengthunit])
model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model") model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model")
self.context = ifcopenshell.api.run( 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=building, relating_object=site)
ifcopenshell.api.run("aggregate.assign_object", self.file, product=self.storey, relating_object=building) 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.origin = self.file.createIfcAxis2Placement3D(
self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)), self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)),
self.file.createIfcDirection((0.0, 0.0, 1.0)), self.file.createIfcDirection((0.0, 0.0, 1.0)),
self.file.createIfcDirection((1.0, 0.0, 0.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) self.history = ifcopenshell.api.run("owner.create_owner_history", self.file)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Converts an OBJ to an IFC") 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() args = parser.parse_args()
obj2ifc = Obj2Ifc(args.obj) obj2ifc = Obj2Ifc(args.obj, args.output)
obj2ifc.execute() obj2ifc.execute()
+43 -26
View File
@@ -27,30 +27,39 @@ from pathlib import Path
import numpy as np import numpy as np
class Obj2Ifc: class Obj2Ifc:
def __init__(self, path): def __init__(self, paths, outfile):
self.path = path self.paths = paths
self.outfile = outfile
self.basename = Path(outfile).stem
def execute(self, version="IFC4"): def execute(self, version="IFC4"):
self.basename = Path(self.path).stem
self.create_ifc_file(version) 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) self.scene = pywavefront.Wavefront(self.path, create_materials=True, collect_faces=True)
for mesh in self.scene.mesh_list: for mesh in self.scene.mesh_list:
faces = mesh.faces faces = mesh.faces
ifc_faces = np.zeros([len(faces)], dtype=object) ifc_faces = np.zeros([len(faces)], dtype=object)
for i, face in enumerate(faces): for i, face in enumerate(faces):
ifc_faces[i] = ( ifc_faces[i] = self.file.createIfcFace(
self.file.createIfcFace( [
[ self.file.createIfcFaceOuterBound(
self.file.createIfcFaceOuterBound( self.file.createIfcPolyLoop(
self.file.createIfcPolyLoop( [
[self.file.createIfcCartesianPoint(self.scene.vertices[index]) for index in face] self.file.createIfcCartesianPoint(self.get_coordinates(self.scene.vertices[index]))
), for index in face
True, ]
) ),
] True,
) )
]
) )
representation = self.file.createIfcProductDefinitionShape( representation = self.file.createIfcProductDefinitionShape(
None, None,
@@ -66,15 +75,18 @@ class Obj2Ifc:
) )
product = self.file.create_entity( product = self.file.create_entity(
"IfcBuildingElementProxy", "IfcBuildingElementProxy",
**{ GlobalId=ifcopenshell.guid.new(),
"GlobalId": ifcopenshell.guid.new(), Name=mesh.name or self.basename,
"Name": mesh.name or self.basename, Representation=representation,
"ObjectPlacement": self.placement,
"Representation": representation,
}
) )
ifcopenshell.api.run("spatial.assign_container", self.file, product=product, relating_structure=self.storey) 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): def create_ifc_file(self, version):
self.file = ifcopenshell.api.run("project.create_file", version=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 ifcopenshell.api.owner.settings.get_application = lambda ifc: application
project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject", name=self.basename) 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]) ifcopenshell.api.run("unit.assign_unit", self.file, units=[lengthunit])
model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model") model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model")
self.context = ifcopenshell.api.run( 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=building, relating_object=site)
ifcopenshell.api.run("aggregate.assign_object", self.file, product=self.storey, relating_object=building) 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.origin = self.file.createIfcAxis2Placement3D(
self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)), self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)),
self.file.createIfcDirection((0.0, 0.0, 1.0)), self.file.createIfcDirection((0.0, 0.0, 1.0)),
self.file.createIfcDirection((1.0, 0.0, 0.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) self.history = ifcopenshell.api.run("owner.create_owner_history", self.file)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Converts an OBJ to an IFC") 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() args = parser.parse_args()
obj2ifc = Obj2Ifc(args.obj) obj2ifc = Obj2Ifc(args.obj, args.output)
obj2ifc.execute() obj2ifc.execute()