├── quat_euler_rotation_mode_converter.blend ├── README.md ├── quaternions_to_euler.py ├── change_rotation_mode_old_class.py ├── change_rotation_mode_old.py └── change_rotation_mode_addon.py /quat_euler_rotation_mode_converter.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarioMey/rotation_mode_addon/HEAD/quat_euler_rotation_mode_converter.blend -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Quat/Euler Rotation Mode Converter v0.2 2 | ======================================= 3 | 4 | This script/addon: 5 | ------------------ 6 | 7 | - Changes (pose) bone rotation mode 8 | - Converts keyframes from one rotation mode to another 9 | - Creates fcurves/keyframes in target rotation mode 10 | - Deletes previous fcurves/keyframes. 11 | - Converts multiple bones 12 | - Converts multiple Actions 13 | 14 | TO-DO: 15 | ------ 16 | 17 | - To convert object's rotation mode (alrady done in Mutant Bob script, 18 | but not done in this one. 19 | - To understand "EnumProperty" and write it well. 20 | - Code clean 21 | - ... 22 | 23 | [GitHub](https://github.com/MarioMey/rotation_mode_addon/) 24 | [BlenderArtist thread](http://blenderartists.org/forum/showthread.php?388197-Quat-Euler-Rotation-Mode-Converter) 25 | 26 | **Mutant Bob** did the "hard code" of this script for [Blender Stack Exchange](blender.stackexchange.com/questions/40711/how-to-convert-quaternions-keyframes-to-euler-ones-in-several-actions). 27 | Thanks him! 28 | 29 | Version log: 30 | - 0.1 - Initial release 31 | - 0.2 - Pratik Solanki (http://www.dragoneex.com/) fixed the installation as an addon. 32 | 33 | -------------------------------------------------------------------------------- /quaternions_to_euler.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | def get_or_create_fcurve(action, data_path, array_index=-1, group=None): 4 | for fc in action.fcurves: 5 | if fc.data_path == data_path and (array_index<0 or fc.array_index == array_index): 6 | return fc 7 | 8 | fc = action.fcurves.new(data_path, array_index) 9 | fc.group = group 10 | return fc 11 | 12 | def add_keyframe_euler(action, euler, frame, bone_prefix, group): 13 | for i in range(len(euler)): 14 | fc = get_or_create_fcurve(action, bone_prefix+"rotation_euler", i, group) 15 | pos = len(fc.keyframe_points) 16 | fc.keyframe_points.add(1) 17 | fc.keyframe_points[pos].co = [frame, euler[i]] 18 | fc.update() 19 | 20 | 21 | def frames_matching(action, data_path): 22 | frames = set() 23 | for fc in action.fcurves: 24 | if fc.data_path == data_path: 25 | fri = [kp.co[0] for kp in fc.keyframe_points] 26 | frames.update(fri) 27 | return frames 28 | 29 | 30 | def fcurves_group(action, data_path): 31 | for fc in action.fcurves: 32 | if fc.data_path == data_path and fc.group is not None: 33 | return fc.group 34 | return None 35 | 36 | 37 | def convert_quaternion_to_euler(action, obj, order): 38 | 39 | bone_prefixes = set() 40 | for fc in action.fcurves: 41 | if fc.data_path == "rotation_quaternion" or fc.data_path[-20:]==".rotation_quaternion": 42 | bone_prefixes.add(fc.data_path[:-19]) 43 | 44 | print(bone_prefixes) 45 | 46 | 47 | for bone_prefix in bone_prefixes: 48 | if (bone_prefix == ""): 49 | bone = obj 50 | else: 51 | bone = eval("obj."+bone_prefix[:-1]) # I wish I knew a better way to do this 52 | 53 | data_path = bone_prefix + "rotation_quaternion" 54 | frames = frames_matching(action, data_path) 55 | group = fcurves_group(action, data_path) 56 | 57 | for fr in frames: 58 | quat = bone.rotation_quaternion.copy() 59 | for fc in action.fcurves: 60 | if fc.data_path == data_path: 61 | quat[fc.array_index] = fc.evaluate(fr) 62 | euler = quat.to_euler(order) 63 | 64 | add_keyframe_euler(action, euler, fr, bone_prefix, group) 65 | bone.rotation_mode = order 66 | # 67 | # 68 | 69 | scn = bpy.context.scene 70 | obj = bpy.context.active_object 71 | action = obj.animation_data.action 72 | order='XYZ' 73 | 74 | convert_quaternion_to_euler(action, obj, order) 75 | -------------------------------------------------------------------------------- /change_rotation_mode_old_class.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class convert(): 4 | 5 | def get_or_create_fcurve(self, action, data_path, array_index=-1, group=None): 6 | for fc in action.fcurves: 7 | if fc.data_path == data_path and (array_index<0 or fc.array_index == array_index): 8 | return fc 9 | 10 | fc = action.fcurves.new(data_path, array_index) 11 | fc.group = group 12 | return fc 13 | 14 | def add_keyframe_quat(self, action, quat, frame, bone_prefix, group): 15 | for i in range(len(quat)): 16 | fc = self.get_or_create_fcurve(action, bone_prefix+"rotation_quaternion", i, group) 17 | pos = len(fc.keyframe_points) 18 | fc.keyframe_points.add(1) 19 | fc.keyframe_points[pos].co = [frame, quat[i]] 20 | fc.update() 21 | 22 | def add_keyframe_euler(self, action, euler, frame, bone_prefix, group): 23 | for i in range(len(euler)): 24 | fc = self.get_or_create_fcurve(action, bone_prefix+"rotation_euler", i, group) 25 | pos = len(fc.keyframe_points) 26 | fc.keyframe_points.add(1) 27 | fc.keyframe_points[pos].co = [frame, euler[i]] 28 | fc.update() 29 | 30 | 31 | def frames_matching(self, action, data_path): 32 | frames = set() 33 | for fc in action.fcurves: 34 | if fc.data_path == data_path: 35 | fri = [kp.co[0] for kp in fc.keyframe_points] 36 | frames.update(fri) 37 | return frames 38 | 39 | # Converts only one group/bone in one action - Quat to euler 40 | def group_qe(self, obj, action, bone, bone_prefix, order): 41 | 42 | pose_bone = bone 43 | data_path = bone_prefix + "rotation_quaternion" 44 | frames = self.frames_matching(action, data_path) 45 | group = action.groups[bone.name] 46 | 47 | for fr in frames: 48 | quat = bone.rotation_quaternion.copy() 49 | for fc in action.fcurves: 50 | if fc.data_path == data_path: 51 | quat[fc.array_index] = fc.evaluate(fr) 52 | euler = quat.to_euler(order) 53 | 54 | self.add_keyframe_euler(action, euler, fr, bone_prefix, group) 55 | bone.rotation_mode = order 56 | 57 | # Converts only one group/bone in one action - Euler to Quat 58 | def group_eq(self, obj, action, bone, bone_prefix, order): 59 | 60 | pose_bone = bone 61 | data_path = bone_prefix + "rotation_euler" 62 | frames = self.frames_matching(action, data_path) 63 | group = action.groups[bone.name] 64 | 65 | for fr in frames: 66 | euler = bone.rotation_euler.copy() 67 | for fc in action.fcurves: 68 | if fc.data_path == data_path: 69 | euler[fc.array_index] = fc.evaluate(fr) 70 | quat = euler.to_quaternion() 71 | 72 | self.add_keyframe_quat(action, quat, fr, bone_prefix, group) 73 | bone.rotation_mode = order 74 | 75 | # One Action - One Bone 76 | def one_act_one_bon(self, obj, action, bone, order): 77 | do = False 78 | bone_prefix = '' 79 | 80 | # What kind of conversion 81 | cond1 = order == 'XYZ' 82 | cond2 = order == 'XZY' 83 | cond3 = order == 'YZX' 84 | cond4 = order == 'YXZ' 85 | cond5 = order == 'ZXY' 86 | cond6 = order == 'ZYX' 87 | 88 | order_euler = cond1 or cond2 or cond3 or cond4 or cond5 or cond6 89 | order_quat = order == 'QUATERNION' 90 | 91 | for fcurve in action.fcurves: 92 | if fcurve.group.name == bone.name: 93 | if order_euler: 94 | if fcurve.data_path.endswith('rotation_quaternion'): 95 | do = True 96 | bone_prefix = fcurve.data_path[:-len('rotation_quaternion')] 97 | break 98 | 99 | elif order_quat: 100 | if fcurve.data_path.endswith('rotation_euler'): 101 | do = True 102 | bone_prefix = fcurve.data_path[:-len('rotation_euler')] 103 | break 104 | else: 105 | print('Bad order') 106 | pass 107 | 108 | if do and order_euler: 109 | # Converts the group/bone from Quat to Euler 110 | self.group_qe(obj, action, bone, bone_prefix, order) 111 | 112 | # Removes quaternion fcurves 113 | for key in action.fcurves: 114 | if key.data_path == 'pose.bones["' + bone.name + '"].rotation_quaternion': 115 | action.fcurves.remove(key) 116 | 117 | elif do and order_quat: 118 | # Converts the group/bone from Euler to Quat 119 | self.group_eq(obj, action, bone, bone_prefix, order) 120 | 121 | # Removes euler fcurves 122 | for key in action.fcurves: 123 | if key.data_path == 'pose.bones["' + bone.name + '"].rotation_euler': 124 | action.fcurves.remove(key) 125 | 126 | # Changes rotation mode to new one 127 | bone.rotation_mode = order 128 | 129 | 130 | # One Action, selected bones 131 | def one_act_sel_bon(self, obj, action, pose_bones, order): 132 | for bone in pose_bones: 133 | self.one_act_one_bon(obj, action, bone, order) 134 | 135 | # One action, all Bones (in Action) 136 | def one_act_every_bon(self, obj, action, order): 137 | 138 | # Collects pose_bones that are in the action 139 | pose_bones = set() 140 | # Checks all fcurves 141 | for fcurve in action.fcurves: 142 | # Look for the ones that has rotation_quaternion 143 | if order == 'QUATERNION': 144 | if fcurve.data_path.endswith('rotation_euler'): 145 | if obj.pose.bones[fcurve.group.name] not in pose_bones: 146 | pose_bones.add(obj.pose.bones[fcurve.group.name]) 147 | else: 148 | if fcurve.data_path.endswith('rotation_quaternion'): 149 | if obj.pose.bones[fcurve.group.name] not in pose_bones: 150 | pose_bones.add(obj.pose.bones[fcurve.group.name]) 151 | 152 | 153 | # Convert current action and pose_bones that are in each action 154 | print(pose_bones) 155 | for bone in pose_bones: 156 | self.one_act_one_bon(obj, action, bone, order) 157 | 158 | # All Actions, selected bones 159 | def all_act_sel_bon(self, obj, pose_bones, order): 160 | for action in bpy.data.actions: 161 | for bone in pose_bones: 162 | self.one_act_one_bon(obj, action, bone, order) 163 | 164 | # All actions, All Bones (in each Action) 165 | def all_act_every_bon(self, obj, order): 166 | for action in bpy.data.actions: 167 | self.one_act_every_bon(obj, action, order) 168 | 169 | 170 | convert = convert() 171 | 172 | obj = bpy.context.active_object 173 | pose_bones = bpy.context.selected_pose_bones 174 | action = obj.animation_data.action 175 | 176 | #~ order='XYZ' 177 | order='QUATERNION' 178 | 179 | 180 | #~ convert.one_act_sel_bon(obj, action, pose_bones, order) 181 | #~ convert.one_act_every_bon(obj, action, order) 182 | #~ convert.all_act_sel_bon(obj, pose_bones, order) 183 | convert.all_act_every_bon(obj, order) 184 | 185 | -------------------------------------------------------------------------------- /change_rotation_mode_old.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | obj = bpy.context.active_object 4 | pose_bones = bpy.context.selected_pose_bones 5 | action = obj.animation_data.action 6 | 7 | 8 | def get_or_create_fcurve(action, data_path, array_index=-1, group=None): 9 | for fc in action.fcurves: 10 | if fc.data_path == data_path and (array_index<0 or fc.array_index == array_index): 11 | return fc 12 | 13 | fc = action.fcurves.new(data_path, array_index) 14 | fc.group = group 15 | return fc 16 | 17 | def add_keyframe_quat(action, quat, frame, bone_prefix, group): 18 | for i in range(len(quat)): 19 | fc = get_or_create_fcurve(action, bone_prefix+"rotation_quaternion", i, group) 20 | pos = len(fc.keyframe_points) 21 | fc.keyframe_points.add(1) 22 | fc.keyframe_points[pos].co = [frame, quat[i]] 23 | fc.update() 24 | 25 | def add_keyframe_euler(action, euler, frame, bone_prefix, group): 26 | for i in range(len(euler)): 27 | fc = get_or_create_fcurve(action, bone_prefix+"rotation_euler", i, group) 28 | pos = len(fc.keyframe_points) 29 | fc.keyframe_points.add(1) 30 | fc.keyframe_points[pos].co = [frame, euler[i]] 31 | fc.update() 32 | 33 | 34 | def frames_matching(action, data_path): 35 | frames = set() 36 | for fc in action.fcurves: 37 | if fc.data_path == data_path: 38 | fri = [kp.co[0] for kp in fc.keyframe_points] 39 | frames.update(fri) 40 | return frames 41 | 42 | # Converts only one group/bone in one action - Quat to euler 43 | def convert_group_qe(obj, action, bone, bone_prefix, order): 44 | 45 | pose_bone = bone 46 | data_path = bone_prefix + "rotation_quaternion" 47 | frames = frames_matching(action, data_path) 48 | group = action.groups[bone.name] 49 | 50 | for fr in frames: 51 | quat = bone.rotation_quaternion.copy() 52 | for fc in action.fcurves: 53 | if fc.data_path == data_path: 54 | quat[fc.array_index] = fc.evaluate(fr) 55 | euler = quat.to_euler(order) 56 | 57 | add_keyframe_euler(action, euler, fr, bone_prefix, group) 58 | bone.rotation_mode = order 59 | 60 | # Converts only one group/bone in one action - Euler to Quat 61 | def convert_group_eq(obj, action, bone, bone_prefix, order): 62 | 63 | pose_bone = bone 64 | data_path = bone_prefix + "rotation_euler" 65 | frames = frames_matching(action, data_path) 66 | group = action.groups[bone.name] 67 | 68 | for fr in frames: 69 | euler = bone.rotation_euler.copy() 70 | for fc in action.fcurves: 71 | if fc.data_path == data_path: 72 | euler[fc.array_index] = fc.evaluate(fr) 73 | quat = euler.to_quaternion() 74 | 75 | add_keyframe_quat(action, quat, fr, bone_prefix, group) 76 | bone.rotation_mode = order 77 | 78 | # One Action - One Bone 79 | def convert_one_act_one_bon(obj, action, bone, order): 80 | do = False 81 | bone_prefix = '' 82 | 83 | # What kind of conversion 84 | cond1 = order == 'XYZ' 85 | cond2 = order == 'XZY' 86 | cond3 = order == 'YZX' 87 | cond4 = order == 'YXZ' 88 | cond5 = order == 'ZXY' 89 | cond6 = order == 'ZYX' 90 | 91 | order_euler = cond1 or cond2 or cond3 or cond4 or cond5 or cond6 92 | order_quat = order == 'QUATERNION' 93 | 94 | for fcurve in action.fcurves: 95 | if fcurve.group.name == bone.name: 96 | if order_euler: 97 | if fcurve.data_path.endswith('rotation_quaternion'): 98 | do = True 99 | bone_prefix = fcurve.data_path[:-len('rotation_quaternion')] 100 | break 101 | 102 | elif order_quat: 103 | if fcurve.data_path.endswith('rotation_euler'): 104 | do = True 105 | bone_prefix = fcurve.data_path[:-len('rotation_euler')] 106 | break 107 | else: 108 | print('Bad order') 109 | pass 110 | 111 | if do and order_euler: 112 | # Converts the group/bone from Quat to Euler 113 | convert_group_qe(obj, action, bone, bone_prefix, order) 114 | 115 | # Removes quaternion fcurves 116 | for key in action.fcurves: 117 | if key.data_path == 'pose.bones["' + bone.name + '"].rotation_quaternion': 118 | action.fcurves.remove(key) 119 | 120 | elif do and order_quat: 121 | # Converts the group/bone from Euler to Quat 122 | convert_group_eq(obj, action, bone, bone_prefix, order) 123 | 124 | # Removes euler fcurves 125 | for key in action.fcurves: 126 | if key.data_path == 'pose.bones["' + bone.name + '"].rotation_euler': 127 | action.fcurves.remove(key) 128 | 129 | # Changes rotation mode to new one 130 | bone.rotation_mode = order 131 | 132 | # Borra todos los channels en Quaterniones 133 | # Para usar en conjunto con el script de Blender Stack 134 | def borra_quat_channels(test=True): 135 | for accion in bpy.data.actions: 136 | for key in accion.fcurves: 137 | if 'rotation_quaternion' in key.data_path: 138 | print('Action:', accion.name) 139 | print('Data_Path original:', key.data_path) 140 | if not test: 141 | accion.fcurves.remove(key) 142 | 143 | 144 | 145 | # One Actions, selected bones 146 | def convert_one_act_sel_bon(obj, action, pose_bones, order): 147 | for bone in pose_bones: 148 | convert_one_act_one_bon(obj, action, bone, order) 149 | 150 | # Current action, all Bones (in Action) 151 | def convert_one_act_every_bon(obj, action, order): 152 | 153 | # Collects fcurve.groups with its names 154 | pose_bones_names = set() 155 | for fcurve in action.fcurves: 156 | if fcurve.data_path.endswith('rotation_quaternion'): 157 | if fcurve.group.name not in pose_bones_names: 158 | pose_bones_names.add(fcurve.group.name) 159 | 160 | # Use the names to select pose_bones 161 | pose_bones = set() 162 | for pose_bone_name in pose_bones_names: 163 | pose_bones.add(obj.pose.bones[pose_bone_name]) 164 | 165 | # Convert current action and pose_bones that are in each action 166 | for bone in pose_bones: 167 | convert_one_act_one_bon(obj, action, bone, order) 168 | 169 | # All Actions, selected bones 170 | def convert_all_act_sel_bon(obj, pose_bones, order): 171 | for action in bpy.data.actions: 172 | for bone in pose_bones: 173 | convert_one_act_one_bon(obj, action, bone, order) 174 | 175 | # All actions, All Bones (in each Action) 176 | def convert_all_act_every_bon(obj, order): 177 | for action in bpy.data.actions: 178 | convert_one_act_every_bon(obj, action, order) 179 | 180 | order='XYZ' 181 | #~ order='QUATERNION' 182 | 183 | 184 | #~ convert_one_act_sel_bon(obj, action, pose_bones, order) 185 | convert_one_act_every_bon(obj, action, order) 186 | #~ convert_all_act_sel_bon(obj, pose_bones, order) 187 | #~ convert_all_act_every_bon(obj, order) 188 | 189 | -------------------------------------------------------------------------------- /change_rotation_mode_addon.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Quat/Euler Rotation Mode Converter v0.2 3 | 4 | This script/addon: 5 | - Changes (pose) bone rotation mode 6 | - Converts keyframes from one rotation mode to another 7 | - Creates fcurves/keyframes in target rotation mode 8 | - Deletes previous fcurves/keyframes. 9 | - Converts multiple bones 10 | - Converts multiple Actions 11 | 12 | TO-DO: 13 | - To convert object's rotation mode (alrady done in Mutant Bob script, 14 | but not done in this one. 15 | - To understand "EnumProperty" and write it well. 16 | - Code clean 17 | - ... 18 | 19 | GitHub: https://github.com/MarioMey/rotation_mode_addon/ 20 | BlenderArtist thread: http://blenderartists.org/forum/showthread.php?388197-Quat-Euler-Rotation-Mode-Converter 21 | 22 | Mutant Bob did the "hard code" of this script. Thanks him! 23 | blender.stackexchange.com/questions/40711/how-to-convert-quaternions-keyframes-to-euler-ones-in-several-actions 24 | 25 | Version log: 26 | 0.1 - Initial release 27 | 0.2 - Pratik Solanki (http://www.dragoneex.com/) fixed the installation as an addon. 28 | 29 | ''' 30 | 31 | 32 | bl_info = { 33 | "name": "Quat/Euler Rotation Mode Converter", 34 | "author": "Mario Mey / Mutant Bob", 35 | "version": (0, 2, 1), 36 | "blender": (2, 76, 0), 37 | 'location': '', 38 | "description": "Converts bones rotation mode", 39 | "warning": "", 40 | "wiki_url": "", 41 | "tracker_url": "https://github.com/MarioMey/rotation_mode_addon/", 42 | "category": "Animation"} 43 | 44 | 45 | import bpy 46 | from bpy.props import (StringProperty, 47 | BoolProperty, 48 | IntProperty, 49 | FloatProperty, 50 | FloatVectorProperty, 51 | EnumProperty, 52 | PointerProperty, 53 | CollectionProperty 54 | ) 55 | 56 | order_list = ['QUATERNION', 'XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX'] 57 | 58 | class convert(): 59 | 60 | def get_or_create_fcurve(self, action, data_path, array_index=-1, group=None): 61 | for fc in action.fcurves: 62 | if fc.data_path == data_path and (array_index<0 or fc.array_index == array_index): 63 | return fc 64 | 65 | fc = action.fcurves.new(data_path, array_index) 66 | fc.group = group 67 | return fc 68 | 69 | def add_keyframe_quat(self, action, quat, frame, bone_prefix, group): 70 | for i in range(len(quat)): 71 | fc = self.get_or_create_fcurve(action, bone_prefix+"rotation_quaternion", i, group) 72 | pos = len(fc.keyframe_points) 73 | fc.keyframe_points.add(1) 74 | fc.keyframe_points[pos].co = [frame, quat[i]] 75 | fc.update() 76 | 77 | def add_keyframe_euler(self, action, euler, frame, bone_prefix, group): 78 | for i in range(len(euler)): 79 | fc = self.get_or_create_fcurve(action, bone_prefix+"rotation_euler", i, group) 80 | pos = len(fc.keyframe_points) 81 | fc.keyframe_points.add(1) 82 | fc.keyframe_points[pos].co = [frame, euler[i]] 83 | fc.update() 84 | 85 | 86 | def frames_matching(self, action, data_path): 87 | frames = set() 88 | for fc in action.fcurves: 89 | if fc.data_path == data_path: 90 | fri = [kp.co[0] for kp in fc.keyframe_points] 91 | frames.update(fri) 92 | return frames 93 | 94 | # Converts only one group/bone in one action - Quat to euler 95 | def group_qe(self, obj, action, bone, bone_prefix, order): 96 | 97 | pose_bone = bone 98 | data_path = bone_prefix + "rotation_quaternion" 99 | frames = self.frames_matching(action, data_path) 100 | group = action.groups[bone.name] 101 | 102 | for fr in frames: 103 | quat = bone.rotation_quaternion.copy() 104 | for fc in action.fcurves: 105 | if fc.data_path == data_path: 106 | quat[fc.array_index] = fc.evaluate(fr) 107 | euler = quat.to_euler(order) 108 | 109 | self.add_keyframe_euler(action, euler, fr, bone_prefix, group) 110 | bone.rotation_mode = order 111 | 112 | # Converts only one group/bone in one action - Euler to Quat 113 | def group_eq(self, obj, action, bone, bone_prefix, order): 114 | 115 | pose_bone = bone 116 | data_path = bone_prefix + "rotation_euler" 117 | frames = self.frames_matching(action, data_path) 118 | group = action.groups[bone.name] 119 | 120 | for fr in frames: 121 | euler = bone.rotation_euler.copy() 122 | for fc in action.fcurves: 123 | if fc.data_path == data_path: 124 | euler[fc.array_index] = fc.evaluate(fr) 125 | quat = euler.to_quaternion() 126 | 127 | self.add_keyframe_quat(action, quat, fr, bone_prefix, group) 128 | bone.rotation_mode = order 129 | 130 | # One Action - One Bone 131 | def one_act_one_bon(self, obj, action, bone, order): 132 | do = False 133 | bone_prefix = '' 134 | 135 | # What kind of conversion 136 | cond1 = order == 'XYZ' 137 | cond2 = order == 'XZY' 138 | cond3 = order == 'YZX' 139 | cond4 = order == 'YXZ' 140 | cond5 = order == 'ZXY' 141 | cond6 = order == 'ZYX' 142 | 143 | order_euler = cond1 or cond2 or cond3 or cond4 or cond5 or cond6 144 | order_quat = order == 'QUATERNION' 145 | 146 | for fcurve in action.fcurves: 147 | # Una fcurve puede no tener grupo. 148 | if hasattr(fcurve, 'group') and fcurve.group.name == bone.name: 149 | 150 | # If To-Euler conversion 151 | if order != 'QUATERNION': 152 | if fcurve.data_path.endswith('rotation_quaternion'): 153 | do = True 154 | bone_prefix = fcurve.data_path[:-len('rotation_quaternion')] 155 | break 156 | 157 | # If To-Quat conversion 158 | else: 159 | if fcurve.data_path.endswith('rotation_euler'): 160 | do = True 161 | bone_prefix = fcurve.data_path[:-len('rotation_euler')] 162 | break 163 | 164 | # If To-Euler conversion 165 | if do and order != 'QUATERNION': 166 | # Converts the group/bone from Quat to Euler 167 | self.group_qe(obj, action, bone, bone_prefix, order) 168 | 169 | # Removes quaternion fcurves 170 | for key in action.fcurves: 171 | if key.data_path == 'pose.bones["' + bone.name + '"].rotation_quaternion': 172 | action.fcurves.remove(key) 173 | 174 | # If To-Quat conversion 175 | elif do: 176 | # Converts the group/bone from Euler to Quat 177 | self.group_eq(obj, action, bone, bone_prefix, order) 178 | 179 | # Removes euler fcurves 180 | for key in action.fcurves: 181 | if key.data_path == 'pose.bones["' + bone.name + '"].rotation_euler': 182 | action.fcurves.remove(key) 183 | 184 | # Changes rotation mode to new one 185 | bone.rotation_mode = order 186 | 187 | 188 | # One Action, selected bones 189 | def one_act_sel_bon(self, obj, action, pose_bones, order): 190 | for bone in pose_bones: 191 | self.one_act_one_bon(obj, action, bone, order) 192 | 193 | # One action, all Bones (in Action) 194 | def one_act_every_bon(self, obj, action, order): 195 | 196 | # Collects pose_bones that are in the action 197 | pose_bones = set() 198 | # Checks all fcurves 199 | for fcurve in action.fcurves: 200 | # Look for the ones that has rotation_euler 201 | if order == 'QUATERNION': 202 | if fcurve.data_path.endswith('rotation_euler'): 203 | # If the bone from action really exists 204 | if fcurve.group.name in obj.pose.bones: 205 | if obj.pose.bones[fcurve.group.name] not in pose_bones: 206 | pose_bones.add(obj.pose.bones[fcurve.group.name]) 207 | else: 208 | print(fcurve.group.name, 'does not exist in Armature. Fcurve-group is not affected') 209 | 210 | # Look for the ones that has rotation_quaternion 211 | else: 212 | if fcurve.data_path.endswith('rotation_quaternion'): 213 | # If the bone from action really exists 214 | if fcurve.group.name in obj.pose.bones: 215 | if obj.pose.bones[fcurve.group.name] not in pose_bones: 216 | pose_bones.add(obj.pose.bones[fcurve.group.name]) 217 | else: 218 | print(fcurve.group.name, 'does not exist in Armature. Fcurve-group is not affected') 219 | 220 | 221 | # Convert current action and pose_bones that are in each action 222 | for bone in pose_bones: 223 | self.one_act_one_bon(obj, action, bone, order) 224 | 225 | # All Actions, selected bones 226 | def all_act_sel_bon(self, obj, pose_bones, order): 227 | for action in bpy.data.actions: 228 | for bone in pose_bones: 229 | self.one_act_one_bon(obj, action, bone, order) 230 | 231 | # All actions, All Bones (in each Action) 232 | def all_act_every_bon(self, obj, order): 233 | for action in bpy.data.actions: 234 | self.one_act_every_bon(obj, action, order) 235 | 236 | 237 | convert = convert() 238 | 239 | 240 | def initSceneProperties(PropertyGroup): 241 | 242 | bpy.types.Scene.order_list = bpy.props.EnumProperty( 243 | items = [('QUATERNION', 'QUATERNION', 'QUATERNION' ), 244 | ('XYZ', 'XYZ', 'XYZ' ), 245 | ('XZY', 'XZY', 'XZY' ), 246 | ('YXZ', 'YXZ', 'YXZ' ), 247 | ('YZX', 'YZX', 'YZX' ), 248 | ('ZXY', 'ZXY', 'ZXY' ), 249 | ('ZYX', 'ZYX', 'ZYX' ) ], 250 | name = "Order", 251 | description = "The targe rotation mode") 252 | 253 | scn['order_list'] = 0 254 | 255 | 256 | 257 | 258 | # GUI (Panel) 259 | # 260 | class ToolsPanel(bpy.types.Panel): 261 | bl_space_type = 'VIEW_3D' 262 | bl_region_type = 'TOOLS' 263 | bl_category = "Tools" 264 | bl_context = "posemode" 265 | bl_label = 'Quat/Euler Converter' 266 | 267 | # draw the gui 268 | def draw(self, context): 269 | layout = self.layout 270 | scn = context.scene 271 | #~ toolsettings = context.tool_settings 272 | 273 | col = layout.column(align=True) 274 | row = col.row(align=True) 275 | 276 | layout.prop(scn, 'order_list') 277 | 278 | col = layout.column(align=True) 279 | row = col.row(align=True) 280 | 281 | col.label(text="Current Action:") 282 | col.operator('current.selected') 283 | col.operator('current.every') 284 | 285 | row = col.row(align=True) 286 | col = layout.column(align=True) 287 | 288 | col.label(text="All Actions:") 289 | col.operator('all.selected') 290 | col.operator('all.every') 291 | 292 | 293 | class CONVERT_OT_current_action_selected_bones(bpy.types.Operator): 294 | bl_label = 'Selected Bones' 295 | bl_idname = 'current.selected' 296 | bl_description = 'Converts selected bones in current Action' 297 | bl_options = {'REGISTER', 'UNDO'} 298 | 299 | # on mouse up: 300 | def invoke(self, context, event): 301 | self.execute(context) 302 | return {'FINISHED'} 303 | 304 | def execute(op, context): 305 | obj = bpy.context.active_object 306 | pose_bones = bpy.context.selected_pose_bones 307 | action = obj.animation_data.action 308 | order = order_list[bpy.context.scene['order_list']] 309 | 310 | convert.one_act_sel_bon(obj, action, pose_bones, order) 311 | 312 | return {'FINISHED'} 313 | 314 | class CONVERT_OT_current_action_every_bones(bpy.types.Operator): 315 | bl_label = 'All Bones' 316 | bl_idname = 'current.every' 317 | bl_description = 'Converts every bone in current Action' 318 | bl_options = {'REGISTER', 'UNDO'} 319 | 320 | # on mouse up: 321 | def invoke(self, context, event): 322 | self.execute(context) 323 | return {'FINISHED'} 324 | 325 | def execute(op, context): 326 | obj = bpy.context.active_object 327 | pose_bones = bpy.context.selected_pose_bones 328 | action = obj.animation_data.action 329 | order = order_list[bpy.context.scene['order_list']] 330 | 331 | convert.one_act_every_bon(obj, action, order) 332 | 333 | return {'FINISHED'} 334 | 335 | class CONVERT_OT_all_actions_selected_bones(bpy.types.Operator): 336 | bl_label = 'Selected Bone' 337 | bl_idname = 'all.selected' 338 | bl_description = 'Converts selected bones in every Action' 339 | bl_options = {'REGISTER', 'UNDO'} 340 | 341 | # on mouse up: 342 | def invoke(self, context, event): 343 | self.execute(context) 344 | return {'FINISHED'} 345 | 346 | def execute(op, context): 347 | obj = bpy.context.active_object 348 | pose_bones = bpy.context.selected_pose_bones 349 | order = order_list[bpy.context.scene['order_list']] 350 | 351 | convert.all_act_sel_bon(obj, pose_bones, order) 352 | 353 | return {'FINISHED'} 354 | 355 | class CONVERT_OT_all_action_every_bones(bpy.types.Operator): 356 | bl_label = 'All Bone' 357 | bl_idname = 'all.every' 358 | bl_description = 'Converts every bone in every Action' 359 | bl_options = {'REGISTER', 'UNDO'} 360 | 361 | # on mouse up: 362 | def invoke(self, context, event): 363 | self.execute(context) 364 | return {'FINISHED'} 365 | 366 | def execute(op, context): 367 | obj = bpy.context.active_object 368 | order = order_list[bpy.context.scene['order_list']] 369 | 370 | convert.all_act_every_bon(obj, order) 371 | 372 | 373 | def register(): 374 | bpy.utils.register_module(__name__) 375 | bpy.types.Scene.order_list = bpy.props.EnumProperty( 376 | items = [('QUATERNION', 'QUATERNION', 'QUATERNION' ), 377 | ('XYZ', 'XYZ', 'XYZ' ), 378 | ('XZY', 'XZY', 'XZY' ), 379 | ('YXZ', 'YXZ', 'YXZ' ), 380 | ('YZX', 'YZX', 'YZX' ), 381 | ('ZXY', 'ZXY', 'ZXY' ), 382 | ('ZYX', 'ZYX', 'ZYX' ) ], 383 | name = "Order", 384 | description = "The targe rotation mode") 385 | #bpy.types.Scene.convertrot = PointerProperty(type=initSceneProperties) 386 | 387 | 388 | def unregister(): 389 | bpy.utils.unregister_module(__name__) 390 | del bpy.types.Scene.order_list 391 | 392 | 393 | if __name__ == "__main__": 394 | register() 395 | --------------------------------------------------------------------------------