From b5d36aacf67ec033eca0ffc0a417dddae31f6589 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 10:31:38 +0200 Subject: [PATCH 1/9] Load styles after removing surface style in RemoveSurfaceStyle operator so UI List is updated --- src/bonsai/bonsai/bim/module/style/operator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bonsai/bonsai/bim/module/style/operator.py b/src/bonsai/bonsai/bim/module/style/operator.py index e7d1d05e0a..d5e1931e44 100644 --- a/src/bonsai/bonsai/bim/module/style/operator.py +++ b/src/bonsai/bonsai/bim/module/style/operator.py @@ -1231,4 +1231,5 @@ class RemoveSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator): surface_style = tool.Style.get_style_elements(style)[props.is_editing_class] ifcopenshell.api.style.remove_surface_style(ifc_file, surface_style) core.disable_editing_style(tool.Style) + core.load_styles(tool.Style, style_type=props.style_type) return {"FINISHED"} From 9bbd2b1854ada43b76b1a83e38c4730a95ebaedb Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 10:51:31 +0200 Subject: [PATCH 2/9] Allow UV mode selection in Loader and add UI warning for SOLID Mode (no Generated or Camera UV) --- src/bonsai/bonsai/bim/module/style/ui.py | 2 ++ src/bonsai/bonsai/tool/loader.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bonsai/bonsai/bim/module/style/ui.py b/src/bonsai/bonsai/bim/module/style/ui.py index 54ca4f5aa4..dc9a0aafa3 100644 --- a/src/bonsai/bonsai/bim/module/style/ui.py +++ b/src/bonsai/bonsai/bim/module/style/ui.py @@ -232,6 +232,8 @@ class BIM_PT_styles(Panel): row.operator("bim.add_surface_texture", text="", icon="ADD") if textures: self.layout.prop(self.props, "uv_mode") + if self.props.uv_mode in ("Generated", "Camera"): + self.layout.label(text="Not available in SOLID Mode", icon="INFO") for i, texture in enumerate(textures): split = self.layout.split(factor=0.30, align=True) diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index d738cc39c0..746f3e1213 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -189,7 +189,7 @@ class Loader(bonsai.core.tool.Loader): uv_mode = "Generated" elif coordinates.is_a("IfcTextureCoordinateGenerator") and coordinates.Mode == "COORD-EYE": uv_mode = "Camera" - surface_texture["uv_mode"] = uv_mode or "Generated" + surface_texture["uv_mode"] = uv_mode or "UV" return surface_texture @classmethod From 6314d9c81841fa6cce133d57063dcb4093430cd1 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 11:39:36 +0200 Subject: [PATCH 3/9] avoid full shader rebuilds in intermediate property write --- .../bonsai/bim/module/style/operator.py | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/style/operator.py b/src/bonsai/bonsai/bim/module/style/operator.py index d5e1931e44..766436a1a4 100644 --- a/src/bonsai/bonsai/bim/module/style/operator.py +++ b/src/bonsai/bonsai/bim/module/style/operator.py @@ -782,34 +782,40 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator): def _execute(self, context): self.props = tool.Style.get_style_props() self.style = tool.Ifc.get().by_id(self.props.is_editing_style) + prev_update_graph = self.props.update_graph + self.props["update_graph"] = False - style_elements = tool.Style.get_style_elements(self.style) - # NOTE: currently this operator is used to edit existing (and only existing) IfcSurfaceStyles - # or new or existing IfcSurfaceStyle components (shading, etc) - # which is kind of confusing. - if self.props.is_editing_class == "IfcSurfaceStyle": - self.surface_style = self.style - else: - self.surface_style = style_elements.get(self.props.is_editing_class, None) - self.shading_style = style_elements.get("IfcSurfaceStyleShading", None) - self.rendering_style = style_elements.get("IfcSurfaceStyleRendering", None) - self.texture_style = style_elements.get("IfcSurfaceStyleWithTextures", None) + try: + style_elements = tool.Style.get_style_elements(self.style) - if self.surface_style: - result = self.edit_existing_style() - else: - result = self.add_new_style() + # NOTE: currently this operator is used to edit existing (and only existing) IfcSurfaceStyles + # or new or existing IfcSurfaceStyle components (shading, etc) + # which is kind of confusing. + if self.props.is_editing_class == "IfcSurfaceStyle": + self.surface_style = self.style + else: + self.surface_style = style_elements.get(self.props.is_editing_class, None) + self.shading_style = style_elements.get("IfcSurfaceStyleShading", None) + self.rendering_style = style_elements.get("IfcSurfaceStyleRendering", None) + self.texture_style = style_elements.get("IfcSurfaceStyleWithTextures", None) - if result: - return result + if self.surface_style: + result = self.edit_existing_style() + else: + result = self.add_new_style() - tool.Style.disable_editing() - core.load_styles(tool.Style, style_type=self.props.style_type) + if result: + return result - # restore selected style type - material = tool.Ifc.get_object(self.style) - msprops = tool.Style.get_material_style_props(material) - msprops.active_style_type = msprops.active_style_type + tool.Style.disable_editing() + core.load_styles(tool.Style, style_type=self.props.style_type) + + # restore selected style type + material = tool.Ifc.get_object(self.style) + msprops = tool.Style.get_material_style_props(material) + msprops.active_style_type = msprops.active_style_type + finally: + self.props["update_graph"] = prev_update_graph def edit_existing_style(self) -> None: ifc_file = tool.Ifc.get() From 4cedeec8139cc27b9b37d3c436e0927dc551c55e Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 11:43:47 +0200 Subject: [PATCH 4/9] Fix FLAT+EMISSIVE texture loading crash --- src/bonsai/bonsai/tool/loader.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index 746f3e1213..f58300ef5d 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -472,12 +472,23 @@ class Loader(bonsai.core.tool.Loader): print(f"{mode} Mode texture will be skipped.") continue - if (image := get_image) is None: + if (image := get_image()) is None: continue - # remove RGB node from `create_surface_style_rendering` - prev_node = bsdf.inputs[2].links[0].from_node - blender_material.node_tree.nodes.remove(prev_node) + # Replace whatever currently feeds the FLAT color input (RGB or previous texture chain). + for link in list(bsdf.inputs[2].links): + prev_node = link.from_node + blender_material.node_tree.links.remove(link) + if prev_node.type == "TEX_IMAGE": + # Remove linked texture coordinate node if it's no longer used. + for vec_link in list(prev_node.inputs["Vector"].links): + coord_node = vec_link.from_node + blender_material.node_tree.links.remove(vec_link) + if coord_node.type == "TEX_COORD" and not any(o.links for o in coord_node.outputs): + blender_material.node_tree.nodes.remove(coord_node) + blender_material.node_tree.nodes.remove(prev_node) + elif prev_node.type == "RGB": + blender_material.node_tree.nodes.remove(prev_node) node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage") node.location = bsdf.location - Vector((200, 250)) From 6dafb7a5c2b835415733118eb6b388baf5bc6749 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 11:44:21 +0200 Subject: [PATCH 5/9] Avoid duplicate image datablocks when loading textures --- src/bonsai/bonsai/tool/loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index f58300ef5d..47613db47c 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -315,7 +315,7 @@ class Loader(bonsai.core.tool.Loader): image_url = str(image_url) if is_relative and bpy.data.filepath: image_url = bpy.path.relpath(image_url) - return bpy.data.images.load(image_url) + return bpy.data.images.load(image_url, check_existing=True) elif texture["type"] == "IfcBlobTexture": # https://blender.stackexchange.com/questions/173206/how-to-efficiently-convert-a-pil-image-to-bpy-types-image From 110e4050c8310825a1672c9d72293d0a90b97e39 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 12:17:05 +0200 Subject: [PATCH 6/9] Add warning messaging for unsupported reflectance methods and texture modes --- src/bonsai/bonsai/bim/module/style/ui.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/style/ui.py b/src/bonsai/bonsai/bim/module/style/ui.py index dc9a0aafa3..9e37a01039 100644 --- a/src/bonsai/bonsai/bim/module/style/ui.py +++ b/src/bonsai/bonsai/bim/module/style/ui.py @@ -176,8 +176,10 @@ class BIM_PT_styles(Panel): row.prop(self.props, "reflectance_method") if self.props.reflectance_method not in ("PHYSICAL", "NOTDEFINED", "FLAT"): - self.layout.label(text="Supported reflectance methods are:") - self.layout.label(text="PHYSICAL / NOTDEFINED / FLAT") + self.layout.label( + text=f"{self.props.reflectance_method} will be skipped: only PHYSICAL / NOTDEFINED / FLAT are supported", + icon="ERROR", + ) row = self.layout.row(align=True) row.label(text="Emissive" if self.props.reflectance_method == "FLAT" else "Diffuse") @@ -246,6 +248,22 @@ class BIM_PT_styles(Panel): op_clear = row.operator("bim.remove_texture_map", text="", icon="X") op_path.texture_map_index = op_clear.texture_map_index = i + reflectance = self.props.reflectance_method + mode = texture.mode + if reflectance == "FLAT": + if mode != "EMISSIVE": + self.layout.label( + text=f"{mode} will be skipped: only EMISSIVE is supported for Render Reflectance FLAT", + icon="ERROR", + ) + elif reflectance in ("PHYSICAL", "NOTDEFINED"): + _SUPPORTED = {"DIFFUSE", "NORMAL", "METALLICROUGHNESS", "EMISSIVE", "OCCLUSION"} + if mode not in _SUPPORTED: + self.layout.label( + text=f"{mode} will be skipped: not supported for Render Reflectance PHYSICAL/NOTDEFINED", + icon="ERROR", + ) + def draw_externally_defined_surface_style(self): row = self.layout.row() op = row.operator("bim.browse_external_style", icon="APPEND_BLEND", text="Append From Blend File") From a89621b1793ec88a60409d6944fc5797aa202645 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 16:17:56 +0200 Subject: [PATCH 7/9] Fix Lighting/refraction UI drawing crashes and add warning message that they are only IFC data not used by Blender for surface appearance --- src/bonsai/bonsai/bim/module/style/operator.py | 2 +- src/bonsai/bonsai/bim/module/style/prop.py | 7 +++++++ src/bonsai/bonsai/bim/module/style/ui.py | 9 ++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/style/operator.py b/src/bonsai/bonsai/bim/module/style/operator.py index 766436a1a4..4672b806f5 100644 --- a/src/bonsai/bonsai/bim/module/style/operator.py +++ b/src/bonsai/bonsai/bim/module/style/operator.py @@ -744,7 +744,7 @@ class EnableEditingSurfaceStyle(bpy.types.Operator): if self.ifc_class == "IfcSurfaceStyleLighting": def callback(attribute_name: str, _: object, data: dict[str, Any]) -> None: - assert attributes + assert attributes is not None color = attributes.add() assert isinstance(color, ColourRgb) color.name = attribute_name diff --git a/src/bonsai/bonsai/bim/module/style/prop.py b/src/bonsai/bonsai/bim/module/style/prop.py index a4cfb2cb9e..7dfcad4f07 100644 --- a/src/bonsai/bonsai/bim/module/style/prop.py +++ b/src/bonsai/bonsai/bim/module/style/prop.py @@ -185,6 +185,13 @@ class ColourRgb(PropertyGroup): # to fit blender.bim.helper.draw_attribute is_optional = False special_type = "" + data_type = "" + ifc_class = "" + use_explorer_ui = False + + @property + def display_name(self): + return self.name def get_value_name(self, *args, **kwargs): return "color_value" diff --git a/src/bonsai/bonsai/bim/module/style/ui.py b/src/bonsai/bonsai/bim/module/style/ui.py index 9e37a01039..7b27d1f324 100644 --- a/src/bonsai/bonsai/bim/module/style/ui.py +++ b/src/bonsai/bonsai/bim/module/style/ui.py @@ -272,10 +272,17 @@ class BIM_PT_styles(Panel): bonsai.bim.helper.draw_attributes(self.props.external_style_attributes, self.layout, enable_search=True) def draw_refraction_surface_style(self): + self.layout.label( + text="Refraction values are IFC-only and do not affect Blender surface appearance", + icon="ERROR", + ) bonsai.bim.helper.draw_attributes(self.props.refraction_style_attributes, self.layout, enable_search=True) - row = self.layout.row(align=True) def draw_lighting_surface_style(self): + self.layout.label( + text="Lighting values are IFC-only and do not affect Blender surface appearance", + icon="ERROR", + ) bonsai.bim.helper.draw_attributes(self.props.lighting_style_colours, self.layout) def draw_edit_ui(self, edit_label: str): From 679fe4dcae4209a61296667d87b3541ddd53d0e4 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 16:42:53 +0200 Subject: [PATCH 8/9] Add warnings for emissive and specular ratios in FLAT reflectance method (IFC only no Blender appearance) --- src/bonsai/bonsai/bim/module/style/ui.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bonsai/bonsai/bim/module/style/ui.py b/src/bonsai/bonsai/bim/module/style/ui.py index 7b27d1f324..ed1a3fdc64 100644 --- a/src/bonsai/bonsai/bim/module/style/ui.py +++ b/src/bonsai/bonsai/bim/module/style/ui.py @@ -180,6 +180,17 @@ class BIM_PT_styles(Panel): text=f"{self.props.reflectance_method} will be skipped: only PHYSICAL / NOTDEFINED / FLAT are supported", icon="ERROR", ) + elif self.props.reflectance_method == "FLAT": + if self.props.diffuse_colour_class == "IfcNormalisedRatioMeasure": + self.layout.label( + text="Emissive ratio is IFC-only in FLAT Reflectance method and does not affect Blender appearance", + icon="ERROR", + ) + if self.props.specular_colour_class == "IfcNormalisedRatioMeasure": + self.layout.label( + text="Specular ratio is IFC-only in FLAT Reflectance method and does not affect Blender appearance", + icon="ERROR", + ) row = self.layout.row(align=True) row.label(text="Emissive" if self.props.reflectance_method == "FLAT" else "Diffuse") From 7881f5992f91e6e7ccfbf7588dafb20fcf29dfd6 Mon Sep 17 00:00:00 2001 From: falken10vdl Date: Mon, 27 Apr 2026 17:51:04 +0200 Subject: [PATCH 9/9] Add warning when PHYSICAL/NOTDEFINED uses IfcColourRgb for Metallic, because this value is IFC-only and does not affect Blender appearance. --- src/bonsai/bonsai/bim/module/style/ui.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/style/ui.py b/src/bonsai/bonsai/bim/module/style/ui.py index ed1a3fdc64..4e3a1daccb 100644 --- a/src/bonsai/bonsai/bim/module/style/ui.py +++ b/src/bonsai/bonsai/bim/module/style/ui.py @@ -180,17 +180,26 @@ class BIM_PT_styles(Panel): text=f"{self.props.reflectance_method} will be skipped: only PHYSICAL / NOTDEFINED / FLAT are supported", icon="ERROR", ) + elif self.props.reflectance_method in ("PHYSICAL", "NOTDEFINED"): + if self.props.specular_colour_class == "IfcColourRgb": + self.layout.label( + text="Metallic color is IFC-only in PHYSICAL/NOTDEFINED and does not affect Blender appearance", + icon="ERROR", + ) elif self.props.reflectance_method == "FLAT": if self.props.diffuse_colour_class == "IfcNormalisedRatioMeasure": self.layout.label( text="Emissive ratio is IFC-only in FLAT Reflectance method and does not affect Blender appearance", icon="ERROR", ) - if self.props.specular_colour_class == "IfcNormalisedRatioMeasure": - self.layout.label( - text="Specular ratio is IFC-only in FLAT Reflectance method and does not affect Blender appearance", - icon="ERROR", - ) + self.layout.label( + text="Specular value is IFC-only in FLAT Reflectance method and does not affect Blender appearance", + icon="ERROR", + ) + self.layout.label( + text="Highlight value is IFC-only in FLAT Reflectance method and does not affect Blender appearance", + icon="ERROR", + ) row = self.layout.row(align=True) row.label(text="Emissive" if self.props.reflectance_method == "FLAT" else "Diffuse")