import bpy, re from . import fn ## step 1 : Create the curve # need to determine a specific acceleration envelope factor to go to end of curve # (set on curves) def remove_spacetime_keys(context=None): if not context: context = bpy.context # remove all spacetime markers and walk collection col_name = 'walk_markers' walk_col = context.scene.collection.children.get(col_name) if walk_col: for o in reversed(walk_col.objects): if o.name.startswith('spacetime_marker_'): bpy.data.objects.remove(o) bpy.data.collections.remove(walk_col) class AW_OT_create_a_b_step(bpy.types.Operator): bl_idname = "autowalk.create_a_b_step" bl_label = "Create Two Points Curve" bl_description = "Create a straight curve between two defined position and time" bl_options = {"REGISTER", "UNDO", "INTERNAL"} # first time ops is launched just set an empty (with market time) # second time (if a mark empty exists determine a AB path to set curve) @classmethod def poll(cls, context): return context.object and context.object.type == 'ARMATURE' def create_marker(self, context): m_name = f'spacetime_marker_{context.scene.frame_current}' mark = bpy.data.objects.new(m_name, None) mark.location = self.position mark.empty_display_size = 2 self.walk_col.objects.link(mark) return mark def execute(self, context): ob = context.object prefs = fn.get_addon_prefs() root_name = prefs.tgt_bone root = ob.pose.bones.get(root_name) self.position = (ob.matrix_world @ root.matrix).to_translation() col_name = 'walk_markers' self.walk_col = fn.get_col(col_name, create=True) # link in scene collection by default markers = [o for o in self.walk_col.objects if o.type == 'EMPTY' and o.name.startswith('spacetime_marker_')] if not markers: self.create_marker(context) return {"FINISHED"} # Create the second marker and append to marker list markers.append(self.create_marker(context)) markers.sort(key=lambda x: x.name) # sort by time (frame written in name) a = markers[0].location b = markers[1].location - a # remove a postion to get position in curve object space a_frame = int(markers[0].name.split('_')[-1].split('.')[0]) b_frame = int(markers[1].name.split('_')[-1].split('.')[0]) # Set the curve and constraint curve = fn.generate_curve(location=a, direction=b, name='curve_path', context=context) settings = context.scene.anim_cycle_settings settings.path_to_follow = curve fn.create_follow_path_constraint(ob, curve) # reset location to remove offset root.location = (0,0,0) root.keyframe_insert('location', frame=a_frame) root.rotation_euler = (0,0,0) root.keyframe_insert('rotation_euler', frame=a_frame) # refresh evaluation so constraint shows up correctly bpy.context.scene.frame_set(bpy.context.scene.frame_current) # set offset animation ??? (but movement not in sync until action is retimed in NLA) ## remove all markers or keep for later reuse ? remove_spacetime_keys(context=None) # Set frame start and end settings.start_frame = a_frame settings.end_frame = b_frame return {"FINISHED"} class AW_OT_remove_a_b_step(bpy.types.Operator): bl_idname = "autowalk.remove_a_b_step" bl_label = "Remove First Position" bl_description = "remove first point defining step" bl_options = {"REGISTER", "UNDO", "INTERNAL"} # Define what is first and last according to time @classmethod def poll(cls, context): return context.object and context.object.type == 'ARMATURE' def execute(self, context): remove_spacetime_keys(context=None) return {"FINISHED"} classes=( AW_OT_create_a_b_step, AW_OT_remove_a_b_step, ) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls) # if __name__ == "__main__": # register()