reze-engine 0.54.16 → 0.55.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/dist/engine.d.ts +210 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1063 -11
- package/dist/graph/presets/pool_floor.d.ts +3 -0
- package/dist/graph/presets/pool_floor.d.ts.map +1 -0
- package/dist/graph/presets/pool_floor.js +42 -0
- package/dist/graph/presets/water.d.ts +3 -0
- package/dist/graph/presets/water.d.ts.map +1 -0
- package/dist/graph/presets/water.js +31 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -0
- package/dist/overlay.d.ts +200 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +806 -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/pmx-document.d.ts +218 -0
- package/dist/pmx-document.d.ts.map +1 -0
- package/dist/pmx-document.js +685 -0
- package/dist/shaders/passes/field-blit.d.ts +26 -0
- package/dist/shaders/passes/field-blit.d.ts.map +1 -0
- package/dist/shaders/passes/field-blit.js +65 -0
- package/dist/shaders/passes/ground-noise.d.ts +7 -0
- package/dist/shaders/passes/ground-noise.d.ts.map +1 -0
- package/dist/shaders/passes/ground-noise.js +88 -0
- package/dist/shaders/passes/overlay.d.ts +3 -0
- package/dist/shaders/passes/overlay.d.ts.map +1 -0
- package/dist/shaders/passes/overlay.js +164 -0
- package/dist/shaders/passes/sim.d.ts +34 -0
- package/dist/shaders/passes/sim.d.ts.map +1 -0
- package/dist/shaders/passes/sim.js +169 -0
- package/dist/shaders/passes/wireframe.d.ts +2 -0
- package/dist/shaders/passes/wireframe.d.ts.map +1 -0
- package/dist/shaders/passes/wireframe.js +107 -0
- package/dist/shaders/score-api.d.ts +10 -0
- package/dist/shaders/score-api.d.ts.map +1 -0
- package/dist/shaders/score-api.js +114 -0
- package/package.json +1 -1
- package/src/engine.ts +1147 -13
- package/src/index.ts +50 -0
- package/src/model.ts +9 -0
- package/src/overlay.ts +1000 -0
- package/src/pmx-document.ts +882 -0
- package/src/shaders/passes/overlay.ts +166 -0
- package/src/shaders/passes/wireframe.ts +108 -0
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
// The PMX file, as data, losslessly.
|
|
2
|
+
//
|
|
3
|
+
// SEPARATE from Model on purpose. Model is what the renderer needs and drops
|
|
4
|
+
// everything else — English names, memos, display frames, additional UVs, SDEF
|
|
5
|
+
// and QDEF weights, per-vertex edge scale. That is correct for drawing: making
|
|
6
|
+
// Model lossless would make every viewer pay per-vertex costs for a scene it
|
|
7
|
+
// never edits. It is fatal for editing. A writer fed from Model would turn
|
|
8
|
+
// "rename a bone and save" into "rename a bone and lose every morph's panel
|
|
9
|
+
// assignment", and the user would rightly call that a broken editor.
|
|
10
|
+
//
|
|
11
|
+
// So this reads the file into a structure that mirrors it one for one, and
|
|
12
|
+
// writes that structure back. Read and write live in ONE module and each section
|
|
13
|
+
// keeps its reader and writer adjacent, because the only real failure mode here
|
|
14
|
+
// is the two drifting apart in field order — which the format cannot detect and
|
|
15
|
+
// which produces a file that loads as garbage.
|
|
16
|
+
//
|
|
17
|
+
// Round-tripping is checked byte-for-byte against real models in
|
|
18
|
+
// tests/pmx-roundtrip.test.mjs. That test is the product promise in executable
|
|
19
|
+
// form, so it is worth more than any of the code below.
|
|
20
|
+
// ──────────────────────────────────────────────────────────────────
|
|
21
|
+
// Bytes
|
|
22
|
+
class Cursor {
|
|
23
|
+
constructor(buffer) {
|
|
24
|
+
this.at = 0;
|
|
25
|
+
this.view = new DataView(buffer);
|
|
26
|
+
this.bytes = new Uint8Array(buffer);
|
|
27
|
+
}
|
|
28
|
+
u8() {
|
|
29
|
+
return this.view.getUint8(this.at++);
|
|
30
|
+
}
|
|
31
|
+
u16() {
|
|
32
|
+
const v = this.view.getUint16(this.at, true);
|
|
33
|
+
this.at += 2;
|
|
34
|
+
return v;
|
|
35
|
+
}
|
|
36
|
+
i32() {
|
|
37
|
+
const v = this.view.getInt32(this.at, true);
|
|
38
|
+
this.at += 4;
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
41
|
+
f32() {
|
|
42
|
+
const v = this.view.getFloat32(this.at, true);
|
|
43
|
+
this.at += 4;
|
|
44
|
+
return v;
|
|
45
|
+
}
|
|
46
|
+
vec2() {
|
|
47
|
+
return [this.f32(), this.f32()];
|
|
48
|
+
}
|
|
49
|
+
vec3() {
|
|
50
|
+
return [this.f32(), this.f32(), this.f32()];
|
|
51
|
+
}
|
|
52
|
+
vec4() {
|
|
53
|
+
return [this.f32(), this.f32(), this.f32(), this.f32()];
|
|
54
|
+
}
|
|
55
|
+
/** Unsigned — vertex indices only. */
|
|
56
|
+
uindex(size) {
|
|
57
|
+
if (size === 1)
|
|
58
|
+
return this.u8();
|
|
59
|
+
if (size === 2)
|
|
60
|
+
return this.u16();
|
|
61
|
+
return this.view.getUint32((this.at += 4) - 4, true);
|
|
62
|
+
}
|
|
63
|
+
/** Signed, with the all-ones pattern meaning "none". Every index but vertex. */
|
|
64
|
+
index(size) {
|
|
65
|
+
if (size === 1)
|
|
66
|
+
return this.view.getInt8(this.at++);
|
|
67
|
+
if (size === 2) {
|
|
68
|
+
const v = this.view.getInt16(this.at, true);
|
|
69
|
+
this.at += 2;
|
|
70
|
+
return v;
|
|
71
|
+
}
|
|
72
|
+
return this.i32();
|
|
73
|
+
}
|
|
74
|
+
text(encoding) {
|
|
75
|
+
const length = this.i32();
|
|
76
|
+
const slice = this.bytes.subarray(this.at, this.at + length);
|
|
77
|
+
this.at += length;
|
|
78
|
+
if (encoding === 1)
|
|
79
|
+
return new TextDecoder("utf-8").decode(slice);
|
|
80
|
+
return new TextDecoder("utf-16le").decode(slice);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class Sink {
|
|
84
|
+
constructor() {
|
|
85
|
+
this.chunks = [];
|
|
86
|
+
this.buf = new Uint8Array(65536);
|
|
87
|
+
this.view = new DataView(this.buf.buffer);
|
|
88
|
+
this.at = 0;
|
|
89
|
+
}
|
|
90
|
+
need(n) {
|
|
91
|
+
if (this.at + n <= this.buf.length)
|
|
92
|
+
return;
|
|
93
|
+
this.chunks.push(this.buf.subarray(0, this.at));
|
|
94
|
+
this.buf = new Uint8Array(Math.max(65536, n));
|
|
95
|
+
this.view = new DataView(this.buf.buffer);
|
|
96
|
+
this.at = 0;
|
|
97
|
+
}
|
|
98
|
+
u8(v) {
|
|
99
|
+
this.need(1);
|
|
100
|
+
this.view.setUint8(this.at++, v);
|
|
101
|
+
}
|
|
102
|
+
u16(v) {
|
|
103
|
+
this.need(2);
|
|
104
|
+
this.view.setUint16(this.at, v, true);
|
|
105
|
+
this.at += 2;
|
|
106
|
+
}
|
|
107
|
+
i32(v) {
|
|
108
|
+
this.need(4);
|
|
109
|
+
this.view.setInt32(this.at, v, true);
|
|
110
|
+
this.at += 4;
|
|
111
|
+
}
|
|
112
|
+
f32(v) {
|
|
113
|
+
this.need(4);
|
|
114
|
+
this.view.setFloat32(this.at, v, true);
|
|
115
|
+
this.at += 4;
|
|
116
|
+
}
|
|
117
|
+
vec(v) {
|
|
118
|
+
for (const n of v)
|
|
119
|
+
this.f32(n);
|
|
120
|
+
}
|
|
121
|
+
uindex(size, v) {
|
|
122
|
+
if (size === 1)
|
|
123
|
+
this.u8(v);
|
|
124
|
+
else if (size === 2)
|
|
125
|
+
this.u16(v);
|
|
126
|
+
else
|
|
127
|
+
this.i32(v | 0);
|
|
128
|
+
}
|
|
129
|
+
index(size, v) {
|
|
130
|
+
if (size === 1)
|
|
131
|
+
this.u8(v & 0xff);
|
|
132
|
+
else if (size === 2)
|
|
133
|
+
this.u16(v & 0xffff);
|
|
134
|
+
else
|
|
135
|
+
this.i32(v);
|
|
136
|
+
}
|
|
137
|
+
raw(v) {
|
|
138
|
+
this.need(v.length);
|
|
139
|
+
this.buf.set(v, this.at);
|
|
140
|
+
this.at += v.length;
|
|
141
|
+
}
|
|
142
|
+
text(encoding, v) {
|
|
143
|
+
const bytes = encoding === 1 ? new TextEncoder().encode(v) : utf16le(v);
|
|
144
|
+
this.i32(bytes.length);
|
|
145
|
+
this.raw(bytes);
|
|
146
|
+
}
|
|
147
|
+
finish() {
|
|
148
|
+
this.chunks.push(this.buf.subarray(0, this.at));
|
|
149
|
+
let total = 0;
|
|
150
|
+
for (const c of this.chunks)
|
|
151
|
+
total += c.length;
|
|
152
|
+
const out = new Uint8Array(total);
|
|
153
|
+
let at = 0;
|
|
154
|
+
for (const c of this.chunks) {
|
|
155
|
+
out.set(c, at);
|
|
156
|
+
at += c.length;
|
|
157
|
+
}
|
|
158
|
+
return out.buffer;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** TextEncoder has no utf-16le, so the surrogate pairs are written by hand. */
|
|
162
|
+
function utf16le(s) {
|
|
163
|
+
const out = new Uint8Array(s.length * 2);
|
|
164
|
+
const view = new DataView(out.buffer);
|
|
165
|
+
for (let i = 0; i < s.length; i++)
|
|
166
|
+
view.setUint16(i * 2, s.charCodeAt(i), true);
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
// ──────────────────────────────────────────────────────────────────
|
|
170
|
+
// Read and write, one section at a time, each pair adjacent
|
|
171
|
+
/** Parses a PMX file into a document that writes back byte for byte. */
|
|
172
|
+
export function readPmxDocument(buffer) {
|
|
173
|
+
const c = new Cursor(buffer);
|
|
174
|
+
// Signature. "Pmx " appears in the wild alongside "PMX ", and PMD is the older
|
|
175
|
+
// format entirely — say which it was rather than "not a PMX file".
|
|
176
|
+
const sig = String.fromCharCode(c.u8(), c.u8(), c.u8(), c.u8());
|
|
177
|
+
if (sig.slice(0, 3).toUpperCase() !== "PMX") {
|
|
178
|
+
if (sig.slice(0, 3).toUpperCase() === "PMD") {
|
|
179
|
+
throw new Error("This is a PMD file (MMD's older format). Convert it to PMX in PMXEditor first.");
|
|
180
|
+
}
|
|
181
|
+
throw new Error(`Not a PMX file (signature "${sig.replace(/[^\x20-\x7e]/g, "?")}")`);
|
|
182
|
+
}
|
|
183
|
+
const version = c.f32();
|
|
184
|
+
const globalsCount = c.u8();
|
|
185
|
+
if (globalsCount < 8)
|
|
186
|
+
throw new Error(`PMX header declares ${globalsCount} globals, expected at least 8`);
|
|
187
|
+
const globals = {
|
|
188
|
+
encoding: c.u8(),
|
|
189
|
+
additionalUvCount: c.u8(),
|
|
190
|
+
vertexIndexSize: c.u8(),
|
|
191
|
+
textureIndexSize: c.u8(),
|
|
192
|
+
materialIndexSize: c.u8(),
|
|
193
|
+
boneIndexSize: c.u8(),
|
|
194
|
+
morphIndexSize: c.u8(),
|
|
195
|
+
rigidbodyIndexSize: c.u8(),
|
|
196
|
+
extra: [],
|
|
197
|
+
};
|
|
198
|
+
for (let i = 8; i < globalsCount; i++)
|
|
199
|
+
globals.extra.push(c.u8());
|
|
200
|
+
const e = globals.encoding;
|
|
201
|
+
const doc = {
|
|
202
|
+
version,
|
|
203
|
+
globals,
|
|
204
|
+
name: c.text(e),
|
|
205
|
+
nameEn: c.text(e),
|
|
206
|
+
comment: c.text(e),
|
|
207
|
+
commentEn: c.text(e),
|
|
208
|
+
vertices: [],
|
|
209
|
+
indices: new Uint32Array(0),
|
|
210
|
+
textures: [],
|
|
211
|
+
materials: [],
|
|
212
|
+
bones: [],
|
|
213
|
+
morphs: [],
|
|
214
|
+
displayFrames: [],
|
|
215
|
+
rigidbodies: [],
|
|
216
|
+
joints: [],
|
|
217
|
+
trailing: null,
|
|
218
|
+
};
|
|
219
|
+
// ── Vertices ──
|
|
220
|
+
const vertexCount = c.i32();
|
|
221
|
+
doc.vertices.length = vertexCount;
|
|
222
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
223
|
+
const v = {
|
|
224
|
+
position: c.vec3(),
|
|
225
|
+
normal: c.vec3(),
|
|
226
|
+
uv: c.vec2(),
|
|
227
|
+
additionalUv: [],
|
|
228
|
+
weightType: 0,
|
|
229
|
+
bones: [],
|
|
230
|
+
weights: [],
|
|
231
|
+
edgeScale: 0,
|
|
232
|
+
};
|
|
233
|
+
for (let k = 0; k < globals.additionalUvCount; k++)
|
|
234
|
+
v.additionalUv.push(c.vec4());
|
|
235
|
+
v.weightType = c.u8();
|
|
236
|
+
const bi = globals.boneIndexSize;
|
|
237
|
+
switch (v.weightType) {
|
|
238
|
+
case 0:
|
|
239
|
+
v.bones = [c.index(bi)];
|
|
240
|
+
break;
|
|
241
|
+
case 1:
|
|
242
|
+
v.bones = [c.index(bi), c.index(bi)];
|
|
243
|
+
v.weights = [c.f32()];
|
|
244
|
+
break;
|
|
245
|
+
case 2:
|
|
246
|
+
case 4:
|
|
247
|
+
v.bones = [c.index(bi), c.index(bi), c.index(bi), c.index(bi)];
|
|
248
|
+
v.weights = [c.f32(), c.f32(), c.f32(), c.f32()];
|
|
249
|
+
break;
|
|
250
|
+
case 3:
|
|
251
|
+
v.bones = [c.index(bi), c.index(bi)];
|
|
252
|
+
v.weights = [c.f32()];
|
|
253
|
+
v.sdef = { c: c.vec3(), r0: c.vec3(), r1: c.vec3() };
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
v.edgeScale = c.f32();
|
|
257
|
+
doc.vertices[i] = v;
|
|
258
|
+
}
|
|
259
|
+
// ── Faces ──
|
|
260
|
+
const indexCount = c.i32();
|
|
261
|
+
const indices = new Uint32Array(indexCount);
|
|
262
|
+
for (let i = 0; i < indexCount; i++)
|
|
263
|
+
indices[i] = c.uindex(globals.vertexIndexSize);
|
|
264
|
+
doc.indices = indices;
|
|
265
|
+
// ── Textures ──
|
|
266
|
+
const textureCount = c.i32();
|
|
267
|
+
for (let i = 0; i < textureCount; i++)
|
|
268
|
+
doc.textures.push(c.text(e));
|
|
269
|
+
// ── Materials ──
|
|
270
|
+
const materialCount = c.i32();
|
|
271
|
+
for (let i = 0; i < materialCount; i++) {
|
|
272
|
+
const m = {
|
|
273
|
+
name: c.text(e),
|
|
274
|
+
nameEn: c.text(e),
|
|
275
|
+
diffuse: c.vec4(),
|
|
276
|
+
specular: c.vec3(),
|
|
277
|
+
specularPower: c.f32(),
|
|
278
|
+
ambient: c.vec3(),
|
|
279
|
+
drawFlags: c.u8(),
|
|
280
|
+
edgeColor: c.vec4(),
|
|
281
|
+
edgeSize: c.f32(),
|
|
282
|
+
textureIndex: c.index(globals.textureIndexSize),
|
|
283
|
+
sphereIndex: c.index(globals.textureIndexSize),
|
|
284
|
+
sphereMode: c.u8(),
|
|
285
|
+
toonShared: c.u8(),
|
|
286
|
+
toonIndex: 0,
|
|
287
|
+
memo: "",
|
|
288
|
+
indexCount: 0,
|
|
289
|
+
};
|
|
290
|
+
m.toonIndex = m.toonShared === 1 ? c.u8() : c.index(globals.textureIndexSize);
|
|
291
|
+
m.memo = c.text(e);
|
|
292
|
+
m.indexCount = c.i32();
|
|
293
|
+
doc.materials.push(m);
|
|
294
|
+
}
|
|
295
|
+
// ── Bones ──
|
|
296
|
+
const boneCount = c.i32();
|
|
297
|
+
const bi = globals.boneIndexSize;
|
|
298
|
+
for (let i = 0; i < boneCount; i++) {
|
|
299
|
+
const b = {
|
|
300
|
+
name: c.text(e),
|
|
301
|
+
nameEn: c.text(e),
|
|
302
|
+
position: c.vec3(),
|
|
303
|
+
parentIndex: c.index(bi),
|
|
304
|
+
layer: c.i32(),
|
|
305
|
+
flags: c.u16(),
|
|
306
|
+
};
|
|
307
|
+
if (b.flags & 0x0001)
|
|
308
|
+
b.tailBoneIndex = c.index(bi);
|
|
309
|
+
else
|
|
310
|
+
b.tailPosition = c.vec3();
|
|
311
|
+
if (b.flags & 0x0300) {
|
|
312
|
+
b.appendParentIndex = c.index(bi);
|
|
313
|
+
b.appendRatio = c.f32();
|
|
314
|
+
}
|
|
315
|
+
if (b.flags & 0x0400)
|
|
316
|
+
b.fixedAxis = c.vec3();
|
|
317
|
+
if (b.flags & 0x0800) {
|
|
318
|
+
b.localAxisX = c.vec3();
|
|
319
|
+
b.localAxisZ = c.vec3();
|
|
320
|
+
}
|
|
321
|
+
if (b.flags & 0x2000)
|
|
322
|
+
b.externalKey = c.i32();
|
|
323
|
+
if (b.flags & 0x0020) {
|
|
324
|
+
const targetIndex = c.index(bi);
|
|
325
|
+
const loopCount = c.i32();
|
|
326
|
+
const limitAngle = c.f32();
|
|
327
|
+
const linkCount = c.i32();
|
|
328
|
+
const links = [];
|
|
329
|
+
for (let k = 0; k < linkCount; k++) {
|
|
330
|
+
const boneIndex = c.index(bi);
|
|
331
|
+
const hasLimit = c.u8() === 1;
|
|
332
|
+
links.push(hasLimit ? { boneIndex, hasLimit, limitMin: c.vec3(), limitMax: c.vec3() } : { boneIndex, hasLimit });
|
|
333
|
+
}
|
|
334
|
+
b.ik = { targetIndex, loopCount, limitAngle, links };
|
|
335
|
+
}
|
|
336
|
+
doc.bones.push(b);
|
|
337
|
+
}
|
|
338
|
+
// ── Morphs ──
|
|
339
|
+
const morphCount = c.i32();
|
|
340
|
+
for (let i = 0; i < morphCount; i++) {
|
|
341
|
+
const name = c.text(e);
|
|
342
|
+
const nameEn = c.text(e);
|
|
343
|
+
const panel = c.u8();
|
|
344
|
+
const type = c.u8();
|
|
345
|
+
const offsetCount = c.i32();
|
|
346
|
+
const offsets = [];
|
|
347
|
+
for (let k = 0; k < offsetCount; k++) {
|
|
348
|
+
switch (type) {
|
|
349
|
+
case 0:
|
|
350
|
+
offsets.push({ kind: "group", index: c.index(globals.morphIndexSize), influence: c.f32() });
|
|
351
|
+
break;
|
|
352
|
+
case 1:
|
|
353
|
+
offsets.push({ kind: "vertex", index: c.uindex(globals.vertexIndexSize), offset: c.vec3() });
|
|
354
|
+
break;
|
|
355
|
+
case 2:
|
|
356
|
+
offsets.push({ kind: "bone", index: c.index(bi), translation: c.vec3(), rotation: c.vec4() });
|
|
357
|
+
break;
|
|
358
|
+
case 3:
|
|
359
|
+
case 4:
|
|
360
|
+
case 5:
|
|
361
|
+
case 6:
|
|
362
|
+
case 7:
|
|
363
|
+
offsets.push({ kind: "uv", index: c.uindex(globals.vertexIndexSize), offset: c.vec4() });
|
|
364
|
+
break;
|
|
365
|
+
case 8:
|
|
366
|
+
offsets.push({
|
|
367
|
+
kind: "material",
|
|
368
|
+
index: c.index(globals.materialIndexSize),
|
|
369
|
+
operation: c.u8(),
|
|
370
|
+
diffuse: c.vec4(),
|
|
371
|
+
specular: c.vec3(),
|
|
372
|
+
specularPower: c.f32(),
|
|
373
|
+
ambient: c.vec3(),
|
|
374
|
+
edgeColor: c.vec4(),
|
|
375
|
+
edgeSize: c.f32(),
|
|
376
|
+
textureTint: c.vec4(),
|
|
377
|
+
sphereTint: c.vec4(),
|
|
378
|
+
toonTint: c.vec4(),
|
|
379
|
+
});
|
|
380
|
+
break;
|
|
381
|
+
case 9:
|
|
382
|
+
offsets.push({ kind: "flip", index: c.index(globals.morphIndexSize), influence: c.f32() });
|
|
383
|
+
break;
|
|
384
|
+
case 10:
|
|
385
|
+
offsets.push({
|
|
386
|
+
kind: "impulse",
|
|
387
|
+
index: c.index(globals.rigidbodyIndexSize),
|
|
388
|
+
local: c.u8(),
|
|
389
|
+
velocity: c.vec3(),
|
|
390
|
+
torque: c.vec3(),
|
|
391
|
+
});
|
|
392
|
+
break;
|
|
393
|
+
default:
|
|
394
|
+
throw new Error(`Morph "${name}" has unknown type ${type}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
doc.morphs.push({ name, nameEn, panel, type, offsets });
|
|
398
|
+
}
|
|
399
|
+
// ── Display frames ──
|
|
400
|
+
const frameCount = c.i32();
|
|
401
|
+
for (let i = 0; i < frameCount; i++) {
|
|
402
|
+
const name = c.text(e);
|
|
403
|
+
const nameEn = c.text(e);
|
|
404
|
+
const special = c.u8();
|
|
405
|
+
const elementCount = c.i32();
|
|
406
|
+
const elements = [];
|
|
407
|
+
for (let k = 0; k < elementCount; k++) {
|
|
408
|
+
const type = c.u8();
|
|
409
|
+
elements.push({ type, index: c.index(type === 1 ? globals.morphIndexSize : bi) });
|
|
410
|
+
}
|
|
411
|
+
doc.displayFrames.push({ name, nameEn, special, elements });
|
|
412
|
+
}
|
|
413
|
+
// ── Rigidbodies ──
|
|
414
|
+
const bodyCount = c.i32();
|
|
415
|
+
for (let i = 0; i < bodyCount; i++) {
|
|
416
|
+
doc.rigidbodies.push({
|
|
417
|
+
name: c.text(e),
|
|
418
|
+
nameEn: c.text(e),
|
|
419
|
+
boneIndex: c.index(bi),
|
|
420
|
+
group: c.u8(),
|
|
421
|
+
nonCollideMask: c.u16(),
|
|
422
|
+
shape: c.u8(),
|
|
423
|
+
size: c.vec3(),
|
|
424
|
+
position: c.vec3(),
|
|
425
|
+
rotation: c.vec3(),
|
|
426
|
+
mass: c.f32(),
|
|
427
|
+
linearDamping: c.f32(),
|
|
428
|
+
angularDamping: c.f32(),
|
|
429
|
+
restitution: c.f32(),
|
|
430
|
+
friction: c.f32(),
|
|
431
|
+
physicsMode: c.u8(),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
// ── Joints ──
|
|
435
|
+
const jointCount = c.i32();
|
|
436
|
+
const ri = globals.rigidbodyIndexSize;
|
|
437
|
+
for (let i = 0; i < jointCount; i++) {
|
|
438
|
+
doc.joints.push({
|
|
439
|
+
name: c.text(e),
|
|
440
|
+
nameEn: c.text(e),
|
|
441
|
+
type: c.u8(),
|
|
442
|
+
rigidbodyA: c.index(ri),
|
|
443
|
+
rigidbodyB: c.index(ri),
|
|
444
|
+
position: c.vec3(),
|
|
445
|
+
rotation: c.vec3(),
|
|
446
|
+
positionMin: c.vec3(),
|
|
447
|
+
positionMax: c.vec3(),
|
|
448
|
+
rotationMin: c.vec3(),
|
|
449
|
+
rotationMax: c.vec3(),
|
|
450
|
+
springPosition: c.vec3(),
|
|
451
|
+
springRotation: c.vec3(),
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
if (c.at < c.bytes.length)
|
|
455
|
+
doc.trailing = c.bytes.slice(c.at);
|
|
456
|
+
return doc;
|
|
457
|
+
}
|
|
458
|
+
/** Writes a document back to PMX bytes. */
|
|
459
|
+
export function writePmxDocument(doc) {
|
|
460
|
+
const w = new Sink();
|
|
461
|
+
const g = doc.globals;
|
|
462
|
+
const e = g.encoding;
|
|
463
|
+
const bi = g.boneIndexSize;
|
|
464
|
+
for (const ch of "PMX ")
|
|
465
|
+
w.u8(ch.charCodeAt(0));
|
|
466
|
+
w.f32(doc.version);
|
|
467
|
+
w.u8(8 + g.extra.length);
|
|
468
|
+
w.u8(g.encoding);
|
|
469
|
+
w.u8(g.additionalUvCount);
|
|
470
|
+
w.u8(g.vertexIndexSize);
|
|
471
|
+
w.u8(g.textureIndexSize);
|
|
472
|
+
w.u8(g.materialIndexSize);
|
|
473
|
+
w.u8(g.boneIndexSize);
|
|
474
|
+
w.u8(g.morphIndexSize);
|
|
475
|
+
w.u8(g.rigidbodyIndexSize);
|
|
476
|
+
for (const x of g.extra)
|
|
477
|
+
w.u8(x);
|
|
478
|
+
w.text(e, doc.name);
|
|
479
|
+
w.text(e, doc.nameEn);
|
|
480
|
+
w.text(e, doc.comment);
|
|
481
|
+
w.text(e, doc.commentEn);
|
|
482
|
+
w.i32(doc.vertices.length);
|
|
483
|
+
for (const v of doc.vertices) {
|
|
484
|
+
w.vec(v.position);
|
|
485
|
+
w.vec(v.normal);
|
|
486
|
+
w.vec(v.uv);
|
|
487
|
+
for (let k = 0; k < g.additionalUvCount; k++)
|
|
488
|
+
w.vec(v.additionalUv[k] ?? [0, 0, 0, 0]);
|
|
489
|
+
w.u8(v.weightType);
|
|
490
|
+
switch (v.weightType) {
|
|
491
|
+
case 0:
|
|
492
|
+
w.index(bi, v.bones[0]);
|
|
493
|
+
break;
|
|
494
|
+
case 1:
|
|
495
|
+
w.index(bi, v.bones[0]);
|
|
496
|
+
w.index(bi, v.bones[1]);
|
|
497
|
+
w.f32(v.weights[0]);
|
|
498
|
+
break;
|
|
499
|
+
case 2:
|
|
500
|
+
case 4:
|
|
501
|
+
for (let k = 0; k < 4; k++)
|
|
502
|
+
w.index(bi, v.bones[k]);
|
|
503
|
+
for (let k = 0; k < 4; k++)
|
|
504
|
+
w.f32(v.weights[k]);
|
|
505
|
+
break;
|
|
506
|
+
case 3:
|
|
507
|
+
w.index(bi, v.bones[0]);
|
|
508
|
+
w.index(bi, v.bones[1]);
|
|
509
|
+
w.f32(v.weights[0]);
|
|
510
|
+
w.vec(v.sdef.c);
|
|
511
|
+
w.vec(v.sdef.r0);
|
|
512
|
+
w.vec(v.sdef.r1);
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
w.f32(v.edgeScale);
|
|
516
|
+
}
|
|
517
|
+
w.i32(doc.indices.length);
|
|
518
|
+
for (let i = 0; i < doc.indices.length; i++)
|
|
519
|
+
w.uindex(g.vertexIndexSize, doc.indices[i]);
|
|
520
|
+
w.i32(doc.textures.length);
|
|
521
|
+
for (const t of doc.textures)
|
|
522
|
+
w.text(e, t);
|
|
523
|
+
w.i32(doc.materials.length);
|
|
524
|
+
for (const m of doc.materials) {
|
|
525
|
+
w.text(e, m.name);
|
|
526
|
+
w.text(e, m.nameEn);
|
|
527
|
+
w.vec(m.diffuse);
|
|
528
|
+
w.vec(m.specular);
|
|
529
|
+
w.f32(m.specularPower);
|
|
530
|
+
w.vec(m.ambient);
|
|
531
|
+
w.u8(m.drawFlags);
|
|
532
|
+
w.vec(m.edgeColor);
|
|
533
|
+
w.f32(m.edgeSize);
|
|
534
|
+
w.index(g.textureIndexSize, m.textureIndex);
|
|
535
|
+
w.index(g.textureIndexSize, m.sphereIndex);
|
|
536
|
+
w.u8(m.sphereMode);
|
|
537
|
+
w.u8(m.toonShared);
|
|
538
|
+
if (m.toonShared === 1)
|
|
539
|
+
w.u8(m.toonIndex);
|
|
540
|
+
else
|
|
541
|
+
w.index(g.textureIndexSize, m.toonIndex);
|
|
542
|
+
w.text(e, m.memo);
|
|
543
|
+
w.i32(m.indexCount);
|
|
544
|
+
}
|
|
545
|
+
w.i32(doc.bones.length);
|
|
546
|
+
for (const b of doc.bones) {
|
|
547
|
+
w.text(e, b.name);
|
|
548
|
+
w.text(e, b.nameEn);
|
|
549
|
+
w.vec(b.position);
|
|
550
|
+
w.index(bi, b.parentIndex);
|
|
551
|
+
w.i32(b.layer);
|
|
552
|
+
w.u16(b.flags);
|
|
553
|
+
if (b.flags & 0x0001)
|
|
554
|
+
w.index(bi, b.tailBoneIndex);
|
|
555
|
+
else
|
|
556
|
+
w.vec(b.tailPosition);
|
|
557
|
+
if (b.flags & 0x0300) {
|
|
558
|
+
w.index(bi, b.appendParentIndex);
|
|
559
|
+
w.f32(b.appendRatio);
|
|
560
|
+
}
|
|
561
|
+
if (b.flags & 0x0400)
|
|
562
|
+
w.vec(b.fixedAxis);
|
|
563
|
+
if (b.flags & 0x0800) {
|
|
564
|
+
w.vec(b.localAxisX);
|
|
565
|
+
w.vec(b.localAxisZ);
|
|
566
|
+
}
|
|
567
|
+
if (b.flags & 0x2000)
|
|
568
|
+
w.i32(b.externalKey);
|
|
569
|
+
if (b.flags & 0x0020) {
|
|
570
|
+
const ik = b.ik;
|
|
571
|
+
w.index(bi, ik.targetIndex);
|
|
572
|
+
w.i32(ik.loopCount);
|
|
573
|
+
w.f32(ik.limitAngle);
|
|
574
|
+
w.i32(ik.links.length);
|
|
575
|
+
for (const l of ik.links) {
|
|
576
|
+
w.index(bi, l.boneIndex);
|
|
577
|
+
w.u8(l.hasLimit ? 1 : 0);
|
|
578
|
+
if (l.hasLimit) {
|
|
579
|
+
w.vec(l.limitMin);
|
|
580
|
+
w.vec(l.limitMax);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
w.i32(doc.morphs.length);
|
|
586
|
+
for (const m of doc.morphs) {
|
|
587
|
+
w.text(e, m.name);
|
|
588
|
+
w.text(e, m.nameEn);
|
|
589
|
+
w.u8(m.panel);
|
|
590
|
+
w.u8(m.type);
|
|
591
|
+
w.i32(m.offsets.length);
|
|
592
|
+
for (const o of m.offsets) {
|
|
593
|
+
switch (o.kind) {
|
|
594
|
+
case "group":
|
|
595
|
+
w.index(g.morphIndexSize, o.index);
|
|
596
|
+
w.f32(o.influence);
|
|
597
|
+
break;
|
|
598
|
+
case "vertex":
|
|
599
|
+
w.uindex(g.vertexIndexSize, o.index);
|
|
600
|
+
w.vec(o.offset);
|
|
601
|
+
break;
|
|
602
|
+
case "bone":
|
|
603
|
+
w.index(bi, o.index);
|
|
604
|
+
w.vec(o.translation);
|
|
605
|
+
w.vec(o.rotation);
|
|
606
|
+
break;
|
|
607
|
+
case "uv":
|
|
608
|
+
w.uindex(g.vertexIndexSize, o.index);
|
|
609
|
+
w.vec(o.offset);
|
|
610
|
+
break;
|
|
611
|
+
case "material":
|
|
612
|
+
w.index(g.materialIndexSize, o.index);
|
|
613
|
+
w.u8(o.operation);
|
|
614
|
+
w.vec(o.diffuse);
|
|
615
|
+
w.vec(o.specular);
|
|
616
|
+
w.f32(o.specularPower);
|
|
617
|
+
w.vec(o.ambient);
|
|
618
|
+
w.vec(o.edgeColor);
|
|
619
|
+
w.f32(o.edgeSize);
|
|
620
|
+
w.vec(o.textureTint);
|
|
621
|
+
w.vec(o.sphereTint);
|
|
622
|
+
w.vec(o.toonTint);
|
|
623
|
+
break;
|
|
624
|
+
case "flip":
|
|
625
|
+
w.index(g.morphIndexSize, o.index);
|
|
626
|
+
w.f32(o.influence);
|
|
627
|
+
break;
|
|
628
|
+
case "impulse":
|
|
629
|
+
w.index(g.rigidbodyIndexSize, o.index);
|
|
630
|
+
w.u8(o.local);
|
|
631
|
+
w.vec(o.velocity);
|
|
632
|
+
w.vec(o.torque);
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
w.i32(doc.displayFrames.length);
|
|
638
|
+
for (const f of doc.displayFrames) {
|
|
639
|
+
w.text(e, f.name);
|
|
640
|
+
w.text(e, f.nameEn);
|
|
641
|
+
w.u8(f.special);
|
|
642
|
+
w.i32(f.elements.length);
|
|
643
|
+
for (const el of f.elements) {
|
|
644
|
+
w.u8(el.type);
|
|
645
|
+
w.index(el.type === 1 ? g.morphIndexSize : bi, el.index);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
w.i32(doc.rigidbodies.length);
|
|
649
|
+
for (const b of doc.rigidbodies) {
|
|
650
|
+
w.text(e, b.name);
|
|
651
|
+
w.text(e, b.nameEn);
|
|
652
|
+
w.index(bi, b.boneIndex);
|
|
653
|
+
w.u8(b.group);
|
|
654
|
+
w.u16(b.nonCollideMask);
|
|
655
|
+
w.u8(b.shape);
|
|
656
|
+
w.vec(b.size);
|
|
657
|
+
w.vec(b.position);
|
|
658
|
+
w.vec(b.rotation);
|
|
659
|
+
w.f32(b.mass);
|
|
660
|
+
w.f32(b.linearDamping);
|
|
661
|
+
w.f32(b.angularDamping);
|
|
662
|
+
w.f32(b.restitution);
|
|
663
|
+
w.f32(b.friction);
|
|
664
|
+
w.u8(b.physicsMode);
|
|
665
|
+
}
|
|
666
|
+
w.i32(doc.joints.length);
|
|
667
|
+
for (const j of doc.joints) {
|
|
668
|
+
w.text(e, j.name);
|
|
669
|
+
w.text(e, j.nameEn);
|
|
670
|
+
w.u8(j.type);
|
|
671
|
+
w.index(g.rigidbodyIndexSize, j.rigidbodyA);
|
|
672
|
+
w.index(g.rigidbodyIndexSize, j.rigidbodyB);
|
|
673
|
+
w.vec(j.position);
|
|
674
|
+
w.vec(j.rotation);
|
|
675
|
+
w.vec(j.positionMin);
|
|
676
|
+
w.vec(j.positionMax);
|
|
677
|
+
w.vec(j.rotationMin);
|
|
678
|
+
w.vec(j.rotationMax);
|
|
679
|
+
w.vec(j.springPosition);
|
|
680
|
+
w.vec(j.springRotation);
|
|
681
|
+
}
|
|
682
|
+
if (doc.trailing)
|
|
683
|
+
w.raw(doc.trailing);
|
|
684
|
+
return w.finish();
|
|
685
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The field layer, drawn INTO the scene.
|
|
3
|
+
*
|
|
4
|
+
* Field effects render into their own targets at their own resolution — that
|
|
5
|
+
* part was always right. What was wrong was where the result landed: the
|
|
6
|
+
* composite sampled it AFTER tone mapping, which is why a field effect could
|
|
7
|
+
* never bloom, could never light anything, and tone mapped differently from the
|
|
8
|
+
* particle and ribbon mounts drawing the same kind of glow.
|
|
9
|
+
*
|
|
10
|
+
* Two draws inside the scene pass — background before any geometry, foreground
|
|
11
|
+
* after all of it — so the bloom pyramid sees field effects and one tone map
|
|
12
|
+
* covers the whole frame.
|
|
13
|
+
*
|
|
14
|
+
* NO DEPTH, neither written nor tested. A foreground is handed the scene's
|
|
15
|
+
* depth as a parameter and does its own occlusion; testing it here would
|
|
16
|
+
* occlude it a second time, and a background has nothing to test against.
|
|
17
|
+
*
|
|
18
|
+
* COVERAGE IS CLAMPED HERE. Additive field effects SUM their coverage into the
|
|
19
|
+
* layer (see FIELD_LAYER_BLEND_ADDITIVE), which is what stops the composite's
|
|
20
|
+
* unpremultiply from dividing colour by nothing — but N additive effects can
|
|
21
|
+
* sum past 1, and the layer is rgba16float so nothing stopped it on the way in.
|
|
22
|
+
* A coverage above 1 would darken what the layer is drawn over by more than
|
|
23
|
+
* fully covering it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function fieldBlitShaderWgsl(): string;
|
|
26
|
+
//# sourceMappingURL=field-blit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-blit.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/field-blit.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAuC5C"}
|