Added sanity check baseline made by claude.

Also changed the location to a panel in VIEW_3D instead of menu.
This commit is contained in:
Nero Frame 13 committed 2026-10-13 16:33:03 +02:00
1 parent 71b1a61b5c
commit 4617c47ad0
33 files changed
+2771 -52

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
import math
import bpy
from ... import config
from ...core import Check, Issue, contexts
class ModifiersCheck(Check):
id = "modifiers"
label = "Modifiers"
description = "No modifiers other than Subdivision Surface and Multiresolution."
category = "Objects"
contexts = contexts.ASSET - {contexts.CHAR_LINEART}
fix_description = "Apply the modifiers that are not allowed"
# Config key holding the allowed modifier types; subclasses change it.
allowed_config_key = "ALLOWED_MODIFIERS"
def run(self, ctx):
allowed = getattr(config, self.allowed_config_key)
for ob in ctx.objects():
names = [m.name for m in ob.modifiers if m.type not in allowed]
if names:
yield Issue.for_id(
f"{ob.name}: {', '.join(names)}",
ob,
data={"modifiers": names},
)
def fix(self, ctx, issues):
failed = []
for issue in issues:
ob = issue.get_id()
if ob is None:
continue
for name in issue.data["modifiers"]:
try:
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()}")
return failed
class ApplyTransformCheck(Check):
id = "apply_transform"
label = "Transforms applied"
description = "Rotation and scale (and location if set in config) of geometry objects are applied."
category = "Objects"
contexts = contexts.ASSET
fix_description = "Apply the transforms (multi-user data is reported, not changed)"
def run(self, ctx):
tolerance = config.TRANSFORM_TOLERANCE
channels = config.APPLY_TRANSFORM
for ob in ctx.objects(*config.APPLY_TRANSFORM_TYPES):
loc, rot, scale = ob.matrix_basis.decompose()
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:
problems.append("rotation")
if channels.get("scale") and any(abs(s - 1.0) > tolerance for s in scale):
problems.append("scale")
if problems:
yield Issue.for_id(f"{ob.name}: {', '.join(problems)} not applied", ob)
def fix(self, ctx, issues):
failed = []
channels = config.APPLY_TRANSFORM
for issue in issues:
ob = issue.get_id()
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")
continue
try:
with bpy.context.temp_override(
object=ob, active_object=ob, selected_objects=[ob], selected_editable_objects=[ob]
):
bpy.ops.object.transform_apply(
location=bool(channels.get("location")),
rotation=bool(channels.get("rotation")),
scale=bool(channels.get("scale")),
)
except RuntimeError as exc:
failed.append(f"{ob.name}: {str(exc).strip()}")
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"
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)