#pragma once #include "CoreMinimal.h" /** How a part of the arm moves. Generated into Player/Arm/ArmLayout.inl by Tools/blender/export_arm.py. */ enum class EDescentArmJoint : uint8 { Fixed, // RA_Root: the shoulder anchor, does not move itself Ball, // RA_UpperArm: the shoulder, yaw / pitch / roll Hinge, // RA_Forearm (elbow) and RA_Hand (wrist flex): one rotation about JointAxis Roll, // RA_Wrist: rotation about the forearm axis Slide, // RA_PistonRod: translation along JointAxis Finger, // a phalanx: one rotation about JointAxis (curl) }; /** One row of the generated table: a segment of the arm. Centimetres, Unreal axes, arm at rest. */ struct FDescentArmPartDef { /** Blender object name = component name: "RA_UpperArm", "RA_Index_2", ... */ const TCHAR* Name; /** Index of the parent row in the table, -1 for the root. */ int32 ParentIndex; /** Asset under /Game/Player/Arm/ ("SM_Arm_Hand"); empty for the root, which has no mesh. */ const TCHAR* MeshAsset; EDescentArmJoint Joint; /** Where the part's joint is, in the parent's frame (cm). */ FVector LocalOffsetCm; /** The part's orientation at rest, relative to the parent. */ FQuat RestRotation; /** Hinge / roll / finger: FQuat(JointAxis, +angle) is the model's positive rotation. Slide: the +distance direction. In the part's own frame. */ FVector JointAxis; /** Fingers: the curl about JointAxis already built into the rest rotation (degrees). 0 for the thumb root. */ double RestCurlDeg; /** Where the joint is at rest in arm space (cm), for tests: the parent chain must land exactly here. */ FVector PivotRestCm; /** Bounds of the part's mesh in its own frame (cm). */ FVector LocalBoundsMinCm; FVector LocalBoundsMaxCm; int32 Triangles; }; /** Index of each part in the generated table (the order is fixed by export_arm.py; a test compares the names). */ enum class EDescentArmPart : uint8 { Root, UpperArm, Forearm, PistonRod, Wrist, Hand, Thumb1, Thumb2, Thumb3, Index1, Index2, Index3, Middle1, Middle2, Middle3, Ring1, Ring2, Ring3, Pinky1, Pinky2, Pinky3, Count }; /** Fingers in the order of every per-finger array. */ enum class EDescentArmFinger : uint8 { Thumb, Index, Middle, Ring, Pinky, Count }; namespace Descent::Arm { constexpr int32 PartCount = static_cast(EDescentArmPart::Count); constexpr int32 FingerCount = static_cast(EDescentArmFinger::Count); /** The generated table. */ THEDESCENT_API TConstArrayView Parts(); THEDESCENT_API const FDescentArmPartDef& Def(EDescentArmPart Part); /** Phalanx 1..3 (Joint 0..2) of a finger. */ inline EDescentArmPart FingerPart(int32 Finger, int32 Joint) { return static_cast(static_cast(EDescentArmPart::Thumb1) + Finger * 3 + Joint); } /** Mesh folder: /Game/Player/Arm/ */ THEDESCENT_API const TCHAR* MeshFolder(); /** Measured by the export. */ THEDESCENT_API double ShoulderToElbowCm(); THEDESCENT_API double ElbowToHandPivotCm(); // elbow to the hand's flex axis THEDESCENT_API double HandPivotToFingertipCm(); // at the pose the model was authored in THEDESCENT_API double ShoulderToFingertipCm(); /** True when the exported geometry is a right arm (it is; the export says so from the palm / thumb analysis). */ THEDESCENT_API bool ModelIsRightArm(); }