@gmvjs/dunia1 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +72 -2
- package/dist/fat.d.ts +57 -0
- package/dist/fat.js +226 -0
- package/dist/fat.js.map +1 -0
- package/dist/fcb.d.ts +14 -0
- package/dist/fcb.js +86 -0
- package/dist/fcb.js.map +1 -0
- package/dist/games/farcry2.d.ts +7 -0
- package/dist/games/farcry2.js +11 -0
- package/dist/games/farcry2.js.map +1 -0
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.js +12 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/level.d.ts +120 -0
- package/dist/level.js +751 -0
- package/dist/level.js.map +1 -0
- package/dist/mab.d.ts +40 -0
- package/dist/mab.js +228 -0
- package/dist/mab.js.map +1 -0
- package/dist/realtree.d.ts +27 -0
- package/dist/realtree.js +127 -0
- package/dist/realtree.js.map +1 -0
- package/dist/rml.d.ts +15 -0
- package/dist/rml.js +77 -0
- package/dist/rml.js.map +1 -0
- package/dist/xbg.d.ts +115 -0
- package/dist/xbg.js +524 -0
- package/dist/xbg.js.map +1 -0
- package/dist/xbt.d.ts +3 -0
- package/dist/xbt.js +14 -0
- package/dist/xbt.js.map +1 -0
- package/package.json +54 -4
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { NodeMotion } from '@gmvjs/core/types';
|
|
2
|
+
/** Quaternion from 6 bytes -> [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
|
+
/** Quaternion [w,x,y,z] of a matrix's rotation part (column-major). */
|
|
18
|
+
export declare function matQuat(m: ArrayLike<number>): number[];
|
|
19
|
+
/**
|
|
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
|
+
*/
|
|
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
|
+
* 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
|
+
*/
|
|
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
|
package/dist/mab.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
|
@@ -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
|
+
/** Approximate mesh: branches as tubes (`sides` faces), foliage as cards at branch ends; bark/leaf are .xbm material paths. */
|
|
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[];
|