283 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			283 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import bpy
							 | 
						||
| 
								 | 
							
								import textwrap
							 | 
						||
| 
								 | 
							
								from bpy.props import *
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import lnx.props_ui
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if lnx.is_reload(__name__):
							 | 
						||
| 
								 | 
							
								    lnx.props_ui = lnx.reload_module(lnx.props_ui)
							 | 
						||
| 
								 | 
							
								else:
							 | 
						||
| 
								 | 
							
								    lnx.enable_reload(__name__)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LnxRetargetActions(bpy.types.Operator):
							 | 
						||
| 
								 | 
							
								    bl_idname = 'lnx.retarget_action'
							 | 
						||
| 
								 | 
							
								    bl_label = 'Retarget action data'
							 | 
						||
| 
								 | 
							
								    bl_description = 'Retargets action data from one bone to another for root motion'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def poll(cls, context):
							 | 
						||
| 
								 | 
							
								        obj = context.object
							 | 
						||
| 
								 | 
							
								        if obj.type != 'ARMATURE':
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        if obj.mode != 'OBJECT':
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        wrd = bpy.data.worlds['Lnx']
							 | 
						||
| 
								 | 
							
								        if wrd.lnx_retarget_from == wrd.lnx_retarget_to:
							 | 
						||
| 
								 | 
							
								            return False
							 | 
						||
| 
								 | 
							
								        if context.space_data.type == 'DOPESHEET_EDITOR':
							 | 
						||
| 
								 | 
							
								            ds_mode = context.space_data.mode
							 | 
						||
| 
								 | 
							
								            if ds_mode in {'DOPESHEET', 'ACTION'}:
							 | 
						||
| 
								 | 
							
								                return bool(context.active_action)
							 | 
						||
| 
								 | 
							
								        if context.space_data.type == 'NLA_EDITOR':
							 | 
						||
| 
								 | 
							
								            if context.active_nla_strip:
							 | 
						||
| 
								 | 
							
								                if context.active_nla_strip.action:
							 | 
						||
| 
								 | 
							
								                    return True
							 | 
						||
| 
								 | 
							
								        return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def execute(self, context):
							 | 
						||
| 
								 | 
							
								        if context.space_data.type == 'DOPESHEET_EDITOR':
							 | 
						||
| 
								 | 
							
								            ds_mode = context.space_data.mode
							 | 
						||
| 
								 | 
							
								            if ds_mode in {'DOPESHEET', 'ACTION'}:
							 | 
						||
| 
								 | 
							
								                self.action = context.active_action
							 | 
						||
| 
								 | 
							
								        if context.space_data.type == 'NLA_EDITOR':
							 | 
						||
| 
								 | 
							
								            self.action = context.active_nla_strip.action
							 | 
						||
| 
								 | 
							
								        wrd = bpy.data.worlds['Lnx']
							 | 
						||
| 
								 | 
							
								        obj = context.object
							 | 
						||
| 
								 | 
							
								        # Create helper object
							 | 
						||
| 
								 | 
							
								        helper1 = bpy.data.objects.new(name="lnx_helper_1", object_data=None)
							 | 
						||
| 
								 | 
							
								        helper1.rotation_mode = 'QUATERNION'
							 | 
						||
| 
								 | 
							
								        helper2 = bpy.data.objects.new(name="lnx_helper_2", object_data=None)
							 | 
						||
| 
								 | 
							
								        helper2.rotation_mode = 'QUATERNION'
							 | 
						||
| 
								 | 
							
								        # Get bones
							 | 
						||
| 
								 | 
							
								        pose = obj.pose
							 | 
						||
| 
								 | 
							
								        from_bone = pose.bones.get(wrd.lnx_retarget_from)
							 | 
						||
| 
								 | 
							
								        to_bone = pose.bones.get(wrd.lnx_retarget_to)
							 | 
						||
| 
								 | 
							
								        if from_bone is None or to_bone is None:
							 | 
						||
| 
								 | 
							
								            self.report({'ERROR'}, "Actions not retargeted. Armature or bones do not exist.")
							 | 
						||
| 
								 | 
							
								            return{'CANCELLED'}
							 | 
						||
| 
								 | 
							
								        # Copy selected transform bake to helper1
							 | 
						||
| 
								 | 
							
								        helper1_scl = helper1.constraints.new('COPY_SCALE')
							 | 
						||
| 
								 | 
							
								        helper1_scl.target = obj
							 | 
						||
| 
								 | 
							
								        helper1_scl.subtarget = wrd.lnx_retarget_from
							 | 
						||
| 
								 | 
							
								        helper1_rot = helper1.constraints.new('COPY_ROTATION')
							 | 
						||
