@gmvjs/xray-v1 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -2
  3. package/dist/db.d.ts +12 -0
  4. package/dist/db.js +134 -0
  5. package/dist/db.js.map +1 -0
  6. package/dist/details.d.ts +42 -0
  7. package/dist/details.js +210 -0
  8. package/dist/details.js.map +1 -0
  9. package/dist/export.d.ts +38 -0
  10. package/dist/export.js +241 -0
  11. package/dist/export.js.map +1 -0
  12. package/dist/games/cop.d.ts +7 -0
  13. package/dist/games/cop.js +11 -0
  14. package/dist/games/cop.js.map +1 -0
  15. package/dist/games/cs.d.ts +7 -0
  16. package/dist/games/cs.js +11 -0
  17. package/dist/games/cs.js.map +1 -0
  18. package/dist/games/sdk.d.ts +7 -0
  19. package/dist/games/sdk.js +10 -0
  20. package/dist/games/sdk.js.map +1 -0
  21. package/dist/games/soc.d.ts +9 -0
  22. package/dist/games/soc.js +13 -0
  23. package/dist/games/soc.js.map +1 -0
  24. package/dist/i18n.d.ts +2 -0
  25. package/dist/i18n.js +48 -0
  26. package/dist/i18n.js.map +1 -0
  27. package/dist/index.d.ts +5 -0
  28. package/dist/index.js +131 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/level.d.ts +39 -0
  31. package/dist/level.js +234 -0
  32. package/dist/level.js.map +1 -0
  33. package/dist/load.d.ts +5 -0
  34. package/dist/load.js +126 -0
  35. package/dist/load.js.map +1 -0
  36. package/dist/lzhuf.d.ts +2 -0
  37. package/dist/lzhuf.js +136 -0
  38. package/dist/lzhuf.js.map +1 -0
  39. package/dist/motion.d.ts +28 -0
  40. package/dist/motion.js +20 -0
  41. package/dist/motion.js.map +1 -0
  42. package/dist/object.d.ts +138 -0
  43. package/dist/object.js +361 -0
  44. package/dist/object.js.map +1 -0
  45. package/dist/ogf.d.ts +93 -0
  46. package/dist/ogf.js +418 -0
  47. package/dist/ogf.js.map +1 -0
  48. package/dist/scene.d.ts +28 -0
  49. package/dist/scene.js +141 -0
  50. package/dist/scene.js.map +1 -0
  51. package/dist/skeleton.d.ts +34 -0
  52. package/dist/skeleton.js +252 -0
  53. package/dist/skeleton.js.map +1 -0
  54. package/dist/smotion.d.ts +44 -0
  55. package/dist/smotion.js +283 -0
  56. package/dist/smotion.js.map +1 -0
  57. package/dist/soc-key.d.ts +18 -0
  58. package/dist/soc-key.js +125 -0
  59. package/dist/soc-key.js.map +1 -0
  60. package/dist/thm.d.ts +26 -0
  61. package/dist/thm.js +69 -0
  62. package/dist/thm.js.map +1 -0
  63. package/dist/write-object.d.ts +31 -0
  64. package/dist/write-object.js +228 -0
  65. package/dist/write-object.js.map +1 -0
  66. package/dist/write-omf.d.ts +47 -0
  67. package/dist/write-scene.d.ts +26 -0
  68. package/dist/write-scene.js +114 -0
  69. package/dist/write-scene.js.map +1 -0
  70. package/package.json +70 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["// X-Ray Engine (S.T.A.L.K.E.R.): OGF v4 + анимации OMF, архивы .db/.xdb.\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 // первый чанк — OGF_HEADER (id 1), затем байт версии; иначе не уверены, но и не исключаем\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 // первый чанк — заголовок уровня (id 1, 4 байта: версия и качество)\n sniff: (h) => (u32le(h, 0) === 1 && u32le(h, 4) === 4 && u32le(h, 8) !== 16 ? 0.9 : 0), // 16 — версия уровня Metro 2033 (4A)\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 // начинается с имени шейдера (латиница, «\\\\», «_»)\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 (ЗП) сохраняет сцену как ini; бинарные .level старых SDK пока не читаются\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 // ТЧ: исполняемый файл игры — спутник архивов (из него берётся ключ таблиц файлов)\n companionExtensions: ['exe'],\n // в «Папке игры» архивы открываются сами (читается только таблица файлов): ЧН/ЗП — с заголовком, ТЧ — gamedata.db*\n openFromFolder: (h, name) => u32le(h, 0) === 0x29a || (u32le(h, 0) === 0 && /^gamedata\\.db[0-9a-z]$/i.test(name)),\n // ЧН/ЗП начинаются с текстового чанка [header] (id 0x29A), ТЧ и моды — с чанка данных (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/** Выгрузка модели/уровня в исходники X-Ray SDK (.object, .level, текстуры); модуль подгружается по требованию. */\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
+ /** Вершинные буферы: объявление до D3DDECL_END (поток 0xFF), число вершин, данные. */
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
+ /** Окно индексов прогрессивного меша (SWI): смещение и число треугольников самого подробного уровня. */
30
+ type Slide = [offset: number, tris: number];
31
+ /** Общие SWI уровня (level.geom, чанк 11; ЧН/ЗП): деревья TREE_PM ссылаются на них номером (чанк 20 визуала). */
32
+ export declare function parseSwis(u: Uint8Array): Array<Slide | null>;
33
+ export declare function parseVisuals(u: Uint8Array, swis?: Array<Slide | null>): LevelVisual[];
34
+ /** Строка шейдера уровня «шейдер/текстура,лайтмап…» -> [шейдер, текстура]. */
35
+ export declare function splitShader(s: string): [string, string];
36
+ /** Уровень: визуалы с геометрией, объединённые по шейдеру. */
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":["// Скомпилированный уровень X-Ray (levels/<уровень>/level + level.geom): статическая геометрия для просмотра и сверки.\n// level: 1 заголовок, 2 шейдеры («шейдер/текстура,лайтмап»), 3 визуалы (OGF-подобные чанки), 8 секторы.\n// level.geom: 9 вершинные буферы (объявление D3DVERTEXELEMENT9 + данные), 10 индексные буферы.\n// Визуал ссылается на буферы чанком GCONTAINER (ЧН/ЗП, 21) или VCONTAINER/ICONTAINER (ТЧ, 7/8); индексы — от начала своего куска VB.\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/** Типы визуалов с собственной геометрией: обычный, прогрессивный, деревья. */\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/** Вершинные буферы: объявление до D3DDECL_END (поток 0xFF), число вершин, данные. */\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/** Окно индексов прогрессивного меша (SWI): смещение и число треугольников самого подробного уровня. */\ntype Slide = [offset: number, tris: number];\n/** Первое окно SWI: 4×u32 резерв, u32 число окон, окна {u32 смещение, u16 треугольников, u16 вершин}. */\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/** Общие SWI уровня (level.geom, чанк 11; ЧН/ЗП): деревья TREE_PM ссылаются на них номером (чанк 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 // прогрессивный меш: в буфере индексов лежат все уровни детализации; берём первое «окно» SWI — самое подробное\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/** Строка шейдера уровня «шейдер/текстура,лайтмап…» -> [шейдер, текстура]. */\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/** Уровень: визуалы с геометрией, объединённые по шейдеру. */\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; } // сжатые позиции (деревья ЗП) — позже\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) { // строки-векторы: 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 — вершины деревьев и MU-моделей (машины, бочки, камни — визуалы TREE_ST/PM с матрицей): UV × 2048,\n // z, w — коэффициенты ветра (FTreeVisual); сверено по атласам техники во всех трёх играх (диапазон 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: координата × 1024, дробная поправка — в альфе касательной и бинормали\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 // трава: level.details + высоты по 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":"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"}
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
+ /** Исходный объект SDK (.object): меши по поверхностям, текстуры из поверхностей. */
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/** Внешние анимации по расширению: .omf (по именам костей из 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/** Анимации из S_MOTION_REFS / ссылок объекта: meshes/<ссылка>.omf в открытых архивах и папках. */\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/** Исходный объект SDK (.object): меши по поверхностям, текстуры из поверхностей. */\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 // скелет: bind-поза из костей объекта (те же углы setXYZi, что в IK-данных OGF)\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
+ /** Распаковать LZHUF. outSize — если размер известен заранее (иначе берётся из первых 4 байт). */
2
+ export declare function lzhufDecompress(src: Uint8Array, outSize?: number): Uint8Array;