527 lines
22 KiB
Python
527 lines
22 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: factory/sbsar.py
|
|
# brief: Dynamic class creation for sbsar objects
|
|
# author Adobe - 3D & Immersive
|
|
# copyright 2023 Adobe Inc. All rights reserved.
|
|
|
|
|
|
import bpy
|
|
from copy import deepcopy
|
|
import sys
|
|
import traceback
|
|
from ..utils import SUBSTANCE_Utils
|
|
from ..sbsar.callbacks import SUBSTANCE_SbsarCallbacks
|
|
from ..common import (
|
|
Code_InputWidget,
|
|
Code_InputType,
|
|
Code_InputIdentifier,
|
|
Code_OutputSizeSuffix,
|
|
Code_Response,
|
|
RESOLUTIONS_DICT,
|
|
INPUT_ANGLE_CONVERSION,
|
|
INPUT_INT_MAX
|
|
)
|
|
|
|
|
|
# Inputs functions
|
|
def _to_json(self):
|
|
pass
|
|
|
|
|
|
def _from_json(self, inputs, data):
|
|
for _item in data:
|
|
if Code_InputIdentifier.outputsize.value == _item["identifier"]:
|
|
_output_size = _item["value"]
|
|
_output_size_linked = _output_size[0] == _output_size[1]
|
|
setattr(
|
|
self,
|
|
Code_InputIdentifier.outputsize.value + Code_OutputSizeSuffix.width.value,
|
|
str(_output_size[0]))
|
|
setattr(
|
|
self,
|
|
Code_InputIdentifier.outputsize.value + Code_OutputSizeSuffix.height.value,
|
|
str(_output_size[1]))
|
|
setattr(
|
|
self,
|
|
Code_InputIdentifier.outputsize.value + Code_OutputSizeSuffix.linked.value,
|
|
_output_size_linked)
|
|
continue
|
|
elif not hasattr(self, _item["identifier"]):
|
|
continue
|
|
|
|
_input = inputs[_item["identifier"]]
|
|
|
|
_value = _item["value"]
|
|
if _input.guiWidget == Code_InputWidget.combobox.value:
|
|
_value = str(_value)
|
|
elif _input.guiWidget == Code_InputWidget.slider.value:
|
|
pass
|
|
elif _input.guiWidget == Code_InputWidget.color.value:
|
|
pass
|
|
elif _input.guiWidget == Code_InputWidget.togglebutton.value:
|
|
_value = str(_value)
|
|
elif _input.guiWidget == Code_InputWidget.angle.value:
|
|
_value = _value * INPUT_ANGLE_CONVERSION
|
|
elif _input.guiWidget == Code_InputWidget.position.value:
|
|
pass
|
|
elif _input.guiWidget == Code_InputWidget.image.value:
|
|
pass
|
|
elif _input.identifier == Code_InputIdentifier.randomseed.value:
|
|
pass
|
|
else:
|
|
pass
|
|
|
|
setattr(self, _item["identifier"], _value)
|
|
|
|
|
|
class SUBSTANCE_SbsarFactory():
|
|
|
|
@staticmethod
|
|
def init_enum_values(values):
|
|
_items = []
|
|
for _item in values:
|
|
_items.append(
|
|
(str(_item.first), _item.second, "{}:{}".format(_item.first, _item.second))
|
|
)
|
|
return _items
|
|
|
|
@staticmethod
|
|
def init_toggle_values(values):
|
|
_items = []
|
|
for _idx, _item in enumerate(values):
|
|
_items.append(
|
|
(str(_idx), _item, "{}:{}".format(_idx, _item))
|
|
)
|
|
return _items
|
|
|
|
@staticmethod
|
|
def create_input_item(input, class_name):
|
|
if input.type == Code_InputType.string.name:
|
|
return bpy.props.StringProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=str(input.defaultValue),
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(self, context, input.identifier))
|
|
elif input.guiWidget == Code_InputWidget.combobox.value:
|
|
return bpy.props.EnumProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=str(input.defaultValue),
|
|
items=SUBSTANCE_SbsarFactory.init_enum_values(input.enumValues),
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(self, context, input.identifier))
|
|
|
|
elif input.guiWidget == Code_InputWidget.slider.value:
|
|
if input.type == Code_InputType.integer.name:
|
|
_max = input.maxValue
|
|
_min = input.minValue
|
|
if _max == _min:
|
|
_max = INPUT_INT_MAX
|
|
_min = INPUT_INT_MAX*-1
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.IntProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.IntProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
|
|
elif input.type == Code_InputType.float.name:
|
|
_max = input.maxValue
|
|
_min = input.minValue
|
|
if _max == _min:
|
|
_max = sys.float_info.max
|
|
_min = sys.float_info.min
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.FloatProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.FloatProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
|
|
elif (input.type == Code_InputType.integer2.name or
|
|
input.type == Code_InputType.integer3.name or
|
|
input.type == Code_InputType.integer4.name):
|
|
_dimension = len(input.defaultValue)
|
|
_max = input.maxValue[0]
|
|
_min = input.minValue[0]
|
|
if _max == _min:
|
|
_max = INPUT_INT_MAX
|
|
_min = INPUT_INT_MAX*-1
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.IntVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.IntVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
|
|
elif (input.type == Code_InputType.float2.name or
|
|
input.type == Code_InputType.float3.name or
|
|
input.type == Code_InputType.float4.name):
|
|
_dimension = len(input.defaultValue)
|
|
_max = input.maxValue[0]
|
|
_min = input.minValue[0]
|
|
if _max == _min:
|
|
_max = sys.float_info.max
|
|
_min = sys.float_info.min
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.FloatVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.FloatVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return None
|
|
|
|
elif input.guiWidget == Code_InputWidget.color.value:
|
|
if input.type == Code_InputType.float.name:
|
|
_max = input.maxValue
|
|
_min = input.minValue
|
|
if _max == _min:
|
|
_max = 1
|
|
_min = 0
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.FloatProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.FloatProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
elif input.type == Code_InputType.float3.name or input.type == Code_InputType.float4.name:
|
|
_dimension = len(input.defaultValue)
|
|
_max = input.maxValue[0]
|
|
_min = input.minValue[0]
|
|
if _max == _min:
|
|
_max = 1
|
|
_min = 0
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.FloatVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
subtype="COLOR",
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.FloatVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
subtype="COLOR",
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return None
|
|
|
|
elif input.guiWidget == Code_InputWidget.togglebutton.value:
|
|
_label_true = input.labelTrue if input.labelTrue != "" else "True"
|
|
_label_false = input.labelFalse if input.labelFalse != "" else "False"
|
|
_toggle_labels = [_label_false, _label_true]
|
|
return bpy.props.EnumProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=str(input.defaultValue),
|
|
items=SUBSTANCE_SbsarFactory.init_toggle_values(_toggle_labels),
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
|
|
elif input.guiWidget == Code_InputWidget.angle.value:
|
|
_max = input.maxValue * INPUT_ANGLE_CONVERSION
|
|
_min = input.minValue * INPUT_ANGLE_CONVERSION
|
|
_default = input.defaultValue * INPUT_ANGLE_CONVERSION
|
|
if _max == _min:
|
|
_max = sys.float_info.max
|
|
_min = sys.float_info.min
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.FloatProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
subtype="ANGLE",
|
|
default=_default,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.FloatProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
subtype="ANGLE",
|
|
default=_default,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
|
|
elif input.guiWidget == Code_InputWidget.position.value:
|
|
_dimension = len(input.defaultValue)
|
|
_max = input.maxValue[0]
|
|
_min = input.minValue[0]
|
|
if _max == _min:
|
|
_max = 1
|
|
_min = 0
|
|
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.FloatVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
subtype="XYZ",
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
max=_max,
|
|
min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.FloatVectorProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
subtype="XYZ",
|
|
size=_dimension,
|
|
default=input.defaultValue,
|
|
soft_max=_max,
|
|
soft_min=_min,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
|
|
elif input.guiWidget == Code_InputWidget.image.value:
|
|
return bpy.props.PointerProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
type=bpy.types.Image,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
|
|
elif input.guiWidget == Code_InputWidget.nowidget.value:
|
|
if input.identifier == Code_InputIdentifier.randomseed.value:
|
|
if hasattr(input, "sliderClamp") and input.sliderClamp:
|
|
return bpy.props.IntProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
max=input.maxValue,
|
|
min=input.minValue,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
else:
|
|
return bpy.props.IntProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue,
|
|
soft_max=input.maxValue,
|
|
soft_min=input.minValue,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_input_changed(
|
|
self,
|
|
context,
|
|
input.identifier))
|
|
if input.identifier == Code_InputIdentifier.outputsize.value:
|
|
return [
|
|
bpy.props.EnumProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=str(input.defaultValue[0]),
|
|
items=RESOLUTIONS_DICT,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_linked_changed(
|
|
self,
|
|
context,
|
|
input.identifier + Code_OutputSizeSuffix.width.value)),
|
|
bpy.props.EnumProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=str(input.defaultValue[1]),
|
|
items=RESOLUTIONS_DICT,
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_outputsize_changed(
|
|
self,
|
|
context,
|
|
input.identifier + Code_OutputSizeSuffix.height.value)),
|
|
bpy.props.BoolProperty(
|
|
name=input.label,
|
|
description=input.guiDescription,
|
|
default=input.defaultValue[0] == input.defaultValue[1],
|
|
update=lambda self, context: SUBSTANCE_SbsarCallbacks.on_linked_changed(
|
|
self,
|
|
context,
|
|
input.identifier + Code_OutputSizeSuffix.linked.value))
|
|
]
|
|
else:
|
|
return None
|
|
else:
|
|
return None
|
|
|
|
@staticmethod
|
|
def register_inputs_class(sbsar):
|
|
for _graph in sbsar.graphs:
|
|
_attributes = {}
|
|
for _key, _input in _graph.inputs.items():
|
|
_attribute = SUBSTANCE_SbsarFactory.create_input_item(_input, _graph.inputs_class_name)
|
|
if _attribute:
|
|
if _input.identifier == Code_InputIdentifier.outputsize.value:
|
|
_attributes[_input.identifier + Code_OutputSizeSuffix.width.value] = _attribute[0]
|
|
_attributes[_input.identifier + Code_OutputSizeSuffix.height.value] = _attribute[1]
|
|
_attributes[_input.identifier + Code_OutputSizeSuffix.linked.value] = _attribute[2]
|
|
else:
|
|
_attributes[_input.identifier] = _attribute
|
|
|
|
else:
|
|
SUBSTANCE_Utils.log_data("WARNING", "Input not created [{}]".format(_input.identifier))
|
|
|
|
_inputs_class = type(_graph.inputs_class_name, (bpy.types.PropertyGroup,), {
|
|
"__annotations__": _attributes,
|
|
"sbsar_uuid": sbsar.uuid,
|
|
"default": deepcopy(_graph),
|
|
"callback": {"enabled": True},
|
|
"to_json": _to_json,
|
|
"from_json": _from_json
|
|
})
|
|
bpy.utils.register_class(_inputs_class)
|
|
setattr(
|
|
bpy.types.Scene,
|
|
_graph.inputs_class_name,
|
|
bpy.props.PointerProperty(name=_graph.inputs_class_name, type=_inputs_class))
|
|
|
|
@staticmethod
|
|
def register_class(sbsar):
|
|
try:
|
|
SUBSTANCE_SbsarFactory.register_inputs_class(sbsar)
|
|
return (Code_Response.success, sbsar)
|
|
except Exception:
|
|
SUBSTANCE_Utils.log_data("ERROR", "Exception - Susbtance register error:")
|
|
SUBSTANCE_Utils.log_traceback(traceback.format_exc())
|
|
return (Code_Response.sbsar_factory_register_error, None)
|
|
|
|
@staticmethod
|
|
def unregister_class(class_name):
|
|
if hasattr(bpy.context.scene, class_name):
|
|
_object = getattr(bpy.context.scene, class_name)
|
|
_class_type = type(_object)
|
|
|
|
delattr(bpy.types.Scene, class_name)
|
|
bpy.utils.unregister_class(_class_type)
|