add explode model

- Add method that allows for exploding the model into pieces for kit bashing or painting in substance painter.
This commit is contained in:
989onan
2025-04-05 17:54:39 -04:00
parent cf2a5a22cc
commit 6bafc7d7ac
3 changed files with 102 additions and 2 deletions
+94 -1
View File
@@ -1,7 +1,7 @@
import bpy
import numpy as np
from bpy.types import Operator, Context
from typing import Set
from typing import Set, Literal
from ...core.translations import t
from ...core.logging_setup import logger
from ...core.common import get_active_armature, get_all_meshes
@@ -99,3 +99,96 @@ class AvatarToolkit_OT_SelectShortestSeamPath(Operator):
return {'FINISHED'}
class AvatarToolkit_OT_ExplodeMesh(Operator):
"""Explodes the mesh for use with painting programs, or painting inside blender."""
bl_idname = "avatar_toolkit.explode_mesh"
bl_label = t("Tools.explode_mesh")
bl_description = t("Tools.explode_mesh_desc")
bl_options = {'REGISTER', 'UNDO'}
distance: bpy.props.FloatProperty(default=2.0,name=t("Tools.explode_mesh.distance"),description=t("Tools.explode_mesh.distance_desc"))
split_on_seams: bpy.props.BoolProperty(default=True,name=t("Tools.explode_mesh.split_on_seams"),description=t("Tools.explode_mesh.split_on_seams_desc"))
def draw(self, context: Context) -> None:
"""Draw the operator's UI"""
layout = self.layout
layout.prop(self, "distance")
def invoke(self, context: Context, event: bpy.types.Event) -> set[str]:
"""Initialize the operator"""
return context.window_manager.invoke_props_dialog(self)
@classmethod
def poll(cls, context: Context) -> bool:
return context.view_layer.objects.active.type == "MESH" and len(context.view_layer.objects.selected) == 1
def execute(self, context: Context) -> Set[str]:
mesh_obj: bpy.types.Object = context.view_layer.objects.active.type
mesh: bpy.types.Mesh = context.view_layer.objects.active.data
if(self.split_on_seams):
#set to correct mode
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_mode(type='EDGE')
#mark seams by islands
bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.uv.select_all(action="SELECT")
bpy.ops.uv.seams_from_islands(mark_seams=True,mark_sharp=False)
#clear selection
bpy.ops.mesh.select_all(action="DESELECT")
bpy.ops.object.mode_set(mode='OBJECT')
bm = bmesh.new() # create an empty BMesh
bm.from_mesh(mesh) # fill it in from active mesh
#select seam edges
for idx,edge in enumerate(bm.edges):
edge.select = edge.seam
bm.to_mesh(mesh)
bm.free()
#split edges.
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.edge_split()
#separate by loose.
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_mode(type='FACE')
bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.mesh.separate(type='LOOSE')
distance: float = self.distance
#set origins to geometry
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY",center="BOUNDS")
#store original settings
origin_only_orig: bool = context.scene.tool_settings.use_transform_data_origin
pos_only_orig: bool = context.scene.tool_settings.use_transform_pivot_point_align
parents_only_orig: bool = context.scene.tool_settings.use_transform_skip_children
original_pivot: Literal['BOUNDING_BOX_CENTER', 'CURSOR', 'INDIVIDUAL_ORIGINS', 'MEDIAN_POINT', 'ACTIVE_ELEMENT'] = context.scene.tool_settings.transform_pivot_point
#set scene settings correctly.
context.scene.tool_settings.use_transform_data_origin = False
context.scene.tool_settings.use_transform_pivot_point_align = True
context.scene.tool_settings.use_transform_skip_children = False
context.scene.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
#spread out separated objects
bpy.ops.transform.resize(value=(self.distance, self.distance, self.distance), orient_type='GLOBAL')
#restore settings.
context.scene.tool_settings.use_transform_data_origin = origin_only_orig
context.scene.tool_settings.use_transform_pivot_point_align = pos_only_orig
context.scene.tool_settings.use_transform_skip_children = parents_only_orig
context.scene.tool_settings.transform_pivot_point = original_pivot
return {'FINISHED'}
+6
View File
@@ -215,6 +215,12 @@
"Tools.clean_weights_threshold_desc": "Minimum weight value to consider a bone as weighted",
"Tools.find_shortest_seam_path": "Find Shortest Seam Path",
"Tools.find_shortest_seam_path_desc": "Find shortest path of seams between two selected vertices connected to seams.",
"Tools.explode_mesh":"Explode Mesh for Painting",
"Tools.explode_mesh_desc": "Explodes the mesh for use with painting programs, or painting inside blender.",
"Tools.explode_mesh.distance": "Distance",
"Tools.explode_mesh.distance_desc": "Scale factor for distance between exploded items on model.",
"Tools.explode_mesh.split_on_seams_desc":"Split model on UV seams to separate islands from each other.",
"Tools.explode_mesh.split_on_seams":"Split on Seams",
"Tools.apply_modifier_on_shapekey_obj":"Apply Modifier on Shapekey Object",
"Tools.apply_modifier_on_shapekey_obj_desc":"Applies a modifier on an object regardless of it having shapekeys.",
"Tools.merge_title": "Merge Tools",
+2 -1
View File
@@ -18,7 +18,7 @@ from ..functions.tools.bone_tools import (
from ..functions.tools.standardize_armature import AvatarToolkit_OT_StandardizeArmature
from ..functions.tools.merge_tools import AvatarToolkit_OT_MergeToActive, AvatarToolkit_OT_MergeToParent, AvatarToolkit_OT_ConnectBones
from ..functions.tools.rigify_converter import AvatarToolkit_OT_ConvertRigifyToUnity
from ..functions.tools.general_mesh_tools import AvatarToolkit_OT_SelectShortestSeamPath
from ..functions.tools.general_mesh_tools import AvatarToolkit_OT_SelectShortestSeamPath, AvatarToolkit_OT_ExplodeMesh
from ..functions.custom_tools.force_apply_modifier import AvatarToolkit_OT_ApplyModifierForShapkeyObj
class AvatarToolKit_PT_ToolsPanel(Panel):
@@ -68,6 +68,7 @@ class AvatarToolKit_PT_ToolsPanel(Panel):
col.separator(factor=0.5)
col.operator(AvatarToolkit_OT_SelectShortestSeamPath.bl_idname,text=t("Tools.find_shortest_seam_path"),icon="MESH_DATA")
col.operator(AvatarToolkit_OT_ApplyModifierForShapkeyObj.bl_idname,text=t("Tools.apply_modifier_on_shapekey_obj"),icon="SHAPEKEY_DATA")
col.operator(AvatarToolkit_OT_ExplodeMesh.bl_idname,text=t("Tools.explode_mesh"),icon="MOD_EXPLODE")
# Standardization Tools