remove ground feet and some bugfixes

1.3.2

- removed: ground feet (added initial support for override compatibility)
- fixed: error with key and jump
master
Pullusb 2022-04-21 14:25:43 +02:00
parent cd23f50c55
commit 072a483188
7 changed files with 101 additions and 55 deletions

View File

@ -1,10 +1,14 @@
# Changelog
1.3.2
- removed: ground feet (added initial support for override compatibility wip)
- fixed: error with key and jump
1.3.1
- added: button to remove follow path constraint
1.3.0
- changed: rename addon: `unfold anim cycle` >> `auto walk`

View File

@ -488,7 +488,7 @@ class AW_OT_step_back_actions(bpy.types.Operator):
if a == context.object.animation_data.action:
layout.label(text=f'(current) >> {a.name}', icon='ACTION')
continue
layout.operator('AW_OT_set_action', text=a.name, icon='ACTION').act_name = a.name
layout.operator('autowalk.set_action', text=a.name, icon='ACTION').act_name = a.name
def execute(self, context):
return {"FINISHED"}

View File

@ -1,6 +1,7 @@
import bpy
import bmesh
import re
from mathutils import Vector
from . import fn
def raycast_from_loc_to_obj(src, tgt, direction=None, dg=None):
@ -55,6 +56,12 @@ def worldspace_move_posebone(b, vec, is_target=False):
to_space='POSE')
return target_vec
def set_subsurf_viewport(ob, show=True):
for m in ob.modifiers:
# if m.type in ('SUBSURF', 'TRIANGULATE'):
if m.type == 'SUBSURF':
m.show_viewport = show
def snap_foot(pb, gnd):
'''Get posebone and ground to touch'''
@ -63,29 +70,69 @@ def snap_foot(pb, gnd):
print('arm: ', arm)
# find tip bone :
tip = [p for p in pb.children_recursive if p.name.startswith('DEF')][-1]
# tip = [p for p in pb.children_recursive if p.name.startswith('DEF')][-1]
def_name = f'DEF.{pb.name}'
tip = arm.pose.bones.get(def_name)
## guess fallbacks
if not tip:
## Regex for potential separator variance
# "." dot separator is not literal anymore : DEF.foot\.L
parttern = f'DEF.{re.escape(pb.name)}'
for pb in arm.pose.bones:
if re.search(parttern, pb.name):
tip = pb
break
if not tip:
## name proximity
best_ratio = 0
for pb in arm.pose.bones:
if (ratio := fn.fuzzy_match_ratio(pb.name, def_name)) > best_ratio:
tip = pb # assign tip
best_ratio = ratio
if best_ratio < 0.85:
# no bones name is close enough
return ('ERROR', f'no bone name is close enough of "{def_name}" (best was "{tip}" with close-ratio {best_ratio})')
print('tip: ', tip)
# get deformed object VG (find skinned mesh)
ob = None
for o in arm.proxy_collection.instance_collection.all_objects:
for o in arm.users_collection[0].all_objects:
if o.type != 'MESH':
continue
if not 'body' in o.name: # might be not needed if filter on vertex group
continue
if not o.vertex_groups.get(tip.name):
# has vertex_group pointing to the bone
continue
for m in o.modifiers:
if m.type == 'ARMATURE':
# print(o.name, m.object)
if m.object == arm.proxy: # if point to orignal rig
## here we want body, not body_deform
if not 'body' in o.name:
continue
if '_deform' in o.name:
continue
if m.object == arm: # if point to orignal rig
ob = o
break
## find object with old proxy method
# for o in arm.proxy_collection.instance_collection.all_objects:
# if o.type != 'MESH':
# continue
# for m in o.modifiers:
# if m.type == 'ARMATURE':
# # print(o.name, m.object)
# if m.object == arm.proxy: # if point to orignal rig
# ## here we want body, not body_deform
# if not 'body' in o.name:
# continue
# if '_deform' in o.name:
# continue
# ob = o
# break
if not ob:
print('ERROR', 'no skinned mesh found')
return
return ('ERROR', 'no skinned mesh found')
print('check skinning of', ob.name)
### MESH baking
@ -119,10 +166,7 @@ def snap_foot(pb, gnd):
#-# Get Vertices position for a specific vertex group if over weight limit
#-# (Does not work if a subdivision modifier is on)
for m in ob.modifiers:
# if m.type in ('SUBSURF', 'TRIANGULATE'):
if m.type == 'SUBSURF':
m.show_viewport = False
set_subsurf_viewport(ob, show=False)
dg = bpy.context.evaluated_depsgraph_get()
obeval = ob.evaluated_get(dg) #.copy()
@ -142,14 +186,21 @@ def snap_foot(pb, gnd):
vg = obeval.vertex_groups[tip.name]
world_co = []
for idx, vert in enumerate(bake_mesh.vertices):
print('idx, vert: ', idx, vert)
grp_indexes = [i.group for i in vert.groups]
print('grp_indexes: ', grp_indexes)
print('vg.index in grp_indexes: ', vg.index in grp_indexes)
if vg.index in grp_indexes:
print(':: vg.weight(idx) > 0.5: ', vg.weight(idx) > 0.5)
# print()
if vg.index in grp_indexes and vg.weight(idx) > 0.5:
ct +=1
world_co.append(ob.matrix_world @ vert.co)
if not ct:
print('ERROR', 'No vertices found')
return
set_subsurf_viewport(ob)
obeval.to_mesh_clear()
return ('ERROR', 'No vertices found')
#-# # list comprehension
# verts = [vert for vid, vert in enumerate(bake_mesh.vertices) \
@ -184,8 +235,10 @@ def snap_foot(pb, gnd):
# empty_at(contact, size=0.2)
if not dists and not updists:
print('ERROR', 'raycast could not found contact')
return
set_subsurf_viewport(ob)
obeval.to_mesh_clear()
return ('ERROR', 'raycast could not found contact')
# move bones by the minimal amount.
if updists:
@ -200,14 +253,8 @@ def snap_foot(pb, gnd):
worldspace_move_posebone(pb, vec)
print('INFO', f'move down by {move}')
## restore
for m in ob.modifiers:
if m.type == 'SUBSURF':
# if m.type in ('SUBSURF', 'TRIANGULATE'):
m.show_viewport = True
set_subsurf_viewport(ob)
obeval.to_mesh_clear()
@ -233,9 +280,9 @@ def snap_feet():
for pb in bpy.context.selected_pose_bones:
## find the foot bones.
if '_shoe' in pb.name:
if 'foot' in pb.name:
# get pb lowest surface deformed point
snap_foot(pb, gnd)
return snap_foot(pb, gnd)
class AW_OT_contact_to_ground(bpy.types.Operator):
bl_idname = "autowalk.contact_to_ground"

