Issue on animations with rigify and fbx exporter. Halp!!

Hey guys, so I rigged my character with the rigify add-on and animated it, and it seems fine on the .blend file, but when I export it, the arms get messed up, tested it on unreal engine 4 and also tried importing to blender and same issue happens.

Here is the test animation as intended on blender

And here is how it appears when I export it

On other animations it happens too, and always everything is fine but the arms. I’m using the IK config and not the FK.
Not only the arms get snapped some times through the animation, but also the rotations are a bit odd even when not snapping. Everything else on the rig seems fine on other animations.

Notece that the shaky square thing is the VIS-hand.ik and it is actually animating as intended, but somehow the mesh doesn’t go along.

Can anybody help me pretty pleeease? :slight_smile: Thanks!

You should post a blend file.

The only clean and easy way to ensure that you export nice clean rig data into your game is to have a separate, simple version of the rig that inherits the animations. Some call it a shadow rig, i call it the bind rig because it’s the rig you bind the mesh to. Creating a shadow rig isn’t too hard because you can use the meta-rig so long as you alter it as needed. For instance, the meta rig lacks a root bone and you’ll need to subdivide some bones to have ulna like twists. The other thing you want in this case is to transfer weights from your rig to the bind rig, so the names of the bones need to be the same. It’s simply a matter of adding the prefix: ‘DEF-’ to the start of every bone, but fear not because there’s a really good multirename addon by DOM107. So make a copy of your meta-rig, add a root bone, subdivde the fore arms (for ulna twist) and call this new rig ‘Bind_Rig’. Then grab this code, paste it in notepad, save it as 'object_multi_rename.py in your ADDONS folder so you can fix the names of the bones to be for example: ‘DEF-spine.001’ instead of ‘spine.001’.

(the addon code is so long i have to put it in the next reply)

Then go to the modifier tab of your mesh, change the rig to your new one and that will transfer weights over to the new rig. From there you just need to go to each joint and add a copy transform constraints to each bone of the same name in the original generated rig. That will make the bind rig follow your controlling rig.

When you’re ready to export, ensure that you are baking the animation.

Here’s the rename addon:

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

bl_info = {
    "name": "MultiRename_dom",
    "author": "dom107",
    "version": (1,0),
    "blender": (2, 6, 3),
    "location": "View3D > Toolbar",
    "description": "Provides a find & replace function for renaming multiple objects and bones",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}

# Adapted from a script by Maximilian Eham
# as found at http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Object/Object_Multi_Rename on 2012/08/16

import re, fnmatch

import bpy
from bpy.props import *
from bpy.app.handlers import persistent

def draw_rename_panel(self, context):
    layout = self.layout
    sce = context.scene
    mode = context.mode

    col = layout.column(align=True)
    col.operator("object.multi_object_rename_dom")

    layout.prop(sce, "erase_string_panel")
    layout.prop(sce, "replace_panel")
    layout.prop(sce, "trim_panel_start")
    layout.prop(sce, "trim_panel_end")
    layout.prop(sce, "prefix_panel")
    layout.prop(sce, "suffix_panel")
    layout.prop(sce, "rename_selected_panel")
    if context.mode == 'OBJECT':
        layout.prop(sce, "rename_object_data_panel")

    col = layout.column(align=True)
    col.operator("object.multi_object_rename_dom_reset")


class OBJECT_RENAME_tools_multiobjectrename(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_context = "objectmode"
    bl_label = "Objects Renamer"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        return (context.object is not None)

    def draw_header(self, context):
        layout = self.layout
 
    def draw(self, context):
        draw_rename_panel(self, context)
 
                
class OBJECT_RENAME_tools_multibonerename(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_context = "armature_edit"
    bl_label = "Objects Renamer"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        return (context.object is not None)

    def draw_header(self, context):
        layout = self.layout
 
    def draw(self, context):
        draw_rename_panel(self, context)


class OBJECT_RENAME_tools_multiposebonerename(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_context = "posemode"
    bl_label = "Objects Renamer"
    bl_options = {'DEFAULT_CLOSED'}

    @classmethod
    def poll(cls, context):
        return (context.object is not None)

    def draw_header(self, context):
        layout = self.layout
 
    def draw(self, context):
        draw_rename_panel(self, context)

        
def doSingleObjectRename(object, eraseString, replacementString, trimStart, trimEnd, newPrefix, newSuffix, rename_object_data, mode):
    temp = object.name[trimStart:]
    if trimEnd > 0:
        temp = temp[:-trimEnd]
    temp = re.sub(eraseString, replacementString, temp)
    temp = newPrefix + temp + newSuffix
    object.name = temp[:]
    if mode == 'OBJECT':
        if object.data:
            if rename_object_data:
                object.data.name = object.name
        

def doMultiObjectRename(self, context, eraseString, replacementString, trimStart, trimEnd, newPrefix, newSuffix, rename_selected, rename_object_data):
    eraseString = fnmatch.translate(eraseString)[:-7] #convert shell wildcards to regular expression and trunc the last 7 chars (why do they get generated by fnmatch???)

    num_objects = 0
    if context.mode == 'EDIT_ARMATURE':
        if rename_selected:
            bones_list = context.selected_bones
        else:
            bones_list = context.object.data.bones
        for bone in bones_list:
            doSingleObjectRename(bone, eraseString, replacementString, trimStart, trimEnd, newPrefix, newSuffix, rename_object_data, context.mode)
            num_objects += 1

    elif context.mode == 'OBJECT':
        if rename_selected:
            objects_list = context.selected_objects
        else:
            objects_list = context.scene.objects
        for obj in objects_list:
            doSingleObjectRename(obj, eraseString, replacementString, trimStart, trimEnd, newPrefix, newSuffix, rename_object_data, context.mode)
            num_objects += 1
        
    elif context.mode == 'POSE':
        if rename_selected:
            bones_list = context.selected_pose_bones
        else:
            bones_list = context.object.pose.bones
        for bone in bones_list:
            doSingleObjectRename(bone, eraseString, replacementString, trimStart, trimEnd, newPrefix, newSuffix, rename_object_data, context.mode)
            num_objects += 1

    if num_objects == 0:
        self.report({'INFO'}, "No object processed")
    else:
        self.report({'INFO'}, str(num_objects) + " objects processed")
            

class MultiObjectRename(bpy.types.Operator):
    '''Find & Replace-functionality for names'''
    bl_idname = "object.multi_object_rename_dom"
    bl_label = "Rename"
    bl_description = "Find & Replace functionality for object and bone names"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        sce = context.scene
        
        something_todo = True
        if context.mode == 'OBJECT' and sce.rename_object_data_panel:
            something_todo = True
        else:
            if sce.erase_string_panel == "" and sce.replace_panel == "" and \
                sce.trim_panel_start == 0 and sce.trim_panel_end == 0 and \
                sce.prefix_panel == "" and sce.suffix_panel == "":
                    something_todo = False
                    self.report({'INFO'}, "Nothing to process")
            
        if something_todo:
            doMultiObjectRename(self, context, sce.erase_string_panel, sce.replace_panel,
                sce.trim_panel_start, sce.trim_panel_end,
                sce.prefix_panel, sce.suffix_panel, sce.rename_selected_panel, sce.rename_object_data_panel)
        return {'FINISHED'}


class OBJECT_RENAME_reset_button(bpy.types.Operator):
    bl_label = "Reset fields"
    bl_idname = "object.multi_object_rename_dom_reset"
    bl_description = "Reset input fields"
    bl_options = {'REGISTER'}

    def invoke(self, context, event):
        sce = context.scene

        sce.erase_string_panel = ""
        sce.replace_panel = ""
        sce.trim_panel_start = 0
        sce.trim_panel_end = 0
        sce.prefix_panel = ""
        sce.suffix_panel = ""
        return {'FINISHED'}

        
# http://www.blender.org/documentation/blender_python_api_2_60_6/bpy.props.html
def register():
    bpy.utils.register_module(__name__)

    bpy.types.Scene.erase_string_panel = bpy.props.StringProperty(name="Find String",
        description = "The string to look for in the object names (shell wildcards supported)",
        )
    
    bpy.types.Scene.replace_panel = bpy.props.StringProperty(name = "Replace with",
        description = "The replacement for the string given above",
        )
    
    bpy.types.Scene.trim_panel_start = bpy.props.IntProperty(name="Trim Start:",
        description = "Erases the specified number of characters from the beginning of the object names",
        min = 0,
        max = 50,
        default = 0,
        )

    bpy.types.Scene.trim_panel_end = bpy.props.IntProperty(name="Trim End:",
        description = "Erases the specified number of characters from the end of the object names",
        min = 0,
        max = 50,
        default = 0,
        )

    bpy.types.Scene.prefix_panel = bpy.props.StringProperty(name = "Prefix with",
        description = "Adds the specified characters to the beginning of the object names",
        )

    bpy.types.Scene.suffix_panel = bpy.props.StringProperty(name = "Suffix with",
        description = "Adds the specified characters to the end of the object names",
        )

    bpy.types.Scene.rename_selected_panel = bpy.props.BoolProperty(
        name="Selected objects",
        description="If checked, renaming will only affect selected objects",
        default=0)

    bpy.types.Scene.rename_object_data_panel = bpy.props.BoolProperty(
        name="Object data",
        description="If checked in object mode, the object data gets the object name whether a object name change is done or not",
        default=0)
    
    pass

def unregister():
    bpy.utils.unregister_module(__name__)

    # Remove properties
    del bpy.types.Scene.erase_string_panel
    del bpy.types.Scene.replace_panel
    del bpy.types.Scene.trim_panel_start
    del bpy.types.Scene.trim_panel_end
    del bpy.types.Scene.prefix_panel
    del bpy.types.Scene.suffix_panel
    del bpy.types.Scene.rename_selected_panel
    del bpy.types.Scene.rename_object_data_panel

    pass


if __name__ == '__main__':
    register()

if __name__ == "__main__":
    register()

Also beware, rigify has stretch by default but you should turn it off for games, because if you want stretch to work correctly, you need to parent parts of the stretchy limbs to the root or else you get double transformations down the chains. If that works, it’s ok but this means that rag-doll physics and procedural animations fail.

Thanks Simon for this script, I saved it and named it bindify :stuck_out_tongue:

For Unity there’s assets that can help getting Rigified meshes to the game engine that works.

You mentioned UE4 and I recently switched to it, and for now I create my rigs from scratch but in the future it would be so awesome if there was a script or workflow on marketplace or even from the Epic crew to get it to work. You might have seen this video from unrealengines youtube channel but Kevin even mentions rigify albeit briefly.

[video]https://youtu.be/Ayp1lof0RJU?t=15m23s[/video]

so they are aware of rigify at least.

This video and script seems pretty interesting also.