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
+147 -42

No files matched your search

+15
View File
@@ -40,19 +40,34 @@ def _load_config_override():
# Register modules
def register() -> None:
# Register tool operators
tools_ops.register()
# Load all the sanity checks into the sanity REGISTRY
checks.load_all()
# Register the UI for all panels
ui.register()
# Load the configuration for sanity checks
_load_config_override()
# Load it natively
bpy.app.handlers.load_post.append(_on_load_post)
# Unregister modules
def unregister() -> None:
# Unregister tool operators
tools_ops.unregister()
# Destroy the post loader
if _on_load_post in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.remove(_on_load_post)
# Unregister all panels
ui.unregister()
runner.clear()
# 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).
+29 -19
View File
@@ -35,7 +35,9 @@ class ModifiersCheck(Check):
continue
for name in issue.data["modifiers"]:
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)
except RuntimeError as exc:
failed.append(f"{ob.name} / {name}: {str(exc).strip()}")
@@ -58,7 +60,10 @@ class ApplyTransformCheck(Check):
problems = []
if channels.get("location") and loc.length > tolerance:
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")
if channels.get("scale") and any(abs(s - 1.0) > tolerance for s in scale):
problems.append("scale")
@@ -73,11 +78,16 @@ class ApplyTransformCheck(Check):
if ob is None:
continue
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
try:
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(
location=bool(channels.get("location")),
@@ -89,20 +99,20 @@ class ApplyTransformCheck(Check):
return failed
class LightCameraCheck(Check):
id = "lights_cameras"
label = "Lights & cameras"
description = "Asset files contain no lights or cameras."
category = "Objects"
contexts = contexts.ASSET
fix_description = "Delete the lights and cameras"
# class LightCameraCheck(Check):
# id = "lights_cameras"
# label = "Lights & cameras"
# description = "Asset files contain no lights or cameras."
# category = "Objects"
# contexts = contexts.ASSET
# fix_description = "Delete the lights and cameras"
def run(self, ctx):
for ob in ctx.objects(*config.FORBIDDEN_ASSET_OBJECT_TYPES):
yield Issue.for_id(f"{ob.type.capitalize()} '{ob.name}' must be removed", ob)
# def run(self, ctx):
# for ob in ctx.objects(*config.FORBIDDEN_ASSET_OBJECT_TYPES):
# yield Issue.for_id(f"{ob.type.capitalize()} '{ob.name}' must be removed", ob)
def fix(self, ctx, issues):
for issue in issues:
ob = issue.get_id()
if ob is not None:
bpy.data.objects.remove(ob, do_unlink=True)
# def fix(self, ctx, issues):
# for issue in issues:
# ob = issue.get_id()
# if ob is not None:
# 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.
DEFAULT_NAMES = {
"cube", "plane", "sphere", "uvsphere", "icosphere", "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",
"cube",
"plane",
"sphere",
"uvsphere",
"icosphere",
"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.
REQUIRED_COLLECTIONS = []
@@ -111,7 +143,10 @@ UV_NEGATIVE_TOLERANCE = 1e-6
# --------------------------------------------------------------------------- #
ALLOWED_MODIFIERS = {"SUBSURF", "MULTIRES"}
ALLOWED_MODIFIERS_LINEART = {
"SUBSURF", "MULTIRES", "LINEART", "GREASE_PENCIL_THICKNESS",
"SUBSURF",
"MULTIRES",
"LINEART",
"GREASE_PENCIL_THICKNESS",
}
# 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).
CHAR_PART_KEYWORDS = [
"eye", "iris", "pupil", "cornea", "sclera", "lid", "lash",
"mouth", "teeth", "tooth", "tongue", "gum", "palate",
"eye",
"iris",
"pupil",
"cornea",
"sclera",
"lid",
"lash",
"mouth",
"teeth",
"tooth",
"tongue",
"gum",
"palate",
]
# 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])"
# 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
# --------------------------------------------------------------------------- #
CATEGORY_ORDER = [
"Scene", "File", "Naming", "Structure", "Geometry", "UVs", "Objects",
"Shading", "Animation", "Character", "Lineart", "Shot", "Sound",
"Scene",
"File",
"Naming",
"Structure",
"Geometry",
"UVs",
"Objects",
"Shading",
"Animation",
"Character",
"Lineart",
"Shot",
"Sound",
]
MAX_ISSUES_DRAWN = 30
@@ -175,7 +239,9 @@ def _snapshot():
if _DEFAULTS is None:
module = sys.modules[__name__]
_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
+14 -2
View File
@@ -10,6 +10,7 @@ GENERAL = "GENERAL"
CHAR_MOD = "CHAR_MOD"
CHAR_LINEART = "CHAR_LINEART"
LAYOUT = "LAYOUT"
ANIMATION = "ANIMATION"
# Every asset context gets the general asset checks.
ASSET = frozenset({GENERAL, CHAR_MOD, CHAR_LINEART})
@@ -26,10 +27,21 @@ class Context:
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_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(ANIMATION, "Shot: Animation", "Shot animation file", "SEQUENCE"),
]
+7 -5
View File
@@ -1,10 +1,11 @@
"""Operators."""
"""Operators for the individual tools."""
import bpy
import json
class WTDGetOidForPath(bpy.types.Operator):
import bpy
class WTDGetOidForPath(bpy.types.Operator):
bl_idname = "wtd_tools.getoidforpath"
bl_label = "Get Oid For Open Scene"
@@ -14,10 +15,10 @@ class WTDGetOidForPath(bpy.types.Operator):
actor_name="Flow",
cmd_name="getoidforpath",
args_json=json.dumps([]),
kwargs_json=json.dumps({'path': bpy.data.filepath.replace('\\', '/')}),
kwargs_json=json.dumps({"path": bpy.data.filepath.replace("\\", "/")}),
) == {"FINISHED"}:
result = json.loads(bpy.context.window_manager.kabaret_ipc_last_result)
self.report({'INFO'}, str(result))
self.report({"INFO"}, str(result))
else:
error = bpy.context.window_manager.kabaret_ipc_last_error
return {"FINISHED"}
@@ -26,5 +27,6 @@ class WTDGetOidForPath(bpy.types.Operator):
def register() -> None:
bpy.utils.register_class(WTDGetOidForPath)
def unregister() -> None:
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")
def draw_panel(self, context):
def draw_sanity_checks_panel(self, context):
layout = self.layout
scene = context.scene
settings = scene.wtd_sanity
@@ -170,7 +170,7 @@ class _SanityPanel:
bl_options = {"DEFAULT_CLOSED"} # panel starts collapsed
def draw(self, context):
draw_panel(self, context)
draw_sanity_checks_panel(self, context)
class _ToolsPanel: