284 lines
9.7 KiB
Python
284 lines
9.7 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: utils.py
|
|
# brief: General utility functions
|
|
# author Adobe - 3D & Immersive
|
|
# copyright 2023 Adobe Inc. All rights reserved.
|
|
|
|
|
|
import bpy
|
|
import os
|
|
import json
|
|
from time import time
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from .common import (
|
|
ICONS_DICT,
|
|
ICONS_IMAGES,
|
|
ADDON_ROOT,
|
|
ADDON_PACKAGE,
|
|
INPUT_ANGLE_CONVERSION,
|
|
IMAGE_EXPORT_FORMAT,
|
|
Code_InputWidget,
|
|
Code_InputType,
|
|
Code_InputIdentifier
|
|
)
|
|
|
|
|
|
class SUBSTANCE_Utils():
|
|
# Preview Update FIX
|
|
@staticmethod
|
|
def toggle_redraw(context):
|
|
if context.scene.sbsar_redraw < 2:
|
|
context.scene.sbsar_redraw += 1
|
|
else:
|
|
context.scene.sbsar_redraw = 0
|
|
|
|
# IMAGE COLORSPACES
|
|
@staticmethod
|
|
def get_colorspaces():
|
|
_colorspaces = []
|
|
_items = bpy.types.Image.bl_rna.properties['colorspace_settings'].fixed_type.properties['name'].enum_items
|
|
for _item in _items:
|
|
_colorspaces.append((_item.identifier, _item.name, _item.description))
|
|
return _colorspaces
|
|
|
|
# JSON
|
|
@staticmethod
|
|
def get_json(filepath):
|
|
_json_obj = None
|
|
with open(filepath, encoding="utf8") as _json_file:
|
|
_json_obj = json.load(_json_file)
|
|
return _json_obj
|
|
|
|
@staticmethod
|
|
def set_json(filepath, obj):
|
|
with open(filepath, "w", encoding="utf8") as _json_file:
|
|
json.dump(obj, _json_file, ensure_ascii=False)
|
|
|
|
# Time
|
|
@staticmethod
|
|
def get_current_time_in_ms():
|
|
return int(round(time() * 1000))
|
|
|
|
# Icons
|
|
@staticmethod
|
|
def init_icons():
|
|
_icons_dir = os.path.join(ADDON_ROOT, "_icons").replace("\\", "/")
|
|
for _image in ICONS_IMAGES:
|
|
if _image["id"] not in ICONS_DICT:
|
|
ICONS_DICT.load(_image["id"], os.path.join(_icons_dir, _image["filename"]), 'IMAGE')
|
|
|
|
# Log
|
|
@staticmethod
|
|
def log_data(type, message, display=False):
|
|
if display:
|
|
try:
|
|
bpy.ops.substance.send_message(type=type, message="Substance 3D in Blender: "+message)
|
|
except Exception:
|
|
print("Substance 3D in Blender: {} - {}".format(type, message))
|
|
else:
|
|
print("Substance 3D in Blender: {} - {}".format(type, message))
|
|
|
|
@staticmethod
|
|
def log_traceback(message):
|
|
print("Substance 3D in Blender: ERROR - TRACEBACK:\n")
|
|
print(message)
|
|
|
|
# Geo
|
|
@staticmethod
|
|
def get_selected_geo(selected_objects):
|
|
_filtered = []
|
|
for _item in selected_objects:
|
|
if hasattr(_item.data, 'materials'):
|
|
_filtered.append(_item)
|
|
return _filtered
|
|
|
|
# Graph
|
|
@staticmethod
|
|
def get_selected_graph(context=bpy.context):
|
|
_selected_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
|
_selected_graph_idx = int(_selected_sbsar.graphs_list)
|
|
_selected_graph = _selected_sbsar.graphs[_selected_graph_idx]
|
|
return _selected_graph
|
|
|
|
# Shader
|
|
@staticmethod
|
|
def get_selected_shader(context=bpy.context, idx=-1):
|
|
_addon_prefs = context.preferences.addons[ADDON_PACKAGE].preferences
|
|
|
|
_selected_preset_idx = idx
|
|
if _selected_preset_idx < 0:
|
|
_selected_preset_idx = int(_addon_prefs.shader_list)
|
|
_selected_shader_preset = _addon_prefs.shaders[_selected_preset_idx]
|
|
return _addon_prefs, "{}".format(_selected_preset_idx), _selected_shader_preset
|
|
|
|
@staticmethod
|
|
def get_shader_file(filename):
|
|
_custom_shaders_dir = os.path.join(ADDON_ROOT, "_presets/custom").replace('\\', '/')
|
|
_shaders_dir = os.path.join(ADDON_ROOT, "_presets/default").replace('\\', '/')
|
|
|
|
_path = os.path.join(_custom_shaders_dir, filename)
|
|
if not os.path.exists(_path):
|
|
_path = os.path.join(_shaders_dir, filename)
|
|
|
|
return _path
|
|
|
|
@staticmethod
|
|
def get_shader_prefs(context, shader):
|
|
_temp_inputs = getattr(context.scene, shader.inputs_class_name)
|
|
_temp_outputs = {}
|
|
for _output in shader.outputs:
|
|
_temp_outputs[_output.id] = _output.to_json()
|
|
|
|
return {
|
|
"inputs": _temp_inputs,
|
|
"outputs": _temp_outputs,
|
|
}
|
|
|
|
# SBSAR
|
|
@staticmethod
|
|
def get_unique_name(filename, context):
|
|
_unique_name = filename.replace(".sbsar", "")
|
|
_temp_name = _unique_name
|
|
_counter = 0
|
|
while _temp_name in context.scene.loaded_sbsars:
|
|
_counter += 1
|
|
_temp_name = _unique_name + "_{}".format(_counter)
|
|
|
|
if _counter:
|
|
return _unique_name + "_{}".format(_counter)
|
|
return _unique_name
|
|
|
|
# SBSAR INPUTS
|
|
@staticmethod
|
|
def inputs_empty(inputs):
|
|
for _input in inputs:
|
|
if _input.guiWidget != Code_InputWidget.nowidget.value or _input.type == Code_InputType.string.name:
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def value_fix_type(identifier, widget, type, new_value):
|
|
if widget == Code_InputWidget.combobox.value:
|
|
return int(new_value)
|
|
elif widget == Code_InputWidget.slider.value:
|
|
if type == Code_InputType.integer.name:
|
|
return int(new_value)
|
|
elif type == Code_InputType.float.name:
|
|
return float(new_value)
|
|
elif (type == Code_InputType.integer2.name or
|
|
type == Code_InputType.integer3.name or
|
|
type == Code_InputType.integer4.name):
|
|
return list(new_value)
|
|
elif (type == Code_InputType.float2.name or
|
|
type == Code_InputType.float3.name or
|
|
type == Code_InputType.float4.name):
|
|
return list(new_value)
|
|
else:
|
|
return 0
|
|
elif widget == Code_InputWidget.color.value:
|
|
if type == Code_InputType.float.name:
|
|
return float(new_value)
|
|
elif type == Code_InputType.float3.name or type == Code_InputType.float4.name:
|
|
return list(new_value)
|
|
else:
|
|
return 0
|
|
elif widget == Code_InputWidget.togglebutton.value:
|
|
return int(new_value)
|
|
elif widget == Code_InputWidget.angle.value:
|
|
return float(new_value) / INPUT_ANGLE_CONVERSION
|
|
elif widget == Code_InputWidget.position.value:
|
|
return list(new_value)
|
|
elif widget == Code_InputWidget.image.value:
|
|
if isinstance(new_value, bpy.types.Image):
|
|
return new_value.name
|
|
else:
|
|
return ""
|
|
elif widget == Code_InputWidget.nowidget.value:
|
|
if identifier == Code_InputIdentifier.outputsize.value:
|
|
return list(new_value)
|
|
elif identifier == Code_InputIdentifier.randomseed.value:
|
|
return int(new_value)
|
|
elif type == Code_InputType.string.name:
|
|
return str(new_value)
|
|
else:
|
|
return 0
|
|
else:
|
|
return 0
|
|
|
|
# Render
|
|
@staticmethod
|
|
def render_image_input(img, context):
|
|
if not context:
|
|
context = bpy.context
|
|
|
|
_addon_prefs = context.preferences.addons[ADDON_PACKAGE].preferences
|
|
_default_path = _addon_prefs.path_default
|
|
|
|
_format_idx = int(_addon_prefs.default_export_format)
|
|
_file_extension = IMAGE_EXPORT_FORMAT[_format_idx][2].replace("*", "")
|
|
img.file_format = IMAGE_EXPORT_FORMAT[_format_idx][1]
|
|
_image_filepath = os.path.join(_default_path, img.name + _file_extension).replace("\\", "/")
|
|
img.save_render(_image_filepath)
|
|
|
|
return _image_filepath
|
|
|
|
# Physical Size
|
|
@staticmethod
|
|
def get_physical_size(physical_size, context):
|
|
_scale = context.scene.unit_settings.scale_length
|
|
|
|
_new_physical_size = [
|
|
physical_size[0] * _scale,
|
|
physical_size[1] * _scale,
|
|
physical_size[2] * _scale
|
|
]
|
|
return _new_physical_size
|
|
|
|
# Output Size
|
|
@staticmethod
|
|
def update_preset_outputsize(preset_value, input, type, value):
|
|
_preset_xml = ET.fromstring(preset_value)
|
|
|
|
_input_item = None
|
|
for _preset_input in _preset_xml:
|
|
if _preset_input.attrib["uid"] == str(input.id):
|
|
_input_item = _preset_input
|
|
|
|
if _input_item is not None:
|
|
if type == Code_InputType.integer.value:
|
|
_input_item.attrib["value"] = "{}".format(value)
|
|
elif type == Code_InputType.integer2.value:
|
|
_input_item.attrib["value"] = "{},{}".format(value[0], value[1])
|
|
else:
|
|
_attrib = {
|
|
"identifier": input.identifier,
|
|
"uid": "{}".format(input.id),
|
|
"type": "{}".format(type)
|
|
}
|
|
if type == Code_InputType.integer.value:
|
|
_attrib["value"] = "{}".format(value)
|
|
elif type == Code_InputType.integer2.value:
|
|
_attrib["value"] = "{},{}".format(value[0], value[1])
|
|
|
|
_el = ET.SubElement(_preset_xml, "presetinput", attrib=_attrib)
|
|
_el.tail = "\n"
|
|
|
|
_new_preset_value = ET.tostring(_preset_xml, encoding='unicode')
|
|
return _new_preset_value
|