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/dist/model.js
CHANGED
|
@@ -16,10 +16,16 @@ const _convMat = new Float32Array(16);
|
|
|
16
16
|
// Blend-path scratch: per-entry sample target and the crossfade's two fixed entries.
|
|
17
17
|
const _blendQ = new Quat(0, 0, 0, 1);
|
|
18
18
|
const _blendT = new Vec3(0, 0, 0);
|
|
19
|
+
// Bone-morph scratch: the weighted rotation, slerped out of identity.
|
|
20
|
+
const _boneMorphQ = new Quat(0, 0, 0, 1);
|
|
21
|
+
const _boneMorphIdentity = new Quat(0, 0, 0, 1);
|
|
19
22
|
const _fadeEntries = [
|
|
20
23
|
{ name: "", time: 0, weight: 0 },
|
|
21
24
|
{ name: "", time: 0, weight: 0 },
|
|
22
25
|
];
|
|
26
|
+
/** PMX material-morph blend mode. Multiply lerps toward base*morph; add offsets from base. */
|
|
27
|
+
export const MATERIAL_MORPH_MULTIPLY = 0;
|
|
28
|
+
export const MATERIAL_MORPH_ADD = 1;
|
|
23
29
|
export class Model {
|
|
24
30
|
get name() {
|
|
25
31
|
return this._name;
|
|
@@ -84,6 +90,23 @@ export class Model {
|
|
|
84
90
|
// Non-fatal problems collected while parsing the PMX (see PmxLoader.warn).
|
|
85
91
|
this.loadWarnings = [];
|
|
86
92
|
this.morphsDirty = false; // Flag indicating if morphs need to be applied
|
|
93
|
+
// Bone morphs (type 2), flattened once at load so the per-frame pass is a
|
|
94
|
+
// straight walk with no allocation. Empty for models without them, which is
|
|
95
|
+
// most characters — a stage's doors and platforms are the common case.
|
|
96
|
+
this.boneMorphPlan = [];
|
|
97
|
+
// Bones any bone morph touches, plus their locals as they were BEFORE the last
|
|
98
|
+
// application. A pose source rewrites the locals it owns every frame, but a
|
|
99
|
+
// stage usually has no clip at all — nothing would reset these, and the offset
|
|
100
|
+
// would compound frame after frame into a slow drift.
|
|
101
|
+
this.boneMorphBones = [];
|
|
102
|
+
this.boneMorphRestoreR = [];
|
|
103
|
+
this.boneMorphRestoreT = [];
|
|
104
|
+
this.boneMorphApplied = false;
|
|
105
|
+
/** A full pose pass has run, so the world matrices are real. See isIdle. */
|
|
106
|
+
this.posedOnce = false;
|
|
107
|
+
// Set whenever effective weights change, for material/UV consumers that live
|
|
108
|
+
// outside this class. Vertex morphs use morphWeightsDirty (GPU) instead.
|
|
109
|
+
this.auxMorphDirty = true;
|
|
87
110
|
// Root transform — model's placement in world space, independent of bones.
|
|
88
111
|
// Folded into skin matrices (see getSkinMatrices) so every pass (main VS,
|
|
89
112
|
// shadow VS, any future skinned pass) sees it without per-shader plumbing.
|
|
@@ -317,6 +340,122 @@ export class Model {
|
|
|
317
340
|
}, {}),
|
|
318
341
|
weights: new Float32Array(morphCount),
|
|
319
342
|
};
|
|
343
|
+
const boneCount = this.skeleton.bones.length;
|
|
344
|
+
this.boneMorphPlan = [];
|
|
345
|
+
for (let i = 0; i < morphCount; i++) {
|
|
346
|
+
const morph = this.morphing.morphs[i];
|
|
347
|
+
if (morph.type !== 2 || !morph.boneOffsets)
|
|
348
|
+
continue;
|
|
349
|
+
for (const off of morph.boneOffsets) {
|
|
350
|
+
if (off.boneIndex < 0 || off.boneIndex >= boneCount)
|
|
351
|
+
continue;
|
|
352
|
+
this.boneMorphPlan.push({
|
|
353
|
+
morphIndex: i,
|
|
354
|
+
boneIndex: off.boneIndex,
|
|
355
|
+
translation: new Vec3(off.translation[0], off.translation[1], off.translation[2]),
|
|
356
|
+
rotation: new Quat(off.rotation[0], off.rotation[1], off.rotation[2], off.rotation[3]),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const touched = new Set(this.boneMorphPlan.map((e) => e.boneIndex));
|
|
361
|
+
this.boneMorphBones = [...touched];
|
|
362
|
+
this.boneMorphRestoreR = this.boneMorphBones.map(() => Quat.identity());
|
|
363
|
+
this.boneMorphRestoreT = this.boneMorphBones.map(() => Vec3.zeros());
|
|
364
|
+
this.boneMorphApplied = false;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Bone morphs (type 2) compose over whatever the pose sources produced, the
|
|
368
|
+
* same way boneRotationOffsets do — they are an offset on the animated local
|
|
369
|
+
* transform, not a replacement for it. Re-applied every frame because each
|
|
370
|
+
* pose source rewrites the locals it touches.
|
|
371
|
+
*
|
|
372
|
+
* Stages are the reason this exists: a door or a lift is rigged as a bone
|
|
373
|
+
* morph and there is no VMD anywhere that drives it.
|
|
374
|
+
*/
|
|
375
|
+
applyBoneMorphs() {
|
|
376
|
+
if (this.boneMorphPlan.length === 0)
|
|
377
|
+
return;
|
|
378
|
+
// Undo the previous application first. With a clip running this is a no-op
|
|
379
|
+
// in effect — the pose source already overwrote these bones — but a stage
|
|
380
|
+
// has no clip, so without it the same offset would be re-added every frame.
|
|
381
|
+
if (this.boneMorphApplied) {
|
|
382
|
+
for (let i = 0; i < this.boneMorphBones.length; i++) {
|
|
383
|
+
const b = this.boneMorphBones[i];
|
|
384
|
+
this.runtimeSkeleton.localRotations[b].set(this.boneMorphRestoreR[i]);
|
|
385
|
+
this.runtimeSkeleton.localTranslations[b].set(this.boneMorphRestoreT[i]);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
for (let i = 0; i < this.boneMorphBones.length; i++) {
|
|
389
|
+
const b = this.boneMorphBones[i];
|
|
390
|
+
this.boneMorphRestoreR[i].set(this.runtimeSkeleton.localRotations[b]);
|
|
391
|
+
this.boneMorphRestoreT[i].set(this.runtimeSkeleton.localTranslations[b]);
|
|
392
|
+
}
|
|
393
|
+
this.boneMorphApplied = true;
|
|
394
|
+
const weights = this.getEffectiveMorphWeights();
|
|
395
|
+
for (const entry of this.boneMorphPlan) {
|
|
396
|
+
const w = weights[entry.morphIndex];
|
|
397
|
+
if (w < 0.0001)
|
|
398
|
+
continue;
|
|
399
|
+
const t = this.runtimeSkeleton.localTranslations[entry.boneIndex];
|
|
400
|
+
t.x += entry.translation.x * w;
|
|
401
|
+
t.y += entry.translation.y * w;
|
|
402
|
+
t.z += entry.translation.z * w;
|
|
403
|
+
// Scale the rotation by weight the way MMD does — slerp out of identity,
|
|
404
|
+
// then compose. Multiplying components would not stay a unit quaternion.
|
|
405
|
+
const r = this.runtimeSkeleton.localRotations[entry.boneIndex];
|
|
406
|
+
Quat.slerpInto(_boneMorphIdentity, entry.rotation, w, _boneMorphQ);
|
|
407
|
+
Quat.multiplyInto(r, _boneMorphQ, r);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/** True (once) when effective morph weights changed — the engine re-derives
|
|
411
|
+
* material-morph uniforms from it. Separate from the GPU vertex path's flag
|
|
412
|
+
* so both can consume the same change. */
|
|
413
|
+
consumeAuxMorphDirty() {
|
|
414
|
+
const d = this.auxMorphDirty;
|
|
415
|
+
this.auxMorphDirty = false;
|
|
416
|
+
return d;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Morph indices this model can actually act on, so a UI never offers a control
|
|
420
|
+
* that moves nothing.
|
|
421
|
+
*
|
|
422
|
+
* Driven directly: vertex (1), bone (2), material (8). Excluded: UV (3–7),
|
|
423
|
+
* which are parsed and kept but not yet applied; flip (9) and impulse (10),
|
|
424
|
+
* which are PMX 2.1 and would need the rigidbody solver.
|
|
425
|
+
*
|
|
426
|
+
* A group morph (0) is only as alive as what it points at — one referencing
|
|
427
|
+
* nothing but UV morphs is just as dead as the UV morphs themselves, so it is
|
|
428
|
+
* resolved rather than assumed.
|
|
429
|
+
*/
|
|
430
|
+
getSupportedMorphIndices() {
|
|
431
|
+
const morphs = this.morphing.morphs;
|
|
432
|
+
const drivable = (t) => t === 1 || t === 2 || t === 8;
|
|
433
|
+
// Groups can reference groups, so walk with a seen-set rather than recursing.
|
|
434
|
+
const resolves = (start) => {
|
|
435
|
+
const seen = new Set();
|
|
436
|
+
const stack = [start];
|
|
437
|
+
while (stack.length > 0) {
|
|
438
|
+
const i = stack.pop();
|
|
439
|
+
if (seen.has(i) || i < 0 || i >= morphs.length)
|
|
440
|
+
continue;
|
|
441
|
+
seen.add(i);
|
|
442
|
+
const m = morphs[i];
|
|
443
|
+
if (drivable(m.type))
|
|
444
|
+
return true;
|
|
445
|
+
if (m.type === 0 && m.groupReferences) {
|
|
446
|
+
for (const ref of m.groupReferences)
|
|
447
|
+
stack.push(ref.morphIndex);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
};
|
|
452
|
+
const out = [];
|
|
453
|
+
for (let i = 0; i < morphs.length; i++) {
|
|
454
|
+
const t = morphs[i].type;
|
|
455
|
+
if (drivable(t) || (t === 0 && resolves(i)))
|
|
456
|
+
out.push(i);
|
|
457
|
+
}
|
|
458
|
+
return out;
|
|
320
459
|
}
|
|
321
460
|
// Tween update - processes all tweens together with a single time reference
|
|
322
461
|
// This avoids conflicts and ensures consistent timing across all tween types
|
|
@@ -676,6 +815,12 @@ export class Model {
|
|
|
676
815
|
this.runtimeMorph.weights[idx] = clampedWeight;
|
|
677
816
|
this.tweenState.morphActive[idx] = 0;
|
|
678
817
|
this.applyMorphs();
|
|
818
|
+
// Vertex and material morphs are done by applyMorphs alone, but a bone
|
|
819
|
+
// morph lands in the pose pass — and a model with no clip (every stage)
|
|
820
|
+
// reports idle, so without this the pass never runs and the switch does
|
|
821
|
+
// nothing. Costs one redundant applyMorphs on the next frame.
|
|
822
|
+
if (this.boneMorphPlan.length > 0)
|
|
823
|
+
this.morphsDirty = true;
|
|
679
824
|
try {
|
|
680
825
|
Engine.getInstance().markVertexBufferDirty(this);
|
|
681
826
|
}
|
|
@@ -733,6 +878,10 @@ export class Model {
|
|
|
733
878
|
for (let i = 0; i < morphCount; i++) {
|
|
734
879
|
effectiveWeights[i] = Math.max(0, Math.min(1, effectiveWeights[i]));
|
|
735
880
|
}
|
|
881
|
+
// Bone morphs read these every frame, but material/UV consumers live outside
|
|
882
|
+
// this class and need telling. Set on BOTH paths — a model whose only morphs
|
|
883
|
+
// are material morphs never enables the GPU vertex path at all.
|
|
884
|
+
this.auxMorphDirty = true;
|
|
736
885
|
// GPU path: the compute pass applies the vertex offsets from these weights.
|
|
737
886
|
if (this.gpuMorphEnabled) {
|
|
738
887
|
this.morphWeightsDirty = true;
|
|
@@ -1557,6 +1706,45 @@ export class Model {
|
|
|
1557
1706
|
// within that. A host driving bones directly — motion capture writing FK
|
|
1558
1707
|
// rotations every frame with no clip playing — turns it off wholesale, because
|
|
1559
1708
|
// there is no motion present to carry the per-chain answer.
|
|
1709
|
+
/**
|
|
1710
|
+
* Nothing can have moved this frame: no clip, no blend, no live tween, no
|
|
1711
|
+
* morph weight change.
|
|
1712
|
+
*
|
|
1713
|
+
* Environment geometry is in this state almost every frame, so the engine
|
|
1714
|
+
* skips the whole pose pass — sampling, world matrices, and the skin-matrix
|
|
1715
|
+
* upload — for a stage that reports idle. A stage is usually the heaviest mesh
|
|
1716
|
+
* in the scene and the one that never moves; paying a full pose pass for it
|
|
1717
|
+
* every frame is the thing worth not doing.
|
|
1718
|
+
*/
|
|
1719
|
+
isIdle() {
|
|
1720
|
+
// Never idle before the first pose pass. The constructor leaves the world
|
|
1721
|
+
// matrices identity, so skin = world × inverseBind collapses every vertex
|
|
1722
|
+
// into bone-local space — the mesh piles up at the origin. A cast member is
|
|
1723
|
+
// saved by running update() on frame 1 regardless; a stage that reported
|
|
1724
|
+
// idle immediately would render as a heap and never recover.
|
|
1725
|
+
if (!this.posedOnce)
|
|
1726
|
+
return false;
|
|
1727
|
+
return (!this.morphsDirty &&
|
|
1728
|
+
this.oneShot === null &&
|
|
1729
|
+
this.crossfade === null &&
|
|
1730
|
+
(this.blendEntries === null || this.blendEntries.length === 0) &&
|
|
1731
|
+
this.animationState.getCurrentClip() === null &&
|
|
1732
|
+
!this.hasActiveTweens());
|
|
1733
|
+
}
|
|
1734
|
+
/** Any live rotation / translation / morph tween. */
|
|
1735
|
+
hasActiveTweens() {
|
|
1736
|
+
const s = this.tweenState;
|
|
1737
|
+
for (let i = 0; i < s.rotActive.length; i++)
|
|
1738
|
+
if (s.rotActive[i] === 1)
|
|
1739
|
+
return true;
|
|
1740
|
+
for (let i = 0; i < s.transActive.length; i++)
|
|
1741
|
+
if (s.transActive[i] === 1)
|
|
1742
|
+
return true;
|
|
1743
|
+
for (let i = 0; i < s.morphActive.length; i++)
|
|
1744
|
+
if (s.morphActive[i] === 1)
|
|
1745
|
+
return true;
|
|
1746
|
+
return false;
|
|
1747
|
+
}
|
|
1560
1748
|
update(deltaTime, ikEnabled = true) {
|
|
1561
1749
|
// Update tween time (in milliseconds)
|
|
1562
1750
|
this.tweenTimeMs += deltaTime * 1000;
|
|
@@ -1597,8 +1785,12 @@ export class Model {
|
|
|
1597
1785
|
this.applyMorphs();
|
|
1598
1786
|
this.morphsDirty = false;
|
|
1599
1787
|
}
|
|
1788
|
+
// After the pose sources and the constant offsets, before the world pass —
|
|
1789
|
+
// bone morphs must survive into the world matrices IK then reads.
|
|
1790
|
+
this.applyBoneMorphs();
|
|
1600
1791
|
// Compute world matrices (needed for IK solving to read bone positions)
|
|
1601
1792
|
this.computeWorldMatrices();
|
|
1793
|
+
this.posedOnce = true;
|
|
1602
1794
|
// Solve IK chains (modifies localRotations with final IK rotations). Chains
|
|
1603
1795
|
// the clip switched off are skipped inside.
|
|
1604
1796
|
if (ikEnabled) {
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type Rigidbody } from "./types";
|
|
2
|
+
import type { Skinning } from "../model";
|
|
3
|
+
/**
|
|
4
|
+
* Supplementary colliders fitted to the mesh a PMX rigid body fails to cover.
|
|
5
|
+
*
|
|
6
|
+
* PMX body proxies are bodies of revolution — 下半身 is a capsule lying
|
|
7
|
+
* transversely across the hips, so its cross-section is a circle. A pelvis seen
|
|
8
|
+
* from the side is not a circle, and across every rig measured the butt sits
|
|
9
|
+
* 0.18–0.71 units OUTSIDE that capsule while the front is flush or inside. That
|
|
10
|
+
* gap is where a skirt goes through a character: the cloth is resting correctly
|
|
11
|
+
* on the collider it was given, and the collider stops half a unit short of the
|
|
12
|
+
* skin.
|
|
13
|
+
*
|
|
14
|
+
* A rest offset cannot close it (see RigidBodyStore.restOffset) — it is one
|
|
15
|
+
* scalar on a radially symmetric shape, and the error is asymmetric. Measured
|
|
16
|
+
* on 托特's run cycle, the offset that clears the butt is 0.5, which is simply
|
|
17
|
+
* the size of the missing geometry, and at that value the skirt stands off the
|
|
18
|
+
* front and hips by the same 0.5.
|
|
19
|
+
*
|
|
20
|
+
* So fit the missing piece instead: take each bone-following body's own skinned
|
|
21
|
+
* vertices, find the ones that escape its shape in a concentrated lobe, and fit
|
|
22
|
+
* a capsule to that lobe. That is the fix riggers apply by hand, derived from
|
|
23
|
+
* the mesh rather than guessed.
|
|
24
|
+
*
|
|
25
|
+
* Measured on 托特's run cycle, counting butt vertices swallowed by a cloth
|
|
26
|
+
* body: 32.4 per frame with nothing, 30.9 with a 0.02 rest offset, 12.4 with
|
|
27
|
+
* this pass, 10.4 with both.
|
|
28
|
+
*/
|
|
29
|
+
export interface AutoFitOptions {
|
|
30
|
+
/** A vertex counts as protruding past this much (model units). Below it the
|
|
31
|
+
* mesh is within authoring tolerance of the proxy and needs no help. */
|
|
32
|
+
minProtrusion?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Ignore vertices further out than this multiple of the source shape's
|
|
35
|
+
* RADIUS (its smallest characteristic dimension).
|
|
36
|
+
*
|
|
37
|
+
* A coarse first cut at keeping hair and loose clothing out — anything
|
|
38
|
+
* standing further off the proxy than the proxy is wide is not skin. The
|
|
39
|
+
* discriminator that actually does the work is minConcentration.
|
|
40
|
+
*/
|
|
41
|
+
maxProtrusionRatio?: number;
|
|
42
|
+
/** Fewer protruding vertices than this and it is a spike, not a region. */
|
|
43
|
+
minVertices?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Per-axis quantile the fitted lobe's extent is clipped to, each end. A
|
|
46
|
+
* handful of stray vertices (a weight-painting slip, a seam) would otherwise
|
|
47
|
+
* stretch the capsule far past the surface everyone can see.
|
|
48
|
+
*/
|
|
49
|
+
quantile?: number;
|
|
50
|
+
/** Skip the fit unless the capsule actually reaches this far past what is
|
|
51
|
+
* already covered — otherwise it adds solver rows for nothing. */
|
|
52
|
+
minGain?: number;
|
|
53
|
+
/** How tightly the kept lobe's outward directions must agree, 0–1. A loose
|
|
54
|
+
* sanity check; the real discriminator is minConcentration. */
|
|
55
|
+
minDirectionality?: number;
|
|
56
|
+
/** Vertices outside the dominant lobe, by this cosine, are not part of the
|
|
57
|
+
* feature being fitted. */
|
|
58
|
+
lobeCos?: number;
|
|
59
|
+
/**
|
|
60
|
+
* How much deeper the fitted lobe must be than protrusion typically is on
|
|
61
|
+
* this body, averaged over the vertices that protrude at all.
|
|
62
|
+
*
|
|
63
|
+
* This is the guard that separates "the proxy is missing a lobe" from "the
|
|
64
|
+
* proxy is not a proxy for this mesh at all". A pelvis escapes its capsule by
|
|
65
|
+
* 0.53 at the back and 0.14 at the front, so the back lobe stands well clear
|
|
66
|
+
* of the average. Hair escapes the skull by a similar depth in every
|
|
67
|
+
* direction, so its deepest lobe is no deeper than typical and the ratio sits
|
|
68
|
+
* near 1. See the note at the meanProtrusion computation for what this
|
|
69
|
+
* deliberately refuses to fit.
|
|
70
|
+
*
|
|
71
|
+
* Without it the pass fits the hairstyle — measured on 托特, 頭 reports
|
|
72
|
+
* 3653 of 4669 vertices outside its capsule, and the fitted shape was a crate
|
|
73
|
+
* around the whole head. No threshold on depth or vertex count tells those
|
|
74
|
+
* two cases apart; concentration does.
|
|
75
|
+
*/
|
|
76
|
+
minConcentration?: number;
|
|
77
|
+
/**
|
|
78
|
+
* How many capsules may be fitted to one body, each against what the previous
|
|
79
|
+
* ones already cover.
|
|
80
|
+
*
|
|
81
|
+
* One capsule inscribed in a lobe does not fill it — on 托特's butt a single
|
|
82
|
+
* fit removed 58% of the clipping and left the rest at the edges the capsule
|
|
83
|
+
* could not reach. Each further pass re-measures protrusion against the
|
|
84
|
+
* original shape UNION everything fitted so far, so it lands on whatever is
|
|
85
|
+
* still exposed instead of re-fitting the same bulge.
|
|
86
|
+
*/
|
|
87
|
+
maxShapesPerBody?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Quantile of the lobe's perpendicular spread used as the capsule radius.
|
|
90
|
+
*
|
|
91
|
+
* The clip-versus-float dial, and the one to reach for if a result looks
|
|
92
|
+
* wrong. Lower hugs the body: less standoff in idle, more clipping left. 1.0
|
|
93
|
+
* encloses the lobe entirely, which is what produced a visibly hovering
|
|
94
|
+
* skirt on 托特.
|
|
95
|
+
*/
|
|
96
|
+
radiusQuantile?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Fit only the deepest part of each lobe: points at least this fraction of
|
|
99
|
+
* the lobe's own maximum protrusion.
|
|
100
|
+
*
|
|
101
|
+
* A lobe tapers to nothing at its edges, and a capsule sized to span the
|
|
102
|
+
* whole thing has to be fat enough to reach the edges too. Fitting the deep
|
|
103
|
+
* core keeps each capsule small and lets a later pass handle the shallows
|
|
104
|
+
* with its own, which is both tighter and closer to what the surface is
|
|
105
|
+
* actually doing.
|
|
106
|
+
*/
|
|
107
|
+
lobeDepthFraction?: number;
|
|
108
|
+
}
|
|
109
|
+
/** What the pass decided for one source body — surfaced so a rig can be
|
|
110
|
+
* inspected rather than silently rebuilt underneath its author. */
|
|
111
|
+
export interface AutoFitResult {
|
|
112
|
+
/** Index of the body in the input array that this supplements. */
|
|
113
|
+
sourceIndex: number;
|
|
114
|
+
sourceName: string;
|
|
115
|
+
/** Vertices dominated by that body's bone, and how many were outside it. */
|
|
116
|
+
vertexCount: number;
|
|
117
|
+
protrudingCount: number;
|
|
118
|
+
/** Furthest any kept vertex sat outside the source shape. */
|
|
119
|
+
maxProtrusion: number;
|
|
120
|
+
/** How far the fitted capsule reaches past what was already covered. */
|
|
121
|
+
gain: number;
|
|
122
|
+
/** Length of the kept lobe's mean outward direction, 0–1. */
|
|
123
|
+
directionality: number;
|
|
124
|
+
/** Lobe depth over the body's average protrusion. See minConcentration. */
|
|
125
|
+
concentration: number;
|
|
126
|
+
/**
|
|
127
|
+
* Furthest the capsule's newly exposed surface sits from the skin it was
|
|
128
|
+
* fitted to — the amount cloth resting on it will stand off the body at the
|
|
129
|
+
* worst point, and the number to watch if a result looks puffy. Only the part
|
|
130
|
+
* reaching outside the source shape is measured; the rest is buried.
|
|
131
|
+
*/
|
|
132
|
+
overshoot: number;
|
|
133
|
+
body: Rigidbody;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Fit supplementary colliders for every bone-following body whose own mesh
|
|
137
|
+
* escapes it.
|
|
138
|
+
*
|
|
139
|
+
* `vertices` is the model's interleaved vertex buffer (stride 8, position
|
|
140
|
+
* first) and `skinning` its joints/weights — both in PMX bind pose, the same
|
|
141
|
+
* space as `shapePosition` / `shapeRotation`, so nothing has to be skinned.
|
|
142
|
+
*
|
|
143
|
+
* Returns one entry per body it chose to supplement; the caller appends
|
|
144
|
+
* `.body` to the rigid body list before constructing RezePhysics.
|
|
145
|
+
*/
|
|
146
|
+
export declare function fitSupplementaryColliders(rigidbodies: Rigidbody[], vertices: Float32Array, skinning: Skinning, options?: AutoFitOptions): AutoFitResult[];
|
|
147
|
+
//# sourceMappingURL=autofit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autofit.d.ts","sourceRoot":"","sources":["../../src/physics/autofit.ts"],"names":[],"mappings":"AACA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,cAAc;IAC7B;6EACyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;uEACmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;oEACgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;gCAC4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AA2BD;oEACoE;AACpE,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,6DAA6D;IAC7D,aAAa,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAA;IACZ,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAA;IACrB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,SAAS,CAAA;CAChB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,SAAS,EAAE,EACxB,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,cAAmB,GAC3B,aAAa,EAAE,CA4CjB"}
|