Add digitgrade legs tool
This commit is contained in:
@@ -57,3 +57,14 @@ def get_armature(context, armature_name=None) -> Optional[Object]:
|
|||||||
if obj.type == "ARMATURE":
|
if obj.type == "ARMATURE":
|
||||||
return obj
|
return obj
|
||||||
return next((obj for obj in context.view_layer.objects if obj.type == 'ARMATURE'), None)
|
return next((obj for obj in context.view_layer.objects if obj.type == 'ARMATURE'), None)
|
||||||
|
|
||||||
|
|
||||||
|
def duplicatebone(b: bpy.types.EditBone) -> bpy.types.EditBone:
|
||||||
|
arm = bpy.context.object.data
|
||||||
|
cb = arm.edit_bones.new(b.name)
|
||||||
|
|
||||||
|
cb.head = b.head
|
||||||
|
cb.tail = b.tail
|
||||||
|
cb.matrix = b.matrix
|
||||||
|
cb.parent = b.parent
|
||||||
|
return cb
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import bpy
|
||||||
|
from ..core import common
|
||||||
|
from ..core import register_wrap
|
||||||
|
from .translations import t
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
@register_wrap
|
||||||
|
class CreateDigitigradeLegs(bpy.types.Operator):
|
||||||
|
bl_idname = "avatar_toolkit.createdigitigradelegs"
|
||||||
|
bl_label = t('Tools.create_digitigrade_legs.label')
|
||||||
|
bl_description = t('Tools.create_digitigrade_legs.desc')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if(context.active_object is None):
|
||||||
|
return False
|
||||||
|
if(context.selected_editable_bones is not None):
|
||||||
|
if(len(context.selected_editable_bones) == 2):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
|
||||||
|
for digi0 in context.selected_editable_bones:
|
||||||
|
digi1: bpy.types.EditBone = None
|
||||||
|
digi2: bpy.types.EditBone = None
|
||||||
|
digi3: bpy.types.EditBone = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
digi1 = digi0.children[0]
|
||||||
|
digi2 = digi1.children[0]
|
||||||
|
digi3 = digi2.children[0]
|
||||||
|
except:
|
||||||
|
print("bone format incorrect! Please select a chain of 4 continious bones!") #TODO: Show this to user. this is an error.
|
||||||
|
return {'CANCELLED'}
|
||||||
|
digi4 = None
|
||||||
|
try:
|
||||||
|
digi4 = digi3.children[0]
|
||||||
|
|
||||||
|
except:
|
||||||
|
print("no toe bone. Continuing.")
|
||||||
|
digi0.select = True
|
||||||
|
digi1.select = True
|
||||||
|
digi2.select = True
|
||||||
|
digi3.select = True
|
||||||
|
if(digi4):
|
||||||
|
digi4.select = True
|
||||||
|
bpy.ops.armature.roll_clear()
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
#creating transform for upper leg
|
||||||
|
digi0.select = True
|
||||||
|
bpy.ops.transform.create_orientation(name="Toolkit_digi0", overwrite=True)
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
|
||||||
|
#duplicate digi0 and assign it to thigh
|
||||||
|
thigh = common.duplicatebone(digi0)
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
#make digi2 parrallel to digi1
|
||||||
|
digi2.align_orientation(digi0)
|
||||||
|
|
||||||
|
#extrude thigh
|
||||||
|
thigh.select_tail = True
|
||||||
|
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude={"forked":False},TRANSFORM_OT_translate=None)
|
||||||
|
#set new bone to calf varible
|
||||||
|
bpy.ops.armature.select_more()
|
||||||
|
calf = context.selected_bones[0]
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
#set calf end to digi2 end
|
||||||
|
calf.tail = digi2.tail
|
||||||
|
|
||||||
|
#make copy of calf, flip it, and then align bone so that it's head is moved to match in align phase
|
||||||
|
flipedcalf = common.duplicatebone(calf)
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
flipedcalf.select = True
|
||||||
|
bpy.ops.armature.switch_direction()
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
flippeddigi1 = common.duplicatebone(digi1)
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
flippeddigi1.select = True
|
||||||
|
bpy.ops.armature.switch_direction()
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#align flipped calf to flipped middle leg to move the head
|
||||||
|
flipedcalf.align_orientation(flippeddigi1)
|
||||||
|
|
||||||
|
flipedcalf.length = flippeddigi1.length
|
||||||
|
|
||||||
|
#assign calf tail to flipped calf head so it moves calf's tail to be out at the perfect parallelagram
|
||||||
|
calf.head = flipedcalf.tail
|
||||||
|
|
||||||
|
#delete helper bones
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
flippeddigi1.select = True
|
||||||
|
bpy.ops.armature.delete()
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
flipedcalf.select = True
|
||||||
|
bpy.ops.armature.delete()
|
||||||
|
bpy.ops.armature.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#reparent the foot to the new calf so it will be part of the new foot IK chain
|
||||||
|
digi3.parent = calf
|
||||||
|
#Tada! It's done! now to rename the old 3 segments that make up the old part to noik so resonite doesn't try to select them
|
||||||
|
|
||||||
|
digi0.name = re.compile(re.escape("<noik>"), re.IGNORECASE).sub("",digi0.name)+"<noik>"
|
||||||
|
digi1.name = re.compile(re.escape("<noik>"), re.IGNORECASE).sub("",digi1.name)+"<noik>"
|
||||||
|
digi2.name = re.compile(re.escape("<noik>"), re.IGNORECASE).sub("",digi2.name)+"<noik>"
|
||||||
|
#finally fully done!
|
||||||
|
return {'FINISHED'}
|
||||||
@@ -44,7 +44,7 @@ def load_translations() -> None:
|
|||||||
print("Default translation file 'en_US.json' not found.")
|
print("Default translation file 'en_US.json' not found.")
|
||||||
|
|
||||||
def t(phrase: str, *args, **kwargs) -> str:
|
def t(phrase: str, *args, **kwargs) -> str:
|
||||||
output: str = dictionary.get(phrase)
|
output: str = dictionary.get(phrase, None)
|
||||||
if output is None:
|
if output is None:
|
||||||
if verbose:
|
if verbose:
|
||||||
print('Warning: Unknown phrase: ' + phrase)
|
print('Warning: Unknown phrase: ' + phrase)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import bpy
|
|||||||
from ..core.register import register_wrap
|
from ..core.register import register_wrap
|
||||||
from .panel import AvatarToolkitPanel
|
from .panel import AvatarToolkitPanel
|
||||||
from bpy.types import Context
|
from bpy.types import Context
|
||||||
|
from ..functions.digitigrade_legs import CreateDigitigradeLegs
|
||||||
|
|
||||||
@register_wrap
|
@register_wrap
|
||||||
class AvatarToolkitToolsPanel(bpy.types.Panel):
|
class AvatarToolkitToolsPanel(bpy.types.Panel):
|
||||||
@@ -20,3 +21,4 @@ class AvatarToolkitToolsPanel(bpy.types.Panel):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.scale_y = 1.5
|
row.scale_y = 1.5
|
||||||
row.operator("avatar_toolkit.convert_to_resonite", text="Translate to Resonite")
|
row.operator("avatar_toolkit.convert_to_resonite", text="Translate to Resonite")
|
||||||
|
row.operator(CreateDigitigradeLegs.bl_idname, text="Create Digitigrade Legs")
|
||||||
Reference in New Issue
Block a user