362 lines
13 KiB
Python
362 lines
13 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: props/sbsar_graph.py
|
|
# brief: Substance Property Groups
|
|
# author Adobe - 3D & Immersive
|
|
# copyright 2023 Adobe Inc. All rights reserved.
|
|
|
|
|
|
import os
|
|
import bpy
|
|
from .utils import get_shaders
|
|
from ..sbsar.async_ops import _render_sbsar, _set_input_visibility
|
|
from ..api import SUBSTANCE_Api
|
|
from ..thread_ops import SUBSTANCE_Threads
|
|
from ..material.manager import SUBSTANCE_MaterialManager
|
|
from ..utils import SUBSTANCE_Utils
|
|
from .common import SUBSTANCE_PG_SbsarPhysicalSize
|
|
from .sbsar_input import SUBSTANCE_PG_SbsarInput, SUBSTANCE_PG_SbsarInputGroup
|
|
from .sbsar_output import SUBSTANCE_PG_SbsarOutput
|
|
from .sbsar_preset import SUBSTANCE_PG_SbsarPreset
|
|
from ..common import OUTPUTS_FILTER_DICT, Code_InputIdentifier, Code_Response
|
|
|
|
|
|
class SUBSTANCE_PG_SbsarMaterial(bpy.types.PropertyGroup):
|
|
name: bpy.props.StringProperty(name="name", description="The ID of the Substance 3D shader material") # noqa
|
|
shader: bpy.props.StringProperty(name="shader", default="") # noqa
|
|
|
|
|
|
def on_linked_tiling_changed(self, context):
|
|
if self.linked:
|
|
self.y = self.x
|
|
self.z = self.x
|
|
else:
|
|
on_tiling_changed(self, context)
|
|
|
|
|
|
def on_tiling_changed(self, context):
|
|
if not self.callback:
|
|
return
|
|
_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
|
_graph = _sbsar.graphs[int(_sbsar.graphs_list)]
|
|
_addons_prefs, _selected_shader_idx, _selected_shader_preset = SUBSTANCE_Utils.get_selected_shader(
|
|
context,
|
|
int(_graph.shaders_list))
|
|
_shader_filepath = SUBSTANCE_Utils.get_shader_file(_selected_shader_preset.filename)
|
|
_shader_graph = SUBSTANCE_Utils.get_json(_shader_filepath)
|
|
_material = SUBSTANCE_MaterialManager.get_existing_material(_graph.material.name)
|
|
|
|
if _material is None:
|
|
return
|
|
|
|
for _property in _shader_graph["properties"]:
|
|
if _property["type"] == "tiling":
|
|
_node_name = None
|
|
for _node in _shader_graph["nodes"]:
|
|
if _node["id"] == _property["id"]:
|
|
_node_name = _node["name"]
|
|
break
|
|
if _node_name is None:
|
|
continue
|
|
if _node_name in _material.node_tree.nodes:
|
|
_node = _material.node_tree.nodes[_node_name]
|
|
_value = self.get()
|
|
if "input" in _property:
|
|
setattr(_node.inputs[_property["input"]], _property["name"], _value)
|
|
|
|
|
|
class SUBSTANCE_PG_SbsarTiling(bpy.types.PropertyGroup):
|
|
x: bpy.props.FloatProperty(
|
|
name="x",
|
|
default=2.0,
|
|
description="The X tiling to be used", # noqa
|
|
update=on_linked_tiling_changed)
|
|
y: bpy.props.FloatProperty(
|
|
name="y",
|
|
default=2.0,
|
|
description="The Y tiling to be used", # noqa
|
|
update=on_tiling_changed)
|
|
z: bpy.props.FloatProperty(
|
|
name="z",
|
|
default=2.0,
|
|
description="The Z tiling to be used", # noqa
|
|
update=on_tiling_changed)
|
|
linked: bpy.props.BoolProperty(
|
|
name="linked",
|
|
default=True,
|
|
description='Lock/Unlock the tiling', # noqa
|
|
update=on_linked_tiling_changed)
|
|
callback: bpy.props.BoolProperty(default=True)
|
|
|
|
def init(self, value):
|
|
self.callback = False
|
|
self.x = value[0]
|
|
self.y = value[1]
|
|
self.z = value[2]
|
|
self.callback = True
|
|
|
|
def set(self, value, linked):
|
|
self.callback = False
|
|
self.x = value[0]
|
|
self.y = value[1]
|
|
self.z = value[2]
|
|
self.linked = linked
|
|
self.callback = True
|
|
|
|
def get(self):
|
|
return [self.x, self.y, self.z]
|
|
|
|
|
|
def on_shader_update(self, context):
|
|
if not self.shader_callback:
|
|
return
|
|
|
|
_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
|
_graph_idx = int(_sbsar.graphs_list)
|
|
|
|
_addons_prefs, _selected_shader_idx, _selected_shader = SUBSTANCE_Utils.get_selected_shader(
|
|
context,
|
|
int(self.shaders_list))
|
|
|
|
for _output in self.outputs:
|
|
_output.shader_callback_enabled = False
|
|
if _output.name in _selected_shader.outputs:
|
|
_shader_output = _selected_shader.outputs[_output.name]
|
|
_output.shader_enabled = _shader_output.enabled
|
|
_output.shader_format = _shader_output.format
|
|
_output.shader_bitdepth = _shader_output.bitdepth
|
|
else:
|
|
_output.shader_enabled = _addons_prefs.output_default_enabled
|
|
_output.shader_format = _addons_prefs.output_default_format
|
|
_output.shader_bitdepth = _addons_prefs.output_default_bitdepth
|
|
_output.shader_callback_enabled = True
|
|
|
|
SUBSTANCE_Threads.alt_thread_run(_render_sbsar, (context, _sbsar, _graph_idx))
|
|
|
|
|
|
def on_preset_update(self, context):
|
|
if not self.preset_callback:
|
|
return
|
|
_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
|
_preset_idx = int(self.presets_list)
|
|
_preset = self.presets[_preset_idx]
|
|
|
|
_result = SUBSTANCE_Api.sbsar_preset_load(
|
|
_sbsar,
|
|
self,
|
|
_preset.value)
|
|
|
|
if _result[0] != Code_Response.success:
|
|
return
|
|
|
|
_data = _result[1]["data"]["inputs"]
|
|
SUBSTANCE_Threads.alt_thread_run(_set_input_visibility, (
|
|
_sbsar,
|
|
self,
|
|
_data))
|
|
|
|
SUBSTANCE_Threads.alt_thread_run(_render_sbsar, (
|
|
context,
|
|
_sbsar,
|
|
int(self.index)))
|
|
|
|
_input_class = getattr(context.scene, self.inputs_class_name)
|
|
_input_class.callback["enabled"] = False
|
|
_input_class.from_json(self.inputs, _data)
|
|
_input_class.callback["enabled"] = True
|
|
|
|
|
|
def get_preset_items(self, context):
|
|
_presets = []
|
|
for _preset in self.presets:
|
|
_item = (
|
|
_preset.index,
|
|
_preset.label,
|
|
"{}:{}".format(_preset.index, _preset.label),
|
|
int(_preset.index)
|
|
)
|
|
_presets.append(_item)
|
|
return _presets
|
|
|
|
|
|
class SUBSTANCE_PG_SbsarGraph(bpy.types.PropertyGroup):
|
|
index: bpy.props.StringProperty(
|
|
name="index",
|
|
description="The index of the graph in the Substance 3D material") # noqa
|
|
uid: bpy.props.StringProperty(
|
|
name="uid",
|
|
description="The ID of the Substance 3D graph") # noqa
|
|
name: bpy.props.StringProperty(name="name")
|
|
label: bpy.props.StringProperty(
|
|
name="label",
|
|
description="The name of the Substance 3D graph") # noqa
|
|
identifier: bpy.props.StringProperty(
|
|
name="identifier",
|
|
description="The identifier of the Substance 3D graph") # noqa
|
|
packageUrl: bpy.props.StringProperty(
|
|
name="packageUrl",
|
|
description="The package URL of the Substance 3D graph") # noqa
|
|
material: bpy.props.PointerProperty(type=SUBSTANCE_PG_SbsarMaterial)
|
|
tiling: bpy.props.PointerProperty(
|
|
name="tiling",
|
|
description="The default tiling to be used in the shader network", # noqa
|
|
type=SUBSTANCE_PG_SbsarTiling)
|
|
physical_size: bpy.props.PointerProperty(
|
|
name="physical_size",
|
|
description="The physical size of the material", # noqa
|
|
type=SUBSTANCE_PG_SbsarPhysicalSize)
|
|
|
|
presets: bpy.props.CollectionProperty(type=SUBSTANCE_PG_SbsarPreset)
|
|
presets_list: bpy.props.EnumProperty(
|
|
name="presets",
|
|
description="The available presets on ths SBSAR file", # noqa
|
|
items=get_preset_items,
|
|
update=on_preset_update)
|
|
preset_callback: bpy.props.BoolProperty(name="preset_callback", default=True)
|
|
|
|
outputs_filter: bpy.props.EnumProperty(
|
|
name="outputs_filter",
|
|
description="Filter the outputs displayed", # noqa
|
|
items=OUTPUTS_FILTER_DICT)
|
|
|
|
inputs_class_name: bpy.props.StringProperty(name="inputs_class_name")
|
|
input_groups: bpy.props.CollectionProperty(type=SUBSTANCE_PG_SbsarInputGroup)
|
|
inputs: bpy.props.CollectionProperty(type=SUBSTANCE_PG_SbsarInput)
|
|
|
|
outputs: bpy.props.CollectionProperty(type=SUBSTANCE_PG_SbsarOutput)
|
|
|
|
outputsize_exists: bpy.props.BoolProperty(
|
|
name="outputsize_exists",
|
|
default=False)
|
|
randomseed_exists: bpy.props.BoolProperty(
|
|
name="randomseed_exists",
|
|
default=False)
|
|
|
|
shaders_list: bpy.props.EnumProperty(
|
|
name="shaders_list",
|
|
description="The available shader network presets", # noqa
|
|
items=get_shaders,
|
|
update=on_shader_update)
|
|
shader_callback: bpy.props.BoolProperty(name="shader_callback", default=True)
|
|
update_tx_only: bpy.props.BoolProperty(name="update_tx_only", default=False)
|
|
|
|
thumbnail: bpy.props.StringProperty(name="thumbnail")
|
|
|
|
def init(self, graph, addon_prefs):
|
|
self.index = graph.index
|
|
self.uid = graph.uid
|
|
self.name = graph.identifier
|
|
self.identifier = graph.identifier
|
|
self.packageUrl = graph.packageUrl
|
|
self.label = graph.label
|
|
self.material.name = graph.material
|
|
self.physical_size.init(graph.physicalSize)
|
|
self.tiling.init(graph.tiling)
|
|
self.inputs_class_name = graph.inputs_class_name
|
|
|
|
self.update_tx_only = addon_prefs.default_tx_update
|
|
|
|
for _preset in graph.presets:
|
|
_new_preset = self.presets.add()
|
|
_new_preset.init(_preset)
|
|
|
|
for _idx, _group in enumerate(graph.inputs_groups):
|
|
new_group = self.input_groups.add()
|
|
new_group.init(_idx, _group, addon_prefs)
|
|
|
|
for _key, _input in graph.inputs.items():
|
|
_new_input = self.inputs.add()
|
|
_new_input.init(_input)
|
|
if _input.identifier == Code_InputIdentifier.outputsize.value:
|
|
self.outputsize_exists = True
|
|
if _input.identifier == Code_InputIdentifier.randomseed.value:
|
|
self.randomseed_exists = True
|
|
|
|
for _key, _outputs in graph.outputs.items():
|
|
_new_output = self.outputs.add()
|
|
_new_output.init(_outputs)
|
|
|
|
def init_shader(self, shader_idx, shader, default_output):
|
|
self.shader_callback = False
|
|
self.shaders_list = shader_idx
|
|
self.shader_callback = True
|
|
for _output in self.outputs:
|
|
_output.init_shader(shader, default_output)
|
|
|
|
def init_shader_duplicate(self, shader_index):
|
|
_shader_idx = int(shader_index)
|
|
_addon_prefs, _selected_shader_idx, _selected_shader_preset = SUBSTANCE_Utils.get_selected_shader(
|
|
bpy.context, _shader_idx)
|
|
self.shader_callback = False
|
|
self.shaders_list = _selected_shader_idx
|
|
self.shader_callback = True
|
|
for _output in self.outputs:
|
|
_output.init_shader(_selected_shader_preset, _addon_prefs.get_default_output())
|
|
|
|
def init_tiling(self, value, linked):
|
|
self.tiling.set(value, linked)
|
|
|
|
def init_outputs(self, outputs):
|
|
for _output in self.outputs:
|
|
if _output.name in outputs:
|
|
_output.init_output(outputs[_output.name])
|
|
|
|
def init_preset(self, preset_value):
|
|
self.preset_callback = False
|
|
self.presets_list = preset_value
|
|
self.preset_callback = True
|
|
|
|
def set_thumbnail(self, data):
|
|
if "graph_thumb" not in data:
|
|
return
|
|
|
|
_file = data["graph_thumb"]
|
|
if os.path.exists(_file):
|
|
_thumbnail_img = bpy.data.images.load(_file, check_existing=True)
|
|
_thumbnail_txt = bpy.data.textures.new(name=_thumbnail_img.name, type="IMAGE")
|
|
_thumbnail_txt.image = _thumbnail_img
|
|
_thumbnail_txt.extension = "EXTEND"
|
|
self.thumbnail = _thumbnail_img.name
|
|
else:
|
|
self.thumbnail = ""
|
|
|
|
def get(self):
|
|
_obj = {
|
|
"index": self.index,
|
|
"uid": self.uid,
|
|
"identifier": self.identifier,
|
|
"packagrUrl": self.packageUrl,
|
|
"label": self.label,
|
|
"physical_size": self.physical_size.get(),
|
|
"tiling": self.tiling.get(),
|
|
"presets": [],
|
|
"inputs": [],
|
|
"outputs": [],
|
|
"thumbnail": self.thumbnail
|
|
}
|
|
|
|
for _preset in self.presets:
|
|
_obj["presets"].append(_preset.get())
|
|
|
|
for _input in self.inputs:
|
|
_obj["inputs"].append(_input.get())
|
|
|
|
for _output in self.outputs:
|
|
_obj["outputs"].append(_output.get())
|
|
|
|
return _obj
|