diff options
author | Julien Duroure <julien.duroure@gmail.com> | 2020-01-29 23:57:22 +0300 |
---|---|---|
committer | Julien Duroure <julien.duroure@gmail.com> | 2020-01-29 23:57:22 +0300 |
commit | 2caaf64ab10be6f03e234fa5b533e341512fbed5 (patch) | |
tree | 887f6d0d7c58939c9558c81407486d38072f9d47 /io_scene_gltf2 | |
parent | e88c7ee2a7c091520efe006795817638cd4dcbb0 (diff) |
glTF importer: rewrite material import
Diffstat (limited to 'io_scene_gltf2')
12 files changed, 862 insertions, 1059 deletions
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index fc9f7a79..a2777b0c 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 2, 16), + "version": (1, 2, 17), 'blender': (2, 81, 6), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py index ebb357a8..2b26350d 100755 --- a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py @@ -13,50 +13,6 @@ # limitations under the License. -def get_output_node(node_tree): - """Retrieve output node.""" - output = [node for node in node_tree.nodes if node.type == 'OUTPUT_MATERIAL'][0] - return output - - -def get_output_surface_input(node_tree): - """Retrieve surface input of output node.""" - output_node = get_output_node(node_tree) - return output_node.inputs['Surface'] - - -def get_diffuse_texture(node_tree): - """Retrieve diffuse texture node.""" - for node in node_tree.nodes: - print(node.name) - if node.label == 'BASE COLOR': - return node - - return None - - -def get_preoutput_node_output(node_tree): - """Retrieve node just before output node.""" - output_node = get_output_node(node_tree) - preoutput_node = output_node.inputs['Surface'].links[0].from_node - - # Pre output node is Principled BSDF or any BSDF => BSDF - if 'BSDF' in preoutput_node.type: - return preoutput_node.outputs['BSDF'] - elif 'SHADER' in preoutput_node.type: - return preoutput_node.outputs['Shader'] - else: - print(preoutput_node.type) - - -def get_base_color_node(node_tree): - """Returns the last node of the diffuse block.""" - for node in node_tree.nodes: - if node.label == 'BASE COLOR': - return node - - return None - def get_gltf_node_name(): return "glTF Settings" diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py index 961b6f7a..f684a641 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py @@ -13,255 +13,153 @@ # limitations under the License. import bpy -from .gltf2_blender_material_utils import make_texture_block from ...io.com.gltf2_io import TextureInfo - - -class BlenderKHR_materials_pbrSpecularGlossiness(): - """Blender KHR_materials_pbrSpecularGlossiness extension.""" - def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) - - @staticmethod - def create(gltf, pbrSG, mat_name, vertex_color): - """KHR_materials_pbrSpecularGlossiness creation.""" - engine = bpy.context.scene.render.engine - if engine in ['CYCLES', 'BLENDER_EEVEE']: - BlenderKHR_materials_pbrSpecularGlossiness.create_nodetree(gltf, pbrSG, mat_name, vertex_color) - - @staticmethod - def create_nodetree(gltf, pbrSG, mat_name, vertex_color): - """Node tree creation.""" - material = bpy.data.materials[mat_name] - material.use_nodes = True - node_tree = material.node_tree - - # delete all nodes except output - for node in list(node_tree.nodes): - if not node.type == 'OUTPUT_MATERIAL': - node_tree.nodes.remove(node) - - output_node = node_tree.nodes[0] - output_node.location = 1000, 0 - - # create PBR node - diffuse = node_tree.nodes.new('ShaderNodeBsdfDiffuse') - diffuse.location = 0, 0 - glossy = node_tree.nodes.new('ShaderNodeBsdfGlossy') - glossy.location = 0, 100 - mix = node_tree.nodes.new('ShaderNodeMixShader') - mix.location = 500, 0 - - glossy.inputs[1].default_value = 1 - pbrSG['glossinessFactor'] - - if pbrSG['diffuse_type'] == gltf.SIMPLE: - if not vertex_color: - # change input values - diffuse.inputs[0].default_value = pbrSG['diffuseFactor'] - - else: - # Create vertexcolor node to get COLOR_0 data - vertexcolor_node = node_tree.nodes.new('ShaderNodeVertexColor') - vertexcolor_node.layer_name = 'Col' - vertexcolor_node.location = -500, 0 - - # links - node_tree.links.new(diffuse.inputs[0], vertexcolor_node.outputs[0]) - - elif pbrSG['diffuse_type'] == gltf.TEXTURE_FACTOR: - - # TODO alpha ? - if vertex_color: - # TODO tree locations - # Create vertexcolor / separate / math nodes - vertexcolor_node = node_tree.nodes.new('ShaderNodeVertexColor') - vertexcolor_node.layer_name = 'Col' - - separate_vertex_color = node_tree.nodes.new('ShaderNodeSeparateRGB') - math_vc_R = node_tree.nodes.new('ShaderNodeMath') - math_vc_R.operation = 'MULTIPLY' - - math_vc_G = node_tree.nodes.new('ShaderNodeMath') - math_vc_G.operation = 'MULTIPLY' - - math_vc_B = node_tree.nodes.new('ShaderNodeMath') - math_vc_B.operation = 'MULTIPLY' - - # create UV Map / Mapping / Texture nodes / separate & math and combine - text_node = make_texture_block( - gltf, - node_tree, - TextureInfo.from_dict(pbrSG['diffuseTexture']), - location=(-1000, 500), - label='DIFFUSE', - name='diffuseTexture', - ) - - combine = node_tree.nodes.new('ShaderNodeCombineRGB') - combine.location = -250, 500 - - math_R = node_tree.nodes.new('ShaderNodeMath') - math_R.location = -500, 750 - math_R.operation = 'MULTIPLY' - math_R.inputs[1].default_value = pbrSG['diffuseFactor'][0] - - math_G = node_tree.nodes.new('ShaderNodeMath') - math_G.location = -500, 500 - math_G.operation = 'MULTIPLY' - math_G.inputs[1].default_value = pbrSG['diffuseFactor'][1] - - math_B = node_tree.nodes.new('ShaderNodeMath') - math_B.location = -500, 250 - math_B.operation = 'MULTIPLY' - math_B.inputs[1].default_value = pbrSG['diffuseFactor'][2] - - separate = node_tree.nodes.new('ShaderNodeSeparateRGB') - separate.location = -750, 500 - - # Create links - if vertex_color: - node_tree.links.new(separate_vertex_color.inputs[0], vertexcolor_node.outputs[0]) - node_tree.links.new(math_vc_R.inputs[1], separate_vertex_color.outputs[0]) - node_tree.links.new(math_vc_G.inputs[1], separate_vertex_color.outputs[1]) - node_tree.links.new(math_vc_B.inputs[1], separate_vertex_color.outputs[2]) - node_tree.links.new(math_vc_R.inputs[0], math_R.outputs[0]) - node_tree.links.new(math_vc_G.inputs[0], math_G.outputs[0]) - node_tree.links.new(math_vc_B.inputs[0], math_B.outputs[0]) - node_tree.links.new(combine.inputs[0], math_vc_R.outputs[0]) - node_tree.links.new(combine.inputs[1], math_vc_G.outputs[0]) - node_tree.links.new(combine.inputs[2], math_vc_B.outputs[0]) - - else: - node_tree.links.new(combine.inputs[0], math_R.outputs[0]) - node_tree.links.new(combine.inputs[1], math_G.outputs[0]) - node_tree.links.new(combine.inputs[2], math_B.outputs[0]) - - # Common for both mode (non vertex color / vertex color) - node_tree.links.new(math_R.inputs[0], separate.outputs[0]) - node_tree.links.new(math_G.inputs[0], separate.outputs[1]) - node_tree.links.new(math_B.inputs[0], separate.outputs[2]) - - node_tree.links.new(separate.inputs[0], text_node.outputs[0]) - - node_tree.links.new(diffuse.inputs[0], combine.outputs[0]) - - elif pbrSG['diffuse_type'] == gltf.TEXTURE: - - # TODO alpha ? - if vertex_color: - # Create vertexcolor / separate / math nodes - vertexcolor_node = node_tree.nodes.new('ShaderNodeVertexColor') - vertexcolor_node.layer_name = 'Col' - vertexcolor_node.location = -2000, 250 - - separate_vertex_color = node_tree.nodes.new('ShaderNodeSeparateRGB') - separate_vertex_color.location = -1500, 250 - - math_vc_R = node_tree.nodes.new('ShaderNodeMath') - math_vc_R.operation = 'MULTIPLY' - math_vc_R.location = -1000, 750 - - math_vc_G = node_tree.nodes.new('ShaderNodeMath') - math_vc_G.operation = 'MULTIPLY' - math_vc_G.location = -1000, 500 - - math_vc_B = node_tree.nodes.new('ShaderNodeMath') - math_vc_B.operation = 'MULTIPLY' - math_vc_B.location = -1000, 250 - - combine = node_tree.nodes.new('ShaderNodeCombineRGB') - combine.location = -500, 500 - - separate = node_tree.nodes.new('ShaderNodeSeparateRGB') - separate.location = -1500, 500 - - # create UV Map / Mapping / Texture nodes / separate & math and combine - if vertex_color: - location = (-2000, 500) - else: - location = (-500, 500) - text_node = text_node = make_texture_block( - gltf, - node_tree, - TextureInfo.from_dict(pbrSG['diffuseTexture']), - location=location, - label='DIFFUSE', - name='diffuseTexture', - ) - - # Create links - if vertex_color: - node_tree.links.new(separate_vertex_color.inputs[0], vertexcolor_node.outputs[0]) - - node_tree.links.new(math_vc_R.inputs[1], separate_vertex_color.outputs[0]) - node_tree.links.new(math_vc_G.inputs[1], separate_vertex_color.outputs[1]) - node_tree.links.new(math_vc_B.inputs[1], separate_vertex_color.outputs[2]) - - node_tree.links.new(combine.inputs[0], math_vc_R.outputs[0]) - node_tree.links.new(combine.inputs[1], math_vc_G.outputs[0]) - node_tree.links.new(combine.inputs[2], math_vc_B.outputs[0]) - - node_tree.links.new(separate.inputs[0], text_node.outputs[0]) - - node_tree.links.new(diffuse.inputs[0], combine.outputs[0]) - - node_tree.links.new(math_vc_R.inputs[0], separate.outputs[0]) - node_tree.links.new(math_vc_G.inputs[0], separate.outputs[1]) - node_tree.links.new(math_vc_B.inputs[0], separate.outputs[2]) - - else: - node_tree.links.new(diffuse.inputs[0], text_node.outputs[0]) - - if pbrSG['specgloss_type'] == gltf.SIMPLE: - - combine = node_tree.nodes.new('ShaderNodeCombineRGB') - combine.inputs[0].default_value = pbrSG['specularFactor'][0] - combine.inputs[1].default_value = pbrSG['specularFactor'][1] - combine.inputs[2].default_value = pbrSG['specularFactor'][2] - - # links - node_tree.links.new(glossy.inputs[0], combine.outputs[0]) - - elif pbrSG['specgloss_type'] == gltf.TEXTURE: - spec_text = make_texture_block( - gltf, - node_tree, - TextureInfo.from_dict(pbrSG['specularGlossinessTexture']), - location=(-1000, 0), - label='SPECULAR GLOSSINESS', - name='specularGlossinessTexture', - colorspace='NONE', - ) - - # links - node_tree.links.new(glossy.inputs[0], spec_text.outputs[0]) - node_tree.links.new(mix.inputs[0], spec_text.outputs[1]) - - elif pbrSG['specgloss_type'] == gltf.TEXTURE_FACTOR: - spec_text = make_texture_block( - gltf, - node_tree, - TextureInfo.from_dict(pbrSG['specularGlossinessTexture']), - location=(-1000, 0), - label='SPECULAR GLOSSINESS', - name='specularGlossinessTexture', - colorspace='NONE', - ) - - spec_math = node_tree.nodes.new('ShaderNodeMath') - spec_math.operation = 'MULTIPLY' - spec_math.inputs[0].default_value = pbrSG['glossinessFactor'] - spec_math.location = -250, 100 - - # links - - node_tree.links.new(spec_math.inputs[1], spec_text.outputs[0]) - node_tree.links.new(mix.inputs[0], spec_text.outputs[1]) - node_tree.links.new(glossy.inputs[1], spec_math.outputs[0]) - node_tree.links.new(glossy.inputs[0], spec_text.outputs[0]) - - # link node to output - node_tree.links.new(mix.inputs[2], diffuse.outputs[0]) - node_tree.links.new(mix.inputs[1], glossy.outputs[0]) - node_tree.links.new(output_node.inputs[0], mix.outputs[0]) +from .gltf2_blender_pbrMetallicRoughness import \ + base_color, emission, normal, occlusion, make_output_nodes, make_settings_node +from .gltf2_blender_texture import texture + + +def pbr_specular_glossiness(mh): + """Creates node tree for pbrSpecularGlossiness materials.""" + # This does option #1 from + # https://github.com/KhronosGroup/glTF-Blender-IO/issues/303 + + # Sum a Glossy and Diffuse Shader + glossy_node = mh.node_tree.nodes.new('ShaderNodeBsdfGlossy') + diffuse_node = mh.node_tree.nodes.new('ShaderNodeBsdfDiffuse') + add_node = mh.node_tree.nodes.new('ShaderNodeAddShader') + glossy_node.location = 10, 220 + diffuse_node.location = 10, 0 + add_node.location = 230, 100 + mh.node_tree.links.new(add_node.inputs[0], glossy_node.outputs[0]) + mh.node_tree.links.new(add_node.inputs[1], diffuse_node.outputs[0]) + + emission_socket, alpha_socket = make_output_nodes( + mh, + location=(370, 250), + shader_socket=add_node.outputs[0], + make_emission_socket=mh.needs_emissive(), + make_alpha_socket=not mh.is_opaque(), + ) + + emission( + mh, + location=(-200, 860), + color_socket=emission_socket, + ) + + base_color( + mh, + is_diffuse=True, + location=(-200, 380), + color_socket=diffuse_node.inputs['Color'], + alpha_socket=alpha_socket, + ) + + specular_glossiness( + mh, + location=(-200, -100), + specular_socket=glossy_node.inputs['Color'], + roughness_socket=glossy_node.inputs['Roughness'], + ) + copy_socket( + mh, + copy_from=glossy_node.inputs['Roughness'], + copy_to=diffuse_node.inputs['Roughness'], + ) + + normal( + mh, + location=(-200, -580), + normal_socket=glossy_node.inputs['Normal'], + ) + copy_socket( + mh, + copy_from=glossy_node.inputs['Normal'], + copy_to=diffuse_node.inputs['Normal'], + ) + + if mh.pymat.occlusion_texture is not None: + node = make_settings_node(mh, location=(610, -1060)) + occlusion( + mh, + location=(510, -970), + occlusion_socket=node.inputs['Occlusion'], + ) + + +# [Texture] => [Spec/Gloss Factor] => [Gloss to Rough] => +def specular_glossiness(mh, location, specular_socket, roughness_socket): + x, y = location + spec_factor = mh.pymat.extensions \ + ['KHR_materials_pbrSpecularGlossiness'] \ + .get('specularFactor', [1, 1, 1]) + gloss_factor = mh.pymat.extensions \ + ['KHR_materials_pbrSpecularGlossiness'] \ + .get('glossinessFactor', 1) + spec_gloss_texture = mh.pymat.extensions \ + ['KHR_materials_pbrSpecularGlossiness'] \ + .get('specularGlossinessTexture', None) + if spec_gloss_texture is not None: + spec_gloss_texture = TextureInfo.from_dict(spec_gloss_texture) + + if spec_gloss_texture is None: + specular_socket.default_value = spec_factor + [1] + roughness_socket.default_value = 1 - gloss_factor + return + + # (1 - x) converts glossiness to roughness + node = mh.node_tree.nodes.new('ShaderNodeInvert') + node.label = 'Invert (Gloss to Rough)' + node.location = x - 140, y - 75 + # Outputs + mh.node_tree.links.new(roughness_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1 + glossiness_socket = node.inputs['Color'] + + x -= 250 + + # Mix in spec/gloss factor + if spec_factor != [1, 1, 1] or gloss_factor != 1: + if spec_factor != [1, 1, 1]: + node = mh.node_tree.nodes.new('ShaderNodeMixRGB') + node.label = 'Specular Factor' + node.location = x - 140, y + node.blend_type = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(specular_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1.0 + specular_socket = node.inputs['Color1'] + node.inputs['Color2'].default_value = spec_factor + [1] + + if gloss_factor != 1: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Glossiness Factor' + node.location = x - 140, y - 200 + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(glossiness_socket, node.outputs[0]) + # Inputs + glossiness_socket = node.inputs[0] + node.inputs[1].default_value = gloss_factor + + x -= 200 + + texture( + mh, + tex_info=spec_gloss_texture, + label='SPECULAR GLOSSINESS', + location=(x, y), + color_socket=specular_socket, + alpha_socket=glossiness_socket, + ) + + +def copy_socket(mh, copy_from, copy_to): + """Copy the links/default value from one socket to another.""" + copy_to.default_value = copy_from.default_value + for link in copy_from.links: + mh.node_tree.links.new(copy_to, link.from_socket) diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py index 2aae2c60..1d3cc0c2 100644 --- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py @@ -13,37 +13,44 @@ # limitations under the License. import bpy -from ...io.com.gltf2_io import MaterialPBRMetallicRoughness -from .gltf2_blender_pbrMetallicRoughness import BlenderPbr +from .gltf2_blender_pbrMetallicRoughness import base_color, make_output_nodes -class BlenderKHR_materials_unlit(): - """Blender KHR_materials_unlit extension.""" - def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) - @staticmethod - def create(gltf, material_index, unlit, mat_name, vertex_color): - """KHR_materials_unlit creation.""" - engine = bpy.context.scene.render.engine - if engine in ['CYCLES', 'BLENDER_EEVEE']: - BlenderKHR_materials_unlit.create_nodetree(gltf, material_index, unlit, mat_name, vertex_color) +def unlit(mh): + """Creates node tree for unlit materials.""" + # Emission node for the base color + emission_node = mh.node_tree.nodes.new('ShaderNodeEmission') + emission_node.location = 10, 126 - @staticmethod - def create_nodetree(gltf, material_index, unlit, mat_name, vertex_color): - """Node tree creation.""" - material = bpy.data.materials[mat_name] - material.use_nodes = True + # Lightpath trick: makes Emission visible only to camera rays. + # [Is Camera Ray] => [Mix] => + # [Transparent] => [ ] + # [Emission] => [ ] + lightpath_node = mh.node_tree.nodes.new('ShaderNodeLightPath') + transparent_node = mh.node_tree.nodes.new('ShaderNodeBsdfTransparent') + mix_node = mh.node_tree.nodes.new('ShaderNodeMixShader') + lightpath_node.location = 10, 600 + transparent_node.location = 10, 240 + mix_node.location = 260, 320 + mh.node_tree.links.new(mix_node.inputs['Fac'], lightpath_node.outputs['Is Camera Ray']) + mh.node_tree.links.new(mix_node.inputs[1], transparent_node.outputs[0]) + mh.node_tree.links.new(mix_node.inputs[2], emission_node.outputs[0]) + # Using transparency requires alpha blending for Eevee + if mh.is_opaque(): + mh.mat.blend_method = 'HASHED' # TODO check best result in eevee - pymaterial = gltf.data.materials[material_index] - if pymaterial.pbr_metallic_roughness is None: - # If no pbr material is set, we need to apply all default of pbr - pbr = {} - pbr["baseColorFactor"] = [1.0, 1.0, 1.0, 1.0] - pbr["metallicFactor"] = 1.0 - pbr["roughnessFactor"] = 1.0 - pymaterial.pbr_metallic_roughness = MaterialPBRMetallicRoughness.from_dict(pbr) - pymaterial.pbr_metallic_roughness.color_type = gltf.SIMPLE - pymaterial.pbr_metallic_roughness.metallic_type = gltf.SIMPLE + _emission_socket, alpha_socket = make_output_nodes( + mh, + location=(420, 280) if mh.is_opaque() else (150, 130), + shader_socket=mix_node.outputs[0], + make_emission_socket=False, + make_alpha_socket=not mh.is_opaque(), + ) - BlenderPbr.create_nodetree(gltf, pymaterial.pbr_metallic_roughness, mat_name, vertex_color, nodetype='unlit') + base_color( + mh, + location=(-200, 380), + color_socket=emission_node.inputs['Color'], + alpha_socket=alpha_socket, + ) diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py index 7b259759..49cbcf64 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py @@ -87,76 +87,6 @@ class BlenderGlTF(): for material in gltf.data.materials: material.blender_material = {} - if material.pbr_metallic_roughness: - # Init - material.pbr_metallic_roughness.color_type = gltf.SIMPLE - material.pbr_metallic_roughness.vertex_color = False - material.pbr_metallic_roughness.metallic_type = gltf.SIMPLE - - if material.pbr_metallic_roughness.base_color_texture: - material.pbr_metallic_roughness.color_type = gltf.TEXTURE - - if material.pbr_metallic_roughness.metallic_roughness_texture: - material.pbr_metallic_roughness.metallic_type = gltf.TEXTURE - - if material.pbr_metallic_roughness.base_color_factor: - if material.pbr_metallic_roughness.color_type == gltf.TEXTURE and \ - material.pbr_metallic_roughness.base_color_factor != [1.0, 1.0, 1.0, 1.0]: - material.pbr_metallic_roughness.color_type = gltf.TEXTURE_FACTOR - else: - material.pbr_metallic_roughness.base_color_factor = [1.0, 1.0, 1.0, 1.0] - - if material.pbr_metallic_roughness.metallic_factor is not None: - if material.pbr_metallic_roughness.metallic_type == gltf.TEXTURE \ - and material.pbr_metallic_roughness.metallic_factor != 1.0: - material.pbr_metallic_roughness.metallic_type = gltf.TEXTURE_FACTOR - else: - material.pbr_metallic_roughness.metallic_factor = 1.0 - - if material.pbr_metallic_roughness.roughness_factor is not None: - if material.pbr_metallic_roughness.metallic_type == gltf.TEXTURE \ - and material.pbr_metallic_roughness.roughness_factor != 1.0: - material.pbr_metallic_roughness.metallic_type = gltf.TEXTURE_FACTOR - else: - material.pbr_metallic_roughness.roughness_factor = 1.0 - - # pre compute material for KHR_materials_pbrSpecularGlossiness - if material.extensions is not None \ - and 'KHR_materials_pbrSpecularGlossiness' in material.extensions.keys(): - # Init - material.extensions['KHR_materials_pbrSpecularGlossiness']['diffuse_type'] = gltf.SIMPLE - material.extensions['KHR_materials_pbrSpecularGlossiness']['vertex_color'] = False - material.extensions['KHR_materials_pbrSpecularGlossiness']['specgloss_type'] = gltf.SIMPLE - - if 'diffuseTexture' in material.extensions['KHR_materials_pbrSpecularGlossiness'].keys(): - material.extensions['KHR_materials_pbrSpecularGlossiness']['diffuse_type'] = gltf.TEXTURE - - if 'diffuseFactor' in material.extensions['KHR_materials_pbrSpecularGlossiness'].keys(): - if material.extensions['KHR_materials_pbrSpecularGlossiness']['diffuse_type'] == gltf.TEXTURE \ - and material.extensions['KHR_materials_pbrSpecularGlossiness']['diffuseFactor'] != \ - [1.0, 1.0, 1.0, 1.0]: - material.extensions['KHR_materials_pbrSpecularGlossiness']['diffuse_type'] = \ - gltf.TEXTURE_FACTOR - else: - material.extensions['KHR_materials_pbrSpecularGlossiness']['diffuseFactor'] = \ - [1.0, 1.0, 1.0, 1.0] - - if 'specularGlossinessTexture' in material.extensions['KHR_materials_pbrSpecularGlossiness'].keys(): - material.extensions['KHR_materials_pbrSpecularGlossiness']['specgloss_type'] = gltf.TEXTURE - - if 'specularFactor' in material.extensions['KHR_materials_pbrSpecularGlossiness'].keys(): - if material.extensions['KHR_materials_pbrSpecularGlossiness']['specgloss_type'] == \ - gltf.TEXTURE \ - and material.extensions['KHR_materials_pbrSpecularGlossiness']['specularFactor'] != \ - [1.0, 1.0, 1.0]: - material.extensions['KHR_materials_pbrSpecularGlossiness']['specgloss_type'] = \ - gltf.TEXTURE_FACTOR - else: - material.extensions['KHR_materials_pbrSpecularGlossiness']['specularFactor'] = [1.0, 1.0, 1.0] - - if 'glossinessFactor' not in material.extensions['KHR_materials_pbrSpecularGlossiness'].keys(): - material.extensions['KHR_materials_pbrSpecularGlossiness']['glossinessFactor'] = 1.0 - # images if gltf.data.images is not None: for img in gltf.data.images: diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_map_emissive.py b/io_scene_gltf2/blender/imp/gltf2_blender_map_emissive.py deleted file mode 100755 index 35edd212..00000000 --- a/io_scene_gltf2/blender/imp/gltf2_blender_map_emissive.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2018-2019 The glTF-Blender-IO authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import bpy -from .gltf2_blender_material_utils import make_texture_block -from ..com.gltf2_blender_material_helpers import get_preoutput_node_output - - -class BlenderEmissiveMap(): - """Blender Emissive Map.""" - def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) - - @staticmethod - def create(gltf, material_idx, vertex_color, factor_only=False): - """Create emissive map.""" - engine = bpy.context.scene.render.engine - if engine in ['CYCLES', 'BLENDER_EEVEE']: - BlenderEmissiveMap.create_nodetree(gltf, material_idx, vertex_color, factor_only) - - def create_nodetree(gltf, material_idx, vertex_color, factor_only=False): - """Create node tree.""" - pymaterial = gltf.data.materials[material_idx] - - material = bpy.data.materials[pymaterial.blender_material[vertex_color]] - node_tree = material.node_tree - - # check if there is some emissive_factor on material - if pymaterial.emissive_factor is None: - # Default in glTF specification is 0/0/0 --> No emission - pymaterial.emissive_factor = [0.0, 0.0, 0.0] - - # retrieve principled node and output node - principled = get_preoutput_node_output(node_tree) - output = [node for node in node_tree.nodes if node.type == 'OUTPUT_MATERIAL'][0] - - # add nodes - emit = node_tree.nodes.new('ShaderNodeEmission') - emit.location = 0, 1000 - - if factor_only is False: - if pymaterial.emissive_factor != [1.0, 1.0, 1.0]: - mult_node = node_tree.nodes.new('ShaderNodeMixRGB') - mult_node.blend_type = 'MULTIPLY' - mult_node.inputs['Fac'].default_value = 1.0 - mult_node.location = -500, 1000 - mult_node.inputs['Color2'].default_value = [ - pymaterial.emissive_factor[0], - pymaterial.emissive_factor[1], - pymaterial.emissive_factor[2], - 1.0, - ] - text = make_texture_block( - gltf, - node_tree, - pymaterial.emissive_texture, - location=(-1000, 1000), - label='EMISSIVE', - name='emissiveTexture', - ) - - # create links - if pymaterial.emissive_factor != [1.0, 1.0, 1.0]: - node_tree.links.new(mult_node.inputs[1], text.outputs[0]) - node_tree.links.new(emit.inputs[0], mult_node.outputs[0]) - else: - node_tree.links.new(emit.inputs[0], text.outputs[0]) - - else: - emissive_color = pymaterial.emissive_factor + [1] # add alpha - emit.inputs[0].default_value = emissive_color - - - add = node_tree.nodes.new('ShaderNodeAddShader') - add.location = 500, 500 - - # following links will modify PBR node tree - node_tree.links.new(add.inputs[0], emit.outputs[0]) - node_tree.links.new(add.inputs[1], principled) - node_tree.links.new(output.inputs[0], add.outputs[0]) - diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_map_normal.py b/io_scene_gltf2/blender/imp/gltf2_blender_map_normal.py deleted file mode 100755 index a0d19146..00000000 --- a/io_scene_gltf2/blender/imp/gltf2_blender_map_normal.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2018-2019 The glTF-Blender-IO authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import bpy -from .gltf2_blender_material_utils import make_texture_block - - -class BlenderNormalMap(): - """Blender Normal map.""" - def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) - - @staticmethod - def create(gltf, material_idx, vertex_color): - """Creation of Normal map.""" - engine = bpy.context.scene.render.engine - if engine in ['CYCLES', 'BLENDER_EEVEE']: - BlenderNormalMap.create_nodetree(gltf, material_idx, vertex_color) - - def create_nodetree(gltf, material_idx, vertex_color): - """Creation of Nodetree.""" - pymaterial = gltf.data.materials[material_idx] - - material = bpy.data.materials[pymaterial.blender_material[vertex_color]] - node_tree = material.node_tree - - # retrieve principled node and output node - principled = None - diffuse = None - glossy = None - if len([node for node in node_tree.nodes if node.type == "BSDF_PRINCIPLED"]) != 0: - principled = [node for node in node_tree.nodes if node.type == "BSDF_PRINCIPLED"][0] - else: - # No principled, we are probably coming from extension - diffuse = [node for node in node_tree.nodes if node.type == "BSDF_DIFFUSE"][0] - glossy = [node for node in node_tree.nodes if node.type == "BSDF_GLOSSY"][0] - - # add nodes - text = make_texture_block( - gltf, - node_tree, - pymaterial.normal_texture, - location=(-500, -500), - label='NORMALMAP', - name='normalTexture', - colorspace='NONE', - ) - - normalmap_node = node_tree.nodes.new('ShaderNodeNormalMap') - normalmap_node.location = -250, -500 - - tex_info = pymaterial.normal_texture - texcoord_idx = tex_info.tex_coord or 0 - if tex_info.extensions and 'KHR_texture_transform' in tex_info.extensions: - if 'texCoord' in tex_info.extensions['KHR_texture_transform']: - texcoord_idx = tex_info.extensions['KHR_texture_transform']['texCoord'] - - normalmap_node.uv_map = 'UVMap' if texcoord_idx == 0 else 'UVMap.%03d' % texcoord_idx - - # Set strength - if pymaterial.normal_texture.scale is not None: - normalmap_node.inputs[0].default_value = pymaterial.normal_texture.scale - else: - normalmap_node.inputs[0].default_value = 1.0 # Default - - # create links - node_tree.links.new(normalmap_node.inputs[1], text.outputs[0]) - - # following links will modify PBR node tree - if principled: - node_tree.links.new(principled.inputs[19], normalmap_node.outputs[0]) - if diffuse: - node_tree.links.new(diffuse.inputs[2], normalmap_node.outputs[0]) - if glossy: - node_tree.links.new(glossy.inputs[2], normalmap_node.outputs[0]) - diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_map_occlusion.py b/io_scene_gltf2/blender/imp/gltf2_blender_map_occlusion.py deleted file mode 100755 index 176e8ac5..00000000 --- a/io_scene_gltf2/blender/imp/gltf2_blender_map_occlusion.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2018-2019 The glTF-Blender-IO authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import bpy -from .gltf2_blender_image import BlenderImage -from .gltf2_blender_material_utils import make_texture_block -from ..com.gltf2_blender_material_helpers import get_gltf_node_name - - -class BlenderOcclusionMap(): - """Blender Occlusion map.""" - def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) - - @staticmethod - def create(gltf, material_idx, vertex_color): - """Occlusion map creation.""" - engine = bpy.context.scene.render.engine - if engine in ['CYCLES', 'BLENDER_EEVEE']: - BlenderOcclusionMap.create_nodetree(gltf, material_idx, vertex_color) - - def create_nodetree(gltf, material_idx, vertex_color): - """Nodetree creation.""" - pymaterial = gltf.data.materials[material_idx] - pytexture = gltf.data.textures[pymaterial.occlusion_texture.index] - - material = bpy.data.materials[pymaterial.blender_material[vertex_color]] - node_tree = material.node_tree - - # Pack texture. Occlusion is calculated from Cycles or Eevee, so do nothing - blender_image_name = None - if pytexture.source is not None: - BlenderImage.create(gltf, pytexture.source) - pyimg = gltf.data.images[pytexture.source] - blender_image_name = pyimg.blender_image_name - bpy.data.images[blender_image_name].use_fake_user = True - - # Create a fake node group for exporter - gltf_node_group_name = get_gltf_node_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 = bpy.data.node_groups.new(gltf_node_group_name, 'ShaderNodeTree') - gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion") - gltf_node_group.nodes.new('NodeGroupOutput') - gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') - gltf_node_group_input.location = -200, 0 - - # Set the node group inside material node tree - ao_node = node_tree.nodes.new('ShaderNodeGroup') - ao_node.node_tree = gltf_node_group - ao_node.location = 0, 200 - - # Check if the texture node already exists (if used by other parameter metal / roughness) - found = False - if blender_image_name is not None: - for node in [node for node in node_tree.nodes if node.type == "TEX_IMAGE"]: - if node.image and blender_image_name == node.image.name: - # This is our image ! - # Retrieve separate node if found - if node.outputs['Color'].is_linked: - for link in node.outputs['Color'].links: - if link.to_node.type == 'SEPRGB': - node_tree.links.new(ao_node.inputs[0], link.to_node.outputs[0]) - found = True - break - - if found: - break - - if not found: - # Need to create the texture node & separate node - separate = node_tree.nodes.new('ShaderNodeSeparateRGB') - separate.location = -500, 1500 - ao_node.location = 0, 1500 - text = make_texture_block( - gltf, - node_tree, - pymaterial.occlusion_texture, - location=(-1000, 1500), - label='OCCLUSION', - name='occlusionTexture', - ) - - # Links - node_tree.links.new(separate.inputs[0], text.outputs[0]) - node_tree.links.new(ao_node.inputs[0], separate.outputs[0]) - diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_material.py b/io_scene_gltf2/blender/imp/gltf2_blender_material.py index 8d5dd245..c2b48c46 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_material.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_material.py @@ -15,16 +15,9 @@ import bpy from ..com.gltf2_blender_extras import set_extras -from .gltf2_blender_pbrMetallicRoughness import BlenderPbr -from .gltf2_blender_KHR_materials_pbrSpecularGlossiness import BlenderKHR_materials_pbrSpecularGlossiness -from .gltf2_blender_KHR_materials_unlit import BlenderKHR_materials_unlit -from .gltf2_blender_map_emissive import BlenderEmissiveMap -from .gltf2_blender_map_normal import BlenderNormalMap -from .gltf2_blender_map_occlusion import BlenderOcclusionMap -from ..com.gltf2_blender_material_helpers import get_output_surface_input -from ..com.gltf2_blender_material_helpers import get_preoutput_node_output -from ..com.gltf2_blender_material_helpers import get_base_color_node -from ...io.com.gltf2_io import MaterialPBRMetallicRoughness +from .gltf2_blender_pbrMetallicRoughness import MaterialHelper, pbr_metallic_roughness +from .gltf2_blender_KHR_materials_pbrSpecularGlossiness import pbr_specular_glossiness +from .gltf2_blender_KHR_materials_unlit import unlit class BlenderMaterial(): @@ -37,141 +30,65 @@ class BlenderMaterial(): """Material creation.""" pymaterial = gltf.data.materials[material_idx] - if vertex_color is None: - if pymaterial.name is not None: - name = pymaterial.name - else: - name = "Material_" + str(material_idx) - else: - if pymaterial.name is not None: - name = pymaterial.name + "_" + vertex_color - else: - name = "Material_" + str(material_idx) + "_" + vertex_color + name = pymaterial.name + if name is None: + name = "Material_" + str(material_idx) + if vertex_color is not None: + name += "_" + vertex_color mat = bpy.data.materials.new(name) pymaterial.blender_material[vertex_color] = mat.name set_extras(mat, pymaterial.extras) + BlenderMaterial.set_double_sided(pymaterial, mat) + BlenderMaterial.set_alpha_mode(pymaterial, mat) + BlenderMaterial.set_viewport_color(pymaterial, mat, vertex_color) - mat.use_backface_culling = (pymaterial.double_sided != True) + mat.use_nodes = True + while mat.node_tree.nodes: # clear all nodes + mat.node_tree.nodes.remove(mat.node_tree.nodes[0]) + + mh = MaterialHelper(gltf, pymaterial, mat, vertex_color) - ignore_map = False - - if pymaterial.extensions is not None : - if 'KHR_materials_unlit' in pymaterial.extensions.keys(): - ignore_map = True - BlenderKHR_materials_unlit.create( - gltf, material_idx, - pymaterial.extensions['KHR_materials_unlit'], - mat.name, - vertex_color - ) - elif 'KHR_materials_pbrSpecularGlossiness' in pymaterial.extensions.keys(): - BlenderKHR_materials_pbrSpecularGlossiness.create( - gltf, pymaterial.extensions['KHR_materials_pbrSpecularGlossiness'], mat.name, vertex_color - ) + exts = pymaterial.extensions or {} + if 'KHR_materials_unlit' in exts: + unlit(mh) + elif 'KHR_materials_pbrSpecularGlossiness' in exts: + pbr_specular_glossiness(mh) else: - # create pbr material - if pymaterial.pbr_metallic_roughness is None: - # If no pbr material is set, we need to apply all default of pbr - pbr = {} - pbr["baseColorFactor"] = [1.0, 1.0, 1.0, 1.0] - pbr["metallicFactor"] = 1.0 - pbr["roughnessFactor"] = 1.0 - pymaterial.pbr_metallic_roughness = MaterialPBRMetallicRoughness.from_dict(pbr) - pymaterial.pbr_metallic_roughness.color_type = gltf.SIMPLE - pymaterial.pbr_metallic_roughness.metallic_type = gltf.SIMPLE - - BlenderPbr.create(gltf, pymaterial.pbr_metallic_roughness, mat.name, vertex_color) - - if ignore_map == False: - # add emission map if needed - if pymaterial.emissive_texture is not None: - BlenderEmissiveMap.create(gltf, material_idx, vertex_color) - elif pymaterial.emissive_factor is not None: - # add emissive factor only if there is not emissive texture - BlenderEmissiveMap.create(gltf, material_idx, vertex_color, factor_only=True) - - - # add normal map if needed - if pymaterial.normal_texture is not None: - BlenderNormalMap.create(gltf, material_idx, vertex_color) - - # add occlusion map if needed - # will be pack, but not used - if pymaterial.occlusion_texture is not None: - BlenderOcclusionMap.create(gltf, material_idx, vertex_color) - - if pymaterial.alpha_mode is not None and pymaterial.alpha_mode != 'OPAQUE': - BlenderMaterial.blender_alpha(gltf, material_idx, vertex_color, pymaterial.alpha_mode) + pbr_metallic_roughness(mh) @staticmethod - def blender_alpha(gltf, material_idx, vertex_color, alpha_mode): - """Set alpha.""" - pymaterial = gltf.data.materials[material_idx] - material = bpy.data.materials[pymaterial.blender_material[vertex_color]] + def set_double_sided(pymaterial, mat): + mat.use_backface_culling = (pymaterial.double_sided != True) - # Set alpha value in material + @staticmethod + def set_alpha_mode(pymaterial, mat): + alpha_mode = pymaterial.alpha_mode if alpha_mode == 'BLEND': - material.blend_method = 'BLEND' - elif alpha_mode == "MASK": - material.blend_method = 'CLIP' - alpha_cutoff = pymaterial.alpha_cutoff if pymaterial.alpha_cutoff is not None else 0.5 - material.alpha_threshold = alpha_cutoff - - node_tree = material.node_tree - # Add nodes for basic transparency - # Add mix shader between output and Principled BSDF - trans = node_tree.nodes.new('ShaderNodeBsdfTransparent') - trans.location = 750, -500 - mix = node_tree.nodes.new('ShaderNodeMixShader') - mix.location = 1000, 0 - - output_surface_input = get_output_surface_input(node_tree) - preoutput_node_output = get_preoutput_node_output(node_tree) - - link = output_surface_input.links[0] - node_tree.links.remove(link) - - # PBR => Mix input 1 - node_tree.links.new(preoutput_node_output, mix.inputs[1]) - - # Trans => Mix input 2 - node_tree.links.new(trans.outputs['BSDF'], mix.inputs[2]) - - # Mix => Output - node_tree.links.new(mix.outputs['Shader'], output_surface_input) - - # alpha blend factor - add = node_tree.nodes.new('ShaderNodeMath') - add.operation = 'ADD' - add.location = 750, -250 - - diffuse_factor = 1.0 - if pymaterial.extensions is not None and 'KHR_materials_pbrSpecularGlossiness' in pymaterial.extensions: - diffuse_factor = pymaterial.extensions['KHR_materials_pbrSpecularGlossiness']['diffuseFactor'][3] - elif pymaterial.pbr_metallic_roughness: - diffuse_factor = pymaterial.pbr_metallic_roughness.base_color_factor[3] - - add.inputs[0].default_value = abs(1.0 - diffuse_factor) - add.inputs[1].default_value = 0.0 - node_tree.links.new(add.outputs['Value'], mix.inputs[0]) - - # Take diffuse texture alpha into account if any - diffuse_texture = get_base_color_node(node_tree) - if diffuse_texture: - inverter = node_tree.nodes.new('ShaderNodeInvert') - inverter.location = 250, -250 - inverter.inputs[1].default_value = (1.0, 1.0, 1.0, 1.0) - node_tree.links.new(diffuse_texture.outputs['Alpha'], inverter.inputs[0]) - - mult = node_tree.nodes.new('ShaderNodeMath') - mult.operation = 'MULTIPLY' if pymaterial.alpha_mode == 'BLEND' else 'GREATER_THAN' - mult.location = 500, -250 - # Note that `1.0 - pymaterial.alpha_cutoff` is used due to the invert node above. - alpha_cutoff = 1.0 if pymaterial.alpha_mode == 'BLEND' else \ - 1.0 - pymaterial.alpha_cutoff if pymaterial.alpha_cutoff is not None else 0.5 - mult.inputs[1].default_value = alpha_cutoff - node_tree.links.new(inverter.outputs['Color'], mult.inputs[0]) - node_tree.links.new(mult.outputs['Value'], add.inputs[0]) + mat.blend_method = 'BLEND' + elif alpha_mode == 'MASK': + mat.blend_method = 'CLIP' + alpha_cutoff = pymaterial.alpha_cutoff + alpha_cutoff = alpha_cutoff if alpha_cutoff is not None else 0.5 + mat.alpha_threshold = alpha_cutoff + + @staticmethod + def set_viewport_color(pymaterial, mat, vertex_color): + # If there is no texture and no vertex color, use the base color as + # the color for the Solid view. + if vertex_color: + return + + exts = pymaterial.extensions or {} + if 'KHR_materials_pbrSpecularGlossiness' in exts: + # TODO + return + else: + pbr = pymaterial.pbr_metallic_roughness + if pbr is None or pbr.base_color_texture is not None: + return + color = pbr.base_color_factor or [1, 1, 1, 1] + + mat.diffuse_color = color diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 04e68823..4ea5f28c 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -13,252 +13,478 @@ # limitations under the License. import bpy -from .gltf2_blender_material_utils import make_texture_block -from ..com.gltf2_blender_conversion import texture_transform_gltf_to_blender - - -class BlenderPbr(): - """Blender Pbr.""" - def __new__(cls, *args, **kwargs): - raise RuntimeError("%s should not be instantiated" % cls) - - def create(gltf, pypbr, mat_name, vertex_color): - """Pbr creation.""" - engine = bpy.context.scene.render.engine - if engine in ['CYCLES', 'BLENDER_EEVEE']: - BlenderPbr.create_nodetree(gltf, pypbr, mat_name, vertex_color) - - def create_nodetree(gltf, pypbr, mat_name, vertex_color, nodetype='principled'): - """Nodetree creation.""" - material = bpy.data.materials[mat_name] - material.use_nodes = True - node_tree = material.node_tree - - # If there is no diffuse texture, but only a color, wihtout - # vertex_color, we set this color in viewport color - if pypbr.color_type == gltf.SIMPLE and not vertex_color: - # Manage some change in beta version on 20190129 - if len(material.diffuse_color) == 3: - material.diffuse_color = pypbr.base_color_factor[:3] - else: - material.diffuse_color = pypbr.base_color_factor - - # delete all nodes except output - for node in list(node_tree.nodes): - if not node.type == 'OUTPUT_MATERIAL': - node_tree.nodes.remove(node) - - output_node = node_tree.nodes[0] - output_node.location = 1250, 0 - - # create Main node - if nodetype == "principled": - main_node = node_tree.nodes.new('ShaderNodeBsdfPrincipled') - main_node.location = 0, 0 - elif nodetype == "unlit": - main_node = node_tree.nodes.new('ShaderNodeEmission') - main_node.location = 750, -300 - - if pypbr.color_type == gltf.SIMPLE: - - if not vertex_color: - - # change input values - main_node.inputs[0].default_value = pypbr.base_color_factor - if nodetype == "principled": - # TODO : currently set metallic & specular in same way - main_node.inputs[5].default_value = pypbr.metallic_factor - main_node.inputs[7].default_value = pypbr.roughness_factor - - else: - # Create attribute node to get COLOR_0 data - vertexcolor_node = node_tree.nodes.new('ShaderNodeVertexColor') - vertexcolor_node.layer_name = 'Col' - vertexcolor_node.location = -500, 0 - - if nodetype == "principled": - # TODO : currently set metallic & specular in same way - main_node.inputs[5].default_value = pypbr.metallic_factor - main_node.inputs[7].default_value = pypbr.roughness_factor - - # links - rgb_node = node_tree.nodes.new('ShaderNodeMixRGB') - rgb_node.blend_type = 'MULTIPLY' - rgb_node.inputs['Fac'].default_value = 1.0 - rgb_node.inputs['Color1'].default_value = pypbr.base_color_factor - node_tree.links.new(rgb_node.inputs['Color2'], vertexcolor_node.outputs[0]) - node_tree.links.new(main_node.inputs[0], rgb_node.outputs[0]) - - elif pypbr.color_type == gltf.TEXTURE_FACTOR: - - # TODO alpha ? - if vertex_color: - # TODO tree locations - # Create attribute / separate / math nodes - vertexcolor_node = node_tree.nodes.new('ShaderNodeVertexColor') - vertexcolor_node.layer_name = 'Col' - - vc_mult_node = node_tree.nodes.new('ShaderNodeMixRGB') - vc_mult_node.blend_type = 'MULTIPLY' - vc_mult_node.inputs['Fac'].default_value = 1.0 - - # create UV Map / Mapping / Texture nodes / separate & math and combine - text_node = make_texture_block( - gltf, - node_tree, - pypbr.base_color_texture, - location=(-1000, 500), - label='BASE COLOR', - name='baseColorTexture', - ) - - mult_node = node_tree.nodes.new('ShaderNodeMixRGB') - mult_node.blend_type = 'MULTIPLY' - mult_node.inputs['Fac'].default_value = 1.0 - mult_node.inputs['Color2'].default_value = [ - pypbr.base_color_factor[0], - pypbr.base_color_factor[1], - pypbr.base_color_factor[2], - pypbr.base_color_factor[3], - ] - - # Create links - if vertex_color: - node_tree.links.new(vc_mult_node.inputs[2], vertexcolor_node.outputs[0]) - node_tree.links.new(vc_mult_node.inputs[1], mult_node.outputs[0]) - node_tree.links.new(main_node.inputs[0], vc_mult_node.outputs[0]) - - else: - node_tree.links.new(main_node.inputs[0], mult_node.outputs[0]) - - # Common for both mode (non vertex color / vertex color) - node_tree.links.new(mult_node.inputs[1], text_node.outputs[0]) - - elif pypbr.color_type == gltf.TEXTURE: - - # TODO alpha ? - if vertex_color: - # Create attribute / separate / math nodes - vertexcolor_node = node_tree.nodes.new('ShaderNodeVertexColor') - vertexcolor_node.layer_name = 'Col' - vertexcolor_node.location = -2000, 250 - - vc_mult_node = node_tree.nodes.new('ShaderNodeMixRGB') - vc_mult_node.blend_type = 'MULTIPLY' - vc_mult_node.inputs['Fac'].default_value = 1.0 - - # create UV Map / Mapping / Texture nodes / separate & math and combine - if vertex_color: - location = -2000, 500 - else: - location = -500, 500 - text_node = make_texture_block( - gltf, - node_tree, - pypbr.base_color_texture, - location=location, - label='BASE COLOR', - name='baseColorTexture', - ) - - # Create links - if vertex_color: - node_tree.links.new(vc_mult_node.inputs[2], vertexcolor_node.outputs[0]) - node_tree.links.new(vc_mult_node.inputs[1], text_node.outputs[0]) - node_tree.links.new(main_node.inputs[0], vc_mult_node.outputs[0]) - - else: - node_tree.links.new(main_node.inputs[0], text_node.outputs[0]) - - if nodetype == 'principled': - # Says metallic, but it means metallic & Roughness values - if pypbr.metallic_type == gltf.SIMPLE: - main_node.inputs[4].default_value = pypbr.metallic_factor - main_node.inputs[7].default_value = pypbr.roughness_factor - - elif pypbr.metallic_type == gltf.TEXTURE: - - metallic_text = make_texture_block( - gltf, - node_tree, - pypbr.metallic_roughness_texture, - location=(-500, 0), - label='METALLIC ROUGHNESS', - name='metallicRoughnessTexture', - colorspace='NONE', - ) - - metallic_separate = node_tree.nodes.new('ShaderNodeSeparateRGB') - metallic_separate.location = -250, 0 - - # links - node_tree.links.new(metallic_separate.inputs[0], metallic_text.outputs[0]) - node_tree.links.new(main_node.inputs[4], metallic_separate.outputs[2]) # metallic - node_tree.links.new(main_node.inputs[7], metallic_separate.outputs[1]) # Roughness - - elif pypbr.metallic_type == gltf.TEXTURE_FACTOR: - - metallic_text = make_texture_block( - gltf, - node_tree, - pypbr.metallic_roughness_texture, - location=(-1000, 0), - label='METALLIC ROUGHNESS', - name='metallicRoughnessTexture', - colorspace='NONE', - ) - - metallic_separate = node_tree.nodes.new('ShaderNodeSeparateRGB') - metallic_separate.location = -500, 0 - - metallic_math = node_tree.nodes.new('ShaderNodeMath') - metallic_math.operation = 'MULTIPLY' - metallic_math.inputs[1].default_value = pypbr.metallic_factor - metallic_math.location = -250, 100 - - roughness_math = node_tree.nodes.new('ShaderNodeMath') - roughness_math.operation = 'MULTIPLY' - roughness_math.inputs[1].default_value = pypbr.roughness_factor - roughness_math.location = -250, -100 - - # links - node_tree.links.new(metallic_separate.inputs[0], metallic_text.outputs[0]) - - # metallic - node_tree.links.new(metallic_math.inputs[0], metallic_separate.outputs[2]) - node_tree.links.new(main_node.inputs[4], metallic_math.outputs[0]) - - # roughness - node_tree.links.new(roughness_math.inputs[0], metallic_separate.outputs[1]) - node_tree.links.new(main_node.inputs[7], roughness_math.outputs[0]) - - # link node to output - if nodetype == 'principled': - node_tree.links.new(output_node.inputs[0], main_node.outputs[0]) - elif nodetype == 'unlit': - mix = node_tree.nodes.new('ShaderNodeMixShader') - mix.location = 1000, 0 - path = node_tree.nodes.new('ShaderNodeLightPath') - path.location = 500, 300 - if pypbr.color_type != gltf.SIMPLE: - math = node_tree.nodes.new('ShaderNodeMath') - math.location = 750, 200 - math.operation = 'MULTIPLY' - - # Set material alpha mode to blend - # This is needed for Eevee - material.blend_method = 'HASHED' # TODO check best result in eevee - - transparent = node_tree.nodes.new('ShaderNodeBsdfTransparent') - transparent.location = 750, 0 - - node_tree.links.new(output_node.inputs[0], mix.outputs[0]) - node_tree.links.new(mix.inputs[2], main_node.outputs[0]) - node_tree.links.new(mix.inputs[1], transparent.outputs[0]) - if pypbr.color_type != gltf.SIMPLE: - node_tree.links.new(math.inputs[0], path.outputs[0]) - node_tree.links.new(math.inputs[1], text_node.outputs[1]) - node_tree.links.new(mix.inputs[0], math.outputs[0]) - else: - node_tree.links.new(mix.inputs[0], path.outputs[0]) +from ...io.com.gltf2_io import TextureInfo, MaterialPBRMetallicRoughness +from ..com.gltf2_blender_material_helpers import get_gltf_node_name +from .gltf2_blender_texture import texture + + +class MaterialHelper: + """Helper class. Stores material stuff to be passed around everywhere.""" + def __init__(self, gltf, pymat, mat, vertex_color): + self.gltf = gltf + self.pymat = pymat + self.mat = mat + self.node_tree = mat.node_tree + self.vertex_color = vertex_color + if pymat.pbr_metallic_roughness is None: + pymat.pbr_metallic_roughness = \ + MaterialPBRMetallicRoughness.from_dict({}) + + def is_opaque(self): + alpha_mode = self.pymat.alpha_mode + return alpha_mode is None or alpha_mode == 'OPAQUE' + + def needs_emissive(self): + return ( + self.pymat.emissive_texture is not None or + (self.pymat.emissive_factor or [0, 0, 0]) != [0, 0, 0] + ) + + +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 + + # Create material output. + # TODO: when the exporter can understand them, use the emission/alpha + # socket on the Principled node instead + emission_socket, alpha_socket = make_output_nodes( + mh, + location=(250, 260), + shader_socket=pbr_node.outputs[0], + make_emission_socket=mh.needs_emissive(), + make_alpha_socket=not mh.is_opaque(), + ) + + emission( + mh, + location=(-200, 860), + color_socket=emission_socket, + ) + + base_color( + mh, + location=(-200, 380), + color_socket=pbr_node.inputs['Base Color'], + alpha_socket=alpha_socket, + ) + + metallic_roughness( + mh, + location=(-200, -100), + metallic_socket=pbr_node.inputs['Metallic'], + roughness_socket=pbr_node.inputs['Roughness'], + ) + + normal( + mh, + location=(-200, -580), + normal_socket=pbr_node.inputs['Normal'], + ) + + if mh.pymat.occlusion_texture is not None: + node = make_settings_node(mh, location=(610, -1060)) + occlusion( + mh, + location=(510, -970), + occlusion_socket=node.inputs['Occlusion'], + ) + + +# These functions each create one piece of the node graph, slotting +# their outputs into the given socket, or setting its default value. +# location is roughly the upper-right corner of where to put nodes. + + +# [Texture] => [Emissive Factor] => +def emission(mh: MaterialHelper, location, color_socket): + x, y = location + emissive_factor = mh.pymat.emissive_factor or [0, 0, 0] + + if color_socket is None: + return + + if mh.pymat.emissive_texture is None: + color_socket.default_value = emissive_factor + [1] + return + + # Mix emissive factor + if emissive_factor != [1, 1, 1]: + node = mh.node_tree.nodes.new('ShaderNodeMixRGB') + node.label = 'Emissive Factor' + node.location = x - 140, y + node.blend_type = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(color_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1.0 + color_socket = node.inputs['Color1'] + node.inputs['Color2'].default_value = emissive_factor + [1] + + x -= 200 + + texture( + mh, + tex_info=mh.pymat.emissive_texture, + label='EMISSIVE', + location=(x, y), + color_socket=color_socket, + ) + + +# [Texture] => [Mix Colors] => [Color Factor] => +# [Vertex Color] => [Mix Alphas] => [Alpha Factor] => +def base_color( + mh: MaterialHelper, + location, + color_socket, + alpha_socket=None, + is_diffuse=False, +): + """Handle base color (= baseColorTexture * vertexColor * baseColorFactor).""" + x, y = location + pbr = mh.pymat.pbr_metallic_roughness + if not is_diffuse: + base_color_factor = pbr.base_color_factor + base_color_texture = pbr.base_color_texture + else: + # Handle pbrSpecularGlossiness's diffuse with this function too, + # since it's almost exactly the same as base color. + base_color_factor = \ + mh.pymat.extensions['KHR_materials_pbrSpecularGlossiness'] \ + .get('diffuseFactor', [1, 1, 1, 1]) + base_color_texture = \ + mh.pymat.extensions['KHR_materials_pbrSpecularGlossiness'] \ + .get('diffuseTexture', None) + if base_color_texture is not None: + base_color_texture = TextureInfo.from_dict(base_color_texture) + + if base_color_factor is None: + base_color_factor = [1, 1, 1, 1] + + if base_color_texture is None and not mh.vertex_color: + color_socket.default_value = base_color_factor + if alpha_socket is not None: + alpha_socket.default_value = base_color_factor[3] + return + + # Mix in base color factor + needs_color_factor = base_color_factor[:3] != [1, 1, 1] + needs_alpha_factor = base_color_factor[3] != 1.0 and alpha_socket is not None + if needs_color_factor or needs_alpha_factor: + # For now, always create the color factor node because the exporter + # reads the alpha value from here. Can get rid of "or needs_alpha_factor" + # when it learns to understand the alpha socket. + if needs_color_factor or needs_alpha_factor: + node = mh.node_tree.nodes.new('ShaderNodeMixRGB') + node.label = 'Color Factor' + node.location = x - 140, y + node.blend_type = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(color_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1.0 + color_socket = node.inputs['Color1'] + node.inputs['Color2'].default_value = base_color_factor + + if needs_alpha_factor: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Alpha Factor' + node.location = x - 140, y - 200 + # Outputs + mh.node_tree.links.new(alpha_socket, node.outputs[0]) + # Inputs + node.operation = 'MULTIPLY' + alpha_socket = node.inputs[0] + node.inputs[1].default_value = base_color_factor[3] + + x -= 200 + + # These are where the texture/vertex color node will put its output. + texture_color_socket = color_socket + texture_alpha_socket = alpha_socket + vcolor_color_socket = color_socket + vcolor_alpha_socket = alpha_socket + + # Mix texture and vertex color together + if base_color_texture is not None and mh.vertex_color: + node = mh.node_tree.nodes.new('ShaderNodeMixRGB') + node.label = 'Mix Vertex Color' + node.location = x - 140, y + node.blend_type = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(color_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1.0 + texture_color_socket = node.inputs['Color1'] + vcolor_color_socket = node.inputs['Color2'] + + if alpha_socket is not None: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Mix Vertex Alpha' + node.location = x - 140, y - 200 + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(alpha_socket, node.outputs[0]) + # Inputs + texture_alpha_socket = node.inputs[0] + vcolor_alpha_socket = node.inputs[1] + + x -= 200 + + # Vertex Color + if mh.vertex_color: + node = mh.node_tree.nodes.new('ShaderNodeVertexColor') + node.layer_name = 'Col' + node.location = x - 250, y - 240 + # Outputs + mh.node_tree.links.new(vcolor_color_socket, node.outputs['Color']) + if vcolor_alpha_socket is not None: + mh.node_tree.links.new(vcolor_alpha_socket, node.outputs['Alpha']) + + x -= 280 + + # Texture + if base_color_texture is not None: + texture( + mh, + tex_info=base_color_texture, + label='BASE COLOR' if not is_diffuse else 'DIFFUSE', + location=(x, y), + color_socket=texture_color_socket, + alpha_socket=texture_alpha_socket, + ) + + +# [Texture] => [Separate GB] => [Metal/Rough Factor] => +def metallic_roughness(mh: MaterialHelper, location, metallic_socket, roughness_socket): + x, y = location + pbr = mh.pymat.pbr_metallic_roughness + metal_factor = pbr.metallic_factor + rough_factor = pbr.roughness_factor + if metal_factor is None: + metal_factor = 1.0 + if rough_factor is None: + rough_factor = 1.0 + + if pbr.metallic_roughness_texture is None: + metallic_socket.default_value = metal_factor + roughness_socket.default_value = rough_factor + return + + if metal_factor != 1.0 or rough_factor != 1.0: + # Mix metal factor + if metal_factor != 1.0: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Metallic Factor' + node.location = x - 140, y + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(metallic_socket, node.outputs[0]) + # Inputs + metallic_socket = node.inputs[0] + node.inputs[1].default_value = metal_factor + + # Mix rough factor + if rough_factor != 1.0: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Roughness Factor' + node.location = x - 140, y - 200 + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(roughness_socket, node.outputs[0]) + # Inputs + roughness_socket = node.inputs[0] + node.inputs[1].default_value = rough_factor + + x -= 200 + + # Separate RGB + node = mh.node_tree.nodes.new('ShaderNodeSeparateRGB') + node.location = x - 150, y - 75 + # Outputs + mh.node_tree.links.new(metallic_socket, node.outputs['B']) + mh.node_tree.links.new(roughness_socket, node.outputs['G']) + # Inputs + color_socket = node.inputs[0] + + x -= 200 + + texture( + mh, + tex_info=pbr.metallic_roughness_texture, + label='METALLIC ROUGHNESS', + location=(x, y), + is_data=True, + color_socket=color_socket, + ) + + +# [Texture] => [Normal Map] => +def normal(mh: MaterialHelper, location, normal_socket): + x,y = location + tex_info = mh.pymat.normal_texture + + if tex_info is None: + return + + # Normal map + node = mh.node_tree.nodes.new('ShaderNodeNormalMap') + node.location = x - 150, y - 40 + # Set UVMap + uv_idx = tex_info.tex_coord or 0 + try: + uv_idx = tex_info.extensions['KHR_texture_transform']['texCoord'] + except Exception: + pass + node.uv_map = 'UVMap' if uv_idx == 0 else 'UVMap.%03d' % uv_idx + # Set strength + scale = tex_info.scale + scale = scale if scale is not None else 1 + node.inputs['Strength'].default_value = scale + # Outputs + mh.node_tree.links.new(normal_socket, node.outputs['Normal']) + # Inputs + color_socket = node.inputs['Color'] + + x -= 200 + + texture( + mh, + tex_info=tex_info, + label='NORMALMAP', + location=(x, y), + is_data=True, + color_socket=color_socket, + ) + + +# [Texture] => [Separate R] => +def occlusion(mh: MaterialHelper, location, occlusion_socket): + x, y = location + + if mh.pymat.occlusion_texture is None: + return + + # Separate RGB + node = mh.node_tree.nodes.new('ShaderNodeSeparateRGB') + node.location = x - 150, y - 75 + # Outputs + mh.node_tree.links.new(occlusion_socket, node.outputs['R']) + # Inputs + color_socket = node.inputs[0] + + x -= 200 + + texture( + mh, + tex_info=mh.pymat.occlusion_texture, + label='OCCLUSION', + location=(x, y), + is_data=True, + color_socket=color_socket, + ) + + +# => [Add Emission] => [Mix Alpha] => [Material Output] +def make_output_nodes( + mh: MaterialHelper, + location, + shader_socket, + make_emission_socket, + make_alpha_socket, +): + """ + Creates the Material Output node and connects shader_socket to it. + If requested, it can also create places to hookup the emission/alpha + in between shader_socket and the Output node too. + + :return: a pair containing the sockets you should put emission and alpha + in (None if not requested). + """ + x, y = location + emission_socket = None + alpha_socket = None + + # Create an Emission node and add it to the shader. + if make_emission_socket: + # Emission + node = mh.node_tree.nodes.new('ShaderNodeEmission') + node.location = x + 50, y + 250 + # Inputs + emission_socket = node.inputs[0] + # Outputs + emission_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], emission_output) + mh.node_tree.links.new(node.inputs[1], shader_socket) + # Outputs + shader_socket = node.outputs[0] + + if make_alpha_socket: + x += 200 + y += 175 + else: + x += 380 + y += 125 + + # Mix with a Transparent BSDF. Mixing factor is the alpha value. + if make_alpha_socket: + # Transparent BSDF + node = mh.node_tree.nodes.new('ShaderNodeBsdfTransparent') + node.location = x + 100, y - 350 + # Outputs + transparent_out = node.outputs[0] + + # Mix + node = mh.node_tree.nodes.new('ShaderNodeMixShader') + node.location = x + 340, y - 180 + # Inputs + alpha_socket = node.inputs[0] + mh.node_tree.links.new(node.inputs[1], transparent_out) + mh.node_tree.links.new(node.inputs[2], shader_socket) + # Outputs + shader_socket = node.outputs[0] + + + x += 480 + y -= 210 + + # Material output + node = mh.node_tree.nodes.new('ShaderNodeOutputMaterial') + node.location = x + 70, y + 10 + # Outputs + mh.node_tree.links.new(node.inputs[0], shader_socket) + + return emission_socket, alpha_socket + + +def make_settings_node(mh, location): + """ + Make a Group node with a hookup for Occlusion. No effect in Blender, but + used to tell the exporter what the occlusion map should be. + """ + node = mh.node_tree.nodes.new('ShaderNodeGroup') + node.node_tree = get_settings_group() + node.location = location + return node + + +def get_settings_group(): + gltf_node_group_name = get_gltf_node_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 = bpy.data.node_groups.new(gltf_node_group_name, 'ShaderNodeTree') + gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion") + gltf_node_group.nodes.new('NodeGroupOutput') + gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') + gltf_node_group_input.location = -200, 0 + return gltf_node_group diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_texture.py b/io_scene_gltf2/blender/imp/gltf2_blender_texture.py new file mode 100644 index 00000000..a0f90ddf --- /dev/null +++ b/io_scene_gltf2/blender/imp/gltf2_blender_texture.py @@ -0,0 +1,152 @@ +# Copyright 2018-2019 The glTF-Blender-IO authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import bpy +from .gltf2_blender_image import BlenderImage +from ..com.gltf2_blender_conversion import texture_transform_gltf_to_blender +from io_scene_gltf2.io.com.gltf2_io import Sampler +from io_scene_gltf2.io.com.gltf2_io_debug import print_console +from io_scene_gltf2.io.com.gltf2_io_constants import TextureFilter, TextureWrap + +def texture( + mh, + tex_info, + location, # Upper-right corner of the TexImage node + label, # Label for the TexImg node + color_socket, + alpha_socket=None, + is_data=False, +): + """Creates nodes for a TextureInfo and hooks up the color/alpha outputs.""" + x, y = location + + # Image Texture + tex_img = mh.node_tree.nodes.new('ShaderNodeTexImage') + tex_img.location = x - 240, y + tex_img.label = label + # Get image + pytexture = mh.gltf.data.textures[tex_info.index] + if pytexture.source is not None: + BlenderImage.create(mh.gltf, pytexture.source) + pyimg = mh.gltf.data.images[pytexture.source] + blender_image_name = pyimg.blender_image_name + if blender_image_name: + tex_img.image = bpy.data.images[blender_image_name] + # Set colorspace for data images + if is_data: + if tex_img.image: + tex_img.image.colorspace_settings.is_data = True + # Set wrapping/filtering + if pytexture.sampler is not None: + pysampler = mh.gltf.data.samplers[pytexture.sampler] + else: + pysampler = Sampler.from_dict({}) + set_filtering(tex_img, pysampler) + set_wrap_mode(tex_img, pysampler) + # Outputs + mh.node_tree.links.new(color_socket, tex_img.outputs['Color']) + if alpha_socket is not None: + mh.node_tree.links.new(alpha_socket, tex_img.outputs['Alpha']) + # Inputs + uv_socket = tex_img.inputs[0] + + x -= 340 + + # UV Transform (for KHR_texture_transform) + mapping = mh.node_tree.nodes.new('ShaderNodeMapping') + mapping.location = x - 160, y + 30 + mapping.vector_type = 'POINT' + if tex_info.extensions and 'KHR_texture_transform' in tex_info.extensions: + transform = tex_info.extensions['KHR_texture_transform'] + transform = texture_transform_gltf_to_blender(transform) + mapping.inputs['Location'].default_value[0] = transform['offset'][0] + mapping.inputs['Location'].default_value[1] = transform['offset'][1] + mapping.inputs['Rotation'].default_value[2] = transform['rotation'] + mapping.inputs['Scale'].default_value[0] = transform['scale'][0] + mapping.inputs['Scale'].default_value[1] = transform['scale'][1] + # Outputs + mh.node_tree.links.new(uv_socket, mapping.outputs[0]) + # Inputs + uv_socket = mapping.inputs[0] + + x -= 260 + + # UV Map + uv_map = mh.node_tree.nodes.new('ShaderNodeUVMap') + uv_map.location = x - 160, y - 70 + # Get UVMap + uv_idx = tex_info.tex_coord or 0 + try: + uv_idx = tex_info.extensions['KHR_texture_transform']['texCoord'] + except Exception: + pass + uv_map.uv_map = 'UVMap' if uv_idx == 0 else 'UVMap.%03d' % uv_idx + # Outputs + mh.node_tree.links.new(uv_socket, uv_map.outputs[0]) + +def set_filtering(tex_img, pysampler): + """Set the filtering/interpolation on an Image Texture from the glTf sampler.""" + minf = pysampler.min_filter + magf = pysampler.mag_filter + + # Ignore mipmapping + if minf in [TextureFilter.NearestMipmapNearest, TextureFilter.NearestMipmapLinear]: + minf = TextureFilter.Nearest + elif minf in [TextureFilter.LinearMipmapNearest, TextureFilter.LinearMipmapLinear]: + minf = TextureFilter.Linear + + # If both are nearest or the only specified one was nearest, use nearest. + if (minf, magf) in [ + (TextureFilter.Nearest, TextureFilter.Nearest), + (TextureFilter.Nearest, None), + (None, TextureFilter.Nearest), + ]: + tex_img.interpolation = 'Closest' + else: + tex_img.interpolation = 'Linear' + +def set_wrap_mode(tex_img, pysampler): + """Set the extension on an Image Texture node from the pysampler.""" + wrap_s = pysampler.wrap_s + wrap_t = pysampler.wrap_t + + if wrap_s is None: + wrap_s = TextureWrap.Repeat + if wrap_t is None: + wrap_t = TextureWrap.Repeat + + # The extension property on the Image Texture node can only handle the case + # where both directions are the same and are either REPEAT or CLAMP_TO_EDGE. + if (wrap_s, wrap_t) == (TextureWrap.Repeat, TextureWrap.Repeat): + extension = TextureWrap.Repeat + elif (wrap_s, wrap_t) == (TextureWrap.ClampToEdge, TextureWrap.ClampToEdge): + extension = TextureWrap.ClampToEdge + else: + print_console('WARNING', + 'texture wrap mode unsupported: (%s, %s)' % (wrap_name(wrap_s), wrap_name(wrap_t)), + ) + # Default to repeat + extension = TextureWrap.Repeat + + if extension == TextureWrap.Repeat: + tex_img.extension = 'REPEAT' + elif extension == TextureWrap.ClampToEdge: + tex_img.extension = 'EXTEND' + +def wrap_name(wrap): + if wrap == TextureWrap.ClampToEdge: return 'CLAMP_TO_EDGE' + if wrap == TextureWrap.MirroredRepeat: return 'MIRRORED_REPEAT' + if wrap == TextureWrap.Repeat: return 'REPEAT' + return 'UNKNOWN (%s)' % wrap + diff --git a/io_scene_gltf2/io/imp/gltf2_io_gltf.py b/io_scene_gltf2/io/imp/gltf2_io_gltf.py index dc5c5f8f..2248ab64 100755 --- a/io_scene_gltf2/io/imp/gltf2_io_gltf.py +++ b/io_scene_gltf2/io/imp/gltf2_io_gltf.py @@ -40,10 +40,6 @@ class glTFImporter(): self.log = log.logger self.log_handler = log.hdlr - self.SIMPLE = 1 - self.TEXTURE = 2 - self.TEXTURE_FACTOR = 3 - # TODO: move to a com place? self.extensions_managed = [ 'KHR_materials_pbrSpecularGlossiness', |