update amznchartools
This commit is contained in:
2026-03-18 18:03:21 -06:00
parent eea7cc1969
commit 9fcddeca02
26 changed files with 461 additions and 110 deletions
@@ -1,4 +1,5 @@
"""Operator definitions for AMZN Character Tools."""
import importlib
from pathlib import Path
import runpy
import traceback
@@ -8,6 +9,7 @@ from bpy.types import Operator
OPS_DIR = Path(__file__).parent.parent / "ops"
BASE_PACKAGE = (__package__ or "").rsplit(".", 1)[0]
def run_script(script_name: str) -> None:
@@ -25,6 +27,15 @@ def run_script(script_name: str) -> None:
runpy.run_path(str(script_path), run_name="__main__")
def _import_ops_module(module_name: str):
"""Import and reload an ops module from this addon package."""
if not BASE_PACKAGE:
raise RuntimeError("Cannot resolve addon base package for module imports")
full_name = f"{BASE_PACKAGE}.ops.{module_name}"
module = importlib.import_module(full_name)
return importlib.reload(module)
# Icon mapping from old indices to icon names
ICON_MAP = {
144: "PREFERENCES", # Settings/configuration operations
@@ -86,6 +97,15 @@ OP_SPECS = [
"icon": "FILE_REFRESH",
"panel": "devices",
},
{
"name": "RemoveDevicesSettings",
"id": "remove_devices_settings",
"desc": "Removes the 'Devices' custom property from SettingsBone",
"script": "RemoveDevicesSettings.py",
"button": "Remove Devices Settings",
"icon": "CANCEL",
"panel": "devices",
},
{
"name": "GeoSeparator",
"id": "geo_separator",
@@ -122,6 +142,15 @@ OP_SPECS = [
"icon": "PREFERENCES",
"panel": "geo",
},
{
"name": "AddVestAmbassadorColor",
"id": "add_vest_ambassador_color",
"desc": "Adds blue (ambassador) vest color option to active object's vest color node group",
"script": "vest_ambassador_color.py",
"button": "Add Vest Ambassador Color",
"icon": "MATERIAL",
"panel": "vest",
},
{
"name": "HHSpawn",
"id": "hh_spawn",
@@ -192,7 +221,65 @@ def _make_operator(spec: dict) -> type[Operator]:
"""Create an operator class from a specification dictionary."""
def _execute(self, context):
try:
run_script(spec["script"])
# Special handling for operators that need result capture
if spec["script"] == "replace_cel_with_bsdf.py":
script_path = OPS_DIR / spec["script"]
if script_path.exists():
module = _import_ops_module("replace_cel_with_bsdf")
materials_mapped, users_remapped = module.replace_cel_materials()
self.report({"INFO"}, f"Replaced CEL: {materials_mapped} materials, {users_remapped} users remapped")
else:
run_script(spec["script"])
self.report({"INFO"}, f"{spec['button']} complete")
elif spec["script"] == "vest_ambassador_color.py":
script_path = OPS_DIR / spec["script"]
if script_path.exists():
module = _import_ops_module("vest_ambassador_color")
result = module.add_vest_ambassador_color()
success = bool(result.get("success")) if isinstance(result, dict) else bool(result)
reason = result.get("reason") if isinstance(result, dict) else ""
if success:
self.report({"INFO"}, f"{spec['button']} complete")
else:
if reason == "NO_NODE_GROUP":
self.report({"INFO"}, "No vest color node group found on active object")
elif reason == "NO_ACTIVE_OBJECT":
self.report({"INFO"}, "No active object selected")
elif reason == "NO_MENU_SWITCH":
self.report({"INFO"}, "No vest menu switch node found in vest color node group")
else:
self.report({"WARNING"}, "Vest-blue material linked but menu item was not added")
else:
run_script(spec["script"])
self.report({"INFO"}, f"{spec['button']} complete")
elif spec["script"] == "BodyMasker.py":
script_path = OPS_DIR / spec["script"]
if script_path.exists():
module = _import_ops_module("BodyMasker")
result = module.add_body_masks()
if isinstance(result, dict):
if result.get("success"):
self.report({"INFO"}, f"{spec['button']} complete")
else:
reason = result.get("reason")
if reason == "NO_BODY":
self.report({"ERROR"}, "CC_Base_Body not found in scene")
else:
self.report({"WARNING"}, f"{spec['button']} incomplete: {reason}")
else:
# Unexpected return type; fall back to running the script for side-effects
run_script(spec["script"])
self.report({"INFO"}, f"{spec['button']} complete")
elif spec.get("id") == "remove_devices_settings":
# Pre-check: ensure CC_Base_Body exists in the scene. If not, inform the user.
if "CC_Base_Body" not in bpy.data.objects:
self.report({"ERROR"}, "CC_Base_Body not found in scene")
return {"CANCELLED"}
run_script(spec["script"])
self.report({"INFO"}, f"{spec['button']} complete")
else:
run_script(spec["script"])
self.report({"INFO"}, f"{spec['button']} complete")
except Exception as exc: # pragma: no cover - best effort logging
traceback.print_exc()
self.report({"ERROR"}, f"{spec['button']} failed: {exc}")