Last active
July 22, 2021 21:26
-
-
Save ousttrue/e5ae25958b8ccc1f212538b9fa8d2db4 to your computer and use it in GitHub Desktop.
Blender rig helper addon for Unity humanoid.
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
# coding: utf-8 | |
""" | |
UnityHumanoidHelper | |
reference | |
https://github.com/sugiany/blender_mmd_tools | |
""" | |
import bpy | |
import math | |
class SelectObjects: | |
def __init__(self, active_object, selected_objects=[]): | |
if not isinstance(active_object, bpy.types.Object): | |
raise ValueError | |
try: | |
bpy.ops.object.mode_set(mode='OBJECT') | |
except Exception: | |
pass | |
for i in bpy.context.selected_objects: | |
i.select = False | |
self.__active_object = active_object | |
self.__selected_objects = [active_object]+selected_objects | |
self.__hides = [] | |
for i in self.__selected_objects: | |
self.__hides.append(i.hide) | |
i.hide = False | |
i.select = True | |
bpy.context.scene.objects.active = active_object | |
def __enter__(self): | |
return self.__active_object | |
def __exit__(self, type, value, traceback): | |
for i, j in zip(self.__selected_objects, self.__hides): | |
i.hide = j | |
class EditMode: | |
def __init__(self, obj): | |
if not isinstance(obj, bpy.types.Object): | |
raise ValueError | |
self.__prevMode = obj.mode | |
self.__obj = obj | |
with SelectObjects(obj) as act_obj: | |
if obj.mode != 'EDIT': | |
bpy.ops.object.mode_set(mode='EDIT') | |
def __enter__(self): | |
return self.__obj.data | |
def __exit__(self, type, value, traceback): | |
bpy.ops.object.mode_set(mode=self.__prevMode) | |
class BoneDefine: | |
def __init__(self, name, tail, children=[], head=None): | |
self.name=name | |
self.tail=tail | |
self.head=head | |
self.children=children | |
@property | |
def is_connected(self): | |
return not self.head | |
class CreateUnityHumanoid(bpy.types.Operator): | |
bl_idname = "unityhumanoid.add_armature_unity_humanoid" | |
bl_label = "Add Unity Humanoid" | |
bl_description = "add Armature that has Unity Humanoid Bones" | |
bl_options = {'REGISTER', 'UNDO'} | |
@staticmethod | |
def build_tree(armature, parent, data): | |
bone = armature.edit_bones.new(name=data.name) | |
if parent: | |
bone.head = parent.tail | |
else: | |
bone.head = [0, 0, 0] | |
if data.head: | |
bone.head=[x+y for x, y in zip(bone.head, data.head)] | |
bone.tail = [x+y for x, y in zip(bone.head, data.tail)] | |
if parent: | |
bone.parent=parent | |
bone.use_connect=data.is_connected | |
#print(bone) | |
for child in data.children: | |
CreateUnityHumanoid.build_tree(armature, bone, child) | |
def execute(self, context): | |
arm = bpy.data.armatures.new(name='humanoid') | |
armObj = bpy.data.objects.new(name='ArmatureHumanoid', object_data=arm) | |
bpy.context.scene.objects.link(armObj) | |
armObj.show_x_ray=True | |
#arm.show_names=True | |
hip_height=2.1 | |
with EditMode(armObj) as arm: | |
CreateUnityHumanoid.build_tree(arm, None, | |
BoneDefine('Hips', [0, 0, 0.3], [ | |
BoneDefine('Spine', [0, 0, 0.3], [ | |
BoneDefine('Chest', [0, 0, 0.3], [ | |
BoneDefine('Neck', [0, 0, 0.3], [ | |
BoneDefine('Head', [0, 0, 0.5], [ | |
BoneDefine('Eye.L', [0.1, -0.3, 0]), | |
BoneDefine('Jaw', [0, -0.3, -0.2]) | |
]) | |
]), | |
BoneDefine('Shoulder.L', [0.3, 0, 0], [ | |
BoneDefine('UpperArm.L', [0.5, 0, 0], [ | |
BoneDefine('LowerArm.L', [0.5, 0, 0], [ | |
BoneDefine('Hand.L', [0.1, 0, 0], [ | |
BoneDefine('ThumbProximal.L', [0.03, -0.03, 0], [ | |
BoneDefine('ThumbIntermediate.L', [0.03, -0.03, 0], [ | |
BoneDefine('ThumbDistal.L', [0.03, -0.03, 0]) | |
]) | |
]), | |
BoneDefine('IndexProximal.L', [0.08, -0.03, 0], [ | |
BoneDefine('IndexIntermediate.L', [0.06, 0, 0], [ | |
BoneDefine('IndexDistal.L', [0.06, 0, 0]) | |
]) | |
]), | |
BoneDefine('MiddleProximal.L', [0.08, -0.01, 0], [ | |
BoneDefine('MiddleIntermediate.L', [0.06, 0, 0], [ | |
BoneDefine('MiddleDistal.L', [0.06, 0, 0]) | |
]) | |
]), | |
BoneDefine('RingProximal.L', [0.08, 0.01, 0], [ | |
BoneDefine('RingIntermediate.L', [0.06, 0, 0], [ | |
BoneDefine('RingDistal.L', [0.06, 0, 0]) | |
]) | |
]), | |
BoneDefine('LittleProximal.L', [0.07, 0.03, 0], [ | |
BoneDefine('LittleIntermediate.L', [0.05, 0, 0], [ | |
BoneDefine('LittleDistal.L', [0.05, 0, 0]) | |
]) | |
]), | |
]) | |
]) | |
]) | |
]) | |
]) | |
]), | |
BoneDefine('UpperLeg.L', [0, 0, -1], [ | |
BoneDefine('LowerLeg.L', [0, 0, -1], [ | |
BoneDefine('Foot.L', [0, -0.3, -0.2], [ | |
BoneDefine('Toe.L', [0, -0.1, 0]) | |
]) | |
]) | |
], [0.3, 0, -0.2]) | |
], [0, 0, hip_height]) | |
) | |
bpy.ops.armature.select_all(action='SELECT') | |
bpy.ops.armature.symmetrize() | |
bpy.context.scene.cursor_location = (0.0, 0.0, 0.0) | |
bpy.context.space_data.pivot_point = 'CURSOR' | |
bpy.ops.armature.select_all(action='SELECT') | |
bpy.ops.transform.resize(value=(0.5, 0.5, 0.5)) | |
return {'FINISHED'} | |
class AddXMirrorVertexGroup(bpy.types.Operator): | |
bl_idname = "unityhumanoid.add_xmirror_vertexgroup" | |
bl_label = "Add X-Mirror VertexGroup" | |
bl_description = "add vertex group for mirroring Armature" | |
bl_options = {'REGISTER', 'UNDO'} | |
@staticmethod | |
def createVertexGroupWithEachBone(mesh, armature): | |
print(mesh, armature) | |
for b in armature.bones.keys(): | |
name=b.lower() | |
if name.endswith('.l') or name.endswith('_l') or name.endswith('.r') or name.endswith('_r'): | |
bpy.ops.object.vertex_group_add() | |
bpy.context.active_object.vertex_groups.active.name=b | |
def execute(self, context): | |
o=context.object | |
#print(dir(o)) | |
for m in o.modifiers: | |
print(m) | |
if m.name=="Armature": | |
MirrorVertexGroup.createVertexGroupWithEachBone(o.data, m.object.data) | |
return {'FINISHED'} | |
class FixRotation(bpy.types.Operator): | |
bl_idname = "unityhumanoid.fixrotation" | |
bl_label = "Fix Rotation" | |
bl_description = "Fix rotation to Unity's left handed y-up coordinate" | |
bl_options = {'REGISTER', 'UNDO'} | |
def execute(self, context): | |
o=context.object | |
bpy.ops.transform.rotate(value=-math.pi/2 , axis=(1, 0, 0)) | |
bpy.ops.object.transform_apply(rotation=True) | |
bpy.ops.transform.rotate(value=math.pi/2 , axis=(1, 0, 0)) | |
for m in o.modifiers: | |
if m.name=="Armature": | |
with SelectObjects(m.object): | |
bpy.ops.transform.rotate(value=-math.pi/2 , axis=(1, 0, 0)) | |
bpy.ops.object.transform_apply(rotation=True) | |
bpy.ops.transform.rotate(value=math.pi/2 , axis=(1, 0, 0)) | |
return {'FINISHED'} | |
class UnityHumanoidPanel(bpy.types.Panel): | |
"""Creates a Panel in the Object properties window""" | |
bl_label = "Unity Humanoid" | |
bl_space_type = 'VIEW_3D' | |
bl_region_type = 'TOOLS' | |
def draw(self, context): | |
if context.mode!='OBJECT': | |
return | |
layout = self.layout | |
obj = context.object | |
row = layout.row() | |
row.operator(CreateUnityHumanoid.bl_idname) | |
if bpy.context.selected_objects and context.active_object and context.active_object.type=='MESH': | |
print(context.active_object) | |
row = layout.row() | |
row.operator(AddXMirrorVertexGroup.bl_idname) | |
row = layout.row() | |
row.operator(FixRotation.bl_idname) | |
bl_info = { | |
"name": "UnityHumanoidHelper", | |
"author": "ousttrue", | |
"version": (1, 0), | |
"blender": (2, 77, 0), | |
"location": "", | |
"description": "Helper tools for Unity Humanoid Model", | |
"warning": "", | |
"support": "TESTING", | |
"wiki_url": "", | |
"tracker_url": "", | |
"category": "Object" | |
} | |
def register(): | |
bpy.utils.register_module(__name__) | |
def unregister(): | |
bpy.utils.unregister_module(__name__) | |
if __name__ == "__main__": | |
unregister() | |
register() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment