reze-engine 0.50.2 → 0.50.3
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/engine.d.ts +57 -5
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +107 -45
- package/dist/physics-debug.d.ts +30 -0
- package/dist/physics-debug.d.ts.map +1 -0
- package/dist/physics-debug.js +526 -0
- package/dist/shaders/materials/body.d.ts +2 -0
- package/dist/shaders/materials/body.d.ts.map +1 -0
- package/dist/shaders/materials/body.js +95 -0
- package/dist/shaders/materials/cloth_rough.d.ts +2 -0
- package/dist/shaders/materials/cloth_rough.d.ts.map +1 -0
- package/dist/shaders/materials/cloth_rough.js +69 -0
- package/dist/shaders/materials/cloth_smooth.d.ts +2 -0
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -0
- package/dist/shaders/materials/cloth_smooth.js +61 -0
- package/dist/shaders/materials/default.d.ts +2 -0
- package/dist/shaders/materials/default.d.ts.map +1 -0
- package/dist/shaders/materials/default.js +43 -0
- package/dist/shaders/materials/eye.d.ts +2 -0
- package/dist/shaders/materials/eye.d.ts.map +1 -0
- package/dist/shaders/materials/eye.js +60 -0
- package/dist/shaders/materials/face.d.ts +2 -0
- package/dist/shaders/materials/face.d.ts.map +1 -0
- package/dist/shaders/materials/face.js +95 -0
- package/dist/shaders/materials/hair.d.ts +2 -0
- package/dist/shaders/materials/hair.d.ts.map +1 -0
- package/dist/shaders/materials/hair.js +90 -0
- package/dist/shaders/materials/metal.d.ts +2 -0
- package/dist/shaders/materials/metal.d.ts.map +1 -0
- package/dist/shaders/materials/metal.js +77 -0
- package/dist/shaders/materials/mmd_classic.d.ts +2 -0
- package/dist/shaders/materials/mmd_classic.d.ts.map +1 -0
- package/dist/shaders/materials/mmd_classic.js +66 -0
- package/dist/shaders/materials/stockings.d.ts +2 -0
- package/dist/shaders/materials/stockings.d.ts.map +1 -0
- package/dist/shaders/materials/stockings.js +122 -0
- package/dist/shaders/passes/ground.d.ts.map +1 -1
- package/dist/shaders/passes/ground.js +12 -22
- package/dist/shaders/passes/physics-debug.d.ts +2 -0
- package/dist/shaders/passes/physics-debug.d.ts.map +1 -0
- package/dist/shaders/passes/physics-debug.js +69 -0
- package/package.json +2 -2
- package/src/engine.ts +113 -44
- package/src/shaders/passes/ground.ts +12 -22
- package/dist/physics/autofit.d.ts +0 -147
- package/dist/physics/autofit.d.ts.map +0 -1
- package/dist/physics/autofit.js +0 -501
- package/dist/shaders/passes/field-blit.d.ts +0 -26
- package/dist/shaders/passes/field-blit.d.ts.map +0 -1
- package/dist/shaders/passes/field-blit.js +0 -65
- package/dist/shaders/passes/ground-noise.d.ts +0 -7
- package/dist/shaders/passes/ground-noise.d.ts.map +0 -1
- package/dist/shaders/passes/ground-noise.js +0 -88
- package/dist/shaders/passes/sim.d.ts +0 -34
- package/dist/shaders/passes/sim.d.ts.map +0 -1
- package/dist/shaders/passes/sim.js +0 -169
- package/dist/shaders/score-api.d.ts +0 -10
- package/dist/shaders/score-api.d.ts.map +0 -1
- package/dist/shaders/score-api.js +0 -114
package/src/engine.ts
CHANGED
|
@@ -1232,6 +1232,8 @@ interface EffectInstance {
|
|
|
1232
1232
|
lights: {
|
|
1233
1233
|
pipeline: GPUComputePipeline
|
|
1234
1234
|
bind: GPUBindGroup
|
|
1235
|
+
/** Kept so `bind` can be rebuilt when a shared buffer it names is replaced. */
|
|
1236
|
+
layout: GPUBindGroupLayout
|
|
1235
1237
|
uniform: GPUBuffer
|
|
1236
1238
|
data: Float32Array<ArrayBuffer>
|
|
1237
1239
|
/** How many slots it asked for. Its base is assigned by the engine and can
|
|
@@ -1387,6 +1389,24 @@ export class Engine {
|
|
|
1387
1389
|
// the fragment shader and treats missing dst.a as 1, so the blend math is
|
|
1388
1390
|
// unchanged).
|
|
1389
1391
|
private hdrFormat: GPUTextureFormat = "rgba16float"
|
|
1392
|
+
/**
|
|
1393
|
+
* Force the HDR format instead of taking the device's answer. Null = probe,
|
|
1394
|
+
* which is what ships.
|
|
1395
|
+
*
|
|
1396
|
+
* A diagnostic, and deliberately a coarse one. The choice above is the ONE
|
|
1397
|
+
* render-target difference between a Safari device and a desktop Chrome that
|
|
1398
|
+
* lacks the feature, which makes it the first thing to eliminate when
|
|
1399
|
+
* something renders correctly on one and not the other — and specifically when
|
|
1400
|
+
* the something involves alpha, because rg11b10ufloat is the path with no
|
|
1401
|
+
* alpha channel to carry it. Setting this to "rgba16float" on the device puts
|
|
1402
|
+
* Safari back on the desktop's path at the cost of the tile-memory win, so a
|
|
1403
|
+
* symptom that survives is not about the format and a symptom that vanishes
|
|
1404
|
+
* is.
|
|
1405
|
+
*
|
|
1406
|
+
* Static, like MRT_IDS: read once in init(), before any texture or pipeline
|
|
1407
|
+
* exists, so there is no such thing as changing it on a live engine.
|
|
1408
|
+
*/
|
|
1409
|
+
static HDR_FORMAT_OVERRIDE: GPUTextureFormat | null = null
|
|
1390
1410
|
/** Main-pass depth. Float when the adapter offers depth32float-stencil8, which
|
|
1391
1411
|
* is also what makes reversed-Z worth switching on. */
|
|
1392
1412
|
private depthFormat: GPUTextureFormat = "depth24plus-stencil8"
|
|
@@ -2216,11 +2236,10 @@ export class Engine {
|
|
|
2216
2236
|
* sibling of addGround's own options. False when there is no ground.
|
|
2217
2237
|
*
|
|
2218
2238
|
* ON OR OFF, deliberately not a strength: the reflection is an independent
|
|
2219
|
-
* LAYER
|
|
2220
|
-
*
|
|
2221
|
-
*
|
|
2222
|
-
*
|
|
2223
|
-
* reflected geometry sits behind the surface.
|
|
2239
|
+
* LAYER beneath the floor surface, and how much of it shows is the ground's
|
|
2240
|
+
* own opacity covering it. Blur 0 is a polished mirror; 1 samples the
|
|
2241
|
+
* softest level, scaled by how far the reflected geometry sits behind the
|
|
2242
|
+
* surface.
|
|
2224
2243
|
*/
|
|
2225
2244
|
setGroundMirror(on: boolean, blur?: number): boolean {
|
|
2226
2245
|
if (!this.groundShadowMaterialBuffer) return false
|
|
@@ -2542,6 +2561,72 @@ export class Engine {
|
|
|
2542
2561
|
}
|
|
2543
2562
|
}
|
|
2544
2563
|
|
|
2564
|
+
/**
|
|
2565
|
+
* The grid mount's bind group for one parity.
|
|
2566
|
+
*
|
|
2567
|
+
* A method rather than a closure at the creation site because the SET of
|
|
2568
|
+
* buffers in here is a contract with two parties: the grid is built once, and
|
|
2569
|
+
* rebuilt whenever a shared buffer it names is replaced (see
|
|
2570
|
+
* rebindSharedBuffers). Written twice, the rebuild silently keeps a binding
|
|
2571
|
+
* the creation grew — and a bind group that names a destroyed buffer does not
|
|
2572
|
+
* fail where it was written, it fails at the next submit.
|
|
2573
|
+
*/
|
|
2574
|
+
private gridBindGroup(
|
|
2575
|
+
g: { layout: GPUBindGroupLayout; uniform: GPUBuffer; read: [GPUTextureView, GPUTextureView]; textures: [GPUTexture, GPUTexture] },
|
|
2576
|
+
i: number,
|
|
2577
|
+
): GPUBindGroup {
|
|
2578
|
+
return this.device.createBindGroup({
|
|
2579
|
+
layout: g.layout,
|
|
2580
|
+
entries: [
|
|
2581
|
+
{ binding: 0, resource: { buffer: g.uniform } },
|
|
2582
|
+
{ binding: 1, resource: g.read[i] },
|
|
2583
|
+
{ binding: 2, resource: this.simSampler },
|
|
2584
|
+
{ binding: 3, resource: g.textures[1 - i].createView() },
|
|
2585
|
+
{ binding: 4, resource: { buffer: this.castBuffer } },
|
|
2586
|
+
{ binding: 5, resource: { buffer: this.audioBuffer } },
|
|
2587
|
+
{ binding: 6, resource: { buffer: this.compositeUniformBuffer } },
|
|
2588
|
+
{ binding: 7, resource: { buffer: this.midiBuffer } },
|
|
2589
|
+
{ binding: 8, resource: { buffer: this.lyricsBuffer } },
|
|
2590
|
+
],
|
|
2591
|
+
})
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
/**
|
|
2595
|
+
* Re-point EVERY bind group that names a shared scene buffer at the buffer
|
|
2596
|
+
* that is there NOW.
|
|
2597
|
+
*
|
|
2598
|
+
* setAudioData and setMidiNotes do not write their buffer, they REPLACE it:
|
|
2599
|
+
* the payload is a different length each time, so the old one is destroyed and
|
|
2600
|
+
* a new one takes its place. Every bind group built before that moment still
|
|
2601
|
+
* names the dead buffer, and a bind group is not re-read — it holds the
|
|
2602
|
+
* resource it was given. The failure is therefore not at the swap but one
|
|
2603
|
+
* frame later, as `[Buffer "score"] used in submit while destroyed`, with the
|
|
2604
|
+
* scene dead and nothing pointing at the setter that did it.
|
|
2605
|
+
*
|
|
2606
|
+
* Both setters used to rebind three of the six families that hold these
|
|
2607
|
+
* buffers — composite, ribbons, particles — and miss the field mount, the grid
|
|
2608
|
+
* mount and the light emitter. Which is to say it worked for every effect that
|
|
2609
|
+
* happened not to have a field, and a falling-note effect is exactly the kind
|
|
2610
|
+
* that does. So the list lives HERE, once, and both setters call it: the
|
|
2611
|
+
* question "who holds this buffer?" now has one place to be answered, and the
|
|
2612
|
+
* next binding added is added to a list that everything already consults.
|
|
2613
|
+
*/
|
|
2614
|
+
private rebindSharedBuffers(): void {
|
|
2615
|
+
this.rebuildCompositeBindGroup()
|
|
2616
|
+
this.rebindTrails()
|
|
2617
|
+
this.rebuildFieldBindGroup()
|
|
2618
|
+
for (const e of this.effects) {
|
|
2619
|
+
if (e.particles) {
|
|
2620
|
+
const b = e.particles.rebind()
|
|
2621
|
+
e.particles.computeBind = b.computeBind
|
|
2622
|
+
e.particles.renderBind = b.renderBind
|
|
2623
|
+
e.particles.mirrorRenderBind = b.mirrorRenderBind
|
|
2624
|
+
}
|
|
2625
|
+
if (e.grid) e.grid.binds = [this.gridBindGroup(e.grid, 0), this.gridBindGroup(e.grid, 1)]
|
|
2626
|
+
if (e.lights) e.lights.bind = this.lightEmitBindGroup(e.lights.layout, e.lights.uniform)
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2545
2630
|
/**
|
|
2546
2631
|
* Set a 360° backdrop from an equirectangular (2:1) image — a PhotoDome-style
|
|
2547
2632
|
* skybox at infinity, sampled per-pixel by view direction so it follows the
|
|
@@ -3402,7 +3487,16 @@ export class Engine {
|
|
|
3402
3487
|
uniform.destroy()
|
|
3403
3488
|
return { ok: false, diagnostics: [scoped.message] }
|
|
3404
3489
|
}
|
|
3405
|
-
const bind = this.
|
|
3490
|
+
const bind = this.lightEmitBindGroup(layout, uniform)
|
|
3491
|
+
// The layout travels with the state so the emitter can be rebound when a
|
|
3492
|
+
// shared buffer under it is replaced — see rebindSharedBuffers.
|
|
3493
|
+
return { ok: true, state: { pipeline, bind, layout, uniform, data, count } }
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
/** The light emitter's bind group. One author, for the reason gridBindGroup
|
|
3497
|
+
* gives: it is built once and rebuilt on every shared-buffer swap. */
|
|
3498
|
+
private lightEmitBindGroup(layout: GPUBindGroupLayout, uniform: GPUBuffer): GPUBindGroup {
|
|
3499
|
+
return this.device.createBindGroup({
|
|
3406
3500
|
label: "light emit bind",
|
|
3407
3501
|
layout,
|
|
3408
3502
|
entries: [
|
|
@@ -3415,7 +3509,6 @@ export class Engine {
|
|
|
3415
3509
|
{ binding: 6, resource: { buffer: this.lyricsBuffer } },
|
|
3416
3510
|
],
|
|
3417
3511
|
})
|
|
3418
|
-
return { ok: true, state: { pipeline, bind, uniform, data, count } }
|
|
3419
3512
|
}
|
|
3420
3513
|
|
|
3421
3514
|
/**
|
|
@@ -3851,21 +3944,7 @@ export class Engine {
|
|
|
3851
3944
|
return { ok: false, diagnostics: [scoped.message] }
|
|
3852
3945
|
}
|
|
3853
3946
|
// One per parity: binds[i] READS textures[i] and WRITES the other.
|
|
3854
|
-
const bindFor = (i: number) =>
|
|
3855
|
-
this.device.createBindGroup({
|
|
3856
|
-
layout,
|
|
3857
|
-
entries: [
|
|
3858
|
-
{ binding: 0, resource: { buffer: uniform } },
|
|
3859
|
-
{ binding: 1, resource: read[i] },
|
|
3860
|
-
{ binding: 2, resource: this.simSampler },
|
|
3861
|
-
{ binding: 3, resource: textures[1 - i].createView() },
|
|
3862
|
-
{ binding: 4, resource: { buffer: this.castBuffer } },
|
|
3863
|
-
{ binding: 5, resource: { buffer: this.audioBuffer } },
|
|
3864
|
-
{ binding: 6, resource: { buffer: this.compositeUniformBuffer } },
|
|
3865
|
-
{ binding: 7, resource: { buffer: this.midiBuffer } },
|
|
3866
|
-
{ binding: 8, resource: { buffer: this.lyricsBuffer } },
|
|
3867
|
-
],
|
|
3868
|
-
})
|
|
3947
|
+
const bindFor = (i: number) => this.gridBindGroup({ layout, uniform, read, textures }, i)
|
|
3869
3948
|
return {
|
|
3870
3949
|
ok: true,
|
|
3871
3950
|
state: {
|
|
@@ -4109,6 +4188,16 @@ export class Engine {
|
|
|
4109
4188
|
}
|
|
4110
4189
|
this.device = device
|
|
4111
4190
|
if (hasRg11b10) this.hdrFormat = "rg11b10ufloat"
|
|
4191
|
+
// The override has the last word, including over a device that would have
|
|
4192
|
+
// been left on the fallback anyway — asking for the format you are already
|
|
4193
|
+
// getting is a no-op, not a contradiction. See HDR_FORMAT_OVERRIDE.
|
|
4194
|
+
if (Engine.HDR_FORMAT_OVERRIDE) {
|
|
4195
|
+
this.hdrFormat = Engine.HDR_FORMAT_OVERRIDE
|
|
4196
|
+
// Only when forced. The probed answer is the normal one and does not need
|
|
4197
|
+
// announcing on every boot; a forced one is a state someone set and will
|
|
4198
|
+
// want confirmed, and is the state they will forget they left on.
|
|
4199
|
+
console.info(`[reze] HDR target forced to ${this.hdrFormat}`)
|
|
4200
|
+
}
|
|
4112
4201
|
// The id attachment, if this device will multisample a uint texture at the
|
|
4113
4202
|
// pass's sample count. Probed by ASKING — creating one inside an error
|
|
4114
4203
|
// scope — rather than by reading a feature flag, because there is no
|
|
@@ -6137,17 +6226,7 @@ export class Engine {
|
|
|
6137
6226
|
}
|
|
6138
6227
|
// Every consumer holds the buffer by reference in a bind group; all of them
|
|
6139
6228
|
// re-bind so audio arriving after an effect (or before one) both work.
|
|
6140
|
-
this.
|
|
6141
|
-
this.rebindTrails()
|
|
6142
|
-
// EVERY effect: a buffer arriving after install must reach all of them, or
|
|
6143
|
-
// the ones installed earlier keep reading the buffer this replaced.
|
|
6144
|
-
for (const e of this.effects) {
|
|
6145
|
-
if (!e.particles) continue
|
|
6146
|
-
const b = e.particles.rebind()
|
|
6147
|
-
e.particles.computeBind = b.computeBind
|
|
6148
|
-
e.particles.renderBind = b.renderBind
|
|
6149
|
-
e.particles.mirrorRenderBind = b.mirrorRenderBind
|
|
6150
|
-
}
|
|
6229
|
+
this.rebindSharedBuffers()
|
|
6151
6230
|
}
|
|
6152
6231
|
|
|
6153
6232
|
/**
|
|
@@ -6248,17 +6327,7 @@ export class Engine {
|
|
|
6248
6327
|
// Same reason as setAudioData: every consumer holds the buffer by reference,
|
|
6249
6328
|
// so all of them re-bind and a score arriving before or after an effect both
|
|
6250
6329
|
// work.
|
|
6251
|
-
this.
|
|
6252
|
-
this.rebindTrails()
|
|
6253
|
-
// EVERY effect: a buffer arriving after install must reach all of them, or
|
|
6254
|
-
// the ones installed earlier keep reading the buffer this replaced.
|
|
6255
|
-
for (const e of this.effects) {
|
|
6256
|
-
if (!e.particles) continue
|
|
6257
|
-
const b = e.particles.rebind()
|
|
6258
|
-
e.particles.computeBind = b.computeBind
|
|
6259
|
-
e.particles.renderBind = b.renderBind
|
|
6260
|
-
e.particles.mirrorRenderBind = b.mirrorRenderBind
|
|
6261
|
-
}
|
|
6330
|
+
this.rebindSharedBuffers()
|
|
6262
6331
|
}
|
|
6263
6332
|
|
|
6264
6333
|
/**
|
|
@@ -186,8 +186,8 @@ ${sceneFsOutWgsl()}@fragment fn fs(i: VO) -> FSOut {
|
|
|
186
186
|
// reflection was rendered by the MIRROR camera, so projecting this
|
|
187
187
|
// fragment's world position through that camera yields exactly the texel
|
|
188
188
|
// where its reflection landed — projective mapping, no screen-space guess.
|
|
189
|
-
// There is deliberately NO strength dial
|
|
190
|
-
//
|
|
189
|
+
// There is deliberately NO strength dial: mirror is on or off, and how much
|
|
190
|
+
// of it shows is the SURFACE's own opacity covering it — the same
|
|
191
191
|
// independence the grid won earlier. The branch is on a uniform, so the
|
|
192
192
|
// whole cost vanishes for the scenes that leave it off.
|
|
193
193
|
var reflShadowed = vec3f(0.0);
|
|
@@ -250,28 +250,18 @@ ${sceneFsOutWgsl()}@fragment fn fs(i: VO) -> FSOut {
|
|
|
250
250
|
// and a catcher stacked on top would darken the same shadow twice.
|
|
251
251
|
let catchA = dark * 0.65 * edgeFade * (1.0 - material.opacity) * (1.0 - material.mirror);
|
|
252
252
|
|
|
253
|
-
// FOUR LAYERS, premultiplied, bottom to top: received shadow,
|
|
254
|
-
// surface,
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
//
|
|
263
|
-
// thing it is made of happens to be.
|
|
264
|
-
//
|
|
265
|
-
// The catcher's order against it no longer matters — catchA already carries
|
|
266
|
-
// (1 - mirror), so the two are never both present.
|
|
267
|
-
var pm = vec3f(0.0);
|
|
268
|
-
var cov = catchA;
|
|
269
|
-
// Surface over the received shadow.
|
|
253
|
+
// FOUR LAYERS, premultiplied, bottom to top: reflection, received shadow,
|
|
254
|
+
// ground surface, grid. Each with its own coverage, none scaling another —
|
|
255
|
+
// ground opacity REVEALS the mirror rather than the mirror carrying a
|
|
256
|
+
// strength of its own.
|
|
257
|
+
var pm = reflShadowed * mirrA;
|
|
258
|
+
var cov = mirrA;
|
|
259
|
+
// Shadow catcher over the reflection-or-nothing: black, it covers.
|
|
260
|
+
pm = pm * (1.0 - catchA);
|
|
261
|
+
cov = catchA + cov * (1.0 - catchA);
|
|
262
|
+
// Surface over shadow.
|
|
270
263
|
pm = baseColor * surfA + pm * (1.0 - surfA);
|
|
271
264
|
cov = surfA + cov * (1.0 - surfA);
|
|
272
|
-
// The reflection over the surface, at full strength whatever the opacity.
|
|
273
|
-
pm = reflShadowed * mirrA + pm * (1.0 - mirrA);
|
|
274
|
-
cov = mirrA + cov * (1.0 - mirrA);
|
|
275
265
|
// Grid over surface. Unshadowed, as it has always been — a painted line reads
|
|
276
266
|
// as paint, and darkening it was never what the mix did either.
|
|
277
267
|
pm = material.gridLineColor * gridA + pm * (1.0 - gridA);
|
|
@@ -1,147 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|