diff --git a/README.md b/README.md index 6afb1dbd..d16c1c13 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ A tool designed to shorten steps needed to import and optimise MMD models into V 1. download the plugin: [Cats Blender Plugin](https://github.com/michaeldegroot/cats-blender-plugin/raw/downloads/Cats%20Blender%20Plugin.zip) **Important!!** -You are downloading a older version of the plugin here, which is required: You will need to check the update tab of the plugin and update it to the latest version from there! +You are downloading an older version of the plugin here, which is required: You will need to check the update tab of the plugin and update it to the latest version from there! 2. Install the the addon in blender like so: @@ -35,7 +35,7 @@ You are downloading a older version of the plugin here, which is required: You w #### 0.0.2 - Added: Eye tracking, added a check to see if a vertex group and vertices were assigned to the eye bones before continuing - - Added: Added a auto updater to easily keep track of new updates of the plugin + - Added: Added an auto updater to easily keep track of new updates of the plugin - Added: Mouth viseme, added a strength modifier for the mixing of the shapes - Added: plugin support (install as addon in blender) - Changed: UI improved @@ -67,16 +67,16 @@ You are downloading a older version of the plugin here, which is required: You w - Added: Credits tab :) - Changed: Translation: Using googletrans module ( = faster! thanks Hotox!) - Changed: Codebase modularised, project is more tidy now. Good for future updates - - Changed: UI: every function has it's own collapsable panel now + - Changed: UI: every function has it's own collapsible panel now - Fixed: Mouth viseme: Adding random key shape weight to the mix with 0.0001 weight to fix a weird blender export condition (should fix open mouth) - Fixed: Eye tracking: Adding random key shape weight to the mix with 0.0001 weight to fix a weird blender export condition (should fix any future problems that may arise) #### 0.0.6 - Added: Dependency Tab: Gives a warning if mmd_tools are not activated or installed (Thanks Hotox!) - - Added: Armature: A one fix it all button for MMD models (still work in progress but a good start!) + - Added: Armature: A fix it all button for MMD models (still work in progress but a good start!) - Added: Armature: Fixes the hips angle VRCSDK error - Added: Armature: Deletes rigidbodies and joints - - Added: Armature: Bone hierachy validation: Hips > Spine > Chest etc to make sure you get no problems in Unity + - Added: Armature: Bone hierarchy validation: Hips > Spine > Chest etc to make sure you get no problems in Unity - Added: Armature: Uses MMD_TOOLS to translate bones and reparent and weight them with Shotariya's tool - Added: Armature: Neitri's zero weight and bone constraint code implemented (there are some small issues with this at the moment, should be fixed soon) - Fixed: Viseme: Mesh selection in visemes function would not have effect on the shape key selection list @@ -111,7 +111,7 @@ Lower for faster bake time, higher for more detail. Weight projections vector by faces with larger areas ##### Target mesh -The mesh that you want to create a atlas from +The mesh that you want to create an atlas from ##### Disable multiple textures Texture baking and multiple textures per material can look weird in the end result. Check this box if you are experiencing this. @@ -122,7 +122,7 @@ Texture baking and multiple textures per material can look weird in the end resu **Mouth visemes are used to show more realistic mouth movement in-game when talking over the microphone** The script generates 17 shape keys from the 3 shape keys you specified. It uses the mouth visemes A, OH and CH to generate this output. -*This is still a experimental feature and will be fine tuned over the course of time* +*This is still an experimental feature and will be fine tuned over the course of time* ### Properties @@ -231,13 +231,13 @@ Removes all bone constraints ## Update Plugin -There is a auto updater in the plugin so you don't have to keep checking for new version, this is actually required if you install the plugin for the first time. This is how to check for updates: +There is an auto updater in the plugin so you don't have to keep checking for new version, this is actually required if you install the plugin for the first time. This is how to check for updates: ![](https://i.imgur.com/LbO7Xst.gif) ## Roadmap - MOAR Updates on the armature code - - Texture translation should have a option to rename the filename also + - Texture translation should have an option to rename the filename also ## Feedback diff --git a/__init__.py b/__init__.py index 1dc1eb00..6f6debb9 100644 --- a/__init__.py +++ b/__init__.py @@ -50,15 +50,6 @@ except ImportError: mmd_tools_installed = False -try: - dictionary = bpy.props.EnumProperty( - name='Dictionary', - items=DictionaryEnum.get_dictionary_items, - description='Translate names from Japanese to English using selected dictionary', - ) -except Exception as e: - mmd_tools_installed = False - importlib.reload(tools.viseme) importlib.reload(tools.atlas) importlib.reload(tools.eyetracking) @@ -171,13 +162,13 @@ class ToolPanel(): bpy.types.Scene.wink_right = bpy.props.EnumProperty( name='Blink right', - description='The shape key containing a blink with the right eye', + description='The shape key containing a blink with the right eye. Can be set to "Basis" to leave empty', items=tools.common.get_shapekeys_eye, ) bpy.types.Scene.wink_left = bpy.props.EnumProperty( name='Blink left', - description='The shape key containing a blink with the left eye', + description='The shape key containing a blink with the left eye. Can be set to "Basis" to leave empty', items=tools.common.get_shapekeys_eye, ) @@ -265,22 +256,22 @@ class ToolPanel(): class ArmaturePanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_armature' + bl_idname = 'VIEW3D_PT_armature_v1' bl_label = 'Armature' def draw(self, context): layout = self.layout box = layout.box() row = box.row(align=True) - # row.prop(context.scene, 'remove_zero_weight') - # row = box.row(align=True) - # row.prop(context.scene, 'remove_constraints') - # row = box.row(align=True) + row.prop(context.scene, 'remove_zero_weight') + row = box.row(align=True) + row.prop(context.scene, 'remove_constraints') + row = box.row(align=True) row.operator('armature.fix', icon='BONE_DATA') class EyeTrackingPanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_eye' + bl_idname = 'VIEW3D_PT_eye_v1' bl_label = 'Eye Tracking' bl_options = {'DEFAULT_CLOSED'} @@ -313,7 +304,7 @@ def draw(self, context): class VisemePanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_viseme' + bl_idname = 'VIEW3D_PT_viseme_v1' bl_label = 'Visemes' bl_options = {'DEFAULT_CLOSED'} @@ -335,7 +326,7 @@ def draw(self, context): class TranslationPanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_translation' + bl_idname = 'VIEW3D_PT_translation_v1' bl_label = 'Translation' bl_options = {'DEFAULT_CLOSED'} @@ -352,7 +343,7 @@ def draw(self, context): class BoneRootPanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_boneroot' + bl_idname = 'VIEW3D_PT_boneroot_v1' bl_label = 'Bone Parenting' bl_options = {'DEFAULT_CLOSED'} @@ -367,7 +358,7 @@ def draw(self, context): class AtlasPanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_atlas' + bl_idname = 'VIEW3D_PT_atlas_v1' bl_label = 'Atlas' bl_options = {'DEFAULT_CLOSED'} @@ -390,8 +381,9 @@ def draw(self, context): row = box.row(align=True) row.operator('auto.atlas', icon='TRIA_RIGHT') + class UpdaterPanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_updater' + bl_idname = 'VIEW3D_PT_updater_v1' bl_label = 'Updater' bl_options = {'DEFAULT_CLOSED'} @@ -399,29 +391,27 @@ def draw(self, context): addon_updater_ops.check_for_update_background() addon_updater_ops.update_settings_ui(self, context) + class CreditsPanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_credits' + bl_idname = 'VIEW3D_PT_credits_v1' bl_label = 'Credits' def draw(self, context): layout = self.layout box = layout.box() - row = box.row(align=True) box.label('Cats Blender Plugin') - row = box.row(align=True) box.label('Created by GiveMeAllYourCats for the VRC community <3') - row = box.row(align=True) box.label('Special thanks to: Shotariya, Hotox and Neitri!') + class DependenciesPanel(ToolPanel, bpy.types.Panel): - bl_idname = 'VIEW3D_PT_dependencies' + bl_idname = 'VIEW3D_PT_dependencies_v1' bl_label = 'Missing dependencies!' def draw(self, context): layout = self.layout box = layout.box() - row = box.row(align=True) - box.label('"mmd_tools" is not installed or activated!', icon="ERROR") + box.label('"mmd_tools" is not installed!', icon="ERROR") box.label('Please download the latest version here:') row = box.row(align=True) row.operator('dependencies.download', icon='LOAD_FACTORY') @@ -436,33 +426,33 @@ class DemoPreferences(bpy.types.AddonPreferences): name='Auto-check for Update', description='If enabled, auto-check for updates using an interval', default=False, - ) + ) updater_intrval_months = bpy.props.IntProperty( name='Months', description='Number of months between checking for updates', default=0, min=0 - ) + ) updater_intrval_days = bpy.props.IntProperty( name='Days', description='Number of days between checking for updates', default=7, min=0, - ) + ) updater_intrval_hours = bpy.props.IntProperty( name='Hours', description='Number of hours between checking for updates', default=0, min=0, max=23 - ) + ) updater_intrval_minutes = bpy.props.IntProperty( name='Minutes', description='Number of minutes between checking for updates', default=0, min=0, max=59 - ) + ) def draw(self, context): layout = self.layout @@ -470,6 +460,7 @@ def draw(self, context): # updater draw function addon_updater_ops.update_settings_ui(self, context) + def register(): bpy.utils.register_class(tools.atlas.AutoAtlasButton) bpy.utils.register_class(tools.eyetracking.CreateEyesButton) @@ -496,6 +487,7 @@ def register(): bpy.utils.register_class(DemoPreferences) addon_updater_ops.register(bl_info) + def unregister(): bpy.utils.unregister_class(tools.atlas.AutoAtlasButton) bpy.utils.unregister_class(tools.eyetracking.CreateEyesButton) @@ -509,8 +501,8 @@ def unregister(): bpy.utils.unregister_class(tools.rootbone.RefreshRootButton) bpy.utils.unregister_class(tools.armature.FixArmature) bpy.utils.unregister_class(tools.dependencies.DependenciesButton) - if mmd_tools_installed is False: - bpy.utils.register_class(DependenciesPanel) + if hasattr(bpy.types, "DependenciesPanel"): + bpy.utils.unregister_class(DependenciesPanel) bpy.utils.unregister_class(AtlasPanel) bpy.utils.unregister_class(EyeTrackingPanel) bpy.utils.unregister_class(VisemePanel) @@ -522,5 +514,6 @@ def unregister(): bpy.utils.unregister_class(DemoPreferences) addon_updater_ops.unregister() + if __name__ == '__main__': register() diff --git a/tools/armature.py b/tools/armature.py index c3930919..07a04b78 100644 --- a/tools/armature.py +++ b/tools/armature.py @@ -38,16 +38,6 @@ except ImportError: mmd_tools_installed = False -try: - dictionary = bpy.props.EnumProperty( - name='Dictionary', - items=DictionaryEnum.get_dictionary_items, - description='Translate names from Japanese to English using selected dictionary', - ) -except Exception as e: - mmd_tools_installed = False - - bone_list = ['ControlNode', 'ParentNode', 'Center', 'CenterTip', 'Groove', 'Waist', 'LowerBody2', 'Eyes', 'EyesTip', 'LowerBodyTip', 'UpperBody2Tip', 'GrooveTip', 'NeckTip'] bone_list_with = ['_shadow_', '_dummy_', 'Dummy_', 'WaistCancel', 'LegIKParent', 'LegIK', 'LegIKTip', 'ToeTipIK', @@ -130,6 +120,9 @@ 'UpperBody2': 'Chest', 'Upper body 2': 'Chest', 'Waist upper 2': 'Chest', + 'UpperBody3': 'ChestRemovable', + 'Upper body 3': 'ChestRemovable', + 'Waist upper 3': 'ChestRemovable', 'neck': 'Neck', 'Shoulder_L': 'Left shoulder', 'Shoulder_R': 'Right shoulder', @@ -163,6 +156,13 @@ class FixArmature(bpy.types.Operator): bl_label = 'Fix armature' bl_options = {'REGISTER', 'UNDO'} + if mmd_tools_installed: + dictionary = bpy.props.EnumProperty( + name='Dictionary', + items=DictionaryEnum.get_dictionary_items, + description='Translate names from Japanese to English using selected dictionary', + ) + def execute(self, context): if mmd_tools_installed is False: self.report({'ERROR'}, 'mmd_tools is not installed, this feature is disabled') @@ -208,17 +208,7 @@ def execute(self, context): tools.common.unselect_all() tools.common.select(armature) - try: - dictionary = bpy.props.EnumProperty( - name='Dictionary', - items=DictionaryEnum.get_dictionary_items, - description='Translate names from Japanese to English using selected dictionary', - ) - self.__translator = DictionaryEnum.get_translator(dictionary) - except Exception as e: - self.report({'ERROR'}, 'Failed to load dictionary: %s'%e) - return {'CANCELLED'} - + self.__translator = DictionaryEnum.get_translator(self.dictionary) for bone in armature.data.bones: bone.name = utils.convertNameToLR(bone.name, True) bone.name = self.__translator.translate(bone.name) @@ -230,6 +220,40 @@ def execute(self, context): continue pb.name = value + # TODO Remove UpperChest3 + bpy.ops.object.mode_set(mode='EDIT') + if 'ChestRemovable' in armature.data.edit_bones: + if 'Chest' in armature.data.edit_bones: + if 'Spine' in armature.data.edit_bones: + new_chest = armature.data.edit_bones['ChestRemovable'] + old_chest = armature.data.edit_bones['Chest'] + spine = armature.data.edit_bones['Spine'] + + # Rename chest bones + old_chest.name = 'ChestOld' + new_chest.name = 'Chest' + + # TODO: Delete old_chest bone and add weight painting to spine + # TODO: I tried, but failed :( + + # tools.common.select(armature) + # bpy.ops.object.mode_set(mode='EDIT') + # armature.data.edit_bones.remove(old_chest) + # + # tools.common.unselect_all() + # bpy.ops.object.mode_set(mode='OBJECT') + # tools.common.select(mesh) + # + # pb = mesh.vertex_groups.get(old_chest.name) + # if pb is not None: + # bpy.ops.object.modifier_add(type='VERTEX_WEIGHT_MIX') + # bpy.context.object.modifiers['VertexWeightMix'].vertex_group_a = spine.name + # bpy.context.object.modifiers['VertexWeightMix'].vertex_group_b = old_chest.name + # bpy.context.object.modifiers['VertexWeightMix'].mix_mode = 'ADD' + # bpy.context.object.modifiers['VertexWeightMix'].mix_set = 'B' + # bpy.ops.object.modifier_apply(modifier='VertexWeightMix') + # mesh.vertex_groups.remove(pb) + # Should reparent all bones to be correct for unity mapping and vrc itself bpy.ops.object.mode_set(mode='EDIT') for key, value in bone_list_parenting.items(): @@ -270,6 +294,7 @@ def execute(self, context): delete_bone_constraints() # Hips bone should be fixed as per specification from the SDK code + # TODO: Somehow this doesn't work if bone constrains is ticked bpy.ops.object.mode_set(mode='EDIT') if 'Hips' in armature.data.edit_bones: if 'Left leg' in armature.data.edit_bones: @@ -301,7 +326,7 @@ def execute(self, context): if context.scene.remove_zero_weight: delete_zero_weight() - # At this point, everything should be fixed and now we validate and give errors if need be + # At this point, everything should be fixed and now we validate and give errors if needed # The bone hierachy needs to be validated bpy.ops.object.mode_set(mode='EDIT') @@ -325,6 +350,7 @@ def execute(self, context): self.report({'INFO'}, 'Armature fixed.') return {'FINISHED'} + def check_hierachy(correct_hierachy_array): armature = tools.common.get_armature() error = None @@ -332,13 +358,13 @@ def check_hierachy(correct_hierachy_array): for correct_hierachy in correct_hierachy_array: for item in correct_hierachy: if item in armature.data.edit_bones is False: - error = {'result': False, 'message': item + ' was not found in the hierachy, this will cause problems!'} + error = {'result': False, 'message': item + ' was not found in the hierachy, this will cause problems!'} for index, item in enumerate(correct_hierachy): try: bone = armature.data.edit_bones[item] except: - error = {'result': False, 'message': item + ' bone does not exist, this will cause problems!'} + error = {'result': False, 'message': item + ' bone does not exist, this will cause problems!'} # Make sure checked bones are not connected bone.use_connect = False @@ -356,14 +382,17 @@ def check_hierachy(correct_hierachy_array): try: prevbone = armature.data.edit_bones[correct_hierachy[index - 1]] except KeyError: - error = {'result': False, 'message': correct_hierachy[index - 1] + ' bone does not exist, this will cause problems!'} + error = {'result': False, 'message': correct_hierachy[ + index - 1] + ' bone does not exist, this will cause problems!'} if error is None: if bone.parent is None: - error = {'result': False, 'message': bone.name + ' is not parented at all, this will cause problems!'} + error = {'result': False, + 'message': bone.name + ' is not parented at all, this will cause problems!'} else: if bone.parent.name != prevbone.name: - error = {'result': False, 'message': bone.name + ' is not parented to ' + prevbone.name + ', this will cause problems!'} + error = {'result': False, + 'message': bone.name + ' is not parented to ' + prevbone.name + ', this will cause problems!'} if error is None: return_value = {'result': True} @@ -386,14 +415,14 @@ def delete_zero_weight(): vertex_group_names_used = set() vertex_group_name_to_objects_having_same_named_vertex_group = dict() - for object in armature.children: + for objects in armature.children: vertex_group_id_to_vertex_group_name = dict() - for vertex_group in object.vertex_groups: + for vertex_group in objects.vertex_groups: vertex_group_id_to_vertex_group_name[vertex_group.index] = vertex_group.name if not vertex_group.name in vertex_group_name_to_objects_having_same_named_vertex_group: vertex_group_name_to_objects_having_same_named_vertex_group[vertex_group.name] = set() - vertex_group_name_to_objects_having_same_named_vertex_group[vertex_group.name].add(object) - for vertex in object.data.vertices: + vertex_group_name_to_objects_having_same_named_vertex_group[vertex_group.name].add(objects) + for vertex in objects.data.vertices: for group in vertex.groups: if group.weight > 0: vertex_group_names_used.add(vertex_group_id_to_vertex_group_name[group.group]) @@ -403,10 +432,12 @@ def delete_zero_weight(): for bone_name in not_used_bone_names: armature.data.edit_bones.remove(bone_name_to_edit_bone[bone_name]) # delete bone if bone_name in vertex_group_name_to_objects_having_same_named_vertex_group: - for objects in vertex_group_name_to_objects_having_same_named_vertex_group[bone_name]: # delete vertex groups - vertex_group = object.vertex_groups.get(bone_name) + for objects in vertex_group_name_to_objects_having_same_named_vertex_group[ + bone_name]: # delete vertex groups + vertex_group = objects.vertex_groups.get(bone_name) if vertex_group is not None: - object.vertex_groups.remove(vertex_group) + objects.vertex_groups.remove(vertex_group) + # TODO: Some bone constraints do not get deleted (console output): # Dependency cycle detected: @@ -431,4 +462,4 @@ def delete_bone_constraints(): if len(bone.constraints) > 0: for constraint in bone.constraints: bone.constraints.remove(constraint) - bpy.ops.constraint.delete() + # bpy.ops.constraint.delete() # TODO: This line was broken, fixed for release diff --git a/tools/atlas.py b/tools/atlas.py index a8df25fd..9677551f 100644 --- a/tools/atlas.py +++ b/tools/atlas.py @@ -32,7 +32,6 @@ class AutoAtlasButton(bpy.types.Operator): bl_idname = 'auto.atlas' bl_label = 'Create atlas' - bl_options = {'REGISTER', 'UNDO'} def generateRandom(self, prefix='', suffix=''): diff --git a/tools/eyetracking.py b/tools/eyetracking.py index d897efce..cf1f1087 100644 --- a/tools/eyetracking.py +++ b/tools/eyetracking.py @@ -30,7 +30,6 @@ class CreateEyesButton(bpy.types.Operator): bl_idname = 'create.eyes' bl_label = 'Create eye tracking' - bl_options = {'REGISTER', 'UNDO'} def vertex_group_exists(self, mesh_name, bone_name): @@ -92,9 +91,9 @@ def copy_shape_key(self, target_mesh, shapekey_name, rename_to, new_index, rando # Re-adjust index position position_correct = False while position_correct is False: - bpy.ops.object.shape_key_move(type='DOWN') - - if mesh.active_shape_key_index == new_index: + if mesh.active_shape_key_index > new_index: + bpy.ops.object.shape_key_move(type='UP') + else: position_correct = True # reset shape values back to 0 diff --git a/tools/rootbone.py b/tools/rootbone.py index 92103c4a..77be66f2 100644 --- a/tools/rootbone.py +++ b/tools/rootbone.py @@ -31,7 +31,6 @@ class RootButton(bpy.types.Operator): bl_idname = 'root.function' bl_label = 'Parent bones' - bl_options = {'REGISTER', 'UNDO'} def execute(self, context): @@ -75,7 +74,6 @@ def execute(self, context): class RefreshRootButton(bpy.types.Operator): bl_idname = 'refresh.root' bl_label = 'Refresh list' - bl_options = {'REGISTER', 'UNDO'} def execute(self, context): diff --git a/tools/translate.py b/tools/translate.py index 8331490a..cc0fb881 100644 --- a/tools/translate.py +++ b/tools/translate.py @@ -36,20 +36,10 @@ except ImportError: mmd_tools_installed = False -try: - dictionary = bpy.props.EnumProperty( - name='Dictionary', - items=DictionaryEnum.get_dictionary_items, - description='Translate names from Japanese to English using selected dictionary', - ) -except Exception as e: - mmd_tools_installed = False - class TranslateMeshesButton(bpy.types.Operator): bl_idname = 'translate.meshes' bl_label = 'Meshes' - bl_options = {'REGISTER', 'UNDO'} def execute(self, context): @@ -76,13 +66,12 @@ def execute(self, context): self.report({'INFO'}, 'Translated all meshes') - return{'FINISHED'} + return {'FINISHED'} class TranslateTexturesButton(bpy.types.Operator): bl_idname = 'translate.textures' bl_label = 'Textures' - bl_options = {'REGISTER', 'UNDO'} def execute(self, context): @@ -113,13 +102,12 @@ def execute(self, context): i += 1 self.report({'INFO'}, 'Translated all textures') - return{'FINISHED'} + return {'FINISHED'} class TranslateMaterialsButton(bpy.types.Operator): bl_idname = 'translate.materials' bl_label = 'Materials' - bl_options = {'REGISTER', 'UNDO'} def execute(self, context): @@ -148,7 +136,7 @@ def execute(self, context): i += 1 self.report({'INFO'}, 'Translated all materials') - return{'FINISHED'} + return {'FINISHED'} class TranslateBonesButton(bpy.types.Operator): @@ -156,6 +144,13 @@ class TranslateBonesButton(bpy.types.Operator): bl_label = 'Bones' bl_options = {'REGISTER', 'UNDO'} + if mmd_tools_installed: + dictionary = bpy.props.EnumProperty( + name='Dictionary', + items=DictionaryEnum.get_dictionary_items, + description='Translate names from Japanese to English using selected dictionary', + ) + def execute(self, context): if mmd_tools_installed is False: self.report({'ERROR'}, 'mmd_tools is not installed, this feature is disabled') @@ -164,17 +159,7 @@ def execute(self, context): tools.common.unhide_all() armature = tools.common.get_armature().data - # first mmd translate - try: - dictionary = bpy.props.EnumProperty( - name='Dictionary', - items=DictionaryEnum.get_dictionary_items, - description='Translate names from Japanese to English using selected dictionary', - ) - self.__translator = DictionaryEnum.get_translator(dictionary) - except Exception as e: - self.report({'ERROR'}, 'Failed to load dictionary: %s'%e) - return {'CANCELLED'} + self.__translator = DictionaryEnum.get_translator(self.dictionary) for bone in armature.bones: bone.name = utils.convertNameToLR(bone.name, True) @@ -197,13 +182,12 @@ def execute(self, context): bone.name = translated[i] self.report({'INFO'}, 'Translated all bones') - return{'FINISHED'} + return {'FINISHED'} class TranslateShapekeyButton(bpy.types.Operator): bl_idname = 'translate.shapekeys' bl_label = 'Shape keys' - bl_options = {'REGISTER', 'UNDO'} def execute(self, context): @@ -233,4 +217,4 @@ def execute(self, context): self.report({'INFO'}, 'Translated all shape keys') - return{'FINISHED'} + return {'FINISHED'} diff --git a/tools/viseme.py b/tools/viseme.py index 7de961d2..9791277c 100644 --- a/tools/viseme.py +++ b/tools/viseme.py @@ -29,10 +29,10 @@ from collections import OrderedDict + class AutoVisemeButton(bpy.types.Operator): bl_idname = 'auto.viseme' bl_label = 'Create visemes' - bl_options = {'REGISTER', 'UNDO'} def mix_shapekey(self, target_mesh, shapekey_data, new_index, rename_to, intensity): @@ -68,9 +68,9 @@ def mix_shapekey(self, target_mesh, shapekey_data, new_index, rename_to, intensi # Re-adjust index position position_correct = False while position_correct is False: - bpy.ops.object.shape_key_move(type='DOWN') - - if mesh.active_shape_key_index == new_index: + if mesh.active_shape_key_index > new_index: + bpy.ops.object.shape_key_move(type='UP') + else: position_correct = True def execute(self, context): @@ -182,7 +182,8 @@ def execute(self, context): # Remove any existing viseme shape key for key in shapekey_data: - for index, shapekey in enumerate(bpy.data.objects[context.scene.mesh_name_viseme].data.shape_keys.key_blocks): + for index, shapekey in enumerate( + bpy.data.objects[context.scene.mesh_name_viseme].data.shape_keys.key_blocks): obj = shapekey_data[key] if shapekey.name == key: bpy.context.active_object.active_shape_key_index = index @@ -191,7 +192,8 @@ def execute(self, context): # Add the shape keys for key in shapekey_data: obj = shapekey_data[key] - self.mix_shapekey(context.scene.mesh_name_viseme, obj['mix'], obj['index'], key, context.scene.shape_intensity) + self.mix_shapekey(context.scene.mesh_name_viseme, obj['mix'], obj['index'], key, + context.scene.shape_intensity) # Set shapekey index back to 0 bpy.context.object.active_shape_key_index = 0 @@ -205,4 +207,4 @@ def execute(self, context): self.report({'INFO'}, 'Created mouth visemes!') - return{'FINISHED'} + return {'FINISHED'}