View File

@ -82,7 +82,7 @@ class AW_OT_world_space_paste_next(bpy.types.Operator):
self.report({'WARNING'}, 'No next frame to jump on')
return {'FINISHED'}
context.scene.frame_current = new_frame
context.scene.frame_current = int(new_frame)
return {"FINISHED"}
class AW_OT_world_space_paste_next_frame(bpy.types.Operator):

View File

@ -28,27 +28,19 @@ Sidebar > Anim > unfold anim cycle
### TODO
- pin feets:
- iterate in reverse in keys when pinning so last foot position is correct
- create intermediate keys (at each frame when necessary) to prevent lateral sliding on curved path
(maybe expose as an option... not needed if path is straight for example... or auto detect if path is full straight at the moment of pin ops)
- Expose methods to go back in action history
*things to consider*:
- Expose foot ?
- Store path animation on a separate action (but that mean NLA hadto be used every time)
Bonus:
- Use position A-B method to generate curve (with retimed animation to prioritize speed over fidelity)
- auto-determine foot bone to use for distance reference
- Better A-B position setup:
- auto-retime animation to prioritize speed over base cycle velocity
- create nurb path instead of curve
- Smoothing keys after last freezed to avoid too much gap "pose click".
<!--
### DONE
- auto-determine foot bone to use for distance reference
- Expose methods to go back in action history
- pin feets:
- iterate in reverse in keys when pinning so last foot position is correct
- create intermediate keys (at each frame when necessary) to prevent lateral sliding on curved path
(maybe expose as an option... not needed if path is straight for example... or auto detect if path is full straight at the moment of pin ops)
- align curve to root
- Put curve forward motion on bones modifier's offset value as negative time offset (instead of using curve )
-->

View File

@ -4,7 +4,7 @@ bl_info = {
"name": "Auto Walk",
"description": "Develop a walk/run cycles along a curve and pin feets",
"author": "Samuel Bernou",
"version": (1, 3, 1),
"version": (1, 3, 2),
"blender": (3, 0, 0),
"location": "View3D",
"warning": "",

View File

@ -94,10 +94,10 @@ class AW_PT_walk_cycle_anim_panel(bpy.types.Panel):
row = col.row(align=True)
row.prop(settings, "start_frame", text='Start')
row.prop(settings, "end_frame", text='End')
col.operator('autowalk.animate_path', text='Animate Forward Motion', icon='ANIM')
col.operator('autowalk.animate_path', text='Move On Curve', icon='ANIM') # Animate Forward Motion
row=col.row()
row.operator('autowalk.adjust_animation_length', text='Adjust Forward Speed', icon='MOD_TIME')
row.operator('autowalk.adjust_animation_length', text='Adjust Speed', icon='MOD_TIME') # Adjust Forward Speed
## Bake cycle (on selected)
box = layout.box()
@ -127,13 +127,16 @@ class AW_PT_anim_tools_panel(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Walk"
bl_label = "Walk Tools"
bl_label = "Pin Tools"
def draw(self, context):
layout = self.layout
layout.operator('autowalk.contact_to_ground', text='Ground selected feet', icon='SNAP_OFF')
## FIXME: need to fix vertices get
# layout.operator('autowalk.contact_to_ground', text='Ground selected feet', icon='SNAP_OFF')
row = layout.row()
row.operator('autowalk.world_space_copy', text='Copy Pose', icon='COPYDOWN')
# row.operator('autowalk.world_space_paste', text='Paste', icon='PASTEDOWN')
# row = layout.row(align=False)