@gmvjs/xray-v1 0.1.0 → 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 (47) hide show
  1. package/README.md +35 -29
  2. package/dist/db.d.ts +2 -2
  3. package/dist/db.js.map +1 -1
  4. package/dist/details.d.ts +4 -4
  5. package/dist/details.js.map +1 -1
  6. package/dist/export.d.ts +13 -13
  7. package/dist/export.js.map +1 -1
  8. package/dist/games/cop.js.map +1 -1
  9. package/dist/games/cs.js.map +1 -1
  10. package/dist/games/sdk.d.ts +1 -1
  11. package/dist/games/sdk.js.map +1 -1
  12. package/dist/games/soc.d.ts +1 -1
  13. package/dist/games/soc.js.map +1 -1
  14. package/dist/i18n.js.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +8 -8
  17. package/dist/index.js.map +1 -1
  18. package/dist/level.d.ts +5 -5
  19. package/dist/level.js.map +1 -1
  20. package/dist/load.d.ts +1 -1
  21. package/dist/load.js.map +1 -1
  22. package/dist/lzhuf.d.ts +1 -1
  23. package/dist/lzhuf.js.map +1 -1
  24. package/dist/motion.d.ts +6 -6
  25. package/dist/motion.js.map +1 -1
  26. package/dist/object.d.ts +12 -12
  27. package/dist/object.js.map +1 -1
  28. package/dist/ogf.d.ts +6 -6
  29. package/dist/ogf.js.map +1 -1
  30. package/dist/scene.d.ts +6 -6
  31. package/dist/scene.js.map +1 -1
  32. package/dist/skeleton.d.ts +4 -4
  33. package/dist/skeleton.js +4 -4
  34. package/dist/skeleton.js.map +1 -1
  35. package/dist/smotion.d.ts +5 -5
  36. package/dist/smotion.js.map +1 -1
  37. package/dist/soc-key.d.ts +6 -6
  38. package/dist/soc-key.js.map +1 -1
  39. package/dist/thm.d.ts +1 -1
  40. package/dist/thm.js.map +1 -1
  41. package/dist/write-object.d.ts +7 -7
  42. package/dist/write-object.js +1 -1
  43. package/dist/write-object.js.map +1 -1
  44. package/dist/write-omf.d.ts +9 -9
  45. package/dist/write-scene.d.ts +6 -6
  46. package/dist/write-scene.js.map +1 -1
  47. package/package.json +3 -3
