BBIM Shader Graph Settings - setting uv mode for the textures

Now you can set UV mode that will be used to sample textures (previously it was always using UV data on geometry or breaking if it was otherwise). Basically now you can use autogenerated UV for the objects without UV.

https://i.imgur.com/WtYdpmU.png

Removed BIMMaterialProperties.ifc_coordinate_id as it was only used during the ifc_import and maintained by the `Loader.create_surface_style_with_textures` which is also used to update live shader graph that not always have specific IFC id for the UV.
This commit is contained in:
Andrej730
2023-11-24 15:26:48 +05:00
parent 5299a9d8a2
commit 03a6ee9121
6 changed files with 87 additions and 25 deletions
+17 -4
View File
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import os
import re
import bpy
@@ -38,7 +39,7 @@ from blenderbim.bim.module.drawing.prop import ANNOTATION_TYPES_DATA
class MaterialCreator:
def __init__(self, ifc_import_settings, ifc_importer):
def __init__(self, ifc_import_settings: IfcImportSettings, ifc_importer: IfcImporter):
self.mesh = None
self.obj = None
self.materials = {}
@@ -125,9 +126,21 @@ class MaterialCreator:
return
for style_id in style_ids:
material = self.styles[style_id]
ifc_coordinate_id = material.BIMMaterialProperties.ifc_coordinate_id
if ifc_coordinate_id != 0:
self.load_texture_map(tool.Ifc.get().by_id(ifc_coordinate_id))
def get_ifc_coordinate(style_id):
style = self.ifc_importer.file.by_id(style_id)
if coords := getattr(style, "IsMappedTo", None):
coords = coords[0]
# IfcTextureCoordinateGenerator handled in the style shader graph
if coords.is_a("IfcIndexedTextureMap"):
return coords
# TODO: support IfcTextureMap
if coords.is_a("IfcTextureMap"):
print(f"WARNING. IfcTextureMap texture coordinates is not supported.")
return
if coords := get_ifc_coordinate(style_id):
self.load_texture_map(coords)
if self.mesh.materials.find(material.name) == -1:
self.mesh.materials.append(material)
return True
@@ -162,6 +162,7 @@ def update_shading_style(self, context):
# 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", ""),
@@ -171,6 +172,12 @@ REFLECTANCE_METHODS = [
("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:
@@ -209,7 +216,6 @@ class BIMStyleProperties(PropertyGroup):
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",
@@ -217,6 +223,16 @@ class BIMStyleProperties(PropertyGroup):
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",
@@ -247,6 +263,8 @@ class BIMStyleProperties(PropertyGroup):
)
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,
@@ -423,6 +423,7 @@ class BIM_PT_STYLE_GRAPH(Panel):
if not bpy.data.filepath:
layout.label(text="Save .blend file to keep relative paths", icon="ERROR")
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])
-2
View File
@@ -439,7 +439,6 @@ class BIMMaterialProperties(PropertyGroup):
attributes: CollectionProperty(name="Attributes", type=Attribute)
# In Blender, a material object can map to an IFC material, IFC surface style, or both
ifc_style_id: IntProperty(name="IFC Style ID")
ifc_coordinate_id: IntProperty(name="IFC Coordinate ID")
shading_checksum: StringProperty(name="Shading Checksum")
@@ -466,6 +465,5 @@ class BIMFacet(PropertyGroup):
comparison: StringProperty(name="Comparison")
class BIMFilterGroup(PropertyGroup):
filters: CollectionProperty(type=BIMFacet, name="filters")
+45 -18
View File
@@ -131,6 +131,22 @@ class Loader(blenderbim.core.tool.Loader):
surface_style["SpecularHighlight"] = None
return surface_style
@classmethod
def surface_texture_to_dict(cls, surface_texture):
if isinstance(surface_texture, dict):
return surface_texture
mappings = surface_texture.IsMappedBy or []
surface_texture = surface_texture.get_info()
uv_mode = None
if mappings:
coordinates = mappings[0]
if coordinates.is_a("IfcTextureCoordinateGenerator") and coordinates.Mode == "COORD":
uv_mode = "Generated"
elif coordinates.is_a("IfcTextureCoordinateGenerator") and coordinates.Mode == "COORD-EYE":
uv_mode = "Camera"
surface_texture["uv_mode"] = uv_mode or "UV"
return surface_texture
@classmethod
def create_surface_style_rendering(cls, blender_material, surface_style):
surface_style = cls.surface_style_to_dict(surface_style)
@@ -192,12 +208,16 @@ class Loader(blenderbim.core.tool.Loader):
@classmethod
def create_surface_style_with_textures(cls, blender_material, rendering_style, texture_style):
"""supposed to be called after `create_surface_style_rendering`"""
if not isinstance(texture_style, list):
textures = [t.get_info() for t in texture_style.Textures]
if not isinstance(texture_style, list): # assume it's IfcSurfaceStyleWithTextures
textures = [cls.surface_texture_to_dict(t) for t in texture_style.Textures]
else:
textures = texture_style
rendering_style = cls.surface_style_to_dict(rendering_style)
# `rendering_style` is a dict and `textures` is a list of dicts
# containing ifc data, that way method can be called by just providing those dictionaries
# without actually changing IFC data
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}')
@@ -229,6 +249,16 @@ class Loader(blenderbim.core.tool.Loader):
if reflectance_method in ["PHYSICAL", "NOTDEFINED"]:
bsdf = tool.Blender.get_material_node(blender_material, "BSDF_PRINCIPLED")
SUPPORTED_PBR_TEXTURES = ("NORMAL", "EMISSIVE", "METALLICROUGHNESS", "OCCLUSION", "DIFFUSE")
if mode not in SUPPORTED_PBR_TEXTURES:
print(
f"WARNING. Texture with {mode} Mode is not supported for style with PHYSICAL reflectance method.\n"
f"Supported types are: {', '.join(SUPPORTED_PBR_TEXTURES)}\n"
f"Texture by path {image_url} will be skipped."
)
continue
if mode == "NORMAL":
# add normal map node
normalmap = blender_material.node_tree.nodes.new(type="ShaderNodeNormalMap")
@@ -317,6 +347,10 @@ class Loader(blenderbim.core.tool.Loader):
elif reflectance_method == "FLAT":
bsdf = tool.Blender.get_material_node(blender_material, "MIX_SHADER")
if mode != "EMISSIVE":
print(
"WARNING. Only EMISSIVE Mode textures are supported for style with FLAT reflectance method.\n"
f"{mode} Mode texture by path {image_url} will be skipped."
)
continue
# remove RGB node from `create_surface_style_rendering`
@@ -334,22 +368,15 @@ class Loader(blenderbim.core.tool.Loader):
# extend the image by repeating pixels on its edges if RepeatS or RepeatT is False
repeat_s = texture.get("RepeatS", True)
repeat_t = texture.get("RepeatT", True)
if node and (not repeat_s or not repeat_t):
if not repeat_s or not repeat_t:
node.extension = "EXTEND"
# TODO: add support for texture data not ifc elements
# IsMappedBy could only get with the entity_instance for IFC4/IFC4x3
if isinstance(texture, dict):
texture = tool.Ifc.get().by_id(texture['id'])
if node and getattr(texture, "IsMappedBy", None):
coordinates = texture.IsMappedBy[0]
coord = blender_material.node_tree.nodes.new(type="ShaderNodeTexCoord")
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":
blender_material.node_tree.links.new(coord.outputs["Camera"], node.inputs["Vector"])
else:
blender_material.node_tree.links.new(coord.outputs["UV"], node.inputs["Vector"])
# save the TextureMap id for set uv when set material to mesh
blender_material.BIMMaterialProperties.ifc_coordinate_id = coordinates.id()
coord = blender_material.node_tree.nodes.new(type="ShaderNodeTexCoord")
coord.location = node.location - Vector((200, 0))
if texture["uv_mode"] == "Generated":
blender_material.node_tree.links.new(coord.outputs["Generated"], node.inputs["Vector"])
elif texture["uv_mode"] == "Camera":
blender_material.node_tree.links.new(coord.outputs["Camera"], node.inputs["Vector"])
else: # uv_mode == UV
blender_material.node_tree.links.new(coord.outputs["UV"], node.inputs["Vector"])
+5
View File
@@ -147,6 +147,7 @@ class Style(blenderbim.core.tool.Style):
"Mode": prop_mode,
"type": "IfcImageTexture",
"URLReference": tool.Blender.blender_path_to_posix(path),
"uv_mode": props.uv_mode,
}
textures.append(texture_data)
@@ -184,13 +185,17 @@ class Style(blenderbim.core.tool.Style):
unused_texture_maps = list(STYLE_TEXTURE_PROPS_MAP.keys())
if texture_style:
uv_mode = None
for texture in texture_style.Textures:
texture_data = tool.Loader.surface_texture_to_dict(texture)
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: