import bpy, sys, linecache, ast
import math
from math import *
from mathutils import *
from bpy.types import Panel, UIList
from .utils import *
from .define import *
# OPERATOR CLASSES
##################
class MR_OT_update(bpy.types.Operator):
"""Update old control rig to Blender 3.0"""
bl_idname = "mr.update"
bl_label = "update"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
if context.active_object.type == "ARMATURE":
return "mr_control_rig" in context.active_object.data.keys()
def execute(self, context):
try:
_update(self)
finally:
pass
return {'FINISHED'}
class MR_OT_exportGLTF(bpy.types.Operator):
"""Export to GLTF format"""
bl_idname = "mr.export_gltf"
bl_label = "export_gltf"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
if context.active_object.type == "ARMATURE":
return True
def execute(self, context):
try:
bpy.ops.export_scene.gltf()
finally:
pass
return {'FINISHED'}
class MR_OT_apply_shape(bpy.types.Operator):
"""Apply the selected shape"""
bl_idname = "mr.apply_shape"
bl_label = "apply_shape"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
if context.mode == 'EDIT_MESH':
if 'cs_user' in context.active_object.name:
return True
def execute(self, context):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
_apply_shape()
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
class MR_OT_edit_custom_shape(bpy.types.Operator):
"""Edit the selected bone shape"""
bl_idname = "mr.edit_custom_shape"
bl_label = "edit_custom_shape"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.mode == 'POSE':
if bpy.context.active_pose_bone:
return True
def execute(self, context):
try:
cs = bpy.context.active_pose_bone.custom_shape
if cs:
_edit_custom_shape()
else:
self.report({"ERROR"}, "No custom shapes set for this bone.")
finally:
pass
return {'FINISHED'}
class MR_OT_make_rig(bpy.types.Operator):
"""Generate a control rig from the selected Mixamo skeleton"""
bl_idname = "mr.make_rig"
bl_label = "Create control rig from selected armature"
bl_options = {'UNDO'}
bake_anim: bpy.props.BoolProperty(name="Bake Anim", description="Bake animation to the control bones", default=True)
ik_arms: bpy.props.BoolProperty(name="IK Hands", description="Use IK for arm bones, otherwise use FK (can be toggled later using the rig properties)", default=True)
ik_legs: bpy.props.BoolProperty(name="IK Legs", description="Use IK for leg bones, otherwise use FK (can be toggled later using the rig properties)", default=True)
animated_armature = None
@classmethod
def poll(cls, context):
if context.active_object:
if context.active_object.type == "ARMATURE":
if not "mr_control_rig" in context.active_object.data.keys():
return True
return False
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=450)
def draw(self, context):
layout = self.layout
layout.prop(self, 'bake_anim', text="Apply Animation")
layout.prop(self, 'ik_arms', text="IK Arms")
layout.prop(self, 'ik_legs', text="IK Legs")
def execute(self, context):
debug = False
layer_select = []
try:
# only select the armature
arm = get_object(context.active_object.name)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(arm.name)
# enable all armature layers
layer_select = enable_all_armature_layers()
# animation import: initial steps
if self.bake_anim:
if not "mr_control_rig" in arm.data.keys():# only if the control rig is not already built
# duplicate current skeleton
duplicate_object()
copy_name = arm.name+"_TEMPANIM"
self.animated_armature = get_object(bpy.context.active_object.name)
self.animated_armature.name = copy_name
self.animated_armature["mix_to_del"] = True
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(arm.name)
# set to rest pose, clear animation
_zero_out()
# build control rig
_make_rig(self)
if blender_version._float < 291:
# Child Of constraints inverse matrix must be set manually in Blender versions < 2.91
print("Set inverse ChildOf")
_reset_inverse_constraints()
# animation import: retarget
if self.bake_anim and self.animated_armature:
_import_anim(self.animated_armature, arm)
# set KeyingSet
ks = context.scene.keying_sets_all
try:
ks.active = ks["Location & Rotation"]
except:# doesn't exist in older Blender versions
pass
finally:
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
set_active_object(arm.name)
if debug == False:
restore_armature_layers(layer_select)
remove_retarget_cns(bpy.context.active_object)
remove_temp_objects()
clean_scene()
self.report({"INFO"}, "Control Rig Done!")
return {'FINISHED'}
class MR_OT_zero_out(bpy.types.Operator):
"""Delete all keys and set every bones to (0,0,0) rotation"""
bl_idname = "mr.zero_out"
bl_label = "zero_out"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
return context.active_object.type == "ARMATURE"
return False
def execute(self, context):
scn = bpy.context.scene
try:
_zero_out()
finally:
print("")
return {'FINISHED'}
class MR_OT_bake_anim(bpy.types.Operator):
"""Merge all animation layers (see NLA editor) into a single layer"""
bl_idname = "mr.bake_anim"
bl_label = "bake_anim"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
if context.active_object:
return context.active_object.type == "ARMATURE"
return False
def execute(self, context):
scn = bpy.context.scene
try:
_bake_anim(self)
finally:
pass
return {'FINISHED'}
class MR_OT_import_anim(bpy.types.Operator):
"""Import an animation file (FBX) of the same character to the control rig"""
bl_idname = "mr.impo