Files
blender-portable-repo/scripts/addons/Substance3DInBlender/sbsar/sbsar.py
T
2026-03-17 14:30:01 -06:00

345 lines
12 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: sbsar/sbsar.py
# brief: Substance class object definition
# author Adobe - 3D & Immersive
# copyright 2023 Adobe Inc. All rights reserved.
from ..utils import SUBSTANCE_Utils
from ..common import (
Code_InputType,
Code_InputWidget,
INPUT_DEFAULT_GROUP,
INPUT_IMAGE_DEFAULT_GROUP,
PRESET_CUSTOM,
CLASS_GRAPH_INPUTS
)
class SBS_EnumValue():
def __init__(self, enum):
self.first = enum["first"]
self.second = enum["second"]
def to_json(self):
_obj = {
"first": self.first,
"second": self.second,
}
return _obj
class SBS_Input():
def __init__(self, index, input):
if "index" in input:
self.index = input["index"]
else:
self.index = index
self.id = input["id"]
self.graphID = input["graphID"]
self.identifier = input["identifier"]
self.label = input["label"]
self.guiDescription = input["guiDescription"]
if input["type"] == Code_InputType.image.name:
self.guiGroup = INPUT_IMAGE_DEFAULT_GROUP
elif len(input["guiGroup"]) == 0:
self.guiGroup = INPUT_DEFAULT_GROUP
else:
self.guiGroup = input["guiGroup"]
self.guiVisibleIf = input["guiVisibleIf"]
self.userTag = input["userTag"]
self.type = input["type"]
self.guiWidget = input["guiWidget"]
self.showAsPin = input["showAsPin"]
self.useCache = input["useCache"]
self.visibleIf = input["visibleIf"]
self.isHeavyDuty = input["isHeavyDuty"]
self.enumValues = []
if "labelFalse" in input:
self.labelFalse = input["labelFalse"]
if "labelTrue" in input:
self.labelTrue = input["labelTrue"]
if "sliderClamp" in input:
self.sliderClamp = input["sliderClamp"]
if "sliderStep" in input:
self.sliderStep = input["sliderStep"]
if "maxValue" in input:
self.maxValue = input["maxValue"]
if "minValue" in input:
self.minValue = input["minValue"]
if "defaultValue" in input:
self.defaultValue = input["defaultValue"]
if "value" in input:
self.value = input["value"]
if "channelUse" in input:
self.channelUse = input["channelUse"]
if "enumValues" in input:
for _enum in input["enumValues"]:
_new_enum = SBS_EnumValue(_enum)
self.enumValues.append(_new_enum)
def to_json(self):
_obj = {
"id": self.id,
"graphID": self.graphID,
"identifier": self.identifier,
"label": self.label,
"guiDescription": self.guiDescription,
"guiGroup": self.guiGroup,
"guiVisibleIf": self.guiVisibleIf,
"userTag": self.userTag,
"type": self.type,
"guiWidget": self.guiWidget,
"showAsPin": self.showAsPin,
"useCache": self.useCache,
"visibleIf": self.visibleIf,
"isHeavyDuty": self.isHeavyDuty
}
if hasattr(self, "labelFalse"):
_obj["labelFalse"] = self.labelFalse
if hasattr(self, "labelTrue"):
_obj["labelTrue"] = self.labelTrue
if hasattr(self, "sliderClamp"):
_obj["sliderClamp"] = self.sliderClamp
if hasattr(self, "sliderStep"):
_obj["sliderStep"] = self.sliderStep
if hasattr(self, "maxValue"):
_obj["maxValue"] = self.maxValue
if hasattr(self, "minValue"):
_obj["minValue"] = self.minValue
if hasattr(self, "defaultValue"):
_obj["defaultValue"] = self.defaultValue
if hasattr(self, "value"):
_obj["value"] = self.value
if hasattr(self, "channelUse"):
_obj["channelUse"] = self.channelUse
if hasattr(self, "enumValues"):
_obj["enumValues"] = self.enumValues
return _obj
class SBS_Output():
def __init__(self, output, index):
self.id = output["id"]
self.index = index
self.graphID = output["graphID"]
self.format = output["format"]
self.mipmaps = output["mipmaps"]
self.identifier = output["identifier"]
self.label = output["label"]
self.guiDescription = output["guiDescription"]
self.group = output["group"]
self.guiVisibleIf = output["guiVisibleIf"]
self.userTag = output["userTag"]
self.type = output["type"]
self.guiType = output["guiType"]
self.defaultChannelUse = output["defaultChannelUse"]
self.enabled = output["enabled"]
self.channelUseSpecified = output["channelUseSpecified"]
self.channelUse = output["channelUse"]
def to_json(self):
_obj = {
"id": self.id,
"graphID": self.graphID,
"format": self.format,
"mipmaps": self.mipmaps,
"identifier": self.identifier,
"label": self.label,
"guiDescription": self.guiDescription,
"group": self.group,
"guiVisibleIf": self.guiVisibleIf,
"userTag": self.userTag,
"type": self.type,
"guiType": self.guiType,
"defaultChannelUse": self.defaultChannelUse,
"enabled": self.enabled,
"channelUseSpecified": self.channelUseSpecified,
"channelUse": self.channelUse
}
return _obj
class SBS_Preset():
def __init__(self, preset, addon_prefs=None, inputs=None):
self.index = str(preset["index"])
self.label = preset["label"]
self.value = preset["value"]
if "icon" in preset:
self.icon = preset["icon"]
else:
self.icon = "LOCKED" if self.label != PRESET_CUSTOM else "UNLOCKED"
if "embedded" in preset:
self.embedded = preset["embedded"]
else:
self.embedded = True if self.label != PRESET_CUSTOM else False
if addon_prefs is not None and inputs is not None:
if "$outputsize" in inputs:
self.value = SUBSTANCE_Utils.update_preset_outputsize(
self.value,
inputs["$outputsize"],
Code_InputType.integer2.value,
addon_prefs.default_resolution.get())
if "normal_format" in inputs:
self.value = SUBSTANCE_Utils.update_preset_outputsize(
self.value,
inputs["normal_format"],
Code_InputType.integer.value,
0 if addon_prefs.default_normal_format == "DirectX" else 1)
def to_json(self):
_obj = {
"index": int(self.index),
"label": self.label,
"value": self.value,
"icon": self.icon,
"embedded": self.embedded
}
return _obj
class SBS_Graph():
def __init__(self, unique_name, uuid, index, graph, multi_graph, addon_prefs=None):
self.index = str(index)
self.uid = str(graph["uid"])
self.label = graph["label"]
self.identifier = graph["packageUrl"].replace("pkg://", "")
self.packageUrl = graph["packageUrl"]
if graph["physicalSize"][0] == 0 or graph["physicalSize"][1] == 0:
self.physicalSize = (1/100, 1/100, 1/100)
else:
self.physicalSize = (graph["physicalSize"][0]/100,
graph["physicalSize"][1]/100,
graph["physicalSize"][2]/100)
self.tiling = addon_prefs.default_tiling.get()
self.inputs = {}
self.inputs_groups = {}
self.outputs = {}
self.presets = []
if multi_graph:
_material_name = "{}-{}".format(unique_name, graph["label"]).replace(" ", "_")
_class_name = "{}-{}".format(uuid, self.uid)
else:
_material_name = "{}".format(unique_name).replace(" ", "_")
_class_name = "{}".format(uuid)
self.material = _material_name
self.inputs_class_name = CLASS_GRAPH_INPUTS.format(_class_name)
if len(graph["inputs"]) > 0 and "index" in graph["inputs"][0]:
_sorted_inputs = sorted(graph["inputs"], key=lambda d: d['index'])
else:
_sorted_inputs = graph["inputs"]
_input_index = 0
for _input in _sorted_inputs:
if _input["guiWidget"] != Code_InputWidget.nowidget.value or _input["type"] != Code_InputType.string.name:
if _input["type"] == Code_InputType.image.name:
_group = INPUT_IMAGE_DEFAULT_GROUP
else:
_group = _input["guiGroup"] if len(_input["guiGroup"]) > 0 else INPUT_DEFAULT_GROUP
if _group not in self.inputs_groups:
self.inputs_groups[_group] = [_input["identifier"]]
else:
self.inputs_groups[_group].append(_input["identifier"])
_new_input = SBS_Input(_input_index, _input)
_input_index += 1
self.inputs[_input["identifier"]] = _new_input
for _idx, _output in enumerate(graph["outputs"]):
_new_output = SBS_Output(_output, _idx)
self.outputs[_output["identifier"]] = _new_output
_pre_sort_presets = []
for _preset in graph["presets"]:
_new_preset = SBS_Preset(_preset, addon_prefs, self.inputs)
_pre_sort_presets.append(_new_preset)
self.presets = sorted(_pre_sort_presets, key=lambda _preset: _preset.index, reverse=False)
def to_json(self):
_obj = {
"uid": self.uid,
"label": self.label,
"physicalSize": self.physicalSize,
"inputs": [],
"outputs": [],
"presets": []
}
for _preset in self.presets:
_obj["presets"].append(_preset.to_json())
for _key, _output in self.outputs.items():
_obj["outputs"].append(_output.to_json())
for _key, _input in self.inputs.items():
_obj["inputs"].append(_input.to_json())
return _obj
def reset_presets(self, presets):
self.presets = []
_pre_sort_presets = []
for _preset in presets:
_new_preset = SBS_Preset(_preset)
_pre_sort_presets.append(_new_preset)
self.presets = sorted(_pre_sort_presets, key=lambda _preset: _preset.index, reverse=False)
class SBSAR():
def __init__(self, unique_name, filename, data, addon_prefs=None):
self.uuid = data["uuid"]
self.version = data["version"]
self.name = unique_name
self.filename = filename
self.filepath = data["filename"]
self.graphs = []
_multi_graph = len(data["graphs"]) > 1
for _idx, _graph in enumerate(data["graphs"]):
_new_graph = SBS_Graph(unique_name, self.uuid, _idx, _graph, _multi_graph, addon_prefs)
self.graphs.append(_new_graph)
def to_json(self):
_obj = {
"uuid": self.uuid,
"name": self.name,
"filename": self.filename,
"filepath": self.filepath,
"graphs": []
}
for _graph in self.graphs:
_obj["graphs"].append(_graph.to_json())
return _obj