Skip to content

Commit

Permalink
Major Fix for Custom Bones
Browse files Browse the repository at this point in the history
  • Loading branch information
Menithal committed Oct 31, 2018
1 parent cdaaa4a commit 0cd5459
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 75 deletions.
64 changes: 58 additions & 6 deletions hifi_tools/armature/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@
import hifi_tools
import webbrowser

from hifi_tools.utils.bones import combine_bones, build_skeleton, retarget_armature, correct_scale_rotation, set_selected_bones_physical, remove_selected_bones_physical
from hifi_tools import default_gateway_server

from hifi_tools.gateway import client as GatewayClient
from hifi_tools.utils.bones import combine_bones, build_skeleton, retarget_armature, correct_scale_rotation, set_selected_bones_physical, remove_selected_bones_physical, bone_connection
from hifi_tools.armature.skeleton import structure as base_armature
from hifi_tools.utils.mmd import convert_mmd_avatar_hifi
from hifi_tools.utils.mixamo import convert_mixamo_avatar_hifi
from hifi_tools.utils.makehuman import convert_makehuman_avatar_hifi
from hifi_tools.utils.materials import make_materials_fullbright, make_materials_shadeless, convert_to_png, convert_images_to_mask, remove_materials_metallic
from hifi_tools.utils.custom import HifiCustomAvatarBinderOperator

from hifi_tools.gateway import client as GatewayClient
from bpy.props import StringProperty

from hifi_tools import default_gateway_server

# TODO: Move somewhere more sensible, this contains alot of other UI stuff not just armature


Expand Down Expand Up @@ -85,6 +85,9 @@ def draw(self, context):
layout.operator(HifiSetBonePhysicalOperator.bl_idname)
layout.operator(HifiRemoveBonePhysicalOperator.bl_idname)
layout.operator(HifiCombineBonesOperator.bl_idname)
layout.operator(HifiCombineBonesNonConnectedOperator.bl_idname)
layout.operator(HifiConnectBones.bl_idname)
layout.operator(HifiUnconnectBones.bl_idname)
return None


Expand Down Expand Up @@ -297,6 +300,54 @@ def execute(self, context):
return {'FINISHED'}


class HifiConnectBones(bpy.types.Operator):
bl_idname = "bones_connect_selected.hifi"
bl_label = "Connect Selected "

bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "High Fidelity"

def execute(self, context):
bone_connection(context.selected_editable_bones, True)
return {'FINISHED'}

class HifiUnconnectBones(bpy.types.Operator):
bl_idname = "bones_deconnect_selected.hifi"
bl_label = "Deconnect Selected "

bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "High Fidelity"

def execute(self, context):
bone_connection(context.selected_editable_bones, False)
return {'FINISHED'}



class HifiCombineBonesNonConnectedOperator(bpy.types.Operator):
bl_idname = "bone_combine_detached.hifi"
bl_label = "Combine Bones Detached"

bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "High Fidelity"

@classmethod
def poll(self, context):
return len(context.selected_bones) > 1

def execute(self, context):

use_mirror_x = bpy.context.object.data.use_mirror_x
bpy.context.object.data.use_mirror_x = False
combine_bones(list(context.selected_bones),
context.active_bone, context.active_object, False)
bpy.context.object.data.use_mirror_x = use_mirror_x
return {'FINISHED'}


class HifiCustomAvatarOperator(bpy.types.Operator):
bl_idname = "armature_toolset_fix_custom_avatar.hifi"
bl_label = "Custom Avatar"
Expand Down Expand Up @@ -449,6 +500,7 @@ def draw(self, context):
row = layout.row()
row.label(self.bl_label)


class HifiForumOperator(bpy.types.Operator):
bl_idname = "forum.hifi"
bl_label = "Forum Thread / Bug Reports"
Expand All @@ -467,7 +519,6 @@ def execute(self, context):
return {'FINISHED'}



classes = [
HifiArmaturePanel,
HifiMaterialsPanel,
Expand All @@ -491,7 +542,8 @@ def execute(self, context):
HifiCustomAvatarOperator,
HifiFixScaleOperator,
HifiForumOperator,
HifiCustomAvatarBinderOperator
HifiCustomAvatarBinderOperator,
HifiCombineBonesNonConnectedOperator
]


Expand Down
6 changes: 3 additions & 3 deletions hifi_tools/files/fst/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ class FSTWriterOperator(bpy.types.Operator, ExportHelper):
selected_only = BoolProperty(
default=False, name="Selected Only", description="Selected Only")

anim_graph_url = StringProperty(default="", name="Animation JSON Url",
description="Avatar Animation JSON absolute url path")
#anim_graph_url = StringProperty(default="", name="Animation JSON Url",
# description="Avatar Animation JSON absolute url path")

script = StringProperty(default="", name="Avatar Script Path",
description="Avatar Script absolute url path, Script that is run on avatar")
Expand All @@ -188,7 +188,7 @@ def draw(self, context):

oven_tool = context.user_preferences.addons[hifi_tools.__name__].preferences.oventool

layout.prop(self, "anim_graph_url")
#layout.prop(self, "anim_graph_url")
layout.prop(self, "script")

enabled_ipfs = len(
Expand Down
9 changes: 5 additions & 4 deletions hifi_tools/files/fst/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import os
import uuid
import hifi_tools
import datetime
import os.path as ntpath
import shutil

Expand Down Expand Up @@ -65,7 +66,7 @@
prefix_free_joint = "freeJoint = $\n"

prefix_script = "script = $\n"
prefix_anim_graph_url = "animGraphUrl = $\n"
#prefix_anim_graph_url = "animGraphUrl = $\n"


def default_blend_shape(selected):
Expand All @@ -82,7 +83,7 @@ def fst_export(context, selected):

preferences = bpy.context.user_preferences.addons[hifi_tools.__name__].preferences
# file = open
uuid_gen = uuid.uuid5(uuid.NAMESPACE_DNS, context.filepath)
uuid_gen = uuid.uuid5(uuid.NAMESPACE_DNS, context.filepath + '?' + str(datetime.datetime.now()).replace(" ", ""))
scene_id = str(uuid_gen)

print("Exporting file to filepath", context.filepath)
Expand Down Expand Up @@ -128,8 +129,8 @@ def fst_export(context, selected):
if context.flow:
print("Add Flow Script")

if len(context.anim_graph_url) > 0:
f.write(prefix_anim_graph_url.replace('$', context.anim_graph_url))
#if len(context.anim_graph_url) > 0:
# f.write(prefix_anim_graph_url.replace('$', context.anim_graph_url))

# Writing these in separate loops because they need to done in order.
for bone in armature.data.bones:
Expand Down
80 changes: 51 additions & 29 deletions hifi_tools/utils/bones.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
from hifi_tools.armature.skeleton import structure as base_armature

corrected_axis = {
"GLOBAL_NEG_Z": ["Shoulder", "Arm", "Hand", "Thumb"],
"GLOBAL_NEG_Y": ["Spine", "Head", "Hips", "Leg", "Foot", "Toe", "Eye"]
"GLOBAL_NEG_Z": ["Shoulder", "Arm", "Hand", "Thumb", "Leg", "Foot", "Toe", "Head", "Hips"],
"GLOBAL_NEG_Y": ["Eye", "Spine", "Neck", "Head"]
}

bone_parent_structure = {
Expand Down Expand Up @@ -65,13 +65,29 @@
number_text_re = re.compile(".+(\\d+).*")
blender_copy_re = re.compile("\.001$")
end_re = re.compile("_end$")
mixamorif_prefix = "Mixamorig:"
mixamo_prefix = "mixamo:"

def nuke_mixamo_prefix(edit_bones):
print("Show. No. Mercy to mixamo. Remove All as a prelim")

def combine_bones(selected_bones, active_bone, active_object):
found = False
for bone in edit_bones:
if "mixamo" in bone.name.lower():
found = True
bone.name = bone.name.replace(
mixamorif_prefix, "").replace(mixamo_prefix, "")

if found:
print("Mixamo Purge Complete")

return found


def combine_bones(selected_bones, active_bone, active_object, use_connect=True):
print("----------------------")
print("Combining Bones", len(selected_bones),
"-", active_bone, "-", active_object)
edit_bones = list(active_object.data.edit_bones)
meshes = mesh.get_mesh_from(active_object.children)
names_to_combine = []
active_bone_name = active_bone.name
Expand All @@ -85,7 +101,7 @@ def combine_bones(selected_bones, active_bone, active_object):
active_object.data.edit_bones.remove(bone)
# TODO: Removal is broken :(
for child in children:
child.use_connect = True
child.use_connect = use_connect

print("Combining weights.", meshes)
bpy.ops.object.mode_set(mode="OBJECT")
Expand All @@ -110,6 +126,11 @@ def combine_bones(selected_bones, active_bone, active_object):
print("Done")


def bone_connection(selected_bones, mode=False):
for bone in selected_bones:
bone.use_connect = mode


def scale_helper(obj):
if obj.dimensions.y > 2.4:
print("Avatar too large > 2.4m, maybe incorrect? setting height to 1.9m. You can scale avatar inworld, instead")
Expand All @@ -126,7 +147,7 @@ def scale_helper(obj):
bpy.ops.pose.transforms_clear()

bpy.ops.object.mode_set(mode='OBJECT')


def remove_all_actions():
for action in bpy.data.actions:
Expand Down Expand Up @@ -175,7 +196,8 @@ def __init__(self, side, mirror, name, mirror_name):

def camel_case_split(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1)
s2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1)
return re.sub('(.)(\d+)', r'\1_\2', s2)


def get_bone_side_and_mirrored(bone_name):
Expand Down Expand Up @@ -217,10 +239,9 @@ def get_bone_side_and_mirrored(bone_name):


def clean_up_bone_name(bone_name, remove_clones=True):

cleaned_bones = camel_case_split(bone_name)

cleaned_bones = bone_name.replace(".", "_").replace(" ", "_")

cleaned_bones = camel_case_split(
bone_name).replace(".", "_").replace(" ", "_")
split = cleaned_bones.split("_")

# Remove .001 blender suffic First remove every dot with _ to remove confusion
Expand All @@ -239,25 +260,22 @@ def clean_up_bone_name(bone_name, remove_clones=True):
new_bone_split.append(bone_name)
else:
for idx, val in enumerate(split):
print(idx, val)
if val is "r":
new_bone_split.append("Right")
elif val is "l":
new_bone_split.append("Left")
if val.lower() == "r" or val.lower() == "right":
new_bone_split.insert(0, "Right")
elif val.lower() == "l" or val.lower() == "left":
new_bone_split.insert(0, "Left")
elif number_text_re.match(val):
nr = number_text_re.match(val)
group = nr.groups()
if end:
last = str(int(group[0]) + 1)
last = str(int(group[0]) + 1).capitalize()
else:
last = group[0]
last = group[0].capitalize()
elif number_re.match(val): # value is a number, before the last
print("Idx", idx, length)
if idx < length:
print("Storing")
last = val.capitalize()
else:
new_bone_split.append(val)
new_bone_split.append(val.capitalize())

if last is not None:
if end:
Expand All @@ -279,6 +297,7 @@ def remove_selected_bones_physical(bones):
if physical_re.search(bone.name) is not None:
bone.name = physical_re.sub("", bone.name)


def correct_bone(bone, bones):
if bone.name == "Hips":
bone.parent = None
Expand All @@ -289,12 +308,15 @@ def correct_bone(bone, bones):
if parent_bone is not None:
bone.parent = parent_bone


def correct_bone_parents(bones):
for bone in bones:
correct_bone(bone, bones)


def correct_bone_rotations(obj):

bpy.ops.object.mode_set(mode="EDIT")
name = obj.name
if "Eye" in name:
bone_head = Vector(obj.head)
Expand All @@ -319,18 +341,17 @@ def correct_bone_rotations(obj):
axises = corrected_axis.keys()
correction = None
found = False

for axis in axises:
corrections = corrected_axis.get(axis)
for correction in corrections:
if correction in name:
print("Found correction", name, axis)
bpy.ops.object.mode_set(mode="EDIT")
print("Correcting Rolls,", name, axis)
bpy.ops.armature.select_all(action="DESELECT")

print(obj)
obj.select = True

bpy.ops.armature.calculate_roll(type=axis)
print(obj)

bpy.ops.armature.select_all(action="DESELECT")
found = True
break
Expand Down Expand Up @@ -433,7 +454,7 @@ def correct_scale_rotation(obj, rotation):
bpy.ops.object.select_all(action="DESELECT")

obj.select = True

bpy.context.scene.objects.active = obj
bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
Expand Down Expand Up @@ -478,7 +499,6 @@ def navigate_armature(data, current_rest_node, world_matrix, parent, parent_node
def retarget_armature(options, selected, selected_only=False):

armature = find_armature(selected)
print("selected", selected, "armature", armature)
if armature is not None:
# Center Children First
print(bpy.context.mode, armature)
Expand All @@ -491,11 +511,13 @@ def retarget_armature(options, selected, selected_only=False):

bpy.context.scene.objects.active = armature
armature.select = True
bpy.context.object.data.pose_position = 'POSE'

# Make sure to reset the bones first.
bpy.ops.object.transform_apply(
location=False, rotation=True, scale=True)
print("Selecting Bones")

bpy.ops.object.mode_set(mode="POSE")
bpy.ops.pose.select_all(action="SELECT")
bpy.ops.pose.transforms_clear()
Expand Down
Loading

0 comments on commit 0cd5459

Please sign in to comment.