reze-engine 0.3.9 → 0.3.11
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/ik-solver.d.ts +2 -3
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/ik-solver.js +16 -32
- package/dist/math.d.ts +4 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +72 -13
- package/dist/model.d.ts +5 -5
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +126 -134
- package/dist/physics.d.ts +2 -2
- package/dist/physics.d.ts.map +1 -1
- package/dist/physics.js +6 -10
- package/package.json +1 -1
- package/src/ik-solver.ts +31 -51
- package/src/math.ts +75 -11
- package/src/model.ts +136 -165
- package/src/physics.ts +11 -21
- package/dist/bezier-interpolate.d.ts +0 -15
- package/dist/bezier-interpolate.d.ts.map +0 -1
- package/dist/bezier-interpolate.js +0 -40
- package/dist/engine_r.d.ts +0 -132
- package/dist/engine_r.d.ts.map +0 -1
- package/dist/engine_r.js +0 -1489
package/src/ik-solver.ts
CHANGED
|
@@ -86,9 +86,9 @@ export class IKSolverSystem {
|
|
|
86
86
|
public static solve(
|
|
87
87
|
ikSolvers: IKSolver[],
|
|
88
88
|
bones: Bone[],
|
|
89
|
-
localRotations:
|
|
90
|
-
localTranslations:
|
|
91
|
-
worldMatrices:
|
|
89
|
+
localRotations: Quat[],
|
|
90
|
+
localTranslations: Vec3[],
|
|
91
|
+
worldMatrices: Mat4[],
|
|
92
92
|
ikChainInfo: IKChainInfo[]
|
|
93
93
|
): void {
|
|
94
94
|
for (const solver of ikSolvers) {
|
|
@@ -99,9 +99,9 @@ export class IKSolverSystem {
|
|
|
99
99
|
private static solveIK(
|
|
100
100
|
solver: IKSolver,
|
|
101
101
|
bones: Bone[],
|
|
102
|
-
localRotations:
|
|
103
|
-
localTranslations:
|
|
104
|
-
worldMatrices:
|
|
102
|
+
localRotations: Quat[],
|
|
103
|
+
localTranslations: Vec3[],
|
|
104
|
+
worldMatrices: Mat4[],
|
|
105
105
|
ikChainInfo: IKChainInfo[]
|
|
106
106
|
): void {
|
|
107
107
|
if (solver.links.length === 0) return
|
|
@@ -164,10 +164,9 @@ export class IKSolverSystem {
|
|
|
164
164
|
for (const link of solver.links) {
|
|
165
165
|
const chainInfo = ikChainInfo[link.boneIndex]
|
|
166
166
|
if (chainInfo?.ikRotation) {
|
|
167
|
-
const
|
|
168
|
-
const localRot = this.getQuatFromArray(localRotations, qi)
|
|
167
|
+
const localRot = localRotations[link.boneIndex]
|
|
169
168
|
const finalRot = chainInfo.ikRotation.multiply(localRot).normalize()
|
|
170
|
-
|
|
169
|
+
localRot.set(finalRot)
|
|
171
170
|
}
|
|
172
171
|
}
|
|
173
172
|
}
|
|
@@ -179,9 +178,9 @@ export class IKSolverSystem {
|
|
|
179
178
|
ikBoneIndex: number,
|
|
180
179
|
targetBoneIndex: number,
|
|
181
180
|
bones: Bone[],
|
|
182
|
-
localRotations:
|
|
183
|
-
localTranslations:
|
|
184
|
-
worldMatrices:
|
|
181
|
+
localRotations: Quat[],
|
|
182
|
+
localTranslations: Vec3[],
|
|
183
|
+
worldMatrices: Mat4[],
|
|
185
184
|
ikChainInfo: IKChainInfo[],
|
|
186
185
|
useAxis: boolean
|
|
187
186
|
): void {
|
|
@@ -243,15 +242,15 @@ export class IKSolverSystem {
|
|
|
243
242
|
|
|
244
243
|
// Apply angle constraints if present
|
|
245
244
|
if (chain.minimumAngle && chain.maximumAngle) {
|
|
246
|
-
const
|
|
247
|
-
const localRot = this.getQuatFromArray(localRotations, qi)
|
|
245
|
+
const localRot = localRotations[chainBoneIndex]
|
|
248
246
|
chainInfo.localRotation = localRot.clone()
|
|
249
247
|
|
|
250
248
|
const combinedRot = chainInfo.ikRotation.multiply(localRot)
|
|
251
249
|
const euler = this.extractEulerAngles(combinedRot, chain.rotationOrder)
|
|
252
250
|
const limited = this.limitEulerAngles(euler, chain.minimumAngle, chain.maximumAngle, useAxis)
|
|
253
251
|
chainInfo.ikRotation = this.reconstructQuatFromEuler(limited, chain.rotationOrder)
|
|
254
|
-
|
|
252
|
+
// Clone localRot to avoid mutating, then conjugate and normalize
|
|
253
|
+
chainInfo.ikRotation = chainInfo.ikRotation.multiply(localRot.clone().conjugate().normalize())
|
|
255
254
|
}
|
|
256
255
|
}
|
|
257
256
|
|
|
@@ -275,26 +274,15 @@ export class IKSolverSystem {
|
|
|
275
274
|
}
|
|
276
275
|
}
|
|
277
276
|
|
|
278
|
-
private static getDistance(boneIndex1: number, boneIndex2: number, worldMatrices:
|
|
277
|
+
private static getDistance(boneIndex1: number, boneIndex2: number, worldMatrices: Mat4[]): number {
|
|
279
278
|
const pos1 = this.getWorldTranslation(boneIndex1, worldMatrices)
|
|
280
279
|
const pos2 = this.getWorldTranslation(boneIndex2, worldMatrices)
|
|
281
280
|
return pos1.subtract(pos2).length()
|
|
282
281
|
}
|
|
283
282
|
|
|
284
|
-
private static getWorldTranslation(boneIndex: number, worldMatrices:
|
|
285
|
-
const
|
|
286
|
-
return new Vec3(
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
private static getQuatFromArray(array: Float32Array, offset: number): Quat {
|
|
290
|
-
return new Quat(array[offset], array[offset + 1], array[offset + 2], array[offset + 3])
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
private static setQuatToArray(array: Float32Array, offset: number, quat: Quat): void {
|
|
294
|
-
array[offset] = quat.x
|
|
295
|
-
array[offset + 1] = quat.y
|
|
296
|
-
array[offset + 2] = quat.z
|
|
297
|
-
array[offset + 3] = quat.w
|
|
283
|
+
private static getWorldTranslation(boneIndex: number, worldMatrices: Mat4[]): Vec3 {
|
|
284
|
+
const mat = worldMatrices[boneIndex]
|
|
285
|
+
return new Vec3(mat.values[12], mat.values[13], mat.values[14])
|
|
298
286
|
}
|
|
299
287
|
|
|
300
288
|
private static extractEulerAngles(quat: Quat, order: InternalEulerRotationOrder): Vec3 {
|
|
@@ -361,11 +349,10 @@ export class IKSolverSystem {
|
|
|
361
349
|
return result
|
|
362
350
|
}
|
|
363
351
|
|
|
364
|
-
private static getParentWorldRotationMatrix(boneIndex: number, bones: Bone[], worldMatrices:
|
|
352
|
+
private static getParentWorldRotationMatrix(boneIndex: number, bones: Bone[], worldMatrices: Mat4[]): Mat4 {
|
|
365
353
|
const bone = bones[boneIndex]
|
|
366
354
|
if (bone.parentIndex >= 0) {
|
|
367
|
-
const
|
|
368
|
-
const parentMat = new Mat4(worldMatrices.subarray(parentOffset, parentOffset + 16))
|
|
355
|
+
const parentMat = worldMatrices[bone.parentIndex]
|
|
369
356
|
// Remove translation
|
|
370
357
|
const rotMat = Mat4.identity()
|
|
371
358
|
const m = parentMat.values
|
|
@@ -388,16 +375,14 @@ export class IKSolverSystem {
|
|
|
388
375
|
private static updateWorldMatrix(
|
|
389
376
|
boneIndex: number,
|
|
390
377
|
bones: Bone[],
|
|
391
|
-
localRotations:
|
|
392
|
-
localTranslations:
|
|
393
|
-
worldMatrices:
|
|
378
|
+
localRotations: Quat[],
|
|
379
|
+
localTranslations: Vec3[],
|
|
380
|
+
worldMatrices: Mat4[],
|
|
394
381
|
ikChainInfo?: IKChainInfo[]
|
|
395
382
|
): void {
|
|
396
383
|
const bone = bones[boneIndex]
|
|
397
|
-
const
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
const localRot = this.getQuatFromArray(localRotations, qi)
|
|
384
|
+
const localRot = localRotations[boneIndex]
|
|
385
|
+
const localTrans = localTranslations[boneIndex]
|
|
401
386
|
|
|
402
387
|
// Apply IK rotation if available
|
|
403
388
|
let finalRot = localRot
|
|
@@ -409,23 +394,18 @@ export class IKSolverSystem {
|
|
|
409
394
|
}
|
|
410
395
|
const rotateM = Mat4.fromQuat(finalRot.x, finalRot.y, finalRot.z, finalRot.w)
|
|
411
396
|
|
|
412
|
-
const localTx = localTranslations[ti]
|
|
413
|
-
const localTy = localTranslations[ti + 1]
|
|
414
|
-
const localTz = localTranslations[ti + 2]
|
|
415
|
-
|
|
416
397
|
const localM = Mat4.identity()
|
|
417
398
|
.translateInPlace(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2])
|
|
418
399
|
.multiply(rotateM)
|
|
419
|
-
.translateInPlace(
|
|
400
|
+
.translateInPlace(localTrans.x, localTrans.y, localTrans.z)
|
|
420
401
|
|
|
421
|
-
const
|
|
402
|
+
const worldMat = worldMatrices[boneIndex]
|
|
422
403
|
if (bone.parentIndex >= 0) {
|
|
423
|
-
const
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
worldMatrices.subarray(worldOffset, worldOffset + 16).set(worldMat.values)
|
|
404
|
+
const parentMat = worldMatrices[bone.parentIndex]
|
|
405
|
+
const result = parentMat.multiply(localM)
|
|
406
|
+
worldMat.values.set(result.values)
|
|
427
407
|
} else {
|
|
428
|
-
|
|
408
|
+
worldMat.values.set(localM.values)
|
|
429
409
|
}
|
|
430
410
|
}
|
|
431
411
|
}
|
package/src/math.ts
CHANGED
|
@@ -14,6 +14,10 @@ export class Vec3 {
|
|
|
14
14
|
this.z = z
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
static zeros(): Vec3 {
|
|
18
|
+
return new Vec3(0, 0, 0)
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
add(other: Vec3): Vec3 {
|
|
18
22
|
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z)
|
|
19
23
|
}
|
|
@@ -26,10 +30,20 @@ export class Vec3 {
|
|
|
26
30
|
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
// Normalize this vector in-place (mutates this object)
|
|
29
34
|
normalize(): Vec3 {
|
|
30
35
|
const len = this.length()
|
|
31
|
-
if (len === 0)
|
|
32
|
-
|
|
36
|
+
if (len === 0) {
|
|
37
|
+
this.x = 0
|
|
38
|
+
this.y = 0
|
|
39
|
+
this.z = 0
|
|
40
|
+
} else {
|
|
41
|
+
const invLen = 1 / len
|
|
42
|
+
this.x *= invLen
|
|
43
|
+
this.y *= invLen
|
|
44
|
+
this.z *= invLen
|
|
45
|
+
}
|
|
46
|
+
return this
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
cross(other: Vec3): Vec3 {
|
|
@@ -47,6 +61,14 @@ export class Vec3 {
|
|
|
47
61
|
scale(scalar: number): Vec3 {
|
|
48
62
|
return new Vec3(this.x * scalar, this.y * scalar, this.z * scalar)
|
|
49
63
|
}
|
|
64
|
+
|
|
65
|
+
// Set this vector's components from another vector (in-place mutation)
|
|
66
|
+
set(other: Vec3): Vec3 {
|
|
67
|
+
this.x = other.x
|
|
68
|
+
this.y = other.y
|
|
69
|
+
this.z = other.z
|
|
70
|
+
return this
|
|
71
|
+
}
|
|
50
72
|
}
|
|
51
73
|
|
|
52
74
|
export class Quat {
|
|
@@ -62,6 +84,10 @@ export class Quat {
|
|
|
62
84
|
this.w = w
|
|
63
85
|
}
|
|
64
86
|
|
|
87
|
+
static identity(): Quat {
|
|
88
|
+
return new Quat(0, 0, 0, 1)
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
add(other: Quat): Quat {
|
|
66
92
|
return new Quat(this.x + other.x, this.y + other.y, this.z + other.z, this.w + other.w)
|
|
67
93
|
}
|
|
@@ -80,34 +106,68 @@ export class Quat {
|
|
|
80
106
|
)
|
|
81
107
|
}
|
|
82
108
|
|
|
109
|
+
// Conjugate this quaternion in-place (mutates this object)
|
|
110
|
+
// Conjugate (inverse for unit quaternions): (x, y, z, w) -> (-x, -y, -z, w)
|
|
83
111
|
conjugate(): Quat {
|
|
84
|
-
|
|
85
|
-
|
|
112
|
+
this.x = -this.x
|
|
113
|
+
this.y = -this.y
|
|
114
|
+
this.z = -this.z
|
|
115
|
+
return this
|
|
86
116
|
}
|
|
87
117
|
|
|
88
118
|
length(): number {
|
|
89
119
|
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w)
|
|
90
120
|
}
|
|
91
121
|
|
|
122
|
+
// Normalize this quaternion in-place (mutates this object)
|
|
92
123
|
normalize(): Quat {
|
|
93
124
|
const len = this.length()
|
|
94
|
-
if (len === 0)
|
|
95
|
-
|
|
125
|
+
if (len === 0) {
|
|
126
|
+
this.x = 0
|
|
127
|
+
this.y = 0
|
|
128
|
+
this.z = 0
|
|
129
|
+
this.w = 1
|
|
130
|
+
} else {
|
|
131
|
+
const invLen = 1 / len
|
|
132
|
+
this.x *= invLen
|
|
133
|
+
this.y *= invLen
|
|
134
|
+
this.z *= invLen
|
|
135
|
+
this.w *= invLen
|
|
136
|
+
}
|
|
137
|
+
return this
|
|
96
138
|
}
|
|
97
139
|
|
|
98
140
|
// Static method: create quaternion from rotation axis and angle
|
|
99
141
|
static fromAxisAngle(axis: Vec3, angle: number): Quat {
|
|
100
|
-
|
|
142
|
+
// Clone to avoid mutating input, then normalize
|
|
143
|
+
const nx = axis.x
|
|
144
|
+
const ny = axis.y
|
|
145
|
+
const nz = axis.z
|
|
146
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz)
|
|
147
|
+
const invLen = len > 0 ? 1 / len : 0
|
|
148
|
+
const normalizedX = nx * invLen
|
|
149
|
+
const normalizedY = ny * invLen
|
|
150
|
+
const normalizedZ = nz * invLen
|
|
151
|
+
|
|
101
152
|
const halfAngle = angle * 0.5
|
|
102
153
|
const sinHalf = Math.sin(halfAngle)
|
|
103
154
|
const cosHalf = Math.cos(halfAngle)
|
|
104
|
-
return new Quat(
|
|
155
|
+
return new Quat(normalizedX * sinHalf, normalizedY * sinHalf, normalizedZ * sinHalf, cosHalf)
|
|
105
156
|
}
|
|
106
157
|
|
|
107
158
|
toArray(): [number, number, number, number] {
|
|
108
159
|
return [this.x, this.y, this.z, this.w]
|
|
109
160
|
}
|
|
110
161
|
|
|
162
|
+
// Set this quaternion's components from another quaternion (in-place mutation)
|
|
163
|
+
set(other: Quat): Quat {
|
|
164
|
+
this.x = other.x
|
|
165
|
+
this.y = other.y
|
|
166
|
+
this.z = other.z
|
|
167
|
+
this.w = other.w
|
|
168
|
+
return this
|
|
169
|
+
}
|
|
170
|
+
|
|
111
171
|
// Spherical linear interpolation between two quaternions
|
|
112
172
|
static slerp(a: Quat, b: Quat, t: number): Quat {
|
|
113
173
|
let cos = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
|
|
@@ -205,9 +265,13 @@ export class Mat4 {
|
|
|
205
265
|
// For left-handed: camera looks along +Z direction
|
|
206
266
|
static lookAt(eye: Vec3, target: Vec3, up: Vec3): Mat4 {
|
|
207
267
|
// In left-handed: forward = target - eye (Z+ direction)
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
268
|
+
// These operations create new Vec3 objects, so normalize() mutates those new objects
|
|
269
|
+
const forward = target.subtract(eye)
|
|
270
|
+
forward.normalize()
|
|
271
|
+
const right = up.cross(forward)
|
|
272
|
+
right.normalize() // X+ is right
|
|
273
|
+
const upVec = forward.cross(right)
|
|
274
|
+
upVec.normalize() // Y+ is up
|
|
211
275
|
|
|
212
276
|
return new Mat4(
|
|
213
277
|
new Float32Array([
|