mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 22:43:41 +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
|
return
|
||||||
if not self.mesh or self.mesh.name in self.parsed_meshes:
|
if not self.mesh or self.mesh.name in self.parsed_meshes:
|
||||||
return
|
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:
|
# mesh["ios_materials"] can contain:
|
||||||
element_material = ifcopenshell.util.element.get_material(element)
|
# - ifc style id if style assigned to the representation items directly
|
||||||
if not element_material:
|
# or through material with a style;
|
||||||
return
|
# - ifc material id if both true:
|
||||||
for material in [m for m in self.ifc_importer.file.traverse(element_material) if m.is_a("IfcMaterial")]:
|
# - element has a material without a style;
|
||||||
if not material.HasRepresentation:
|
# - there are parts of the geometry that has no other style assigned to them;
|
||||||
continue
|
# - -1 in case if there is no material;
|
||||||
surface_style = [
|
# - 0 in case if there are default materials used.
|
||||||
s for s in self.ifc_importer.file.traverse(material.HasRepresentation[0]) if s.is_a("IfcSurfaceStyle")
|
# Though 0 value will not occur as we don't use default materials in IfcImporter.
|
||||||
]
|
|
||||||
if surface_style:
|
self.parsed_meshes.add(self.mesh.name)
|
||||||
self.mesh.materials.append(self.styles[surface_style[0].id()])
|
self.load_texture_maps()
|
||||||
return
|
self.assign_material_slots_to_faces()
|
||||||
|
tool.Geometry.record_object_materials(obj)
|
||||||
|
|
||||||
def load_existing_materials(self) -> None:
|
def load_existing_materials(self) -> None:
|
||||||
for material in bpy.data.materials:
|
for material in bpy.data.materials:
|
||||||
if material.BIMMaterialProperties.ifc_style_id:
|
if material.BIMMaterialProperties.ifc_style_id:
|
||||||
self.styles[material.BIMMaterialProperties.ifc_style_id] = material
|
self.styles[material.BIMMaterialProperties.ifc_style_id] = material
|
||||||
|
|
||||||
def parse_representations(self, element: ifcopenshell.entity_instance) -> bool:
|
def get_ifc_coordinate(self, material: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
"""Search for styles in in all `element`'s representation items
|
"""Get IfcTextureCoordinate"""
|
||||||
and adds them to `self.mesh.materials`.\n
|
texture_style = tool.Style.get_texture_style(material)
|
||||||
Returns `True` if any styles were found and added, returns `False` otherwise."""
|
if not texture_style:
|
||||||
has_parsed = False
|
return
|
||||||
if hasattr(element, "Representation"):
|
for texture in texture_style.Textures or []:
|
||||||
for representation in element.Representation.Representations:
|
if coords := getattr(texture, "IsMappedBy", None):
|
||||||
if self.parse_representation(representation):
|
coords = coords[0]
|
||||||
has_parsed = True
|
# IfcTextureCoordinateGenerator handled in the style shader graph
|
||||||
elif hasattr(element, "RepresentationMaps"):
|
if coords.is_a("IfcIndexedTextureMap"):
|
||||||
for representation_map in element.RepresentationMaps:
|
return coords
|
||||||
if not representation_map.MappedRepresentation:
|
# TODO: support IfcTextureMap
|
||||||
has_parsed = True # Accommodate invalid IFC data from Revit
|
if coords.is_a("IfcTextureMap"):
|
||||||
elif self.parse_representation(representation_map.MappedRepresentation):
|
print(f"WARNING. IfcTextureMap texture coordinates is not supported.")
|
||||||
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:
|
|
||||||
return
|
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)
|
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:
|
def assign_material_slots_to_faces(self) -> None:
|
||||||
if "ios_materials" not in self.mesh or not self.mesh["ios_materials"]:
|
if "ios_materials" not in self.mesh or not self.mesh["ios_materials"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Cannot return here if len(ios_materials) == 1 because
|
ios_materials = self.mesh["ios_materials"]
|
||||||
# even with 1 style it may be not assigned to the entire geometry
|
if len(ios_materials) == 1:
|
||||||
# as some faces need another empty material slot.
|
style_or_material_id = ios_materials[0]
|
||||||
material_to_slot: dict[int, int] = {}
|
# 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
|
empty_slot_index = None
|
||||||
|
|
||||||
def get_empty_slot_index() -> int:
|
def get_empty_slot_index() -> int:
|
||||||
@@ -167,17 +136,14 @@ class MaterialCreator:
|
|||||||
|
|
||||||
# TODO: When they are not equal?
|
# TODO: When they are not equal?
|
||||||
if len(self.mesh.polygons) == len(self.mesh["ios_material_ids"]):
|
if len(self.mesh.polygons) == len(self.mesh["ios_material_ids"]):
|
||||||
for i, style_or_material_id in enumerate(self.mesh["ios_materials"]):
|
for i, style_or_material_id in enumerate(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.
|
|
||||||
material_without_style = style_or_material_id not in self.styles
|
material_without_style = style_or_material_id not in self.styles
|
||||||
if material_without_style:
|
if material_without_style:
|
||||||
slot_index = get_empty_slot_index()
|
slot_index = get_empty_slot_index()
|
||||||
else:
|
else:
|
||||||
blender_material = self.styles[style_or_material_id]
|
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
|
material_to_slot[i] = slot_index
|
||||||
|
|
||||||
if -1 in self.mesh["ios_material_ids"]:
|
if -1 in self.mesh["ios_material_ids"]:
|
||||||
|
|||||||
Reference in New Issue
Block a user