step dist calculation ok
This commit is contained in:
		
							parent
							
								
									5a221aa0fe
								
							
						
					
					
						commit
						10e563c476
					
				| @ -1,6 +1,7 @@ | |||||||
| import bpy | import bpy | ||||||
| from . import fn | from . import fn | ||||||
| from mathutils import Vector, Euler | from mathutils import Vector, Euler | ||||||
|  | from mathutils.geometry import intersect_line_plane | ||||||
| ## step 2 : Auto animate the path (with feet selection) and modal to adjust speed | ## step 2 : Auto animate the path (with feet selection) and modal to adjust speed | ||||||
| 
 | 
 | ||||||
| def get_bone_transform_at_frame(b, act, frame): | def get_bone_transform_at_frame(b, act, frame): | ||||||
| @ -11,12 +12,12 @@ def get_bone_transform_at_frame(b, act, frame): | |||||||
|         for i in range(3): |         for i in range(3): | ||||||
|             f = act.fcurves.find(f'pose.bones["{b.name}"].{channel}', index=i) |             f = act.fcurves.find(f'pose.bones["{b.name}"].{channel}', index=i) | ||||||
|             if not f: |             if not f: | ||||||
|                 print(frame, channel, 'not animated ! using current value') |                 # print(frame, channel, 'not animated ! using current value') # Dbg | ||||||
|                 chan_list.append(getattr(b, channel)) # get current value since not animated |                 chan_list.append(getattr(b, channel)) # get current value since not animated | ||||||
|                 continue |                 continue | ||||||
|             chan_list.append(f.evaluate(frame)) |             chan_list.append(f.evaluate(frame)) | ||||||
|          |          | ||||||
|         print(frame, b.name, channel, chan_list) |         # print(frame, b.name, channel, chan_list) # Dbg | ||||||
|         if channel == 'rotation_euler': |         if channel == 'rotation_euler': | ||||||
|             transform[channel] = Euler(chan_list) |             transform[channel] = Euler(chan_list) | ||||||
|         else: |         else: | ||||||
| @ -26,10 +27,11 @@ def get_bone_transform_at_frame(b, act, frame): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def anim_path_from_translate(): | def anim_path_from_translate(): | ||||||
|  |     '''Calculate step size from selected foot and forward axis and animate curve''' | ||||||
|     print(fn.helper()) |     print(fn.helper()) | ||||||
| 
 | 
 | ||||||
|     obj = bpy.context.object |     ob = bpy.context.object | ||||||
|     if obj.type != 'ARMATURE': |     if ob.type != 'ARMATURE': | ||||||
|         return ('ERROR', 'active is not an armature type') |         return ('ERROR', 'active is not an armature type') | ||||||
| 
 | 
 | ||||||
|     # found curve through constraint |     # found curve through constraint | ||||||
| @ -37,20 +39,24 @@ def anim_path_from_translate(): | |||||||
| 
 | 
 | ||||||
|     # if not 'foot' in b.bone.name: |     # if not 'foot' in b.bone.name: | ||||||
|     #     return ('ERROR', 'No "foot" in active bone name\n-> Select foot that has the most reliable contact') |     #     return ('ERROR', 'No "foot" in active bone name\n-> Select foot that has the most reliable contact') | ||||||
| 
 |     settings = bpy.context.scene.anim_cycle_settings | ||||||
|  |     axis = settings.forward_axis | ||||||
|     curve = None |     curve = None | ||||||
|     if bpy.context.scene.anim_cycle_settings.path_to_follow: |     if settings.path_to_follow: | ||||||
|         curve = bpy.context.scene.anim_cycle_settings.path_to_follow |         curve = settings.path_to_follow | ||||||
|  | 
 | ||||||
|  |     if curve and not bpy.context.scene.objects.get(curve.name): | ||||||
|  |         return 'ERROR', f'Curve {curve.name} is not in scene' | ||||||
| 
 | 
 | ||||||
|     # if curve is not defined try to track it from constraints on armature |     # if curve is not defined try to track it from constraints on armature | ||||||
|     if not curve: |     if not curve: | ||||||
|         curve, _const = fn.get_follow_curve_from_armature(obj) |         curve, _const = fn.get_follow_curve_from_armature(ob) | ||||||
|         if isinstance(curve, str): |         if isinstance(curve, str): | ||||||
|             return curve, _const |             return curve, _const | ||||||
| 
 | 
 | ||||||
