2025-12-01
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import importlib
|
||||
from ..lookdev import prefs
|
||||
from ..lookdev import props
|
||||
from ..lookdev import opsdata
|
||||
from ..lookdev import ops
|
||||
from ..lookdev import ui
|
||||
|
||||
|
||||
# ---------REGISTER ----------.
|
||||
|
||||
|
||||
def reload():
|
||||
global prefs
|
||||
global props
|
||||
global opsdata
|
||||
global ops
|
||||
global ui
|
||||
|
||||
prefs = importlib.reload(prefs)
|
||||
props = importlib.reload(props)
|
||||
opsdata = importlib.reload(opsdata)
|
||||
ops = importlib.reload(ops)
|
||||
ui = importlib.reload(ui)
|
||||
|
||||
|
||||
def register():
|
||||
prefs.register()
|
||||
props.register()
|
||||
ops.register()
|
||||
ui.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
ops.unregister()
|
||||
ui.unregister()
|
||||
props.unregister()
|
||||
prefs.unregister()
|
||||
@@ -0,0 +1,106 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import importlib.util
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set, Optional, Tuple, Any
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
from ..logger import LoggerFactory
|
||||
from .. import prefs, util
|
||||
from . import opsdata
|
||||
|
||||
logger = LoggerFactory.getLogger()
|
||||
|
||||
|
||||
class KITSU_OT_lookdev_set_preset(bpy.types.Operator):
|
||||
bl_idname = "kitsu.lookdev_set_preset"
|
||||
bl_label = "Render Preset"
|
||||
bl_property = "files"
|
||||
bl_description = "Sets active render settings preset that can be applied"
|
||||
|
||||
files: bpy.props.EnumProperty(items=opsdata.get_rd_settings_enum_list, name="Files")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
return addon_prefs.lookdev.is_presets_dir_valid
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
file = self.files
|
||||
|
||||
if not file:
|
||||
return {"CANCELLED"}
|
||||
|
||||
if context.scene.lookdev.preset_file == file:
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Update global scene cache version prop.
|
||||
context.scene.lookdev.preset_file = file
|
||||
logger.info("Set render preset file to %s", file)
|
||||
|
||||
# Redraw ui.
|
||||
util.ui_redraw()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> Set[str]:
|
||||
context.window_manager.invoke_search_popup(self) # type: ignore
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class KITSU_OT_lookdev_apply_preset(bpy.types.Operator):
|
||||
bl_idname = "kitsu.lookdev_apply_preset"
|
||||
bl_label = "Apply Preset"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Applies active render settings preset"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
return bool(context.scene.lookdev.preset_file)
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
preset_file = context.scene.lookdev.preset_file
|
||||
preset_path = Path(preset_file).absolute()
|
||||
|
||||
if not preset_file:
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Load module.
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
preset_path.name, preset_path.as_posix()
|
||||
)
|
||||
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# Exec module main function.
|
||||
if "main" not in dir(module):
|
||||
self.report(
|
||||
{"ERROR"}, f"{preset_path.name} does not contain a 'main' function"
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
module.main()
|
||||
self.report({"INFO"}, f"Applied: {preset_path.name}")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
# ---------REGISTER ----------.
|
||||
|
||||
classes = [KITSU_OT_lookdev_set_preset, KITSU_OT_lookdev_apply_preset]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
@@ -0,0 +1,109 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import importlib
|
||||
from typing import Any, Dict, List, Tuple, Union
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
from ..models import FileListModel
|
||||
from ..logger import LoggerFactory
|
||||
from .. import prefs
|
||||
|
||||
logger = LoggerFactory.getLogger()
|
||||
|
||||
RD_PRESET_FILE_MODEL = FileListModel()
|
||||
_rd_preset_enum_list: List[Tuple[str, str, str]] = []
|
||||
_rd_preset_file_model_init: bool = False
|
||||
# we need a second data dict because we want the enum properties data value to be the filepath
|
||||
# but the ui (not only in enum dropdown mode) should display the label defined in the .py
|
||||
# file with 'bl_label'. This dict is basically a mapping from filepath > label
|
||||
|
||||
_rd_preset_data_dict: Dict[str, str] = {}
|
||||
|
||||
|
||||
def init_rd_preset_file_model(
|
||||
context: bpy.types.Context,
|
||||
) -> None:
|
||||
global RD_PRESET_FILE_MODEL
|
||||
global _rd_preset_file_model_init
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
|
||||
# Is None if invalid.
|
||||
if not addon_prefs.lookdev.is_presets_dir_valid:
|
||||
logger.error(
|
||||
"Failed to initialize render settings file model. Invalid path. Check addon preferences"
|
||||
)
|
||||
return
|
||||
|
||||
rd_settings_dir = addon_prefs.lookdev.presets_dir_path
|
||||
|
||||
RD_PRESET_FILE_MODEL.reset()
|
||||
RD_PRESET_FILE_MODEL.root_path = rd_settings_dir
|
||||
valid_items = [file for file in RD_PRESET_FILE_MODEL.items_as_paths if file.suffix == ".py"]
|
||||
if not valid_items:
|
||||
# Update playblast_version prop.
|
||||
context.scene.lookdev.preset_file = ""
|
||||
|
||||
else:
|
||||
# Update playblast_version prop.
|
||||
context.scene.lookdev.preset_file = valid_items[0].as_posix()
|
||||
|
||||
_rd_preset_file_model_init = True
|
||||
|
||||
|
||||
def get_rd_settings_enum_list(
|
||||
self: Any,
|
||||
context: bpy.types.Context,
|
||||
) -> List[Tuple[str, str, str]]:
|
||||
global _rd_preset_enum_list
|
||||
global RD_PRESET_FILE_MODEL
|
||||
global init_rd_preset_file_model
|
||||
global _rd_preset_file_model_init
|
||||
global _rd_preset_data_dict
|
||||
|
||||
# Init model if it did not happen.
|
||||
if not _rd_preset_file_model_init:
|
||||
init_rd_preset_file_model(context)
|
||||
|
||||
# Reload model to update.
|
||||
RD_PRESET_FILE_MODEL.reload()
|
||||
|
||||
# Get all Python files. Ignore hidden files.
|
||||
py_files = [
|
||||
f
|
||||
for f in RD_PRESET_FILE_MODEL.items_as_paths
|
||||
if f.suffix == ".py" and not f.name.startswith('.')
|
||||
]
|
||||
py_labels: List[Tuple[Path, str]] = []
|
||||
|
||||
# Get bl_label of each python file, if not use file name as label.
|
||||
for file in py_files:
|
||||
spec = importlib.util.spec_from_file_location(file.name, file.as_posix())
|
||||
|
||||
# Load module.
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
if "bl_label" not in dir(module):
|
||||
py_labels.append((file, file.name))
|
||||
continue
|
||||
py_labels.append((file, module.bl_label))
|
||||
|
||||
# Generate final enum list and dict from py_labels.
|
||||
enum_list = []
|
||||
data_dict = {}
|
||||
for file, label in py_labels:
|
||||
data_dict[file.name] = label
|
||||
enum_list.append((file.as_posix(), label, ""))
|
||||
|
||||
# Update global variables.
|
||||
_rd_preset_data_dict.clear()
|
||||
_rd_preset_data_dict.update(data_dict)
|
||||
_rd_preset_enum_list.clear()
|
||||
_rd_preset_enum_list.extend(enum_list)
|
||||
|
||||
print(data_dict)
|
||||
return _rd_preset_enum_list
|
||||
@@ -0,0 +1,87 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
from .. import prefs
|
||||
from ..props import get_safely_string_prop
|
||||
import bpy
|
||||
|
||||
from . import opsdata
|
||||
|
||||
|
||||
class LOOKDEV_preferences(bpy.types.PropertyGroup):
|
||||
|
||||
"""
|
||||
Addon preferences for lookdev module.
|
||||
"""
|
||||
|
||||
def update_rd_preset_file_model(self, context: bpy.types.Context) -> None:
|
||||
opsdata.init_rd_preset_file_model(context)
|
||||
|
||||
def set_look_dev_dir(self, input):
|
||||
self['presets_dir'] = input
|
||||
return
|
||||
|
||||
def get_look_dev_dir(self) -> str:
|
||||
proj_root = prefs.project_root_dir_get(bpy.context)
|
||||
if get_safely_string_prop(self, 'presets_dir') == "" and proj_root:
|
||||
frames_dir = proj_root.joinpath("pro/assets/scripts/render_presets/")
|
||||
if frames_dir.exists():
|
||||
return frames_dir.as_posix()
|
||||
return get_safely_string_prop(self, 'presets_dir')
|
||||
|
||||
presets_dir: bpy.props.StringProperty( # type: ignore
|
||||
name="Render Presets Directory",
|
||||
description="Directory path to folder in which render settings python files are stored",
|
||||
default="",
|
||||
subtype="DIR_PATH",
|
||||
update=update_rd_preset_file_model,
|
||||
get=get_look_dev_dir,
|
||||
set=set_look_dev_dir,
|
||||
)
|
||||
|
||||
@property
|
||||
def is_presets_dir_valid(self) -> bool:
|
||||
# Check if file is saved.
|
||||
if not self.presets_dir:
|
||||
return False
|
||||
|
||||
if not bpy.data.filepath and self.presets_dir.startswith("//"):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def presets_dir_path(self) -> Optional[Path]:
|
||||
if not self.presets_dir:
|
||||
return None
|
||||
return Path(os.path.abspath(bpy.path.abspath(self.presets_dir)))
|
||||
|
||||
def draw(
|
||||
self,
|
||||
context: bpy.types.Context,
|
||||
layout: bpy.types.UILayout,
|
||||
) -> None:
|
||||
# Render preset.
|
||||
box = layout.box()
|
||||
box.label(text="Lookdev Tools", icon="RESTRICT_RENDER_OFF")
|
||||
box.row().prop(self, "presets_dir")
|
||||
|
||||
|
||||
# ---------REGISTER ----------.
|
||||
|
||||
classes = [LOOKDEV_preferences]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
# Unregister classes.
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
@@ -0,0 +1,42 @@
|
||||
# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class LOOKDEV_property_group_scene(bpy.types.PropertyGroup):
|
||||
""""""
|
||||
|
||||
# Render settings.
|
||||
preset_file: bpy.props.StringProperty( # type: ignore
|
||||
name="Render Settings File",
|
||||
description="Path to file that is the active render settings preset",
|
||||
default="",
|
||||
subtype="FILE_PATH",
|
||||
)
|
||||
|
||||
|
||||
# ----------------REGISTER--------------.
|
||||
|
||||
classes = [
|
||||
LOOKDEV_property_group_scene,
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
# Scene Properties.
|
||||
bpy.types.Scene.lookdev = bpy.props.PointerProperty(
|
||||
name="Render Preset",
|
||||
type=LOOKDEV_property_group_scene,
|
||||
description="Metadata that is required for lookdev",
|
||||
)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
@@ -0,0 +1,97 @@
|
||||
# SPDX-FileCopyrightText: 2025 Blender Studio Tools Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
from .. import prefs, lookdev, ui, cache
|
||||
from .ops import (
|
||||
KITSU_OT_lookdev_set_preset,
|
||||
KITSU_OT_lookdev_apply_preset,
|
||||
)
|
||||
from . import opsdata
|
||||
|
||||
|
||||
class KITSU_PT_vi3d_lookdev_tools(bpy.types.Panel):
|
||||
"""
|
||||
Panel in 3dview that exposes a set of tools that are useful for general tasks
|
||||
"""
|
||||
|
||||
bl_category = "Kitsu"
|
||||
bl_label = "Lookdev Tools"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_order = 60
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
return bool(
|
||||
cache.task_type_active_get().name
|
||||
in ["Lighting", "Rendering", "Compositing"]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll_error(cls, context: bpy.types.Context) -> bool:
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
return bool(not addon_prefs.lookdev.is_presets_dir_valid)
|
||||
|
||||
def draw_error(self, context: bpy.types.Context) -> None:
|
||||
addon_prefs = prefs.addon_prefs_get(context)
|
||||
layout = self.layout
|
||||
box = ui.draw_error_box(layout)
|
||||
|
||||
if not addon_prefs.lookdev.is_presets_dir_valid:
|
||||
ui.draw_error_invalid_render_preset_dir(box)
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
layout = self.layout
|
||||
|
||||
if self.poll_error(context):
|
||||
self.draw_error(context)
|
||||
|
||||
box = layout.box()
|
||||
box.label(text="Render Settings", icon="RESTRICT_RENDER_OFF")
|
||||
|
||||
# Render settings.
|
||||
row = box.row(align=True)
|
||||
rdpreset_text = "Select Render Preset"
|
||||
if context.scene.lookdev.preset_file:
|
||||
try:
|
||||
rdpreset_text = opsdata._rd_preset_data_dict[
|
||||
Path(context.scene.lookdev.preset_file).name
|
||||
]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
row.operator(
|
||||
KITSU_OT_lookdev_set_preset.bl_idname,
|
||||
text=rdpreset_text,
|
||||
icon="DOWNARROW_HLT",
|
||||
)
|
||||
row.operator(
|
||||
KITSU_OT_lookdev_apply_preset.bl_idname,
|
||||
text="",
|
||||
icon="PLAY",
|
||||
)
|
||||
|
||||
|
||||
class KITSU_PT_comp_lookdev_tools(KITSU_PT_vi3d_lookdev_tools):
|
||||
bl_space_type = "NODE_EDITOR"
|
||||
|
||||
|
||||
# ---------REGISTER ----------
|
||||
# Classes that inherit from another need to be registered first for some reason.
|
||||
classes = [KITSU_PT_comp_lookdev_tools, KITSU_PT_vi3d_lookdev_tools]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
Reference in New Issue
Block a user