auto_walk/OP_setup.py

83 lines
2.6 KiB
Python

import bpy
from mathutils import Vector, Quaternion
from math import sin, cos, radians
import numpy as np
## 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 UAC_OT_autoset_axis(bpy.types.Operator):
bl_idname = "uac.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
## 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()
up = Vector((0,0,1))
# 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"}
def register():
bpy.utils.register_class(UAC_OT_autoset_axis)
def unregister():
bpy.utils.unregister_class(UAC_OT_autoset_axis)