mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
simplify process of loading blender materials #4843
We no longer traverse through representations searching for IfcSurfaceStyles and just rely on geometry.materials. Possible after #4886 since: - create_shape is now considering inherited materials - material.instance_id is not returning IfcMaterial's style id instead of IfcMaterial id.
This commit is contained in:
@@ -59,103 +59,72 @@ class MaterialCreator:
|
||||
return
|
||||
if not self.mesh or self.mesh.name in self.parsed_meshes:
|
||||
return
|
||||
self.parsed_meshes.add(self.mesh.name)
|
||||
self.add_default_material(element)
|
||||
if self.parse_representations(element):
|
||||
self.assign_material_slots_to_faces()
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
|
||||
def add_default_material(self, element: ifcopenshell.entity_instance) -> None:
|
||||
element_material = ifcopenshell.util.element.get_material(element)
|
||||
if not element_material:
|
||||
return
|
||||
for material in [m for m in self.ifc_importer.file.traverse(element_material) if m.is_a("IfcMaterial")]:
|
||||
if not material.HasRepresentation:
|
||||
continue
|
||||
surface_style = [
|
||||
s for s in self.ifc_importer.file.traverse(material.HasRepresentation[0]) if s.is_a("IfcSurfaceStyle")
|
||||
]
|
||||
if surface_style:
|
||||
self.mesh.materials.append(self.styles[surface_style[0].id()])
|
||||
return
|
||||
# mesh["ios_materials"] can contain:
|
||||
# - ifc style id if style assigned to the representation items directly
|
||||
# or through material with a style;
|
||||
# - ifc material id if both true:
|
||||
# - element has a material without a style;
|
||||
# - there are parts of the geometry that has no other style assigned to them;
|
||||
# - -1 in case if there is no material;
|
||||
# - 0 in case if there are default materials used.
|
||||
# Though 0 value will not occur as we don't use default materials in IfcImporter.
|
||||
|
||||
self.parsed_meshes.add(self.mesh.name)
|
||||
self.load_texture_maps()
|
||||
self.assign_material_slots_to_faces()
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
|
||||
def load_existing_materials(self) -> None:
|
||||
for material in bpy.data.materials:
|
||||
if material.BIMMaterialProperties.ifc_style_id:
|
||||
self.styles[material.BIMMaterialProperties.ifc_style_id] = material
|
||||
|
||||
def parse_representations(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
"""Search for styles in in all `element`'s representation items
|
||||
and adds them to `self.mesh.materials`.\n
|
||||
Returns `True` if any styles were found and added, returns `False` otherwise."""
|
||||
has_parsed = False
|
||||
if hasattr(element, "Representation"):
|
||||
for representation in element.Representation.Representations:
|
||||
if self.parse_representation(representation):
|
||||
has_parsed = True
|
||||
elif hasattr(element, "RepresentationMaps"):
|
||||
for representation_map in element.RepresentationMaps:
|
||||
if not representation_map.MappedRepresentation:
|
||||
has_parsed = True # Accommodate invalid IFC data from Revit
|
||||
elif self.parse_representation(representation_map.MappedRepresentation):
|
||||
has_parsed = True
|
||||
return has_parsed
|
||||
|
||||
def parse_representation(self, representation: ifcopenshell.entity_instance) -> bool:
|
||||
has_parsed = False
|
||||
representation_items = self.resolve_all_stylable_representation_items(representation)
|
||||
for item in representation_items:
|
||||
if self.parse_representation_item(item):
|
||||
has_parsed = True
|
||||
return has_parsed
|
||||
|
||||
def parse_representation_item(self, item: ifcopenshell.entity_instance) -> bool:
|
||||
if not item.StyledByItem:
|
||||
return False
|
||||
style_ids = []
|
||||
styles = list(item.StyledByItem[0].Styles)
|
||||
while styles:
|
||||
style = styles.pop()
|
||||
if style.is_a("IfcSurfaceStyle"):
|
||||
style_ids.append(style.id())
|
||||
elif style.is_a("IfcPresentationStyleAssignment"):
|
||||
styles.extend(style.Styles)
|
||||
if not style_ids:
|
||||
return False
|
||||
for style_id in style_ids:
|
||||
material = self.styles[style_id]
|
||||
|
||||
def get_ifc_coordinate(material: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""returns IfcTextureCoordinate"""
|
||||
texture_style = tool.Style.get_texture_style(material)
|
||||
if not texture_style:
|
||||
def get_ifc_coordinate(self, material: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Get IfcTextureCoordinate"""
|
||||
texture_style = tool.Style.get_texture_style(material)
|
||||
if not texture_style:
|
||||
return
|
||||
for texture in texture_style.Textures or []:
|
||||
if coords := getattr(texture, "IsMappedBy", None):
|
||||
coords = coords[0]
|
||||
# IfcTextureCoordinateGenerator handled in the style shader graph
|
||||
if coords.is_a("IfcIndexedTextureMap"):
|
||||
return coords
|
||||
# TODO: support IfcTextureMap
|
||||
if coords.is_a("IfcTextureMap"):
|
||||
print(f"WARNING. IfcTextureMap texture coordinates is not supported.")
|
||||
return
|
||||
for texture in texture_style.Textures or []:
|
||||
if coords := getattr(texture, "IsMappedBy", None):
|
||||
coords = coords[0]
|
||||
# IfcTextureCoordinateGenerator handled in the style shader graph
|
||||
if coords.is_a("IfcIndexedTextureMap"):
|
||||
return coords
|
||||
# TODO: support IfcTextureMap
|
||||
if coords.is_a("IfcTextureMap"):
|
||||
print(f"WARNING. IfcTextureMap texture coordinates is not supported.")
|
||||
return
|
||||
|
||||
if coords := get_ifc_coordinate(material):
|
||||
def load_texture_maps(self) -> None:
|
||||
for style_or_material_id in self.mesh["ios_materials"]:
|
||||
if not (material := self.styles.get(style_or_material_id)):
|
||||
continue
|
||||
|
||||
material = self.styles[style_or_material_id]
|
||||
if coords := self.get_ifc_coordinate(material):
|
||||
tool.Loader.load_indexed_texture_map(coords, self.mesh)
|
||||
if self.mesh.materials.find(material.name) == -1:
|
||||
self.mesh.materials.append(material)
|
||||
return True
|
||||
|
||||
def assign_material_slots_to_faces(self) -> None:
|
||||
if "ios_materials" not in self.mesh or not self.mesh["ios_materials"]:
|
||||
return
|
||||
|
||||
# Cannot return here if len(ios_materials) == 1 because
|
||||
# even with 1 style it may be not assigned to the entire geometry
|
||||
# as some faces need another empty material slot.
|
||||
material_to_slot: dict[int, int] = {}
|
||||
ios_materials = self.mesh["ios_materials"]
|
||||
if len(ios_materials) == 1:
|
||||
style_or_material_id = ios_materials[0]
|
||||
# Has no styles / has just a material without a style.
|
||||
if not (material := self.styles.get(style_or_material_id)):
|
||||
return
|
||||
# Has a style and it's assigned to the entire geometry.
|
||||
# Otherwise we'll need to proceed as faces without styles
|
||||
# will require an empty material slot.
|
||||
if -1 not in self.mesh["ios_material_ids"]:
|
||||
self.mesh.materials.append(material)
|
||||
return
|
||||
|
||||
# Mapping of ios_materials indices
|
||||
# to blender material slots indices.
|
||||
material_to_slot: dict[int, int] = {}
|
||||
empty_slot_index = None
|
||||
|
||||
def get_empty_slot_index() -> int:
|
||||
@@ -167,17 +136,14 @@ class MaterialCreator:
|
||||
|
||||
# TODO: When they are not equal?
|
||||
if len(self.mesh.polygons) == len(self.mesh["ios_material_ids"]):
|
||||
for i, style_or_material_id in enumerate(self.mesh["ios_materials"]):
|
||||
# ios_materials will contain not ifc style but ifc material id if both true:
|
||||
# - there are parts of the geometry that has no style assigned to them;
|
||||
# - element has a material without a style.
|
||||
# It can also contain -1 in case if there is no material.
|
||||
for i, style_or_material_id in enumerate(ios_materials):
|
||||
material_without_style = style_or_material_id not in self.styles
|
||||
if material_without_style:
|
||||
slot_index = get_empty_slot_index()
|
||||
else:
|
||||
blender_material = self.styles[style_or_material_id]
|
||||
slot_index = self.mesh.materials.find(blender_material.name)
|
||||
self.mesh.materials.append(blender_material)
|
||||
slot_index = len(self.mesh.materials) - 1
|
||||
material_to_slot[i] = slot_index
|
||||
|
||||
if -1 in self.mesh["ios_material_ids"]:
|
||||
|
||||
Reference in New Issue
Block a user