reze-engine 0.12.3 → 0.13.1
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/README.md +32 -29
- package/dist/ik-solver.d.ts +5 -6
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +163 -98
- package/dist/math.d.ts +20 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +300 -0
- package/dist/model.d.ts +0 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +61 -46
- package/dist/physics.d.ts.map +1 -1
- package/dist/physics.js +27 -19
- package/package.json +1 -1
- package/src/ik-solver.ts +164 -111
- package/src/math.ts +291 -0
- package/src/model.ts +68 -55
- package/src/physics.ts +31 -23
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ npm install reze-engine
|
|
|
27
27
|
## Usage
|
|
28
28
|
|
|
29
29
|
```javascript
|
|
30
|
-
import { Engine, Vec3 } from "reze-engine"
|
|
30
|
+
import { Engine, Vec3 } from "reze-engine";
|
|
31
31
|
|
|
32
32
|
const engine = new Engine(canvas, {
|
|
33
33
|
world: { color: new Vec3(0.4, 0.49, 0.65), strength: 1.0 },
|
|
@@ -36,17 +36,16 @@ const engine = new Engine(canvas, {
|
|
|
36
36
|
strength: 2.0,
|
|
37
37
|
direction: new Vec3(0, -0.5, 1),
|
|
38
38
|
},
|
|
39
|
+
bloom: {
|
|
40
|
+
color: new Vec3(0.9, 0.1, 0.8),
|
|
41
|
+
intensity: 0.05,
|
|
42
|
+
threshold: 0.5,
|
|
43
|
+
}
|
|
39
44
|
camera: { distance: 31.5, target: new Vec3(0, 11.5, 0) }, // MMD units (1 unit = 8 cm)
|
|
40
|
-
})
|
|
41
|
-
await engine.init()
|
|
45
|
+
});
|
|
46
|
+
await engine.init();
|
|
42
47
|
|
|
43
|
-
engine.
|
|
44
|
-
color: new Vec3(0.9, 0.1, 0.8),
|
|
45
|
-
intensity: 0.05,
|
|
46
|
-
threshold: 0.5,
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const model = await engine.loadModel("hero", "/models/hero/hero.pmx")
|
|
48
|
+
const model = await engine.loadModel("hero", "/models/hero/hero.pmx");
|
|
50
49
|
|
|
51
50
|
// Map PMX material names to NPR presets (unlisted names fall back to `default`).
|
|
52
51
|
engine.setMaterialPresets("hero", {
|
|
@@ -58,15 +57,15 @@ engine.setMaterialPresets("hero", {
|
|
|
58
57
|
cloth_rough: ["jacket", "pants"],
|
|
59
58
|
stockings: ["stockings"],
|
|
60
59
|
metal: ["metal01", "earring"],
|
|
61
|
-
})
|
|
60
|
+
});
|
|
62
61
|
|
|
63
|
-
await model.loadVmd("idle", "/animations/idle.vmd")
|
|
64
|
-
model.show("idle")
|
|
65
|
-
model.play()
|
|
62
|
+
await model.loadVmd("idle", "/animations/idle.vmd");
|
|
63
|
+
model.show("idle");
|
|
64
|
+
model.play();
|
|
66
65
|
|
|
67
|
-
engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0))
|
|
68
|
-
engine.addGround({ width: 160, height: 160 })
|
|
69
|
-
engine.runRenderLoop()
|
|
66
|
+
engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0));
|
|
67
|
+
engine.addGround({ width: 160, height: 160 });
|
|
68
|
+
engine.runRenderLoop();
|
|
70
69
|
```
|
|
71
70
|
|
|
72
71
|
## API
|
|
@@ -115,26 +114,30 @@ Use a hidden `<input type="file" webkitdirectory multiple>` (or drag/drop) and p
|
|
|
115
114
|
2. **`engine.loadModel(name, { files, pmxFile })`** — `pmxFile` selects which `.pmx` when the folder contains several.
|
|
116
115
|
|
|
117
116
|
```javascript
|
|
118
|
-
import {
|
|
117
|
+
import {
|
|
118
|
+
Engine,
|
|
119
|
+
parsePmxFolderInput,
|
|
120
|
+
pmxFileAtRelativePath,
|
|
121
|
+
} from "reze-engine";
|
|
119
122
|
|
|
120
123
|
// In <input onChange>:
|
|
121
|
-
const picked = parsePmxFolderInput(e.target.files)
|
|
122
|
-
e.target.value = ""
|
|
124
|
+
const picked = parsePmxFolderInput(e.target.files);
|
|
125
|
+
e.target.value = "";
|
|
123
126
|
|
|
124
127
|
if (picked.status === "single") {
|
|
125
128
|
const model = await engine.loadModel("myModel", {
|
|
126
129
|
files: picked.files,
|
|
127
130
|
pmxFile: picked.pmxFile,
|
|
128
|
-
})
|
|
131
|
+
});
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
if (picked.status === "multiple") {
|
|
132
135
|
// Let the user choose `chosenPath` from picked.pmxRelativePaths, then:
|
|
133
|
-
const pmxFile = pmxFileAtRelativePath(picked.files, chosenPath)
|
|
136
|
+
const pmxFile = pmxFileAtRelativePath(picked.files, chosenPath);
|
|
134
137
|
const model = await engine.loadModel("myModel", {
|
|
135
138
|
files: picked.files,
|
|
136
139
|
pmxFile,
|
|
137
|
-
})
|
|
140
|
+
});
|
|
138
141
|
}
|
|
139
142
|
```
|
|
140
143
|
|
|
@@ -173,12 +176,12 @@ model.getBoneWorldPosition(name)
|
|
|
173
176
|
`model.exportVmd(name)` serialises a loaded clip back to the VMD binary format and returns an `ArrayBuffer`. Bone and morph names are Shift-JIS encoded for compatibility with standard MMD tools.
|
|
174
177
|
|
|
175
178
|
```javascript
|
|
176
|
-
const buffer = model.exportVmd("idle")
|
|
177
|
-
const blob = new Blob([buffer], { type: "application/octet-stream" })
|
|
178
|
-
const link = document.createElement("a")
|
|
179
|
-
link.href = URL.createObjectURL(blob)
|
|
180
|
-
link.download = "idle.vmd"
|
|
181
|
-
link.click()
|
|
179
|
+
const buffer = model.exportVmd("idle");
|
|
180
|
+
const blob = new Blob([buffer], { type: "application/octet-stream" });
|
|
181
|
+
const link = document.createElement("a");
|
|
182
|
+
link.href = URL.createObjectURL(blob);
|
|
183
|
+
link.download = "idle.vmd";
|
|
184
|
+
link.click();
|
|
182
185
|
```
|
|
183
186
|
|
|
184
187
|
#### Playback
|
package/dist/ik-solver.d.ts
CHANGED
|
@@ -9,12 +9,11 @@ export declare class IKSolverSystem {
|
|
|
9
9
|
private static solveChain;
|
|
10
10
|
private static limitAngle;
|
|
11
11
|
private static getDistance;
|
|
12
|
-
private static
|
|
13
|
-
private static
|
|
14
|
-
private static
|
|
15
|
-
private static
|
|
16
|
-
private static
|
|
17
|
-
private static transformNormal;
|
|
12
|
+
private static readonly EULER_AXES;
|
|
13
|
+
private static extractEulerAnglesInto;
|
|
14
|
+
private static limitEulerAnglesInto;
|
|
15
|
+
private static reconstructQuatFromEulerInto;
|
|
16
|
+
private static getParentWorldRotationMatrixInto;
|
|
18
17
|
private static updateWorldMatrix;
|
|
19
18
|
}
|
|
20
19
|
//# sourceMappingURL=ik-solver.d.ts.map
|
package/dist/ik-solver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG7D,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG7D,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAkF/E,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD,MAAM,CAAC,KAAK,CACV,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,IAAI,EAAE,EACtB,iBAAiB,EAAE,IAAI,EAAE,EACzB,aAAa,EAAE,IAAI,EAAE,EACrB,WAAW,EAAE,WAAW,EAAE,EAC1B,iBAAiB,CAAC,EAAE,mBAAmB,GACtC,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IAoFtB,OAAO,CAAC,MAAM,CAAC,UAAU;IA4HzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAW1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAOjC;IAED,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAsCrC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAMnC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAyB3C,OAAO,CAAC,MAAM,CAAC,gCAAgC;IAkB/C,OAAO,CAAC,MAAM,CAAC,iBAAiB;CA2CjC"}
|
package/dist/ik-solver.js
CHANGED
|
@@ -63,6 +63,19 @@ class IKChain {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
// IK-local scratch pool. Safe because solve() runs synchronously and all scratch
|
|
67
|
+
// use completes before the updateWorldMatrix callback is invoked.
|
|
68
|
+
const _ikVec = [
|
|
69
|
+
new Vec3(0, 0, 0), new Vec3(0, 0, 0), new Vec3(0, 0, 0), new Vec3(0, 0, 0), new Vec3(0, 0, 0),
|
|
70
|
+
new Vec3(0, 0, 0), new Vec3(0, 0, 0), new Vec3(0, 0, 0), new Vec3(0, 0, 0),
|
|
71
|
+
];
|
|
72
|
+
const _ikQuat = [
|
|
73
|
+
new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1),
|
|
74
|
+
new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1), new Quat(0, 0, 0, 1),
|
|
75
|
+
];
|
|
76
|
+
const _ikMat = [
|
|
77
|
+
new Float32Array(16), new Float32Array(16), new Float32Array(16), new Float32Array(16),
|
|
78
|
+
];
|
|
66
79
|
export class IKSolverSystem {
|
|
67
80
|
static solve(ikSolvers, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, updateWorldMatrix) {
|
|
68
81
|
for (const solver of ikSolvers) {
|
|
@@ -116,77 +129,100 @@ export class IKSolverSystem {
|
|
|
116
129
|
if (this.getDistance(ikBoneIndex, targetBoneIndex, worldMatrices) < this.EPSILON)
|
|
117
130
|
break;
|
|
118
131
|
}
|
|
119
|
-
// Apply IK rotations to local rotations
|
|
132
|
+
// Apply IK rotations to local rotations (mutate localRot in place)
|
|
120
133
|
for (const link of solver.links) {
|
|
121
134
|
const chainInfo = ikChainInfo[link.boneIndex];
|
|
122
135
|
if (chainInfo?.ikRotation) {
|
|
123
136
|
const localRot = localRotations[link.boneIndex];
|
|
124
|
-
|
|
125
|
-
localRot.
|
|
137
|
+
Quat.multiplyInto(chainInfo.ikRotation, localRot, localRot);
|
|
138
|
+
localRot.normalize();
|
|
126
139
|
}
|
|
127
140
|
}
|
|
128
141
|
}
|
|
129
142
|
static solveChain(chain, chainIndex, solver, ikBoneIndex, targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, useAxis, updateWorldMatrix) {
|
|
130
143
|
const chainBoneIndex = chain.boneIndex;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
144
|
+
// scratch layout:
|
|
145
|
+
// _ikVec[0]=chainPos, [1]=ikPos, [2]=targetPos, [3]=chainTargetVec, [4]=chainIkVec,
|
|
146
|
+
// [5]=rotAxis, [6]=finalAxis, [7]=eulerTmp, [8]=limitedEuler
|
|
147
|
+
// _ikMat[0]=parentRot, [1]=invParentRot, [2]=quatToMatTmp
|
|
148
|
+
// _ikQuat[0]=ikRotation, [1]=combinedRot, [2]=localRotConj, [3..5]=axisAngleTmp
|
|
149
|
+
const chainPos = Vec3.setFromMat4Translation(worldMatrices[chainBoneIndex].values, _ikVec[0]);
|
|
150
|
+
const ikPos = Vec3.setFromMat4Translation(worldMatrices[ikBoneIndex].values, _ikVec[1]);
|
|
151
|
+
const targetPos = Vec3.setFromMat4Translation(worldMatrices[targetBoneIndex].values, _ikVec[2]);
|
|
152
|
+
const chainTargetVec = Vec3.subtractInto(chainPos, targetPos, _ikVec[3]);
|
|
153
|
+
chainTargetVec.normalize();
|
|
154
|
+
const chainIkVec = Vec3.subtractInto(chainPos, ikPos, _ikVec[4]);
|
|
155
|
+
chainIkVec.normalize();
|
|
156
|
+
const rotAxis = Vec3.crossInto(chainTargetVec, chainIkVec, _ikVec[5]);
|
|
157
|
+
if (rotAxis.length() < this.EPSILON)
|
|
138
158
|
return;
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
let
|
|
159
|
+
// Parent world rotation matrix (translation removed) into _ikMat[0]
|
|
160
|
+
this.getParentWorldRotationMatrixInto(chainBoneIndex, bones, worldMatrices, _ikMat[0]);
|
|
161
|
+
let finalAxis;
|
|
142
162
|
if (chain.minimumAngle !== null && useAxis) {
|
|
143
163
|
switch (chain.solveAxis) {
|
|
144
164
|
case InternalSolveAxis.None: {
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
if (!Mat4.inverseInto(_ikMat[0], _ikMat[1])) {
|
|
166
|
+
finalAxis = rotAxis;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
finalAxis = Vec3.transformMat4RotationInto(rotAxis, _ikMat[1], _ikVec[6]);
|
|
170
|
+
finalAxis.normalize();
|
|
171
|
+
}
|
|
147
172
|
break;
|
|
148
173
|
}
|
|
149
174
|
case InternalSolveAxis.X:
|
|
150
175
|
case InternalSolveAxis.Y:
|
|
151
176
|
case InternalSolveAxis.Z: {
|
|
152
|
-
const m =
|
|
177
|
+
const m = _ikMat[0];
|
|
153
178
|
const axisOffset = (chain.solveAxis - InternalSolveAxis.X) * 4;
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const sign =
|
|
157
|
-
|
|
179
|
+
const ax = m[axisOffset], ay = m[axisOffset + 1], az = m[axisOffset + 2];
|
|
180
|
+
const dotA = rotAxis.x * ax + rotAxis.y * ay + rotAxis.z * az;
|
|
181
|
+
const sign = dotA >= 0 ? 1 : -1;
|
|
182
|
+
finalAxis =
|
|
158
183
|
chain.solveAxis === InternalSolveAxis.X
|
|
159
|
-
?
|
|
184
|
+
? _ikVec[6].setXYZ(sign, 0, 0)
|
|
160
185
|
: chain.solveAxis === InternalSolveAxis.Y
|
|
161
|
-
?
|
|
162
|
-
:
|
|
186
|
+
? _ikVec[6].setXYZ(0, sign, 0)
|
|
187
|
+
: _ikVec[6].setXYZ(0, 0, sign);
|
|
163
188
|
break;
|
|
164
189
|
}
|
|
165
190
|
default:
|
|
166
|
-
|
|
191
|
+
finalAxis = rotAxis;
|
|
167
192
|
}
|
|
168
193
|
}
|
|
169
194
|
else {
|
|
170
|
-
|
|
171
|
-
|
|
195
|
+
if (!Mat4.inverseInto(_ikMat[0], _ikMat[1])) {
|
|
196
|
+
finalAxis = rotAxis;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
finalAxis = Vec3.transformMat4RotationInto(rotAxis, _ikMat[1], _ikVec[6]);
|
|
200
|
+
finalAxis.normalize();
|
|
201
|
+
}
|
|
172
202
|
}
|
|
173
|
-
let
|
|
174
|
-
|
|
175
|
-
const angle = Math.min(solver.limitAngle * (chainIndex + 1), Math.acos(
|
|
176
|
-
const ikRotation = Quat.
|
|
203
|
+
let dotTI = chainTargetVec.dot(chainIkVec);
|
|
204
|
+
dotTI = Math.max(-1.0, Math.min(1.0, dotTI));
|
|
205
|
+
const angle = Math.min(solver.limitAngle * (chainIndex + 1), Math.acos(dotTI));
|
|
206
|
+
const ikRotation = Quat.fromAxisAngleInto(finalAxis.x, finalAxis.y, finalAxis.z, angle, _ikQuat[0]);
|
|
177
207
|
const chainInfo = ikChainInfo[chainBoneIndex];
|
|
178
208
|
if (chainInfo) {
|
|
179
|
-
chainInfo.ikRotation = ikRotation
|
|
180
|
-
|
|
209
|
+
// chainInfo.ikRotation = ikRotation * chainInfo.ikRotation (in place)
|
|
210
|
+
Quat.multiplyInto(ikRotation, chainInfo.ikRotation, chainInfo.ikRotation);
|
|
181
211
|
if (chain.minimumAngle && chain.maximumAngle) {
|
|
182
212
|
const localRot = localRotations[chainBoneIndex];
|
|
183
213
|
chainInfo.localRotation = localRot.clone();
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
//
|
|
189
|
-
|
|
214
|
+
// combinedRot = chainInfo.ikRotation * localRot
|
|
215
|
+
Quat.multiplyInto(chainInfo.ikRotation, localRot, _ikQuat[1]);
|
|
216
|
+
// extract euler into _ikVec[7]
|
|
217
|
+
this.extractEulerAnglesInto(_ikQuat[1], chain.rotationOrder, _ikVec[7]);
|
|
218
|
+
// limit into _ikVec[8]
|
|
219
|
+
this.limitEulerAnglesInto(_ikVec[7], chain.minimumAngle, chain.maximumAngle, useAxis, _ikVec[8]);
|
|
220
|
+
// reconstruct quat into chainInfo.ikRotation (uses _ikQuat[3..5] as tmp)
|
|
221
|
+
this.reconstructQuatFromEulerInto(_ikVec[8], chain.rotationOrder, chainInfo.ikRotation);
|
|
222
|
+
// localRot conjugate into _ikQuat[2] (localRot is unit, so conjugate == inverse)
|
|
223
|
+
_ikQuat[2].setXYZW(-localRot.x, -localRot.y, -localRot.z, localRot.w);
|
|
224
|
+
// chainInfo.ikRotation *= localRotConj
|
|
225
|
+
Quat.multiplyInto(chainInfo.ikRotation, _ikQuat[2], chainInfo.ikRotation);
|
|
190
226
|
}
|
|
191
227
|
}
|
|
192
228
|
// Update world matrices for affected bones (using callback - handles append correctly)
|
|
@@ -220,17 +256,16 @@ export class IKSolverSystem {
|
|
|
220
256
|
}
|
|
221
257
|
}
|
|
222
258
|
static getDistance(boneIndex1, boneIndex2, worldMatrices) {
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
|
|
259
|
+
const m1 = worldMatrices[boneIndex1].values;
|
|
260
|
+
const m2 = worldMatrices[boneIndex2].values;
|
|
261
|
+
const dx = m1[12] - m2[12];
|
|
262
|
+
const dy = m1[13] - m2[13];
|
|
263
|
+
const dz = m1[14] - m2[14];
|
|
264
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
226
265
|
}
|
|
227
|
-
static
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
static extractEulerAngles(quat, order) {
|
|
232
|
-
const rotMatrix = Mat4.fromQuat(quat.x, quat.y, quat.z, quat.w);
|
|
233
|
-
const m = rotMatrix.values;
|
|
266
|
+
static extractEulerAnglesInto(quat, order, out) {
|
|
267
|
+
Mat4.fromQuatInto(quat.x, quat.y, quat.z, quat.w, _ikMat[2], 0);
|
|
268
|
+
const m = _ikMat[2];
|
|
234
269
|
switch (order) {
|
|
235
270
|
case InternalEulerRotationOrder.YXZ: {
|
|
236
271
|
let rX = Math.asin(-m[9]);
|
|
@@ -239,9 +274,10 @@ export class IKSolverSystem {
|
|
|
239
274
|
let cosX = Math.cos(rX);
|
|
240
275
|
if (cosX !== 0)
|
|
241
276
|
cosX = 1 / cosX;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
277
|
+
out.x = rX;
|
|
278
|
+
out.y = Math.atan2(m[8] * cosX, m[10] * cosX);
|
|
279
|
+
out.z = Math.atan2(m[1] * cosX, m[5] * cosX);
|
|
280
|
+
return;
|
|
245
281
|
}
|
|
246
282
|
case InternalEulerRotationOrder.ZYX: {
|
|
247
283
|
let rY = Math.asin(-m[2]);
|
|
@@ -250,9 +286,10 @@ export class IKSolverSystem {
|
|
|
250
286
|
let cosY = Math.cos(rY);
|
|
251
287
|
if (cosY !== 0)
|
|
252
288
|
cosY = 1 / cosY;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
289
|
+
out.x = Math.atan2(m[6] * cosY, m[10] * cosY);
|
|
290
|
+
out.y = rY;
|
|
291
|
+
out.z = Math.atan2(m[1] * cosY, m[0] * cosY);
|
|
292
|
+
return;
|
|
256
293
|
}
|
|
257
294
|
case InternalEulerRotationOrder.XZY: {
|
|
258
295
|
let rZ = Math.asin(-m[4]);
|
|
@@ -261,77 +298,105 @@ export class IKSolverSystem {
|
|
|
261
298
|
let cosZ = Math.cos(rZ);
|
|
262
299
|
if (cosZ !== 0)
|
|
263
300
|
cosZ = 1 / cosZ;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
301
|
+
out.x = Math.atan2(m[6] * cosZ, m[5] * cosZ);
|
|
302
|
+
out.y = Math.atan2(m[8] * cosZ, m[0] * cosZ);
|
|
303
|
+
out.z = rZ;
|
|
304
|
+
return;
|
|
267
305
|
}
|
|
268
306
|
}
|
|
269
307
|
}
|
|
270
|
-
static
|
|
271
|
-
|
|
308
|
+
static limitEulerAnglesInto(euler, min, max, useAxis, out) {
|
|
309
|
+
out.x = this.limitAngle(euler.x, min.x, max.x, useAxis);
|
|
310
|
+
out.y = this.limitAngle(euler.y, min.y, max.y, useAxis);
|
|
311
|
+
out.z = this.limitAngle(euler.z, min.z, max.z, useAxis);
|
|
272
312
|
}
|
|
273
|
-
static
|
|
274
|
-
const axes = [
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
313
|
+
static reconstructQuatFromEulerInto(euler, order, out) {
|
|
314
|
+
const axes = this.EULER_AXES[order];
|
|
315
|
+
const a1 = axes[0], a2 = axes[1], a3 = axes[2];
|
|
316
|
+
const ang1 = order === InternalEulerRotationOrder.YXZ ? euler.y
|
|
317
|
+
: order === InternalEulerRotationOrder.ZYX ? euler.z
|
|
318
|
+
: euler.x;
|
|
319
|
+
const ang2 = order === InternalEulerRotationOrder.YXZ ? euler.x
|
|
320
|
+
: order === InternalEulerRotationOrder.ZYX ? euler.y
|
|
321
|
+
: euler.z;
|
|
322
|
+
const ang3 = order === InternalEulerRotationOrder.YXZ ? euler.z
|
|
323
|
+
: order === InternalEulerRotationOrder.ZYX ? euler.x
|
|
324
|
+
: euler.y;
|
|
325
|
+
// result = axisAngle(a1, ang1); then *= axisAngle(a2, ang2); then *= axisAngle(a3, ang3)
|
|
326
|
+
Quat.fromAxisAngleInto(a1[0], a1[1], a1[2], ang1, out);
|
|
327
|
+
Quat.fromAxisAngleInto(a2[0], a2[1], a2[2], ang2, _ikQuat[3]);
|
|
328
|
+
Quat.multiplyInto(out, _ikQuat[3], out);
|
|
329
|
+
Quat.fromAxisAngleInto(a3[0], a3[1], a3[2], ang3, _ikQuat[3]);
|
|
330
|
+
Quat.multiplyInto(out, _ikQuat[3], out);
|
|
289
331
|
}
|
|
290
|
-
|
|
332
|
+
// Write parent's world rotation (translation stripped) into out Float32Array.
|
|
333
|
+
static getParentWorldRotationMatrixInto(boneIndex, bones, worldMatrices, out) {
|
|
291
334
|
const bone = bones[boneIndex];
|
|
292
335
|
if (bone.parentIndex >= 0) {
|
|
293
|
-
|
|
294
|
-
// Remove translation
|
|
295
|
-
const rotMat = Mat4.identity();
|
|
296
|
-
const m = parentMat.values;
|
|
297
|
-
rotMat.values.set([m[0], m[1], m[2], 0, m[4], m[5], m[6], 0, m[8], m[9], m[10], 0, 0, 0, 0, 1]);
|
|
298
|
-
return rotMat;
|
|
336
|
+
Mat4.copyRotationInto(worldMatrices[bone.parentIndex].values, out);
|
|
299
337
|
}
|
|
300
338
|
else {
|
|
301
|
-
|
|
339
|
+
// Identity
|
|
340
|
+
out[0] = 1;
|
|
341
|
+
out[1] = 0;
|
|
342
|
+
out[2] = 0;
|
|
343
|
+
out[3] = 0;
|
|
344
|
+
out[4] = 0;
|
|
345
|
+
out[5] = 1;
|
|
346
|
+
out[6] = 0;
|
|
347
|
+
out[7] = 0;
|
|
348
|
+
out[8] = 0;
|
|
349
|
+
out[9] = 0;
|
|
350
|
+
out[10] = 1;
|
|
351
|
+
out[11] = 0;
|
|
352
|
+
out[12] = 0;
|
|
353
|
+
out[13] = 0;
|
|
354
|
+
out[14] = 0;
|
|
355
|
+
out[15] = 1;
|
|
302
356
|
}
|
|
303
357
|
}
|
|
304
|
-
static transformNormal(normal, matrix) {
|
|
305
|
-
const m = matrix.values;
|
|
306
|
-
return new Vec3(m[0] * normal.x + m[4] * normal.y + m[8] * normal.z, m[1] * normal.x + m[5] * normal.y + m[9] * normal.z, m[2] * normal.x + m[6] * normal.y + m[10] * normal.z);
|
|
307
|
-
}
|
|
308
358
|
static updateWorldMatrix(boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo) {
|
|
309
359
|
const bone = bones[boneIndex];
|
|
310
360
|
const localRot = localRotations[boneIndex];
|
|
311
361
|
const localTrans = localTranslations[boneIndex];
|
|
312
|
-
|
|
313
|
-
let finalRot = localRot;
|
|
362
|
+
let fx = localRot.x, fy = localRot.y, fz = localRot.z, fw = localRot.w;
|
|
314
363
|
if (ikChainInfo) {
|
|
315
364
|
const chainInfo = ikChainInfo[boneIndex];
|
|
316
365
|
if (chainInfo && chainInfo.ikRotation) {
|
|
317
|
-
|
|
366
|
+
const ik = chainInfo.ikRotation;
|
|
367
|
+
const nx = ik.w * fx + ik.x * fw + ik.y * fz - ik.z * fy;
|
|
368
|
+
const ny = ik.w * fy - ik.x * fz + ik.y * fw + ik.z * fx;
|
|
369
|
+
const nz = ik.w * fz + ik.x * fy - ik.y * fx + ik.z * fw;
|
|
370
|
+
const nw = ik.w * fw - ik.x * fx - ik.y * fy - ik.z * fz;
|
|
371
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz + nw * nw);
|
|
372
|
+
const inv = len > 0 ? 1 / len : 0;
|
|
373
|
+
fx = nx * inv;
|
|
374
|
+
fy = ny * inv;
|
|
375
|
+
fz = nz * inv;
|
|
376
|
+
fw = nw * inv;
|
|
318
377
|
}
|
|
319
378
|
}
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
.translateInPlace(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2])
|
|
323
|
-
.multiply(rotateM)
|
|
324
|
-
.translateInPlace(localTrans.x, localTrans.y, localTrans.z);
|
|
379
|
+
const localMVals = _ikMat[3];
|
|
380
|
+
Mat4.localTransformInto(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2], fx, fy, fz, fw, localTrans.x, localTrans.y, localTrans.z, localMVals);
|
|
325
381
|
const worldMat = worldMatrices[boneIndex];
|
|
326
382
|
if (bone.parentIndex >= 0) {
|
|
327
383
|
const parentMat = worldMatrices[bone.parentIndex];
|
|
328
|
-
|
|
329
|
-
worldMat.values.set(result.values);
|
|
384
|
+
Mat4.multiplyArrays(parentMat.values, 0, localMVals, 0, worldMat.values, 0);
|
|
330
385
|
}
|
|
331
386
|
else {
|
|
332
|
-
worldMat.values.set(
|
|
387
|
+
worldMat.values.set(localMVals);
|
|
333
388
|
}
|
|
334
389
|
}
|
|
335
390
|
}
|
|
336
391
|
IKSolverSystem.EPSILON = 1.0e-8;
|
|
337
392
|
IKSolverSystem.THRESHOLD = (88 * Math.PI) / 180;
|
|
393
|
+
// Euler axis triples for each rotation order (indexed by order enum).
|
|
394
|
+
// Reused to avoid allocations in reconstructQuatFromEulerInto.
|
|
395
|
+
IKSolverSystem.EULER_AXES = [
|
|
396
|
+
// YXZ: Y, X, Z
|
|
397
|
+
[[0, 1, 0], [1, 0, 0], [0, 0, 1]],
|
|
398
|
+
// ZYX: Z, Y, X
|
|
399
|
+
[[0, 0, 1], [0, 1, 0], [1, 0, 0]],
|
|
400
|
+
// XZY: X, Z, Y
|
|
401
|
+
[[1, 0, 0], [0, 0, 1], [0, 1, 0]],
|
|
402
|
+
];
|
package/dist/math.d.ts
CHANGED
|
@@ -13,6 +13,12 @@ export declare class Vec3 {
|
|
|
13
13
|
dot(other: Vec3): number;
|
|
14
14
|
scale(scalar: number): Vec3;
|
|
15
15
|
set(other: Vec3): Vec3;
|
|
16
|
+
setXYZ(x: number, y: number, z: number): Vec3;
|
|
17
|
+
static subtractInto(a: Vec3, b: Vec3, out: Vec3): Vec3;
|
|
18
|
+
static crossInto(a: Vec3, b: Vec3, out: Vec3): Vec3;
|
|
19
|
+
static setFromMat4Translation(m: Float32Array, out: Vec3): Vec3;
|
|
20
|
+
static transformMat4RotationInto(normal: Vec3, m: Float32Array, out: Vec3): Vec3;
|
|
21
|
+
normalizeInPlace(): Vec3;
|
|
16
22
|
}
|
|
17
23
|
export declare class Quat {
|
|
18
24
|
x: number;
|
|
@@ -31,6 +37,11 @@ export declare class Quat {
|
|
|
31
37
|
toArray(): [number, number, number, number];
|
|
32
38
|
set(other: Quat): Quat;
|
|
33
39
|
static slerp(a: Quat, b: Quat, t: number): Quat;
|
|
40
|
+
static multiplyInto(a: Quat, b: Quat, out: Quat): Quat;
|
|
41
|
+
static fromAxisAngleInto(ax: number, ay: number, az: number, angle: number, out: Quat): Quat;
|
|
42
|
+
static slerpInto(a: Quat, b: Quat, t: number, out: Quat): Quat;
|
|
43
|
+
setXYZW(x: number, y: number, z: number, w: number): Quat;
|
|
44
|
+
setIdentity(): Quat;
|
|
34
45
|
static fromEuler(rotX: number, rotY: number, rotZ: number): Quat;
|
|
35
46
|
}
|
|
36
47
|
export declare class Mat4 {
|
|
@@ -43,13 +54,22 @@ export declare class Mat4 {
|
|
|
43
54
|
multiply(other: Mat4): Mat4;
|
|
44
55
|
static multiplyArrays(a: Float32Array, aOffset: number, b: Float32Array, bOffset: number, out: Float32Array, outOffset: number): void;
|
|
45
56
|
clone(): Mat4;
|
|
57
|
+
static fromQuatInto(x: number, y: number, z: number, w: number, out: Float32Array, offset?: number): void;
|
|
58
|
+
static localTransformInto(bx: number, by: number, bz: number, qx: number, qy: number, qz: number, qw: number, lx: number, ly: number, lz: number, out: Float32Array): void;
|
|
59
|
+
static fromPositionRotationInto(px: number, py: number, pz: number, qx: number, qy: number, qz: number, qw: number, out: Float32Array): void;
|
|
60
|
+
static inverseInto(m: Float32Array, out: Float32Array): boolean;
|
|
61
|
+
static copyRotationInto(src: Float32Array, dst: Float32Array): void;
|
|
46
62
|
static fromQuat(x: number, y: number, z: number, w: number): Mat4;
|
|
47
63
|
static fromPositionRotation(position: Vec3, rotation: Quat): Mat4;
|
|
48
64
|
getPosition(): Vec3;
|
|
49
65
|
toQuat(): Quat;
|
|
66
|
+
static toQuatFromArrayInto(m: Float32Array, offset: number, out: Quat): Quat;
|
|
50
67
|
static toQuatFromArray(m: Float32Array, offset: number): Quat;
|
|
51
68
|
setIdentity(): this;
|
|
52
69
|
translateInPlace(tx: number, ty: number, tz: number): this;
|
|
53
70
|
inverse(): Mat4;
|
|
54
71
|
}
|
|
72
|
+
export declare const scratchMat4Values: Float32Array[];
|
|
73
|
+
export declare const scratchVec3: Vec3[];
|
|
74
|
+
export declare const scratchQuat: Quat[];
|
|
55
75
|
//# sourceMappingURL=math.d.ts.map
|
package/dist/math.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"AACA,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,qBAAa,IAAI;IACf,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;gBAEG,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAM3C,MAAM,CAAC,KAAK,IAAI,IAAI;IAIpB,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAItB,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAI3B,MAAM,IAAI,MAAM;IAKhB,SAAS,IAAI,IAAI;IAejB,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;IAQxB,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM;IAIxB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK3B,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"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"}
|