@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/level.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"level.js","sources":["../src/level.ts"],"sourcesContent":["// Уровни Far Cry 2 (Dunia). Мир — worlds/<мир>: сетка секторов 64×64 м (большие миры — 80×80 секторов, 25 карт по 16×16),\n// generated/<мир>.game.xml — описание (сетка, карты, слои ландшафта), generated/entitylibrary.fcb — архетипы объектов.\n// Имён у файлов секторов в архиве нет (не находятся по хешу), поэтому они узнаются по содержимому среди файлов мира\n// (участок .dat до <мир>.sectorsdep.fcb, см. fat.ts):\n// ландшафт сектора — «R\\x10\\x00\\xE9», u32 7, u32 размер, …, u32 номер сектора (0x14; x = номер % ширина, y = номер / ширина),\n// f32 мин./макс. высоты (0x1C, 0x20), 4 байта — номера слоёв ландшафта (0x24, по списку Layers в game.xml),\n// f32 уровень воды сектора (0x3C; 0 — воды нет; материал воды — путь .mlm с 0x44),\n// с 0x250 — 65×65 вершин по 4 байта (строки по Y, шаг 1 м): u16 высота·128, u8 освещённость, u8 маска слоёв\n// (бит k младшей тетрады — слой слота k в вершине; 255 — слот пуст): нижний занятый слот — основа, слоты выше\n// накладываются с прозрачностью по вершинам (переход между слоями — на одну клетку);\n// объекты сектора — .fcb с корнем WorldSector (Id, X, Y): слои миссий → Entity (hidPos, hidAngles в градусах;\n// модель — значение 0xBF9B3A5C компонента CGraphicComponent или его object; hidMeshName — список видимых частей кита;\n// у объектов-архетипов (0x346AAB33 = «OA_Buildings.Buildings.ShantyShelter01») — модель из entitylibrary.fcb по hidName).\nimport { boundsOf } from '@gmvjs/core/geometry';\nimport { LevelBuilder, markerMesh } from '@gmvjs/core/levelbuilder';\nimport { compose } from '@gmvjs/core/math';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport type { Archive, Mesh, Model, VFile } from '@gmvjs/core/types';\nimport { duniaApi, LEVEL_MARK, type DuniaArchiveApi, type DuniaWorld, type FatRef, type LevelSpec } from './fat';\nimport { fcbFind, fcbHash, fcbString, fcbVec3, parseFcb, type FcbObject } from './fcb';\nimport { xmlElements } from './rml';\nimport { isRealTree, parseRealTree, realTreeMeshes, type RealTree } from './realtree';\nimport { bakeBindPose, kitHiddenParts, parseXbg, parseXbm, xbgMeshes, type Xbg, type XbmMaterial } from './xbg';\n\nconst SECTOR = 64, GRID = 65, TER_MAGIC = 0xe9001052;\n/** Бюджет треугольников объектов карты (сверх — дальние LOD у самых тяжёлых моделей). */\nconst TRI_BUDGET = 4_000_000;\n/** Малый мир (сетевые карты): файлов в блоке ландшафта меньше — объекты ищутся по всему миру. */\nconst SMALL_WORLD = 12_000;\n/** Коллекции: уровень LOD и бюджет треугольников (сверх — равномерное прореживание). */\nconst COLLECTION_LOD = 1, COLLECTION_BUDGET = 3_000_000;\n/** Детерминированный случайный поток (mulberry32): прореживание одинаково при каждом открытии. */\nfunction rngOf(seed: number): () => number {\n let a = seed >>> 0;\n return () => { a = (a + 0x6d2b79f5) >>> 0; let t = a; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };\n}\nconst H = {\n Entity: fcbHash('Entity'), WorldSector: fcbHash('WorldSector'), hidName: fcbHash('hidName'), hidPos: fcbHash('hidPos'), hidAngles: fcbHash('hidAngles'),\n hidMeshName: fcbHash('hidMeshName'), X: fcbHash('X'), Y: fcbHash('Y'), model: 0xbf9b3a5c, archetype: 0x346aab33, layer: 0x494c09f2, layerName: 0xc56f9204,\n};\n\nexport interface TerrainLayer { name: string; texture: string; normal: string; tiling: number }\n\n/** Слои ландшафта и сетка мира из game.xml. */\nexport function worldDescriptor(u: Uint8Array): { layers: TerrainLayer[]; countX: number; countY: number } {\n const layers: TerrainLayer[] = [];\n // ширина мира в секторах = секторов на карту × карт (GridMapSectors × GridWorldMaps)\n let sx = 0, sy = 0, mx = 1, my = 1, inLayers = -1;\n for (const el of xmlElements(u)) {\n if (el.name === 'GridMapSectors' && !sx) { sx = +el.attrs.CountX! || 0; sy = +el.attrs.CountY! || 0; }\n if (el.name === 'GridWorldMaps') { mx = +el.attrs.CountX! || 1; my = +el.attrs.CountY! || 1; }\n if (el.name === 'Layers') { inLayers = el.depth; continue; }\n if (inLayers >= 0 && el.depth <= inLayers) inLayers = -1;\n if (inLayers >= 0 && el.depth === inLayers + 1 && el.name === 'Layer') {\n layers.push({ name: (el.attrs.Name ?? '').trim(), texture: el.attrs.Texture ?? '', normal: el.attrs.NormalMap ?? '', tiling: +el.attrs.Tiling! || 20 });\n }\n }\n return { layers, countX: sx * mx, countY: sy * my };\n}\n\nexport interface TerrainSector { id: number; min: number; max: number; layers: number[]; height: Float32Array; mask: Uint8Array; water: number }\n\n/** Ландшафт сектора (см. заголовок файла). */\nexport function parseTerrainSector(u: Uint8Array): TerrainSector {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 0x250 + GRID * GRID * 4 || dv.getUint32(0, true) !== TER_MAGIC) throw new Error('Не ландшафт сектора Far Cry 2');\n const height = new Float32Array(GRID * GRID), mask = new Uint8Array(GRID * GRID);\n for (let i = 0; i < GRID * GRID; i++) { height[i] = dv.getUint16(0x250 + i * 4, true) / 128; mask[i] = u[0x250 + i * 4 + 3]!; }\n const water = dv.getFloat32(0x3c, true);\n return { id: dv.getUint32(0x14, true), min: dv.getFloat32(0x1c, true), max: dv.getFloat32(0x20, true), layers: [u[0x24]!, u[0x25]!, u[0x26]!, u[0x27]!], height, mask, water: Number.isFinite(water) && water > 0 ? water : 0 };\n}\n\n/**\n * Слой вершины: старший установленный бит младшей тетрады маски, чей слот занят (верхний нарисованный слой);\n * пустой слот — 255; без бит (край сектора) — первый занятый слот.\n */\nexport function vertexLayer(mask: number, slots: readonly number[]): number {\n for (let k = 3; k >= 0; k--) if (mask & (1 << k) && slots[k] !== 255) return slots[k]!;\n for (const s of slots) if (s !== 255) return s;\n return -1;\n}\n\n/**\n * Углы объекта (градусы) -> кватернион [w, x, y, z]: R = Rz(z)·Ry(y)·Rx(x) (сначала X, затем Y, затем Z — рыскание).\n * Проверено по объектам на склонах: ось «вверх» объекта совпадает с нормалью земли (см. тест уровней).\n */\nexport function anglesQuat(a: readonly number[]): number[] {\n const h = (d: number) => (d * Math.PI) / 360;\n const qx = [Math.cos(h(a[0]!)), Math.sin(h(a[0]!)), 0, 0], qy = [Math.cos(h(a[1]!)), 0, Math.sin(h(a[1]!)), 0], qz = [Math.cos(h(a[2]!)), 0, 0, Math.sin(h(a[2]!))];\n return qmul(qmul(qz, qy), qx);\n}\nconst qmul = (a: number[], b: number[]) => [\n a[0]! * b[0]! - a[1]! * b[1]! - a[2]! * b[2]! - a[3]! * b[3]!,\n a[0]! * b[1]! + a[1]! * b[0]! + a[2]! * b[3]! - a[3]! * b[2]!,\n a[0]! * b[2]! - a[1]! * b[3]! + a[2]! * b[0]! + a[3]! * b[1]!,\n a[0]! * b[3]! + a[1]! * b[2]! - a[2]! * b[1]! + a[3]! * b[0]!,\n];\n\n/** Объект уровня: модель, видимые части (пусто — все), поза. */\nexport interface LevelEntity { name: string; model: string; parts: string[]; pos: number[]; angles: number[]; layer: string; archetype?: string }\n\nconst modelOf = (e: FcbObject): string | undefined => {\n const walk = (o: FcbObject, d: number): string | undefined => {\n const v = fcbString(o.values.get(H.model));\n if (v) return v;\n if (d > 6) return undefined;\n for (const c of o.children) { if (c.type === H.Entity) continue; const r = walk(c, d + 1); if (r) return r; }\n return undefined;\n };\n return walk(e, 0);\n};\nconst meshNamesOf = (e: FcbObject): string[] => {\n for (const o of fcbFind(e, fcbHash('object'))) { const s = fcbString(o.values.get(H.hidMeshName)); if (s && s.includes(';')) return s.split(';').filter(Boolean); }\n return [];\n};\n\n/** Архетипы entitylibrary.fcb: hidName -> модель и части. */\nexport function entityLibrary(root: FcbObject): Map<string, { model: string; parts: string[] }> {\n const out = new Map<string, { model: string; parts: string[] }>();\n for (const e of fcbFind(root, H.Entity)) {\n const n = fcbString(e.values.get(H.hidName)), m = modelOf(e);\n if (n && m && !out.has(n)) out.set(n, { model: m, parts: meshNamesOf(e) });\n }\n return out;\n}\n\n/** Объекты сектора (WorldSector) с моделями; архетипы — по библиотеке. */\nexport interface LevelMarker { kind: 'particle' | 'light'; name: string; pos: number[] }\nconst PARTICLES = fcbHash('CNewParticlesComponent'), LIGHT = fcbHash('CDynamicLightComponent'), PARTICLE_NAME = 0x1943b130;\n\nexport function sectorEntities(root: FcbObject, lib: Map<string, { model: string; parts: string[] }>): { list: LevelEntity[]; noModel: number; unknownArchetype: Set<string>; markers: LevelMarker[] } {\n const list: LevelEntity[] = [], unknownArchetype = new Set<string>(), markers: LevelMarker[] = [];\n let noModel = 0;\n const visit = (o: FcbObject, layer: string) => {\n if (o.type === H.layer) layer = fcbString(o.values.get(H.layerName)) ?? layer;\n if (o.type === H.Entity) {\n const pos = fcbVec3(o.values.get(H.hidPos));\n const arch = fcbString(o.values.get(H.archetype));\n let model = modelOf(o), parts = meshNamesOf(o);\n if (!model && arch) { const L = lib.get(arch); if (L) { model = L.model; if (!parts.length) parts = L.parts; } else unknownArchetype.add(arch); }\n if (pos && model) list.push({ name: fcbString(o.values.get(H.hidName)) ?? arch ?? 'entity', model, parts, pos, angles: fcbVec3(o.values.get(H.hidAngles)) ?? [0, 0, 0], layer, ...(arch ? { archetype: arch } : {}) });\n else if (pos) {\n noModel++;\n // без модели: метки частиц (имя эффекта) и динамического света\n const name = fcbString(o.values.get(H.hidName)) ?? 'entity';\n const pc = fcbFind(o, PARTICLES)[0];\n if (pc) markers.push({ kind: 'particle', name: fcbString(pc.values.get(PARTICLE_NAME)) ?? name, pos });\n else if (fcbFind(o, LIGHT).length) markers.push({ kind: 'light', name, pos });\n }\n }\n for (const c of o.children) visit(c, layer);\n };\n visit(root, 'main');\n return { list, noModel, unknownArchetype, markers };\n}\n\n/**\n * Коллекции сектора (CCollectionComponent у CSectorEntity — камни, мусор, растительность, расставленные кистью редактора):\n * 0x114CFEE7 — набор (рамка 0x17E7C62E–0x2BEAF977), его данные 0xA18C8484: 0x89C8C095 — хеши путей моделей (как имена\n * в .fat), 0x58AD43E2 — число групп каждой модели, 0x798C33D5 — группы (u8 число экземпляров, далее номер первого),\n * 0xB0484974 — центры и радиусы групп; экземпляры: 0xD28100C0 — u16 x, u16 y (дециметры), 0x8E2752CA — f32 z,\n * 0x98C678BD — u32 (младшие 16 бит — поворот вокруг Z, полный круг = 65536; старшие — не разобраны), 0x9D1CAE67 — цвета.\n * Все массивы — u32 число, затем элементы.\n */\nexport function sectorCollections(root: FcbObject): Array<{ hash: number; pos: number[]; yaw: number }> {\n const out: Array<{ hash: number; pos: number[]; yaw: number }> = [];\n for (const set of fcbFind(root, 0x114cfee7)) {\n const d = set.children.find((c) => c.type === 0xa18c8484);\n if (!d) continue;\n const arr = (k: number) => { const v = d.values.get(k); if (!v || v.length < 4) return null; const dv = new DataView(v.buffer, v.byteOffset, v.byteLength); const n = dv.getUint32(0, true); return n * 4 + 4 <= v.length ? { dv, n } : null; };\n const models = arr(0x89c8c095), counts = arr(0x58ad43e2), groups = arr(0x798c33d5), xy = arr(0xd28100c0), zs = arr(0x8e2752ca), rot = arr(0x98c678bd);\n if (!models || !counts || !groups || !xy || !zs || counts.n !== models.n) continue;\n let g = 0;\n for (let m = 0; m < models.n; m++) {\n const hash = models.dv.getUint32(4 + m * 4, true), ng = counts.dv.getUint32(4 + m * 4, true);\n for (let k = 0; k < ng && g < groups.n; k++, g++) {\n const gv = groups.dv.getUint32(4 + g * 4, true), cnt = gv & 0xff, first = gv >>> 8;\n for (let i = first; i < first + cnt && i < xy.n && i < zs.n; i++) {\n const x = xy.dv.getUint16(4 + i * 4, true) / 10, y = xy.dv.getUint16(6 + i * 4, true) / 10, z = zs.dv.getFloat32(4 + i * 4, true);\n const yaw = rot && i < rot.n ? ((rot.dv.getUint32(4 + i * 4, true) & 0xffff) / 65536) * 360 : 0;\n out.push({ hash, pos: [x, y, z], yaw });\n }\n }\n }\n }\n return out;\n}\n\n/** Чтение многих записей параллельно (по n за раз). */\nasync function mapLimit<T, R>(items: T[], n: number, f: (x: T) => Promise<R>): Promise<R[]> {\n const out: R[] = new Array(items.length);\n let k = 0;\n await Promise.all(Array.from({ length: Math.min(n, items.length) }, async () => { while (k < items.length) { const i = k++; out[i] = await f(items[i]!); } }));\n return out;\n}\n\n/** Шаг сетки ландшафта (м): степень двойки, чтобы вершин было не больше бюджета. */\nexport const terrainStep = (w: number, h: number, budget = 520_000) => {\n let s = 1;\n while ((Math.floor((w * SECTOR) / s) + 1) * (Math.floor((h * SECTOR) / s) + 1) > budget && s < SECTOR) s *= 2;\n return s;\n};\n\n/** Меши ландшафта области: по мешу на слой (треугольник — по слою его угла), UV — плитка слоя (Tiling повторов на сектор). */\nexport function terrainMeshes(spec: { x0: number; y0: number; w: number; h: number }, sectors: Map<number, TerrainSector>, widthInSectors: number, layers: TerrainLayer[], step: number): Mesh[] {\n const NX = (spec.w * SECTOR) / step + 1, NY = (spec.h * SECTOR) / step + 1, n = NX * NY;\n // по вершине: высота, слой основы и слои слотов 1..3 сектора с битом маски (-1 — слоя нет)\n const hgt = new Float32Array(n).fill(NaN), base = new Int16Array(n).fill(-1), over = [1, 2, 3].map(() => new Int16Array(n).fill(-1));\n for (let gy = 0; gy < NY; gy++) {\n for (let gx = 0; gx < NX; gx++) {\n const mx = gx * step, my = gy * step; // метры от угла области\n let sx = Math.floor(mx / SECTOR), sy = Math.floor(my / SECTOR);\n if (sx >= spec.w) sx = spec.w - 1;\n if (sy >= spec.h) sy = spec.h - 1;\n const s = sectors.get((spec.y0 + sy) * widthInSectors + spec.x0 + sx);\n if (!s) continue;\n const lx = mx - sx * SECTOR, ly = my - sy * SECTOR, k = ly * GRID + lx, g = gy * NX + gx, m = s.mask[k]!;\n hgt[g] = s.height[k]!;\n // основа — нижний занятый слот; выше — слоты 1..3, где стоит бит маски (наложение с прозрачностью по вершинам)\n const b0 = s.layers.findIndex((l) => l !== 255);\n base[g] = b0 >= 0 ? s.layers[b0]! : -1;\n for (let j = 1; j <= 3; j++) if (j > b0 && m & (1 << j) && s.layers[j] !== 255) over[j - 1]![g] = s.layers[j]!;\n }\n }\n // нормали по соседям\n const nor = new Float32Array(n * 3);\n const at = (x: number, y: number, d: number) => { const v = hgt[Math.min(NY - 1, Math.max(0, y)) * NX + Math.min(NX - 1, Math.max(0, x))]!; return Number.isNaN(v) ? d : v; };\n for (let y = 0; y < NY; y++) for (let x = 0; x < NX; x++) {\n const c = hgt[y * NX + x]!; if (Number.isNaN(c)) continue;\n const dx = (at(x + 1, y, c) - at(x - 1, y, c)) / (2 * step), dy = (at(x, y + 1, c) - at(x, y - 1, c)) / (2 * step), l = Math.hypot(dx, dy, 1);\n nor[(y * NX + x) * 3] = -dx / l; nor[(y * NX + x) * 3 + 1] = -dy / l; nor[(y * NX + x) * 3 + 2] = 1 / l;\n }\n const meshes: Mesh[] = [], local = new Int32Array(n);\n const ox = spec.x0 * SECTOR, oy = spec.y0 * SECTOR;\n const valid = (g: number) => !Number.isNaN(hgt[g]!) && !Number.isNaN(hgt[g + 1]!) && !Number.isNaN(hgt[g + NX]!) && !Number.isNaN(hgt[g + NX + 1]!);\n /** Меш слоя L по клеткам take(g); alpha — прозрачность вершины (слой поверх основы). */\n const emit = (L: number, name: string, take: (g: number) => boolean, alpha?: (g: number) => number) => {\n local.fill(-1);\n const P: number[] = [], U: number[] = [], N: number[] = [], A: number[] = [], I: number[] = [];\n const layer = layers[L], rep = (layer?.tiling ?? 20) / SECTOR;\n const vi = (x: number, y: number) => {\n const g = y * NX + x;\n let v = local[g]!;\n if (v < 0) {\n v = local[g] = P.length / 3;\n const wx = ox + x * step, wy = oy + y * step;\n P.push(wx, wy, hgt[g]!); N.push(nor[g * 3]!, nor[g * 3 + 1]!, nor[g * 3 + 2]!); U.push(wx * rep, wy * rep);\n if (alpha) A.push(alpha(g));\n }\n return v;\n };\n for (let y = 0; y + 1 < NY; y++) for (let x = 0; x + 1 < NX; x++) {\n const g = y * NX + x;\n if (!take(g) || !valid(g)) continue;\n const a = vi(x, y), b = vi(x + 1, y), c = vi(x, y + 1), d = vi(x + 1, y + 1);\n I.push(a, b, d, a, d, c);\n }\n if (!I.length) return;\n const nv = P.length / 3;\n meshes.push({\n name, texture: layer?.texture ?? '', normalMap: layer?.normal || undefined,\n vertexCount: nv, pos: Float32Array.from(P), nor: Float32Array.from(N), uv: Float32Array.from(U), bone: new Float32Array(0), weight: new Float32Array(0),\n indices: nv > 65535 ? Uint32Array.from(I) : Uint16Array.from(I), skinned: false, ...(layer ? {} : { color: [0.45, 0.42, 0.36] }), ...(alpha ? { vertexAlpha: Float32Array.from(A) } : {}),\n });\n };\n const label = (L: number) => layers[L]?.name || (L < 0 ? 'без слоя' : `слой ${L}`);\n // основа: клетка — по слою основы её угла\n for (const L of [...new Set(base)].sort((a, b) => a - b)) emit(L, `Ландшафт · ${label(L)}`, (g) => base[g] === L);\n // слоты 1..3 по порядку (выше — позже): клетка, где у любого угла есть этот слой; прозрачность угла — есть ли слой в нём\n over.forEach((o, j) => {\n for (const L of [...new Set(o)].filter((l) => l >= 0).sort((a, b) => a - b)) {\n emit(L, `Ландшафт · ${label(L)} (наложение ${j + 1})`, (g) => o[g] === L || o[g + 1] === L || o[g + NX] === L || o[g + NX + 1] === L, (g) => (o[g] === L ? 1 : 0));\n }\n });\n return meshes;\n}\n\n/** Вода: по сектору с уровнем воды — клетки сетки, где земля ниже уровня, плоскостью на уровне; полупрозрачная. */\nexport function waterMesh(spec: { x0: number; y0: number; w: number; h: number }, sectors: Map<number, TerrainSector>, widthInSectors: number, step: number): Mesh | null {\n const P: number[] = [], I: number[] = [];\n const cells = SECTOR / step;\n for (let sy = 0; sy < spec.h; sy++) for (let sx = 0; sx < spec.w; sx++) {\n const s = sectors.get((spec.y0 + sy) * widthInSectors + spec.x0 + sx);\n if (!s || !s.water || s.min >= s.water) continue;\n const ox = (spec.x0 + sx) * SECTOR, oy = (spec.y0 + sy) * SECTOR, L = s.water;\n for (let cy = 0; cy < cells; cy++) for (let cx = 0; cx < cells; cx++) {\n let lo = Infinity;\n for (const [dx, dy] of [[0, 0], [1, 0], [0, 1], [1, 1]] as const) lo = Math.min(lo, s.height[(cy + dy) * step * GRID + (cx + dx) * step]!);\n if (lo >= L) continue;\n const v = P.length / 3, x = ox + cx * step, y = oy + cy * step;\n P.push(x, y, L, x + step, y, L, x, y + step, L, x + step, y + step, L);\n I.push(v, v + 1, v + 3, v, v + 3, v + 2);\n }\n }\n if (!I.length) return null;\n const nv = P.length / 3, nor = new Float32Array(nv * 3), uv = new Float32Array(nv * 2);\n for (let i = 0; i < nv; i++) { nor[i * 3 + 2] = 1; uv[i * 2] = P[i * 3]! / 8; uv[i * 2 + 1] = P[i * 3 + 1]! / 8; }\n return { name: 'Вода', texture: '', color: [0.16, 0.3, 0.36], vertexCount: nv, pos: Float32Array.from(P), nor, uv, bone: new Float32Array(0), weight: new Float32Array(0),\n indices: nv > 65535 ? Uint32Array.from(I) : Uint16Array.from(I), skinned: false, vertexAlpha: new Float32Array(nv).fill(0.7) };\n}\n\nconst isTerrainSize = (s: number) => s >= 23536 && s <= 26000 && (s - 23536) % 12 === 0;\n\n/** Указатель мира: файлы мира, ландшафт по номеру сектора, файлы объектов (WorldSector) по X, Y. Строится один раз на архив. */\ninterface WorldIndex { files: FatRef[]; terrain: Map<number, FatRef>; terrainEnd: number; objects?: Promise<Map<number, FatRef[]>>; full?: boolean }\ninterface TerrainGroup { start: number; terrain: Map<number, FatRef>; end: number }\nconst indexCache = new WeakMap<DuniaArchiveApi, Map<string, Promise<WorldIndex>>>();\nconst groupCache = new WeakMap<DuniaArchiveApi, Map<unknown, Promise<TerrainGroup[]>>>();\n\n/** Ландшафт среди записей: номер сектора — в начале файла (частичная распаковка). */\nasync function terrainOf(api: DuniaArchiveApi, files: FatRef[]): Promise<Array<{ ref: FatRef; id: number }>> {\n const refs = files.filter((r) => isTerrainSize(r.fe.size));\n const heads = await mapLimit(refs, 16, async (r) => { try { return await api.readRef(r, 0x18); } catch { return null; } });\n const out: Array<{ ref: FatRef; id: number }> = [];\n heads.forEach((h, i) => {\n if (!h || h.length < 0x18) return;\n const dv = new DataView(h.buffer, h.byteOffset, h.byteLength);\n if (dv.getUint32(0, true) === TER_MAGIC && dv.getUint32(4, true) === 7) out.push({ ref: refs[i]!, id: dv.getUint32(0x14, true) });\n });\n return out;\n}\n\n/** Блоки ландшафта .dat по порядку: новый блок — когда номер сектора повторяется (миры пишутся подряд). */\nfunction terrainGroups(api: DuniaArchiveApi, dat: FatRef['dat']): Promise<TerrainGroup[]> {\n let m = groupCache.get(api);\n if (!m) { m = new Map(); groupCache.set(api, m); }\n let p = m.get(dat);\n if (!p) {\n p = (async () => {\n const groups: TerrainGroup[] = [];\n let cur: TerrainGroup | null = null;\n for (const t of await terrainOf(api, api.datRefs(dat))) {\n if (!cur || cur.terrain.has(t.id)) { cur = { start: t.ref.fe.off, terrain: new Map(), end: 0 }; groups.push(cur); }\n cur.terrain.set(t.id, t.ref);\n cur.end = t.ref.fe.off;\n }\n return groups;\n })();\n m.set(dat, p);\n }\n return p;\n}\n\n/**\n * Указатель мира. Обычно файлы мира — участок .dat между .sectorsdep.fcb предыдущего мира и своим (worlds.dat). Если там\n * нет ландшафта или номера секторов повторяются (dlc1.dat: сначала сектора всех миров, потом их именованные файлы), k-й блок ландшафта .dat — k-го мира\n * по порядку .sectorsdep.fcb, а файлы мира — от начала его блока до начала следующего.\n */\nexport function worldIndex(api: DuniaArchiveApi, world: DuniaWorld): Promise<WorldIndex> {\n let m = indexCache.get(api);\n if (!m) { m = new Map(); indexCache.set(api, m); }\n let p = m.get(world.name);\n if (!p) {\n p = (async () => {\n let files = world.files();\n const own = await terrainOf(api, files);\n let terrain = new Map(own.map((t) => [t.id, t.ref]));\n if (!terrain.size || terrain.size < own.length) { // нет ландшафта или в участке несколько миров\n const groups = await terrainGroups(api, world.dat);\n const worlds = api.duniaWorlds().filter((w) => w.dat === world.dat).sort((x, y) => x.end - y.end);\n const k = worlds.findIndex((w) => w.name === world.name);\n if (groups.length === worlds.length && k >= 0) {\n const g = groups[k]!, next = groups[k + 1]?.start ?? Infinity;\n terrain = g.terrain;\n files = api.datRefs(world.dat).filter((r) => r.fe.off >= g.start && r.fe.off < next);\n }\n }\n let terrainEnd = 0;\n for (const r of terrain.values()) terrainEnd = Math.max(terrainEnd, r.fe.off);\n return { files, terrain, terrainEnd };\n })();\n m.set(world.name, p);\n }\n return p;\n}\n\n/**\n * Файлы объектов мира: .fcb с корнем WorldSector. У больших миров — после блока ландшафта (объекты слоёв миссий; рядом\n * с ландшафтом — только сектора-коллекции растительности), у малых — вперемешку с ландшафтом. X, Y — первые значения\n * корня, читаются из начала файла.\n */\nexport function worldObjects(api: DuniaArchiveApi, world: DuniaWorld, idx: WorldIndex): Promise<Map<number, FatRef[]>> {\n idx.objects ??= (async () => {\n // по порядку в .dat; блок объектов кончается перед ресурсами мира (модели, текстуры, звук) — после 2 пустых\n // порций подряд чтение останавливается. У малых миров (сетевые карты) объекты лежат вперемешку с ландшафтом —\n // у них просматривается весь мир, у больших — сначала после ландшафта.\n const out = new Map<number, FatRef[]>();\n const scan = async (cand: FatRef[]) => {\n const CHUNK = 2048;\n let empty = 0, found = 0;\n for (let i = 0; i < cand.length && empty < 2; i += CHUNK) {\n const part = cand.slice(i, i + CHUNK);\n const heads = await mapLimit(part, 32, async (r) => { try { return await api.readRef(r, 64); } catch { return null; } });\n let here = 0;\n heads.forEach((h, k) => {\n const xy = h && worldSectorXY(h);\n if (!xy) return;\n const key = xy[1] * 65536 + xy[0];\n let l = out.get(key);\n if (!l) { l = []; out.set(key, l); }\n l.push(part[k]!);\n here++;\n });\n found += here;\n empty = here || !found ? 0 : empty + 1;\n }\n return found;\n };\n const files = idx.files.filter((r) => r.fe.size >= 64);\n const before = files.filter((r) => r.fe.off <= idx.terrainEnd && !isTerrainSize(r.fe.size)), after = files.filter((r) => r.fe.off > idx.terrainEnd);\n if (before.length < SMALL_WORLD) { await scan([...before, ...after]); idx.full = true; }\n else if (!(await scan(after))) { await scan(before); idx.full = true; }\n return out;\n })();\n return idx.objects;\n}\n\n/** X, Y из начала .fcb с корнем WorldSector (иначе null). */\nexport function worldSectorXY(h: Uint8Array): [number, number] | null {\n if (h.length < 24) return null;\n const dv = new DataView(h.buffer, h.byteOffset, h.byteLength);\n if (dv.getUint32(0, true) !== 0x4643626e) return null;\n let p = 16;\n if (h[p]! >= 0xfe) p += 4;\n p++;\n if (dv.getUint32(p, true) !== H.WorldSector) return null;\n p += 4;\n let n = h[p++]!;\n if (n >= 0xfe) return null;\n let x = -1, y = -1;\n for (; n > 0 && p + 5 <= h.length; n--) {\n const k = dv.getUint32(p, true), sz = h[p + 4]!;\n p += 5;\n if (sz >= 0xfe || p + sz > h.length) break;\n if (sz === 4 && k === H.X) x = dv.getUint32(p, true);\n if (sz === 4 && k === H.Y) y = dv.getUint32(p, true);\n p += sz;\n }\n return x >= 0 && y >= 0 ? [x, y] : null;\n}\n\nexport async function loadDuniaLevel(text: string, main: VFile, ctx: LoadContext): Promise<Model> {\n const spec = JSON.parse(text.slice(LEVEL_MARK.length)) as LevelSpec;\n const arc = main.archive ?? ctx.archives.find((a) => duniaApi(a)) ?? null;\n const api = duniaApi(arc);\n const world = api?.duniaWorld(spec.world);\n if (!arc || !api || !world) throw new Error('Уровень Far Cry 2 открывается из архивов игры (папка Data_Win32)');\n const desc = worldDescriptor(await api.readRef(world.gameXml));\n const width = desc.countX || spec.x0 + spec.w;\n const idx = await worldIndex(api, world);\n const sectors = new Map<number, TerrainSector>();\n const want: FatRef[] = [];\n for (let y = spec.y0; y < spec.y0 + spec.h; y++) for (let x = spec.x0; x < spec.x0 + spec.w; x++) { const r = idx.terrain.get(y * width + x); if (r) want.push(r); }\n await mapLimit(want, 8, async (r) => { try { const s = parseTerrainSector(await api.readRef(r)); sectors.set(s.id, s); } catch { /* повреждён */ } });\n const step = terrainStep(spec.w, spec.h);\n const terrain = terrainMeshes(spec, sectors, width, desc.layers, step);\n const water = waterMesh(spec, sectors, width, step);\n if (water) terrain.push(water);\n\n const B = new LevelBuilder();\n const info = [`Far Cry 2 (Dunia): ${spec.world} · ${spec.name} — сектора ${spec.x0}…${spec.x0 + spec.w - 1} × ${spec.y0}…${spec.y0 + spec.h - 1} (${spec.w * 64}×${spec.h * 64} м)`,\n `Ландшафт: секторов ${sectors.size} из ${spec.w * spec.h}, сетка ${step} м, слоёв ${terrain.length}`];\n let shown = 0;\n if (!spec.terrainOnly) {\n const objs = await worldObjects(api, world, idx);\n const refs: FatRef[] = [];\n const inReg = (x: number, y: number) => x >= spec.x0 && x < spec.x0 + spec.w && y >= spec.y0 && y < spec.y0 + spec.h;\n for (let y = spec.y0; y < spec.y0 + spec.h; y++) for (let x = spec.x0; x < spec.x0 + spec.w; x++) refs.push(...(objs.get(y * 65536 + x) ?? []));\n if (!idx.full && want.length) {\n // большой мир: сектора-коллекции лежат в блоке ландшафта рядом со своим ландшафтом (порядок Мортона по картам)\n const lo = Math.min(...want.map((r) => r.fe.off)), hi = Math.max(...want.map((r) => r.fe.off));\n const cand = idx.files.filter((r) => r.fe.off > lo - 65536 && r.fe.off < hi + 65536 && r.fe.size >= 64 && !isTerrainSize(r.fe.size));\n const heads = await mapLimit(cand, 32, async (r) => { try { return await api.readRef(r, 64); } catch { return null; } });\n heads.forEach((h, i) => { const xy = h && worldSectorXY(h); if (xy && inReg(xy[0], xy[1]) && !refs.includes(cand[i]!)) refs.push(cand[i]!); });\n }\n shown = await placeEntities(arc, api, world.name, refs, B, (s) => info.push(s));\n } else info.push('Весь мир — только ландшафт; объекты — на картах worlds/…/maps/*.fc2level');\n const meshes = [...B.build(), ...terrain];\n if (!meshes.length) throw new Error('В области уровня нет секторов');\n if (!shown && !spec.terrainOnly) info.push('Объекты не найдены');\n info.push(water ? `Вода: по уровню сектора (${[...new Set([...sectors.values()].map((x) => x.water).filter(Boolean))].slice(0, 5).join(', ')} м), полупрозрачная плоскость там, где земля ниже` : 'Воды в области нет');\n info.push('Не показаны: небо; деревья RealTree — приближённо');\n return { meshes, instances: B.scene, skeleton: null, motions: [], bounds: boundsOf(meshes), info, defaultZUp: true };\n}\n\nasync function placeEntities(arc: Archive, api: DuniaArchiveApi, worldName: string, sectorRefs: FatRef[], B: LevelBuilder, note: (s: string) => void): Promise<number> {\n // библиотека архетипов мира\n let lib = new Map<string, { model: string; parts: string[] }>();\n const le = arc.lookup(`worlds/${worldName}/generated/entitylibrary.fcb`);\n if (le) { try { lib = entityLibrary(parseFcb(await arc.read(le))); } catch { note('entitylibrary.fcb не прочитана'); } }\n const ents: LevelEntity[] = [], unknown = new Set<string>();\n let noModel = 0, sectorsWithObjects = 0, collected = 0;\n const markers: LevelMarker[] = [];\n await mapLimit(sectorRefs, 8, async (r) => {\n let root: FcbObject;\n try { root = parseFcb(await api.readRef(r)); } catch { return; }\n const s = sectorEntities(root, lib);\n if (s.list.length) sectorsWithObjects++;\n ents.push(...s.list); noModel += s.noModel; markers.push(...s.markers);\n for (const c of sectorCollections(root)) {\n const name = api.nameOfHash(c.hash);\n ents.push({ name: name?.split('\\\\').pop()?.replace(/\\.xbg$/i, '') ?? `#${c.hash.toString(16)}`, model: `#${c.hash.toString(16).padStart(8, '0')}`, parts: [], pos: c.pos, angles: [0, 0, c.yaw], layer: 'collections' });\n collected++;\n }\n for (const a of s.unknownArchetype) unknown.add(a);\n });\n ents.sort((a, b) => a.name.localeCompare(b.name) || a.pos[0]! - b.pos[0]! || a.pos[1]! - b.pos[1]! || a.pos[2]! - b.pos[2]!);\n // модели: ближний LOD; если вместе больше бюджета треугольников — у самых «тяжёлых» (число × треугольники) дальний LOD\n // коллекции (трава, кусты, камни — сотни тысяч экземпляров на карту) — отдельно: LOD1 и прореживание до своего бюджета\n const counts = new Map<string, number>(), colCounts = new Map<string, number>();\n for (const e of ents) { const m = e.layer === 'collections' ? colCounts : counts; m.set(e.model.toLowerCase(), (m.get(e.model.toLowerCase()) ?? 0) + 1); }\n const parsedModels = new Map<string, { u: Uint8Array; x: Xbg } | null>();\n // модель по пути или по хешу (#xxxxxxxx — коллекции); не .xbg (RealTree-деревья .rtr) — пропускаются\n const realtrees = new Set<string>(), rtMeshes = new Map<string, Mesh[]>();\n // RealTree: приближённая сетка (ветви трубками, листва карточками) с материалами коры и листвы из .xbm\n const realTreeOf = async (t: RealTree): Promise<Mesh[]> => {\n const mats = await Promise.all(t.materials.map(async (m) => {\n const e = arc.lookup(m.replace(/\\.mlm$/i, '.xbm'));\n try { return e ? { path: m, x: parseXbm(await arc.read(e)) } : null; } catch { return null; }\n }));\n const slot = (x: XbmMaterial | undefined, ...keys: string[]) => { for (const k of keys) if (x?.textures[k]) return x.textures[k]; return undefined; };\n const pick = (trunk: boolean) => {\n const m = mats.find((q) => q && /trunk|bark/i.test(q.x.shader + q.x.name) === trunk) ?? mats[trunk ? 0 : 1] ?? null;\n return { material: m?.path, texture: slot(m?.x, 'DiffuseTexture1', 'DiffuseTexture') ?? '', normal: slot(m?.x, 'NormalTexture1', 'NormalTexture') };\n };\n return realTreeMeshes(t, pick(true), pick(false));\n };\n const displayName = (k: string) => (k.startsWith('#') ? api.nameOfHash(parseInt(k.slice(1), 16)) ?? k : k);\n const readModel = async (k: string) => {\n if (k.startsWith('#')) { const r = api.refByHash(parseInt(k.slice(1), 16)); return r ? api.readRef(r) : null; }\n const e = arc.lookup(k);\n return e ? arc.read(e) : null;\n };\n await mapLimit([...new Set([...counts.keys(), ...colCounts.keys()])], 8, async (k) => {\n try {\n const u = await readModel(k);\n if (!u) { parsedModels.set(k, null); return; }\n if (String.fromCharCode(u[0]!, u[1]!, u[2]!, u[3]!) !== 'HSEM') {\n parsedModels.set(k, null);\n if (isRealTree(u)) { realtrees.add(k); try { rtMeshes.set(k, await realTreeOf(parseRealTree(u))); } catch { /* не разобран */ } }\n return;\n }\n parsedModels.set(k, { u, x: parseXbg(u) });\n } catch { parsedModels.set(k, null); }\n });\n const lodOf = new Map<string, number>();\n const trisOf = (k: string, l: number) => {\n const rt = rtMeshes.get(k);\n if (rt) return rt.reduce((n, m) => n + m.indices.length / 3, 0);\n const p = parsedModels.get(k), L = p?.x.lods[Math.min(l, (p?.x.lods.length ?? 1) - 1)];\n if (!p || !L) return 0;\n // как в xbgMeshes: без скрытых по умолчанию частей (повреждённые состояния, ночные фары)\n let n = 0;\n L.prims.forEach((pr, i) => { const name = p.x.parts[pr.part]?.name ?? ''; if (!/STATE0*[2-9]$|_N_/i.test(name.replace(/_LOD\\d+$/i, ''))) n += ((L.prims[i + 1]?.firstIndex ?? L.icount) - pr.firstIndex) / 3; });\n return n;\n };\n let total = 0;\n for (const [k, n] of counts) total += n * trisOf(k, 0);\n const lodNote = total;\n if (total > TRI_BUDGET) {\n const order = [...counts].sort((a, b) => b[1] * trisOf(b[0], 0) - a[1] * trisOf(a[0], 0));\n for (const [k, n] of order) {\n if (total <= TRI_BUDGET) break;\n const p = parsedModels.get(k);\n if (!p || p.x.lods.length < 2) continue;\n const far = Math.min(2, p.x.lods.length - 1);\n total -= n * (trisOf(k, 0) - trisOf(k, far));\n lodOf.set(k, far);\n }\n }\n const colLod = (k: string) => Math.min(COLLECTION_LOD, (parsedModels.get(k)?.x.lods.length ?? 1) - 1);\n // бюджет делится поровну между моделями коллекций: редкие (камни, кусты) остаются целиком, прореживается массовое (трава)\n const colKeepOf = new Map<string, number>();\n let colTotal = 0;\n {\n const rows = [...colCounts].map(([k, n]) => ({ k, t: n * trisOf(k, colLod(k)) })).filter((r) => r.t > 0).sort((a, b) => a.t - b.t);\n let left = COLLECTION_BUDGET;\n rows.forEach((r, i) => { colTotal += r.t; const keep = Math.min(1, left / (rows.length - i) / r.t); colKeepOf.set(r.k, keep); left -= keep * r.t; });\n }\n const matCache = new Map<string, Promise<XbmMaterial | null>>();\n const protos = new Map<string, Promise<{ pi: number; parts: Mesh[] } | null>>();\n const missing = new Set<string>();\n let skinned = 0;\n const protoOf = (model: string, parts: string[], lod: number) => {\n const mk = model.toLowerCase(), key = `${mk}|${lod}|${parts.join(';')}`;\n let pr = protos.get(key);\n if (!pr) {\n pr = (async () => {\n const rt = rtMeshes.get(mk);\n if (rt?.length) return { pi: B.proto(displayName(model), rt, 'static', (m) => m.name), parts: rt };\n const p = parsedModels.get(mk);\n if (!p) { if (!realtrees.has(mk)) missing.add(model); return null; }\n try {\n const r = await xbgMeshes(p.u, arc, matCache, lod, p.x);\n // со скелетом (люди, животные) — в bind-позе; у кита без списка частей — один вариант на слот\n const all = r.skinned ? bakeBindPose(r.x, r.meshes) : r.meshes;\n if (r.skinned) skinned++;\n const want = new Set(parts.map((q) => q.toUpperCase()));\n const kit = want.size ? new Set<string>() : kitHiddenParts(all.map((m) => m.name.split(' · ')[0]!));\n const list = all.filter((m) => { const part = m.name.split(' · ')[0]!; return !m.defaultHidden && !kit.has(part) && (!want.size || want.has(part.toUpperCase())); });\n if (!list.length) return null;\n const label = (m: Mesh) => m.material?.split('\\\\').pop() || m.texture.split('\\\\').pop() || '?';\n return { pi: B.proto(displayName(model), list, 'static', label), parts: list };\n } catch { missing.add(model); return null; }\n })();\n protos.set(key, pr);\n }\n return pr;\n };\n let placed = 0, placedCol = 0, treeInst = 0;\n const treeMarks: Array<{ name: string; pos: number[] }> = [];\n const pick = rngOf(0x5eed);\n let thinned = 0;\n for (const e of ents) {\n const col = e.layer === 'collections', mk = e.model.toLowerCase();\n if (col && (colKeepOf.get(mk) ?? 1) < 1 && pick() >= colKeepOf.get(mk)!) { thinned++; continue; }\n const pr = await protoOf(e.model, e.parts, col ? colLod(mk) : lodOf.get(mk) ?? 0);\n if (!pr) { if (realtrees.has(e.model.toLowerCase())) { treeInst++; treeMarks.push({ name: e.name, pos: e.pos }); } continue; }\n const M = new Float64Array(16);\n compose(e.pos, anglesQuat(e.angles), [1, 1, 1], M);\n const path = displayName(e.model);\n const folder = path.startsWith('#') ? 'collections' : path.replace(/\\\\/g, '/').split('/').slice(0, -1).join('/') || 'graphics';\n B.place(pr.pi, pr.parts, M, e.name, e.layer === 'collections' ? `collections/${folder}` : folder);\n placed++;\n if (e.layer === 'collections') placedCol++;\n }\n // метки (скрыты; включаются в списке мешей): частицы, свет, деревья RealTree (их сетка строится движком — здесь только место)\n const mark = (list: Array<{ name: string; pos: number[] }>, label: string, color: number[], size: number, layer: string) => {\n if (!list.length) return;\n const m = markerMesh(label, size, color), pi = B.proto(label, [m], 'marker', () => label, true);\n for (const x of list) { const M = new Float64Array(16); compose(x.pos, [1, 0, 0, 0], [1, 1, 1], M); B.place(pi, [m], M, x.name, layer); }\n };\n mark(markers.filter((x) => x.kind === 'particle'), 'Метки · частицы', [0.3, 0.8, 1], 0.3, 'particle');\n mark(markers.filter((x) => x.kind === 'light'), 'Метки · свет', [1, 0.85, 0.3], 0.3, 'light');\n mark(treeMarks, 'Метки · деревья RealTree (не разобраны)', [0.25, 0.75, 0.2], 1.5, 'realtree');\n note(`Объекты: ${placed} из ${ents.length} с моделью (секторов с объектами ${sectorsWithObjects}, файлов секторов ${sectorRefs.length}; моделей ${[...protos.keys()].length}, треугольников ${B.tris})`);\n if (lodOf.size) note(`Дальний LOD у ${lodOf.size} моделей (ближние LOD всех объектов — ${Math.round(lodNote / 1e6 * 10) / 10} млн треугольников, бюджет ${TRI_BUDGET / 1e6} млн)`);\n if (collected) note(`Коллекции (трава, кусты, камни, мусор — кисть редактора), LOD${COLLECTION_LOD}: ${placedCol} из ${collected}${thinned ? ` (массовые модели прорежены: всего ${Math.round(colTotal / 1e5) / 10} млн треугольников, бюджет ${COLLECTION_BUDGET / 1e6} млн)` : ''}${realtrees.size ? `; деревья и кусты RealTree (.rtr): моделей ${realtrees.size}, приближённо — ветви трубками по узлам, листва карточками у концов ветвей (сетку игра строит сама)${treeInst ? `, не разобрано ${treeInst} экземпляров` : ''}` : ''}`);\n if (noModel) note(`Без модели (логика, точки, звук, частицы, свет): ${noModel}`);\n if (markers.length || treeMarks.length) note(`Метки (скрыты, включаются в списке мешей): частицы ${markers.filter((x) => x.kind === 'particle').length}, свет ${markers.filter((x) => x.kind === 'light').length}, деревья RealTree ${treeMarks.length}`);\n if (skinned) note(`Модели со скелетом (люди, животные) — в bind-позе: ${skinned}`);\n if (missing.size) note(`Модели не найдены: ${missing.size} (${[...missing].slice(0, 3).join(', ')}${missing.size > 3 ? '…' : ''})`);\n if (unknown.size) note(`Архетипы не найдены в entitylibrary.fcb: ${unknown.size} (${[...unknown].slice(0, 2).join(', ')})`);\n return placed;\n}\n"],"names":["pick","n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"level.js","sources":["../src/level.ts"],"sourcesContent":["// Far Cry 2 levels (Dunia). A world is worlds/<world>: a grid of 64×64 m sectors (large worlds: 80×80 sectors, 25 maps of 16×16),\n// generated/<world>.game.xml is the description (grid, maps, terrain layers), generated/entitylibrary.fcb holds object archetypes.\n// Sector files have no names in the archive (they cannot be found by hash), so they are recognized by content among the world files\n// (the .dat range before <world>.sectorsdep.fcb, see fat.ts):\n// sector terrain: \"R\\x10\\x00\\xE9\", u32 7, u32 size, …, u32 sector number (0x14; x = number % width, y = number / width),\n// f32 min/max heights (0x1C, 0x20), 4 bytes of terrain layer numbers (0x24, from the Layers list in game.xml),\n// f32 sector water level (0x3C; 0 = no water; the water material is a .mlm path at 0x44),\n// from 0x250, 65×65 vertices of 4 bytes (rows along Y, 1 m step): u16 height·128, u8 lighting, u8 layer mask\n// (bit k of the low nibble = the layer of slot k at the vertex; 255 = empty slot): the lowest used slot is the base, higher slots\n// are overlaid with per-vertex opacity (the transition between layers spans one cell);\n// sector objects: .fcb with a WorldSector root (Id, X, Y): mission layers → Entity (hidPos, hidAngles in degrees;\n// the model is value 0xBF9B3A5C of the CGraphicComponent component or its object; hidMeshName lists visible kit parts;\n// archetype objects (0x346AAB33 = \"OA_Buildings.Buildings.ShantyShelter01\") take the model from entitylibrary.fcb by hidName).\nimport { boundsOf } from '@gmvjs/core/geometry';\nimport { LevelBuilder, markerMesh } from '@gmvjs/core/levelbuilder';\nimport { compose } from '@gmvjs/core/math';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport type { Archive, Mesh, Model, VFile } from '@gmvjs/core/types';\nimport { duniaApi, LEVEL_MARK, type DuniaArchiveApi, type DuniaWorld, type FatRef, type LevelSpec } from './fat';\nimport { fcbFind, fcbHash, fcbString, fcbVec3, parseFcb, type FcbObject } from './fcb';\nimport { xmlElements } from './rml';\nimport { isRealTree, parseRealTree, realTreeMeshes, type RealTree } from './realtree';\nimport { bakeBindPose, kitHiddenParts, parseXbg, parseXbm, xbgMeshes, type Xbg, type XbmMaterial } from './xbg';\n\nconst SECTOR = 64, GRID = 65, TER_MAGIC = 0xe9001052;\n/** Map object triangle budget (above it, the heaviest models get distant LODs). */\nconst TRI_BUDGET = 4_000_000;\n/** A small world (multiplayer maps): the terrain block has fewer files, objects are looked up across the whole world. */\nconst SMALL_WORLD = 12_000;\n/** Collections: LOD level and triangle budget (above it, uniform thinning). */\nconst COLLECTION_LOD = 1, COLLECTION_BUDGET = 3_000_000;\n/** Deterministic random stream (mulberry32): thinning is the same on every open. */\nfunction rngOf(seed: number): () => number {\n let a = seed >>> 0;\n return () => { a = (a + 0x6d2b79f5) >>> 0; let t = a; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };\n}\nconst H = {\n Entity: fcbHash('Entity'), WorldSector: fcbHash('WorldSector'), hidName: fcbHash('hidName'), hidPos: fcbHash('hidPos'), hidAngles: fcbHash('hidAngles'),\n hidMeshName: fcbHash('hidMeshName'), X: fcbHash('X'), Y: fcbHash('Y'), model: 0xbf9b3a5c, archetype: 0x346aab33, layer: 0x494c09f2, layerName: 0xc56f9204,\n};\n\nexport interface TerrainLayer { name: string; texture: string; normal: string; tiling: number }\n\n/** Terrain layers and the world grid from game.xml. */\nexport function worldDescriptor(u: Uint8Array): { layers: TerrainLayer[]; countX: number; countY: number } {\n const layers: TerrainLayer[] = [];\n // world width in sectors = sectors per map × maps (GridMapSectors × GridWorldMaps)\n let sx = 0, sy = 0, mx = 1, my = 1, inLayers = -1;\n for (const el of xmlElements(u)) {\n if (el.name === 'GridMapSectors' && !sx) { sx = +el.attrs.CountX! || 0; sy = +el.attrs.CountY! || 0; }\n if (el.name === 'GridWorldMaps') { mx = +el.attrs.CountX! || 1; my = +el.attrs.CountY! || 1; }\n if (el.name === 'Layers') { inLayers = el.depth; continue; }\n if (inLayers >= 0 && el.depth <= inLayers) inLayers = -1;\n if (inLayers >= 0 && el.depth === inLayers + 1 && el.name === 'Layer') {\n layers.push({ name: (el.attrs.Name ?? '').trim(), texture: el.attrs.Texture ?? '', normal: el.attrs.NormalMap ?? '', tiling: +el.attrs.Tiling! || 20 });\n }\n }\n return { layers, countX: sx * mx, countY: sy * my };\n}\n\nexport interface TerrainSector { id: number; min: number; max: number; layers: number[]; height: Float32Array; mask: Uint8Array; water: number }\n\n/** Sector terrain (see the file header). */\nexport function parseTerrainSector(u: Uint8Array): TerrainSector {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 0x250 + GRID * GRID * 4 || dv.getUint32(0, true) !== TER_MAGIC) throw new Error('Не ландшафт сектора Far Cry 2');\n const height = new Float32Array(GRID * GRID), mask = new Uint8Array(GRID * GRID);\n for (let i = 0; i < GRID * GRID; i++) { height[i] = dv.getUint16(0x250 + i * 4, true) / 128; mask[i] = u[0x250 + i * 4 + 3]!; }\n const water = dv.getFloat32(0x3c, true);\n return { id: dv.getUint32(0x14, true), min: dv.getFloat32(0x1c, true), max: dv.getFloat32(0x20, true), layers: [u[0x24]!, u[0x25]!, u[0x26]!, u[0x27]!], height, mask, water: Number.isFinite(water) && water > 0 ? water : 0 };\n}\n\n/**\n * Vertex layer: the highest set bit of the mask's low nibble whose slot is used (the top painted layer);\n * an empty slot is 255; with no bits (sector edge), the first used slot.\n */\nexport function vertexLayer(mask: number, slots: readonly number[]): number {\n for (let k = 3; k >= 0; k--) if (mask & (1 << k) && slots[k] !== 255) return slots[k]!;\n for (const s of slots) if (s !== 255) return s;\n return -1;\n}\n\n/**\n * Object angles (degrees) -> quaternion [w, x, y, z]: R = Rz(z)·Ry(y)·Rx(x) (X first, then Y, then Z, the yaw).\n * Verified on objects on slopes: the object's \"up\" axis matches the ground normal (see the level test).\n */\nexport function anglesQuat(a: readonly number[]): number[] {\n const h = (d: number) => (d * Math.PI) / 360;\n const qx = [Math.cos(h(a[0]!)), Math.sin(h(a[0]!)), 0, 0], qy = [Math.cos(h(a[1]!)), 0, Math.sin(h(a[1]!)), 0], qz = [Math.cos(h(a[2]!)), 0, 0, Math.sin(h(a[2]!))];\n return qmul(qmul(qz, qy), qx);\n}\nconst qmul = (a: number[], b: number[]) => [\n a[0]! * b[0]! - a[1]! * b[1]! - a[2]! * b[2]! - a[3]! * b[3]!,\n a[0]! * b[1]! + a[1]! * b[0]! + a[2]! * b[3]! - a[3]! * b[2]!,\n a[0]! * b[2]! - a[1]! * b[3]! + a[2]! * b[0]! + a[3]! * b[1]!,\n a[0]! * b[3]! + a[1]! * b[2]! - a[2]! * b[1]! + a[3]! * b[0]!,\n];\n\n/** Level object: model, visible parts (empty = all), pose. */\nexport interface LevelEntity { name: string; model: string; parts: string[]; pos: number[]; angles: number[]; layer: string; archetype?: string }\n\nconst modelOf = (e: FcbObject): string | undefined => {\n const walk = (o: FcbObject, d: number): string | undefined => {\n const v = fcbString(o.values.get(H.model));\n if (v) return v;\n if (d > 6) return undefined;\n for (const c of o.children) { if (c.type === H.Entity) continue; const r = walk(c, d + 1); if (r) return r; }\n return undefined;\n };\n return walk(e, 0);\n};\nconst meshNamesOf = (e: FcbObject): string[] => {\n for (const o of fcbFind(e, fcbHash('object'))) { const s = fcbString(o.values.get(H.hidMeshName)); if (s && s.includes(';')) return s.split(';').filter(Boolean); }\n return [];\n};\n\n/** entitylibrary.fcb archetypes: hidName -> model and parts. */\nexport function entityLibrary(root: FcbObject): Map<string, { model: string; parts: string[] }> {\n const out = new Map<string, { model: string; parts: string[] }>();\n for (const e of fcbFind(root, H.Entity)) {\n const n = fcbString(e.values.get(H.hidName)), m = modelOf(e);\n if (n && m && !out.has(n)) out.set(n, { model: m, parts: meshNamesOf(e) });\n }\n return out;\n}\n\n/** Sector objects (WorldSector) with models; archetypes from the library. */\nexport interface LevelMarker { kind: 'particle' | 'light'; name: string; pos: number[] }\nconst PARTICLES = fcbHash('CNewParticlesComponent'), LIGHT = fcbHash('CDynamicLightComponent'), PARTICLE_NAME = 0x1943b130;\n\nexport function sectorEntities(root: FcbObject, lib: Map<string, { model: string; parts: string[] }>): { list: LevelEntity[]; noModel: number; unknownArchetype: Set<string>; markers: LevelMarker[] } {\n const list: LevelEntity[] = [], unknownArchetype = new Set<string>(), markers: LevelMarker[] = [];\n let noModel = 0;\n const visit = (o: FcbObject, layer: string) => {\n if (o.type === H.layer) layer = fcbString(o.values.get(H.layerName)) ?? layer;\n if (o.type === H.Entity) {\n const pos = fcbVec3(o.values.get(H.hidPos));\n const arch = fcbString(o.values.get(H.archetype));\n let model = modelOf(o), parts = meshNamesOf(o);\n if (!model && arch) { const L = lib.get(arch); if (L) { model = L.model; if (!parts.length) parts = L.parts; } else unknownArchetype.add(arch); }\n if (pos && model) list.push({ name: fcbString(o.values.get(H.hidName)) ?? arch ?? 'entity', model, parts, pos, angles: fcbVec3(o.values.get(H.hidAngles)) ?? [0, 0, 0], layer, ...(arch ? { archetype: arch } : {}) });\n else if (pos) {\n noModel++;\n // no model: particle markers (effect name) and dynamic lights\n const name = fcbString(o.values.get(H.hidName)) ?? 'entity';\n const pc = fcbFind(o, PARTICLES)[0];\n if (pc) markers.push({ kind: 'particle', name: fcbString(pc.values.get(PARTICLE_NAME)) ?? name, pos });\n else if (fcbFind(o, LIGHT).length) markers.push({ kind: 'light', name, pos });\n }\n }\n for (const c of o.children) visit(c, layer);\n };\n visit(root, 'main');\n return { list, noModel, unknownArchetype, markers };\n}\n\n/**\n * Sector collections (CCollectionComponent of CSectorEntity: rocks, debris, vegetation placed with the editor brush):\n * 0x114CFEE7 is a set (frame 0x17E7C62E–0x2BEAF977), its data 0xA18C8484: 0x89C8C095 = model path hashes (like the names\n * in .fat), 0x58AD43E2 = group count per model, 0x798C33D5 = groups (u8 instance count, then the first index),\n * 0xB0484974 = group centers and radii; instances: 0xD28100C0 = u16 x, u16 y (decimetres), 0x8E2752CA = f32 z,\n * 0x98C678BD = u32 (low 16 bits: rotation about Z, full circle = 65536; high bits not decoded), 0x9D1CAE67 = colors.\n * Every array is a u32 count followed by the elements.\n */\nexport function sectorCollections(root: FcbObject): Array<{ hash: number; pos: number[]; yaw: number }> {\n const out: Array<{ hash: number; pos: number[]; yaw: number }> = [];\n for (const set of fcbFind(root, 0x114cfee7)) {\n const d = set.children.find((c) => c.type === 0xa18c8484);\n if (!d) continue;\n const arr = (k: number) => { const v = d.values.get(k); if (!v || v.length < 4) return null; const dv = new DataView(v.buffer, v.byteOffset, v.byteLength); const n = dv.getUint32(0, true); return n * 4 + 4 <= v.length ? { dv, n } : null; };\n const models = arr(0x89c8c095), counts = arr(0x58ad43e2), groups = arr(0x798c33d5), xy = arr(0xd28100c0), zs = arr(0x8e2752ca), rot = arr(0x98c678bd);\n if (!models || !counts || !groups || !xy || !zs || counts.n !== models.n) continue;\n let g = 0;\n for (let m = 0; m < models.n; m++) {\n const hash = models.dv.getUint32(4 + m * 4, true), ng = counts.dv.getUint32(4 + m * 4, true);\n for (let k = 0; k < ng && g < groups.n; k++, g++) {\n const gv = groups.dv.getUint32(4 + g * 4, true), cnt = gv & 0xff, first = gv >>> 8;\n for (let i = first; i < first + cnt && i < xy.n && i < zs.n; i++) {\n const x = xy.dv.getUint16(4 + i * 4, true) / 10, y = xy.dv.getUint16(6 + i * 4, true) / 10, z = zs.dv.getFloat32(4 + i * 4, true);\n const yaw = rot && i < rot.n ? ((rot.dv.getUint32(4 + i * 4, true) & 0xffff) / 65536) * 360 : 0;\n out.push({ hash, pos: [x, y, z], yaw });\n }\n }\n }\n }\n return out;\n}\n\n/** Read many records in parallel (n at a time). */\nasync function mapLimit<T, R>(items: T[], n: number, f: (x: T) => Promise<R>): Promise<R[]> {\n const out: R[] = new Array(items.length);\n let k = 0;\n await Promise.all(Array.from({ length: Math.min(n, items.length) }, async () => { while (k < items.length) { const i = k++; out[i] = await f(items[i]!); } }));\n return out;\n}\n\n/** Terrain grid step (m): a power of two so that the vertex count stays within the budget. */\nexport const terrainStep = (w: number, h: number, budget = 520_000) => {\n let s = 1;\n while ((Math.floor((w * SECTOR) / s) + 1) * (Math.floor((h * SECTOR) / s) + 1) > budget && s < SECTOR) s *= 2;\n return s;\n};\n\n/** Terrain meshes of an area: one mesh per layer (a triangle goes by the layer of its corner), UVs tile the layer (Tiling repeats per sector). */\nexport function terrainMeshes(spec: { x0: number; y0: number; w: number; h: number }, sectors: Map<number, TerrainSector>, widthInSectors: number, layers: TerrainLayer[], step: number): Mesh[] {\n const NX = (spec.w * SECTOR) / step + 1, NY = (spec.h * SECTOR) / step + 1, n = NX * NY;\n // per vertex: height, base layer and the sector's slot 1..3 layers with a mask bit (-1 = no layer)\n const hgt = new Float32Array(n).fill(NaN), base = new Int16Array(n).fill(-1), over = [1, 2, 3].map(() => new Int16Array(n).fill(-1));\n for (let gy = 0; gy < NY; gy++) {\n for (let gx = 0; gx < NX; gx++) {\n const mx = gx * step, my = gy * step; // metres from the area corner\n let sx = Math.floor(mx / SECTOR), sy = Math.floor(my / SECTOR);\n if (sx >= spec.w) sx = spec.w - 1;\n if (sy >= spec.h) sy = spec.h - 1;\n const s = sectors.get((spec.y0 + sy) * widthInSectors + spec.x0 + sx);\n if (!s) continue;\n const lx = mx - sx * SECTOR, ly = my - sy * SECTOR, k = ly * GRID + lx, g = gy * NX + gx, m = s.mask[k]!;\n hgt[g] = s.height[k]!;\n // base is the lowest used slot; above it, slots 1..3 where the mask bit is set (overlaid with per-vertex opacity)\n const b0 = s.layers.findIndex((l) => l !== 255);\n base[g] = b0 >= 0 ? s.layers[b0]! : -1;\n for (let j = 1; j <= 3; j++) if (j > b0 && m & (1 << j) && s.layers[j] !== 255) over[j - 1]![g] = s.layers[j]!;\n }\n }\n // normals from neighbours\n const nor = new Float32Array(n * 3);\n const at = (x: number, y: number, d: number) => { const v = hgt[Math.min(NY - 1, Math.max(0, y)) * NX + Math.min(NX - 1, Math.max(0, x))]!; return Number.isNaN(v) ? d : v; };\n for (let y = 0; y < NY; y++) for (let x = 0; x < NX; x++) {\n const c = hgt[y * NX + x]!; if (Number.isNaN(c)) continue;\n const dx = (at(x + 1, y, c) - at(x - 1, y, c)) / (2 * step), dy = (at(x, y + 1, c) - at(x, y - 1, c)) / (2 * step), l = Math.hypot(dx, dy, 1);\n nor[(y * NX + x) * 3] = -dx / l; nor[(y * NX + x) * 3 + 1] = -dy / l; nor[(y * NX + x) * 3 + 2] = 1 / l;\n }\n const meshes: Mesh[] = [], local = new Int32Array(n);\n const ox = spec.x0 * SECTOR, oy = spec.y0 * SECTOR;\n const valid = (g: number) => !Number.isNaN(hgt[g]!) && !Number.isNaN(hgt[g + 1]!) && !Number.isNaN(hgt[g + NX]!) && !Number.isNaN(hgt[g + NX + 1]!);\n /** Mesh of layer L over the cells take(g); alpha is vertex opacity (the layer over the base). */\n const emit = (L: number, name: string, take: (g: number) => boolean, alpha?: (g: number) => number) => {\n local.fill(-1);\n const P: number[] = [], U: number[] = [], N: number[] = [], A: number[] = [], I: number[] = [];\n const layer = layers[L], rep = (layer?.tiling ?? 20) / SECTOR;\n const vi = (x: number, y: number) => {\n const g = y * NX + x;\n let v = local[g]!;\n if (v < 0) {\n v = local[g] = P.length / 3;\n const wx = ox + x * step, wy = oy + y * step;\n P.push(wx, wy, hgt[g]!); N.push(nor[g * 3]!, nor[g * 3 + 1]!, nor[g * 3 + 2]!); U.push(wx * rep, wy * rep);\n if (alpha) A.push(alpha(g));\n }\n return v;\n };\n for (let y = 0; y + 1 < NY; y++) for (let x = 0; x + 1 < NX; x++) {\n const g = y * NX + x;\n if (!take(g) || !valid(g)) continue;\n const a = vi(x, y), b = vi(x + 1, y), c = vi(x, y + 1), d = vi(x + 1, y + 1);\n I.push(a, b, d, a, d, c);\n }\n if (!I.length) return;\n const nv = P.length / 3;\n meshes.push({\n name, texture: layer?.texture ?? '', normalMap: layer?.normal || undefined,\n vertexCount: nv, pos: Float32Array.from(P), nor: Float32Array.from(N), uv: Float32Array.from(U), bone: new Float32Array(0), weight: new Float32Array(0),\n indices: nv > 65535 ? Uint32Array.from(I) : Uint16Array.from(I), skinned: false, ...(layer ? {} : { color: [0.45, 0.42, 0.36] }), ...(alpha ? { vertexAlpha: Float32Array.from(A) } : {}),\n });\n };\n const label = (L: number) => layers[L]?.name || (L < 0 ? 'без слоя' : `слой ${L}`);\n // base: a cell goes by the base layer of its corner\n for (const L of [...new Set(base)].sort((a, b) => a - b)) emit(L, `Ландшафт · ${label(L)}`, (g) => base[g] === L);\n // slots 1..3 in order (higher ones later): cells where any corner has this layer; corner opacity says whether the layer is there\n over.forEach((o, j) => {\n for (const L of [...new Set(o)].filter((l) => l >= 0).sort((a, b) => a - b)) {\n emit(L, `Ландшафт · ${label(L)} (наложение ${j + 1})`, (g) => o[g] === L || o[g + 1] === L || o[g + NX] === L || o[g + NX + 1] === L, (g) => (o[g] === L ? 1 : 0));\n }\n });\n return meshes;\n}\n\n/** Water: for each sector with a water level, grid cells where the ground is below it, as a plane at that level; translucent. */\nexport function waterMesh(spec: { x0: number; y0: number; w: number; h: number }, sectors: Map<number, TerrainSector>, widthInSectors: number, step: number): Mesh | null {\n const P: number[] = [], I: number[] = [];\n const cells = SECTOR / step;\n for (let sy = 0; sy < spec.h; sy++) for (let sx = 0; sx < spec.w; sx++) {\n const s = sectors.get((spec.y0 + sy) * widthInSectors + spec.x0 + sx);\n if (!s || !s.water || s.min >= s.water) continue;\n const ox = (spec.x0 + sx) * SECTOR, oy = (spec.y0 + sy) * SECTOR, L = s.water;\n for (let cy = 0; cy < cells; cy++) for (let cx = 0; cx < cells; cx++) {\n let lo = Infinity;\n for (const [dx, dy] of [[0, 0], [1, 0], [0, 1], [1, 1]] as const) lo = Math.min(lo, s.height[(cy + dy) * step * GRID + (cx + dx) * step]!);\n if (lo >= L) continue;\n const v = P.length / 3, x = ox + cx * step, y = oy + cy * step;\n P.push(x, y, L, x + step, y, L, x, y + step, L, x + step, y + step, L);\n I.push(v, v + 1, v + 3, v, v + 3, v + 2);\n }\n }\n if (!I.length) return null;\n const nv = P.length / 3, nor = new Float32Array(nv * 3), uv = new Float32Array(nv * 2);\n for (let i = 0; i < nv; i++) { nor[i * 3 + 2] = 1; uv[i * 2] = P[i * 3]! / 8; uv[i * 2 + 1] = P[i * 3 + 1]! / 8; }\n return { name: 'Вода', texture: '', color: [0.16, 0.3, 0.36], vertexCount: nv, pos: Float32Array.from(P), nor, uv, bone: new Float32Array(0), weight: new Float32Array(0),\n indices: nv > 65535 ? Uint32Array.from(I) : Uint16Array.from(I), skinned: false, vertexAlpha: new Float32Array(nv).fill(0.7) };\n}\n\nconst isTerrainSize = (s: number) => s >= 23536 && s <= 26000 && (s - 23536) % 12 === 0;\n\n/** World index: world files, terrain by sector number, object files (WorldSector) by X, Y. Built once per archive. */\ninterface WorldIndex { files: FatRef[]; terrain: Map<number, FatRef>; terrainEnd: number; objects?: Promise<Map<number, FatRef[]>>; full?: boolean }\ninterface TerrainGroup { start: number; terrain: Map<number, FatRef>; end: number }\nconst indexCache = new WeakMap<DuniaArchiveApi, Map<string, Promise<WorldIndex>>>();\nconst groupCache = new WeakMap<DuniaArchiveApi, Map<unknown, Promise<TerrainGroup[]>>>();\n\n/** Terrain among records: the sector number is at the start of the file (partial decompression). */\nasync function terrainOf(api: DuniaArchiveApi, files: FatRef[]): Promise<Array<{ ref: FatRef; id: number }>> {\n const refs = files.filter((r) => isTerrainSize(r.fe.size));\n const heads = await mapLimit(refs, 16, async (r) => { try { return await api.readRef(r, 0x18); } catch { return null; } });\n const out: Array<{ ref: FatRef; id: number }> = [];\n heads.forEach((h, i) => {\n if (!h || h.length < 0x18) return;\n const dv = new DataView(h.buffer, h.byteOffset, h.byteLength);\n if (dv.getUint32(0, true) === TER_MAGIC && dv.getUint32(4, true) === 7) out.push({ ref: refs[i]!, id: dv.getUint32(0x14, true) });\n });\n return out;\n}\n\n/** .dat terrain blocks in order: a new block starts when a sector number repeats (worlds are written in sequence). */\nfunction terrainGroups(api: DuniaArchiveApi, dat: FatRef['dat']): Promise<TerrainGroup[]> {\n let m = groupCache.get(api);\n if (!m) { m = new Map(); groupCache.set(api, m); }\n let p = m.get(dat);\n if (!p) {\n p = (async () => {\n const groups: TerrainGroup[] = [];\n let cur: TerrainGroup | null = null;\n for (const t of await terrainOf(api, api.datRefs(dat))) {\n if (!cur || cur.terrain.has(t.id)) { cur = { start: t.ref.fe.off, terrain: new Map(), end: 0 }; groups.push(cur); }\n cur.terrain.set(t.id, t.ref);\n cur.end = t.ref.fe.off;\n }\n return groups;\n })();\n m.set(dat, p);\n }\n return p;\n}\n\n/**\n * World index. Normally a world's files are the .dat range between the previous world's .sectorsdep.fcb and its own (worlds.dat). If\n * that range has no terrain or sector numbers repeat (dlc1.dat: first the sectors of all worlds, then their named files), the k-th .dat terrain block belongs to the k-th world\n * in .sectorsdep.fcb order, and the world's files run from the start of its block to the start of the next.\n */\nexport function worldIndex(api: DuniaArchiveApi, world: DuniaWorld): Promise<WorldIndex> {\n let m = indexCache.get(api);\n if (!m) { m = new Map(); indexCache.set(api, m); }\n let p = m.get(world.name);\n if (!p) {\n p = (async () => {\n let files = world.files();\n const own = await terrainOf(api, files);\n let terrain = new Map(own.map((t) => [t.id, t.ref]));\n if (!terrain.size || terrain.size < own.length) { // no terrain, or several worlds in the range\n const groups = await terrainGroups(api, world.dat);\n const worlds = api.duniaWorlds().filter((w) => w.dat === world.dat).sort((x, y) => x.end - y.end);\n const k = worlds.findIndex((w) => w.name === world.name);\n if (groups.length === worlds.length && k >= 0) {\n const g = groups[k]!, next = groups[k + 1]?.start ?? Infinity;\n terrain = g.terrain;\n files = api.datRefs(world.dat).filter((r) => r.fe.off >= g.start && r.fe.off < next);\n }\n }\n let terrainEnd = 0;\n for (const r of terrain.values()) terrainEnd = Math.max(terrainEnd, r.fe.off);\n return { files, terrain, terrainEnd };\n })();\n m.set(world.name, p);\n }\n return p;\n}\n\n/**\n * World object files: .fcb with a WorldSector root. In large worlds they follow the terrain block (mission layer objects; next to\n * the terrain are only vegetation collection sectors), in small worlds they are mixed with the terrain. X, Y are the root's first\n * values, read from the start of the file.\n */\nexport function worldObjects(api: DuniaArchiveApi, world: DuniaWorld, idx: WorldIndex): Promise<Map<number, FatRef[]>> {\n idx.objects ??= (async () => {\n // in .dat order; the object block ends before the world resources (models, textures, sound): after 2 empty\n // batches in a row reading stops. In small worlds (multiplayer maps) objects are mixed with the terrain:\n // the whole world is scanned there; in large ones, first the part after the terrain.\n const out = new Map<number, FatRef[]>();\n const scan = async (cand: FatRef[]) => {\n const CHUNK = 2048;\n let empty = 0, found = 0;\n for (let i = 0; i < cand.length && empty < 2; i += CHUNK) {\n const part = cand.slice(i, i + CHUNK);\n const heads = await mapLimit(part, 32, async (r) => { try { return await api.readRef(r, 64); } catch { return null; } });\n let here = 0;\n heads.forEach((h, k) => {\n const xy = h && worldSectorXY(h);\n if (!xy) return;\n const key = xy[1] * 65536 + xy[0];\n let l = out.get(key);\n if (!l) { l = []; out.set(key, l); }\n l.push(part[k]!);\n here++;\n });\n found += here;\n empty = here || !found ? 0 : empty + 1;\n }\n return found;\n };\n const files = idx.files.filter((r) => r.fe.size >= 64);\n const before = files.filter((r) => r.fe.off <= idx.terrainEnd && !isTerrainSize(r.fe.size)), after = files.filter((r) => r.fe.off > idx.terrainEnd);\n if (before.length < SMALL_WORLD) { await scan([...before, ...after]); idx.full = true; }\n else if (!(await scan(after))) { await scan(before); idx.full = true; }\n return out;\n })();\n return idx.objects;\n}\n\n/** X, Y from the start of an .fcb with a WorldSector root (otherwise null). */\nexport function worldSectorXY(h: Uint8Array): [number, number] | null {\n if (h.length < 24) return null;\n const dv = new DataView(h.buffer, h.byteOffset, h.byteLength);\n if (dv.getUint32(0, true) !== 0x4643626e) return null;\n let p = 16;\n if (h[p]! >= 0xfe) p += 4;\n p++;\n if (dv.getUint32(p, true) !== H.WorldSector) return null;\n p += 4;\n let n = h[p++]!;\n if (n >= 0xfe) return null;\n let x = -1, y = -1;\n for (; n > 0 && p + 5 <= h.length; n--) {\n const k = dv.getUint32(p, true), sz = h[p + 4]!;\n p += 5;\n if (sz >= 0xfe || p + sz > h.length) break;\n if (sz === 4 && k === H.X) x = dv.getUint32(p, true);\n if (sz === 4 && k === H.Y) y = dv.getUint32(p, true);\n p += sz;\n }\n return x >= 0 && y >= 0 ? [x, y] : null;\n}\n\nexport async function loadDuniaLevel(text: string, main: VFile, ctx: LoadContext): Promise<Model> {\n const spec = JSON.parse(text.slice(LEVEL_MARK.length)) as LevelSpec;\n const arc = main.archive ?? ctx.archives.find((a) => duniaApi(a)) ?? null;\n const api = duniaApi(arc);\n const world = api?.duniaWorld(spec.world);\n if (!arc || !api || !world) throw new Error('Уровень Far Cry 2 открывается из архивов игры (папка Data_Win32)');\n const desc = worldDescriptor(await api.readRef(world.gameXml));\n const width = desc.countX || spec.x0 + spec.w;\n const idx = await worldIndex(api, world);\n const sectors = new Map<number, TerrainSector>();\n const want: FatRef[] = [];\n for (let y = spec.y0; y < spec.y0 + spec.h; y++) for (let x = spec.x0; x < spec.x0 + spec.w; x++) { const r = idx.terrain.get(y * width + x); if (r) want.push(r); }\n await mapLimit(want, 8, async (r) => { try { const s = parseTerrainSector(await api.readRef(r)); sectors.set(s.id, s); } catch { /* damaged */ } });\n const step = terrainStep(spec.w, spec.h);\n const terrain = terrainMeshes(spec, sectors, width, desc.layers, step);\n const water = waterMesh(spec, sectors, width, step);\n if (water) terrain.push(water);\n\n const B = new LevelBuilder();\n const info = [`Far Cry 2 (Dunia): ${spec.world} · ${spec.name} — сектора ${spec.x0}…${spec.x0 + spec.w - 1} × ${spec.y0}…${spec.y0 + spec.h - 1} (${spec.w * 64}×${spec.h * 64} м)`,\n `Ландшафт: секторов ${sectors.size} из ${spec.w * spec.h}, сетка ${step} м, слоёв ${terrain.length}`];\n let shown = 0;\n if (!spec.terrainOnly) {\n const objs = await worldObjects(api, world, idx);\n const refs: FatRef[] = [];\n const inReg = (x: number, y: number) => x >= spec.x0 && x < spec.x0 + spec.w && y >= spec.y0 && y < spec.y0 + spec.h;\n for (let y = spec.y0; y < spec.y0 + spec.h; y++) for (let x = spec.x0; x < spec.x0 + spec.w; x++) refs.push(...(objs.get(y * 65536 + x) ?? []));\n if (!idx.full && want.length) {\n // large world: collection sectors sit in the terrain block next to their terrain (Morton order by map)\n const lo = Math.min(...want.map((r) => r.fe.off)), hi = Math.max(...want.map((r) => r.fe.off));\n const cand = idx.files.filter((r) => r.fe.off > lo - 65536 && r.fe.off < hi + 65536 && r.fe.size >= 64 && !isTerrainSize(r.fe.size));\n const heads = await mapLimit(cand, 32, async (r) => { try { return await api.readRef(r, 64); } catch { return null; } });\n heads.forEach((h, i) => { const xy = h && worldSectorXY(h); if (xy && inReg(xy[0], xy[1]) && !refs.includes(cand[i]!)) refs.push(cand[i]!); });\n }\n shown = await placeEntities(arc, api, world.name, refs, B, (s) => info.push(s));\n } else info.push('Весь мир — только ландшафт; объекты — на картах worlds/…/maps/*.fc2level');\n const meshes = [...B.build(), ...terrain];\n if (!meshes.length) throw new Error('В области уровня нет секторов');\n if (!shown && !spec.terrainOnly) info.push('Объекты не найдены');\n info.push(water ? `Вода: по уровню сектора (${[...new Set([...sectors.values()].map((x) => x.water).filter(Boolean))].slice(0, 5).join(', ')} м), полупрозрачная плоскость там, где земля ниже` : 'Воды в области нет');\n info.push('Не показаны: небо; деревья RealTree — приближённо');\n return { meshes, instances: B.scene, skeleton: null, motions: [], bounds: boundsOf(meshes), info, defaultZUp: true };\n}\n\nasync function placeEntities(arc: Archive, api: DuniaArchiveApi, worldName: string, sectorRefs: FatRef[], B: LevelBuilder, note: (s: string) => void): Promise<number> {\n // the world's archetype library\n let lib = new Map<string, { model: string; parts: string[] }>();\n const le = arc.lookup(`worlds/${worldName}/generated/entitylibrary.fcb`);\n if (le) { try { lib = entityLibrary(parseFcb(await arc.read(le))); } catch { note('entitylibrary.fcb не прочитана'); } }\n const ents: LevelEntity[] = [], unknown = new Set<string>();\n let noModel = 0, sectorsWithObjects = 0, collected = 0;\n const markers: LevelMarker[] = [];\n await mapLimit(sectorRefs, 8, async (r) => {\n let root: FcbObject;\n try { root = parseFcb(await api.readRef(r)); } catch { return; }\n const s = sectorEntities(root, lib);\n if (s.list.length) sectorsWithObjects++;\n ents.push(...s.list); noModel += s.noModel; markers.push(...s.markers);\n for (const c of sectorCollections(root)) {\n const name = api.nameOfHash(c.hash);\n ents.push({ name: name?.split('\\\\').pop()?.replace(/\\.xbg$/i, '') ?? `#${c.hash.toString(16)}`, model: `#${c.hash.toString(16).padStart(8, '0')}`, parts: [], pos: c.pos, angles: [0, 0, c.yaw], layer: 'collections' });\n collected++;\n }\n for (const a of s.unknownArchetype) unknown.add(a);\n });\n ents.sort((a, b) => a.name.localeCompare(b.name) || a.pos[0]! - b.pos[0]! || a.pos[1]! - b.pos[1]! || a.pos[2]! - b.pos[2]!);\n // models: near LOD; if together they exceed the triangle budget, the \"heaviest\" ones (count × triangles) get the distant LOD\n // collections (grass, bushes, rocks: hundreds of thousands of instances per map) separately: LOD1 and thinning to their own budget\n const counts = new Map<string, number>(), colCounts = new Map<string, number>();\n for (const e of ents) { const m = e.layer === 'collections' ? colCounts : counts; m.set(e.model.toLowerCase(), (m.get(e.model.toLowerCase()) ?? 0) + 1); }\n const parsedModels = new Map<string, { u: Uint8Array; x: Xbg } | null>();\n // model by path or by hash (#xxxxxxxx: collections); non-.xbg ones (RealTree trees, .rtr) are skipped\n const realtrees = new Set<string>(), rtMeshes = new Map<string, Mesh[]>();\n // RealTree: an approximate mesh (branches as tubes, foliage as cards) with bark and foliage materials from .xbm\n const realTreeOf = async (t: RealTree): Promise<Mesh[]> => {\n const mats = await Promise.all(t.materials.map(async (m) => {\n const e = arc.lookup(m.replace(/\\.mlm$/i, '.xbm'));\n try { return e ? { path: m, x: parseXbm(await arc.read(e)) } : null; } catch { return null; }\n }));\n const slot = (x: XbmMaterial | undefined, ...keys: string[]) => { for (const k of keys) if (x?.textures[k]) return x.textures[k]; return undefined; };\n const pick = (trunk: boolean) => {\n const m = mats.find((q) => q && /trunk|bark/i.test(q.x.shader + q.x.name) === trunk) ?? mats[trunk ? 0 : 1] ?? null;\n return { material: m?.path, texture: slot(m?.x, 'DiffuseTexture1', 'DiffuseTexture') ?? '', normal: slot(m?.x, 'NormalTexture1', 'NormalTexture') };\n };\n return realTreeMeshes(t, pick(true), pick(false));\n };\n const displayName = (k: string) => (k.startsWith('#') ? api.nameOfHash(parseInt(k.slice(1), 16)) ?? k : k);\n const readModel = async (k: string) => {\n if (k.startsWith('#')) { const r = api.refByHash(parseInt(k.slice(1), 16)); return r ? api.readRef(r) : null; }\n const e = arc.lookup(k);\n return e ? arc.read(e) : null;\n };\n await mapLimit([...new Set([...counts.keys(), ...colCounts.keys()])], 8, async (k) => {\n try {\n const u = await readModel(k);\n if (!u) { parsedModels.set(k, null); return; }\n if (String.fromCharCode(u[0]!, u[1]!, u[2]!, u[3]!) !== 'HSEM') {\n parsedModels.set(k, null);\n if (isRealTree(u)) { realtrees.add(k); try { rtMeshes.set(k, await realTreeOf(parseRealTree(u))); } catch { /* not decoded */ } }\n return;\n }\n parsedModels.set(k, { u, x: parseXbg(u) });\n } catch { parsedModels.set(k, null); }\n });\n const lodOf = new Map<string, number>();\n const trisOf = (k: string, l: number) => {\n const rt = rtMeshes.get(k);\n if (rt) return rt.reduce((n, m) => n + m.indices.length / 3, 0);\n const p = parsedModels.get(k), L = p?.x.lods[Math.min(l, (p?.x.lods.length ?? 1) - 1)];\n if (!p || !L) return 0;\n // as in xbgMeshes: without parts hidden by default (damage states, night headlights)\n let n = 0;\n L.prims.forEach((pr, i) => { const name = p.x.parts[pr.part]?.name ?? ''; if (!/STATE0*[2-9]$|_N_/i.test(name.replace(/_LOD\\d+$/i, ''))) n += ((L.prims[i + 1]?.firstIndex ?? L.icount) - pr.firstIndex) / 3; });\n return n;\n };\n let total = 0;\n for (const [k, n] of counts) total += n * trisOf(k, 0);\n const lodNote = total;\n if (total > TRI_BUDGET) {\n const order = [...counts].sort((a, b) => b[1] * trisOf(b[0], 0) - a[1] * trisOf(a[0], 0));\n for (const [k, n] of order) {\n if (total <= TRI_BUDGET) break;\n const p = parsedModels.get(k);\n if (!p || p.x.lods.length < 2) continue;\n const far = Math.min(2, p.x.lods.length - 1);\n total -= n * (trisOf(k, 0) - trisOf(k, far));\n lodOf.set(k, far);\n }\n }\n const colLod = (k: string) => Math.min(COLLECTION_LOD, (parsedModels.get(k)?.x.lods.length ?? 1) - 1);\n // the budget is split equally between collection models: rare ones (rocks, bushes) stay whole, bulk ones (grass) are thinned\n const colKeepOf = new Map<string, number>();\n let colTotal = 0;\n {\n const rows = [...colCounts].map(([k, n]) => ({ k, t: n * trisOf(k, colLod(k)) })).filter((r) => r.t > 0).sort((a, b) => a.t - b.t);\n let left = COLLECTION_BUDGET;\n rows.forEach((r, i) => { colTotal += r.t; const keep = Math.min(1, left / (rows.length - i) / r.t); colKeepOf.set(r.k, keep); left -= keep * r.t; });\n }\n const matCache = new Map<string, Promise<XbmMaterial | null>>();\n const protos = new Map<string, Promise<{ pi: number; parts: Mesh[] } | null>>();\n const missing = new Set<string>();\n let skinned = 0;\n const protoOf = (model: string, parts: string[], lod: number) => {\n const mk = model.toLowerCase(), key = `${mk}|${lod}|${parts.join(';')}`;\n let pr = protos.get(key);\n if (!pr) {\n pr = (async () => {\n const rt = rtMeshes.get(mk);\n if (rt?.length) return { pi: B.proto(displayName(model), rt, 'static', (m) => m.name), parts: rt };\n const p = parsedModels.get(mk);\n if (!p) { if (!realtrees.has(mk)) missing.add(model); return null; }\n try {\n const r = await xbgMeshes(p.u, arc, matCache, lod, p.x);\n // with a skeleton (people, animals): in the bind pose; a kit without a part list gets one variant per slot\n const all = r.skinned ? bakeBindPose(r.x, r.meshes) : r.meshes;\n if (r.skinned) skinned++;\n const want = new Set(parts.map((q) => q.toUpperCase()));\n const kit = want.size ? new Set<string>() : kitHiddenParts(all.map((m) => m.name.split(' · ')[0]!));\n const list = all.filter((m) => { const part = m.name.split(' · ')[0]!; return !m.defaultHidden && !kit.has(part) && (!want.size || want.has(part.toUpperCase())); });\n if (!list.length) return null;\n const label = (m: Mesh) => m.material?.split('\\\\').pop() || m.texture.split('\\\\').pop() || '?';\n return { pi: B.proto(displayName(model), list, 'static', label), parts: list };\n } catch { missing.add(model); return null; }\n })();\n protos.set(key, pr);\n }\n return pr;\n };\n let placed = 0, placedCol = 0, treeInst = 0;\n const treeMarks: Array<{ name: string; pos: number[] }> = [];\n const pick = rngOf(0x5eed);\n let thinned = 0;\n for (const e of ents) {\n const col = e.layer === 'collections', mk = e.model.toLowerCase();\n if (col && (colKeepOf.get(mk) ?? 1) < 1 && pick() >= colKeepOf.get(mk)!) { thinned++; continue; }\n const pr = await protoOf(e.model, e.parts, col ? colLod(mk) : lodOf.get(mk) ?? 0);\n if (!pr) { if (realtrees.has(e.model.toLowerCase())) { treeInst++; treeMarks.push({ name: e.name, pos: e.pos }); } continue; }\n const M = new Float64Array(16);\n compose(e.pos, anglesQuat(e.angles), [1, 1, 1], M);\n const path = displayName(e.model);\n const folder = path.startsWith('#') ? 'collections' : path.replace(/\\\\/g, '/').split('/').slice(0, -1).join('/') || 'graphics';\n B.place(pr.pi, pr.parts, M, e.name, e.layer === 'collections' ? `collections/${folder}` : folder);\n placed++;\n if (e.layer === 'collections') placedCol++;\n }\n // markers (hidden; enabled in the mesh list): particles, lights, RealTree trees (their mesh is built by the engine, here only the placement)\n const mark = (list: Array<{ name: string; pos: number[] }>, label: string, color: number[], size: number, layer: string) => {\n if (!list.length) return;\n const m = markerMesh(label, size, color), pi = B.proto(label, [m], 'marker', () => label, true);\n for (const x of list) { const M = new Float64Array(16); compose(x.pos, [1, 0, 0, 0], [1, 1, 1], M); B.place(pi, [m], M, x.name, layer); }\n };\n mark(markers.filter((x) => x.kind === 'particle'), 'Метки · частицы', [0.3, 0.8, 1], 0.3, 'particle');\n mark(markers.filter((x) => x.kind === 'light'), 'Метки · свет', [1, 0.85, 0.3], 0.3, 'light');\n mark(treeMarks, 'Метки · деревья RealTree (не разобраны)', [0.25, 0.75, 0.2], 1.5, 'realtree');\n note(`Объекты: ${placed} из ${ents.length} с моделью (секторов с объектами ${sectorsWithObjects}, файлов секторов ${sectorRefs.length}; моделей ${[...protos.keys()].length}, треугольников ${B.tris})`);\n if (lodOf.size) note(`Дальний LOD у ${lodOf.size} моделей (ближние LOD всех объектов — ${Math.round(lodNote / 1e6 * 10) / 10} млн треугольников, бюджет ${TRI_BUDGET / 1e6} млн)`);\n if (collected) note(`Коллекции (трава, кусты, камни, мусор — кисть редактора), LOD${COLLECTION_LOD}: ${placedCol} из ${collected}${thinned ? ` (массовые модели прорежены: всего ${Math.round(colTotal / 1e5) / 10} млн треугольников, бюджет ${COLLECTION_BUDGET / 1e6} млн)` : ''}${realtrees.size ? `; деревья и кусты RealTree (.rtr): моделей ${realtrees.size}, приближённо — ветви трубками по узлам, листва карточками у концов ветвей (сетку игра строит сама)${treeInst ? `, не разобрано ${treeInst} экземпляров` : ''}` : ''}`);\n if (noModel) note(`Без модели (логика, точки, звук, частицы, свет): ${noModel}`);\n if (markers.length || treeMarks.length) note(`Метки (скрыты, включаются в списке мешей): частицы ${markers.filter((x) => x.kind === 'particle').length}, свет ${markers.filter((x) => x.kind === 'light').length}, деревья RealTree ${treeMarks.length}`);\n if (skinned) note(`Модели со скелетом (люди, животные) — в bind-позе: ${skinned}`);\n if (missing.size) note(`Модели не найдены: ${missing.size} (${[...missing].slice(0, 3).join(', ')}${missing.size > 3 ? '…' : ''})`);\n if (unknown.size) note(`Архетипы не найдены в entitylibrary.fcb: ${unknown.size} (${[...unknown].slice(0, 2).join(', ')})`);\n return placed;\n}\n"],"names":["pick","n"],"mappings":"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"}
|
package/dist/mab.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NodeMotion } from '@gmvjs/core/types';
|
|
2
|
-
/**
|
|
2
|
+
/** Quaternion from 6 bytes -> [w, x, y, z]. */
|
|
3
3
|
export declare function unpackQuat(a: number, b: number, c: number): number[];
|
|
4
4
|
export interface MabAnim {
|
|
5
5
|
version: number;
|
|
@@ -14,11 +14,11 @@ export interface MabAnim {
|
|
|
14
14
|
posKeys: Map<number, number[][]>;
|
|
15
15
|
}
|
|
16
16
|
export declare function parseMab(u: Uint8Array): MabAnim;
|
|
17
|
-
/**
|
|
17
|
+
/** Quaternion [w,x,y,z] of a matrix's rotation part (column-major). */
|
|
18
18
|
export declare function matQuat(m: ArrayLike<number>): number[];
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
* (
|
|
20
|
+
* Animation for model nodes: bones[i] is the node for bone i (O2BM list). fix: for bones whose parent in the model is not a bone
|
|
21
|
+
* (pelvis), the matrix C = (parent world pose)⁻¹ · T that maps the bone pose from the animation frame into the parent node's space.
|
|
22
22
|
*/
|
|
23
23
|
export declare function buildMabMotion(name: string, a: MabAnim, bones: readonly number[], fix?: ReadonlyMap<number, ArrayLike<number>>, root?: {
|
|
24
24
|
bone: number;
|
|
@@ -26,10 +26,10 @@ export declare function buildMabMotion(name: string, a: MabAnim, bones: readonly
|
|
|
26
26
|
pos: number[];
|
|
27
27
|
}): NodeMotion;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* u16
|
|
32
|
-
*
|
|
29
|
+
* Animation skeleton .skeleton ("LKS\0", SKL backwards; u32 0x12, u32 skeleton hash, u32 7, u16 bone count, u16).
|
|
30
|
+
* Record: u32 skeleton hash, u32 7, quaternion xyzw, position f32×3, f32, u16 number, u16 parent (0xffff = root),
|
|
31
|
+
* u16 first child, u16 next, flags (twist bones also have a driver bone and a fraction), u32 name hash, u32 length,
|
|
32
|
+
* name, 0, tail. The bone number in .mab masks is the record number. Records are found by the "skeleton hash + 7" marker.
|
|
33
33
|
*/
|
|
34
34
|
export interface DuniaSkelBone {
|
|
35
35
|
name: string;
|
package/dist/mab.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mab.js","sources":["../src/mab.ts"],"sourcesContent":["// Анимации Dunia (Far Cry 2) .mab — разобрано по файлам игры (заголовок и упаковка кватернионов — как в заметках\n// PY-DuniaAnimationExtractor для Far Cry 3; раскладка разделов Far Cry 2, маски костей и блоки ключей — по образцам).\n// 0x00: u8 0x4c (Far Cry 2), u8 0, u8 версия (2 — 15 ключей/с, 3 — 30), u8 0; u32 CRC; 8 байт.\n// 0x10: четыре маски костей по 160 бит (5 × u32): 0 — постоянный поворот, 1 — анимированный поворот,\n// 2 — постоянное смещение, 3 — анимированное смещение. Номер бита — номер кости в списке O2BM модели.\n// 0x84: f32 длительность (с); 0x88: 10 × u32 смещений разделов (от 0x10): 0 — смещение корня по ключам, 1 — поворот\n// корня по ключам, 2 — постоянные повороты, 3 — повороты по ключам, 4 — постоянные смещения, 5 — смещения по ключам,\n// дальше — события (PlaySound…) и прочее.\n// Корень: u16 1, u16 ключей, u32; затем ключи (кватернион 6 байт / f32×3).\n// Постоянные: u32 n, u32 0, n × (кватернион 6 байт / f32×3) — по костям маски в порядке битов.\n// Смещения по ключам: u16 дорожек, u16 ключей, u32; по дорожке — все ключи f32×3.\n// Повороты по ключам: u16 дорожек, u16 ключей, u32; u32 смещения блоков (по 8 ключей; последнее — конец);\n// блок: первый ключ блока у всех дорожек (6 байт), по байту-маске на дорожку (бит k — есть ключ 8·b+1+k),\n// выравнивание до 2, затем по дорожкам их ключи по маске. Пропущенные ключи — интерполяция.\n// Кватернион 6 байт: три u16, компонента = (v & 0x7fff) · 4.315969e-5 − 0.7071068, четвёртая — из нормы;\n// старшие биты первых двух слов — какая компонента пропущена (как «три меньших»).\nimport type { NodeChannel, NodeMotion } from '@gmvjs/core/types';\n\nconst K = 4.315969e-5, H = 0.7071068;\n\n/** Кватернион из 6 байт -> [w, x, y, z]. */\nexport function unpackQuat(a: number, b: number, c: number): number[] {\n const f1 = (a & 0x7fff) * K - H, f2 = (b & 0x7fff) * K - H, f3 = (c & 0x7fff) * K - H;\n const f4 = Math.sqrt(Math.max(0, 1 - f1 * f1 - f2 * f2 - f3 * f3));\n const A = a >> 15, B = b >> 15;\n // (x, y, z, w)\n const q = A === 0 && B === 1 ? [f1, f2, f4, f3] : A === 1 && B === 1 ? [f1, f2, f3, f4] : A === 1 ? [f1, f4, f2, f3] : [f4, f1, f2, f3];\n return [q[3]!, q[0]!, q[1]!, q[2]!];\n}\n\nexport interface MabAnim {\n version: number; duration: number; rate: number; keys: number;\n rootRot: number[][] | null; rootPos: number[][] | null;\n constRot: Map<number, number[]>; rotKeys: Map<number, number[][]>;\n constPos: Map<number, number[]>; posKeys: Map<number, number[][]>;\n}\n\nconst maskBits = (dv: DataView, at: number): number[] => {\n const out: number[] = [];\n for (let w = 0; w < 5; w++) { const v = dv.getUint32(at + w * 4, true); for (let b = 0; b < 32; b++) if (v & (1 << b)) out.push(w * 32 + b); }\n return out;\n};\n\nconst nlerp = (a: number[], b: number[], t: number) => {\n const d = a[0]! * b[0]! + a[1]! * b[1]! + a[2]! * b[2]! + a[3]! * b[3]!, s = d < 0 ? -1 : 1;\n const q = a.map((v, i) => v + (s * b[i]! - v) * t), l = Math.hypot(...q) || 1;\n return q.map((v) => v / l);\n};\n\nexport function parseMab(u: Uint8Array): MabAnim {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 0xb0 || u[0] !== 0x4c) throw new Error(`Анимация Dunia: версия 0x${(u[0] ?? 0).toString(16)} не поддерживается (нужна 0x4c — Far Cry 2)`);\n const version = u[2]!, sec = Array.from({ length: 10 }, (_, i) => { const o = dv.getUint32(0x88 + i * 4, true); return o ? o + 0x10 : 0; });\n const masks = [0, 1, 2, 3].map((m) => maskBits(dv, 0x10 + m * 20));\n const duration = dv.getFloat32(0x84, true);\n const q6 = (o: number) => unpackQuat(dv.getUint16(o, true), dv.getUint16(o + 2, true), dv.getUint16(o + 4, true));\n const v3 = (o: number) => [dv.getFloat32(o, true), dv.getFloat32(o + 4, true), dv.getFloat32(o + 8, true)];\n const check = (o: number, n: number) => { if (o + n > u.length) throw new Error('.mab: раздел за концом файла'); };\n if (!sec[3]) throw new Error('.mab: нет поворотов по ключам');\n const nb = dv.getUint16(sec[3], true), keys = dv.getUint16(sec[3] + 2, true);\n if (nb !== masks[1]!.length) throw new Error(`.mab: дорожек ${nb}, а в маске ${masks[1]!.length}`);\n // ключей в секунду: по длительности (близкое к 15/30 — округляется), без неё — по версии (2 — 15, 3 — 30)\n const raw = duration > 0 && keys > 1 ? (keys - 1) / duration : version >= 3 ? 30 : 15;\n const rate = [15, 30].find((r) => Math.abs(raw - r) < r * 0.02) ?? raw;\n const anim: MabAnim = { version, duration, rate, keys, rootRot: null, rootPos: null, constRot: new Map(), rotKeys: new Map(), constPos: new Map(), posKeys: new Map() };\n // корень\n if (sec[1]) { const n = dv.getUint16(sec[1] + 2, true); check(sec[1] + 8, n * 6); anim.rootRot = Array.from({ length: n }, (_, k) => q6(sec[1]! + 8 + k * 6)); }\n if (sec[0]) { const n = dv.getUint16(sec[0] + 2, true); check(sec[0] + 8, n * 12); anim.rootPos = Array.from({ length: n }, (_, k) => v3(sec[0]! + 8 + k * 12)); }\n // постоянные\n if (sec[2]) { const n = dv.getUint32(sec[2], true); check(sec[2] + 8, n * 6); masks[0]!.slice(0, n).forEach((b, i) => anim.constRot.set(b, q6(sec[2]! + 8 + i * 6))); }\n if (sec[4]) { const n = dv.getUint32(sec[4], true); check(sec[4] + 8, n * 12); masks[2]!.slice(0, n).forEach((b, i) => anim.constPos.set(b, v3(sec[4]! + 8 + i * 12))); }\n if (sec[5]) {\n const nt = dv.getUint16(sec[5], true), nk = dv.getUint16(sec[5] + 2, true);\n check(sec[5] + 8, nt * nk * 12);\n masks[3]!.slice(0, nt).forEach((b, t) => anim.posKeys.set(b, Array.from({ length: nk }, (_, k) => v3(sec[5]! + 8 + (t * nk + k) * 12))));\n }\n // повороты по ключам: блоки по 8\n const nblk = nb ? Math.ceil(keys / 8) : 0; // без анимированных поворотов (только постоянные позы) блоков нет\n check(sec[3] + 8, (nblk + 1) * 4);\n const offs = Array.from({ length: nblk + 1 }, (_, i) => dv.getUint32(sec[3]! + 8 + i * 4, true));\n const tracks: Array<Array<number[] | null>> = Array.from({ length: nb }, () => new Array(keys).fill(null));\n for (let bi = 0; bi < nblk; bi++) {\n let p = sec[3] + offs[bi]!;\n check(p, nb * 7);\n for (let t = 0; t < nb; t++) if (bi * 8 < keys) tracks[t]![bi * 8] = q6(p + t * 6);\n p += nb * 6;\n const mask = u.subarray(p, p + nb);\n p += nb; p += p & 1;\n for (let t = 0; t < nb; t++) {\n for (let k = 0; k < 8; k++) {\n if (!(mask[t]! & (1 << k))) continue;\n check(p, 6);\n const key = bi * 8 + 1 + k;\n if (key < keys) tracks[t]![key] = q6(p);\n p += 6;\n }\n }\n }\n tracks.forEach((tr, t) => {\n // пропуски: интерполяция между соседними ключами (последний — держится)\n let last = -1;\n for (let k = 0; k < keys; k++) {\n if (!tr[k]) continue;\n if (last >= 0 && k - last > 1) for (let j = last + 1; j < k; j++) tr[j] = nlerp(tr[last]!, tr[k]!, (j - last) / (k - last));\n last = k;\n }\n for (let k = last + 1; k < keys && last >= 0; k++) tr[k] = tr[last]!;\n if (last >= 0) anim.rotKeys.set(masks[1]![t]!, tr as number[][]);\n });\n return anim;\n}\n\nconst qmul = (p: number[], q: number[]) => [\n p[0]! * q[0]! - p[1]! * q[1]! - p[2]! * q[2]! - p[3]! * q[3]!, p[0]! * q[1]! + p[1]! * q[0]! + p[2]! * q[3]! - p[3]! * q[2]!,\n p[0]! * q[2]! - p[1]! * q[3]! + p[2]! * q[0]! + p[3]! * q[1]!, p[0]! * q[3]! + p[1]! * q[2]! - p[2]! * q[1]! + p[3]! * q[0]!,\n];\n\n/** Кватернион [w,x,y,z] поворотной части матрицы (column-major). */\nexport function matQuat(m: ArrayLike<number>): number[] {\n const m00 = m[0]!, m11 = m[5]!, m22 = m[10]!, tr = m00 + m11 + m22;\n let q: number[];\n if (tr > 0) { const s = Math.sqrt(tr + 1) * 2; q = [s / 4, (m[6]! - m[9]!) / s, (m[8]! - m[2]!) / s, (m[1]! - m[4]!) / s]; }\n else if (m00 > m11 && m00 > m22) { const s = Math.sqrt(1 + m00 - m11 - m22) * 2; q = [(m[6]! - m[9]!) / s, s / 4, (m[4]! + m[1]!) / s, (m[8]! + m[2]!) / s]; }\n else if (m11 > m22) { const s = Math.sqrt(1 + m11 - m00 - m22) * 2; q = [(m[8]! - m[2]!) / s, (m[4]! + m[1]!) / s, s / 4, (m[9]! + m[6]!) / s]; }\n else { const s = Math.sqrt(1 + m22 - m00 - m11) * 2; q = [(m[1]! - m[4]!) / s, (m[8]! + m[2]!) / s, (m[9]! + m[6]!) / s, s / 4]; }\n const l = Math.hypot(...q) || 1;\n return q.map((v) => v / l);\n}\n\n/**\n * Анимация для узлов модели: bones[i] — узел для кости i (список O2BM). fix — для костей, чей родитель в модели не кость\n * (таз): матрица C = (мировая поза родителя)⁻¹ · T, переводящая позу кости из кадра анимации в пространство родителя-узла.\n */\nexport function buildMabMotion(name: string, a: MabAnim, bones: readonly number[], fix: ReadonlyMap<number, ArrayLike<number>> = new Map(),\n root?: { bone: number; rot: number[]; pos: number[] }): NodeMotion {\n const chan = new Map<number, NodeChannel>();\n const get = (b: number) => { const n = bones[b]; if (n === undefined || n < 0) return null; let c = chan.get(n); if (!c) { c = { pos: null, rot: null, scl: null }; chan.set(n, c); } return c; };\n const rotOf = (b: number, q: number[]) => { const C = fix.get(b); return C ? qmul(matQuat(C), q) : q; };\n const posOf = (b: number, p: number[]) => { const C = fix.get(b); return C ? [0, 1, 2].map((i) => C[i]! * p[0]! + C[4 + i]! * p[1]! + C[8 + i]! * p[2]! + C[12 + i]!) : p; };\n for (const [b, q] of a.constRot) { const c = get(b); if (c) c.rot = [[0, rotOf(b, q)]]; }\n for (const [b, list] of a.rotKeys) { const c = get(b); if (c) c.rot = list.map((q, k) => [k, rotOf(b, q)]); }\n for (const [b, v] of a.constPos) { const c = get(b); if (c) c.pos = [[0, posOf(b, v)]]; }\n for (const [b, list] of a.posKeys) { const c = get(b); if (c) c.pos = list.map((p, k) => [k, posOf(b, p)]); }\n // движение корня: D(k) = M(0)⁻¹ · M(k) (в осях первого ключа: персонаж стартует на месте и идёт «вперёд»),\n // кость-корень скелета (таз) = D(k) · своя поза\n const n = Math.max(a.rootPos?.length ?? 0, a.rootRot?.length ?? 0);\n if (root && n > 1 && bones[root.bone] !== undefined && bones[root.bone]! >= 0) {\n const r0 = a.rootRot?.[0] ?? [1, 0, 0, 0], p0 = a.rootPos?.[0] ?? [0, 0, 0], inv0 = conj(r0);\n let moves = false;\n const D = Array.from({ length: n }, (_, k) => {\n const rk = a.rootRot?.[k] ?? r0, pk = a.rootPos?.[k] ?? p0;\n const q = qmul(inv0, rk), t = qrot(inv0, pk.map((v, i) => v - p0[i]!));\n if (Math.abs(q[0]!) < 0.9999 || Math.hypot(...t) > 1e-3) moves = true;\n return { q, t };\n });\n if (moves) {\n const b = root.bone, keys = Math.max(n, a.keys);\n const rotK = (k: number) => a.rotKeys.get(b)?.[Math.min(k, a.keys - 1)] ?? a.constRot.get(b) ?? root.rot;\n const posK = (k: number) => a.posKeys.get(b)?.[Math.min(k, a.keys - 1)] ?? a.constPos.get(b) ?? root.pos;\n const c = get(b)!;\n c.rot = []; c.pos = [];\n for (let k = 0; k < keys; k++) {\n const d = D[Math.min(k, n - 1)]!, pp = qrot(d.q, posK(k)).map((v, i) => v + d.t[i]!);\n c.rot.push([k, rotOf(b, qmul(d.q, rotK(k)))]);\n c.pos.push([k, posOf(b, pp)]);\n }\n }\n }\n return { kind: 'node', name, tps: a.rate, chan, duration: a.duration, frames: a.keys };\n}\n\nconst conj = (q: number[]) => [q[0]!, -q[1]!, -q[2]!, -q[3]!];\nconst qrot = (q: number[], v: number[]) => qmul(qmul(q, [0, v[0]!, v[1]!, v[2]!]), conj(q)).slice(1);\n\n/**\n * Скелет анимаций .skeleton («LKS\\0» — SKL наоборот; u32 0x12, u32 хеш скелета, u32 7, u16 число костей, u16).\n * Запись: u32 хеш скелета, u32 7, кватернион xyzw, позиция f32×3, f32, u16 номер, u16 родитель (0xffff — корень),\n * u16 первый ребёнок, u16 следующий, флаги (у костей-«твистов» — ещё ведущая кость и доля), u32 хеш имени, u32 длина,\n * имя, 0, хвост. Номер кости в масках .mab — номер записи. Записи ищутся по метке «хеш скелета + 7».\n */\nexport interface DuniaSkelBone { name: string; parent: number; rot: number[]; pos: number[] }\n\nexport function parseSkeleton(u: Uint8Array): DuniaSkelBone[] {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 28 || String.fromCharCode(u[2]!, u[1]!, u[0]!) !== 'SKL') throw new Error('Не скелет Dunia (нет «SKL»)');\n const n = dv.getUint16(16, true), key = dv.getUint32(8, true), out: DuniaSkelBone[] = [];\n let p = 0x14;\n const isKey = (o: number) => o + 8 <= u.length && dv.getUint32(o, true) === key && dv.getUint32(o + 4, true) === 7;\n for (let i = 0; i < n; i++) {\n while (p < u.length && !isKey(p)) p++;\n if (p + 58 > u.length) throw new Error('.skeleton: записей меньше, чем объявлено');\n const f = (k: number) => dv.getFloat32(p + 8 + k * 4, true);\n const parent = dv.getUint16(p + 42, true);\n // имя: u32 длина, печатные символы (за ними — 0, у корня — флаги)\n let name = '';\n for (let o = p + 50; o + 4 < Math.min(u.length, p + 80); o++) {\n const len = dv.getUint32(o, true);\n if (len < 1 || len > 64 || o + 4 + len >= u.length || (u[o + 4 + len]! >= 32 && u[o + 4 + len]! < 127)) continue;\n let ok = true, s = '';\n for (let k = 0; k < len && ok; k++) { const c = u[o + 4 + k]!; ok = c >= 32 && c < 127; s += String.fromCharCode(c); }\n if (ok) { name = s; break; }\n }\n out.push({ name, parent: parent === 0xffff ? -1 : parent, rot: [f(3), f(0), f(1), f(2)], pos: [f(4), f(5), f(6)] });\n p += 58;\n }\n return out;\n}\n"],"names":["n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mab.js","sources":["../src/mab.ts"],"sourcesContent":["// Dunia (Far Cry 2) .mab animations, worked out from game files (header and quaternion packing as in the notes of\n// PY-DuniaAnimationExtractor for Far Cry 3; the Far Cry 2 section layout, bone masks and key blocks from samples).\n// 0x00: u8 0x4c (Far Cry 2), u8 0, u8 version (2 = 15 keys/s, 3 = 30), u8 0; u32 CRC; 8 bytes.\n// 0x10: four 160-bit bone masks (5 × u32): 0 = constant rotation, 1 = animated rotation,\n// 2 = constant translation, 3 = animated translation. The bit number is the bone number in the model's O2BM list.\n// 0x84: f32 duration (s); 0x88: 10 × u32 section offsets (from 0x10): 0 = keyed root translation, 1 = keyed root\n// rotation, 2 = constant rotations, 3 = keyed rotations, 4 = constant translations, 5 = keyed translations,\n// then events (PlaySound…) and the rest.\n// Root: u16 1, u16 keys, u32; then keys (6-byte quaternion / f32×3).\n// Constants: u32 n, u32 0, n × (6-byte quaternion / f32×3), for the mask's bones in bit order.\n// Keyed translations: u16 tracks, u16 keys, u32; per track, all keys as f32×3.\n// Keyed rotations: u16 tracks, u16 keys, u32; u32 block offsets (8 keys each; the last is the end);\n// block: the block's first key for every track (6 bytes), a mask byte per track (bit k = key 8·b+1+k is present),\n// alignment to 2, then each track's keys per its mask. Missing keys are interpolated.\n// 6-byte quaternion: three u16, component = (v & 0x7fff) · 4.315969e-5 − 0.7071068, the fourth comes from the norm;\n// the top bits of the first two words tell which component was dropped (like \"smallest three\").\nimport type { NodeChannel, NodeMotion } from '@gmvjs/core/types';\n\nconst K = 4.315969e-5, H = 0.7071068;\n\n/** Quaternion from 6 bytes -> [w, x, y, z]. */\nexport function unpackQuat(a: number, b: number, c: number): number[] {\n const f1 = (a & 0x7fff) * K - H, f2 = (b & 0x7fff) * K - H, f3 = (c & 0x7fff) * K - H;\n const f4 = Math.sqrt(Math.max(0, 1 - f1 * f1 - f2 * f2 - f3 * f3));\n const A = a >> 15, B = b >> 15;\n // (x, y, z, w)\n const q = A === 0 && B === 1 ? [f1, f2, f4, f3] : A === 1 && B === 1 ? [f1, f2, f3, f4] : A === 1 ? [f1, f4, f2, f3] : [f4, f1, f2, f3];\n return [q[3]!, q[0]!, q[1]!, q[2]!];\n}\n\nexport interface MabAnim {\n version: number; duration: number; rate: number; keys: number;\n rootRot: number[][] | null; rootPos: number[][] | null;\n constRot: Map<number, number[]>; rotKeys: Map<number, number[][]>;\n constPos: Map<number, number[]>; posKeys: Map<number, number[][]>;\n}\n\nconst maskBits = (dv: DataView, at: number): number[] => {\n const out: number[] = [];\n for (let w = 0; w < 5; w++) { const v = dv.getUint32(at + w * 4, true); for (let b = 0; b < 32; b++) if (v & (1 << b)) out.push(w * 32 + b); }\n return out;\n};\n\nconst nlerp = (a: number[], b: number[], t: number) => {\n const d = a[0]! * b[0]! + a[1]! * b[1]! + a[2]! * b[2]! + a[3]! * b[3]!, s = d < 0 ? -1 : 1;\n const q = a.map((v, i) => v + (s * b[i]! - v) * t), l = Math.hypot(...q) || 1;\n return q.map((v) => v / l);\n};\n\nexport function parseMab(u: Uint8Array): MabAnim {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 0xb0 || u[0] !== 0x4c) throw new Error(`Анимация Dunia: версия 0x${(u[0] ?? 0).toString(16)} не поддерживается (нужна 0x4c — Far Cry 2)`);\n const version = u[2]!, sec = Array.from({ length: 10 }, (_, i) => { const o = dv.getUint32(0x88 + i * 4, true); return o ? o + 0x10 : 0; });\n const masks = [0, 1, 2, 3].map((m) => maskBits(dv, 0x10 + m * 20));\n const duration = dv.getFloat32(0x84, true);\n const q6 = (o: number) => unpackQuat(dv.getUint16(o, true), dv.getUint16(o + 2, true), dv.getUint16(o + 4, true));\n const v3 = (o: number) => [dv.getFloat32(o, true), dv.getFloat32(o + 4, true), dv.getFloat32(o + 8, true)];\n const check = (o: number, n: number) => { if (o + n > u.length) throw new Error('.mab: раздел за концом файла'); };\n if (!sec[3]) throw new Error('.mab: нет поворотов по ключам');\n const nb = dv.getUint16(sec[3], true), keys = dv.getUint16(sec[3] + 2, true);\n if (nb !== masks[1]!.length) throw new Error(`.mab: дорожек ${nb}, а в маске ${masks[1]!.length}`);\n // keys per second: from the duration (close to 15/30 is rounded), without it from the version (2 = 15, 3 = 30)\n const raw = duration > 0 && keys > 1 ? (keys - 1) / duration : version >= 3 ? 30 : 15;\n const rate = [15, 30].find((r) => Math.abs(raw - r) < r * 0.02) ?? raw;\n const anim: MabAnim = { version, duration, rate, keys, rootRot: null, rootPos: null, constRot: new Map(), rotKeys: new Map(), constPos: new Map(), posKeys: new Map() };\n // root\n if (sec[1]) { const n = dv.getUint16(sec[1] + 2, true); check(sec[1] + 8, n * 6); anim.rootRot = Array.from({ length: n }, (_, k) => q6(sec[1]! + 8 + k * 6)); }\n if (sec[0]) { const n = dv.getUint16(sec[0] + 2, true); check(sec[0] + 8, n * 12); anim.rootPos = Array.from({ length: n }, (_, k) => v3(sec[0]! + 8 + k * 12)); }\n // constants\n if (sec[2]) { const n = dv.getUint32(sec[2], true); check(sec[2] + 8, n * 6); masks[0]!.slice(0, n).forEach((b, i) => anim.constRot.set(b, q6(sec[2]! + 8 + i * 6))); }\n if (sec[4]) { const n = dv.getUint32(sec[4], true); check(sec[4] + 8, n * 12); masks[2]!.slice(0, n).forEach((b, i) => anim.constPos.set(b, v3(sec[4]! + 8 + i * 12))); }\n if (sec[5]) {\n const nt = dv.getUint16(sec[5], true), nk = dv.getUint16(sec[5] + 2, true);\n check(sec[5] + 8, nt * nk * 12);\n masks[3]!.slice(0, nt).forEach((b, t) => anim.posKeys.set(b, Array.from({ length: nk }, (_, k) => v3(sec[5]! + 8 + (t * nk + k) * 12))));\n }\n // keyed rotations: blocks of 8\n const nblk = nb ? Math.ceil(keys / 8) : 0; // without animated rotations (constant poses only) there are no blocks\n check(sec[3] + 8, (nblk + 1) * 4);\n const offs = Array.from({ length: nblk + 1 }, (_, i) => dv.getUint32(sec[3]! + 8 + i * 4, true));\n const tracks: Array<Array<number[] | null>> = Array.from({ length: nb }, () => new Array(keys).fill(null));\n for (let bi = 0; bi < nblk; bi++) {\n let p = sec[3] + offs[bi]!;\n check(p, nb * 7);\n for (let t = 0; t < nb; t++) if (bi * 8 < keys) tracks[t]![bi * 8] = q6(p + t * 6);\n p += nb * 6;\n const mask = u.subarray(p, p + nb);\n p += nb; p += p & 1;\n for (let t = 0; t < nb; t++) {\n for (let k = 0; k < 8; k++) {\n if (!(mask[t]! & (1 << k))) continue;\n check(p, 6);\n const key = bi * 8 + 1 + k;\n if (key < keys) tracks[t]![key] = q6(p);\n p += 6;\n }\n }\n }\n tracks.forEach((tr, t) => {\n // gaps: interpolation between neighbouring keys (the last one is held)\n let last = -1;\n for (let k = 0; k < keys; k++) {\n if (!tr[k]) continue;\n if (last >= 0 && k - last > 1) for (let j = last + 1; j < k; j++) tr[j] = nlerp(tr[last]!, tr[k]!, (j - last) / (k - last));\n last = k;\n }\n for (let k = last + 1; k < keys && last >= 0; k++) tr[k] = tr[last]!;\n if (last >= 0) anim.rotKeys.set(masks[1]![t]!, tr as number[][]);\n });\n return anim;\n}\n\nconst qmul = (p: number[], q: number[]) => [\n p[0]! * q[0]! - p[1]! * q[1]! - p[2]! * q[2]! - p[3]! * q[3]!, p[0]! * q[1]! + p[1]! * q[0]! + p[2]! * q[3]! - p[3]! * q[2]!,\n p[0]! * q[2]! - p[1]! * q[3]! + p[2]! * q[0]! + p[3]! * q[1]!, p[0]! * q[3]! + p[1]! * q[2]! - p[2]! * q[1]! + p[3]! * q[0]!,\n];\n\n/** Quaternion [w,x,y,z] of a matrix's rotation part (column-major). */\nexport function matQuat(m: ArrayLike<number>): number[] {\n const m00 = m[0]!, m11 = m[5]!, m22 = m[10]!, tr = m00 + m11 + m22;\n let q: number[];\n if (tr > 0) { const s = Math.sqrt(tr + 1) * 2; q = [s / 4, (m[6]! - m[9]!) / s, (m[8]! - m[2]!) / s, (m[1]! - m[4]!) / s]; }\n else if (m00 > m11 && m00 > m22) { const s = Math.sqrt(1 + m00 - m11 - m22) * 2; q = [(m[6]! - m[9]!) / s, s / 4, (m[4]! + m[1]!) / s, (m[8]! + m[2]!) / s]; }\n else if (m11 > m22) { const s = Math.sqrt(1 + m11 - m00 - m22) * 2; q = [(m[8]! - m[2]!) / s, (m[4]! + m[1]!) / s, s / 4, (m[9]! + m[6]!) / s]; }\n else { const s = Math.sqrt(1 + m22 - m00 - m11) * 2; q = [(m[1]! - m[4]!) / s, (m[8]! + m[2]!) / s, (m[9]! + m[6]!) / s, s / 4]; }\n const l = Math.hypot(...q) || 1;\n return q.map((v) => v / l);\n}\n\n/**\n * Animation for model nodes: bones[i] is the node for bone i (O2BM list). fix: for bones whose parent in the model is not a bone\n * (pelvis), the matrix C = (parent world pose)⁻¹ · T that maps the bone pose from the animation frame into the parent node's space.\n */\nexport function buildMabMotion(name: string, a: MabAnim, bones: readonly number[], fix: ReadonlyMap<number, ArrayLike<number>> = new Map(),\n root?: { bone: number; rot: number[]; pos: number[] }): NodeMotion {\n const chan = new Map<number, NodeChannel>();\n const get = (b: number) => { const n = bones[b]; if (n === undefined || n < 0) return null; let c = chan.get(n); if (!c) { c = { pos: null, rot: null, scl: null }; chan.set(n, c); } return c; };\n const rotOf = (b: number, q: number[]) => { const C = fix.get(b); return C ? qmul(matQuat(C), q) : q; };\n const posOf = (b: number, p: number[]) => { const C = fix.get(b); return C ? [0, 1, 2].map((i) => C[i]! * p[0]! + C[4 + i]! * p[1]! + C[8 + i]! * p[2]! + C[12 + i]!) : p; };\n for (const [b, q] of a.constRot) { const c = get(b); if (c) c.rot = [[0, rotOf(b, q)]]; }\n for (const [b, list] of a.rotKeys) { const c = get(b); if (c) c.rot = list.map((q, k) => [k, rotOf(b, q)]); }\n for (const [b, v] of a.constPos) { const c = get(b); if (c) c.pos = [[0, posOf(b, v)]]; }\n for (const [b, list] of a.posKeys) { const c = get(b); if (c) c.pos = list.map((p, k) => [k, posOf(b, p)]); }\n // root motion: D(k) = M(0)⁻¹ · M(k) (in the axes of the first key: the character starts in place and walks \"forward\"),\n // the skeleton's root bone (pelvis) = D(k) · its own pose\n const n = Math.max(a.rootPos?.length ?? 0, a.rootRot?.length ?? 0);\n if (root && n > 1 && bones[root.bone] !== undefined && bones[root.bone]! >= 0) {\n const r0 = a.rootRot?.[0] ?? [1, 0, 0, 0], p0 = a.rootPos?.[0] ?? [0, 0, 0], inv0 = conj(r0);\n let moves = false;\n const D = Array.from({ length: n }, (_, k) => {\n const rk = a.rootRot?.[k] ?? r0, pk = a.rootPos?.[k] ?? p0;\n const q = qmul(inv0, rk), t = qrot(inv0, pk.map((v, i) => v - p0[i]!));\n if (Math.abs(q[0]!) < 0.9999 || Math.hypot(...t) > 1e-3) moves = true;\n return { q, t };\n });\n if (moves) {\n const b = root.bone, keys = Math.max(n, a.keys);\n const rotK = (k: number) => a.rotKeys.get(b)?.[Math.min(k, a.keys - 1)] ?? a.constRot.get(b) ?? root.rot;\n const posK = (k: number) => a.posKeys.get(b)?.[Math.min(k, a.keys - 1)] ?? a.constPos.get(b) ?? root.pos;\n const c = get(b)!;\n c.rot = []; c.pos = [];\n for (let k = 0; k < keys; k++) {\n const d = D[Math.min(k, n - 1)]!, pp = qrot(d.q, posK(k)).map((v, i) => v + d.t[i]!);\n c.rot.push([k, rotOf(b, qmul(d.q, rotK(k)))]);\n c.pos.push([k, posOf(b, pp)]);\n }\n }\n }\n return { kind: 'node', name, tps: a.rate, chan, duration: a.duration, frames: a.keys };\n}\n\nconst conj = (q: number[]) => [q[0]!, -q[1]!, -q[2]!, -q[3]!];\nconst qrot = (q: number[], v: number[]) => qmul(qmul(q, [0, v[0]!, v[1]!, v[2]!]), conj(q)).slice(1);\n\n/**\n * Animation skeleton .skeleton (\"LKS\\0\", SKL backwards; u32 0x12, u32 skeleton hash, u32 7, u16 bone count, u16).\n * Record: u32 skeleton hash, u32 7, quaternion xyzw, position f32×3, f32, u16 number, u16 parent (0xffff = root),\n * u16 first child, u16 next, flags (twist bones also have a driver bone and a fraction), u32 name hash, u32 length,\n * name, 0, tail. The bone number in .mab masks is the record number. Records are found by the \"skeleton hash + 7\" marker.\n */\nexport interface DuniaSkelBone { name: string; parent: number; rot: number[]; pos: number[] }\n\nexport function parseSkeleton(u: Uint8Array): DuniaSkelBone[] {\n const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);\n if (u.length < 28 || String.fromCharCode(u[2]!, u[1]!, u[0]!) !== 'SKL') throw new Error('Не скелет Dunia (нет «SKL»)');\n const n = dv.getUint16(16, true), key = dv.getUint32(8, true), out: DuniaSkelBone[] = [];\n let p = 0x14;\n const isKey = (o: number) => o + 8 <= u.length && dv.getUint32(o, true) === key && dv.getUint32(o + 4, true) === 7;\n for (let i = 0; i < n; i++) {\n while (p < u.length && !isKey(p)) p++;\n if (p + 58 > u.length) throw new Error('.skeleton: записей меньше, чем объявлено');\n const f = (k: number) => dv.getFloat32(p + 8 + k * 4, true);\n const parent = dv.getUint16(p + 42, true);\n // name: u32 length, printable characters (followed by 0, flags for the root)\n let name = '';\n for (let o = p + 50; o + 4 < Math.min(u.length, p + 80); o++) {\n const len = dv.getUint32(o, true);\n if (len < 1 || len > 64 || o + 4 + len >= u.length || (u[o + 4 + len]! >= 32 && u[o + 4 + len]! < 127)) continue;\n let ok = true, s = '';\n for (let k = 0; k < len && ok; k++) { const c = u[o + 4 + k]!; ok = c >= 32 && c < 127; s += String.fromCharCode(c); }\n if (ok) { name = s; break; }\n }\n out.push({ name, parent: parent === 0xffff ? -1 : parent, rot: [f(3), f(0), f(1), f(2)], pos: [f(4), f(5), f(6)] });\n p += 58;\n }\n return out;\n}\n"],"names":["n"],"mappings":"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"}
|
package/dist/realtree.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface RealTree {
|
|
|
15
15
|
}
|
|
16
16
|
export declare const isRealTree: (u: Uint8Array) => boolean;
|
|
17
17
|
export declare function parseRealTree(u: Uint8Array): RealTree;
|
|
18
|
-
/**
|
|
18
|
+
/** Approximate mesh: branches as tubes (`sides` faces), foliage as cards at branch ends; bark/leaf are .xbm material paths. */
|
|
19
19
|
export declare function realTreeMeshes(t: RealTree, bark: {
|
|
20
20
|
material?: string;
|
|
21
21
|
texture: string;
|