-
Dalai Felinto authored
Since some recent change in 2.8 the UI for rigify is no longer working. Note: if your object has ANY custom property, the custom property shows as a Misc. category, it is not particular to rigify, so I will not bother with this here.
Dalai Felinto authoredSince some recent change in 2.8 the UI for rigify is no longer working. Note: if your object has ANY custom property, the custom property shows as a Misc. category, it is not particular to rigify, so I will not bother with this here.
rot_mode.py 11.63 KiB
'''
Quat/Euler Rotation Mode Converter v0.1
This script/addon:
- Changes (pose) bone rotation mode
- Converts keyframes from one rotation mode to another
- Creates fcurves/keyframes in target rotation mode
- Deletes previous fcurves/keyframes.
- Converts multiple bones
- Converts multiple Actions
TO-DO:
- To convert object's rotation mode (already done in Mutant Bob script,
but not done in this one.
- To understand "EnumProperty" and write it well.
- Code clean
- ...
GitHub: https://github.com/MarioMey/rotation_mode_addon/
BlenderArtist thread: http://blenderartists.org/forum/showthread.php?388197-Quat-Euler-Rotation-Mode-Converter
Mutant Bob did the "hard code" of this script. Thanks him!
blender.stackexchange.com/questions/40711/how-to-convert-quaternions-keyframes-to-euler-ones-in-several-actions
'''
# bl_info = {
# "name": "Quat/Euler Rotation Mode Converter",
# "author": "Mario Mey / Mutant Bob",
# "version": (0, 1),
# "blender": (2, 76, 0),
# 'location': '',
# "description": "Converts bones rotation mode",
# "warning": "",
# "wiki_url": "",
# "tracker_url": "https://github.com/MarioMey/rotation_mode_addon/",
# "category": "Animation"}
import bpy
from bpy.props import (
BoolProperty,
EnumProperty,
)
class convert():
def get_or_create_fcurve(self, action, data_path, array_index=-1, group=None):
for fc in action.fcurves:
if fc.data_path == data_path and (array_index < 0 or fc.array_index == array_index):
return fc
fc = action.fcurves.new(data_path, array_index)
fc.group = group
return fc
def add_keyframe_quat(self, action, quat, frame, bone_prefix, group):
for i in range(len(quat)):
fc = self.get_or_create_fcurve(action, bone_prefix + "rotation_quaternion", i, group)
pos = len(fc.keyframe_points)
fc.keyframe_points.add(1)
fc.keyframe_points[pos].co = [frame, quat[i]]
fc.update()
def add_keyframe_euler(self, action, euler, frame, bone_prefix, group):
for i in range(len(euler)):
fc = self.get_or_create_fcurve(action, bone_prefix + "rotation_euler", i, group)
pos = len(fc.keyframe_points)
fc.keyframe_points.add(1)
fc.keyframe_points[pos].co = [frame, euler[i]]
fc.update()
def frames_matching(self, action, data_path):
frames = set()
for fc in action.fcurves:
if fc.data_path == data_path:
fri = [kp.co[0] for kp in fc.keyframe_points]
frames.update(fri)
return frames
# Converts only one group/bone in one action - Quat to euler
def group_qe(self, obj, action, bone, bone_prefix, order):
pose_bone = bone
data_path = bone_prefix + "rotation_quaternion"
frames = self.frames_matching(action, data_path)
group = action.groups[bone.name]
for fr in frames:
quat = bone.rotation_quaternion.copy()
for fc in action.fcurves:
if fc.data_path == data_path:
quat[fc.array_index] = fc.evaluate(fr)
euler = quat.to_euler(order)
self.add_keyframe_euler(action, euler, fr, bone_prefix, group)
bone.rotation_mode = order
# Converts only one group/bone in one action - Euler to Quat
def group_eq(self, obj, action, bone, bone_prefix, order):
pose_bone = bone
data_path = bone_prefix + "rotation_euler"
frames = self.frames_matching(action, data_path)
group = action.groups[bone.name]
for fr in frames:
euler = bone.rotation_euler.copy()
for fc in action.fcurves:
if fc.data_path == data_path:
euler[fc.array_index] = fc.evaluate(fr)
quat = euler.to_quaternion()
self.add_keyframe_quat(action, quat, fr, bone_prefix, group)
bone.rotation_mode = order
# One Action - One Bone
def one_act_one_bon(self, obj, action, bone, order):
do = False
bone_prefix = ''
# What kind of conversion
cond1 = order == 'XYZ'
cond2 = order == 'XZY'
cond3 = order == 'YZX'
cond4 = order == 'YXZ'
cond5 = order == 'ZXY'
cond6 = order == 'ZYX'
order_euler = cond1 or cond2 or cond3 or cond4 or cond5 or cond6
order_quat = order == 'QUATERNION'
for fcurve in action.fcurves:
if fcurve.group.name == bone.name:
# If To-Euler conversion
if order != 'QUATERNION':
if fcurve.data_path.endswith('rotation_quaternion'):
do = True
bone_prefix = fcurve.data_path[:-len('rotation_quaternion')]
break
# If To-Quat conversion
else:
if fcurve.data_path.endswith('rotation_euler'):
do = True
bone_prefix = fcurve.data_path[:-len('rotation_euler')]
break
# If To-Euler conversion
if do and order != 'QUATERNION':
# Converts the group/bone from Quat to Euler
self.group_qe(obj, action, bone, bone_prefix, order)
# Removes quaternion fcurves
for key in action.fcurves:
if key.data_path == 'pose.bones["' + bone.name + '"].rotation_quaternion':
action.fcurves.remove(key)
# If To-Quat conversion
elif do:
# Converts the group/bone from Euler to Quat
self.group_eq(obj, action, bone, bone_prefix, order)
# Removes euler fcurves
for key in action.fcurves:
if key.data_path == 'pose.bones["' + bone.name + '"].rotation_euler':
action.fcurves.remove(key)
# Changes rotation mode to new one
bone.rotation_mode = order
# One Action, selected bones
def one_act_sel_bon(self, obj, action, pose_bones, order):
for bone in pose_bones:
self.one_act_one_bon(obj, action, bone, order)
# One action, all Bones (in Action)
def one_act_every_bon(self, obj, action, order):
# Collects pose_bones that are in the action
pose_bones = set()
# Checks all fcurves
for fcurve in action.fcurves:
# Look for the ones that has rotation_euler
if order == 'QUATERNION':
if fcurve.data_path.endswith('rotation_euler'):
# If the bone from action really exists
if fcurve.group.name in obj.pose.bones:
if obj.pose.bones[fcurve.group.name] not in pose_bones:
pose_bones.add(obj.pose.bones[fcurve.group.name])
else:
print(fcurve.group.name, 'does not exist in Armature. Fcurve-group is not affected')
# Look for the ones that has rotation_quaternion
else:
if fcurve.data_path.endswith('rotation_quaternion'):
# If the bone from action really exists
if fcurve.group.name in obj.pose.bones:
if obj.pose.bones[fcurve.group.name] not in pose_bones:
pose_bones.add(obj.pose.bones[fcurve.group.name])
else:
print(fcurve.group.name, 'does not exist in Armature. Fcurve-group is not affected')
# Convert current action and pose_bones that are in each action
for bone in pose_bones:
self.one_act_one_bon(obj, action, bone, order)
# All Actions, selected bones
def all_act_sel_bon(self, obj, pose_bones, order):
for action in bpy.data.actions:
for bone in pose_bones:
self.one_act_one_bon(obj, action, bone, order)
# All actions, All Bones (in each Action)
def all_act_every_bon(self, obj, order):
for action in bpy.data.actions:
self.one_act_every_bon(obj, action, order)
convert = convert()
class ToolsPanel(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'View'
bl_context = "posemode"
bl_label = 'Rigify Quat/Euler Converter'
# draw the gui
def draw(self, context):
layout = self.layout
scn = context.scene
# ~ toolsettings = context.tool_settings
col = layout.column(align=True)
row = col.row(align=True)
id_store = context.window_manager
layout.prop(scn, 'order_list')
if id_store.rigify_convert_only_selected:
icon = 'OUTLINER_DATA_ARMATURE'
else:
icon = 'ARMATURE_DATA'
layout.prop(id_store, 'rigify_convert_only_selected', toggle=True, icon=icon)
col = layout.column(align=True)
row = col.row(align=True)
row.operator('rigify_quat2eu.current', icon='ACTION')
row = col.row(align=True)
row.operator('rigify_quat2eu.all', icon='NLA')
class CONVERT_OT_quat2eu_current_action(bpy.types.Operator):
bl_label = 'Convert Current Action'
bl_idname = 'rigify_quat2eu.current'
bl_description = 'Converts bones in current Action'
bl_options = {'REGISTER', 'UNDO'}
# on mouse up:
def invoke(self, context, event):
self.execute(context)
return {'FINISHED'}
def execute(op, context):
obj = bpy.context.active_object
pose_bones = bpy.context.selected_pose_bones
action = obj.animation_data.action
order = bpy.context.scene.order_list
id_store = context.window_manager
if id_store.rigify_convert_only_selected:
convert.one_act_sel_bon(obj, action, pose_bones, order)
else:
convert.one_act_every_bon(obj, action, order)
return {'FINISHED'}
class CONVERT_OT_quat2eu_all_actions(bpy.types.Operator):
bl_label = 'Convert All Actions'
bl_idname = 'rigify_quat2eu.all'
bl_description = 'Converts bones in every Action'
bl_options = {'REGISTER', 'UNDO'}
# on mouse up:
def invoke(self, context, event):
self.execute(context)
return {'FINISHED'}
def execute(op, context):
obj = bpy.context.active_object
pose_bones = bpy.context.selected_pose_bones
order = bpy.context.scene.order_list
id_store = context.window_manager
if id_store.rigify_convert_only_selected:
convert.all_act_sel_bon(obj, pose_bones, order)
else:
convert.all_act_every_bon(obj, order)
return {'FINISHED'}
### Registering ###
classes = (
ToolsPanel,
CONVERT_OT_quat2eu_current_action,
CONVERT_OT_quat2eu_all_actions,
)
def register():
from bpy.utils import register_class
# Properties.
items = [('QUATERNION', 'QUATERNION', 'QUATERNION'),
('XYZ', 'XYZ', 'XYZ'),
('XZY', 'XZY', 'XZY'),
('YXZ', 'YXZ', 'YXZ'),
('YZX', 'YZX', 'YZX'),
('ZXY', 'ZXY', 'ZXY'),
('ZYX', 'ZYX', 'ZYX')]
bpy.types.Scene.order_list = EnumProperty(
items=items, name='Convert to',
description="The target rotation mode", default='QUATERNION')
IDStore = bpy.types.WindowManager
IDStore.rigify_convert_only_selected = BoolProperty(
name="Convert Only Selected",
description="Convert selected bones only", default=True)
# Classes.
for cls in classes:
register_class(cls)
def unregister():
from bpy.utils import unregister_class
# Classes.
for cls in classes:
unregister_class(cls)
# Properties.
IDStore = bpy.types.WindowManager
del IDStore.rigify_convert_only_selected