work: save startup because I'm sick of the arnoldesque materials
This commit is contained in:
@@ -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 Blender’s 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
|
||||
|
||||
Reference in New Issue
Block a user