work: save startup because I'm sick of the arnoldesque materials

This commit is contained in:
2026-04-24 14:39:38 -06:00
parent b401eb4bcf
commit 2f8e5f472f
57 changed files with 2257 additions and 1643 deletions
@@ -58,7 +58,7 @@
"id": "atomic_data_manager",
"name": "Atomic Data Manager",
"tagline": "Smart cleanup and inspection of Blender data-blocks",
"version": "2.6.2",
"version": "2.6.3",
"type": "add-on",
"maintainer": "RaincloudTheDragon",
"license": [
@@ -70,9 +70,9 @@
"management",
"cleanup"
],
"archive_url": "https://github.com/RaincloudTheDragon/atomic-data-manager/releases/download/v2.6.2/Atomic_Data_Manager.v2.6.2.zip",
"archive_size": 121191,
"archive_hash": "sha256:1f4af882cdf73d3bb0b8cf1badc094b179bf9e982486ee516c45a6a2d478c05d"
"archive_url": "https://github.com/RaincloudTheDragon/atomic-data-manager/releases/download/v2.6.3/Atomic_Data_Manager.v2.6.3.zip",
"archive_size": 122700,
"archive_hash": "sha256:b444463a0443864077abcfe97332406e606a697d3567ac32f85843760da11372"
},
{
"schema_version": "1.0.0",
@@ -0,0 +1,100 @@
{
"version": "v1",
"blocklist": [],
"data": [
{
"schema_version": "1.0.0",
"id": "basedplayblast",
"name": "BasedPlayblast",
"tagline": "Easily create playblasts from Blender and Flamenco",
"version": "2.6.3",
"type": "add-on",
"maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"website": "https://github.com/RaincloudTheDragon/BasedPlayblast",
"permissions": {
"files": "Import/export files and data"
},
"tags": [
"Animation",
"Render",
"Workflow",
"Video"
],
"archive_url": "https://github.com/RaincloudTheDragon/BasedPlayblast/releases/download/v2.6.3/BasedPlayblast.v2.6.3.zip",
"archive_size": 49732,
"archive_hash": "sha256:078b406105ce6f4802e75233569841e2f73d082e09cd1d954696681ebf72b627"
},
{
"schema_version": "1.0.0",
"id": "rainclouds_bulk_scene_tools",
"name": "Raincloud's Bulk Scene Tools",
"tagline": "Bulk utilities for optimizing scene data",
"version": "0.17.0",
"type": "add-on",
"maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"website": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools",
"permissions": {
"files": "Read and write external resources referenced by scenes"
},
"tags": [
"Scene",
"Workflow",
"Materials"
],
"archive_url": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools/releases/download/v0.17.0/Rainys_Bulk_Scene_Tools.v0.17.0.zip",
"archive_size": 80981,
"archive_hash": "sha256:419433069465b45ea903bd7bb46d89aa28b9c96c541d587d5f3be651a762811f"
},
{
"schema_version": "1.0.0",
"id": "atomic_data_manager",
"name": "Atomic Data Manager",
"tagline": "Smart cleanup and inspection of Blender data-blocks",
"version": "2.6.3",
"type": "add-on",
"maintainer": "RaincloudTheDragon",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"tags": [
"utility",
"management",
"cleanup"
],
"archive_url": "https://github.com/RaincloudTheDragon/atomic-data-manager/releases/download/v2.6.3/Atomic_Data_Manager.v2.6.3.zip",
"archive_size": 122700,
"archive_hash": "sha256:b444463a0443864077abcfe97332406e606a697d3567ac32f85843760da11372"
},
{
"schema_version": "1.0.0",
"id": "sheepit_project_submitter",
"name": "SheepIt Project Submitter",
"tagline": "Submit projects to SheepIt render farm",
"version": "0.0.8",
"type": "add-on",
"maintainer": "RaincloudTheDragon",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "3.0.0",
"tags": [
"render",
"farm",
"submission",
"utility"
],
"archive_url": "https://github.com/RaincloudTheDragon/sheepit_project_submitter/releases/download/v0.0.8/SheepIt_Project_Submitter.v0.0.8.zip",
"archive_size": 47667,
"archive_hash": "sha256:93cd8f18456079130c48c66cfd40235f7fe6414f929f59f90670e7a864821110"
}
]
}
@@ -0,0 +1,100 @@
{
"version": "v1",
"blocklist": [],
"data": [
{
"schema_version": "1.0.0",
"id": "basedplayblast",
"name": "BasedPlayblast",
"tagline": "Easily create playblasts from Blender and Flamenco",
"version": "2.6.3",
"type": "add-on",
"maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"website": "https://github.com/RaincloudTheDragon/BasedPlayblast",
"permissions": {
"files": "Import/export files and data"
},
"tags": [
"Animation",
"Render",
"Workflow",
"Video"
],
"archive_url": "https://github.com/RaincloudTheDragon/BasedPlayblast/releases/download/v2.6.3/BasedPlayblast.v2.6.3.zip",
"archive_size": 49732,
"archive_hash": "sha256:078b406105ce6f4802e75233569841e2f73d082e09cd1d954696681ebf72b627"
},
{
"schema_version": "1.0.0",
"id": "rainclouds_bulk_scene_tools",
"name": "Raincloud's Bulk Scene Tools",
"tagline": "Bulk utilities for optimizing scene data",
"version": "0.17.0",
"type": "add-on",
"maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"website": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools",
"permissions": {
"files": "Read and write external resources referenced by scenes"
},
"tags": [
"Scene",
"Workflow",
"Materials"
],
"archive_url": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools/releases/download/v0.17.0/Rainys_Bulk_Scene_Tools.v0.17.0.zip",
"archive_size": 80981,
"archive_hash": "sha256:419433069465b45ea903bd7bb46d89aa28b9c96c541d587d5f3be651a762811f"
},
{
"schema_version": "1.0.0",
"id": "atomic_data_manager",
"name": "Atomic Data Manager",
"tagline": "Smart cleanup and inspection of Blender data-blocks",
"version": "2.6.3",
"type": "add-on",
"maintainer": "RaincloudTheDragon",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"tags": [
"utility",
"management",
"cleanup"
],
"archive_url": "https://github.com/RaincloudTheDragon/atomic-data-manager/releases/download/v2.6.3/Atomic_Data_Manager.v2.6.3.zip",
"archive_size": 122700,
"archive_hash": "sha256:b444463a0443864077abcfe97332406e606a697d3567ac32f85843760da11372"
},
{
"schema_version": "1.0.0",
"id": "sheepit_project_submitter",
"name": "SheepIt Project Submitter",
"tagline": "Submit projects to SheepIt render farm",
"version": "0.0.8",
"type": "add-on",
"maintainer": "RaincloudTheDragon",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "3.0.0",
"tags": [
"render",
"farm",
"submission",
"utility"
],
"archive_url": "https://github.com/RaincloudTheDragon/sheepit_project_submitter/releases/download/v0.0.8/SheepIt_Project_Submitter.v0.0.8.zip",
"archive_size": 47667,
"archive_hash": "sha256:93cd8f18456079130c48c66cfd40235f7fe6414f929f59f90670e7a864821110"
}
]
}
@@ -1,3 +1,9 @@
## [v2.6.3] - 2026-04-22
### Fixes
- **CC3 / iClone import caches**: `Scene['CC3ImportProps']` (e.g. `pbr_material_cache`) no longer blocks unused detection—when `material_all()` is empty, cache-only refs (including Blenders 2× `users` per cell) are ignored for clean and RNA material scans (`stats/ghost_users.py`).
## [v2.6.2] - 2026-04-06
### Fixes
@@ -2,7 +2,7 @@ schema_version = "1.0.0"
id = "atomic_data_manager"
name = "Atomic Data Manager"
version = "2.6.2"
version = "2.6.3"
type = "add-on"
author = "RaincloudTheDragon"
maintainer = "RaincloudTheDragon"
@@ -0,0 +1,88 @@
"""
Detect 'ghost' ID references (e.g. Reallusion CC / iClone import caches on Scene)
that keep bpy.users > 0 but are not real scene usage. Used by materials_deep and
any future cleanup paths.
"""
import bpy
from .. import config
def _idprop_count_material(p, mat, seen):
"""Recursively count pointers equal to `mat` under an ID property value."""
if p is None or id(p) in seen:
return 0
if isinstance(p, (str, int, float, bool)):
return 0
seen = seen | {id(p)}
if isinstance(p, bpy.types.Material) and p == mat:
return 1
n = 0
t = type(p).__name__
if isinstance(p, (list, tuple)):
for x in p:
n += _idprop_count_material(x, mat, seen)
elif isinstance(p, dict):
for v in p.values():
n += _idprop_count_material(v, mat, seen)
elif t == "IDPropertyGroup" or (hasattr(p, "keys") and hasattr(p, "values") and t != "dict"):
try:
for k in p.keys():
try:
v = p[k]
except Exception:
continue
n += _idprop_count_material(v, mat, seen)
except Exception:
pass
return n
def count_cc3_import_cache_references(material):
"""
Count Material pointers stored in Scene ID property group CC3ImportProps
(Character Creator 3 / iClone pipeline). These are import-cache ghosts and
are not object/world/brush use.
"""
m = 0
for scene in bpy.data.scenes:
try:
if "CC3ImportProps" not in scene:
continue
except Exception:
continue
try:
cc3 = scene["CC3ImportProps"]
except Exception:
continue
m += _idprop_count_material(cc3, material, set())
return m
def material_blender_users_fully_cc3_ghosts(material):
"""
True if every material.user can be explained by CC3 import_cache pointers.
Blender often reports *two* users per pbr cache slot: the Scene and the
pbr_material_cache cell both contribute to bpy.types.Material.users, while
our walk only counts Material pointers in the idprop tree (one per cell).
So u == 2 * cc3 is normal; u == cc3 can occur when the count already matches
1:1 in some file versions.
"""
try:
u = material.users
except Exception:
return False
if u == 0:
return True
cc3 = count_cc3_import_cache_references(material)
if cc3 == 0:
return False
if cc3 == u or u == 2 * cc3:
return True
if config.enable_debug_prints:
config.debug_print(
f"[Atomic Debug] ghost_users: material '{material.name}' users={u} "
f"cc3_count={cc3} (not fully explained by CC3; keep conservative block)"
)
return False
@@ -28,6 +28,7 @@ import json
import os
from .. import config
from ..utils import compat
from . import ghost_users
# Data-block types we care about for dependency analysis
@@ -1197,7 +1198,9 @@ def analyze_unused_from_graph(graph, category, include_fake_users=None):
if category == 'materials':
try:
if datablock.users > 0 and not datablock.use_fake_user:
continue
if not ghost_users.material_blender_users_fully_cc3_ghosts(
datablock):
continue
except (AttributeError, RuntimeError, ReferenceError):
pass
unused.append(item_name)
@@ -28,6 +28,7 @@ from .. import config
from ..utils import compat
from ..utils import version
from . import users
from . import ghost_users
def shallow(data):
@@ -223,10 +224,10 @@ def materials_deep():
# check if material has a fake user or if ignore fake users
# is enabled
if not material.use_fake_user or config.include_fake_users:
# If Blender still counts users but we found none, don't flag (name collisions
# with linked IDs, drivers, or refs we don't traverse). Fake-user purge unchanged.
if material.users > 0 and not material.use_fake_user:
continue
# CC3 / iClone import_cache ID props keep bpy.users>0 with no object/world use.
if not ghost_users.material_blender_users_fully_cc3_ghosts(material):
continue
unused.append(material.name)
else:
# Second check: material is used, but check if it's ONLY used by unused objects
@@ -1,6 +1,7 @@
import bpy
from ..stats import unused
from ..stats import users
from . import ghost_users
from .. import config
from ..utils import compat
@@ -128,7 +129,8 @@ def _has_any_unused_materials():
if not users.material_all(material.name):
if not material.use_fake_user or config.include_fake_users:
if material.users > 0 and not material.use_fake_user:
continue
if not ghost_users.material_blender_users_fully_cc3_ghosts(material):
continue
return True
else:
# Second check: material is used, but check if it's ONLY used by unused objects