@gmvjs/xray-v1 0.0.0-stage → 0.1.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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -2
  3. package/dist/db.d.ts +12 -0
  4. package/dist/db.js +134 -0
  5. package/dist/db.js.map +1 -0
  6. package/dist/details.d.ts +42 -0
  7. package/dist/details.js +210 -0
  8. package/dist/details.js.map +1 -0
  9. package/dist/export.d.ts +38 -0
  10. package/dist/export.js +241 -0
  11. package/dist/export.js.map +1 -0
  12. package/dist/games/cop.d.ts +7 -0
  13. package/dist/games/cop.js +11 -0
  14. package/dist/games/cop.js.map +1 -0
  15. package/dist/games/cs.d.ts +7 -0
  16. package/dist/games/cs.js +11 -0
  17. package/dist/games/cs.js.map +1 -0
  18. package/dist/games/sdk.d.ts +7 -0
  19. package/dist/games/sdk.js +10 -0
  20. package/dist/games/sdk.js.map +1 -0
  21. package/dist/games/soc.d.ts +9 -0
  22. package/dist/games/soc.js +13 -0
  23. package/dist/games/soc.js.map +1 -0
  24. package/dist/i18n.d.ts +2 -0
  25. package/dist/i18n.js +48 -0
  26. package/dist/i18n.js.map +1 -0
  27. package/dist/index.d.ts +5 -0
  28. package/dist/index.js +131 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/level.d.ts +39 -0
  31. package/dist/level.js +234 -0
  32. package/dist/level.js.map +1 -0
  33. package/dist/load.d.ts +5 -0
  34. package/dist/load.js +126 -0
  35. package/dist/load.js.map +1 -0
  36. package/dist/lzhuf.d.ts +2 -0
  37. package/dist/lzhuf.js +136 -0
  38. package/dist/lzhuf.js.map +1 -0
  39. package/dist/motion.d.ts +28 -0
  40. package/dist/motion.js +20 -0
  41. package/dist/motion.js.map +1 -0
  42. package/dist/object.d.ts +138 -0
  43. package/dist/object.js +361 -0
  44. package/dist/object.js.map +1 -0
  45. package/dist/ogf.d.ts +93 -0
  46. package/dist/ogf.js +418 -0
  47. package/dist/ogf.js.map +1 -0
  48. package/dist/scene.d.ts +28 -0
  49. package/dist/scene.js +141 -0
  50. package/dist/scene.js.map +1 -0
  51. package/dist/skeleton.d.ts +34 -0
  52. package/dist/skeleton.js +252 -0
  53. package/dist/skeleton.js.map +1 -0
  54. package/dist/smotion.d.ts +44 -0
  55. package/dist/smotion.js +283 -0
  56. package/dist/smotion.js.map +1 -0
  57. package/dist/soc-key.d.ts +18 -0
  58. package/dist/soc-key.js +125 -0
  59. package/dist/soc-key.js.map +1 -0
  60. package/dist/thm.d.ts +26 -0
  61. package/dist/thm.js +69 -0
  62. package/dist/thm.js.map +1 -0
  63. package/dist/write-object.d.ts +31 -0
  64. package/dist/write-object.js +228 -0
  65. package/dist/write-object.js.map +1 -0
  66. package/dist/write-omf.d.ts +47 -0
  67. package/dist/write-scene.d.ts +26 -0
  68. package/dist/write-scene.js +114 -0
  69. package/dist/write-scene.js.map +1 -0
  70. package/package.json +70 -4