| 
								 | 
							
								        helper1_rot.target = obj
							 | 
						||
| 
								 | 
							
								        helper1_rot.subtarget = wrd.lnx_retarget_from
							 | 
						||
| 
								 | 
							
								        helper1_rot.use_x = wrd.lnx_action_retarget_rot_x
							 | 
						||
| 
								 | 
							
								        helper1_rot.use_y = wrd.lnx_action_retarget_rot_y
							 | 
						||
| 
								 | 
							
								        helper1_rot.use_z = wrd.lnx_action_retarget_rot_z
							 | 
						||
| 
								 | 
							
								        helper1_loc = helper1.constraints.new('COPY_LOCATION')
							 | 
						||
| 
								 | 
							
								        helper1_loc.target = obj
							 | 
						||
| 
								 | 
							
								        helper1_loc.subtarget = wrd.lnx_retarget_from
							 | 
						||
| 
								 | 
							
								        helper1_loc.use_x = wrd.lnx_action_retarget_pos_x
							 | 
						||
| 
								 | 
							
								        helper1_loc.use_y = wrd.lnx_action_retarget_pos_y
							 | 
						||
| 
								 | 
							
								        helper1_loc.use_z = wrd.lnx_action_retarget_pos_z
							 | 
						||
| 
								 | 
							
								        # Remove selected transform and bake to helper2
							 | 
						||
| 
								 | 
							
								        helper2_scl = helper2.constraints.new('COPY_SCALE')
							 | 
						||
| 
								 | 
							
								        helper2_scl.target = obj
							 | 
						||
| 
								 | 
							
								        helper2_scl.subtarget = wrd.lnx_retarget_from
							 | 
						||
| 
								 | 
							
								        helper2_rot = helper2.constraints.new('COPY_ROTATION')
							 | 
						||
| 
								 | 
							
								        helper2_rot.target = obj
							 | 
						||
| 
								 | 
							
								        helper2_rot.subtarget = wrd.lnx_retarget_from
							 | 
						||
| 
								 | 
							
								        helper2_rot.use_x = not wrd.lnx_action_retarget_rot_x
							 | 
						||
| 
								 | 
							
								        helper2_rot.use_y = not wrd.lnx_action_retarget_rot_y
							 | 
						||
| 
								 | 
							
								        helper2_rot.use_z = not wrd.lnx_action_retarget_rot_z
							 | 
						||
| 
								 | 
							
								        helper2_loc = helper2.constraints.new('COPY_LOCATION')
							 | 
						||
| 
								 | 
							
								        helper2_loc.target = obj
							 | 
						||
| 
								 | 
							
								        helper2_loc.subtarget = wrd.lnx_retarget_from
							 | 
						||
| 
								 | 
							
								        helper2_loc.use_x = not wrd.lnx_action_retarget_pos_x
							 | 
						||
| 
								 | 
							
								        helper2_loc.use_y = not wrd.lnx_action_retarget_pos_y
							 | 
						||
| 
								 | 
							
								        helper2_loc.use_z = not wrd.lnx_action_retarget_pos_z
							 | 
						||
| 
								 | 
							
								        # Select helper only
							 | 
						||
| 
								 | 
							
								        bpy.ops.object.select_all(action='DESELECT')
							 | 
						||
| 
								 | 
							
								        bpy.context.scene.collection.objects.link(helper1)
							 | 
						||
| 
								 | 
							
								        bpy.context.scene.collection.objects.link(helper2)
							 | 
						||
| 
								 | 
							
								        helper1.select_set(True)
							 | 
						||
| 
								 | 
							
								        helper2.select_set(True)
							 | 
						||
| 
								 | 
							
								        bpy.context.view_layer.objects.active = helper1
							 | 
						||
| 
								 | 
							
								        obj.animation_data.action = self.action
							 | 
						||
| 
								 | 
							
								        framerange = self.action.frame_range
							 | 
						||
| 
								 | 
							
								         # Set helper locations
							 | 
						||
| 
								 | 
							
								        bpy.context.scene.frame_set(1)
							 | 
						||
| 
								 | 
							
								        world_loc = obj.matrix_world @ from_bone.head
							 | 
						||
| 
								 | 
							
								        helper1.location = world_loc
							 | 
						||
| 
								 | 
							
								        helper2.location = world_loc
							 | 
						||
| 
								 | 
							
								        # Bake to helper
							 | 
						||
| 
								 | 
							
								        bpy.ops.nla.bake(frame_start=int(framerange[0]), frame_end=int(framerange[1]), step=1, only_selected=True, visual_keying=True,
							 | 
						||
| 
								 | 
							
								                         clear_constraints=True, clean_curves=True, clear_parents=False, use_current_action=False, bake_types={'OBJECT'})
							 | 
						||
