-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7abb3b2
Showing
14 changed files
with
213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
__pycache__ | ||
.vscode | ||
|
||
*.blend1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# What's this? | ||
A blender addon used for removing empty vertex groups and bones with no corresponding vertex groups. | ||
|
||
# Install | ||
* Download from [github release](https://github.com/aoirusann/BlenderAddon_RemoveUnuseVertexGroupsAndBones/tags) | ||
* Then just install the `.zip` file in the same way as other blender addons | ||
|
||
# Usage | ||
|
||
## Remove unused vertex groups | ||
1. Select some mesh objects whose vertex groups will be checked. | ||
2. F3 - Remove Unused VeretxGroups | ||
3. The empty vertex groups on the mesh objects should have been removed. | ||
|
||
For example (in `demo.blend`): | ||
|
||
* Vertex Group `Using` is being used | ||
*  | ||
* Vertex Group `Unused` is empty | ||
*  | ||
* Select `Cylinder` | ||
* Press `F3` | ||
* Type `Remove Unused VeretxGroups` | ||
*  | ||
* Vertex Group `Unused` was removed | ||
*  | ||
* Log can be found in `Window - Toggle System Console` | ||
*  | ||
|
||
|
||
## Remove unused bones | ||
1. Select some mesh objects whose vertex groups will be checked. | ||
2. Ctrl+Click select an armature object whose bone will be removed. (Make sure the armature is the active object) | ||
3. F3 - Remove Unused Bones | ||
4. The bones on the armature object which are not referred by the vertex groups of mesh objects should have been removed. | ||
|
||
For example (in `demo.blend`): | ||
|
||
* Bone `Using` has the corresponding vertex group `Using` in the `Cylinder` | ||
*  | ||
* Bone `UnusedBone` has no corresponding vertex group in the `Cylinder` | ||
*  | ||
* Select `Cylinder` | ||
* CTRL+Click Select `Armature` | ||
* Press `F3` | ||
* Type `Remove Unused Bone` | ||
*  | ||
* Bone `UnusedBone` was removed | ||
*  | ||
* Log can be found in `Window - Toggle System Console` | ||
*  |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
# Test on Blender 3.6.1 | ||
|
||
bl_info = { | ||
"name": "Remove unused Vertex Groups and Bones", | ||
"author": "Aoirusann", | ||
"version": (1, 0), | ||
"blender": (3, 6, 1), | ||
"location": "In search (Edit > Operator Search) type 'Remove Unused'", | ||
"description": "Remove the unused vertex groups from mesh objects and remove the unused bones from armature objects.", | ||
"doc_url": "https://github.com/aoirusann/BlenderAddon_RemoveUnuseVertexGroupsAndBones", | ||
"category": "Model", | ||
} | ||
|
||
import bpy | ||
import copy | ||
|
||
# 1. Select some mesh objects whose vertex groups will be checked. | ||
# 2. F3 - Remove Unused VeretxGroups | ||
# 3. The vertex groups with empty weights on the mesh objects should be removed. | ||
class RemoveUnusedVertexGroups(bpy.types.Operator): | ||
"""Remove Unused VertexGroups""" | ||
bl_idname = "armature.remove_unused_vertexgroups" | ||
bl_label = "Remove Unused Vertex Groups" | ||
bl_options = {'REGISTER', 'UNDO'} | ||
|
||
## From https://gist.github.com/OptoCloud/55f1c61f4deea619bf5b39cb21781f6a | ||
|
||
|
||
## Remove empty vertex groups and shape keys | ||
## Credits: | ||
## https://blender.stackexchange.com/questions/237599/is-there-a-way-to-automatically-delete-shape-keys-that-are-identical-to-the-basi | ||
## https://blender.stackexchange.com/questions/16517/how-to-quickly-remove-all-zero-weight-vertex-groups | ||
|
||
def execute(self, context): | ||
if bpy.context.mode != 'OBJECT': | ||
self.report({"ERROR"}, "Must be in object mode!") | ||
return {"CANCELLED"} | ||
|
||
def survey(obj): | ||
'''Collect the highest vertex weight in each vertex group''' | ||
maxWeight = {} | ||
for i in obj.vertex_groups: | ||
maxWeight[i.index] = 0 | ||
|
||
for v in obj.data.vertices: | ||
for g in v.groups: | ||
gn = g.group | ||
w = obj.vertex_groups[g.group].weight(v.index) | ||
if (maxWeight.get(gn) is None or w>maxWeight[gn]): | ||
maxWeight[gn] = w | ||
return maxWeight | ||
|
||
for obj in bpy.context.selected_objects: | ||
if obj.type != 'MESH': | ||
self.report({"WARNING"}, f"{obj.name} is not a mesh, so nothing will happen on it.") | ||
continue | ||
self.report({"INFO"}, f"Processing {obj.name}...") | ||
|
||
maxWeight = survey(obj) | ||
ka = [] | ||
ka.extend(maxWeight.keys()) | ||
ka.sort(key=lambda gn: -gn) # Inverse it, so remove can works well. | ||
for gn in ka: | ||
if maxWeight[gn]<=0: | ||
self.report({"INFO"}, f"Remove {obj.vertex_groups[gn].name}") | ||
obj.vertex_groups.remove(obj.vertex_groups[gn]) # actually remove the group | ||
self.report({"INFO"}, f"Processed {obj.name}.") | ||
|
||
self.report({"INFO"}, f"All mesh objects are checked.") | ||
return {'FINISHED'} | ||
|
||
|
||
|
||
# 1. Select some mesh objects whose vertex groups will be checked. | ||
# 2. Ctrl+Click select an armature object whose bone will be removed. | ||
# 3. F3 - Remove Unused Bones | ||
# 4. The bones which are not referred by the mesh objects on the armature object should be removed. | ||
class RemoveUnusedBones(bpy.types.Operator): | ||
"""Remove Unused Bones""" | ||
bl_idname = "armature.remove_unused_bones" | ||
bl_label = "Remove Unused Bones" | ||
bl_options = {'REGISTER', 'UNDO'} | ||
|
||
def _dps(self, bone: bpy.types.Bone, vertex_groups: set[str], visited: set[str], armature: bpy.types.Object) -> bool: | ||
if bone.name in visited: | ||
return bone.name in armature.data.edit_bones.keys() | ||
visited.add(bone.name) | ||
|
||
# If any of the child is kept, this bone should be kept too | ||
should_kept = False | ||
children = copy.copy(bone.children) | ||
for child in children: | ||
should_kept |= self._dps(child, vertex_groups, visited, armature) | ||
if should_kept: | ||
return True | ||
|
||
# Otherwise check if this bone is being used | ||
if bone.name in vertex_groups: | ||
return True | ||
|
||
# Remove this unused bone | ||
self.report({"INFO"}, "Remove " + bone.name) | ||
armature.data.edit_bones.remove(bone) | ||
return False | ||
|
||
def execute(self, context): | ||
# UserInput Check | ||
# The last object should be an armature, and the others should be meshes | ||
if len(bpy.context.selectable_objects) == 0: | ||
self.report({"ERROR"}, "Nothing is selected. Please select some meshes and an armature (which should be the last one).") | ||
return {"CANCELLED"} | ||
obj_armature: bpy.types.Object = bpy.context.active_object | ||
if obj_armature.type != "ARMATURE": | ||
self.report({"ERROR"}, "The last object must be an armature whose bones are to be removed.") | ||
return {"CANCELLED"} | ||
objs_mesh: list[bpy.types.Object] = [] | ||
for obj in bpy.context.selected_objects: | ||
if obj.type == "MESH": | ||
objs_mesh.append(obj) | ||
|
||
# Debug output | ||
self.report({"INFO"}, "Armature: " + obj_armature.name) | ||
self.report({"INFO"}, "Meshs: " + ",".join([obj_mesh.name for obj_mesh in objs_mesh])) | ||
|
||
# Enter edit mode | ||
obj_mode = obj_armature.mode # memo the current mode | ||
bpy.ops.object.mode_set(mode="EDIT") | ||
|
||
# Collect all the vertex groups being used | ||
vertex_groups: set[str] = set() | ||
for obj_mesh in objs_mesh: | ||
vertex_groups = vertex_groups.union(obj_mesh.vertex_groups.keys()) | ||
|
||
# Debug output | ||
self.report({"INFO"}, "Vertex Groups: " + ",".join(vertex_groups)) | ||
|
||
# Depth-first search on the armature | ||
visited: set[str] = set() | ||
for bone in obj_armature.data.edit_bones: | ||
self._dps(bone, vertex_groups, visited, obj_armature) | ||
|
||
# Exit edit mode | ||
bpy.ops.object.mode_set(mode=obj_mode) | ||
|
||
return {'FINISHED'} | ||
|
||
def register(): | ||
bpy.utils.register_class(RemoveUnusedBones) | ||
bpy.utils.register_class(RemoveUnusedVertexGroups) | ||
|
||
|
||
def unregister(): | ||
bpy.utils.unregister_class(RemoveUnusedBones) | ||
bpy.utils.unregister_class(RemoveUnusedVertexGroups) | ||
|
||
|
||
if __name__ == "__main__": | ||
register() |
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.