glTF importer: rewrite material import
This commit is contained in:
parent
e88c7ee2a7
commit
2caaf64ab1
|
@ -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',
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
from .gltf2_blender_pbrMetallicRoughness import \
|
||||
base_color, emission, normal, occlusion, make_output_nodes, make_settings_node
|
||||
from .gltf2_blender_texture import texture
|
||||
|
||||
|
||||
class BlenderKHR_materials_pbrSpecularGlossiness():
|
||||
"""Blender KHR_materials_pbrSpecularGlossiness extension."""
|
||||
def __new__(cls, *args, **kwargs):
|
||||
raise RuntimeError("%s should not be instantiated" % cls)
|
||||
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
|
||||
|
||||
@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)
|
||||
# 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])
|
||||
|
||||
@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
|
||||
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(),
|
||||
)
|
||||
|
||||
# delete all nodes except output
|
||||
for node in list(node_tree.nodes):
|
||||
if not node.type == 'OUTPUT_MATERIAL':
|
||||
node_tree.nodes.remove(node)
|
||||
emission(
|
||||
mh,
|
||||
location=(-200, 860),
|
||||
color_socket=emission_socket,
|
||||
)
|
||||
|
||||
output_node = node_tree.nodes[0]
|
||||
output_node.location = 1000, 0
|
||||
base_color(
|
||||
mh,
|
||||
is_diffuse=True,
|
||||
location=(-200, 380),
|
||||
color_socket=diffuse_node.inputs['Color'],
|
||||
alpha_socket=alpha_socket,
|
||||
)
|
||||
|
||||
# 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
|
||||
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'],
|
||||
)
|
||||
|
||||
glossy.inputs[1].default_value = 1 - pbrSG['glossinessFactor']
|
||||
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 pbrSG['diffuse_type'] == gltf.SIMPLE:
|
||||
if not vertex_color:
|
||||
# change input values
|
||||
diffuse.inputs[0].default_value = pbrSG['diffuseFactor']
|
||||
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'],
|
||||
)
|
||||
|
||||
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])
|
||||
# [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)
|
||||
|
||||
elif pbrSG['diffuse_type'] == gltf.TEXTURE_FACTOR:
|
||||
if spec_gloss_texture is None:
|
||||
specular_socket.default_value = spec_factor + [1]
|
||||
roughness_socket.default_value = 1 - gloss_factor
|
||||
return
|
||||
|
||||
# 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'
|
||||
# (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']
|
||||
|
||||
separate_vertex_color = node_tree.nodes.new('ShaderNodeSeparateRGB')
|
||||
math_vc_R = node_tree.nodes.new('ShaderNodeMath')
|
||||
math_vc_R.operation = 'MULTIPLY'
|
||||
x -= 250
|
||||
|
||||
math_vc_G = node_tree.nodes.new('ShaderNodeMath')
|
||||
math_vc_G.operation = 'MULTIPLY'
|
||||
# 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]
|
||||
|
||||
math_vc_B = node_tree.nodes.new('ShaderNodeMath')
|
||||
math_vc_B.operation = 'MULTIPLY'
|
||||
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
|
||||
|
||||
# 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',
|
||||
)
|
||||
x -= 200
|
||||
|
||||
combine = node_tree.nodes.new('ShaderNodeCombineRGB')
|
||||
combine.location = -250, 500
|
||||
texture(
|
||||
mh,
|
||||
tex_info=spec_gloss_texture,
|
||||
label='SPECULAR GLOSSINESS',
|
||||
location=(x, y),
|
||||
color_socket=specular_socket,
|
||||
alpha_socket=glossiness_socket,
|
||||
)
|
||||
|
||||
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])
|
||||
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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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])
|
||||
|
|
@ -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])
|
||||
|
|
@ -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])
|
||||
|
|
@ -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])
|
||||
|
||||
ignore_map = False
|
||||
mh = MaterialHelper(gltf, pymaterial, mat, vertex_color)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
@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
|
||||
|
||||
output_surface_input = get_output_surface_input(node_tree)
|
||||
preoutput_node_output = get_preoutput_node_output(node_tree)
|
||||
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]
|
||||
|
||||
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.diffuse_color = color
|
||||
|
||||
|
|
|
@ -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
|
||||
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 BlenderPbr():
|
||||
"""Blender Pbr."""
|
||||
def __new__(cls, *args, **kwargs):
|
||||
raise RuntimeError("%s should not be instantiated" % cls)
|
||||
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 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 is_opaque(self):
|
||||
alpha_mode = self.pymat.alpha_mode
|
||||
return alpha_mode is None or alpha_mode == 'OPAQUE'
|
||||
|
||||
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
|
||||
def needs_emissive(self):
|
||||
return (
|
||||
self.pymat.emissive_texture is not None or
|
||||
(self.pymat.emissive_factor or [0, 0, 0]) != [0, 0, 0]
|
||||
)
|
||||
|
||||
# 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)
|
||||
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
|
||||
|
||||
output_node = node_tree.nodes[0]
|
||||
output_node.location = 1250, 0
|
||||
# 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(),
|
||||
)
|
||||
|
||||
# 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
|
||||
emission(
|
||||
mh,
|
||||
location=(-200, 860),
|
||||
color_socket=emission_socket,
|
||||
)
|
||||
|
||||
if pypbr.color_type == gltf.SIMPLE:
|
||||
base_color(
|
||||
mh,
|
||||
location=(-200, 380),
|
||||
color_socket=pbr_node.inputs['Base Color'],
|
||||
alpha_socket=alpha_socket,
|
||||
)
|
||||
|
||||
if not vertex_color:
|
||||
metallic_roughness(
|
||||
mh,
|
||||
location=(-200, -100),
|
||||
metallic_socket=pbr_node.inputs['Metallic'],
|
||||
roughness_socket=pbr_node.inputs['Roughness'],
|
||||
)
|
||||
|
||||
# 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
|
||||
normal(
|
||||
mh,
|
||||
location=(-200, -580),
|
||||
normal_socket=pbr_node.inputs['Normal'],
|
||||
)
|
||||
|
||||
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 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'],
|
||||
)
|
||||
|
||||
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])
|
||||
# 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.
|
||||
|
||||
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'
|
||||
# [Texture] => [Emissive Factor] =>
|
||||
def emission(mh: MaterialHelper, location, color_socket):
|
||||
x, y = location
|
||||
emissive_factor = mh.pymat.emissive_factor or [0, 0, 0]
|
||||
|
||||
vc_mult_node = node_tree.nodes.new('ShaderNodeMixRGB')
|
||||
vc_mult_node.blend_type = 'MULTIPLY'
|
||||
vc_mult_node.inputs['Fac'].default_value = 1.0
|
||||
if color_socket is None:
|
||||
return
|
||||
|
||||
# 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',
|
||||
)
|
||||
if mh.pymat.emissive_texture is None:
|
||||
color_socket.default_value = emissive_factor + [1]
|
||||
return
|
||||
|
||||
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],
|
||||
]
|
||||
# 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]
|
||||
|
||||
# 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])
|
||||
x -= 200
|
||||
|
||||
else:
|
||||
node_tree.links.new(main_node.inputs[0], mult_node.outputs[0])
|
||||
texture(
|
||||
mh,
|
||||
tex_info=mh.pymat.emissive_texture,
|
||||
label='EMISSIVE',
|
||||
location=(x, y),
|
||||
color_socket=color_socket,
|
||||
)
|
||||
|
||||
# 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:
|
||||
# [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)
|
||||
|
||||
# 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
|
||||
if base_color_factor is None:
|
||||
base_color_factor = [1, 1, 1, 1]
|
||||
|
||||
vc_mult_node = node_tree.nodes.new('ShaderNodeMixRGB')
|
||||
vc_mult_node.blend_type = 'MULTIPLY'
|
||||
vc_mult_node.inputs['Fac'].default_value = 1.0
|
||||
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
|
||||
|
||||
# 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',
|
||||
)
|
||||
# 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
|
||||
|
||||
# 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])
|
||||
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]
|
||||
|
||||
else:
|
||||
node_tree.links.new(main_node.inputs[0], text_node.outputs[0])
|
||||
x -= 200
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
elif pypbr.metallic_type == gltf.TEXTURE:
|
||||
# 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']
|
||||
|
||||
metallic_text = make_texture_block(
|
||||
gltf,
|
||||
node_tree,
|
||||
pypbr.metallic_roughness_texture,
|
||||
location=(-500, 0),
|
||||
label='METALLIC ROUGHNESS',
|
||||
name='metallicRoughnessTexture',
|
||||
colorspace='NONE',
|
||||
)
|
||||
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]
|
||||
|
||||
metallic_separate = node_tree.nodes.new('ShaderNodeSeparateRGB')
|
||||
metallic_separate.location = -250, 0
|
||||
x -= 200
|
||||
|
||||
# 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
|
||||
# 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'])
|
||||
|
||||
elif pypbr.metallic_type == gltf.TEXTURE_FACTOR:
|
||||
x -= 280
|
||||
|
||||
metallic_text = make_texture_block(
|
||||
gltf,
|
||||
node_tree,
|
||||
pypbr.metallic_roughness_texture,
|
||||
location=(-1000, 0),
|
||||
label='METALLIC ROUGHNESS',
|
||||
name='metallicRoughnessTexture',
|
||||
colorspace='NONE',
|
||||
)
|
||||
# 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,
|
||||
)
|
||||
|
||||
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
|
||||
# [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
|
||||
|
||||
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
|
||||
if pbr.metallic_roughness_texture is None:
|
||||
metallic_socket.default_value = metal_factor
|
||||
roughness_socket.default_value = rough_factor
|
||||
return
|
||||
|
||||
# links
|
||||
node_tree.links.new(metallic_separate.inputs[0], metallic_text.outputs[0])
|
||||
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
|
||||
|
||||
# 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])
|
||||
# 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
|
||||
|
||||
# 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])
|
||||
x -= 200
|
||||
|
||||
# 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'
|
||||
# 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]
|
||||
|
||||
# Set material alpha mode to blend
|
||||
# This is needed for Eevee
|
||||
material.blend_method = 'HASHED' # TODO check best result in eevee
|
||||
x -= 200
|
||||
|
||||
transparent = node_tree.nodes.new('ShaderNodeBsdfTransparent')
|
||||
transparent.location = 750, 0
|
||||
texture(
|
||||
mh,
|
||||
tex_info=pbr.metallic_roughness_texture,
|
||||
label='METALLIC ROUGHNESS',
|
||||
location=(x, y),
|
||||
is_data=True,
|
||||
color_socket=color_socket,
|
||||
)
|
||||
|
||||
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])
|
||||
|
||||
# [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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue