step dist calculation ok
This commit is contained in:
parent
5a221aa0fe
commit
10e563c476
@ -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
|
||||
@ -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):
|
||||
bl_idname = "anim.animate_path"
|
||||
bl_label = "Animate Path"
|
||||
|
@ -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')
|
||||
|
@ -39,6 +39,10 @@ Sidebar > Anim > unfold anim cycle
|
||||
|
||||
## Changelog:
|
||||
|
||||
0.3.5
|
||||
|
||||
- partly working
|
||||
|
||||
0.3.3
|
||||
|
||||
- total wip
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user