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:
@@ -1,7 +1,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from bpy.types import Operator, Context
|
from bpy.types import Operator, Context
|
||||||
from typing import Set
|
from typing import Set, Literal
|
||||||
from ...core.translations import t
|
from ...core.translations import t
|
||||||
from ...core.logging_setup import logger
|
from ...core.logging_setup import logger
|
||||||
from ...core.common import get_active_armature, get_all_meshes
|
from ...core.common import get_active_armature, get_all_meshes
|
||||||
@@ -99,3 +99,96 @@ class AvatarToolkit_OT_SelectShortestSeamPath(Operator):
|
|||||||
|
|
||||||
return {'FINISHED'}
|
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'}
|
||||||
@@ -215,6 +215,12 @@
|
|||||||
"Tools.clean_weights_threshold_desc": "Minimum weight value to consider a bone as weighted",
|
"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": "Find Shortest Seam Path",
|
||||||
"Tools.find_shortest_seam_path_desc": "Find shortest path of seams between two selected vertices connected to seams.",
|
"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":"Apply Modifier on Shapekey Object",
|
||||||
"Tools.apply_modifier_on_shapekey_obj_desc":"Applies a modifier on an object regardless of it having shapekeys.",
|
"Tools.apply_modifier_on_shapekey_obj_desc":"Applies a modifier on an object regardless of it having shapekeys.",
|
||||||
"Tools.merge_title": "Merge Tools",
|
"Tools.merge_title": "Merge Tools",
|
||||||
|
|||||||
+2
-1
@@ -18,7 +18,7 @@ from ..functions.tools.bone_tools import (
|
|||||||
from ..functions.tools.standardize_armature import AvatarToolkit_OT_StandardizeArmature
|
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.merge_tools import AvatarToolkit_OT_MergeToActive, AvatarToolkit_OT_MergeToParent, AvatarToolkit_OT_ConnectBones
|
||||||
from ..functions.tools.rigify_converter import AvatarToolkit_OT_ConvertRigifyToUnity
|
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
|
from ..functions.custom_tools.force_apply_modifier import AvatarToolkit_OT_ApplyModifierForShapkeyObj
|
||||||
|
|
||||||
class AvatarToolKit_PT_ToolsPanel(Panel):
|
class AvatarToolKit_PT_ToolsPanel(Panel):
|
||||||
@@ -68,6 +68,7 @@ class AvatarToolKit_PT_ToolsPanel(Panel):
|
|||||||
col.separator(factor=0.5)
|
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_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_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
|
# Standardization Tools
|
||||||
|
|||||||
Reference in New Issue
Block a user