UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

915 lines 130 kB
/** This file must only contain pure code and pure imports */ import { WebXRAbstractFeature } from "./WebXRAbstractFeature.js"; import { WebXRFeatureName, WebXRFeaturesManager } from "../webXRFeaturesManager.js"; import { Matrix, Quaternion, Vector3 } from "../../Maths/math.vector.pure.js"; import { Observable } from "../../Misc/observable.pure.js"; import { TransformNode } from "../../Meshes/transformNode.pure.js"; import { Logger } from "../../Misc/logger.js"; /** * All 83 body joint names as defined by the WebXR Body Tracking specification. * @see https://immersive-web.github.io/body-tracking/#xrbody-interface */ export var WebXRBodyJoint; (function (WebXRBodyJoint) { /** The center of the hips / pelvis */ WebXRBodyJoint["HIPS"] = "hips"; /** Lower spine (lumbar) */ WebXRBodyJoint["SPINE_LOWER"] = "spine-lower"; /** Middle spine (thoracic) */ WebXRBodyJoint["SPINE_MIDDLE"] = "spine-middle"; /** Upper spine */ WebXRBodyJoint["SPINE_UPPER"] = "spine-upper"; /** Chest */ WebXRBodyJoint["CHEST"] = "chest"; /** Neck */ WebXRBodyJoint["NECK"] = "neck"; /** Head */ WebXRBodyJoint["HEAD"] = "head"; // ── Left Arm ────────────────────────────────────────────── /** Left shoulder */ WebXRBodyJoint["LEFT_SHOULDER"] = "left-shoulder"; /** Left scapula */ WebXRBodyJoint["LEFT_SCAPULA"] = "left-scapula"; /** Left upper arm */ WebXRBodyJoint["LEFT_ARM_UPPER"] = "left-arm-upper"; /** Left forearm (lower arm) */ WebXRBodyJoint["LEFT_ARM_LOWER"] = "left-arm-lower"; /** Left hand wrist twist (forearm twist) */ WebXRBodyJoint["LEFT_HAND_WRIST_TWIST"] = "left-hand-wrist-twist"; // ── Right Arm ───────────────────────────────────────────── /** Right shoulder */ WebXRBodyJoint["RIGHT_SHOULDER"] = "right-shoulder"; /** Right scapula */ WebXRBodyJoint["RIGHT_SCAPULA"] = "right-scapula"; /** Right upper arm */ WebXRBodyJoint["RIGHT_ARM_UPPER"] = "right-arm-upper"; /** Right forearm (lower arm) */ WebXRBodyJoint["RIGHT_ARM_LOWER"] = "right-arm-lower"; /** Right hand wrist twist (forearm twist) */ WebXRBodyJoint["RIGHT_HAND_WRIST_TWIST"] = "right-hand-wrist-twist"; // ── Left Hand ───────────────────────────────────────────── /** Left palm center */ WebXRBodyJoint["LEFT_HAND_PALM"] = "left-hand-palm"; /** Left wrist */ WebXRBodyJoint["LEFT_HAND_WRIST"] = "left-hand-wrist"; /** Left thumb metacarpal */ WebXRBodyJoint["LEFT_HAND_THUMB_METACARPAL"] = "left-hand-thumb-metacarpal"; /** Left thumb proximal phalanx */ WebXRBodyJoint["LEFT_HAND_THUMB_PHALANX_PROXIMAL"] = "left-hand-thumb-phalanx-proximal"; /** Left thumb distal phalanx */ WebXRBodyJoint["LEFT_HAND_THUMB_PHALANX_DISTAL"] = "left-hand-thumb-phalanx-distal"; /** Left thumb tip */ WebXRBodyJoint["LEFT_HAND_THUMB_TIP"] = "left-hand-thumb-tip"; /** Left index finger metacarpal */ WebXRBodyJoint["LEFT_HAND_INDEX_METACARPAL"] = "left-hand-index-metacarpal"; /** Left index finger proximal phalanx */ WebXRBodyJoint["LEFT_HAND_INDEX_PHALANX_PROXIMAL"] = "left-hand-index-phalanx-proximal"; /** Left index finger intermediate phalanx */ WebXRBodyJoint["LEFT_HAND_INDEX_PHALANX_INTERMEDIATE"] = "left-hand-index-phalanx-intermediate"; /** Left index finger distal phalanx */ WebXRBodyJoint["LEFT_HAND_INDEX_PHALANX_DISTAL"] = "left-hand-index-phalanx-distal"; /** Left index finger tip */ WebXRBodyJoint["LEFT_HAND_INDEX_TIP"] = "left-hand-index-tip"; /** Left middle finger metacarpal */ WebXRBodyJoint["LEFT_HAND_MIDDLE_METACARPAL"] = "left-hand-middle-metacarpal"; /** Left middle finger proximal phalanx */ WebXRBodyJoint["LEFT_HAND_MIDDLE_PHALANX_PROXIMAL"] = "left-hand-middle-phalanx-proximal"; /** Left middle finger intermediate phalanx */ WebXRBodyJoint["LEFT_HAND_MIDDLE_PHALANX_INTERMEDIATE"] = "left-hand-middle-phalanx-intermediate"; /** Left middle finger distal phalanx */ WebXRBodyJoint["LEFT_HAND_MIDDLE_PHALANX_DISTAL"] = "left-hand-middle-phalanx-distal"; /** Left middle finger tip */ WebXRBodyJoint["LEFT_HAND_MIDDLE_TIP"] = "left-hand-middle-tip"; /** Left ring finger metacarpal */ WebXRBodyJoint["LEFT_HAND_RING_METACARPAL"] = "left-hand-ring-metacarpal"; /** Left ring finger proximal phalanx */ WebXRBodyJoint["LEFT_HAND_RING_PHALANX_PROXIMAL"] = "left-hand-ring-phalanx-proximal"; /** Left ring finger intermediate phalanx */ WebXRBodyJoint["LEFT_HAND_RING_PHALANX_INTERMEDIATE"] = "left-hand-ring-phalanx-intermediate"; /** Left ring finger distal phalanx */ WebXRBodyJoint["LEFT_HAND_RING_PHALANX_DISTAL"] = "left-hand-ring-phalanx-distal"; /** Left ring finger tip */ WebXRBodyJoint["LEFT_HAND_RING_TIP"] = "left-hand-ring-tip"; /** Left little finger metacarpal */ WebXRBodyJoint["LEFT_HAND_LITTLE_METACARPAL"] = "left-hand-little-metacarpal"; /** Left little finger proximal phalanx */ WebXRBodyJoint["LEFT_HAND_LITTLE_PHALANX_PROXIMAL"] = "left-hand-little-phalanx-proximal"; /** Left little finger intermediate phalanx */ WebXRBodyJoint["LEFT_HAND_LITTLE_PHALANX_INTERMEDIATE"] = "left-hand-little-phalanx-intermediate"; /** Left little finger distal phalanx */ WebXRBodyJoint["LEFT_HAND_LITTLE_PHALANX_DISTAL"] = "left-hand-little-phalanx-distal"; /** Left little finger tip */ WebXRBodyJoint["LEFT_HAND_LITTLE_TIP"] = "left-hand-little-tip"; // ── Right Hand ──────────────────────────────────────────── /** Right palm center */ WebXRBodyJoint["RIGHT_HAND_PALM"] = "right-hand-palm"; /** Right wrist */ WebXRBodyJoint["RIGHT_HAND_WRIST"] = "right-hand-wrist"; /** Right thumb metacarpal */ WebXRBodyJoint["RIGHT_HAND_THUMB_METACARPAL"] = "right-hand-thumb-metacarpal"; /** Right thumb proximal phalanx */ WebXRBodyJoint["RIGHT_HAND_THUMB_PHALANX_PROXIMAL"] = "right-hand-thumb-phalanx-proximal"; /** Right thumb distal phalanx */ WebXRBodyJoint["RIGHT_HAND_THUMB_PHALANX_DISTAL"] = "right-hand-thumb-phalanx-distal"; /** Right thumb tip */ WebXRBodyJoint["RIGHT_HAND_THUMB_TIP"] = "right-hand-thumb-tip"; /** Right index finger metacarpal */ WebXRBodyJoint["RIGHT_HAND_INDEX_METACARPAL"] = "right-hand-index-metacarpal"; /** Right index finger proximal phalanx */ WebXRBodyJoint["RIGHT_HAND_INDEX_PHALANX_PROXIMAL"] = "right-hand-index-phalanx-proximal"; /** Right index finger intermediate phalanx */ WebXRBodyJoint["RIGHT_HAND_INDEX_PHALANX_INTERMEDIATE"] = "right-hand-index-phalanx-intermediate"; /** Right index finger distal phalanx */ WebXRBodyJoint["RIGHT_HAND_INDEX_PHALANX_DISTAL"] = "right-hand-index-phalanx-distal"; /** Right index finger tip */ WebXRBodyJoint["RIGHT_HAND_INDEX_TIP"] = "right-hand-index-tip"; /** Right middle finger metacarpal */ WebXRBodyJoint["RIGHT_HAND_MIDDLE_METACARPAL"] = "right-hand-middle-metacarpal"; /** Right middle finger proximal phalanx */ WebXRBodyJoint["RIGHT_HAND_MIDDLE_PHALANX_PROXIMAL"] = "right-hand-middle-phalanx-proximal"; /** Right middle finger intermediate phalanx */ WebXRBodyJoint["RIGHT_HAND_MIDDLE_PHALANX_INTERMEDIATE"] = "right-hand-middle-phalanx-intermediate"; /** Right middle finger distal phalanx */ WebXRBodyJoint["RIGHT_HAND_MIDDLE_PHALANX_DISTAL"] = "right-hand-middle-phalanx-distal"; /** Right middle finger tip */ WebXRBodyJoint["RIGHT_HAND_MIDDLE_TIP"] = "right-hand-middle-tip"; /** Right ring finger metacarpal */ WebXRBodyJoint["RIGHT_HAND_RING_METACARPAL"] = "right-hand-ring-metacarpal"; /** Right ring finger proximal phalanx */ WebXRBodyJoint["RIGHT_HAND_RING_PHALANX_PROXIMAL"] = "right-hand-ring-phalanx-proximal"; /** Right ring finger intermediate phalanx */ WebXRBodyJoint["RIGHT_HAND_RING_PHALANX_INTERMEDIATE"] = "right-hand-ring-phalanx-intermediate"; /** Right ring finger distal phalanx */ WebXRBodyJoint["RIGHT_HAND_RING_PHALANX_DISTAL"] = "right-hand-ring-phalanx-distal"; /** Right ring finger tip */ WebXRBodyJoint["RIGHT_HAND_RING_TIP"] = "right-hand-ring-tip"; /** Right little finger metacarpal */ WebXRBodyJoint["RIGHT_HAND_LITTLE_METACARPAL"] = "right-hand-little-metacarpal"; /** Right little finger proximal phalanx */ WebXRBodyJoint["RIGHT_HAND_LITTLE_PHALANX_PROXIMAL"] = "right-hand-little-phalanx-proximal"; /** Right little finger intermediate phalanx */ WebXRBodyJoint["RIGHT_HAND_LITTLE_PHALANX_INTERMEDIATE"] = "right-hand-little-phalanx-intermediate"; /** Right little finger distal phalanx */ WebXRBodyJoint["RIGHT_HAND_LITTLE_PHALANX_DISTAL"] = "right-hand-little-phalanx-distal"; /** Right little finger tip */ WebXRBodyJoint["RIGHT_HAND_LITTLE_TIP"] = "right-hand-little-tip"; // ── Left Leg / Foot ─────────────────────────────────────── /** Left upper leg (thigh) */ WebXRBodyJoint["LEFT_UPPER_LEG"] = "left-upper-leg"; /** Left lower leg (shin) */ WebXRBodyJoint["LEFT_LOWER_LEG"] = "left-lower-leg"; /** Left foot ankle twist */ WebXRBodyJoint["LEFT_FOOT_ANKLE_TWIST"] = "left-foot-ankle-twist"; /** Left foot ankle */ WebXRBodyJoint["LEFT_FOOT_ANKLE"] = "left-foot-ankle"; /** Left foot subtalar */ WebXRBodyJoint["LEFT_FOOT_SUBTALAR"] = "left-foot-subtalar"; /** Left foot transverse */ WebXRBodyJoint["LEFT_FOOT_TRANSVERSE"] = "left-foot-transverse"; /** Left foot ball */ WebXRBodyJoint["LEFT_FOOT_BALL"] = "left-foot-ball"; // ── Right Leg / Foot ────────────────────────────────────── /** Right upper leg (thigh) */ WebXRBodyJoint["RIGHT_UPPER_LEG"] = "right-upper-leg"; /** Right lower leg (shin) */ WebXRBodyJoint["RIGHT_LOWER_LEG"] = "right-lower-leg"; /** Right foot ankle twist */ WebXRBodyJoint["RIGHT_FOOT_ANKLE_TWIST"] = "right-foot-ankle-twist"; /** Right foot ankle */ WebXRBodyJoint["RIGHT_FOOT_ANKLE"] = "right-foot-ankle"; /** Right foot subtalar */ WebXRBodyJoint["RIGHT_FOOT_SUBTALAR"] = "right-foot-subtalar"; /** Right foot transverse */ WebXRBodyJoint["RIGHT_FOOT_TRANSVERSE"] = "right-foot-transverse"; /** Right foot ball */ WebXRBodyJoint["RIGHT_FOOT_BALL"] = "right-foot-ball"; })(WebXRBodyJoint || (WebXRBodyJoint = {})); /** * The ordered array of all 83 body joints, matching the iteration order defined * by the WebXR Body Tracking specification. * @see https://immersive-web.github.io/body-tracking/#xrbody-interface */ const BodyJointReferenceArray = [ "hips" /* WebXRBodyJoint.HIPS */, "spine-lower" /* WebXRBodyJoint.SPINE_LOWER */, "spine-middle" /* WebXRBodyJoint.SPINE_MIDDLE */, "spine-upper" /* WebXRBodyJoint.SPINE_UPPER */, "chest" /* WebXRBodyJoint.CHEST */, "neck" /* WebXRBodyJoint.NECK */, "head" /* WebXRBodyJoint.HEAD */, "left-shoulder" /* WebXRBodyJoint.LEFT_SHOULDER */, "left-scapula" /* WebXRBodyJoint.LEFT_SCAPULA */, "left-arm-upper" /* WebXRBodyJoint.LEFT_ARM_UPPER */, "left-arm-lower" /* WebXRBodyJoint.LEFT_ARM_LOWER */, "left-hand-wrist-twist" /* WebXRBodyJoint.LEFT_HAND_WRIST_TWIST */, "right-shoulder" /* WebXRBodyJoint.RIGHT_SHOULDER */, "right-scapula" /* WebXRBodyJoint.RIGHT_SCAPULA */, "right-arm-upper" /* WebXRBodyJoint.RIGHT_ARM_UPPER */, "right-arm-lower" /* WebXRBodyJoint.RIGHT_ARM_LOWER */, "right-hand-wrist-twist" /* WebXRBodyJoint.RIGHT_HAND_WRIST_TWIST */, "left-hand-palm" /* WebXRBodyJoint.LEFT_HAND_PALM */, "left-hand-wrist" /* WebXRBodyJoint.LEFT_HAND_WRIST */, "left-hand-thumb-metacarpal" /* WebXRBodyJoint.LEFT_HAND_THUMB_METACARPAL */, "left-hand-thumb-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_THUMB_PHALANX_PROXIMAL */, "left-hand-thumb-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_THUMB_PHALANX_DISTAL */, "left-hand-thumb-tip" /* WebXRBodyJoint.LEFT_HAND_THUMB_TIP */, "left-hand-index-metacarpal" /* WebXRBodyJoint.LEFT_HAND_INDEX_METACARPAL */, "left-hand-index-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_INDEX_PHALANX_PROXIMAL */, "left-hand-index-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_INDEX_PHALANX_INTERMEDIATE */, "left-hand-index-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_INDEX_PHALANX_DISTAL */, "left-hand-index-tip" /* WebXRBodyJoint.LEFT_HAND_INDEX_TIP */, "left-hand-middle-metacarpal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_METACARPAL */, "left-hand-middle-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_PHALANX_PROXIMAL */, "left-hand-middle-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_PHALANX_INTERMEDIATE */, "left-hand-middle-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_PHALANX_DISTAL */, "left-hand-middle-tip" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_TIP */, "left-hand-ring-metacarpal" /* WebXRBodyJoint.LEFT_HAND_RING_METACARPAL */, "left-hand-ring-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_RING_PHALANX_PROXIMAL */, "left-hand-ring-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_RING_PHALANX_INTERMEDIATE */, "left-hand-ring-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_RING_PHALANX_DISTAL */, "left-hand-ring-tip" /* WebXRBodyJoint.LEFT_HAND_RING_TIP */, "left-hand-little-metacarpal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_METACARPAL */, "left-hand-little-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_PHALANX_PROXIMAL */, "left-hand-little-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_LITTLE_PHALANX_INTERMEDIATE */, "left-hand-little-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_PHALANX_DISTAL */, "left-hand-little-tip" /* WebXRBodyJoint.LEFT_HAND_LITTLE_TIP */, "right-hand-palm" /* WebXRBodyJoint.RIGHT_HAND_PALM */, "right-hand-wrist" /* WebXRBodyJoint.RIGHT_HAND_WRIST */, "right-hand-thumb-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_THUMB_METACARPAL */, "right-hand-thumb-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_THUMB_PHALANX_PROXIMAL */, "right-hand-thumb-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_THUMB_PHALANX_DISTAL */, "right-hand-thumb-tip" /* WebXRBodyJoint.RIGHT_HAND_THUMB_TIP */, "right-hand-index-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_METACARPAL */, "right-hand-index-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_PHALANX_PROXIMAL */, "right-hand-index-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_INDEX_PHALANX_INTERMEDIATE */, "right-hand-index-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_PHALANX_DISTAL */, "right-hand-index-tip" /* WebXRBodyJoint.RIGHT_HAND_INDEX_TIP */, "right-hand-middle-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_METACARPAL */, "right-hand-middle-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_PHALANX_PROXIMAL */, "right-hand-middle-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_PHALANX_INTERMEDIATE */, "right-hand-middle-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_PHALANX_DISTAL */, "right-hand-middle-tip" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_TIP */, "right-hand-ring-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_RING_METACARPAL */, "right-hand-ring-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_RING_PHALANX_PROXIMAL */, "right-hand-ring-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_RING_PHALANX_INTERMEDIATE */, "right-hand-ring-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_RING_PHALANX_DISTAL */, "right-hand-ring-tip" /* WebXRBodyJoint.RIGHT_HAND_RING_TIP */, "right-hand-little-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_METACARPAL */, "right-hand-little-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_PHALANX_PROXIMAL */, "right-hand-little-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_PHALANX_INTERMEDIATE */, "right-hand-little-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_PHALANX_DISTAL */, "right-hand-little-tip" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_TIP */, "left-upper-leg" /* WebXRBodyJoint.LEFT_UPPER_LEG */, "left-lower-leg" /* WebXRBodyJoint.LEFT_LOWER_LEG */, "left-foot-ankle-twist" /* WebXRBodyJoint.LEFT_FOOT_ANKLE_TWIST */, "left-foot-ankle" /* WebXRBodyJoint.LEFT_FOOT_ANKLE */, "left-foot-subtalar" /* WebXRBodyJoint.LEFT_FOOT_SUBTALAR */, "left-foot-transverse" /* WebXRBodyJoint.LEFT_FOOT_TRANSVERSE */, "left-foot-ball" /* WebXRBodyJoint.LEFT_FOOT_BALL */, "right-upper-leg" /* WebXRBodyJoint.RIGHT_UPPER_LEG */, "right-lower-leg" /* WebXRBodyJoint.RIGHT_LOWER_LEG */, "right-foot-ankle-twist" /* WebXRBodyJoint.RIGHT_FOOT_ANKLE_TWIST */, "right-foot-ankle" /* WebXRBodyJoint.RIGHT_FOOT_ANKLE */, "right-foot-subtalar" /* WebXRBodyJoint.RIGHT_FOOT_SUBTALAR */, "right-foot-transverse" /* WebXRBodyJoint.RIGHT_FOOT_TRANSVERSE */, "right-foot-ball" /* WebXRBodyJoint.RIGHT_FOOT_BALL */, ]; /** * Reverse lookup: {@link WebXRBodyJoint} → index in {@link BodyJointReferenceArray}. * Used for O(1) name-based access where previously `indexOf` was called in hot paths. */ const BodyJointNameToIndex = /*#__PURE__*/ new Map(BodyJointReferenceArray.map((j, i) => [j, i])); /** * The total number of joints in the body tracking spec. * The XRBody size attribute MUST return this value. */ const BODY_JOINT_COUNT = 83; /** * Parent index for each joint in {@link BodyJointReferenceArray} order. * -1 means "root" (no parent). Used to convert world-space XR poses to * local-space transforms suitable for skeleton bones. * * Hierarchy follows the WebXR Body Tracking specification and standard * humanoid anatomy. */ // prettier-ignore export const BodyJointParentIndex = [ // 0: hips (root) -1, // 1: spine-lower → hips 0, // 2: spine-middle → spine-lower 1, // 3: spine-upper → spine-middle 2, // 4: chest → spine-upper 3, // 5: neck → chest 4, // 6: head → neck 5, // Left arm (7-11) 4, // 7: left-shoulder → chest 7, // 8: left-scapula → left-shoulder 8, // 9: left-arm-upper → left-scapula 9, // 10: left-arm-lower → left-arm-upper 10, // 11: left-hand-wrist-twist → left-arm-lower // Right arm (12-16) 4, // 12: right-shoulder → chest 12, // 13: right-scapula → right-shoulder 13, // 14: right-arm-upper → right-scapula 14, // 15: right-arm-lower → right-arm-upper 15, // 16: right-hand-wrist-twist → right-arm-lower // Left hand (17-42) 11, // 17: left-hand-palm → left-hand-wrist-twist 11, // 18: left-hand-wrist → left-hand-wrist-twist 18, // 19: left-hand-thumb-metacarpal → left-hand-wrist 19, // 20: left-hand-thumb-phalanx-proximal 20, // 21: left-hand-thumb-phalanx-distal 21, // 22: left-hand-thumb-tip 18, // 23: left-hand-index-metacarpal → left-hand-wrist 23, // 24: left-hand-index-phalanx-proximal 24, // 25: left-hand-index-phalanx-intermediate 25, // 26: left-hand-index-phalanx-distal 26, // 27: left-hand-index-tip 18, // 28: left-hand-middle-metacarpal → left-hand-wrist 28, // 29: left-hand-middle-phalanx-proximal 29, // 30: left-hand-middle-phalanx-intermediate 30, // 31: left-hand-middle-phalanx-distal 31, // 32: left-hand-middle-tip 18, // 33: left-hand-ring-metacarpal → left-hand-wrist 33, // 34: left-hand-ring-phalanx-proximal 34, // 35: left-hand-ring-phalanx-intermediate 35, // 36: left-hand-ring-phalanx-distal 36, // 37: left-hand-ring-tip 18, // 38: left-hand-little-metacarpal → left-hand-wrist 38, // 39: left-hand-little-phalanx-proximal 39, // 40: left-hand-little-phalanx-intermediate 40, // 41: left-hand-little-phalanx-distal 41, // 42: left-hand-little-tip // Right hand (43-68) 16, // 43: right-hand-palm → right-hand-wrist-twist 16, // 44: right-hand-wrist → right-hand-wrist-twist 44, // 45: right-hand-thumb-metacarpal → right-hand-wrist 45, // 46: right-hand-thumb-phalanx-proximal 46, // 47: right-hand-thumb-phalanx-distal 47, // 48: right-hand-thumb-tip 44, // 49: right-hand-index-metacarpal → right-hand-wrist 49, // 50: right-hand-index-phalanx-proximal 50, // 51: right-hand-index-phalanx-intermediate 51, // 52: right-hand-index-phalanx-distal 52, // 53: right-hand-index-tip 44, // 54: right-hand-middle-metacarpal → right-hand-wrist 54, // 55: right-hand-middle-phalanx-proximal 55, // 56: right-hand-middle-phalanx-intermediate 56, // 57: right-hand-middle-phalanx-distal 57, // 58: right-hand-middle-tip 44, // 59: right-hand-ring-metacarpal → right-hand-wrist 59, // 60: right-hand-ring-phalanx-proximal 60, // 61: right-hand-ring-phalanx-intermediate 61, // 62: right-hand-ring-phalanx-distal 62, // 63: right-hand-ring-tip 44, // 64: right-hand-little-metacarpal → right-hand-wrist 64, // 65: right-hand-little-phalanx-proximal 65, // 66: right-hand-little-phalanx-intermediate 66, // 67: right-hand-little-phalanx-distal 67, // 68: right-hand-little-tip // Left leg / foot (69-75) 0, // 69: left-upper-leg → hips 69, // 70: left-lower-leg → left-upper-leg 70, // 71: left-foot-ankle-twist → left-lower-leg 71, // 72: left-foot-ankle → left-foot-ankle-twist 72, // 73: left-foot-subtalar → left-foot-ankle 73, // 74: left-foot-transverse → left-foot-subtalar 74, // 75: left-foot-ball → left-foot-transverse // Right leg / foot (76-82) 0, // 76: right-upper-leg → hips 76, // 77: right-lower-leg → right-upper-leg 77, // 78: right-foot-ankle-twist → right-lower-leg 78, // 79: right-foot-ankle → right-foot-ankle-twist 79, // 80: right-foot-subtalar → right-foot-ankle 80, // 81: right-foot-transverse → right-foot-subtalar 81, // 82: right-foot-ball → right-foot-transverse ]; /** * Logical body parts for convenient grouping of joints. */ export var BodyPart; (function (BodyPart) { /** Torso / spine (hips through head) */ BodyPart["TORSO"] = "torso"; /** Left arm (shoulder through wrist twist) */ BodyPart["LEFT_ARM"] = "left-arm"; /** Right arm (shoulder through wrist twist) */ BodyPart["RIGHT_ARM"] = "right-arm"; /** Left hand (palm through finger tips) */ BodyPart["LEFT_HAND"] = "left-hand"; /** Right hand (palm through finger tips) */ BodyPart["RIGHT_HAND"] = "right-hand"; /** Left leg (upper leg through foot ball) */ BodyPart["LEFT_LEG"] = "left-leg"; /** Right leg (upper leg through foot ball) */ BodyPart["RIGHT_LEG"] = "right-leg"; })(BodyPart || (BodyPart = {})); /** * Which body joints belong to each body part. */ const BodyPartsDefinition = { ["torso" /* BodyPart.TORSO */]: [ "hips" /* WebXRBodyJoint.HIPS */, "spine-lower" /* WebXRBodyJoint.SPINE_LOWER */, "spine-middle" /* WebXRBodyJoint.SPINE_MIDDLE */, "spine-upper" /* WebXRBodyJoint.SPINE_UPPER */, "chest" /* WebXRBodyJoint.CHEST */, "neck" /* WebXRBodyJoint.NECK */, "head" /* WebXRBodyJoint.HEAD */, ], ["left-arm" /* BodyPart.LEFT_ARM */]: [ "left-shoulder" /* WebXRBodyJoint.LEFT_SHOULDER */, "left-scapula" /* WebXRBodyJoint.LEFT_SCAPULA */, "left-arm-upper" /* WebXRBodyJoint.LEFT_ARM_UPPER */, "left-arm-lower" /* WebXRBodyJoint.LEFT_ARM_LOWER */, "left-hand-wrist-twist" /* WebXRBodyJoint.LEFT_HAND_WRIST_TWIST */, ], ["right-arm" /* BodyPart.RIGHT_ARM */]: [ "right-shoulder" /* WebXRBodyJoint.RIGHT_SHOULDER */, "right-scapula" /* WebXRBodyJoint.RIGHT_SCAPULA */, "right-arm-upper" /* WebXRBodyJoint.RIGHT_ARM_UPPER */, "right-arm-lower" /* WebXRBodyJoint.RIGHT_ARM_LOWER */, "right-hand-wrist-twist" /* WebXRBodyJoint.RIGHT_HAND_WRIST_TWIST */, ], ["left-hand" /* BodyPart.LEFT_HAND */]: [ "left-hand-palm" /* WebXRBodyJoint.LEFT_HAND_PALM */, "left-hand-wrist" /* WebXRBodyJoint.LEFT_HAND_WRIST */, "left-hand-thumb-metacarpal" /* WebXRBodyJoint.LEFT_HAND_THUMB_METACARPAL */, "left-hand-thumb-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_THUMB_PHALANX_PROXIMAL */, "left-hand-thumb-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_THUMB_PHALANX_DISTAL */, "left-hand-thumb-tip" /* WebXRBodyJoint.LEFT_HAND_THUMB_TIP */, "left-hand-index-metacarpal" /* WebXRBodyJoint.LEFT_HAND_INDEX_METACARPAL */, "left-hand-index-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_INDEX_PHALANX_PROXIMAL */, "left-hand-index-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_INDEX_PHALANX_INTERMEDIATE */, "left-hand-index-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_INDEX_PHALANX_DISTAL */, "left-hand-index-tip" /* WebXRBodyJoint.LEFT_HAND_INDEX_TIP */, "left-hand-middle-metacarpal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_METACARPAL */, "left-hand-middle-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_PHALANX_PROXIMAL */, "left-hand-middle-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_PHALANX_INTERMEDIATE */, "left-hand-middle-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_PHALANX_DISTAL */, "left-hand-middle-tip" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_TIP */, "left-hand-ring-metacarpal" /* WebXRBodyJoint.LEFT_HAND_RING_METACARPAL */, "left-hand-ring-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_RING_PHALANX_PROXIMAL */, "left-hand-ring-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_RING_PHALANX_INTERMEDIATE */, "left-hand-ring-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_RING_PHALANX_DISTAL */, "left-hand-ring-tip" /* WebXRBodyJoint.LEFT_HAND_RING_TIP */, "left-hand-little-metacarpal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_METACARPAL */, "left-hand-little-phalanx-proximal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_PHALANX_PROXIMAL */, "left-hand-little-phalanx-intermediate" /* WebXRBodyJoint.LEFT_HAND_LITTLE_PHALANX_INTERMEDIATE */, "left-hand-little-phalanx-distal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_PHALANX_DISTAL */, "left-hand-little-tip" /* WebXRBodyJoint.LEFT_HAND_LITTLE_TIP */, ], ["right-hand" /* BodyPart.RIGHT_HAND */]: [ "right-hand-palm" /* WebXRBodyJoint.RIGHT_HAND_PALM */, "right-hand-wrist" /* WebXRBodyJoint.RIGHT_HAND_WRIST */, "right-hand-thumb-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_THUMB_METACARPAL */, "right-hand-thumb-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_THUMB_PHALANX_PROXIMAL */, "right-hand-thumb-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_THUMB_PHALANX_DISTAL */, "right-hand-thumb-tip" /* WebXRBodyJoint.RIGHT_HAND_THUMB_TIP */, "right-hand-index-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_METACARPAL */, "right-hand-index-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_PHALANX_PROXIMAL */, "right-hand-index-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_INDEX_PHALANX_INTERMEDIATE */, "right-hand-index-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_PHALANX_DISTAL */, "right-hand-index-tip" /* WebXRBodyJoint.RIGHT_HAND_INDEX_TIP */, "right-hand-middle-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_METACARPAL */, "right-hand-middle-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_PHALANX_PROXIMAL */, "right-hand-middle-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_PHALANX_INTERMEDIATE */, "right-hand-middle-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_PHALANX_DISTAL */, "right-hand-middle-tip" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_TIP */, "right-hand-ring-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_RING_METACARPAL */, "right-hand-ring-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_RING_PHALANX_PROXIMAL */, "right-hand-ring-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_RING_PHALANX_INTERMEDIATE */, "right-hand-ring-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_RING_PHALANX_DISTAL */, "right-hand-ring-tip" /* WebXRBodyJoint.RIGHT_HAND_RING_TIP */, "right-hand-little-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_METACARPAL */, "right-hand-little-phalanx-proximal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_PHALANX_PROXIMAL */, "right-hand-little-phalanx-intermediate" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_PHALANX_INTERMEDIATE */, "right-hand-little-phalanx-distal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_PHALANX_DISTAL */, "right-hand-little-tip" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_TIP */, ], ["left-leg" /* BodyPart.LEFT_LEG */]: [ "left-upper-leg" /* WebXRBodyJoint.LEFT_UPPER_LEG */, "left-lower-leg" /* WebXRBodyJoint.LEFT_LOWER_LEG */, "left-foot-ankle-twist" /* WebXRBodyJoint.LEFT_FOOT_ANKLE_TWIST */, "left-foot-ankle" /* WebXRBodyJoint.LEFT_FOOT_ANKLE */, "left-foot-subtalar" /* WebXRBodyJoint.LEFT_FOOT_SUBTALAR */, "left-foot-transverse" /* WebXRBodyJoint.LEFT_FOOT_TRANSVERSE */, "left-foot-ball" /* WebXRBodyJoint.LEFT_FOOT_BALL */, ], ["right-leg" /* BodyPart.RIGHT_LEG */]: [ "right-upper-leg" /* WebXRBodyJoint.RIGHT_UPPER_LEG */, "right-lower-leg" /* WebXRBodyJoint.RIGHT_LOWER_LEG */, "right-foot-ankle-twist" /* WebXRBodyJoint.RIGHT_FOOT_ANKLE_TWIST */, "right-foot-ankle" /* WebXRBodyJoint.RIGHT_FOOT_ANKLE */, "right-foot-subtalar" /* WebXRBodyJoint.RIGHT_FOOT_SUBTALAR */, "right-foot-transverse" /* WebXRBodyJoint.RIGHT_FOOT_TRANSVERSE */, "right-foot-ball" /* WebXRBodyJoint.RIGHT_FOOT_BALL */, ], }; // ──────────────────────────────────────────────────────────────────────────── // Built-in rig mappings // ──────────────────────────────────────────────────────────────────────────── /** * Default rig mapping for Mixamo-rigged humanoid characters. * * Maps each supported {@link WebXRBodyJoint} to the corresponding Mixamo bone * name, **without** the `mixamorig:` prefix. When the feature applies this * mapping, it auto-detects whether the skeleton uses the `mixamorig:` prefix * and prepends it as needed, so the same table works for both prefixed and * unprefixed exports. * * @example * ```ts * xr.featuresManager.enableFeature(WebXRFeatureName.BODY_TRACKING, "latest", { * bodyMesh: myMixamoMesh, * isMixamoModel: true, * }); * ``` * * Or, if you want to extend or customize it: * ```ts * import { MixamoRigMapping } from "@babylonjs/core"; * const rigMapping: XRBodyMeshRigMapping = { ...MixamoRigMapping, [WebXRBodyJoint.NECK]: "MyNeckBone" }; * ``` */ export const MixamoRigMapping = { ["hips" /* WebXRBodyJoint.HIPS */]: "Hips", ["spine-lower" /* WebXRBodyJoint.SPINE_LOWER */]: "Spine", ["spine-middle" /* WebXRBodyJoint.SPINE_MIDDLE */]: "Spine1", ["spine-upper" /* WebXRBodyJoint.SPINE_UPPER */]: "Spine2", ["neck" /* WebXRBodyJoint.NECK */]: "Neck", ["head" /* WebXRBodyJoint.HEAD */]: "Head", ["left-shoulder" /* WebXRBodyJoint.LEFT_SHOULDER */]: "LeftShoulder", ["left-arm-upper" /* WebXRBodyJoint.LEFT_ARM_UPPER */]: "LeftArm", ["left-arm-lower" /* WebXRBodyJoint.LEFT_ARM_LOWER */]: "LeftForeArm", ["left-hand-wrist" /* WebXRBodyJoint.LEFT_HAND_WRIST */]: "LeftHand", ["right-shoulder" /* WebXRBodyJoint.RIGHT_SHOULDER */]: "RightShoulder", ["right-arm-upper" /* WebXRBodyJoint.RIGHT_ARM_UPPER */]: "RightArm", ["right-arm-lower" /* WebXRBodyJoint.RIGHT_ARM_LOWER */]: "RightForeArm", ["right-hand-wrist" /* WebXRBodyJoint.RIGHT_HAND_WRIST */]: "RightHand", ["left-upper-leg" /* WebXRBodyJoint.LEFT_UPPER_LEG */]: "LeftUpLeg", ["left-lower-leg" /* WebXRBodyJoint.LEFT_LOWER_LEG */]: "LeftLeg", ["left-foot-ankle" /* WebXRBodyJoint.LEFT_FOOT_ANKLE */]: "LeftFoot", ["left-foot-ball" /* WebXRBodyJoint.LEFT_FOOT_BALL */]: "LeftToeBase", ["right-upper-leg" /* WebXRBodyJoint.RIGHT_UPPER_LEG */]: "RightUpLeg", ["right-lower-leg" /* WebXRBodyJoint.RIGHT_LOWER_LEG */]: "RightLeg", ["right-foot-ankle" /* WebXRBodyJoint.RIGHT_FOOT_ANKLE */]: "RightFoot", ["right-foot-ball" /* WebXRBodyJoint.RIGHT_FOOT_BALL */]: "RightToeBase", }; /** * Default aim-child overrides for Mixamo-rigged humanoids. * * Redirects the short / noisy XR spine segments to longer, stable ones so that * {@link IWebXRBodyTrackingOptions.useBoneOrientationOffsets} produces clean * torso rotations. In WebXR data, `hips`→`spine-lower` is typically only ~1 cm * apart — too short to give a stable aim direction — so we reroute Mixamo's * Hips/Spine/Spine1 bones to aim at `spine-upper` / `neck` instead. */ export const MixamoAimChildOverrides = { ["hips" /* WebXRBodyJoint.HIPS */]: "spine-upper" /* WebXRBodyJoint.SPINE_UPPER */, ["spine-lower" /* WebXRBodyJoint.SPINE_LOWER */]: "neck" /* WebXRBodyJoint.NECK */, ["spine-middle" /* WebXRBodyJoint.SPINE_MIDDLE */]: "neck" /* WebXRBodyJoint.NECK */, // Hands have no mapped finger descendants on a typical Mixamo rig. Aim // them at the tracked Middle-finger metacarpal joint to give the wrist // a real orientation reference instead of relying on Meta's raw wrist // rotation (which tends to flop around when the cameras can't resolve // the hand pose). ["left-hand-wrist" /* WebXRBodyJoint.LEFT_HAND_WRIST */]: "left-hand-middle-metacarpal" /* WebXRBodyJoint.LEFT_HAND_MIDDLE_METACARPAL */, ["right-hand-wrist" /* WebXRBodyJoint.RIGHT_HAND_WRIST */]: "right-hand-middle-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_MIDDLE_METACARPAL */, }; const HandTwistReferenceJoints = { ["left-hand-wrist" /* WebXRBodyJoint.LEFT_HAND_WRIST */]: { first: "left-hand-index-metacarpal" /* WebXRBodyJoint.LEFT_HAND_INDEX_METACARPAL */, second: "left-hand-little-metacarpal" /* WebXRBodyJoint.LEFT_HAND_LITTLE_METACARPAL */, }, ["right-hand-wrist" /* WebXRBodyJoint.RIGHT_HAND_WRIST */]: { first: "right-hand-index-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_INDEX_METACARPAL */, second: "right-hand-little-metacarpal" /* WebXRBodyJoint.RIGHT_HAND_LITTLE_METACARPAL */, }, }; /** * Resolve the Mixamo rig mapping for a given body mesh, auto-detecting the * `mixamorig:` bone-name prefix. Falls back to the unprefixed names. * @param bodyMesh The rigged Mixamo body mesh. * @returns An {@link XRBodyMeshRigMapping} whose bone names include the * detected prefix (if any). * @internal */ export function _ResolveMixamoRigMapping(bodyMesh) { let skeleton = bodyMesh.skeleton; if (!skeleton) { for (const child of bodyMesh.getChildMeshes()) { if (child.skeleton) { skeleton = child.skeleton; break; } } } let prefix = ""; if (skeleton) { // Look for the Hips bone to determine whether bones use the mixamorig: prefix. if (skeleton.getBoneIndexByName("mixamorig:Hips") !== -1) { prefix = "mixamorig:"; } else if (skeleton.getBoneIndexByName("Hips") === -1) { // Neither prefixed nor unprefixed Hips was found — try to auto-detect // the prefix by scanning bone names. for (const b of skeleton.bones) { const match = /^(.*?)Hips$/.exec(b.name); if (match) { prefix = match[1]; break; } } } } if (!prefix) { return MixamoRigMapping; } const resolved = {}; for (const key of Object.keys(MixamoRigMapping)) { resolved[key] = prefix + MixamoRigMapping[key]; } return resolved; } // ──────────────────────────────────────────────────────────────────────────── // WebXRTrackedBody — runtime representation of a tracked body // ──────────────────────────────────────────────────────────────────────────── /** * Represents a tracked body during a WebXR session. * * This class manages the bridge between the WebXR body pose data and the * Babylon.js scene graph. It creates a set of {@link TransformNode}s — one per * body joint — whose transforms are updated every frame from the XR runtime. * When a rigged body mesh is attached, its skeleton bones are linked to these * transform nodes, causing the mesh to follow the user's body automatically. * * Coordinate-system handling: * - WebXR delivers poses in a right-handed system. * - By default, Babylon.js uses a left-handed system. * - The class converts the data in-place (negating the Z components of every * 4 × 4 joint matrix) before decomposing into Babylon transforms. * - If the mesh was authored in a right-handed tool (the common case for glTF), * the bone transforms are un-flipped so the skeleton interprets them correctly. */ export class WebXRTrackedBody { /** * Get the current body mesh (if any). */ get bodyMesh() { return this._bodyMesh; } /** * Get or set the scale factor for local joint offsets. * @see {@link IWebXRBodyTrackingOptions.jointScaleFactor} */ get jointScaleFactor() { return this._jointScaleFactor; } set jointScaleFactor(value) { this._jointScaleFactor = value; } /** * Returns the array of transform nodes representing each body joint. * The order matches {@link WebXRBodyTracking.AllBodyJoints}; use * {@link getJointTransform} or {@link getBodyPartTransforms} for * name-based lookup. * * Note: when a body mesh is attached, these transform nodes are also * used as the skeleton's link targets for mapped joints. In that case * the values held by mapped-joint nodes are skeleton-local (parent bone's * frame), not XR world-space. Unmapped-joint nodes always hold world-space * pose. If you need world-space poses for every joint regardless of * mapping, sample the bone matrices directly via the attached skeleton. */ get jointTransforms() { return this._jointTransforms; } /** * Get the transform node for a specific body joint. * @param jointName The name of the body joint (from {@link WebXRBodyJoint}). * @returns The transform node corresponding to that joint, or `undefined` if not found. */ getJointTransform(jointName) { const idx = BodyJointNameToIndex.get(jointName); return idx !== undefined ? this._jointTransforms[idx] : undefined; } /** * Get all joint transform nodes that belong to a given body part. * @param part The body part to query. * @param result Optional pre-allocated array to fill (avoids per-call allocation). * The array is cleared and populated with the results. * @returns An array of TransformNodes for that body part. */ getBodyPartTransforms(part, result) { const joints = BodyPartsDefinition[part]; if (!result) { result = new Array(joints.length); } else { result.length = joints.length; } for (let i = 0; i < joints.length; i++) { result[i] = this._jointTransforms[BodyJointNameToIndex.get(joints[i])]; } return result; } /** * Construct a new tracked body instance. * @param scene The Babylon.js scene. * @param bodyMesh Optional rigged body mesh to attach immediately. * @param rigMapping Optional mapping from WebXR joint names to skeleton bone names. * @param jointScaleFactor Scale factor for local joint offsets (default 1.0). * @param preserveBindPoseBonePositions Whether mapped bones should keep bind-pose local translations. * @param useBoneOrientationOffsets Whether mapped bones should correct XR joint rotations using bind-space offsets. * @param aimChildOverrides Per–XR-joint override for the aim child used with `useBoneOrientationOffsets`. * @param jointLocalRotationOffset Optional rotation re-basing each XR joint's local frame (e.g. Z-along-bone → Y-along-bone). */ constructor(scene, bodyMesh, rigMapping, jointScaleFactor = 1.0, preserveBindPoseBonePositions = false, useBoneOrientationOffsets = false, aimChildOverrides, jointLocalRotationOffset) { /** * Fired when the body mesh is changed via {@link setBodyMesh}. */ this.onBodyMeshSetObservable = new Observable(); /** * One {@link TransformNode} per joint. These receive the WebXR matrix data * every frame and serve as link targets for skeleton bones. */ this._jointTransforms = new Array(BODY_JOINT_COUNT); /** * Flat Float32Array that receives transform matrices directly from the * WebXR API (via `fillPoses`). 16 floats per joint × 83 joints = 1 328 floats. */ this._jointTransformMatrices = new Float32Array(BODY_JOINT_COUNT * 16); /** * Copy of the raw RHS XR matrices (before LHS conversion). * Used to compute bone-local transforms for glTF skeletons that * operate in RHS space. */ this._jointTransformMatricesRHS = new Float32Array(BODY_JOINT_COUNT * 16); /** * Cached array of XRBodySpace objects extracted from the XRBody, kept in the * same order as {@link BodyJointReferenceArray}. */ this._jointSpaces = new Array(BODY_JOINT_COUNT); /** Temporary matrix: this joint's XR world-space matrix. */ this._tempJointMatrix = new Matrix(); /** Temporary matrix: parent joint's XR world-space matrix. */ this._tempParentMatrix = new Matrix(); /** Temporary matrix: computed bone-local matrix. */ this._tempLocalMatrix = new Matrix(); /** Temporary vector for scale extracted from decompose. */ this._tempScaleVector = new Vector3(); /** Temporary quaternion for decompose. */ this._tempRotQuat = new Quaternion(); /** Temporary position vector for decompose. */ this._tempPosVec = new Vector3(); /** Temporary quaternion for alternate rotation calculations. */ this._tempRotQuat2 = new Quaternion(); /** Temporary vector for desired child direction. */ this._tempDirection = new Vector3(); /** Temporary vector for joint-local child direction. */ this._tempLocalDirection = new Vector3(); /** Cached desired final positions for mapped joints. */ this._desiredFinalPositions = Array.from({ length: BODY_JOINT_COUNT }, () => new Vector3()); /** * For each joint index, the joint index of the nearest mapped SKELETON * ancestor bone. -1 when the bone has no mapped ancestor (root level). * Precomputed in {@link setBodyMesh} by walking the skeleton hierarchy. */ this._jointParentJointIdx = new Array(BODY_JOINT_COUNT).fill(-1); /** Tracks which joint indices have a linked bone (for step 4b). */ this._jointHasBone = new Array(BODY_JOINT_COUNT).fill(false); /** Bone → XR joint index lookup, built in {@link setBodyMesh}. */ this._boneToJointIdx = new Map(); /** Original bind-pose local matrices for mapped bones. */ this._mappedBoneBindLocals = new Map(); /** Nearest mapped child bone for each mapped bone. */ this._mappedChildBones = new Map(); /** Bind-space local child direction for each mapped bone. */ this._bindLocalAimDirections = new Map(); /** Bind-space local hand-plane normal used to correct wrist/hand twist from tracked finger positions. */ this._bindLocalTwistNormals = new Map(); /** * XR joint index to aim each mapped bone at. This can be a mapped joint * (same as `_boneToJointIdx.get(aimChildBone)`) or an **unmapped** XR * joint whose tracked position is nonetheless useful for aim correction — * e.g. `LEFT_HAND_MIDDLE_METACARPAL` for `mixamorig:LeftHand`, which has * no mapped finger descendant but whose tracked position still defines * "where the hand is pointing". */ this._boneAimTargetJointIdx = new Map(); /** Per-bone pair of tracked joints that define the hand plane used for twist correction. */ this._boneTwistReferenceJointIdx = new Map(); /** * Per-mapped-bone bind-pose world rotation in mesh-local space * (decomposed from `bone.getFinalMatrix()` at bind time). Used by the * delta-from-bind retarget path (axis-convention-invariant). */ this._bindBoneWorldRotMeshLocal = new Map(); /** * Bind-pose tracked joint rotation in mesh-local space per mapped joint. * Captured on the first tracked frame (or on demand via * {@link captureTrackedBind}). `null` until captured. */ this._trackedBindDesiredFinalRot = null; /** Bind-pose tracked joint position (mesh-local), captured alongside rotation. */ this._trackedBindDesiredFinalPos = null; /** True once a tracked-bind snapshot has been taken. */ this._hasTrackedBind = false; /** * When `true` (default), the first tracked frame after the feature * attaches is used as the "rest" pose for delta-from-bind retargeting. * Set to `false` to require an explicit {@link captureTrackedBind} call. */ this.autoCaptureBindOnFirstFrame = true; /** Scratch quaternion for retarget delta composition. */ this._tempDeltaQuat = new Quaternion(); this._tempBoneWorldRot = new Quaternion(); this._tempParentNewWorldRotInv = new Quaternion(); this._tempTrackedCurRot = new Quaternion(); this._tempTrackedCurPos = new Vector3(); this._tempBindLocalScale = new Vector3(); this._tempBindLocalPos = new Vector3(); /** * Per-bone cache of the current frame's computed world rotation. * Entries are pooled across frames (values are reused via `copyFrom`) * to avoid allocating a fresh Quaternion per mapped bone per frame. * A bone is considered "populated this frame" iff it has been visited * by the current retarget pass (tracked via `_computedBoneNewWorldRotFrameId`). */ this._computedBoneNewWorldRot = new Map(); /** Per-bone marker: frame id at which the pooled rotation above was last set. */ this._computedBoneNewWorldRotFrameId = new Map(); this._currentRetargetFrameId = 0; /** Scratch quaternion reused for the parent-world accumulation loop. */ this._tempParentAccumRot = new Quaternion(); /** Scratch quaternion reused for the parent-world intermediate product. */ this._tempParentAccumTmp = new Quaternion(); /** Scratch vectors reused by hand twist correction. */ this._tempTwistFirst = new Vector3(); this._tempTwistSecond = new Vector3(); this._tempTwistNormal = new Vector3(); this._tempCurrentTwistNormal = new Vector3(); this._tempProjectedTwistNormal = new Vector3(); this._tempProjectedDesiredTwistNormal = new Vector3(); this._tempTwistAimAxis = new Vector3(); this._tempTwistCross = new Vector3(); /** The skeleton reference for iterating bones in parent-first order. */ this._skeleton = null; /** Cached inverse of the skeleton mesh's world matrix. */ this._meshWorldMatrixInverse = new Matrix(); /** Cached inverse of the skeleton mesh pose matrix when initial skinning is used. */ this._initialSkinMatrixInverse = new Matrix(); /** * Pre-allocated desiredFinal matrices (one per joint slot). * `desiredFinal[i] = strip(xrWorld[i] × inv(meshWorld))` — the bone's * target skeleton-space final matrix with parasitic scale removed. */ this._desiredFinals = []; /** * Standalone TransformNodes created for unmapped skinned bones. * These TNs are initialized to the bone's bind-pose local and linked * so that `prepare()` reads deterministic values rather than the * original glTF scene-graph TNs (which we don't control). */ this._unmappedBoneNodes = []; /** The mesh that owns the skeleton (used for world-matrix inverse). */ this._skeletonMesh = null; /** The body mesh root (topmost parent), used to parent the mesh to the camera. */ this._bodyMeshRoot = null; /** The rigged body mesh, if any. */ this._bodyMesh = null; /** * Runtime-mutable rotation applied in each tracked joint's local frame to * re-base XR joint axes (e.g., "+Z-along-bone" → "+Y-along-bone"). * `null` = identity / disabled. */ this.jointLocalRotationOffset = null; /** Cached 4×4 matrix form of {@link jointLocalRotationOffset} for the fast path. */ this._jointLocalRotationOffsetMatrix = new Matrix(); /** Temporary matrix used when applying {@link jointLocalRotationOffset}. */ this._tempOffsetAppliedMatrix = new Matrix(); /** * When true, bypass skeleton.prepare() and write skin matrices directly. * This is a diagnostic flag to help isolate rendering issues. When the * standard pipeline (TN → bone → prepare → skin matrices) produces * unexpected results, enabling this writes `absInvBind × final` directly * into the skeleton's transform matrix buffer. * @internal */ this._directSkinWrite = false; /** * Debug info string from the last `updateFromXRFrame` call. * Useful for diagnosing tracking failures on-device. * @internal */ this._lastDebugInfo = "TB:not called"; this._scene = scene; this._jointScaleFactor = jointScaleFactor