@zephyr3d/scene 0.7.1 → 0.8.0
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/animation/animation.js +0 -18
- package/dist/animation/animation.js.map +1 -1
- package/dist/animation/animationset.js +2 -2
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/ik/ccd_solver.js +372 -0
- package/dist/animation/ik/ccd_solver.js.map +1 -0
- package/dist/animation/ik/fabrik_solver.js +399 -0
- package/dist/animation/ik/fabrik_solver.js.map +1 -0
- package/dist/animation/ik/ik_angle_constraint.js +181 -0
- package/dist/animation/ik/ik_angle_constraint.js.map +1 -0
- package/dist/animation/ik/ik_chain.js +163 -0
- package/dist/animation/ik/ik_chain.js.map +1 -0
- package/dist/animation/ik/ik_chain_builder.js +68 -0
- package/dist/animation/ik/ik_chain_builder.js.map +1 -0
- package/dist/animation/ik/ik_constraint.js +25 -0
- package/dist/animation/ik/ik_constraint.js.map +1 -0
- package/dist/animation/ik/ik_pole_constraint.js +123 -0
- package/dist/animation/ik/ik_pole_constraint.js.map +1 -0
- package/dist/animation/ik/ik_solver.js +48 -0
- package/dist/animation/ik/ik_solver.js.map +1 -0
- package/dist/animation/ik/ik_track.js +96 -0
- package/dist/animation/ik/ik_track.js.map +1 -0
- package/dist/animation/ik/ik_utils.js +270 -0
- package/dist/animation/ik/ik_utils.js.map +1 -0
- package/dist/animation/ik/two_bone_ik_solver.js +332 -0
- package/dist/animation/ik/two_bone_ik_solver.js.map +1 -0
- package/dist/animation/ik_modifier.js +61 -0
- package/dist/animation/ik_modifier.js.map +1 -0
- package/dist/animation/ik_postprocessor.js +71 -0
- package/dist/animation/ik_postprocessor.js.map +1 -0
- package/dist/animation/manual_transform_processor.js +156 -0
- package/dist/animation/manual_transform_processor.js.map +1 -0
- package/dist/animation/skeleton.js +55 -51
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/skeleton_modifier.js +38 -0
- package/dist/animation/skeleton_modifier.js.map +1 -0
- package/dist/animation/skeleton_postprocessor.js +50 -0
- package/dist/animation/skeleton_postprocessor.js.map +1 -0
- package/dist/animation/spring/multi_chain_spring_system.js +503 -0
- package/dist/animation/spring/multi_chain_spring_system.js.map +1 -0
- package/dist/animation/spring/spring_chain.js +103 -0
- package/dist/animation/spring/spring_chain.js.map +1 -0
- package/dist/animation/spring/spring_collider.js +247 -0
- package/dist/animation/spring/spring_collider.js.map +1 -0
- package/dist/animation/spring/spring_constraint.js +21 -0
- package/dist/animation/spring/spring_constraint.js.map +1 -0
- package/dist/animation/spring/spring_particle.js +20 -0
- package/dist/animation/spring/spring_particle.js.map +1 -0
- package/dist/animation/spring/spring_system.js +526 -0
- package/dist/animation/spring/spring_system.js.map +1 -0
- package/dist/animation/spring_modifier.js +45 -0
- package/dist/animation/spring_modifier.js.map +1 -0
- package/dist/animation/spring_postprocessor.js +54 -0
- package/dist/animation/spring_postprocessor.js.map +1 -0
- package/dist/app/screen.js +1 -1
- package/dist/app/screen.js.map +1 -1
- package/dist/asset/model.js +8 -4
- package/dist/asset/model.js.map +1 -1
- package/dist/camera/perspectivecamera.js +3 -3
- package/dist/camera/perspectivecamera.js.map +1 -1
- package/dist/index.d.ts +1385 -4
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +66 -4
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/shader/helper.js +5 -3
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/scene/mesh.js +15 -23
- package/dist/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +21 -20
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +62 -0
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_pole_constraint.js","sources":["../../../src/animation/ik/ik_pole_constraint.ts"],"sourcesContent":["import { Vector3 } from '@zephyr3d/base';\r\nimport { IKConstraint } from './ik_constraint';\r\nimport type { IKJoint } from './ik_joint';\r\n\r\n/**\r\n * Pole vector constraint for IK chains.\r\n *\r\n * Controls the twist/rotation of a joint chain by ensuring the middle joint\r\n * bends toward a specified pole vector position.\r\n *\r\n * @remarks\r\n * This is commonly used for arms and legs to control which direction\r\n * the elbow or knee points. For example, ensuring an elbow points\r\n * backward or a knee points forward.\r\n *\r\n * The pole vector defines a plane, and the middle joint is constrained\r\n * to lie on this plane.\r\n *\r\n * @public\r\n */\r\nexport class IKPoleVectorConstraint extends IKConstraint {\r\n /** The pole vector position in world space */\r\n private _poleVector: Vector3;\r\n /** Weight of the constraint (0 = no effect, 1 = full effect) */\r\n private _weight: number;\r\n\r\n /**\r\n * Create a pole vector constraint.\r\n *\r\n * @param jointIndex - Index of the middle joint to constrain (typically elbow or knee)\r\n * @param poleVector - Position in world space that the joint should point toward\r\n * @param weight - Constraint weight (0-1, default: 1)\r\n */\r\n constructor(jointIndex: number, poleVector: Vector3, weight = 1) {\r\n super(jointIndex);\r\n this._poleVector = poleVector.clone();\r\n this._weight = Math.max(0, Math.min(1, weight));\r\n }\r\n\r\n /**\r\n * Get the pole vector position.\r\n */\r\n get poleVector(): Vector3 {\r\n return this._poleVector;\r\n }\r\n\r\n /**\r\n * Set the pole vector position.\r\n */\r\n set poleVector(value: Vector3) {\r\n this._poleVector.set(value);\r\n }\r\n\r\n /**\r\n * Get the constraint weight.\r\n */\r\n get weight(): number {\r\n return this._weight;\r\n }\r\n\r\n /**\r\n * Set the constraint weight (0-1).\r\n */\r\n set weight(value: number) {\r\n this._weight = Math.max(0, Math.min(1, value));\r\n }\r\n\r\n /**\r\n * Apply the pole vector constraint.\r\n *\r\n * This adjusts the middle joint position to align with the pole vector\r\n * while maintaining bone lengths.\r\n */\r\n apply(joints: IKJoint[]): void {\r\n const index = this._jointIndex;\r\n\r\n // Need at least 3 joints (parent, this, child)\r\n if (index < 1 || index >= joints.length - 1) {\r\n return;\r\n }\r\n\r\n if (this._weight < 0.001) {\r\n return; // No effect\r\n }\r\n\r\n const parentJoint = joints[index - 1];\r\n const currentJoint = joints[index];\r\n const childJoint = joints[index + 1];\r\n\r\n // Calculate the plane defined by parent, child, and pole vector\r\n const parentPos = parentJoint.position;\r\n const childPos = childJoint.position;\r\n const currentPos = currentJoint.position;\r\n\r\n // Vector from parent to child (the \"line\" we're bending around)\r\n const parentToChild = Vector3.sub(childPos, parentPos, new Vector3());\r\n const lineLength = parentToChild.magnitude;\r\n\r\n if (lineLength < 0.000001) {\r\n return; // Parent and child are at same position\r\n }\r\n\r\n const lineDir = Vector3.scale(parentToChild, 1 / lineLength, new Vector3());\r\n\r\n // Project current joint onto the parent-child line\r\n const parentToCurrent = Vector3.sub(currentPos, parentPos, new Vector3());\r\n const projectionLength = Vector3.dot(parentToCurrent, lineDir);\r\n const projectionPoint = Vector3.scale(lineDir, projectionLength, new Vector3());\r\n projectionPoint.addBy(parentPos);\r\n\r\n // Vector from projection point to current joint (perpendicular to line)\r\n const perpendicular = Vector3.sub(currentPos, projectionPoint, new Vector3());\r\n const perpLength = perpendicular.magnitude;\r\n\r\n if (perpLength < 0.000001) {\r\n return; // Current joint is on the line\r\n }\r\n\r\n // Calculate desired direction toward pole vector\r\n const parentToPole = Vector3.sub(this._poleVector, parentPos, new Vector3());\r\n const poleProjectionLength = Vector3.dot(parentToPole, lineDir);\r\n const poleProjectionPoint = Vector3.scale(lineDir, poleProjectionLength, new Vector3());\r\n poleProjectionPoint.addBy(parentPos);\r\n\r\n // Direction from projection to pole (perpendicular to line)\r\n const poleDirection = Vector3.sub(this._poleVector, poleProjectionPoint, new Vector3());\r\n const poleDirLength = poleDirection.magnitude;\r\n\r\n if (poleDirLength < 0.000001) {\r\n return; // Pole is on the line\r\n }\r\n\r\n poleDirection.scaleBy(1 / poleDirLength);\r\n\r\n // Calculate new position for current joint\r\n // Keep the same distance from the line, but rotate toward pole direction\r\n const desiredPerpendicular = Vector3.scale(poleDirection, perpLength, new Vector3());\r\n const newPosition = Vector3.add(projectionPoint, desiredPerpendicular, new Vector3());\r\n\r\n // Apply weight (blend between original and constrained position)\r\n if (this._weight < 0.999) {\r\n // Manual lerp: result = a + (b - a) * t\r\n const blended = new Vector3();\r\n blended.x = currentPos.x + (newPosition.x - currentPos.x) * this._weight;\r\n blended.y = currentPos.y + (newPosition.y - currentPos.y) * this._weight;\r\n blended.z = currentPos.z + (newPosition.z - currentPos.z) * this._weight;\r\n currentJoint.position.set(blended);\r\n } else {\r\n currentJoint.position.set(newPosition);\r\n }\r\n }\r\n}\r\n"],"names":["IKPoleVectorConstraint","IKConstraint","jointIndex","poleVector","weight","_poleVector","clone","_weight","Math","max","min","value","set","apply","joints","index","_jointIndex","length","parentJoint","currentJoint","childJoint","parentPos","position","childPos","currentPos","parentToChild","Vector3","sub","lineLength","magnitude","lineDir","scale","parentToCurrent","projectionLength","dot","projectionPoint","addBy","perpendicular","perpLength","parentToPole","poleProjectionLength","poleProjectionPoint","poleDirection","poleDirLength","scaleBy","desiredPerpendicular","newPosition","add","blended","x","y","z"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for IK solvers.
|
|
3
|
+
*
|
|
4
|
+
* @public
|
|
5
|
+
*/ class IKSolver {
|
|
6
|
+
/** The IK chain to solve */ _chain;
|
|
7
|
+
/** Maximum number of iterations */ _maxIterations;
|
|
8
|
+
/** Convergence tolerance (distance threshold) */ _tolerance;
|
|
9
|
+
/**
|
|
10
|
+
* Create an IK solver.
|
|
11
|
+
*
|
|
12
|
+
* @param chain - The IK chain to solve
|
|
13
|
+
* @param maxIterations - Maximum number of iterations (default: 10)
|
|
14
|
+
* @param tolerance - Convergence tolerance in world units (default: 0.001)
|
|
15
|
+
*/ constructor(chain, maxIterations = 10, tolerance = 0.001){
|
|
16
|
+
this._chain = chain;
|
|
17
|
+
this._maxIterations = maxIterations;
|
|
18
|
+
this._tolerance = tolerance;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the IK chain.
|
|
22
|
+
*/ get chain() {
|
|
23
|
+
return this._chain;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Set maximum number of iterations.
|
|
27
|
+
*/ setMaxIterations(value) {
|
|
28
|
+
this._maxIterations = Math.max(1, value);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get maximum number of iterations.
|
|
32
|
+
*/ getMaxIterations() {
|
|
33
|
+
return this._maxIterations;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set convergence tolerance.
|
|
37
|
+
*/ setTolerance(value) {
|
|
38
|
+
this._tolerance = Math.max(0, value);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get convergence tolerance.
|
|
42
|
+
*/ getTolerance() {
|
|
43
|
+
return this._tolerance;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { IKSolver };
|
|
48
|
+
//# sourceMappingURL=ik_solver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_solver.js","sources":["../../../src/animation/ik/ik_solver.ts"],"sourcesContent":["import type { Vector3 } from '@zephyr3d/base';\r\nimport type { IKChain } from './ik_chain';\r\n\r\n/**\r\n * Base class for IK solvers.\r\n *\r\n * @public\r\n */\r\nexport abstract class IKSolver {\r\n /** The IK chain to solve */\r\n protected _chain: IKChain;\r\n /** Maximum number of iterations */\r\n protected _maxIterations: number;\r\n /** Convergence tolerance (distance threshold) */\r\n protected _tolerance: number;\r\n\r\n /**\r\n * Create an IK solver.\r\n *\r\n * @param chain - The IK chain to solve\r\n * @param maxIterations - Maximum number of iterations (default: 10)\r\n * @param tolerance - Convergence tolerance in world units (default: 0.001)\r\n */\r\n constructor(chain: IKChain, maxIterations = 10, tolerance = 0.001) {\r\n this._chain = chain;\r\n this._maxIterations = maxIterations;\r\n this._tolerance = tolerance;\r\n }\r\n\r\n /**\r\n * Get the IK chain.\r\n */\r\n get chain(): IKChain {\r\n return this._chain;\r\n }\r\n\r\n /**\r\n * Set maximum number of iterations.\r\n */\r\n setMaxIterations(value: number): void {\r\n this._maxIterations = Math.max(1, value);\r\n }\r\n\r\n /**\r\n * Get maximum number of iterations.\r\n */\r\n getMaxIterations(): number {\r\n return this._maxIterations;\r\n }\r\n\r\n /**\r\n * Set convergence tolerance.\r\n */\r\n setTolerance(value: number): void {\r\n this._tolerance = Math.max(0, value);\r\n }\r\n\r\n /**\r\n * Get convergence tolerance.\r\n */\r\n getTolerance(): number {\r\n return this._tolerance;\r\n }\r\n\r\n /**\r\n * Solve the IK chain to reach the target position.\r\n *\r\n * @param target - Target position for the end effector\r\n * @returns True if converged, false otherwise\r\n */\r\n abstract solve(target: Vector3): boolean;\r\n\r\n /**\r\n * Apply the solved positions to the scene nodes as rotations.\r\n *\r\n * @param weight - Blend weight (0 = original, 1 = full IK)\r\n */\r\n abstract applyToNodes(weight?: number): void;\r\n}\r\n"],"names":["IKSolver","chain","maxIterations","tolerance","_chain","_maxIterations","_tolerance","setMaxIterations","value","Math","max","getMaxIterations","setTolerance","getTolerance"],"mappings":"AAGA;;;;AAIC,IACM,MAAeA,QAAAA,CAAAA;iCAEpB,MAA0B;wCAE1B,cAAiC;sDAEjC,UAA6B;AAE7B;;;;;;MAOA,WAAA,CAAYC,KAAc,EAAEC,aAAAA,GAAgB,EAAE,EAAEC,SAAAA,GAAY,KAAK,CAAE;QACjE,IAAI,CAACC,MAAM,GAAGH,KAAAA;QACd,IAAI,CAACI,cAAc,GAAGH,aAAAA;QACtB,IAAI,CAACI,UAAU,GAAGH,SAAAA;AACpB;AAEA;;AAEC,MACD,IAAIF,KAAiB,GAAA;QACnB,OAAO,IAAI,CAACG,MAAM;AACpB;AAEA;;MAGAG,gBAAAA,CAAiBC,KAAa,EAAQ;AACpC,QAAA,IAAI,CAACH,cAAc,GAAGI,IAAKC,CAAAA,GAAG,CAAC,CAAGF,EAAAA,KAAAA,CAAAA;AACpC;AAEA;;AAEC,MACDG,gBAA2B,GAAA;QACzB,OAAO,IAAI,CAACN,cAAc;AAC5B;AAEA;;MAGAO,YAAAA,CAAaJ,KAAa,EAAQ;AAChC,QAAA,IAAI,CAACF,UAAU,GAAGG,IAAKC,CAAAA,GAAG,CAAC,CAAGF,EAAAA,KAAAA,CAAAA;AAChC;AAEA;;AAEC,MACDK,YAAuB,GAAA;QACrB,OAAO,IAAI,CAACP,UAAU;AACxB;AAgBF;;;;"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Quaternion } from '@zephyr3d/base';
|
|
2
|
+
import { AnimationTrack } from '../animationtrack.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Animation track that applies IK solving.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This track integrates IK solving into the animation system, allowing
|
|
9
|
+
* IK to be blended with other animations. The track solves the IK chain
|
|
10
|
+
* each frame and produces joint rotations that can be mixed with other
|
|
11
|
+
* animation tracks.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/ class IKTrack extends AnimationTrack {
|
|
15
|
+
/** The IK solver */ _solver;
|
|
16
|
+
/** Cached state for reuse */ _state;
|
|
17
|
+
/** Duration of the track (can be infinite for procedural IK) */ _duration;
|
|
18
|
+
/**
|
|
19
|
+
* Create an IK animation track.
|
|
20
|
+
*
|
|
21
|
+
* @param solver - The FABRIK solver to use
|
|
22
|
+
* @param duration - Duration of the track in seconds (default: Infinity for procedural)
|
|
23
|
+
*/ constructor(solver, duration = Infinity){
|
|
24
|
+
super(false);
|
|
25
|
+
this._solver = solver;
|
|
26
|
+
this._duration = duration;
|
|
27
|
+
// Initialize state with identity rotations
|
|
28
|
+
const numJoints = solver.chain.joints.length - 1; // Exclude end effector
|
|
29
|
+
this._state = {
|
|
30
|
+
rotations: Array.from({
|
|
31
|
+
length: numJoints
|
|
32
|
+
}, ()=>new Quaternion())
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the IK solver.
|
|
37
|
+
*/ get solver() {
|
|
38
|
+
return this._solver;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Set the duration of the track.
|
|
42
|
+
*/ setDuration(duration) {
|
|
43
|
+
this._duration = Math.max(0, duration);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Calculate IK state at the given time.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* For procedural IK, the time parameter is typically ignored.
|
|
50
|
+
* The solver uses the current target position set externally.
|
|
51
|
+
*/ calculateState(_target, _currentTime) {
|
|
52
|
+
const joints = this._solver.chain.joints;
|
|
53
|
+
// Store current rotations as the calculated state
|
|
54
|
+
for(let i = 0; i < joints.length - 1; i++){
|
|
55
|
+
this._state.rotations[i].set(joints[i].rotation);
|
|
56
|
+
}
|
|
57
|
+
return this._state;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Apply IK state to the target scene nodes.
|
|
61
|
+
*/ applyState(_target, state) {
|
|
62
|
+
const joints = this._solver.chain.joints;
|
|
63
|
+
// Apply rotations to scene nodes
|
|
64
|
+
for(let i = 0; i < state.rotations.length && i < joints.length - 1; i++){
|
|
65
|
+
joints[i].node.rotation = state.rotations[i];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Mix two IK states using spherical interpolation.
|
|
70
|
+
*/ mixState(a, b, t) {
|
|
71
|
+
const result = {
|
|
72
|
+
rotations: []
|
|
73
|
+
};
|
|
74
|
+
const count = Math.min(a.rotations.length, b.rotations.length);
|
|
75
|
+
for(let i = 0; i < count; i++){
|
|
76
|
+
result.rotations.push(Quaternion.slerp(a.rotations[i], b.rotations[i], t));
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the blend identifier for this track.
|
|
82
|
+
*
|
|
83
|
+
* IK tracks with the same root joint can be blended together.
|
|
84
|
+
*/ getBlendId() {
|
|
85
|
+
const rootNode = this._solver.chain.root.node;
|
|
86
|
+
return `ik-chain-${rootNode.runtimeId}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the duration of this track.
|
|
90
|
+
*/ getDuration() {
|
|
91
|
+
return this._duration;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { IKTrack };
|
|
96
|
+
//# sourceMappingURL=ik_track.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_track.js","sources":["../../../src/animation/ik/ik_track.ts"],"sourcesContent":["import { Quaternion } from '@zephyr3d/base';\r\nimport { AnimationTrack } from '../animationtrack';\r\nimport type { FABRIKSolver } from './fabrik_solver';\r\n\r\n/**\r\n * IK animation track state.\r\n *\r\n * Represents the rotations of all joints in an IK chain at a specific time.\r\n */\r\ninterface IKState {\r\n /** Array of joint rotations (one per joint, excluding end effector) */\r\n rotations: Quaternion[];\r\n}\r\n\r\n/**\r\n * Animation track that applies IK solving.\r\n *\r\n * @remarks\r\n * This track integrates IK solving into the animation system, allowing\r\n * IK to be blended with other animations. The track solves the IK chain\r\n * each frame and produces joint rotations that can be mixed with other\r\n * animation tracks.\r\n *\r\n * @public\r\n */\r\nexport class IKTrack extends AnimationTrack<IKState> {\r\n /** The IK solver */\r\n private _solver: FABRIKSolver;\r\n /** Cached state for reuse */\r\n private _state: IKState;\r\n /** Duration of the track (can be infinite for procedural IK) */\r\n private _duration: number;\r\n\r\n /**\r\n * Create an IK animation track.\r\n *\r\n * @param solver - The FABRIK solver to use\r\n * @param duration - Duration of the track in seconds (default: Infinity for procedural)\r\n */\r\n constructor(solver: FABRIKSolver, duration = Infinity) {\r\n super(false);\r\n this._solver = solver;\r\n this._duration = duration;\r\n\r\n // Initialize state with identity rotations\r\n const numJoints = solver.chain.joints.length - 1; // Exclude end effector\r\n this._state = {\r\n rotations: Array.from({ length: numJoints }, () => new Quaternion())\r\n };\r\n }\r\n\r\n /**\r\n * Get the IK solver.\r\n */\r\n get solver(): FABRIKSolver {\r\n return this._solver;\r\n }\r\n\r\n /**\r\n * Set the duration of the track.\r\n */\r\n setDuration(duration: number): void {\r\n this._duration = Math.max(0, duration);\r\n }\r\n\r\n /**\r\n * Calculate IK state at the given time.\r\n *\r\n * @remarks\r\n * For procedural IK, the time parameter is typically ignored.\r\n * The solver uses the current target position set externally.\r\n */\r\n calculateState(_target: object, _currentTime: number): IKState {\r\n const joints = this._solver.chain.joints;\r\n\r\n // Store current rotations as the calculated state\r\n for (let i = 0; i < joints.length - 1; i++) {\r\n this._state.rotations[i].set(joints[i].rotation);\r\n }\r\n\r\n return this._state;\r\n }\r\n\r\n /**\r\n * Apply IK state to the target scene nodes.\r\n */\r\n applyState(_target: object, state: IKState): void {\r\n const joints = this._solver.chain.joints;\r\n\r\n // Apply rotations to scene nodes\r\n for (let i = 0; i < state.rotations.length && i < joints.length - 1; i++) {\r\n joints[i].node.rotation = state.rotations[i];\r\n }\r\n }\r\n\r\n /**\r\n * Mix two IK states using spherical interpolation.\r\n */\r\n mixState(a: IKState, b: IKState, t: number): IKState {\r\n const result: IKState = {\r\n rotations: []\r\n };\r\n\r\n const count = Math.min(a.rotations.length, b.rotations.length);\r\n for (let i = 0; i < count; i++) {\r\n result.rotations.push(Quaternion.slerp(a.rotations[i], b.rotations[i], t));\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Get the blend identifier for this track.\r\n *\r\n * IK tracks with the same root joint can be blended together.\r\n */\r\n getBlendId(): string {\r\n const rootNode = this._solver.chain.root.node;\r\n return `ik-chain-${rootNode.runtimeId}`;\r\n }\r\n\r\n /**\r\n * Get the duration of this track.\r\n */\r\n getDuration(): number {\r\n return this._duration;\r\n }\r\n}\r\n"],"names":["IKTrack","AnimationTrack","solver","duration","Infinity","_solver","_duration","numJoints","chain","joints","length","_state","rotations","Array","from","Quaternion","setDuration","Math","max","calculateState","_target","_currentTime","i","set","rotation","applyState","state","node","mixState","a","b","t","result","count","min","push","slerp","getBlendId","rootNode","root","runtimeId","getDuration"],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { Vector3, Quaternion } from '@zephyr3d/base';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Utility functions for IK calculations.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/ class IKUtils {
|
|
8
|
+
/**
|
|
9
|
+
* Calculate the rotation needed to align 'from' direction to 'to' direction.
|
|
10
|
+
*
|
|
11
|
+
* @param from - Starting direction vector
|
|
12
|
+
* @param to - Target direction vector
|
|
13
|
+
* @param result - Output quaternion
|
|
14
|
+
* @returns The rotation quaternion
|
|
15
|
+
*/ static fromToRotation(from, to, result) {
|
|
16
|
+
const fromNorm = Vector3.normalize(from, new Vector3());
|
|
17
|
+
const toNorm = Vector3.normalize(to, new Vector3());
|
|
18
|
+
const dot = Vector3.dot(fromNorm, toNorm);
|
|
19
|
+
// Vectors are parallel
|
|
20
|
+
if (dot >= 0.999999) {
|
|
21
|
+
return result.identity();
|
|
22
|
+
}
|
|
23
|
+
// Vectors are opposite
|
|
24
|
+
if (dot <= -0.999999) {
|
|
25
|
+
// Find an orthogonal axis
|
|
26
|
+
let axis = Vector3.cross(Vector3.axisPX(), fromNorm, new Vector3());
|
|
27
|
+
const lenSq = axis.x * axis.x + axis.y * axis.y + axis.z * axis.z;
|
|
28
|
+
if (lenSq < 0.000001) {
|
|
29
|
+
axis = Vector3.cross(Vector3.axisPY(), fromNorm, new Vector3());
|
|
30
|
+
}
|
|
31
|
+
axis.inplaceNormalize();
|
|
32
|
+
return result.fromAxisAngle(axis, Math.PI);
|
|
33
|
+
}
|
|
34
|
+
// General case
|
|
35
|
+
const axis = Vector3.cross(fromNorm, toNorm).inplaceNormalize();
|
|
36
|
+
const angle = Math.acos(dot);
|
|
37
|
+
return result.fromAxisAngle(axis, angle);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Calculate bone length between two positions.
|
|
41
|
+
*
|
|
42
|
+
* @param start - Start position
|
|
43
|
+
* @param end - End position
|
|
44
|
+
* @returns Distance between positions
|
|
45
|
+
*/ static calculateBoneLength(start, end) {
|
|
46
|
+
return Vector3.distance(start, end);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Decompose a quaternion into swing and twist components around a given axis.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* Given a rotation Q and an axis A, decompose Q into:
|
|
53
|
+
* - Twist: rotation around A
|
|
54
|
+
* - Swing: rotation perpendicular to A
|
|
55
|
+
* Such that Q = Twist * Swing
|
|
56
|
+
*
|
|
57
|
+
* Algorithm:
|
|
58
|
+
* 1. Project Q's imaginary part onto axis to get twist component
|
|
59
|
+
* 2. Normalize to get pure twist rotation
|
|
60
|
+
* 3. Calculate swing = twist^-1 * Q
|
|
61
|
+
*
|
|
62
|
+
* @param q - The quaternion to decompose
|
|
63
|
+
* @param axis - The twist axis (must be normalized)
|
|
64
|
+
* @param outSwing - Output swing rotation
|
|
65
|
+
* @param outTwist - Output twist rotation
|
|
66
|
+
*/ static decomposeSwingTwist(q, axis, outSwing, outTwist) {
|
|
67
|
+
// Project quaternion's imaginary part (xyz) onto twist axis
|
|
68
|
+
// twist quaternion = (w, (xyz · axis) * axis)
|
|
69
|
+
const dot = q.x * axis.x + q.y * axis.y + q.z * axis.z;
|
|
70
|
+
outTwist.x = axis.x * dot;
|
|
71
|
+
outTwist.y = axis.y * dot;
|
|
72
|
+
outTwist.z = axis.z * dot;
|
|
73
|
+
outTwist.w = q.w;
|
|
74
|
+
// Normalize twist quaternion
|
|
75
|
+
const twistLen = Math.sqrt(outTwist.x * outTwist.x + outTwist.y * outTwist.y + outTwist.z * outTwist.z + outTwist.w * outTwist.w);
|
|
76
|
+
if (twistLen < 0.000001) {
|
|
77
|
+
// No rotation or rotation perpendicular to axis
|
|
78
|
+
outTwist.identity();
|
|
79
|
+
outSwing.set(q);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
outTwist.x /= twistLen;
|
|
83
|
+
outTwist.y /= twistLen;
|
|
84
|
+
outTwist.z /= twistLen;
|
|
85
|
+
outTwist.w /= twistLen;
|
|
86
|
+
// Calculate swing: Q = Twist * Swing => Swing = Twist^-1 * Q
|
|
87
|
+
const twistInv = Quaternion.conjugate(outTwist, new Quaternion());
|
|
88
|
+
Quaternion.multiply(twistInv, q, outSwing);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the twist angle (in radians) from a twist quaternion around an axis.
|
|
92
|
+
*
|
|
93
|
+
* @param twist - The twist quaternion
|
|
94
|
+
* @param axis - The twist axis (must be normalized)
|
|
95
|
+
* @returns Twist angle in radians, range [-PI, PI]
|
|
96
|
+
*/ static getTwistAngle(twist, axis) {
|
|
97
|
+
// For a twist quaternion around axis: twist = (cos(θ/2), sin(θ/2) * axis)
|
|
98
|
+
// We can extract the angle from the quaternion components
|
|
99
|
+
// Ensure twist is normalized
|
|
100
|
+
const len = Math.sqrt(twist.x * twist.x + twist.y * twist.y + twist.z * twist.z + twist.w * twist.w);
|
|
101
|
+
if (len < 0.000001) {
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
const w = twist.w / len;
|
|
105
|
+
const halfAngle = Math.acos(Math.max(-1, Math.min(1, w)));
|
|
106
|
+
// Determine sign based on axis direction
|
|
107
|
+
const dot = twist.x * axis.x + twist.y * axis.y + twist.z * axis.z;
|
|
108
|
+
const sign = dot >= 0 ? 1 : -1;
|
|
109
|
+
return sign * 2 * halfAngle;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create a twist quaternion from an angle and axis.
|
|
113
|
+
*
|
|
114
|
+
* @param angle - Twist angle in radians
|
|
115
|
+
* @param axis - The twist axis (must be normalized)
|
|
116
|
+
* @param result - Output quaternion
|
|
117
|
+
* @returns The twist quaternion
|
|
118
|
+
*/ static createTwist(angle, axis, result) {
|
|
119
|
+
const halfAngle = angle * 0.5;
|
|
120
|
+
const s = Math.sin(halfAngle);
|
|
121
|
+
result.x = axis.x * s;
|
|
122
|
+
result.y = axis.y * s;
|
|
123
|
+
result.z = axis.z * s;
|
|
124
|
+
result.w = Math.cos(halfAngle);
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Clamp twist angle and optionally smooth with previous twist.
|
|
129
|
+
*
|
|
130
|
+
* @param currentTwist - Current twist angle in radians
|
|
131
|
+
* @param previousTwist - Previous frame's twist angle in radians (optional)
|
|
132
|
+
* @param minTwist - Minimum allowed twist angle in radians
|
|
133
|
+
* @param maxTwist - Maximum allowed twist angle in radians
|
|
134
|
+
* @param smoothFactor - Smoothing factor [0-1], 0 = no smoothing, 1 = full smoothing
|
|
135
|
+
* @returns Clamped and smoothed twist angle
|
|
136
|
+
*/ static clampAndSmoothTwist(currentTwist, previousTwist, minTwist, maxTwist, smoothFactor = 0.5) {
|
|
137
|
+
// Clamp to range
|
|
138
|
+
let clampedTwist = Math.max(minTwist, Math.min(maxTwist, currentTwist));
|
|
139
|
+
// Smooth with previous frame if available
|
|
140
|
+
if (previousTwist !== undefined && smoothFactor > 0.001) {
|
|
141
|
+
// Normalize angle difference to [-PI, PI]
|
|
142
|
+
let diff = clampedTwist - previousTwist;
|
|
143
|
+
while(diff > Math.PI){
|
|
144
|
+
diff -= 2 * Math.PI;
|
|
145
|
+
}
|
|
146
|
+
while(diff < -Math.PI){
|
|
147
|
+
diff += 2 * Math.PI;
|
|
148
|
+
}
|
|
149
|
+
// Lerp
|
|
150
|
+
clampedTwist = previousTwist + diff * (1 - smoothFactor);
|
|
151
|
+
}
|
|
152
|
+
return clampedTwist;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Calculate a look-at rotation with twist constraint using a pole vector.
|
|
156
|
+
*
|
|
157
|
+
* @remarks
|
|
158
|
+
* This creates a rotation that:
|
|
159
|
+
* 1. Aligns 'forward' direction to 'targetDir'
|
|
160
|
+
* 2. Keeps the perpendicular plane aligned with 'poleDir'
|
|
161
|
+
*
|
|
162
|
+
* This prevents unwanted twist around the bone axis by using an external
|
|
163
|
+
* reference direction (pole vector) rather than relying on previous rotations.
|
|
164
|
+
*
|
|
165
|
+
* @param forward - Current forward direction (will be aligned to targetDir)
|
|
166
|
+
* @param targetDir - Desired forward direction
|
|
167
|
+
* @param poleDir - Direction that should be in the "up" hemisphere (from joint position)
|
|
168
|
+
* @param result - Output quaternion
|
|
169
|
+
* @returns The rotation quaternion
|
|
170
|
+
*/ static lookAtRotationWithPole(forward, targetDir, poleDir, result) {
|
|
171
|
+
const forwardNorm = Vector3.normalize(forward, new Vector3());
|
|
172
|
+
const targetDirNorm = Vector3.normalize(targetDir, new Vector3());
|
|
173
|
+
const poleDirNorm = Vector3.normalize(poleDir, new Vector3());
|
|
174
|
+
// First, align forward to targetDir (swing rotation)
|
|
175
|
+
const swingRotation = new Quaternion();
|
|
176
|
+
this.fromToRotation(forwardNorm, targetDirNorm, swingRotation);
|
|
177
|
+
// Calculate the "right" vector perpendicular to targetDir and poleDir
|
|
178
|
+
// This defines the plane that the bone should bend in
|
|
179
|
+
const right = Vector3.cross(targetDirNorm, poleDirNorm, new Vector3());
|
|
180
|
+
const rightLenSq = right.x * right.x + right.y * right.y + right.z * right.z;
|
|
181
|
+
// If pole is parallel to target direction, no twist correction possible
|
|
182
|
+
if (rightLenSq < 0.000001) {
|
|
183
|
+
result.set(swingRotation);
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
right.scaleBy(1 / Math.sqrt(rightLenSq));
|
|
187
|
+
// Calculate the "up" vector perpendicular to targetDir in the pole plane
|
|
188
|
+
const up = Vector3.cross(right, targetDirNorm, new Vector3()).inplaceNormalize();
|
|
189
|
+
// Now we need to find the twist rotation around targetDir
|
|
190
|
+
// We want the local "up" axis to align with the calculated "up" vector
|
|
191
|
+
// Get a perpendicular vector to forward in the original space
|
|
192
|
+
let originalUp = Vector3.cross(forwardNorm, Vector3.axisPY(), new Vector3());
|
|
193
|
+
const originalUpLenSq = originalUp.x * originalUp.x + originalUp.y * originalUp.y + originalUp.z * originalUp.z;
|
|
194
|
+
if (originalUpLenSq < 0.000001) {
|
|
195
|
+
originalUp = Vector3.cross(forwardNorm, Vector3.axisPX(), new Vector3());
|
|
196
|
+
}
|
|
197
|
+
originalUp.inplaceNormalize();
|
|
198
|
+
// Rotate originalUp by swing rotation to see where it ends up
|
|
199
|
+
const rotatedUp = swingRotation.transform(originalUp, new Vector3());
|
|
200
|
+
// Project rotatedUp onto the plane perpendicular to targetDir
|
|
201
|
+
const rotatedUpDot = Vector3.dot(rotatedUp, targetDirNorm);
|
|
202
|
+
const rotatedUpProj = Vector3.scale(targetDirNorm, rotatedUpDot, new Vector3());
|
|
203
|
+
const rotatedUpPerp = Vector3.sub(rotatedUp, rotatedUpProj, new Vector3());
|
|
204
|
+
const rotatedUpPerpLen = rotatedUpPerp.magnitude;
|
|
205
|
+
if (rotatedUpPerpLen < 0.000001) {
|
|
206
|
+
result.set(swingRotation);
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
rotatedUpPerp.scaleBy(1 / rotatedUpPerpLen);
|
|
210
|
+
// Calculate twist rotation to align rotatedUpPerp with up
|
|
211
|
+
const twistRotation = new Quaternion();
|
|
212
|
+
this.fromToRotation(rotatedUpPerp, up, twistRotation);
|
|
213
|
+
// Combine swing and twist
|
|
214
|
+
return Quaternion.multiply(twistRotation, swingRotation, result);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Calculate a look-at rotation with twist constraint.
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* This creates a rotation that:
|
|
221
|
+
* 1. Aligns 'forward' direction to 'targetDir'
|
|
222
|
+
* 2. Keeps 'up' direction as close as possible to 'targetUp'
|
|
223
|
+
*
|
|
224
|
+
* This prevents unwanted twist around the bone axis.
|
|
225
|
+
*
|
|
226
|
+
* @param forward - Current forward direction (will be aligned to targetDir)
|
|
227
|
+
* @param targetDir - Desired forward direction
|
|
228
|
+
* @param up - Current up direction (perpendicular to forward)
|
|
229
|
+
* @param targetUp - Desired up direction (will be projected perpendicular to targetDir)
|
|
230
|
+
* @param result - Output quaternion
|
|
231
|
+
* @returns The rotation quaternion
|
|
232
|
+
*/ static lookAtRotation(forward, targetDir, up, targetUp, result) {
|
|
233
|
+
const forwardNorm = Vector3.normalize(forward, new Vector3());
|
|
234
|
+
const targetDirNorm = Vector3.normalize(targetDir, new Vector3());
|
|
235
|
+
// First, align forward to targetDir (swing rotation)
|
|
236
|
+
const swingRotation = new Quaternion();
|
|
237
|
+
this.fromToRotation(forwardNorm, targetDirNorm, swingRotation);
|
|
238
|
+
// Rotate the up vector by swing rotation
|
|
239
|
+
const rotatedUp = swingRotation.transform(up, new Vector3());
|
|
240
|
+
// Project targetUp onto plane perpendicular to targetDir
|
|
241
|
+
const dot = Vector3.dot(targetUp, targetDirNorm);
|
|
242
|
+
const projection = Vector3.scale(targetDirNorm, dot, new Vector3());
|
|
243
|
+
const targetUpPerp = Vector3.sub(targetUp, projection, new Vector3());
|
|
244
|
+
const targetUpPerpLen = targetUpPerp.magnitude;
|
|
245
|
+
// If targetUp is parallel to targetDir, no twist correction needed
|
|
246
|
+
if (targetUpPerpLen < 0.000001) {
|
|
247
|
+
result.set(swingRotation);
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
targetUpPerp.scaleBy(1 / targetUpPerpLen);
|
|
251
|
+
// Project rotatedUp onto the same plane
|
|
252
|
+
const rotatedUpDot = Vector3.dot(rotatedUp, targetDirNorm);
|
|
253
|
+
const rotatedUpProj = Vector3.scale(targetDirNorm, rotatedUpDot, new Vector3());
|
|
254
|
+
const rotatedUpPerp = Vector3.sub(rotatedUp, rotatedUpProj, new Vector3());
|
|
255
|
+
const rotatedUpPerpLen = rotatedUpPerp.magnitude;
|
|
256
|
+
if (rotatedUpPerpLen < 0.000001) {
|
|
257
|
+
result.set(swingRotation);
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
rotatedUpPerp.scaleBy(1 / rotatedUpPerpLen);
|
|
261
|
+
// Calculate twist rotation around targetDir
|
|
262
|
+
const twistRotation = new Quaternion();
|
|
263
|
+
this.fromToRotation(rotatedUpPerp, targetUpPerp, twistRotation);
|
|
264
|
+
// Combine swing and twist
|
|
265
|
+
return Quaternion.multiply(twistRotation, swingRotation, result);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export { IKUtils };
|
|
270
|
+
//# sourceMappingURL=ik_utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ik_utils.js","sources":["../../../src/animation/ik/ik_utils.ts"],"sourcesContent":["import { Vector3, Quaternion } from '@zephyr3d/base';\r\n\r\n/**\r\n * Utility functions for IK calculations.\r\n *\r\n * @internal\r\n */\r\nexport class IKUtils {\r\n /**\r\n * Calculate the rotation needed to align 'from' direction to 'to' direction.\r\n *\r\n * @param from - Starting direction vector\r\n * @param to - Target direction vector\r\n * @param result - Output quaternion\r\n * @returns The rotation quaternion\r\n */\r\n static fromToRotation(from: Vector3, to: Vector3, result: Quaternion): Quaternion {\r\n const fromNorm = Vector3.normalize(from, new Vector3());\r\n const toNorm = Vector3.normalize(to, new Vector3());\r\n\r\n const dot = Vector3.dot(fromNorm, toNorm);\r\n\r\n // Vectors are parallel\r\n if (dot >= 0.999999) {\r\n return result.identity();\r\n }\r\n\r\n // Vectors are opposite\r\n if (dot <= -0.999999) {\r\n // Find an orthogonal axis\r\n let axis = Vector3.cross(Vector3.axisPX(), fromNorm, new Vector3());\r\n const lenSq = axis.x * axis.x + axis.y * axis.y + axis.z * axis.z;\r\n if (lenSq < 0.000001) {\r\n axis = Vector3.cross(Vector3.axisPY(), fromNorm, new Vector3());\r\n }\r\n axis.inplaceNormalize();\r\n return result.fromAxisAngle(axis, Math.PI);\r\n }\r\n\r\n // General case\r\n const axis = Vector3.cross(fromNorm, toNorm).inplaceNormalize();\r\n const angle = Math.acos(dot);\r\n return result.fromAxisAngle(axis, angle);\r\n }\r\n\r\n /**\r\n * Calculate bone length between two positions.\r\n *\r\n * @param start - Start position\r\n * @param end - End position\r\n * @returns Distance between positions\r\n */\r\n static calculateBoneLength(start: Vector3, end: Vector3): number {\r\n return Vector3.distance(start, end);\r\n }\r\n\r\n /**\r\n * Decompose a quaternion into swing and twist components around a given axis.\r\n *\r\n * @remarks\r\n * Given a rotation Q and an axis A, decompose Q into:\r\n * - Twist: rotation around A\r\n * - Swing: rotation perpendicular to A\r\n * Such that Q = Twist * Swing\r\n *\r\n * Algorithm:\r\n * 1. Project Q's imaginary part onto axis to get twist component\r\n * 2. Normalize to get pure twist rotation\r\n * 3. Calculate swing = twist^-1 * Q\r\n *\r\n * @param q - The quaternion to decompose\r\n * @param axis - The twist axis (must be normalized)\r\n * @param outSwing - Output swing rotation\r\n * @param outTwist - Output twist rotation\r\n */\r\n static decomposeSwingTwist(q: Quaternion, axis: Vector3, outSwing: Quaternion, outTwist: Quaternion): void {\r\n // Project quaternion's imaginary part (xyz) onto twist axis\r\n // twist quaternion = (w, (xyz · axis) * axis)\r\n const dot = q.x * axis.x + q.y * axis.y + q.z * axis.z;\r\n\r\n outTwist.x = axis.x * dot;\r\n outTwist.y = axis.y * dot;\r\n outTwist.z = axis.z * dot;\r\n outTwist.w = q.w;\r\n\r\n // Normalize twist quaternion\r\n const twistLen = Math.sqrt(\r\n outTwist.x * outTwist.x + outTwist.y * outTwist.y + outTwist.z * outTwist.z + outTwist.w * outTwist.w\r\n );\r\n\r\n if (twistLen < 0.000001) {\r\n // No rotation or rotation perpendicular to axis\r\n outTwist.identity();\r\n outSwing.set(q);\r\n return;\r\n }\r\n\r\n outTwist.x /= twistLen;\r\n outTwist.y /= twistLen;\r\n outTwist.z /= twistLen;\r\n outTwist.w /= twistLen;\r\n\r\n // Calculate swing: Q = Twist * Swing => Swing = Twist^-1 * Q\r\n const twistInv = Quaternion.conjugate(outTwist, new Quaternion());\r\n Quaternion.multiply(twistInv, q, outSwing);\r\n }\r\n\r\n /**\r\n * Get the twist angle (in radians) from a twist quaternion around an axis.\r\n *\r\n * @param twist - The twist quaternion\r\n * @param axis - The twist axis (must be normalized)\r\n * @returns Twist angle in radians, range [-PI, PI]\r\n */\r\n static getTwistAngle(twist: Quaternion, axis: Vector3): number {\r\n // For a twist quaternion around axis: twist = (cos(θ/2), sin(θ/2) * axis)\r\n // We can extract the angle from the quaternion components\r\n\r\n // Ensure twist is normalized\r\n const len = Math.sqrt(twist.x * twist.x + twist.y * twist.y + twist.z * twist.z + twist.w * twist.w);\r\n if (len < 0.000001) {\r\n return 0;\r\n }\r\n\r\n const w = twist.w / len;\r\n const halfAngle = Math.acos(Math.max(-1, Math.min(1, w)));\r\n\r\n // Determine sign based on axis direction\r\n const dot = twist.x * axis.x + twist.y * axis.y + twist.z * axis.z;\r\n const sign = dot >= 0 ? 1 : -1;\r\n\r\n return sign * 2 * halfAngle;\r\n }\r\n\r\n /**\r\n * Create a twist quaternion from an angle and axis.\r\n *\r\n * @param angle - Twist angle in radians\r\n * @param axis - The twist axis (must be normalized)\r\n * @param result - Output quaternion\r\n * @returns The twist quaternion\r\n */\r\n static createTwist(angle: number, axis: Vector3, result: Quaternion): Quaternion {\r\n const halfAngle = angle * 0.5;\r\n const s = Math.sin(halfAngle);\r\n result.x = axis.x * s;\r\n result.y = axis.y * s;\r\n result.z = axis.z * s;\r\n result.w = Math.cos(halfAngle);\r\n return result;\r\n }\r\n\r\n /**\r\n * Clamp twist angle and optionally smooth with previous twist.\r\n *\r\n * @param currentTwist - Current twist angle in radians\r\n * @param previousTwist - Previous frame's twist angle in radians (optional)\r\n * @param minTwist - Minimum allowed twist angle in radians\r\n * @param maxTwist - Maximum allowed twist angle in radians\r\n * @param smoothFactor - Smoothing factor [0-1], 0 = no smoothing, 1 = full smoothing\r\n * @returns Clamped and smoothed twist angle\r\n */\r\n static clampAndSmoothTwist(\r\n currentTwist: number,\r\n previousTwist: number | undefined,\r\n minTwist: number,\r\n maxTwist: number,\r\n smoothFactor: number = 0.5\r\n ): number {\r\n // Clamp to range\r\n let clampedTwist = Math.max(minTwist, Math.min(maxTwist, currentTwist));\r\n\r\n // Smooth with previous frame if available\r\n if (previousTwist !== undefined && smoothFactor > 0.001) {\r\n // Normalize angle difference to [-PI, PI]\r\n let diff = clampedTwist - previousTwist;\r\n while (diff > Math.PI) {\r\n diff -= 2 * Math.PI;\r\n }\r\n while (diff < -Math.PI) {\r\n diff += 2 * Math.PI;\r\n }\r\n\r\n // Lerp\r\n clampedTwist = previousTwist + diff * (1 - smoothFactor);\r\n }\r\n\r\n return clampedTwist;\r\n }\r\n\r\n /**\r\n * Calculate a look-at rotation with twist constraint using a pole vector.\r\n *\r\n * @remarks\r\n * This creates a rotation that:\r\n * 1. Aligns 'forward' direction to 'targetDir'\r\n * 2. Keeps the perpendicular plane aligned with 'poleDir'\r\n *\r\n * This prevents unwanted twist around the bone axis by using an external\r\n * reference direction (pole vector) rather than relying on previous rotations.\r\n *\r\n * @param forward - Current forward direction (will be aligned to targetDir)\r\n * @param targetDir - Desired forward direction\r\n * @param poleDir - Direction that should be in the \"up\" hemisphere (from joint position)\r\n * @param result - Output quaternion\r\n * @returns The rotation quaternion\r\n */\r\n static lookAtRotationWithPole(\r\n forward: Vector3,\r\n targetDir: Vector3,\r\n poleDir: Vector3,\r\n result: Quaternion\r\n ): Quaternion {\r\n const forwardNorm = Vector3.normalize(forward, new Vector3());\r\n const targetDirNorm = Vector3.normalize(targetDir, new Vector3());\r\n const poleDirNorm = Vector3.normalize(poleDir, new Vector3());\r\n\r\n // First, align forward to targetDir (swing rotation)\r\n const swingRotation = new Quaternion();\r\n this.fromToRotation(forwardNorm, targetDirNorm, swingRotation);\r\n\r\n // Calculate the \"right\" vector perpendicular to targetDir and poleDir\r\n // This defines the plane that the bone should bend in\r\n const right = Vector3.cross(targetDirNorm, poleDirNorm, new Vector3());\r\n const rightLenSq = right.x * right.x + right.y * right.y + right.z * right.z;\r\n\r\n // If pole is parallel to target direction, no twist correction possible\r\n if (rightLenSq < 0.000001) {\r\n result.set(swingRotation);\r\n return result;\r\n }\r\n\r\n right.scaleBy(1 / Math.sqrt(rightLenSq));\r\n\r\n // Calculate the \"up\" vector perpendicular to targetDir in the pole plane\r\n const up = Vector3.cross(right, targetDirNorm, new Vector3()).inplaceNormalize();\r\n\r\n // Now we need to find the twist rotation around targetDir\r\n // We want the local \"up\" axis to align with the calculated \"up\" vector\r\n\r\n // Get a perpendicular vector to forward in the original space\r\n let originalUp = Vector3.cross(forwardNorm, Vector3.axisPY(), new Vector3());\r\n const originalUpLenSq =\r\n originalUp.x * originalUp.x + originalUp.y * originalUp.y + originalUp.z * originalUp.z;\r\n if (originalUpLenSq < 0.000001) {\r\n originalUp = Vector3.cross(forwardNorm, Vector3.axisPX(), new Vector3());\r\n }\r\n originalUp.inplaceNormalize();\r\n\r\n // Rotate originalUp by swing rotation to see where it ends up\r\n const rotatedUp = swingRotation.transform(originalUp, new Vector3());\r\n\r\n // Project rotatedUp onto the plane perpendicular to targetDir\r\n const rotatedUpDot = Vector3.dot(rotatedUp, targetDirNorm);\r\n const rotatedUpProj = Vector3.scale(targetDirNorm, rotatedUpDot, new Vector3());\r\n const rotatedUpPerp = Vector3.sub(rotatedUp, rotatedUpProj, new Vector3());\r\n const rotatedUpPerpLen = rotatedUpPerp.magnitude;\r\n\r\n if (rotatedUpPerpLen < 0.000001) {\r\n result.set(swingRotation);\r\n return result;\r\n }\r\n\r\n rotatedUpPerp.scaleBy(1 / rotatedUpPerpLen);\r\n\r\n // Calculate twist rotation to align rotatedUpPerp with up\r\n const twistRotation = new Quaternion();\r\n this.fromToRotation(rotatedUpPerp, up, twistRotation);\r\n\r\n // Combine swing and twist\r\n return Quaternion.multiply(twistRotation, swingRotation, result);\r\n }\r\n\r\n /**\r\n * Calculate a look-at rotation with twist constraint.\r\n *\r\n * @remarks\r\n * This creates a rotation that:\r\n * 1. Aligns 'forward' direction to 'targetDir'\r\n * 2. Keeps 'up' direction as close as possible to 'targetUp'\r\n *\r\n * This prevents unwanted twist around the bone axis.\r\n *\r\n * @param forward - Current forward direction (will be aligned to targetDir)\r\n * @param targetDir - Desired forward direction\r\n * @param up - Current up direction (perpendicular to forward)\r\n * @param targetUp - Desired up direction (will be projected perpendicular to targetDir)\r\n * @param result - Output quaternion\r\n * @returns The rotation quaternion\r\n */\r\n static lookAtRotation(\r\n forward: Vector3,\r\n targetDir: Vector3,\r\n up: Vector3,\r\n targetUp: Vector3,\r\n result: Quaternion\r\n ): Quaternion {\r\n const forwardNorm = Vector3.normalize(forward, new Vector3());\r\n const targetDirNorm = Vector3.normalize(targetDir, new Vector3());\r\n\r\n // First, align forward to targetDir (swing rotation)\r\n const swingRotation = new Quaternion();\r\n this.fromToRotation(forwardNorm, targetDirNorm, swingRotation);\r\n\r\n // Rotate the up vector by swing rotation\r\n const rotatedUp = swingRotation.transform(up, new Vector3());\r\n\r\n // Project targetUp onto plane perpendicular to targetDir\r\n const dot = Vector3.dot(targetUp, targetDirNorm);\r\n const projection = Vector3.scale(targetDirNorm, dot, new Vector3());\r\n const targetUpPerp = Vector3.sub(targetUp, projection, new Vector3());\r\n const targetUpPerpLen = targetUpPerp.magnitude;\r\n\r\n // If targetUp is parallel to targetDir, no twist correction needed\r\n if (targetUpPerpLen < 0.000001) {\r\n result.set(swingRotation);\r\n return result;\r\n }\r\n\r\n targetUpPerp.scaleBy(1 / targetUpPerpLen);\r\n\r\n // Project rotatedUp onto the same plane\r\n const rotatedUpDot = Vector3.dot(rotatedUp, targetDirNorm);\r\n const rotatedUpProj = Vector3.scale(targetDirNorm, rotatedUpDot, new Vector3());\r\n const rotatedUpPerp = Vector3.sub(rotatedUp, rotatedUpProj, new Vector3());\r\n const rotatedUpPerpLen = rotatedUpPerp.magnitude;\r\n\r\n if (rotatedUpPerpLen < 0.000001) {\r\n result.set(swingRotation);\r\n return result;\r\n }\r\n\r\n rotatedUpPerp.scaleBy(1 / rotatedUpPerpLen);\r\n\r\n // Calculate twist rotation around targetDir\r\n const twistRotation = new Quaternion();\r\n this.fromToRotation(rotatedUpPerp, targetUpPerp, twistRotation);\r\n\r\n // Combine swing and twist\r\n return Quaternion.multiply(twistRotation, swingRotation, result);\r\n }\r\n}\r\n"],"names":["IKUtils","fromToRotation","from","to","result","fromNorm","Vector3","normalize","toNorm","dot","identity","axis","cross","axisPX","lenSq","x","y","z","axisPY","inplaceNormalize","fromAxisAngle","Math","PI","angle","acos","calculateBoneLength","start","end","distance","decomposeSwingTwist","q","outSwing","outTwist","w","twistLen","sqrt","set","twistInv","Quaternion","conjugate","multiply","getTwistAngle","twist","len","halfAngle","max","min","sign","createTwist","s","sin","cos","clampAndSmoothTwist","currentTwist","previousTwist","minTwist","maxTwist","smoothFactor","clampedTwist","undefined","diff","lookAtRotationWithPole","forward","targetDir","poleDir","forwardNorm","targetDirNorm","poleDirNorm","swingRotation","right","rightLenSq","scaleBy","up","originalUp","originalUpLenSq","rotatedUp","transform","rotatedUpDot","rotatedUpProj","scale","rotatedUpPerp","sub","rotatedUpPerpLen","magnitude","twistRotation","lookAtRotation","targetUp","projection","targetUpPerp","targetUpPerpLen"],"mappings":"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"}
|