2025-07-01
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
bl_info = {
|
||||
"name": "Datablock Tools",
|
||||
"author": "Mackenzie Crawford, Vitor Balbio",
|
||||
"version": (2, 2, 1),
|
||||
"blender": (2, 80, 0),
|
||||
"location": "View3D > Object > Datablock Tools",
|
||||
"description": "Some tools to handle datablocks",
|
||||
"warning": "",
|
||||
"tracker_url": "",
|
||||
"category": "3D View"}
|
||||
|
||||
import bpy
|
||||
import re
|
||||
|
||||
class CleanImagesOP(bpy.types.Operator):
|
||||
#replace the ".0x" images with the original and mark this to remove in next load
|
||||
bl_idname = "object.clean_images"
|
||||
bl_label = "Clean Images Datablocks"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects is not None
|
||||
|
||||
def execute(self, context):
|
||||
ImageList = []
|
||||
#get full image name including extension
|
||||
for i in bpy.data.images:
|
||||
ImageList.append([i.name,i])
|
||||
|
||||
for obj in bpy.context.selected_objects:
|
||||
for uv in obj.data.uv_textures.items():
|
||||
for faceTex in uv[1].data:
|
||||
if (not (faceTex.image is None)):
|
||||
image = faceTex.image
|
||||
|
||||
if (bool(re.search(r'\.(\d){3}', image.name))): #if an image is set, and its name contains a . followed by 3 digits
|
||||
replaced = False
|
||||
for ima_name, ima in ImageList:
|
||||
if (image.name.startswith(ima_name) and (".0" not in ima_name)):
|
||||
faceTex.image.user_clear()
|
||||
faceTex.image = ima
|
||||
replaced = True
|
||||
if (replaced == False):
|
||||
#we didn't find a match, rename this one
|
||||
image.name = image.name.rsplit(".", 1)[0] #splits on the last . in the image name and returns the left part. Allows handling of names with multiple .s
|
||||
return {'FINISHED'}
|
||||
|
||||
class CleanMaterialsOP(bpy.types.Operator):
|
||||
#replace the ".0x" materials with the original and mark this to remove in next load
|
||||
bl_idname = "object.clean_materials"
|
||||
bl_label = "Clean Materials Datablocks"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects is not None
|
||||
|
||||
def execute(self, context):
|
||||
matlist = bpy.data.materials
|
||||
for obj in bpy.context.selected_objects:
|
||||
for mat_slt in obj.material_slots:
|
||||
if (mat_slt.material != None and bool(re.search(r'\.(\d){3}', mat_slt.material.name))): #if a material is set, and its name contains a . followed by 3 digits
|
||||
replaced = False
|
||||
for mat in matlist:
|
||||
if ((mat.name == mat_slt.material.name.rsplit(".", 1)[0]) and (not bool(re.search(r'\.(\d){3}', mat.name)))):
|
||||
mat_slt.material = mat
|
||||
replaced = True
|
||||
if replaced == False:
|
||||
#we didn't find a match, rename this one
|
||||
mat_slt.material.name = mat_slt.material.name.rsplit(".", 1)[0] #splits on the last . in the material name and returns the left part. Allows handling of names with multiple .s
|
||||
return {'FINISHED'}
|
||||
|
||||
class RemoveAllMaterialsOP(bpy.types.Operator):
|
||||
#removes all materials from selected objects
|
||||
bl_idname = "object.remove_materials"
|
||||
bl_label = "Remove All Materials Datablocks"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects is not None
|
||||
|
||||
def execute(self, context):
|
||||
for obj in bpy.context.selected_objects:
|
||||
#iterate through materials list and remove each sequentially
|
||||
obj.active_material_index = 0
|
||||
for i in range(len(obj.material_slots)):
|
||||
bpy.ops.object.material_slot_remove({'object': obj})
|
||||
return {'FINISHED'}
|
||||
|
||||
class DatablockToolsMenu(bpy.types.Menu):
|
||||
bl_label = "Datablock Tools"
|
||||
bl_idname = "VIEW_MT_datablock_tools"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("object.clean_materials")
|
||||
layout.operator("object.remove_materials")
|
||||
layout.operator("object.clean_images")
|
||||
layout.operator("object.set_instance")
|
||||
|
||||
def draw_item(self, context):
|
||||
layout = self.layout
|
||||
layout.menu(DatablockToolsMenu.bl_idname)
|
||||
|
||||
classes = (CleanMaterialsOP, RemoveAllMaterialsOP, CleanImagesOP, DatablockToolsMenu)
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
bpy.types.VIEW3D_MT_object.append(draw_item)
|
||||
|
||||
def unregister():
|
||||
from bpy.utils import unregister_class
|
||||
for cls in reverse(classes):
|
||||
unregister_class(cls)
|
||||
|
||||
bpy.types.VIEW3D_MT_object.remove(draw_item)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
@@ -0,0 +1,57 @@
|
||||
# Datablock-Tools-2
|
||||
Updated version of Datablock Tools for Blender https://blenderartists.org/forum/showthread.php?320593-Datablock-Tools
|
||||
|
||||
Datablock Tools provides an easy way to remove duplicated material or UV image datablocks ("image.png.001", etc), typically created when copy-pasting objects. This reduces memory footprint and also makes editing materials a lot easier.
|
||||
|
||||
## Usage
|
||||
All datablock tools can be found under Object -> Datablock Tools.
|
||||
|
||||
* **Clean Images Datablocks:** Removes duplicated UV images from selected objects. If an original image is found ("image.png" instead of "image.png.001"), all duplicate instances will be replaced with the original. Otherwise, one instance is renamed to removed the .xxx suffix
|
||||
|
||||
* **Clean Materials Datablocks:** Removes duplicated materials from selected objects. If an original material is found ("material.png" instead of "material.png.001"), all duplicate instances will be replaced with the original. Otherwise, one instance is renamed to removed the .xxx suffix
|
||||
|
||||
* **Remove All Materials Datablocks:** Removes all materials from selected objects.
|
||||
|
||||
* **Set As Instance:** Converts all *selected* objects into instances of the *active* object. Any change to the geometry or materials of a single instance will affect all instances of the object.
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2.2.1
|
||||
|
||||
* Remove "set as instance" tool. Blender includes an equivalent tool built-in anyway, no point duplicating code
|
||||
|
||||
### 2.2
|
||||
|
||||
* Update for Blender 2.80 API. Still compatible with 2.7x
|
||||
|
||||
### 2.1
|
||||
|
||||
* Fixed fatal bug with image datablock clean on objects with incomplete unwrapping
|
||||
|
||||
* If image datablock has no "original" version (without .xxx suffix), rename it to become that
|
||||
|
||||
* Image datablock cleaner now uses regex search, like the material cleaner
|
||||
|
||||
* Support image/material names with multiple periods, preserves image extensions
|
||||
|
||||
* Code styling and commenting improvements for future maintainability
|
||||
|
||||
* Text and layout tweaks
|
||||
|
||||
* Tool to remove all materials from an object
|
||||
|
||||
### 2.0
|
||||
|
||||
* First version by Mackenzie Crawford, taking over as de facto developer of apparently orphaned project
|
||||
|
||||
* Update for Blender 2.78 onwards
|
||||
|
||||
* Switch to regex-based datablock name comparisons
|
||||
|
||||
* In absence of a "virgin" material (not image yet) datablock, rename one of the existing duplicates instead of leaving all duplicates unchanged
|
||||
|
||||
* Restructure program and translate comments to English for better maintainability in the future
|
||||
|
||||
### 1.0, 1.1
|
||||
|
||||
* Original release by Vitor Balbio. See BlenderArtists thread [here](https://blenderartists.org/t/datablock-tool/595316) and deprecated Blender Wiki page [here](https://archive.blender.org/wiki/index.php/Extensions:2.6/Py/Scripts/3D_interaction/Datablock_Tools/)
|
||||
Reference in New Issue
Block a user