Small config tweaks, removed cam/light check, code cleanup

This commit is contained in:
Nero Frame 13 committed 2026-10-14 13:49:18 +02:00
1 parent 4617c47ad0
commit b800e5a68b
6 files changed
+148 -43

No files matched your search

+15
View File
@@ -40,19 +40,34 @@ def _load_config_override():
# Register modules # Register modules
def register() -> None: def register() -> None:
# Register tool operators
tools_ops.register() tools_ops.register()
# Load all the sanity checks into the sanity REGISTRY
checks.load_all() checks.load_all()
# Register the UI for all panels
ui.register() ui.register()
# Load the configuration for sanity checks
_load_config_override() _load_config_override()
# Load it natively
bpy.app.handlers.load_post.append(_on_load_post) bpy.app.handlers.load_post.append(_on_load_post)
# Unregister modules # Unregister modules
def unregister() -> None: def unregister() -> None:
# Unregister tool operators
tools_ops.unregister() tools_ops.unregister()
# Destroy the post loader
if _on_load_post in bpy.app.handlers.load_post: if _on_load_post in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.remove(_on_load_post) bpy.app.handlers.load_post.remove(_on_load_post)
# Unregister all panels
ui.unregister() ui.unregister()
runner.clear() runner.clear()
# The check registry is plain Python and is rebuilt with the modules on # The check registry is plain Python and is rebuilt with the modules on
# re-import, so it is kept as is (enable/disable without re-import). # re-import, so it is kept as is (enable/disable without re-import).
+29 -19
View File
@@ -35,7 +35,9 @@ class ModifiersCheck(Check):
continue continue
for name in issue.data["modifiers"]: for name in issue.data["modifiers"]:
try: try:
with bpy.context.temp_override(object=ob, active_object=ob, selected_objects=[ob]): with bpy.context.temp_override(
object=ob, active_object=ob, selected_objects=[ob]
):
bpy.ops.object.modifier_apply(modifier=name) bpy.ops.object.modifier_apply(modifier=name)
except RuntimeError as exc: except RuntimeError as exc:
failed.append(f"{ob.name} / {name}: {str(exc).strip()}") failed.append(f"{ob.name} / {name}: {str(exc).strip()}")
@@ -58,7 +60,10 @@ class ApplyTransformCheck(Check):
problems = [] problems = []
if channels.get("location") and loc.length > tolerance: if channels.get("location") and loc.length > tolerance:
problems.append("location") problems.append("location")
if channels.get("rotation") and min(rot.angle, math.tau - rot.angle) > tolerance: if (
channels.get("rotation")
and min(rot.angle, math.tau - rot.angle) > tolerance
):
problems.append("rotation") problems.append("rotation")
if channels.get("scale") and any(abs(s - 1.0) > tolerance for s in scale): if channels.get("scale") and any(abs(s - 1.0) > tolerance for s in scale):
problems.append("scale") problems.append("scale")
@@ -73,11 +78,16 @@ class ApplyTransformCheck(Check):
if ob is None: if ob is None:
continue continue
if ob.data is not None and ob.data.users > 1: if ob.data is not None and ob.data.users > 1:
failed.append(f"{ob.name}: data shared by {ob.data.users} users, apply by hand") failed.append(
f"{ob.name}: data shared by {ob.data.users} users, apply by hand"
)
continue continue
try: try:
with bpy.context.temp_override( with bpy.context.temp_override(
object=ob, active_object=ob, selected_objects=[ob], selected_editable_objects=[ob] object=ob,
active_object=ob,
selected_objects=[ob],
selected_editable_objects=[ob],
): ):
bpy.ops.object.transform_apply( bpy.ops.object.transform_apply(
location=bool(channels.get("location")), location=bool(channels.get("location")),
@@ -89,20 +99,20 @@ class ApplyTransformCheck(Check):
return failed return failed
class LightCameraCheck(Check): # class LightCameraCheck(Check):
id = "lights_cameras" # id = "lights_cameras"
label = "Lights & cameras" # label = "Lights & cameras"
description = "Asset files contain no lights or cameras." # description = "Asset files contain no lights or cameras."
category = "Objects" # category = "Objects"
contexts = contexts.ASSET # contexts = contexts.ASSET
fix_description = "Delete the lights and cameras" # fix_description = "Delete the lights and cameras"
def run(self, ctx): # def run(self, ctx):
for ob in ctx.objects(*config.FORBIDDEN_ASSET_OBJECT_TYPES): # for ob in ctx.objects(*config.FORBIDDEN_ASSET_OBJECT_TYPES):
yield Issue.for_id(f"{ob.type.capitalize()} '{ob.name}' must be removed", ob) # yield Issue.for_id(f"{ob.type.capitalize()} '{ob.name}' must be removed", ob)
def fix(self, ctx, issues): # def fix(self, ctx, issues):
for issue in issues: # for issue in issues:
ob = issue.get_id() # ob = issue.get_id()
if ob is not None: # if ob is not None:
bpy.data.objects.remove(ob, do_unlink=True) # bpy.data.objects.remove(ob, do_unlink=True)
+80 -14
View File
@@ -82,16 +82,48 @@ MESH_DATA_MATCHES_OBJECT = True
# Blender default names that must never survive in a published file. # Blender default names that must never survive in a published file.
DEFAULT_NAMES = { DEFAULT_NAMES = {
"cube", "plane", "sphere", "uvsphere", "icosphere", "cylinder", "cone", "cube",
"torus", "circle", "grid", "suzanne", "monkey", "empty", "camera", "plane",
"light", "lamp", "point", "sun", "spot", "area", "material", "collection", "sphere",
"beziercurve", "beziercircle", "nurbscurve", "nurbspath", "text", "uvsphere",
"armature", "lattice", "nodegroup", "geometrynodes", "stroke", "gpencil", "icosphere",
"lineart", "scene", "world", "object", "mesh", "cylinder",
"cone",
"torus",
"circle",
"grid",
"suzanne",
"monkey",
"empty",
"camera",
"light",
"lamp",
"point",
"sun",
"spot",
"area",
"material",
"collection",
"beziercurve",
"beziercircle",
"nurbscurve",
"nurbspath",
"text",
"armature",
"lattice",
"nodegroup",
"geometrynodes",
"stroke",
"gpencil",
"lineart",
"scene",
"world",
"object",
"mesh",
} }
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Structure (placeholder, real hierarchy to be defined) # Structure
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Collections that must exist. "{asset}" is replaced by the asset name. # Collections that must exist. "{asset}" is replaced by the asset name.
REQUIRED_COLLECTIONS = [] REQUIRED_COLLECTIONS = []
@@ -111,7 +143,10 @@ UV_NEGATIVE_TOLERANCE = 1e-6
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
ALLOWED_MODIFIERS = {"SUBSURF", "MULTIRES"} ALLOWED_MODIFIERS = {"SUBSURF", "MULTIRES"}
ALLOWED_MODIFIERS_LINEART = { ALLOWED_MODIFIERS_LINEART = {
"SUBSURF", "MULTIRES", "LINEART", "GREASE_PENCIL_THICKNESS", "SUBSURF",
"MULTIRES",
"LINEART",
"GREASE_PENCIL_THICKNESS",
} }
# Which transform channels must be applied. # Which transform channels must be applied.
@@ -135,8 +170,19 @@ CHAR_BODY_NAME = "GEO_{asset}_Body"
# Name keywords that identify inner mouth / eye parts (case-insensitive). # Name keywords that identify inner mouth / eye parts (case-insensitive).
CHAR_PART_KEYWORDS = [ CHAR_PART_KEYWORDS = [
"eye", "iris", "pupil", "cornea", "sclera", "lid", "lash", "eye",
"mouth", "teeth", "tooth", "tongue", "gum", "palate", "iris",
"pupil",
"cornea",
"sclera",
"lid",
"lash",
"mouth",
"teeth",
"tooth",
"tongue",
"gum",
"palate",
] ]
# Vertex groups the body mesh must have. TODO: real list. # Vertex groups the body mesh must have. TODO: real list.
@@ -152,14 +198,32 @@ SET_COLLECTION_PATTERN = r"^(?:SET|ENV|set|env)[_-]"
VERSION_PATTERN = r"(?<![A-Za-z0-9])v(\d{3,4})(?![0-9])" VERSION_PATTERN = r"(?<![A-Za-z0-9])v(\d{3,4})(?![0-9])"
# Datablock types scanned for ".001" duplicated names. # Datablock types scanned for ".001" duplicated names.
DUPLICATE_NAME_TYPES = ["objects", "collections", "cameras", "materials", "actions", "node_groups"] DUPLICATE_NAME_TYPES = [
"objects",
"collections",
"cameras",
"materials",
"actions",
"node_groups",
]
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# UI # UI
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
CATEGORY_ORDER = [ CATEGORY_ORDER = [
"Scene", "File", "Naming", "Structure", "Geometry", "UVs", "Objects", "Scene",
"Shading", "Animation", "Character", "Lineart", "Shot", "Sound", "File",
"Naming",
"Structure",
"Geometry",
"UVs",
"Objects",
"Shading",
"Animation",
"Character",
"Lineart",
"Shot",
"Sound",
] ]
MAX_ISSUES_DRAWN = 30 MAX_ISSUES_DRAWN = 30
@@ -175,7 +239,9 @@ def _snapshot():
if _DEFAULTS is None: if _DEFAULTS is None:
module = sys.modules[__name__] module = sys.modules[__name__]
_DEFAULTS = { _DEFAULTS = {
k: copy.deepcopy(v) for k, v in vars(module).items() if k.isupper() and not k.startswith("_") k: copy.deepcopy(v)
for k, v in vars(module).items()
if k.isupper() and not k.startswith("_")
} }
return _DEFAULTS return _DEFAULTS
+14 -2
View File
@@ -10,6 +10,7 @@ GENERAL = "GENERAL"
CHAR_MOD = "CHAR_MOD" CHAR_MOD = "CHAR_MOD"
CHAR_LINEART = "CHAR_LINEART" CHAR_LINEART = "CHAR_LINEART"
LAYOUT = "LAYOUT" LAYOUT = "LAYOUT"
ANIMATION = "ANIMATION"
# Every asset context gets the general asset checks. # Every asset context gets the general asset checks.
ASSET = frozenset({GENERAL, CHAR_MOD, CHAR_LINEART}) ASSET = frozenset({GENERAL, CHAR_MOD, CHAR_LINEART})
@@ -26,10 +27,21 @@ class Context:
CONTEXTS = [ CONTEXTS = [
Context(GENERAL, "Asset: General", "Props, sets and generic assets (modeling and lineart)", "MESH_CUBE"), Context(
GENERAL,
"Asset: General",
"Props, sets and generic assets (modeling and lineart)",
"MESH_CUBE",
),
Context(CHAR_MOD, "Character: Modeling", "Character modeling file", "USER"), Context(CHAR_MOD, "Character: Modeling", "Character modeling file", "USER"),
Context(CHAR_LINEART, "Character: Lineart / Shading", "Character lineart and shading file", "OUTLINER_OB_GREASEPENCIL"), Context(
CHAR_LINEART,
"Character: Lineart / Shading",
"Character lineart and shading file",
"OUTLINER_OB_GREASEPENCIL",
),
Context(LAYOUT, "Shot: Layout", "Shot layout file", "SEQUENCE"), Context(LAYOUT, "Shot: Layout", "Shot layout file", "SEQUENCE"),
Context(ANIMATION, "Shot: Animation", "Shot animation file", "SEQUENCE"),
] ]
+8 -6
View File
@@ -1,10 +1,11 @@
"""Operators.""" """Operators for the individual tools."""
import bpy
import json import json
class WTDGetOidForPath(bpy.types.Operator): import bpy
class WTDGetOidForPath(bpy.types.Operator):
bl_idname = "wtd_tools.getoidforpath" bl_idname = "wtd_tools.getoidforpath"
bl_label = "Get Oid For Open Scene" bl_label = "Get Oid For Open Scene"
@@ -14,10 +15,10 @@ class WTDGetOidForPath(bpy.types.Operator):
actor_name="Flow", actor_name="Flow",
cmd_name="getoidforpath", cmd_name="getoidforpath",
args_json=json.dumps([]), args_json=json.dumps([]),
kwargs_json=json.dumps({'path': bpy.data.filepath.replace('\\', '/')}), kwargs_json=json.dumps({"path": bpy.data.filepath.replace("\\", "/")}),
) == {"FINISHED"}: ) == {"FINISHED"}:
result = json.loads(bpy.context.window_manager.kabaret_ipc_last_result) result = json.loads(bpy.context.window_manager.kabaret_ipc_last_result)
self.report({'INFO'}, str(result)) self.report({"INFO"}, str(result))
else: else:
error = bpy.context.window_manager.kabaret_ipc_last_error error = bpy.context.window_manager.kabaret_ipc_last_error
return {"FINISHED"} return {"FINISHED"}
@@ -26,5 +27,6 @@ class WTDGetOidForPath(bpy.types.Operator):
def register() -> None: def register() -> None:
bpy.utils.register_class(WTDGetOidForPath) bpy.utils.register_class(WTDGetOidForPath)
def unregister() -> None: def unregister() -> None:
bpy.utils.unregister_class(WTDGetOidForPath) bpy.utils.unregister_class(WTDGetOidForPath)
+2 -2
View File
@@ -32,7 +32,7 @@ def _wrapped_label(layout, context, text, icon="NONE", margin=0):
col.label(text=line, icon=icon if index == 0 else "BLANK1") col.label(text=line, icon=icon if index == 0 else "BLANK1")
def draw_panel(self, context): def draw_sanity_checks_panel(self, context):
layout = self.layout layout = self.layout
scene = context.scene scene = context.scene
settings = scene.wtd_sanity settings = scene.wtd_sanity
@@ -170,7 +170,7 @@ class _SanityPanel:
bl_options = {"DEFAULT_CLOSED"} # panel starts collapsed bl_options = {"DEFAULT_CLOSED"} # panel starts collapsed
def draw(self, context): def draw(self, context):
draw_panel(self, context) draw_sanity_checks_panel(self, context)
class _ToolsPanel: class _ToolsPanel: