119 lines
4.3 KiB
Python
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)
|