2025-07-01
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
"""
|
||||
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: ops/sbsar.py
|
||||
# brief: Sinstance Operators
|
||||
# author Adobe - 3D & Immersive
|
||||
# copyright 2023 Adobe Inc. All rights reserved.
|
||||
|
||||
|
||||
import os
|
||||
import bpy
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
|
||||
from ..api import SUBSTANCE_Api
|
||||
from ..material.manager import SUBSTANCE_MaterialManager
|
||||
from ..utils import SUBSTANCE_Utils
|
||||
from ..thread_ops import SUBSTANCE_Threads
|
||||
from ..sbsar.sbsar import SBSAR
|
||||
from ..sbsar.async_ops import _render_sbsar
|
||||
from .common import load_sbsar
|
||||
from ..common import (
|
||||
Code_SbsarOp,
|
||||
Code_Response,
|
||||
ADDON_PACKAGE,
|
||||
TOOLKIT_EXPECTED_VERSION
|
||||
)
|
||||
|
||||
|
||||
class SUBSTANCE_OT_LoadSBSAR(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = 'substance.load_sbsar'
|
||||
bl_label = 'Load Substance Material'
|
||||
bl_description = 'Open file browser to select a Substance 3D material'
|
||||
bl_options = {'REGISTER'}
|
||||
filename_ext = '.sbsar'
|
||||
filter_glob: bpy.props.StringProperty(default='*.sbsar', options={'HIDDEN'}) # noqa
|
||||
files: bpy.props.CollectionProperty(name='Substance 3D material files', type=bpy.types.OperatorFileListElement) # noqa
|
||||
directory: bpy.props.StringProperty(subtype="DIR_PATH") # noqa
|
||||
|
||||
def __init__(self):
|
||||
self.filepath = ''
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
_, _toolkit_version = SUBSTANCE_Api.toolkit_version_get()
|
||||
return _toolkit_version is not None and _toolkit_version in TOOLKIT_EXPECTED_VERSION
|
||||
|
||||
def invoke(self, context, event):
|
||||
if not SUBSTANCE_Api.currently_running:
|
||||
# Initialize SUBSTANCE_Api
|
||||
_result = SUBSTANCE_Api.initialize()
|
||||
if _result[0] != Code_Response.success:
|
||||
SUBSTANCE_Utils.log_data("ERROR", "[{}] The SRE cannot initialize...".format(_result))
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
_addon_prefs = context.preferences.addons[ADDON_PACKAGE].preferences
|
||||
self.filepath = _addon_prefs.path_library
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def execute(self, context):
|
||||
SUBSTANCE_Threads.cursor_push('WAIT')
|
||||
for _f in self.files:
|
||||
if _f.name.endswith('.sbsar'):
|
||||
if len(_f.name) > 0:
|
||||
_filepath = os.path.join(self.directory, _f.name)
|
||||
|
||||
load_sbsar(_f.name, _filepath)
|
||||
else:
|
||||
SUBSTANCE_Utils.log_data("ERROR", "[{}] is not a valid sbsar file".format(_f.name), display=True)
|
||||
|
||||
SUBSTANCE_Threads.cursor_pop()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SUBSTANCE_OT_ApplySBSAR(bpy.types.Operator):
|
||||
bl_idname = 'substance.apply_sbsar'
|
||||
bl_label = 'Apply the Substance 3D material'
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
_selected_geo = SUBSTANCE_Utils.get_selected_geo(context.selected_objects)
|
||||
if len(context.scene.loaded_sbsars) > 0 and len(_selected_geo) > 0:
|
||||
_selected_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
||||
if _selected_sbsar.load_success:
|
||||
return "Applies the selected Substance 3D material to the selected object(s)"
|
||||
else:
|
||||
return "This Substance 3D material loaded incorrectly"
|
||||
else:
|
||||
return "Please load at least one Substance file and select an object in the scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
_selected_geo = SUBSTANCE_Utils.get_selected_geo(context.selected_objects)
|
||||
if len(context.scene.loaded_sbsars) > 0 and len(_selected_geo) > 0:
|
||||
_selected_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
||||
return _selected_sbsar.load_success
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
_selected_graph = SUBSTANCE_Utils.get_selected_graph(context)
|
||||
_material = SUBSTANCE_MaterialManager.get_existing_material(_selected_graph.material.name)
|
||||
|
||||
if _material is None:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Substance Material needs to finish rendering before applying",
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
_selected_objects = context.selected_objects
|
||||
if len(_selected_objects) == 0:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Material [{}] cannot be added, there are no selected objects".format(_material.name),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
SUBSTANCE_MaterialManager.apply_material(context, _selected_objects, _material)
|
||||
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"INFO",
|
||||
"Material slot with substance [{}] added to the object".format(_material.name),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SUBSTANCE_OT_RemoveSBSAR(bpy.types.Operator):
|
||||
bl_idname = 'substance.remove_sbsar'
|
||||
bl_label = 'Remove the Substance 3D material'
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
if len(context.scene.loaded_sbsars) > 0:
|
||||
return "Remove the selected Substance 3D material"
|
||||
else:
|
||||
return "Please load at least one Substance file"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if len(context.scene.loaded_sbsars) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
_idx = context.scene.sbsar_index
|
||||
_selected_sbsar = context.scene.loaded_sbsars[_idx]
|
||||
_sbsar_uuid = _selected_sbsar.uuid
|
||||
_sbsar_name = _selected_sbsar.name
|
||||
|
||||
context.scene.loaded_sbsars.remove(_idx)
|
||||
if context.scene.sbsar_index != 0 and context.scene.sbsar_index >= len(context.scene.loaded_sbsars):
|
||||
context.scene.sbsar_index = len(context.scene.loaded_sbsars) - 1
|
||||
|
||||
_result = SUBSTANCE_Api.sbsar_remove(_sbsar_uuid)
|
||||
if _result[0] != Code_Response.success:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Substance [{}] could not be removed".format(_sbsar_name),
|
||||
display=True)
|
||||
if _result[1]["result"] != Code_Response.success.value:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"An error ocurred while removing Substance [{}]. Failed with error [{}]".format(
|
||||
_sbsar_name, _result[1]["result"]),
|
||||
display=True)
|
||||
_result = SUBSTANCE_Api.sbsar_unregister(_sbsar_uuid)
|
||||
if _result[0] != Code_Response.success:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Classes of Substance [{}] could not be removed".format(_sbsar_name),
|
||||
display=True)
|
||||
context.area.tag_redraw()
|
||||
|
||||
SUBSTANCE_Utils.log_data("INFO", "Substance [{}] was removed correctly".format(_sbsar_name), display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SUBSTANCE_OT_ReloadSBSAR(bpy.types.Operator):
|
||||
bl_idname = 'substance.reload_sbsar'
|
||||
bl_label = 'Reload the Substance 3D material'
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
if len(context.scene.loaded_sbsars) > 0:
|
||||
return "Reload the selected Substance 3D material"
|
||||
else:
|
||||
return "Please load at least one Substance file"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if len(context.scene.loaded_sbsars) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
_idx = context.scene.sbsar_index
|
||||
_selected_sbsar = context.scene.loaded_sbsars[_idx]
|
||||
_sbsar_uuid = _selected_sbsar.uuid
|
||||
_sbsar_name = _selected_sbsar.name
|
||||
_sbsar_filename = _selected_sbsar.filename
|
||||
_sbsar_filepath = _selected_sbsar.filepath
|
||||
|
||||
_addon_prefs, _selected_shader_idx, _selected_shader_preset = SUBSTANCE_Utils.get_selected_shader(context)
|
||||
_normal_format = _addon_prefs.default_normal_format
|
||||
_output_size = _addon_prefs.default_resolution.get()
|
||||
|
||||
# REMOVE OLD SBSAR
|
||||
context.scene.loaded_sbsars.remove(_idx)
|
||||
if context.scene.sbsar_index != 0 and context.scene.sbsar_index >= len(context.scene.loaded_sbsars):
|
||||
context.scene.sbsar_index = len(context.scene.loaded_sbsars) - 1
|
||||
|
||||
_result = SUBSTANCE_Api.sbsar_remove(_sbsar_uuid)
|
||||
if _result[0] != Code_Response.success:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Substance [{}] could not be removed".format(_sbsar_name),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
if _result[1]["result"] != Code_Response.success.value:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"An error ocurred while removing Substance [{}]. Failed with error [{}]".format(
|
||||
_sbsar_name, _result[1]["result"]),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
_result = SUBSTANCE_Api.sbsar_unregister(_sbsar_uuid)
|
||||
if _result[0] != Code_Response.success:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Classes of Substance [{}] could not be removed".format(_sbsar_name),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
# LOAD NEW SBSAR
|
||||
SUBSTANCE_Utils.log_data("INFO", "Begin loading substance from file [{}]".format(_sbsar_filename))
|
||||
_data = {
|
||||
"filepath": _sbsar_filepath,
|
||||
"name": _sbsar_name,
|
||||
"$outputsize": _output_size,
|
||||
"normal_format": 0 if _normal_format == "DirectX" else 1
|
||||
}
|
||||
|
||||
# Load the sbsar to the SRE
|
||||
_result = SUBSTANCE_Api.sbsar_load(_data)
|
||||
if _result[0] != Code_Response.success:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Substance file [{}] located at [{}] could not be loaded".format(_sbsar_filename, _sbsar_filepath),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
if _result[1]["result"] != Code_Response.success.value:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"An error ocurred while loading Substance file [{}]. Failed with error [{}]".format(
|
||||
_sbsar_filepath, _result[1]["result"]),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
_sbsar = SBSAR(_sbsar_name, _sbsar_filename, _result[1]["data"], _addon_prefs)
|
||||
_loaded_sbsar = context.scene.loaded_sbsars.add()
|
||||
_loaded_sbsar.init(_sbsar)
|
||||
_loaded_sbsar.init_shader(_selected_shader_idx, _selected_shader_preset, _addon_prefs.get_default_output())
|
||||
SUBSTANCE_Api.sbsar_register(_sbsar)
|
||||
context.scene.sbsar_index = len(context.scene.loaded_sbsars) - 1
|
||||
|
||||
for _idx, _graph in enumerate(_loaded_sbsar.graphs):
|
||||
SUBSTANCE_Threads.alt_thread_run(_render_sbsar, (context, _loaded_sbsar, _idx))
|
||||
|
||||
SUBSTANCE_Utils.log_data("INFO", "Substance from file [{}] loaded".format(_sbsar_filename))
|
||||
|
||||
context.area.tag_redraw()
|
||||
SUBSTANCE_Utils.log_data("INFO", "Substance [{}] was removed correctly".format(_sbsar_name), display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SUBSTANCE_OT_DuplicateSBSAR(bpy.types.Operator):
|
||||
bl_idname = 'substance.duplicate_sbsar'
|
||||
bl_label = 'Duplicate the Substance 3D material'
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
if len(context.scene.loaded_sbsars) > 0:
|
||||
_selected_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
||||
if _selected_sbsar.load_success:
|
||||
return "Duplicate the selected Substance 3D material"
|
||||
else:
|
||||
return "This Substance 3D material loaded incorrectly"
|
||||
else:
|
||||
return "Please load at least one Substance file"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if len(context.scene.loaded_sbsars) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
SUBSTANCE_Threads.cursor_push('WAIT')
|
||||
_tmp_sbsar = context.scene.loaded_sbsars[context.scene.sbsar_index]
|
||||
_sbsar_name = _tmp_sbsar.name
|
||||
_sbsar = context.scene.loaded_sbsars[_sbsar_name]
|
||||
|
||||
_unique_name = SUBSTANCE_Utils.get_unique_name(_sbsar.name, context)
|
||||
|
||||
_data = {
|
||||
"filepath": _sbsar.filepath,
|
||||
"uuid": _sbsar.uuid,
|
||||
"name": _unique_name
|
||||
}
|
||||
|
||||
# Load the sbsar to the SRE
|
||||
_result = SUBSTANCE_Api.sbsar_load(_data, operation=Code_SbsarOp.duplicate)
|
||||
if _result[0] != Code_Response.success:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"Substance file [{}] located at [{}] could not be loaded".format(_sbsar.filename, _sbsar.filepath),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
if _result[1]["result"] != Code_Response.success.value:
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"ERROR",
|
||||
"An error ocurred while loading Substance file [{}]. Failed with error [{}]".format(
|
||||
_sbsar.filepath, _result[1]["result"]),
|
||||
display=True)
|
||||
return {'FINISHED'}
|
||||
|
||||
_addon_prefs = bpy.context.preferences.addons[ADDON_PACKAGE].preferences
|
||||
_new_sbsar = SBSAR(_unique_name, _sbsar.filename, _result[1]["data"], _addon_prefs)
|
||||
|
||||
for _graph_obj in _new_sbsar.graphs:
|
||||
_presets = []
|
||||
_preset_list = _sbsar.graphs[int(_graph_obj.index)].presets
|
||||
for _item in _preset_list:
|
||||
_obj = {
|
||||
"index": int(_item.index),
|
||||
"label": _item.label,
|
||||
"value": _item.value,
|
||||
"icon": _item.icon,
|
||||
"embedded": _item.embedded
|
||||
}
|
||||
_presets.append(_obj)
|
||||
_graph_obj.reset_presets(_presets)
|
||||
_loaded_sbsar = context.scene.loaded_sbsars.add()
|
||||
|
||||
# Reset _sbsar reference otherwise we get a memory error
|
||||
_sbsar = context.scene.loaded_sbsars[_sbsar_name]
|
||||
|
||||
_loaded_sbsar.init(_new_sbsar)
|
||||
_loaded_sbsar.init_shader_duplicate(_sbsar)
|
||||
_loaded_sbsar.init_tiling(_sbsar)
|
||||
_loaded_sbsar.init_outputs(_sbsar)
|
||||
_loaded_sbsar.init_presets(_sbsar)
|
||||
SUBSTANCE_Api.sbsar_register(_new_sbsar)
|
||||
context.scene.sbsar_index = len(context.scene.loaded_sbsars) - 1
|
||||
|
||||
for _idx, _graph in enumerate(_loaded_sbsar.graphs):
|
||||
SUBSTANCE_Threads.alt_thread_run(_render_sbsar, (context, _loaded_sbsar, _idx))
|
||||
|
||||
SUBSTANCE_Utils.log_data(
|
||||
"INFO",
|
||||
"Substance [{}] duplicated succesfully".format(_sbsar.name),
|
||||
display=True)
|
||||
SUBSTANCE_Threads.cursor_pop()
|
||||
return {'FINISHED'}
|
||||
Reference in New Issue
Block a user