Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Duroure <julien.duroure@gmail.com>2020-01-29 23:57:22 +0300
committerJulien Duroure <julien.duroure@gmail.com>2020-01-29 23:57:22 +0300
commit2caaf64ab10be6f03e234fa5b533e341512fbed5 (patch)
tree887f6d0d7c58939c9558c81407486d38072f9d47 /io_scene_gltf2
parente88c7ee2a7c091520efe006795817638cd4dcbb0 (diff)
glTF importer: rewrite material import
Diffstat (limited to 'io_scene_gltf2')
-rwxr-xr-xio_scene_gltf2/__init__.py2
-rwxr-xr-xio_scene_gltf2/blender/com/gltf2_blender_material_helpers.py44
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py398
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py63
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_gltf.py70
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_map_emissive.py92
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_map_normal.py87
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_map_occlusion.py100
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_material.py187
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py722
-rw-r--r--io_scene_gltf2/blender/imp/gltf2_blender_texture.py152
-rwxr-xr-xio_scene_gltf2/io/imp/gltf2_io_gltf.py4
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',