SkeletonData
class AR51SDK_API SkeletonData
A plain C++ class — not a USTRUCT, not Blueprint-exposed. Holds one frame of skeleton joint data: a parallel pair of arrays (Positions + Confidence) indexed by the Keypoints scheme, plus indexer access and a set of derived-rotation helpers that compute body/limb orientations from triangles of keypoints. This is the raw frame the SDK receives and feeds to the solver; USkeletonConsumer drives characters from it each frame.
Unreal works in cm / degrees. Positions are vectors indexed by Keypoints; treat them as cm after SDK scaling. ⚠️ The space/units of this raw container are unverified — the IK pipeline operates in UE world-space cm, but the container may carry meters before scaling. Treat the index → joint mapping as authoritative; treat units as cm-after-scaling unless you confirm otherwise.
Fields
| Field | Type | Description |
|---|---|---|
Positions | TArray<FVector> | joint positions, indexed by Keypoints (or raw int). ⚠️ space/units unverified — see caution above (treat as cm after scaling) |
Confidence | TArray<float> | per-joint confidence, parallel to Positions, roughly 0–1. Low-confidence joints are de-weighted or ignored by the solver |
The two arrays are parallel: Confidence[i] is the confidence for Positions[i], where i is a Keypoints index.
Constructors
SkeletonData(const TArray<FVector>& positions, const TArray<float>& confidence);
SkeletonData(int capacity);
Construct from an existing positions+confidence pair, or pre-size an empty frame to capacity joints.
KeypointspositionsIndexer
FVector& operator[](Keypoints joint);
const FVector& operator[](Keypoints joint) const;
FVector& operator[](int index);
const FVector& operator[](int index) const;
Read or write a joint position by Keypoints value or by raw integer index (const and mutable overloads). Prefer the Keypoints overload — it is self-documenting and immune to index drift for the auto-numbered joints.
// Read the head/wrist positions from a frame (UE cm, after scaling)
const FVector Nose = Frame[Keypoints::Nose];
const FVector LWrist = Frame[Keypoints::LWrist];
if (Frame.Confidence[(int)Keypoints::LWrist] > 0.5f)
{
// ...use LWrist
}
Derived-rotation helpers
FQuat GetHipRotation();
FQuat GetHipRotation(FVector& forward);
FQuat GetHeadRotation();
FQuat GetSpineRotation();
FQuat GetSpineRotation(FVector& forward);
FQuat GetUpperChestRotation();
FQuat GetUpperChestRotation(FVector& forward);
FQuat GetLShoulderRotation(); FQuat GetLShoulderRotation(FVector& forward);
FQuat GetRShoulderRotation(); FQuat GetRShoulderRotation(FVector& forward);
FQuat GetLHipRotation(); FQuat GetLHipRotation(FVector& forward);
FQuat GetRHipRotation(); FQuat GetRHipRotation(FVector& forward);
FQuat GetLimbRotation(Keypoints top, Keypoints mid, Keypoints bottom, FVector& forward);
FQuat GetRotation(Keypoints rBottom, Keypoints lBottom, Keypoints rTop, Keypoints lTop, FVector& forward);
static FQuat GetRotation(FVector rightPoint, FVector leftPoint, FVector topPoint, FVector& forward);
Compute joint/body orientations from triangles of keypoints in this frame. Each has a (FVector& forward) overload that also returns the body/limb forward vector as an out-param. GetLimbRotation is the general form — pass the three keypoints that define a limb; GetRotation builds an orientation from a left/right/top point set (the static overload takes raw FVectors instead of keypoint indices).
The internals are a black box, but the methods are public and useful when a game wants to read body or limb orientation directly from a frame rather than from a driven character.
Keypoints
enum class (plain C++) C++ onlyenum class Keypoints : int
A plain C++ enum class, int-backed — not a UENUM, not Blueprint-exposed. The canonical joint-index scheme: the joint/bone naming that the whole data model uses to index SkeletonData::Positions and Confidence.
The body joints (OpenPose-style) have fixed integer indices — safe to rely on:
enum class Keypoints : int
{
Unknown = -1,
Nose = 0,
Neck = 1,
RShoulder = 2,
RElbow = 3,
RWrist = 4,
LShoulder = 5,
LElbow = 6,
LWrist = 7,
MidHip = 8,
RHip = 9,
RKnee = 10,
RAnkle = 11,
LHip = 12,
LKnee = 13,
LAnkle = 14,
REye = 15,
LEye = 16,
REar = 17,
LEar = 18,
LBigToe = 19,
LSmallToe = 20,
LHeel = 21,
RBigToe = 22,
RSmallToe = 23,
RHeel = 24,
// ...auto-numbered hand + extra joints follow (see below)
};
After RHeel, the enum continues with auto-numbered entries (the compiler assigns sequential values; order-dependent):
- Right-hand fingers:
RightWrist, thenR{Thumb,Index,Middle,Ring,Pinky}Finger0..3— 4 joints per finger,0= base →3= tip. - Left-hand fingers:
LeftWrist, thenL{Thumb,Index,Middle,Ring,Pinky}Finger0..3— same layout. - Extra body joints:
UpperChest,Spine,RClavicle,LClavicle,RForearmeTwist0,LForearmeTwist0,RForearmeTwist1,LForearmeTwist1. - Sentinels:
Background, thenLast(the count sentinel — equals the number of keypoints).
Do not hardcode the integer values of the hand/extra joints. Only the fixed body block Nose=0 … RHeel=24 is guaranteed stable. The finger, twist, clavicle, chest/spine, and sentinel values are auto-numbered and will shift if the enum changes. Always reference them by name (Keypoints::RIndexFinger2), never by literal index.
KeypointsHelper::AllKeypoints
static const TArray<Keypoints>& KeypointsHelper::AllKeypoints();
Every enumerated keypoint, in order — iterate this instead of writing your own range, so hand/extra joints are included without hardcoding bounds.
for (Keypoints K : KeypointsHelper::AllKeypoints())
{
const FVector P = Frame[K]; // UE cm, after scaling
// ...
}
See also
USkeletonConsumer— receives these frames and drives characters from themUAR51Character— the mocap-driven mesh whoseSolve(positions, confidence)consumesKeypoints-indexed dataBoneTransform— per-bone handle the solver hands out- Class index — all Unreal SDK types