|     act = fn.get_obj_action(obj) |     act = fn.get_obj_action(ob) | ||||||
|     if not act: |     if not act: | ||||||
|         return ('ERROR', f'No action active on {obj.name}') |         return ('ERROR', f'No action active on {ob.name}') | ||||||
|      |      | ||||||
|     # use original action as ref |     # use original action as ref | ||||||
|     if '_expanded' in act.name: |     if '_expanded' in act.name: | ||||||
| @ -104,32 +110,91 @@ def anim_path_from_translate(): | |||||||
|                     break |                     break | ||||||
| 
 | 
 | ||||||
|     if start_frame is None or end_frame is None: |     if start_frame is None or end_frame is None: | ||||||
|         return ('ERROR', f'No (or not enough) keyframe marked Extreme {obj.name} > {b.name}') |         return ('ERROR', f'No (or not enough) keyframe marked Extreme {ob.name} > {b.name}') | ||||||
|     if start_frame == end_frame: |     if start_frame == end_frame: | ||||||
|        return ('ERROR', f'Only one key detected as extreme (at frame {start_frame}) !\nNeed at least two chained marked keys') |        return ('ERROR', f'Only one key detected as extreme (at frame {start_frame}) !\nNeed at least two chained marked keys') | ||||||
| 
 | 
 | ||||||
|     print(f'Offset from key range. start: {start_frame} - end: {end_frame}') |     print(f'Offset from key range. start: {start_frame} - end: {end_frame}') | ||||||
|     move_frame = end_frame - start_frame |     move_frame = end_frame - start_frame | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     ## Find move_val from diff position at start and end frame wihtin character forward axis |     ## Find move_val from diff position at start and end frame wihtin character forward axis | ||||||
| 
 | 
 | ||||||
|      |  | ||||||
|     start_transform = get_bone_transform_at_frame(b, act, start_frame) |     start_transform = get_bone_transform_at_frame(b, act, start_frame) | ||||||
|     start_mat = fn.compose_matrix(start_transform['location'], start_transform['rotation_euler'], start_transform['scale']) |     start_mat = fn.compose_matrix(start_transform['location'], start_transform['rotation_euler'], start_transform['scale']) | ||||||
| 
 | 
 | ||||||
|     end_transform = get_bone_transform_at_frame(b, act, end_frame) |     end_transform = get_bone_transform_at_frame(b, act, end_frame) | ||||||
|     end_mat = fn.compose_matrix(end_transform['location'], end_transform['rotation_euler'], end_transform['scale']) |     end_mat = fn.compose_matrix(end_transform['location'], end_transform['rotation_euler'], end_transform['scale']) | ||||||
| 
 | 
 | ||||||
|     # bpy.context.scene.cursor.location = (b.bone.matrix_local @ start_mat).to_translation() # obj.matrix_world @  |  | ||||||
|     bpy.context.scene.cursor.location = (b.bone.matrix_local @ end_mat).to_translation() # obj.matrix_world @  |  | ||||||
|     # bpy.context.scene.cursor.location = (b.matrix_basis.inverted() @ start_mat).to_translation() # obj.matrix_world @  |  | ||||||
|     # bpy.context.scene.cursor.location =  start_transform['location'] # obj.matrix_world @  |  | ||||||
|     return # FIXME |  | ||||||
| 
 | 
 | ||||||
