mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 01:11:40 +00:00
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:
@@ -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])
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user