@gmvjs/dunia1 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +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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"}
package/dist/mab.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type { NodeMotion } from '@gmvjs/core/types';
2
+ /** Кватернион из 6 байт -> [w, x, y, z]. */
3
+ export declare function unpackQuat(a: number, b: number, c: number): number[];
4
+ export interface MabAnim {
5
+ version: number;
6
+ duration: number;
7
+ rate: number;
8
+ keys: number;
9
+ rootRot: number[][] | null;
10
+ rootPos: number[][] | null;
11
+ constRot: Map<number, number[]>;
12
+ rotKeys: Map<number, number[][]>;
13
+ constPos: Map<number, number[]>;
14
+ posKeys: Map<number, number[][]>;
15
+ }
16
+ export declare function parseMab(u: Uint8Array): MabAnim;
17
+ /** Кватернион [w,x,y,z] поворотной части матрицы (column-major). */
18
+ export declare function matQuat(m: ArrayLike<number>): number[];
19
+ /**
20
+ * Анимация для узлов модели: bones[i] — узел для кости i (список O2BM). fix — для костей, чей родитель в модели не кость
21
+ * (таз): матрица C = (мировая поза родителя)⁻¹ · T, переводящая позу кости из кадра анимации в пространство родителя-узла.
22
+ */
23
+ export declare function buildMabMotion(name: string, a: MabAnim, bones: readonly number[], fix?: ReadonlyMap<number, ArrayLike<number>>, root?: {
24
+ bone: number;
25
+ rot: number[];
26
+ pos: number[];
27
+ }): NodeMotion;
28
+ /**
29
+ * Скелет анимаций .skeleton («LKS\0» — SKL наоборот; u32 0x12, u32 хеш скелета, u32 7, u16 число костей, u16).
30
+ * Запись: u32 хеш скелета, u32 7, кватернион xyzw, позиция f32×3, f32, u16 номер, u16 родитель (0xffff — корень),
31
+ * u16 первый ребёнок, u16 следующий, флаги (у костей-«твистов» — ещё ведущая кость и доля), u32 хеш имени, u32 длина,
32
+ * имя, 0, хвост. Номер кости в масках .mab — номер записи. Записи ищутся по метке «хеш скелета + 7».
33
+ */
34
+ export interface DuniaSkelBone {
35
+ name: string;
36
+ parent: number;
37
+ rot: number[];
38
+ pos: number[];
39
+ }
40
+ export declare function parseSkeleton(u: Uint8Array): DuniaSkelBone[];
package/dist/mab.js ADDED
@@ -0,0 +1,228 @@
1
+ const K = 4315969e-11, H = 0.7071068;
2
+ function unpackQuat(a, b, c) {
3
+ const f1 = (a & 32767) * K - H, f2 = (b & 32767) * K - H, f3 = (c & 32767) * K - H;
4
+ const f4 = Math.sqrt(Math.max(0, 1 - f1 * f1 - f2 * f2 - f3 * f3));
5
+ const A = a >> 15, B = b >> 15;
6
+ 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];
7
+ return [q[3], q[0], q[1], q[2]];
8
+ }
9
+ const maskBits = (dv, at) => {
10
+ const out = [];
11
+ for (let w = 0; w < 5; w++) {
12
+ const v = dv.getUint32(at + w * 4, true);
13
+ for (let b = 0; b < 32; b++) if (v & 1 << b) out.push(w * 32 + b);
14
+ }
15
+ return out;
16
+ };
17
+ const nlerp = (a, b, t) => {
18
+ const d = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3], s = d < 0 ? -1 : 1;
19
+ const q = a.map((v, i) => v + (s * b[i] - v) * t), l = Math.hypot(...q) || 1;
20
+ return q.map((v) => v / l);
21
+ };
22
+ function parseMab(u) {
23
+ const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);
24
+ if (u.length < 176 || u[0] !== 76) throw new Error(`Анимация Dunia: версия 0x${(u[0] ?? 0).toString(16)} не поддерживается (нужна 0x4c — Far Cry 2)`);
25
+ const version = u[2], sec = Array.from({ length: 10 }, (_, i) => {
26
+ const o = dv.getUint32(136 + i * 4, true);
27
+ return o ? o + 16 : 0;
28
+ });
29
+ const masks = [0, 1, 2, 3].map((m) => maskBits(dv, 16 + m * 20));
30
+ const duration = dv.getFloat32(132, true);
31
+ const q6 = (o) => unpackQuat(dv.getUint16(o, true), dv.getUint16(o + 2, true), dv.getUint16(o + 4, true));
32
+ const v3 = (o) => [dv.getFloat32(o, true), dv.getFloat32(o + 4, true), dv.getFloat32(o + 8, true)];
33
+ const check = (o, n) => {
34
+ if (o + n > u.length) throw new Error(".mab: раздел за концом файла");
35
+ };
36
+ if (!sec[3]) throw new Error(".mab: нет поворотов по ключам");
37
+ const nb = dv.getUint16(sec[3], true), keys = dv.getUint16(sec[3] + 2, true);
38
+ if (nb !== masks[1].length) throw new Error(`.mab: дорожек ${nb}, а в маске ${masks[1].length}`);
39
+ const raw = duration > 0 && keys > 1 ? (keys - 1) / duration : version >= 3 ? 30 : 15;
40
+ const rate = [15, 30].find((r) => Math.abs(raw - r) < r * 0.02) ?? raw;
41
+ const anim = { version, duration, rate, keys, rootRot: null, rootPos: null, constRot: /* @__PURE__ */ new Map(), rotKeys: /* @__PURE__ */ new Map(), constPos: /* @__PURE__ */ new Map(), posKeys: /* @__PURE__ */ new Map() };
42
+ if (sec[1]) {
43
+ const n = dv.getUint16(sec[1] + 2, true);
44
+ check(sec[1] + 8, n * 6);
45
+ anim.rootRot = Array.from({ length: n }, (_, k) => q6(sec[1] + 8 + k * 6));
46
+ }
47
+ if (sec[0]) {
48
+ const n = dv.getUint16(sec[0] + 2, true);
49
+ check(sec[0] + 8, n * 12);
50
+ anim.rootPos = Array.from({ length: n }, (_, k) => v3(sec[0] + 8 + k * 12));
51
+ }
52
+ if (sec[2]) {
53
+ const n = dv.getUint32(sec[2], true);
54
+ check(sec[2] + 8, n * 6);
55
+ masks[0].slice(0, n).forEach((b, i) => anim.constRot.set(b, q6(sec[2] + 8 + i * 6)));
56
+ }
57
+ if (sec[4]) {
58
+ const n = dv.getUint32(sec[4], true);
59
+ check(sec[4] + 8, n * 12);
60
+ masks[2].slice(0, n).forEach((b, i) => anim.constPos.set(b, v3(sec[4] + 8 + i * 12)));
61
+ }
62
+ if (sec[5]) {
63
+ const nt = dv.getUint16(sec[5], true), nk = dv.getUint16(sec[5] + 2, true);
64
+ check(sec[5] + 8, nt * nk * 12);
65
+ 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))));
66
+ }
67
+ const nblk = nb ? Math.ceil(keys / 8) : 0;
68
+ check(sec[3] + 8, (nblk + 1) * 4);
69
+ const offs = Array.from({ length: nblk + 1 }, (_, i) => dv.getUint32(sec[3] + 8 + i * 4, true));
70
+ const tracks = Array.from({ length: nb }, () => new Array(keys).fill(null));
71
+ for (let bi = 0; bi < nblk; bi++) {
72
+ let p = sec[3] + offs[bi];
73
+ check(p, nb * 7);
74
+ for (let t = 0; t < nb; t++) if (bi * 8 < keys) tracks[t][bi * 8] = q6(p + t * 6);
75
+ p += nb * 6;
76
+ const mask = u.subarray(p, p + nb);
77
+ p += nb;
78
+ p += p & 1;
79
+ for (let t = 0; t < nb; t++) {
80
+ for (let k = 0; k < 8; k++) {
81
+ if (!(mask[t] & 1 << k)) continue;
82
+ check(p, 6);
83
+ const key = bi * 8 + 1 + k;
84
+ if (key < keys) tracks[t][key] = q6(p);
85
+ p += 6;
86
+ }
87
+ }
88
+ }
89
+ tracks.forEach((tr, t) => {
90
+ let last = -1;
91
+ for (let k = 0; k < keys; k++) {
92
+ if (!tr[k]) continue;
93
+ 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));
94
+ last = k;
95
+ }
96
+ for (let k = last + 1; k < keys && last >= 0; k++) tr[k] = tr[last];
97
+ if (last >= 0) anim.rotKeys.set(masks[1][t], tr);
98
+ });
99
+ return anim;
100
+ }
101
+ const qmul = (p, q) => [
102
+ p[0] * q[0] - p[1] * q[1] - p[2] * q[2] - p[3] * q[3],
103
+ p[0] * q[1] + p[1] * q[0] + p[2] * q[3] - p[3] * q[2],
104
+ p[0] * q[2] - p[1] * q[3] + p[2] * q[0] + p[3] * q[1],
105
+ p[0] * q[3] + p[1] * q[2] - p[2] * q[1] + p[3] * q[0]
106
+ ];
107
+ function matQuat(m) {
108
+ const m00 = m[0], m11 = m[5], m22 = m[10], tr = m00 + m11 + m22;
109
+ let q;
110
+ if (tr > 0) {
111
+ const s = Math.sqrt(tr + 1) * 2;
112
+ q = [s / 4, (m[6] - m[9]) / s, (m[8] - m[2]) / s, (m[1] - m[4]) / s];
113
+ } else if (m00 > m11 && m00 > m22) {
114
+ const s = Math.sqrt(1 + m00 - m11 - m22) * 2;
115
+ q = [(m[6] - m[9]) / s, s / 4, (m[4] + m[1]) / s, (m[8] + m[2]) / s];
116
+ } else if (m11 > m22) {
117
+ const s = Math.sqrt(1 + m11 - m00 - m22) * 2;
118
+ q = [(m[8] - m[2]) / s, (m[4] + m[1]) / s, s / 4, (m[9] + m[6]) / s];
119
+ } else {
120
+ const s = Math.sqrt(1 + m22 - m00 - m11) * 2;
121
+ q = [(m[1] - m[4]) / s, (m[8] + m[2]) / s, (m[9] + m[6]) / s, s / 4];
122
+ }
123
+ const l = Math.hypot(...q) || 1;
124
+ return q.map((v) => v / l);
125
+ }
126
+ function buildMabMotion(name, a, bones, fix = /* @__PURE__ */ new Map(), root) {
127
+ const chan = /* @__PURE__ */ new Map();
128
+ const get = (b) => {
129
+ const n2 = bones[b];
130
+ if (n2 === void 0 || n2 < 0) return null;
131
+ let c = chan.get(n2);
132
+ if (!c) {
133
+ c = { pos: null, rot: null, scl: null };
134
+ chan.set(n2, c);
135
+ }
136
+ return c;
137
+ };
138
+ const rotOf = (b, q) => {
139
+ const C = fix.get(b);
140
+ return C ? qmul(matQuat(C), q) : q;
141
+ };
142
+ const posOf = (b, p) => {
143
+ const C = fix.get(b);
144
+ 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;
145
+ };
146
+ for (const [b, q] of a.constRot) {
147
+ const c = get(b);
148
+ if (c) c.rot = [[0, rotOf(b, q)]];
149
+ }
150
+ for (const [b, list] of a.rotKeys) {
151
+ const c = get(b);
152
+ if (c) c.rot = list.map((q, k) => [k, rotOf(b, q)]);
153
+ }
154
+ for (const [b, v] of a.constPos) {
155
+ const c = get(b);
156
+ if (c) c.pos = [[0, posOf(b, v)]];
157
+ }
158
+ for (const [b, list] of a.posKeys) {
159
+ const c = get(b);
160
+ if (c) c.pos = list.map((p, k) => [k, posOf(b, p)]);
161
+ }
162
+ const n = Math.max(a.rootPos?.length ?? 0, a.rootRot?.length ?? 0);
163
+ if (root && n > 1 && bones[root.bone] !== void 0 && bones[root.bone] >= 0) {
164
+ const r0 = a.rootRot?.[0] ?? [1, 0, 0, 0], p0 = a.rootPos?.[0] ?? [0, 0, 0], inv0 = conj(r0);
165
+ let moves = false;
166
+ const D = Array.from({ length: n }, (_, k) => {
167
+ const rk = a.rootRot?.[k] ?? r0, pk = a.rootPos?.[k] ?? p0;
168
+ const q = qmul(inv0, rk), t = qrot(inv0, pk.map((v, i) => v - p0[i]));
169
+ if (Math.abs(q[0]) < 0.9999 || Math.hypot(...t) > 1e-3) moves = true;
170
+ return { q, t };
171
+ });
172
+ if (moves) {
173
+ const b = root.bone, keys = Math.max(n, a.keys);
174
+ const rotK = (k) => a.rotKeys.get(b)?.[Math.min(k, a.keys - 1)] ?? a.constRot.get(b) ?? root.rot;
175
+ const posK = (k) => a.posKeys.get(b)?.[Math.min(k, a.keys - 1)] ?? a.constPos.get(b) ?? root.pos;
176
+ const c = get(b);
177
+ c.rot = [];
178
+ c.pos = [];
179
+ for (let k = 0; k < keys; k++) {
180
+ const d = D[Math.min(k, n - 1)], pp = qrot(d.q, posK(k)).map((v, i) => v + d.t[i]);
181
+ c.rot.push([k, rotOf(b, qmul(d.q, rotK(k)))]);
182
+ c.pos.push([k, posOf(b, pp)]);
183
+ }
184
+ }
185
+ }
186
+ return { kind: "node", name, tps: a.rate, chan, duration: a.duration, frames: a.keys };
187
+ }
188
+ const conj = (q) => [q[0], -q[1], -q[2], -q[3]];
189
+ const qrot = (q, v) => qmul(qmul(q, [0, v[0], v[1], v[2]]), conj(q)).slice(1);
190
+ function parseSkeleton(u) {
191
+ const dv = new DataView(u.buffer, u.byteOffset, u.byteLength);
192
+ if (u.length < 28 || String.fromCharCode(u[2], u[1], u[0]) !== "SKL") throw new Error("Не скелет Dunia (нет «SKL»)");
193
+ const n = dv.getUint16(16, true), key = dv.getUint32(8, true), out = [];
194
+ let p = 20;
195
+ const isKey = (o) => o + 8 <= u.length && dv.getUint32(o, true) === key && dv.getUint32(o + 4, true) === 7;
196
+ for (let i = 0; i < n; i++) {
197
+ while (p < u.length && !isKey(p)) p++;
198
+ if (p + 58 > u.length) throw new Error(".skeleton: записей меньше, чем объявлено");
199
+ const f = (k) => dv.getFloat32(p + 8 + k * 4, true);
200
+ const parent = dv.getUint16(p + 42, true);
201
+ let name = "";
202
+ for (let o = p + 50; o + 4 < Math.min(u.length, p + 80); o++) {
203
+ const len = dv.getUint32(o, true);
204
+ if (len < 1 || len > 64 || o + 4 + len >= u.length || u[o + 4 + len] >= 32 && u[o + 4 + len] < 127) continue;
205
+ let ok = true, s = "";
206
+ for (let k = 0; k < len && ok; k++) {
207
+ const c = u[o + 4 + k];
208
+ ok = c >= 32 && c < 127;
209
+ s += String.fromCharCode(c);
210
+ }
211
+ if (ok) {
212
+ name = s;
213
+ break;
214
+ }
215
+ }
216
+ out.push({ name, parent: parent === 65535 ? -1 : parent, rot: [f(3), f(0), f(1), f(2)], pos: [f(4), f(5), f(6)] });
217
+ p += 58;
218
+ }
219
+ return out;
220
+ }
221
+ export {
222
+ buildMabMotion,
223
+ matQuat,
224
+ parseMab,
225
+ parseSkeleton,
226
+ unpackQuat
227
+ };
228
+ //# sourceMappingURL=mab.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,27 @@
1
+ import type { Mesh } from '@gmvjs/core/types';
2
+ export interface RealTree {
3
+ path: string;
4
+ branches: Array<{
5
+ pts: number[][];
6
+ tips: number[];
7
+ scale: number;
8
+ }>;
9
+ cards: Array<{
10
+ n: number[];
11
+ a: number[];
12
+ }>;
13
+ materials: string[];
14
+ height: number;
15
+ }
16
+ export declare const isRealTree: (u: Uint8Array) => boolean;
17
+ export declare function parseRealTree(u: Uint8Array): RealTree;
18
+ /** Приближённая сетка: ветви — трубки (sides граней), листва — карточки у концов ветвей; bark/leaf — пути материалов .xbm. */
19
+ export declare function realTreeMeshes(t: RealTree, bark: {
20
+ material?: string;
21
+ texture: string;
22
+ normal?: string;
23
+ }, leaf: {
24
+ material?: string;
25
+ texture: string;
26
+ normal?: string;
27
+ }, sides?: number): Mesh[];