@gmvjs/dunia1 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 +28 -22
- package/dist/fat.d.ts +12 -12
- package/dist/fat.js +1 -1
- package/dist/fat.js.map +1 -1
- package/dist/fcb.d.ts +2 -2
- package/dist/fcb.js.map +1 -1
- package/dist/games/farcry2.js.map +1 -1
- package/dist/i18n.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/level.d.ts +26 -26
- package/dist/level.js.map +1 -1
- package/dist/mab.d.ts +8 -8
- package/dist/mab.js.map +1 -1
- package/dist/realtree.d.ts +1 -1
- package/dist/realtree.js.map +1 -1
- package/dist/rml.d.ts +1 -1
- package/dist/rml.js.map +1 -1
- package/dist/xbg.d.ts +18 -18
- package/dist/xbg.js.map +1 -1
- package/dist/xbt.js.map +1 -1
- package/package.json +3 -3
package/dist/realtree.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtree.js","sources":["../src/realtree.ts"],"sourcesContent":["// Деревья и кусты RealTree (Far Cry 2): в архиве — по хешу пути, имени нет; внутри — путь «…\\Realtrees\\RT_*.rta».\n// Файл: u32 размер, u32 размер Havok-части, u32 136 (начало данных), 0, 0, u32 размер данных; с 24 — путь; с 136 — дамп\n// памяти редактора (указатели — адреса; база — наименьший указатель − 0x360), после данных — список материалов (.mlm,\n// читаются как .xbm: кора «RealtreeTrunk» и листва), затем — Havok (коллизия).\n// Заголовок в 0x118: u32, u32, f32, u32 уровней ветвей, u32 узлов, ptr узлов, ptr, u32 ветвей, ptr, ptr параметров,\n// u32 карточек листвы, ptr карточек.\n// Узел (76 байт; поля со смещением 52 от начала записи): позиция f32×3, u32 — узлов до конца ветви (у начала ветви\n// больше, чем у предыдущего узла — так ветви делятся), f32 масштаб уровня (ствол 1 или 2, ветви 0.5).\n// Карточка листвы (92 байта): f32 размер с 16, нормаль с 20, касательная с 32, ось полукарточки с 68 (её длина = размер).\n// Сетку дерева игра строит сама по параметрам ветвей — здесь приближение: ветви — трубки по узлам, листва — карточки\n// шаблонов у концов ветвей.\nimport type { Mesh } from '@gmvjs/core/types';\n\nexport interface RealTree { path: string; branches: Array<{ pts: number[][]; tips: number[]; scale: number }>; cards: Array<{ n: number[]; a: number[] }>; materials: string[]; height: number }\n\nexport const isRealTree = (u: Uint8Array): boolean => {\n if (u.length < 160) return false;\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (dv.getUint32(8, true) !== 136) return false;\n const s = new TextDecoder('latin1').decode(u.subarray(24, 136));\n return /realtrees?\\\\/i.test(s);\n};\n\nexport function parseRealTree(u: Uint8Array): RealTree {\n if (!isRealTree(u)) throw new Error('Не RealTree');\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength), dl = dv.getUint32(20, true);\n let e = u.indexOf(0, 24); if (e < 0 || e > 136) e = 136;\n const path = new TextDecoder('latin1').decode(u.subarray(24, e));\n const dataEnd = Math.min(u.length, 136 + dl);\n // база указателей: наименьший указатель-адрес в данных − 0x360 (первая структура после заголовка)\n let minPtr = Infinity;\n const hdr = Array.from({ length: 12 }, (_, i) => dv.getUint32(0x118 + i * 4, true));\n for (const i of [5, 6, 8, 9, 11]) if (hdr[i]! > 0x10000) minPtr = Math.min(minPtr, hdr[i]!);\n for (let o = 136; o + 4 <= dataEnd; o += 4) { const v = dv.getUint32(o, true); if (v >= 0x01000000 && v < minPtr && (v & 0xffff0000) === (minPtr & 0xffff0000)) minPtr = v; }\n if (!Number.isFinite(minPtr)) throw new Error('RealTree: нет указателей');\n const base = minPtr - 0x360, off = (p: number) => p - base + 0x88;\n const [nNodes, pNodes, nCards, pCards] = [hdr[4]!, hdr[5]!, hdr[10]!, hdr[11]!];\n const branches: RealTree['branches'] = [];\n let cur: RealTree['branches'][number] | null = null, height = 0;\n for (let r = 0; r < nNodes; r++) {\n const o = off(pNodes) + r * 76;\n if (o < 136 || o + 76 > dataEnd) break;\n const x = dv.getFloat32(o + 52, true), y = dv.getFloat32(o + 56, true), z = dv.getFloat32(o + 60, true), tip = dv.getUint32(o + 64, true), sc = dv.getFloat32(o + 68, true);\n if (tip > 100000 || ![x, y, z, sc].every(Number.isFinite) || Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) > 1000) break;\n if (!cur || tip > cur.tips[cur.tips.length - 1]!) { cur = { pts: [], tips: [], scale: sc > 0 && sc < 10 ? sc : 1 }; branches.push(cur); }\n cur.pts.push([x, y, z]); cur.tips.push(tip);\n height = Math.max(height, z);\n }\n const cards: RealTree['cards'] = [];\n for (let i = 0; i < nCards; i++) {\n const o = off(pCards) + i * 92;\n if (o < 136 || o + 92 > dataEnd) break;\n const f = (k: number) => dv.getFloat32(o + k, true);\n const n = [f(20), f(24), f(28)], a = [f(68), f(72), f(76)];\n if (![...n, ...a].every(Number.isFinite) || Math.hypot(...a) > 50) continue;\n if (Math.hypot(...a) > 1e-3) cards.push({ n, a });\n }\n // материалы: строки «…\\.mlm» после данных\n const materials: string[] = [];\n const tail = new TextDecoder('latin1').decode(u.subarray(dataEnd, Math.min(u.length, dataEnd + 4096)));\n for (const m of tail.matchAll(/graphics\\\\[\\w\\\\ .-]+?\\.mlm/gi)) materials.push(m[0]);\n return { path, branches: branches.filter((b) => b.pts.length >= 2), cards, materials, height };\n}\n\n/** Масштаб карточек листвы и показатель сужения ветвей (подобраны на глаз по кустам и фикусу). */\nconst LEAF_SCALE = 0.7, TAPER = 1.1;\n\nconst sub = (a: number[], b: number[]) => [a[0]! - b[0]!, a[1]! - b[1]!, a[2]! - b[2]!];\nconst cross = (a: number[], b: number[]) => [a[1]! * b[2]! - a[2]! * b[1]!, a[2]! * b[0]! - a[0]! * b[2]!, a[0]! * b[1]! - a[1]! * b[0]!];\nconst norm = (a: number[]) => { const l = Math.hypot(a[0]!, a[1]!, a[2]!) || 1; return [a[0]! / l, a[1]! / l, a[2]! / l]; };\n\n/** Приближённая сетка: ветви — трубки (sides граней), листва — карточки у концов ветвей; bark/leaf — пути материалов .xbm. */\nexport function realTreeMeshes(t: RealTree, bark: { material?: string; texture: string; normal?: string }, leaf: { material?: string; texture: string; normal?: string }, sides = 4): Mesh[] {\n const R0 = Math.max(0.02, t.height * 0.025);\n const P: number[] = [], N: number[] = [], U: number[] = [], I: number[] = [];\n for (const b of t.branches) {\n const t0 = b.tips[0]! + 1, first = P.length / 3;\n let len = 0;\n b.pts.forEach((p, i) => {\n const d = norm(sub(b.pts[Math.min(i + 1, b.pts.length - 1)]!, b.pts[Math.max(i - 1, 0)]!));\n const up = Math.abs(d[2]!) < 0.9 ? [0, 0, 1] : [1, 0, 0], s1 = norm(cross(d, up)), s2 = cross(d, s1);\n const r = R0 * b.scale * Math.max(0.15, Math.pow((b.tips[i]! + 1) / t0, TAPER));\n if (i) len += Math.hypot(...sub(p, b.pts[i - 1]!));\n for (let k = 0; k <= sides; k++) {\n const a = (k / sides) * Math.PI * 2, c = Math.cos(a), s = Math.sin(a);\n const nx = s1[0]! * c + s2[0]! * s, ny = s1[1]! * c + s2[1]! * s, nz = s1[2]! * c + s2[2]! * s;\n P.push(p[0]! + nx * r, p[1]! + ny * r, p[2]! + nz * r); N.push(nx, ny, nz); U.push(k / sides, len);\n }\n });\n for (let i = 0; i + 1 < b.pts.length; i++) for (let k = 0; k < sides; k++) {\n const a = first + i * (sides + 1) + k, c = a + sides + 1;\n I.push(a, c, a + 1, a + 1, c, c + 1);\n }\n }\n const out: Mesh[] = [];\n const mk = (name: string, m: { material?: string; texture: string; normal?: string }, p: number[], n: number[], uv: number[], idx: number[], alpha: boolean): Mesh => ({\n name, texture: m.texture, material: m.material, normalMap: m.normal, vertexCount: p.length / 3, pos: Float32Array.from(p), nor: Float32Array.from(n), uv: Float32Array.from(uv),\n bone: new Float32Array(0), weight: new Float32Array(0), indices: p.length / 3 > 65535 ? Uint32Array.from(idx) : Uint16Array.from(idx), skinned: false, ...(alpha ? { alphaTest: true } : {}),\n });\n if (I.length) out.push(mk('RealTree · ветви (приближённо)', bark, P, N, U, I, false));\n // листва: у конца каждой ветви (кроме ствола) и у её середины — карточка шаблона (по кругу)\n if (t.cards.length) {\n const LP: number[] = [], LN: number[] = [], LU: number[] = [], LI: number[] = [];\n let k = 0;\n t.branches.forEach((b, bi) => {\n if (bi === 0 && t.branches.length > 1) return;\n for (const at of [b.pts.length - 1, Math.floor(b.pts.length / 2)]) {\n // карточка сужена (шаблон — полуразмер облака листвы, у концов ветвей оно меньше)\n const c = b.pts[at]!, card = t.cards[k++ % t.cards.length]!, A = card.a.map((v) => v * LEAF_SCALE), n = norm(card.n), B = cross(n, A);\n const v0 = LP.length / 3;\n for (const [sa, sb, u, v] of [[-1, -1, 0, 1], [1, -1, 1, 1], [1, 1, 1, 0], [-1, 1, 0, 0]] as const) {\n LP.push(c[0]! + A[0]! * sa + B[0]! * sb, c[1]! + A[1]! * sa + B[1]! * sb, c[2]! + A[2]! * sa + B[2]! * sb); LN.push(n[0]!, n[1]!, n[2]!); LU.push(u, v);\n }\n LI.push(v0, v0 + 1, v0 + 2, v0, v0 + 2, v0 + 3);\n }\n });\n if (LI.length) out.push(mk('RealTree · листва (приближённо)', leaf, LP, LN, LU, LI, true));\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"realtree.js","sources":["../src/realtree.ts"],"sourcesContent":["// RealTree trees and bushes (Far Cry 2): stored in the archive by path hash, no name; inside is the path \"…\\Realtrees\\RT_*.rta\".\n// File: u32 size, u32 Havok part size, u32 136 (data start), 0, 0, u32 data size; from 24 the path; from 136 a dump\n// of editor memory (pointers are addresses; the base is the smallest pointer − 0x360), after the data a list of materials (.mlm,\n// read as .xbm: \"RealtreeTrunk\" bark and foliage), then Havok (collision).\n// Header at 0x118: u32, u32, f32, u32 branch levels, u32 nodes, ptr nodes, ptr, u32 branches, ptr, ptr parameters,\n// u32 foliage cards, ptr cards.\n// Node (76 bytes; fields at offset 52 from the record start): position f32×3, u32 = nodes to the branch end (at a branch start\n// it is larger than at the previous node; that is how branches split), f32 level scale (trunk 1 or 2, branches 0.5).\n// Foliage card (92 bytes): f32 size at 16, normal at 20, tangent at 32, half-card axis at 68 (its length = size).\n// The game builds the tree mesh itself from branch parameters; here is an approximation: branches as tubes along the nodes, foliage as\n// template cards at the branch ends.\nimport type { Mesh } from '@gmvjs/core/types';\n\nexport interface RealTree { path: string; branches: Array<{ pts: number[][]; tips: number[]; scale: number }>; cards: Array<{ n: number[]; a: number[] }>; materials: string[]; height: number }\n\nexport const isRealTree = (u: Uint8Array): boolean => {\n if (u.length < 160) return false;\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (dv.getUint32(8, true) !== 136) return false;\n const s = new TextDecoder('latin1').decode(u.subarray(24, 136));\n return /realtrees?\\\\/i.test(s);\n};\n\nexport function parseRealTree(u: Uint8Array): RealTree {\n if (!isRealTree(u)) throw new Error('Не RealTree');\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength), dl = dv.getUint32(20, true);\n let e = u.indexOf(0, 24); if (e < 0 || e > 136) e = 136;\n const path = new TextDecoder('latin1').decode(u.subarray(24, e));\n const dataEnd = Math.min(u.length, 136 + dl);\n // pointer base: the smallest address pointer in the data − 0x360 (the first structure after the header)\n let minPtr = Infinity;\n const hdr = Array.from({ length: 12 }, (_, i) => dv.getUint32(0x118 + i * 4, true));\n for (const i of [5, 6, 8, 9, 11]) if (hdr[i]! > 0x10000) minPtr = Math.min(minPtr, hdr[i]!);\n for (let o = 136; o + 4 <= dataEnd; o += 4) { const v = dv.getUint32(o, true); if (v >= 0x01000000 && v < minPtr && (v & 0xffff0000) === (minPtr & 0xffff0000)) minPtr = v; }\n if (!Number.isFinite(minPtr)) throw new Error('RealTree: нет указателей');\n const base = minPtr - 0x360, off = (p: number) => p - base + 0x88;\n const [nNodes, pNodes, nCards, pCards] = [hdr[4]!, hdr[5]!, hdr[10]!, hdr[11]!];\n const branches: RealTree['branches'] = [];\n let cur: RealTree['branches'][number] | null = null, height = 0;\n for (let r = 0; r < nNodes; r++) {\n const o = off(pNodes) + r * 76;\n if (o < 136 || o + 76 > dataEnd) break;\n const x = dv.getFloat32(o + 52, true), y = dv.getFloat32(o + 56, true), z = dv.getFloat32(o + 60, true), tip = dv.getUint32(o + 64, true), sc = dv.getFloat32(o + 68, true);\n if (tip > 100000 || ![x, y, z, sc].every(Number.isFinite) || Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) > 1000) break;\n if (!cur || tip > cur.tips[cur.tips.length - 1]!) { cur = { pts: [], tips: [], scale: sc > 0 && sc < 10 ? sc : 1 }; branches.push(cur); }\n cur.pts.push([x, y, z]); cur.tips.push(tip);\n height = Math.max(height, z);\n }\n const cards: RealTree['cards'] = [];\n for (let i = 0; i < nCards; i++) {\n const o = off(pCards) + i * 92;\n if (o < 136 || o + 92 > dataEnd) break;\n const f = (k: number) => dv.getFloat32(o + k, true);\n const n = [f(20), f(24), f(28)], a = [f(68), f(72), f(76)];\n if (![...n, ...a].every(Number.isFinite) || Math.hypot(...a) > 50) continue;\n if (Math.hypot(...a) > 1e-3) cards.push({ n, a });\n }\n // materials: \"…\\.mlm\" strings after the data\n const materials: string[] = [];\n const tail = new TextDecoder('latin1').decode(u.subarray(dataEnd, Math.min(u.length, dataEnd + 4096)));\n for (const m of tail.matchAll(/graphics\\\\[\\w\\\\ .-]+?\\.mlm/gi)) materials.push(m[0]);\n return { path, branches: branches.filter((b) => b.pts.length >= 2), cards, materials, height };\n}\n\n/** Foliage card scale and branch taper exponent (eyeballed on bushes and a ficus). */\nconst LEAF_SCALE = 0.7, TAPER = 1.1;\n\nconst sub = (a: number[], b: number[]) => [a[0]! - b[0]!, a[1]! - b[1]!, a[2]! - b[2]!];\nconst cross = (a: number[], b: number[]) => [a[1]! * b[2]! - a[2]! * b[1]!, a[2]! * b[0]! - a[0]! * b[2]!, a[0]! * b[1]! - a[1]! * b[0]!];\nconst norm = (a: number[]) => { const l = Math.hypot(a[0]!, a[1]!, a[2]!) || 1; return [a[0]! / l, a[1]! / l, a[2]! / l]; };\n\n/** Approximate mesh: branches as tubes (`sides` faces), foliage as cards at branch ends; bark/leaf are .xbm material paths. */\nexport function realTreeMeshes(t: RealTree, bark: { material?: string; texture: string; normal?: string }, leaf: { material?: string; texture: string; normal?: string }, sides = 4): Mesh[] {\n const R0 = Math.max(0.02, t.height * 0.025);\n const P: number[] = [], N: number[] = [], U: number[] = [], I: number[] = [];\n for (const b of t.branches) {\n const t0 = b.tips[0]! + 1, first = P.length / 3;\n let len = 0;\n b.pts.forEach((p, i) => {\n const d = norm(sub(b.pts[Math.min(i + 1, b.pts.length - 1)]!, b.pts[Math.max(i - 1, 0)]!));\n const up = Math.abs(d[2]!) < 0.9 ? [0, 0, 1] : [1, 0, 0], s1 = norm(cross(d, up)), s2 = cross(d, s1);\n const r = R0 * b.scale * Math.max(0.15, Math.pow((b.tips[i]! + 1) / t0, TAPER));\n if (i) len += Math.hypot(...sub(p, b.pts[i - 1]!));\n for (let k = 0; k <= sides; k++) {\n const a = (k / sides) * Math.PI * 2, c = Math.cos(a), s = Math.sin(a);\n const nx = s1[0]! * c + s2[0]! * s, ny = s1[1]! * c + s2[1]! * s, nz = s1[2]! * c + s2[2]! * s;\n P.push(p[0]! + nx * r, p[1]! + ny * r, p[2]! + nz * r); N.push(nx, ny, nz); U.push(k / sides, len);\n }\n });\n for (let i = 0; i + 1 < b.pts.length; i++) for (let k = 0; k < sides; k++) {\n const a = first + i * (sides + 1) + k, c = a + sides + 1;\n I.push(a, c, a + 1, a + 1, c, c + 1);\n }\n }\n const out: Mesh[] = [];\n const mk = (name: string, m: { material?: string; texture: string; normal?: string }, p: number[], n: number[], uv: number[], idx: number[], alpha: boolean): Mesh => ({\n name, texture: m.texture, material: m.material, normalMap: m.normal, vertexCount: p.length / 3, pos: Float32Array.from(p), nor: Float32Array.from(n), uv: Float32Array.from(uv),\n bone: new Float32Array(0), weight: new Float32Array(0), indices: p.length / 3 > 65535 ? Uint32Array.from(idx) : Uint16Array.from(idx), skinned: false, ...(alpha ? { alphaTest: true } : {}),\n });\n if (I.length) out.push(mk('RealTree · ветви (приближённо)', bark, P, N, U, I, false));\n // foliage: a template card at the end of every branch (except the trunk) and at its middle (in a circle)\n if (t.cards.length) {\n const LP: number[] = [], LN: number[] = [], LU: number[] = [], LI: number[] = [];\n let k = 0;\n t.branches.forEach((b, bi) => {\n if (bi === 0 && t.branches.length > 1) return;\n for (const at of [b.pts.length - 1, Math.floor(b.pts.length / 2)]) {\n // the card is narrowed (the template is half the foliage cloud size, which is smaller at branch ends)\n const c = b.pts[at]!, card = t.cards[k++ % t.cards.length]!, A = card.a.map((v) => v * LEAF_SCALE), n = norm(card.n), B = cross(n, A);\n const v0 = LP.length / 3;\n for (const [sa, sb, u, v] of [[-1, -1, 0, 1], [1, -1, 1, 1], [1, 1, 1, 0], [-1, 1, 0, 0]] as const) {\n LP.push(c[0]! + A[0]! * sa + B[0]! * sb, c[1]! + A[1]! * sa + B[1]! * sb, c[2]! + A[2]! * sa + B[2]! * sb); LN.push(n[0]!, n[1]!, n[2]!); LU.push(u, v);\n }\n LI.push(v0, v0 + 1, v0 + 2, v0, v0 + 2, v0 + 3);\n }\n });\n if (LI.length) out.push(mk('RealTree · листва (приближённо)', leaf, LP, LN, LU, LI, true));\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/rml.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface RmlNode {
|
|
|
7
7
|
export declare function parseRml(u: Uint8Array): RmlNode;
|
|
8
8
|
export declare const rmlChild: (n: RmlNode | undefined, name: string) => RmlNode | undefined;
|
|
9
9
|
export declare const rmlAll: (n: RmlNode | undefined, name: string) => RmlNode[];
|
|
10
|
-
/**
|
|
10
|
+
/** XML elements in order (Dunia binary XML or text, as in worlds/tmpla): name, attributes, depth. */
|
|
11
11
|
export declare function xmlElements(u: Uint8Array): Array<{
|
|
12
12
|
name: string;
|
|
13
13
|
attrs: Record<string, string>;
|
package/dist/rml.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rml.js","sources":["../src/rml.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"rml.js","sources":["../src/rml.ts"],"sourcesContent":["// Dunia binary XML (XmlResourceFile: worlds/<world>/generated/<world>.game.xml and others).\n// u8 0, u8 ?, then packed u32 (u8 < 0xFE or 0xFF + u32): string table size, node count, attribute count;\n// node: name, value (string table offsets), attribute count, child count; attribute: 0, name, value; children follow in sequence.\n// After the tree comes the string table (UTF-8, \\0-separated); names and values are offsets into it.\n\nexport interface RmlNode { name: string; value: string; attrs: Record<string, string>; children: RmlNode[] }\n\nexport function parseRml(u: Uint8Array): RmlNode {\n if (u.length < 5 || u[0] !== 0) throw new Error('Не двоичный XML Dunia');\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n let p = 2;\n const pk = () => {\n if (p >= u.length) throw new Error('Двоичный XML обрезан');\n const b = u[p++]!;\n if (b < 0xfe) return b;\n if (b === 0xfe || p + 4 > u.length) throw new Error('Двоичный XML: неверное число');\n const v = dv.getUint32(p, true); p += 4; return v;\n };\n const stSize = pk(); pk(); pk();\n type Raw = { n: number; v: number; a: Array<[number, number]>; c: Raw[] };\n const node = (depth: number): Raw => {\n if (depth > 256) throw new Error('Двоичный XML: слишком глубоко');\n const n = pk(), v = pk(), ac = pk(), cc = pk();\n const a: Array<[number, number]> = [];\n for (let i = 0; i < ac; i++) { pk(); a.push([pk(), pk()]); }\n const c: Raw[] = [];\n for (let i = 0; i < cc; i++) c.push(node(depth + 1));\n return { n, v, a, c };\n };\n const raw = node(0);\n if (p + stSize > u.length) throw new Error('Двоичный XML: таблица строк обрезана');\n const st = u.subarray(p, p + stSize), dec = new TextDecoder('utf-8'), cache = new Map<number, string>();\n const s = (i: number) => {\n let r = cache.get(i);\n if (r === undefined) { let e = st.indexOf(0, i); if (e < 0) e = st.length; r = dec.decode(st.subarray(i, e)); cache.set(i, r); }\n return r;\n };\n const conv = (r: Raw): RmlNode => ({ name: s(r.n), value: s(r.v), attrs: Object.fromEntries(r.a.map(([k, v]) => [s(k), s(v)])), children: r.c.map(conv) });\n return conv(raw);\n}\n\nexport const rmlChild = (n: RmlNode | undefined, name: string): RmlNode | undefined => n?.children.find((c) => c.name === name);\nexport const rmlAll = (n: RmlNode | undefined, name: string): RmlNode[] => n?.children.filter((c) => c.name === name) ?? [];\n\n/** XML elements in order (Dunia binary XML or text, as in worlds/tmpla): name, attributes, depth. */\nexport function xmlElements(u: Uint8Array): Array<{ name: string; attrs: Record<string, string>; depth: number }> {\n const out: Array<{ name: string; attrs: Record<string, string>; depth: number }> = [];\n if (u[0] === 0) {\n const walk = (n: RmlNode, d: number) => { out.push({ name: n.name, attrs: n.attrs, depth: d }); for (const c of n.children) walk(c, d + 1); };\n walk(parseRml(u), 0);\n return out;\n }\n const text = new TextDecoder('utf-8').decode(u);\n let depth = 0;\n for (const m of text.matchAll(/<(\\/?)([A-Za-z_][\\w.-]*)([^>]*?)(\\/?)>/g)) {\n if (m[1]) { depth--; continue; }\n const attrs: Record<string, string> = {};\n for (const a of m[3]!.matchAll(/([\\w.:-]+)\\s*=\\s*\"([^\"]*)\"/g)) attrs[a[1]!] = a[2]!.replace(/"/g, '\"').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');\n out.push({ name: m[2]!, attrs, depth });\n if (!m[4]) depth++;\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/xbg.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface XbgPrim {
|
|
|
30
30
|
lastVertex: number;
|
|
31
31
|
firstVertex: number;
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
33
|
+
/** LOD vertex buffer: format, stride, vertex count, offset in the LOD's shared vertex data. */
|
|
34
34
|
export interface XbgBuffer {
|
|
35
35
|
format: number;
|
|
36
36
|
stride: number;
|
|
@@ -58,7 +58,7 @@ export interface Xbg {
|
|
|
58
58
|
pcmp: [number, number];
|
|
59
59
|
ucmp: [number, number];
|
|
60
60
|
u: Uint8Array;
|
|
61
|
-
/**
|
|
61
|
+
/** Animation bones (O2BM): inverse bind matrices (column-major) in bone order. */
|
|
62
62
|
o2bm: Float64Array[];
|
|
63
63
|
}
|
|
64
64
|
export declare function parseXbg(u: Uint8Array): Xbg;
|
|
@@ -69,37 +69,37 @@ export interface XbmMaterial {
|
|
|
69
69
|
}
|
|
70
70
|
export declare function parseXbm(u: Uint8Array): XbmMaterial;
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
72
|
+
* Character "kits" (merc_kit, multiplayer classes, female_civilian_kit): all swappable parts live in one model.
|
|
73
|
+
* By default one part per slot is visible (a head with its eyes, a body of its race, hair, top, bottom, shoes; "no hat",
|
|
74
|
+
* "no glasses", "no gear" where available), "DUMMY…" are hidden. Returns the names of the parts to hide.
|
|
75
75
|
*/
|
|
76
76
|
export declare function kitHiddenParts(names: readonly string[]): Set<string>;
|
|
77
77
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* T
|
|
81
|
-
*
|
|
78
|
+
* Animation bones -> nodes. Bone i of the O2BM list is the node whose world pose matches the bone pose up to a common
|
|
79
|
+
* transform T (for animals T = 1; for humans O2BM bones are relative to the pelvis at the origin, rotated 90° about Z).
|
|
80
|
+
* T is found by trying nodes for bone 0. The list lacks "…Nub", the root and parts, so numbers differ from NODE.
|
|
81
|
+
* Returns the nodes (−1 = not found) and T (animation frame -> model world).
|
|
82
82
|
*/
|
|
83
83
|
export declare function o2bmNodes(x: Xbg): {
|
|
84
84
|
nodes: number[];
|
|
85
85
|
frame: Float64Array;
|
|
86
86
|
};
|
|
87
87
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
88
|
+
* Hierarchy as in the animation skeleton: helper bones (knees, elbows) hang from the pelvis in the model but from the
|
|
89
|
+
* thigh/upper arm in .skeleton; without re-parenting they do not follow the limb. The bind pose is preserved. parentOf: parent name by bone name.
|
|
90
90
|
*/
|
|
91
91
|
export declare function reparentNodes(sn: SkeletonNode[], parentOf: ReadonlyMap<string, string>): SkeletonNode[];
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
93
|
+
* Skeleton nodes: node poses are in parent space. frame: the vertex frame of skinned meshes; for humans vertices are written
|
|
94
|
+
* in the O2BM bone frame (pelvis at the origin) while nodes are in the model frame; offset = (node world bind pose)⁻¹ · frame.
|
|
95
95
|
*/
|
|
96
96
|
export declare function xbgSkeletonNodes(nodes: XbgNode[], frame?: ArrayLike<number>): SkeletonNode[];
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
98
|
+
* The model's animation skeleton: "<model name>_ref.skeleton" next to it, then up the folders "hips_ref.skeleton",
|
|
99
|
+
* "pelvis_ref.skeleton", "_common\\pelvis_ref.skeleton"; for humans outside graphics\\characters (actors), the shared human skeleton.
|
|
100
100
|
*/
|
|
101
101
|
export declare function skeletonCandidates(modelPath: string, human?: boolean): string[];
|
|
102
|
-
/**
|
|
102
|
+
/** Near-LOD meshes of an .xbg model (with .xbm materials from the archive); matCache is a shared material cache (for levels). */
|
|
103
103
|
export declare function xbgMeshes(u: Uint8Array, arc: Archive | null, matCache?: Map<string, Promise<XbmMaterial | null>>, lodIndex?: number, parsed?: Xbg): Promise<{
|
|
104
104
|
x: Xbg;
|
|
105
105
|
lod: XbgLod;
|
|
@@ -107,9 +107,9 @@ export declare function xbgMeshes(u: Uint8Array, arc: Archive | null, matCache?:
|
|
|
107
107
|
skinned: boolean;
|
|
108
108
|
missing: string[];
|
|
109
109
|
}>;
|
|
110
|
-
/**
|
|
110
|
+
/** Skinned meshes in the bind pose as static geometry (for levels): vertices via the model nodes' skin matrices. */
|
|
111
111
|
export declare function bakeBindPose(x: Xbg, meshes: Mesh[]): Mesh[];
|
|
112
112
|
export declare function loadXbg(main: VFile, ctx: LoadContext): Promise<Model>;
|
|
113
|
-
/**
|
|
113
|
+
/** Animations for the model (data on selection): every .mab in the skeleton folder and below. */
|
|
114
114
|
export declare function listMabMotions(model: Model, archive: Archive): NodeMotion[];
|
|
115
115
|
export {};
|
package/dist/xbg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xbg.js","sources":["../src/xbg.ts"],"sourcesContent":["// Модели Dunia (Far Cry 2): .xbg (геометрия) и .xbm (материал) — один контейнер «HSEM» (MESH):\n// u32 «MESH», u16 версия 0x2a, u16, u32 хеш, …, u32 [0x1c] число корневых чанков; чанк — u32 тег (байты наоборот),\n// u32 1, u32 полный размер, u32 размер своих данных, u32 число детей; дети идут перед своими данными.\n// Чанки .xbg:\n// RMTL — материалы: u32 n, u32 0, n × (u32 длина, путь .xbm, 0);\n// NODE — узлы: u32 n, u32 0, n × (i32 первый ребёнок, i32 следующий, i32 родитель, кватернион xyzw, позиция, масштаб,\n// i32, f32, f32, u32 длина, имя, 0, u32 хеш);\n// SKID — части: u32 n, n × (u32 хеш, u16 LOD, u16 узел); SKND — части: u32 n, n × (f32 дистанция, сфера f32×4,\n// рамка f32×6, u32 LOD, u32, u32 длина, имя «…_LOD0», 0), детский CLUS — по части: u32 k, k × кластер\n// (u16 материал, u16 треугольников ×2, u16 индексов, u16 шаг, u16 вершин, u16 формат, u16×48 — узлы для костей вершин);\n// LODS — геометрия: u32 число LOD, на LOD: f32 дистанция, u32 буферов, буферы (u32 формат, u32 шаг, u32 вершин,\n// u32 смещение в данных вершин LOD), u32 число примитивов, примитивы по 7×u32 (буфер, часть, кластер в части,\n// первый индекс, последняя вершина в буфере, смещение первой вершины в байтах, 0), u32 байт вершин +\n// выравнивание до 16 (по файлу), вершины всех буферов, u32 индексов + выравнивание, u16 индексы (от начала буфера);\n// PCMP / UCMP — распаковка позиций / UV: f32 сдвиг, f32 множитель (значение = s16 · множитель + сдвиг).\n// Вершина 16 байт (0x008a, трава): позиция, UV, цвет — нормали считаются по треугольникам.\n// Вершина 32 байта (формат 0x0bca): позиция s16×3 + s16 (1), UV s16×2, UV2 s16×2, нормаль u8×4 (BGRA), цвет u8×4,\n// касательная, бинормаль. 40 байт (0x0bda, скин): позиция, UV, UV2, веса u8×4, кости u8×4 (номер в таблице кластера),\n// нормаль, цвет, касательная, бинормаль. Треугольники — по часовой (D3D): при чтении меняем порядок.\n// Материал .xbm: тот же контейнер, чанк DMTL: 5 байт, u32+имя, u32+шейдер, u32 n, n × (u32+путь .xbt, u32+слот), числа.\n// Слоты цвета по шейдерам: Generic/Weapon/Vehicle/Hair/Leaf — DiffuseTexture1, Skin — SkinTexture, Cloth — FabricTexture.\n// Система координат — Z вверх, метры.\nimport { boundsOf, computeNormals, skinnedBounds } from '@gmvjs/core/geometry';\nimport { compose, mulCol, rigidInv } from '@gmvjs/core/math';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport { NodeSkeleton, type SkeletonNode } from '@gmvjs/core/skeleton';\nimport type { Archive, Mesh, Model, NodeMotion, VFile } from '@gmvjs/core/types';\nimport { buildMabMotion, matQuat, parseMab, parseSkeleton, type DuniaSkelBone } from './mab';\n\ninterface Chunk { tag: string; at: number; size: number; data: number; dsize: number; kids: Chunk[] }\n\nexport function readChunks(u: Uint8Array): Chunk[] {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 0x20 || dv.getUint32(0, true) !== 0x4d455348) throw new Error('Не модель Dunia (нет «MESH»)');\n const walk = (p: number, end: number, depth: number): Chunk[] => {\n const out: Chunk[] = [];\n while (p + 20 <= end) {\n const tag = String.fromCharCode(u[p + 3]!, u[p + 2]!, u[p + 1]!, u[p]!), size = dv.getUint32(p + 8, true), dsize = dv.getUint32(p + 12, true), nk = dv.getUint32(p + 16, true);\n if (size < 20 || p + size > end || dsize > size - 20 || depth > 8) throw new Error(`XBG: чанк ${tag} повреждён`);\n const data = p + size - dsize;\n out.push({ tag, at: p, size, data, dsize, kids: nk ? walk(p + 20, data, depth + 1) : [] });\n p += size;\n }\n return out;\n };\n return walk(0x20, u.length, 0);\n}\n\nclass R {\n readonly dv: DataView;\n constructor(readonly u: Uint8Array, public p: number) { this.dv = new DataView(u.buffer, u.byteOffset, u.byteLength); }\n u8() { return this.u[this.p++]!; }\n u16() { const v = this.dv.getUint16(this.p, true); this.p += 2; return v; }\n u32() { const v = this.dv.getUint32(this.p, true); this.p += 4; return v; }\n i32() { const v = this.dv.getInt32(this.p, true); this.p += 4; return v; }\n f32() { const v = this.dv.getFloat32(this.p, true); this.p += 4; return v; }\n str() {\n const n = this.u32();\n if (n > 4096 || this.p + n > this.u.length) throw new Error(`Dunia: строка длиной ${n} на ${this.p - 4}`);\n let s = ''; for (let i = 0; i < n; i++) s += String.fromCharCode(this.u[this.p + i]!); this.p += n + 1; return s;\n }\n align16() { this.p = (this.p + 15) & ~15; }\n}\n\nexport interface XbgNode { name: string; parent: number; rot: number[]; pos: number[]; scale: number[] }\nexport interface XbgCluster { mat: number; tris: number; palette: number[] }\nexport interface XbgPrim { buffer: number; part: number; cluster: number; firstIndex: number; lastVertex: number; firstVertex: number }\n/** Вершинный буфер LOD: формат, шаг, число вершин, смещение в общих данных вершин LOD. */\nexport interface XbgBuffer { format: number; stride: number; vcount: number; base: number }\nexport interface XbgLod { dist: number; buffers: XbgBuffer[]; prims: XbgPrim[]; vAt: number; iAt: number; icount: number }\nexport interface Xbg {\n materials: string[]; nodes: XbgNode[]; parts: Array<{ name: string; lod: number; node: number }>; clusters: XbgCluster[][];\n lods: XbgLod[]; pcmp: [number, number]; ucmp: [number, number]; u: Uint8Array;\n /** Кости анимации (O2BM): обратные bind-матрицы (column-major) по порядку костей. */\n o2bm: Float64Array[];\n}\n\nexport function parseXbg(u: Uint8Array): Xbg {\n const chunks = readChunks(u);\n const find = (t: string, list = chunks): Chunk | undefined => { for (const c of list) { if (c.tag === t) return c; const k = find(t, c.kids); if (k) return k; } return undefined; };\n const x: Xbg = { materials: [], nodes: [], parts: [], clusters: [], lods: [], pcmp: [0, 1 / 32768], ucmp: [0, 1 / 32768], u, o2bm: [] };\n const rm = find('RMTL');\n if (rm) { const r = new R(u, rm.data), n = r.u32(); r.u32(); for (let i = 0; i < n; i++) x.materials.push(r.str()); }\n const nd = find('NODE');\n if (nd) {\n const r = new R(u, nd.data), n = r.u32(); r.u32();\n for (let i = 0; i < n; i++) {\n r.i32(); r.i32(); const parent = r.i32();\n const q = [r.f32(), r.f32(), r.f32(), r.f32()], pos = [r.f32(), r.f32(), r.f32()], scale = [r.f32(), r.f32(), r.f32()];\n r.i32(); r.f32(); r.f32();\n const name = r.str(); r.u32();\n x.nodes.push({ name, parent, rot: [q[3]!, q[0]!, q[1]!, q[2]!], pos, scale });\n }\n }\n const sk = find('SKND'), sid = find('SKID');\n const partNode: number[] = [];\n if (sid) { const r = new R(u, sid.data), n = r.u32(); for (let i = 0; i < n; i++) { r.u32(); r.u16(); const nd2 = r.u16(); partNode.push(nd2 === 0xffff ? -1 : nd2); } }\n if (sk) {\n const r = new R(u, sk.data), n = r.u32();\n for (let i = 0; i < n; i++) { r.p += 44; const lod = r.u32(); r.u32(); x.parts.push({ name: r.str(), lod, node: partNode[i] ?? -1 }); }\n const cl = sk.kids.find((k) => k.tag === 'CLUS');\n if (cl) {\n const r2 = new R(u, cl.data);\n for (let i = 0; i < n && r2.p < cl.data + cl.dsize; i++) {\n const k = r2.u32(), list: XbgCluster[] = [];\n for (let j = 0; j < k; j++) {\n const mat = r2.u16(), tris = r2.u16(); r2.p += 10;\n const palette: number[] = [];\n for (let b = 0; b < 48; b++) palette.push(r2.u16());\n list.push({ mat, tris, palette });\n }\n x.clusters.push(list);\n }\n }\n }\n const ld = find('LODS');\n if (!ld) throw new Error('XBG: нет геометрии (LODS)');\n {\n const r = new R(u, ld.data), n = r.u32();\n for (let l = 0; l < n; l++) {\n const dist = r.f32(), nb = r.u32(), buffers: XbgBuffer[] = [];\n for (let b = 0; b < nb; b++) buffers.push({ format: r.u32(), stride: r.u32(), vcount: r.u32(), base: r.u32() });\n const np = r.u32(), prims: XbgPrim[] = [];\n for (let i = 0; i < np; i++) {\n const buffer = r.u32(), part = r.u32(), cluster = r.u32(), firstIndex = r.u32(), lastVertex = r.u32(), voff = r.u32(); r.u32();\n const bf = buffers[buffer];\n if (!bf) throw new Error('XBG: примитив ссылается на несуществующий буфер');\n prims.push({ buffer, part, cluster, firstIndex, lastVertex, firstVertex: (voff - bf.base) / bf.stride });\n }\n const vb = r.u32(); r.align16(); const vAt = r.p; r.p += vb;\n const ic = r.u32(); r.align16(); const iAt = r.p; r.p += ic * 2;\n if (r.p > ld.data + ld.dsize || vb !== buffers.reduce((s2, b) => s2 + b.vcount * b.stride, 0)) throw new Error('XBG: буфер LOD повреждён');\n x.lods.push({ dist, buffers, prims, vAt, iAt, icount: ic });\n }\n }\n const ob = find('O2BM');\n if (ob && ob.dsize >= 4) {\n const dv2 = new DataView(u.buffer, u.byteOffset, u.byteLength), n = dv2.getUint32(ob.data, true);\n if (4 + n * 64 <= ob.dsize) for (let i = 0; i < n; i++) x.o2bm.push(Float64Array.from({ length: 16 }, (_, k) => dv2.getFloat32(ob.data + 4 + i * 64 + k * 4, true)));\n }\n const cmp = (t: string): [number, number] | null => { const c = find(t); if (!c || c.dsize < 8) return null; const dv = new DataView(u.buffer, u.byteOffset + c.data, 8); return [dv.getFloat32(0, true), dv.getFloat32(4, true)]; };\n x.pcmp = cmp('PCMP') ?? x.pcmp;\n x.ucmp = cmp('UCMP') ?? x.ucmp;\n return x;\n}\n\nexport interface XbmMaterial { name: string; shader: string; textures: Record<string, string> }\n\nexport function parseXbm(u: Uint8Array): XbmMaterial {\n const c = readChunks(u).find((k) => k.tag === 'DMTL');\n if (!c) throw new Error('XBM: нет чанка DMTL');\n const r = new R(u, c.data + 5);\n const name = r.str(), shader = r.str(), n = r.u32(), textures: Record<string, string> = {};\n for (let i = 0; i < n && i < 64; i++) { const path = r.str(), slot = r.str(); if (path) textures[slot] = path; }\n return { name, shader, textures };\n}\n\n/**\n * «Киты» персонажей (merc_kit, мультиплеерные классы, female_civilian_kit): все сменные части лежат в одной модели.\n * По умолчанию видна одна часть на слот (голова и глаза к ней, тело её расы, волосы, верх, низ, обувь; «без шапки»,\n * «без очков», «без снаряжения», если такие есть), «DUMMY…» скрыты. Возвращает имена частей, которые скрыть.\n */\nexport function kitHiddenParts(names: readonly string[]): Set<string> {\n const parts = [...new Set(names)], hidden = new Set<string>();\n const heads = parts.filter((n) => /HEAD/.test(n) && !/EYES|ROOT/.test(n));\n if (heads.length < 2 && !parts.some((n) => /DUMMY/.test(n))) return hidden; // не кит\n const key = (n: string) => n.replace(/^P_[A-Z]+_/, '').replace(/_LOD\\d+$/, '').replace(/_/g, '');\n const head = heads.find((n) => /CAUCASIANHEAD01/.test(n)) ?? heads[0];\n const race = head && /NUBIAN/.test(head) ? 'NUBIAN' : 'CAUCASIAN';\n const choose = (list: string[], prefer: RegExp[] = []) => {\n if (list.length < 2) return;\n let keep = list[0]!;\n for (const re of prefer) { const f = list.find((n) => re.test(n)); if (f) { keep = f; break; } }\n for (const n of list) if (n !== keep) hidden.add(n);\n };\n for (const n of parts) if (/DUMMY/.test(n)) hidden.add(n);\n for (const n of heads) if (n !== head) hidden.add(n);\n if (head) for (const n of parts.filter((x) => /EYES/.test(x))) if (key(n).replace(/^EYES/, '') !== key(head)) hidden.add(n);\n const rest = parts.filter((n) => !hidden.has(n) && !heads.includes(n) && !/EYES/.test(n));\n const slot = (re: RegExp, not?: RegExp) => rest.filter((n) => re.test(n) && !(not && not.test(n)));\n const top = slot(/_UB_|DRESS/); // TOP_BODY — тело с руками, не одежда\n choose(top);\n const topKept = top.find((n) => !hidden.has(n)) ?? '';\n const armsStyle = /BEATER/.test(topKept) ? 'BEATER' : /VEST/.test(topKept) ? 'LONGVEST' : 'SHIRT';\n // тело: своей расы; руки — под верх\n for (const re of [/_ARMS$/, /BCHEST/, /_LEGS$/]) {\n const list = slot(re);\n choose(list, re.source === '_ARMS$' ? [new RegExp(`${race}_${armsStyle}_ARMS`), new RegExp(race)] : [new RegExp(race)]);\n }\n choose(slot(/HAIR/), [race === 'NUBIAN' ? /AFRO/ : /CAUC/]);\n choose(slot(/HAT|CAP\\d|BEANIE|BANDANA/), [/NOHAT/]);\n choose(slot(/GLASS|GOGGLE/), [/NOGLASS/]);\n choose(slot(/_LB_.*(PANT|JEANS|SKIRT)|LEGS_.*SKIRT/));\n choose(slot(/BOOT/));\n choose(slot(/GEAR|BACKPACK|STRAPS|LEATHER_VEST|TACTICAL_VEST|FLACK_JACKET|VESTSPORT/), [/NOGEAR/]);\n for (const j of ['BRACELET', 'NECKLACE']) choose(slot(new RegExp(j)));\n return hidden;\n}\n\n/** Раскладка вершины по шагу: смещение нормали (−1 — нет, считается по треугольникам), веса/кости с 16. */\nconst LAYOUT: Record<number, { normal: number; skin: boolean }> = { 16: { normal: -1, skin: false }, 32: { normal: 16, skin: false }, 40: { normal: 24, skin: true } };\n\n/** Шейдеры листвы, травы и волос — вырезание по альфе. */\nconst ALPHA_SHADERS = new Set(['Leaf', 'BigLeaf', 'Grass', 'Hair', 'Decal']);\n\nconst slotOf = (t: Record<string, string>, ...keys: string[]) => { for (const k of keys) if (t[k]) return t[k]; return undefined; };\n\n/**\n * Кости анимации -> узлы. Кость i списка O2BM — узел, чья мировая поза совпадает с позой кости с точностью до общего\n * преобразования T (у животных T = 1; у людей кости O2BM — от таза в начале координат, повернуты на 90° вокруг Z).\n * T ищется перебором узла для кости 0. В списке нет «…Nub», корня и частей, поэтому номера не совпадают с NODE.\n * Возвращает узлы (−1 — не найден) и T (кадр анимации -> мир модели).\n */\nexport function o2bmNodes(x: Xbg): { nodes: number[]; frame: Float64Array } {\n const N = xbgSkeletonNodes(x.nodes).map((s) => rigidInv(s.offset!));\n const O = x.o2bm.map((m) => rigidInv(m));\n const id: Float64Array = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);\n if (!O.length) return { nodes: [], frame: id };\n const dist = (a: ArrayLike<number>, b: ArrayLike<number>) => Math.hypot(a[12]! - b[12]!, a[13]! - b[13]!, a[14]! - b[14]!) + Math.hypot(...[0, 1, 2, 4, 5, 6, 8, 9, 10].map((k) => a[k]! - b[k]!));\n const match = (T: ArrayLike<number>, limit: number) => {\n const out: number[] = [];\n for (const o of O) {\n const w = mulCol(T, o);\n let bj = -1, bd = 0.02;\n N.forEach((n, j) => { const d = dist(n, w); if (d < bd) { bd = d; bj = j; } });\n out.push(bj);\n if (out.length >= limit && out.every((v) => v < 0)) break;\n }\n return out;\n };\n // быстрый путь: кадры совпадают\n let best: { n: number; nodes: number[]; frame: Float64Array } = { n: -1, nodes: [], frame: id };\n const direct = match(id, O.length);\n best = { n: direct.filter((j) => j >= 0).length, nodes: direct, frame: id };\n if (best.n < O.length) {\n N.forEach((n, c) => {\n const T = mulCol(n, rigidInv(O[0]!));\n const probe = match(T, 8);\n if (probe.length < O.length && probe.every((v) => v < 0)) return;\n const nodes = probe.length === O.length ? probe : match(T, O.length), k = nodes.filter((j) => j >= 0).length;\n if (k > best.n) best = { n: k, nodes, frame: T };\n });\n }\n return { nodes: best.nodes, frame: best.frame };\n}\n\n/**\n * Иерархия как в скелете анимаций: вспомогательные кости (колени, локти) в модели висят на тазу, а в .skeleton — на\n * бедре/плече; без переподвешивания они не следуют за конечностью. Bind-поза сохраняется. parentOf — имя родителя по имени кости.\n */\nexport function reparentNodes(sn: SkeletonNode[], parentOf: ReadonlyMap<string, string>): SkeletonNode[] {\n const byName = new Map(sn.map((n, i) => [n.name.toLowerCase(), i]));\n const world: Float64Array[] = [];\n sn.forEach((n, i) => { world[i] = n.parent >= 0 ? mulCol(world[n.parent]!, n.local) : Float64Array.from(n.local); });\n return sn.map((n, i) => {\n const pn = parentOf.get(n.name.toLowerCase());\n const p = pn === undefined ? undefined : byName.get(pn);\n if (p === undefined || p === n.parent || p >= i) return n;\n return { ...n, parent: p, local: mulCol(rigidInv(world[p]!), world[i]!) };\n });\n}\n\n/**\n * Узлы скелета: позы узлов — в пространстве родителя. frame — кадр вершин скиновых мешей: у людей вершины записаны\n * в кадре костей O2BM (таз в начале координат), а узлы — в кадре модели; offset = (мировая bind-поза узла)⁻¹ · frame.\n */\nexport function xbgSkeletonNodes(nodes: XbgNode[], frame?: ArrayLike<number>): SkeletonNode[] {\n const local = nodes.map((n) => { const m = new Float64Array(16); compose(n.pos, n.rot, n.scale, m); return m; });\n const world: Float64Array[] = [];\n nodes.forEach((n, i) => { world[i] = n.parent >= 0 && n.parent < i ? mulCol(world[n.parent]!, local[i]!) : local[i]!; });\n return nodes.map((n, i) => ({ name: n.name, parent: n.parent >= 0 && n.parent < i ? n.parent : -1, local: local[i]!, offset: frame ? mulCol(rigidInv(world[i]!), frame) : rigidInv(world[i]!) }));\n}\n\n/**\n * Скелет анимаций модели: «<имя модели>_ref.skeleton» рядом, затем вверх по папкам — «hips_ref.skeleton»,\n * «pelvis_ref.skeleton», «_common\\\\pelvis_ref.skeleton»; для людей вне graphics\\\\characters (actors) — общий скелет людей.\n */\nexport function skeletonCandidates(modelPath: string, human = false): string[] {\n const parts = modelPath.replace(/\\//g, '\\\\').toLowerCase().split('\\\\'), stem = parts[parts.length - 1]!.replace(/\\.xbg$/, '');\n const dirs = parts.slice(0, -1), out = [`${dirs.join('\\\\')}\\\\${stem}_ref.skeleton`];\n for (let n = dirs.length; n >= 2; n--) {\n const dir = dirs.slice(0, n).join('\\\\');\n out.push(`${dir}\\\\hips_ref.skeleton`, `${dir}\\\\pelvis_ref.skeleton`, `${dir}\\\\_common\\\\pelvis_ref.skeleton`);\n }\n if (human) out.push('graphics\\\\characters\\\\_common\\\\pelvis_ref.skeleton');\n return out;\n}\n\n/** Меши ближнего LOD модели .xbg (с материалами .xbm из архива); matCache — общий кэш материалов (для уровней). */\nexport async function xbgMeshes(u: Uint8Array, arc: Archive | null, matCache?: Map<string, Promise<XbmMaterial | null>>, lodIndex = 0, parsed?: Xbg) {\n const x = parsed ?? parseXbg(u), lod = x.lods[Math.min(lodIndex, x.lods.length - 1)];\n if (!lod) throw new Error('XBG: нет LOD');\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n for (const b of lod.buffers) if (!LAYOUT[b.stride]) throw new Error(`XBG: вершина ${b.stride} байт (формат ${b.format.toString(16)}) пока не поддерживается`);\n const skinned = lod.buffers.some((b) => LAYOUT[b.stride]!.skin);\n const readMat = async (p: string) => {\n const e = arc?.lookup(p);\n if (!arc || !e) return null;\n try { return parseXbm(await arc.read(e)); } catch { return null; }\n };\n const mats = await Promise.all(x.materials.map((p) => {\n if (!matCache) return readMat(p);\n const k = p.toLowerCase();\n let r = matCache.get(k);\n if (!r) { r = readMat(p); matCache.set(k, r); }\n return r;\n }));\n const [po, ps] = x.pcmp, [uo, us] = x.ucmp;\n const meshes: Mesh[] = [];\n lod.prims.forEach((pr, k) => {\n const next = lod.prims[k + 1], end = next ? next.firstIndex : lod.icount;\n const cl = x.clusters[pr.part]?.[pr.cluster], v0 = pr.firstVertex, nv = pr.lastVertex - v0 + 1;\n const bf = lod.buffers[pr.buffer]!, L = LAYOUT[bf.stride]!;\n if (nv <= 0 || end <= pr.firstIndex) return;\n const pos = new Float32Array(nv * 3), nor = new Float32Array(nv * 3), uv = new Float32Array(nv * 2);\n const bone = new Float32Array(nv * 4), weight = new Float32Array(nv * 4);\n for (let i = 0; i < nv; i++) {\n const o = lod.vAt + bf.base + (v0 + i) * bf.stride;\n for (let c = 0; c < 3; c++) pos[i * 3 + c] = dv.getInt16(o + c * 2, true) * ps + po;\n uv[i * 2] = dv.getInt16(o + 8, true) * us + uo; uv[i * 2 + 1] = dv.getInt16(o + 10, true) * us + uo;\n if (L.normal >= 0) { const n = o + L.normal; nor[i * 3] = u[n + 2]! / 127.5 - 1; nor[i * 3 + 1] = u[n + 1]! / 127.5 - 1; nor[i * 3 + 2] = u[n]! / 127.5 - 1; }\n if (L.skin && cl) {\n let sum = 0;\n for (let b = 0; b < 4; b++) { const w = u[o + 16 + b]! / 255; weight[i * 4 + b] = w; sum += w; bone[i * 4 + b] = cl.palette[u[o + 20 + b]!] ?? 0; }\n if (sum > 0) for (let b = 0; b < 4; b++) weight[i * 4 + b]! /= sum; else weight[i * 4] = 1;\n } else weight[i * 4] = 1;\n }\n const cnt = end - pr.firstIndex, indices = nv > 65535 ? new Uint32Array(cnt) : new Uint16Array(cnt);\n for (let t = 0; t + 2 < cnt; t += 3) {\n const a = lod.iAt + (pr.firstIndex + t) * 2;\n indices[t] = dv.getUint16(a, true) - v0; indices[t + 1] = dv.getUint16(a + 4, true) - v0; indices[t + 2] = dv.getUint16(a + 2, true) - v0;\n }\n for (let t = 0; t < cnt; t++) if (indices[t]! >= nv) throw new Error('XBG: индекс вне вершин примитива');\n const normals = L.normal >= 0 ? nor : computeNormals(pos, indices);\n const part = x.parts[pr.part], matPath = cl ? x.materials[cl.mat] : undefined, mat = cl ? mats[cl.mat] : null;\n const partName = (part?.name ?? `part${pr.part}`).replace(/_LOD\\d+$/i, '');\n const tex = mat ? slotOf(mat.textures, 'DiffuseTexture1', 'SkinTexture', 'FabricTexture', 'DiffuseTexture', 'DiffuseTexture2') : undefined;\n const mesh: Mesh = {\n name: `${partName} · ${mat?.name ?? matPath?.split('\\\\').pop() ?? '?'}`,\n texture: tex ?? '', material: matPath, vertexCount: nv, pos, nor: normals, uv, bone, weight, indices, skinned: L.skin && !!cl,\n };\n if (mat) {\n const nm = slotOf(mat.textures, 'NormalTexture1', 'NormalTexture'), sp = slotOf(mat.textures, 'SpecularTexture1', 'SpecularTexture');\n if (nm) mesh.normalMap = nm;\n if (sp) mesh.surface = { specular: { map: sp, channel: 0 }, source: 'Far Cry 2 .xbm' };\n mesh.shader = mat.shader;\n if (ALPHA_SHADERS.has(mat.shader)) mesh.alphaTest = true;\n }\n // по умолчанию — целая машина днём: повреждённые состояния (…STATE02), ночные фары (…_N_…) и световые конусы скрыты\n if (/STATE0*[2-9]$/i.test(partName) || /_N_/i.test(partName) || /LIGHT(HALO|CONE|BEAM)/i.test(mat?.name ?? '')) mesh.defaultHidden = true;\n meshes.push(mesh);\n });\n if (!meshes.length) throw new Error('XBG: нет примитивов в ближнем LOD');\n return { x, lod, meshes, skinned, missing: x.materials.filter((_, i) => !mats[i]) };\n}\n\n/** Скиновые меши в bind-позе как статика (для уровней): вершины через матрицы скина узлов модели. */\nexport function bakeBindPose(x: Xbg, meshes: Mesh[]): Mesh[] {\n if (!x.nodes.length) return meshes;\n const sk = new NodeSkeleton(xbgSkeletonNodes(x.nodes, x.o2bm.length ? o2bmNodes(x).frame : undefined));\n sk.pose(null, 0, false);\n return meshes.map((m) => {\n if (!m.skinned) return m;\n const pos = new Float32Array(m.pos.length), nor = new Float32Array(m.nor.length);\n for (let v = 0; v < m.vertexCount; v++) {\n const px = m.pos[v * 3]!, py = m.pos[v * 3 + 1]!, pz = m.pos[v * 3 + 2]!, nx = m.nor[v * 3]!, ny = m.nor[v * 3 + 1]!, nz = m.nor[v * 3 + 2]!;\n let 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 if (!M) continue;\n ws += w;\n for (let c = 0; c < 3; c++) {\n pos[v * 3 + c]! += w * (M[c]! * px + M[4 + c]! * py + M[8 + c]! * pz + M[12 + c]!);\n nor[v * 3 + c]! += w * (M[c]! * nx + M[4 + c]! * ny + M[8 + c]! * nz);\n }\n }\n if (!ws) { pos[v * 3] = px; pos[v * 3 + 1] = py; pos[v * 3 + 2] = pz; nor[v * 3] = nx; nor[v * 3 + 1] = ny; nor[v * 3 + 2] = nz; }\n const l = Math.hypot(nor[v * 3]!, nor[v * 3 + 1]!, nor[v * 3 + 2]!) || 1;\n for (let c = 0; c < 3; c++) nor[v * 3 + c]! /= l;\n }\n return { ...m, pos, nor, skinned: false, bone: new Float32Array(0), weight: new Float32Array(0) };\n });\n}\n\nexport async function loadXbg(main: VFile, ctx: LoadContext): Promise<Model> {\n const arc = main.archive ?? ctx.archives.find((a) => a.kind === 'dunia-fat') ?? null;\n const { x, lod, meshes, skinned, missing } = await xbgMeshes(new Uint8Array(await main.arrayBuffer()), arc);\n const kitHidden = kitHiddenParts(meshes.map((m) => m.name.split(' · ')[0]!));\n for (const m of meshes) if (kitHidden.has(m.name.split(' · ')[0]!)) m.defaultHidden = true;\n let skeleton: NodeSkeleton | null = null, skel: { path: string; bones: DuniaSkelBone[] } | null = null;\n let frame: Float64Array | null = null;\n if (skinned && x.nodes.length) {\n frame = x.o2bm.length ? o2bmNodes(x).frame : null;\n let sn = xbgSkeletonNodes(x.nodes, frame ?? undefined);\n // скелет анимаций: иерархия вспомогательных костей и номера костей в .mab\n const mp = main.path ?? main.name;\n for (const c of arc ? skeletonCandidates(mp, x.nodes.some((n) => n.name === 'Pelvis')) : []) {\n const e = arc!.lookup(c);\n if (!e || !e.size) continue;\n try { skel = { path: c, bones: parseSkeleton(await arc!.read(e)) }; break; } catch { /* не скелет */ }\n }\n if (skel) {\n const bones = skel.bones;\n sn = reparentNodes(sn, new Map(bones.filter((b) => b.parent >= 0).map((b) => [b.name.toLowerCase(), bones[b.parent]!.name.toLowerCase()])));\n }\n skeleton = new NodeSkeleton(sn);\n }\n const info = [\n `Far Cry 2 (Dunia): ${main.name}`,\n `LOD: ${x.lods.length} (показан ближний, до ${lod.dist} м), примитивов ${lod.prims.length}, вершин ${lod.buffers.reduce((s2, b) => s2 + b.vcount, 0)}${skinned ? ', скин' : ''}`,\n `Узлов: ${x.nodes.length}, частей: ${new Set(lod.prims.map((p) => p.part)).size}, материалов: ${x.materials.length}`,\n ];\n if (kitHidden.size) info.push(`Кит персонажа: показан один вариант на слот, скрыто частей ${kitHidden.size} (включаются в списке мешей)`);\n if (missing.length) info.push(`Не найдены материалы: ${missing.slice(0, 3).map((m) => m.split('\\\\').pop()).join(', ')}${missing.length > 3 ? '…' : ''}`);\n if (skel) info.push(`Скелет анимаций: ${skel.path.split('\\\\').slice(-2).join('\\\\')} (${skel.bones.length} костей)`);\n const model: Model = { meshes, skeleton, motions: [], bounds: skeleton ? skinnedBounds(skeleton, meshes) : boundsOf(meshes), info, defaultZUp: true };\n if (skeleton && skel) attachMabMotions(model, skeleton, x, skel);\n return model;\n}\n\n/** Анимации .mab к модели: кости по именам из .skeleton, таз — из кадра анимации (Z вверх, земля) в пространство родителя-узла. */\nfunction attachMabMotions(model: Model, sk: NodeSkeleton, x: Xbg, skel: { path: string; bones: DuniaSkelBone[] }): void {\n const byName = new Map(x.nodes.map((n, i) => [n.name.toLowerCase(), i]));\n const bones = skel.bones.map((b) => byName.get(b.name.toLowerCase()) ?? -1);\n // мировые bind-позы узлов (по иерархии скелета)\n const world: Float64Array[] = [];\n sk.nodes.forEach((n, i) => { world[i] = n.parent >= 0 ? mulCol(world[n.parent]!, n.local) : Float64Array.from(n.local); });\n const fix = new Map<number, Float64Array>();\n let root: { bone: number; rot: number[]; pos: number[] } | undefined;\n skel.bones.forEach((b, i) => {\n const n = bones[i]!;\n if (b.parent >= 0 || n < 0) return;\n const p = sk.nodes[n]!.parent;\n if (p >= 0) fix.set(i, rigidInv(world[p]!));\n // поза корня в кадре анимации — из модели (у людей кость таза в .skeleton стоит в начале координат)\n if (!root) root = { bone: i, rot: matQuat(world[n]!), pos: [world[n]![12]!, world[n]![13]!, world[n]![14]!] };\n });\n model.meta = { ...model.meta, duniaSkeleton: skel.path };\n model.loadMotions = async (file) => {\n const u = new Uint8Array(await file.arrayBuffer());\n return [buildMabMotion(file.name.replace(/\\.mab$/i, ''), parseMab(u), bones, fix, root)];\n };\n}\n\n/** Анимации для модели (данные — при выборе): все .mab в папке скелета и глубже. */\nexport function listMabMotions(model: Model, archive: Archive): NodeMotion[] {\n const sp = model.meta?.duniaSkeleton as string | undefined;\n if (!sp || !model.loadMotions) return [];\n const dir = sp.replace(/\\\\/g, '/').toLowerCase().split('/').slice(0, -1).join('/') + '/';\n const out: NodeMotion[] = [];\n for (const e of archive.entries) {\n if (!e.path.endsWith('.mab') || !e.path.startsWith(dir)) continue;\n const rel = e.path.slice(dir.length).replace(/\\.mab$/, '');\n out.push({ kind: 'node', name: rel, tps: 30, chan: new Map(), duration: 0, load: async () => (await model.loadMotions!(archive.fileOf(e)))[0]! } as NodeMotion);\n }\n return out.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"xbg.js","sources":["../src/xbg.ts"],"sourcesContent":["// Dunia models (Far Cry 2): .xbg (geometry) and .xbm (material) share the \"HSEM\" (MESH) container:\n// u32 \"MESH\", u16 version 0x2a, u16, u32 hash, …, u32 [0x1c] root chunk count; a chunk is u32 tag (bytes reversed),\n// u32 1, u32 full size, u32 own data size, u32 child count; children come before their data.\n// .xbg chunks:\n// RMTL = materials: u32 n, u32 0, n × (u32 length, .xbm path, 0);\n// NODE = nodes: u32 n, u32 0, n × (i32 first child, i32 next, i32 parent, quaternion xyzw, position, scale,\n// i32, f32, f32, u32 length, name, 0, u32 hash);\n// SKID = parts: u32 n, n × (u32 hash, u16 LOD, u16 node); SKND = parts: u32 n, n × (f32 distance, sphere f32×4,\n// box f32×6, u32 LOD, u32, u32 length, name \"…_LOD0\", 0), a child CLUS per part: u32 k, k × cluster\n// (u16 material, u16 triangles ×2, u16 indices, u16 stride, u16 vertices, u16 format, u16×48 = nodes for vertex bones);\n// LODS = geometry: u32 LOD count, per LOD: f32 distance, u32 buffers, buffers (u32 format, u32 stride, u32 vertices,\n// u32 offset in the LOD vertex data), u32 primitive count, primitives of 7×u32 (buffer, part, cluster in the part,\n// first index, last vertex in the buffer, byte offset of the first vertex, 0), u32 vertex bytes +\n// alignment to 16 (in the file), vertices of all buffers, u32 indices + alignment, u16 indices (from the buffer start);\n// PCMP / UCMP = position / UV decompression: f32 offset, f32 multiplier (value = s16 · multiplier + offset).\n// 16-byte vertex (0x008a, grass): position, UV, color; normals are computed from triangles.\n// 32-byte vertex (format 0x0bca): position s16×3 + s16 (1), UV s16×2, UV2 s16×2, normal u8×4 (BGRA), color u8×4,\n// tangent, binormal. 40 bytes (0x0bda, skinned): position, UV, UV2, weights u8×4, bones u8×4 (cluster table index),\n// normal, color, tangent, binormal. Triangles are clockwise (D3D): the order is swapped on read.\n// .xbm material: the same container, DMTL chunk: 5 bytes, u32+name, u32+shader, u32 n, n × (u32+.xbt path, u32+slot), numbers.\n// Color slots by shader: Generic/Weapon/Vehicle/Hair/Leaf use DiffuseTexture1, Skin uses SkinTexture, Cloth uses FabricTexture.\n// Coordinate system: Z up, metres.\nimport { boundsOf, computeNormals, skinnedBounds } from '@gmvjs/core/geometry';\nimport { compose, mulCol, rigidInv } from '@gmvjs/core/math';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport { NodeSkeleton, type SkeletonNode } from '@gmvjs/core/skeleton';\nimport type { Archive, Mesh, Model, NodeMotion, VFile } from '@gmvjs/core/types';\nimport { buildMabMotion, matQuat, parseMab, parseSkeleton, type DuniaSkelBone } from './mab';\n\ninterface Chunk { tag: string; at: number; size: number; data: number; dsize: number; kids: Chunk[] }\n\nexport function readChunks(u: Uint8Array): Chunk[] {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 0x20 || dv.getUint32(0, true) !== 0x4d455348) throw new Error('Не модель Dunia (нет «MESH»)');\n const walk = (p: number, end: number, depth: number): Chunk[] => {\n const out: Chunk[] = [];\n while (p + 20 <= end) {\n const tag = String.fromCharCode(u[p + 3]!, u[p + 2]!, u[p + 1]!, u[p]!), size = dv.getUint32(p + 8, true), dsize = dv.getUint32(p + 12, true), nk = dv.getUint32(p + 16, true);\n if (size < 20 || p + size > end || dsize > size - 20 || depth > 8) throw new Error(`XBG: чанк ${tag} повреждён`);\n const data = p + size - dsize;\n out.push({ tag, at: p, size, data, dsize, kids: nk ? walk(p + 20, data, depth + 1) : [] });\n p += size;\n }\n return out;\n };\n return walk(0x20, u.length, 0);\n}\n\nclass R {\n readonly dv: DataView;\n constructor(readonly u: Uint8Array, public p: number) { this.dv = new DataView(u.buffer, u.byteOffset, u.byteLength); }\n u8() { return this.u[this.p++]!; }\n u16() { const v = this.dv.getUint16(this.p, true); this.p += 2; return v; }\n u32() { const v = this.dv.getUint32(this.p, true); this.p += 4; return v; }\n i32() { const v = this.dv.getInt32(this.p, true); this.p += 4; return v; }\n f32() { const v = this.dv.getFloat32(this.p, true); this.p += 4; return v; }\n str() {\n const n = this.u32();\n if (n > 4096 || this.p + n > this.u.length) throw new Error(`Dunia: строка длиной ${n} на ${this.p - 4}`);\n let s = ''; for (let i = 0; i < n; i++) s += String.fromCharCode(this.u[this.p + i]!); this.p += n + 1; return s;\n }\n align16() { this.p = (this.p + 15) & ~15; }\n}\n\nexport interface XbgNode { name: string; parent: number; rot: number[]; pos: number[]; scale: number[] }\nexport interface XbgCluster { mat: number; tris: number; palette: number[] }\nexport interface XbgPrim { buffer: number; part: number; cluster: number; firstIndex: number; lastVertex: number; firstVertex: number }\n/** LOD vertex buffer: format, stride, vertex count, offset in the LOD's shared vertex data. */\nexport interface XbgBuffer { format: number; stride: number; vcount: number; base: number }\nexport interface XbgLod { dist: number; buffers: XbgBuffer[]; prims: XbgPrim[]; vAt: number; iAt: number; icount: number }\nexport interface Xbg {\n materials: string[]; nodes: XbgNode[]; parts: Array<{ name: string; lod: number; node: number }>; clusters: XbgCluster[][];\n lods: XbgLod[]; pcmp: [number, number]; ucmp: [number, number]; u: Uint8Array;\n /** Animation bones (O2BM): inverse bind matrices (column-major) in bone order. */\n o2bm: Float64Array[];\n}\n\nexport function parseXbg(u: Uint8Array): Xbg {\n const chunks = readChunks(u);\n const find = (t: string, list = chunks): Chunk | undefined => { for (const c of list) { if (c.tag === t) return c; const k = find(t, c.kids); if (k) return k; } return undefined; };\n const x: Xbg = { materials: [], nodes: [], parts: [], clusters: [], lods: [], pcmp: [0, 1 / 32768], ucmp: [0, 1 / 32768], u, o2bm: [] };\n const rm = find('RMTL');\n if (rm) { const r = new R(u, rm.data), n = r.u32(); r.u32(); for (let i = 0; i < n; i++) x.materials.push(r.str()); }\n const nd = find('NODE');\n if (nd) {\n const r = new R(u, nd.data), n = r.u32(); r.u32();\n for (let i = 0; i < n; i++) {\n r.i32(); r.i32(); const parent = r.i32();\n const q = [r.f32(), r.f32(), r.f32(), r.f32()], pos = [r.f32(), r.f32(), r.f32()], scale = [r.f32(), r.f32(), r.f32()];\n r.i32(); r.f32(); r.f32();\n const name = r.str(); r.u32();\n x.nodes.push({ name, parent, rot: [q[3]!, q[0]!, q[1]!, q[2]!], pos, scale });\n }\n }\n const sk = find('SKND'), sid = find('SKID');\n const partNode: number[] = [];\n if (sid) { const r = new R(u, sid.data), n = r.u32(); for (let i = 0; i < n; i++) { r.u32(); r.u16(); const nd2 = r.u16(); partNode.push(nd2 === 0xffff ? -1 : nd2); } }\n if (sk) {\n const r = new R(u, sk.data), n = r.u32();\n for (let i = 0; i < n; i++) { r.p += 44; const lod = r.u32(); r.u32(); x.parts.push({ name: r.str(), lod, node: partNode[i] ?? -1 }); }\n const cl = sk.kids.find((k) => k.tag === 'CLUS');\n if (cl) {\n const r2 = new R(u, cl.data);\n for (let i = 0; i < n && r2.p < cl.data + cl.dsize; i++) {\n const k = r2.u32(), list: XbgCluster[] = [];\n for (let j = 0; j < k; j++) {\n const mat = r2.u16(), tris = r2.u16(); r2.p += 10;\n const palette: number[] = [];\n for (let b = 0; b < 48; b++) palette.push(r2.u16());\n list.push({ mat, tris, palette });\n }\n x.clusters.push(list);\n }\n }\n }\n const ld = find('LODS');\n if (!ld) throw new Error('XBG: нет геометрии (LODS)');\n {\n const r = new R(u, ld.data), n = r.u32();\n for (let l = 0; l < n; l++) {\n const dist = r.f32(), nb = r.u32(), buffers: XbgBuffer[] = [];\n for (let b = 0; b < nb; b++) buffers.push({ format: r.u32(), stride: r.u32(), vcount: r.u32(), base: r.u32() });\n const np = r.u32(), prims: XbgPrim[] = [];\n for (let i = 0; i < np; i++) {\n const buffer = r.u32(), part = r.u32(), cluster = r.u32(), firstIndex = r.u32(), lastVertex = r.u32(), voff = r.u32(); r.u32();\n const bf = buffers[buffer];\n if (!bf) throw new Error('XBG: примитив ссылается на несуществующий буфер');\n prims.push({ buffer, part, cluster, firstIndex, lastVertex, firstVertex: (voff - bf.base) / bf.stride });\n }\n const vb = r.u32(); r.align16(); const vAt = r.p; r.p += vb;\n const ic = r.u32(); r.align16(); const iAt = r.p; r.p += ic * 2;\n if (r.p > ld.data + ld.dsize || vb !== buffers.reduce((s2, b) => s2 + b.vcount * b.stride, 0)) throw new Error('XBG: буфер LOD повреждён');\n x.lods.push({ dist, buffers, prims, vAt, iAt, icount: ic });\n }\n }\n const ob = find('O2BM');\n if (ob && ob.dsize >= 4) {\n const dv2 = new DataView(u.buffer, u.byteOffset, u.byteLength), n = dv2.getUint32(ob.data, true);\n if (4 + n * 64 <= ob.dsize) for (let i = 0; i < n; i++) x.o2bm.push(Float64Array.from({ length: 16 }, (_, k) => dv2.getFloat32(ob.data + 4 + i * 64 + k * 4, true)));\n }\n const cmp = (t: string): [number, number] | null => { const c = find(t); if (!c || c.dsize < 8) return null; const dv = new DataView(u.buffer, u.byteOffset + c.data, 8); return [dv.getFloat32(0, true), dv.getFloat32(4, true)]; };\n x.pcmp = cmp('PCMP') ?? x.pcmp;\n x.ucmp = cmp('UCMP') ?? x.ucmp;\n return x;\n}\n\nexport interface XbmMaterial { name: string; shader: string; textures: Record<string, string> }\n\nexport function parseXbm(u: Uint8Array): XbmMaterial {\n const c = readChunks(u).find((k) => k.tag === 'DMTL');\n if (!c) throw new Error('XBM: нет чанка DMTL');\n const r = new R(u, c.data + 5);\n const name = r.str(), shader = r.str(), n = r.u32(), textures: Record<string, string> = {};\n for (let i = 0; i < n && i < 64; i++) { const path = r.str(), slot = r.str(); if (path) textures[slot] = path; }\n return { name, shader, textures };\n}\n\n/**\n * Character \"kits\" (merc_kit, multiplayer classes, female_civilian_kit): all swappable parts live in one model.\n * By default one part per slot is visible (a head with its eyes, a body of its race, hair, top, bottom, shoes; \"no hat\",\n * \"no glasses\", \"no gear\" where available), \"DUMMY…\" are hidden. Returns the names of the parts to hide.\n */\nexport function kitHiddenParts(names: readonly string[]): Set<string> {\n const parts = [...new Set(names)], hidden = new Set<string>();\n const heads = parts.filter((n) => /HEAD/.test(n) && !/EYES|ROOT/.test(n));\n if (heads.length < 2 && !parts.some((n) => /DUMMY/.test(n))) return hidden; // not a kit\n const key = (n: string) => n.replace(/^P_[A-Z]+_/, '').replace(/_LOD\\d+$/, '').replace(/_/g, '');\n const head = heads.find((n) => /CAUCASIANHEAD01/.test(n)) ?? heads[0];\n const race = head && /NUBIAN/.test(head) ? 'NUBIAN' : 'CAUCASIAN';\n const choose = (list: string[], prefer: RegExp[] = []) => {\n if (list.length < 2) return;\n let keep = list[0]!;\n for (const re of prefer) { const f = list.find((n) => re.test(n)); if (f) { keep = f; break; } }\n for (const n of list) if (n !== keep) hidden.add(n);\n };\n for (const n of parts) if (/DUMMY/.test(n)) hidden.add(n);\n for (const n of heads) if (n !== head) hidden.add(n);\n if (head) for (const n of parts.filter((x) => /EYES/.test(x))) if (key(n).replace(/^EYES/, '') !== key(head)) hidden.add(n);\n const rest = parts.filter((n) => !hidden.has(n) && !heads.includes(n) && !/EYES/.test(n));\n const slot = (re: RegExp, not?: RegExp) => rest.filter((n) => re.test(n) && !(not && not.test(n)));\n const top = slot(/_UB_|DRESS/); // TOP_BODY is the body with arms, not clothing\n choose(top);\n const topKept = top.find((n) => !hidden.has(n)) ?? '';\n const armsStyle = /BEATER/.test(topKept) ? 'BEATER' : /VEST/.test(topKept) ? 'LONGVEST' : 'SHIRT';\n // body of its own race; arms under the top\n for (const re of [/_ARMS$/, /BCHEST/, /_LEGS$/]) {\n const list = slot(re);\n choose(list, re.source === '_ARMS$' ? [new RegExp(`${race}_${armsStyle}_ARMS`), new RegExp(race)] : [new RegExp(race)]);\n }\n choose(slot(/HAIR/), [race === 'NUBIAN' ? /AFRO/ : /CAUC/]);\n choose(slot(/HAT|CAP\\d|BEANIE|BANDANA/), [/NOHAT/]);\n choose(slot(/GLASS|GOGGLE/), [/NOGLASS/]);\n choose(slot(/_LB_.*(PANT|JEANS|SKIRT)|LEGS_.*SKIRT/));\n choose(slot(/BOOT/));\n choose(slot(/GEAR|BACKPACK|STRAPS|LEATHER_VEST|TACTICAL_VEST|FLACK_JACKET|VESTSPORT/), [/NOGEAR/]);\n for (const j of ['BRACELET', 'NECKLACE']) choose(slot(new RegExp(j)));\n return hidden;\n}\n\n/** Vertex layout by stride: normal offset (−1 = none, computed from triangles), weights/bones from 16. */\nconst LAYOUT: Record<number, { normal: number; skin: boolean }> = { 16: { normal: -1, skin: false }, 32: { normal: 16, skin: false }, 40: { normal: 24, skin: true } };\n\n/** Foliage, grass and hair shaders: alpha cutout. */\nconst ALPHA_SHADERS = new Set(['Leaf', 'BigLeaf', 'Grass', 'Hair', 'Decal']);\n\nconst slotOf = (t: Record<string, string>, ...keys: string[]) => { for (const k of keys) if (t[k]) return t[k]; return undefined; };\n\n/**\n * Animation bones -> nodes. Bone i of the O2BM list is the node whose world pose matches the bone pose up to a common\n * transform T (for animals T = 1; for humans O2BM bones are relative to the pelvis at the origin, rotated 90° about Z).\n * T is found by trying nodes for bone 0. The list lacks \"…Nub\", the root and parts, so numbers differ from NODE.\n * Returns the nodes (−1 = not found) and T (animation frame -> model world).\n */\nexport function o2bmNodes(x: Xbg): { nodes: number[]; frame: Float64Array } {\n const N = xbgSkeletonNodes(x.nodes).map((s) => rigidInv(s.offset!));\n const O = x.o2bm.map((m) => rigidInv(m));\n const id: Float64Array = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);\n if (!O.length) return { nodes: [], frame: id };\n const dist = (a: ArrayLike<number>, b: ArrayLike<number>) => Math.hypot(a[12]! - b[12]!, a[13]! - b[13]!, a[14]! - b[14]!) + Math.hypot(...[0, 1, 2, 4, 5, 6, 8, 9, 10].map((k) => a[k]! - b[k]!));\n const match = (T: ArrayLike<number>, limit: number) => {\n const out: number[] = [];\n for (const o of O) {\n const w = mulCol(T, o);\n let bj = -1, bd = 0.02;\n N.forEach((n, j) => { const d = dist(n, w); if (d < bd) { bd = d; bj = j; } });\n out.push(bj);\n if (out.length >= limit && out.every((v) => v < 0)) break;\n }\n return out;\n };\n // fast path: the frames match\n let best: { n: number; nodes: number[]; frame: Float64Array } = { n: -1, nodes: [], frame: id };\n const direct = match(id, O.length);\n best = { n: direct.filter((j) => j >= 0).length, nodes: direct, frame: id };\n if (best.n < O.length) {\n N.forEach((n, c) => {\n const T = mulCol(n, rigidInv(O[0]!));\n const probe = match(T, 8);\n if (probe.length < O.length && probe.every((v) => v < 0)) return;\n const nodes = probe.length === O.length ? probe : match(T, O.length), k = nodes.filter((j) => j >= 0).length;\n if (k > best.n) best = { n: k, nodes, frame: T };\n });\n }\n return { nodes: best.nodes, frame: best.frame };\n}\n\n/**\n * Hierarchy as in the animation skeleton: helper bones (knees, elbows) hang from the pelvis in the model but from the\n * thigh/upper arm in .skeleton; without re-parenting they do not follow the limb. The bind pose is preserved. parentOf: parent name by bone name.\n */\nexport function reparentNodes(sn: SkeletonNode[], parentOf: ReadonlyMap<string, string>): SkeletonNode[] {\n const byName = new Map(sn.map((n, i) => [n.name.toLowerCase(), i]));\n const world: Float64Array[] = [];\n sn.forEach((n, i) => { world[i] = n.parent >= 0 ? mulCol(world[n.parent]!, n.local) : Float64Array.from(n.local); });\n return sn.map((n, i) => {\n const pn = parentOf.get(n.name.toLowerCase());\n const p = pn === undefined ? undefined : byName.get(pn);\n if (p === undefined || p === n.parent || p >= i) return n;\n return { ...n, parent: p, local: mulCol(rigidInv(world[p]!), world[i]!) };\n });\n}\n\n/**\n * Skeleton nodes: node poses are in parent space. frame: the vertex frame of skinned meshes; for humans vertices are written\n * in the O2BM bone frame (pelvis at the origin) while nodes are in the model frame; offset = (node world bind pose)⁻¹ · frame.\n */\nexport function xbgSkeletonNodes(nodes: XbgNode[], frame?: ArrayLike<number>): SkeletonNode[] {\n const local = nodes.map((n) => { const m = new Float64Array(16); compose(n.pos, n.rot, n.scale, m); return m; });\n const world: Float64Array[] = [];\n nodes.forEach((n, i) => { world[i] = n.parent >= 0 && n.parent < i ? mulCol(world[n.parent]!, local[i]!) : local[i]!; });\n return nodes.map((n, i) => ({ name: n.name, parent: n.parent >= 0 && n.parent < i ? n.parent : -1, local: local[i]!, offset: frame ? mulCol(rigidInv(world[i]!), frame) : rigidInv(world[i]!) }));\n}\n\n/**\n * The model's animation skeleton: \"<model name>_ref.skeleton\" next to it, then up the folders \"hips_ref.skeleton\",\n * \"pelvis_ref.skeleton\", \"_common\\\\pelvis_ref.skeleton\"; for humans outside graphics\\\\characters (actors), the shared human skeleton.\n */\nexport function skeletonCandidates(modelPath: string, human = false): string[] {\n const parts = modelPath.replace(/\\//g, '\\\\').toLowerCase().split('\\\\'), stem = parts[parts.length - 1]!.replace(/\\.xbg$/, '');\n const dirs = parts.slice(0, -1), out = [`${dirs.join('\\\\')}\\\\${stem}_ref.skeleton`];\n for (let n = dirs.length; n >= 2; n--) {\n const dir = dirs.slice(0, n).join('\\\\');\n out.push(`${dir}\\\\hips_ref.skeleton`, `${dir}\\\\pelvis_ref.skeleton`, `${dir}\\\\_common\\\\pelvis_ref.skeleton`);\n }\n if (human) out.push('graphics\\\\characters\\\\_common\\\\pelvis_ref.skeleton');\n return out;\n}\n\n/** Near-LOD meshes of an .xbg model (with .xbm materials from the archive); matCache is a shared material cache (for levels). */\nexport async function xbgMeshes(u: Uint8Array, arc: Archive | null, matCache?: Map<string, Promise<XbmMaterial | null>>, lodIndex = 0, parsed?: Xbg) {\n const x = parsed ?? parseXbg(u), lod = x.lods[Math.min(lodIndex, x.lods.length - 1)];\n if (!lod) throw new Error('XBG: нет LOD');\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n for (const b of lod.buffers) if (!LAYOUT[b.stride]) throw new Error(`XBG: вершина ${b.stride} байт (формат ${b.format.toString(16)}) пока не поддерживается`);\n const skinned = lod.buffers.some((b) => LAYOUT[b.stride]!.skin);\n const readMat = async (p: string) => {\n const e = arc?.lookup(p);\n if (!arc || !e) return null;\n try { return parseXbm(await arc.read(e)); } catch { return null; }\n };\n const mats = await Promise.all(x.materials.map((p) => {\n if (!matCache) return readMat(p);\n const k = p.toLowerCase();\n let r = matCache.get(k);\n if (!r) { r = readMat(p); matCache.set(k, r); }\n return r;\n }));\n const [po, ps] = x.pcmp, [uo, us] = x.ucmp;\n const meshes: Mesh[] = [];\n lod.prims.forEach((pr, k) => {\n const next = lod.prims[k + 1], end = next ? next.firstIndex : lod.icount;\n const cl = x.clusters[pr.part]?.[pr.cluster], v0 = pr.firstVertex, nv = pr.lastVertex - v0 + 1;\n const bf = lod.buffers[pr.buffer]!, L = LAYOUT[bf.stride]!;\n if (nv <= 0 || end <= pr.firstIndex) return;\n const pos = new Float32Array(nv * 3), nor = new Float32Array(nv * 3), uv = new Float32Array(nv * 2);\n const bone = new Float32Array(nv * 4), weight = new Float32Array(nv * 4);\n for (let i = 0; i < nv; i++) {\n const o = lod.vAt + bf.base + (v0 + i) * bf.stride;\n for (let c = 0; c < 3; c++) pos[i * 3 + c] = dv.getInt16(o + c * 2, true) * ps + po;\n uv[i * 2] = dv.getInt16(o + 8, true) * us + uo; uv[i * 2 + 1] = dv.getInt16(o + 10, true) * us + uo;\n if (L.normal >= 0) { const n = o + L.normal; nor[i * 3] = u[n + 2]! / 127.5 - 1; nor[i * 3 + 1] = u[n + 1]! / 127.5 - 1; nor[i * 3 + 2] = u[n]! / 127.5 - 1; }\n if (L.skin && cl) {\n let sum = 0;\n for (let b = 0; b < 4; b++) { const w = u[o + 16 + b]! / 255; weight[i * 4 + b] = w; sum += w; bone[i * 4 + b] = cl.palette[u[o + 20 + b]!] ?? 0; }\n if (sum > 0) for (let b = 0; b < 4; b++) weight[i * 4 + b]! /= sum; else weight[i * 4] = 1;\n } else weight[i * 4] = 1;\n }\n const cnt = end - pr.firstIndex, indices = nv > 65535 ? new Uint32Array(cnt) : new Uint16Array(cnt);\n for (let t = 0; t + 2 < cnt; t += 3) {\n const a = lod.iAt + (pr.firstIndex + t) * 2;\n indices[t] = dv.getUint16(a, true) - v0; indices[t + 1] = dv.getUint16(a + 4, true) - v0; indices[t + 2] = dv.getUint16(a + 2, true) - v0;\n }\n for (let t = 0; t < cnt; t++) if (indices[t]! >= nv) throw new Error('XBG: индекс вне вершин примитива');\n const normals = L.normal >= 0 ? nor : computeNormals(pos, indices);\n const part = x.parts[pr.part], matPath = cl ? x.materials[cl.mat] : undefined, mat = cl ? mats[cl.mat] : null;\n const partName = (part?.name ?? `part${pr.part}`).replace(/_LOD\\d+$/i, '');\n const tex = mat ? slotOf(mat.textures, 'DiffuseTexture1', 'SkinTexture', 'FabricTexture', 'DiffuseTexture', 'DiffuseTexture2') : undefined;\n const mesh: Mesh = {\n name: `${partName} · ${mat?.name ?? matPath?.split('\\\\').pop() ?? '?'}`,\n texture: tex ?? '', material: matPath, vertexCount: nv, pos, nor: normals, uv, bone, weight, indices, skinned: L.skin && !!cl,\n };\n if (mat) {\n const nm = slotOf(mat.textures, 'NormalTexture1', 'NormalTexture'), sp = slotOf(mat.textures, 'SpecularTexture1', 'SpecularTexture');\n if (nm) mesh.normalMap = nm;\n if (sp) mesh.surface = { specular: { map: sp, channel: 0 }, source: 'Far Cry 2 .xbm' };\n mesh.shader = mat.shader;\n if (ALPHA_SHADERS.has(mat.shader)) mesh.alphaTest = true;\n }\n // by default the whole car by day: damage states (…STATE02), night headlights (…_N_…) and light cones are hidden\n if (/STATE0*[2-9]$/i.test(partName) || /_N_/i.test(partName) || /LIGHT(HALO|CONE|BEAM)/i.test(mat?.name ?? '')) mesh.defaultHidden = true;\n meshes.push(mesh);\n });\n if (!meshes.length) throw new Error('XBG: нет примитивов в ближнем LOD');\n return { x, lod, meshes, skinned, missing: x.materials.filter((_, i) => !mats[i]) };\n}\n\n/** Skinned meshes in the bind pose as static geometry (for levels): vertices via the model nodes' skin matrices. */\nexport function bakeBindPose(x: Xbg, meshes: Mesh[]): Mesh[] {\n if (!x.nodes.length) return meshes;\n const sk = new NodeSkeleton(xbgSkeletonNodes(x.nodes, x.o2bm.length ? o2bmNodes(x).frame : undefined));\n sk.pose(null, 0, false);\n return meshes.map((m) => {\n if (!m.skinned) return m;\n const pos = new Float32Array(m.pos.length), nor = new Float32Array(m.nor.length);\n for (let v = 0; v < m.vertexCount; v++) {\n const px = m.pos[v * 3]!, py = m.pos[v * 3 + 1]!, pz = m.pos[v * 3 + 2]!, nx = m.nor[v * 3]!, ny = m.nor[v * 3 + 1]!, nz = m.nor[v * 3 + 2]!;\n let 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 if (!M) continue;\n ws += w;\n for (let c = 0; c < 3; c++) {\n pos[v * 3 + c]! += w * (M[c]! * px + M[4 + c]! * py + M[8 + c]! * pz + M[12 + c]!);\n nor[v * 3 + c]! += w * (M[c]! * nx + M[4 + c]! * ny + M[8 + c]! * nz);\n }\n }\n if (!ws) { pos[v * 3] = px; pos[v * 3 + 1] = py; pos[v * 3 + 2] = pz; nor[v * 3] = nx; nor[v * 3 + 1] = ny; nor[v * 3 + 2] = nz; }\n const l = Math.hypot(nor[v * 3]!, nor[v * 3 + 1]!, nor[v * 3 + 2]!) || 1;\n for (let c = 0; c < 3; c++) nor[v * 3 + c]! /= l;\n }\n return { ...m, pos, nor, skinned: false, bone: new Float32Array(0), weight: new Float32Array(0) };\n });\n}\n\nexport async function loadXbg(main: VFile, ctx: LoadContext): Promise<Model> {\n const arc = main.archive ?? ctx.archives.find((a) => a.kind === 'dunia-fat') ?? null;\n const { x, lod, meshes, skinned, missing } = await xbgMeshes(new Uint8Array(await main.arrayBuffer()), arc);\n const kitHidden = kitHiddenParts(meshes.map((m) => m.name.split(' · ')[0]!));\n for (const m of meshes) if (kitHidden.has(m.name.split(' · ')[0]!)) m.defaultHidden = true;\n let skeleton: NodeSkeleton | null = null, skel: { path: string; bones: DuniaSkelBone[] } | null = null;\n let frame: Float64Array | null = null;\n if (skinned && x.nodes.length) {\n frame = x.o2bm.length ? o2bmNodes(x).frame : null;\n let sn = xbgSkeletonNodes(x.nodes, frame ?? undefined);\n // animation skeleton: helper bone hierarchy and bone numbers in .mab\n const mp = main.path ?? main.name;\n for (const c of arc ? skeletonCandidates(mp, x.nodes.some((n) => n.name === 'Pelvis')) : []) {\n const e = arc!.lookup(c);\n if (!e || !e.size) continue;\n try { skel = { path: c, bones: parseSkeleton(await arc!.read(e)) }; break; } catch { /* not a skeleton */ }\n }\n if (skel) {\n const bones = skel.bones;\n sn = reparentNodes(sn, new Map(bones.filter((b) => b.parent >= 0).map((b) => [b.name.toLowerCase(), bones[b.parent]!.name.toLowerCase()])));\n }\n skeleton = new NodeSkeleton(sn);\n }\n const info = [\n `Far Cry 2 (Dunia): ${main.name}`,\n `LOD: ${x.lods.length} (показан ближний, до ${lod.dist} м), примитивов ${lod.prims.length}, вершин ${lod.buffers.reduce((s2, b) => s2 + b.vcount, 0)}${skinned ? ', скин' : ''}`,\n `Узлов: ${x.nodes.length}, частей: ${new Set(lod.prims.map((p) => p.part)).size}, материалов: ${x.materials.length}`,\n ];\n if (kitHidden.size) info.push(`Кит персонажа: показан один вариант на слот, скрыто частей ${kitHidden.size} (включаются в списке мешей)`);\n if (missing.length) info.push(`Не найдены материалы: ${missing.slice(0, 3).map((m) => m.split('\\\\').pop()).join(', ')}${missing.length > 3 ? '…' : ''}`);\n if (skel) info.push(`Скелет анимаций: ${skel.path.split('\\\\').slice(-2).join('\\\\')} (${skel.bones.length} костей)`);\n const model: Model = { meshes, skeleton, motions: [], bounds: skeleton ? skinnedBounds(skeleton, meshes) : boundsOf(meshes), info, defaultZUp: true };\n if (skeleton && skel) attachMabMotions(model, skeleton, x, skel);\n return model;\n}\n\n/** .mab animations for the model: bones by name from .skeleton, the pelvis mapped from the animation frame (Z up, ground) into the parent node's space. */\nfunction attachMabMotions(model: Model, sk: NodeSkeleton, x: Xbg, skel: { path: string; bones: DuniaSkelBone[] }): void {\n const byName = new Map(x.nodes.map((n, i) => [n.name.toLowerCase(), i]));\n const bones = skel.bones.map((b) => byName.get(b.name.toLowerCase()) ?? -1);\n // node world bind poses (by the skeleton hierarchy)\n const world: Float64Array[] = [];\n sk.nodes.forEach((n, i) => { world[i] = n.parent >= 0 ? mulCol(world[n.parent]!, n.local) : Float64Array.from(n.local); });\n const fix = new Map<number, Float64Array>();\n let root: { bone: number; rot: number[]; pos: number[] } | undefined;\n skel.bones.forEach((b, i) => {\n const n = bones[i]!;\n if (b.parent >= 0 || n < 0) return;\n const p = sk.nodes[n]!.parent;\n if (p >= 0) fix.set(i, rigidInv(world[p]!));\n // root pose in the animation frame comes from the model (for humans the pelvis bone in .skeleton sits at the origin)\n if (!root) root = { bone: i, rot: matQuat(world[n]!), pos: [world[n]![12]!, world[n]![13]!, world[n]![14]!] };\n });\n model.meta = { ...model.meta, duniaSkeleton: skel.path };\n model.loadMotions = async (file) => {\n const u = new Uint8Array(await file.arrayBuffer());\n return [buildMabMotion(file.name.replace(/\\.mab$/i, ''), parseMab(u), bones, fix, root)];\n };\n}\n\n/** Animations for the model (data on selection): every .mab in the skeleton folder and below. */\nexport function listMabMotions(model: Model, archive: Archive): NodeMotion[] {\n const sp = model.meta?.duniaSkeleton as string | undefined;\n if (!sp || !model.loadMotions) return [];\n const dir = sp.replace(/\\\\/g, '/').toLowerCase().split('/').slice(0, -1).join('/') + '/';\n const out: NodeMotion[] = [];\n for (const e of archive.entries) {\n if (!e.path.endsWith('.mab') || !e.path.startsWith(dir)) continue;\n const rel = e.path.slice(dir.length).replace(/\\.mab$/, '');\n out.push({ kind: 'node', name: rel, tps: 30, chan: new Map(), duration: 0, load: async () => (await model.loadMotions!(archive.fileOf(e)))[0]! } as NodeMotion);\n }\n return out.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n"],"names":[],"mappings":"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"}
|
package/dist/xbt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xbt.js","sources":["../src/xbt.ts"],"sourcesContent":["//
|
|
1
|
+
{"version":3,"file":"xbt.js","sources":["../src/xbt.ts"],"sourcesContent":["// Dunia (Far Cry 2) .xbt textures: \"TBX\\0\", u32 version 0x0b, u32 DDS offset (header size), u32 2, 12 bytes,\n// the path \"…_mip0.xbt\" (if the top level lives in a separate file), then a regular DDS (DXT1/DXT5, mips).\nimport { decodeDDS } from '@gmvjs/core/images/dds';\nimport type { DecodedImage } from '@gmvjs/core/types';\n\nexport function xbtDds(buf: ArrayBuffer): ArrayBuffer {\n const dv = new DataView(buf);\n if (buf.byteLength < 0x20 || dv.getUint32(0, true) !== 0x00584254) throw new Error('Не текстура Dunia (нет «TBX»)');\n const off = dv.getUint32(8, true);\n if (off + 128 > buf.byteLength || dv.getUint32(off, true) !== 0x20534444) throw new Error('XBT: нет DDS после заголовка');\n return buf.slice(off);\n}\n\nexport const decodeXbt = (buf: ArrayBuffer): DecodedImage => decodeDDS(xbtDds(buf));\n"],"names":[],"mappings":";AAKO,SAAS,OAAO,KAA+B;AACpD,QAAM,KAAK,IAAI,SAAS,GAAG;AAC3B,MAAI,IAAI,aAAa,MAAQ,GAAG,UAAU,GAAG,IAAI,MAAM,QAAY,OAAM,IAAI,MAAM,+BAA+B;AAClH,QAAM,MAAM,GAAG,UAAU,GAAG,IAAI;AAChC,MAAI,MAAM,MAAM,IAAI,cAAc,GAAG,UAAU,KAAK,IAAI,MAAM,UAAY,OAAM,IAAI,MAAM,8BAA8B;AACxH,SAAO,IAAI,MAAM,GAAG;AACtB;AAEO,MAAM,YAAY,CAAC,QAAmC,UAAU,OAAO,GAAG,CAAC;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmvjs/dunia1",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "gmv: Dunia Engine 1 (Far Cry 2)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gmv",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"build": "node ../../scripts/build-package.mjs"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@gmvjs/codecs": "^0.1.
|
|
48
|
-
"@gmvjs/core": "^0.1.
|
|
47
|
+
"@gmvjs/codecs": "^0.1.1",
|
|
48
|
+
"@gmvjs/core": "^0.1.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@gmvjs/test-utils": "^0.1.0"
|