From 042fbefac686666190915d206600a5dab8e03066 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Thu, 7 Jul 2022 08:03:39 +0200 Subject: glTF importer/exporter: Manage some official Khronos Extensions about Materials KHR_materials_ior KHR_materials_sheen KHR_materials_specular KHR_materials_transmission KHR_materials_variants KHR_materials_emissive_strength KHR_materials_volume Documentation update is still in progress --- .../imp/gltf2_blender_pbrMetallicRoughness.py | 238 +++++++++++++++++++-- 1 file changed, 223 insertions(+), 15 deletions(-) (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py') diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 4c32c35d..6f6279dc 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -1,13 +1,20 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2018-2021 The glTF-Blender-IO authors. +from re import M import bpy from ...io.com.gltf2_io import TextureInfo, MaterialPBRMetallicRoughness from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group +from ..com.gltf2_blender_material_helpers import get_gltf_pbr_non_converted_name, create_gltf_pbr_non_converted_group from .gltf2_blender_texture import texture from .gltf2_blender_KHR_materials_clearcoat import \ clearcoat, clearcoat_roughness, clearcoat_normal - +from .gltf2_blender_KHR_materials_transmission import transmission +from .gltf2_blender_KHR_materials_ior import ior +from .gltf2_blender_KHR_materials_volume import volume +from .gltf2_blender_KHR_materials_specular import specular +from .gltf2_blender_KHR_materials_sheen import sheen +from ...io.com.gltf2_io_constants import GLTF_IOR class MaterialHelper: """Helper class. Stores material stuff to be passed around everywhere.""" @@ -20,6 +27,8 @@ class MaterialHelper: if pymat.pbr_metallic_roughness is None: pymat.pbr_metallic_roughness = \ MaterialPBRMetallicRoughness.from_dict({}) + self.settings_node = None + self.original_pbr_node = None def is_opaque(self): alpha_mode = self.pymat.alpha_mode @@ -36,15 +45,58 @@ def pbr_metallic_roughness(mh: MaterialHelper): """Creates node tree for pbrMetallicRoughness materials.""" pbr_node = mh.node_tree.nodes.new('ShaderNodeBsdfPrincipled') pbr_node.location = 10, 300 + additional_location = 40, -370 # For occlusion and/or volume / original PBR extensions + + # Set IOR to 1.5, this is the default in glTF + # This value may be overidden later if IOR extension is set on file + pbr_node.inputs['IOR'].default_value = GLTF_IOR - make_output_nodes( + if mh.pymat.occlusion_texture is not None: + if mh.settings_node is None: + mh.settings_node = make_settings_node(mh) + mh.settings_node.location = additional_location + mh.settings_node.width = 180 + additional_location = additional_location[0], additional_location[1] - 150 + + need_volume_node = False + if mh.pymat.extensions and 'KHR_materials_volume' in mh.pymat.extensions: + if 'thicknessFactor' in mh.pymat.extensions['KHR_materials_volume'] \ + and mh.pymat.extensions['KHR_materials_volume']['thicknessFactor'] != 0.0: + + need_volume_node = True + + # We also need glTF Settings Node, to set thicknessFactor and thicknessTexture + mh.settings_node = make_settings_node(mh) + mh.settings_node.location = additional_location + mh.settings_node.width = 180 + volume_location = additional_location + additional_location = additional_location[0], additional_location[1] - 150 + + need_velvet_node = False + if mh.pymat.extensions and 'KHR_materials_sheen' in mh.pymat.extensions: + need_velvet_node = True + + _, _, volume_socket, velvet_node = make_output_nodes( mh, location=(250, 260), + additional_location=additional_location, shader_socket=pbr_node.outputs[0], - make_emission_socket=False, - make_alpha_socket=False, + make_emission_socket=False, # is managed by Principled shader node + make_alpha_socket=False, # is managed by Principled shader node + make_volume_socket=need_volume_node, + make_velvet_socket=need_velvet_node ) + if mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions: + # We need glTF PBR Non Converted Extensions Node + mh.original_pbr_node = make_pbr_non_converted_extensions_node(mh) + mh.original_pbr_node.location = additional_location + mh.original_pbr_node.width = 180 + additional_location = additional_location[0], additional_location[1] - 150 + + if mh.pymat.extensions and 'KHR_materials_sheen': + pass #TOTOEXT + locs = calc_locations(mh) emission( @@ -75,13 +127,10 @@ def pbr_metallic_roughness(mh: MaterialHelper): ) if mh.pymat.occlusion_texture is not None: - node = make_settings_node(mh) - node.location = 40, -370 - node.width = 180 occlusion( mh, location=locs['occlusion'], - occlusion_socket=node.inputs['Occlusion'], + occlusion_socket=mh.settings_node.inputs['Occlusion'], ) clearcoat( @@ -102,6 +151,46 @@ def pbr_metallic_roughness(mh: MaterialHelper): normal_socket=pbr_node.inputs['Clearcoat Normal'], ) + transmission( + mh, + location=locs['transmission'], + transmission_socket=pbr_node.inputs['Transmission'] + ) + + if need_volume_node: + volume( + mh, + location=locs['volume_thickness'], + volume_socket=volume_socket, + thickness_socket=mh.settings_node.inputs[1] if mh.settings_node else None + ) + + specular( + mh, + location_specular=locs['specularTexture'], + location_specular_tint=locs['specularColorTexture'], + specular_socket=pbr_node.inputs['Specular'], + specular_tint_socket=pbr_node.inputs['Specular Tint'], + original_specular_socket=mh.original_pbr_node.inputs[0] if mh.original_pbr_node else None, + original_specularcolor_socket=mh.original_pbr_node.inputs[1] if mh.original_pbr_node else None, + location_original_specular=locs['original_specularTexture'], + location_original_specularcolor=locs['original_specularColorTexture'] + ) + + if need_velvet_node: + sheen( + mh, + location_sheenColor=locs['sheenColorTexture'], + location_sheenRoughness=locs['sheenRoughnessTexture'], + sheenColor_socket=velvet_node.inputs[0], + sheenRoughness_socket=velvet_node.inputs[1] + ) + + ior( + mh, + ior_socket=pbr_node.inputs['IOR'] + ) + def calc_locations(mh): """Calculate locations to place each bit of the node graph at.""" @@ -116,18 +205,53 @@ def calc_locations(mh): except Exception: clearcoat_ext = {} + try: + transmission_ext = mh.pymat.exntesions['KHR_materials_transmission'] + except: + transmission_ext = {} + + try: + volume_ext = mh.pymat.extensions['KHR_materials_volume'] + except Exception: + volume_ext = {} + + try: + specular_ext = mh.pymat.extensions['KHR_materials_specular'] + except: + specular_ext = {} + + try: + sheen_ext = mh.pymat.extensions['KHR_materials_sheen'] + except: + sheen_ext = {} + + locs['sheenColorTexture'] = (x, y) + if 'sheenColorTexture' in sheen_ext: + y -= height + locs['sheenRoughnessTexture'] = (x, y) + if 'sheenRoughnessTexture' in sheen_ext: + y -= height locs['base_color'] = (x, y) if mh.pymat.pbr_metallic_roughness.base_color_texture is not None or mh.vertex_color: y -= height locs['metallic_roughness'] = (x, y) if mh.pymat.pbr_metallic_roughness.metallic_roughness_texture is not None: y -= height + locs['specularTexture'] = (x, y) + if 'specularTexture' in specular_ext: + y -= height + locs['specularColorTexture'] = (x, y) + if 'specularColorTexture' in specular_ext: + y -= height locs['clearcoat'] = (x, y) if 'clearcoatTexture' in clearcoat_ext: y -= height locs['clearcoat_roughness'] = (x, y) if 'clearcoatRoughnessTexture' in clearcoat_ext: y -= height + locs['transmission'] = (x, y) + if 'transmissionTexture' in transmission_ext: + y -= height locs['emission'] = (x, y) if mh.pymat.emissive_texture is not None: y -= height @@ -140,6 +264,22 @@ def calc_locations(mh): locs['occlusion'] = (x, y) if mh.pymat.occlusion_texture is not None: y -= height + locs['volume_thickness'] = (x, y) + if 'thicknessTexture' in volume_ext: + y -= height + locs['original_specularTexture'] = (x, y) + if 'specularTexture' in specular_ext: + y -= height + locs['original_specularColorTexture'] = (x, y) + if 'specularColorTexture' in specular_ext: + y -= height + locs['original_sheenColorTexture'] = (x, y) + if 'sheenColorTexture' in sheen_ext: + y -= height + locs['original_sheenRoughnessTexture'] = (x, y) + if 'sheenRoughnessTexture' in sheen_ext: + y -= height + # Center things total_height = -y @@ -157,21 +297,29 @@ def calc_locations(mh): # [Texture] => [Emissive Factor] => -def emission(mh: MaterialHelper, location, color_socket, strength_socket=None): +def emission(mh: MaterialHelper, location, color_socket, strength_socket): x, y = location emissive_factor = mh.pymat.emissive_factor or [0, 0, 0] + strength = 1 + try: + # Get strength from KHR_materials_emissive_strength if exists + strength = mh.pymat.extensions['KHR_materials_emissive_strength']['emissiveStrength'] + except Exception: + pass + if color_socket is None: return if mh.pymat.emissive_texture is None: color_socket.default_value = emissive_factor + [1] + strength_socket.default_value = strength return # Put grayscale emissive factors into the Emission Strength e0, e1, e2 = emissive_factor if strength_socket and e0 == e1 == e2: - strength_socket.default_value = e0 + strength_socket.default_value = e0 * strength # Otherwise, use a multiply node for it else: @@ -189,6 +337,8 @@ def emission(mh: MaterialHelper, location, color_socket, strength_socket=None): x -= 200 + strength_socket.default_value = strength + texture( mh, tex_info=mh.pymat.emissive_texture, @@ -470,13 +620,16 @@ def occlusion(mh: MaterialHelper, location, occlusion_socket): def make_output_nodes( mh: MaterialHelper, location, + additional_location, shader_socket, make_emission_socket, make_alpha_socket, + make_volume_socket, + make_velvet_socket, # For sheen ): """ Creates the Material Output node and connects shader_socket to it. - If requested, it can also create places to hookup the emission/alpha + If requested, it can also create places to hookup the emission/alpha.sheen in between shader_socket and the Output node too. :return: a pair containing the sockets you should put emission and alpha @@ -484,6 +637,7 @@ def make_output_nodes( """ x, y = location emission_socket = None + velvet_node = None alpha_socket = None # Create an Emission node and add it to the shader. @@ -512,6 +666,31 @@ def make_output_nodes( x += 380 y += 125 + # Create an Velvet node add add it to the shader + # Note that you can not have Emission & Velvet at the same time + if make_velvet_socket: + # Velvet + node = mh.node_tree.nodes.new("ShaderNodeBsdfVelvet") + node.location = x + 50, y + 250 + # Node + velvet_node = node + # Outputs + velvet_output = node.outputs[0] + + # Add + node = mh.node_tree.nodes.new('ShaderNodeAddShader') + node.location = x + 250, y + 160 + # Inputs + mh.node_tree.links.new(node.inputs[0], velvet_output) + mh.node_tree.links.new(node.inputs[1], shader_socket) + # Outputs + shader_socket = node.outputs[0] + + + x += 380 + y += 125 + + # Mix with a Transparent BSDF. Mixing factor is the alpha value. if make_alpha_socket: # Transparent BSDF @@ -535,12 +714,23 @@ def make_output_nodes( y -= 210 # Material output - node = mh.node_tree.nodes.new('ShaderNodeOutputMaterial') - node.location = x + 70, y + 10 + node_output = mh.node_tree.nodes.new('ShaderNodeOutputMaterial') + node_output.location = x + 70, y + 10 + # Outputs - mh.node_tree.links.new(node.inputs[0], shader_socket) + mh.node_tree.links.new(node_output.inputs[0], shader_socket) + + # Volume Node + volume_socket = None + if make_volume_socket: + node = mh.node_tree.nodes.new('ShaderNodeVolumeAbsorption') + node.location = additional_location + # Outputs + mh.node_tree.links.new(node_output.inputs[1], node.outputs[0]) + volume_socket = node.outputs[0] - return emission_socket, alpha_socket + + return emission_socket, alpha_socket, volume_socket, velvet_node def make_settings_node(mh): @@ -560,3 +750,21 @@ def get_settings_group(): # Create a new node group gltf_node_group = create_settings_group(gltf_node_group_name) return gltf_node_group + +def make_pbr_non_converted_extensions_node(mh): + """ + Make a Group node with a hookup for PBR Non Converted Extensions. No effect in Blender, but + used to tell the exporter what the original map(s) should be. + """ + node = mh.node_tree.nodes.new('ShaderNodeGroup') + node.node_tree = get_pbr_non_converted_extensions_group() + return node + +def get_pbr_non_converted_extensions_group(): + gltf_node_group_name = get_gltf_pbr_non_converted_name() + if gltf_node_group_name in bpy.data.node_groups: + gltf_node_group = bpy.data.node_groups[gltf_node_group_name] + else: + # Create a new node group + gltf_node_group = create_gltf_pbr_non_converted_group(gltf_node_group_name) + return gltf_node_group \ No newline at end of file -- cgit v1.2.3 From c648829f83d66969cd6afb954630b8c4c6971f3f Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 11:50:04 +0200 Subject: glTF importer: update comment --- io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py') diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 6f6279dc..cc001b72 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -616,7 +616,9 @@ def occlusion(mh: MaterialHelper, location, occlusion_socket): ) -# => [Add Emission] => [Mix Alpha] => [Material Output] +# => [Add Emission] => [Mix Alpha] => [Material Output] if needed, only for SpecGlossiness +# => [Volume] => [Add Shader] => [Material Output] if needed +# => [Velvet] => [Add Shader] => [Material Output] if nedded def make_output_nodes( mh: MaterialHelper, location, -- cgit v1.2.3 From c58c8f3c3525247c6f584684260eae4ea87a57a3 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 12:07:14 +0200 Subject: glTF: rename glTF Settings node to glTF Material Output --- io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py') diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index cc001b72..696aa1fc 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -65,7 +65,7 @@ def pbr_metallic_roughness(mh: MaterialHelper): need_volume_node = True - # We also need glTF Settings Node, to set thicknessFactor and thicknessTexture + # We also need glTF Material Output Node, to set thicknessFactor and thicknessTexture mh.settings_node = make_settings_node(mh) mh.settings_node.location = additional_location mh.settings_node.width = 180 -- cgit v1.2.3 From ae29cfd1860e53e61d1b58d9c406638927a8ab40 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 8 Aug 2022 17:03:41 +0200 Subject: glTF: Merge glTF Material Output nodes into a single one --- .../imp/gltf2_blender_pbrMetallicRoughness.py | 32 ++-------------------- 1 file changed, 3 insertions(+), 29 deletions(-) (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py') diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 696aa1fc..d130f4e6 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -5,7 +5,6 @@ from re import M import bpy from ...io.com.gltf2_io import TextureInfo, MaterialPBRMetallicRoughness from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group -from ..com.gltf2_blender_material_helpers import get_gltf_pbr_non_converted_name, create_gltf_pbr_non_converted_group from .gltf2_blender_texture import texture from .gltf2_blender_KHR_materials_clearcoat import \ clearcoat, clearcoat_roughness, clearcoat_normal @@ -28,7 +27,6 @@ class MaterialHelper: pymat.pbr_metallic_roughness = \ MaterialPBRMetallicRoughness.from_dict({}) self.settings_node = None - self.original_pbr_node = None def is_opaque(self): alpha_mode = self.pymat.alpha_mode @@ -51,7 +49,7 @@ def pbr_metallic_roughness(mh: MaterialHelper): # This value may be overidden later if IOR extension is set on file pbr_node.inputs['IOR'].default_value = GLTF_IOR - if mh.pymat.occlusion_texture is not None: + if mh.pymat.occlusion_texture is not None or (mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions): if mh.settings_node is None: mh.settings_node = make_settings_node(mh) mh.settings_node.location = additional_location @@ -87,12 +85,6 @@ def pbr_metallic_roughness(mh: MaterialHelper): make_velvet_socket=need_velvet_node ) - if mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions: - # We need glTF PBR Non Converted Extensions Node - mh.original_pbr_node = make_pbr_non_converted_extensions_node(mh) - mh.original_pbr_node.location = additional_location - mh.original_pbr_node.width = 180 - additional_location = additional_location[0], additional_location[1] - 150 if mh.pymat.extensions and 'KHR_materials_sheen': pass #TOTOEXT @@ -171,8 +163,8 @@ def pbr_metallic_roughness(mh: MaterialHelper): location_specular_tint=locs['specularColorTexture'], specular_socket=pbr_node.inputs['Specular'], specular_tint_socket=pbr_node.inputs['Specular Tint'], - original_specular_socket=mh.original_pbr_node.inputs[0] if mh.original_pbr_node else None, - original_specularcolor_socket=mh.original_pbr_node.inputs[1] if mh.original_pbr_node else None, + original_specular_socket=mh.settings_node.inputs[2] if mh.settings_node else None, + original_specularcolor_socket=mh.settings_node.inputs[3] if mh.settings_node else None, location_original_specular=locs['original_specularTexture'], location_original_specularcolor=locs['original_specularColorTexture'] ) @@ -752,21 +744,3 @@ def get_settings_group(): # Create a new node group gltf_node_group = create_settings_group(gltf_node_group_name) return gltf_node_group - -def make_pbr_non_converted_extensions_node(mh): - """ - Make a Group node with a hookup for PBR Non Converted Extensions. No effect in Blender, but - used to tell the exporter what the original map(s) should be. - """ - node = mh.node_tree.nodes.new('ShaderNodeGroup') - node.node_tree = get_pbr_non_converted_extensions_group() - return node - -def get_pbr_non_converted_extensions_group(): - gltf_node_group_name = get_gltf_pbr_non_converted_name() - if gltf_node_group_name in bpy.data.node_groups: - gltf_node_group = bpy.data.node_groups[gltf_node_group_name] - else: - # Create a new node group - gltf_node_group = create_gltf_pbr_non_converted_group(gltf_node_group_name) - return gltf_node_group \ No newline at end of file -- cgit v1.2.3 From 99576a8478e1ea90bb112e1fd2ff6aa851c05bdc Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 9 Aug 2022 19:23:13 +0200 Subject: Cleanup: fix typos in blender addons Contributed by luzpaz. Differential Revision: https://developer.blender.org/D15646 --- io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py') diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index d130f4e6..b6b8e19f 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -46,7 +46,7 @@ def pbr_metallic_roughness(mh: MaterialHelper): additional_location = 40, -370 # For occlusion and/or volume / original PBR extensions # Set IOR to 1.5, this is the default in glTF - # This value may be overidden later if IOR extension is set on file + # This value may be overridden later if IOR extension is set on file pbr_node.inputs['IOR'].default_value = GLTF_IOR if mh.pymat.occlusion_texture is not None or (mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions): @@ -610,7 +610,7 @@ def occlusion(mh: MaterialHelper, location, occlusion_socket): # => [Add Emission] => [Mix Alpha] => [Material Output] if needed, only for SpecGlossiness # => [Volume] => [Add Shader] => [Material Output] if needed -# => [Velvet] => [Add Shader] => [Material Output] if nedded +# => [Velvet] => [Add Shader] => [Material Output] if needed def make_output_nodes( mh: MaterialHelper, location, -- cgit v1.2.3