Module kawa_scripts.shader_nodes
Expand source code
# Kawashirov's Scripts (c) 2021 by Sergey V. Kawashirov
#
# Kawashirov's Scripts is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/3.0/>.
#
#
import bpy as _bpy
from mathutils import Vector as _Vector
from . import commons as _commons
from ._internals import log as _log
import typing as _typing
if _typing.TYPE_CHECKING:
from typing import *
from bpy.types import *
KAWA_BAKE_TARGET = 'KAWA_BAKE_TARGET'
KAWA_BAKE_DEFAULT = 'KAWA_BAKE_DEFAULT'
KAWA_BAKE_ALPHA = 'KAWA_BAKE_ALPHA'
def get_shader_node_by_label(mat: 'Material', name: str, _type: type):
nodes = mat.node_tree.nodes
sh_default = nodes.get(name)
if sh_default is not None and not isinstance(sh_default, _type):
msg = "Has shader node with label {0}, but it has type {1} instead of {2}.".format(name, type(sh_default), _type)
raise _commons.MaterialConfigurationError(mat, msg)
if sh_default is None:
sh_defaults = [n for n in nodes.values() if isinstance(n, _type) and n.label == name]
if len(sh_defaults) > 1:
raise _commons.MaterialConfigurationError(mat, "Has {0} shader nodes with label {1}.".format(len(sh_defaults), name))
if len(sh_defaults) == 1:
sh_default = sh_defaults[0]
sh_default.name = name
return sh_default
def get_socket_input_safe(socket: 'NodeSocket') -> 'Optional[NodeSocket]':
# Возвращает выходной сокет, который подключено к node во входной сокет с именем name
# При этом ни node, ни входа, ни подключения может не быть
if socket is None or len(socket.links) != 1 or socket.links[0] is None:
return None
return socket.links[0].from_socket
def get_socket_outputs_safe(socket: 'NodeSocket') -> 'List[NodeSocket]':
# Возвращает выходной сокет, который подключено к node во входной сокет с именем name
# При этом ни node, ни входа, ни подключения может не быть
return [socket.to_socket for link in socket.links]
def get_node_input_safe(node: 'Node', name: str) -> 'Optional[NodeSocket]':
# Возвращает выходной сокет, который подключено к node во входной сокет с именем name
# При этом ни node, ни входа, ни подключения может не быть
if node is None or node.inputs is None:
return None
return get_socket_input_safe(node.inputs.get(name))
def prepare_and_get_teximage_node(mat: 'Material', name: str) -> 'ShaderNodeTexImage':
nodes = mat.node_tree.nodes
n_teximage = get_shader_node_by_label(mat, name, _bpy.types.ShaderNodeTexImage)
if n_teximage is None:
n_teximage = nodes.new('ShaderNodeTexImage')
n_teximage.name = name
n_teximage.label = name
return n_teximage
def prepare_and_get_node_for_baking(mat: 'Material') -> 'ShaderNodeTexImage':
# Cycles запекает в активный TEX_IMAGE
# Возвращает или создает новый ShaderNodeTexImage
# Даёт ему имя KAWA_BAKE_TARGET
# Делает его выбраным и активным
nodes = mat.node_tree.nodes
n_bake = prepare_and_get_teximage_node(mat, KAWA_BAKE_TARGET)
n_bake.interpolation = 'Cubic'
for node in nodes:
node.select = False
n_bake.select = True
nodes.active = n_bake
return n_bake
def get_material_output(mat: 'Material') -> 'Optional[ShaderNodeOutputMaterial]':
# Находит Material Output
nodes = mat.node_tree.nodes
outputs = [n for n in nodes if isinstance(n, _bpy.types.ShaderNodeOutputMaterial)]
if len(outputs) != 1:
return None
return outputs[0]
def get_material_output_socket(mat: 'Material') -> 'Optional[NodeSocket]':
# Находит Material Output
nodes = mat.node_tree.nodes
outputs = [n for n in nodes if isinstance(n, _bpy.types.ShaderNodeOutputMaterial)]
if len(outputs) != 1:
return None
return outputs[0].inputs.get('Surface')
def get_material_output_surface(mat: 'Material') -> 'Optional[ShaderNode]':
# Находит node, который подключен как Surface Material Output
# Срёт ошибкой, если нету или несколько ShaderNodeOutputMaterial или там кривые связи
n_out = get_material_output_socket(mat)
if n_out is None:
raise _commons.MaterialConfigurationError(mat, "Can not find output socket.")
n_sh_s = n_out.links[0].from_socket if len(n_out.links) == 1 else None
if n_sh_s is None:
raise _commons.MaterialConfigurationError(mat, "Can not find connected 'Surface' output shader.")
return n_sh_s.node
def prepare_and_get_default_shader_node(mat: 'Material') -> 'ShaderNode':
# Находет ShaderNode с именем KAWA_BAKE_DEFAULT
# Если такого нет, то ищет с меткой KAWA_BAKE_DEFAULT
# Если такого нет, то пытается понять что подключено в Surface
# Если найдено, то убеждается, что имя и метка = KAWA_BAKE_DEFAULT
# Срёт ошибками если метки выставлены не верно.
sh_default = get_shader_node_by_label(mat, KAWA_BAKE_DEFAULT, _bpy.types.ShaderNode)
if sh_default is None:
sh_default = get_material_output_surface(mat)
if sh_default is None:
raise _commons.MaterialConfigurationError(mat, "Can not find KAWA_BAKE_DEFAULT shader node.")
sh_default.name = KAWA_BAKE_DEFAULT
sh_default.label = KAWA_BAKE_DEFAULT
return sh_default
def prepare_and_get_alpha_shader_node(mat: 'Material'):
# Находет ShaderNode с именем KAWA_BAKE_ALPHA
# Если такого нет, то ищет с меткой KAWA_BAKE_ALPHA
try:
nodes = mat.node_tree.nodes
sh_default = prepare_and_get_default_shader_node(mat)
sh_alpha = get_shader_node_by_label(mat, KAWA_BAKE_ALPHA, _bpy.types.ShaderNodeEmission)
if sh_alpha is None:
sh_alpha = nodes.new('ShaderNodeEmission')
sh_alpha.name = KAWA_BAKE_ALPHA
sh_alpha.label = KAWA_BAKE_ALPHA
sh_alpha_in = sh_alpha.inputs['Color']
if sh_default is not None:
# Если есть default шейдер, то размещаем новый над ним и пытаемся своровать 'Alpha'
sh_alpha.location = sh_default.location + _Vector((0, 200))
n_alpha = get_node_input_safe(sh_default, 'Alpha')
if n_alpha is not None and get_socket_input_safe(sh_alpha_in) is None:
# Если ничего не забинджено в ALPHA шедер, то подрубаем из DEFAULT
mat.node_tree.links.new(n_alpha, sh_alpha.inputs['Color'])
return sh_alpha
except Exception as exc:
raise _commons.MaterialConfigurationError(mat, "Can not prepare ALPHA shader node") from exc
sh_default = prepare_and_get_default_shader_node(mat)
if sh_default is None:
raise RuntimeError(mat, "There is no DEFAULT shader node, can not switch to ALPHA.")
sh_alpha = prepare_and_get_alpha_shader_node(mat)
if sh_alpha is None:
raise RuntimeError(mat, "There is no ALPHA shader node, can not switch to ALPHA.")
while len(output_s.links) > 0:
mat.node_tree.links.remove(output_s.links[0])
mat.node_tree.links.new(sh_alpha.outputs['Emission'], output_s)
def configure_for_baking_default(mat: 'Material'):
# TODO broken
# Подключает default шейдер на выход материала
# Если не найден выход или DEFAULT, срёт ошибками
n_out_s = get_material_output_socket(mat)
if n_out_s is None:
raise _commons.MaterialConfigurationError(mat, "Can not find output socket.")
sh_default = prepare_and_get_default_shader_node(mat)
if sh_default is None:
raise _commons.MaterialConfigurationError(mat, "There is no DEFAULT shader node, can not switch to ALPHA.")
sockets = [s for s in sh_default.outputs if s.type == 'SHADER']
while len(n_out_s.links) > 0:
mat.node_tree.links.remove(n_out_s.links[0])
mat.node_tree.links.new(sockets[0], n_out_s)
Functions
def configure_for_baking_default(mat: Material)
-
Expand source code
def configure_for_baking_default(mat: 'Material'): # TODO broken # Подключает default шейдер на выход материала # Если не найден выход или DEFAULT, срёт ошибками n_out_s = get_material_output_socket(mat) if n_out_s is None: raise _commons.MaterialConfigurationError(mat, "Can not find output socket.") sh_default = prepare_and_get_default_shader_node(mat) if sh_default is None: raise _commons.MaterialConfigurationError(mat, "There is no DEFAULT shader node, can not switch to ALPHA.") sockets = [s for s in sh_default.outputs if s.type == 'SHADER'] while len(n_out_s.links) > 0: mat.node_tree.links.remove(n_out_s.links[0]) mat.node_tree.links.new(sockets[0], n_out_s)
def get_material_output(mat: Material) ‑> Optional[ShaderNodeOutputMaterial]
-
Expand source code
def get_material_output(mat: 'Material') -> 'Optional[ShaderNodeOutputMaterial]': # Находит Material Output nodes = mat.node_tree.nodes outputs = [n for n in nodes if isinstance(n, _bpy.types.ShaderNodeOutputMaterial)] if len(outputs) != 1: return None return outputs[0]
def get_material_output_socket(mat: Material) ‑> Optional[NodeSocket]
-
Expand source code
def get_material_output_socket(mat: 'Material') -> 'Optional[NodeSocket]': # Находит Material Output nodes = mat.node_tree.nodes outputs = [n for n in nodes if isinstance(n, _bpy.types.ShaderNodeOutputMaterial)] if len(outputs) != 1: return None return outputs[0].inputs.get('Surface')
def get_material_output_surface(mat: Material) ‑> Optional[ShaderNode]
-
Expand source code
def get_material_output_surface(mat: 'Material') -> 'Optional[ShaderNode]': # Находит node, который подключен как Surface Material Output # Срёт ошибкой, если нету или несколько ShaderNodeOutputMaterial или там кривые связи n_out = get_material_output_socket(mat) if n_out is None: raise _commons.MaterialConfigurationError(mat, "Can not find output socket.") n_sh_s = n_out.links[0].from_socket if len(n_out.links) == 1 else None if n_sh_s is None: raise _commons.MaterialConfigurationError(mat, "Can not find connected 'Surface' output shader.") return n_sh_s.node
def get_node_input_safe(node: Node, name: str) ‑> Optional[NodeSocket]
-
Expand source code
def get_node_input_safe(node: 'Node', name: str) -> 'Optional[NodeSocket]': # Возвращает выходной сокет, который подключено к node во входной сокет с именем name # При этом ни node, ни входа, ни подключения может не быть if node is None or node.inputs is None: return None return get_socket_input_safe(node.inputs.get(name))
def get_shader_node_by_label(mat: Material, name: str, _type: type)
-
Expand source code
def get_shader_node_by_label(mat: 'Material', name: str, _type: type): nodes = mat.node_tree.nodes sh_default = nodes.get(name) if sh_default is not None and not isinstance(sh_default, _type): msg = "Has shader node with label {0}, but it has type {1} instead of {2}.".format(name, type(sh_default), _type) raise _commons.MaterialConfigurationError(mat, msg) if sh_default is None: sh_defaults = [n for n in nodes.values() if isinstance(n, _type) and n.label == name] if len(sh_defaults) > 1: raise _commons.MaterialConfigurationError(mat, "Has {0} shader nodes with label {1}.".format(len(sh_defaults), name)) if len(sh_defaults) == 1: sh_default = sh_defaults[0] sh_default.name = name return sh_default
def get_socket_input_safe(socket: NodeSocket) ‑> Optional[NodeSocket]
-
Expand source code
def get_socket_input_safe(socket: 'NodeSocket') -> 'Optional[NodeSocket]': # Возвращает выходной сокет, который подключено к node во входной сокет с именем name # При этом ни node, ни входа, ни подключения может не быть if socket is None or len(socket.links) != 1 or socket.links[0] is None: return None return socket.links[0].from_socket
def get_socket_outputs_safe(socket: NodeSocket) ‑> List[NodeSocket]
-
Expand source code
def get_socket_outputs_safe(socket: 'NodeSocket') -> 'List[NodeSocket]': # Возвращает выходной сокет, который подключено к node во входной сокет с именем name # При этом ни node, ни входа, ни подключения может не быть return [socket.to_socket for link in socket.links]
def prepare_and_get_alpha_shader_node(mat: Material)
-
Expand source code
def prepare_and_get_alpha_shader_node(mat: 'Material'): # Находет ShaderNode с именем KAWA_BAKE_ALPHA # Если такого нет, то ищет с меткой KAWA_BAKE_ALPHA try: nodes = mat.node_tree.nodes sh_default = prepare_and_get_default_shader_node(mat) sh_alpha = get_shader_node_by_label(mat, KAWA_BAKE_ALPHA, _bpy.types.ShaderNodeEmission) if sh_alpha is None: sh_alpha = nodes.new('ShaderNodeEmission') sh_alpha.name = KAWA_BAKE_ALPHA sh_alpha.label = KAWA_BAKE_ALPHA sh_alpha_in = sh_alpha.inputs['Color'] if sh_default is not None: # Если есть default шейдер, то размещаем новый над ним и пытаемся своровать 'Alpha' sh_alpha.location = sh_default.location + _Vector((0, 200)) n_alpha = get_node_input_safe(sh_default, 'Alpha') if n_alpha is not None and get_socket_input_safe(sh_alpha_in) is None: # Если ничего не забинджено в ALPHA шедер, то подрубаем из DEFAULT mat.node_tree.links.new(n_alpha, sh_alpha.inputs['Color']) return sh_alpha except Exception as exc: raise _commons.MaterialConfigurationError(mat, "Can not prepare ALPHA shader node") from exc sh_default = prepare_and_get_default_shader_node(mat) if sh_default is None: raise RuntimeError(mat, "There is no DEFAULT shader node, can not switch to ALPHA.") sh_alpha = prepare_and_get_alpha_shader_node(mat) if sh_alpha is None: raise RuntimeError(mat, "There is no ALPHA shader node, can not switch to ALPHA.") while len(output_s.links) > 0: mat.node_tree.links.remove(output_s.links[0]) mat.node_tree.links.new(sh_alpha.outputs['Emission'], output_s)
def prepare_and_get_default_shader_node(mat: Material) ‑> ShaderNode
-
Expand source code
def prepare_and_get_default_shader_node(mat: 'Material') -> 'ShaderNode': # Находет ShaderNode с именем KAWA_BAKE_DEFAULT # Если такого нет, то ищет с меткой KAWA_BAKE_DEFAULT # Если такого нет, то пытается понять что подключено в Surface # Если найдено, то убеждается, что имя и метка = KAWA_BAKE_DEFAULT # Срёт ошибками если метки выставлены не верно. sh_default = get_shader_node_by_label(mat, KAWA_BAKE_DEFAULT, _bpy.types.ShaderNode) if sh_default is None: sh_default = get_material_output_surface(mat) if sh_default is None: raise _commons.MaterialConfigurationError(mat, "Can not find KAWA_BAKE_DEFAULT shader node.") sh_default.name = KAWA_BAKE_DEFAULT sh_default.label = KAWA_BAKE_DEFAULT return sh_default
def prepare_and_get_node_for_baking(mat: Material) ‑> ShaderNodeTexImage
-
Expand source code
def prepare_and_get_node_for_baking(mat: 'Material') -> 'ShaderNodeTexImage': # Cycles запекает в активный TEX_IMAGE # Возвращает или создает новый ShaderNodeTexImage # Даёт ему имя KAWA_BAKE_TARGET # Делает его выбраным и активным nodes = mat.node_tree.nodes n_bake = prepare_and_get_teximage_node(mat, KAWA_BAKE_TARGET) n_bake.interpolation = 'Cubic' for node in nodes: node.select = False n_bake.select = True nodes.active = n_bake return n_bake
def prepare_and_get_teximage_node(mat: Material, name: str) ‑> ShaderNodeTexImage
-
Expand source code
def prepare_and_get_teximage_node(mat: 'Material', name: str) -> 'ShaderNodeTexImage': nodes = mat.node_tree.nodes n_teximage = get_shader_node_by_label(mat, name, _bpy.types.ShaderNodeTexImage) if n_teximage is None: n_teximage = nodes.new('ShaderNodeTexImage') n_teximage.name = name n_teximage.label = name return n_teximage