|     ### old method using directly one axis |     ## Determine direction vector of the charater (root) | ||||||
|     """ |     orient_vectors = { | ||||||
|  |     'FORWARD_X' : Vector((1,0,0)), | ||||||
|  |     'FORWARD_Y' : Vector((0,1,0)), | ||||||
|  |     'FORWARD_Z' : Vector((0,0,1)), | ||||||
|  |     'TRACK_NEGATIVE_X' : Vector((-1,0,0)), | ||||||
|  |     'TRACK_NEGATIVE_Y' : Vector((0,-1,0)), | ||||||
|  |     'TRACK_NEGATIVE_Z' : Vector((0,0,-1)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ## TODO root need to be user defined | ||||||
|  |     root = ob.pose.bones.get('world') | ||||||
|  |     if not root: | ||||||
|  |         print('No root found') | ||||||
|  |         return {"CANCELLED"} | ||||||
|  | 
 | ||||||
|  |     root_axis_vec = orient_vectors[axis] # world space | ||||||
|  |     root_axis_vec = root.bone.matrix_local @ root_axis_vec # aligned with object | ||||||
|  |     # bpy.context.scene.cursor.location = root_axis_vec # Dbg root direction | ||||||
|  | 
 | ||||||
|  |     ## Get difference between start_loc and ends loc on forward axis | ||||||
|  |     start_loc = (b.bone.matrix_local @ start_mat).to_translation() | ||||||
|  |     end_loc = (b.bone.matrix_local @ end_mat).to_translation() | ||||||
|  |     # bpy.context.scene.cursor.location = start_loc # Dbg foot start position | ||||||
|  |     | ||||||
|  |     print('root vec : ', root_axis_vec) | ||||||
|  |     print('start loc: ', start_loc) | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  |     ##  get distance on forward axis | ||||||
|  |     move_val = (intersect_line_plane(start_loc, start_loc + root_axis_vec, end_loc, root_axis_vec) - start_loc).length | ||||||
|  |     print('move_val: ', move_val) | ||||||
|  | 
 | ||||||
|  |     length = fn.get_curve_length(curve)     | ||||||
|  |      | ||||||
|  |     steps = length / move_val | ||||||
|  | 
 | ||||||
|  |     frame_duration = int(steps * move_frame) | ||||||
|  | 
 | ||||||
|  |     ## Clear 'eval_time' keyframe before creating new ones # curve.data.animation_data_clear() # too much.. delete only eval_time | ||||||
|  |     if curve.data.animation_data and curve.data.animation_data.action: | ||||||
|  |         for fcu in curve.data.animation_data.action.fcurves: | ||||||
|  |             if fcu.data_path == 'eval_time': | ||||||
|  |                 curve.data.animation_data.action.fcurves.remove(fcu) | ||||||
|  |                 break | ||||||
|  |      | ||||||
|  |     ## add eval time animation on curve | ||||||
|  |     anim_frame = settings.start_frame | ||||||
|  |     curve.data.path_duration = frame_duration | ||||||
|  |     curve.data.eval_time = 0 | ||||||
|  |     curve.data.keyframe_insert('eval_time', frame=anim_frame) # , options={'INSERTKEY_AVAILABLE'} | ||||||
|  |      | ||||||
|  |     curve.data.eval_time = frame_duration | ||||||
|  |     curve.data.keyframe_insert('eval_time', frame=anim_frame + frame_duration) | ||||||
|  | 
 | ||||||
|  |     ## all to linear (will be set to CONSTANT at the moment of sampling) | ||||||
|  |     for fcu in curve.data.animation_data.action.fcurves: | ||||||
|  |         if fcu.data_path == 'eval_time': | ||||||
|  |             for k in fcu.keyframe_points: | ||||||
|  |                 k.interpolation = 'LINEAR' | ||||||
|  |      | ||||||
|  |     ## set all to constant | ||||||
|  |     # for k in t_fcu.keyframe_points: | ||||||
|  |     #     k.interpolation = 'CONSTANT' | ||||||
|  | 
 | ||||||
|  |     print('end of set_follow_path_anim') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     """ ### old method using directly one axis | ||||||
|     axis = {'X':0, 'Y':1, 'Z':2} |     axis = {'X':0, 'Y':1, 'Z':2} | ||||||
|     ## calculate offset from bones |     ## calculate offset from bones | ||||||
|     loc_fcu = None |     loc_fcu = None | ||||||
| @ -177,43 +242,6 @@ def anim_path_from_translate(): | |||||||
|     """ |     """ | ||||||
|      |      | ||||||
| 
 | 
 | ||||||
|     print('move_val: ', move_val) |  | ||||||
| 
 |  | ||||||
|     length = fn.get_curve_length(curve)     |  | ||||||
|      |  | ||||||
|     steps = length / move_val |  | ||||||
| 
 |  | ||||||
|     frame_duration = steps * move_frame |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     ### Clear keyframe before creating new ones  |  | ||||||
|     # curve.data.animation_data_clear() # too much.. delete only eval_time |  | ||||||
|     if curve.data.animation_data and curve.data.animation_data.action: |  | ||||||
|         for fcu in curve.data.animation_data.action.fcurves: |  | ||||||
|             if fcu.data_path == 'eval_time': |  | ||||||
|                 curve.data.animation_data.action.fcurves.remove(fcu) |  | ||||||
|                 break |  | ||||||
| 
 |  | ||||||
