@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.
- package/README.md +35 -29
- package/dist/db.d.ts +2 -2
- package/dist/db.js.map +1 -1
- package/dist/details.d.ts +4 -4
- package/dist/details.js.map +1 -1
- package/dist/export.d.ts +13 -13
- package/dist/export.js.map +1 -1
- package/dist/games/cop.js.map +1 -1
- package/dist/games/cs.js.map +1 -1
- package/dist/games/sdk.d.ts +1 -1
- package/dist/games/sdk.js.map +1 -1
- package/dist/games/soc.d.ts +1 -1
- package/dist/games/soc.js.map +1 -1
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/level.d.ts +5 -5
- package/dist/level.js.map +1 -1
- package/dist/load.d.ts +1 -1
- package/dist/load.js.map +1 -1
- package/dist/lzhuf.d.ts +1 -1
- package/dist/lzhuf.js.map +1 -1
- package/dist/motion.d.ts +6 -6
- package/dist/motion.js.map +1 -1
- package/dist/object.d.ts +12 -12
- package/dist/object.js.map +1 -1
- package/dist/ogf.d.ts +6 -6
- package/dist/ogf.js.map +1 -1
- package/dist/scene.d.ts +6 -6
- package/dist/scene.js.map +1 -1
- package/dist/skeleton.d.ts +4 -4
- package/dist/skeleton.js +4 -4
- package/dist/skeleton.js.map +1 -1
- package/dist/smotion.d.ts +5 -5
- package/dist/smotion.js.map +1 -1
- package/dist/soc-key.d.ts +6 -6
- package/dist/soc-key.js.map +1 -1
- package/dist/thm.d.ts +1 -1
- package/dist/thm.js.map +1 -1
- package/dist/write-object.d.ts +7 -7
- package/dist/write-object.js +1 -1
- package/dist/write-object.js.map +1 -1
- package/dist/write-omf.d.ts +9 -9
- package/dist/write-scene.d.ts +6 -6
- package/dist/write-scene.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# @gmvjs/xray-v1 — X-Ray Engine 1.x
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.patreon.com/cw/shibisty)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
X-Ray Engine 1.x (S.T.A.L.K.E.R.: 1.0 Shadow of Chernobyl, 1.5 Clear Sky, 1.6 Call of Pripyat; the parser tells the versions apart itself): OGF v4 models with skeletons and animations (embedded and external `.omf`, body parts), `.db`/`.xdb` archives (including Shadow of Chernobyl's encrypted `gamedata.db*`), compiled levels with grass from `level.details`, `.dm` details, SDK sources (`.object`, `.skl/.skls`, `.level` scenes) and export to X-Ray SDK 0.7.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
An engine plugin for [`@gmvjs/core`](../core/README.md): formats, archives and game presets. Heavy parsers are loaded on demand (`import()` inside `load`), so adding the package costs almost nothing until a file of this engine is opened.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
8
10
|
|
|
9
11
|
```sh
|
|
10
12
|
npm i @gmvjs/core @gmvjs/xray-v1 @gmvjs/codecs
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
##
|
|
15
|
+
## Usage
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
The whole engine (all games):
|
|
16
18
|
|
|
17
19
|
```ts
|
|
18
20
|
import { createRegistry, ViewerSession } from '@gmvjs/core';
|
|
@@ -21,7 +23,7 @@ import { xrayV1Engine } from '@gmvjs/xray-v1';
|
|
|
21
23
|
const session = new ViewerSession(createRegistry([xrayV1Engine]));
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
One game: a package subpath (the game preset and an engine limited to that game):
|
|
25
27
|
|
|
26
28
|
```ts
|
|
27
29
|
import { createRegistry, ViewerSession } from '@gmvjs/core';
|
|
@@ -30,43 +32,47 @@ import soc from '@gmvjs/xray-v1/soc';
|
|
|
30
32
|
const session = new ViewerSession(createRegistry([soc.engine]), soc.preset.id);
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
##
|
|
35
|
+
## Games (subpaths)
|
|
34
36
|
|
|
35
|
-
|
|
|
37
|
+
| Import | Preset | Game | Status | Only for this game |
|
|
36
38
|
|---|---|---|---|---|
|
|
37
|
-
| `@gmvjs/xray-v1/soc` | `stalker-soc` | S.T.A.L.K.E.R.:
|
|
38
|
-
| `@gmvjs/xray-v1/cs` | `stalker-cs` | S.T.A.L.K.E.R.:
|
|
39
|
-
| `@gmvjs/xray-v1/cop` | `stalker-cop` | S.T.A.L.K.E.R.:
|
|
40
|
-
| `@gmvjs/xray-v1/sdk` | — |
|
|
39
|
+
| `@gmvjs/xray-v1/soc` | `stalker-soc` | S.T.A.L.K.E.R.: Shadow of Chernobyl | 🟡 beta | `soc-key.ts`; the `gamedata.db*` file table key is looked up in the user's `bin/XR_3DA.exe`; the package contains no keys |
|
|
40
|
+
| `@gmvjs/xray-v1/cs` | `stalker-cs` | S.T.A.L.K.E.R.: Clear Sky | 🟡 beta | — |
|
|
41
|
+
| `@gmvjs/xray-v1/cop` | `stalker-cop` | S.T.A.L.K.E.R.: Call of Pripyat | 🟡 beta | — |
|
|
42
|
+
| `@gmvjs/xray-v1/sdk` | — | export to X-Ray SDK 0.7 (`ExporterPlugin` `xray-sdk`): `.object`, `.level` + `scene_object.part`, DDS + `.thm`; also used for Metro maps | — | `export.ts`, `write-object.ts`, `write-scene.ts` |
|
|
41
43
|
|
|
42
|
-
##
|
|
44
|
+
## Formats
|
|
43
45
|
|
|
44
|
-
| id |
|
|
46
|
+
| id | What | Files | Status |
|
|
45
47
|
|---|---|---|---|
|
|
46
|
-
| `xray-ogf` | X-Ray OGF | `.ogf` +
|
|
47
|
-
| `xray-object` | X-Ray SDK .object | `.object` +
|
|
48
|
-
| `xray-level` | X-Ray:
|
|
49
|
-
| `xray-dm` | X-Ray:
|
|
50
|
-
| `xray-scene` | X-Ray SDK:
|
|
48
|
+
| `xray-ogf` | X-Ray OGF | `.ogf` + `.omf`, `.skl`, `.skls` animations | 🟡 beta |
|
|
49
|
+
| `xray-object` | X-Ray SDK .object | `.object` + `.omf`, `.skl`, `.skls` animations | 🟡 beta |
|
|
50
|
+
| `xray-level` | X-Ray: compiled level (level + level.geom) | by path `/(^|\/)levels\/[^/]+\/level$/` | 🟡 beta |
|
|
51
|
+
| `xray-dm` | X-Ray: detail (.dm: grass, bushes) | `.dm` | 🟡 beta |
|
|
52
|
+
| `xray-scene` | X-Ray SDK: level (.level + scene_object.part) | `.level` | 🟡 beta |
|
|
51
53
|
|
|
52
|
-
##
|
|
54
|
+
## Archives
|
|
53
55
|
|
|
54
|
-
- `xray-db` — X-Ray .db / .xdb (
|
|
56
|
+
- `xray-db` — X-Ray .db / .xdb (Clear Sky, Call of Pripyat) (`.db`, `.xdb`, `.db0`, `.xdb0`, `.db1`, `.xdb1`, `.db2`, `.xdb2`…)
|
|
55
57
|
|
|
56
|
-
##
|
|
58
|
+
## Export
|
|
57
59
|
|
|
58
|
-
- `xray-sdk` — S.T.A.L.K.E.R.:
|
|
60
|
+
- `xray-sdk` — S.T.A.L.K.E.R.: X-Ray SDK 0.7 sources (subpath `@gmvjs/xray-v1/sdk`)
|
|
59
61
|
|
|
60
|
-
##
|
|
62
|
+
## Dependencies
|
|
61
63
|
|
|
62
|
-
- `@gmvjs/core` — API
|
|
64
|
+
- `@gmvjs/core` — plugin API, types, ZIP/folder archives, zlib, DDS/BC, geometry
|
|
63
65
|
- `@gmvjs/codecs` — LZO
|
|
64
66
|
|
|
65
|
-
##
|
|
67
|
+
## Not done yet
|
|
66
68
|
|
|
67
|
-
- OGF
|
|
68
|
-
- SDK:
|
|
69
|
+
- OGF older than v4 and binary `.level` files of old SDKs (no samples)
|
|
70
|
+
- SDK: checking in LevelEditor, mapping materials to shaders/gamemtl, writing OGF/OMF
|
|
69
71
|
|
|
70
|
-
##
|
|
72
|
+
## License
|
|
71
73
|
|
|
72
74
|
MIT
|
|
75
|
+
|
|
76
|
+
[](https://www.patreon.com/cw/shibisty)
|
|
77
|
+
|
|
78
|
+
If this project helps you, consider supporting its development on Patreon ❤️
|
package/dist/db.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ interface DbEntry extends ArchiveEntry {
|
|
|
4
4
|
real: number;
|
|
5
5
|
packed: number;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/** Parse the (unpacked) file table. */
|
|
8
8
|
export declare function parseDbTable(t: Uint8Array): DbEntry[];
|
|
9
|
-
/**
|
|
9
|
+
/** Forget found keys, including stored ones (tests, "reset"). */
|
|
10
10
|
export declare const resetSocKeys: () => void;
|
|
11
11
|
export declare function openXrayDb(file: BlobFile, siblings?: readonly BlobFile[]): Promise<Archive>;
|
|
12
12
|
export {};
|
package/dist/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sources":["../src/db.ts"],"sourcesContent":["// Архивы X-Ray (.db, .db0…, .xdb): Чистое небо, Зов Припяти и моды. Файл — цепочка чанков [id u32, размер u32]:\n// 0x29A — текстовый [header] (ЧН/ЗП), 0 — данные, 1 — таблица файлов (флаг 0x80000000 — сжата LZHUF).\n// Запись таблицы: u16 длина, затем размер, сжатый размер, CRC, имя (без нуля), смещение в архиве.\n// Сжатые файлы (размер ≠ сжатому) — LZO1X. Тень Чернобыля шифрует таблицу файлов — такие архивы не открываются.\nimport { normPath } from '@gmvjs/core/binary';\nimport { createArchive, readRange } from '@gmvjs/core/archives/common';\nimport { lzhufDecompress } from './lzhuf';\nimport { lzo1xDecompress } from '@gmvjs/codecs';\nimport type { Archive, ArchiveEntry, BlobFile } from '@gmvjs/core/types';\nimport type { SocKey } from './soc-key';\n\ninterface DbEntry extends ArchiveEntry { off: number; real: number; packed: number }\n\nconst CHUNK_HEADER = 1, COMPRESSED = 0x80000000;\n\n/** Найти чанк таблицы файлов, пробегая заголовки чанков. */\nasync function findTable(file: BlobFile): Promise<{ id: number; off: number; size: number } | null> {\n let p = 0;\n for (let n = 0; n < 64 && p + 8 <= file.size; n++) {\n const h = await readRange(file, p, p + 8), dv = new DataView(h.buffer, h.byteOffset, 8);\n const id = dv.getUint32(0, true), size = dv.getUint32(4, true);\n if (p + 8 + size > file.size) return null;\n if ((id & ~COMPRESSED) >>> 0 === CHUNK_HEADER) return { id, off: p + 8, size };\n p += 8 + size;\n }\n return null;\n}\n\n/** Разбор таблицы файлов (распакованной). */\nexport function parseDbTable(t: Uint8Array): DbEntry[] {\n const dv = new DataView(t.buffer, t.byteOffset, t.byteLength), out: DbEntry[] = [];\n let q = 0;\n while (q + 2 <= t.length) {\n const bs = dv.getUint16(q, true);\n if (bs < 16 || q + 2 + bs > t.length) throw new Error(`таблица файлов повреждена (смещение ${q})`);\n const real = dv.getUint32(q + 2, true), packed = dv.getUint32(q + 6, true);\n let name = '';\n for (let k = q + 14; k < q + 2 + bs - 4; k++) name += String.fromCharCode(t[k]!);\n const off = dv.getUint32(q + 2 + bs - 4, true);\n q += 2 + bs;\n if (!real && !packed) continue; // папка\n out.push({ path: normPath(name), size: real, off, real, packed });\n }\n return out;\n}\n\n/** Ключи «Тени Чернобыля», найденные в исполняемых файлах пользователя (по имени и размеру файла), — на время работы страницы. */\nconst socKeys = new Map<string, SocKey>();\n/**\n * Найденные ключи хранятся и в localStorage страницы (только на компьютере пользователя): после того как ключ один раз\n * найден в его XR_3DA.exe, архивы ТЧ открываются и без exe. В сборку расширения ключи не попадают.\n */\nconst STORE = 'gmv.xray.socKeys';\nfunction loadStored(): void {\n try {\n const list = JSON.parse(globalThis.localStorage?.getItem(STORE) ?? '[]') as Array<[string, SocKey]>;\n for (const [id, k] of list) if (k && Number.isFinite(k.tableSeed) && Number.isFinite(k.encryptSeed) && Number.isFinite(k.iterations)) socKeys.set(id, k);\n } catch { /* хранилище недоступно — ключи только на время работы страницы */ }\n}\nfunction saveStored(): void {\n try { globalThis.localStorage?.setItem(STORE, JSON.stringify([...socKeys])); } catch { /* недоступно */ }\n}\nlet storedLoaded = false;\n/** Забыть найденные ключи, в том числе сохранённые (тесты, «сброс»). */\nexport const resetSocKeys = (): void => { socKeys.clear(); storedLoaded = true; try { globalThis.localStorage?.removeItem(STORE); } catch { /* недоступно */ } };\n\n/** Разобрать таблицу: записи есть и лежат внутри архива. */\nfunction tableEntries(table: Uint8Array, fileSize: number): DbEntry[] | null {\n try { const e = parseDbTable(table); return e.length && !e.some((x) => x.off + x.packed > fileSize) ? e : null; } catch { return null; }\n}\n\nexport async function openXrayDb(file: BlobFile, siblings: readonly BlobFile[] = []): Promise<Archive> {\n const tab = await findTable(file);\n if (!tab) throw new Error(`${file.name}: не архив X-Ray (нет таблицы файлов)`);\n const raw = await readRange(file, tab.off, tab.off + tab.size);\n let entries: DbEntry[] | null = null;\n try { entries = tableEntries(tab.id & COMPRESSED ? lzhufDecompress(raw) : raw, file.size); } catch { entries = null; }\n if (!entries && tab.id & COMPRESSED) {\n // «Тень Чернобыля»: таблица зашифрована — ключ ищется в XR_3DA.exe, выбранном вместе с архивами (или в папке игры)\n const { findSocKey, socDecode } = await import('./soc-key');\n const check = (t: Uint8Array) => !!tableEntries(t, file.size);\n if (!storedLoaded) { storedLoaded = true; loadStored(); }\n for (const key of socKeys.values()) {\n try { entries = tableEntries(lzhufDecompress(socDecode(key, raw)), file.size); } catch { entries = null; }\n if (entries) break;\n }\n const exes = siblings.filter((f) => /\\.exe$/i.test(f.name)).sort((a, b) => +!/xr_3da/i.test(a.name) - +!/xr_3da/i.test(b.name));\n const tried: string[] = [];\n for (const exe of entries ? [] : exes) {\n const id = `${exe.name}:${exe.size}`;\n if (socKeys.has(id)) continue;\n let key: SocKey | null = null;\n try { key = findSocKey(new Uint8Array(await exe.arrayBuffer()), raw, check); }\n catch (e) { tried.push(`${exe.webkitRelativePath || exe.name}: ${(e as Error).message}`); continue; }\n if (!key) { tried.push(`${exe.webkitRelativePath || exe.name} (${exe.size} Б) — ключ не найден`); continue; }\n socKeys.set(id, key);\n saveStored();\n entries = tableEntries(lzhufDecompress(socDecode(key, raw)), file.size);\n if (entries) break;\n }\n if (!entries)\n throw new Error(`${file.name}: таблица файлов зашифрована (архивы «Тени Чернобыля»). Откройте папку игры целиком («Папка игры…») или выберите кнопкой «Открыть .db…» сначала bin\\\\XR_3DA.exe, затем архивы — ключ берётся из него. ${tried.length ? `Проверены: ${tried.join('; ')}.` : `Исполняемых файлов игры среди выбранных нет (выбрано файлов: ${siblings.length}).`}`);\n }\n if (!entries) throw new Error(`${file.name}: таблица файлов повреждена.`);\n return createArchive<DbEntry>({\n name: file.name,\n kind: 'xray-db',\n version: 0,\n entries,\n async read(e) {\n const u = await readRange(file, e.off, e.off + e.packed);\n if (e.packed === e.real) return u;\n const out = new Uint8Array(e.real);\n lzo1xDecompress(u, 0, out);\n return out;\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"db.js","sources":["../src/db.ts"],"sourcesContent":["// X-Ray archives (.db, .db0…, .xdb): Clear Sky, Call of Pripyat and mods. A file is a chain of chunks [id u32, size u32]:\n// 0x29A = the text [header] (CS/CoP), 0 = data, 1 = the file table (flag 0x80000000 = LZHUF-compressed).\n// Table record: u16 length, then size, compressed size, CRC, name (no zero), offset in the archive.\n// Compressed files (size ≠ compressed size) use LZO1X. Shadow of Chernobyl encrypts the file table; such archives do not open without the key.\nimport { normPath } from '@gmvjs/core/binary';\nimport { createArchive, readRange } from '@gmvjs/core/archives/common';\nimport { lzhufDecompress } from './lzhuf';\nimport { lzo1xDecompress } from '@gmvjs/codecs';\nimport type { Archive, ArchiveEntry, BlobFile } from '@gmvjs/core/types';\nimport type { SocKey } from './soc-key';\n\ninterface DbEntry extends ArchiveEntry { off: number; real: number; packed: number }\n\nconst CHUNK_HEADER = 1, COMPRESSED = 0x80000000;\n\n/** Find the file table chunk by walking the chunk headers. */\nasync function findTable(file: BlobFile): Promise<{ id: number; off: number; size: number } | null> {\n let p = 0;\n for (let n = 0; n < 64 && p + 8 <= file.size; n++) {\n const h = await readRange(file, p, p + 8), dv = new DataView(h.buffer, h.byteOffset, 8);\n const id = dv.getUint32(0, true), size = dv.getUint32(4, true);\n if (p + 8 + size > file.size) return null;\n if ((id & ~COMPRESSED) >>> 0 === CHUNK_HEADER) return { id, off: p + 8, size };\n p += 8 + size;\n }\n return null;\n}\n\n/** Parse the (unpacked) file table. */\nexport function parseDbTable(t: Uint8Array): DbEntry[] {\n const dv = new DataView(t.buffer, t.byteOffset, t.byteLength), out: DbEntry[] = [];\n let q = 0;\n while (q + 2 <= t.length) {\n const bs = dv.getUint16(q, true);\n if (bs < 16 || q + 2 + bs > t.length) throw new Error(`таблица файлов повреждена (смещение ${q})`);\n const real = dv.getUint32(q + 2, true), packed = dv.getUint32(q + 6, true);\n let name = '';\n for (let k = q + 14; k < q + 2 + bs - 4; k++) name += String.fromCharCode(t[k]!);\n const off = dv.getUint32(q + 2 + bs - 4, true);\n q += 2 + bs;\n if (!real && !packed) continue; // folder\n out.push({ path: normPath(name), size: real, off, real, packed });\n }\n return out;\n}\n\n/** Shadow of Chernobyl keys found in the user's executables (by file name and size), kept while the page is open. */\nconst socKeys = new Map<string, SocKey>();\n/**\n * Found keys are also stored in the page's localStorage (only on the user's computer): once a key has been\n * found in their XR_3DA.exe, SoC archives open without the exe. Keys are never included in the extension build.\n */\nconst STORE = 'gmv.xray.socKeys';\nfunction loadStored(): void {\n try {\n const list = JSON.parse(globalThis.localStorage?.getItem(STORE) ?? '[]') as Array<[string, SocKey]>;\n for (const [id, k] of list) if (k && Number.isFinite(k.tableSeed) && Number.isFinite(k.encryptSeed) && Number.isFinite(k.iterations)) socKeys.set(id, k);\n } catch { /* storage unavailable: keys live only while the page is open */ }\n}\nfunction saveStored(): void {\n try { globalThis.localStorage?.setItem(STORE, JSON.stringify([...socKeys])); } catch { /* unavailable */ }\n}\nlet storedLoaded = false;\n/** Forget found keys, including stored ones (tests, \"reset\"). */\nexport const resetSocKeys = (): void => { socKeys.clear(); storedLoaded = true; try { globalThis.localStorage?.removeItem(STORE); } catch { /* unavailable */ } };\n\n/** Parse the table: records exist and lie inside the archive. */\nfunction tableEntries(table: Uint8Array, fileSize: number): DbEntry[] | null {\n try { const e = parseDbTable(table); return e.length && !e.some((x) => x.off + x.packed > fileSize) ? e : null; } catch { return null; }\n}\n\nexport async function openXrayDb(file: BlobFile, siblings: readonly BlobFile[] = []): Promise<Archive> {\n const tab = await findTable(file);\n if (!tab) throw new Error(`${file.name}: не архив X-Ray (нет таблицы файлов)`);\n const raw = await readRange(file, tab.off, tab.off + tab.size);\n let entries: DbEntry[] | null = null;\n try { entries = tableEntries(tab.id & COMPRESSED ? lzhufDecompress(raw) : raw, file.size); } catch { entries = null; }\n if (!entries && tab.id & COMPRESSED) {\n // Shadow of Chernobyl: the table is encrypted; the key is searched for in the XR_3DA.exe selected together with the archives (or in the game folder)\n const { findSocKey, socDecode } = await import('./soc-key');\n const check = (t: Uint8Array) => !!tableEntries(t, file.size);\n if (!storedLoaded) { storedLoaded = true; loadStored(); }\n for (const key of socKeys.values()) {\n try { entries = tableEntries(lzhufDecompress(socDecode(key, raw)), file.size); } catch { entries = null; }\n if (entries) break;\n }\n const exes = siblings.filter((f) => /\\.exe$/i.test(f.name)).sort((a, b) => +!/xr_3da/i.test(a.name) - +!/xr_3da/i.test(b.name));\n const tried: string[] = [];\n for (const exe of entries ? [] : exes) {\n const id = `${exe.name}:${exe.size}`;\n if (socKeys.has(id)) continue;\n let key: SocKey | null = null;\n try { key = findSocKey(new Uint8Array(await exe.arrayBuffer()), raw, check); }\n catch (e) { tried.push(`${exe.webkitRelativePath || exe.name}: ${(e as Error).message}`); continue; }\n if (!key) { tried.push(`${exe.webkitRelativePath || exe.name} (${exe.size} Б) — ключ не найден`); continue; }\n socKeys.set(id, key);\n saveStored();\n entries = tableEntries(lzhufDecompress(socDecode(key, raw)), file.size);\n if (entries) break;\n }\n if (!entries)\n throw new Error(`${file.name}: таблица файлов зашифрована (архивы «Тени Чернобыля»). Откройте папку игры целиком («Папка игры…») или выберите кнопкой «Открыть .db…» сначала bin\\\\XR_3DA.exe, затем архивы — ключ берётся из него. ${tried.length ? `Проверены: ${tried.join('; ')}.` : `Исполняемых файлов игры среди выбранных нет (выбрано файлов: ${siblings.length}).`}`);\n }\n if (!entries) throw new Error(`${file.name}: таблица файлов повреждена.`);\n return createArchive<DbEntry>({\n name: file.name,\n kind: 'xray-db',\n version: 0,\n entries,\n async read(e) {\n const u = await readRange(file, e.off, e.off + e.packed);\n if (e.packed === e.real) return u;\n const out = new Uint8Array(e.real);\n lzo1xDecompress(u, 0, out);\n return out;\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
package/dist/details.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface DetailModel {
|
|
|
10
10
|
indices: Uint16Array;
|
|
11
11
|
}
|
|
12
12
|
export declare function parseDM(u: Uint8Array): DetailModel;
|
|
13
|
-
/**
|
|
13
|
+
/** .dm model mesh (normals up, as grass is lit). */
|
|
14
14
|
export declare function dmMesh(m: DetailModel, name: string): Mesh;
|
|
15
15
|
export interface DetailsMap {
|
|
16
16
|
version: number;
|
|
@@ -22,7 +22,7 @@ export interface DetailsMap {
|
|
|
22
22
|
slots: DataView;
|
|
23
23
|
}
|
|
24
24
|
export declare function parseDetails(u: Uint8Array): DetailsMap;
|
|
25
|
-
/**
|
|
25
|
+
/** Level collision (level.cform): u32 version 4, vertices, triangles, box (6×f32); vertices f32×3; triangles {u32×3, u32 material}. */
|
|
26
26
|
export interface CForm {
|
|
27
27
|
verts: Float32Array;
|
|
28
28
|
tris: Uint32Array;
|
|
@@ -36,7 +36,7 @@ export interface DetailsResult {
|
|
|
36
36
|
keep: number;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
39
|
+
* Detail placement from the map and collision; budget is the triangle limit (the excess is thinned uniformly).
|
|
40
|
+
* Coordinates are as for the level geometry (left-handed, Y up).
|
|
41
41
|
*/
|
|
42
42
|
export declare function buildDetails(map: DetailsMap, cf: CForm, budget?: number): DetailsResult;
|
package/dist/details.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"details.js","sources":["../src/details.ts"],"sourcesContent":["// Трава и мелкие детали X-Ray: модели .dm и карта level.details (ТЧ, ЧН, ЗП).\n// .dm: шейдер, текстура (строки), u32 флаги, f32 мин./макс. масштаб, u32 вершин, u32 индексов, вершины {f32 x,y,z,u,v}, индексы u16.\n// level.details — чанки: 0 заголовок (u32 версия 3, объектов, offs_x, offs_z, size_x, size_z), 1 модели (вложенные чанки .dm),\n// 2 ячейки 2×2 м по 16 байт: u32 y_base:12 (×0.2 − 200 м), y_height:8 (×0.1 м), id0:6, id1:6; u32 id2:6, id3:6, свет…;\n// 4×u16 плотности (по 4 бита на угол ячейки). Ячейка (x, z) лежит в мире с ((x − offs_x)·2, (z − offs_z)·2).\n// Расстановку игра считает сама при показе (CDetailManager::cache_Decompress): сетка точек с дрожанием, выбор модели\n// по интерполированной плотности с упорядоченным дизерингом, высота — луч вниз по коллизии уровня (level.cform),\n// поворот вокруг Y и масштаб — случайные. Здесь тот же порядок; при большом числе треугольников трава прореживается.\nimport type { Mesh } from '@gmvjs/core/types';\n\nexport interface DetailModel { shader: string; texture: string; flags: number; minScale: number; maxScale: number; pos: Float32Array; uv: Float32Array; indices: Uint16Array }\n\nconst dvOf = (u: Uint8Array) => new DataView(u.buffer, u.byteOffset, u.byteLength);\n\nfunction chunks(u: Uint8Array): Map<number, Uint8Array> {\n const d = dvOf(u), out = new Map<number, Uint8Array>();\n for (let p = 0; p + 8 <= u.length; ) {\n const id = d.getUint32(p, true) & 0x7fffffff, size = d.getUint32(p + 4, true);\n if (p + 8 + size > u.length) break;\n out.set(id, u.subarray(p + 8, p + 8 + size));\n p += 8 + size;\n }\n return out;\n}\n\nexport function parseDM(u: Uint8Array): DetailModel {\n const d = dvOf(u);\n let p = 0;\n const str = () => { const e = u.indexOf(0, p); if (e < 0) throw new Error('.dm: строка не закрыта'); const s = new TextDecoder('latin1').decode(u.subarray(p, e)); p = e + 1; return s; };\n const shader = str(), texture = str();\n if (p + 20 > u.length) throw new Error('.dm обрезан');\n const flags = d.getUint32(p, true), minScale = d.getFloat32(p + 4, true), maxScale = d.getFloat32(p + 8, true), nv = d.getUint32(p + 12, true), ni = d.getUint32(p + 16, true);\n p += 20;\n if (nv > 65536 || ni % 3 || p + nv * 20 + ni * 2 > u.length) throw new Error('.dm: неверное число вершин или индексов');\n const pos = new Float32Array(nv * 3), uv = new Float32Array(nv * 2), indices = new Uint16Array(ni);\n for (let i = 0; i < nv; i++, p += 20) { pos[i * 3] = d.getFloat32(p, true); pos[i * 3 + 1] = d.getFloat32(p + 4, true); pos[i * 3 + 2] = d.getFloat32(p + 8, true); uv[i * 2] = d.getFloat32(p + 12, true); uv[i * 2 + 1] = d.getFloat32(p + 16, true); }\n for (let i = 0; i < ni; i++, p += 2) indices[i] = d.getUint16(p, true);\n return { shader, texture, flags, minScale, maxScale, pos, uv, indices };\n}\n\n/** Меш модели .dm (нормали вверх — как освещается трава). */\nexport function dmMesh(m: DetailModel, name: string): Mesh {\n const n = m.pos.length / 3, nor = new Float32Array(n * 3);\n for (let i = 0; i < n; i++) nor[i * 3 + 1] = 1;\n return { name, texture: m.texture, shader: m.shader, vertexCount: n, pos: m.pos, nor, uv: m.uv, bone: new Float32Array(n * 4), weight: new Float32Array(n * 4).map((_, i) => (i % 4 === 0 ? 1 : 0)), indices: m.indices, skinned: false, alphaTest: true };\n}\n\nexport interface DetailsMap { version: number; offsX: number; offsZ: number; sizeX: number; sizeZ: number; models: DetailModel[]; slots: DataView }\n\nexport function parseDetails(u: Uint8Array): DetailsMap {\n const c = chunks(u), h = c.get(0), objs = c.get(1), sl = c.get(2);\n if (!h || h.length < 24 || !objs || !sl) throw new Error('level.details: нет заголовка, моделей или ячеек');\n const hd = dvOf(h), [version, , offsX, offsZ, sizeX, sizeZ] = [0, 4, 8, 12, 16, 20].map((o) => hd.getUint32(o, true)) as [number, number, number, number, number, number];\n if (version !== 3) throw new Error(`level.details версии ${version} не поддерживается`);\n if (sizeX * sizeZ * 16 > sl.length) throw new Error('level.details: ячейки обрезаны');\n const models = [...chunks(objs)].sort((a, b) => a[0] - b[0]).map(([, b]) => parseDM(b));\n return { version, offsX, offsZ, sizeX, sizeZ, models, slots: dvOf(sl) };\n}\n\n/** Коллизия уровня (level.cform): u32 версия 4, вершин, треугольников, рамка (6×f32); вершины f32×3; треугольники {u32×3, u32 материал}. */\nexport interface CForm { verts: Float32Array; tris: Uint32Array }\nexport function parseCForm(u: Uint8Array): CForm {\n const d = dvOf(u), ver = d.getUint32(0, true), nv = d.getUint32(4, true), nt = d.getUint32(8, true);\n if (ver !== 4 || 36 + nv * 12 + nt * 16 > u.length) throw new Error('level.cform: неизвестный формат');\n const verts = new Float32Array(nv * 3), tris = new Uint32Array(nt * 3);\n for (let i = 0, p = 36; i < nv * 3; i++, p += 4) verts[i] = d.getFloat32(p, true);\n for (let i = 0, p = 36 + nv * 12; i < nt; i++, p += 16) { tris[i * 3] = d.getUint32(p, true); tris[i * 3 + 1] = d.getUint32(p + 4, true); tris[i * 3 + 2] = d.getUint32(p + 8, true); }\n return { verts, tris };\n}\n\n/** CRandom движка (как rand() MSVC). */\nclass CRandom {\n constructor(private s: number) {}\n next() { this.s = (Math.imul(this.s, 214013) + 2531011) >>> 0; return (this.s >>> 16) & 0x7fff; }\n randI(n: number) { return this.next() % n; }\n randF(a: number, b: number) { return a + ((b - a) * this.next()) / 32767; }\n randFs(r: number) { return this.randF(-r, r); }\n}\n\n/** Упорядоченный дизеринг 16×16 (матрица Байера, пороги 0..255). */\nconst DITHER = (() => {\n let m = [[0]];\n for (let n = 1; n < 16; n *= 2) m = Array.from({ length: n * 2 }, (_, y) => Array.from({ length: n * 2 }, (_, x) => 4 * m[y % n]![x % n]! + [[0, 2], [3, 1]][+(y >= n)]![+(x >= n)]!));\n return Uint8Array.from(m.flat());\n})();\nexport const DETAIL_TRI_BUDGET = 1_500_000;\n/** Плотность травы по умолчанию (r__detail_density): шаг сетки точек в метрах. */\nconst DENSITY = 0.3, SLOT = 2;\n\nexport interface DetailsResult { meshes: Mesh[]; items: number; candidates: number; keep: number }\n\n/**\n * Расстановка деталей по карте и коллизии; budget — предел треугольников (лишнее прореживается равномерно).\n * Координаты — как у геометрии уровня (левосторонние, Y вверх).\n */\nexport function buildDetails(map: DetailsMap, cf: CForm, budget = DETAIL_TRI_BUDGET): DetailsResult {\n const { sizeX, sizeZ, offsX, offsZ, slots, models } = map, nObj = models.length;\n const dSize = Math.ceil(SLOT / DENSITY), jitter = DENSITY / 1.7;\n // непустые ячейки и их высоты\n const slotOk = new Uint8Array(sizeX * sizeZ), yLo = new Float32Array(sizeX * sizeZ), yHi = new Float32Array(sizeX * sizeZ);\n for (let i = 0; i < sizeX * sizeZ; i++) {\n const a = slots.getUint32(i * 16, true), b = slots.getUint32(i * 16 + 4, true);\n const ids = [(a >>> 20) & 63, (a >>> 26) & 63, b & 63, (b >>> 6) & 63];\n let any = false;\n for (let k = 0; k < 4; k++) if (ids[k]! < nObj && slots.getUint16(i * 16 + 8 + k * 2, true)) any = true;\n if (!any) continue;\n slotOk[i] = 1; yLo[i] = (a & 0xfff) * 0.2 - 200; yHi[i] = yLo[i]! + ((a >>> 12) & 0xff) * 0.1;\n }\n // треугольники коллизии по ячейкам (только непустые)\n const { verts: V, tris: T } = cf, nt = T.length / 3;\n const cnt = new Uint32Array(sizeX * sizeZ + 1);\n const forCells = (t: number, f: (cell: number) => void) => {\n const a = T[t * 3]! * 3, b = T[t * 3 + 1]! * 3, c = T[t * 3 + 2]! * 3;\n const x0 = Math.floor(Math.min(V[a]!, V[b]!, V[c]!) / SLOT) + offsX, x1 = Math.floor(Math.max(V[a]!, V[b]!, V[c]!) / SLOT) + offsX;\n const z0 = Math.floor(Math.min(V[a + 2]!, V[b + 2]!, V[c + 2]!) / SLOT) + offsZ, z1 = Math.floor(Math.max(V[a + 2]!, V[b + 2]!, V[c + 2]!) / SLOT) + offsZ;\n const ylo = Math.min(V[a + 1]!, V[b + 1]!, V[c + 1]!), yhi = Math.max(V[a + 1]!, V[b + 1]!, V[c + 1]!);\n for (let z = Math.max(0, z0); z <= Math.min(sizeZ - 1, z1); z++) for (let x = Math.max(0, x0); x <= Math.min(sizeX - 1, x1); x++) {\n const cell = z * sizeX + x;\n if (slotOk[cell] && yhi >= yLo[cell]! - 0.01 && ylo <= yHi[cell]! + 0.01) f(cell);\n }\n };\n for (let t = 0; t < nt; t++) forCells(t, (cell) => cnt[cell + 1]!++);\n for (let i = 0; i < sizeX * sizeZ; i++) cnt[i + 1]! += cnt[i]!;\n const fill = cnt.slice(0, sizeX * sizeZ), list = new Uint32Array(cnt[sizeX * sizeZ]!);\n for (let t = 0; t < nt; t++) forCells(t, (cell) => { list[fill[cell]!++] = t; });\n\n // проход по ячейкам: kind 0 — подсчёт треугольников, 1 — постановка\n const triOf = models.map((m) => m.indices.length / 3);\n const alpha = new Float32Array(16), ids = new Int32Array(4), sel: number[] = [];\n const walk = (emit: (obj: number, x: number, y: number, z: number, scale: number, yaw: number, cell: number) => void, needY: boolean) => {\n for (let dz = 0; dz < sizeZ; dz++) for (let dx = 0; dx < sizeX; dx++) {\n const cell = dz * sizeX + dx;\n if (!slotOk[cell]) continue;\n const o = cell * 16, a = slots.getUint32(o, true), b = slots.getUint32(o + 4, true);\n ids[0] = (a >>> 20) & 63; ids[1] = (a >>> 26) & 63; ids[2] = b & 63; ids[3] = (b >>> 6) & 63;\n for (let k = 0; k < 4; k++) { const pal = slots.getUint16(o + 8 + k * 2, true); for (let j = 0; j < 4; j++) alpha[k * 4 + j] = (255 * ((pal >> (j * 4)) & 15)) / 15; }\n const sx = dx - offsX, sz = dz - offsZ, seed = (0x12071980 ^ Math.imul(sx, sz)) >>> 0;\n const rSel = new CRandom(seed), rJit = new CRandom(seed), rYaw = new CRandom(seed), rScl = new CRandom(seed);\n const minX = sx * SLOT, minZ = sz * SLOT, top = yHi[cell]!, bottom = yLo[cell]!;\n for (let z = 0; z <= dSize; z++) for (let x = 0; x <= dSize; x++) {\n const shx = rJit.randI(16), shz = rJit.randI(16);\n sel.length = 0;\n const cx = Math.min(x, dSize - 1), cz = Math.min(z, dSize - 1), fx = cx / dSize, fz = cz / dSize;\n for (let k = 0; k < 4; k++) {\n if (ids[k]! >= nObj) continue;\n const c01 = alpha[k * 4]! * (1 - fx) + alpha[k * 4 + 1]! * fx, c02 = alpha[k * 4 + 2]! * (1 - fx) + alpha[k * 4 + 3]! * fx;\n const c = Math.min(255, Math.max(0, Math.floor(c01 * (1 - fz) + c02 * fz + 0.5)));\n if (c > DITHER[((cx + shx) % 16) * 16 + ((cz + shz) % 16)]!) sel.push(k);\n }\n if (!sel.length) continue;\n const obj = ids[sel.length === 1 ? sel[0]! : sel[rSel.randI(sel.length)]!]!;\n const px = (x / dSize) * SLOT + minX + rJit.randFs(jitter), pz = (z / dSize) * SLOT + minZ + rJit.randFs(jitter);\n let y = bottom - 5;\n if (needY) {\n for (let i = cnt[cell]!; i < cnt[cell + 1]!; i++) {\n const t = list[i]!, A = T[t * 3]! * 3, B = T[t * 3 + 1]! * 3, C = T[t * 3 + 2]! * 3;\n // вертикальный луч: барицентрические координаты в плоскости XZ\n const ax = V[A]!, az = V[A + 2]!, e1x = V[B]! - ax, e1z = V[B + 2]! - az, e2x = V[C]! - ax, e2z = V[C + 2]! - az;\n const det = e1x * e2z - e2x * e1z;\n if (Math.abs(det) < 1e-9) continue;\n const qx = px - ax, qz = pz - az, u = (qx * e2z - e2x * qz) / det, v = (e1x * qz - qx * e1z) / det;\n if (u < 0 || v < 0 || u + v > 1) continue;\n const yt = V[A + 1]! + u * (V[B + 1]! - V[A + 1]!) + v * (V[C + 1]! - V[A + 1]!);\n if (yt <= top + 1e-3 && yt > y) y = yt;\n }\n if (y < bottom) continue;\n }\n const m = models[obj]!, scale = rScl.randF(m.minScale * 0.5, m.maxScale * 0.9), yaw = rYaw.randF(0, Math.PI * 2);\n emit(obj, px, y, pz, scale, yaw, cell);\n }\n }\n };\n let candTris = 0, candidates = 0;\n walk((obj) => { candTris += triOf[obj]!; candidates++; }, false);\n const keep = candTris > budget ? budget / candTris : 1;\n const thin = new CRandom(0x5eed);\n const P: number[][] = models.map(() => []), U: number[][] = models.map(() => []), I: number[][] = models.map(() => []);\n let items = 0;\n walk((obj, x, y, z, s, yaw) => {\n if (keep < 1 && thin.next() / 32768 >= keep) return;\n const m = models[obj]!, c = Math.cos(yaw), sn = Math.sin(yaw), p = P[obj]!, base = p.length / 3;\n if (base + m.pos.length / 3 > 4_000_000) return;\n for (let i = 0; i < m.pos.length; i += 3) {\n const vx = m.pos[i]! * s, vy = m.pos[i + 1]! * s, vz = m.pos[i + 2]! * s;\n p.push(x + vx * c + vz * sn, y + vy, z - vx * sn + vz * c); // поворот вокруг Y (как Fmatrix::rotateY)\n }\n for (let i = 0; i < m.uv.length; i++) U[obj]!.push(m.uv[i]!);\n for (let i = 0; i < m.indices.length; i++) I[obj]!.push(base + m.indices[i]!);\n items++;\n }, true);\n const meshes: Mesh[] = [];\n models.forEach((m, k) => {\n if (!I[k]!.length) return;\n const n = P[k]!.length / 3, nor = new Float32Array(n * 3);\n for (let i = 0; i < n; i++) nor[i * 3 + 1] = 1;\n meshes.push({ name: `Трава · ${m.texture}`, texture: m.texture, shader: m.shader, vertexCount: n, pos: Float32Array.from(P[k]!), nor, uv: Float32Array.from(U[k]!), bone: new Float32Array(n * 4), weight: new Float32Array(n * 4).map((_, i) => (i % 4 === 0 ? 1 : 0)), indices: Uint32Array.from(I[k]!), skinned: false, alphaTest: true });\n });\n return { meshes, items, candidates, keep };\n}\n"],"names":["_","ids"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"details.js","sources":["../src/details.ts"],"sourcesContent":["// X-Ray grass and small details: .dm models and the level.details map (SoC, CS, CoP).\n// .dm: shader, texture (strings), u32 flags, f32 min/max scale, u32 vertices, u32 indices, vertices {f32 x,y,z,u,v}, u16 indices.\n// level.details is chunks: 0 header (u32 version 3, objects, offs_x, offs_z, size_x, size_z), 1 models (nested .dm chunks),\n// 2 cells of 2×2 m, 16 bytes each: u32 y_base:12 (×0.2 − 200 m), y_height:8 (×0.1 m), id0:6, id1:6; u32 id2:6, id3:6, light…;\n// 4×u16 densities (4 bits per cell corner). Cell (x, z) lies in the world at ((x − offs_x)·2, (z − offs_z)·2).\n// The game computes placement itself at display time (CDetailManager::cache_Decompress): a jittered point grid, model choice\n// by interpolated density with ordered dithering, height by a ray down onto the level collision (level.cform),\n// rotation about Y and scale are random. The same order is used here; with too many triangles the grass is thinned.\nimport type { Mesh } from '@gmvjs/core/types';\n\nexport interface DetailModel { shader: string; texture: string; flags: number; minScale: number; maxScale: number; pos: Float32Array; uv: Float32Array; indices: Uint16Array }\n\nconst dvOf = (u: Uint8Array) => new DataView(u.buffer, u.byteOffset, u.byteLength);\n\nfunction chunks(u: Uint8Array): Map<number, Uint8Array> {\n const d = dvOf(u), out = new Map<number, Uint8Array>();\n for (let p = 0; p + 8 <= u.length; ) {\n const id = d.getUint32(p, true) & 0x7fffffff, size = d.getUint32(p + 4, true);\n if (p + 8 + size > u.length) break;\n out.set(id, u.subarray(p + 8, p + 8 + size));\n p += 8 + size;\n }\n return out;\n}\n\nexport function parseDM(u: Uint8Array): DetailModel {\n const d = dvOf(u);\n let p = 0;\n const str = () => { const e = u.indexOf(0, p); if (e < 0) throw new Error('.dm: строка не закрыта'); const s = new TextDecoder('latin1').decode(u.subarray(p, e)); p = e + 1; return s; };\n const shader = str(), texture = str();\n if (p + 20 > u.length) throw new Error('.dm обрезан');\n const flags = d.getUint32(p, true), minScale = d.getFloat32(p + 4, true), maxScale = d.getFloat32(p + 8, true), nv = d.getUint32(p + 12, true), ni = d.getUint32(p + 16, true);\n p += 20;\n if (nv > 65536 || ni % 3 || p + nv * 20 + ni * 2 > u.length) throw new Error('.dm: неверное число вершин или индексов');\n const pos = new Float32Array(nv * 3), uv = new Float32Array(nv * 2), indices = new Uint16Array(ni);\n for (let i = 0; i < nv; i++, p += 20) { pos[i * 3] = d.getFloat32(p, true); pos[i * 3 + 1] = d.getFloat32(p + 4, true); pos[i * 3 + 2] = d.getFloat32(p + 8, true); uv[i * 2] = d.getFloat32(p + 12, true); uv[i * 2 + 1] = d.getFloat32(p + 16, true); }\n for (let i = 0; i < ni; i++, p += 2) indices[i] = d.getUint16(p, true);\n return { shader, texture, flags, minScale, maxScale, pos, uv, indices };\n}\n\n/** .dm model mesh (normals up, as grass is lit). */\nexport function dmMesh(m: DetailModel, name: string): Mesh {\n const n = m.pos.length / 3, nor = new Float32Array(n * 3);\n for (let i = 0; i < n; i++) nor[i * 3 + 1] = 1;\n return { name, texture: m.texture, shader: m.shader, vertexCount: n, pos: m.pos, nor, uv: m.uv, bone: new Float32Array(n * 4), weight: new Float32Array(n * 4).map((_, i) => (i % 4 === 0 ? 1 : 0)), indices: m.indices, skinned: false, alphaTest: true };\n}\n\nexport interface DetailsMap { version: number; offsX: number; offsZ: number; sizeX: number; sizeZ: number; models: DetailModel[]; slots: DataView }\n\nexport function parseDetails(u: Uint8Array): DetailsMap {\n const c = chunks(u), h = c.get(0), objs = c.get(1), sl = c.get(2);\n if (!h || h.length < 24 || !objs || !sl) throw new Error('level.details: нет заголовка, моделей или ячеек');\n const hd = dvOf(h), [version, , offsX, offsZ, sizeX, sizeZ] = [0, 4, 8, 12, 16, 20].map((o) => hd.getUint32(o, true)) as [number, number, number, number, number, number];\n if (version !== 3) throw new Error(`level.details версии ${version} не поддерживается`);\n if (sizeX * sizeZ * 16 > sl.length) throw new Error('level.details: ячейки обрезаны');\n const models = [...chunks(objs)].sort((a, b) => a[0] - b[0]).map(([, b]) => parseDM(b));\n return { version, offsX, offsZ, sizeX, sizeZ, models, slots: dvOf(sl) };\n}\n\n/** Level collision (level.cform): u32 version 4, vertices, triangles, box (6×f32); vertices f32×3; triangles {u32×3, u32 material}. */\nexport interface CForm { verts: Float32Array; tris: Uint32Array }\nexport function parseCForm(u: Uint8Array): CForm {\n const d = dvOf(u), ver = d.getUint32(0, true), nv = d.getUint32(4, true), nt = d.getUint32(8, true);\n if (ver !== 4 || 36 + nv * 12 + nt * 16 > u.length) throw new Error('level.cform: неизвестный формат');\n const verts = new Float32Array(nv * 3), tris = new Uint32Array(nt * 3);\n for (let i = 0, p = 36; i < nv * 3; i++, p += 4) verts[i] = d.getFloat32(p, true);\n for (let i = 0, p = 36 + nv * 12; i < nt; i++, p += 16) { tris[i * 3] = d.getUint32(p, true); tris[i * 3 + 1] = d.getUint32(p + 4, true); tris[i * 3 + 2] = d.getUint32(p + 8, true); }\n return { verts, tris };\n}\n\n/** The engine's CRandom (like MSVC rand()). */\nclass CRandom {\n constructor(private s: number) {}\n next() { this.s = (Math.imul(this.s, 214013) + 2531011) >>> 0; return (this.s >>> 16) & 0x7fff; }\n randI(n: number) { return this.next() % n; }\n randF(a: number, b: number) { return a + ((b - a) * this.next()) / 32767; }\n randFs(r: number) { return this.randF(-r, r); }\n}\n\n/** 16×16 ordered dithering (a Bayer matrix, thresholds 0..255). */\nconst DITHER = (() => {\n let m = [[0]];\n for (let n = 1; n < 16; n *= 2) m = Array.from({ length: n * 2 }, (_, y) => Array.from({ length: n * 2 }, (_, x) => 4 * m[y % n]![x % n]! + [[0, 2], [3, 1]][+(y >= n)]![+(x >= n)]!));\n return Uint8Array.from(m.flat());\n})();\nexport const DETAIL_TRI_BUDGET = 1_500_000;\n/** Default grass density (r__detail_density): the point grid step in metres. */\nconst DENSITY = 0.3, SLOT = 2;\n\nexport interface DetailsResult { meshes: Mesh[]; items: number; candidates: number; keep: number }\n\n/**\n * Detail placement from the map and collision; budget is the triangle limit (the excess is thinned uniformly).\n * Coordinates are as for the level geometry (left-handed, Y up).\n */\nexport function buildDetails(map: DetailsMap, cf: CForm, budget = DETAIL_TRI_BUDGET): DetailsResult {\n const { sizeX, sizeZ, offsX, offsZ, slots, models } = map, nObj = models.length;\n const dSize = Math.ceil(SLOT / DENSITY), jitter = DENSITY / 1.7;\n // non-empty cells and their heights\n const slotOk = new Uint8Array(sizeX * sizeZ), yLo = new Float32Array(sizeX * sizeZ), yHi = new Float32Array(sizeX * sizeZ);\n for (let i = 0; i < sizeX * sizeZ; i++) {\n const a = slots.getUint32(i * 16, true), b = slots.getUint32(i * 16 + 4, true);\n const ids = [(a >>> 20) & 63, (a >>> 26) & 63, b & 63, (b >>> 6) & 63];\n let any = false;\n for (let k = 0; k < 4; k++) if (ids[k]! < nObj && slots.getUint16(i * 16 + 8 + k * 2, true)) any = true;\n if (!any) continue;\n slotOk[i] = 1; yLo[i] = (a & 0xfff) * 0.2 - 200; yHi[i] = yLo[i]! + ((a >>> 12) & 0xff) * 0.1;\n }\n // collision triangles per cell (non-empty cells only)\n const { verts: V, tris: T } = cf, nt = T.length / 3;\n const cnt = new Uint32Array(sizeX * sizeZ + 1);\n const forCells = (t: number, f: (cell: number) => void) => {\n const a = T[t * 3]! * 3, b = T[t * 3 + 1]! * 3, c = T[t * 3 + 2]! * 3;\n const x0 = Math.floor(Math.min(V[a]!, V[b]!, V[c]!) / SLOT) + offsX, x1 = Math.floor(Math.max(V[a]!, V[b]!, V[c]!) / SLOT) + offsX;\n const z0 = Math.floor(Math.min(V[a + 2]!, V[b + 2]!, V[c + 2]!) / SLOT) + offsZ, z1 = Math.floor(Math.max(V[a + 2]!, V[b + 2]!, V[c + 2]!) / SLOT) + offsZ;\n const ylo = Math.min(V[a + 1]!, V[b + 1]!, V[c + 1]!), yhi = Math.max(V[a + 1]!, V[b + 1]!, V[c + 1]!);\n for (let z = Math.max(0, z0); z <= Math.min(sizeZ - 1, z1); z++) for (let x = Math.max(0, x0); x <= Math.min(sizeX - 1, x1); x++) {\n const cell = z * sizeX + x;\n if (slotOk[cell] && yhi >= yLo[cell]! - 0.01 && ylo <= yHi[cell]! + 0.01) f(cell);\n }\n };\n for (let t = 0; t < nt; t++) forCells(t, (cell) => cnt[cell + 1]!++);\n for (let i = 0; i < sizeX * sizeZ; i++) cnt[i + 1]! += cnt[i]!;\n const fill = cnt.slice(0, sizeX * sizeZ), list = new Uint32Array(cnt[sizeX * sizeZ]!);\n for (let t = 0; t < nt; t++) forCells(t, (cell) => { list[fill[cell]!++] = t; });\n\n // pass over cells: kind 0 = counting triangles, 1 = placement\n const triOf = models.map((m) => m.indices.length / 3);\n const alpha = new Float32Array(16), ids = new Int32Array(4), sel: number[] = [];\n const walk = (emit: (obj: number, x: number, y: number, z: number, scale: number, yaw: number, cell: number) => void, needY: boolean) => {\n for (let dz = 0; dz < sizeZ; dz++) for (let dx = 0; dx < sizeX; dx++) {\n const cell = dz * sizeX + dx;\n if (!slotOk[cell]) continue;\n const o = cell * 16, a = slots.getUint32(o, true), b = slots.getUint32(o + 4, true);\n ids[0] = (a >>> 20) & 63; ids[1] = (a >>> 26) & 63; ids[2] = b & 63; ids[3] = (b >>> 6) & 63;\n for (let k = 0; k < 4; k++) { const pal = slots.getUint16(o + 8 + k * 2, true); for (let j = 0; j < 4; j++) alpha[k * 4 + j] = (255 * ((pal >> (j * 4)) & 15)) / 15; }\n const sx = dx - offsX, sz = dz - offsZ, seed = (0x12071980 ^ Math.imul(sx, sz)) >>> 0;\n const rSel = new CRandom(seed), rJit = new CRandom(seed), rYaw = new CRandom(seed), rScl = new CRandom(seed);\n const minX = sx * SLOT, minZ = sz * SLOT, top = yHi[cell]!, bottom = yLo[cell]!;\n for (let z = 0; z <= dSize; z++) for (let x = 0; x <= dSize; x++) {\n const shx = rJit.randI(16), shz = rJit.randI(16);\n sel.length = 0;\n const cx = Math.min(x, dSize - 1), cz = Math.min(z, dSize - 1), fx = cx / dSize, fz = cz / dSize;\n for (let k = 0; k < 4; k++) {\n if (ids[k]! >= nObj) continue;\n const c01 = alpha[k * 4]! * (1 - fx) + alpha[k * 4 + 1]! * fx, c02 = alpha[k * 4 + 2]! * (1 - fx) + alpha[k * 4 + 3]! * fx;\n const c = Math.min(255, Math.max(0, Math.floor(c01 * (1 - fz) + c02 * fz + 0.5)));\n if (c > DITHER[((cx + shx) % 16) * 16 + ((cz + shz) % 16)]!) sel.push(k);\n }\n if (!sel.length) continue;\n const obj = ids[sel.length === 1 ? sel[0]! : sel[rSel.randI(sel.length)]!]!;\n const px = (x / dSize) * SLOT + minX + rJit.randFs(jitter), pz = (z / dSize) * SLOT + minZ + rJit.randFs(jitter);\n let y = bottom - 5;\n if (needY) {\n for (let i = cnt[cell]!; i < cnt[cell + 1]!; i++) {\n const t = list[i]!, A = T[t * 3]! * 3, B = T[t * 3 + 1]! * 3, C = T[t * 3 + 2]! * 3;\n // a vertical ray: barycentric coordinates in the XZ plane\n const ax = V[A]!, az = V[A + 2]!, e1x = V[B]! - ax, e1z = V[B + 2]! - az, e2x = V[C]! - ax, e2z = V[C + 2]! - az;\n const det = e1x * e2z - e2x * e1z;\n if (Math.abs(det) < 1e-9) continue;\n const qx = px - ax, qz = pz - az, u = (qx * e2z - e2x * qz) / det, v = (e1x * qz - qx * e1z) / det;\n if (u < 0 || v < 0 || u + v > 1) continue;\n const yt = V[A + 1]! + u * (V[B + 1]! - V[A + 1]!) + v * (V[C + 1]! - V[A + 1]!);\n if (yt <= top + 1e-3 && yt > y) y = yt;\n }\n if (y < bottom) continue;\n }\n const m = models[obj]!, scale = rScl.randF(m.minScale * 0.5, m.maxScale * 0.9), yaw = rYaw.randF(0, Math.PI * 2);\n emit(obj, px, y, pz, scale, yaw, cell);\n }\n }\n };\n let candTris = 0, candidates = 0;\n walk((obj) => { candTris += triOf[obj]!; candidates++; }, false);\n const keep = candTris > budget ? budget / candTris : 1;\n const thin = new CRandom(0x5eed);\n const P: number[][] = models.map(() => []), U: number[][] = models.map(() => []), I: number[][] = models.map(() => []);\n let items = 0;\n walk((obj, x, y, z, s, yaw) => {\n if (keep < 1 && thin.next() / 32768 >= keep) return;\n const m = models[obj]!, c = Math.cos(yaw), sn = Math.sin(yaw), p = P[obj]!, base = p.length / 3;\n if (base + m.pos.length / 3 > 4_000_000) return;\n for (let i = 0; i < m.pos.length; i += 3) {\n const vx = m.pos[i]! * s, vy = m.pos[i + 1]! * s, vz = m.pos[i + 2]! * s;\n p.push(x + vx * c + vz * sn, y + vy, z - vx * sn + vz * c); // rotation about Y (like Fmatrix::rotateY)\n }\n for (let i = 0; i < m.uv.length; i++) U[obj]!.push(m.uv[i]!);\n for (let i = 0; i < m.indices.length; i++) I[obj]!.push(base + m.indices[i]!);\n items++;\n }, true);\n const meshes: Mesh[] = [];\n models.forEach((m, k) => {\n if (!I[k]!.length) return;\n const n = P[k]!.length / 3, nor = new Float32Array(n * 3);\n for (let i = 0; i < n; i++) nor[i * 3 + 1] = 1;\n meshes.push({ name: `Трава · ${m.texture}`, texture: m.texture, shader: m.shader, vertexCount: n, pos: Float32Array.from(P[k]!), nor, uv: Float32Array.from(U[k]!), bone: new Float32Array(n * 4), weight: new Float32Array(n * 4).map((_, i) => (i % 4 === 0 ? 1 : 0)), indices: Uint32Array.from(I[k]!), skinned: false, alphaTest: true });\n });\n return { meshes, items, candidates, keep };\n}\n"],"names":["_","ids"],"mappings":"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"}
|
package/dist/export.d.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import type { DecodedImage, Model } from '@gmvjs/core/types';
|
|
2
2
|
export interface TextureForExport {
|
|
3
|
-
/**
|
|
3
|
+
/** Source texture name (for the file name). */
|
|
4
4
|
name: string;
|
|
5
|
-
/**
|
|
5
|
+
/** A source DDS is copied without re-encoding. */
|
|
6
6
|
dds?: Uint8Array;
|
|
7
|
-
/**
|
|
7
|
+
/** Otherwise a decoded image. */
|
|
8
8
|
image?: DecodedImage | null;
|
|
9
9
|
}
|
|
10
10
|
export interface XrayExportOptions {
|
|
11
|
-
/**
|
|
11
|
+
/** Level/object name: Latin letters, digits, "_". */
|
|
12
12
|
name: string;
|
|
13
|
-
/**
|
|
13
|
+
/** Folder inside rawdata/objects and gamedata/textures. Default: "gmv\\<name>". */
|
|
14
14
|
prefix?: string;
|
|
15
|
-
/**
|
|
15
|
+
/** Unit-to-metre multiplier (X-Ray: 1 = 1 m). */
|
|
16
16
|
scale?: number;
|
|
17
|
-
/**
|
|
17
|
+
/** Split cell size, m; 0 = one object. Default: 0 for models, 32 m for scenes larger than 100 m or 60 thousand triangles, 64 m above 400 m. */
|
|
18
18
|
cell?: number;
|
|
19
|
-
/**
|
|
19
|
+
/** Mesh texture by number (from the viewing session). */
|
|
20
20
|
texture?: (meshIndex: number) => Promise<TextureForExport | null>;
|
|
21
|
-
/**
|
|
21
|
+
/** Hidden meshes are not exported. */
|
|
22
22
|
hidden?: ReadonlySet<number>;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
* false
|
|
24
|
+
* Level objects (model.instances): true (default) = each model as one .object in "<prefix>\\props", the scene holds references with poses;
|
|
25
|
+
* false = objects are merged into the cell geometry.
|
|
26
26
|
*/
|
|
27
27
|
instances?: boolean;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* The source is X-Ray itself (a compiled level, OGF, .object): texture names, shaders and materials stay as in the game,
|
|
30
|
+
* textures are not copied (they are already in gamedata).
|
|
31
31
|
*/
|
|
32
32
|
xraySource?: boolean;
|
|
33
33
|
}
|
package/dist/export.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.js","sources":["../src/export.ts"],"sourcesContent":["// Выгрузка любой загруженной модели или уровня в исходники X-Ray SDK (ЗП, SDK 0.7):\n// rawdata/objects/<префикс>/<имя>_<ячейка>.object — геометрия, разбитая на ячейки сетки (для больших карт);\n// rawdata/levels/<имя>.level + <имя>/scene_object.part — сцена с этими объектами;\n// gamedata/textures/<префикс>/<текстура>.dds + .thm — текстуры (DDS копируется как есть, остальное — без сжатия).\n// Дальше: LevelEditor → свет, секторы/порталы, спавн → сборка xrLC/xrAI/xrDO.\nimport { encodeDdsRgba, hasAlpha } from '@gmvjs/core/writers/dds';\nimport type { DecodedImage, Model } from '@gmvjs/core/types';\nimport { visibleIndices } from '@gmvjs/core/scene';\nimport { writeThm } from './thm';\nimport { writeObject, type ObjectMeshIn, type ObjectSurfaceIn } from './write-object';\nimport { writeScene, type SceneObjectOut } from './write-scene';\n\nexport interface TextureForExport {\n /** Имя исходной текстуры (для имени файла). */\n name: string;\n /** Исходный DDS — копируется без перекодирования. */\n dds?: Uint8Array;\n /** Иначе — декодированная картинка. */\n image?: DecodedImage | null;\n}\n\nexport interface XrayExportOptions {\n /** Имя уровня/объекта: латиница, цифры, «_». */\n name: string;\n /** Папка внутри rawdata/objects и gamedata/textures. По умолчанию «gmv\\\\<имя>». */\n prefix?: string;\n /** Множитель единиц в метры (X-Ray: 1 = 1 м). */\n scale?: number;\n /** Размер ячейки разбиения, м; 0 — один объект. По умолчанию: 0 для моделей, 32 м для сцен больше 100 м или 60 тыс. треугольников, 64 м — больше 400 м. */\n cell?: number;\n /** Текстура меша по номеру (из сессии просмотра). */\n texture?: (meshIndex: number) => Promise<TextureForExport | null>;\n /** Скрытые меши не выгружаются. */\n hidden?: ReadonlySet<number>;\n /**\n * Объекты уровня (model.instances): true (по умолчанию) — каждая модель одним .object в «<префикс>\\\\props», в сцене — ссылки с позами;\n * false — объекты вливаются в геометрию ячеек.\n */\n instances?: boolean;\n /**\n * Источник — сам X-Ray (скомпилированный уровень, OGF, .object): имена текстур, шейдеры и материалы остаются игровыми,\n * текстуры не копируются (они уже есть в gamedata).\n */\n xraySource?: boolean;\n}\n\nexport interface XrayExportResult { files: Map<string, Uint8Array>; report: string[] }\n\nconst clean = (s: string) => s.toLowerCase().replace(/\\.[a-z0-9]+$/, '').replace(/[^a-z0-9_]+/g, '_').replace(/^_+|_+$/g, '') || 'tex';\n\n/** Вершины меша в координатах X-Ray: поза bind, Z-вверх -> Y-вверх, правые -> левые (с разворотом обхода), масштаб. */\nfunction toXray(model: Model, mi: number, scale: number): { pos: Float32Array; nor: Float32Array; indices: Uint32Array } {\n const m = model.meshes[mi]!, n = m.vertexCount, sk = model.skeleton, pos = new Float32Array(n * 3), nor = new Float32Array(n * 3);\n if (sk) sk.pose(null, 0, false);\n for (let v = 0; v < n; v++) {\n let x = m.pos[v * 3]!, y = m.pos[v * 3 + 1]!, z = m.pos[v * 3 + 2]!, a = m.nor[v * 3]!, b = m.nor[v * 3 + 1]!, c = m.nor[v * 3 + 2]!;\n if (sk && m.skinned) {\n let px = 0, py = 0, pz = 0, na = 0, nb = 0, nc = 0, ws = 0;\n for (let k = 0; k < 4; k++) {\n const w = m.weight[v * 4 + k]!;\n if (!w) continue;\n const M = sk.skinV[m.bone[v * 4 + k]!]!;\n ws += w;\n px += w * (x * M[0]! + y * M[4]! + z * M[8]! + M[12]!); py += w * (x * M[1]! + y * M[5]! + z * M[9]! + M[13]!); pz += w * (x * M[2]! + y * M[6]! + z * M[10]! + M[14]!);\n na += w * (a * M[0]! + b * M[4]! + c * M[8]!); nb += w * (a * M[1]! + b * M[5]! + c * M[9]!); nc += w * (a * M[2]! + b * M[6]! + c * M[10]!);\n }\n if (ws) { x = px; y = py; z = pz; a = na; b = nb; c = nc; }\n }\n if (model.defaultZUp) { [y, z] = [z, -y]; [b, c] = [c, -b]; }\n if (!model.flipZ) { z = -z; c = -c; }\n const l = Math.hypot(a, b, c) || 1;\n pos[v * 3] = x * scale; pos[v * 3 + 1] = y * scale; pos[v * 3 + 2] = z * scale;\n nor[v * 3] = a / l; nor[v * 3 + 1] = b / l; nor[v * 3 + 2] = c / l;\n }\n const indices = visibleIndices(model, mi);\n // отражение меняет обход: внешняя нормаль X-Ray = (b − a) × (c − a)\n if (!model.flipZ) for (let t = 0; t + 2 < indices.length; t += 3) { const q = indices[t + 1]!; indices[t + 1] = indices[t + 2]!; indices[t + 2] = q; }\n return { pos, nor, indices };\n}\n\nexport async function exportXraySdk(model: Model, o: XrayExportOptions): Promise<XrayExportResult> {\n const name = clean(o.name), prefix = (o.prefix ?? `gmv\\\\${name}`).replace(/\\//g, '\\\\').replace(/\\\\+$/, ''), scale = o.scale ?? 1;\n const files = new Map<string, Uint8Array>(), report: string[] = [];\n const inst = o.instances !== false && model.instances && model.flipZ && !model.defaultZUp ? model.instances : null;\n const instMeshes = new Set(inst?.meshes ?? []);\n const meshIdx = model.meshes.map((_, i) => i).filter((i) => !o.hidden?.has(i) && model.meshes[i]!.indices.length >= 3);\n const geo = new Map(meshIdx.filter((i) => !instMeshes.has(i)).map((i) => [i, toXray(model, i, scale)]));\n // размер сцены -> ячейка\n const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];\n for (const g of geo.values()) for (let i = 0; i < g.pos.length; i++) { lo[i % 3] = Math.min(lo[i % 3]!, g.pos[i]!); hi[i % 3] = Math.max(hi[i % 3]!, g.pos[i]!); }\n const extent = Math.max(hi[0]! - lo[0]!, hi[2]! - lo[2]!);\n // авто: большие сцены режутся на ячейки, чтобы объекты в LevelEditor оставались удобными\n const triCount = [...geo.values()].reduce((a, g) => a + g.indices.length / 3, 0);\n const cell = o.cell ?? (extent > 400 ? 64 : extent > 100 || triCount > 60000 ? 32 : 0);\n\n // текстуры и поверхности: одна поверхность на текстуру (+ двусторонность по названию шейдера не угадываем)\n const surfaces: ObjectSurfaceIn[] = [], surfOfMesh = new Map<number, number>(), texOut = new Map<string, string>();\n let copied = 0, encoded = 0, missingTex = 0;\n for (const i of meshIdx) {\n const m = model.meshes[i]!, key = m.texture || `mesh_${i}`;\n if (o.xraySource) {\n // игровые имена: поверхность на сочетание текстуры, шейдера и материала\n const sk = `${m.texture}|${m.shader ?? ''}|${m.material ?? ''}`;\n let si = surfaces.findIndex((x) => `${x.texture}|${x.eshader}|${x.gamemtl}` === sk);\n if (si < 0) si = surfaces.push({ name: `${clean(m.texture.split('\\\\').pop() ?? 's')}_${surfaces.length}`, texture: m.texture, eshader: m.shader || 'default', gamemtl: m.material || 'default' }) - 1;\n surfOfMesh.set(i, si);\n continue;\n }\n let xname = texOut.get(key);\n if (!xname) {\n const base = clean(key.split(/[\\\\/]/).pop() ?? key);\n let cand = base, k = 1;\n while ([...texOut.values()].includes(`${prefix}\\\\${cand}`)) cand = `${base}_${k++}`;\n xname = `${prefix}\\\\${cand}`;\n texOut.set(key, xname);\n const t = o.texture ? await o.texture(i).catch(() => null) : null;\n const path = `gamedata/textures/${xname.replace(/\\\\/g, '/')}`;\n if (t?.dds && t.dds.length > 128) {\n files.set(`${path}.dds`, t.dds);\n const dv = new DataView(t.dds.buffer, t.dds.byteOffset), fourcc = String.fromCharCode(t.dds[84]!, t.dds[85]!, t.dds[86]!, t.dds[87]!);\n files.set(`${path}.thm`, writeThm({ width: dv.getUint32(16, true), height: dv.getUint32(12, true), alpha: fourcc !== 'DXT1' }));\n copied++;\n } else if (t?.image?.rgba) {\n const a = hasAlpha(t.image);\n files.set(`${path}.dds`, encodeDdsRgba({ width: t.image.width, height: t.image.height, rgba: t.image.rgba }));\n files.set(`${path}.thm`, writeThm({ width: t.image.width, height: t.image.height, alpha: a }));\n encoded++;\n } else missingTex++;\n surfaces.push({ name: cand, texture: xname });\n }\n surfOfMesh.set(i, surfaces.findIndex((s) => s.texture === xname));\n }\n\n // ячейки: треугольник относится к ячейке своего центра\n const cells = new Map<string, Map<number, number[]>>();\n for (const [i, g] of geo) {\n for (let t = 0; t + 2 < g.indices.length; t += 3) {\n let key = 'all';\n if (cell > 0) {\n const a = g.indices[t]! * 3, b = g.indices[t + 1]! * 3, c = g.indices[t + 2]! * 3;\n const cx = (g.pos[a]! + g.pos[b]! + g.pos[c]!) / 3, cz = (g.pos[a + 2]! + g.pos[b + 2]! + g.pos[c + 2]!) / 3;\n key = `${Math.floor((cx - lo[0]!) / cell)}_${Math.floor((cz - lo[2]!) / cell)}`;\n }\n const byMesh = cells.get(key) ?? cells.set(key, new Map()).get(key)!;\n (byMesh.get(i) ?? byMesh.set(i, []).get(i)!).push(t);\n }\n }\n const objects: Array<{ name: string; reference: string }> = [];\n let tris = 0;\n for (const [key, byMesh] of cells) {\n const meshes: ObjectMeshIn[] = [];\n for (const [i, list] of byMesh) {\n const g = geo.get(i)!, src = model.meshes[i]!, remap = new Map<number, number>(), pos: number[] = [], nor: number[] = [], uv: number[] = [], idx: number[] = [];\n for (const t of list) for (let k = 0; k < 3; k++) {\n const v = g.indices[t + k]!;\n let r = remap.get(v);\n if (r === undefined) {\n r = pos.length / 3; remap.set(v, r);\n pos.push(g.pos[v * 3]!, g.pos[v * 3 + 1]!, g.pos[v * 3 + 2]!); nor.push(g.nor[v * 3]!, g.nor[v * 3 + 1]!, g.nor[v * 3 + 2]!); uv.push(src.uv[v * 2]!, src.uv[v * 2 + 1]!);\n }\n idx.push(r);\n }\n tris += list.length;\n meshes.push({ name: clean(src.name || `mesh_${i}`), pos, nor, uv, indices: idx, surface: surfOfMesh.get(i)! });\n }\n const oname = cell > 0 ? `${name}_${key}` : name, reference = `${prefix}\\\\${oname}`;\n // поверхности объекта — только используемые им, с перенумерацией\n const used = [...new Set(meshes.map((m) => m.surface as number))], local = new Map(used.map((s, k) => [s, k]));\n for (const m of meshes) m.surface = local.get(m.surface as number)!;\n files.set(`rawdata/objects/${reference.replace(/\\\\/g, '/')}.object`, writeObject({ meshes, surfaces: used.map((s) => surfaces[s]!) }));\n objects.push({ name: oname, reference });\n }\n const scene: SceneObjectOut[] = objects.map((x) => ({ ...x, position: [0, 0, 0] }));\n let instTris = 0, protoCount = 0, mirrored = 0;\n if (inst) {\n // прототипы: один .object на модель; зеркальные позы (det < 0) поворотом не выразить — их геометрия вливается в отдельный объект\n const protoRef = new Map<number, string>(), used = new Set(inst.placements.map((p) => p.proto)), mirrorMeshes: ObjectMeshIn[] = [];\n const names = new Set<string>();\n const meshesOf = (pi: number, M: number[] | null): ObjectMeshIn[] => inst.protos[pi]!.parts.filter((pt) => !o.hidden?.has(pt.mesh) && surfOfMesh.has(pt.mesh) && pt.indices.length >= 3).map((pt, k) => {\n const n = pt.pos.length / 3, pos: number[] = [], nor: number[] = [];\n for (let v = 0; v < n; v++) {\n let x = pt.pos[v * 3]!, y = pt.pos[v * 3 + 1]!, z = pt.pos[v * 3 + 2]!, a = pt.nor[v * 3]!, b = pt.nor[v * 3 + 1]!, c = pt.nor[v * 3 + 2]!;\n if (M) {\n [x, y, z] = [x * M[0]! + y * M[4]! + z * M[8]! + M[12]!, x * M[1]! + y * M[5]! + z * M[9]! + M[13]!, x * M[2]! + y * M[6]! + z * M[10]! + M[14]!];\n [a, b, c] = [a * M[0]! + b * M[4]! + c * M[8]!, a * M[1]! + b * M[5]! + c * M[9]!, a * M[2]! + b * M[6]! + c * M[10]!];\n const l = Math.hypot(a, b, c) || 1; a /= l; b /= l; c /= l;\n }\n pos.push(x * scale, y * scale, z * scale); nor.push(a, b, c);\n }\n const indices = Array.from(pt.indices);\n if (M) for (let t = 0; t + 2 < indices.length; t += 3) { const q = indices[t + 1]!; indices[t + 1] = indices[t + 2]!; indices[t + 2] = q; }\n return { name: `part_${k}`, pos, nor, uv: pt.uv, indices, surface: surfOfMesh.get(pt.mesh)! };\n });\n const writeWith = (reference: string, meshes: ObjectMeshIn[]) => {\n const usedS = [...new Set(meshes.map((m) => m.surface as number))], local = new Map(usedS.map((x, k) => [x, k]));\n for (const m of meshes) m.surface = local.get(m.surface as number)!;\n files.set(`rawdata/objects/${reference.replace(/\\\\/g, '/')}.object`, writeObject({ meshes, surfaces: usedS.map((x) => surfaces[x]!) }));\n };\n for (const pl of inst.placements) {\n if (!used.has(pl.proto) || pl.hidden) continue;\n const M = pl.pose, R = [M[0]!, M[1]!, M[2]!, M[4]!, M[5]!, M[6]!, M[8]!, M[9]!, M[10]!];\n const sc = [Math.hypot(R[0]!, R[1]!, R[2]!), Math.hypot(R[3]!, R[4]!, R[5]!), Math.hypot(R[6]!, R[7]!, R[8]!)];\n const det = R[0]! * (R[4]! * R[8]! - R[5]! * R[7]!) - R[1]! * (R[3]! * R[8]! - R[5]! * R[6]!) + R[2]! * (R[3]! * R[7]! - R[4]! * R[6]!);\n if (det < 0) { const ms = meshesOf(pl.proto, M); mirrorMeshes.push(...ms); mirrored++; for (const m of ms) instTris += m.indices.length / 3; continue; }\n let ref = protoRef.get(pl.proto);\n if (ref === undefined) {\n const ms = meshesOf(pl.proto, null);\n if (!ms.length) { protoRef.set(pl.proto, ''); continue; }\n let base = clean(inst.protos[pl.proto]!.name.split(/[\\\\/]/).pop() ?? 'obj'), cand = base, k = 1;\n while (names.has(cand)) cand = `${base}_${k++}`;\n names.add(cand);\n ref = `${prefix}\\\\props\\\\${cand}`;\n writeWith(ref, ms);\n protoRef.set(pl.proto, ref);\n protoCount++;\n }\n if (!ref) continue;\n for (const pt of inst.protos[pl.proto]!.parts) instTris += pt.indices.length / 3;\n let nm = clean(pl.name), j = 1;\n while (names.has(nm)) nm = `${clean(pl.name)}_${j++}`;\n names.add(nm);\n scene.push({ name: nm, reference: ref, position: [M[12]! * scale, M[13]! * scale, M[14]! * scale], matrix: R.map((v, i) => v / sc[Math.floor(i / 3)]!), scale: sc });\n }\n if (mirrorMeshes.length) {\n const ref = `${prefix}\\\\${name}_mirrored`;\n writeWith(ref, mirrorMeshes);\n scene.push({ name: `${name}_mirrored`, reference: ref, position: [0, 0, 0] });\n }\n }\n for (const [path, data] of writeScene({ name, objects: scene })) files.set(`rawdata/levels/${path}`, data);\n\n report.push(\n `Объектов .object: ${objects.length}${cell > 0 ? ` (ячейки ${cell} м)` : ''}, треугольников: ${tris}, размер сцены: ${(hi[0]! - lo[0]!).toFixed(1)} × ${(hi[1]! - lo[1]!).toFixed(1)} × ${(hi[2]! - lo[2]!).toFixed(1)} м`,\n ...(inst ? [`Объекты уровня: ${inst.placements.length} экземпляров, ${protoCount} моделей в ${prefix}\\\\props (треугольников ${instTris})${mirrored ? `; зеркальных ${mirrored} — влиты в ${name}_mirrored` : ''}`] : []),\n o.xraySource ? `Текстуры, шейдеры и материалы — игровые (${surfaces.length} поверхностей), файлы текстур не копируются` : `Текстуры: ${copied} DDS скопировано, ${encoded} перекодировано в DDS без сжатия${missingTex ? `, ${missingTex} не найдено (объект ссылается на имя, файл положите сами)` : ''}`,\n `Сцена: rawdata/levels/${name}.level — откройте в LevelEditor, добавьте свет, секторы/порталы и спавн, затем соберите xrLC.`,\n );\n if (!o.xraySource) {\n report.push('Шейдеры поверхностей — «default», материал — «default»: настройте в редакторе (стекло, листва, двусторонние грани).');\n const mats = [...new Set(meshIdx.map((i) => model.meshes[i]!.material).filter((x): x is string => !!x))];\n if (mats.length) report.push(`Материалы исходника (для настройки game material): ${mats.slice(0, 30).join(', ')}${mats.length > 30 ? '…' : ''}`);\n }\n if (model.skeleton) report.push('Скелетная модель выгружена в bind-позе как статика (кости и анимации в .object пока не пишутся).');\n if (!model.flipZ) report.push('Координаты переведены в левостороннюю систему X-Ray (Z отражён, обход граней развёрнут).');\n return { files, report };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"export.js","sources":["../src/export.ts"],"sourcesContent":["// Export of any loaded model or level into X-Ray SDK sources (CoP, SDK 0.7):\n// rawdata/objects/<prefix>/<name>_<cell>.object = geometry split into grid cells (for large maps);\n// rawdata/levels/<name>.level + <name>/scene_object.part = a scene with those objects;\n// gamedata/textures/<prefix>/<texture>.dds + .thm = textures (a DDS is copied as is, the rest is written uncompressed).\n// Next: LevelEditor → lights, sectors/portals, spawn → build with xrLC/xrAI/xrDO.\nimport { encodeDdsRgba, hasAlpha } from '@gmvjs/core/writers/dds';\nimport type { DecodedImage, Model } from '@gmvjs/core/types';\nimport { visibleIndices } from '@gmvjs/core/scene';\nimport { writeThm } from './thm';\nimport { writeObject, type ObjectMeshIn, type ObjectSurfaceIn } from './write-object';\nimport { writeScene, type SceneObjectOut } from './write-scene';\n\nexport interface TextureForExport {\n /** Source texture name (for the file name). */\n name: string;\n /** A source DDS is copied without re-encoding. */\n dds?: Uint8Array;\n /** Otherwise a decoded image. */\n image?: DecodedImage | null;\n}\n\nexport interface XrayExportOptions {\n /** Level/object name: Latin letters, digits, \"_\". */\n name: string;\n /** Folder inside rawdata/objects and gamedata/textures. Default: \"gmv\\\\<name>\". */\n prefix?: string;\n /** Unit-to-metre multiplier (X-Ray: 1 = 1 m). */\n scale?: number;\n /** Split cell size, m; 0 = one object. Default: 0 for models, 32 m for scenes larger than 100 m or 60 thousand triangles, 64 m above 400 m. */\n cell?: number;\n /** Mesh texture by number (from the viewing session). */\n texture?: (meshIndex: number) => Promise<TextureForExport | null>;\n /** Hidden meshes are not exported. */\n hidden?: ReadonlySet<number>;\n /**\n * Level objects (model.instances): true (default) = each model as one .object in \"<prefix>\\\\props\", the scene holds references with poses;\n * false = objects are merged into the cell geometry.\n */\n instances?: boolean;\n /**\n * The source is X-Ray itself (a compiled level, OGF, .object): texture names, shaders and materials stay as in the game,\n * textures are not copied (they are already in gamedata).\n */\n xraySource?: boolean;\n}\n\nexport interface XrayExportResult { files: Map<string, Uint8Array>; report: string[] }\n\nconst clean = (s: string) => s.toLowerCase().replace(/\\.[a-z0-9]+$/, '').replace(/[^a-z0-9_]+/g, '_').replace(/^_+|_+$/g, '') || 'tex';\n\n/** Mesh vertices in X-Ray coordinates: bind pose, Z-up -> Y-up, right-handed -> left-handed (with reversed winding), scale. */\nfunction toXray(model: Model, mi: number, scale: number): { pos: Float32Array; nor: Float32Array; indices: Uint32Array } {\n const m = model.meshes[mi]!, n = m.vertexCount, sk = model.skeleton, pos = new Float32Array(n * 3), nor = new Float32Array(n * 3);\n if (sk) sk.pose(null, 0, false);\n for (let v = 0; v < n; v++) {\n let x = m.pos[v * 3]!, y = m.pos[v * 3 + 1]!, z = m.pos[v * 3 + 2]!, a = m.nor[v * 3]!, b = m.nor[v * 3 + 1]!, c = m.nor[v * 3 + 2]!;\n if (sk && m.skinned) {\n let px = 0, py = 0, pz = 0, na = 0, nb = 0, nc = 0, ws = 0;\n for (let k = 0; k < 4; k++) {\n const w = m.weight[v * 4 + k]!;\n if (!w) continue;\n const M = sk.skinV[m.bone[v * 4 + k]!]!;\n ws += w;\n px += w * (x * M[0]! + y * M[4]! + z * M[8]! + M[12]!); py += w * (x * M[1]! + y * M[5]! + z * M[9]! + M[13]!); pz += w * (x * M[2]! + y * M[6]! + z * M[10]! + M[14]!);\n na += w * (a * M[0]! + b * M[4]! + c * M[8]!); nb += w * (a * M[1]! + b * M[5]! + c * M[9]!); nc += w * (a * M[2]! + b * M[6]! + c * M[10]!);\n }\n if (ws) { x = px; y = py; z = pz; a = na; b = nb; c = nc; }\n }\n if (model.defaultZUp) { [y, z] = [z, -y]; [b, c] = [c, -b]; }\n if (!model.flipZ) { z = -z; c = -c; }\n const l = Math.hypot(a, b, c) || 1;\n pos[v * 3] = x * scale; pos[v * 3 + 1] = y * scale; pos[v * 3 + 2] = z * scale;\n nor[v * 3] = a / l; nor[v * 3 + 1] = b / l; nor[v * 3 + 2] = c / l;\n }\n const indices = visibleIndices(model, mi);\n // mirroring flips the winding: the X-Ray outward normal = (b − a) × (c − a)\n if (!model.flipZ) for (let t = 0; t + 2 < indices.length; t += 3) { const q = indices[t + 1]!; indices[t + 1] = indices[t + 2]!; indices[t + 2] = q; }\n return { pos, nor, indices };\n}\n\nexport async function exportXraySdk(model: Model, o: XrayExportOptions): Promise<XrayExportResult> {\n const name = clean(o.name), prefix = (o.prefix ?? `gmv\\\\${name}`).replace(/\\//g, '\\\\').replace(/\\\\+$/, ''), scale = o.scale ?? 1;\n const files = new Map<string, Uint8Array>(), report: string[] = [];\n const inst = o.instances !== false && model.instances && model.flipZ && !model.defaultZUp ? model.instances : null;\n const instMeshes = new Set(inst?.meshes ?? []);\n const meshIdx = model.meshes.map((_, i) => i).filter((i) => !o.hidden?.has(i) && model.meshes[i]!.indices.length >= 3);\n const geo = new Map(meshIdx.filter((i) => !instMeshes.has(i)).map((i) => [i, toXray(model, i, scale)]));\n // scene size -> cell\n const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];\n for (const g of geo.values()) for (let i = 0; i < g.pos.length; i++) { lo[i % 3] = Math.min(lo[i % 3]!, g.pos[i]!); hi[i % 3] = Math.max(hi[i % 3]!, g.pos[i]!); }\n const extent = Math.max(hi[0]! - lo[0]!, hi[2]! - lo[2]!);\n // auto: large scenes are cut into cells so that objects stay manageable in LevelEditor\n const triCount = [...geo.values()].reduce((a, g) => a + g.indices.length / 3, 0);\n const cell = o.cell ?? (extent > 400 ? 64 : extent > 100 || triCount > 60000 ? 32 : 0);\n\n // textures and surfaces: one surface per texture (+ we do not guess double-sidedness from the shader name)\n const surfaces: ObjectSurfaceIn[] = [], surfOfMesh = new Map<number, number>(), texOut = new Map<string, string>();\n let copied = 0, encoded = 0, missingTex = 0;\n for (const i of meshIdx) {\n const m = model.meshes[i]!, key = m.texture || `mesh_${i}`;\n if (o.xraySource) {\n // game names: a surface per combination of texture, shader and material\n const sk = `${m.texture}|${m.shader ?? ''}|${m.material ?? ''}`;\n let si = surfaces.findIndex((x) => `${x.texture}|${x.eshader}|${x.gamemtl}` === sk);\n if (si < 0) si = surfaces.push({ name: `${clean(m.texture.split('\\\\').pop() ?? 's')}_${surfaces.length}`, texture: m.texture, eshader: m.shader || 'default', gamemtl: m.material || 'default' }) - 1;\n surfOfMesh.set(i, si);\n continue;\n }\n let xname = texOut.get(key);\n if (!xname) {\n const base = clean(key.split(/[\\\\/]/).pop() ?? key);\n let cand = base, k = 1;\n while ([...texOut.values()].includes(`${prefix}\\\\${cand}`)) cand = `${base}_${k++}`;\n xname = `${prefix}\\\\${cand}`;\n texOut.set(key, xname);\n const t = o.texture ? await o.texture(i).catch(() => null) : null;\n const path = `gamedata/textures/${xname.replace(/\\\\/g, '/')}`;\n if (t?.dds && t.dds.length > 128) {\n files.set(`${path}.dds`, t.dds);\n const dv = new DataView(t.dds.buffer, t.dds.byteOffset), fourcc = String.fromCharCode(t.dds[84]!, t.dds[85]!, t.dds[86]!, t.dds[87]!);\n files.set(`${path}.thm`, writeThm({ width: dv.getUint32(16, true), height: dv.getUint32(12, true), alpha: fourcc !== 'DXT1' }));\n copied++;\n } else if (t?.image?.rgba) {\n const a = hasAlpha(t.image);\n files.set(`${path}.dds`, encodeDdsRgba({ width: t.image.width, height: t.image.height, rgba: t.image.rgba }));\n files.set(`${path}.thm`, writeThm({ width: t.image.width, height: t.image.height, alpha: a }));\n encoded++;\n } else missingTex++;\n surfaces.push({ name: cand, texture: xname });\n }\n surfOfMesh.set(i, surfaces.findIndex((s) => s.texture === xname));\n }\n\n // cells: a triangle belongs to the cell of its centre\n const cells = new Map<string, Map<number, number[]>>();\n for (const [i, g] of geo) {\n for (let t = 0; t + 2 < g.indices.length; t += 3) {\n let key = 'all';\n if (cell > 0) {\n const a = g.indices[t]! * 3, b = g.indices[t + 1]! * 3, c = g.indices[t + 2]! * 3;\n const cx = (g.pos[a]! + g.pos[b]! + g.pos[c]!) / 3, cz = (g.pos[a + 2]! + g.pos[b + 2]! + g.pos[c + 2]!) / 3;\n key = `${Math.floor((cx - lo[0]!) / cell)}_${Math.floor((cz - lo[2]!) / cell)}`;\n }\n const byMesh = cells.get(key) ?? cells.set(key, new Map()).get(key)!;\n (byMesh.get(i) ?? byMesh.set(i, []).get(i)!).push(t);\n }\n }\n const objects: Array<{ name: string; reference: string }> = [];\n let tris = 0;\n for (const [key, byMesh] of cells) {\n const meshes: ObjectMeshIn[] = [];\n for (const [i, list] of byMesh) {\n const g = geo.get(i)!, src = model.meshes[i]!, remap = new Map<number, number>(), pos: number[] = [], nor: number[] = [], uv: number[] = [], idx: number[] = [];\n for (const t of list) for (let k = 0; k < 3; k++) {\n const v = g.indices[t + k]!;\n let r = remap.get(v);\n if (r === undefined) {\n r = pos.length / 3; remap.set(v, r);\n pos.push(g.pos[v * 3]!, g.pos[v * 3 + 1]!, g.pos[v * 3 + 2]!); nor.push(g.nor[v * 3]!, g.nor[v * 3 + 1]!, g.nor[v * 3 + 2]!); uv.push(src.uv[v * 2]!, src.uv[v * 2 + 1]!);\n }\n idx.push(r);\n }\n tris += list.length;\n meshes.push({ name: clean(src.name || `mesh_${i}`), pos, nor, uv, indices: idx, surface: surfOfMesh.get(i)! });\n }\n const oname = cell > 0 ? `${name}_${key}` : name, reference = `${prefix}\\\\${oname}`;\n // object surfaces: only the ones it uses, renumbered\n const used = [...new Set(meshes.map((m) => m.surface as number))], local = new Map(used.map((s, k) => [s, k]));\n for (const m of meshes) m.surface = local.get(m.surface as number)!;\n files.set(`rawdata/objects/${reference.replace(/\\\\/g, '/')}.object`, writeObject({ meshes, surfaces: used.map((s) => surfaces[s]!) }));\n objects.push({ name: oname, reference });\n }\n const scene: SceneObjectOut[] = objects.map((x) => ({ ...x, position: [0, 0, 0] }));\n let instTris = 0, protoCount = 0, mirrored = 0;\n if (inst) {\n // prototypes: one .object per model; mirrored poses (det < 0) cannot be expressed as a rotation, so their geometry goes into a separate object\n const protoRef = new Map<number, string>(), used = new Set(inst.placements.map((p) => p.proto)), mirrorMeshes: ObjectMeshIn[] = [];\n const names = new Set<string>();\n const meshesOf = (pi: number, M: number[] | null): ObjectMeshIn[] => inst.protos[pi]!.parts.filter((pt) => !o.hidden?.has(pt.mesh) && surfOfMesh.has(pt.mesh) && pt.indices.length >= 3).map((pt, k) => {\n const n = pt.pos.length / 3, pos: number[] = [], nor: number[] = [];\n for (let v = 0; v < n; v++) {\n let x = pt.pos[v * 3]!, y = pt.pos[v * 3 + 1]!, z = pt.pos[v * 3 + 2]!, a = pt.nor[v * 3]!, b = pt.nor[v * 3 + 1]!, c = pt.nor[v * 3 + 2]!;\n if (M) {\n [x, y, z] = [x * M[0]! + y * M[4]! + z * M[8]! + M[12]!, x * M[1]! + y * M[5]! + z * M[9]! + M[13]!, x * M[2]! + y * M[6]! + z * M[10]! + M[14]!];\n [a, b, c] = [a * M[0]! + b * M[4]! + c * M[8]!, a * M[1]! + b * M[5]! + c * M[9]!, a * M[2]! + b * M[6]! + c * M[10]!];\n const l = Math.hypot(a, b, c) || 1; a /= l; b /= l; c /= l;\n }\n pos.push(x * scale, y * scale, z * scale); nor.push(a, b, c);\n }\n const indices = Array.from(pt.indices);\n if (M) for (let t = 0; t + 2 < indices.length; t += 3) { const q = indices[t + 1]!; indices[t + 1] = indices[t + 2]!; indices[t + 2] = q; }\n return { name: `part_${k}`, pos, nor, uv: pt.uv, indices, surface: surfOfMesh.get(pt.mesh)! };\n });\n const writeWith = (reference: string, meshes: ObjectMeshIn[]) => {\n const usedS = [...new Set(meshes.map((m) => m.surface as number))], local = new Map(usedS.map((x, k) => [x, k]));\n for (const m of meshes) m.surface = local.get(m.surface as number)!;\n files.set(`rawdata/objects/${reference.replace(/\\\\/g, '/')}.object`, writeObject({ meshes, surfaces: usedS.map((x) => surfaces[x]!) }));\n };\n for (const pl of inst.placements) {\n if (!used.has(pl.proto) || pl.hidden) continue;\n const M = pl.pose, R = [M[0]!, M[1]!, M[2]!, M[4]!, M[5]!, M[6]!, M[8]!, M[9]!, M[10]!];\n const sc = [Math.hypot(R[0]!, R[1]!, R[2]!), Math.hypot(R[3]!, R[4]!, R[5]!), Math.hypot(R[6]!, R[7]!, R[8]!)];\n const det = R[0]! * (R[4]! * R[8]! - R[5]! * R[7]!) - R[1]! * (R[3]! * R[8]! - R[5]! * R[6]!) + R[2]! * (R[3]! * R[7]! - R[4]! * R[6]!);\n if (det < 0) { const ms = meshesOf(pl.proto, M); mirrorMeshes.push(...ms); mirrored++; for (const m of ms) instTris += m.indices.length / 3; continue; }\n let ref = protoRef.get(pl.proto);\n if (ref === undefined) {\n const ms = meshesOf(pl.proto, null);\n if (!ms.length) { protoRef.set(pl.proto, ''); continue; }\n let base = clean(inst.protos[pl.proto]!.name.split(/[\\\\/]/).pop() ?? 'obj'), cand = base, k = 1;\n while (names.has(cand)) cand = `${base}_${k++}`;\n names.add(cand);\n ref = `${prefix}\\\\props\\\\${cand}`;\n writeWith(ref, ms);\n protoRef.set(pl.proto, ref);\n protoCount++;\n }\n if (!ref) continue;\n for (const pt of inst.protos[pl.proto]!.parts) instTris += pt.indices.length / 3;\n let nm = clean(pl.name), j = 1;\n while (names.has(nm)) nm = `${clean(pl.name)}_${j++}`;\n names.add(nm);\n scene.push({ name: nm, reference: ref, position: [M[12]! * scale, M[13]! * scale, M[14]! * scale], matrix: R.map((v, i) => v / sc[Math.floor(i / 3)]!), scale: sc });\n }\n if (mirrorMeshes.length) {\n const ref = `${prefix}\\\\${name}_mirrored`;\n writeWith(ref, mirrorMeshes);\n scene.push({ name: `${name}_mirrored`, reference: ref, position: [0, 0, 0] });\n }\n }\n for (const [path, data] of writeScene({ name, objects: scene })) files.set(`rawdata/levels/${path}`, data);\n\n report.push(\n `Объектов .object: ${objects.length}${cell > 0 ? ` (ячейки ${cell} м)` : ''}, треугольников: ${tris}, размер сцены: ${(hi[0]! - lo[0]!).toFixed(1)} × ${(hi[1]! - lo[1]!).toFixed(1)} × ${(hi[2]! - lo[2]!).toFixed(1)} м`,\n ...(inst ? [`Объекты уровня: ${inst.placements.length} экземпляров, ${protoCount} моделей в ${prefix}\\\\props (треугольников ${instTris})${mirrored ? `; зеркальных ${mirrored} — влиты в ${name}_mirrored` : ''}`] : []),\n o.xraySource ? `Текстуры, шейдеры и материалы — игровые (${surfaces.length} поверхностей), файлы текстур не копируются` : `Текстуры: ${copied} DDS скопировано, ${encoded} перекодировано в DDS без сжатия${missingTex ? `, ${missingTex} не найдено (объект ссылается на имя, файл положите сами)` : ''}`,\n `Сцена: rawdata/levels/${name}.level — откройте в LevelEditor, добавьте свет, секторы/порталы и спавн, затем соберите xrLC.`,\n );\n if (!o.xraySource) {\n report.push('Шейдеры поверхностей — «default», материал — «default»: настройте в редакторе (стекло, листва, двусторонние грани).');\n const mats = [...new Set(meshIdx.map((i) => model.meshes[i]!.material).filter((x): x is string => !!x))];\n if (mats.length) report.push(`Материалы исходника (для настройки game material): ${mats.slice(0, 30).join(', ')}${mats.length > 30 ? '…' : ''}`);\n }\n if (model.skeleton) report.push('Скелетная модель выгружена в bind-позе как статика (кости и анимации в .object пока не пишутся).');\n if (!model.flipZ) report.push('Координаты переведены в левостороннюю систему X-Ray (Z отражён, обход граней развёрнут).');\n return { files, report };\n}\n"],"names":[],"mappings":"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"}
|
package/dist/games/cop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cop.js","sources":["../../src/games/cop.ts"],"sourcesContent":["// S.T.A.L.K.E.R.:
|
|
1
|
+
{"version":3,"file":"cop.js","sources":["../../src/games/cop.ts"],"sourcesContent":["// S.T.A.L.K.E.R.: Call of Pripyat — subpath `@gmvjs/xray-v1/cop`: the game preset and an engine limited to it.\nimport { pickGames } from '@gmvjs/core/plugin';\nimport { xrayV1Engine } from '../index';\n\nexport const engine = pickGames(xrayV1Engine, 'stalker-cop');\nexport const preset = engine.games![0]!;\nexport default { engine, preset };\n"],"names":[],"mappings":";;AAIO,MAAM,SAAS,UAAU,cAAc,aAAa;AACpD,MAAM,SAAS,OAAO,MAAO,CAAC;AACrC,MAAA,MAAe,EAAE,QAAQ,OAAA;"}
|
package/dist/games/cs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cs.js","sources":["../../src/games/cs.ts"],"sourcesContent":["// S.T.A.L.K.E.R.:
|
|
1
|
+
{"version":3,"file":"cs.js","sources":["../../src/games/cs.ts"],"sourcesContent":["// S.T.A.L.K.E.R.: Clear Sky — subpath `@gmvjs/xray-v1/cs`: the game preset and an engine limited to it.\nimport { pickGames } from '@gmvjs/core/plugin';\nimport { xrayV1Engine } from '../index';\n\nexport const engine = pickGames(xrayV1Engine, 'stalker-cs');\nexport const preset = engine.games![0]!;\nexport default { engine, preset };\n"],"names":[],"mappings":";;AAIO,MAAM,SAAS,UAAU,cAAc,YAAY;AACnD,MAAM,SAAS,OAAO,MAAO,CAAC;AACrC,MAAA,KAAe,EAAE,QAAQ,OAAA;"}
|
package/dist/games/sdk.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { exportXraySdk, type XrayExportOptions, type XrayExportResult, type TextureForExport } from '../index';
|
|
2
|
-
/**
|
|
2
|
+
/** The "X-Ray SDK 0.7 sources" export option (ExporterPlugin). */
|
|
3
3
|
export declare const exporter: import("@gmvjs/core/plugin").ExporterPlugin;
|
|
4
4
|
declare const _default: {
|
|
5
5
|
exporter: import("@gmvjs/core/plugin").ExporterPlugin;
|
package/dist/games/sdk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.js","sources":["../../src/games/sdk.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"sdk.js","sources":["../../src/games/sdk.ts"],"sourcesContent":["// Export to X-Ray SDK 0.7 — subpath `@gmvjs/xray-v1/sdk`.\nimport { xrayV1Engine } from '../index';\n\nexport { exportXraySdk, type XrayExportOptions, type XrayExportResult, type TextureForExport } from '../index';\n/** The \"X-Ray SDK 0.7 sources\" export option (ExporterPlugin). */\nexport const exporter = xrayV1Engine.exporters!.find((x) => x.id === 'xray-sdk')!;\nexport default { exporter };\n"],"names":[],"mappings":";;AAKO,MAAM,WAAW,aAAa,UAAW,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AAC/E,MAAA,MAAe,EAAE,SAAA;"}
|
package/dist/games/soc.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ declare const _default: {
|
|
|
5
5
|
preset: import("@gmvjs/core/plugin").GamePreset;
|
|
6
6
|
};
|
|
7
7
|
export default _default;
|
|
8
|
-
/**
|
|
8
|
+
/** Encryption of the gamedata.db* file table (for tests and building archives). */
|
|
9
9
|
export { socEncode } from '../soc-key';
|
package/dist/games/soc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"soc.js","sources":["../../src/games/soc.ts"],"sourcesContent":["// S.T.A.L.K.E.R.:
|
|
1
|
+
{"version":3,"file":"soc.js","sources":["../../src/games/soc.ts"],"sourcesContent":["// S.T.A.L.K.E.R.: Shadow of Chernobyl — subpath `@gmvjs/xray-v1/soc`: the game preset and an engine limited to it.\nimport { pickGames } from '@gmvjs/core/plugin';\nimport { xrayV1Engine } from '../index';\n\nexport const engine = pickGames(xrayV1Engine, 'stalker-soc');\nexport const preset = engine.games![0]!;\nexport default { engine, preset };\n/** Encryption of the gamedata.db* file table (for tests and building archives). */\nexport { socEncode } from '../soc-key';\n"],"names":[],"mappings":";;;AAIO,MAAM,SAAS,UAAU,cAAc,aAAa;AACpD,MAAM,SAAS,OAAO,MAAO,CAAC;AACrC,MAAA,MAAe,EAAE,QAAQ,OAAA;"}
|