step dist calculation ok

master
Pullusb 2022-03-30 17:40:52 +02:00
parent 5a221aa0fe
commit 10e563c476
5 changed files with 96 additions and 63 deletions

View File

@ -1,6 +1,7 @@
import bpy
from . import fn
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
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):
f = act.fcurves.find(f'pose.bones["{b.name}"].{channel}', index=i)
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
continue
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':
transform[channel] = Euler(chan_list)
else:
@ -26,10 +27,11 @@ def get_bone_transform_at_frame(b, act, frame):
def anim_path_from_translate():
'''Calculate step size from selected foot and forward axis and animate curve'''
print(fn.helper())
obj = bpy.context.object
if obj.type != 'ARMATURE':
ob = bpy.context.object
if ob.type != 'ARMATURE':
return ('ERROR', 'active is not an armature type')
# found curve through constraint
@ -37,20 +39,24 @@ def anim_path_from_translate():
# if not 'foot' in b.bone.name:
# 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
if bpy.context.scene.anim_cycle_settings.path_to_follow:
curve = bpy.context.scene.anim_cycle_settings.path_to_follow
if 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 not curve:
curve, _const = fn.get_follow_curve_from_armature(obj)
curve, _const = fn.get_follow_curve_from_armature(ob)
if isinstance(curve, str):
return curve, _const
act = fn.get_obj_action(obj)
act = fn.get_obj_action(ob)
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
if '_expanded' in act.name:
@ -104,32 +110,91 @@ def anim_path_from_translate():
break
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:
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}')
move_frame = end_frame - start_frame
## 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_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_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}
## calculate offset from bones
loc_fcu = None
@ -175,44 +240,7 @@ def anim_path_from_translate():
# Y positive value (forward) ->
move_val = abs(start_val - end_val)
"""
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):
bl_idname = "anim.animate_path"

View File

@ -27,7 +27,7 @@ class UAC_OT_autoset_axis(bpy.types.Operator):
def execute(self, context):
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')
if not root:
print('No root found')

View File

@ -39,6 +39,10 @@ Sidebar > Anim > unfold anim cycle
## Changelog:
0.3.5
- partly working
0.3.3
- total wip

View File

@ -2,7 +2,7 @@ bl_info = {
"name": "Unfold Anim Cycle",
"description": "Anim tools to develop walk/run cycles along a curve",
"author": "Samuel Bernou",
"version": (0, 3, 3),
"version": (0, 3, 5),
"blender": (3, 0, 0),
"location": "View3D",
"warning": "WIP",

View File

@ -17,11 +17,12 @@ class UAC_PG_settings(bpy.types.PropertyGroup) :
expand_on_selected_bones : bpy.props.BoolProperty(
name="On selected", description="Expand on selected bones",
default=True, options={'HIDDEN'})
default=False, options={'HIDDEN'})
linear : bpy.props.BoolProperty(
name="Linear", description="keep the animation path linear (Else step the path usings cycle keys)",
default=False, options={'HIDDEN'})
name="Linear", description="keep the animation path linear\
\nElse step the path usings follow path cycle keys)",
default=True, options={'HIDDEN'})
start_frame : bpy.props.IntProperty(
name="Start Frame", description="Starting frame for animation path",