work: save startup because I'm sick of the arnoldesque materials
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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