Skip to content
Closed
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# These are supported funding model platforms

github: [aothms]
162 changes: 112 additions & 50 deletions src/ifcblender/io_import_scene_ifc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@
if "ifcopenshell" in locals():
importlib.reload(ifcopenshell)

from bpy.props import (
BoolProperty,
IntProperty,
StringProperty,
)
from bpy_extras.io_utils import ImportHelper
from collections import defaultdict
import bpy
import logging
import mathutils
from bpy.props import StringProperty, IntProperty, BoolProperty
from bpy_extras.io_utils import ImportHelper
import os

major, minor = bpy.app.version[0:2]
transpose_matrices = minor >= 62
Expand All @@ -62,29 +69,85 @@
name="IFC Entity Type",
description="The STEP Datatype keyword")

def _get_parent(instance):
"""This is based on ifcopenshell.app.geom"""
if instance.is_a("IfcOpeningElement"):
# We skip opening elements as they are nameless.
# We use this function to get usable collections.
return _get_parent(instance.VoidsElements[0].RelatingBuildingElement)
if instance.is_a("IfcElement"):
fills = instance.FillsVoids
if len(fills):
return fills[0].RelatingOpeningElement
containments = instance.ContainedInStructure
if len(containments):
return containments[0].RelatingStructure
if instance.is_a("IfcObjectDefinition"):
decompositions = instance.Decomposes
if len(decompositions):
return decompositions[0].RelatingObject



def import_ifc(filename, use_names, process_relations, blender_booleans):
from . import ifcopenshell
from .ifcopenshell import geom as ifcopenshell_geom
print(f"Reading {bpy.path.basename(filename)}...")
settings = ifcopenshell_geom.settings()
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, blender_booleans)
iterator = ifcopenshell_geom.iterator(settings, filename)
assert os.path.exists(filename), filename
ifc_file = ifcopenshell.open(filename)
iterator = ifcopenshell_geom.iterator(settings, ifc_file)
valid_file = iterator.initialize()
if not valid_file:
return False
print("Done reading file")
id_to_object = {}
id_to_object = defaultdict(list)
id_to_parent = {}
id_to_matrix = {}
openings = []
old_progress = -1
print("Creating geometry...")
collection = bpy.data.collections.new(f"{bpy.path.basename(filename)}")
bpy.context.scene.collection.children.link(collection)
root_collection = bpy.data.collections.new(f"{bpy.path.basename(filename)}")
bpy.context.scene.collection.children.link(root_collection)

collections = {
0: root_collection
}
def get_collection(cid):
if cid == 0:
return root_collection

collection = collections.get(cid)
if collection is None:
try:
ifc_object = ifc_file.by_id(cid)
except Exception as exc:
logging.exception(exc)
ifc_object = None

if ifc_object is not None:
# FIXME: I am really unsure if that is correct way to get parent object
ifc_parent_object = _get_parent(ifc_object)
parent_id = ifc_parent_object.id() if ifc_parent_object is not None else 0
parent_collection = get_collection(parent_id)
name = ifc_object.Name or f'{ifc_object.is_a()}[{cid}]'
else:
parent_collection = get_collection(0)
name = f'unresolved_{cid}'

collection = bpy.data.collections.new(name)
parent_collection.children.link(collection)
collections[cid] = collection

return collection

if process_relations:
rel_collection = bpy.data.collections.new("Relations")
collection.children.link(rel_collection)

project_meshes = dict()

while True:
ob = iterator.get()

Expand All @@ -97,16 +160,18 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
nm = ob.name if len(ob.name) and use_names else ob.guid
# MESH CREATION
# Depending on version, geometry.id will be either int or str
mesh_name = 'mesh-%r' % ob.geometry.id
if mesh_name in bpy.data.meshes:
me = bpy.data.meshes[mesh_name]
else:
mesh_name = f'mesh-{ob.geometry.id}'

me = project_meshes.get(mesh_name)
if me is None:
verts = [[v[i], v[i + 1], v[i + 2]]
for i in range(0, len(v), 3)]
faces = [[f[i], f[i + 1], f[i + 2]]
for i in range(0, len(f), 3)]

me = bpy.data.meshes.new(mesh_name)
project_meshes[mesh_name] = me

me.from_pydata(verts, [], faces)
me.validate()
# MATERIAL CREATION
Expand Down Expand Up @@ -163,7 +228,8 @@ def add_material(mname, props):
id_to_matrix[ob.id] = mat
else:
bob.matrix_world = mat
collection.objects.link(bob)

get_collection(ob.parent_id).objects.link(bob)

bpy.context.view_layer.objects.active = bob
bpy.ops.object.mode_set(mode='EDIT')
Expand All @@ -178,8 +244,6 @@ def add_material(mname, props):
bob.hide_viewport = bob.hide_render = True
bob.display_type = 'WIRE'

