110 lines
3.5 KiB
Python
110 lines
3.5 KiB
Python
import bpy
|
|
import numpy as np
|
|
from mathutils import Vector, Quaternion
|
|
from math import sin, cos, radians
|
|
from . import fn
|
|
|
|
## all action needed to setup the walk
|
|
|
|
## Need to know what is the forward axis
|
|
|
|
|
|
## https://www.meccanismocomplesso.org/en/3d-rotations-and-euler-angles-in-python/
|
|
|
|
class AW_OT_autoset_axis(bpy.types.Operator):
|
|
bl_idname = "autowalk.autoset_axis"
|
|
bl_label = "Auto Set Axis"
|
|
bl_description = "Define forward axis from armature"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
# def invoke(self, context, event):
|
|
# self.shift = event.shift
|
|
# return self.execute(context)
|
|
|
|
def execute(self, context):
|
|
ob = context.object
|
|
|
|
## TODO root need to be user defined to assume corner cases
|
|
root = ob.pose.bones.get('world')
|
|
if not root:
|
|
print('No root found')
|
|
return {"CANCELLED"}
|
|
|
|
root_pos = ob.matrix_world @ root.matrix.to_translation()
|
|
|
|
# gather all .R suffixed bones
|
|
|
|
all_right_pos = [ob.matrix_world @ b.matrix.to_translation() for b in ob.pose.bones if b.bone.name.lower().endswith('.r')]
|
|
|
|
if not all_right_pos:
|
|
print('No .r suffix found, need to adjust manually')
|
|
return {"CANCELLED"}
|
|
|
|
# get median position
|
|
median_r_pos = (sum(all_right_pos, Vector()) / len(all_right_pos)) - root_pos
|
|
|
|
median_r_vec = Vector((median_r_pos[0], median_r_pos[1], 0))
|
|
median_r_vec.normalize()
|
|
|
|
# Determine forward direction by rotating 90 degrees CCW on up axis
|
|
guessed_forward = Quaternion(Vector((0,0,1)), radians(90)) @ median_r_vec
|
|
|
|
## TODO determine what axis of root align bests with guessed_forward (minimum 3D angle)
|
|
|
|
axises = {'FORWARD_Y':(0,1,0), 'TRACK_NEGATIVE_Y':(0,-1,0), 'FORWARD_Z':(0,0,1), 'TRACK_NEGATIVE_Z':(0,0,-1)} # 'FORWARD_X':(1,0,0), 'TRACK_NEGATIVE_X':(-1,0,0)
|
|
ref_angle = 10
|
|
best_axis = False
|
|
for axis, vec in axises.items(): # (1,0,0), (-1,0,0)
|
|
angle = guessed_forward.angle(ob.matrix_world @ root.matrix @ Vector(vec))
|
|
print(axis, 'angle: ', angle)
|
|
if angle < ref_angle:
|
|
ref_angle = angle
|
|
best_axis = axis
|
|
|
|
if not best_axis:
|
|
print('No axis found')
|
|
return {"CANCELLED"}
|
|
|
|
context.scene.anim_cycle_settings.forward_axis = best_axis
|
|
return {"FINISHED"}
|
|
|
|
class AW_OT_create_cycles_modifiers(bpy.types.Operator):
|
|
bl_idname = "autowalk.create_cycles_modifiers"
|
|
bl_label = "Add Cycles Modifiers"
|
|
bl_description = "Add cycles modifier on all bones not prefixed [mch, org, def, vis,fdl]\
|
|
\nCtrl + Click : remove ALL Cycles modifiers on all fcurves"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def invoke(self, context, event):
|
|
self.ctrl = event.ctrl
|
|
return self.execute(context)
|
|
|
|
def execute(self, context):
|
|
if self.ctrl:
|
|
fn.remove_all_cycles_modifier(context.object)
|
|
else:
|
|
fn.create_cycle_modifiers(context.object)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
classes=(
|
|
AW_OT_autoset_axis,
|
|
AW_OT_create_cycles_modifiers,
|
|
)
|
|
|
|
def register():
|
|
for cls in classes:
|
|
bpy.utils.register_class(cls)
|
|
|
|
def unregister():
|
|
for cls in reversed(classes):
|
|
bpy.utils.unregister_class(cls) |