@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["// X-Ray Engine 1.x (S.T.A.L.K.E.R.): OGF v4 + OMF animations, .db/.xdb archives.\nimport { ascii, u32le } from '@gmvjs/core/binary';\nimport { defineEngine, type GamePreset } from '@gmvjs/core/plugin';\nimport { EN } from './i18n';\n\nconst DB_EXTS = ['db', 'xdb', ...'0123456789'.split('').flatMap((d) => ['db' + d, 'xdb' + d]), 'dba', 'dbb', 'dbc', 'dbd'];\nconst ui: GamePreset['ui'] = { motionLabel: '+ .omf', invertQuat: true, archives: true, archiveExts: [...DB_EXTS, 'exe'], archiveLabel: 'Открыть .db…' };\nconst game = (id: string, title: string, hint: string): GamePreset => ({ id, group: 'Игры', title, engine: 'xray-v1', formats: ['xray-ogf', 'xray-object', 'xray-level', 'xray-dm', 'xray-scene'], hint, ui, status: 'beta', markers: ['gamedata', 'fsgame.ltx'] });\n\nexport const xrayV1Engine = defineEngine({\n id: 'xray-v1',\n title: 'X-Ray Engine 1.x',\n i18n: { en: EN },\n formats: [\n {\n id: 'xray-ogf',\n title: 'X-Ray OGF',\n engine: 'xray-v1',\n extensions: ['ogf'],\n motionExtensions: ['omf', 'skl', 'skls'],\n status: 'beta',\n // the first chunk is OGF_HEADER (id 1), then a version byte; otherwise not certain, but not excluded either\n sniff: (h) => (u32le(h, 0) === 1 ? (h[8] === 4 ? 1 : 0.6) : 0.2),\n load: async (main, ctx) => (await import('./load')).loadOgf(main, ctx),\n },\n {\n id: 'xray-object',\n title: 'X-Ray SDK .object',\n engine: 'xray-v1',\n extensions: ['object'],\n motionExtensions: ['omf', 'skl', 'skls'],\n status: 'beta',\n sniff: (h) => (u32le(h, 0) === 0x7777 ? 1 : 0),\n load: async (main, ctx) => (await import('./load')).loadObject(main, ctx),\n },\n {\n id: 'xray-level',\n title: 'X-Ray: скомпилированный уровень (level + level.geom)',\n engine: 'xray-v1',\n extensions: [],\n pathPattern: /(^|\\/)levels\\/[^/]+\\/level$/,\n status: 'beta',\n // the first chunk is the level header (id 1, 4 bytes: version and quality)\n sniff: (h) => (u32le(h, 0) === 1 && u32le(h, 4) === 4 && u32le(h, 8) !== 16 ? 0.9 : 0), // 16 is the Metro 2033 (4A) level version\n load: async (main, ctx) => (await import('./level')).loadLevel(main, ctx),\n },\n {\n id: 'xray-dm',\n title: 'X-Ray: деталь (.dm — трава, кусты)',\n engine: 'xray-v1',\n extensions: ['dm'],\n status: 'beta',\n // starts with a shader name (Latin letters, \"\\\\\", \"_\")\n sniff: (h) => (/^[\\w\\\\.-]{2,}\\0/.test(ascii(h, 0, Math.min(h.length, 64))) ? 0.8 : 0),\n load: async (main) => {\n const { parseDM, dmMesh } = await import('./details');\n const { boundsOf } = await import('@gmvjs/core/geometry');\n const m = parseDM(new Uint8Array(await main.arrayBuffer())), mesh = dmMesh(m, m.texture);\n return { meshes: [mesh], skeleton: null, motions: [], bounds: boundsOf([mesh]), flipZ: true, info: [`Деталь X-Ray: ${m.shader} · ${m.texture}`, `Вершин ${mesh.vertexCount}, треугольников ${m.indices.length / 3}, масштаб ${m.minScale}…${m.maxScale}`] };\n },\n },\n {\n id: 'xray-scene',\n title: 'X-Ray SDK: уровень (.level + scene_object.part)',\n engine: 'xray-v1',\n extensions: ['level'],\n status: 'beta',\n // SDK 0.7 (CoP) saves the scene as ini; binary .level files of older SDKs are not read yet\n sniff: (h) => (/^\\s*\\[(build_params|camera|guid|level_options|level_tag|version)\\]/.test(ascii(h, 0, Math.min(h.length, 64))) ? 1 : 0),\n load: async (main, ctx) => (await import('./scene')).loadSceneLevel(main, ctx),\n },\n ],\n archives: [\n {\n id: 'xray-db',\n title: 'X-Ray .db / .xdb (Чистое небо, Зов Припяти)',\n engine: 'xray-v1',\n extensions: DB_EXTS,\n // SoC: the game executable is an archive companion (the file table key is taken from it)\n companionExtensions: ['exe'],\n // in the \"Game folder\" archives open by themselves (only the file table is read): CS/CoP with a header, SoC gamedata.db*\n openFromFolder: (h, name) => u32le(h, 0) === 0x29a || (u32le(h, 0) === 0 && /^gamedata\\.db[0-9a-z]$/i.test(name)),\n // CS/CoP start with the text [header] chunk (id 0x29A), SoC and mods with the data chunk (id 0)\n sniff: (h) => (u32le(h, 0) === 0x29a ? 1 : u32le(h, 0) === 0 ? 0.5 : 0),\n open: async (file, siblings) => (await import('./db')).openXrayDb(file, siblings),\n },\n ],\n exporters: [\n {\n id: 'xray-sdk',\n title: 'S.T.A.L.K.E.R.: исходники X-Ray SDK 0.7',\n target: 'xray',\n description: 'Для LevelEditor и ActorEditor (Зов Припяти, SDK 0.7): геометрия .object (большие карты режутся на ячейки), сцена .level + scene_object.part, текстуры DDS + .thm — одним ZIP. Свет, секторы, AI-сетку и сборку xrLC делаете в SDK.',\n from: ['4a', 'xray-v1'],\n fields: [\n { id: 'name', label: 'Имя уровня', type: 'text', hint: 'Латиница, цифры, «_»: rawdata/objects/gmv/<имя>, rawdata/levels/<имя>.level', default: (_m, base) => base.toLowerCase().replace(/[^a-z0-9_]+/g, '_').replace(/^_+|_+$/g, '') || 'level' },\n { id: 'cell', label: 'Ячейка, м', type: 'number', min: 0, step: 8, placeholder: 'авто', hint: 'Разбиение геометрии на объекты (0 — одним объектом; для карт удобно 32–64)' },\n { id: 'scale', label: 'Масштаб', type: 'number', min: 0, step: 0.01, default: 1, hint: 'Сколько метров в одной единице модели (X-Ray: 1 единица = 1 м)' },\n { id: 'instances', label: 'Объекты уровня — ссылками', type: 'checkbox', default: true, hint: 'Каждая модель объекта одним .object в props, в сцене — ссылки с позами. Снимите, чтобы влить объекты в геометрию ячеек', when: (m) => !!m.instances },\n ],\n run: async (model, v, ctx) => {\n const { exportXraySdk } = await import('./export');\n const name = String(v.name || ctx.baseName);\n const r = await exportXraySdk(model, {\n name, cell: v.cell === '' || v.cell === undefined ? undefined : +v.cell, scale: +v.scale || 1, hidden: ctx.hidden,\n texture: (i) => ctx.texture(i), xraySource: ctx.sourceEngine === 'xray-v1', instances: v.instances !== false,\n });\n return { ...r, name: `${name}_xray_sdk` };\n },\n },\n ],\n games: [\n game('stalker-soc', 'S.T.A.L.K.E.R.: Тень Чернобыля', 'OGF v4. Анимации — внутри модели или во внешнем .omf. Архивы gamedata.db* открываются через «Папку игры» (ключ таблиц файлов берётся из bin/XR_3DA.exe вашей игры) или кнопкой «Открыть .db…» вместе с XR_3DA.exe; .xdb модов и распакованная gamedata тоже открываются. Внешние .omf из ссылок модели подключаются сами, если открыта папка gamedata или архивы; анимации торса играют вместе с ногами (выбор — «Другие части тела»). Уровни — с травой из level.details; исходники SDK: .object со скелетом и анимациями, .skl/.skls.'),\n game('stalker-cs', 'S.T.A.L.K.E.R.: Чистое небо', 'OGF v4. Откройте resources.db0–db2 (модели и текстуры) и patches/xpatch_*.db или папку gamedata. Внешние .omf из ссылок модели подключаются сами, если открыта папка gamedata или архивы; анимации торса играют вместе с ногами (выбор — «Другие части тела»). Уровни — с травой из level.details; исходники SDK: .object со скелетом и анимациями, .skl/.skls.'),\n game('stalker-cop', 'S.T.A.L.K.E.R.: Зов Припяти', 'OGF v4, в том числе 3/4-весовые вершины. Архивы .db — как у Чистого неба. Внешние .omf из ссылок модели подключаются сами, если открыта папка gamedata или архивы; анимации торса играют вместе с ногами (выбор — «Другие части тела»). Уровни — с травой из level.details; исходники SDK: .object со скелетом и анимациями, .skl/.skls.'),\n ],\n});\n\n/** Export of a model/level into X-Ray SDK sources (.object, .level, textures); the module is loaded on demand. */\nexport async function exportXraySdk(...args: Parameters<typeof import('./export').exportXraySdk>): ReturnType<typeof import('./export').exportXraySdk> {\n return (await import('./export')).exportXraySdk(...args);\n}\nexport type { XrayExportOptions, XrayExportResult, TextureForExport } from './export';\nexport { autoLayers, XRAY_FX_PART, type XrayMotion, type XrayTrack } from './motion';\n"],"names":["exportXraySdk"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { LoadContext } from '@gmvjs/core/plugin';
2
+ import type { Model, VFile } from '@gmvjs/core/types';
3
+ interface VB {
4
+ stride: number;
5
+ count: number;
6
+ data: DataView;
7
+ pos: number;
8
+ posType: number;
9
+ nor: number;
10
+ tc: number;
11
+ tcType: number;
12
+ tan: number;
13
+ bin: number;
14
+ }
15
+ /** Vertex buffers: the declaration up to D3DDECL_END (stream 0xFF), the vertex count, the data. */
16
+ export declare function parseVBs(u: Uint8Array): VB[];
17
+ export declare function parseIBs(u: Uint8Array): Uint16Array[];
18
+ export interface LevelVisual {
19
+ type: number;
20
+ shader: number;
21
+ vb: number;
22
+ vbOff: number;
23
+ vbCount: number;
24
+ ib: number;
25
+ ibOff: number;
26
+ ibCount: number;
27
+ xform: Float32Array | null;
28
+ }
29
+ /** Index window of a progressive mesh (SWI): offset and triangle count of the most detailed level. */
30
+ type Slide = [offset: number, tris: number];
31
+ /** The level's shared SWIs (level.geom, chunk 11; CS/CoP): TREE_PM trees refer to them by number (visual chunk 20). */
32
+ export declare function parseSwis(u: Uint8Array): Array<Slide | null>;
33
+ export declare function parseVisuals(u: Uint8Array, swis?: Array<Slide | null>): LevelVisual[];
34
+ /** Level shader string "shader/texture,lightmap…" -> [shader, texture]. */
35
+ export declare function splitShader(s: string): [string, string];
36
+ /** Level: visuals with geometry, merged by shader. */
37
+ export declare function levelModel(level: Uint8Array, geom: Uint8Array, name: string): Model;
38
+ export declare function loadLevel(main: VFile, ctx: LoadContext): Promise<Model>;
39
+ export {};
package/dist/level.js ADDED
@@ -0,0 +1,234 @@
1
+ import { boundsOf } from "@gmvjs/core/geometry";
2
+ import { lzhufDecompress } from "./lzhuf.js";
3
+ const FSL = { HEADER: 1, SHADERS: 2, VISUALS: 3, VB: 9, IB: 10, SWIS: 11 };
4
+ const OGFL = { HEADER: 1, SWIDATA: 6, VCONTAINER: 7, ICONTAINER: 8, TREEDEF2: 12, SWICONTAINER: 20, GCONTAINER: 21 };
5
+ const GEOM_TYPES = /* @__PURE__ */ new Set([0, 2, 7, 11]);
6
+ function chunks(u8) {
7
+ const dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength), out = [];
8
+ let p = 0;
9
+ while (p + 8 <= u8.length) {
10
+ const raw = dv.getUint32(p, true), size = dv.getUint32(p + 4, true);
11
+ if (p + 8 + size > u8.length) break;
12
+ let data = u8.subarray(p + 8, p + 8 + size);
13
+ if (raw & 2147483648) data = lzhufDecompress(data);
14
+ out.push({ id: raw & 2147483647, data });
15
+ p += 8 + size;
16
+ }
17
+ return out;
18
+ }
19
+ const dvOf = (u) => new DataView(u.buffer, u.byteOffset, u.byteLength);
20
+ function parseVBs(u) {
21
+ const dv = dvOf(u), n = dv.getUint32(0, true), out = [];
22
+ const SIZE = { 0: 4, 1: 8, 2: 12, 3: 16, 4: 4, 5: 4, 6: 4, 7: 8, 8: 4, 9: 8, 10: 4, 11: 8, 12: 4, 13: 8, 15: 4, 16: 8 };
23
+ let p = 4;
24
+ for (let k = 0; k < n; k++) {
25
+ let stride = 0, pos = -1, posType = 2, nor = -1, tc = -1, tcType = 1, tan = -1, bin = -1;
26
+ for (; ; ) {
27
+ const stream = dv.getUint16(p, true), off = dv.getUint16(p + 2, true), type = dv.getUint8(p + 4), usage = dv.getUint8(p + 6), ui = dv.getUint8(p + 7);
28
+ p += 8;
29
+ if (stream === 255) break;
30
+ stride = Math.max(stride, off + (SIZE[type] ?? 4));
31
+ if (usage === 0 && pos < 0) {
32
+ pos = off;
33
+ posType = type;
34
+ } else if (usage === 3) nor = off;
35
+ else if (usage === 6) tan = off;
36
+ else if (usage === 7) bin = off;
37
+ else if (usage === 5 && ui === 0) {
38
+ tc = off;
39
+ tcType = type;
40
+ }
41
+ }
42
+ const count = dv.getUint32(p, true);
43
+ p += 4;
44
+ out.push({ stride, count, data: new DataView(u.buffer, u.byteOffset + p, stride * count), pos, posType, nor, tc, tcType, tan, bin });
45
+ p += stride * count;
46
+ }
47
+ return out;
48
+ }
49
+ function parseIBs(u) {
50
+ const dv = dvOf(u), n = dv.getUint32(0, true), out = [];
51
+ let p = 4;
52
+ for (let k = 0; k < n; k++) {
53
+ const cnt = dv.getUint32(p, true);
54
+ p += 4;
55
+ const a = new Uint16Array(cnt);
56
+ for (let i = 0; i < cnt; i++) a[i] = dv.getUint16(p + i * 2, true);
57
+ out.push(a);
58
+ p += cnt * 2;
59
+ }
60
+ return out;
61
+ }
62
+ const firstSlide = (d, p) => p + 28 <= d.byteLength && d.getUint32(p + 16, true) > 0 ? [d.getUint32(p + 20, true), d.getUint16(p + 24, true)] : null;
63
+ function parseSwis(u) {
64
+ const d = dvOf(u), n = d.getUint32(0, true), out = [];
65
+ let p = 4;
66
+ for (let k = 0; k < n && p + 20 <= u.length; k++) {
67
+ out.push(firstSlide(d, p));
68
+ p += 20 + d.getUint32(p + 16, true) * 8;
69
+ }
70
+ return out;
71
+ }
72
+ function parseVisuals(u, swis = []) {
73
+ const out = [];
74
+ for (const v of chunks(u)) {
75
+ const sub = chunks(v.data), hdr = sub.find((c) => c.id === OGFL.HEADER);
76
+ if (!hdr) continue;
77
+ const h = dvOf(hdr.data), type = h.getUint8(1), shader = h.getUint16(2, true);
78
+ const g = sub.find((c) => c.id === OGFL.GCONTAINER), vc = sub.find((c) => c.id === OGFL.VCONTAINER), ic = sub.find((c) => c.id === OGFL.ICONTAINER);
79
+ let vb = -1, vbOff = 0, vbCount = 0, ib = -1, ibOff = 0, ibCount = 0;
80
+ if (g) {
81
+ const d = dvOf(g.data);
82
+ [vb, vbOff, vbCount, ib, ibOff, ibCount] = [0, 4, 8, 12, 16, 20].map((o) => d.getUint32(o, true));
83
+ } else if (vc && ic) {
84
+ const a = dvOf(vc.data), b = dvOf(ic.data);
85
+ [vb, vbOff, vbCount] = [a.getUint32(0, true), a.getUint32(4, true), a.getUint32(8, true)];
86
+ [ib, ibOff, ibCount] = [b.getUint32(0, true), b.getUint32(4, true), b.getUint32(8, true)];
87
+ }
88
+ const swi = sub.find((c) => c.id === OGFL.SWIDATA), swc = sub.find((c) => c.id === OGFL.SWICONTAINER);
89
+ const slide = swi ? firstSlide(dvOf(swi.data), 0) : swc && swc.data.length >= 4 ? swis[dvOf(swc.data).getUint32(0, true)] ?? null : null;
90
+ if (slide) {
91
+ const [off, tris] = slide;
92
+ if (off + tris * 3 <= ibCount) {
93
+ ibOff += off;
94
+ ibCount = tris * 3;
95
+ }
96
+ }
97
+ let xform = null;
98
+ const td = sub.find((c) => c.id === OGFL.TREEDEF2);
99
+ if (td) {
100
+ const d = dvOf(td.data);
101
+ xform = new Float32Array(16);
102
+ for (let k = 0; k < 16; k++) xform[k] = d.getFloat32(k * 4, true);
103
+ }
104
+ out.push({ type, shader, vb, vbOff, vbCount, ib, ibOff, ibCount, xform });
105
+ }
106
+ return out;
107
+ }
108
+ function splitShader(s) {
109
+ const i = s.indexOf("/");
110
+ return i < 0 ? [s, ""] : [s.slice(0, i), s.slice(i + 1).split(",")[0]];
111
+ }
112
+ function levelModel(level, geom, name) {
113
+ const top = chunks(level), g = chunks(geom);
114
+ const sh = top.find((c) => c.id === FSL.SHADERS), vis = top.find((c) => c.id === FSL.VISUALS), hd = top.find((c) => c.id === FSL.HEADER);
115
+ const vbc = g.find((c) => c.id === FSL.VB), ibc = g.find((c) => c.id === FSL.IB);
116
+ if (!sh || !vis || !vbc || !ibc) throw new Error("Не уровень X-Ray: нет шейдеров, визуалов или буферов level.geom");
117
+ const shaders = [];
118
+ {
119
+ const d = dvOf(sh.data), n = d.getUint32(0, true);
120
+ let p = 4;
121
+ for (let k = 0; k < n; k++) {
122
+ let s = "";
123
+ while (p < sh.data.length && sh.data[p]) s += String.fromCharCode(sh.data[p++]);
124
+ p++;
125
+ shaders.push(s);
126
+ }
127
+ }
128
+ const swc = g.find((c) => c.id === FSL.SWIS);
129
+ const vbs = parseVBs(vbc.data), ibs = parseIBs(ibc.data), visuals = parseVisuals(vis.data, swc ? parseSwis(swc.data) : []);
130
+ const groups = /* @__PURE__ */ new Map();
131
+ let used = 0, skipped = 0;
132
+ for (const v of visuals) {
133
+ if (!GEOM_TYPES.has(v.type) || v.vb < 0) continue;
134
+ const vb = vbs[v.vb], ib = ibs[v.ib];
135
+ if (!vb || !ib || vb.pos < 0 || v.vbOff + v.vbCount > vb.count || v.ibOff + v.ibCount > ib.length) {
136
+ skipped++;
137
+ continue;
138
+ }
139
+ if (vb.posType !== 2 && vb.posType !== 3) {
140
+ skipped++;
141
+ continue;
142
+ }
143
+ let gr = groups.get(v.shader);
144
+ if (!gr) groups.set(v.shader, gr = { pos: [], nor: [], uv: [], idx: [] });
145
+ const base = gr.pos.length / 3, d = vb.data, X = v.xform;
146
+ for (let i = 0; i < v.vbCount; i++) {
147
+ const o = (v.vbOff + i) * vb.stride;
148
+ let x = d.getFloat32(o + vb.pos, true), y = d.getFloat32(o + vb.pos + 4, true), z = d.getFloat32(o + vb.pos + 8, true);
149
+ let nx = 0, ny = 1, nz = 0;
150
+ if (vb.nor >= 0) {
151
+ nx = d.getUint8(o + vb.nor + 2) / 255 * 2 - 1;
152
+ ny = d.getUint8(o + vb.nor + 1) / 255 * 2 - 1;
153
+ nz = d.getUint8(o + vb.nor) / 255 * 2 - 1;
154
+ }
155
+ if (X) {
156
+ [x, y, z] = [x * X[0] + y * X[4] + z * X[8] + X[12], x * X[1] + y * X[5] + z * X[9] + X[13], x * X[2] + y * X[6] + z * X[10] + X[14]];
157
+ [nx, ny, nz] = [nx * X[0] + ny * X[4] + nz * X[8], nx * X[1] + ny * X[5] + nz * X[9], nx * X[2] + ny * X[6] + nz * X[10]];
158
+ }
159
+ const l = Math.hypot(nx, ny, nz) || 1;
160
+ gr.pos.push(x, y, z);
161
+ gr.nor.push(nx / l, ny / l, nz / l);
162
+ let u = 0, w = 0;
163
+ if (vb.tc >= 0) {
164
+ if (vb.tcType === 1) {
165
+ u = d.getFloat32(o + vb.tc, true);
166
+ w = d.getFloat32(o + vb.tc + 4, true);
167
+ } else if (vb.tcType === 7) {
168
+ u = d.getInt16(o + vb.tc, true) / 2048;
169
+ w = d.getInt16(o + vb.tc + 2, true) / 2048;
170
+ } else {
171
+ const du = vb.tan >= 0 ? d.getUint8(o + vb.tan + 3) / 255 : 0, dv = vb.bin >= 0 ? d.getUint8(o + vb.bin + 3) / 255 : 0;
172
+ u = (d.getInt16(o + vb.tc, true) + du) / 1024;
173
+ w = (d.getInt16(o + vb.tc + 2, true) + dv) / 1024;
174
+ }
175
+ }
176
+ gr.uv.push(u, w);
177
+ }
178
+ for (let i = 0; i < v.ibCount; i++) gr.idx.push(base + ib[v.ibOff + i]);
179
+ used++;
180
+ }
181
+ const meshes = [...groups].map(([si, gr]) => {
182
+ const [shader, texture] = splitShader(shaders[si] ?? ""), n = gr.pos.length / 3;
183
+ return {
184
+ name: `${texture || "?"} · ${shader}`,
185
+ texture,
186
+ shader,
187
+ vertexCount: n,
188
+ pos: Float32Array.from(gr.pos),
189
+ nor: Float32Array.from(gr.nor),
190
+ uv: Float32Array.from(gr.uv),
191
+ bone: new Float32Array(n * 4),
192
+ weight: new Float32Array(n * 4).map((_, i) => i % 4 === 0 ? 1 : 0),
193
+ indices: Uint32Array.from(gr.idx),
194
+ skinned: false
195
+ };
196
+ }).filter((m) => m.indices.length);
197
+ const ver = hd ? dvOf(hd.data).getUint16(0, true) : 0;
198
+ const info = [`Уровень X-Ray (скомпилированный, версия ${ver}): ${name}`, `Визуалов ${visuals.length}, показано ${used}${skipped ? `, пропущено ${skipped}` : ""}; буферов VB ${vbs.length}, IB ${ibs.length}; шейдеров ${shaders.length}`, `Мешей (по шейдерам) ${meshes.length}, вершин ${meshes.reduce((a, m) => a + m.vertexCount, 0)}, треугольников ${meshes.reduce((a, m) => a + m.indices.length / 3, 0)}`];
199
+ return { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: true, info };
200
+ }
201
+ async function loadLevel(main, ctx) {
202
+ const geom = await ctx.sibling("level.geom");
203
+ if (!geom) throw new Error("Рядом с level нет level.geom");
204
+ const dir = (main.path ?? "").split("/").slice(-2, -1)[0] ?? "";
205
+ const model = levelModel(new Uint8Array(await main.arrayBuffer()), new Uint8Array(geom), dir || main.name);
206
+ const det = await ctx.sibling("level.details");
207
+ if (det) {
208
+ try {
209
+ const { parseDetails, parseCForm, buildDetails } = await import("./details.js");
210
+ const map = parseDetails(new Uint8Array(det)), cf = await ctx.sibling("level.cform");
211
+ if (!cf) model.info.push("Трава: рядом нет level.cform — высоты не найти, трава не показана");
212
+ else {
213
+ const r = buildDetails(map, parseCForm(new Uint8Array(cf)));
214
+ model.meshes.push(...r.meshes);
215
+ model.bounds = boundsOf(model.meshes);
216
+ const tris = r.meshes.reduce((a, m) => a + m.indices.length / 3, 0);
217
+ model.info.push(`Трава (level.details): моделей ${map.models.length}, поставлено ${r.items} из ${r.candidates}${r.keep < 1 ? ` (прорежено до ${Math.round(r.keep * 100)} %)` : ""}, треугольников ${tris}`);
218
+ }
219
+ } catch (e) {
220
+ model.info.push(`Трава: ${e.message}`);
221
+ }
222
+ }
223
+ return model;
224
+ }
225
+ export {
226
+ levelModel,
227
+ loadLevel,
228
+ parseIBs,
229
+ parseSwis,
230
+ parseVBs,
231
+ parseVisuals,
232
+ splitShader
233
+ };
234
+ //# sourceMappingURL=level.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"level.js","sources":["../src/level.ts"],"sourcesContent":["// A compiled X-Ray level (levels/<level>/level + level.geom): static geometry for viewing and comparison.\n// level: 1 header, 2 shaders (\"shader/texture,lightmap\"), 3 visuals (OGF-like chunks), 8 sectors.\n// level.geom: 9 vertex buffers (a D3DVERTEXELEMENT9 declaration + data), 10 index buffers.\n// A visual refers to buffers with a GCONTAINER chunk (CS/CoP, 21) or VCONTAINER/ICONTAINER (SoC, 7/8); indices are relative to its own VB range.\nimport { boundsOf } from '@gmvjs/core/geometry';\nimport { lzhufDecompress } from './lzhuf';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport type { Mesh, Model, VFile } from '@gmvjs/core/types';\n\nconst FSL = { HEADER: 1, SHADERS: 2, VISUALS: 3, VB: 9, IB: 10, SWIS: 11 } as const;\nconst OGFL = { HEADER: 1, SWIDATA: 6, VCONTAINER: 7, ICONTAINER: 8, CHILDREN_L: 10, TREEDEF2: 12, SWICONTAINER: 20, GCONTAINER: 21 } as const;\n/** Visual types with their own geometry: regular, progressive, trees. */\nconst GEOM_TYPES = new Set([0, 2, 7, 11]);\n\ninterface Chunk { id: number; data: Uint8Array }\nfunction chunks(u8: Uint8Array): Chunk[] {\n const dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength), out: Chunk[] = [];\n let p = 0;\n while (p + 8 <= u8.length) {\n const raw = dv.getUint32(p, true), size = dv.getUint32(p + 4, true);\n if (p + 8 + size > u8.length) break;\n let data = u8.subarray(p + 8, p + 8 + size);\n if (raw & 0x80000000) data = lzhufDecompress(data);\n out.push({ id: raw & 0x7fffffff, data });\n p += 8 + size;\n }\n return out;\n}\nconst dvOf = (u: Uint8Array) => new DataView(u.buffer, u.byteOffset, u.byteLength);\n\ninterface VB { stride: number; count: number; data: DataView; pos: number; posType: number; nor: number; tc: number; tcType: number; tan: number; bin: number }\n\n/** Vertex buffers: the declaration up to D3DDECL_END (stream 0xFF), the vertex count, the data. */\nexport function parseVBs(u: Uint8Array): VB[] {\n const dv = dvOf(u), n = dv.getUint32(0, true), out: VB[] = [];\n const SIZE: Record<number, number> = { 0: 4, 1: 8, 2: 12, 3: 16, 4: 4, 5: 4, 6: 4, 7: 8, 8: 4, 9: 8, 10: 4, 11: 8, 12: 4, 13: 8, 15: 4, 16: 8 };\n let p = 4;\n for (let k = 0; k < n; k++) {\n let stride = 0, pos = -1, posType = 2, nor = -1, tc = -1, tcType = 1, tan = -1, bin = -1;\n for (;;) {\n const stream = dv.getUint16(p, true), off = dv.getUint16(p + 2, true), type = dv.getUint8(p + 4), usage = dv.getUint8(p + 6), ui = dv.getUint8(p + 7);\n p += 8;\n if (stream === 0xff) break;\n stride = Math.max(stride, off + (SIZE[type] ?? 4));\n if (usage === 0 && pos < 0) { pos = off; posType = type; }\n else if (usage === 3) nor = off;\n else if (usage === 6) tan = off;\n else if (usage === 7) bin = off;\n else if (usage === 5 && ui === 0) { tc = off; tcType = type; }\n }\n const count = dv.getUint32(p, true);\n p += 4;\n out.push({ stride, count, data: new DataView(u.buffer, u.byteOffset + p, stride * count), pos, posType, nor, tc, tcType, tan, bin });\n p += stride * count;\n }\n return out;\n}\n\nexport function parseIBs(u: Uint8Array): Uint16Array[] {\n const dv = dvOf(u), n = dv.getUint32(0, true), out: Uint16Array[] = [];\n let p = 4;\n for (let k = 0; k < n; k++) {\n const cnt = dv.getUint32(p, true);\n p += 4;\n const a = new Uint16Array(cnt);\n for (let i = 0; i < cnt; i++) a[i] = dv.getUint16(p + i * 2, true);\n out.push(a);\n p += cnt * 2;\n }\n return out;\n}\n\nexport interface LevelVisual { type: number; shader: number; vb: number; vbOff: number; vbCount: number; ib: number; ibOff: number; ibCount: number; xform: Float32Array | null }\n\n/** Index window of a progressive mesh (SWI): offset and triangle count of the most detailed level. */\ntype Slide = [offset: number, tris: number];\n/** The first SWI window: 4×u32 reserved, u32 window count, windows {u32 offset, u16 triangles, u16 vertices}. */\nconst firstSlide = (d: DataView, p: number): Slide | null => (p + 28 <= d.byteLength && d.getUint32(p + 16, true) > 0 ? [d.getUint32(p + 20, true), d.getUint16(p + 24, true)] : null);\n\n/** The level's shared SWIs (level.geom, chunk 11; CS/CoP): TREE_PM trees refer to them by number (visual chunk 20). */\nexport function parseSwis(u: Uint8Array): Array<Slide | null> {\n const d = dvOf(u), n = d.getUint32(0, true), out: Array<Slide | null> = [];\n let p = 4;\n for (let k = 0; k < n && p + 20 <= u.length; k++) { out.push(firstSlide(d, p)); p += 20 + d.getUint32(p + 16, true) * 8; }\n return out;\n}\n\nexport function parseVisuals(u: Uint8Array, swis: Array<Slide | null> = []): LevelVisual[] {\n const out: LevelVisual[] = [];\n for (const v of chunks(u)) {\n const sub = chunks(v.data), hdr = sub.find((c) => c.id === OGFL.HEADER);\n if (!hdr) continue;\n const h = dvOf(hdr.data), type = h.getUint8(1), shader = h.getUint16(2, true);\n const g = sub.find((c) => c.id === OGFL.GCONTAINER), vc = sub.find((c) => c.id === OGFL.VCONTAINER), ic = sub.find((c) => c.id === OGFL.ICONTAINER);\n let vb = -1, vbOff = 0, vbCount = 0, ib = -1, ibOff = 0, ibCount = 0;\n if (g) { const d = dvOf(g.data); [vb, vbOff, vbCount, ib, ibOff, ibCount] = [0, 4, 8, 12, 16, 20].map((o) => d.getUint32(o, true)) as [number, number, number, number, number, number]; }\n else if (vc && ic) { const a = dvOf(vc.data), b = dvOf(ic.data); [vb, vbOff, vbCount] = [a.getUint32(0, true), a.getUint32(4, true), a.getUint32(8, true)]; [ib, ibOff, ibCount] = [b.getUint32(0, true), b.getUint32(4, true), b.getUint32(8, true)]; }\n // progressive mesh: the index buffer holds every level of detail; take the first SWI \"window\", the most detailed one\n const swi = sub.find((c) => c.id === OGFL.SWIDATA), swc = sub.find((c) => c.id === OGFL.SWICONTAINER);\n const slide = swi ? firstSlide(dvOf(swi.data), 0) : swc && swc.data.length >= 4 ? swis[dvOf(swc.data).getUint32(0, true)] ?? null : null;\n if (slide) { const [off, tris] = slide; if (off + tris * 3 <= ibCount) { ibOff += off; ibCount = tris * 3; } }\n let xform: Float32Array | null = null;\n const td = sub.find((c) => c.id === OGFL.TREEDEF2);\n if (td) { const d = dvOf(td.data); xform = new Float32Array(16); for (let k = 0; k < 16; k++) xform[k] = d.getFloat32(k * 4, true); }\n out.push({ type, shader, vb, vbOff, vbCount, ib, ibOff, ibCount, xform });\n }\n return out;\n}\n\n/** Level shader string \"shader/texture,lightmap…\" -> [shader, texture]. */\nexport function splitShader(s: string): [string, string] {\n const i = s.indexOf('/');\n return i < 0 ? [s, ''] : [s.slice(0, i), s.slice(i + 1).split(',')[0]!];\n}\n\n/** Level: visuals with geometry, merged by shader. */\nexport function levelModel(level: Uint8Array, geom: Uint8Array, name: string): Model {\n const top = chunks(level), g = chunks(geom);\n const sh = top.find((c) => c.id === FSL.SHADERS), vis = top.find((c) => c.id === FSL.VISUALS), hd = top.find((c) => c.id === FSL.HEADER);\n const vbc = g.find((c) => c.id === FSL.VB), ibc = g.find((c) => c.id === FSL.IB);\n if (!sh || !vis || !vbc || !ibc) throw new Error('Не уровень X-Ray: нет шейдеров, визуалов или буферов level.geom');\n const shaders: string[] = [];\n { const d = dvOf(sh.data), n = d.getUint32(0, true); let p = 4; for (let k = 0; k < n; k++) { let s = ''; while (p < sh.data.length && sh.data[p]) s += String.fromCharCode(sh.data[p++]!); p++; shaders.push(s); } }\n const swc = g.find((c) => c.id === FSL.SWIS);\n const vbs = parseVBs(vbc.data), ibs = parseIBs(ibc.data), visuals = parseVisuals(vis.data, swc ? parseSwis(swc.data) : []);\n const groups = new Map<number, { pos: number[]; nor: number[]; uv: number[]; idx: number[] }>();\n let used = 0, skipped = 0;\n for (const v of visuals) {\n if (!GEOM_TYPES.has(v.type) || v.vb < 0) continue;\n const vb = vbs[v.vb], ib = ibs[v.ib];\n if (!vb || !ib || vb.pos < 0 || v.vbOff + v.vbCount > vb.count || v.ibOff + v.ibCount > ib.length) { skipped++; continue; }\n if (vb.posType !== 2 && vb.posType !== 3) { skipped++; continue; } // compressed positions (CoP trees): later\n let gr = groups.get(v.shader);\n if (!gr) groups.set(v.shader, (gr = { pos: [], nor: [], uv: [], idx: [] }));\n const base = gr.pos.length / 3, d = vb.data, X = v.xform;\n for (let i = 0; i < v.vbCount; i++) {\n const o = (v.vbOff + i) * vb.stride;\n let x = d.getFloat32(o + vb.pos, true), y = d.getFloat32(o + vb.pos + 4, true), z = d.getFloat32(o + vb.pos + 8, true);\n let nx = 0, ny = 1, nz = 0;\n if (vb.nor >= 0) { nx = (d.getUint8(o + vb.nor + 2) / 255) * 2 - 1; ny = (d.getUint8(o + vb.nor + 1) / 255) * 2 - 1; nz = (d.getUint8(o + vb.nor) / 255) * 2 - 1; }\n if (X) { // row vectors: v·M\n [x, y, z] = [x * X[0]! + y * X[4]! + z * X[8]! + X[12]!, x * X[1]! + y * X[5]! + z * X[9]! + X[13]!, x * X[2]! + y * X[6]! + z * X[10]! + X[14]!];\n [nx, ny, nz] = [nx * X[0]! + ny * X[4]! + nz * X[8]!, nx * X[1]! + ny * X[5]! + nz * X[9]!, nx * X[2]! + ny * X[6]! + nz * X[10]!];\n }\n const l = Math.hypot(nx, ny, nz) || 1;\n gr.pos.push(x, y, z); gr.nor.push(nx / l, ny / l, nz / l);\n let u = 0, w = 0;\n if (vb.tc >= 0) {\n if (vb.tcType === 1) { u = d.getFloat32(o + vb.tc, true); w = d.getFloat32(o + vb.tc + 4, true); }\n // SHORT4 is used by tree and MU-model vertices (vehicles, barrels, rocks: TREE_ST/PM visuals with a matrix): UV × 2048,\n // z, w are wind coefficients (FTreeVisual); checked against vehicle atlases in all three games (range 0..2048)\n else if (vb.tcType === 7) { u = d.getInt16(o + vb.tc, true) / 2048; w = d.getInt16(o + vb.tc + 2, true) / 2048; }\n else { // SHORT2: coordinate × 1024, the fractional correction is in the alpha of the tangent and binormal\n const du = vb.tan >= 0 ? d.getUint8(o + vb.tan + 3) / 255 : 0, dv = vb.bin >= 0 ? d.getUint8(o + vb.bin + 3) / 255 : 0;\n u = (d.getInt16(o + vb.tc, true) + du) / 1024; w = (d.getInt16(o + vb.tc + 2, true) + dv) / 1024;\n }\n }\n gr.uv.push(u, w);\n }\n for (let i = 0; i < v.ibCount; i++) gr.idx.push(base + ib[v.ibOff + i]!);\n used++;\n }\n const meshes: Mesh[] = [...groups].map(([si, gr]) => {\n const [shader, texture] = splitShader(shaders[si] ?? ''), n = gr.pos.length / 3;\n return {\n name: `${texture || '?'} · ${shader}`, texture, shader, vertexCount: n, pos: Float32Array.from(gr.pos), nor: Float32Array.from(gr.nor), uv: Float32Array.from(gr.uv),\n bone: new Float32Array(n * 4), weight: new Float32Array(n * 4).map((_, i) => (i % 4 === 0 ? 1 : 0)), indices: Uint32Array.from(gr.idx), skinned: false,\n };\n }).filter((m) => m.indices.length);\n const ver = hd ? dvOf(hd.data).getUint16(0, true) : 0;\n const info = [`Уровень X-Ray (скомпилированный, версия ${ver}): ${name}`, `Визуалов ${visuals.length}, показано ${used}${skipped ? `, пропущено ${skipped}` : ''}; буферов VB ${vbs.length}, IB ${ibs.length}; шейдеров ${shaders.length}`, `Мешей (по шейдерам) ${meshes.length}, вершин ${meshes.reduce((a, m) => a + m.vertexCount, 0)}, треугольников ${meshes.reduce((a, m) => a + m.indices.length / 3, 0)}`];\n return { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: true, info };\n}\n\nexport async function loadLevel(main: VFile, ctx: LoadContext): Promise<Model> {\n const geom = await ctx.sibling('level.geom');\n if (!geom) throw new Error('Рядом с level нет level.geom');\n const dir = (main.path ?? '').split('/').slice(-2, -1)[0] ?? '';\n const model = levelModel(new Uint8Array(await main.arrayBuffer()), new Uint8Array(geom), dir || main.name);\n // grass: level.details + heights from level.cform\n const det = await ctx.sibling('level.details');\n if (det) {\n try {\n const { parseDetails, parseCForm, buildDetails } = await import('./details');\n const map = parseDetails(new Uint8Array(det)), cf = await ctx.sibling('level.cform');\n if (!cf) model.info.push('Трава: рядом нет level.cform — высоты не найти, трава не показана');\n else {\n const r = buildDetails(map, parseCForm(new Uint8Array(cf)));\n model.meshes.push(...r.meshes);\n model.bounds = boundsOf(model.meshes);\n const tris = r.meshes.reduce((a, m) => a + m.indices.length / 3, 0);\n model.info.push(`Трава (level.details): моделей ${map.models.length}, поставлено ${r.items} из ${r.candidates}${r.keep < 1 ? ` (прорежено до ${Math.round(r.keep * 100)} %)` : ''}, треугольников ${tris}`);\n }\n } catch (e) { model.info.push(`Трава: ${(e as Error).message}`); }\n }\n return model;\n}\n"],"names":[],"mappings":";;AASA,MAAM,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,IAAI,MAAM,GAAA;AACtE,MAAM,OAAO,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,GAAG,YAAY,GAAmB,UAAU,IAAI,cAAc,IAAI,YAAY,GAAA;AAEhI,MAAM,iCAAiB,IAAI,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;AAGxC,SAAS,OAAO,IAAyB;AACvC,QAAM,KAAK,IAAI,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,MAAe,CAAA;AACjF,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,GAAG,QAAQ;AACzB,UAAM,MAAM,GAAG,UAAU,GAAG,IAAI,GAAG,OAAO,GAAG,UAAU,IAAI,GAAG,IAAI;AAClE,QAAI,IAAI,IAAI,OAAO,GAAG,OAAQ;AAC9B,QAAI,OAAO,GAAG,SAAS,IAAI,GAAG,IAAI,IAAI,IAAI;AAC1C,QAAI,MAAM,WAAY,QAAO,gBAAgB,IAAI;AACjD,QAAI,KAAK,EAAE,IAAI,MAAM,YAAY,MAAM;AACvC,SAAK,IAAI;AAAA,EACX;AACA,SAAO;AACT;AACA,MAAM,OAAO,CAAC,MAAkB,IAAI,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU;AAK1E,SAAS,SAAS,GAAqB;AAC5C,QAAM,KAAK,KAAK,CAAC,GAAG,IAAI,GAAG,UAAU,GAAG,IAAI,GAAG,MAAY,CAAA;AAC3D,QAAM,OAA+B,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAA;AAC5I,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,SAAS,GAAG,MAAM,IAAI,UAAU,GAAG,MAAM,IAAI,KAAK,IAAI,SAAS,GAAG,MAAM,IAAI,MAAM;AACtF,eAAS;AACP,YAAM,SAAS,GAAG,UAAU,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,IAAI,CAAC,GAAG,QAAQ,GAAG,SAAS,IAAI,CAAC,GAAG,KAAK,GAAG,SAAS,IAAI,CAAC;AACpJ,WAAK;AACL,UAAI,WAAW,IAAM;AACrB,eAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,IAAI,KAAK,EAAE;AACjD,UAAI,UAAU,KAAK,MAAM,GAAG;AAAE,cAAM;AAAK,kBAAU;AAAA,MAAM,WAChD,UAAU,EAAG,OAAM;AAAA,eACnB,UAAU,EAAG,OAAM;AAAA,eACnB,UAAU,EAAG,OAAM;AAAA,eACnB,UAAU,KAAK,OAAO,GAAG;AAAE,aAAK;AAAK,iBAAS;AAAA,MAAM;AAAA,IAC/D;AACA,UAAM,QAAQ,GAAG,UAAU,GAAG,IAAI;AAClC,SAAK;AACL,QAAI,KAAK,EAAE,QAAQ,OAAO,MAAM,IAAI,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,SAAS,KAAK,GAAG,KAAK,SAAS,KAAK,IAAI,QAAQ,KAAK,KAAK;AACnI,SAAK,SAAS;AAAA,EAChB;AACA,SAAO;AACT;AAEO,SAAS,SAAS,GAA8B;AACrD,QAAM,KAAK,KAAK,CAAC,GAAG,IAAI,GAAG,UAAU,GAAG,IAAI,GAAG,MAAqB,CAAA;AACpE,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,MAAM,GAAG,UAAU,GAAG,IAAI;AAChC,SAAK;AACL,UAAM,IAAI,IAAI,YAAY,GAAG;AAC7B,aAAS,IAAI,GAAG,IAAI,KAAK,IAAK,GAAE,CAAC,IAAI,GAAG,UAAU,IAAI,IAAI,GAAG,IAAI;AACjE,QAAI,KAAK,CAAC;AACV,SAAK,MAAM;AAAA,EACb;AACA,SAAO;AACT;AAOA,MAAM,aAAa,CAAC,GAAa,MAA6B,IAAI,MAAM,EAAE,cAAc,EAAE,UAAU,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,UAAU,IAAI,IAAI,IAAI,GAAG,EAAE,UAAU,IAAI,IAAI,IAAI,CAAC,IAAI;AAG1K,SAAS,UAAU,GAAoC;AAC5D,QAAM,IAAI,KAAK,CAAC,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,MAA2B,CAAA;AACxE,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,MAAM,EAAE,QAAQ,KAAK;AAAE,QAAI,KAAK,WAAW,GAAG,CAAC,CAAC;AAAG,SAAK,KAAK,EAAE,UAAU,IAAI,IAAI,IAAI,IAAI;AAAA,EAAG;AACzH,SAAO;AACT;AAEO,SAAS,aAAa,GAAe,OAA4B,IAAmB;AACzF,QAAM,MAAqB,CAAA;AAC3B,aAAW,KAAK,OAAO,CAAC,GAAG;AACzB,UAAM,MAAM,OAAO,EAAE,IAAI,GAAG,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM;AACtE,QAAI,CAAC,IAAK;AACV,UAAM,IAAI,KAAK,IAAI,IAAI,GAAG,OAAO,EAAE,SAAS,CAAC,GAAG,SAAS,EAAE,UAAU,GAAG,IAAI;AAC5E,UAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,UAAU,GAAG,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,UAAU,GAAG,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,UAAU;AAClJ,QAAI,KAAK,IAAI,QAAQ,GAAG,UAAU,GAAG,KAAK,IAAI,QAAQ,GAAG,UAAU;AACnE,QAAI,GAAG;AAAE,YAAM,IAAI,KAAK,EAAE,IAAI;AAAG,OAAC,IAAI,OAAO,SAAS,IAAI,OAAO,OAAO,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;AAAA,IAAuD,WAC/K,MAAM,IAAI;AAAE,YAAM,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,KAAK,GAAG,IAAI;AAAG,OAAC,IAAI,OAAO,OAAO,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;AAAG,OAAC,IAAI,OAAO,OAAO,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;AAAA,IAAG;AAEvP,UAAM,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,YAAY;AACpG,UAAM,QAAQ,MAAM,WAAW,KAAK,IAAI,IAAI,GAAG,CAAC,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,KAAK,KAAK,IAAI,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC,KAAK,OAAO;AACpI,QAAI,OAAO;AAAE,YAAM,CAAC,KAAK,IAAI,IAAI;AAAO,UAAI,MAAM,OAAO,KAAK,SAAS;AAAE,iBAAS;AAAK,kBAAU,OAAO;AAAA,MAAG;AAAA,IAAE;AAC7G,QAAI,QAA6B;AACjC,UAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,QAAQ;AACjD,QAAI,IAAI;AAAE,YAAM,IAAI,KAAK,GAAG,IAAI;AAAG,cAAQ,IAAI,aAAa,EAAE;AAAG,eAAS,IAAI,GAAG,IAAI,IAAI,IAAK,OAAM,CAAC,IAAI,EAAE,WAAW,IAAI,GAAG,IAAI;AAAA,IAAG;AACpI,QAAI,KAAK,EAAE,MAAM,QAAQ,IAAI,OAAO,SAAS,IAAI,OAAO,SAAS,MAAA,CAAO;AAAA,EAC1E;AACA,SAAO;AACT;AAGO,SAAS,YAAY,GAA6B;AACvD,QAAM,IAAI,EAAE,QAAQ,GAAG;AACvB,SAAO,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,CAAE;AACxE;AAGO,SAAS,WAAW,OAAmB,MAAkB,MAAqB;AACnF,QAAM,MAAM,OAAO,KAAK,GAAG,IAAI,OAAO,IAAI;AAC1C,QAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,OAAO,GAAG,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,OAAO,GAAG,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,MAAM;AACvI,QAAM,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE,GAAG,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE;AAC/E,MAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAK,OAAM,IAAI,MAAM,iEAAiE;AAClH,QAAM,UAAoB,CAAA;AAC1B;AAAE,UAAM,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI;AAAG,QAAI,IAAI;AAAG,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAAE,UAAI,IAAI;AAAI,aAAO,IAAI,GAAG,KAAK,UAAU,GAAG,KAAK,CAAC,EAAG,MAAK,OAAO,aAAa,GAAG,KAAK,GAAG,CAAE;AAAG;AAAK,cAAQ,KAAK,CAAC;AAAA,IAAG;AAAA,EAAE;AACpN,QAAM,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,IAAI;AAC3C,QAAM,MAAM,SAAS,IAAI,IAAI,GAAG,MAAM,SAAS,IAAI,IAAI,GAAG,UAAU,aAAa,IAAI,MAAM,MAAM,UAAU,IAAI,IAAI,IAAI,EAAE;AACzH,QAAM,6BAAa,IAAA;AACnB,MAAI,OAAO,GAAG,UAAU;AACxB,aAAW,KAAK,SAAS;AACvB,QAAI,CAAC,WAAW,IAAI,EAAE,IAAI,KAAK,EAAE,KAAK,EAAG;AACzC,UAAM,KAAK,IAAI,EAAE,EAAE,GAAG,KAAK,IAAI,EAAE,EAAE;AACnC,QAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,SAAS,EAAE,QAAQ,EAAE,UAAU,GAAG,QAAQ;AAAE;AAAW;AAAA,IAAU;AAC1H,QAAI,GAAG,YAAY,KAAK,GAAG,YAAY,GAAG;AAAE;AAAW;AAAA,IAAU;AACjE,QAAI,KAAK,OAAO,IAAI,EAAE,MAAM;AAC5B,QAAI,CAAC,GAAI,QAAO,IAAI,EAAE,QAAS,KAAK,EAAE,KAAK,CAAA,GAAI,KAAK,IAAI,IAAI,CAAA,GAAI,KAAK,CAAA,GAAK;AAC1E,UAAM,OAAO,GAAG,IAAI,SAAS,GAAG,IAAI,GAAG,MAAM,IAAI,EAAE;AACnD,aAAS,IAAI,GAAG,IAAI,EAAE,SAAS,KAAK;AAClC,YAAM,KAAK,EAAE,QAAQ,KAAK,GAAG;AAC7B,UAAI,IAAI,EAAE,WAAW,IAAI,GAAG,KAAK,IAAI,GAAG,IAAI,EAAE,WAAW,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,EAAE,WAAW,IAAI,GAAG,MAAM,GAAG,IAAI;AACrH,UAAI,KAAK,GAAG,KAAK,GAAG,KAAK;AACzB,UAAI,GAAG,OAAO,GAAG;AAAE,aAAM,EAAE,SAAS,IAAI,GAAG,MAAM,CAAC,IAAI,MAAO,IAAI;AAAG,aAAM,EAAE,SAAS,IAAI,GAAG,MAAM,CAAC,IAAI,MAAO,IAAI;AAAG,aAAM,EAAE,SAAS,IAAI,GAAG,GAAG,IAAI,MAAO,IAAI;AAAA,MAAG;AAClK,UAAI,GAAG;AACL,SAAC,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAK,IAAI,EAAE,CAAC,IAAK,IAAI,EAAE,CAAC,IAAK,EAAE,EAAE,GAAI,IAAI,EAAE,CAAC,IAAK,IAAI,EAAE,CAAC,IAAK,IAAI,EAAE,CAAC,IAAK,EAAE,EAAE,GAAI,IAAI,EAAE,CAAC,IAAK,IAAI,EAAE,CAAC,IAAK,IAAI,EAAE,EAAE,IAAK,EAAE,EAAE,CAAE;AAChJ,SAAC,IAAI,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,IAAK,KAAK,EAAE,CAAC,IAAK,KAAK,EAAE,CAAC,GAAI,KAAK,EAAE,CAAC,IAAK,KAAK,EAAE,CAAC,IAAK,KAAK,EAAE,CAAC,GAAI,KAAK,EAAE,CAAC,IAAK,KAAK,EAAE,CAAC,IAAK,KAAK,EAAE,EAAE,CAAE;AAAA,MACnI;AACA,YAAM,IAAI,KAAK,MAAM,IAAI,IAAI,EAAE,KAAK;AACpC,SAAG,IAAI,KAAK,GAAG,GAAG,CAAC;AAAG,SAAG,IAAI,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AACxD,UAAI,IAAI,GAAG,IAAI;AACf,UAAI,GAAG,MAAM,GAAG;AACd,YAAI,GAAG,WAAW,GAAG;AAAE,cAAI,EAAE,WAAW,IAAI,GAAG,IAAI,IAAI;AAAG,cAAI,EAAE,WAAW,IAAI,GAAG,KAAK,GAAG,IAAI;AAAA,QAAG,WAGxF,GAAG,WAAW,GAAG;AAAE,cAAI,EAAE,SAAS,IAAI,GAAG,IAAI,IAAI,IAAI;AAAM,cAAI,EAAE,SAAS,IAAI,GAAG,KAAK,GAAG,IAAI,IAAI;AAAA,QAAM,OAC3G;AACH,gBAAM,KAAK,GAAG,OAAO,IAAI,EAAE,SAAS,IAAI,GAAG,MAAM,CAAC,IAAI,MAAM,GAAG,KAAK,GAAG,OAAO,IAAI,EAAE,SAAS,IAAI,GAAG,MAAM,CAAC,IAAI,MAAM;AACrH,eAAK,EAAE,SAAS,IAAI,GAAG,IAAI,IAAI,IAAI,MAAM;AAAM,eAAK,EAAE,SAAS,IAAI,GAAG,KAAK,GAAG,IAAI,IAAI,MAAM;AAAA,QAC9F;AAAA,MACF;AACA,SAAG,GAAG,KAAK,GAAG,CAAC;AAAA,IACjB;AACA,aAAS,IAAI,GAAG,IAAI,EAAE,SAAS,IAAK,IAAG,IAAI,KAAK,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAE;AACvE;AAAA,EACF;AACA,QAAM,SAAiB,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM;AACnD,UAAM,CAAC,QAAQ,OAAO,IAAI,YAAY,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,GAAG,IAAI,SAAS;AAC9E,WAAO;AAAA,MACL,MAAM,GAAG,WAAW,GAAG,MAAM,MAAM;AAAA,MAAI;AAAA,MAAS;AAAA,MAAQ,aAAa;AAAA,MAAG,KAAK,aAAa,KAAK,GAAG,GAAG;AAAA,MAAG,KAAK,aAAa,KAAK,GAAG,GAAG;AAAA,MAAG,IAAI,aAAa,KAAK,GAAG,EAAE;AAAA,MACnK,MAAM,IAAI,aAAa,IAAI,CAAC;AAAA,MAAG,QAAQ,IAAI,aAAa,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,MAAO,IAAI,MAAM,IAAI,IAAI,CAAE;AAAA,MAAG,SAAS,YAAY,KAAK,GAAG,GAAG;AAAA,MAAG,SAAS;AAAA,IAAA;AAAA,EAErJ,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,MAAM;AACjC,QAAM,MAAM,KAAK,KAAK,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,IAAI;AACpD,QAAM,OAAO,CAAC,2CAA2C,GAAG,MAAM,IAAI,IAAI,YAAY,QAAQ,MAAM,cAAc,IAAI,GAAG,UAAU,eAAe,OAAO,KAAK,EAAE,gBAAgB,IAAI,MAAM,QAAQ,IAAI,MAAM,cAAc,QAAQ,MAAM,IAAI,uBAAuB,OAAO,MAAM,YAAY,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,aAAa,CAAC,CAAC,mBAAmB,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,QAAQ,SAAS,GAAG,CAAC,CAAC,EAAE;AAClZ,SAAO,EAAE,QAAQ,UAAU,MAAM,SAAS,CAAA,GAAI,QAAQ,SAAS,MAAM,GAAG,OAAO,MAAM,KAAA;AACvF;AAEA,eAAsB,UAAU,MAAa,KAAkC;AAC7E,QAAM,OAAO,MAAM,IAAI,QAAQ,YAAY;AAC3C,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,8BAA8B;AACzD,QAAM,OAAO,KAAK,QAAQ,IAAI,MAAM,GAAG,EAAE,MAAM,IAAI,EAAE,EAAE,CAAC,KAAK;AAC7D,QAAM,QAAQ,WAAW,IAAI,WAAW,MAAM,KAAK,YAAA,CAAa,GAAG,IAAI,WAAW,IAAI,GAAG,OAAO,KAAK,IAAI;AAEzG,QAAM,MAAM,MAAM,IAAI,QAAQ,eAAe;AAC7C,MAAI,KAAK;AACP,QAAI;AACF,YAAM,EAAE,cAAc,YAAY,iBAAiB,MAAM,OAAO,cAAW;AAC3E,YAAM,MAAM,aAAa,IAAI,WAAW,GAAG,CAAC,GAAG,KAAK,MAAM,IAAI,QAAQ,aAAa;AACnF,UAAI,CAAC,GAAI,OAAM,KAAK,KAAK,mEAAmE;AAAA,WACvF;AACH,cAAM,IAAI,aAAa,KAAK,WAAW,IAAI,WAAW,EAAE,CAAC,CAAC;AAC1D,cAAM,OAAO,KAAK,GAAG,EAAE,MAAM;AAC7B,cAAM,SAAS,SAAS,MAAM,MAAM;AACpC,cAAM,OAAO,EAAE,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,QAAQ,SAAS,GAAG,CAAC;AAClE,cAAM,KAAK,KAAK,kCAAkC,IAAI,OAAO,MAAM,gBAAgB,EAAE,KAAK,OAAO,EAAE,UAAU,GAAG,EAAE,OAAO,IAAI,kBAAkB,KAAK,MAAM,EAAE,OAAO,GAAG,CAAC,QAAQ,EAAE,mBAAmB,IAAI,EAAE;AAAA,MAC5M;AAAA,IACF,SAAS,GAAG;AAAE,YAAM,KAAK,KAAK,UAAW,EAAY,OAAO,EAAE;AAAA,IAAG;AAAA,EACnE;AACA,SAAO;AACT;"}
package/dist/load.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { LoadContext } from '@gmvjs/core/plugin';
2
+ import type { Model, VFile } from '@gmvjs/core/types';
3
+ export declare function loadOgf(main: VFile, ctx?: LoadContext): Promise<Model>;
4
+ /** A source SDK object (.object): meshes by surface, textures from the surfaces. */
5
+ export declare function loadObject(main: VFile, ctx?: LoadContext): Promise<Model>;
package/dist/load.js ADDED
@@ -0,0 +1,126 @@
1
+ import { makeIndices, boundsOf } from "@gmvjs/core/geometry";
2
+ import { parseOGF, parseOMF } from "./ogf.js";
3
+ import { XraySkeleton } from "./skeleton.js";
4
+ async function motionsFromFile(file, bones, skel, parts) {
5
+ const buf = await file.arrayBuffer(), ext = (file.name.split(".").pop() ?? "").toLowerCase();
6
+ if (ext === "skl" || ext === "skls") {
7
+ const { parseSkl, parseSMotions, smotionToXray } = await import("./smotion.js");
8
+ const u = new Uint8Array(buf), list = ext === "skl" ? [parseSkl(u)] : parseSMotions(u);
9
+ return list.map((sm) => smotionToXray(sm, bones.map((b) => b.name), (r, p, o) => skel.local(r, p, o), parts));
10
+ }
11
+ return parseOMF(buf, bones);
12
+ }
13
+ async function referencedMotions(refs, bones, ctx) {
14
+ const list = [], missing = [];
15
+ if (!ctx || !bones.length) return { list, missing };
16
+ for (const ref of refs) {
17
+ const rel = ref.replace(/\\/g, "/").toLowerCase().replace(/\.omf$/, "") + ".omf";
18
+ let u = null;
19
+ for (const p of [`meshes/${rel}`, `gamedata/meshes/${rel}`, rel]) {
20
+ u = await ctx.readPath(p).catch(() => null);
21
+ if (u) break;
22
+ }
23
+ if (!u) {
24
+ missing.push(ref);
25
+ continue;
26
+ }
27
+ try {
28
+ const src = rel.split("/").pop();
29
+ for (const x of parseOMF(u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength), bones)) list.push({ ...x, source: src });
30
+ } catch {
31
+ missing.push(ref);
32
+ }
33
+ }
34
+ return { list, missing };
35
+ }
36
+ const refsInfo = (refs, n, missing) => `Внешние анимации (.omf): ${refs.join(", ")}${n ? ` — подключено ${n}` : ""}${missing.length ? ` (не найдены: ${missing.join(", ")} — откройте папку gamedata или архивы игры)` : ""}`;
37
+ async function loadOgf(main, ctx) {
38
+ const m = parseOGF(await main.arrayBuffer());
39
+ const { list: ext, missing } = await referencedMotions(m.info.motionRefs, m.bones, ctx);
40
+ m.motions.push(...ext);
41
+ m.meshes.forEach((x, i2) => {
42
+ x.name = `#${i2}`;
43
+ });
44
+ const meshes = m.meshes.map((x) => ({ ...x, name: x.name ?? "" }));
45
+ const skel = XraySkeleton.auto(m.bones, meshes);
46
+ const verts = meshes.reduce((a, x) => a + x.vertexCount, 0), i = m.info, info = [];
47
+ info.push(
48
+ `Тип: ${i.type}`,
49
+ `Мешей: ${meshes.length}, вершин: ${verts}, треугольников: ${i.tris}`,
50
+ `Костей: ${m.bones.length}${m.bones.length && !i.ikOk ? " (IK-данные не разобраны — bind-поза неточна)" : ""}`
51
+ );
52
+ if (skel.bindVariant) info.push(`Углы костей: вариант ${skel.bindVariant}`);
53
+ info.push(`Анимаций внутри: ${m.motions.length - ext.length}`);
54
+ if (i.motionRefs.length) info.push(refsInfo(i.motionRefs, ext.length, missing));
55
+ if (i.desc) info.push(`Источник: ${i.desc.source}`, `Автор: ${i.desc.owner ?? ""}`);
56
+ return {
57
+ meshes,
58
+ skeleton: skel.n ? skel : null,
59
+ motions: m.motions,
60
+ bounds: m.bounds,
61
+ flipZ: true,
62
+ info,
63
+ detectInvert: (motion) => motion.kind === "xray" ? skel.detectInvert(meshes, motion) : false,
64
+ loadMotions: async (file) => motionsFromFile(file, m.bones, skel)
65
+ };
66
+ }
67
+ async function loadObject(main, ctx) {
68
+ const { parseObject, objectMeshes, objectSkeleton } = await import("./object.js");
69
+ const o = parseObject(await main.arrayBuffer()), sk = objectSkeleton(o);
70
+ const boneOfMap = sk ? new Map(sk.bones.map((b, i) => [b.wmap.toLowerCase(), i])) : void 0;
71
+ if (sk) sk.bones.forEach((b, i) => {
72
+ if (!boneOfMap.has(b.name.toLowerCase())) boneOfMap.set(b.name.toLowerCase(), i);
73
+ });
74
+ const list = objectMeshes(o, "auto", boneOfMap);
75
+ if (!list.length) throw new Error("В объекте нет граней");
76
+ const meshes = list.map((x) => {
77
+ const n = x.pos.length / 3;
78
+ return {
79
+ name: x.name,
80
+ texture: x.surface?.texture ?? "",
81
+ shader: x.surface?.eshader ?? "",
82
+ material: x.surface?.gamemtl,
83
+ vertexCount: n,
84
+ pos: x.pos,
85
+ nor: x.nor,
86
+ uv: x.uv,
87
+ bone: x.bone ?? new Float32Array(n * 4),
88
+ weight: x.weight ?? new Float32Array(n * 4).map((_, i) => i % 4 === 0 ? 1 : 0),
89
+ indices: makeIndices(n, x.indices),
90
+ skinned: !!x.bone
91
+ };
92
+ });
93
+ const verts = meshes.reduce((a, m) => a + m.vertexCount, 0), tris = meshes.reduce((a, m) => a + m.indices.length / 3, 0);
94
+ const info = [`X-Ray SDK .object: мешей ${o.meshes.length}, поверхностей ${o.surfaces.length}, вершин ${verts}, треугольников ${tris}`];
95
+ for (const s of o.surfaces.slice(0, 12)) info.push(` • ${s.name}: ${s.texture} | ${s.eshader} | ${s.cshader} | ${s.gamemtl}`);
96
+ if (o.revision) info.push(`Автор: ${o.revision.owner}`);
97
+ const model = { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: true, info };
98
+ if (!sk) return model;
99
+ const bones = sk.bones.map((b) => ({ name: b.name, parent: b.parent, rot: b.rot, pos: b.pos }));
100
+ const skel = XraySkeleton.auto(bones, meshes);
101
+ const parts = sk.parts.length ? sk.parts : void 0;
102
+ if (sk.motions) {
103
+ try {
104
+ const { parseSMotions, smotionToXray } = await import("./smotion.js");
105
+ for (const sm of parseSMotions(sk.motions)) model.motions.push(smotionToXray(sm, bones.map((b) => b.name), (r, p, x) => skel.local(r, p, x), parts));
106
+ } catch (e) {
107
+ info.push(`Анимации объекта не прочитаны: ${e.message}`);
108
+ }
109
+ }
110
+ const own = model.motions.length;
111
+ const { list: ext, missing } = await referencedMotions(sk.motionRefs, bones, ctx);
112
+ model.motions.push(...ext);
113
+ info.push(`Костей: ${bones.length}${skel.bindVariant ? ` (углы: вариант ${skel.bindVariant})` : ""}, частей тела: ${sk.parts.length}, анимаций внутри: ${own}`);
114
+ if (sk.motionRefs.length) info.push(refsInfo(sk.motionRefs, ext.length, missing));
115
+ return {
116
+ ...model,
117
+ skeleton: skel,
118
+ detectInvert: (motion) => motion.kind === "xray" ? skel.detectInvert(meshes, motion) : false,
119
+ loadMotions: async (file) => motionsFromFile(file, bones, skel, parts)
120
+ };
121
+ }
122
+ export {
123
+ loadObject,
124
+ loadOgf
125
+ };
126
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sources":["../src/load.ts"],"sourcesContent":["import { boundsOf, makeIndices } from '@gmvjs/core/geometry';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport type { Mesh, Model, VFile } from '@gmvjs/core/types';\nimport type { XrayMotion } from './motion';\nimport { parseOGF, parseOMF, type OgfBone } from './ogf';\nimport { XraySkeleton } from './skeleton';\n\n/** External animations by extension: .omf (by bone names from S_SMPARAMS), .skl/.skls (ActorEditor). */\nasync function motionsFromFile(file: VFile, bones: OgfBone[], skel: XraySkeleton, parts?: Array<{ name: string; bones: string[] }>): Promise<XrayMotion[]> {\n const buf = await file.arrayBuffer(), ext = (file.name.split('.').pop() ?? '').toLowerCase();\n if (ext === 'skl' || ext === 'skls') {\n const { parseSkl, parseSMotions, smotionToXray } = await import('./smotion');\n const u = new Uint8Array(buf), list = ext === 'skl' ? [parseSkl(u)] : parseSMotions(u);\n return list.map((sm) => smotionToXray(sm, bones.map((b) => b.name), (r, p, o) => skel.local(r, p, o), parts));\n }\n return parseOMF(buf, bones);\n}\n\n/** Animations from S_MOTION_REFS / object references: meshes/<reference>.omf in the open archives and folders. */\nasync function referencedMotions(refs: readonly string[], bones: OgfBone[], ctx?: LoadContext): Promise<{ list: XrayMotion[]; missing: string[] }> {\n const list: XrayMotion[] = [], missing: string[] = [];\n if (!ctx || !bones.length) return { list, missing };\n for (const ref of refs) {\n const rel = ref.replace(/\\\\/g, '/').toLowerCase().replace(/\\.omf$/, '') + '.omf';\n let u: Uint8Array | null = null;\n for (const p of [`meshes/${rel}`, `gamedata/meshes/${rel}`, rel]) { u = await ctx.readPath(p).catch(() => null); if (u) break; }\n if (!u) { missing.push(ref); continue; }\n try {\n const src = rel.split('/').pop()!;\n for (const x of parseOMF(u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength) as ArrayBuffer, bones)) list.push({ ...x, source: src });\n } catch { missing.push(ref); }\n }\n return { list, missing };\n}\nconst refsInfo = (refs: readonly string[], n: number, missing: readonly string[]) =>\n `Внешние анимации (.omf): ${refs.join(', ')}${n ? ` — подключено ${n}` : ''}${missing.length ? ` (не найдены: ${missing.join(', ')} — откройте папку gamedata или архивы игры)` : ''}`;\n\nexport async function loadOgf(main: VFile, ctx?: LoadContext): Promise<Model> {\n const m = parseOGF(await main.arrayBuffer());\n const { list: ext, missing } = await referencedMotions(m.info.motionRefs, m.bones, ctx);\n m.motions.push(...ext);\n m.meshes.forEach((x, i) => { x.name = `#${i}`; });\n const meshes = m.meshes.map((x) => ({ ...x, name: x.name ?? '' }));\n const skel = XraySkeleton.auto(m.bones, meshes);\n const verts = meshes.reduce((a, x) => a + x.vertexCount, 0), i = m.info, info: string[] = [];\n info.push(`Тип: ${i.type}`, `Мешей: ${meshes.length}, вершин: ${verts}, треугольников: ${i.tris}`,\n `Костей: ${m.bones.length}${m.bones.length && !i.ikOk ? ' (IK-данные не разобраны — bind-поза неточна)' : ''}`);\n if (skel.bindVariant) info.push(`Углы костей: вариант ${skel.bindVariant}`);\n info.push(`Анимаций внутри: ${m.motions.length - ext.length}`);\n if (i.motionRefs.length) info.push(refsInfo(i.motionRefs, ext.length, missing));\n if (i.desc) info.push(`Источник: ${i.desc.source}`, `Автор: ${i.desc.owner ?? ''}`);\n return {\n meshes, skeleton: skel.n ? skel : null, motions: m.motions, bounds: m.bounds, flipZ: true, info,\n detectInvert: (motion) => (motion.kind === 'xray' ? skel.detectInvert(meshes, motion) : false),\n loadMotions: async (file) => motionsFromFile(file, m.bones, skel),\n };\n}\n\n/** A source SDK object (.object): meshes by surface, textures from the surfaces. */\nexport async function loadObject(main: VFile, ctx?: LoadContext): Promise<Model> {\n const { parseObject, objectMeshes, objectSkeleton } = await import('./object');\n const o = parseObject(await main.arrayBuffer()), sk = objectSkeleton(o);\n const boneOfMap = sk ? new Map(sk.bones.map((b, i) => [b.wmap.toLowerCase(), i])) : undefined;\n if (sk) sk.bones.forEach((b, i) => { if (!boneOfMap!.has(b.name.toLowerCase())) boneOfMap!.set(b.name.toLowerCase(), i); });\n const list = objectMeshes(o, 'auto', boneOfMap);\n if (!list.length) throw new Error('В объекте нет граней');\n const meshes: Mesh[] = list.map((x) => {\n const n = x.pos.length / 3;\n return {\n name: x.name, texture: x.surface?.texture ?? '', shader: x.surface?.eshader ?? '', material: x.surface?.gamemtl, vertexCount: n,\n pos: x.pos, nor: x.nor, uv: x.uv, bone: x.bone ?? new Float32Array(n * 4), weight: x.weight ?? new Float32Array(n * 4).map((_, i) => (i % 4 === 0 ? 1 : 0)),\n indices: makeIndices(n, x.indices), skinned: !!x.bone,\n };\n });\n const verts = meshes.reduce((a, m) => a + m.vertexCount, 0), tris = meshes.reduce((a, m) => a + m.indices.length / 3, 0);\n const info = [`X-Ray SDK .object: мешей ${o.meshes.length}, поверхностей ${o.surfaces.length}, вершин ${verts}, треугольников ${tris}`];\n for (const s of o.surfaces.slice(0, 12)) info.push(` • ${s.name}: ${s.texture} | ${s.eshader} | ${s.cshader} | ${s.gamemtl}`);\n if (o.revision) info.push(`Автор: ${o.revision.owner}`);\n const model: Model = { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: true, info };\n if (!sk) return model;\n // skeleton: the bind pose from the object's bones (the same setXYZi angles as in OGF IK data)\n const bones: OgfBone[] = sk.bones.map((b) => ({ name: b.name, parent: b.parent, rot: b.rot, pos: b.pos }));\n const skel = XraySkeleton.auto(bones, meshes);\n const parts = sk.parts.length ? sk.parts : undefined;\n if (sk.motions) {\n try {\n const { parseSMotions, smotionToXray } = await import('./smotion');\n for (const sm of parseSMotions(sk.motions)) model.motions.push(smotionToXray(sm, bones.map((b) => b.name), (r, p, x) => skel.local(r, p, x), parts));\n } catch (e) { info.push(`Анимации объекта не прочитаны: ${(e as Error).message}`); }\n }\n const own = model.motions.length;\n const { list: ext, missing } = await referencedMotions(sk.motionRefs, bones, ctx);\n model.motions.push(...ext);\n info.push(`Костей: ${bones.length}${skel.bindVariant ? ` (углы: вариант ${skel.bindVariant})` : ''}, частей тела: ${sk.parts.length}, анимаций внутри: ${own}`);\n if (sk.motionRefs.length) info.push(refsInfo(sk.motionRefs, ext.length, missing));\n return {\n ...model, skeleton: skel,\n detectInvert: (motion) => (motion.kind === 'xray' ? skel.detectInvert(meshes, motion) : false),\n loadMotions: async (file) => motionsFromFile(file, bones, skel, parts),\n };\n}\n"],"names":["i"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Decompress LZHUF. outSize: if the size is known in advance (otherwise taken from the first 4 bytes). */
2
+ export declare function lzhufDecompress(src: Uint8Array, outSize?: number): Uint8Array;