if ob.id not in id_to_object:
id_to_object[ob.id] = []
id_to_object[ob.id].append(bob)

if ob.parent_id > 0:
Expand All @@ -201,45 +265,43 @@ def add_material(mname, props):

if process_relations:
print("Processing relations...")
while len(id_to_parent_temp):
id, parent_id = id_to_parent_temp.popitem()

while len(id_to_parent_temp) and process_relations:
id, parent_id = id_to_parent_temp.popitem()

if parent_id in id_to_object:
bob = id_to_object[parent_id][0]
else:
parent_ob = iterator.getObject(parent_id)
if parent_ob.id == -1:
bob = None
if parent_id in id_to_object:
bob = id_to_object[parent_id][0]
else:
m = parent_ob.transformation.matrix.data
nm = parent_ob.name if len(parent_ob.name) and use_names \
else parent_ob.guid
bob = bpy.data.objects.new(nm, None)

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]))
if transpose_matrices:
mat.transpose()
id_to_matrix[parent_ob.id] = mat

rel_collection.objects.link(bob)

bob.ifc_id = parent_ob.id
bob.ifc_name, bob.ifc_type, bob.ifc_guid = \
parent_ob.name, parent_ob.type, parent_ob.guid

if parent_ob.parent_id > 0:
id_to_parent[parent_id] = parent_ob.parent_id
id_to_parent_temp[parent_id] = parent_ob.parent_id
if parent_id not in id_to_object: id_to_object[parent_id] = []
id_to_object[parent_id].append(bob)
if bob:
for ob in id_to_object[id]:
ob.parent = bob
parent_ob = iterator.getObject(parent_id)
if parent_ob.id == -1:
bob = None
else:
m = parent_ob.transformation.matrix.data
nm = parent_ob.name if len(parent_ob.name) and use_names \
else parent_ob.guid
bob = bpy.data.objects.new(nm, None)

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]))
if transpose_matrices:
mat.transpose()
id_to_matrix[parent_ob.id] = mat

rel_collection.objects.link(bob)

bob.ifc_id = parent_ob.id
bob.ifc_name, bob.ifc_type, bob.ifc_guid = \
parent_ob.name, parent_ob.type, parent_ob.guid

if parent_ob.parent_id > 0:
id_to_parent[parent_id] = parent_ob.parent_id
id_to_parent_temp[parent_id] = parent_ob.parent_id
id_to_object[parent_id].append(bob)
if bob:
for ob in id_to_object[id]:
ob.parent = bob

id_to_matrix_temp = dict(id_to_matrix)

Expand Down
22 changes: 21 additions & 1 deletion src/ifcopenshell-python/ifcopenshell/geom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,25 @@
from __future__ import division
from __future__ import print_function

from . import occ_utils as utils
def _has_occ():
try:
import OCC.Core.BRepTools
return True
except ImportError:
pass

try:
import OCC.BRepTools
return True
except ImportError:
pass

return False


has_occ = _has_occ()

if has_occ:
from . import occ_utils as utils

from .main import *
23 changes: 8 additions & 15 deletions src/ifcopenshell-python/ifcopenshell/geom/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,7 @@
from ..file import file
from ..entity_instance import entity_instance


def has_occ():
try:
import OCC.BRepTools
except BaseException:
return False
return True


has_occ = has_occ()
from . import has_occ


def wrap_shape_creation(settings, shape):
Expand All @@ -47,6 +38,11 @@ def wrap_shape_creation(settings, shape):
if has_occ:
from . import occ_utils as utils

try:
from OCC.Core import TopoDS
except ImportError:
from OCC import TopoDS

def wrap_shape_creation(settings, shape): return utils.create_shape_from_serialization(shape) if getattr(settings,
'use_python_opencascade',
False) else shape
Expand Down Expand Up @@ -118,8 +114,7 @@ def unwrap(value):
if isinstance(value, entity_instance):
args.append(kwargs.get("completely_within", False))
elif has_occ:
import OCC.TopoDS
if isinstance(value, OCC.TopoDS.TopoDS_Shape):
if isinstance(value, TopoDS.TopoDS_Shape):
args[1] = utils.serialize_shape(value)
return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select(*args)]

Expand Down Expand Up @@ -183,10 +178,8 @@ def entity_instance_or_none(e):
return None if e is None else entity_instance(e)

if has_occ:
import OCC.TopoDS

def _(string_or_shape, *args):
if isinstance(string_or_shape, OCC.TopoDS.TopoDS_Shape):
if isinstance(string_or_shape, TopoDS.TopoDS_Shape):
string_or_shape = utils.serialize_shape(string_or_shape)
return entity_instance_or_none(fn(string_or_shape, *args))
else:
Expand Down
Loading