711 lines
32 KiB
Python
711 lines
32 KiB
Python
"""
|
|
Copyright (C) 2023 Adobe.
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
# file: material/manager.py
|
|
# brief: Material operations manager
|
|
# author Adobe - 3D & Immersive
|
|
# copyright 2023 Adobe Inc. All rights reserved.
|
|
|
|
|
|
import os
|
|
import bpy
|
|
import time
|
|
import traceback
|
|
from copy import deepcopy
|
|
|
|
from ..thread_ops import SUBSTANCE_Threads
|
|
from ..utils import SUBSTANCE_Utils
|
|
from ..common import RENDER_IMG_UPDATE_DELAY_S, ADDON_PACKAGE
|
|
|
|
|
|
class SUBSTANCE_MaterialManager():
|
|
|
|
@staticmethod
|
|
def get_image(name, filepath):
|
|
if name in bpy.data.images:
|
|
_image = bpy.data.images[name]
|
|
_image_filepath = bpy.path.abspath(_image.filepath)
|
|
_image_filepath = os.path.abspath(_image_filepath)
|
|
_image_filepath = _image_filepath.replace("\\", "/")
|
|
_new_filepath = filepath.replace("\\", "/")
|
|
|
|
if _image_filepath != _new_filepath:
|
|
_image.filepath = _new_filepath
|
|
_image.reload()
|
|
return _image
|
|
_image = bpy.data.images.load(filepath=filepath.replace("\\", "/"))
|
|
_image.name = name
|
|
return _image
|
|
|
|
@staticmethod
|
|
def get_existing_material(material_name):
|
|
if material_name in bpy.data.materials:
|
|
return bpy.data.materials[material_name]
|
|
return None
|
|
|
|
@staticmethod
|
|
def reset_image(node, image):
|
|
def _set_image():
|
|
node.image = image
|
|
SUBSTANCE_Threads.main_thread_run(_set_image)
|
|
|
|
@staticmethod
|
|
def auto_apply_material(context, data, material):
|
|
_selected_geo = SUBSTANCE_Utils.get_selected_geo(context.selected_objects)
|
|
if len(_selected_geo) > 0 and data["graph_idx"] == 0:
|
|
_selected_objects = [bpy.context.active_object]
|
|
SUBSTANCE_MaterialManager.apply_material(context, _selected_objects, material)
|
|
|
|
@staticmethod
|
|
def apply_material(context, objects, material):
|
|
_addon_prefs = context.preferences.addons[ADDON_PACKAGE].preferences
|
|
|
|
for _obj in objects:
|
|
if not hasattr(_obj.data, "materials"):
|
|
continue
|
|
if _addon_prefs.default_apply_type == "INSERT" and _obj.data.materials:
|
|
# _obj.data.materials.append(material)
|
|
_obj.active_material = material
|
|
else:
|
|
_obj.data.materials.append(material)
|
|
|
|
@staticmethod
|
|
def empty_material(material, graph):
|
|
material.use_nodes = True
|
|
try:
|
|
material.cycles.displacement_method = 'BOTH'
|
|
except Exception:
|
|
pass
|
|
|
|
# Cleanup graph
|
|
_mat_nodes = material.node_tree.nodes
|
|
for _node in _mat_nodes:
|
|
_mat_nodes.remove(_node)
|
|
|
|
# Cleanup Node groups
|
|
if graph.material.name in bpy.data.node_groups:
|
|
_subgroup = bpy.data.node_groups[graph.material.name]
|
|
for _node in _subgroup.nodes:
|
|
_subgroup.nodes.remove(_node)
|
|
|
|
if bpy.app.version >= (4, 0, 0):
|
|
for _item in _subgroup.interface.items_tree:
|
|
if _item.item_type == 'SOCKET':
|
|
if _item.in_out == 'INPUT' or _item.in_out == 'OUTPUT':
|
|
_subgroup.interface.remove(_item)
|
|
else:
|
|
for _input in _subgroup.inputs:
|
|
_subgroup.inputs.remove(_input)
|
|
for _output in _subgroup.outputs:
|
|
_subgroup.outputs.remove(_output)
|
|
|
|
@staticmethod
|
|
def init_nodes(material, graph, shader_graph):
|
|
_items = {}
|
|
for _node in shader_graph["nodes"]:
|
|
_name = _node["name"].replace("$matname", graph.material.name)
|
|
if _node["path"] == "root":
|
|
_nodes = material.node_tree.nodes
|
|
else:
|
|
_nodes = _items[_node["path"].replace("root/", "")].node_tree.nodes
|
|
|
|
if _name in _nodes:
|
|
_items[_node["id"]] = _nodes[_name]
|
|
else:
|
|
_items[_node["id"]] = _nodes.new(_node["type"])
|
|
_items[_node["id"]].name = _name
|
|
if _node["type"] == "NodeFrame":
|
|
_items[_node["id"]].label = _node["label"]
|
|
continue
|
|
|
|
_items[_node["id"]].location = (_node["location"][0], _node["location"][1])
|
|
if _node["type"] == "ShaderNodeGroup":
|
|
if graph.material.name in bpy.data.node_groups:
|
|
_node_tree = bpy.data.node_groups[graph.material.name]
|
|
else:
|
|
_node_tree = bpy.data.node_groups.new(
|
|
type='ShaderNodeTree',
|
|
name=graph.material.name)
|
|
_items[_node["id"]].node_tree = _node_tree
|
|
return _items
|
|
|
|
@staticmethod
|
|
def init_outputs(items, sbs_group, shader_prefs, shader_graph, addons_prefs, data, graph, new_nodes=None):
|
|
_location = [-600, 0]
|
|
for _output in graph.outputs:
|
|
if _output.name not in data["outputs"]:
|
|
continue
|
|
if not data["outputs"][_output.name]["enabled"]:
|
|
continue
|
|
|
|
if _output.type == "image":
|
|
if data["outputs"][_output.name]["path"] != "":
|
|
_filepath = data["outputs"][_output.name]["path"]
|
|
_filename = bpy.path.basename(data["outputs"][_output.name]["path"])
|
|
_output.filepath = _filepath
|
|
_output.filename = _filename
|
|
else:
|
|
_output.filepath = ""
|
|
_output.filename = ""
|
|
continue
|
|
|
|
_name = "Tx {}".format(_output.name)
|
|
_key = "tx_{}".format(_output.name)
|
|
if _name in sbs_group.node_tree.nodes:
|
|
items[_key] = sbs_group.node_tree.nodes[_name]
|
|
else:
|
|
if new_nodes is not None:
|
|
new_nodes.append(_key)
|
|
items[_key] = sbs_group.node_tree.nodes.new("ShaderNodeTexImage")
|
|
items[_key].name = "Tx {}".format(_output.name)
|
|
|
|
_img_name = "{}_{}".format(graph.material.name, _output.name)
|
|
_img = SUBSTANCE_MaterialManager.get_image(_img_name, _filepath)
|
|
items[_key].image = _img
|
|
|
|
if _output.name in shader_prefs["outputs"]:
|
|
_shader_output = shader_prefs["outputs"][_output.name]
|
|
_img.colorspace_settings.name = _shader_output["colorspace"]
|
|
else:
|
|
_img.colorspace_settings.name = addons_prefs.output_default_colorspace
|
|
|
|
if (_output.name in shader_graph["outputs"] and
|
|
"normal" in shader_graph["outputs"][_output.name]):
|
|
_nrm_key = "normal_{}".format(_output.name)
|
|
if new_nodes is not None:
|
|
new_nodes.append(_nrm_key)
|
|
items[_nrm_key] = sbs_group.node_tree.nodes.new("ShaderNodeNormalMap")
|
|
items[_nrm_key].name = "Normal {}".format(_output.name)
|
|
items[_nrm_key].location = (_location[0] + 300, _location[1])
|
|
|
|
# Add common properties to outputs that are not part of the shader
|
|
if _output.name not in shader_graph["outputs"]:
|
|
for _property in shader_graph["properties"]:
|
|
if _property["id"] == "tx_":
|
|
_new_propery = deepcopy(_property)
|
|
_new_propery["id"] = "tx_{}".format(_output.name)
|
|
shader_graph["properties"].append(_new_propery)
|
|
items[_key].location = (_location[0], _location[1])
|
|
_location[1] -= 300
|
|
else:
|
|
_name = "Val {}".format(_output.name)
|
|
_key = "val_{}".format(_output.name)
|
|
_value = data["outputs"][_output.name]["value"]
|
|
if _output.type == "integer2" or _output.type == "float2":
|
|
_value.append(0)
|
|
_value.append(0)
|
|
elif _output.type == "integer3" or _output.type == "float3":
|
|
_value.append(0)
|
|
|
|
if _name in sbs_group.node_tree.nodes:
|
|
items[_key] = sbs_group.node_tree.nodes[_name]
|
|
else:
|
|
if new_nodes is not None:
|
|
new_nodes.append(_key)
|
|
if _output.type == "integer" or _output.type == "float":
|
|
items[_key] = sbs_group.node_tree.nodes.new("ShaderNodeValue")
|
|
else:
|
|
items[_key] = sbs_group.node_tree.nodes.new("ShaderNodeCombineXYZ")
|
|
|
|
if _output.type == "integer" or _output.type == "float":
|
|
items[_key].outputs[0].default_value = _value
|
|
else:
|
|
items[_key].inputs[0].default_value = _value[0]
|
|
items[_key].inputs[1].default_value = _value[1]
|
|
items[_key].inputs[2].default_value = _value[2]
|
|
|
|
items[_key].location = (_location[0], _location[1])
|
|
_location[1] -= 300
|
|
|
|
@staticmethod
|
|
def init_sockets(items, shader_graph):
|
|
# Initialize shader sockets
|
|
for _socket in shader_graph["sockets"]:
|
|
if "dependency" in _socket:
|
|
if _socket["dependency"] not in items:
|
|
continue
|
|
if bpy.app.version >= (4, 0, 0):
|
|
if _socket["source"] == "input" and _socket["name"] not in items[_socket["id"]].inputs:
|
|
items[_socket["id"]].node_tree.interface.new_socket(
|
|
_socket["name"],
|
|
in_out="INPUT",
|
|
socket_type=_socket["type"])
|
|
elif _socket["source"] == "output" and _socket["name"] not in items[_socket["id"]].outputs:
|
|
items[_socket["id"]].node_tree.interface.new_socket(
|
|
_socket["name"],
|
|
in_out="OUTPUT",
|
|
socket_type=_socket["type"])
|
|
else:
|
|
if _socket["source"] == "input" and _socket["name"] not in items[_socket["id"]].inputs:
|
|
items[_socket["id"]].node_tree.inputs.new(_socket["type"], _socket["name"])
|
|
elif _socket["source"] == "output" and _socket["name"] not in items[_socket["id"]].outputs:
|
|
items[_socket["id"]].node_tree.outputs.new(_socket["type"], _socket["name"])
|
|
|
|
@staticmethod
|
|
def create_dyna_links(dyna_links, sbs_group, graph, data):
|
|
for _output in graph.outputs:
|
|
if _output.name not in data["outputs"]:
|
|
continue
|
|
if _output.name not in sbs_group.outputs and data["outputs"][_output.name]["enabled"]:
|
|
# Create links
|
|
if _output.type == "image":
|
|
dyna_links.append({
|
|
"from": "NODE_GROUP_IN",
|
|
"to": "tx_{}".format(_output.name),
|
|
"output": "UV",
|
|
"input": "Vector",
|
|
"path": "root/SBSAR"
|
|
})
|
|
dyna_links.append({
|
|
"from": "tx_{}".format(_output.name),
|
|
"to": "NODE_GROUP_OUT",
|
|
"output": "Color",
|
|
"input": _output.name,
|
|
"path": "root/SBSAR"
|
|
})
|
|
elif _output.type == "integer" or _output.type == "float":
|
|
dyna_links.append({
|
|
"from": "val_{}".format(_output.name),
|
|
"to": "NODE_GROUP_OUT",
|
|
"output": "Value",
|
|
"input": _output.name,
|
|
"path": "root/SBSAR"
|
|
})
|
|
else:
|
|
dyna_links.append({
|
|
"from": "val_{}".format(_output.name),
|
|
"to": "NODE_GROUP_OUT",
|
|
"output": "Vector",
|
|
"input": _output.name,
|
|
"path": "root/SBSAR"
|
|
})
|
|
# Create sockets
|
|
if bpy.app.version >= (4, 0, 0):
|
|
if _output.type == "image":
|
|
sbs_group.node_tree.interface.new_socket(
|
|
name=_output.name,
|
|
in_out="OUTPUT",
|
|
socket_type="NodeSocketColor")
|
|
elif _output.type == "integer":
|
|
sbs_group.node_tree.interface.new_socket(
|
|
name=_output.name,
|
|
in_out="OUTPUT",
|
|
socket_type="NodeSocketInt")
|
|
elif _output.type == "float":
|
|
sbs_group.node_tree.interface.new_socket(
|
|
name=_output.name,
|
|
in_out="OUTPUT",
|
|
socket_type="NodeSocketFloat")
|
|
else:
|
|
sbs_group.node_tree.interface.new_socket(
|
|
name=_output.name,
|
|
in_out="OUTPUT",
|
|
socket_type="NodeSocketVectorXYZ")
|
|
else:
|
|
if _output.type == "image":
|
|
sbs_group.node_tree.outputs.new("NodeSocketColor", _output.name)
|
|
elif _output.type == "integer":
|
|
sbs_group.node_tree.outputs.new("NodeSocketInt", _output.name)
|
|
elif _output.type == "float":
|
|
sbs_group.node_tree.outputs.new("NodeSocketFloat", _output.name)
|
|
else:
|
|
sbs_group.node_tree.outputs.new("NodeSocketVectorXYZ", _output.name)
|
|
|
|
@staticmethod
|
|
def init_links(material, items, shader_graph):
|
|
for _link in shader_graph["links"]:
|
|
if bpy.app.version >= (4, 0, 0):
|
|
pass
|
|
else:
|
|
if _link["input"] == "Emission Color":
|
|
_link["input"] = "Emission"
|
|
if (_link["from"] in items and
|
|
_link["to"] in items and
|
|
_link["output"] in items[_link["from"]].outputs and
|
|
_link["input"] in items[_link["to"]].inputs):
|
|
if _link["path"] == "root":
|
|
material.node_tree.links.new(
|
|
items[_link["from"]].outputs[_link["output"]],
|
|
items[_link["to"]].inputs[_link["input"]])
|
|
else:
|
|
items[_link["path"].replace("root/", "")].node_tree.links.new(
|
|
items[_link["from"]].outputs[_link["output"]],
|
|
items[_link["to"]].inputs[_link["input"]])
|
|
|
|
@staticmethod
|
|
def init_dyna_links(material, items, dyna_links):
|
|
for _link in dyna_links:
|
|
if (_link["from"] in items and
|
|
_link["to"] in items and
|
|
_link["output"] in items[_link["from"]].outputs and
|
|
_link["input"] in items[_link["to"]].inputs):
|
|
if _link["path"] == "root":
|
|
material.node_tree.links.new(
|
|
items[_link["from"]].outputs[_link["output"]],
|
|
items[_link["to"]].inputs[_link["input"]])
|
|
else:
|
|
items[_link["path"].replace("root/", "")].node_tree.links.new(
|
|
items[_link["from"]].outputs[_link["output"]],
|
|
items[_link["to"]].inputs[_link["input"]])
|
|
|
|
@staticmethod
|
|
def remove_unused_nodes(items, shader_graph, mat_nodes):
|
|
for _node in shader_graph["nodes"]:
|
|
if "dependency" in _node:
|
|
for _value in _node["dependency"]:
|
|
if _value not in items:
|
|
if _node["path"] == "root":
|
|
mat_nodes.remove(items[_node["id"]])
|
|
else:
|
|
_node_path = _node["path"].replace("root/", "")
|
|
items[_node_path].node_tree.nodes.remove(items[_node["id"]])
|
|
del items[_node["id"]]
|
|
break
|
|
if _node["type"] == "NodeFrame":
|
|
for _key in _node["children"]:
|
|
if _key in items:
|
|
items[_key].parent = items[_node["id"]]
|
|
items[_node["id"]].update()
|
|
|
|
@staticmethod
|
|
def init_properties(items, shader_graph, shader_prefs, graph, data, context, new_nodes=None):
|
|
if new_nodes is None:
|
|
new_nodes = []
|
|
for _item in items.keys():
|
|
new_nodes.append(_item)
|
|
|
|
for _property in shader_graph["properties"]:
|
|
if _property["type"] == "emissive_intensity":
|
|
if "input" in _property and "emissive" in data["outputs"]:
|
|
_value = getattr(shader_prefs["inputs"], _property["type"])
|
|
if bpy.app.version >= (4, 0, 0):
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], _value)
|
|
else:
|
|
setattr(items[_property["id"]].inputs[20], _property["name"], _value)
|
|
else:
|
|
if bpy.app.version >= (4, 0, 0):
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], 0.0)
|
|
else:
|
|
setattr(items[_property["id"]].inputs[20], _property["name"], 0.0)
|
|
|
|
else:
|
|
if _property["id"] not in items:
|
|
continue
|
|
|
|
if _property["id"] not in new_nodes:
|
|
continue
|
|
|
|
if "dependency" in _property:
|
|
for _value in _property["dependency"]:
|
|
if _value not in items:
|
|
continue
|
|
|
|
if _property["type"] == "string":
|
|
setattr(items[_property["id"]], _property["name"], _property["value"])
|
|
elif _property["type"] == "float":
|
|
setattr(items[_property["id"]], _property["name"], _property["value"])
|
|
elif _property["type"] == "tiling":
|
|
if "input" in _property:
|
|
_value = graph.tiling.get()
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], _value)
|
|
elif _property["type"] == "physical_size":
|
|
if "input" in _property:
|
|
_value = graph.physical_size.get()
|
|
_value = SUBSTANCE_Utils.get_physical_size(_value, context)
|
|
_value = [1/_value[0], 1/_value[1], 1/_value[0]]
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], _value)
|
|
elif _property["type"] == "ao_mix":
|
|
if "input" in _property:
|
|
_value = getattr(shader_prefs["inputs"], _property["type"])
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], _value)
|
|
elif _property["type"] == "disp_midlevel":
|
|
if "input" in _property:
|
|
_value = getattr(shader_prefs["inputs"], _property["type"])
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], _value)
|
|
elif _property["type"] == "disp_scale":
|
|
if "input" in _property:
|
|
_value = getattr(shader_prefs["inputs"], _property["type"])
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], _value)
|
|
elif _property["type"] == "disp_physical_scale":
|
|
if "input" in _property:
|
|
_value = graph.physical_size.get()[2]
|
|
setattr(items[_property["id"]].inputs[_property["input"]], _property["name"], _value)
|
|
elif _property["type"] == "projection_blend":
|
|
_value = getattr(shader_prefs["inputs"], _property["type"])
|
|
setattr(items[_property["id"]], _property["name"], _value)
|
|
else:
|
|
setattr(items[_property["id"]], _property["name"], _property["value"])
|
|
|
|
@staticmethod
|
|
def clean_sbsar_group(sbs_group, sbs_group_nodes, graph, data):
|
|
# Remove unused sockets
|
|
if bpy.app.version >= (4, 0, 0):
|
|
for _output in sbs_group.node_tree.interface.items_tree:
|
|
if _output.name not in data["outputs"]:
|
|
if _output.item_type == 'SOCKET':
|
|
if _output.in_out == 'OUTPUT':
|
|
sbs_group.node_tree.interface.remove(_output)
|
|
else:
|
|
for _output in sbs_group.node_tree.outputs:
|
|
if _output.name not in data["outputs"]:
|
|
sbs_group.node_tree.outputs.remove(_output)
|
|
|
|
# Remove disabled SBSAR nodes
|
|
for _node in sbs_group_nodes:
|
|
if (type(_node) is not bpy.types.ShaderNodeTexImage and
|
|
type(_node) is not bpy.types.ShaderNodeNormalMap and
|
|
type(_node) is not bpy.types.ShaderNodeValue and
|
|
type(_node) is not bpy.types.ShaderNodeCombineXYZ):
|
|
continue
|
|
|
|
_output = _node.name.replace("Tx ", "").replace("Normal ", "").replace("Val ", "")
|
|
if not graph.outputs[_output].shader_enabled:
|
|
sbs_group_nodes.remove(_node)
|
|
|
|
@staticmethod
|
|
def update_images(graph, data):
|
|
for _output in graph.outputs:
|
|
if _output.type == "image":
|
|
if _output.name in data["outputs"]:
|
|
_filepath = data["outputs"][_output.name]["path"]
|
|
_img_name = "{}_{}".format(graph.material.name, _output.name)
|
|
SUBSTANCE_MaterialManager.get_image(_img_name, _filepath)
|
|
|
|
@staticmethod
|
|
def get_current_nodes(items, new_nodes, mat_nodes, shader_graph, graph):
|
|
for _node in shader_graph["nodes"]:
|
|
_name = _node["name"].replace("$matname", graph.material.name)
|
|
if _node["path"] == "root":
|
|
_nodes = mat_nodes
|
|
else:
|
|
_nodes = items[_node["path"].replace("root/", "")].node_tree.nodes
|
|
|
|
if _name in _nodes:
|
|
items[_node["id"]] = _nodes[_name]
|
|
else:
|
|
new_nodes.append(_node["id"])
|
|
items[_node["id"]] = _nodes.new(_node["type"])
|
|
items[_node["id"]].name = _name
|
|
if _node["type"] == "NodeFrame":
|
|
items[_node["id"]].label = _node["label"]
|
|
continue
|
|
|
|
items[_node["id"]].location = (_node["location"][0], _node["location"][1])
|
|
if _node["type"] == "ShaderNodeGroup":
|
|
_node_tree = bpy.data.node_groups.new(
|
|
type='ShaderNodeTree',
|
|
name=graph.material.name)
|
|
items[_node["id"]].node_tree = _node_tree
|
|
|
|
@staticmethod
|
|
def cycles_update(items):
|
|
for _key in items:
|
|
if not hasattr(items[_key], "image"):
|
|
continue
|
|
|
|
_img = items[_key].image
|
|
items[_key].image = None
|
|
SUBSTANCE_Threads.timer_thread_run(
|
|
RENDER_IMG_UPDATE_DELAY_S,
|
|
SUBSTANCE_MaterialManager.reset_image,
|
|
(items[_key], _img))
|
|
|
|
@staticmethod
|
|
def create_shader_network(context, sbsar, graph, data):
|
|
try:
|
|
# Time
|
|
SUBSTANCE_Threads.cursor_push('WAIT')
|
|
_time_start = time.time()
|
|
|
|
_addons_prefs, _shader_idx, _shader = SUBSTANCE_Utils.get_selected_shader(context, int(graph.shaders_list))
|
|
_shader_file = SUBSTANCE_Utils.get_shader_file(_shader.filename)
|
|
_shader_graph = SUBSTANCE_Utils.get_json(_shader_file)
|
|
_shader_prefs = SUBSTANCE_Utils.get_shader_prefs(context, _shader)
|
|
|
|
_material = SUBSTANCE_MaterialManager.get_existing_material(graph.material.name)
|
|
|
|
if _material is None or _shader.name != graph.material.shader:
|
|
if _material is None:
|
|
_material = bpy.data.materials.new(name=graph.material.name)
|
|
if _addons_prefs.auto_apply_material:
|
|
SUBSTANCE_MaterialManager.auto_apply_material(context, data, _material)
|
|
|
|
SUBSTANCE_MaterialManager.empty_material(_material, graph)
|
|
|
|
_items = SUBSTANCE_MaterialManager.init_nodes(_material, graph, _shader_graph)
|
|
|
|
# Get the substance Node Group
|
|
_mat_nodes = _material.node_tree.nodes
|
|
_sbs_group = _material.node_tree.nodes["{}_sbsar".format(graph.material.name)]
|
|
_sbs_group_nodes = _sbs_group.node_tree.nodes
|
|
|
|
# Initialize Enabled outputs
|
|
SUBSTANCE_MaterialManager.init_outputs(
|
|
_items,
|
|
_sbs_group,
|
|
_shader_prefs,
|
|
_shader_graph,
|
|
_addons_prefs,
|
|
data,
|
|
graph)
|
|
|
|
# Initialize Sbsar NodeGroup socket
|
|
SUBSTANCE_MaterialManager.init_sockets(_items, _shader_graph)
|
|
|
|
# Create sockets & links for enabled outputs that are not part of the shader
|
|
_dyna_links = []
|
|
SUBSTANCE_MaterialManager.create_dyna_links(_dyna_links, _sbs_group, graph, data)
|
|
|
|
# Remove unused Nodes
|
|
SUBSTANCE_MaterialManager.remove_unused_nodes(_items, _shader_graph, _mat_nodes)
|
|
|
|
# Initialize shader properties
|
|
SUBSTANCE_MaterialManager.init_properties(
|
|
_items,
|
|
_shader_graph,
|
|
_shader_prefs,
|
|
graph,
|
|
data,
|
|
context)
|
|
|
|
# Initialize links
|
|
SUBSTANCE_MaterialManager.init_links(_material, _items, _shader_graph)
|
|
|
|
# Initialize dynamic links
|
|
SUBSTANCE_MaterialManager.init_dyna_links(_material, _items, _dyna_links)
|
|
|
|
graph.material.shader = _shader.name
|
|
_material.use_fake_user = _addons_prefs.auto_fake_usr
|
|
|
|
_out_message_type = "INFO"
|
|
_out_message = "Material [{}] create in [{}]seconds"
|
|
else:
|
|
# Reload image files and update file format
|
|
SUBSTANCE_MaterialManager.update_images(graph, data)
|
|
|
|
# Get the substance Node Group
|
|
_mat_nodes = _material.node_tree.nodes
|
|
_sbs_group = _material.node_tree.nodes["{}_sbsar".format(graph.material.name)]
|
|
_sbs_group_nodes = _sbs_group.node_tree.nodes
|
|
|
|
if graph.update_tx_only:
|
|
_items = {}
|
|
# Add all SBSAR texture nodes
|
|
for _node in _sbs_group_nodes:
|
|
if (type(_node) is not bpy.types.ShaderNodeTexImage):
|
|
continue
|
|
_output = _node.name.replace("Tx ", "")
|
|
_items[_output] = _node
|
|
|
|
_out_message_type = "INFO"
|
|
_out_message = "Textures [{}] updated in [{}]seconds"
|
|
|
|
else:
|
|
# Cleanup SBSAR group
|
|
SUBSTANCE_MaterialManager.clean_sbsar_group(_sbs_group, _sbs_group_nodes, graph, data)
|
|
|
|
# Get all graph items
|
|
_new_nodes = []
|
|
_items = {}
|
|
SUBSTANCE_MaterialManager.get_current_nodes(
|
|
_items,
|
|
_new_nodes,
|
|
_mat_nodes,
|
|
_shader_graph,
|
|
graph)
|
|
|
|
SUBSTANCE_MaterialManager.init_outputs(
|
|
_items,
|
|
_sbs_group,
|
|
_shader_prefs,
|
|
_shader_graph,
|
|
_addons_prefs,
|
|
data,
|
|
graph,
|
|
_new_nodes)
|
|
|
|
# Remove unused Nodes
|
|
SUBSTANCE_MaterialManager.remove_unused_nodes(_items, _shader_graph, _mat_nodes)
|
|
|
|
# Initialize shader properties
|
|
SUBSTANCE_MaterialManager.init_properties(
|
|
_items,
|
|
_shader_graph,
|
|
_shader_prefs,
|
|
graph,
|
|
data,
|
|
context,
|
|
_new_nodes)
|
|
|
|
# Initialize Sbsar NodeGroup socket
|
|
SUBSTANCE_MaterialManager.init_sockets(_items, _shader_graph)
|
|
|
|
# Create sockets & links for enabled outputs that are not part of the shader
|
|
_dyna_links = []
|
|
SUBSTANCE_MaterialManager.create_dyna_links(_dyna_links, _sbs_group, graph, data)
|
|
|
|
# Sort sockets
|
|
if bpy.app.version >= (4, 0, 0):
|
|
pass
|
|
else:
|
|
_n = len(_sbs_group.node_tree.outputs)
|
|
for _idx in range(_n):
|
|
_sorted = True
|
|
for _jdx in range(_n - _idx - 1):
|
|
_a_key = _sbs_group.node_tree.outputs[_jdx].name
|
|
_b_key = _sbs_group.node_tree.outputs[_jdx + 1].name
|
|
_a_weight = graph.outputs[_a_key].index
|
|
_b_weight = graph.outputs[_b_key].index
|
|
if _a_weight > _b_weight:
|
|
_sbs_group.node_tree.outputs.move(_jdx, _jdx + 1)
|
|
_sorted = False
|
|
if _sorted:
|
|
break
|
|
|
|
# Initialize links
|
|
SUBSTANCE_MaterialManager.init_links(_material, _items, _shader_graph)
|
|
|
|
# Initialize dynamic links
|
|
SUBSTANCE_MaterialManager.init_dyna_links(_material, _items, _dyna_links)
|
|
|
|
_out_message_type = "INFO"
|
|
_out_message = "Material [{}] updated in [{}]seconds"
|
|
|
|
# Hack to autoupdate images in cycles
|
|
if _addons_prefs.cycles_autoupdate_enabled:
|
|
SUBSTANCE_MaterialManager.cycles_update(_items)
|
|
|
|
except Exception:
|
|
_out_message_type = "ERROR"
|
|
_out_message = "An error ocurred while setting the material [{}]. [{}]seconds"
|
|
SUBSTANCE_Utils.log_data("ERROR", "Exception - Susbtance material creation error")
|
|
SUBSTANCE_Utils.log_traceback(traceback.format_exc())
|
|
|
|
# Time
|
|
_time_end = time.time()
|
|
_load_time = round(_time_end - _time_start, 3)
|
|
SUBSTANCE_Utils.log_data(
|
|
_out_message_type,
|
|
_out_message.format(graph.material.name, _load_time),
|
|
display=True)
|
|
|
|
# REDRAW
|
|
# _idx = context.scene.sbsar_index
|
|
# context.scene.sbsar_index = _idx
|
|
SUBSTANCE_Utils.toggle_redraw(context)
|
|
|
|
SUBSTANCE_Threads.cursor_pop()
|