| 
								 | 
							
								        # Copy transform to new root bone
							 | 
						||
| 
								 | 
							
								        copy_transform1 = to_bone.constraints.new('COPY_TRANSFORMS')
							 | 
						||
| 
								 | 
							
								        copy_transform1.target = helper1
							 | 
						||
| 
								 | 
							
								        # Copy transform from old root bone
							 | 
						||
| 
								 | 
							
								        copy_transform2 = from_bone.constraints.new('COPY_TRANSFORMS')
							 | 
						||
| 
								 | 
							
								        copy_transform2.target = helper2
							 | 
						||
| 
								 | 
							
								        # Select from and to bones only
							 | 
						||
| 
								 | 
							
								        bpy.ops.object.select_all(action='DESELECT')
							 | 
						||
| 
								 | 
							
								        obj.select_set(True)
							 | 
						||
| 
								 | 
							
								        bpy.context.view_layer.objects.active = obj
							 | 
						||
| 
								 | 
							
								        for bone in obj.data.bones:
							 | 
						||
| 
								 | 
							
								            bone.select = False
							 | 
						||
| 
								 | 
							
								        to_bone.bone.select = True
							 | 
						||
| 
								 | 
							
								        from_bone.bone.select = True
							 | 
						||
| 
								 | 
							
								        obj.data.bones.active = to_bone.bone
							 | 
						||
| 
								 | 
							
								        framerange = self.action.frame_range
							 | 
						||
| 
								 | 
							
								        obj.animation_data.action = self.action
							 | 
						||
| 
								 | 
							
								        overwrite = wrd.lnx_retarget_overwrite
							 | 
						||
| 
								 | 
							
								        if not overwrite:
							 | 
						||
| 
								 | 
							
								            new_action = self.action.copy()
							 | 
						||
| 
								 | 
							
								            new_action.name = self.action.name + '_retarget'
							 | 
						||
| 
								 | 
							
								            obj.animation_data.action = new_action
							 | 
						||
| 
								 | 
							
								        # Bake to from and to bones
							 | 
						||
| 
								 | 
							
								        bpy.ops.nla.bake(frame_start=int(framerange[0]), frame_end=int(framerange[1]), step=1, only_selected=True, visual_keying=True,
							 | 
						||
| 
								 | 
							
								                         clear_constraints=True, clean_curves=True, clear_parents=False, use_current_action=True, bake_types={'POSE'})
							 | 
						||
| 
								 | 
							
								        # Clean up
							 | 
						||
| 
								 | 
							
								        bpy.data.actions.remove(helper1.animation_data.action)
							 | 
						||
| 
								 | 
							
								        bpy.data.actions.remove(helper2.animation_data.action)
							 | 
						||
| 
								 | 
							
								        bpy.data.objects.remove(helper1)
							 | 
						||
| 
								 | 
							
								        bpy.data.objects.remove(helper2)
							 | 
						||
| 
								 | 
							
								        self.report({'INFO'}, "Action retargeted successfully")
							 | 
						||
| 
								 | 
							
								        return{'FINISHED'}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LnxDrawRetargetPanel:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def check_bone(cls, armature, bone):
							 | 
						||
| 
								 | 
							
								        if bone is not None:
							 | 
						||
| 
								 | 
							
								            if armature is not None:
							 | 
						||
| 
								 | 
							
								                return bone in armature.bones
							 | 
						||
| 
								 | 
							
								        return False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod 
							 | 
						||
| 
								 | 
							
								    def draw(cls, context, action, layout):
							 | 
						||
| 
								 | 
							
								        layout.label(text='Action: ' + action.name)
							 | 
						||
| 
								 | 
							
								        wrd = bpy.data.worlds['Lnx']
							 | 
						||
| 
								 | 
							
								        # Armature Object
							 | 
						||
| 
								 | 
							
								        layout.prop(wrd, 'lnx_retarget_lnxature')
							 | 
						||
| 
								 | 
							
								        con = False
							 | 
						||
| 
								 | 
							
								        # From, To bones
							 | 
						||
| 
								 | 
							
								        if wrd.lnx_retarget_lnxature:
							 | 
						||
| 
								 | 
							
								            if wrd.lnx_retarget_lnxature.type == 'ARMATURE':
							 | 
						||
| 
								 | 
							
								                con = True
							 | 
						||
| 
								 | 
							
								                sub = layout.row(align=True)
							 | 
						||
| 
								 | 
							
								                sub.label(text="From Bone:")
							 | 
						||
