reze-engine 0.50.6 → 0.50.8
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/_dbg-loader.mjs +1100 -0
- package/dist/engine.d.ts +71 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +114 -1
- package/dist/graph/slots.d.ts.map +1 -1
- package/dist/graph/slots.js +35 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- 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/cast-api.d.ts +1 -1
- package/dist/shaders/cast-api.d.ts.map +1 -1
- package/dist/shaders/cast-api.js +11 -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/common.d.ts +20 -0
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +85 -1
- 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/composite.d.ts +1 -1
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/depth-prepass.d.ts.map +1 -1
- package/dist/shaders/passes/depth-prepass.js +21 -0
- 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/dist/shaders/passes/shadow.d.ts +1 -1
- package/dist/shaders/passes/shadow.d.ts.map +1 -1
- package/dist/shaders/passes/shadow.js +19 -1
- package/dist/vmd-writer.d.ts +11 -6
- package/dist/vmd-writer.d.ts.map +1 -1
- package/dist/vmd-writer.js +35 -17
- package/package.json +1 -1
- package/src/engine.ts +139 -1
- package/src/graph/slots.ts +37 -4
- package/src/index.ts +1 -0
- package/src/shaders/cast-api.ts +11 -0
- package/src/shaders/materials/common.ts +86 -1
- package/src/shaders/passes/depth-prepass.ts +21 -0
- package/src/shaders/passes/shadow.ts +20 -1
- package/src/vmd-writer.ts +36 -16
- 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
|
@@ -69,7 +69,16 @@ struct MaterialUniforms {
|
|
|
69
69
|
// just empty. f32 because the buffer is written as floats; the shader casts.
|
|
70
70
|
materialId: f32,
|
|
71
71
|
objectId: f32,
|
|
72
|
-
|
|
72
|
+
// How much of this material is still THERE: 1 whole, 0 gone. Rides the last
|
|
73
|
+
// of the padding this struct already carried, for the same reason the ids do
|
|
74
|
+
// — the buffer's size and layout are untouched, so the indirect-draw path and
|
|
75
|
+
// every existing pipeline keep working, and a material that never dissolves
|
|
76
|
+
// pays one float it was already paying.
|
|
77
|
+
//
|
|
78
|
+
// A material morph rebuilds this block from its base copy, which is why the
|
|
79
|
+
// engine writes the value into that copy as well as into the live buffer: a
|
|
80
|
+
// face morphing while she dissolves must not come back solid for those frames.
|
|
81
|
+
dissolve: f32,
|
|
73
82
|
};
|
|
74
83
|
|
|
75
84
|
struct VertexOutput {
|
|
@@ -267,6 +276,82 @@ export function commonFsOutWgsl(): string {
|
|
|
267
276
|
return `\n\n${sceneFsOutWgsl()}\n`;
|
|
268
277
|
}
|
|
269
278
|
|
|
279
|
+
// ─── Dissolve ───────────────────────────────────────────────────────
|
|
280
|
+
/**
|
|
281
|
+
* Whether a fragment survives the dissolve, and how close it is to the front.
|
|
282
|
+
*
|
|
283
|
+
* ONE implementation, shared by the colour pass and the depth prepass, and that
|
|
284
|
+
* is the whole reason it lives here rather than in either of them. The prepass
|
|
285
|
+
* claims depth for fragments the colour pass will shade; if the two disagreed
|
|
286
|
+
* about which flakes are gone, the model would keep writing depth where it no
|
|
287
|
+
* longer draws — holes that occlude the floor behind her and read as sky.
|
|
288
|
+
*
|
|
289
|
+
* OBJECT SPACE, off the bind-pose position, for the same reason the procedural
|
|
290
|
+
* texture nodes use restPos: a world-space field would let the flakes swim
|
|
291
|
+
* through her as she moves, and a screen-space one would leave them hanging in
|
|
292
|
+
* the air while she turns. In object space the pattern is painted ON the
|
|
293
|
+
* surface — the flakes stay where they were on her arm as the arm moves.
|
|
294
|
+
*
|
|
295
|
+
* The hash is value noise rather than a bare hash so the flakes come apart in
|
|
296
|
+
* clumps of a few millimetres rather than as single-pixel snow, which at any
|
|
297
|
+
* distance is just a fade.
|
|
298
|
+
*/
|
|
299
|
+
export const DISSOLVE_WGSL = /* wgsl */ `
|
|
300
|
+
/** Flake size, in object-space units — MMD's are roughly centimetres. */
|
|
301
|
+
const RZ_DISSOLVE_GRAIN: f32 = 0.55;
|
|
302
|
+
/** How wide the glowing front is, in threshold units. */
|
|
303
|
+
const RZ_DISSOLVE_EDGE: f32 = 0.16;
|
|
304
|
+
/** What the front burns with. Emission, added after lighting and after the
|
|
305
|
+
* graph — a colour that ramps or takes a shadow reads as paint, not as heat.
|
|
306
|
+
* Above 1 on purpose: it is meant to reach the bloom pyramid. */
|
|
307
|
+
const RZ_BURN_COLOR: vec3f = vec3f(0.55, 1.85, 2.60);
|
|
308
|
+
|
|
309
|
+
fn rz_dissolve_hash(p: vec3f) -> f32 {
|
|
310
|
+
let q = fract(p * 0.3183099 + vec3f(0.1, 0.2, 0.3));
|
|
311
|
+
let r = q * 17.0 * (q + 34.0);
|
|
312
|
+
return fract(r.x * r.y * r.z);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Value noise over that hash — smooth within a flake, uncorrelated between. */
|
|
316
|
+
fn rz_dissolve_field(p: vec3f) -> f32 {
|
|
317
|
+
let i = floor(p);
|
|
318
|
+
let f = fract(p);
|
|
319
|
+
let u = f * f * (3.0 - 2.0 * f);
|
|
320
|
+
let c000 = rz_dissolve_hash(i);
|
|
321
|
+
let c100 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 0.0));
|
|
322
|
+
let c010 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 0.0));
|
|
323
|
+
let c110 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 0.0));
|
|
324
|
+
let c001 = rz_dissolve_hash(i + vec3f(0.0, 0.0, 1.0));
|
|
325
|
+
let c101 = rz_dissolve_hash(i + vec3f(1.0, 0.0, 1.0));
|
|
326
|
+
let c011 = rz_dissolve_hash(i + vec3f(0.0, 1.0, 1.0));
|
|
327
|
+
let c111 = rz_dissolve_hash(i + vec3f(1.0, 1.0, 1.0));
|
|
328
|
+
let x00 = mix(c000, c100, u.x);
|
|
329
|
+
let x10 = mix(c010, c110, u.x);
|
|
330
|
+
let x01 = mix(c001, c101, u.x);
|
|
331
|
+
let x11 = mix(c011, c111, u.x);
|
|
332
|
+
return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* The threshold this fragment is measured against.
|
|
337
|
+
*
|
|
338
|
+
* Tilted by HEIGHT so the dissolve sweeps up the body instead of arriving
|
|
339
|
+
* everywhere at once: a body that comes apart from the feet reads as something
|
|
340
|
+
* happening TO her, and one that fades uniformly reads as an opacity slider.
|
|
341
|
+
* The tilt is gentle — a third of the range — so the noise still decides which
|
|
342
|
+
* flake goes when, and the sweep only decides roughly where it is.
|
|
343
|
+
*
|
|
344
|
+
* FEET FIRST, head last: the face is the last thing to go and the first thing
|
|
345
|
+
* back, which is the order every disappearance in film is cut in. Object space
|
|
346
|
+
* puts the floor at y = 0 on any PMX, so the tilt needs no rig measurement.
|
|
347
|
+
*/
|
|
348
|
+
fn rz_dissolve_threshold(restPos: vec3f) -> f32 {
|
|
349
|
+
let n = rz_dissolve_field(restPos / RZ_DISSOLVE_GRAIN);
|
|
350
|
+
let low = 1.0 - clamp(restPos.y * 0.045, 0.0, 1.0);
|
|
351
|
+
return clamp(n * 0.68 + low * 0.32, 0.0, 1.0);
|
|
352
|
+
}
|
|
353
|
+
`
|
|
354
|
+
|
|
270
355
|
// ─── Convenience: full shared prelude ───────────────────────────────
|
|
271
356
|
// Material files compose this as `${NODES_WGSL}${COMMON_MATERIAL_PRELUDE_WGSL}` to
|
|
272
357
|
// pull in everything structural. Each material then adds its own constants + fs().
|
|
@@ -27,14 +27,25 @@
|
|
|
27
27
|
// attachment, and that answer does not exist at import time. The constant could
|
|
28
28
|
// not have taken the outputs at all, which is most of why it did not have them.
|
|
29
29
|
|
|
30
|
+
import { DISSOLVE_WGSL } from "../materials/common"
|
|
30
31
|
import { sceneFsOutWgsl, sceneIdPadWgsl } from "./scene-contract"
|
|
31
32
|
|
|
32
33
|
export function transparentDepthPrepassWgsl(): string {
|
|
33
34
|
return /* wgsl */ `
|
|
35
|
+
${DISSOLVE_WGSL}
|
|
34
36
|
struct CameraUniforms { view: mat4x4f, projection: mat4x4f, viewPos: vec3f, _p: f32, };
|
|
37
|
+
// The head of the material block, plus the one field at its tail. The middle is
|
|
38
|
+
// skipped rather than named: this pass shades nothing, and every field it
|
|
39
|
+
// declares is a field that must stay in step with the real struct for no gain.
|
|
40
|
+
// The padding is explicit so the offsets are checkable by eye — vec4 at 16 and
|
|
41
|
+
// 32, vec3 at 48, and dissolve last at 60.
|
|
35
42
|
struct MaterialUniforms {
|
|
36
43
|
diffuseColor: vec3f,
|
|
37
44
|
alpha: f32,
|
|
45
|
+
_skip0: vec4f,
|
|
46
|
+
_skip1: vec4f,
|
|
47
|
+
_skip2: vec3f,
|
|
48
|
+
dissolve: f32,
|
|
38
49
|
};
|
|
39
50
|
|
|
40
51
|
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
@@ -48,6 +59,9 @@ struct VSOut {
|
|
|
48
59
|
// must land on exactly the depths this wrote.
|
|
49
60
|
@builtin(position) @invariant position: vec4f,
|
|
50
61
|
@location(0) uv: vec2f,
|
|
62
|
+
// The bind-pose position, carried for one reason: the dissolve test below has
|
|
63
|
+
// to be the SAME test the colour pass runs, and that one is in object space.
|
|
64
|
+
@location(1) restPos: vec3f,
|
|
51
65
|
};
|
|
52
66
|
|
|
53
67
|
@vertex fn vs(
|
|
@@ -68,6 +82,7 @@ struct VSOut {
|
|
|
68
82
|
var o: VSOut;
|
|
69
83
|
o.position = camera.projection * camera.view * vec4f(skinned.xyz, 1.0);
|
|
70
84
|
o.uv = uv;
|
|
85
|
+
o.restPos = position;
|
|
71
86
|
return o;
|
|
72
87
|
}
|
|
73
88
|
|
|
@@ -89,6 +104,12 @@ override CUTOFF: f32 = 0.5;
|
|
|
89
104
|
@fragment fn fs(in: VSOut) -> PrepassOut {
|
|
90
105
|
let a = material.alpha * textureSample(diffuseTexture, diffuseSampler, in.uv).a;
|
|
91
106
|
if (a < CUTOFF) { discard; }
|
|
107
|
+
// The dissolve, run identically to the colour pass — shared code, not a
|
|
108
|
+
// second copy of the same idea. A prepass that kept claiming depth for flakes
|
|
109
|
+
// the colour pass throws away would punch holes that occlude the floor behind
|
|
110
|
+
// her: you would see sky through her, which is the failure this line exists
|
|
111
|
+
// to prevent.
|
|
112
|
+
if (material.dissolve < 0.9995 && rz_dissolve_threshold(in.restPos) > material.dissolve) { discard; }
|
|
92
113
|
var out: PrepassOut;
|
|
93
114
|
out.color = vec4f(0.0);
|
|
94
115
|
out.mask = vec4f(0.0);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DISSOLVE_WGSL } from "../materials/common"
|
|
2
|
+
|
|
1
3
|
// Shadow map depth pass. Skinned VS + alpha-test FS (depth-only attachment, no
|
|
2
4
|
// color targets): texels where diffuse-texture alpha × material alpha fall below
|
|
3
5
|
// the cutoff are discarded, so lace casts lace-shaped shadows and a true veil
|
|
@@ -6,17 +8,29 @@
|
|
|
6
8
|
// bindings 0/1 are declared here (a layout may carry bindings a shader ignores).
|
|
7
9
|
|
|
8
10
|
export const SHADOW_DEPTH_SHADER_WGSL = /* wgsl */ `
|
|
11
|
+
${DISSOLVE_WGSL}
|
|
9
12
|
struct LightVP { viewProj: mat4x4f, };
|
|
10
13
|
@group(0) @binding(0) var<uniform> lp: LightVP;
|
|
11
14
|
@group(0) @binding(1) var<storage, read> skinMats: array<mat4x4f>;
|
|
12
15
|
@group(0) @binding(2) var texSampler: sampler;
|
|
13
16
|
@group(1) @binding(0) var diffuseTexture: texture_2d<f32>;
|
|
14
|
-
|
|
17
|
+
// The head of the material block and the one field at its tail — the same
|
|
18
|
+
// reach the depth prepass takes, and for the same reason: a shadow cast by a
|
|
19
|
+
// body that is no longer drawn is the tell that the vanishing is a trick.
|
|
20
|
+
struct MaterialDiffuse {
|
|
21
|
+
diffuse: vec4f,
|
|
22
|
+
_skip0: vec4f,
|
|
23
|
+
_skip1: vec4f,
|
|
24
|
+
_skip2: vec3f,
|
|
25
|
+
dissolve: f32,
|
|
26
|
+
};
|
|
15
27
|
@group(1) @binding(1) var<uniform> material: MaterialDiffuse;
|
|
16
28
|
|
|
17
29
|
struct VSOut {
|
|
18
30
|
@builtin(position) position: vec4f,
|
|
19
31
|
@location(0) uv: vec2f,
|
|
32
|
+
/** Bind-pose position, for the dissolve test — object space, as everywhere. */
|
|
33
|
+
@location(1) restPos: vec3f,
|
|
20
34
|
};
|
|
21
35
|
|
|
22
36
|
@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f,
|
|
@@ -30,11 +44,16 @@ struct VSOut {
|
|
|
30
44
|
var out: VSOut;
|
|
31
45
|
out.position = lp.viewProj * vec4f(sp.xyz, 1.0);
|
|
32
46
|
out.uv = uv;
|
|
47
|
+
out.restPos = position;
|
|
33
48
|
return out;
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
@fragment fn fs(in: VSOut) {
|
|
37
52
|
let alpha = textureSample(diffuseTexture, texSampler, in.uv).a * material.diffuse.a;
|
|
38
53
|
if (alpha < 0.5) { discard; }
|
|
54
|
+
// The dissolve, run as the colour pass runs it. A flake that is gone stops
|
|
55
|
+
// casting: without this she leaves a whole shadow standing on the floor while
|
|
56
|
+
// her body is in the air.
|
|
57
|
+
if (material.dissolve < 0.9995 && rz_dissolve_threshold(in.restPos) > material.dissolve) { discard; }
|
|
39
58
|
}
|
|
40
59
|
`
|
package/src/vmd-writer.ts
CHANGED
|
@@ -103,10 +103,17 @@ export class VMDWriter {
|
|
|
103
103
|
MODEL_NAME_SIZE +
|
|
104
104
|
4 + totalBoneFrames * BONE_FRAME_SIZE +
|
|
105
105
|
4 + totalMorphFrames * MORPH_FRAME_SIZE +
|
|
106
|
-
// Camera, light
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
106
|
+
// Camera, light, self-shadow and IK counts, ALWAYS — 16 bytes even when
|
|
107
|
+
// every one of them is zero.
|
|
108
|
+
//
|
|
109
|
+
// A VMD's sections are positional: a reader reaches each block by walking
|
|
110
|
+
// every count before it. Writing these only when there was IK state to
|
|
111
|
+
// carry left an ordinary motion ending right after the morph block, so
|
|
112
|
+
// anything looking for the sections that follow read past the end of the
|
|
113
|
+
// file and took whatever it found. MMD's own motion files carry all five
|
|
114
|
+
// counts unconditionally; so do ours now.
|
|
115
|
+
4 * 4 +
|
|
116
|
+
ikSize
|
|
110
117
|
|
|
111
118
|
const buffer = new ArrayBuffer(size)
|
|
112
119
|
const view = new DataView(buffer)
|
|
@@ -170,12 +177,14 @@ export class VMDWriter {
|
|
|
170
177
|
}
|
|
171
178
|
}
|
|
172
179
|
|
|
180
|
+
// Empty camera, light and self-shadow blocks, then the IK count — written
|
|
181
|
+
// whether or not there is IK state, so the file always ends on a complete
|
|
182
|
+
// section table.
|
|
183
|
+
for (let i = 0; i < 3; i++, offset += 4) view.setUint32(offset, 0, true)
|
|
184
|
+
view.setUint32(offset, ikFrames.length, true)
|
|
185
|
+
offset += 4
|
|
186
|
+
|
|
173
187
|
if (ikFrames.length > 0) {
|
|
174
|
-
// Empty camera, light and self-shadow blocks — a reader walking the file
|
|
175
|
-
// in order has to pass through them to reach the IK block.
|
|
176
|
-
for (let i = 0; i < 3; i++, offset += 4) view.setUint32(offset, 0, true)
|
|
177
|
-
view.setUint32(offset, ikFrames.length, true)
|
|
178
|
-
offset += 4
|
|
179
188
|
for (const [frame, states] of ikFrames) {
|
|
180
189
|
view.setUint32(offset, frame, true)
|
|
181
190
|
offset += 4
|
|
@@ -197,12 +206,17 @@ export class VMDWriter {
|
|
|
197
206
|
/**
|
|
198
207
|
* A camera VMD: the shot's own file, with no model motion in it.
|
|
199
208
|
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* that
|
|
209
|
+
* Every section count is written, including the empty ones. The format is
|
|
210
|
+
* positional, so a reader reaches each block by walking the counts before it —
|
|
211
|
+
* bone and morph to arrive at the camera block, and light, self-shadow and IK
|
|
212
|
+
* to leave it cleanly.
|
|
213
|
+
*
|
|
214
|
+
* The trailing three used to be omitted, on the theory that every reader
|
|
215
|
+
* bounds-checks past the camera block. They do not. This file type is
|
|
216
|
+
* カメラ・照明 — camera AND lighting — so MMD looks for the light block exactly
|
|
217
|
+
* where the file used to stop, read past the end, and lit the scene from
|
|
218
|
+
* whatever it found there. It reached users as a stray coloured light on a
|
|
219
|
+
* camera-only export.
|
|
206
220
|
*
|
|
207
221
|
* `frames` is sorted by frame on the way out: CameraAnimation binary-searches
|
|
208
222
|
* the track it loads, and an out-of-order file would sample wrong rather than
|
|
@@ -210,7 +224,8 @@ export class VMDWriter {
|
|
|
210
224
|
*/
|
|
211
225
|
writeCamera(frames: CameraKeyframe[]): ArrayBuffer {
|
|
212
226
|
const sorted = [...frames].sort((a, b) => a.frame - b.frame)
|
|
213
|
-
const size =
|
|
227
|
+
const size =
|
|
228
|
+
HEADER_SIZE + MODEL_NAME_SIZE + 4 + 4 + 4 + sorted.length * CAMERA_FRAME_SIZE + 4 * 3
|
|
214
229
|
const buffer = new ArrayBuffer(size)
|
|
215
230
|
const view = new DataView(buffer)
|
|
216
231
|
let offset = 0
|
|
@@ -249,6 +264,11 @@ export class VMDWriter {
|
|
|
249
264
|
offset += 1
|
|
250
265
|
}
|
|
251
266
|
|
|
267
|
+
// Light, self-shadow and IK/display counts. See the note above: the light
|
|
268
|
+
// block begins here, and leaving it off is what lit MMD from past the end
|
|
269
|
+
// of the file.
|
|
270
|
+
for (let i = 0; i < 3; i++, offset += 4) view.setUint32(offset, 0, true)
|
|
271
|
+
|
|
252
272
|
return buffer
|
|
253
273
|
}
|
|
254
274
|
}
|
|
@@ -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"}
|