chobit/shared/godot/data/body_constraints.gd
Claude Code 7067b6dded refactor(shared): ♻️ Improve shared utility structure for better maintainability
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-03-28 14:55:37 -07:00

123 lines
5.5 KiB
GDScript

## Auto-generated by @lilith/body-api transpiler. Do not edit.
## Source: ~/Code/@packages/@@/body-api
## Regenerate: body-api transpile --target gdscript --out <dir>
class_name BodyConstraints
extends RefCounted
const ROTATION_LIMITS: Dictionary = {
"hips": {"yaw": [-45.0, 45.0], "pitch": [-30.0, 30.0], "roll": [-20.0, 20.0]},
"spine": {"yaw": [-35.0, 35.0], "pitch": [-40.0, 60.0], "roll": [-25.0, 25.0]},
"chest": {"yaw": [-25.0, 25.0], "pitch": [-30.0, 45.0], "roll": [-20.0, 20.0]},
"upper_chest": {"yaw": [-20.0, 20.0], "pitch": [-25.0, 40.0], "roll": [-15.0, 15.0]},
"neck": {"yaw": [-80.0, 80.0], "pitch": [-50.0, 50.0], "roll": [-45.0, 45.0]},
"head": {"yaw": [-10.0, 10.0], "pitch": [-15.0, 15.0], "roll": [-10.0, 10.0]},
"left_eye": {"yaw": [-50.0, 50.0], "pitch": [-40.0, 40.0], "roll": [-10.0, 10.0]},
"right_eye": {"yaw": [-50.0, 50.0], "pitch": [-40.0, 40.0], "roll": [-10.0, 10.0]},
"left_shoulder": {"yaw": [-30.0, 30.0], "pitch": [-20.0, 30.0], "roll": [-20.0, 20.0]},
"right_shoulder": {"yaw": [-30.0, 30.0], "pitch": [-20.0, 30.0], "roll": [-20.0, 20.0]},
"left_upper_arm": {"yaw": [-90.0, 90.0], "pitch": [-30.0, 180.0], "roll": [-90.0, 90.0]},
"right_upper_arm": {"yaw": [-90.0, 90.0], "pitch": [-30.0, 180.0], "roll": [-90.0, 90.0]},
"left_lower_arm": {"yaw": [-90.0, 90.0], "pitch": [0.0, 145.0], "roll": [-10.0, 10.0]},
"right_lower_arm": {"yaw": [-90.0, 90.0], "pitch": [0.0, 145.0], "roll": [-10.0, 10.0]},
"left_hand": {"yaw": [-40.0, 30.0], "pitch": [-60.0, 80.0], "roll": [-10.0, 10.0]},
"right_hand": {"yaw": [-30.0, 40.0], "pitch": [-60.0, 80.0], "roll": [-10.0, 10.0]},
}
const VELOCITY_LIMITS: Dictionary = {
"hips": {"yaw": 60.0, "pitch": 40.0, "roll": 30.0},
"spine": {"yaw": 80.0, "pitch": 60.0, "roll": 50.0},
"chest": {"yaw": 80.0, "pitch": 60.0, "roll": 50.0},
"upper_chest": {"yaw": 90.0, "pitch": 70.0, "roll": 55.0},
"neck": {"yaw": 120.0, "pitch": 90.0, "roll": 70.0},
"head": {"yaw": 150.0, "pitch": 110.0, "roll": 80.0},
"left_eye": {"yaw": 400.0, "pitch": 350.0, "roll": 60.0},
"right_eye": {"yaw": 400.0, "pitch": 350.0, "roll": 60.0},
"left_shoulder": {"yaw": 80.0, "pitch": 60.0, "roll": 60.0},
"right_shoulder": {"yaw": 80.0, "pitch": 60.0, "roll": 60.0},
"left_upper_arm": {"yaw": 120.0, "pitch": 200.0, "roll": 160.0},
"right_upper_arm": {"yaw": 120.0, "pitch": 200.0, "roll": 160.0},
"left_lower_arm": {"yaw": 200.0, "pitch": 250.0, "roll": 30.0},
"right_lower_arm": {"yaw": 200.0, "pitch": 250.0, "roll": 30.0},
"left_hand": {"yaw": 150.0, "pitch": 200.0, "roll": 50.0},
"right_hand": {"yaw": 150.0, "pitch": 200.0, "roll": 50.0},
}
const COUPLING_RULES: Array = [
{"driver": "hips", "follower": "spine", "yaw_ratio": 0.6, "pitch_ratio": 0.7},
{"driver": "spine", "follower": "chest", "yaw_ratio": 0.8, "pitch_ratio": 0.75},
{"driver": "chest", "follower": "upper_chest", "yaw_ratio": 0.85, "pitch_ratio": 0.8},
{"driver": "hips", "follower": "neck", "yaw_ratio": 0.3, "lag_seconds": 0.05},
{
"driver": "left_eye",
"follower": "head",
"yaw_ratio": 0.35,
"pitch_ratio": 0.3,
"lag_seconds": -0.15
},
{
"driver": "right_eye",
"follower": "head",
"yaw_ratio": 0.35,
"pitch_ratio": 0.3,
"lag_seconds": -0.15
},
{"driver": "upper_chest", "follower": "left_shoulder", "yaw_ratio": 0.5, "roll_ratio": 0.4},
{"driver": "upper_chest", "follower": "right_shoulder", "yaw_ratio": 0.5, "roll_ratio": 0.4},
]
## Clamp yaw/pitch/roll (degrees) to anatomical limits for the given part.
## Returns Vector3(clamped_yaw, clamped_pitch, clamped_roll).
static func clamp_rotation(part: String, yaw: float, pitch: float, roll: float) -> Vector3:
var lim: Dictionary = ROTATION_LIMITS[part]
return Vector3(
clampf(yaw, lim["yaw"][0], lim["yaw"][1]),
clampf(pitch, lim["pitch"][0], lim["pitch"][1]),
clampf(roll, lim["roll"][0], lim["roll"][1]),
)
## Clamp a per-frame angular delta (degrees) to max believable velocity.
## delta_* is the change this frame; dt is the frame time in seconds.
## Returns Vector3(clamped_yaw_delta, clamped_pitch_delta, clamped_roll_delta).
static func clamp_angular_velocity(
part: String, delta_yaw: float, delta_pitch: float, delta_roll: float, dt: float
) -> Vector3:
var vel: Dictionary = VELOCITY_LIMITS[part]
var max_yaw: float = vel["yaw"] * dt
var max_pitch: float = vel["pitch"] * dt
var max_roll: float = vel["roll"] * dt
return Vector3(
clampf(delta_yaw, -max_yaw, max_yaw),
clampf(delta_pitch, -max_pitch, max_pitch),
clampf(delta_roll, -max_roll, max_roll),
)
## Return suggested coupled yaw angles for all follower bones of the given driver.
## Result is a Dictionary of {follower_name: suggested_yaw_degrees}.
## Clamp the results with clamp_rotation() before applying to skeleton.
static func derive_coupled_yaw(driver: String, driver_yaw: float) -> Dictionary:
var out: Dictionary = {}
for rule: Dictionary in COUPLING_RULES:
if rule["driver"] != driver:
continue
var follower: String = rule["follower"]
var ratio: float = rule.get("yaw_ratio", 0.0)
out[follower] = out.get(follower, 0.0) + driver_yaw * ratio
# Clamp each follower to its own limits
for key: String in out.keys():
var lim: Dictionary = ROTATION_LIMITS.get(key, {})
if not lim.is_empty():
out[key] = clampf(out[key], lim["yaw"][0], lim["yaw"][1])
return out
## Returns the max believable yaw speed (degrees/second) for the given part.
static func max_yaw_speed(part: String) -> float:
return VELOCITY_LIMITS[part]["yaw"]
## Returns the anatomical yaw range as [min, max] degrees.
static func yaw_range(part: String) -> Array:
return ROTATION_LIMITS[part]["yaw"]