| 
								 | 
							
								                sub.prop_search(wrd, 'lnx_retarget_from', 
							 | 
						||
| 
								 | 
							
								                                   wrd.lnx_retarget_lnxature.data, "bones", text="")
							 | 
						||
| 
								 | 
							
								                con = cls.check_bone(wrd.lnx_retarget_lnxature.data, wrd.lnx_retarget_from)
							 | 
						||
| 
								 | 
							
								                sub = layout.row(align=True)
							 | 
						||
| 
								 | 
							
								                sub.label(text="To Bone:")
							 | 
						||
| 
								 | 
							
								                sub.prop_search(wrd,'lnx_retarget_to',
							 | 
						||
| 
								 | 
							
								                                   wrd.lnx_retarget_lnxature.data, "bones", text="")
							 | 
						||
| 
								 | 
							
								                con = cls.check_bone(wrd.lnx_retarget_lnxature.data, wrd.lnx_retarget_to)
							 | 
						||
| 
								 | 
							
								        # Check if bones exist
							 | 
						||
| 
								 | 
							
								        condition = con and bool(wrd.lnx_retarget_from) and bool(wrd.lnx_retarget_to)
							 | 
						||
| 
								 | 
							
								        section = layout.column()
							 | 
						||
| 
								 | 
							
								        section.enabled = condition
							 | 
						||
| 
								 | 
							
								        # Position
							 | 
						||
| 
								 | 
							
								        sub = section.row(align=True)
							 | 
						||
| 
								 | 
							
								        sub.label(text="Position:")
							 | 
						||
| 
								 | 
							
								        sub.prop(wrd, 'lnx_action_retarget_pos_x', text='X', toggle=True)
							 | 
						||
| 
								 | 
							
								        sub.prop(wrd, 'lnx_action_retarget_pos_y', text='Y', toggle=True)
							 | 
						||
| 
								 | 
							
								        sub.prop(wrd, 'lnx_action_retarget_pos_z', text='Z', toggle=True)
							 | 
						||
| 
								 | 
							
								        # Rotation
							 | 
						||
| 
								 | 
							
								        sub = section.row(align=True)
							 | 
						||
| 
								 | 
							
								        sub.label(text="Rotation:")
							 | 
						||
| 
								 | 
							
								        sub.prop(wrd, 'lnx_action_retarget_rot_x', text='X', toggle=True)
							 | 
						||
| 
								 | 
							
								        sub.prop(wrd, 'lnx_action_retarget_rot_y', text='Y', toggle=True)
							 | 
						||
| 
								 | 
							
								        sub.prop(wrd, 'lnx_action_retarget_rot_z', text='Z', toggle=True)
							 | 
						||
| 
								 | 
							
								        # Retarget Operator
							 | 
						||
| 
								 | 
							
								        sub = section.row()
							 | 
						||
| 
								 | 
							
								        sub.prop(wrd, 'lnx_retarget_overwrite')
							 | 
						||
| 
								 | 
							
								        box = section.box()
							 | 
						||
| 
								 | 
							
								        text='Retargeting will apply all constraints for the selected objects and then baked.'
							 | 
						||
| 
								 | 
							
								        'The constraints are removed after retarget.'
							 | 
						||
