# vr_pose.py - the references' wide stance without the bow: feet apart, arms relaxed at the # sides, hands half closed (a hand-at-the-chest pose needed a twist that turns the shoulder # block into the chest, so it was dropped). Posed by aiming bones at world directions, parent first. import bpy, math from mathutils import Vector as V, Matrix AIM = [ # bone, world direction (faces -Y, character's left = +X) ("thigh_l", (0.07, 0.0, -1)), ("calf_l", (0.06, 0.04, -1)), # wide hips: legs barely splay ("thigh_r", (-0.07, 0.0, -1)), ("calf_r", (-0.06, 0.04, -1)), ] # arms by hinge: upper arm aimed, twisted about itself, elbow bent about its own axis (Z). # Elbow flex is +Z on the right side, -Z on the left (mirrored bone axes). ARMS = {"_r": {"aim": (-0.30, -0.06, -1), "flex": 35, "target": None, "twist": 20}, # relaxed stance, clear of the hip blocks "_l": {"aim": (0.30, -0.06, -1), "flex": 35, "target": None, "twist": -20}} CURL = {"_01": -0.5, "_02": -0.7, "_03": -0.5} # radians about each finger bone's X (toward the palm) def _aim(rig, name, d): pb = rig.pose.bones[name] M = pb.matrix.copy() y = V((M[0][1], M[1][1], M[2][1])).normalized() q = y.rotation_difference(V(d).normalized()) R = q.to_matrix().to_4x4() t = M.translation.copy() N = R @ M; N.translation = t pb.matrix = N bpy.context.view_layer.update() def rest(rig=None): rig = rig or bpy.data.objects["VR_Rig"] for pb in rig.pose.bones: pb.matrix_basis = Matrix() bpy.context.view_layer.update() def pose_ref(rig=None): rig = rig or bpy.data.objects["VR_Rig"] rest(rig) feet = {s: rig.pose.bones["foot" + s].matrix.copy() for s in ("_l", "_r")} for name, d in AIM: _aim(rig, name, d) for s_ in ("_l", "_r"): # feet stay flat, as in rest _aim(rig, "foot" + s_, V((feet[s_][0][1], feet[s_][1][1], feet[s_][2][1]))) for s_, a in ARMS.items(): sign = 1 if s_ == "_r" else -1 def put(tw): _aim(rig, "upperarm" + s_, a["aim"]) ub = rig.pose.bones["upperarm" + s_] ub.matrix = ub.matrix @ Matrix.Rotation(math.radians(tw), 4, 'Y') lb = rig.pose.bones["lowerarm" + s_]; lb.rotation_mode = 'XYZ' lb.rotation_euler = (0, 0, sign * math.radians(a["flex"])) bpy.context.view_layer.update() return rig.matrix_world @ rig.pose.bones["hand" + s_].head if a["target"]: best = min(range(-120, 121, 5), key=lambda tw: (put(tw) - V(a["target"])).length) else: best = a.get("twist", 0) put(best); a["used_twist"] = best for pb in rig.pose.bones: if any(pb.name.startswith(f) for f in ("index", "middle", "ring", "pinky", "thumb")): pb.rotation_mode = 'XYZ' k = CURL[pb.name[-5:-2]] * (0.6 if pb.name.startswith("thumb") else 1.0) pb.rotation_euler = (k, 0, 0) head = rig.pose.bones["head"]; head.rotation_mode = 'XYZ'; head.rotation_euler = (math.radians(6), 0, 0) bpy.context.view_layer.update() # drop the body until the lower foot touches the floor dg = bpy.context.evaluated_depsgraph_get() zmin = min((o.evaluated_get(dg).matrix_world @ v.co).z for o in bpy.data.objects if o.name.startswith(("VR_Foot", "VR_Toe")) for v in o.evaluated_get(dg).to_mesh().vertices) root = rig.pose.bones["root"] root.location = (0, 0, 0) bpy.context.view_layer.update() rb = rig.pose.bones["root"] # root bone's local Y is world +Y, local Z is world +Z (rest), so shift along its local Z axis zr = rb.bone.matrix_local.to_3x3().inverted() @ V((0, 0, -zmin)) rb.location = zr bpy.context.view_layer.update() return {"dropped": round(zmin, 4)}