@gmvjs/xray-v1 0.0.0-stage → 0.1.1

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 +77 -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) and OMF parser. No external dependencies.\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 (CS/CoP): the file holds the weights of the first bones, the last bone's weight is the remainder (as in the engine's 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/** Compressed chunks (LZHUF, the top bit of the id) are unpacked into a separate buffer; the rest stay as is. */\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// ---- Animations ----\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/** Animation description from S_SMPARAMS: flags (esmFX = 1 = an \"effect\" on one bone), body part or bone, animation number. */\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: body parts, track order and animation descriptions. Tracks in S_MOTIONS go by m_idx,\n * and the bone is determined by name (as in the engine's motions_value::load), so the bone order of the model and the animation may differ.\n * A part animation (legs, torso, head) drives only its part's bones; the other tracks in the file are not significant.\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()); } } // marks: a name up to \"\\r\\n\", intervals\n defs.push({ name, flags, boneOrPart, motion, speed });\n }\n if (r.pos > r.end) defs.length = 0;\n } catch { defs.length = 0; /* animation descriptions are optional */ }\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/** Animations of the S_MOTIONS chunk; tracks are mapped to model bones by the names from S_SMPARAMS (if present). */\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 // an animation of one body part: the other bones stay in the bind pose (in game another animation drives them)\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 { /* skip a broken animation */ }\n }\n return out;\n}\n\n/** External .omf animations for a model with bones `bones` (tracks matched by bone name). */\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 { /* optional chunk */ }\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 { /* optional chunk */ }\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
+ /** X-Ray ini parsing (sections, "key = value", ";" comments). */
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
+ /** Objects from scene_object.part. */
16
+ export declare function sceneObjects(ini: Ini): SceneObjectRef[];
17
+ /**
18
+ * Scene object rotation matrix (row vectors: v' = v·R). Verified on labx8 (CoP): the vertices of 40 out of 40 rotated scene objects
19
+ * matched the compiled level geometry exactly with R = transpose(setHPB(y, x, z)); other angle interpretations did not.
20
+ */
21
+ export declare function sceneRotation(rot: number[]): number[];
22
+ /** Object mesh vertices into the scene world: v·S·R + P (row vectors). */
23
+ export declare function placeMesh(m: XrObjectMesh, ref: SceneObjectRef): {
24
+ pos: Float32Array;
25
+ nor: Float32Array;
26
+ };
27
+ /** SDK level: .level + scene_object.part + objects. Meshes are merged by texture and shader. */
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