Files
blender-portable-repo/scripts/addons/SimpleBake/presets_local.py
T
2026-07-16 12:41:53 -06:00

306 lines
9.8 KiB
Python

import bpy
from bpy.types import Operator
from bpy.utils import register_class, unregister_class
from bpy.props import StringProperty
from .presets import basic_props, scene_props
from .messages import print_message
from . import utils
from .ui.objects_list import refresh_mat_bake_list
import json
#TODO - Loading order problem
def deep_setattr(obj, attr, value):
"""Recursively set attributes on an object, including sub-objects."""
pre, _, post = attr.rpartition('.')
return setattr(deep_getattr(obj, pre) if pre else obj, post, value)
def deep_getattr(obj, attr):
"""Recursively get attributes from an object, including sub-objects."""
for part in attr.split('.'):
obj = getattr(obj, part)
return obj
def sb_prop_group_to_dict(context):
"""
Puts all SimpleBake current settings into a dict
"""
sbp = context.scene.SimpleBake_Props
d = {}
for p in basic_props:
try:
val = getattr(sbp, p)
except UnicodeDecodeError as e:
print(f"[SimpleBake] UnicodeDecodeError on prop '{p}': {e}")
if isinstance(val, bpy.types.bpy_prop_array):
d[p] = list(val)
else:
d[p] = val
#Now for the objects
d["objects_list"] = [o.name for o in sbp.objects_list]
d["mat_bake_list"] = [(e.obj_name, e.mat_name, e.enabled) for e in sbp.mat_bake_list]
d["pbr_target_obj"] = sbp.targetobj.name if sbp.targetobj != None else None
d["cycles_target_obj"] = sbp.targetobj_cycles.name if sbp.targetobj_cycles != None else None
#Cage object if there is one
if context.scene.render.bake.cage_object != None:
d["cage_object"] = context.scene.render.bake.cage_object.name
else:
d["cage_object"] = None
#Now the channel pack images
cp_images_dict = {}
for cpt in sbp.cp_list:
thiscpt_dict = {}
thiscpt_dict["R"] = cpt.R
thiscpt_dict["G"] = cpt.G
thiscpt_dict["B"] = cpt.B
thiscpt_dict["A"] = cpt.A
thiscpt_dict["file_format"] = cpt.file_format
thiscpt_dict["exr_codec"] = cpt.exr_codec
thiscpt_dict["png_compression"] = cpt.png_compression
cp_images_dict[cpt.name] = thiscpt_dict
if len(cp_images_dict)>0:
d["channel_packed_images"] = cp_images_dict
#UV map list
d["uvs"] = [(i.name, i.uv_name) for i in sbp.uv_items]
return d
def existing_preset_to_dict(context, friendly_name):
"""
Pulls out an existing saved local preset into a dict
"""
sbp = context.scene.SimpleBake_Props
name = f"SB_local_preset_{friendly_name}"
s = sbp[name]
d = json.loads(s)
return d
def save_preset(context, friendly_name):
"""
Saves the current SimpleBake settings to a local preset of name friendly_name
"""
sbp = context.scene.SimpleBake_Props
sb = sb_prop_group_to_dict(context)
cs = {}
for p in scene_props:
cs[p] = deep_getattr(context.scene, p)
sbp[f"SB_local_preset_{friendly_name}"] = json.dumps(sb | cs)
def _apply_preset_dict(context, d):
"""Apply a settings dict to the current scene. d is modified in-place."""
sbp = context.scene.SimpleBake_Props
utils.suppress_for_preset_load = True
sbp.objects_list.clear()
#First the object list
for obj_name in d["objects_list"]:
if obj_name in context.scene.objects:
#Find where name attribute of each object in the advanced selection list matches the name
l = [o.name for o in sbp.objects_list if o.name == obj_name]
if len(l) == 0:
#Not already in the list
i = sbp.objects_list.add()
i.name = obj_name #Advanced object list has a name and pointers arritbute
i.obj_point = context.scene.objects[obj_name]
if d["pbr_target_obj"] != None and d["pbr_target_obj"] in context.scene.objects:
sbp.targetobj = context.scene.objects[d["pbr_target_obj"]]
if d["cycles_target_obj"] != None and d["cycles_target_obj"] in context.scene.objects:
sbp.targetobj_cycles = context.scene.objects[d["cycles_target_obj"]]
if d["cage_object"] != None and d["cage_object"] in context.scene.objects:
context.scene.render.bake.cage_object = context.scene.objects[d["cage_object"]]
#Mat bake list — refresh first to populate all materials, then apply saved enabled states
refresh_mat_bake_list(context)
if "mat_bake_list" in d:
saved_states = {(obj_name, mat_name): enabled for obj_name, mat_name, enabled in d["mat_bake_list"]}
for entry in sbp.mat_bake_list:
key = (entry.obj_name, entry.mat_name)
if key in saved_states:
entry.enabled = saved_states[key]
del d["objects_list"]
d.pop("mat_bake_list", None)
#Now channel pack
if "channel_packed_images" in d:
channel_packed_images = d["channel_packed_images"]
if len(channel_packed_images) > 0:
sbp.cp_list.clear()
for imgname in channel_packed_images:
thiscpt_dict = channel_packed_images[imgname]
#Create the list item
li = sbp.cp_list.add()
li.name = imgname
#Set the list item properies
li.R = thiscpt_dict["R"]
li.G = thiscpt_dict["G"]
li.B = thiscpt_dict["B"]
li.A = thiscpt_dict["A"]
li.file_format = thiscpt_dict["file_format"]
li.exr_codec = thiscpt_dict["exr_codec"]
if "png_compression" in thiscpt_dict:
li.png_compression = thiscpt_dict["png_compression"]
else:
li.png_compression = 15
#UV maps list
bpy.ops.simplebake.sync_uv_list()
if "uvs" in d:
for n, uvn in d["uvs"]:
if (i:=sbp.uv_items.find(n)) != -1:
sbp.uv_items[i].uv_name = uvn
try:
for k in d:
if k not in scene_props:
try:
setattr(sbp, k, d[k])
except:
pass
else:
try:
deep_setattr(context.scene, k, d[k])
except:
pass
finally:
utils.suppress_for_preset_load = False
def load_preset(context, friendly_name):
_apply_preset_dict(context, existing_preset_to_dict(context, friendly_name))
class SimpleBake_OT_local_preset_save(Operator):
"""Save current SimpleBake settings to local preset"""
bl_idname = "simplebake.local_preset_save"
bl_label = "Save local preset"
@classmethod
def poll(cls,context):
sbp = context.scene.SimpleBake_Props
return sbp.local_preset_name != ""
def execute(self, context):
sbp = context.scene.SimpleBake_Props
save_preset(context, sbp.local_preset_name)
bpy.ops.simplebake.local_preset_refresh()
self.report({"INFO"}, f"Local Preset {sbp.local_preset_name} saved")
return {'FINISHED'}
class SimpleBake_OT_local_preset_save_override(Operator):
"""Save current SimpleBake settings to local preset overriding the UI for the name"""
bl_idname = "simplebake.local_preset_save_override"
bl_label = "Save local preset"
name_override: StringProperty()
def execute(self, context):
sbp = context.scene.SimpleBake_Props
save_preset(context, self.name_override)
return {'FINISHED'}
class SimpleBake_OT_local_preset_load(Operator):
"""Load selected SimpleBake preset"""
bl_idname = "simplebake.local_preset_load"
bl_label = "Load local preset"
@classmethod
def poll(cls,context):
sbp = context.scene.SimpleBake_Props
try:
sbp.local_presets_list[sbp.local_presets_list_index].name
return True
except:
return False
def execute(self, context):
sbp = context.scene.SimpleBake_Props
load_preset(context, friendly_name=sbp.local_preset_name)
self.report({"INFO"}, f"Local Preset {sbp.local_preset_name} loaded")
return {'FINISHED'}
class SimpleBake_OT_local_preset_load_override(Operator):
"""Load selected SimpleBake preset overriding the UI for the name"""
bl_idname = "simplebake.local_preset_load_override"
bl_label = "Load local preset"
name_override: StringProperty()
def execute(self, context):
load_preset(context, friendly_name=self.name_override)
return {'FINISHED'}
class SimpleBake_OT_Paste_Settings_Clipboard(Operator):
"""Paste SimpleBake settings from clipboard JSON (copied via Copy Settings to Clipboard)"""
bl_idname = "simplebake.paste_settings_clipboard"
bl_label = "Paste Settings from Clipboard"
def execute(self, context):
raw = context.window_manager.clipboard
if not raw or not raw.strip():
self.report({"ERROR"}, "Clipboard is empty")
return {'CANCELLED'}
try:
d = json.loads(raw)
except json.JSONDecodeError as e:
self.report({"ERROR"}, f"Clipboard does not contain valid JSON: {e}")
return {'CANCELLED'}
required = {"objects_list", "pbr_target_obj", "cycles_target_obj", "cage_object"}
if not required.issubset(d.keys()):
self.report({"ERROR"}, "Clipboard JSON does not look like SimpleBake settings")
return {'CANCELLED'}
_apply_preset_dict(context, d)
self.report({"INFO"}, "SimpleBake settings pasted from clipboard")
return {'FINISHED'}
classes = ([
SimpleBake_OT_local_preset_load,
SimpleBake_OT_local_preset_save,
SimpleBake_OT_local_preset_save_override,
SimpleBake_OT_local_preset_load_override,
SimpleBake_OT_Paste_Settings_Clipboard,
])
def register():
global classes
for cls in classes:
register_class(cls)
def unregister():
global classes
for cls in classes:
unregister_class(cls)