@aura3d/animation 3.0.1 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnimationController.d.ts +8 -0
- package/dist/AnimationController.d.ts.map +1 -1
- package/dist/AnimationController.js +93 -0
- package/dist/AnimationController.js.map +1 -1
- package/dist/AnimationMixer.d.ts.map +1 -1
- package/dist/AnimationMixer.js +11 -0
- package/dist/AnimationMixer.js.map +1 -1
- package/dist/FootIk.d.ts +41 -0
- package/dist/FootIk.d.ts.map +1 -1
- package/dist/FootIk.js +202 -0
- package/dist/FootIk.js.map +1 -1
- package/dist/IK.d.ts +26 -0
- package/dist/IK.d.ts.map +1 -1
- package/dist/IK.js +515 -0
- package/dist/IK.js.map +1 -1
- package/dist/Keyframe.d.ts.map +1 -1
- package/dist/Keyframe.js +13 -12
- package/dist/Keyframe.js.map +1 -1
- package/dist/SpringBones.d.ts +80 -0
- package/dist/SpringBones.d.ts.map +1 -1
- package/dist/SpringBones.js +593 -49
- package/dist/SpringBones.js.map +1 -1
- package/dist/browser-index.d.ts +1 -1
- package/dist/browser-index.d.ts.map +1 -1
- package/dist/browser-index.js +1 -1
- package/dist/browser-index.js.map +1 -1
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +7 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/pose.d.ts +25 -0
- package/dist/contracts/pose.d.ts.map +1 -0
- package/dist/contracts/pose.js +41 -0
- package/dist/contracts/pose.js.map +1 -0
- package/dist/experimental/CrowdAnimation.d.ts.map +1 -0
- package/dist/experimental/CrowdAnimation.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lanes/index.d.ts +17 -0
- package/dist/lanes/index.d.ts.map +1 -0
- package/dist/lanes/index.js +17 -0
- package/dist/lanes/index.js.map +1 -0
- package/dist/lanes/prd01.d.ts +6 -0
- package/dist/lanes/prd01.d.ts.map +1 -0
- package/dist/lanes/prd01.js +6 -0
- package/dist/lanes/prd01.js.map +1 -0
- package/dist/lanes/prd02.d.ts +6 -0
- package/dist/lanes/prd02.d.ts.map +1 -0
- package/dist/lanes/prd02.js +6 -0
- package/dist/lanes/prd02.js.map +1 -0
- package/dist/lanes/prd03.d.ts +6 -0
- package/dist/lanes/prd03.d.ts.map +1 -0
- package/dist/lanes/prd03.js +6 -0
- package/dist/lanes/prd03.js.map +1 -0
- package/dist/lanes/prd04.d.ts +6 -0
- package/dist/lanes/prd04.d.ts.map +1 -0
- package/dist/lanes/prd04.js +6 -0
- package/dist/lanes/prd04.js.map +1 -0
- package/dist/lanes/prd05.d.ts +6 -0
- package/dist/lanes/prd05.d.ts.map +1 -0
- package/dist/lanes/prd05.js +6 -0
- package/dist/lanes/prd05.js.map +1 -0
- package/dist/lanes/prd06.d.ts +37 -0
- package/dist/lanes/prd06.d.ts.map +1 -0
- package/dist/lanes/prd06.js +26 -0
- package/dist/lanes/prd06.js.map +1 -0
- package/dist/lanes/prd07.d.ts +6 -0
- package/dist/lanes/prd07.d.ts.map +1 -0
- package/dist/lanes/prd07.js +6 -0
- package/dist/lanes/prd07.js.map +1 -0
- package/dist/lanes/prd08.d.ts +6 -0
- package/dist/lanes/prd08.d.ts.map +1 -0
- package/dist/lanes/prd08.js +6 -0
- package/dist/lanes/prd08.js.map +1 -0
- package/dist/lanes/prd09.d.ts +6 -0
- package/dist/lanes/prd09.d.ts.map +1 -0
- package/dist/lanes/prd09.js +6 -0
- package/dist/lanes/prd09.js.map +1 -0
- package/dist/lanes/prd10.d.ts +6 -0
- package/dist/lanes/prd10.d.ts.map +1 -0
- package/dist/lanes/prd10.js +6 -0
- package/dist/lanes/prd10.js.map +1 -0
- package/dist/lanes/prd11.d.ts +6 -0
- package/dist/lanes/prd11.d.ts.map +1 -0
- package/dist/lanes/prd11.js +6 -0
- package/dist/lanes/prd11.js.map +1 -0
- package/dist/lanes/prd12.d.ts +6 -0
- package/dist/lanes/prd12.d.ts.map +1 -0
- package/dist/lanes/prd12.js +6 -0
- package/dist/lanes/prd12.js.map +1 -0
- package/dist/lanes/prd13.d.ts +6 -0
- package/dist/lanes/prd13.d.ts.map +1 -0
- package/dist/lanes/prd13.js +6 -0
- package/dist/lanes/prd13.js.map +1 -0
- package/dist/lanes/prd14.d.ts +6 -0
- package/dist/lanes/prd14.d.ts.map +1 -0
- package/dist/lanes/prd14.js +6 -0
- package/dist/lanes/prd14.js.map +1 -0
- package/dist/lanes/prd15.d.ts +6 -0
- package/dist/lanes/prd15.d.ts.map +1 -0
- package/dist/lanes/prd15.js +6 -0
- package/dist/lanes/prd15.js.map +1 -0
- package/dist/pose/BoneMask.d.ts +39 -0
- package/dist/pose/BoneMask.d.ts.map +1 -0
- package/dist/pose/BoneMask.js +160 -0
- package/dist/pose/BoneMask.js.map +1 -0
- package/dist/pose/CcdIkConstraint.d.ts +38 -0
- package/dist/pose/CcdIkConstraint.d.ts.map +1 -0
- package/dist/pose/CcdIkConstraint.js +198 -0
- package/dist/pose/CcdIkConstraint.js.map +1 -0
- package/dist/pose/CompiledClip.d.ts +48 -0
- package/dist/pose/CompiledClip.d.ts.map +1 -0
- package/dist/pose/CompiledClip.js +209 -0
- package/dist/pose/CompiledClip.js.map +1 -0
- package/dist/pose/LookAtConstraint.d.ts +48 -0
- package/dist/pose/LookAtConstraint.d.ts.map +1 -0
- package/dist/pose/LookAtConstraint.js +251 -0
- package/dist/pose/LookAtConstraint.js.map +1 -0
- package/dist/pose/MotionMetrics.d.ts +136 -0
- package/dist/pose/MotionMetrics.d.ts.map +1 -0
- package/dist/pose/MotionMetrics.js +253 -0
- package/dist/pose/MotionMetrics.js.map +1 -0
- package/dist/pose/PoseBuffer.d.ts +23 -0
- package/dist/pose/PoseBuffer.d.ts.map +1 -0
- package/dist/pose/PoseBuffer.js +48 -0
- package/dist/pose/PoseBuffer.js.map +1 -0
- package/dist/pose/PoseInertializer.d.ts +38 -0
- package/dist/pose/PoseInertializer.d.ts.map +1 -0
- package/dist/pose/PoseInertializer.js +208 -0
- package/dist/pose/PoseInertializer.js.map +1 -0
- package/dist/pose/PoseMixer.d.ts +256 -0
- package/dist/pose/PoseMixer.d.ts.map +1 -0
- package/dist/pose/PoseMixer.js +848 -0
- package/dist/pose/PoseMixer.js.map +1 -0
- package/dist/pose/Retarget.d.ts +77 -0
- package/dist/pose/Retarget.d.ts.map +1 -0
- package/dist/pose/Retarget.js +378 -0
- package/dist/pose/Retarget.js.map +1 -0
- package/dist/pose/RetargetCache.d.ts +21 -0
- package/dist/pose/RetargetCache.d.ts.map +1 -0
- package/dist/pose/RetargetCache.js +102 -0
- package/dist/pose/RetargetCache.js.map +1 -0
- package/dist/pose/RetargetWorker.d.ts +21 -0
- package/dist/pose/RetargetWorker.d.ts.map +1 -0
- package/dist/pose/RetargetWorker.js +56 -0
- package/dist/pose/RetargetWorker.js.map +1 -0
- package/dist/pose/SkeletonBinding.d.ts +55 -0
- package/dist/pose/SkeletonBinding.d.ts.map +1 -0
- package/dist/pose/SkeletonBinding.js +62 -0
- package/dist/pose/SkeletonBinding.js.map +1 -0
- package/dist/pose/blendKernels.d.ts +32 -0
- package/dist/pose/blendKernels.d.ts.map +1 -0
- package/dist/pose/blendKernels.js +101 -0
- package/dist/pose/blendKernels.js.map +1 -0
- package/dist/pose/makeClipAdditive.d.ts +22 -0
- package/dist/pose/makeClipAdditive.d.ts.map +1 -0
- package/dist/pose/makeClipAdditive.js +101 -0
- package/dist/pose/makeClipAdditive.js.map +1 -0
- package/dist/pose/poseMixerFlags.d.ts +18 -0
- package/dist/pose/poseMixerFlags.d.ts.map +1 -0
- package/dist/pose/poseMixerFlags.js +30 -0
- package/dist/pose/poseMixerFlags.js.map +1 -0
- package/dist/pose/quatFlat.d.ts +16 -0
- package/dist/pose/quatFlat.d.ts.map +1 -0
- package/dist/pose/quatFlat.js +88 -0
- package/dist/pose/quatFlat.js.map +1 -0
- package/dist/pose/retarget.worker.d.ts +25 -0
- package/dist/pose/retarget.worker.d.ts.map +1 -0
- package/dist/pose/retarget.worker.js +22 -0
- package/dist/pose/retarget.worker.js.map +1 -0
- package/package.json +14 -2
- package/dist/CrowdAnimation.d.ts.map +0 -1
- package/dist/CrowdAnimation.js.map +0 -1
- /package/dist/{CrowdAnimation.d.ts → experimental/CrowdAnimation.d.ts} +0 -0
- /package/dist/{CrowdAnimation.js → experimental/CrowdAnimation.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd08.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd08.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd08.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd08.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd09.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd09.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd09.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd09.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd10.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd10.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd10.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd10.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd11.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd11.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd11.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd11.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd12.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd12.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd12.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd12.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd13.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd13.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd13.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd13.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd14.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd14.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd14.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd14.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd15.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd15.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd15.js","sourceRoot":"","sources":["../../../../../packages/animation/src/lanes/prd15.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-06 T1.5 — per-bone blend weights in a flat `Float32Array`.
|
|
3
|
+
*
|
|
4
|
+
* Selectors:
|
|
5
|
+
* - `"Spine"` — exact joint name (a duplicated name weights *all* its joints).
|
|
6
|
+
* - `{ bone: "Spine", descendants: true }` — the joint plus its subtree via
|
|
7
|
+
* the skeleton's parent indices.
|
|
8
|
+
* - `{ humanoid: "upper-body" }` — preset resolved through
|
|
9
|
+
* `HumanoidBoneInference` (§6.4): `upper-body` = Spine and descendants
|
|
10
|
+
* excluding the leg chains; `lower-body` = Hips + the leg chains; the
|
|
11
|
+
* sided presets pick one limb chain (plus its subtree).
|
|
12
|
+
* - `{ substring: "mixamorig" }` — deprecated name-fragment match, kept only
|
|
13
|
+
* for callers migrating off the old substring path.
|
|
14
|
+
*
|
|
15
|
+
* `spec.weights` supplies a falloff: `{ "Spine": 0.3, "Spine1": 0.6,
|
|
16
|
+
* "Spine2": 1.0 }` or preset keys like `{ "upper-body": 0.5 }`.
|
|
17
|
+
*/
|
|
18
|
+
export type BoneMaskPreset = "upper-body" | "lower-body" | "left-arm" | "right-arm" | "left-leg" | "right-leg" | "head";
|
|
19
|
+
export type BoneMaskSelector = string | {
|
|
20
|
+
readonly bone: string;
|
|
21
|
+
readonly descendants?: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
readonly humanoid: BoneMaskPreset;
|
|
24
|
+
} | {
|
|
25
|
+
readonly substring: string;
|
|
26
|
+
};
|
|
27
|
+
export interface BoneMaskSpec {
|
|
28
|
+
/** Selectors that set weight 1 (overridable per-bone by `weights`). */
|
|
29
|
+
readonly include?: readonly BoneMaskSelector[];
|
|
30
|
+
/** Per-bone-name or per-preset weight falloff. */
|
|
31
|
+
readonly weights?: Readonly<Record<string, number>>;
|
|
32
|
+
}
|
|
33
|
+
export interface BoneMaskSkeleton {
|
|
34
|
+
readonly jointNames: readonly string[];
|
|
35
|
+
/** Parent joint index per joint (-1 for roots). */
|
|
36
|
+
readonly parentIndices?: readonly number[];
|
|
37
|
+
}
|
|
38
|
+
export declare function createBoneMask(spec: BoneMaskSpec, skeleton: BoneMaskSkeleton): Float32Array;
|
|
39
|
+
//# sourceMappingURL=BoneMask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoneMask.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/pose/BoneMask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,YAAY,GACZ,UAAU,GACV,WAAW,GACX,UAAU,GACV,WAAW,GACX,MAAM,CAAC;AAEX,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,GACrC;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,kDAAkD;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,mDAAmD;IACnD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAiBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,GAAG,YAAY,CA8G3F"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD-06 T1.5 — per-bone blend weights in a flat `Float32Array`.
|
|
3
|
+
*
|
|
4
|
+
* Selectors:
|
|
5
|
+
* - `"Spine"` — exact joint name (a duplicated name weights *all* its joints).
|
|
6
|
+
* - `{ bone: "Spine", descendants: true }` — the joint plus its subtree via
|
|
7
|
+
* the skeleton's parent indices.
|
|
8
|
+
* - `{ humanoid: "upper-body" }` — preset resolved through
|
|
9
|
+
* `HumanoidBoneInference` (§6.4): `upper-body` = Spine and descendants
|
|
10
|
+
* excluding the leg chains; `lower-body` = Hips + the leg chains; the
|
|
11
|
+
* sided presets pick one limb chain (plus its subtree).
|
|
12
|
+
* - `{ substring: "mixamorig" }` — deprecated name-fragment match, kept only
|
|
13
|
+
* for callers migrating off the old substring path.
|
|
14
|
+
*
|
|
15
|
+
* `spec.weights` supplies a falloff: `{ "Spine": 0.3, "Spine1": 0.6,
|
|
16
|
+
* "Spine2": 1.0 }` or preset keys like `{ "upper-body": 0.5 }`.
|
|
17
|
+
*/
|
|
18
|
+
import { inferHumanoidRig } from "../HumanoidBoneInference.js";
|
|
19
|
+
/** Slots whose chains make up a leg (used to exclude them from `upper-body`). */
|
|
20
|
+
const LEG_SLOTS = [
|
|
21
|
+
"leftUpperLeg", "leftLowerLeg", "leftFoot", "leftToes",
|
|
22
|
+
"rightUpperLeg", "rightLowerLeg", "rightFoot", "rightToes"
|
|
23
|
+
];
|
|
24
|
+
const LEFT_ARM_SLOTS = ["leftShoulder", "leftUpperArm", "leftLowerArm", "leftHand"];
|
|
25
|
+
const RIGHT_ARM_SLOTS = ["rightShoulder", "rightUpperArm", "rightLowerArm", "rightHand"];
|
|
26
|
+
const LEFT_LEG_SLOTS = ["leftUpperLeg", "leftLowerLeg", "leftFoot", "leftToes"];
|
|
27
|
+
const RIGHT_LEG_SLOTS = ["rightUpperLeg", "rightLowerLeg", "rightFoot", "rightToes"];
|
|
28
|
+
const UPPER_BODY_SLOTS = [
|
|
29
|
+
"spine", "chest", "upperChest", "neck", "head",
|
|
30
|
+
"leftShoulder", "leftUpperArm", "leftLowerArm", "leftHand",
|
|
31
|
+
"rightShoulder", "rightUpperArm", "rightLowerArm", "rightHand"
|
|
32
|
+
];
|
|
33
|
+
export function createBoneMask(spec, skeleton) {
|
|
34
|
+
const count = skeleton.jointNames.length;
|
|
35
|
+
const mask = new Float32Array(count);
|
|
36
|
+
const indicesByName = new Map();
|
|
37
|
+
skeleton.jointNames.forEach((name, index) => {
|
|
38
|
+
const bucket = indicesByName.get(name);
|
|
39
|
+
if (bucket)
|
|
40
|
+
bucket.push(index);
|
|
41
|
+
else
|
|
42
|
+
indicesByName.set(name, [index]);
|
|
43
|
+
});
|
|
44
|
+
const parents = skeleton.parentIndices ?? new Array(count).fill(-1);
|
|
45
|
+
const childrenOf = new Map();
|
|
46
|
+
parents.forEach((parent, index) => {
|
|
47
|
+
if (parent < 0 || parent >= count)
|
|
48
|
+
return;
|
|
49
|
+
const bucket = childrenOf.get(parent);
|
|
50
|
+
if (bucket)
|
|
51
|
+
bucket.push(index);
|
|
52
|
+
else
|
|
53
|
+
childrenOf.set(parent, [index]);
|
|
54
|
+
});
|
|
55
|
+
const mark = (index, weight) => {
|
|
56
|
+
if (index >= 0 && index < count)
|
|
57
|
+
mask[index] = weight;
|
|
58
|
+
};
|
|
59
|
+
const markSubtree = (index, weight) => {
|
|
60
|
+
mark(index, weight);
|
|
61
|
+
for (const child of childrenOf.get(index) ?? [])
|
|
62
|
+
markSubtree(child, weight);
|
|
63
|
+
};
|
|
64
|
+
const markNames = (names, weight, includeDescendants) => {
|
|
65
|
+
for (const name of names) {
|
|
66
|
+
if (name === undefined)
|
|
67
|
+
continue;
|
|
68
|
+
for (const index of indicesByName.get(name) ?? []) {
|
|
69
|
+
if (includeDescendants)
|
|
70
|
+
markSubtree(index, weight);
|
|
71
|
+
else
|
|
72
|
+
mark(index, weight);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const rig = inferHumanoidRig(skeleton.jointNames);
|
|
77
|
+
const slotName = (slot) => rig.bones[slot]?.name;
|
|
78
|
+
const slotChainNames = (slots) => slots.map((slot) => slotName(slot)).filter((name) => name !== undefined);
|
|
79
|
+
const legNames = new Set(slotChainNames(LEG_SLOTS));
|
|
80
|
+
const applyPreset = (preset, weight) => {
|
|
81
|
+
switch (preset) {
|
|
82
|
+
case "upper-body": {
|
|
83
|
+
// Spine and descendants, excluding legs (§6.4).
|
|
84
|
+
const spine = slotName("spine");
|
|
85
|
+
for (const index of indicesByName.get(spine ?? "") ?? [])
|
|
86
|
+
markSubtree(index, weight);
|
|
87
|
+
markNames(slotChainNames(UPPER_BODY_SLOTS), weight, false);
|
|
88
|
+
for (const legName of legNames) {
|
|
89
|
+
for (const index of indicesByName.get(legName) ?? [])
|
|
90
|
+
markSubtree(index, 0);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "lower-body":
|
|
95
|
+
// Hips + legs (§6.4).
|
|
96
|
+
markNames([slotName("hips")], weight, false);
|
|
97
|
+
markNames(slotChainNames(LEG_SLOTS), weight, true);
|
|
98
|
+
break;
|
|
99
|
+
case "left-arm":
|
|
100
|
+
markNames(slotChainNames(LEFT_ARM_SLOTS), weight, true);
|
|
101
|
+
break;
|
|
102
|
+
case "right-arm":
|
|
103
|
+
markNames(slotChainNames(RIGHT_ARM_SLOTS), weight, true);
|
|
104
|
+
break;
|
|
105
|
+
case "left-leg":
|
|
106
|
+
markNames(slotChainNames(LEFT_LEG_SLOTS), weight, true);
|
|
107
|
+
break;
|
|
108
|
+
case "right-leg":
|
|
109
|
+
markNames(slotChainNames(RIGHT_LEG_SLOTS), weight, true);
|
|
110
|
+
break;
|
|
111
|
+
case "head":
|
|
112
|
+
markNames([slotName("head"), slotName("neck")], weight, true);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const applySelector = (selector, weight) => {
|
|
117
|
+
if (typeof selector === "string") {
|
|
118
|
+
markNames([selector], weight, false);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if ("bone" in selector) {
|
|
122
|
+
for (const index of indicesByName.get(selector.bone) ?? []) {
|
|
123
|
+
if (selector.descendants)
|
|
124
|
+
markSubtree(index, weight);
|
|
125
|
+
else
|
|
126
|
+
mark(index, weight);
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if ("humanoid" in selector) {
|
|
131
|
+
applyPreset(selector.humanoid, weight);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if ("substring" in selector) {
|
|
135
|
+
const needle = selector.substring.toLowerCase();
|
|
136
|
+
skeleton.jointNames.forEach((name, index) => {
|
|
137
|
+
if (name.toLowerCase().includes(needle))
|
|
138
|
+
mark(index, weight);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
for (const selector of spec.include ?? [])
|
|
143
|
+
applySelector(selector, 1);
|
|
144
|
+
if (spec.weights) {
|
|
145
|
+
for (const [key, weight] of Object.entries(spec.weights)) {
|
|
146
|
+
if (isBoneMaskPreset(key))
|
|
147
|
+
applyPreset(key, weight);
|
|
148
|
+
else
|
|
149
|
+
markNames([key], weight, false);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return mask;
|
|
153
|
+
}
|
|
154
|
+
const PRESETS = new Set([
|
|
155
|
+
"upper-body", "lower-body", "left-arm", "right-arm", "left-leg", "right-leg", "head"
|
|
156
|
+
]);
|
|
157
|
+
function isBoneMaskPreset(key) {
|
|
158
|
+
return PRESETS.has(key);
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=BoneMask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoneMask.js","sourceRoot":"","sources":["../../../../../packages/animation/src/pose/BoneMask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T3.4 (PRD-06 §7.1/7.2) — `CcdIkConstraint`: cyclic coordinate descent over a
|
|
3
|
+
* named joint chain — each iteration sweeps tip-to-root rotating every joint so
|
|
4
|
+
* the tip's direction toward the target maximally closes. Cone limits per bone
|
|
5
|
+
* (`coneLimitDeg`), hard cap `iterations ≤ 8`, early-out at `tolerance`
|
|
6
|
+
* (default 1 mm).
|
|
7
|
+
*
|
|
8
|
+
* Pose-space like the rest of the lane: the target arrives world-space and is
|
|
9
|
+
* transformed into model space by `invertTRS(modelMatrix)` before the sweep.
|
|
10
|
+
*/
|
|
11
|
+
import type { PoseBuffer } from "./PoseBuffer.js";
|
|
12
|
+
import type { SkeletonBinding } from "./SkeletonBinding.js";
|
|
13
|
+
export interface CcdIkConstraintSpec {
|
|
14
|
+
/** Ordered joint names root→tip. */
|
|
15
|
+
readonly chain: readonly string[];
|
|
16
|
+
/** Max sweep iterations — clamped to 8 (default 8). */
|
|
17
|
+
readonly iterations?: number;
|
|
18
|
+
/** Early-out distance in metres (default 1 mm). */
|
|
19
|
+
readonly tolerance?: number;
|
|
20
|
+
/** Per-bone cone limits (degrees) — caps each joint's per-iteration rotation. */
|
|
21
|
+
readonly coneLimitDeg?: Readonly<Record<string, number>>;
|
|
22
|
+
/** Blend of the solve into the pose (1 = full, 0 = no-op). */
|
|
23
|
+
readonly weight?: number;
|
|
24
|
+
}
|
|
25
|
+
type Vec3 = readonly [number, number, number];
|
|
26
|
+
export interface CcdIkResult {
|
|
27
|
+
readonly iterations: number;
|
|
28
|
+
readonly tipError: number;
|
|
29
|
+
readonly reached: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Evaluate a CCD chain: sweeps `spec.chain` tip→root up to `iterations` times,
|
|
33
|
+
* rotating each joint by the minimal arc that pulls the tip toward `target`,
|
|
34
|
+
* cone-capped per bone. Writes local rotations back into `pose`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function solveCcdIk(pose: PoseBuffer, skeleton: SkeletonBinding, modelMatrix: Float32Array | readonly number[], spec: CcdIkConstraintSpec, target: Vec3): CcdIkResult;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=CcdIkConstraint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CcdIkConstraint.d.ts","sourceRoot":"","sources":["../../../../../packages/animation/src/pose/CcdIkConstraint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAClC,oCAAoC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,uDAAuD;IACvD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzD,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,KAAK,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAqG9C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,eAAe,EACzB,WAAW,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE,EAC7C,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,IAAI,GACX,WAAW,CAwEb"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T3.4 (PRD-06 §7.1/7.2) — `CcdIkConstraint`: cyclic coordinate descent over a
|
|
3
|
+
* named joint chain — each iteration sweeps tip-to-root rotating every joint so
|
|
4
|
+
* the tip's direction toward the target maximally closes. Cone limits per bone
|
|
5
|
+
* (`coneLimitDeg`), hard cap `iterations ≤ 8`, early-out at `tolerance`
|
|
6
|
+
* (default 1 mm).
|
|
7
|
+
*
|
|
8
|
+
* Pose-space like the rest of the lane: the target arrives world-space and is
|
|
9
|
+
* transformed into model space by `invertTRS(modelMatrix)` before the sweep.
|
|
10
|
+
*/
|
|
11
|
+
function jointIndex(skeleton, name) {
|
|
12
|
+
const indices = skeleton.jointIndicesByName.get(name);
|
|
13
|
+
if (!indices || indices.length === 0)
|
|
14
|
+
throw new Error(`CcdIkConstraint: unknown bone "${name}".`);
|
|
15
|
+
return indices[0];
|
|
16
|
+
}
|
|
17
|
+
function frames(pose, skeleton) {
|
|
18
|
+
const cache = new Map();
|
|
19
|
+
const resolve = (joint) => {
|
|
20
|
+
const hit = cache.get(joint);
|
|
21
|
+
if (hit)
|
|
22
|
+
return hit;
|
|
23
|
+
const pos = [pose.positions[joint * 3], pose.positions[joint * 3 + 1], pose.positions[joint * 3 + 2]];
|
|
24
|
+
const rot = [pose.rotations[joint * 4], pose.rotations[joint * 4 + 1], pose.rotations[joint * 4 + 2], pose.rotations[joint * 4 + 3]];
|
|
25
|
+
const scl = [pose.scales[joint * 3], pose.scales[joint * 3 + 1], pose.scales[joint * 3 + 2]];
|
|
26
|
+
const parent = skeleton.parentIndices[joint] ?? -1;
|
|
27
|
+
if (parent < 0 || parent >= skeleton.boneCount || parent === joint) {
|
|
28
|
+
const f = { position: pos, rotation: rot, scale: scl };
|
|
29
|
+
cache.set(joint, f);
|
|
30
|
+
return f;
|
|
31
|
+
}
|
|
32
|
+
const pf = resolve(parent);
|
|
33
|
+
const scaled = [pos[0] * pf.scale[0], pos[1] * pf.scale[1], pos[2] * pf.scale[2]];
|
|
34
|
+
const rotated = rotateVec3(pf.rotation, scaled);
|
|
35
|
+
const f = {
|
|
36
|
+
position: [pf.position[0] + rotated[0], pf.position[1] + rotated[1], pf.position[2] + rotated[2]],
|
|
37
|
+
rotation: multiplyQuat(pf.rotation, rot),
|
|
38
|
+
scale: [pf.scale[0] * scl[0], pf.scale[1] * scl[1], pf.scale[2] * scl[2]]
|
|
39
|
+
};
|
|
40
|
+
cache.set(joint, f);
|
|
41
|
+
return f;
|
|
42
|
+
};
|
|
43
|
+
const out = [];
|
|
44
|
+
for (let i = 0; i < skeleton.boneCount; i += 1)
|
|
45
|
+
out.push(resolve(i));
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
function rotateVec3(q, v) {
|
|
49
|
+
const [x, y, z, w] = q;
|
|
50
|
+
const tx = 2 * (y * v[2] - z * v[1]);
|
|
51
|
+
const ty = 2 * (z * v[0] - x * v[2]);
|
|
52
|
+
const tz = 2 * (x * v[1] - y * v[0]);
|
|
53
|
+
return [v[0] + w * tx + (y * tz - z * ty), v[1] + w * ty + (z * tx - x * tz), v[2] + w * tz + (x * ty - y * tx)];
|
|
54
|
+
}
|
|
55
|
+
function multiplyQuat(a, b) {
|
|
56
|
+
const [ax, ay, az, aw] = a;
|
|
57
|
+
const [bx, by, bz, bw] = b;
|
|
58
|
+
return [
|
|
59
|
+
ax * bw + aw * bx + ay * bz - az * by,
|
|
60
|
+
ay * bw + aw * by + az * bx - ax * bz,
|
|
61
|
+
az * bw + aw * bz + ax * by - ay * bx,
|
|
62
|
+
aw * bw - ax * bx - ay * by - az * bz
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
function axisAngle(axis, radians) {
|
|
66
|
+
const half = radians / 2;
|
|
67
|
+
const s = Math.sin(half);
|
|
68
|
+
return [axis[0] * s, axis[1] * s, axis[2] * s, Math.cos(half)];
|
|
69
|
+
}
|
|
70
|
+
function normalize(v) {
|
|
71
|
+
const l = Math.hypot(v[0], v[1], v[2]);
|
|
72
|
+
return l <= 1e-9 ? [0, 0, 0] : [v[0] / l, v[1] / l, v[2] / l];
|
|
73
|
+
}
|
|
74
|
+
function invertModelTRS(matrix) {
|
|
75
|
+
const sx = Math.hypot(matrix[0], matrix[1], matrix[2]);
|
|
76
|
+
const sy = Math.hypot(matrix[4], matrix[5], matrix[6]);
|
|
77
|
+
const sz = Math.hypot(matrix[8], matrix[9], matrix[10]);
|
|
78
|
+
const m00 = matrix[0] / (sx || 1), m10 = matrix[1] / (sx || 1), m20 = matrix[2] / (sx || 1);
|
|
79
|
+
const m01 = matrix[4] / (sy || 1), m11 = matrix[5] / (sy || 1), m21 = matrix[6] / (sy || 1);
|
|
80
|
+
const m02 = matrix[8] / (sz || 1), m12 = matrix[9] / (sz || 1), m22 = matrix[10] / (sz || 1);
|
|
81
|
+
const trace = m00 + m11 + m22;
|
|
82
|
+
let q;
|
|
83
|
+
if (trace > 0) {
|
|
84
|
+
const s = 0.5 / Math.sqrt(trace + 1);
|
|
85
|
+
q = [(m21 - m12) * s, (m02 - m20) * s, (m10 - m01) * s, 0.25 / s];
|
|
86
|
+
}
|
|
87
|
+
else if (m00 > m11 && m00 > m22) {
|
|
88
|
+
const s = 2 * Math.sqrt(1 + m00 - m11 - m22);
|
|
89
|
+
q = [0.25 * s, (m01 + m10) / s, (m02 + m20) / s, (m21 - m12) / s];
|
|
90
|
+
}
|
|
91
|
+
else if (m11 > m22) {
|
|
92
|
+
const s = 2 * Math.sqrt(1 + m11 - m00 - m22);
|
|
93
|
+
q = [(m01 + m10) / s, 0.25 * s, (m12 + m21) / s, (m02 - m20) / s];
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const s = 2 * Math.sqrt(1 + m22 - m00 - m11);
|
|
97
|
+
q = [(m02 + m20) / s, (m12 + m21) / s, 0.25 * s, (m10 - m01) / s];
|
|
98
|
+
}
|
|
99
|
+
const l = Math.hypot(q[0], q[1], q[2], q[3]) || 1;
|
|
100
|
+
return { rotation: [q[0] / l, q[1] / l, q[2] / l, q[3] / l], scale: [sx, sy, sz], position: [matrix[12], matrix[13], matrix[14]] };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Evaluate a CCD chain: sweeps `spec.chain` tip→root up to `iterations` times,
|
|
104
|
+
* rotating each joint by the minimal arc that pulls the tip toward `target`,
|
|
105
|
+
* cone-capped per bone. Writes local rotations back into `pose`.
|
|
106
|
+
*/
|
|
107
|
+
export function solveCcdIk(pose, skeleton, modelMatrix, spec, target) {
|
|
108
|
+
const weight = spec.weight ?? 1;
|
|
109
|
+
if (weight <= 0 || spec.chain.length === 0)
|
|
110
|
+
return { iterations: 0, tipError: Infinity, reached: false };
|
|
111
|
+
const maxIterations = Math.min(8, spec.iterations ?? 8);
|
|
112
|
+
const tolerance = spec.tolerance ?? 0.001;
|
|
113
|
+
const invModel = invertModelTRS(modelMatrix);
|
|
114
|
+
const shifted = [target[0] - invModel.position[0], target[1] - invModel.position[1], target[2] - invModel.position[2]];
|
|
115
|
+
const invRot = [-invModel.rotation[0], -invModel.rotation[1], -invModel.rotation[2], invModel.rotation[3]];
|
|
116
|
+
const scaledShift = rotateVec3(invRot, shifted);
|
|
117
|
+
const targetLocal = [
|
|
118
|
+
scaledShift[0] / (invModel.scale[0] || 1),
|
|
119
|
+
scaledShift[1] / (invModel.scale[1] || 1),
|
|
120
|
+
scaledShift[2] / (invModel.scale[2] || 1)
|
|
121
|
+
];
|
|
122
|
+
const chainIndices = spec.chain.map((name) => jointIndex(skeleton, name));
|
|
123
|
+
const tipIndex = chainIndices[chainIndices.length - 1];
|
|
124
|
+
let iterations = 0;
|
|
125
|
+
let tipError = Infinity;
|
|
126
|
+
for (let iter = 0; iter < maxIterations; iter += 1) {
|
|
127
|
+
const current = frames(pose, skeleton); // refresh after each write below
|
|
128
|
+
tipError = Math.hypot(current[tipIndex].position[0] - targetLocal[0], current[tipIndex].position[1] - targetLocal[1], current[tipIndex].position[2] - targetLocal[2]);
|
|
129
|
+
if (tipError <= tolerance)
|
|
130
|
+
break;
|
|
131
|
+
iterations += 1;
|
|
132
|
+
// Sweep tip→root (chain is ordered root→tip, so walk backwards).
|
|
133
|
+
for (let i = chainIndices.length - 1; i >= 0; i -= 1) {
|
|
134
|
+
const joint = chainIndices[i];
|
|
135
|
+
const snapshot = frames(pose, skeleton);
|
|
136
|
+
const jointPos = snapshot[joint].position;
|
|
137
|
+
const tipPos = snapshot[tipIndex].position;
|
|
138
|
+
const toTip = normalize([tipPos[0] - jointPos[0], tipPos[1] - jointPos[1], tipPos[2] - jointPos[2]]);
|
|
139
|
+
const toTarget = normalize([targetLocal[0] - jointPos[0], targetLocal[1] - jointPos[1], targetLocal[2] - jointPos[2]]);
|
|
140
|
+
if (Math.hypot(toTip[0], toTip[1], toTip[2]) <= 1e-9 || Math.hypot(toTarget[0], toTarget[1], toTarget[2]) <= 1e-9)
|
|
141
|
+
continue;
|
|
142
|
+
const cross = [
|
|
143
|
+
toTip[1] * toTarget[2] - toTip[2] * toTarget[1],
|
|
144
|
+
toTip[2] * toTarget[0] - toTip[0] * toTarget[2],
|
|
145
|
+
toTip[0] * toTarget[1] - toTip[1] * toTarget[0]
|
|
146
|
+
];
|
|
147
|
+
const crossLen = Math.hypot(cross[0], cross[1], cross[2]);
|
|
148
|
+
const dot = Math.max(-1, Math.min(1, toTip[0] * toTarget[0] + toTip[1] * toTarget[1] + toTip[2] * toTarget[2]));
|
|
149
|
+
let angle = Math.atan2(crossLen, dot);
|
|
150
|
+
const coneLimit = spec.coneLimitDeg?.[spec.chain[i]];
|
|
151
|
+
if (coneLimit !== undefined) {
|
|
152
|
+
angle = Math.min(angle, (coneLimit * Math.PI) / 180);
|
|
153
|
+
}
|
|
154
|
+
if (angle <= 1e-7 || crossLen <= 1e-9)
|
|
155
|
+
continue;
|
|
156
|
+
const axis = [cross[0] / crossLen, cross[1] / crossLen, cross[2] / crossLen];
|
|
157
|
+
const rot = axisAngle(axis, angle);
|
|
158
|
+
const worldNew = multiplyQuat(rot, snapshot[joint].rotation);
|
|
159
|
+
const parent = skeleton.parentIndices[joint] ?? -1;
|
|
160
|
+
const parentRot = parent >= 0 ? snapshot[parent].rotation : [0, 0, 0, 1];
|
|
161
|
+
const inv = [-parentRot[0], -parentRot[1], -parentRot[2], parentRot[3]];
|
|
162
|
+
const local = multiplyQuat(inv, worldNew);
|
|
163
|
+
const ll = Math.hypot(local[0], local[1], local[2], local[3]) || 1;
|
|
164
|
+
// Weight blends the CCD step with the current local rotation.
|
|
165
|
+
const out = new Float32Array(4);
|
|
166
|
+
slerpFlat(pose.rotations, joint * 4, local, weight, out);
|
|
167
|
+
pose.rotations[joint * 4] = out[0];
|
|
168
|
+
pose.rotations[joint * 4 + 1] = out[1];
|
|
169
|
+
pose.rotations[joint * 4 + 2] = out[2];
|
|
170
|
+
pose.rotations[joint * 4 + 3] = out[3];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { iterations, tipError, reached: tipError <= tolerance };
|
|
174
|
+
}
|
|
175
|
+
function slerpFlat(current, offset, local, t, out) {
|
|
176
|
+
let ax = current[offset], ay = current[offset + 1], az = current[offset + 2], aw = current[offset + 3];
|
|
177
|
+
let bx = local[0], by = local[1], bz = local[2], bw = local[3];
|
|
178
|
+
let d = ax * bx + ay * by + az * bz + aw * bw;
|
|
179
|
+
if (d < 0) {
|
|
180
|
+
bx = -bx;
|
|
181
|
+
by = -by;
|
|
182
|
+
bz = -bz;
|
|
183
|
+
bw = -bw;
|
|
184
|
+
d = -d;
|
|
185
|
+
}
|
|
186
|
+
let s0 = 1 - t, s1 = t;
|
|
187
|
+
if (d < 0.9995) {
|
|
188
|
+
const theta = Math.acos(d);
|
|
189
|
+
const sin = Math.sin(theta);
|
|
190
|
+
s0 = Math.sin(s0 * theta) / sin;
|
|
191
|
+
s1 = Math.sin(s1 * theta) / sin;
|
|
192
|
+
}
|
|
193
|
+
out[0] = ax * s0 + bx * s1;
|
|
194
|
+
out[1] = ay * s0 + by * s1;
|
|
195
|
+
out[2] = az * s0 + bz * s1;
|
|
196
|
+
out[3] = aw * s0 + bw * s1;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=CcdIkConstraint.js.map
|