File size: 2,378 Bytes
d9bb6b1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import raw from "./assets/rig-metadata.json";

export type LegName = "lf" | "lm" | "lh" | "rf" | "rm" | "rh";

export type WingSide = "l" | "r";

export interface JointInfo {
  node: string;
  axis: [number, number, number];
  /** Biomechanical range in radians (wing-hinge joints only). */
  range?: [number, number];
}

export const GROUND_OFFSET_MM: number = raw.groundOffsetMm;
export const EXTENT_MM: [number, number, number] = raw.extentMm as [number, number, number];
// Angle (atan2(x, z) convention) of the rig's own bind-pose forward
// direction. The mesh does not necessarily face +Z at rotation.y=0, so this
// must be subtracted when turning the model to face its direction of
// travel, or it visually appears to strafe instead of walking forward.
export const FACING_OFFSET_RAD: number = raw.facingOffsetRad;
export const LEGS: Record<LegName, string[]> = raw.legs;
// Each wing is one MuJoCo body (child of c_thorax) with 3 hinge DOFs: yaw =
// stroke angle (forward/back sweep), roll = deviation angle (up/down),
// pitch = rotation angle (blade twist) -- the real biomechanical wing-hinge
// axes from the flybody model, not a simplified single-axis flap.
export const WINGS: Record<WingSide, string[]> = raw.wings;
export const JOINTS: Record<string, JointInfo> = raw.joints as unknown as Record<string, JointInfo>;

const LEG_LEVEL_NODES: Record<"ThC" | "CTr" | "FTi" | "TiTa", (leg: LegName) => [string, string]> = {
  ThC: (leg) => ["c_thorax", `${leg}_coxa`],
  CTr: (leg) => [`${leg}_coxa`, `${leg}_trochanterfemur`],
  FTi: (leg) => [`${leg}_trochanterfemur`, `${leg}_tibia`],
  TiTa: (leg) => [`${leg}_tibia`, `${leg}_tarsus1`],
};

/**
 * Builds a joint name from its anatomical level + axis rather than relying
 * on array position in LEGS[leg] — different fly bodies define their DOFs in
 * different orders (e.g. flybody is ThC yaw/roll/pitch, NeuroMechFly is
 * yaw/pitch/roll), so positional destructuring would silently drive the
 * wrong joint if the underlying rig changes.
 */
export function legJointName(
  leg: LegName,
  level: "ThC" | "CTr" | "FTi" | "TiTa",
  axis: "yaw" | "roll" | "pitch",
): string {
  const [parent, child] = LEG_LEVEL_NODES[level](leg);
  return `${parent}-${child}-${axis}`;
}

export function wingJointName(side: WingSide, axis: "yaw" | "roll" | "pitch"): string {
  return `c_thorax-${side}_wing-${axis}`;
}