Files

119 lines
4.3 KiB
Python

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)