| 
								 | 
							
								        textwrap_width = int(bpy.context.region.width//2)
							 | 
						||
| 
								 | 
							
								        col = lnx.props_ui.draw_multiline_with_icon(box, textwrap_width, 'ERROR', text)
							 | 
						||
| 
								 | 
							
								        sub = section.row(align=True)
							 | 
						||
| 
								 | 
							
								        sub.operator('lnx.retarget_action', text='Retarget', icon='FILE_REFRESH')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LNX_PT_DSRootMotionRetargetPanel(bpy.types.Panel):
							 | 
						||
| 
								 | 
							
								    bl_label = 'Leenkx Root Motion Retarget'
							 | 
						||
| 
								 | 
							
								    bl_idname = 'LNX_PT_DSRootMotionRetargetPanel'
							 | 
						||
| 
								 | 
							
								    bl_space_type = 'DOPESHEET_EDITOR'
							 | 
						||
| 
								 | 
							
								    bl_region_type = 'UI'
							 | 
						||
| 
								 | 
							
								    bl_context = 'data'
							 | 
						||
| 
								 | 
							
								    bl_category = 'Leenkx'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def poll(cls, context):
							 | 
						||
| 
								 | 
							
								        ds_mode = context.space_data.mode
							 | 
						||
| 
								 | 
							
								        if ds_mode in {'DOPESHEET', 'ACTION'}:
							 | 
						||
| 
								 | 
							
								            return bool(context.active_action)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def draw(self, context):
							 | 
						||
| 
								 | 
							
								        LnxDrawRetargetPanel.draw(context, context.active_action, self.layout)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LNX_PT_NLARootMotionRetargetPanel(bpy.types.Panel):
							 | 
						||
| 
								 | 
							
								    bl_label = 'Leenkx Root Motion Retarget'
							 | 
						||
| 
								 | 
							
								    bl_idname = 'LNX_PT_NLARootMotionRetargetPanel'
							 | 
						||
| 
								 | 
							
								    bl_space_type = 'NLA_EDITOR'
							 | 
						||
| 
								 | 
							
								    bl_region_type = 'UI'
							 | 
						||
| 
								 | 
							
								    bl_context = 'data'
							 | 
						||
| 
								 | 
							
								    bl_category = 'Leenkx'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def poll(cls, context):
							 | 
						||
| 
								 | 
							
								        return bool(context.active_nla_strip)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def draw(self, context):
							 | 
						||
| 
								 | 
							
								        LnxDrawRetargetPanel.draw(context, context.active_nla_strip.action, self.layout)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LNX_PT_DopeSheetRootMotionPanel(bpy.types.Panel):
							 | 
						||
| 
								 | 
							
								    bl_label = 'Leenkx Root Motion'
							 | 
						||
| 
								 | 
							
								    bl_idname = 'LNX_PT_DopeSheetRootMotionPanel'
							 | 
						||
| 
								 | 
							
								    bl_space_type = 'DOPESHEET_EDITOR'
							 | 
						||
| 
								 | 
							
								    bl_region_type = 'UI'
							 | 
						||
| 
								 | 
							
								    bl_context = 'data'
							 | 
						||
| 
								 | 
							
								    bl_category = 'Leenkx'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def poll(cls, context):
							 | 
						||
| 
								 | 
							
								        ds_mode = context.space_data.mode
							 | 
						||
| 
								 | 
							
								        if ds_mode in {'DOPESHEET', 'ACTION'}:
							 | 
						||
| 
								 | 
							
								            return bool(context.active_action)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def draw(self, context):
							 | 
						||
| 
								 | 
							
								        action = context.active_action
							 | 
						||
| 
								 | 
							
								        layout = self.layout
							 | 
						||
| 
								 | 
							
								        layout.label(text='Action: ' + action.name)
							 | 
						||
| 
								 | 
							
								        layout.prop(action, 'lnx_root_motion_pos')
							 | 
						||
| 
								 | 
							
								        layout.prop(action, 'lnx_root_motion_rot')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LNX_PT_NLARootMotionPanel(bpy.types.Panel):
							 | 
						||
| 
								 | 
							
								    bl_label = 'Leenkx Root Motion'
							 | 
						||
| 
								 | 
							
								    bl_idname = 'LNX_PT_NLARootMotionPanel'
							 | 
						||
| 
								 | 
							
								    bl_space_type = 'NLA_EDITOR'
							 | 
						||
| 
								 | 
							
								    bl_region_type = 'UI'
							 | 
						||
| 
								 | 
							
								    bl_context = 'data'
							 | 
						||
| 
								 | 
							
								    bl_category = 'Leenkx'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def poll(cls, context):
							 | 
						||
| 
								 | 
							
								        return bool(context.active_nla_strip)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def draw(self, context):
							 | 
						||
| 
								 | 
							
								        action = context.active_nla_strip.action
							 | 
						||
| 
								 | 
							
								        layout = self.layout
							 | 
						||
| 
								 | 
							
								        layout.label(text='Action: ' + action.name)
							 | 
						||
| 
								 | 
							
								        layout.prop(action, 'lnx_root_motion_pos')
							 | 
						||
| 
								 | 
							
								        layout.prop(action, 'lnx_root_motion_rot')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								__REG_CLASSES = (
							 | 
						||
| 
								 | 
							
								    LnxRetargetActions,
							 | 
						||
| 
								 | 
							
								    LNX_PT_DSRootMotionRetargetPanel,
							 | 
						||
| 
								 | 
							
								    LNX_PT_NLARootMotionRetargetPanel,
							 | 
						||
| 
								 | 
							
								    LNX_PT_DopeSheetRootMotionPanel,
							 | 
						||
| 
								 | 
							
								    LNX_PT_NLARootMotionPanel,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								__reg_classes, __unreg_classes = bpy.utils.register_classes_factory(__REG_CLASSES)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def register():
							 | 
						||
| 
								 | 
							
								    __reg_classes()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def unregister():
							 | 
						||
| 
								 | 
							
								    __unreg_classes()
							 |