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:
1 parent
71b1a61b5c
commit
4617c47ad0
33 files changed
+2771
-52
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import bpy
|
||||
|
||||
from ...core import Check, Issue, Severity, contexts, is_local
|
||||
|
||||
# Node types that make a tree produce something.
|
||||
_OUTPUT_TYPES = {
|
||||
"OUTPUT_MATERIAL", "OUTPUT_WORLD", "OUTPUT_LIGHT", "OUTPUT_AOV", "OUTPUT_LINESTYLE",
|
||||
"COMPOSITE", "OUTPUT_FILE", "VIEWER",
|
||||
}
|
||||
# Node types never reported as unused.
|
||||
_IGNORED_TYPES = {"FRAME", "GROUP_INPUT"}
|
||||
|
||||
|
||||
def unused_nodes(tree):
|
||||
"""Nodes that do not contribute to any output of ``tree``.
|
||||
|
||||
Returns None when the tree has no output at all (nothing to compare with).
|
||||
"""
|
||||
roots = [
|
||||
node for node in tree.nodes
|
||||
if node.type in _OUTPUT_TYPES
|
||||
or (node.type == "GROUP_OUTPUT" and getattr(node, "is_active_output", True))
|
||||
]
|
||||
if not roots:
|
||||
return None
|
||||
|
||||
upstream = {}
|
||||
for link in tree.links:
|
||||
upstream.setdefault(link.to_node.name, []).append(link.from_node)
|
||||
|
||||
used = set()
|
||||
stack = list(roots)
|
||||
while stack:
|
||||
node = stack.pop()
|
||||
if node.name in used:
|
||||
continue
|
||||
used.add(node.name)
|
||||
stack.extend(upstream.get(node.name, ()))
|
||||
|
||||
return [node for node in tree.nodes if node.name not in used and node.type not in _IGNORED_TYPES]
|
||||
|
||||
|
||||
def _node_trees():
|
||||
"""(owner datablock, node tree) of every local node tree in use."""
|
||||
for material in bpy.data.materials:
|
||||
if is_local(material) and material.users and material.node_tree:
|
||||
yield material, material.node_tree
|
||||
for world in bpy.data.worlds:
|
||||
if is_local(world) and world.users and world.node_tree:
|
||||
yield world, world.node_tree
|
||||
for group in bpy.data.node_groups:
|
||||
if is_local(group) and group.users:
|
||||
yield group, group
|
||||
|
||||
|
||||
def _tree_of(owner):
|
||||
return owner if isinstance(owner, bpy.types.NodeTree) else owner.node_tree
|
||||
|
||||
|
||||
class UnusedNodesCheck(Check):
|
||||
id = "unused_nodes"
|
||||
label = "Unused nodes"
|
||||
description = "Material, world and node group trees contain no node disconnected from the output."
|
||||
category = "Shading"
|
||||
contexts = contexts.ASSET
|
||||
severity = Severity.WARNING
|
||||
fix_description = "Delete the unused nodes"
|
||||
|
||||
def run(self, ctx):
|
||||
for owner, tree in _node_trees():
|
||||
nodes = unused_nodes(tree)
|
||||
if nodes:
|
||||
names = [node.name for node in nodes]
|
||||
shown = ", ".join(names[:5]) + (" ..." if len(names) > 5 else "")
|
||||
yield Issue.for_id(
|
||||
f"{owner.name}: {len(names)} unused node(s): {shown}",
|
||||
owner,
|
||||
data={"nodes": names},
|
||||
)
|
||||
|
||||
def fix(self, ctx, issues):
|
||||
for issue in issues:
|
||||
owner = issue.get_id()
|
||||
if owner is None:
|
||||
continue
|
||||
tree = _tree_of(owner)
|
||||
for name in issue.data["nodes"]:
|
||||
node = tree.nodes.get(name)
|
||||
if node is not None:
|
||||
tree.nodes.remove(node)
|
||||
Reference in new issue
Block a user