|     anim_frame = bpy.context.scene.anim_cycle_settings.start_frame |  | ||||||
|     curve.data.path_duration = frame_duration |  | ||||||
|     curve.data.eval_time = 0 |  | ||||||
|     curve.data.keyframe_insert('eval_time', frame=anim_frame)# , options={'INSERTKEY_AVAILABLE'} |  | ||||||
|      |  | ||||||
|     curve.data.eval_time = frame_duration |  | ||||||
|     curve.data.keyframe_insert('eval_time', frame=anim_frame + frame_duration) |  | ||||||
| 
 |  | ||||||
|     ## all to linear (will be set to CONSTANT at the moment of sampling) |  | ||||||
|     for fcu in curve.data.animation_data.action.fcurves: |  | ||||||
|         if fcu.data_path == 'eval_time': |  | ||||||
|             for k in fcu.keyframe_points: |  | ||||||
|                 k.interpolation = 'LINEAR' |  | ||||||
|      |  | ||||||
|     ## set all to constant |  | ||||||
|     # for k in t_fcu.keyframe_points: |  | ||||||
|     #     k.interpolation = 'CONSTANT' |  | ||||||
| 
 |  | ||||||
|     print('end of set_follow_path_anim') |  | ||||||
| 
 |  | ||||||
| class UAC_OT_animate_path(bpy.types.Operator): | class UAC_OT_animate_path(bpy.types.Operator): | ||||||
|     bl_idname = "anim.animate_path" |     bl_idname = "anim.animate_path" | ||||||
|     bl_label = "Animate Path" |     bl_label = "Animate Path" | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ class UAC_OT_autoset_axis(bpy.types.Operator): | |||||||
|     def execute(self, context): |     def execute(self, context): | ||||||
|         ob = context.object |         ob = context.object | ||||||
| 
 | 
 | ||||||
|         ## root need to be user defined to assume corner cases |         ## TODO root need to be user defined to assume corner cases | ||||||
|         root = ob.pose.bones.get('world') |         root = ob.pose.bones.get('world') | ||||||
|         if not root: |         if not root: | ||||||
|             print('No root found') |             print('No root found') | ||||||
|  | |||||||
| @ -39,6 +39,10 @@ Sidebar > Anim > unfold anim cycle | |||||||
| 
 | 
 | ||||||
| ## Changelog: | ## Changelog: | ||||||
| 
 | 
 | ||||||
|  | 0.3.5 | ||||||
|  | 
 | ||||||
|  | - partly working | ||||||
|  | 
 | ||||||
| 0.3.3 | 0.3.3 | ||||||
| 
 | 
 | ||||||
| - total wip | - total wip | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ bl_info = { | |||||||
|     "name": "Unfold Anim Cycle", |     "name": "Unfold Anim Cycle", | ||||||
|     "description": "Anim tools to develop walk/run cycles along a curve", |     "description": "Anim tools to develop walk/run cycles along a curve", | ||||||
|     "author": "Samuel Bernou", |     "author": "Samuel Bernou", | ||||||
|     "version": (0, 3, 3), |     "version": (0, 3, 5), | ||||||
|     "blender": (3, 0, 0), |     "blender": (3, 0, 0), | ||||||
|     "location": "View3D", |     "location": "View3D", | ||||||
|     "warning": "WIP", |     "warning": "WIP", | ||||||
|  | |||||||
| @ -17,11 +17,12 @@ class UAC_PG_settings(bpy.types.PropertyGroup) : | |||||||
|      |      | ||||||
|     expand_on_selected_bones : bpy.props.BoolProperty( |     expand_on_selected_bones : bpy.props.BoolProperty( | ||||||
|         name="On selected", description="Expand on selected bones", |         name="On selected", description="Expand on selected bones", | ||||||
|         default=True, options={'HIDDEN'}) |         default=False, options={'HIDDEN'}) | ||||||
|      |      | ||||||
|     linear : bpy.props.BoolProperty( |     linear : bpy.props.BoolProperty( | ||||||
|         name="Linear", description="keep the animation path linear (Else step the path usings cycle keys)", |         name="Linear", description="keep the animation path linear\ | ||||||
|         default=False, options={'HIDDEN'}) |             \nElse step the path usings follow path cycle keys)", | ||||||
|  |         default=True, options={'HIDDEN'}) | ||||||
| 
 | 
 | ||||||
|     start_frame : bpy.props.IntProperty( |     start_frame : bpy.props.IntProperty( | ||||||
|         name="Start Frame", description="Starting frame for animation path",  |         name="Start Frame", description="Starting frame for animation path",  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user