package/dist/ogf.js ADDED
@@ -0,0 +1,418 @@
1
+ import { XRAY_FX_PART } from "./motion.js";
2
+ import { lzhufDecompress } from "./lzhuf.js";
3
+ const OGF = {
4
+ HEADER: 1,
5
+ TEXTURE: 2,
6
+ VERTICES: 3,
7
+ INDICES: 4,
8
+ CHILDREN: 9,
9
+ S_BONE_NAMES: 13,
10
+ S_MOTIONS: 14,
11
+ S_SMPARAMS: 15,
12
+ S_IKDATA: 16,
13
+ S_USERDATA: 17,
14
+ S_DESC: 18,
15
+ S_MOTION_REFS0: 19,
16
+ S_MOTION_REFS1: 24
17
+ };
18
+ const TYPE_NAMES = [
19
+ "NORMAL",
20
+ "HIERRARHY",
21
+ "PROGRESSIVE",
22
+ "SKELETON_ANIM",
23
+ "SKELETON_GEOMDEF_PM",
24
+ "SKELETON_GEOMDEF_ST",
25
+ "LOD",
26
+ "TREE_ST",
27
+ "PARTICLE_EFFECT",
28
+ "PARTICLE_GROUP",
29
+ "SKELETON_RIGID",
30
+ "TREE_PM"
31
+ ];
32
+ const FPS = 30;
33
+ class Reader {
34
+ constructor(buf, pos, end) {
35
+ this.pos = pos;
36
+ this.end = end;
37
+ this.dv = new DataView(buf);
38
+ this.u8 = new Uint8Array(buf);
39
+ }
40
+ pos;
41
+ end;
42
+ dv;
43
+ u8;
44
+ byte() {
45
+ return this.dv.getUint8(this.pos++);
46
+ }
47
+ u16() {
48
+ const v = this.dv.getUint16(this.pos, true);
49
+ this.pos += 2;
50
+ return v;
51
+ }
52
+ u32() {
53
+ const v = this.dv.getUint32(this.pos, true);
54
+ this.pos += 4;
55
+ return v;
56
+ }
57
+ f32() {
58
+ const v = this.dv.getFloat32(this.pos, true);
59
+ this.pos += 4;
60
+ return v;
61
+ }
62
+ skip(n) {
63
+ this.pos += n;
64
+ }
65
+ get eof() {
66
+ return this.pos >= this.end;
67
+ }
68
+ stringz() {
69
+ let s = "";
70
+ while (this.pos < this.end) {
71
+ const c = this.u8[this.pos++];
72
+ if (!c) break;
73
+ s += String.fromCharCode(c);
74
+ }
75
+ return s;
76
+ }
77
+ }
78
+ function readChunks(buf, start, end) {
79
+ const dv = new DataView(buf), list = [];
80
+ let p = start;
81
+ while (p + 8 <= end) {
82
+ const raw = dv.getUint32(p, true), size = dv.getUint32(p + 4, true);
83
+ if (p + 8 + size > end) break;
84
+ list.push({ id: raw & 2147483647, comp: (raw & 2147483648) !== 0, offset: p + 8, size });
85
+ p += 8 + size;
86
+ }
87
+ return list;
88
+ }
89
+ function readHeader(buf, c) {
90
+ const r = new Reader(buf, c.offset, c.offset + c.size);
91
+ const version = r.byte(), type = r.byte(), shader = r.u16();
92
+ const bmin = [r.f32(), r.f32(), r.f32()], bmax = [r.f32(), r.f32(), r.f32()];
93
+ return { version, type, shader, bmin, bmax };
94
+ }
95
+ function parseGeometry(buf, vtx, idx, tex, nBones) {
96
+ const dv = new DataView(buf);
97
+ const fmt = dv.getUint32(vtx.offset, true), n = dv.getUint32(vtx.offset + 4, true), base = vtx.offset + 8;
98
+ const stride = n ? Math.floor((vtx.size - 8) / n) : 0;
99
+ if (![32, 60, 64, 70, 72, 76].includes(stride)) throw new Error(`Неподдерживаемый формат вершин 0x${fmt.toString(16)} (stride ${stride})`);
100
+ const pos = new Float32Array(n * 3), nor = new Float32Array(n * 3), uv = new Float32Array(n * 2);
101
+ const bone = new Float32Array(n * 4), wt = new Float32Array(n * 4);
102
+ const f = (o) => dv.getFloat32(o, true), h = (o) => dv.getUint16(o, true);
103
+ const cl = (b) => Math.min(b, Math.max(0, nBones - 1));
104
+ for (let i = 0; i < n; i++) {
105
+ const o = base + i * stride;
106
+ let P, N, UV, bi = [0, 0, 0, 0], w = [1, 0, 0, 0];
107
+ if (stride === 32) {
108
+ P = o;
109
+ N = o + 12;
110
+ UV = o + 24;
111
+ } else if (stride === 60) {
112
+ P = o;
113
+ N = o + 12;
114
+ UV = o + 48;
115
+ bi[0] = dv.getUint32(o + 56, true);
116
+ } else if (stride === 64) {
117
+ bi[0] = h(o);
118
+ bi[1] = h(o + 2);
119
+ P = o + 4;
120
+ N = o + 16;
121
+ UV = o + 56;
122
+ const w1 = f(o + 52);
123
+ w = [1 - w1, w1, 0, 0];
124
+ } else if (stride === 70 || stride === 72) {
125
+ bi = [h(o), h(o + 2), h(o + 4), 0];
126
+ P = o + 6;
127
+ N = o + 18;
128
+ UV = o + 62;
129
+ const a = f(o + 54), b = f(o + 58);
130
+ w = [a, b, 1 - a - b, 0];
131
+ } else {
132
+ bi = [h(o), h(o + 2), h(o + 4), h(o + 6)];
133
+ P = o + 8;
134
+ N = o + 20;
135
+ UV = o + 68;
136
+ const a = f(o + 56), b = f(o + 60), c = f(o + 64);
137
+ w = [a, b, c, 1 - a - b - c];
138
+ }
139
+ for (let k = 0; k < 3; k++) {
140
+ pos[i * 3 + k] = f(P + 4 * k);
141
+ nor[i * 3 + k] = f(N + 4 * k);
142
+ }
143
+ uv[i * 2] = f(UV);
144
+ uv[i * 2 + 1] = f(UV + 4);
145
+ for (let k = 0; k < 4; k++) {
146
+ bone[i * 4 + k] = cl(bi[k]);
147
+ wt[i * 4 + k] = w[k];
148
+ }
149
+ }
150
+ const ic = dv.getUint32(idx.offset, true), indices = new Uint16Array(ic);
151
+ for (let i = 0; i < ic; i++) indices[i] = dv.getUint16(idx.offset + 4 + i * 2, true);
152
+ let texture = "", shader = "";
153
+ if (tex) {
154
+ const r = new Reader(buf, tex.offset, tex.offset + tex.size);
155
+ texture = r.stringz();
156
+ shader = r.stringz();
157
+ }
158
+ return { texture, shader, vertexCount: n, pos, nor, uv, bone, weight: wt, indices, skinned: stride !== 32, stride };
159
+ }
160
+ function unpackChunks(buf, chunks) {
161
+ if (!chunks.some((c) => c.comp)) return { buf, chunks };
162
+ const parts = chunks.map((c) => {
163
+ const raw = new Uint8Array(buf, c.offset, c.size);
164
+ return c.comp ? lzhufDecompress(raw) : raw;
165
+ });
166
+ const out = new Uint8Array(parts.reduce((a, p) => a + p.length, 0)), list = [];
167
+ let o = 0;
168
+ parts.forEach((p, i) => {
169
+ out.set(p, o);
170
+ list.push({ id: chunks[i].id, comp: false, offset: o, size: p.length });
171
+ o += p.length;
172
+ });
173
+ return { buf: out.buffer, chunks: list };
174
+ }
175
+ function collectMeshes(buf0, chunks0, out, nBones) {
176
+ const { buf, chunks } = unpackChunks(buf0, chunks0);
177
+ const find = (id) => chunks.find((c) => c.id === id);
178
+ const vtx = find(OGF.VERTICES), idx = find(OGF.INDICES);
179
+ if (vtx && idx) out.push(parseGeometry(buf, vtx, idx, find(OGF.TEXTURE), nBones));
180
+ const ch = find(OGF.CHILDREN);
181
+ if (ch) for (const s of readChunks(buf, ch.offset, ch.offset + ch.size)) collectMeshes(buf, readChunks(buf, s.offset, s.offset + s.size), out, nBones);
182
+ }
183
+ function parseBoneNames(buf, c) {
184
+ const attempt = (withCount) => {
185
+ const r = new Reader(buf, c.offset, c.offset + c.size);
186
+ const count = withCount ? r.u32() : Infinity, out = [];
187
+ while (!r.eof && out.length < count) {
188
+ const name = r.stringz(), parentName = r.stringz();
189
+ r.skip(60);
190
+ out.push({ name, parentName });
191
+ }
192
+ if (r.pos !== r.end || withCount && out.length !== count) return null;
193
+ return out;
194
+ };
195
+ const list = attempt(true) ?? attempt(false);
196
+ if (!list) throw new Error("Не удалось разобрать S_BONE_NAMES");
197
+ const map = new Map(list.map((b, i) => [b.name.toLowerCase(), i]));
198
+ return list.map((b) => ({ name: b.name, parent: b.parentName ? map.get(b.parentName.toLowerCase()) ?? -1 : -1, rot: [0, 0, 0], pos: [0, 0, 0] }));
199
+ }
200
+ function parseIK(buf, c, bones) {
201
+ const attempt = (friction) => {
202
+ const r = new Reader(buf, c.offset, c.offset + c.size), out = [];
203
+ for (let i = 0; i < bones.length; i++) {
204
+ if (r.pos + 4 > r.end) return null;
205
+ r.u32();
206
+ r.stringz();
207
+ r.skip(112 + 4 + 48 + 8 + 12 + (friction ? 4 : 0));
208
+ if (r.pos + 24 > r.end) return null;
209
+ const rot = [r.f32(), r.f32(), r.f32()], pos = [r.f32(), r.f32(), r.f32()];
210
+ r.skip(16);
211
+ out.push({ rot, pos });
212
+ }
213
+ return r.pos === r.end ? out : null;
214
+ };
215
+ const res = attempt(true) ?? attempt(false);
216
+ if (res) res.forEach((d, i) => {
217
+ bones[i].rot = d.rot;
218
+ bones[i].pos = d.pos;
219
+ });
220
+ return !!res;
221
+ }
222
+ function readTracks(buf, start, end, frames, n) {
223
+ const dv = new DataView(buf), tracks = [];
224
+ let p = start;
225
+ for (let i = 0; i < n; i++) {
226
+ if (p + 1 > end) return null;
227
+ const flags = dv.getUint8(p++);
228
+ let rot, trans, constT = false;
229
+ if (flags & 2) {
230
+ if (p + 8 > end) return null;
231
+ rot = new Int16Array(4);
232
+ for (let k = 0; k < 4; k++) rot[k] = dv.getInt16(p + 2 * k, true);
233
+ p += 8;
234
+ } else {
235
+ if (p + 4 + frames * 8 > end) return null;
236
+ p += 4;
237
+ rot = new Int16Array(frames * 4);
238
+ for (let k = 0; k < frames * 4; k++) rot[k] = dv.getInt16(p + 2 * k, true);
239
+ p += frames * 8;
240
+ }
241
+ if (flags & 1) {
242
+ const wide = (flags & 4) !== 0, bytes = frames * 3 * (wide ? 2 : 1);
243
+ if (p + 4 + bytes + 24 > end) return null;
244
+ p += 4;
245
+ const raw = new Float32Array(frames * 3), kp = p;
246
+ for (let k = 0; k < frames * 3; k++) raw[k] = wide ? dv.getInt16(kp + 2 * k, true) : dv.getInt8(kp + k);
247
+ p += bytes;
248
+ const sz = [dv.getFloat32(p, true), dv.getFloat32(p + 4, true), dv.getFloat32(p + 8, true)];
249
+ const ini = [dv.getFloat32(p + 12, true), dv.getFloat32(p + 16, true), dv.getFloat32(p + 20, true)];
250
+ p += 24;
251
+ for (let k = 0; k < frames * 3; k++) raw[k] = raw[k] * sz[k % 3] + ini[k % 3];
252
+ trans = raw;
253
+ } else {
254
+ if (p + 12 > end) return null;
255
+ trans = new Float32Array([dv.getFloat32(p, true), dv.getFloat32(p + 4, true), dv.getFloat32(p + 8, true)]);
256
+ p += 12;
257
+ constT = true;
258
+ }
259
+ tracks.push({ rot, trans, constT });
260
+ }
261
+ return { tracks, p };
262
+ }
263
+ function parseSMParams(buf, c) {
264
+ try {
265
+ const r = new Reader(buf, c.offset, c.offset + c.size), version = r.u16(), nParts = r.u16(), trackBones = [], parts = [];
266
+ for (let p = 0; p < nParts; p++) {
267
+ const part = { name: r.stringz(), bones: [] }, nb = r.u16();
268
+ for (let b = 0; b < nb; b++) {
269
+ const name = r.stringz(), idx = version === 1 ? trackBones.length : r.u32();
270
+ trackBones[idx] = name;
271
+ part.bones.push(name);
272
+ }
273
+ parts.push(part);
274
+ }
275
+ if (r.pos > r.end) return null;
276
+ const defs = [];
277
+ try {
278
+ const n = r.u16();
279
+ for (let k = 0; k < n && r.pos < r.end; k++) {
280
+ const name = r.stringz(), flags = r.u32(), boneOrPart = r.u16(), motion = r.u16(), speed = r.f32();
281
+ r.skip(12);
282
+ if (version >= 4) {
283
+ const nm = r.u32();
284
+ for (let i = 0; i < nm; i++) {
285
+ while (r.pos < r.end && r.byte() !== 10) ;
286
+ r.skip(8 * r.u32());
287
+ }
288
+ }
289
+ defs.push({ name, flags, boneOrPart, motion, speed });
290
+ }
291
+ if (r.pos > r.end) defs.length = 0;
292
+ } catch {
293
+ defs.length = 0;
294
+ }
295
+ return { version, trackBones, parts, defs };
296
+ } catch {
297
+ return null;
298
+ }
299
+ }
300
+ function parseMotion(buf, s, nTracks) {
301
+ const r = new Reader(buf, s.offset, s.offset + s.size);
302
+ const name = r.stringz(), frames = r.u32(), start = r.pos, end = s.offset + s.size;
303
+ if (!frames || frames > 1e5) return null;
304
+ let res = readTracks(buf, start, end, frames, nTracks);
305
+ if (!res || res.p !== end) {
306
+ res = null;
307
+ for (let n = 1; n <= 1024 && !res; n++) {
308
+ const t = readTracks(buf, start, end, frames, n);
309
+ if (t && t.p === end) res = t;
310
+ }
311
+ if (!res) res = readTracks(buf, start, end, frames, nTracks);
312
+ }
313
+ if (!res) return null;
314
+ return { kind: "xray", name, frames, tracks: res.tracks, duration: frames / FPS };
315
+ }
316
+ function parseMotionsChunk(buf, c, bones, params) {
317
+ const out = [];
318
+ const byName = new Map(bones.map((b, i) => [b.name.toLowerCase(), i]));
319
+ const idx = (n) => byName.get((n ?? "").toLowerCase()) ?? -1;
320
+ const map = params?.trackBones.length ? params.trackBones.map(idx) : null;
321
+ const nTracks = map ? map.length : bones.length;
322
+ const partBones = params?.parts.map((p) => new Set(p.bones.map(idx))) ?? [];
323
+ const defOf = new Map(params?.defs.map((d) => [d.motion, d]) ?? []);
324
+ for (const s of readChunks(buf, c.offset, c.offset + c.size)) {
325
+ if (s.id === 0) continue;
326
+ try {
327
+ const m = parseMotion(buf, s, nTracks);
328
+ if (!m) continue;
329
+ if (map) {
330
+ const t = [];
331
+ map.forEach((bi, k) => {
332
+ if (bi >= 0 && m.tracks[k]) t[bi] = m.tracks[k];
333
+ });
334
+ m.tracks = t;
335
+ }
336
+ const def = defOf.get(s.id - 1);
337
+ if (def) {
338
+ const fx = (def.flags & 1) !== 0, only = fx ? /* @__PURE__ */ new Set([def.boneOrPart]) : partBones[def.boneOrPart];
339
+ if (only && only.size && (fx || partBones.length > 1)) {
340
+ const t = [];
341
+ for (const bi of only) if (m.tracks[bi]) t[bi] = m.tracks[bi];
342
+ m.tracks = t;
343
+ m.part = fx ? XRAY_FX_PART : params.parts[def.boneOrPart].name;
344
+ }
345
+ }
346
+ out.push(m);
347
+ } catch {
348
+ }
349
+ }
350
+ return out;
351
+ }
352
+ function parseOMF(buf, bones) {
353
+ const top = readChunks(buf, 0, buf.byteLength), c = top.find((x) => x.id === OGF.S_MOTIONS);
354
+ if (!c) throw new Error("В файле нет чанка анимаций (S_MOTIONS)");
355
+ const pc = top.find((x) => x.id === OGF.S_SMPARAMS);
356
+ return parseMotionsChunk(buf, c, bones, pc ? parseSMParams(buf, pc) : null);
357
+ }
358
+ function parseOGF(buf0) {
359
+ const { buf, chunks: top } = unpackChunks(buf0, readChunks(buf0, 0, buf0.byteLength));
360
+ const hc = top.find((c) => c.id === OGF.HEADER);
361
+ if (!hc) throw new Error("Не OGF файл: нет заголовка");
362
+ const hdr = readHeader(buf, hc);
363
+ if (hdr.version !== 4) throw new Error(`Поддерживается только OGF версии 4 (в файле v${hdr.version})`);
364
+ const find = (id) => top.find((c) => c.id === id);
365
+ const bnc = find(OGF.S_BONE_NAMES);
366
+ const bones = bnc ? parseBoneNames(buf, bnc) : [];
367
+ const ikc = find(OGF.S_IKDATA);
368
+ const ikOk = ikc && bones.length ? parseIK(buf, ikc, bones) : false;
369
+ const meshes = [];
370
+ collectMeshes(buf, top, meshes, bones.length);
371
+ if (!meshes.length) throw new Error(`Геометрия не найдена (тип модели: ${TYPE_NAMES[hdr.type] ?? hdr.type})`);
372
+ const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
373
+ let tris = 0;
374
+ for (const m of meshes) {
375
+ tris += m.indices.length / 3;
376
+ for (let i = 0; i < m.pos.length; i++) {
377
+ const k = i % 3;
378
+ if (m.pos[i] < min[k]) min[k] = m.pos[i];
379
+ if (m.pos[i] > max[k]) max[k] = m.pos[i];
380
+ }
381
+ }
382
+ const mc = find(OGF.S_MOTIONS);
383
+ const pc = find(OGF.S_SMPARAMS);
384
+ const motions = mc ? parseMotionsChunk(buf, mc, bones, pc ? parseSMParams(buf, pc) : null) : [];
385
+ let refs = [];
386
+ try {
387
+ const c19 = find(OGF.S_MOTION_REFS0), c20 = find(OGF.S_MOTION_REFS1);
388
+ if (c19) refs = new Reader(buf, c19.offset, c19.offset + c19.size).stringz().split(",").map((s) => s.trim()).filter(Boolean);
389
+ else if (c20) {
390
+ const r = new Reader(buf, c20.offset, c20.offset + c20.size);
391
+ const k = r.u32();
392
+ for (let i = 0; i < k; i++) refs.push(r.stringz());
393
+ }
394
+ } catch {
395
+ }
396
+ let desc = null;
397
+ try {
398
+ const d = find(OGF.S_DESC);
399
+ if (d) {
400
+ const r = new Reader(buf, d.offset, d.offset + d.size);
401
+ desc = { source: r.stringz(), tool: r.stringz() };
402
+ r.u32();
403
+ desc.owner = r.stringz();
404
+ }
405
+ } catch {
406
+ }
407
+ return { info: { type: TYPE_NAMES[hdr.type] ?? String(hdr.type), tris, ikOk, motionRefs: refs, desc }, meshes, bones, motions, bounds: { min, max } };
408
+ }
409
+ export {
410
+ FPS,
411
+ OGF,
412
+ parseOGF,
413
+ parseOMF,
414
+ parseSMParams,
415
+ readChunks,
416
+ unpackChunks
417
+ };
418
+ //# sourceMappingURL=ogf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ogf.js","sources":["../src/ogf.ts"],"sourcesContent":["// Парсер X-Ray OGF (v4) и OMF. Без внешних зависимостей.\nimport { XRAY_FX_PART } from './motion';\nimport { lzhufDecompress } from './lzhuf';\nimport type { Bounds } from '@gmvjs/core/types';\nimport type { XrayMotion, XrayTrack } from './motion';\n\nexport const OGF = {\n HEADER: 1, TEXTURE: 2, VERTICES: 3, INDICES: 4, CHILDREN: 9, S_BONE_NAMES: 13, S_MOTIONS: 14,\n S_SMPARAMS: 15, S_IKDATA: 16, S_USERDATA: 17, S_DESC: 18, S_MOTION_REFS0: 19, S_MOTION_REFS1: 24,\n} as const;\nconst TYPE_NAMES = ['NORMAL', 'HIERRARHY', 'PROGRESSIVE', 'SKELETON_ANIM', 'SKELETON_GEOMDEF_PM', 'SKELETON_GEOMDEF_ST',\n 'LOD', 'TREE_ST', 'PARTICLE_EFFECT', 'PARTICLE_GROUP', 'SKELETON_RIGID', 'TREE_PM'];\nexport const FPS = 30;\n\nexport interface OgfChunk { id: number; comp: boolean; offset: number; size: number }\nexport interface OgfBone { name: string; parent: number; rot: number[]; pos: number[] }\nexport interface OgfMesh {\n texture: string; shader: string; vertexCount: number;\n pos: Float32Array; nor: Float32Array; uv: Float32Array; bone: Float32Array; weight: Float32Array;\n indices: Uint16Array; skinned: boolean; stride: number; name?: string;\n}\nexport interface OgfModel {\n info: { type: string; tris: number; ikOk: boolean; motionRefs: string[]; desc: { source: string; tool: string; owner?: string } | null };\n meshes: OgfMesh[]; bones: OgfBone[]; motions: XrayMotion[]; bounds: Bounds;\n}\n\nclass Reader {\n readonly dv: DataView;\n readonly u8: Uint8Array;\n constructor(buf: ArrayBuffer, public pos: number, public end: number) { this.dv = new DataView(buf); this.u8 = new Uint8Array(buf); }\n byte() { return this.dv.getUint8(this.pos++); }\n u16() { const v = this.dv.getUint16(this.pos, true); this.pos += 2; return v; }\n u32() { const v = this.dv.getUint32(this.pos, true); this.pos += 4; return v; }\n f32() { const v = this.dv.getFloat32(this.pos, true); this.pos += 4; return v; }\n skip(n: number) { this.pos += n; }\n get eof() { return this.pos >= this.end; }\n stringz() { let s = ''; while (this.pos < this.end) { const c = this.u8[this.pos++]!; if (!c) break; s += String.fromCharCode(c); } return s; }\n}\n\nexport function readChunks(buf: ArrayBuffer, start: number, end: number): OgfChunk[] {\n const dv = new DataView(buf), list: OgfChunk[] = [];\n let p = start;\n while (p + 8 <= end) {\n const raw = dv.getUint32(p, true), size = dv.getUint32(p + 4, true);\n if (p + 8 + size > end) break;\n list.push({ id: raw & 0x7fffffff, comp: (raw & 0x80000000) !== 0, offset: p + 8, size });\n p += 8 + size;\n }\n return list;\n}\n\nfunction readHeader(buf: ArrayBuffer, c: OgfChunk) {\n const r = new Reader(buf, c.offset, c.offset + c.size);\n const version = r.byte(), type = r.byte(), shader = r.u16();\n const bmin = [r.f32(), r.f32(), r.f32()], bmax = [r.f32(), r.f32(), r.f32()];\n return { version, type, shader, bmin, bmax };\n}\n\nfunction parseGeometry(buf: ArrayBuffer, vtx: OgfChunk, idx: OgfChunk, tex: OgfChunk | undefined, nBones: number): OgfMesh {\n const dv = new DataView(buf);\n const fmt = dv.getUint32(vtx.offset, true), n = dv.getUint32(vtx.offset + 4, true), base = vtx.offset + 8;\n const stride = n ? Math.floor((vtx.size - 8) / n) : 0;\n if (![32, 60, 64, 70, 72, 76].includes(stride)) throw new Error(`Неподдерживаемый формат вершин 0x${fmt.toString(16)} (stride ${stride})`);\n const pos = new Float32Array(n * 3), nor = new Float32Array(n * 3), uv = new Float32Array(n * 2);\n const bone = new Float32Array(n * 4), wt = new Float32Array(n * 4);\n const f = (o: number) => dv.getFloat32(o, true), h = (o: number) => dv.getUint16(o, true);\n const cl = (b: number) => Math.min(b, Math.max(0, nBones - 1));\n for (let i = 0; i < n; i++) {\n const o = base + i * stride;\n let P: number, N: number, UV: number, bi = [0, 0, 0, 0], w = [1, 0, 0, 0];\n if (stride === 32) { P = o; N = o + 12; UV = o + 24; }\n else if (stride === 60) { P = o; N = o + 12; UV = o + 48; bi[0] = dv.getUint32(o + 56, true); }\n else if (stride === 64) { bi[0] = h(o); bi[1] = h(o + 2); P = o + 4; N = o + 16; UV = o + 56; const w1 = f(o + 52); w = [1 - w1, w1, 0, 0]; }\n // 3L/4L (ЧН/ЗП): в файле веса первых костей, вес последней — остаток (как в Vertices3W/4W движка)\n else if (stride === 70 || stride === 72) { bi = [h(o), h(o + 2), h(o + 4), 0]; P = o + 6; N = o + 18; UV = o + 62; const a = f(o + 54), b = f(o + 58); w = [a, b, 1 - a - b, 0]; }\n else { bi = [h(o), h(o + 2), h(o + 4), h(o + 6)]; P = o + 8; N = o + 20; UV = o + 68; const a = f(o + 56), b = f(o + 60), c = f(o + 64); w = [a, b, c, 1 - a - b - c]; }\n for (let k = 0; k < 3; k++) { pos[i * 3 + k] = f(P + 4 * k); nor[i * 3 + k] = f(N + 4 * k); }\n uv[i * 2] = f(UV); uv[i * 2 + 1] = f(UV + 4);\n for (let k = 0; k < 4; k++) { bone[i * 4 + k] = cl(bi[k]!); wt[i * 4 + k] = w[k]!; }\n }\n const ic = dv.getUint32(idx.offset, true), indices = new Uint16Array(ic);\n for (let i = 0; i < ic; i++) indices[i] = dv.getUint16(idx.offset + 4 + i * 2, true);\n let texture = '', shader = '';\n if (tex) { const r = new Reader(buf, tex.offset, tex.offset + tex.size); texture = r.stringz(); shader = r.stringz(); }\n return { texture, shader, vertexCount: n, pos, nor, uv, bone, weight: wt, indices, skinned: stride !== 32, stride };\n}\n\n/** Сжатые (LZHUF, старший бит id) чанки — распаковываются в отдельный буфер; остальные остаются как есть. */\nexport function unpackChunks(buf: ArrayBuffer, chunks: OgfChunk[]): { buf: ArrayBuffer; chunks: OgfChunk[] } {\n if (!chunks.some((c) => c.comp)) return { buf, chunks };\n const parts = chunks.map((c) => { const raw = new Uint8Array(buf, c.offset, c.size); return c.comp ? lzhufDecompress(raw) : raw; });\n const out = new Uint8Array(parts.reduce((a, p) => a + p.length, 0)), list: OgfChunk[] = [];\n let o = 0;\n parts.forEach((p, i) => { out.set(p, o); list.push({ id: chunks[i]!.id, comp: false, offset: o, size: p.length }); o += p.length; });\n return { buf: out.buffer, chunks: list };\n}\n\nfunction collectMeshes(buf0: ArrayBuffer, chunks0: OgfChunk[], out: OgfMesh[], nBones: number): void {\n const { buf, chunks } = unpackChunks(buf0, chunks0);\n const find = (id: number) => chunks.find((c) => c.id === id);\n const vtx = find(OGF.VERTICES), idx = find(OGF.INDICES);\n if (vtx && idx) out.push(parseGeometry(buf, vtx, idx, find(OGF.TEXTURE), nBones));\n const ch = find(OGF.CHILDREN);\n if (ch) for (const s of readChunks(buf, ch.offset, ch.offset + ch.size)) collectMeshes(buf, readChunks(buf, s.offset, s.offset + s.size), out, nBones);\n}\n\nfunction parseBoneNames(buf: ArrayBuffer, c: OgfChunk): OgfBone[] {\n const attempt = (withCount: boolean) => {\n const r = new Reader(buf, c.offset, c.offset + c.size);\n const count = withCount ? r.u32() : Infinity, out: Array<{ name: string; parentName: string }> = [];\n while (!r.eof && out.length < count) { const name = r.stringz(), parentName = r.stringz(); r.skip(60); out.push({ name, parentName }); }\n if (r.pos !== r.end || (withCount && out.length !== count)) return null;\n return out;\n };\n const list = attempt(true) ?? attempt(false);\n if (!list) throw new Error('Не удалось разобрать S_BONE_NAMES');\n const map = new Map(list.map((b, i) => [b.name.toLowerCase(), i]));\n return list.map((b) => ({ name: b.name, parent: b.parentName ? map.get(b.parentName.toLowerCase()) ?? -1 : -1, rot: [0, 0, 0], pos: [0, 0, 0] }));\n}\n\nfunction parseIK(buf: ArrayBuffer, c: OgfChunk, bones: OgfBone[]): boolean {\n const attempt = (friction: boolean) => {\n const r = new Reader(buf, c.offset, c.offset + c.size), out: Array<{ rot: number[]; pos: number[] }> = [];\n for (let i = 0; i < bones.length; i++) {\n if (r.pos + 4 > r.end) return null;\n r.u32(); r.stringz(); r.skip(112 + 4 + 48 + 8 + 12 + (friction ? 4 : 0));\n if (r.pos + 24 > r.end) return null;\n const rot = [r.f32(), r.f32(), r.f32()], pos = [r.f32(), r.f32(), r.f32()];\n r.skip(16);\n out.push({ rot, pos });\n }\n return r.pos === r.end ? out : null;\n };\n const res = attempt(true) ?? attempt(false);\n if (res) res.forEach((d, i) => { bones[i]!.rot = d.rot; bones[i]!.pos = d.pos; });\n return !!res;\n}\n\n// ---- Анимации ----\nfunction readTracks(buf: ArrayBuffer, start: number, end: number, frames: number, n: number): { tracks: XrayTrack[]; p: number } | null {\n const dv = new DataView(buf), tracks: XrayTrack[] = [];\n let p = start;\n for (let i = 0; i < n; i++) {\n if (p + 1 > end) return null;\n const flags = dv.getUint8(p++);\n let rot: Int16Array, trans: Float32Array, constT = false;\n if (flags & 2) {\n if (p + 8 > end) return null;\n rot = new Int16Array(4);\n for (let k = 0; k < 4; k++) rot[k] = dv.getInt16(p + 2 * k, true);\n p += 8;\n } else {\n if (p + 4 + frames * 8 > end) return null;\n p += 4;\n rot = new Int16Array(frames * 4);\n for (let k = 0; k < frames * 4; k++) rot[k] = dv.getInt16(p + 2 * k, true);\n p += frames * 8;\n }\n if (flags & 1) {\n const wide = (flags & 4) !== 0, bytes = frames * 3 * (wide ? 2 : 1);\n if (p + 4 + bytes + 24 > end) return null;\n p += 4;\n const raw = new Float32Array(frames * 3), kp = p;\n for (let k = 0; k < frames * 3; k++) raw[k] = wide ? dv.getInt16(kp + 2 * k, true) : dv.getInt8(kp + k);\n p += bytes;\n const sz = [dv.getFloat32(p, true), dv.getFloat32(p + 4, true), dv.getFloat32(p + 8, true)];\n const ini = [dv.getFloat32(p + 12, true), dv.getFloat32(p + 16, true), dv.getFloat32(p + 20, true)];\n p += 24;\n for (let k = 0; k < frames * 3; k++) raw[k] = raw[k]! * sz[k % 3]! + ini[k % 3]!;\n trans = raw;\n } else {\n if (p + 12 > end) return null;\n trans = new Float32Array([dv.getFloat32(p, true), dv.getFloat32(p + 4, true), dv.getFloat32(p + 8, true)]);\n p += 12;\n constT = true;\n }\n tracks.push({ rot, trans, constT });\n }\n return { tracks, p };\n}\n\n/** Описание анимации из S_SMPARAMS: флаги (esmFX = 1 — «эффект» на одну кость), часть тела или кость, номер анимации. */\nexport interface XrayMotionDef { name: string; flags: number; boneOrPart: number; motion: number; speed: number }\nexport interface XraySMParams { version: number; trackBones: string[]; parts: Array<{ name: string; bones: string[] }>; defs: XrayMotionDef[] }\n\n/**\n * S_SMPARAMS: части тела, порядок треков и описания анимаций. Треки в S_MOTIONS идут по номеру m_idx,\n * а кость определяется по имени (как в motions_value::load движка), поэтому порядок костей модели и анимации может различаться.\n * Анимация части (ноги, торс, голова) управляет только костями своей части — остальные треки в файле не значимы.\n */\nexport function parseSMParams(buf: ArrayBuffer, c: OgfChunk): XraySMParams | null {\n try {\n const r = new Reader(buf, c.offset, c.offset + c.size), version = r.u16(), nParts = r.u16(), trackBones: string[] = [], parts: XraySMParams['parts'] = [];\n for (let p = 0; p < nParts; p++) {\n const part = { name: r.stringz(), bones: [] as string[] }, nb = r.u16();\n for (let b = 0; b < nb; b++) { const name = r.stringz(), idx = version === 1 ? trackBones.length : r.u32(); trackBones[idx] = name; part.bones.push(name); }\n parts.push(part);\n }\n if (r.pos > r.end) return null;\n const defs: XrayMotionDef[] = [];\n try {\n const n = r.u16();\n for (let k = 0; k < n && r.pos < r.end; k++) {\n const name = r.stringz(), flags = r.u32(), boneOrPart = r.u16(), motion = r.u16(), speed = r.f32();\n r.skip(12); // power, accrue, falloff\n if (version >= 4) { const nm = r.u32(); for (let i = 0; i < nm; i++) { while (r.pos < r.end && r.byte() !== 10); r.skip(8 * r.u32()); } } // метки: имя до «\\r\\n», интервалы\n defs.push({ name, flags, boneOrPart, motion, speed });\n }\n if (r.pos > r.end) defs.length = 0;\n } catch { defs.length = 0; /* описания анимаций необязательны */ }\n return { version, trackBones, parts, defs };\n } catch { return null; }\n}\n\nfunction parseMotion(buf: ArrayBuffer, s: OgfChunk, nTracks: number): XrayMotion | null {\n const r = new Reader(buf, s.offset, s.offset + s.size);\n const name = r.stringz(), frames = r.u32(), start = r.pos, end = s.offset + s.size;\n if (!frames || frames > 100000) return null;\n let res = readTracks(buf, start, end, frames, nTracks);\n if (!res || res.p !== end) {\n res = null;\n for (let n = 1; n <= 1024 && !res; n++) { const t = readTracks(buf, start, end, frames, n); if (t && t.p === end) res = t; }\n if (!res) res = readTracks(buf, start, end, frames, nTracks);\n }\n if (!res) return null;\n return { kind: 'xray', name, frames, tracks: res.tracks, duration: frames / FPS };\n}\n\n/** Анимации чанка S_MOTIONS; треки раскладываются по костям модели по именам из S_SMPARAMS (если есть). */\nfunction parseMotionsChunk(buf: ArrayBuffer, c: OgfChunk, bones: readonly OgfBone[], params: XraySMParams | null): XrayMotion[] {\n const out: XrayMotion[] = [];\n const byName = new Map(bones.map((b, i) => [b.name.toLowerCase(), i]));\n const idx = (n: string | undefined) => byName.get((n ?? '').toLowerCase()) ?? -1;\n const map = params?.trackBones.length ? params.trackBones.map(idx) : null;\n const nTracks = map ? map.length : bones.length;\n const partBones = params?.parts.map((p) => new Set(p.bones.map(idx))) ?? [];\n const defOf = new Map(params?.defs.map((d) => [d.motion, d]) ?? []);\n for (const s of readChunks(buf, c.offset, c.offset + c.size)) {\n if (s.id === 0) continue;\n try {\n const m = parseMotion(buf, s, nTracks);\n if (!m) continue;\n if (map) { const t: XrayTrack[] = []; map.forEach((bi, k) => { if (bi >= 0 && m.tracks[k]) t[bi] = m.tracks[k]!; }); m.tracks = t; }\n // анимация одной части тела: остальные кости остаются в bind-позе (в игре их ведёт другая анимация)\n const def = defOf.get(s.id - 1);\n if (def) {\n const fx = (def.flags & 1) !== 0, only = fx ? new Set([def.boneOrPart]) : partBones[def.boneOrPart];\n if (only && only.size && (fx || partBones.length > 1)) {\n const t: XrayTrack[] = [];\n for (const bi of only) if (m.tracks[bi]) t[bi] = m.tracks[bi]!;\n m.tracks = t;\n m.part = fx ? XRAY_FX_PART : params!.parts[def.boneOrPart]!.name;\n }\n }\n out.push(m);\n } catch { /* пропускаем битую анимацию */ }\n }\n return out;\n}\n\n/** Внешние анимации .omf для модели с костями bones (сопоставление треков по именам костей). */\nexport function parseOMF(buf: ArrayBuffer, bones: readonly OgfBone[]): XrayMotion[] {\n const top = readChunks(buf, 0, buf.byteLength), c = top.find((x) => x.id === OGF.S_MOTIONS);\n if (!c) throw new Error('В файле нет чанка анимаций (S_MOTIONS)');\n const pc = top.find((x) => x.id === OGF.S_SMPARAMS);\n return parseMotionsChunk(buf, c, bones, pc ? parseSMParams(buf, pc) : null);\n}\n\nexport function parseOGF(buf0: ArrayBuffer): OgfModel {\n const { buf, chunks: top } = unpackChunks(buf0, readChunks(buf0, 0, buf0.byteLength));\n const hc = top.find((c) => c.id === OGF.HEADER);\n if (!hc) throw new Error('Не OGF файл: нет заголовка');\n const hdr = readHeader(buf, hc);\n if (hdr.version !== 4) throw new Error(`Поддерживается только OGF версии 4 (в файле v${hdr.version})`);\n const find = (id: number) => top.find((c) => c.id === id);\n\n const bnc = find(OGF.S_BONE_NAMES);\n const bones = bnc ? parseBoneNames(buf, bnc) : [];\n const ikc = find(OGF.S_IKDATA);\n const ikOk = ikc && bones.length ? parseIK(buf, ikc, bones) : false;\n\n const meshes: OgfMesh[] = [];\n collectMeshes(buf, top, meshes, bones.length);\n if (!meshes.length) throw new Error(`Геометрия не найдена (тип модели: ${TYPE_NAMES[hdr.type] ?? hdr.type})`);\n\n const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];\n let tris = 0;\n for (const m of meshes) {\n tris += m.indices.length / 3;\n for (let i = 0; i < m.pos.length; i++) { const k = i % 3; if (m.pos[i]! < min[k]!) min[k] = m.pos[i]!; if (m.pos[i]! > max[k]!) max[k] = m.pos[i]!; }\n }\n\n const mc = find(OGF.S_MOTIONS);\n const pc = find(OGF.S_SMPARAMS);\n const motions = mc ? parseMotionsChunk(buf, mc, bones, pc ? parseSMParams(buf, pc) : null) : [];\n\n let refs: string[] = [];\n try {\n const c19 = find(OGF.S_MOTION_REFS0), c20 = find(OGF.S_MOTION_REFS1);\n if (c19) refs = new Reader(buf, c19.offset, c19.offset + c19.size).stringz().split(',').map((s) => s.trim()).filter(Boolean);\n else if (c20) { const r = new Reader(buf, c20.offset, c20.offset + c20.size); const k = r.u32(); for (let i = 0; i < k; i++) refs.push(r.stringz()); }\n } catch { /* необязательный чанк */ }\n\n let desc: OgfModel['info']['desc'] = null;\n try {\n const d = find(OGF.S_DESC);\n if (d) { const r = new Reader(buf, d.offset, d.offset + d.size); desc = { source: r.stringz(), tool: r.stringz() }; r.u32(); desc.owner = r.stringz(); }\n } catch { /* необязательный чанк */ }\n\n return { info: { type: TYPE_NAMES[hdr.type] ?? String(hdr.type), tris, ikOk, motionRefs: refs, desc }, meshes, bones, motions, bounds: { min, max } };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { LoadContext } from '@gmvjs/core/plugin';
2
+ import type { Model, VFile } from '@gmvjs/core/types';
3
+ import { type XrObjectMesh } from './object';
4
+ export type Ini = Map<string, Map<string, string>>;
5
+ /** Разбор ini X-Ray (секции, «ключ = значение», комментарии «;»). */
6
+ export declare function parseIni(text: string): Ini;
7
+ export interface SceneObjectRef {
8
+ name: string;
9
+ reference: string;
10
+ position: number[];
11
+ rotation: number[];
12
+ scale: number[];
13
+ flags: number;
14
+ }
15
+ /** Объекты из scene_object.part. */
16
+ export declare function sceneObjects(ini: Ini): SceneObjectRef[];
17
+ /**
18
+ * Матрица поворота объекта сцены (строки-векторы: v' = v·R). Проверено на labx8 (ЗП): вершины 40 из 40 повёрнутых объектов
19
+ * сцены совпали с геометрией скомпилированного уровня точно при R = transpose(setHPB(y, x, z)); другие трактовки углов — нет.
20
+ */
21
+ export declare function sceneRotation(rot: number[]): number[];
22
+ /** Вершины меша объекта в мир сцены: v·S·R + P (строки-векторы). */
23
+ export declare function placeMesh(m: XrObjectMesh, ref: SceneObjectRef): {
24
+ pos: Float32Array;
25
+ nor: Float32Array;
26
+ };
27
+ /** Уровень SDK: .level + scene_object.part + объекты. Меши объединяются по текстуре и шейдеру. */
28
+ export declare function loadSceneLevel(main: VFile, ctx: LoadContext): Promise<Model>;
package/dist/scene.js ADDED
@@ -0,0 +1,141 @@
1
+ import { boundsOf } from "@gmvjs/core/geometry";
2
+ import { objectMeshes, parseObject } from "./object.js";
3
+ function parseIni(text) {
4
+ const out = /* @__PURE__ */ new Map();
5
+ let cur = null;
6
+ for (const raw of text.split(/\r?\n/)) {
7
+ const line = raw.replace(/;.*$/, "").trim();
8
+ if (!line) continue;
9
+ const sec = /^\[([^\]]+)\]/.exec(line);
10
+ if (sec) {
11
+ cur = /* @__PURE__ */ new Map();
12
+ out.set(sec[1].trim(), cur);
13
+ continue;
14
+ }
15
+ const eq = line.indexOf("=");
16
+ if (cur && eq > 0) cur.set(line.slice(0, eq).trim(), line.slice(eq + 1).trim());
17
+ }
18
+ return out;
19
+ }
20
+ const vec = (s, d) => s ? s.split(",").map((x) => +x.trim()) : d;
21
+ function sceneObjects(ini) {
22
+ const out = [];
23
+ for (const [name, sec] of ini) {
24
+ if (!/^object_\d+$/.test(name) || !sec.get("reference_name")) continue;
25
+ out.push({
26
+ name: sec.get("name") ?? name,
27
+ reference: sec.get("reference_name"),
28
+ flags: +(sec.get("flags") ?? 0),
29
+ position: vec(sec.get("position"), [0, 0, 0]),
30
+ rotation: vec(sec.get("rotation"), [0, 0, 0]),
31
+ scale: vec(sec.get("scale"), [1, 1, 1])
32
+ });
33
+ }
34
+ return out;
35
+ }
36
+ function sceneRotation(rot) {
37
+ const h = rot[1], p = rot[0], b = rot[2];
38
+ const sh = Math.sin(h), ch = Math.cos(h), sp = Math.sin(p), cp = Math.cos(p), sb = Math.sin(b), cb = Math.cos(b);
39
+ const cc = ch * cb, cs = ch * sb, sc = sh * cb, ss = sh * sb;
40
+ const m = [cc - sp * ss, cs + sp * sc, -cp * sh, -cp * sb, cp * cb, sp, sc + sp * cs, ss - sp * cc, cp * ch];
41
+ return [m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8]];
42
+ }
43
+ function placeMesh(m, ref) {
44
+ const R = sceneRotation(ref.rotation), [sx, sy, sz] = ref.scale, [px, py, pz] = ref.position;
45
+ const n = m.pos.length / 3, pos = new Float32Array(n * 3), nor = new Float32Array(n * 3);
46
+ for (let i = 0; i < n; i++) {
47
+ const x = m.pos[i * 3] * sx, y = m.pos[i * 3 + 1] * sy, z = m.pos[i * 3 + 2] * sz;
48
+ pos[i * 3] = x * R[0] + y * R[3] + z * R[6] + px;
49
+ pos[i * 3 + 1] = x * R[1] + y * R[4] + z * R[7] + py;
50
+ pos[i * 3 + 2] = x * R[2] + y * R[5] + z * R[8] + pz;
51
+ const a = m.nor[i * 3] / sx, b = m.nor[i * 3 + 1] / sy, c = m.nor[i * 3 + 2] / sz;
52
+ let nx = a * R[0] + b * R[3] + c * R[6], ny = a * R[1] + b * R[4] + c * R[7], nz = a * R[2] + b * R[5] + c * R[8];
53
+ const l = Math.hypot(nx, ny, nz) || 1;
54
+ nx /= l;
55
+ ny /= l;
56
+ nz /= l;
57
+ nor[i * 3] = nx;
58
+ nor[i * 3 + 1] = ny;
59
+ nor[i * 3 + 2] = nz;
60
+ }
61
+ return { pos, nor };
62
+ }
63
+ async function readRaw(ctx, rel) {
64
+ const want = rel.toLowerCase().replace(/\\/g, "/");
65
+ for (const a of ctx.archives) {
66
+ const e = a.lookup(want) ?? a.entries.find((x) => x.path.endsWith("/" + want));
67
+ if (e) return a.read(e);
68
+ }
69
+ return null;
70
+ }
71
+ async function loadSceneLevel(main, ctx) {
72
+ const level = parseIni(new TextDecoder("windows-1251").decode(await main.arrayBuffer()));
73
+ const opts = level.get("level_options"), name = main.name.replace(/\.level$/i, "");
74
+ const dir = (main.path ?? "").replace(/[^/]*$/, "");
75
+ const partPath = `${dir}${name}/scene_object.part`;
76
+ let part = await ctx.readPath(partPath).catch(() => null);
77
+ if (!part) part = await readRaw(ctx, `rawdata/levels/${name}/scene_object.part`);
78
+ if (!part) throw new Error(`Нет ${name}/scene_object.part рядом с ${main.name}: откройте папку SDK (editors/rawdata) целиком`);
79
+ const refs = sceneObjects(parseIni(new TextDecoder("windows-1251").decode(part)));
80
+ const cache = /* @__PURE__ */ new Map(), missing = /* @__PURE__ */ new Set();
81
+ const groups = /* @__PURE__ */ new Map();
82
+ let placed = 0;
83
+ for (const ref of refs) {
84
+ let list = cache.get(ref.reference);
85
+ if (list === void 0) {
86
+ const raw = await readRaw(ctx, `rawdata/objects/${ref.reference}.object`);
87
+ try {
88
+ list = raw ? objectMeshes(parseObject(raw.buffer.slice(raw.byteOffset, raw.byteOffset + raw.byteLength))) : null;
89
+ } catch {
90
+ list = null;
91
+ }
92
+ cache.set(ref.reference, list);
93
+ }
94
+ if (!list) {
95
+ missing.add(ref.reference);
96
+ continue;
97
+ }
98
+ placed++;
99
+ for (const m of list) {
100
+ const s = m.surface, key = `${s?.texture ?? ""}|${s?.eshader ?? ""}`;
101
+ let g = groups.get(key);
102
+ if (!g) groups.set(key, g = { texture: s?.texture ?? "", shader: s?.eshader ?? "", material: s?.gamemtl, pos: [], nor: [], uv: [], idx: [] });
103
+ const { pos, nor } = placeMesh(m, ref), base = g.pos.length / 3;
104
+ for (let i = 0; i < pos.length; i++) {
105
+ g.pos.push(pos[i]);
106
+ g.nor.push(nor[i]);
107
+ }
108
+ for (let i = 0; i < m.uv.length; i++) g.uv.push(m.uv[i]);
109
+ for (let i = 0; i < m.indices.length; i++) g.idx.push(m.indices[i] + base);
110
+ }
111
+ }
112
+ const meshes = [...groups.values()].map((g) => {
113
+ const n = g.pos.length / 3;
114
+ return {
115
+ name: `${g.texture || "(без текстуры)"} · ${g.shader}`,
116
+ texture: g.texture,
117
+ shader: g.shader,
118
+ material: g.material,
119
+ vertexCount: n,
120
+ pos: Float32Array.from(g.pos),
121
+ nor: Float32Array.from(g.nor),
122
+ uv: Float32Array.from(g.uv),
123
+ bone: new Float32Array(n * 4),
124
+ weight: new Float32Array(n * 4).map((_, i) => i % 4 === 0 ? 1 : 0),
125
+ indices: Uint32Array.from(g.idx),
126
+ skinned: false
127
+ };
128
+ });
129
+ if (!meshes.length) throw new Error(`Объекты уровня не найдены (${missing.size} ссылок): откройте папку SDK с rawdata/objects`);
130
+ const info = [`Уровень SDK: ${opts?.get("level_path") ?? name}, объектов ${refs.length}, показано ${placed}, мешей (по текстурам) ${meshes.length}`];
131
+ if (missing.size) info.push(`Нет файлов объектов (${missing.size}): ${[...missing].slice(0, 6).join(", ")}${missing.size > 6 ? "…" : ""}`);
132
+ return { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: true, info };
133
+ }
134
+ export {
135
+ loadSceneLevel,
136
+ parseIni,
137
+ placeMesh,
138
+ sceneObjects,
139
+ sceneRotation
140
+ };
141
+ //# sourceMappingURL=scene.js.map