Generating IFC compatible shader graphs

Added new panel. in shader graph N-panel that allows you to create fully GLTF and IFC compatible shader graphs by just tweaking parameters from the panel. Note that it's still saved to IFC only when you hit "Save Current Style", before that it's still just stored in blender.

https://imgur.com/a/vmkoLPH

The premise is that if you want to have non-external style you'd be able to create it entirely from this panel - so there will no need to guess or to remember which parameters from the graph are supported by IFC and which are not.

It's still work in progress, still need to polish it, add support for more reflectance methods and add suport for textures (need to be able to import/export it with .ifc and set them from this panel).
This commit is contained in:
Andrej730
2023-06-23 18:04:32 +05:00
parent addce9f0ea
commit 2dc9dc2000
9 changed files with 430 additions and 107 deletions
@@ -45,6 +45,7 @@ classes = (
ui.BIM_PT_style_attributes,
ui.BIM_PT_external_style_attributes,
ui.BIM_UL_styles,
ui.BIM_PT_STYLE_GRAPH,
)
@@ -30,11 +30,26 @@ import os
class UpdateStyleColours(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.update_style_colours"
bl_label = "Save Current Shading Style"
bl_description = "Save current style values to IfcSurfaceStyleShading"
bl_description = (
"Save current style values to IfcSurfaceStyleShading.\n\n" + "ALT+CLICK to see saved values details"
)
bl_options = {"REGISTER", "UNDO"}
verbose: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"})
def invoke(self, context, event):
# verobse print to console on alt+click
# make sure to use SKIP_SAVE on property, otherwise it might get stuck
if event.type == "LEFTMOUSE" and event.alt:
self.verbose = True
return self.execute(context)
def _execute(self, context):
core.update_style_colours(tool.Ifc, tool.Style, obj=context.active_object.active_material)
mat = context.active_object.active_material
core.update_style_colours(tool.Ifc, tool.Style, obj=mat, verbose=self.verbose)
if self.verbose:
self.report({"INFO"}, "Check the system console to see saved style properties")
tool.Style.set_surface_style_props(mat)
class UpdateStyleTextures(bpy.types.Operator, tool.Ifc.Operator):
@@ -96,9 +96,41 @@ def update_shading_style(self, context):
if rendering_style and texture_style:
tool.Loader.create_surface_style_with_textures(blender_material, rendering_style, texture_style)
tool.Style.set_surface_style_props(blender_material)
tool.Style.record_shading(blender_material)
# TODO: support more more methods and also textures
REFLECTANCE_METHODS = [
("PHYSICAL", "PHYSICAL", ""),
("FLAT", "FLAT", ""),
# ("METAL", "METAL", ""),
# ("MATT", "MATT", ""),
# ("GLASS", "GLASS", ""),
# ("NOTDEFINED", "NOTDEFINED", ""),
]
def update_shader_graph(self, context):
if not self.update_graph:
return
material = self.id_data
style_data = tool.Style.get_surface_style_from_props(material)
tool.Loader.create_surface_style_rendering(material, style_data)
def update_graph_get(self):
return self.get("update_graph", True)
def update_graph_set(self, value):
self["update_graph"] = value
if value:
material = self.id_data
tool.Style.set_surface_style_props(material)
class BIMStyleProperties(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=Attribute)
is_editing: BoolProperty(name="Is Editing")
@@ -113,3 +145,42 @@ class BIMStyleProperties(PropertyGroup):
default="Shading",
update=update_shading_style,
)
# GLTF style properties
update_graph: BoolProperty(
name="Update Shade Graph on Prop Change",
description="Update shader graph in real time\nas you update style properties",
default=True,
get=update_graph_get,
set=update_graph_set,
)
reflectance_method: EnumProperty(
name="Reflectance Method",
description="Reflectance method to use for the material",
items=REFLECTANCE_METHODS,
default="PHYSICAL",
update=update_shader_graph,
)
surface_color: bpy.props.FloatVectorProperty(
name="Surface Color",
subtype="COLOR",
default=(1, 1, 1, 1),
min=0.0,
max=1.0,
size=4,
update=update_shader_graph,
)
diffuse_color: bpy.props.FloatVectorProperty(
name="Diffuse Color",
subtype="COLOR",
default=(1, 1, 1, 1),
min=0.0,
max=1.0,
size=4,
update=update_shader_graph,
)
transparency: bpy.props.FloatProperty(
name="Transparency", default=0.0, min=0.0, max=1.0, update=update_shader_graph
)
roughness: bpy.props.FloatProperty(name="Roughness", default=0.0, min=0.0, max=1.0, update=update_shader_graph)
metallic: bpy.props.FloatProperty(name="Metallic", default=0.0, min=0.0, max=1.0, update=update_shader_graph)
@@ -65,6 +65,24 @@ class BIM_PT_styles(Panel):
self.layout.template_list("BIM_UL_styles", "", self.props, "styles", self.props, "active_style_index")
def draw_style_ui(self, context):
mat = context.material
props = mat.BIMMaterialProperties
style_props = mat.BIMStyleProperties
row = self.layout.row(align=True)
if not props.ifc_style_id:
row.operator("bim.add_style", icon="ADD")
return
row.prop(style_props, "active_style_type", icon="SHADING_RENDERED", text="")
row.operator("bim.update_current_style", icon="FILE_REFRESH", text="")
row = self.layout.row(align=True)
row.operator("bim.update_style_colours", icon="GREASEPENCIL")
row.operator("bim.update_style_textures", icon="TEXTURE", text="")
row.operator("bim.unlink_style", icon="UNLINKED", text="")
row.operator("bim.remove_style", icon="X", text="").style = props.ifc_style_id
class BIM_PT_style(MaterialButtonsPanel, Panel):
bl_label = "IFC Style"
bl_idname = "BIM_PT_style"
@@ -81,22 +99,11 @@ class BIM_PT_style(MaterialButtonsPanel, Panel):
)
def draw(self, context):
props = context.active_object.active_material.BIMMaterialProperties
style_props = context.active_object.active_material.BIMStyleProperties
mat = context.material
row = self.layout.row(align=True)
props = mat.BIMMaterialProperties
draw_style_ui(self, context)
if not props.ifc_style_id:
row.operator("bim.add_style", icon="ADD")
return
row.prop(style_props, "active_style_type", icon="SHADING_RENDERED", text="")
row.operator("bim.update_current_style", icon="FILE_REFRESH", text="")
row = self.layout.row(align=True)
row.operator("bim.update_style_colours", icon="GREASEPENCIL")
row.operator("bim.update_style_textures", icon="TEXTURE", text="")
row.operator("bim.unlink_style", icon="UNLINKED", text="")
row.operator("bim.remove_style", icon="X", text="").style = props.ifc_style_id
row = self.layout.row(align=True)
row.prop(mat, "diffuse_color", text="Viewport Color" if mat.use_nodes else "Render Color")
@@ -212,3 +219,38 @@ class BIM_UL_styles(UIList):
row2 = row.row()
row2.alignment = "RIGHT"
row2.label(text=str(item.total_elements))
class BIM_PT_STYLE_GRAPH(Panel):
bl_idname = "BIM_PT_style_graph"
bl_space_type = "NODE_EDITOR"
bl_label = "IFC Style Graph Settings"
bl_region_type = "UI"
bl_category = "BBIM"
def draw(self, context):
layout = self.layout
props = context.active_object.active_material.BIMStyleProperties
draw_style_ui(self, context)
layout.separator()
box = layout.box()
box.label(text="Creating shader from this panel")
box.label(text="ensures that shader is")
box.label(text="GLTF compatible")
box.label(text="and therefore will be ")
box.label(text="stored in IFC safely.")
layout.separator()
layout.prop(props, "update_graph", text="Graph Auto Update")
layout.label(text="Reflectance Method:")
layout.prop(props, "reflectance_method", text="")
layout.prop(props, "surface_color")
layout.prop(props, "transparency")
layout.prop(props, "diffuse_color")
if props.reflectance_method == "PHYSICAL":
layout.prop(props, "metallic")
if props.reflectance_method not in ("FLAT", "NOTDEFINED"):
layout.prop(props, "roughness")
+1 -6
View File
@@ -491,7 +491,7 @@ class BIM_OT_add_section_plane(bpy.types.Operator):
continue
material.blend_method = "HASHED"
material.shadow_method = "HASHED"
material_output = self.get_node(material.node_tree.nodes, "OUTPUT_MATERIAL")
material_output = tool.Blender.get_material_node(material, "OUTPUT_MATERIAL", {"is_active_output": True})
if not material_output:
continue
from_socket = material_output.inputs[0].links[0].from_socket
@@ -501,11 +501,6 @@ class BIM_OT_add_section_plane(bpy.types.Operator):
material.node_tree.links.new(from_socket, section_override.inputs[0])
material.node_tree.links.new(section_override.outputs[0], material_output.inputs[0])
def get_node(self, nodes, node_type):
for node in nodes:
if node.type == node_type:
return node
class BIM_OT_remove_section_plane(bpy.types.Operator):
"""Remove selected section plane. No effect if executed on a regular object"""
+13 -3
View File
@@ -54,18 +54,28 @@ def remove_style(ifc, material, style_tool, style=None):
style_tool.import_presentation_styles(style_tool.get_active_style_type())
def update_style_colours(ifc, style, obj=None):
def update_style_colours(ifc, style, obj=None, verbose=False):
element = style.get_style(obj)
if style.can_support_rendering_style(obj):
rendering_style = style.get_surface_rendering_style(obj)
attributes = style.get_surface_rendering_attributes(obj)
style_elements = style.get_style_elements(obj)
rendering_style = style_elements.get("IfcSurfaceStyleRendering", None)
texture_style = style_elements.get("IfcSurfaceStyleWithTextures", None)
attributes = style.get_surface_rendering_attributes(obj, verbose)
if rendering_style:
ifc.run("style.edit_surface_style", style=rendering_style, attributes=attributes)
else:
ifc.run(
"style.add_surface_style", style=element, ifc_class="IfcSurfaceStyleRendering", attributes=attributes
)
# # TODO: uncomment to support saving textures
# # TODO: uvs?
# textures = ifc.run("style.add_surface_textures", material=obj)
# if not texture_style and textures:
# texture_style = ifc.get().createIfcSurfaceStyleWithTextures()
# if texture_style:
# texture_style.Textures = textures
else:
shading_style = style.get_surface_shading_style(obj)
attributes = style.get_surface_shading_attributes(obj)
+9 -2
View File
@@ -107,6 +107,13 @@ class Blender:
return False
return False
@classmethod
def show_error_message(cls, text):
"""useful for showing error messages outside blender operators"""
def error(self, context):
self.layout.label(text=text)
bpy.context.window_manager.popup_menu(error, title="Error", icon="ERROR")
@classmethod
def get_viewport_context(cls):
"""Get viewport area context for context overriding.
@@ -170,13 +177,13 @@ class Blender:
shader_editor.pin = previous_pin_setting
@classmethod
def get_material_node(cls, blender_material, node_type):
def get_material_node(cls, blender_material, node_type, kwargs={}):
"""returns first node from the `blender_material` shader graph with type `node_type`"""
if not blender_material.use_nodes:
return
nodes = blender_material.node_tree.nodes
for node in nodes:
if node.type == node_type:
if node.type == node_type and all(getattr(node, a) == kwargs[a] for a in kwargs):
return node
@classmethod
+102 -60
View File
@@ -22,7 +22,7 @@ import ifcopenshell.util.element
import blenderbim.core.tool
import blenderbim.tool as tool
import os
import mathutils
from mathutils import Vector
# Progressively we'll refactor loading elements into Blender objects into this
@@ -58,16 +58,12 @@ class Loader(blenderbim.core.tool.Loader):
@classmethod
def create_surface_style_shading(cls, blender_material, surface_style):
surface_style = cls.surface_style_to_dict(surface_style)
alpha = 1.0
# Transparency was added in IFC4
if hasattr(surface_style, "Transparency") and surface_style.Transparency:
alpha = 1 - surface_style.Transparency
blender_material.diffuse_color = (
surface_style.SurfaceColour.Red,
surface_style.SurfaceColour.Green,
surface_style.SurfaceColour.Blue,
alpha,
)
if transparency := surface_style.get("Transparency", None):
alpha = 1 - transparency
blender_material.diffuse_color = surface_style["SurfaceColour"][:3] + (alpha,)
@classmethod
def restart_material_node_tree(cls, blender_material):
@@ -76,44 +72,75 @@ class Loader(blenderbim.core.tool.Loader):
for n in nodes[:]:
nodes.remove(n)
output = nodes.new("ShaderNodeOutputMaterial")
output.location = Vector((300, 300))
bsdf = nodes.new("ShaderNodeBsdfPrincipled")
bsdf.location = Vector((10, 300))
links.new(bsdf.outputs["BSDF"], output.inputs["Surface"])
@classmethod
def surface_style_to_dict(cls, surface_style):
if isinstance(surface_style, dict):
return surface_style
surface_style = surface_style.get_info()
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue, 1)
if surface_style["SurfaceColour"]:
surface_style["SurfaceColour"] = color_to_tuple(surface_style["SurfaceColour"])
if surface_style["DiffuseColour"] and surface_style["DiffuseColour"].is_a("IfcColourRgb"):
surface_style["DiffuseColour"] = ("IfcColourRgb", color_to_tuple(surface_style["DiffuseColour"]))
elif surface_style["DiffuseColour"] and surface_style["DiffuseColour"].is_a("IfcNormalisedRatioMeasure"):
diffuse_color_value = surface_style["DiffuseColour"].wrappedValue
diffuse_color = [v * diffuse_color_value for v in surface_style["SurfaceColor"][:3]] + [1]
surface_style["DiffuseColour"] = ("IfcNormalisedRatioMeasure", diffuse_color)
else:
surface_style["DiffuseColour"] = None
if surface_style["SpecularColour"] and surface_style["SpecularColour"].is_a("IfcNormalisedRatioMeasure"):
surface_style["SpecularColour"] = surface_style["SpecularColour"].wrappedValue
else:
surface_style["SpecularColour"] = None
if surface_style["SpecularHighlight"] and surface_style["SpecularHighlight"].is_a("IfcSpecularRoughness"):
surface_style["SpecularHighlight"] = surface_style["SpecularHighlight"].wrappedValue
else:
surface_style["SpecularHighlight"] = None
return surface_style
@classmethod
def create_surface_style_rendering(cls, blender_material, surface_style):
surface_style = cls.surface_style_to_dict(surface_style)
cls.create_surface_style_shading(blender_material, surface_style)
if surface_style.ReflectanceMethod in ["PHYSICAL", "NOTDEFINED"]:
reflectance_method = surface_style["ReflectanceMethod"]
if reflectance_method not in ("PHYSICAL", "NOTDEFINED", "FLAT"):
print(f'WARNING. Unsupported reflectance method "{reflectance_method}" on style {surface_style}')
return
if reflectance_method in ["PHYSICAL", "NOTDEFINED"]:
blender_material.use_nodes = True
cls.restart_material_node_tree(blender_material)
bsdf = tool.Blender.get_material_node(blender_material, "BSDF_PRINCIPLED")
if surface_style.DiffuseColour:
if surface_style.DiffuseColour.is_a("IfcColourRgb"):
bsdf.inputs["Base Color"].default_value = (
surface_style.DiffuseColour.Red,
surface_style.DiffuseColour.Green,
surface_style.DiffuseColour.Blue,
1,
)
elif surface_style.DiffuseColour.is_a("IfcNormalisedRatioMeasure"):
bsdf.inputs["Base Color"].default_value = (
surface_style.SurfaceColour.Red * surface_style.DiffuseColour.wrappedValue,
surface_style.SurfaceColour.Green * surface_style.DiffuseColour.wrappedValue,
surface_style.SurfaceColour.Blue * surface_style.DiffuseColour.wrappedValue,
1,
)
if surface_style.SpecularColour and surface_style.SpecularColour.is_a("IfcNormalisedRatioMeasure"):
bsdf.inputs["Metallic"].default_value = surface_style.SpecularColour.wrappedValue
if surface_style.SpecularHighlight and surface_style.SpecularHighlight.is_a("IfcSpecularRoughness"):
bsdf.inputs["Roughness"].default_value = surface_style.SpecularHighlight.wrappedValue
if hasattr(surface_style, "Transparency") and surface_style.Transparency:
bsdf.inputs["Alpha"].default_value = 1 - surface_style.Transparency
if surface_style["DiffuseColour"]:
color_type, color_value = surface_style["DiffuseColour"]
if color_type == "IfcColourRgb":
bsdf.inputs["Base Color"].default_value = color_value
elif color_type == "IfcNormalisedRatioMeasure":
bsdf.inputs["Base Color"].default_value = color_value
if surface_style["SpecularColour"]:
bsdf.inputs["Metallic"].default_value = surface_style["SpecularColour"]
if surface_style["SpecularHighlight"]:
bsdf.inputs["Roughness"].default_value = surface_style["SpecularHighlight"]
if transparency := surface_style.get("Transparency", None):
bsdf.inputs["Alpha"].default_value = 1 - transparency
blender_material.blend_method = "BLEND"
elif surface_style.ReflectanceMethod == "FLAT":
elif reflectance_method == "FLAT":
blender_material.use_nodes = True
cls.restart_material_node_tree(blender_material)
output = {n.type: n for n in blender_material.node_tree.nodes}.get("OUTPUT_MATERIAL", None)
output = tool.Blender.get_material_node(blender_material, "OUTPUT_MATERIAL")
bsdf = tool.Blender.get_material_node(blender_material, "BSDF_PRINCIPLED")
mix = blender_material.node_tree.nodes.new(type="ShaderNodeMixShader")
@@ -123,24 +150,21 @@ class Loader(blenderbim.core.tool.Loader):
blender_material.node_tree.nodes.remove(bsdf)
lightpath = blender_material.node_tree.nodes.new(type="ShaderNodeLightPath")
lightpath.location = mix.location - mathutils.Vector((200, -200))
lightpath.location = mix.location - Vector((200, -200))
blender_material.node_tree.links.new(lightpath.outputs[0], mix.inputs[0])
bsdf = blender_material.node_tree.nodes.new(type="ShaderNodeBsdfTransparent")
bsdf.location = mix.location - mathutils.Vector((200, 0))
bsdf.location = mix.location - Vector((200, 150))
blender_material.node_tree.links.new(bsdf.outputs[0], mix.inputs[1])
rgb = blender_material.node_tree.nodes.new(type="ShaderNodeRGB")
rgb.location = mix.location - mathutils.Vector((200, 200))
rgb.location = mix.location - Vector((200, 250))
blender_material.node_tree.links.new(rgb.outputs[0], mix.inputs[2])
if surface_style.DiffuseColour and surface_style.DiffuseColour.is_a("IfcColourRgb"):
rgb.outputs[0].default_value = (
surface_style.DiffuseColour.Red,
surface_style.DiffuseColour.Green,
surface_style.DiffuseColour.Blue,
1,
)
if surface_style["DiffuseColour"]:
color_type, color_value = surface_style["DiffuseColour"]
if color_type == "IfcColourRgb":
rgb.outputs[0].default_value = color_value
@classmethod
def create_surface_style_with_textures(cls, blender_material, rendering_style, texture_style):
@@ -153,73 +177,91 @@ class Loader(blenderbim.core.tool.Loader):
if not os.path.abspath(texture.URLReference) and tool.Ifc.get_path():
image_url = os.path.join(os.path.dirname(tool.Ifc.get_path()), texture.URLReference)
reflectance_method = rendering_style.ReflectanceMethod
if reflectance_method not in ("PHYSICAL", "NOTDEFINED", "FLAT"):
print(f'WARNING. Unsupported reflectance method "{reflectance_method}" on style {rendering_style}')
return
if rendering_style.ReflectanceMethod in ["PHYSICAL", "NOTDEFINED"]:
bsdf = tool.Blender.get_material_node(blender_material, "BSDF_PRINCIPLED")
if mode == "NORMAL":
# add normal map node
normalmap = blender_material.node_tree.nodes.new(type="ShaderNodeNormalMap")
normalmap.location = bsdf.location - mathutils.Vector((200, 0))
normalmap.location = bsdf.location - Vector((200, 0))
blender_material.node_tree.links.new(normalmap.outputs[0], bsdf.inputs["Normal"])
# add normal map sampler
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
node.location = normalmap.location - mathutils.Vector((200, 0))
node.location = normalmap.location - Vector((200, 0))
image = bpy.data.images.load(image_url)
image.colorspace_settings.name = "Non-Color"
node.image = image
blender_material.node_tree.links.new(node.outputs[0], normalmap.inputs["Color"])
elif mode == "EMISSIVE":
output = {n.type: n for n in blender_material.node_tree.nodes}.get("OUTPUT_MATERIAL", None)
elif mode == "EMISSIVE":
output = tool.Blender.get_material_node(blender_material, "OUTPUT_MATERIAL")
# add "Add Shader" node
add = blender_material.node_tree.nodes.new(type="ShaderNodeAddShader")
add.location = bsdf.location + mathutils.Vector((200, 0))
add.location = bsdf.location + Vector((200, 0))
blender_material.node_tree.links.new(bsdf.outputs[0], add.inputs[1])
blender_material.node_tree.links.new(add.outputs[0], output.inputs[0])
# add emssion shader node
emission = blender_material.node_tree.nodes.new(type="ShaderNodeEmission")
emission.location = add.location - mathutils.Vector((200, 0))
emission.location = add.location - Vector((200, 0))
blender_material.node_tree.links.new(emission.outputs[0], add.inputs[0])
# add emission texture sampler
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
node.location = emission.location - mathutils.Vector((200, 0))
node.location = emission.location - Vector((200, 0))
image = bpy.data.images.load(image_url)
node.image = image
blender_material.node_tree.links.new(node.outputs[0], emission.inputs[0])
elif mode == "METALLICROUGHNESS":
separate = blender_material.node_tree.nodes.new(type="ShaderNodeSeparateRGB")
separate.location = bsdf.location - mathutils.Vector((200, 0))
separate.location = bsdf.location - Vector((200, 0))
blender_material.node_tree.links.new(separate.outputs[1], bsdf.inputs["Roughness"])
blender_material.node_tree.links.new(separate.outputs[2], bsdf.inputs["Metallic"])
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
node.location = separate.location - mathutils.Vector((200, 0))
node.location = separate.location - Vector((200, 0))
image = bpy.data.images.load(image_url)
image.colorspace_settings.name = "Non-Color"
node.image = image
blender_material.node_tree.links.new(node.outputs[0], separate.inputs[0])
elif mode == "OCCLUSION":
# TODO work out how to implement glTF settings here
# https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html
pass
elif mode == "DIFFUSE":
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
node.location = bsdf.location - mathutils.Vector((400, 0))
node.location = bsdf.location - Vector((400, 0))
image = bpy.data.images.load(image_url)
node.image = image
blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs["Base Color"])
blender_material.node_tree.links.new(node.outputs[1], bsdf.inputs["Alpha"])
blender_material.blend_method = "BLEND"
elif rendering_style.ReflectanceMethod == "FLAT":
bsdf = tool.Blender.get_material_node(blender_material, "MIX_SHADER")
if mode == "EMISSIVE":
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
node.location = bsdf.location - mathutils.Vector((200, 0))
image = bpy.data.images.load(image_url)
node.image = image
blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs[2])
if mode != "EMISSIVE":
continue
node = blender_material.node_tree.nodes.new(type="ShaderNodeTexImage")
node.location = bsdf.location - Vector((200, 0))
image = bpy.data.images.load(image_url)
# TODO: orphaned textures after shader recreated?
node.image = image
blender_material.node_tree.links.new(node.outputs[0], bsdf.inputs[2])
if node and getattr(texture, "IsMappedBy", None):
coordinates = texture.IsMappedBy[0]
coord = blender_material.node_tree.nodes.new(type="ShaderNodeTexCoord")
coord.location = node.location - mathutils.Vector((200, 0))
coord.location = node.location - Vector((200, 0))
if coordinates.is_a("IfcTextureCoordinateGenerator") and coordinates.Mode == "COORD":
blender_material.node_tree.links.new(coord.outputs["Generated"], node.inputs["Vector"])
elif coordinates.is_a("IfcTextureCoordinateGenerator") and coordinates.Mode == "COORD-EYE":
+161 -21
View File
@@ -24,6 +24,16 @@ import blenderbim.tool as tool
import blenderbim.bim.helper
STYLE_PROPS_MAP = {
"reflectance_method": "ReflectanceMethod",
"diffuse_color": "DiffuseColour",
"surface_color": "SurfaceColour",
"transparency": "Transparency",
"roughness": "SpecularHighlight",
"metallic": "SpecularColour",
}
class Style(blenderbim.core.tool.Style):
@classmethod
def can_support_rendering_style(cls, obj):
@@ -87,6 +97,8 @@ class Style(blenderbim.core.tool.Style):
@classmethod
def get_style_elements(cls, blender_material):
if not blender_material.BIMMaterialProperties.ifc_style_id:
return {}
style = tool.Ifc.get().by_id(blender_material.BIMMaterialProperties.ifc_style_id)
style_elements = {}
for style in style.Styles:
@@ -94,52 +106,184 @@ class Style(blenderbim.core.tool.Style):
return style_elements
@classmethod
def get_surface_rendering_attributes(cls, obj):
def get_surface_style_from_props(cls, blender_material):
"""convert blender style props to ifc props"""
surface_style_data = dict()
props = blender_material.BIMStyleProperties
for prop_blender, prop_ifc in STYLE_PROPS_MAP.items():
surface_style_data[prop_ifc] = getattr(props, prop_blender)
if surface_style_data["ReflectanceMethod"] == "PHYSICAL" and tool.Ifc.get_schema() != "IFC4X3":
surface_style_data["ReflectanceMethod"] = "NOTDEFINED"
surface_style_data["DiffuseColour"] = ("IfcColourRgb", surface_style_data["DiffuseColour"])
return surface_style_data
@classmethod
def set_surface_style_props(cls, blender_material):
"""set blender style props based on current surface material"""
props = blender_material.BIMStyleProperties
# make sure won't be updating while we changing it
prev_update_graph_value = props.update_graph
props["update_graph"] = False
style_elements = tool.Style.get_style_elements(blender_material)
surface_style = style_elements["IfcSurfaceStyleRendering"]
style_data = tool.Loader.surface_style_to_dict(surface_style)
if style_data["ReflectanceMethod"] == "NOTDEFINED":
style_data["ReflectanceMethod"] = "PHYSICAL"
style_data["DiffuseColour"] = style_data["DiffuseColour"][1]
for prop_blender, prop_ifc in STYLE_PROPS_MAP.items():
prop_value = style_data[prop_ifc]
# TODO: set to default?
if prop_value is None:
continue
setattr(props, prop_blender, prop_value)
props["update_graph"] = prev_update_graph_value
@classmethod
def get_surface_rendering_attributes(cls, obj, verbose=True):
transparency = 1 - obj.diffuse_color[3]
diffuse_color = obj.diffuse_color
report = (lambda *x: print(*x)) if verbose else (lambda *x: None)
viewport_color = {
"Name": None,
"Red": obj.diffuse_color[0],
"Green": obj.diffuse_color[1],
"Blue": obj.diffuse_color[2],
}
attributes = {
"SurfaceColour": {
"Name": None,
"Red": obj.diffuse_color[0],
"Green": obj.diffuse_color[1],
"Blue": obj.diffuse_color[2],
},
"SurfaceColour": viewport_color,
"Transparency": transparency,
}
GREEN = "\033[32m"
BLUE = "\033[1;34m"
R = "\033[0m" # RESET symbol
bsdfs = {n.type: n for n in obj.node_tree.nodes}
if "BSDF_GLOSSY" in bsdfs:
attributes["ReflectanceMethod"] = "METAL"
bsdf = bsdfs["BSDF_GLOSSY"]
def get_input_node(node, input_name=None, of_type=None, input_index=None):
input_pin = node.inputs[input_name] if input_index is None else node.inputs[input_index]
if of_type:
return next((l.from_node for l in input_pin.links if l.from_node.type == of_type), None)
return next((l.from_node for l in input_pin.links), None)
report("--------------------")
report("Verbose method of getting surface rendering attributes enabled.")
report("If some attribute is not mentioned below, then it won't be saved to IFC.")
report("--------------------")
report(f"{GREEN}Viewport color{R} saved as {GREEN}SurfaceColour{R}")
# TODO: make sure bsdf is connected to the output?
bsdfs = {n.type: n for n in obj.node_tree.nodes if n.outputs and n.outputs[0].is_linked}
if "BSDF_PRINCIPLED" not in bsdfs:
report(f"{GREEN}Viewport color alpha{R} saved as {GREEN}Transparency{R}")
# TODO: should escape referring to pins by their name to support different languages
material_output = tool.Blender.get_material_node(obj, "OUTPUT_MATERIAL", {"is_active_output": True})
surface_output = get_input_node(material_output, "Surface")
if surface_output and surface_output.type == "MIX_SHADER":
mix_shader = surface_output
if (
get_input_node(mix_shader, "Fac", "LIGHT_PATH")
and get_input_node(mix_shader, input_index=1, of_type="BSDF_TRANSPARENT")
and (rgb := get_input_node(mix_shader, input_index=2, of_type="RGB"))
):
report(
f"Because of {BLUE}MIX_SHADER + LIGHT_PATH + BSDF_TRANSPARENT + RGB{R} node setup reflectance method identified as {BLUE}FLAT{R}"
)
attributes["ReflectanceMethod"] = "FLAT"
attributes["SpecularHighlight"] = None
report(f"RGB {GREEN}Color{R} saved as {GREEN}DiffuseColour{R}")
diffuse_color = rgb.outputs[0].default_value
elif surface_output and surface_output.type == "BSDF_PRINCIPLED":
report(f"Because of {BLUE}BSDF_PRINCIPLED{R} node reflectance method identified as {BLUE}PHYSICAL{R}")
attributes["ReflectanceMethod"] = "NOTDEFINED" if tool.Ifc.get_schema() != "IFC4X3" else "PHYSICAL"
bsdf = surface_output
report(f"BSDF {GREEN}Base Color{R} saved as {GREEN}DiffuseColour{R}")
diffuse_color = bsdf.inputs["Base Color"].default_value
report(f"BSDF {GREEN}Metallic{R} saved as {GREEN}SpecularColour{R}")
attributes["SpecularColour"] = round(bsdf.inputs["Metallic"].default_value, 3)
report(f"BSDF {GREEN}Roughness{R} saved as {GREEN}IfcSpecularRoughness{R}")
attributes["SpecularHighlight"] = {"IfcSpecularRoughness": round(bsdf.inputs["Roughness"].default_value, 3)}
report(f"BSDF {GREEN}Alpha{R} saved as {GREEN}Transparency{R}")
attributes["Transparency"] = 1 - bsdf.inputs["Alpha"].default_value
elif "BSDF_GLOSSY" in bsdfs:
attributes["ReflectanceMethod"] = "METAL"
report(f"Because of {BLUE}BSDF_GLOSSY{R} node reflectance method identified as {BLUE}METAL{R}")
bsdf = bsdfs["BSDF_GLOSSY"]
report(f"BSDF {GREEN}Roughness{R} saved as {GREEN}IfcSpecularRoughness{R}")
attributes["SpecularHighlight"] = {"IfcSpecularRoughness": round(bsdf.inputs["Roughness"].default_value, 3)}
report(f"BSDF {GREEN}Color{R} saved as {GREEN}DiffuseColour{R}")
diffuse_color = bsdf.inputs["Color"].default_value
elif "BSDF_DIFFUSE" in bsdfs:
report(f"Because of {BLUE}BSDF_DIFFUSE{R} node reflectance method identified as {BLUE}MATT{R}")
attributes["ReflectanceMethod"] = "MATT"
bsdf = bsdfs["BSDF_DIFFUSE"]
report(f"BSDF {GREEN}Roughness{R} saved as {GREEN}IfcSpecularRoughness{R}")
attributes["SpecularHighlight"] = {"IfcSpecularRoughness": round(bsdf.inputs["Roughness"].default_value, 3)}
report(f"BSDF {GREEN}Color{R} saved as {GREEN}DiffuseColour{R}")
diffuse_color = bsdf.inputs["Color"].default_value
elif "BSDF_GLASS" in bsdfs:
report(f"Because of {BLUE}BSDF_GLASS{R} node reflectance method identified as {BLUE}GLASS{R}")
attributes["ReflectanceMethod"] = "GLASS"
bsdf = bsdfs["BSDF_GLASS"]
report(f"BSDF {GREEN}Roughness{R} saved as {GREEN}IfcSpecularRoughness{R}")
attributes["SpecularHighlight"] = {"IfcSpecularRoughness": round(bsdf.inputs["Roughness"].default_value, 3)}
report(f"BSDF {GREEN}Color{R} saved as {GREEN}DiffuseColour{R}")
diffuse_color = bsdf.inputs["Color"].default_value
# TODO: remove?
elif "EMISSION" in bsdfs:
report(f"Because of {BLUE}EMISSION{R} node reflectance method identified as {BLUE}FLAT{R}")
attributes["ReflectanceMethod"] = "FLAT"
bsdf = bsdfs["EMISSION"]
attributes["SpecularHighlight"] = None
report(f"BSDF {GREEN}Color{R} saved as {GREEN}DiffuseColour{R}")
diffuse_color = bsdf.inputs["Color"].default_value
# TODO: remove?
elif "BSDF_PRINCIPLED" in bsdfs:
attributes["ReflectanceMethod"] = "NOTDEFINED"
report(f"Because of {BLUE}BSDF_PRINCIPLED{R} node reflectance method identified as {BLUE}PHYSICAL{R}")
attributes["ReflectanceMethod"] = "NOTDEFINED" if tool.Ifc.get_schema() != "IFC4X3" else "PHYSICAL"
bsdf = bsdfs["BSDF_PRINCIPLED"]
attributes["SpecularColour"] = round(bsdf.inputs["Metallic"].default_value, 3)
attributes["SpecularHighlight"] = {"IfcSpecularRoughness": round(bsdf.inputs["Roughness"].default_value, 3)}
report(f"BSDF {GREEN}Base Color{R} saved as {GREEN}DiffuseColour{R}")
diffuse_color = bsdf.inputs["Base Color"].default_value
report(f"BSDF {GREEN}Metallic{R} saved as {GREEN}SpecularColour{R}")
attributes["SpecularColour"] = round(bsdf.inputs["Metallic"].default_value, 3)
report(f"BSDF {GREEN}Roughness{R} saved as {GREEN}IfcSpecularRoughness{R}")
attributes["SpecularHighlight"] = {"IfcSpecularRoughness": round(bsdf.inputs["Roughness"].default_value, 3)}
report(f"BSDF {GREEN}Alpha{R} saved as {GREEN}Transparency{R}")
attributes["Transparency"] = 1 - bsdf.inputs["Alpha"].default_value
else:
report(f"No supported bsdfs found - reflectance method identified as {BLUE}NOTDEFINED{R}")
attributes["ReflectanceMethod"] = "NOTDEFINED"
attributes["SpecularHighlight"] = None
attributes["DiffuseColour"] = attributes["SurfaceColour"]
report(f"{GREEN}Viewport color{R} saved as {GREEN}DiffuseColour{R}")
attributes["DiffuseColour"] = viewport_color
return attributes
attributes["DiffuseColour"] = {
@@ -148,16 +292,12 @@ class Style(blenderbim.core.tool.Style):
"Green": diffuse_color[1],
"Blue": diffuse_color[2],
}
return attributes
@classmethod
def get_surface_rendering_style(cls, obj):
if obj.BIMMaterialProperties.ifc_style_id:
style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
items = [s for s in style.Styles if s.is_a("IfcSurfaceStyleRendering")]
if items:
return items[0]
style_elements = cls.get_style_elements(obj)
return style_elements.get("IfcSurfaceStyleRendering", None)
@classmethod
def get_surface_shading_attributes(cls, obj):