I'm trying to export an IfcWindow constituted with several parts, which I've simplified to wooden frames and a glass.
It's a single object in Blender. i've assigned the Wood material to the frame, and the Glass material to the glass.
Up until there, easy peezey. After skimming through the docs and studying the example library that comes with the BlenderBIM addon I've elected using a IfcMaterialConstituentSet, adding two constituents linked to a Wood and a Glass material. I then create a Brep geometry representation for the object, and link everything together.
The Constituent Set seems to be correctly exported, along with my two materials, but I'm missing something somewhere to actually assign the correct faces to their corrsponding material. The Brep creation code in get_brep_representation
does seggregate the polygons by material index in items
so the information is already here and passed along with file.createIfcShapeRepresentation
, I just don't understand how to make the link between the geometry and the material in ifc :)
The docs state "NOTE See the "Material Use Definition" at the individual element to which an IfcMaterialConstituentSet may apply for a required or recommended definition of such keywords." but I've not seen this information explained anywhere...
When I roundtrip the file the occurence does have an IfcMaterialConstituentSet with my two materials
But the object is only assigned the Glass material
I'm using Blender but I guess this could be applied to any software or even plain geometry data.
Here's the code for anyone interested in lending a hand or giving any insight into my endeavour :p
Cheers :)
# This can be substituted for your own filepath
from pathlib import Path
import bpy
blend_path = Path(bpy.data.filepath)
blend_name = blend_path.stem
filepath = str(blend_path.with_name(blend_name + "_test.ifc"))
obj_blend = bpy.context.active_object
# Retrieve the object placement in the world
def edit_object_placement(file, product):
run(
"geometry.edit_object_placement",
file,
product=product,
matrix=obj_blend.matrix_world.copy(),
is_si=False,
)
# Transform the blender mesh data to Brep
def get_brep_representation(file, body):
import bpy
# Note : copy/pasted from https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py#L552-L575
matrix = obj_blend.matrix_world.copy()
depsgraph = bpy.context.evaluated_depsgraph_get()
obj_blender_evaluated = obj_blend.evaluated_get(depsgraph)
mesh_evaluated = obj_blender_evaluated.data
ifc_vertices = [file.createIfcCartesianPoint(v.co) for v in mesh_evaluated.vertices]
ifc_raw_items = [[]] * max(1, len(obj_blender_evaluated.material_slots))
for polygon in mesh_evaluated.polygons:
ifc_raw_items[polygon.material_index % max(1, len(obj_blender_evaluated.material_slots))].append(
file.createIfcFace(
[
file.createIfcFaceOuterBound(
file.createIfcPolyLoop([ifc_vertices[vertex] for vertex in polygon.vertices]),
True,
)
]
)
)
items = [file.createIfcFacetedBrep(file.createIfcClosedShell(i)) for i in ifc_raw_items if i]
# items is a list of list of faces
# the index of sub-lists corresponds to the index of the material in the material slots
# How do I link it to the MaterialConstituentSet ??
return file.createIfcShapeRepresentation(
body,
body.ContextIdentifier,
"Brep",
items,
)
from ifcopenshell.api import run
import ifcopenshell
file = run("project.create_file")
# Boilerplate
project = run("root.create_entity", file, ifc_class="IfcProject", name="My Project")
context = run("context.add_context", file, context_type="Model")
body = run(
"context.add_context",
file,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=context,
)
run("unit.assign_unit", file, length={"is_metric": True, "raw": "METERS"})
site = run("root.create_entity", file, ifc_class="IfcSite", name="My Site")
building = run("root.create_entity", file, ifc_class="IfcBuilding", name="Building A")
storey = run("root.create_entity", file, ifc_class="IfcBuildingStorey", name="Storey 0")
run("aggregate.assign_object", file, relating_object=project, product=site)
run("aggregate.assign_object", file, relating_object=site, product=building)
run("aggregate.assign_object", file, relating_object=building, product=storey)
# Create material constituent set with 2 materials : Glass and Wood
material_set = run(
"material.add_material_set", file, **{"name": "WindowSet", "set_type": "IfcMaterialConstituentSet"}
)
material_glass = run("material.add_material", file, name="Glass")
run("material.add_constituent", file, **{"constituent_set": material_set, "material": material_glass})
material_wood = run("material.add_material", file, name="Wood")
run("material.add_constituent", file, **{"constituent_set": material_set, "material": material_wood})
# Create a window occurrence, setup its representation
product = run(
"root.create_entity",
file,
ifc_class="IfcWindow",
predefined_type="WINDOW",
name="Window",
)
representation = get_brep_representation(file, body)
run(
"geometry.assign_representation",
file,
product=product,
representation=representation,
)
edit_object_placement(file, product)
run("spatial.assign_container", file, relating_structure=storey, product=product)
# Add the constituent set to the window
file.createIfcRelAssociatesMaterial(
ifcopenshell.guid.new(),
None,
None,
None,
[product],
material_set,
)
file.write(filepath)