Skip to content

Commit ae2fd67

Browse files
committed
IfcOpenShell#2342 New Meshlab based OBJ2IFC converter
1 parent 9def041 commit ae2fd67

1 file changed

Lines changed: 139 additions & 0 deletions

File tree

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# BlenderBIM Add-on - OpenBIM Blender Add-on
2+
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
3+
#
4+
# This file is part of BlenderBIM Add-on.
5+
#
6+
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# BlenderBIM Add-on is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
18+
19+
20+
# This can be packaged with `pyinstaller --onefile --hidden-import numpy --collect-all ifcopenshell --clean obj2ifc.py`
21+
import argparse
22+
import pymeshlab
23+
import ifcopenshell
24+
import ifcopenshell.api
25+
import ifcopenshell.api.owner.settings
26+
from pathlib import Path
27+
28+
29+
class Obj2Ifc:
30+
def __init__(self, path):
31+
self.path = path
32+
33+
def execute(self):
34+
self.basename = Path(self.path).stem
35+
self.create_ifc_file()
36+
mesh_set = pymeshlab.MeshSet()
37+
mesh_set.load_new_mesh(self.path)
38+
39+
self.format = None
40+
if self.path.lower().endswith(".obj"):
41+
self.format = "obj"
42+
43+
for mesh in mesh_set:
44+
faces = mesh.face_matrix()
45+
vertices = mesh.vertex_matrix()
46+
47+
ifc_faces = []
48+
for face in faces:
49+
ifc_faces.append(
50+
self.file.createIfcFace(
51+
[
52+
self.file.createIfcFaceOuterBound(
53+
self.file.createIfcPolyLoop(
54+
[self.file.createIfcCartesianPoint(self.get_coordinates(vertices[index].tolist())) for index in face]
55+
),
56+
True,
57+
)
58+
]
59+
)
60+
)
61+
representation = self.file.createIfcProductDefinitionShape(
62+
None,
63+
None,
64+
[
65+
self.file.createIfcShapeRepresentation(
66+
self.context,
67+
"Body",
68+
"Brep",
69+
[self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(ifc_faces))],
70+
)
71+
],
72+
)
73+
product = self.file.create_entity(
74+
"IfcBuildingElementProxy",
75+
**{
76+
"GlobalId": ifcopenshell.guid.new(),
77+
"Name": mesh.label() or self.basename,
78+
"ObjectPlacement": self.placement,
79+
"Representation": representation,
80+
}
81+
)
82+
ifcopenshell.api.run("spatial.assign_container", self.file, product=product, relating_structure=self.storey)
83+
self.file.write(self.path + ".ifc")
84+
85+
def get_coordinates(self, coordinates):
86+
if self.format == "obj":
87+
# OBJ swaps Y and Z axis
88+
return [coordinates[0], -coordinates[2], coordinates[1]]
89+
90+
def create_ifc_file(self):
91+
self.file = ifcopenshell.api.run("project.create_file", version="IFC2X3")
92+
person = ifcopenshell.api.run("owner.add_person", self.file)
93+
person.Id = person.GivenName = None
94+
person.FamilyName = "user"
95+
org = ifcopenshell.api.run("owner.add_organisation", self.file)
96+
org.Id = None
97+
org.Name = "template"
98+
user = ifcopenshell.api.run("owner.add_person_and_organisation", self.file, person=person, organisation=org)
99+
application = ifcopenshell.api.run("owner.add_application", self.file)
100+
ifcopenshell.api.owner.settings.get_user = lambda ifc: user
101+
ifcopenshell.api.owner.settings.get_application = lambda ifc: application
102+
103+
project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject", name=self.basename)
104+
lengthunit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", name="METRE")
105+
ifcopenshell.api.run("unit.assign_unit", self.file, units=[lengthunit])
106+
model = ifcopenshell.api.run("context.add_context", self.file, context_type="Model")
107+
self.context = ifcopenshell.api.run(
108+
"context.add_context",
109+
self.file,
110+
context_type="Model",
111+
context_identifier="Body",
112+
target_view="MODEL_VIEW",
113+
parent=model,
114+
)
115+
site = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite", name="My Site")
116+
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding", name="My Building")
117+
self.storey = ifcopenshell.api.run(
118+
"root.create_entity", self.file, ifc_class="IfcBuildingStorey", name="My Storey"
119+
)
120+
ifcopenshell.api.run("aggregate.assign_object", self.file, product=site, relating_object=project)
121+
ifcopenshell.api.run("aggregate.assign_object", self.file, product=building, relating_object=site)
122+
ifcopenshell.api.run("aggregate.assign_object", self.file, product=self.storey, relating_object=building)
123+
124+
self.origin = self.file.createIfcAxis2Placement3D(
125+
self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)),
126+
self.file.createIfcDirection((0.0, 0.0, 1.0)),
127+
self.file.createIfcDirection((1.0, 0.0, 0.0)),
128+
)
129+
self.placement = self.file.createIfcLocalPlacement(None, self.origin)
130+
self.history = ifcopenshell.api.run("owner.create_owner_history", self.file)
131+
132+
133+
if __name__ == "__main__":
134+
parser = argparse.ArgumentParser(description="Converts an OBJ to an IFC")
135+
parser.add_argument("obj", type=str, help="The OBJ file")
136+
args = parser.parse_args()
137+
138+
obj2ifc = Obj2Ifc(args.obj)
139+
obj2ifc.execute()

0 commit comments

Comments
 (0)