reze-engine 0.36.0 → 0.37.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/README.md +4 -3
- package/dist/camera.d.ts +17 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +25 -0
- package/dist/engine.d.ts +94 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +250 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +81 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +192 -0
- package/dist/physics/autofit.d.ts +147 -0
- package/dist/physics/autofit.d.ts.map +1 -0
- package/dist/physics/autofit.js +501 -0
- package/dist/physics/contact.d.ts +12 -3
- package/dist/physics/contact.d.ts.map +1 -1
- package/dist/physics/contact.js +5 -2
- package/dist/physics/manifold.d.ts +32 -11
- package/dist/physics/manifold.d.ts.map +1 -1
- package/dist/physics/manifold.js +137 -57
- package/dist/physics/solver.d.ts +7 -1
- package/dist/physics/solver.d.ts.map +1 -1
- package/dist/physics/solver.js +536 -182
- package/dist/physics/world.d.ts +2 -0
- package/dist/physics/world.d.ts.map +1 -1
- package/dist/physics/world.js +16 -43
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +84 -44
- package/package.json +1 -1
- package/src/camera.ts +26 -0
- package/src/engine.ts +305 -18
- package/src/index.ts +11 -1
- package/src/model.ts +231 -1
- package/src/physics/contact.ts +17 -7
- package/src/physics/manifold.ts +151 -0
- package/src/physics/solver.ts +520 -184
- package/src/physics/world.ts +17 -43
- package/src/pmx-loader.ts +84 -44
package/src/physics/world.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Vec3 } from "../math"
|
|
|
2
2
|
import type { RigidBodyStore } from "./body"
|
|
3
3
|
import { RigidbodyType } from "./types"
|
|
4
4
|
import type { SixDofSpringConstraint } from "./constraint"
|
|
5
|
-
import { solveConstraints, type SolverCache } from "./solver"
|
|
5
|
+
import { solveConstraints, saveContactImpulses, applySplitImpulsePush, type SolverCache } from "./solver"
|
|
6
|
+
import { ManifoldCache } from "./manifold"
|
|
6
7
|
import { findContacts, type ContactPool } from "./contact"
|
|
7
8
|
|
|
8
9
|
// World step: predict velocities → collide → solve → position correction →
|
|
@@ -38,6 +39,9 @@ export class World {
|
|
|
38
39
|
readonly gravity: Vec3
|
|
39
40
|
solverIterations = 10
|
|
40
41
|
|
|
42
|
+
/** Per-pair contact impulse history behind warm starting. */
|
|
43
|
+
private manifolds = new ManifoldCache()
|
|
44
|
+
|
|
41
45
|
// Per-body damping factors pow(1−damping, dt), cached because damping and
|
|
42
46
|
// the fixed dt never change — two Math.pow per body per substep otherwise.
|
|
43
47
|
private dampCacheDt = -1
|
|
@@ -166,50 +170,20 @@ export class World {
|
|
|
166
170
|
|
|
167
171
|
// 3. Solve joint + contact constraints (velocity-only).
|
|
168
172
|
if (constraints.length > 0 || contacts.count > 0) {
|
|
169
|
-
solveConstraints(store, constraints, cache, contacts, dt, this.solverIterations)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// it because it doesn't go through the velocity channel. Inverse-mass
|
|
175
|
-
// weighted so a kinematic body stays put and only the dynamic one
|
|
176
|
-
// translates.
|
|
177
|
-
// Softer and later than a rigid-body engine would use, deliberately. MMD
|
|
178
|
-
// cloth is a dense chain of small bodies resting on the character, and
|
|
179
|
-
// aggressive penetration recovery feeds those contacts energy that the
|
|
180
|
-
// joint springs hand straight back — the tips never settle. Measured on a
|
|
181
|
-
// 688-body model: resting peak velocity 1.79 → 1.10, and 12.9 → 6.6 under
|
|
182
|
-
// an idle animation, for a slop of two hundredths of a unit (under 2 mm at
|
|
183
|
-
// MMD scale) that no eye finds.
|
|
184
|
-
const POS_CORRECTION_FACTOR = 0.15
|
|
185
|
-
const POS_SLOP = 0.02
|
|
186
|
-
for (let ci = 0; ci < contacts.count; ci++) {
|
|
187
|
-
const c = contacts.get(ci)
|
|
188
|
-
if (c.depth <= POS_SLOP) continue
|
|
189
|
-
const imA = invMass[c.bodyA]
|
|
190
|
-
const imB = invMass[c.bodyB]
|
|
191
|
-
const total = imA + imB
|
|
192
|
-
if (total <= 0) continue
|
|
193
|
-
const correction = (c.depth - POS_SLOP) * POS_CORRECTION_FACTOR
|
|
194
|
-
const dx = correction * c.nx
|
|
195
|
-
const dy = correction * c.ny
|
|
196
|
-
const dz = correction * c.nz
|
|
197
|
-
const ai = c.bodyA * 3
|
|
198
|
-
const bi = c.bodyB * 3
|
|
199
|
-
if (imA > 0) {
|
|
200
|
-
const fA = imA / total
|
|
201
|
-
pos[ai + 0] -= dx * fA
|
|
202
|
-
pos[ai + 1] -= dy * fA
|
|
203
|
-
pos[ai + 2] -= dz * fA
|
|
204
|
-
}
|
|
205
|
-
if (imB > 0) {
|
|
206
|
-
const fB = imB / total
|
|
207
|
-
pos[bi + 0] += dx * fB
|
|
208
|
-
pos[bi + 1] += dy * fB
|
|
209
|
-
pos[bi + 2] += dz * fB
|
|
210
|
-
}
|
|
173
|
+
solveConstraints(store, constraints, cache, contacts, dt, this.solverIterations, this.manifolds)
|
|
174
|
+
applySplitImpulsePush(store, dt)
|
|
175
|
+
saveContactImpulses(store, contacts, this.manifolds)
|
|
176
|
+
} else {
|
|
177
|
+
this.manifolds.clear()
|
|
211
178
|
}
|
|
212
179
|
|
|
180
|
+
// 4. Penetration recovery happens in the contact velocity row (Baumgarte,
|
|
181
|
+
// CONTACT_ERP), not here. Bullet 2.75 — the build MMD's physics runs —
|
|
182
|
+
// defaults m_splitImpulse to false and folds positionalError straight
|
|
183
|
+
// into m_rhs, so a separate position-translation pass is a divergence
|
|
184
|
+
// from what PMX rigs are authored against. It also could not be made to
|
|
185
|
+
// behave: every contact translated the body by its own full share, so a
|
|
186
|
+
// panel carrying dozens of rows was shoved out dozens of times.
|
|
213
187
|
// 5. Integrate. Cap angular velocity at π/2 per step — a high-impulse
|
|
214
188
|
// contact spike on a low-inertia body would otherwise spin past π
|
|
215
189
|
// in one step and trash the quaternion integration. Linear velocity
|
package/src/pmx-loader.ts
CHANGED
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
Morphing,
|
|
10
10
|
VertexMorphOffset,
|
|
11
11
|
GroupMorphReference,
|
|
12
|
+
BoneMorphOffset,
|
|
13
|
+
MaterialMorphOffset,
|
|
14
|
+
UvMorphOffset,
|
|
12
15
|
IKLink,
|
|
13
16
|
} from "./model"
|
|
14
17
|
import { Mat4, Vec3 } from "./math"
|
|
@@ -533,6 +536,11 @@ export class PmxLoader {
|
|
|
533
536
|
vertexOffsets: [],
|
|
534
537
|
groupReferences: [],
|
|
535
538
|
}
|
|
539
|
+
// Allocated only for the type that uses them, so `morph.boneOffsets`
|
|
540
|
+
// being undefined means "not that kind of morph" rather than "empty".
|
|
541
|
+
if (morphType === 2) morph.boneOffsets = []
|
|
542
|
+
else if (morphType >= 3 && morphType <= 7) morph.uvOffsets = []
|
|
543
|
+
else if (morphType === 8) morph.materialOffsets = []
|
|
536
544
|
|
|
537
545
|
// Parse vertex morphs (type 1)
|
|
538
546
|
if (morphType === 1) {
|
|
@@ -569,60 +577,92 @@ export class PmxLoader {
|
|
|
569
577
|
}
|
|
570
578
|
}
|
|
571
579
|
} else {
|
|
572
|
-
//
|
|
580
|
+
// Bone, UV and material morphs are kept; flip and impulse (PMX 2.1)
|
|
581
|
+
// are still read past — nothing in the wild uses them, and impulse
|
|
582
|
+
// would need to drive rigidbodies.
|
|
573
583
|
for (let j = 0; j < offsetCount; j++) {
|
|
574
584
|
if (this.offset >= this.view.buffer.byteLength) {
|
|
575
585
|
break
|
|
576
586
|
}
|
|
577
587
|
if (morphType === 2) {
|
|
578
588
|
// Bone morph
|
|
579
|
-
this.getNonVertexIndex(this.boneIndexSize)
|
|
580
|
-
this.getFloat32()
|
|
581
|
-
this.getFloat32()
|
|
582
|
-
this.getFloat32()
|
|
583
|
-
this.getFloat32()
|
|
584
|
-
this.getFloat32()
|
|
585
|
-
this.getFloat32()
|
|
586
|
-
this.getFloat32()
|
|
589
|
+
const boneIndex = this.getNonVertexIndex(this.boneIndexSize)
|
|
590
|
+
const tx = this.getFloat32()
|
|
591
|
+
const ty = this.getFloat32()
|
|
592
|
+
const tz = this.getFloat32()
|
|
593
|
+
const rx = this.getFloat32()
|
|
594
|
+
const ry = this.getFloat32()
|
|
595
|
+
const rz = this.getFloat32()
|
|
596
|
+
const rw = this.getFloat32()
|
|
597
|
+
if (boneIndex >= 0) {
|
|
598
|
+
const offset: BoneMorphOffset = {
|
|
599
|
+
boneIndex,
|
|
600
|
+
translation: [tx, ty, tz],
|
|
601
|
+
rotation: [rx, ry, rz, rw],
|
|
602
|
+
}
|
|
603
|
+
morph.boneOffsets!.push(offset)
|
|
604
|
+
}
|
|
587
605
|
} else if (morphType >= 3 && morphType <= 7) {
|
|
588
606
|
// UV morph + additional UV channels 1-4; offset is always a Float4 per spec
|
|
589
|
-
this.getIndex(this.vertexIndexSize)
|
|
590
|
-
this.getFloat32()
|
|
591
|
-
this.getFloat32()
|
|
592
|
-
this.getFloat32()
|
|
593
|
-
this.getFloat32()
|
|
607
|
+
const vertexIndex = this.getIndex(this.vertexIndexSize)
|
|
608
|
+
const x = this.getFloat32()
|
|
609
|
+
const y = this.getFloat32()
|
|
610
|
+
const z = this.getFloat32()
|
|
611
|
+
const w = this.getFloat32()
|
|
612
|
+
if (vertexIndex >= 0 && vertexIndex < this.vertexCount) {
|
|
613
|
+
const offset: UvMorphOffset = { vertexIndex, offset: [x, y, z, w] }
|
|
614
|
+
morph.uvOffsets!.push(offset)
|
|
615
|
+
}
|
|
594
616
|
} else if (morphType === 8) {
|
|
595
617
|
// Material morph
|
|
596
|
-
this.getNonVertexIndex(this.materialIndexSize)
|
|
597
|
-
this.getUint8()
|
|
598
|
-
this.getFloat32()
|
|
599
|
-
this.getFloat32()
|
|
600
|
-
this.getFloat32()
|
|
601
|
-
this.getFloat32()
|
|
602
|
-
this.getFloat32()
|
|
603
|
-
this.getFloat32()
|
|
604
|
-
this.getFloat32()
|
|
605
|
-
this.getFloat32()
|
|
606
|
-
this.getFloat32()
|
|
607
|
-
this.getFloat32()
|
|
608
|
-
this.getFloat32()
|
|
609
|
-
this.getFloat32()
|
|
610
|
-
this.getFloat32()
|
|
611
|
-
this.getFloat32()
|
|
612
|
-
this.getFloat32()
|
|
613
|
-
this.getFloat32()
|
|
614
|
-
this.getFloat32()
|
|
615
|
-
this.getFloat32()
|
|
616
|
-
this.getFloat32()
|
|
617
|
-
this.getFloat32()
|
|
618
|
-
this.getFloat32()
|
|
619
|
-
this.getFloat32()
|
|
620
|
-
this.getFloat32()
|
|
621
|
-
this.getFloat32()
|
|
622
|
-
this.getFloat32()
|
|
623
|
-
this.getFloat32()
|
|
624
|
-
this.getFloat32()
|
|
625
|
-
this.getFloat32()
|
|
618
|
+
const materialIndex = this.getNonVertexIndex(this.materialIndexSize)
|
|
619
|
+
const offsetType = this.getUint8()
|
|
620
|
+
const diffuseR = this.getFloat32()
|
|
621
|
+
const diffuseG = this.getFloat32()
|
|
622
|
+
const diffuseB = this.getFloat32()
|
|
623
|
+
const diffuseA = this.getFloat32()
|
|
624
|
+
const specularR = this.getFloat32()
|
|
625
|
+
const specularG = this.getFloat32()
|
|
626
|
+
const specularB = this.getFloat32()
|
|
627
|
+
const shininess = this.getFloat32()
|
|
628
|
+
const ambientR = this.getFloat32()
|
|
629
|
+
const ambientG = this.getFloat32()
|
|
630
|
+
const ambientB = this.getFloat32()
|
|
631
|
+
const edgeR = this.getFloat32()
|
|
632
|
+
const edgeG = this.getFloat32()
|
|
633
|
+
const edgeB = this.getFloat32()
|
|
634
|
+
const edgeA = this.getFloat32()
|
|
635
|
+
const edgeSize = this.getFloat32()
|
|
636
|
+
const texR = this.getFloat32()
|
|
637
|
+
const texG = this.getFloat32()
|
|
638
|
+
const texB = this.getFloat32()
|
|
639
|
+
const texA = this.getFloat32()
|
|
640
|
+
const sphR = this.getFloat32()
|
|
641
|
+
const sphG = this.getFloat32()
|
|
642
|
+
const sphB = this.getFloat32()
|
|
643
|
+
const sphA = this.getFloat32()
|
|
644
|
+
const toonR = this.getFloat32()
|
|
645
|
+
const toonG = this.getFloat32()
|
|
646
|
+
const toonB = this.getFloat32()
|
|
647
|
+
const toonA = this.getFloat32()
|
|
648
|
+
// -1 is legal and means "every material" — kept as-is so the
|
|
649
|
+
// applier can fan it out without a second convention.
|
|
650
|
+
if (materialIndex >= -1) {
|
|
651
|
+
const offset: MaterialMorphOffset = {
|
|
652
|
+
materialIndex,
|
|
653
|
+
offsetType,
|
|
654
|
+
diffuse: [diffuseR, diffuseG, diffuseB, diffuseA],
|
|
655
|
+
specular: [specularR, specularG, specularB],
|
|
656
|
+
shininess,
|
|
657
|
+
ambient: [ambientR, ambientG, ambientB],
|
|
658
|
+
edgeColor: [edgeR, edgeG, edgeB, edgeA],
|
|
659
|
+
edgeSize,
|
|
660
|
+
textureCoeff: [texR, texG, texB, texA],
|
|
661
|
+
sphereCoeff: [sphR, sphG, sphB, sphA],
|
|
662
|
+
toonCoeff: [toonR, toonG, toonB, toonA],
|
|
663
|
+
}
|
|
664
|
+
morph.materialOffsets!.push(offset)
|
|
665
|
+
}
|
|
626
666
|
} else if (morphType === 9) {
|
|
627
667
|
// Flip morph (PMX 2.1): same layout as group morph
|
|
628
668
|
this.getNonVertexIndex(this.morphIndexSize) // morphIndex
|