mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Styles UI - moved UI from shader graph N-panel to Styles UI #3912
Moved all shading style and textures parameters from shader-graph N-panel to general Styles UI. Shader graph now also update in real time on any prop changes in Styles UI. Now there is practically no need to switch to shader graph to edit styles shaders - all parameters are available from the Styles UI. That also allows you to edit styles and see the results in viewport even if the edited style is not the one that's currently active. small demo - https://imgur.com/a/i2suoZw
This commit is contained in:
@@ -23,8 +23,9 @@ classes = (
|
||||
operator.ActivateExternalStyle,
|
||||
operator.AddPresentationStyle,
|
||||
operator.AddStyle,
|
||||
operator.AddSurfaceTexture,
|
||||
operator.BrowseExternalStyle,
|
||||
operator.ClearTextureMapPath,
|
||||
operator.RemoveTextureMap,
|
||||
operator.ChooseTextureMapPath,
|
||||
operator.DisableAddingPresentationStyle,
|
||||
operator.DisableEditingExternalStyle,
|
||||
@@ -46,6 +47,7 @@ classes = (
|
||||
operator.UpdateStyleColours,
|
||||
operator.UpdateStyleTextures,
|
||||
prop.Style,
|
||||
prop.Texture,
|
||||
prop.BIMStylesProperties,
|
||||
prop.BIMStyleProperties,
|
||||
ui.BIM_PT_styles,
|
||||
@@ -53,7 +55,6 @@ classes = (
|
||||
ui.BIM_PT_style_attributes,
|
||||
ui.BIM_PT_external_style_attributes,
|
||||
ui.BIM_UL_styles,
|
||||
ui.BIM_PT_STYLE_GRAPH,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -34,12 +34,23 @@ class StylesData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"styles_to_blender_material_names": cls.styles_to_blender_material_names(),
|
||||
"style_types": cls.style_types(),
|
||||
"total_styles": cls.total_styles(),
|
||||
"reflectance_methods": cls.reflectance_methods(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def styles_to_blender_material_names(cls):
|
||||
ifc_file = tool.Ifc.get()
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
materials = []
|
||||
for style in props.styles:
|
||||
material = tool.Ifc.get_object(ifc_file.by_id(style.ifc_definition_id))
|
||||
materials.append(material.name if material is not None else None)
|
||||
return materials
|
||||
|
||||
@classmethod
|
||||
def reflectance_methods(cls):
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcReflectanceMethodEnum")
|
||||
|
||||
@@ -24,8 +24,7 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.style as core
|
||||
import ifcopenshell.util.representation
|
||||
from pathlib import Path
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.style.data import StylesData, StyleAttributesData
|
||||
from blenderbim.bim.module.style.data import StyleAttributesData
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
@@ -33,14 +32,15 @@ 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.\n\n" + "ALT+CLICK to see saved values details"
|
||||
"Update IfcSurfaceStyleShading based on current blender material shading graph.\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
|
||||
# verbose 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
|
||||
@@ -51,12 +51,12 @@ class UpdateStyleColours(bpy.types.Operator, tool.Ifc.Operator):
|
||||
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):
|
||||
bl_idname = "bim.update_style_textures"
|
||||
bl_label = "Update Style Textures"
|
||||
bl_description = "Update IfcSurfaceStyleWithTextures based on current blender material shading graph"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
@@ -82,6 +82,7 @@ class RemoveStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
class AddStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_style"
|
||||
bl_label = "Add Style"
|
||||
bl_description = "Add IfcSurfaceStyle to the active material"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
@@ -120,6 +121,12 @@ class DisableEditingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
|
||||
style = tool.Ifc.get().by_id(props.is_editing_style)
|
||||
material = tool.Ifc.get_object(style)
|
||||
# just to trigger style update
|
||||
material.BIMStyleProperties.active_style_type = material.BIMStyleProperties.active_style_type
|
||||
|
||||
props.is_editing_style = 0
|
||||
|
||||
|
||||
@@ -146,19 +153,14 @@ class UpdateCurrentStyle(bpy.types.Operator):
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
update_all: bpy.props.BoolProperty(name="Update All", default=False, options={"SKIP_SAVE"})
|
||||
style_id: bpy.props.IntProperty(default=0, options={"SKIP_SAVE"})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
poll = (
|
||||
context.active_object is not None
|
||||
and context.active_object.active_material is not None
|
||||
and context.active_object.active_material.BIMMaterialProperties.ifc_style_id != 0
|
||||
)
|
||||
if not poll:
|
||||
cls.poll_message_set(
|
||||
"Object is not selected or material is not assigned or material doesn't have IFC Style"
|
||||
)
|
||||
return poll
|
||||
if not context.selected_objects:
|
||||
cls.poll_message_set("No objects selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
# updating all styles on shift+click
|
||||
@@ -168,16 +170,18 @@ class UpdateCurrentStyle(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
current_style_type = context.active_object.active_material.BIMStyleProperties.active_style_type
|
||||
style = tool.Ifc.get().by_id(self.style_id)
|
||||
material = tool.Ifc.get_object(style)
|
||||
current_style_type = material.BIMStyleProperties.active_style_type
|
||||
|
||||
if self.update_all:
|
||||
context.scene.BIMStylesProperties.active_style_type = current_style_type
|
||||
return {"FINISHED"}
|
||||
|
||||
materials = []
|
||||
for obj in context.selected_objects:
|
||||
mat = obj.active_material
|
||||
if mat and mat.BIMMaterialProperties.ifc_style_id != 0:
|
||||
mat.BIMStyleProperties.active_style_type = current_style_type
|
||||
for mat in obj.data.materials:
|
||||
if mat and mat.BIMMaterialProperties.ifc_style_id != 0:
|
||||
mat.BIMStyleProperties.active_style_type = current_style_type
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -231,7 +235,9 @@ class BrowseExternalStyle(bpy.types.Operator):
|
||||
description="List of objects in the .blend file",
|
||||
items=get_data_blocks,
|
||||
)
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
use_relative_path: bpy.props.BoolProperty(
|
||||
name="Use Relative Path", description="Save path relative to IFC file", default=True
|
||||
)
|
||||
directory: bpy.props.StringProperty(
|
||||
name="Directory",
|
||||
description="Start file browsing directory",
|
||||
@@ -266,11 +272,11 @@ class BrowseExternalStyle(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
if self.data_block_type == "0":
|
||||
self.report({"ERROR"}, "Select a data block type")
|
||||
self.report({"ERROR"}, "Select a data block type in the side panel of the file browser")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if self.data_block == "":
|
||||
self.report({"ERROR"}, "Select a data block")
|
||||
self.report({"ERROR"}, "Select a data block in the side panel of the file browser")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if not os.path.exists(self.filepath):
|
||||
@@ -413,10 +419,12 @@ class SelectByStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
class ChooseTextureMapPath(bpy.types.Operator):
|
||||
bl_idname = "bim.choose_texture_map_path"
|
||||
bl_label = "Choose Texture Map Path"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
texture_map_prop: bpy.props.StringProperty(default="")
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
texture_map_index: bpy.props.IntProperty(default=-1, options={"SKIP_SAVE"})
|
||||
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
use_relative_path: bpy.props.BoolProperty(
|
||||
name="Use Relative Path", description="Save path relative to IFC file", default=True
|
||||
)
|
||||
filepath: bpy.props.StringProperty(
|
||||
name="File Path", description="Filepath used to import from", maxlen=1024, default="", subtype="FILE_PATH"
|
||||
)
|
||||
@@ -427,16 +435,9 @@ class ChooseTextureMapPath(bpy.types.Operator):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
poll = getattr(context, "material", None)
|
||||
if not poll:
|
||||
cls.poll_message_set("Select a material")
|
||||
return poll
|
||||
|
||||
def execute(self, context):
|
||||
if not self.texture_map_prop:
|
||||
self.report({"ERROR"}, "Provide a texture map")
|
||||
if self.texture_map_index < 0:
|
||||
self.report({"ERROR"}, "Provide a texture map index")
|
||||
return {"CANCELLED"}
|
||||
|
||||
abs_path = Path(self.filepath)
|
||||
@@ -445,30 +446,26 @@ class ChooseTextureMapPath(bpy.types.Operator):
|
||||
else:
|
||||
image_filepath = abs_path
|
||||
|
||||
props = context.material.BIMStyleProperties
|
||||
setattr(props, self.texture_map_prop, image_filepath.as_posix())
|
||||
texture = context.scene.BIMStylesProperties.textures[self.texture_map_index]
|
||||
texture.path = image_filepath.as_posix()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ClearTextureMapPath(bpy.types.Operator):
|
||||
bl_idname = "bim.clear_texture_map_path"
|
||||
bl_label = "Clear Texture Map Path"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
texture_map_prop: bpy.props.StringProperty(default="")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
poll = getattr(context, "material", None)
|
||||
if not poll:
|
||||
cls.poll_message_set("Select a material")
|
||||
return poll
|
||||
class RemoveTextureMap(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_texture_map"
|
||||
bl_label = "Remove Texture Map"
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
texture_map_index: bpy.props.IntProperty(default=-1, options={"SKIP_SAVE"})
|
||||
|
||||
def execute(self, context):
|
||||
if not self.texture_map_prop:
|
||||
self.report({"ERROR"}, "Provide a texture map")
|
||||
if self.texture_map_index < 0:
|
||||
self.report({"ERROR"}, "Provide a texture map index")
|
||||
return {"CANCELLED"}
|
||||
props = context.material.BIMStyleProperties
|
||||
setattr(props, self.texture_map_prop, "")
|
||||
|
||||
props = context.scene.BIMStylesProperties
|
||||
props.textures.remove(self.texture_map_index)
|
||||
# just to trigger shader graph update
|
||||
props.surface_colour = props.surface_colour
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -546,74 +543,15 @@ class EnableEditingSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
style = tool.Ifc.get().by_id(self.style)
|
||||
|
||||
shading = None
|
||||
|
||||
surface_style = None
|
||||
for style2 in style.Styles:
|
||||
if style2.is_a() == self.ifc_class:
|
||||
surface_style = style2
|
||||
if style2.is_a() == "IfcSurfaceStyleShading":
|
||||
shading = style2
|
||||
|
||||
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue)
|
||||
|
||||
if surface_style:
|
||||
if self.ifc_class == "IfcSurfaceStyleShading":
|
||||
props.surface_colour = color_to_tuple(surface_style.SurfaceColour)
|
||||
props.transparency = surface_style.Transparency or 0.0
|
||||
elif self.ifc_class == "IfcSurfaceStyleRendering":
|
||||
props.surface_colour = color_to_tuple(surface_style.SurfaceColour)
|
||||
props.transparency = surface_style.Transparency or 0.0
|
||||
|
||||
if surface_style.DiffuseColour:
|
||||
props.is_diffuse_colour_null = False
|
||||
if surface_style.DiffuseColour.is_a("IfcColourRgb"):
|
||||
props.diffuse_colour_class = "IfcColourRgb"
|
||||
props.diffuse_colour = color_to_tuple(surface_style.DiffuseColour)
|
||||
else:
|
||||
props.diffuse_colour_class = "IfcNormalisedRatioMeasure"
|
||||
props.diffuse_colour_ratio = surface_style.DiffuseColour.wrappedValue
|
||||
else:
|
||||
props.is_diffuse_colour_null = False
|
||||
|
||||
if surface_style.SpecularColour:
|
||||
props.is_specular_colour_null = False
|
||||
if surface_style.SpecularColour.is_a("IfcColourRgb"):
|
||||
props.specular_colour_class = "IfcColourRgb"
|
||||
props.specular_colour = color_to_tuple(surface_style.SpecularColour)
|
||||
else:
|
||||
props.specular_colour_class = "IfcNormalisedRatioMeasure"
|
||||
props.specular_colour_ratio = surface_style.SpecularColour.wrappedValue
|
||||
else:
|
||||
props.is_specular_colour_null = False
|
||||
|
||||
if surface_style.SpecularHighlight:
|
||||
props.is_specular_highlight_null = False
|
||||
if surface_style.SpecularHighlight.is_a("IfcSpecularRoughness"):
|
||||
props.specular_highlight = surface_style.SpecularHighlight.wrappedValue
|
||||
else:
|
||||
props.is_specular_highlight_null = False # Exponent is meaningless
|
||||
else:
|
||||
props.is_specular_highlight_null = True
|
||||
|
||||
props.reflectance_method = surface_style.ReflectanceMethod
|
||||
elif self.ifc_class == "IfcExternallyDefinedSurfaceStyle":
|
||||
attributes = props.external_style_attributes
|
||||
attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(surface_style, attributes)
|
||||
else:
|
||||
if self.ifc_class == "IfcSurfaceStyleRendering":
|
||||
if shading:
|
||||
props.surface_colour = color_to_tuple(shading.SurfaceColour)
|
||||
props.transparency = shading.Transparency or 0.0
|
||||
elif self.ifc_class == "IfcExternallyDefinedSurfaceStyle":
|
||||
attributes = props.external_style_attributes
|
||||
attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(self.ifc_class, attributes)
|
||||
|
||||
props.is_editing_style = self.style
|
||||
props.is_editing_class = self.ifc_class
|
||||
tool.Style.set_surface_style_props()
|
||||
|
||||
surface_style = tool.Style.get_style_elements(style).get(self.ifc_class, None)
|
||||
if self.ifc_class == "IfcExternallyDefinedSurfaceStyle":
|
||||
attributes = props.external_style_attributes
|
||||
attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(surface_style or self.ifc_class, attributes)
|
||||
|
||||
|
||||
class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -625,16 +563,11 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.props = bpy.context.scene.BIMStylesProperties
|
||||
self.style = tool.Ifc.get().by_id(self.props.is_editing_style)
|
||||
|
||||
self.surface_style = None
|
||||
self.shading_style = None
|
||||
self.rendering_style = None
|
||||
for style2 in self.style.Styles:
|
||||
if style2.is_a() == self.props.is_editing_class:
|
||||
self.surface_style = style2
|
||||
if style2.is_a() == "IfcSurfaceStyleShading":
|
||||
self.shading_style = style2
|
||||
if style2.is_a() == "IfcSurfaceStyleRendering":
|
||||
self.rendering_style = style2
|
||||
style_elements = tool.Style.get_style_elements(self.style)
|
||||
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 self.surface_style:
|
||||
self.edit_existing_style()
|
||||
@@ -665,8 +598,27 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
attributes=self.get_rendering_attributes(),
|
||||
)
|
||||
tool.Loader.create_surface_style_rendering(material, self.surface_style)
|
||||
elif self.props.is_editing_class == "IfcSurfaceStyleWithTextures":
|
||||
# TODO: fix same issues as with creating new IfcSurfaceStyleWithTextures
|
||||
material = tool.Ifc.get_object(self.style)
|
||||
shading_style = self.rendering_style or self.shading_style
|
||||
if self.rendering_style is None:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"Editing texture styles without defining shading/rendering style is not yet supported. "
|
||||
"Define shading/render style first",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
textures = tool.Ifc.run("style.add_surface_textures", material=material, uv_maps=[])
|
||||
texture_style = tool.Ifc.run(
|
||||
"style.add_surface_style",
|
||||
style=self.style,
|
||||
ifc_class="IfcSurfaceStyleWithTextures",
|
||||
attributes={"Textures": textures},
|
||||
)
|
||||
tool.Loader.create_surface_style_with_textures(material, shading_style, texture_style)
|
||||
elif self.surface_style.is_a() == "IfcExternallyDefinedSurfaceStyle":
|
||||
surface_style = ifcopenshell.api.run(
|
||||
ifcopenshell.api.run(
|
||||
"style.edit_surface_style",
|
||||
tool.Ifc.get(),
|
||||
style=self.surface_style,
|
||||
@@ -693,6 +645,30 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
attributes=self.get_rendering_attributes(),
|
||||
)
|
||||
tool.Loader.create_surface_style_rendering(material, surface_style)
|
||||
elif self.props.is_editing_class == "IfcSurfaceStyleWithTextures":
|
||||
# TODO: rework add_surface_textures to work without blender
|
||||
# otherwise we lose textures that are not used in the shader
|
||||
# and we also doesn't recognize relative paths if .blend file is not saved
|
||||
# TODO: provide `uv_maps` - need to rework .get_uv_maps not to depend on a single representation
|
||||
material = tool.Ifc.get_object(self.style)
|
||||
shading_style = self.rendering_style or self.shading_style
|
||||
# TODO: support creating texture styles without defining shading style first
|
||||
if self.rendering_style is None:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"Creating texture styles without defining shading/rendering style is not yet supported. "
|
||||
"Define shading/render style first",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
textures = tool.Ifc.run("style.add_surface_textures", material=material, uv_maps=[])
|
||||
if textures:
|
||||
texture_style = tool.Ifc.run(
|
||||
"style.add_surface_style",
|
||||
style=self.style,
|
||||
ifc_class="IfcSurfaceStyleWithTextures",
|
||||
attributes={"Textures": textures},
|
||||
)
|
||||
tool.Loader.create_surface_style_with_textures(material, shading_style, texture_style)
|
||||
elif self.props.is_editing_class == "IfcExternallyDefinedSurfaceStyle":
|
||||
surface_style = ifcopenshell.api.run(
|
||||
"style.add_surface_style",
|
||||
@@ -726,7 +702,7 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if self.props.is_specular_highlight_null:
|
||||
specular_highlight = None
|
||||
else:
|
||||
specular_highlight = {"SpecularRoughness": self.props.specular_highlight}
|
||||
specular_highlight = {"IfcSpecularRoughness": self.props.specular_highlight}
|
||||
|
||||
return {
|
||||
"SurfaceColour": self.color_to_dict(self.props.surface_colour),
|
||||
@@ -741,6 +717,24 @@ class EditSurfaceStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"Red": x[0], "Green": x[1], "Blue": x[2]}
|
||||
|
||||
|
||||
class AddSurfaceTexture(bpy.types.Operator):
|
||||
bl_idname = "bim.add_surface_texture"
|
||||
bl_label = "Add Surface Texture"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if len(context.scene.BIMStylesProperties.textures) >= 8:
|
||||
cls.poll_message_set("Only 8 texture maps available")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStylesProperties
|
||||
props.textures.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SaveUVToStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.save_uv_to_style"
|
||||
bl_label = "Save UV To Style"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.style.data import StylesData
|
||||
from bpy.types import PropertyGroup
|
||||
@@ -31,7 +31,6 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def get_style_types(self, context):
|
||||
@@ -74,6 +73,45 @@ def update_shading_styles(self, context):
|
||||
tool.Style.change_current_style_type(mat, self.active_style_type)
|
||||
|
||||
|
||||
def update_shader_graph(self, context):
|
||||
props = self.id_data.BIMStylesProperties if isinstance(self, Texture) else self
|
||||
|
||||
if not props.update_graph:
|
||||
return
|
||||
style = tool.Ifc.get().by_id(props.is_editing_style)
|
||||
material = tool.Ifc.get_object(style)
|
||||
|
||||
shading_data = tool.Style.get_shading_style_data_from_props()
|
||||
textures_data = tool.Style.get_texture_style_data_from_props()
|
||||
tool.Loader.create_surface_style_rendering(material, shading_data)
|
||||
tool.Loader.create_surface_style_with_textures(material, shading_data, textures_data)
|
||||
|
||||
|
||||
UV_MODES = [
|
||||
("UV", "UV", "Actual UV data presented on the geometry"),
|
||||
("Generated", "Generated", "Automatically-generated UV from the vertex positions of the mesh"),
|
||||
("Camera", "Camera", "UV from position coordinate in camera space"),
|
||||
]
|
||||
|
||||
|
||||
TEXTURE_MAPS_MODS = (
|
||||
("DIFFUSE", "DIFFUSE", ""),
|
||||
("NORMAL", "NORMAL", ""),
|
||||
("METALLICROUGHNESS", "METALLICROUGHNESS", "Green Channel = Roughness,\nBlue Channel = Metallic"),
|
||||
("SPECULAR", "SPECULAR", ""),
|
||||
("SHININESS", "SHININESS", ""),
|
||||
("EMISSIVE", "EMISSIVE", ""),
|
||||
("OCCLUSION", "OCCLUSION", ""),
|
||||
("AMBIENT", "AMBIENT", ""),
|
||||
)
|
||||
|
||||
|
||||
class Texture(PropertyGroup):
|
||||
mode: EnumProperty(name="Type Of Texture", items=TEXTURE_MAPS_MODS, update=update_shader_graph)
|
||||
# NOTE: subtype `FILE_PATH` is not used to avoid .blend relative paths
|
||||
path: StringProperty(name="Texture Path", update=update_shader_graph)
|
||||
|
||||
|
||||
class BIMStylesProperties(PropertyGroup):
|
||||
is_adding: BoolProperty(name="Is Adding")
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
@@ -98,31 +136,81 @@ class BIMStylesProperties(PropertyGroup):
|
||||
name="Surface Style Class",
|
||||
default="IfcSurfaceStyleShading",
|
||||
)
|
||||
surface_colour: bpy.props.FloatVectorProperty(
|
||||
name="Surface Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
update_graph: BoolProperty(
|
||||
name="Update Shade Graph on Prop Change",
|
||||
description="Update shader graph in real time\nas you update style properties",
|
||||
default=True,
|
||||
)
|
||||
transparency: bpy.props.FloatProperty(name="Transparency", default=0.0, min=0.0, max=1.0)
|
||||
|
||||
# shading props
|
||||
surface_colour: bpy.props.FloatVectorProperty(
|
||||
name="Surface Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3, update=update_shader_graph
|
||||
)
|
||||
transparency: bpy.props.FloatProperty(
|
||||
name="Transparency", default=0.0, min=0.0, max=1.0, update=update_shader_graph
|
||||
)
|
||||
# TODO: do something on null?
|
||||
is_diffuse_colour_null: BoolProperty(name="Is Null")
|
||||
diffuse_colour_class: EnumProperty(
|
||||
items=[(x, x, "") for x in ("IfcColourRgb", "IfcNormalisedRatioMeasure")],
|
||||
name="Diffuse Colour Class",
|
||||
update=update_shader_graph,
|
||||
)
|
||||
diffuse_colour: bpy.props.FloatVectorProperty(
|
||||
name="Diffuse Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
name="Diffuse Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3, update=update_shader_graph
|
||||
)
|
||||
diffuse_colour_ratio: bpy.props.FloatProperty(
|
||||
name="Diffuse Ratio", default=0.0, min=0.0, max=1.0, update=update_shader_graph
|
||||
)
|
||||
diffuse_colour_ratio: bpy.props.FloatProperty(name="Diffuse Ratio", default=0.0, min=0.0, max=1.0)
|
||||
is_specular_colour_null: BoolProperty(name="Is Null")
|
||||
specular_colour_class: EnumProperty(
|
||||
items=[(x, x, "") for x in ("IfcColourRgb", "IfcNormalisedRatioMeasure")],
|
||||
name="Specular Colour Class",
|
||||
update=update_shader_graph,
|
||||
default="IfcNormalisedRatioMeasure",
|
||||
)
|
||||
specular_colour: bpy.props.FloatVectorProperty(
|
||||
name="Specular Colour", subtype="COLOR", default=(1, 1, 1), min=0.0, max=1.0, size=3
|
||||
name="Specular Colour",
|
||||
subtype="COLOR",
|
||||
default=(1, 1, 1),
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
size=3,
|
||||
update=update_shader_graph,
|
||||
)
|
||||
specular_colour_ratio: bpy.props.FloatProperty(
|
||||
name="Specular Ratio",
|
||||
description="Used as Metallic value in PHYSICAL Reflectance Method",
|
||||
default=0.0,
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
update=update_shader_graph,
|
||||
)
|
||||
specular_colour_ratio: bpy.props.FloatProperty(name="Specular Ratio", default=0.0, min=0.0, max=1.0)
|
||||
is_specular_highlight_null: BoolProperty(name="Is Null")
|
||||
specular_highlight: bpy.props.FloatProperty(name="Specular Highlight", default=0.0, min=0.0, max=1.0)
|
||||
reflectance_method: EnumProperty(name="Reflectance Method", items=get_reflectance_methods)
|
||||
specular_highlight: bpy.props.FloatProperty(
|
||||
name="Specular Highlight",
|
||||
description="Used as Roughness value in PHYSICAL Reflectance Method",
|
||||
default=0.0,
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
update=update_shader_graph,
|
||||
)
|
||||
reflectance_method: EnumProperty(
|
||||
name="Reflectance Method",
|
||||
items=get_reflectance_methods,
|
||||
update=update_shader_graph,
|
||||
)
|
||||
|
||||
# textures props
|
||||
textures: CollectionProperty(name="Textures", type=Texture)
|
||||
uv_mode: EnumProperty(
|
||||
name="UV Mode",
|
||||
description="Type of UV used for the textures",
|
||||
items=UV_MODES,
|
||||
default="UV",
|
||||
update=update_shader_graph,
|
||||
)
|
||||
|
||||
styles: CollectionProperty(name="Styles", type=Style)
|
||||
active_style_index: IntProperty(name="Active Style Index")
|
||||
active_style_type: EnumProperty(
|
||||
@@ -157,54 +245,13 @@ 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
|
||||
# based on https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcReflectanceMethodEnum.htm
|
||||
REFLECTANCE_METHODS = [
|
||||
("PHYSICAL", "PHYSICAL", ""),
|
||||
("FLAT", "FLAT", ""),
|
||||
("METAL", "METAL", ""),
|
||||
("MATT", "MATT", ""),
|
||||
("GLASS", "GLASS", ""),
|
||||
("NOTDEFINED", "NOTDEFINED", ""),
|
||||
]
|
||||
|
||||
UV_MODES = [
|
||||
("UV", "UV", "Actual UV data presented on the geometry"),
|
||||
("Generated", "Generated", "Automatically-generated UV from the vertex positions of the mesh"),
|
||||
("Camera", "Camera", "UV from position coordinate in camera space"),
|
||||
]
|
||||
|
||||
|
||||
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)
|
||||
textures_data = tool.Style.get_texture_style_from_props(material)
|
||||
tool.Loader.create_surface_style_rendering(material, style_data)
|
||||
tool.Loader.create_surface_style_with_textures(material, style_data, textures_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):
|
||||
# TODO: remove, as attributes already moved to styles ui
|
||||
attributes: CollectionProperty(name="Attributes", type=Attribute)
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
|
||||
external_style_attributes: CollectionProperty(name="External Style Attributes", type=Attribute)
|
||||
is_editing_external_style: BoolProperty(name="Is Editing External Style")
|
||||
|
||||
@@ -215,85 +262,3 @@ class BIMStyleProperties(PropertyGroup):
|
||||
default="Shading",
|
||||
update=update_shading_style,
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
uv_mode: EnumProperty(
|
||||
name="UV Mode",
|
||||
description="Type of UV used for the textures",
|
||||
items=UV_MODES,
|
||||
default="UV",
|
||||
update=update_shader_graph,
|
||||
)
|
||||
|
||||
# GLTF style properties
|
||||
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)
|
||||
|
||||
# texture paths
|
||||
normal_path: bpy.props.StringProperty(
|
||||
name="NormalMap",
|
||||
maxlen=1024,
|
||||
default="",
|
||||
update=update_shader_graph,
|
||||
)
|
||||
emissive_path: bpy.props.StringProperty(
|
||||
name="Emissive",
|
||||
maxlen=1024,
|
||||
default="",
|
||||
update=update_shader_graph,
|
||||
)
|
||||
metallic_roughness_path: bpy.props.StringProperty(
|
||||
name="Metallic/Roughness",
|
||||
maxlen=1024,
|
||||
default="",
|
||||
update=update_shader_graph,
|
||||
description="Green Channel = Roughness,\nBlue Channel = Metallic",
|
||||
)
|
||||
diffuse_path: bpy.props.StringProperty(
|
||||
name="Diffuse",
|
||||
maxlen=1024,
|
||||
default="",
|
||||
update=update_shader_graph,
|
||||
)
|
||||
occlusion_path: bpy.props.StringProperty(
|
||||
name="Occlusion",
|
||||
description="Note that occlusion isn't actually used in Blender shader, we're just storing the data",
|
||||
maxlen=1024,
|
||||
default="",
|
||||
update=update_shader_graph,
|
||||
)
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
|
||||
import bpy
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.style.data import StylesData, StyleAttributesData
|
||||
from bl_ui.properties_material import MaterialButtonsPanel
|
||||
from blenderbim.tool.style import TEXTURE_MAPS_BY_METHODS, STYLE_TEXTURE_PROPS_MAP
|
||||
|
||||
|
||||
class BIM_PT_styles(Panel):
|
||||
@@ -59,6 +59,7 @@ class BIM_PT_styles(Panel):
|
||||
|
||||
self.layout.template_list("BIM_UL_styles", "", self.props, "styles", self.props, "active_style_index")
|
||||
|
||||
# adding a new IfcSurfaceStyle
|
||||
if self.props.is_adding:
|
||||
box = self.layout.box()
|
||||
row = box.row()
|
||||
@@ -75,14 +76,23 @@ class BIM_PT_styles(Panel):
|
||||
row.operator("bim.add_presentation_style", text="Save New Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_adding_presentation_style", text="", icon="CANCEL")
|
||||
|
||||
# style ui tools
|
||||
if self.props.styles and self.props.active_style_index < len(self.props.styles):
|
||||
row = self.layout.row(align=True)
|
||||
style = self.props.styles[self.props.active_style_index]
|
||||
material_name = StylesData.data["styles_to_blender_material_names"][self.props.active_style_index]
|
||||
material = bpy.data.materials[material_name]
|
||||
|
||||
op = row.operator("bim.enable_editing_style", text="Edit Style", icon="GREASEPENCIL")
|
||||
op.style = style.ifc_definition_id
|
||||
row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF").style = style.ifc_definition_id
|
||||
row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(material.BIMStyleProperties, "active_style_type", icon="SHADING_RENDERED", text="")
|
||||
op = row.operator("bim.update_current_style", icon="FILE_REFRESH", text="")
|
||||
op.style_id = style.ifc_definition_id
|
||||
|
||||
if self.props.style_type == "IfcSurfaceStyle":
|
||||
self.layout.label(text="Surface Style Element:")
|
||||
col = self.layout.column(align=True)
|
||||
@@ -109,6 +119,7 @@ class BIM_PT_styles(Panel):
|
||||
op.ifc_class = "IfcExternallyDefinedSurfaceStyle"
|
||||
op.style = style.ifc_definition_id
|
||||
|
||||
# display style elements props during edit
|
||||
if self.props.is_editing_style:
|
||||
if self.props.is_editing_class == "IfcSurfaceStyle":
|
||||
blenderbim.bim.helper.draw_attributes(self.props.attributes, self.layout)
|
||||
@@ -121,8 +132,10 @@ class BIM_PT_styles(Panel):
|
||||
self.draw_surface_style_rendering()
|
||||
elif self.props.is_editing_class == "IfcExternallyDefinedSurfaceStyle":
|
||||
self.draw_externally_defined_surface_style()
|
||||
elif self.props.is_editing_class == "IfcSurfaceStyleWithTextures":
|
||||
self.draw_surface_style_with_textures()
|
||||
else:
|
||||
# TODO: UI for Texture, Lighting, Refract
|
||||
# TODO: UI Lighting, Refract
|
||||
self.layout.label(text=f"{self.props.is_editing_class} UI is not yet supported.")
|
||||
|
||||
def draw_surface_style_shading(self):
|
||||
@@ -142,8 +155,12 @@ class BIM_PT_styles(Panel):
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "reflectance_method")
|
||||
|
||||
if self.props.reflectance_method not in ("PHYSICAL", "NOTDEFINED", "FLAT"):
|
||||
self.layout.label(text=f"Supported reflectance methods are:")
|
||||
self.layout.label(text=f"PHYSICAL / NOTDEFINED / FLAT")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Diffuse")
|
||||
row.label(text="Emissive" if self.props.reflectance_method == "FLAT" else "Diffuse")
|
||||
row.prop(self.props, "diffuse_colour_class", text="")
|
||||
if self.props.diffuse_colour_class == "IfcColourRgb":
|
||||
row.prop(self.props, "diffuse_colour", text="")
|
||||
@@ -192,6 +209,28 @@ class BIM_PT_styles(Panel):
|
||||
row.operator("bim.edit_surface_style", text="Save Rendering Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_style", text="", icon="CANCEL")
|
||||
|
||||
def draw_surface_style_with_textures(self):
|
||||
textures = self.props.textures
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Style has {len(textures)} textures", icon="SHADING_TEXTURE")
|
||||
row.operator("bim.add_surface_texture", text="", icon="ADD")
|
||||
self.layout.prop(self.props, "uv_mode")
|
||||
|
||||
for i, texture in enumerate(textures):
|
||||
split = self.layout.split(factor=0.30, align=True)
|
||||
split.column(align=True).prop(texture, "mode", text="")
|
||||
|
||||
# path
|
||||
row = split.column(align=True).row(align=True)
|
||||
row.prop(texture, "path", text="")
|
||||
op_path = row.operator("bim.choose_texture_map_path", text="", icon="FILEBROWSER")
|
||||
op_clear = row.operator("bim.remove_texture_map", text="", icon="X")
|
||||
op_path.texture_map_index = op_clear.texture_map_index = i
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_surface_style", text="Save Texture Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_style", text="", icon="CANCEL")
|
||||
|
||||
def draw_externally_defined_surface_style(self):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.external_style_attributes, self.layout)
|
||||
row = self.layout.row(align=True)
|
||||
@@ -199,24 +238,6 @@ class BIM_PT_styles(Panel):
|
||||
row.operator("bim.disable_editing_style", text="", icon="CANCEL")
|
||||
|
||||
|
||||
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 = "Style"
|
||||
bl_idname = "BIM_PT_style"
|
||||
@@ -235,11 +256,15 @@ class BIM_PT_style(MaterialButtonsPanel, Panel):
|
||||
def draw(self, context):
|
||||
mat = context.material
|
||||
props = mat.BIMMaterialProperties
|
||||
draw_style_ui(self, context)
|
||||
row = self.layout.row(align=True)
|
||||
if not props.ifc_style_id:
|
||||
row.operator("bim.add_style", icon="ADD")
|
||||
return
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(mat, "diffuse_color", text="Viewport Color" if mat.use_nodes else "Render Color")
|
||||
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_attributes(Panel):
|
||||
@@ -369,66 +394,3 @@ class BIM_UL_styles(UIList):
|
||||
row2.label(text="", icon="LIGHT_POINT")
|
||||
elif style.name == "IfcExternallyDefinedSurfaceStyle":
|
||||
row2.label(text="", icon="APPEND_BLEND")
|
||||
|
||||
|
||||
class BIM_PT_STYLE_GRAPH(Panel):
|
||||
bl_idname = "BIM_PT_style_graph"
|
||||
bl_space_type = "NODE_EDITOR"
|
||||
bl_label = "Style Graph Settings"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BBIM"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return getattr(context, "material", None)
|
||||
|
||||
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")
|
||||
|
||||
if not (props.reflectance_method == "PHYSICAL" and props.diffuse_path) and not (
|
||||
props.reflectance_method == "FLAT" and props.emissive_path
|
||||
):
|
||||
prop_name = "Emissive Color" if props.reflectance_method == "FLAT" else "Diffuse Color"
|
||||
layout.prop(props, "diffuse_color", text=prop_name)
|
||||
|
||||
if props.reflectance_method == "PHYSICAL" and not props.metallic_roughness_path:
|
||||
layout.prop(props, "metallic")
|
||||
|
||||
if props.reflectance_method not in ("PHYSICAL", "FLAT", "NOTDEFINED") or (
|
||||
props.reflectance_method == "PHYSICAL" and not props.metallic_roughness_path
|
||||
):
|
||||
layout.prop(props, "roughness")
|
||||
|
||||
def add_texture_path(path_name):
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, path_name)
|
||||
op_path = row.operator("bim.choose_texture_map_path", text="", icon="FILEBROWSER")
|
||||
op_clear = row.operator("bim.clear_texture_map_path", text="", icon="X")
|
||||
op_path.texture_map_prop = op_clear.texture_map_prop = path_name
|
||||
|
||||
layout.separator()
|
||||
texture_maps = TEXTURE_MAPS_BY_METHODS.get(props.reflectance_method, [])
|
||||
if not texture_maps:
|
||||
return
|
||||
|
||||
layout.prop(props, "uv_mode")
|
||||
layout.label(text="Texture Maps:")
|
||||
for texture_type in texture_maps:
|
||||
add_texture_path(STYLE_TEXTURE_PROPS_MAP[texture_type])
|
||||
|
||||
@@ -80,7 +80,7 @@ class Loader(blenderbim.core.tool.Loader):
|
||||
# Transparency was added in IFC4
|
||||
if transparency := surface_style.get("Transparency", None):
|
||||
alpha = 1 - transparency
|
||||
blender_material.diffuse_color = surface_style["SurfaceColour"][:3] + (alpha,)
|
||||
blender_material.diffuse_color = surface_style["SurfaceColour"] + (alpha,)
|
||||
blender_material.use_nodes = False
|
||||
|
||||
@classmethod
|
||||
@@ -100,36 +100,39 @@ class Loader(blenderbim.core.tool.Loader):
|
||||
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)
|
||||
|
||||
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue)
|
||||
|
||||
def convert_ifc_color_or_factor(color_or_factor):
|
||||
if color_or_factor is None:
|
||||
return
|
||||
if color_or_factor.is_a("IfcColourRgb"):
|
||||
return ("IfcColourRgb", color_to_tuple(color_or_factor))
|
||||
# IfcNormalisedRatioMeasure
|
||||
return ("IfcNormalisedRatioMeasure", color_or_factor.wrappedValue)
|
||||
|
||||
# can be only IfcColourRgb
|
||||
if surface_style["SurfaceColour"]:
|
||||
surface_style["SurfaceColour"] = color_to_tuple(surface_style["SurfaceColour"])
|
||||
|
||||
if surface_style.get("DiffuseColour", None) and surface_style["DiffuseColour"].is_a("IfcColourRgb"):
|
||||
surface_style["DiffuseColour"] = ("IfcColourRgb", color_to_tuple(surface_style["DiffuseColour"]))
|
||||
if surface_style["type"] == "IfcSurfaceStyleShading":
|
||||
return surface_style
|
||||
|
||||
elif surface_style.get("DiffuseColour", None) 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["SurfaceColour"][:3]] + [1]
|
||||
surface_style["DiffuseColour"] = ("IfcNormalisedRatioMeasure", diffuse_color)
|
||||
else:
|
||||
surface_style["DiffuseColour"] = None
|
||||
# IfcSurfaceStyleRendering
|
||||
# IfcColourOrFactor
|
||||
surface_style["DiffuseColour"] = convert_ifc_color_or_factor(surface_style["DiffuseColour"])
|
||||
surface_style["SpecularColour"] = convert_ifc_color_or_factor(surface_style["SpecularColour"])
|
||||
|
||||
if surface_style.get("SpecularColour", None) and surface_style["SpecularColour"].is_a(
|
||||
"IfcNormalisedRatioMeasure"
|
||||
):
|
||||
surface_style["SpecularColour"] = surface_style["SpecularColour"].wrappedValue
|
||||
else:
|
||||
surface_style["SpecularColour"] = None
|
||||
if specular_highlight := surface_style["SpecularHighlight"]:
|
||||
if specular_highlight.is_a("IfcSpecularRoughness"):
|
||||
surface_style["SpecularHighlight"] = specular_highlight.wrappedValue
|
||||
else: # discard IfcSpecularExponent value
|
||||
surface_style["SpecularHighlight"] = None
|
||||
|
||||
# NOTE: IfcSurfaceStyleRendering also has following attributes but we ignore them
|
||||
# as they're about to get deprecated:
|
||||
# TransmissionColour, DiffuseTransmissionColour, ReflectionColour
|
||||
|
||||
if surface_style.get("SpecularHighlight", None) and surface_style["SpecularHighlight"].is_a(
|
||||
"IfcSpecularRoughness"
|
||||
):
|
||||
surface_style["SpecularHighlight"] = surface_style["SpecularHighlight"].wrappedValue
|
||||
else:
|
||||
surface_style["SpecularHighlight"] = None
|
||||
return surface_style
|
||||
|
||||
@classmethod
|
||||
@@ -158,20 +161,29 @@ class Loader(blenderbim.core.tool.Loader):
|
||||
print(f'WARNING. Unsupported reflectance method "{reflectance_method}" on style {surface_style}')
|
||||
return
|
||||
|
||||
# TODO: reset pins to default values if no values passed
|
||||
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"]:
|
||||
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
|
||||
bsdf.inputs["Base Color"].default_value = color_value + (1,)
|
||||
else: # "IfcNormalisedRatioMeasure"
|
||||
color_value = [v * color_value for v in surface_style["SurfaceColour"]]
|
||||
bsdf.inputs["Base Color"].default_value = color_value + (1,)
|
||||
|
||||
if surface_style["SpecularColour"]:
|
||||
bsdf.inputs["Metallic"].default_value = surface_style["SpecularColour"]
|
||||
color_type, color_value = surface_style["SpecularColour"]
|
||||
if color_type == "IfcNormalisedRatioMeasure":
|
||||
bsdf.inputs["Metallic"].default_value = color_value
|
||||
# IfcColourRgb is ignored
|
||||
|
||||
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"
|
||||
@@ -204,7 +216,7 @@ class Loader(blenderbim.core.tool.Loader):
|
||||
if surface_style["DiffuseColour"]:
|
||||
color_type, color_value = surface_style["DiffuseColour"]
|
||||
if color_type == "IfcColourRgb":
|
||||
rgb.outputs[0].default_value = color_value
|
||||
rgb.outputs[0].default_value = color_value + (1,)
|
||||
|
||||
@classmethod
|
||||
def create_surface_style_with_textures(cls, blender_material, rendering_style, texture_style):
|
||||
|
||||
@@ -22,7 +22,7 @@ import ifcopenshell
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
import os.path
|
||||
from mathutils import Color
|
||||
|
||||
# fmt: off
|
||||
TEXTURE_MAPS_BY_METHODS = {
|
||||
@@ -33,19 +33,11 @@ TEXTURE_MAPS_BY_METHODS = {
|
||||
|
||||
STYLE_PROPS_MAP = {
|
||||
"reflectance_method": "ReflectanceMethod",
|
||||
"diffuse_color": "DiffuseColour",
|
||||
"surface_color": "SurfaceColour",
|
||||
"diffuse_colour": "DiffuseColour",
|
||||
"surface_colour": "SurfaceColour",
|
||||
"transparency": "Transparency",
|
||||
"roughness": "SpecularHighlight",
|
||||
"metallic": "SpecularColour",
|
||||
}
|
||||
|
||||
STYLE_TEXTURE_PROPS_MAP = {
|
||||
"EMISSIVE": "emissive_path",
|
||||
"NORMAL": "normal_path",
|
||||
"METALLICROUGHNESS": "metallic_roughness_path",
|
||||
"DIFFUSE": "diffuse_path",
|
||||
"OCCLUSION": "occlusion_path",
|
||||
"specular_highlight": "SpecularHighlight",
|
||||
"specular_colour": "SpecularColour",
|
||||
}
|
||||
|
||||
|
||||
@@ -111,42 +103,60 @@ class Style(blenderbim.core.tool.Style):
|
||||
return
|
||||
|
||||
@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)
|
||||
def get_style_elements(cls, blender_material_or_style):
|
||||
if isinstance(blender_material_or_style, bpy.types.Material):
|
||||
if not blender_material_or_style.BIMMaterialProperties.ifc_style_id:
|
||||
return {}
|
||||
style = tool.Ifc.get().by_id(blender_material_or_style.BIMMaterialProperties.ifc_style_id)
|
||||
else:
|
||||
style = blender_material_or_style
|
||||
style_elements = {}
|
||||
for style in style.Styles:
|
||||
style_elements[style.is_a()] = style
|
||||
return style_elements
|
||||
|
||||
@classmethod
|
||||
def get_surface_style_from_props(cls, blender_material):
|
||||
"""convert blender style props to ifc props"""
|
||||
def get_shading_style_data_from_props(cls) -> dict:
|
||||
"""returns style data from blender props in similar way to `Loader.surface_style_to_dict`
|
||||
to be compatible with `Loader.create_surface_style_rendering`"""
|
||||
surface_style_data = dict()
|
||||
props = blender_material.BIMStyleProperties
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
|
||||
available_props = props.bl_rna.properties.keys()
|
||||
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"])
|
||||
class_prop_name = f"{prop_blender}_class"
|
||||
|
||||
# get detailed color properties if available
|
||||
if class_prop_name in available_props:
|
||||
prop_class = getattr(props, class_prop_name)
|
||||
if prop_class == "IfcColourRgb":
|
||||
prop_value = tuple(getattr(props, prop_blender))
|
||||
else: # IfcNormalisedRatioMeasure
|
||||
ratio_prop_name = f"{prop_blender}_ratio"
|
||||
prop_value = getattr(props, ratio_prop_name)
|
||||
prop_value = (prop_class, prop_value)
|
||||
else:
|
||||
prop_value = getattr(props, prop_blender)
|
||||
if isinstance(prop_value, Color):
|
||||
prop_value = tuple(prop_value)
|
||||
|
||||
surface_style_data[prop_ifc] = prop_value
|
||||
return surface_style_data
|
||||
|
||||
@classmethod
|
||||
def get_texture_style_from_props(cls, blender_material):
|
||||
props = blender_material.BIMStyleProperties
|
||||
def get_texture_style_data_from_props(cls) -> list[dict]:
|
||||
"""returns style data from blender props in similar way to `Loader.surface_texture_to_dict`
|
||||
to be compatible with `Loader.create_surface_style_with_textures`"""
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
|
||||
textures = []
|
||||
texture_maps = TEXTURE_MAPS_BY_METHODS[props.reflectance_method]
|
||||
for prop_mode in texture_maps:
|
||||
prop_name = STYLE_TEXTURE_PROPS_MAP[prop_mode]
|
||||
path = getattr(props, prop_name)
|
||||
if not path:
|
||||
for texture in props.textures:
|
||||
if not texture.path:
|
||||
continue
|
||||
texture_data = {
|
||||
"Mode": prop_mode,
|
||||
"Mode": texture.mode,
|
||||
"type": "IfcImageTexture",
|
||||
"URLReference": tool.Blender.blender_path_to_posix(path),
|
||||
"URLReference": texture.path,
|
||||
"uv_mode": props.uv_mode,
|
||||
}
|
||||
textures.append(texture_data)
|
||||
@@ -154,54 +164,70 @@ class Style(blenderbim.core.tool.Style):
|
||||
return textures
|
||||
|
||||
@classmethod
|
||||
def set_surface_style_props(cls, blender_material):
|
||||
"""set blender style props based on current surface material"""
|
||||
props = blender_material.BIMStyleProperties
|
||||
def set_surface_style_props(cls):
|
||||
"""set blender style props based on currently edited IfcSurfaceStyle,
|
||||
reset unrelated props to default values"""
|
||||
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
style = tool.Ifc.get().by_id(props.is_editing_style)
|
||||
# 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)
|
||||
style_elements = tool.Style.get_style_elements(style)
|
||||
surface_style = style_elements.get("IfcSurfaceStyleRendering", None)
|
||||
if surface_style is None:
|
||||
surface_style = style_elements.get("IfcSurfaceStyleShading", None)
|
||||
style_data = tool.Loader.surface_style_to_dict(surface_style) if surface_style else {}
|
||||
texture_style = style_elements.get("IfcSurfaceStyleWithTextures", None)
|
||||
|
||||
# in case we have just IfcSurfaceStyleShading
|
||||
if not surface_style:
|
||||
return
|
||||
|
||||
style_data = tool.Loader.surface_style_to_dict(surface_style)
|
||||
if style_data["ReflectanceMethod"] == "NOTDEFINED":
|
||||
style_data["ReflectanceMethod"] = "PHYSICAL"
|
||||
diffuse_color = style_data["DiffuseColour"]
|
||||
style_data["DiffuseColour"] = diffuse_color[1] if diffuse_color else None
|
||||
|
||||
for prop_blender, prop_ifc in STYLE_PROPS_MAP.items():
|
||||
prop_value = style_data[prop_ifc]
|
||||
def set_prop(prop_blender, prop_value):
|
||||
if prop_value is None:
|
||||
prop_value = tool.Blender.get_blender_prop_default_value(props, prop_blender)
|
||||
setattr(props, prop_blender, prop_value)
|
||||
|
||||
texture_maps = TEXTURE_MAPS_BY_METHODS[style_data["ReflectanceMethod"]]
|
||||
unused_texture_maps = list(STYLE_TEXTURE_PROPS_MAP.keys())
|
||||
available_props = props.bl_rna.properties.keys()
|
||||
# fallback value for reflectance method
|
||||
if style_data.get("ReflectanceMethod", None) is None:
|
||||
style_data["ReflectanceMethod"] = "NOTDEFINED"
|
||||
for prop_blender, prop_ifc in STYLE_PROPS_MAP.items():
|
||||
prop_value = style_data.get(prop_ifc, None)
|
||||
is_null = prop_value is None
|
||||
|
||||
# set null property if available
|
||||
null_prop_name = f"is_{prop_blender}_null"
|
||||
if null_prop_name in available_props:
|
||||
set_prop(null_prop_name, is_null)
|
||||
|
||||
# set detailed color properties if available
|
||||
class_prop_name = f"{prop_blender}_class"
|
||||
if class_prop_name in available_props:
|
||||
prop_class, prop_value = prop_value or (None, None)
|
||||
# set class enum
|
||||
set_prop(class_prop_name, prop_class)
|
||||
# set prop value
|
||||
ratio_prop_name = f"{prop_blender}_ratio"
|
||||
if prop_class == "IfcColourRgb":
|
||||
set_prop(prop_blender, prop_value)
|
||||
set_prop(ratio_prop_name, None)
|
||||
else: # IfcNormalisedRatioMeasure
|
||||
set_prop(ratio_prop_name, prop_value)
|
||||
set_prop(prop_blender, None)
|
||||
continue
|
||||
|
||||
set_prop(prop_blender, prop_value)
|
||||
|
||||
uv_mode = None
|
||||
props.textures.clear()
|
||||
if texture_style:
|
||||
for texture in texture_style.Textures:
|
||||
# we use surface_texture_to_dict as it calculates uv_mode
|
||||
texture_data = tool.Loader.surface_texture_to_dict(texture)
|
||||
texture_prop = props.textures.add()
|
||||
texture_prop.mode = texture_data["Mode"]
|
||||
texture_prop.path = texture_data["URLReference"]
|
||||
uv_mode = texture_data["uv_mode"]
|
||||
if texture.Mode not in texture_maps:
|
||||
print(f"WARNING. Unsupported texture mode: {texture.Mode}. Supported maps: {texture_maps}")
|
||||
continue
|
||||
prop_blender = STYLE_TEXTURE_PROPS_MAP.get(texture.Mode, None)
|
||||
setattr(props, prop_blender, texture.URLReference)
|
||||
unused_texture_maps.remove(texture.Mode)
|
||||
props.uv_mode = uv_mode if uv_mode else "UV"
|
||||
|
||||
# clear empty texture fields
|
||||
for texture_mode in unused_texture_maps:
|
||||
prop_blender = STYLE_TEXTURE_PROPS_MAP[texture_mode]
|
||||
setattr(props, prop_blender, "")
|
||||
|
||||
props["update_graph"] = prev_update_graph_value
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -73,7 +73,7 @@ Scenario: Disable editing styles
|
||||
And I add a material
|
||||
And I press "bim.add_style"
|
||||
And I press "bim.load_styles(style_type='IfcSurfaceStyle')"
|
||||
When I press "bim.disable_editing_style"
|
||||
When I press "bim.disable_editing_styles"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Select by style
|
||||
|
||||
@@ -48,11 +48,11 @@ class TestCreatingStyles(NewFile):
|
||||
|
||||
style_data = {
|
||||
"ReflectanceMethod": "NOTDEFINED",
|
||||
"DiffuseColour": ("IfcColourRgb", (0.5, 0.5, 0.5, 1.0)),
|
||||
"SurfaceColour": (0.3, 0.3, 0.3, 1.0),
|
||||
"DiffuseColour": ("IfcColourRgb", (0.5, 0.5, 0.5)),
|
||||
"SurfaceColour": (0.3, 0.3, 0.3),
|
||||
"Transparency": 0.3,
|
||||
"SpecularHighlight": 0.4,
|
||||
"SpecularColour": 0.03,
|
||||
"SpecularColour": ("IfcNormalisedRatioMeasure", 0.03),
|
||||
}
|
||||
texture_data = [
|
||||
{
|
||||
@@ -71,12 +71,13 @@ class TestCreatingStyles(NewFile):
|
||||
|
||||
bsdf = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED")
|
||||
alpha = 1 - style_data["Transparency"]
|
||||
diffuse_color = style_data["SurfaceColour"][:3] + (alpha,)
|
||||
diffuse_color = style_data["SurfaceColour"] + (alpha,)
|
||||
assert np.allclose(material.diffuse_color[:], diffuse_color)
|
||||
assert np.allclose(bsdf.inputs["Base Color"].default_value[:], style_data["DiffuseColour"][1])
|
||||
base_color = style_data["DiffuseColour"][1] + (1.0,)
|
||||
assert np.allclose(bsdf.inputs["Base Color"].default_value[:], base_color)
|
||||
assert np.isclose(bsdf.inputs["Alpha"].default_value, alpha)
|
||||
assert np.isclose(bsdf.inputs["Roughness"].default_value, style_data["SpecularHighlight"])
|
||||
assert np.isclose(bsdf.inputs["Metallic"].default_value, style_data["SpecularColour"])
|
||||
assert np.isclose(bsdf.inputs["Metallic"].default_value, style_data["SpecularColour"][1])
|
||||
|
||||
image_node = tool.Blender.get_material_node(material, "TEX_IMAGE")
|
||||
assert image_node.outputs["Color"].links[0].to_socket.name == "Base Color"
|
||||
|
||||
Reference in New Issue
Block a user