package/dist/i18n.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","sources":["../src/i18n.ts"],"sourcesContent":["// Английские тексты движка xray-v1 (русские — в самих плагинах).\nimport type { EngineTexts } from '@gmvjs/core/plugin';\n\nexport const EN: EngineTexts = {\n \"presets\": {\n \"stalker-soc\": {\n \"title\": \"S.T.A.L.K.E.R.: Shadow of Chernobyl\",\n \"hint\": \"OGF v4. Animations are inside the model or in an external .omf. gamedata.db* archives open through “Game folder” (the file-table key is taken from your game’s bin/XR_3DA.exe) or with “Open .db…” together with XR_3DA.exe; mod .xdb archives and an unpacked gamedata folder open too. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.\",\n \"archiveLabel\": \"Open .db…\"\n },\n \"stalker-cs\": {\n \"title\": \"S.T.A.L.K.E.R.: Clear Sky\",\n \"hint\": \"OGF v4. Open resources.db0–db2 (models and textures) and patches/xpatch_*.db, or a gamedata folder. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.\",\n \"archiveLabel\": \"Open .db…\"\n },\n \"stalker-cop\": {\n \"title\": \"S.T.A.L.K.E.R.: Call of Pripyat\",\n \"hint\": \"OGF v4, including 3- and 4-weight vertices. .db archives as in Clear Sky. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.\",\n \"archiveLabel\": \"Open .db…\"\n }\n },\n \"exporters\": {\n \"xray-sdk\": {\n \"title\": \"S.T.A.L.K.E.R.: X-Ray SDK 0.7 sources\",\n \"description\": \"For LevelEditor and ActorEditor (Call of Pripyat, SDK 0.7): .object geometry (large maps are split into cells), a .level + scene_object.part scene, DDS + .thm textures — one ZIP. Lights, sectors, the AI grid and the xrLC build are done in the SDK.\",\n \"fields\": {\n \"name\": {\n \"label\": \"Level name\",\n \"hint\": \"Latin letters, digits, \\\"_\\\": rawdata/objects/gmv/<name>, rawdata/levels/<name>.level\"\n },\n \"cell\": {\n \"label\": \"Cell, m\",\n \"placeholder\": \"auto\",\n \"hint\": \"Split the geometry into objects (0 — a single object; 32–64 suits maps)\"\n },\n \"scale\": {\n \"label\": \"Scale\",\n \"hint\": \"Metres per model unit (X-Ray: 1 unit = 1 m)\"\n },\n \"instances\": {\n \"label\": \"Level objects as references\",\n \"hint\": \"Each object model becomes one .object in props, the scene holds references with poses. Uncheck to merge objects into the cell geometry\"\n }\n }\n }\n }\n};\n"],"names":[],"mappings":"AAGO,MAAM,KAAkB;AAAA,EAC7B,WAAW;AAAA,IACT,eAAe;AAAA,MACb,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,IAElB,cAAc;AAAA,MACZ,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,IAElB,eAAe;AAAA,MACb,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,EAClB;AAAA,EAEF,aAAa;AAAA,IACX,YAAY;AAAA,MACV,SAAS;AAAA,MACT,eAAe;AAAA,MACf,UAAU;AAAA,QACR,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,QAAQ;AAAA,QAAA;AAAA,QAEV,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,eAAe;AAAA,UACf,QAAQ;AAAA,QAAA;AAAA,QAEV,SAAS;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,QAAA;AAAA,QAEV,aAAa;AAAA,UACX,SAAS;AAAA,UACT,QAAQ;AAAA,QAAA;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEJ;"}
1
+ {"version":3,"file":"i18n.js","sources":["../src/i18n.ts"],"sourcesContent":["// English texts of the xray-v1 engine (Russian ones live in the plugins themselves).\nimport type { EngineTexts } from '@gmvjs/core/plugin';\n\nexport const EN: EngineTexts = {\n \"presets\": {\n \"stalker-soc\": {\n \"title\": \"S.T.A.L.K.E.R.: Shadow of Chernobyl\",\n \"hint\": \"OGF v4. Animations are inside the model or in an external .omf. gamedata.db* archives open through “Game folder” (the file-table key is taken from your game’s bin/XR_3DA.exe) or with “Open .db…” together with XR_3DA.exe; mod .xdb archives and an unpacked gamedata folder open too. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.\",\n \"archiveLabel\": \"Open .db…\"\n },\n \"stalker-cs\": {\n \"title\": \"S.T.A.L.K.E.R.: Clear Sky\",\n \"hint\": \"OGF v4. Open resources.db0–db2 (models and textures) and patches/xpatch_*.db, or a gamedata folder. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.\",\n \"archiveLabel\": \"Open .db…\"\n },\n \"stalker-cop\": {\n \"title\": \"S.T.A.L.K.E.R.: Call of Pripyat\",\n \"hint\": \"OGF v4, including 3- and 4-weight vertices. .db archives as in Clear Sky. External .omf files referenced by a model are attached automatically when the gamedata folder or archives are open; torso animations play together with legs (choose in “Other body parts”). Levels include grass from level.details; SDK sources: .object with skeleton and animations, .skl/.skls.\",\n \"archiveLabel\": \"Open .db…\"\n }\n },\n \"exporters\": {\n \"xray-sdk\": {\n \"title\": \"S.T.A.L.K.E.R.: X-Ray SDK 0.7 sources\",\n \"description\": \"For LevelEditor and ActorEditor (Call of Pripyat, SDK 0.7): .object geometry (large maps are split into cells), a .level + scene_object.part scene, DDS + .thm textures — one ZIP. Lights, sectors, the AI grid and the xrLC build are done in the SDK.\",\n \"fields\": {\n \"name\": {\n \"label\": \"Level name\",\n \"hint\": \"Latin letters, digits, \\\"_\\\": rawdata/objects/gmv/<name>, rawdata/levels/<name>.level\"\n },\n \"cell\": {\n \"label\": \"Cell, m\",\n \"placeholder\": \"auto\",\n \"hint\": \"Split the geometry into objects (0 — a single object; 32–64 suits maps)\"\n },\n \"scale\": {\n \"label\": \"Scale\",\n \"hint\": \"Metres per model unit (X-Ray: 1 unit = 1 m)\"\n },\n \"instances\": {\n \"label\": \"Level objects as references\",\n \"hint\": \"Each object model becomes one .object in props, the scene holds references with poses. Uncheck to merge objects into the cell geometry\"\n }\n }\n }\n }\n};\n"],"names":[],"mappings":"AAGO,MAAM,KAAkB;AAAA,EAC7B,WAAW;AAAA,IACT,eAAe;AAAA,MACb,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,IAElB,cAAc;AAAA,MACZ,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,IAElB,eAAe;AAAA,MACb,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,EAClB;AAAA,EAEF,aAAa;AAAA,IACX,YAAY;AAAA,MACV,SAAS;AAAA,MACT,eAAe;AAAA,MACf,UAAU;AAAA,QACR,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,QAAQ;AAAA,QAAA;AAAA,QAEV,QAAQ;AAAA,UACN,SAAS;AAAA,UACT,eAAe;AAAA,UACf,QAAQ;AAAA,QAAA;AAAA,QAEV,SAAS;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,QAAA;AAAA,QAEV,aAAa;AAAA,UACX,SAAS;AAAA,UACT,QAAQ;AAAA,QAAA;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEJ;"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare const xrayV1Engine: import("@gmvjs/core/plugin").EnginePlugin;
2
- /** Выгрузка модели/уровня в исходники X-Ray SDK (.object, .level, текстуры); модуль подгружается по требованию. */
2
+ /** Export of a model/level into X-Ray SDK sources (.object, .level, textures); the module is loaded on demand. */
3
3
  export declare function exportXraySdk(...args: Parameters<typeof import('./export').exportXraySdk>): ReturnType<typeof import('./export').exportXraySdk>;
4
4
  export type { XrayExportOptions, XrayExportResult, TextureForExport } from './export';
5
5
  export { autoLayers, XRAY_FX_PART, type XrayMotion, type XrayTrack } from './motion';
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ const xrayV1Engine = defineEngine({
17
17
  extensions: ["ogf"],
18
18
  motionExtensions: ["omf", "skl", "skls"],
19
19
  status: "beta",
20
- // первый чанк — OGF_HEADER (id 1), затем байт версии; иначе не уверены, но и не исключаем
20
+ // the first chunk is OGF_HEADER (id 1), then a version byte; otherwise not certain, but not excluded either
21
21
  sniff: (h) => u32le(h, 0) === 1 ? h[8] === 4 ? 1 : 0.6 : 0.2,
22
22
  load: async (main, ctx) => (await import("./load.js")).loadOgf(main, ctx)
23
23
  },
@@ -38,9 +38,9 @@ const xrayV1Engine = defineEngine({
38
38
  extensions: [],
39
39
  pathPattern: /(^|\/)levels\/[^/]+\/level$/,
40
40
  status: "beta",
41
- // первый чанк — заголовок уровня (id 1, 4 байта: версия и качество)
41
+ // the first chunk is the level header (id 1, 4 bytes: version and quality)
42
42
  sniff: (h) => u32le(h, 0) === 1 && u32le(h, 4) === 4 && u32le(h, 8) !== 16 ? 0.9 : 0,
43
- // 16 — версия уровня Metro 2033 (4A)
43
+ // 16 is the Metro 2033 (4A) level version
44
44
  load: async (main, ctx) => (await import("./level.js")).loadLevel(main, ctx)
45
45
  },
46
46
  {
@@ -49,7 +49,7 @@ const xrayV1Engine = defineEngine({
49
49
  engine: "xray-v1",
50
50
  extensions: ["dm"],
51
51
  status: "beta",
52
- // начинается с имени шейдера (латиница, «\\», «_»)
52
+ // starts with a shader name (Latin letters, "\\", "_")
53
53
  sniff: (h) => /^[\w\\.-]{2,}\0/.test(ascii(h, 0, Math.min(h.length, 64))) ? 0.8 : 0,
54
54
  load: async (main) => {
55
55
  const { parseDM, dmMesh } = await import("./details.js");
@@ -64,7 +64,7 @@ const xrayV1Engine = defineEngine({
64
64
  engine: "xray-v1",
65
65
  extensions: ["level"],
66
66
  status: "beta",
67
- // SDK 0.7 (ЗП) сохраняет сцену как ini; бинарные .level старых SDK пока не читаются
67
+ // SDK 0.7 (CoP) saves the scene as ini; binary .level files of older SDKs are not read yet
68
68
  sniff: (h) => /^\s*\[(build_params|camera|guid|level_options|level_tag|version)\]/.test(ascii(h, 0, Math.min(h.length, 64))) ? 1 : 0,
69
69
  load: async (main, ctx) => (await import("./scene.js")).loadSceneLevel(main, ctx)
70
70
  }
@@ -75,11 +75,11 @@ const xrayV1Engine = defineEngine({
75
75
  title: "X-Ray .db / .xdb (Чистое небо, Зов Припяти)",
76
76
  engine: "xray-v1",
77
77
  extensions: DB_EXTS,
78
- // ТЧ: исполняемый файл игры — спутник архивов (из него берётся ключ таблиц файлов)
78
+ // SoC: the game executable is an archive companion (the file table key is taken from it)
79
79
  companionExtensions: ["exe"],
80
- // в «Папке игры» архивы открываются сами (читается только таблица файлов): ЧН/ЗП — с заголовком, ТЧ — gamedata.db*
80
+ // in the "Game folder" archives open by themselves (only the file table is read): CS/CoP with a header, SoC gamedata.db*
81
81
  openFromFolder: (h, name) => u32le(h, 0) === 666 || u32le(h, 0) === 0 && /^gamedata\.db[0-9a-z]$/i.test(name),
82
- // ЧН/ЗП начинаются с текстового чанка [header] (id 0x29A), ТЧ и моды — с чанка данных (id 0)
82
+ // CS/CoP start with the text [header] chunk (id 0x29A), SoC and mods with the data chunk (id 0)
83
83
  sniff: (h) => u32le(h, 0) === 666 ? 1 : u32le(h, 0) === 0 ? 0.5 : 0,
84
84
  open: async (file, siblings) => (await import("./db.js")).openXrayDb(file, siblings)
85
85
  }
package/dist/index.js.map CHANGED
@@ -1 +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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"}
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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"}
package/dist/level.d.ts CHANGED
@@ -12,7 +12,7 @@ interface VB {
12
12
  tan: number;
13
13
  bin: number;
14
14
  }
15
- /** Вершинные буферы: объявление до D3DDECL_END (поток 0xFF), число вершин, данные. */
15
+ /** Vertex buffers: the declaration up to D3DDECL_END (stream 0xFF), the vertex count, the data. */
16
16
  export declare function parseVBs(u: Uint8Array): VB[];
17
17
  export declare function parseIBs(u: Uint8Array): Uint16Array[];
18
18
  export interface LevelVisual {
@@ -26,14 +26,14 @@ export interface LevelVisual {
26
26
  ibCount: number;
27
27
  xform: Float32Array | null;
28
28
  }
29
- /** Окно индексов прогрессивного меша (SWI): смещение и число треугольников самого подробного уровня. */
29
+ /** Index window of a progressive mesh (SWI): offset and triangle count of the most detailed level. */
30
30
  type Slide = [offset: number, tris: number];
31
- /** Общие SWI уровня (level.geom, чанк 11; ЧН/ЗП): деревья TREE_PM ссылаются на них номером (чанк 20 визуала). */
31
+ /** The level's shared SWIs (level.geom, chunk 11; CS/CoP): TREE_PM trees refer to them by number (visual chunk 20). */
32
32
  export declare function parseSwis(u: Uint8Array): Array<Slide | null>;
33
33
  export declare function parseVisuals(u: Uint8Array, swis?: Array<Slide | null>): LevelVisual[];
34
- /** Строка шейдера уровня «шейдер/текстура,лайтмап…» -> [шейдер, текстура]. */
34
+ /** Level shader string "shader/texture,lightmap…" -> [shader, texture]. */
35
35
  export declare function splitShader(s: string): [string, string];
36
- /** Уровень: визуалы с геометрией, объединённые по шейдеру. */
36
+ /** Level: visuals with geometry, merged by shader. */
37
37
  export declare function levelModel(level: Uint8Array, geom: Uint8Array, name: string): Model;
38
38
  export declare function loadLevel(main: VFile, ctx: LoadContext): Promise<Model>;
39
39
  export {};
package/dist/level.js.map CHANGED
@@ -1 +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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"}
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":"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"}
package/dist/load.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { LoadContext } from '@gmvjs/core/plugin';
2
2
  import type { Model, VFile } from '@gmvjs/core/types';
3
3
  export declare function loadOgf(main: VFile, ctx?: LoadContext): Promise<Model>;
4
- /** Исходный объект SDK (.object): меши по поверхностям, текстуры из поверхностей. */
4
+ /** A source SDK object (.object): meshes by surface, textures from the surfaces. */
5
5
  export declare function loadObject(main: VFile, ctx?: LoadContext): Promise<Model>;
package/dist/load.js.map CHANGED
@@ -1 +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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"}
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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"}
package/dist/lzhuf.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- /** Распаковать LZHUF. outSize — если размер известен заранее (иначе берётся из первых 4 байт). */
1
+ /** Decompress LZHUF. outSize: if the size is known in advance (otherwise taken from the first 4 bytes). */
2
2
  export declare function lzhufDecompress(src: Uint8Array, outSize?: number): Uint8Array;
package/dist/lzhuf.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lzhuf.js","sources":["../src/lzhuf.ts"],"sourcesContent":["// LZHUF (LZSS + адаптивный Хаффман, схема Окумуры 1988): N = 4096, F = 60, порог 2.\n// В xrCore перестройка дерева — при частоте корня 0x4000 (а не 0x8000, как в исходном LZHUF.C); проверено на архивах Чистого неба.\n// В X-Ray так сжаты чанки с флагом 0x80000000 (заголовки архивов .db, части level-файлов). Вход: u32 размер + поток бит.\nconst N = 4096, F = 60, THRESHOLD = 2, N_CHAR = 256 - THRESHOLD + F, T = N_CHAR * 2 - 1, R = T - 1, MAX_FREQ = 0x4000;\n\n// таблицы декодирования старших 6 бит позиции: коды длиной 3..8 бит (1, 3, 8, 12, 24, 16 кодов)\nconst D_CODE = new Uint8Array(256), D_LEN = new Uint8Array(256);\n{\n let i = 0, code = 0;\n [[3, 1], [4, 3], [5, 8], [6, 12], [7, 24], [8, 16]].forEach(([len, n]) => {\n for (let k = 0; k < n!; k++, code++) for (let r = 0; r < 1 << (8 - len!); r++) { D_CODE[i] = code; D_LEN[i] = len!; i++; }\n });\n}\n\n/** Распаковать LZHUF. outSize — если размер известен заранее (иначе берётся из первых 4 байт). */\nexport function lzhufDecompress(src: Uint8Array, outSize?: number): Uint8Array {\n let ip = 0;\n const size = outSize ?? (src[0]! | (src[1]! << 8) | (src[2]! << 16) | (src[3]! << 24)) >>> 0;\n if (outSize === undefined) ip = 4;\n // зашифрованный или чужой поток даёт абсурдный размер: больше 64× сжатого считаем признаком шифрования\n if (size > Math.max(1 << 16, src.length * 64)) throw new Error(`LZHUF: неправдоподобный размер ${size} (данные зашифрованы или повреждены)`);\n const out = new Uint8Array(size);\n const freq = new Uint32Array(T + 1), prnt = new Int32Array(T + N_CHAR), son = new Int32Array(T);\n for (let i = 0; i < N_CHAR; i++) { freq[i] = 1; son[i] = i + T; prnt[i + T] = i; }\n for (let i = 0, j = N_CHAR; j <= R; i += 2, j++) { freq[j] = freq[i]! + freq[i + 1]!; son[j] = i; prnt[i] = prnt[i + 1] = j; }\n freq[T] = 0xffff; prnt[R] = 0;\n\n let getbuf = 0, getlen = 0;\n const fill = () => { while (getlen <= 8) { getbuf = (getbuf | ((ip < src.length ? src[ip++]! : 0) << (8 - getlen))) & 0xffff; getlen += 8; } };\n const bit = () => { fill(); const i = getbuf; getbuf = (getbuf << 1) & 0xffff; getlen--; return (i >> 15) & 1; };\n const byte = () => { fill(); const i = getbuf; getbuf = (getbuf << 8) & 0xffff; getlen -= 8; return (i >> 8) & 0xff; };\n\n const reconst = () => {\n let j = 0;\n for (let i = 0; i < T; i++) if (son[i]! >= T) { freq[j] = (freq[i]! + 1) >> 1; son[j] = son[i]!; j++; }\n for (let i = 0, jj = N_CHAR; jj < T; i += 2, jj++) {\n const f = (freq[jj] = freq[i]! + freq[i + 1]!);\n let k = jj - 1;\n while (f < freq[k]!) k--;\n k++;\n freq.copyWithin(k + 1, k, jj); freq[k] = f;\n son.copyWithin(k + 1, k, jj); son[k] = i;\n }\n for (let i = 0; i < T; i++) { const k = son[i]!; if (k >= T) prnt[k] = i; else prnt[k] = prnt[k + 1] = i; }\n };\n const update = (ch: number) => {\n if (freq[R] === MAX_FREQ) reconst();\n let c = prnt[ch + T]!;\n do {\n const k = ++freq[c]!;\n let l = c + 1;\n if (k > freq[l]!) {\n while (k > freq[++l]!);\n l--;\n freq[c] = freq[l]!; freq[l] = k;\n const i = son[c]!;\n prnt[i] = l; if (i < T) prnt[i + 1] = l;\n const j = son[l]!;\n son[l] = i;\n prnt[j] = c; if (j < T) prnt[j + 1] = c;\n son[c] = j;\n c = l;\n }\n } while ((c = prnt[c]!) !== 0);\n };\n const decodeChar = () => { let c = son[R]!; while (c < T) c = son[c + bit()]!; c -= T; update(c); return c; };\n const decodePosition = () => {\n let i = byte();\n const c = D_CODE[i]! << 6;\n let j = D_LEN[i]! - 2;\n while (j--) i = (i << 1) + bit();\n return c | (i & 0x3f);\n };\n\n const text = new Uint8Array(N).fill(0x20);\n let r = N - F, o = 0;\n while (o < size) {\n const c = decodeChar();\n if (c < 256) { out[o++] = c; text[r] = c; r = (r + 1) & (N - 1); }\n else {\n const i = (r - decodePosition() - 1) & (N - 1), len = c - 255 + THRESHOLD;\n for (let k = 0; k < len && o < size; k++) { const b = text[(i + k) & (N - 1)]!; out[o++] = b; text[r] = b; r = (r + 1) & (N - 1); }\n }\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lzhuf.js","sources":["../src/lzhuf.ts"],"sourcesContent":["// LZHUF (LZSS + adaptive Huffman, Okumura's 1988 scheme): N = 4096, F = 60, threshold 2.\n// In xrCore the tree is rebuilt at a root frequency of 0x4000 (not 0x8000 as in the original LZHUF.C); verified on Clear Sky archives.\n// X-Ray compresses chunks with the 0x80000000 flag this way (.db archive headers, parts of level files). Input: u32 size + a bit stream.\nconst N = 4096, F = 60, THRESHOLD = 2, N_CHAR = 256 - THRESHOLD + F, T = N_CHAR * 2 - 1, R = T - 1, MAX_FREQ = 0x4000;\n\n// decoding tables for the top 6 bits of the position: codes of 3..8 bits (1, 3, 8, 12, 24, 16 codes)\nconst D_CODE = new Uint8Array(256), D_LEN = new Uint8Array(256);\n{\n let i = 0, code = 0;\n [[3, 1], [4, 3], [5, 8], [6, 12], [7, 24], [8, 16]].forEach(([len, n]) => {\n for (let k = 0; k < n!; k++, code++) for (let r = 0; r < 1 << (8 - len!); r++) { D_CODE[i] = code; D_LEN[i] = len!; i++; }\n });\n}\n\n/** Decompress LZHUF. outSize: if the size is known in advance (otherwise taken from the first 4 bytes). */\nexport function lzhufDecompress(src: Uint8Array, outSize?: number): Uint8Array {\n let ip = 0;\n const size = outSize ?? (src[0]! | (src[1]! << 8) | (src[2]! << 16) | (src[3]! << 24)) >>> 0;\n if (outSize === undefined) ip = 4;\n // an encrypted or foreign stream gives an absurd size: more than 64× the compressed size is treated as a sign of encryption\n if (size > Math.max(1 << 16, src.length * 64)) throw new Error(`LZHUF: неправдоподобный размер ${size} (данные зашифрованы или повреждены)`);\n const out = new Uint8Array(size);\n const freq = new Uint32Array(T + 1), prnt = new Int32Array(T + N_CHAR), son = new Int32Array(T);\n for (let i = 0; i < N_CHAR; i++) { freq[i] = 1; son[i] = i + T; prnt[i + T] = i; }\n for (let i = 0, j = N_CHAR; j <= R; i += 2, j++) { freq[j] = freq[i]! + freq[i + 1]!; son[j] = i; prnt[i] = prnt[i + 1] = j; }\n freq[T] = 0xffff; prnt[R] = 0;\n\n let getbuf = 0, getlen = 0;\n const fill = () => { while (getlen <= 8) { getbuf = (getbuf | ((ip < src.length ? src[ip++]! : 0) << (8 - getlen))) & 0xffff; getlen += 8; } };\n const bit = () => { fill(); const i = getbuf; getbuf = (getbuf << 1) & 0xffff; getlen--; return (i >> 15) & 1; };\n const byte = () => { fill(); const i = getbuf; getbuf = (getbuf << 8) & 0xffff; getlen -= 8; return (i >> 8) & 0xff; };\n\n const reconst = () => {\n let j = 0;\n for (let i = 0; i < T; i++) if (son[i]! >= T) { freq[j] = (freq[i]! + 1) >> 1; son[j] = son[i]!; j++; }\n for (let i = 0, jj = N_CHAR; jj < T; i += 2, jj++) {\n const f = (freq[jj] = freq[i]! + freq[i + 1]!);\n let k = jj - 1;\n while (f < freq[k]!) k--;\n k++;\n freq.copyWithin(k + 1, k, jj); freq[k] = f;\n son.copyWithin(k + 1, k, jj); son[k] = i;\n }\n for (let i = 0; i < T; i++) { const k = son[i]!; if (k >= T) prnt[k] = i; else prnt[k] = prnt[k + 1] = i; }\n };\n const update = (ch: number) => {\n if (freq[R] === MAX_FREQ) reconst();\n let c = prnt[ch + T]!;\n do {\n const k = ++freq[c]!;\n let l = c + 1;\n if (k > freq[l]!) {\n while (k > freq[++l]!);\n l--;\n freq[c] = freq[l]!; freq[l] = k;\n const i = son[c]!;\n prnt[i] = l; if (i < T) prnt[i + 1] = l;\n const j = son[l]!;\n son[l] = i;\n prnt[j] = c; if (j < T) prnt[j + 1] = c;\n son[c] = j;\n c = l;\n }\n } while ((c = prnt[c]!) !== 0);\n };\n const decodeChar = () => { let c = son[R]!; while (c < T) c = son[c + bit()]!; c -= T; update(c); return c; };\n const decodePosition = () => {\n let i = byte();\n const c = D_CODE[i]! << 6;\n let j = D_LEN[i]! - 2;\n while (j--) i = (i << 1) + bit();\n return c | (i & 0x3f);\n };\n\n const text = new Uint8Array(N).fill(0x20);\n let r = N - F, o = 0;\n while (o < size) {\n const c = decodeChar();\n if (c < 256) { out[o++] = c; text[r] = c; r = (r + 1) & (N - 1); }\n else {\n const i = (r - decodePosition() - 1) & (N - 1), len = c - 255 + THRESHOLD;\n for (let k = 0; k < len && o < size; k++) { const b = text[(i + k) & (N - 1)]!; out[o++] = b; text[r] = b; r = (r + 1) & (N - 1); }\n }\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
package/dist/motion.d.ts CHANGED
@@ -7,11 +7,11 @@ export interface XrayTrack {
7
7
  export interface XrayMotion extends MotionBase {
8
8
  kind: 'xray';
9
9
  frames: number;
10
- /** Треки по номеру кости модели; пропуск — кость не анимируется (остаётся в bind-позе). */
10
+ /** Tracks by model bone number; a gap means the bone is not animated (it stays in the bind pose). */
11
11
  tracks: XrayTrack[];
12
- /** Часть тела (ноги, торс, голова) или «эффект», если анимация управляет не всем скелетом. */
12
+ /** Body part (legs, torso, head) or "effect", if the animation does not drive the whole skeleton. */
13
13
  part?: string;
14
- /** Анимации других частей тела, играющие одновременно (ноги + торс): кости без своего трека берутся из них. */
14
+ /** Animations of other body parts playing at the same time (legs + torso): bones without their own track take it from them. */
15
15
  layers?: XrayMotion[];
16
16
  }
17
17
  declare module '@gmvjs/core/types' {
@@ -19,10 +19,10 @@ declare module '@gmvjs/core/types' {
19
19
  xray: XrayMotion;
20
20
  }
21
21
  }
22
- /** Часть «эффект»: анимация одной кости поверх остальных (esmFX). */
22
+ /** The "effect" part: an animation of one bone on top of the rest (esmFX). */
23
23
  export declare const XRAY_FX_PART = "\u044D\u0444\u0444\u0435\u043A\u0442";
24
24
  /**
25
- * Слои для анимации одной части тела (как в игре: ноги и торс играют разные анимации): для каждой другой части —
26
- * её анимация с наибольшим числом общих слов в имени («norm_torso_2_walk_0» → «norm_walk_fwd_0»), при равенстве — «idle»/«stand».
25
+ * Layers for an animation of one body part (as in the game, legs and torso play different animations): for every other part,
26
+ * its animation sharing the most words in the name ("norm_torso_2_walk_0" → "norm_walk_fwd_0"), on a tie "idle"/"stand".
27
27
  */
28
28
  export declare function autoLayers(main: XrayMotion, list: readonly Motion[]): XrayMotion[];
@@ -1 +1 @@
1
- {"version":3,"file":"motion.js","sources":["../src/motion.ts"],"sourcesContent":["// Анимации X-Ray (OGF/OMF/SDK): квантованные кватернионы и позиции по кадрам, части тела (S_SMPARAMS).\nimport type { Motion, MotionBase } from '@gmvjs/core/types';\n\nexport interface XrayTrack {\n rot: Int16Array;\n trans: Float32Array;\n constT?: boolean;\n}\nexport interface XrayMotion extends MotionBase {\n kind: 'xray';\n frames: number;\n /** Треки по номеру кости модели; пропуск — кость не анимируется (остаётся в bind-позе). */\n tracks: XrayTrack[];\n /** Часть тела (ноги, торс, голова) или «эффект», если анимация управляет не всем скелетом. */\n part?: string;\n /** Анимации других частей тела, играющие одновременно (ноги + торс): кости без своего трека берутся из них. */\n layers?: XrayMotion[];\n}\n\ndeclare module '@gmvjs/core/types' {\n interface MotionKinds { xray: XrayMotion }\n}\n\n/** Часть «эффект»: анимация одной кости поверх остальных (esmFX). */\nexport const XRAY_FX_PART = 'эффект';\n\n/**\n * Слои для анимации одной части тела (как в игре: ноги и торс играют разные анимации): для каждой другой части —\n * её анимация с наибольшим числом общих слов в имени («norm_torso_2_walk_0» → «norm_walk_fwd_0»), при равенстве — «idle»/«stand».\n */\nexport function autoLayers(main: XrayMotion, list: readonly Motion[]): XrayMotion[] {\n if (!main.part || main.part === XRAY_FX_PART) return [];\n const words = (n: string) => n.toLowerCase().split(/[_\\s]+/).filter((w) => w && !/^\\d+$/.test(w) && !['norm', 'torso', 'legs', 'head'].includes(w));\n const mw = new Set(words(main.name));\n const best = new Map<string, [XrayMotion, number]>();\n for (const x of list) {\n if (x.kind !== 'xray' || !x.part || x.part === main.part || x.part === XRAY_FX_PART || x.load) continue;\n const w = words(x.name);\n const score = w.filter((k) => mw.has(k)).length * 10 + (w.some((k) => k === 'idle' || k === 'stand') ? 2 : 0) - w.length * 0.1 - (x.name.endsWith('_') ? 1 : 0);\n const cur = best.get(x.part);\n if (!cur || score > cur[1]) best.set(x.part, [x, score]);\n }\n return [...best.values()].map(([x]) => x);\n}\n"],"names":[],"mappings":"AAwBO,MAAM,eAAe;AAMrB,SAAS,WAAW,MAAkB,MAAuC;AAClF,MAAI,CAAC,KAAK,QAAQ,KAAK,SAAS,qBAAqB,CAAA;AACrD,QAAM,QAAQ,CAAC,MAAc,EAAE,YAAA,EAAc,MAAM,QAAQ,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,QAAQ,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,SAAS,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;AAClJ,QAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,CAAC;AACnC,QAAM,2BAAW,IAAA;AACjB,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,SAAS,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,KAAK,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAM;AAC/F,UAAM,IAAI,MAAM,EAAE,IAAI;AACtB,UAAM,QAAQ,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,MAAM,UAAU,MAAM,OAAO,IAAI,IAAI,KAAK,EAAE,SAAS,OAAO,EAAE,KAAK,SAAS,GAAG,IAAI,IAAI;AAC7J,UAAM,MAAM,KAAK,IAAI,EAAE,IAAI;AAC3B,QAAI,CAAC,OAAO,QAAQ,IAAI,CAAC,EAAG,MAAK,IAAI,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;AAAA,EACzD;AACA,SAAO,CAAC,GAAG,KAAK,OAAA,CAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1C;"}
1
+ {"version":3,"file":"motion.js","sources":["../src/motion.ts"],"sourcesContent":["// X-Ray animations (OGF/OMF/SDK): quantized per-frame quaternions and positions, body parts (S_SMPARAMS).\nimport type { Motion, MotionBase } from '@gmvjs/core/types';\n\nexport interface XrayTrack {\n rot: Int16Array;\n trans: Float32Array;\n constT?: boolean;\n}\nexport interface XrayMotion extends MotionBase {\n kind: 'xray';\n frames: number;\n /** Tracks by model bone number; a gap means the bone is not animated (it stays in the bind pose). */\n tracks: XrayTrack[];\n /** Body part (legs, torso, head) or \"effect\", if the animation does not drive the whole skeleton. */\n part?: string;\n /** Animations of other body parts playing at the same time (legs + torso): bones without their own track take it from them. */\n layers?: XrayMotion[];\n}\n\ndeclare module '@gmvjs/core/types' {\n interface MotionKinds { xray: XrayMotion }\n}\n\n/** The \"effect\" part: an animation of one bone on top of the rest (esmFX). */\nexport const XRAY_FX_PART = 'эффект';\n\n/**\n * Layers for an animation of one body part (as in the game, legs and torso play different animations): for every other part,\n * its animation sharing the most words in the name (\"norm_torso_2_walk_0\" → \"norm_walk_fwd_0\"), on a tie \"idle\"/\"stand\".\n */\nexport function autoLayers(main: XrayMotion, list: readonly Motion[]): XrayMotion[] {\n if (!main.part || main.part === XRAY_FX_PART) return [];\n const words = (n: string) => n.toLowerCase().split(/[_\\s]+/).filter((w) => w && !/^\\d+$/.test(w) && !['norm', 'torso', 'legs', 'head'].includes(w));\n const mw = new Set(words(main.name));\n const best = new Map<string, [XrayMotion, number]>();\n for (const x of list) {\n if (x.kind !== 'xray' || !x.part || x.part === main.part || x.part === XRAY_FX_PART || x.load) continue;\n const w = words(x.name);\n const score = w.filter((k) => mw.has(k)).length * 10 + (w.some((k) => k === 'idle' || k === 'stand') ? 2 : 0) - w.length * 0.1 - (x.name.endsWith('_') ? 1 : 0);\n const cur = best.get(x.part);\n if (!cur || score > cur[1]) best.set(x.part, [x, score]);\n }\n return [...best.values()].map(([x]) => x);\n}\n"],"names":[],"mappings":"AAwBO,MAAM,eAAe;AAMrB,SAAS,WAAW,MAAkB,MAAuC;AAClF,MAAI,CAAC,KAAK,QAAQ,KAAK,SAAS,qBAAqB,CAAA;AACrD,QAAM,QAAQ,CAAC,MAAc,EAAE,YAAA,EAAc,MAAM,QAAQ,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,QAAQ,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,SAAS,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;AAClJ,QAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,CAAC;AACnC,QAAM,2BAAW,IAAA;AACjB,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,SAAS,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,KAAK,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAM;AAC/F,UAAM,IAAI,MAAM,EAAE,IAAI;AACtB,UAAM,QAAQ,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,KAAK,CAAC,MAAM,MAAM,UAAU,MAAM,OAAO,IAAI,IAAI,KAAK,EAAE,SAAS,OAAO,EAAE,KAAK,SAAS,GAAG,IAAI,IAAI;AAC7J,UAAM,MAAM,KAAK,IAAI,EAAE,IAAI;AAC3B,QAAI,CAAC,OAAO,QAAQ,IAAI,CAAC,EAAG,MAAK,IAAI,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;AAAA,EACzD;AACA,SAAO,CAAC,GAAG,KAAK,OAAA,CAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1C;"}
package/dist/object.d.ts CHANGED
@@ -29,7 +29,7 @@ export declare const EMESH: {
29
29
  readonly VMAPS2: 4114;
30
30
  readonly SG: 4115;
31
31
  };
32
- /** Флаги объекта (EditObject): динамический, прогрессивный, LOD, HOM, многократное использование, глушитель звука, HQ-экспорт. */
32
+ /** Object flags (EditObject): dynamic, progressive, LOD, HOM, multiple usage, sound occluder, HQ export. */
33
33
  export declare const EOBJ_FLAG: {
34
34
  readonly DYNAMIC: 1;
35
35
  readonly PROGRESSIVE: 2;
@@ -54,10 +54,10 @@ export interface XrVmap {
54
54
  dim: number;
55
55
  poly: boolean;
56
56
  type: number;
57
- /** size × dim значений. */
57
+ /** size × dim values. */
58
58
  data: Float32Array;
59
59
  vertices: Uint32Array;
60
- /** Для «поли»-карт: номер грани каждой записи. */
60
+ /** For "poly" maps: the face number of every record. */
61
61
  faces: Uint32Array | null;
62
62
  }
63
63
  export interface XrEditMesh {
@@ -65,12 +65,12 @@ export interface XrEditMesh {
65
65
  flags: number;
66
66
  options: [number, number];
67
67
  points: Float32Array;
68
- /** По 3 угла на грань: [точка, vmref]. */
68
+ /** 3 corners per face: [point, vmref]. */
69
69
  faces: Uint32Array;
70
70
  sg: Uint32Array | null;
71
- /** vmref -> список [номер vmap, индекс в vmap]. */
71
+ /** vmref -> a list of [vmap number, index in the vmap]. */
72
72
  vmrefs: Array<Array<[number, number]>>;
73
- /** Поверхность -> номера граней. */
73
+ /** Surface -> face numbers. */
74
74
  sfaces: Array<{
75
75
  surface: string;
76
76
  faces: Uint32Array;
@@ -90,20 +90,20 @@ export interface XrObject {
90
90
  modifier: string;
91
91
  modified: number;
92
92
  } | null;
93
- /** Чанки верхнего уровня, которые читатель не разбирает (кости, анимации…): сохраняются как есть. */
93
+ /** Top-level chunks the reader does not parse (bones, animations…): kept as is. */
94
94
  extra: Array<{
95
95
  id: number;
96
96
  data: Uint8Array;
97
97
  }>;
98
98
  }
99
- /** Чанки [id u32, размер u32] в диапазоне. */
99
+ /** Chunks [id u32, size u32] within a range. */
100
100
  export declare function chunksOf(buf: ArrayBuffer, start: number, end: number): Array<{
101
101
  id: number;
102
102
  start: number;
103
103
  end: number;
104
104
  }>;
105
105
  export declare function parseObject(buf: ArrayBuffer): XrObject;
106
- /** Меш для показа: по грани на угол — точка + UV из vmref; нормали по группам сглаживания. */
106
+ /** Display mesh: per face corner, a point + UV from vmref; normals by smoothing groups. */
107
107
  export interface XrObjectMesh {
108
108
  name: string;
109
109
  surface: XrSurface | undefined;
@@ -115,7 +115,7 @@ export interface XrObjectMesh {
115
115
  weight?: Float32Array;
116
116
  }
117
117
  export declare function objectMeshes(o: XrObject, smoothing?: 'auto' | 'cop' | 'soc', boneOfMap?: Map<string, number>): XrObjectMesh[];
118
- /** Кость объекта: имя, родитель (номер), имя весовой карты вершин, bind-поза. */
118
+ /** Object bone: name, parent (number), vertex weight map name, bind pose. */
119
119
  export interface XrObjectBone {
120
120
  name: string;
121
121
  parent: number;
@@ -132,7 +132,7 @@ export interface XrObjectSkeleton {
132
132
  motions: Uint8Array | null;
133
133
  motionRefs: string[];
134
134
  }
135
- /** Кости (0x0921), части тела (0x0923/0x0919), анимации (0x0916, как .skls) и ссылки на .omf (0x0926) из чанков объекта. */
135
+ /** Bones (0x0921), body parts (0x0923/0x0919), animations (0x0916, like .skls) and .omf references (0x0926) from the object chunks. */
136
136
  export declare function objectSkeleton(o: XrObject): XrObjectSkeleton | null;
137
- /** Веса вершин угла грани (vmref): до 4 костей по весовым картам (vmap типа 1), нормированные. */
137
+ /** Vertex weights of a face corner (vmref): up to 4 bones from weight maps (type-1 vmaps), normalized. */
138
138
  export declare function cornerWeights(m: XrEditMesh, vmref: number, boneOfMap: Map<string, number>): [number[], number[]];