diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c00fd0..2210bcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/OP_expand_cycle_step.py b/OP_expand_cycle_step.py index 2d49b4d..ca765d0 100644 --- a/OP_expand_cycle_step.py +++ b/OP_expand_cycle_step.py @@ -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"} diff --git a/OP_snap_contact.py b/OP_snap_contact.py index aec1527..2cf34bc 100644 --- a/OP_snap_contact.py +++ b/OP_snap_contact.py @@ -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" diff --git a/OP_world_copy_paste.py b/OP_world_copy_paste.py index dd7a90d..9743d26 100644 --- a/OP_world_copy_paste.py +++ b/OP_world_copy_paste.py @@ -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): diff --git a/README.md b/README.md index 61f0908..d0444da 100644 --- a/README.md +++ b/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". + + diff --git a/__init__.py b/__init__.py index 93a5525..2dca564 100644 --- a/__init__.py +++ b/__init__.py @@ -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": "", diff --git a/panels.py b/panels.py index cc661bd..7df6546 100644 --- a/panels.py +++ b/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)