remove ground feet and some bugfixes
1.3.2 - removed: ground feet (added initial support for override compatibility) - fixed: error with key and jumpmaster
parent
cd23f50c55
commit
072a483188
|
@ -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`
|
||||
|
|
|
@ -184,7 +184,7 @@ def step_path():
|
|||
|
||||
class AW_OT_bake_cycle_and_step(bpy.types.Operator):
|
||||
bl_idname = "autowalk.bake_cycle_and_step"
|
||||
bl_label = "Bake key and step path "
|
||||
bl_label = "Bake key and step path"
|
||||
bl_description = "Bake the key and step the animation path according to those key\
|
||||
\n(duplicate to a new 'baked' action)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
@ -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"}
|
||||
|
|
|
@ -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:
|
||||
|
@ -199,15 +252,9 @@ def snap_foot(pb, gnd):
|
|||
vec = Vector((0,0, -move))
|
||||
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"
|
||||
|
|
|
@ -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):
|
||||
|
|
26
README.md
26
README.md
|
@ -28,27 +28,19 @@ Sidebar > Anim > unfold anim cycle
|
|||
|
||||
### TODO
|
||||
|
||||
- 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)
|
||||
|
||||
- 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
|
||||
- create nurb path instead of curve
|
||||
- Smoothing keys after last freezed to avoid too much gap "pose click".
|
||||
|
||||
<!--
|
||||
### DONE
|
||||
- align curve to root
|
||||
- Put curve forward motion on bones modifier's offset value as negative time offset (instead of using curve )
|
||||
-->
|
||||
|
|
|
@ -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": "",
|
||||
|
|
11
panels.py
11
panels.py
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue