Small config tweaks, removed cam/light check, code cleanup
This commit is contained in:
1 parent
4617c47ad0
commit
b800e5a68b
6 files changed
+148
-43
No files matched your search
+15
@@ -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
@@ -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)
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
Reference in new issue
Block a user