@gmvjs/xray-v1 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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -2
  3. package/dist/db.d.ts +12 -0
  4. package/dist/db.js +134 -0
  5. package/dist/db.js.map +1 -0
  6. package/dist/details.d.ts +42 -0
  7. package/dist/details.js +210 -0
  8. package/dist/details.js.map +1 -0
  9. package/dist/export.d.ts +38 -0
  10. package/dist/export.js +241 -0
  11. package/dist/export.js.map +1 -0
  12. package/dist/games/cop.d.ts +7 -0
  13. package/dist/games/cop.js +11 -0
  14. package/dist/games/cop.js.map +1 -0
  15. package/dist/games/cs.d.ts +7 -0
  16. package/dist/games/cs.js +11 -0
  17. package/dist/games/cs.js.map +1 -0
  18. package/dist/games/sdk.d.ts +7 -0
  19. package/dist/games/sdk.js +10 -0
  20. package/dist/games/sdk.js.map +1 -0
  21. package/dist/games/soc.d.ts +9 -0
  22. package/dist/games/soc.js +13 -0
  23. package/dist/games/soc.js.map +1 -0
  24. package/dist/i18n.d.ts +2 -0
  25. package/dist/i18n.js +48 -0
  26. package/dist/i18n.js.map +1 -0
  27. package/dist/index.d.ts +5 -0
  28. package/dist/index.js +131 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/level.d.ts +39 -0
  31. package/dist/level.js +234 -0
  32. package/dist/level.js.map +1 -0
  33. package/dist/load.d.ts +5 -0
  34. package/dist/load.js +126 -0
  35. package/dist/load.js.map +1 -0
  36. package/dist/lzhuf.d.ts +2 -0
  37. package/dist/lzhuf.js +136 -0
  38. package/dist/lzhuf.js.map +1 -0
  39. package/dist/motion.d.ts +28 -0
  40. package/dist/motion.js +20 -0
  41. package/dist/motion.js.map +1 -0
  42. package/dist/object.d.ts +138 -0
  43. package/dist/object.js +361 -0
  44. package/dist/object.js.map +1 -0
  45. package/dist/ogf.d.ts +93 -0
  46. package/dist/ogf.js +418 -0
  47. package/dist/ogf.js.map +1 -0
  48. package/dist/scene.d.ts +28 -0
  49. package/dist/scene.js +141 -0
  50. package/dist/scene.js.map +1 -0
  51. package/dist/skeleton.d.ts +34 -0
  52. package/dist/skeleton.js +252 -0
  53. package/dist/skeleton.js.map +1 -0
  54. package/dist/smotion.d.ts +44 -0
  55. package/dist/smotion.js +283 -0
  56. package/dist/smotion.js.map +1 -0
  57. package/dist/soc-key.d.ts +18 -0
  58. package/dist/soc-key.js +125 -0
  59. package/dist/soc-key.js.map +1 -0
  60. package/dist/thm.d.ts +26 -0
  61. package/dist/thm.js +69 -0
  62. package/dist/thm.js.map +1 -0
  63. package/dist/write-object.d.ts +31 -0
  64. package/dist/write-object.js +228 -0
  65. package/dist/write-object.js.map +1 -0
  66. package/dist/write-omf.d.ts +47 -0
  67. package/dist/write-scene.d.ts +26 -0
  68. package/dist/write-scene.js +114 -0
  69. package/dist/write-scene.js.map +1 -0
  70. package/package.json +70 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene.js","sources":["../src/scene.ts"],"sourcesContent":["// Сцена уровня X-Ray SDK 0.7 (ЗП): текстовые .level и части rawdata/levels/<уровень>/*.part (формат ini).\n// scene_object.part: объекты [object_N] с reference_name (путь в rawdata/objects без .object), position, rotation, scale.\nimport { boundsOf } from '@gmvjs/core/geometry';\nimport type { LoadContext } from '@gmvjs/core/plugin';\nimport type { Mesh, Model, VFile } from '@gmvjs/core/types';\nimport { objectMeshes, parseObject, type XrObjectMesh } from './object';\n\nexport type Ini = Map<string, Map<string, string>>;\n\n/** Разбор ini X-Ray (секции, «ключ = значение», комментарии «;»). */\nexport function parseIni(text: string): Ini {\n const out: Ini = new Map();\n let cur: Map<string, string> | null = null;\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.replace(/;.*$/, '').trim();\n if (!line) continue;\n const sec = /^\\[([^\\]]+)\\]/.exec(line);\n if (sec) { cur = new Map(); out.set(sec[1]!.trim(), cur); continue; }\n const eq = line.indexOf('=');\n if (cur && eq > 0) cur.set(line.slice(0, eq).trim(), line.slice(eq + 1).trim());\n }\n return out;\n}\n\nexport interface SceneObjectRef { name: string; reference: string; position: number[]; rotation: number[]; scale: number[]; flags: number }\n\nconst vec = (s: string | undefined, d: number[]) => (s ? s.split(',').map((x) => +x.trim()) : d);\n\n/** Объекты из scene_object.part. */\nexport function sceneObjects(ini: Ini): SceneObjectRef[] {\n const out: SceneObjectRef[] = [];\n for (const [name, sec] of ini) {\n if (!/^object_\\d+$/.test(name) || !sec.get('reference_name')) continue;\n out.push({\n name: sec.get('name') ?? name, reference: sec.get('reference_name')!, flags: +(sec.get('flags') ?? 0),\n position: vec(sec.get('position'), [0, 0, 0]), rotation: vec(sec.get('rotation'), [0, 0, 0]), scale: vec(sec.get('scale'), [1, 1, 1]),\n });\n }\n return out;\n}\n\n/**\n * Матрица поворота объекта сцены (строки-векторы: v' = v·R). Проверено на labx8 (ЗП): вершины 40 из 40 повёрнутых объектов\n * сцены совпали с геометрией скомпилированного уровня точно при R = transpose(setHPB(y, x, z)); другие трактовки углов — нет.\n */\nexport function sceneRotation(rot: number[]): number[] {\n const h = rot[1]!, p = rot[0]!, b = rot[2]!;\n const sh = Math.sin(h), ch = Math.cos(h), sp = Math.sin(p), cp = Math.cos(p), sb = Math.sin(b), cb = Math.cos(b);\n const cc = ch * cb, cs = ch * sb, sc = sh * cb, ss = sh * sb;\n const m = [cc - sp * ss, cs + sp * sc, -cp * sh, -cp * sb, cp * cb, sp, sc + sp * cs, ss - sp * cc, cp * ch];\n return [m[0]!, m[3]!, m[6]!, m[1]!, m[4]!, m[7]!, m[2]!, m[5]!, m[8]!];\n}\n\n/** Вершины меша объекта в мир сцены: v·S·R + P (строки-векторы). */\nexport function placeMesh(m: XrObjectMesh, ref: SceneObjectRef): { pos: Float32Array; nor: Float32Array } {\n const R = sceneRotation(ref.rotation), [sx, sy, sz] = ref.scale as [number, number, number], [px, py, pz] = ref.position as [number, number, number];\n const n = m.pos.length / 3, pos = new Float32Array(n * 3), nor = new Float32Array(n * 3);\n for (let i = 0; i < n; i++) {\n const x = m.pos[i * 3]! * sx, y = m.pos[i * 3 + 1]! * sy, z = m.pos[i * 3 + 2]! * sz;\n pos[i * 3] = x * R[0]! + y * R[3]! + z * R[6]! + px;\n pos[i * 3 + 1] = x * R[1]! + y * R[4]! + z * R[7]! + py;\n pos[i * 3 + 2] = x * R[2]! + y * R[5]! + z * R[8]! + pz;\n // нормаль: при неравномерном масштабе — через обратный масштаб\n const a = m.nor[i * 3]! / sx, b = m.nor[i * 3 + 1]! / sy, c = m.nor[i * 3 + 2]! / sz;\n let nx = a * R[0]! + b * R[3]! + c * R[6]!, ny = a * R[1]! + b * R[4]! + c * R[7]!, nz = a * R[2]! + b * R[5]! + c * R[8]!;\n const l = Math.hypot(nx, ny, nz) || 1; nx /= l; ny /= l; nz /= l;\n nor[i * 3] = nx; nor[i * 3 + 1] = ny; nor[i * 3 + 2] = nz;\n }\n return { pos, nor };\n}\n\n/** Найти файл по пути rawdata/… (в открытых папках/архивах путь может начинаться выше: editors/rawdata, sdk/editors/rawdata). */\nasync function readRaw(ctx: LoadContext, rel: string): Promise<Uint8Array | null> {\n const want = rel.toLowerCase().replace(/\\\\/g, '/');\n for (const a of ctx.archives) {\n const e = a.lookup(want) ?? a.entries.find((x) => x.path.endsWith('/' + want));\n if (e) return a.read(e);\n }\n return null;\n}\n\n/** Уровень SDK: .level + scene_object.part + объекты. Меши объединяются по текстуре и шейдеру. */\nexport async function loadSceneLevel(main: VFile, ctx: LoadContext): Promise<Model> {\n const level = parseIni(new TextDecoder('windows-1251').decode(await main.arrayBuffer()));\n const opts = level.get('level_options'), name = main.name.replace(/\\.level$/i, '');\n const dir = (main.path ?? '').replace(/[^/]*$/, '');\n const partPath = `${dir}${name}/scene_object.part`;\n let part = await ctx.readPath(partPath).catch(() => null);\n if (!part) part = await readRaw(ctx, `rawdata/levels/${name}/scene_object.part`);\n if (!part) throw new Error(`Нет ${name}/scene_object.part рядом с ${main.name}: откройте папку SDK (editors/rawdata) целиком`);\n const refs = sceneObjects(parseIni(new TextDecoder('windows-1251').decode(part)));\n const cache = new Map<string, XrObjectMesh[] | null>(), missing = new Set<string>();\n const groups = new Map<string, { texture: string; shader: string; material?: string; pos: number[]; nor: number[]; uv: number[]; idx: number[] }>();\n let placed = 0;\n for (const ref of refs) {\n let list = cache.get(ref.reference);\n if (list === undefined) {\n const raw = await readRaw(ctx, `rawdata/objects/${ref.reference}.object`);\n try { list = raw ? objectMeshes(parseObject(raw.buffer.slice(raw.byteOffset, raw.byteOffset + raw.byteLength) as ArrayBuffer)) : null; } catch { list = null; }\n cache.set(ref.reference, list);\n }\n if (!list) { missing.add(ref.reference); continue; }\n placed++;\n for (const m of list) {\n const s = m.surface, key = `${s?.texture ?? ''}|${s?.eshader ?? ''}`;\n let g = groups.get(key);\n if (!g) groups.set(key, (g = { texture: s?.texture ?? '', shader: s?.eshader ?? '', material: s?.gamemtl, pos: [], nor: [], uv: [], idx: [] }));\n const { pos, nor } = placeMesh(m, ref), base = g.pos.length / 3;\n for (let i = 0; i < pos.length; i++) { g.pos.push(pos[i]!); g.nor.push(nor[i]!); }\n for (let i = 0; i < m.uv.length; i++) g.uv.push(m.uv[i]!);\n for (let i = 0; i < m.indices.length; i++) g.idx.push(m.indices[i]! + base);\n }\n }\n const meshes: Mesh[] = [...groups.values()].map((g) => {\n const n = g.pos.length / 3;\n return {\n name: `${g.texture || '(без текстуры)'} · ${g.shader}`, texture: g.texture, shader: g.shader, material: g.material, vertexCount: n,\n pos: Float32Array.from(g.pos), nor: Float32Array.from(g.nor), uv: Float32Array.from(g.uv),\n bone: new Float32Array(n * 4), weight: new Float32Array(n * 4).map((_, i) => (i % 4 === 0 ? 1 : 0)), indices: Uint32Array.from(g.idx), skinned: false,\n };\n });\n if (!meshes.length) throw new Error(`Объекты уровня не найдены (${missing.size} ссылок): откройте папку SDK с rawdata/objects`);\n const info = [`Уровень SDK: ${opts?.get('level_path') ?? name}, объектов ${refs.length}, показано ${placed}, мешей (по текстурам) ${meshes.length}`];\n if (missing.size) info.push(`Нет файлов объектов (${missing.size}): ${[...missing].slice(0, 6).join(', ')}${missing.size > 6 ? '…' : ''}`);\n return { meshes, skeleton: null, motions: [], bounds: boundsOf(meshes), flipZ: true, info };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Mesh, Motion, SkeletonRuntime } from '@gmvjs/core/types';
2
+ import type { XrayMotion } from './motion';
3
+ import { type OgfBone } from './ogf';
4
+ type M16 = Float32Array;
5
+ export declare class XraySkeleton implements SkeletonRuntime {
6
+ readonly transposeBind: boolean;
7
+ readonly n: number;
8
+ readonly parent: number[];
9
+ readonly order: number[];
10
+ readonly bindLocal: Float32Array;
11
+ readonly bindWorld: Float32Array;
12
+ readonly invBind: Float32Array;
13
+ readonly world: Float32Array;
14
+ readonly skin: Float32Array;
15
+ readonly bindLocalV: M16[];
16
+ readonly bindWorldV: M16[];
17
+ readonly invV: M16[];
18
+ readonly worldV: M16[];
19
+ readonly skinV: M16[];
20
+ private readonly scratch;
21
+ bindVariant: string;
22
+ bindErr: [number, number];
23
+ constructor(bones: OgfBone[], transposeBind?: boolean);
24
+ /** Матрица кости по углам setXYZi и позиции — в той же трактовке, что bind-поза (для анимаций SDK). */
25
+ local(rot: number[], pos: number[], o: Float32Array): void;
26
+ /** Выбор варианта трактовки углов: вершины должны лежать рядом со своими костями. */
27
+ static auto(bones: OgfBone[], meshes: readonly Mesh[]): XraySkeleton;
28
+ pose(motion: Motion | null, time: number, loop: boolean, invQ?: boolean): void;
29
+ /** Автоподбор знака кватерниона: у верного варианта длины рёбер меша почти не меняются. */
30
+ detectInvert(meshes: readonly Mesh[], motion: XrayMotion): boolean;
31
+ /** Искажение меша анимацией: средняя относительная ошибка длин рёбер на трёх кадрах (0 — рёбра не тянутся). */
32
+ motionError(meshes: readonly Mesh[], motion: XrayMotion, inv: boolean): number;
33
+ }
34
+ export {};
@@ -0,0 +1,252 @@
1
+ import { FPS } from "./ogf.js";
2
+ const Q = 1 / 32767;
3
+ function mulRow(a, b, o) {
4
+ for (let r = 0; r < 4; r++)
5
+ for (let c = 0; c < 4; c++)
6
+ o[r * 4 + c] = a[r * 4] * b[c] + a[r * 4 + 1] * b[4 + c] + a[r * 4 + 2] * b[8 + c] + a[r * 4 + 3] * b[12 + c];
7
+ }
8
+ function rigidInv(a, o) {
9
+ for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) o[r * 4 + c] = a[c * 4 + r];
10
+ for (let c = 0; c < 3; c++) o[12 + c] = -(a[12] * a[c * 4] + a[13] * a[c * 4 + 1] + a[14] * a[c * 4 + 2]);
11
+ o[3] = o[7] = o[11] = 0;
12
+ o[15] = 1;
13
+ }
14
+ function eulerLocal(rot, pos, o, transpose) {
15
+ const cx = Math.cos(rot[0]), sx = Math.sin(rot[0]), cy = Math.cos(rot[1]), sy = Math.sin(rot[1]), cz = Math.cos(rot[2]), sz = Math.sin(rot[2]);
16
+ const m = [cz * cy + sz * sx * sy, sz * cx, -cz * sy + sz * sx * cy, -sz * cy + cz * sx * sy, cz * cx, sz * sy + cz * sx * cy, cx * sy, -sx, cx * cy];
17
+ for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) o[r * 4 + c] = transpose ? m[c * 3 + r] : m[r * 3 + c];
18
+ o[3] = o[7] = o[11] = 0;
19
+ o[12] = pos[0];
20
+ o[13] = pos[1];
21
+ o[14] = pos[2];
22
+ o[15] = 1;
23
+ }
24
+ function quatLocal(x, y, z, w, tx, ty, tz, o) {
25
+ const xx = x * x, yy = y * y, zz = z * z, xy = x * y, xz = x * z, yz = y * z, wx = w * x, wy = w * y, wz = w * z;
26
+ o[0] = 1 - 2 * (yy + zz);
27
+ o[1] = 2 * (xy - wz);
28
+ o[2] = 2 * (xz + wy);
29
+ o[3] = 0;
30
+ o[4] = 2 * (xy + wz);
31
+ o[5] = 1 - 2 * (xx + zz);
32
+ o[6] = 2 * (yz - wx);
33
+ o[7] = 0;
34
+ o[8] = 2 * (xz - wy);
35
+ o[9] = 2 * (yz + wx);
36
+ o[10] = 1 - 2 * (xx + yy);
37
+ o[11] = 0;
38
+ o[12] = tx;
39
+ o[13] = ty;
40
+ o[14] = tz;
41
+ o[15] = 1;
42
+ }
43
+ function trackLocal(t, f0, f1, a, invQ, o) {
44
+ const R = t.rot;
45
+ let x, y, z, w;
46
+ if (R.length === 4) {
47
+ x = R[0] * Q;
48
+ y = R[1] * Q;
49
+ z = R[2] * Q;
50
+ w = R[3] * Q;
51
+ } else {
52
+ const i = f0 * 4, j = f1 * 4;
53
+ const x0 = R[i] * Q, y0 = R[i + 1] * Q, z0 = R[i + 2] * Q, w0 = R[i + 3] * Q;
54
+ let x1 = R[j] * Q, y1 = R[j + 1] * Q, z1 = R[j + 2] * Q, w1 = R[j + 3] * Q;
55
+ if (x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1 < 0) {
56
+ x1 = -x1;
57
+ y1 = -y1;
58
+ z1 = -z1;
59
+ w1 = -w1;
60
+ }
61
+ x = x0 + (x1 - x0) * a;
62
+ y = y0 + (y1 - y0) * a;
63
+ z = z0 + (z1 - z0) * a;
64
+ w = w0 + (w1 - w0) * a;
65
+ }
66
+ const l = Math.hypot(x, y, z, w) || 1;
67
+ x /= l;
68
+ y /= l;
69
+ z /= l;
70
+ w /= l;
71
+ if (invQ) {
72
+ x = -x;
73
+ y = -y;
74
+ z = -z;
75
+ }
76
+ const T = t.trans;
77
+ let tx, ty, tz;
78
+ if (T.length === 3) {
79
+ tx = T[0];
80
+ ty = T[1];
81
+ tz = T[2];
82
+ } else {
83
+ const i = f0 * 3, j = f1 * 3;
84
+ tx = T[i] + (T[j] - T[i]) * a;
85
+ ty = T[i + 1] + (T[j + 1] - T[i + 1]) * a;
86
+ tz = T[i + 2] + (T[j + 2] - T[i + 2]) * a;
87
+ }
88
+ quatLocal(x, y, z, w, tx, ty, tz, o);
89
+ }
90
+ class XraySkeleton {
91
+ constructor(bones, transposeBind = false) {
92
+ this.transposeBind = transposeBind;
93
+ const n = this.n = bones.length;
94
+ this.parent = bones.map((b) => b.parent);
95
+ const order = [], seen = new Uint8Array(n);
96
+ const visit = (i) => {
97
+ if (seen[i]) return;
98
+ seen[i] = 1;
99
+ if (this.parent[i] >= 0) visit(this.parent[i]);
100
+ order.push(i);
101
+ };
102
+ for (let i = 0; i < n; i++) visit(i);
103
+ this.order = order;
104
+ const mk = () => new Float32Array(16 * n), view = (arr) => Array.from({ length: n }, (_, i) => arr.subarray(16 * i, 16 * i + 16));
105
+ this.bindLocal = mk();
106
+ this.bindWorld = mk();
107
+ this.invBind = mk();
108
+ this.world = mk();
109
+ this.skin = mk();
110
+ this.bindLocalV = view(this.bindLocal);
111
+ this.bindWorldV = view(this.bindWorld);
112
+ this.invV = view(this.invBind);
113
+ this.worldV = view(this.world);
114
+ this.skinV = view(this.skin);
115
+ for (const i of order) {
116
+ eulerLocal(bones[i].rot, bones[i].pos, this.bindLocalV[i], transposeBind);
117
+ const p = this.parent[i];
118
+ if (p < 0) this.bindWorldV[i].set(this.bindLocalV[i]);
119
+ else mulRow(this.bindLocalV[i], this.bindWorldV[p], this.bindWorldV[i]);
120
+ rigidInv(this.bindWorldV[i], this.invV[i]);
121
+ }
122
+ for (let k = 0; k < 16 * n; k++) this.skin[k] = k % 16 % 5 === 0 ? 1 : 0;
123
+ this.world.set(this.bindWorld);
124
+ }
125
+ transposeBind;
126
+ n;
127
+ parent;
128
+ order;
129
+ bindLocal;
130
+ bindWorld;
131
+ invBind;
132
+ world;
133
+ skin;
134
+ bindLocalV;
135
+ bindWorldV;
136
+ invV;
137
+ worldV;
138
+ skinV;
139
+ scratch = new Float32Array(16);
140
+ bindVariant = "";
141
+ bindErr = [0, 0];
142
+ /** Матрица кости по углам setXYZi и позиции — в той же трактовке, что bind-поза (для анимаций SDK). */
143
+ local(rot, pos, o) {
144
+ eulerLocal(rot, pos, o, this.transposeBind);
145
+ }
146
+ /** Выбор варианта трактовки углов: вершины должны лежать рядом со своими костями. */
147
+ static auto(bones, meshes) {
148
+ const a = new XraySkeleton(bones, false);
149
+ if (!a.n) return a;
150
+ const b = new XraySkeleton(bones, true);
151
+ const err = (s2) => {
152
+ let e = 0, c = 0;
153
+ for (const m of meshes) {
154
+ if (!m.skinned) continue;
155
+ const step = Math.max(1, Math.floor(m.vertexCount / 1500));
156
+ for (let v = 0; v < m.vertexCount; v += step) {
157
+ let bi = m.bone[v * 4], bw = m.weight[v * 4];
158
+ for (let k = 1; k < 4; k++) if (m.weight[v * 4 + k] > bw) {
159
+ bw = m.weight[v * 4 + k];
160
+ bi = m.bone[v * 4 + k];
161
+ }
162
+ const W = s2.bindWorldV[bi];
163
+ e += Math.hypot(m.pos[v * 3] - W[12], m.pos[v * 3 + 1] - W[13], m.pos[v * 3 + 2] - W[14]);
164
+ c++;
165
+ }
166
+ }
167
+ return c ? e / c : 0;
168
+ };
169
+ const ea = err(a), eb = err(b);
170
+ const s = eb < ea * 0.98 ? b : a;
171
+ s.bindVariant = s === b ? "B (транспонированный)" : "A";
172
+ s.bindErr = [ea, eb];
173
+ return s;
174
+ }
175
+ pose(motion, time, loop, invQ = false) {
176
+ if (!this.n) return;
177
+ const m = motion && motion.kind === "xray" ? motion : null;
178
+ const frame = (x, lp) => {
179
+ const F = x.frames;
180
+ let fr = time * FPS;
181
+ fr = lp ? (fr % F + F) % F : Math.min(Math.max(fr, 0), F - 1);
182
+ const f0 = Math.floor(fr);
183
+ return [f0, lp ? (f0 + 1) % F : Math.min(f0 + 1, F - 1), fr - f0];
184
+ };
185
+ const list = m ? [[m, ...frame(m, loop)], ...(m.layers ?? []).map((x) => [x, ...frame(x, true)])] : [];
186
+ for (const i of this.order) {
187
+ const L = this.scratch;
188
+ let done = false;
189
+ for (const [x, f0, f1, a] of list) {
190
+ const tr = x.tracks[i];
191
+ if (tr) {
192
+ trackLocal(tr, f0, f1, a, invQ, L);
193
+ done = true;
194
+ break;
195
+ }
196
+ }
197
+ if (!done) L.set(this.bindLocalV[i]);
198
+ const p = this.parent[i];
199
+ if (p < 0) this.worldV[i].set(L);
200
+ else mulRow(L, this.worldV[p], this.worldV[i]);
201
+ mulRow(this.invV[i], this.worldV[i], this.skinV[i]);
202
+ }
203
+ }
204
+ /** Автоподбор знака кватерниона: у верного варианта длины рёбер меша почти не меняются. */
205
+ detectInvert(meshes, motion) {
206
+ const s0 = this.motionError(meshes, motion, false), s1 = this.motionError(meshes, motion, true);
207
+ return s1 < s0 * 0.9;
208
+ }
209
+ /** Искажение меша анимацией: средняя относительная ошибка длин рёбер на трёх кадрах (0 — рёбра не тянутся). */
210
+ motionError(meshes, motion, inv) {
211
+ const edges = [];
212
+ for (const m of meshes) {
213
+ if (!m.skinned) continue;
214
+ const tc = m.indices.length / 3, step = Math.max(1, Math.floor(tc / 1500));
215
+ for (let t = 0; t < tc; t += step) edges.push([m, m.indices[t * 3], m.indices[t * 3 + 1]]);
216
+ }
217
+ if (!edges.length) return 0;
218
+ const sk = (m, v, o) => {
219
+ const x = m.pos[v * 3], y = m.pos[v * 3 + 1], z = m.pos[v * 3 + 2];
220
+ o[0] = o[1] = o[2] = 0;
221
+ for (let k = 0; k < 4; k++) {
222
+ const w = m.weight[v * 4 + k];
223
+ if (!w) continue;
224
+ const M = this.skinV[m.bone[v * 4 + k]];
225
+ o[0] += w * (x * M[0] + y * M[4] + z * M[8] + M[12]);
226
+ o[1] += w * (x * M[1] + y * M[5] + z * M[9] + M[13]);
227
+ o[2] += w * (x * M[2] + y * M[6] + z * M[10] + M[14]);
228
+ }
229
+ };
230
+ const A = [0, 0, 0], B = [0, 0, 0];
231
+ const score = (inv2) => {
232
+ let d = 0, l = 0;
233
+ for (const fr of [0, Math.floor(motion.frames / 2), motion.frames - 1]) {
234
+ this.pose(motion, fr / FPS, false, inv2);
235
+ for (const [m, a, b] of edges) {
236
+ sk(m, a, A);
237
+ sk(m, b, B);
238
+ const l1 = Math.hypot(A[0] - B[0], A[1] - B[1], A[2] - B[2]);
239
+ const l0 = Math.hypot(m.pos[a * 3] - m.pos[b * 3], m.pos[a * 3 + 1] - m.pos[b * 3 + 1], m.pos[a * 3 + 2] - m.pos[b * 3 + 2]);
240
+ d += Math.abs(l1 - l0);
241
+ l += l0;
242
+ }
243
+ }
244
+ return d / (l || 1);
245
+ };
246
+ return score(inv);
247
+ }
248
+ }
249
+ export {
250
+ XraySkeleton
251
+ };
252
+ //# sourceMappingURL=skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skeleton.js","sources":["../src/skeleton.ts"],"sourcesContent":["// Скелет X-Ray: углы bind-позы из IK-данных, анимации — квантованные кватернионы (row-vector матрицы, как в движке).\nimport type { Mesh, Motion, SkeletonRuntime } from '@gmvjs/core/types';\nimport type { XrayMotion, XrayTrack } from './motion';\nimport { FPS, type OgfBone } from './ogf';\n\nconst Q = 1 / 32767;\ntype M16 = Float32Array;\n\nfunction mulRow(a: M16, b: M16, o: M16): void { // строки-векторы: сначала a, затем b\n for (let r = 0; r < 4; r++)\n for (let c = 0; c < 4; c++)\n o[r * 4 + c] = a[r * 4]! * b[c]! + a[r * 4 + 1]! * b[4 + c]! + a[r * 4 + 2]! * b[8 + c]! + a[r * 4 + 3]! * b[12 + c]!;\n}\nfunction rigidInv(a: M16, o: M16): void {\n for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) o[r * 4 + c] = a[c * 4 + r]!;\n for (let c = 0; c < 3; c++) o[12 + c] = -(a[12]! * a[c * 4]! + a[13]! * a[c * 4 + 1]! + a[14]! * a[c * 4 + 2]!);\n o[3] = o[7] = o[11] = 0; o[15] = 1;\n}\nfunction eulerLocal(rot: number[], pos: number[], o: M16, transpose: boolean): void {\n // Fmatrix::setXYZi(x, y, z) = setHPB(−y, −x, −z): для вектора-строки сначала поворот вокруг Z, затем X, затем Y\n // (крен, тангаж, курс). Проверено по скинингу ЗП: длины рёбер меша в анимациях сохраняются только при таком порядке.\n const cx = Math.cos(rot[0]!), sx = Math.sin(rot[0]!), cy = Math.cos(rot[1]!), sy = Math.sin(rot[1]!), cz = Math.cos(rot[2]!), sz = Math.sin(rot[2]!);\n const m = [cz * cy + sz * sx * sy, sz * cx, -cz * sy + sz * sx * cy, -sz * cy + cz * sx * sy, cz * cx, sz * sy + cz * sx * cy, cx * sy, -sx, cx * cy];\n for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) o[r * 4 + c] = transpose ? m[c * 3 + r]! : m[r * 3 + c]!;\n o[3] = o[7] = o[11] = 0; o[12] = pos[0]!; o[13] = pos[1]!; o[14] = pos[2]!; o[15] = 1;\n}\nfunction quatLocal(x: number, y: number, z: number, w: number, tx: number, ty: number, tz: number, o: M16): void { // Fmatrix::rotation(Fquaternion) + translate_over\n const xx = x * x, yy = y * y, zz = z * z, xy = x * y, xz = x * z, yz = y * z, wx = w * x, wy = w * y, wz = w * z;\n o[0] = 1 - 2 * (yy + zz); o[1] = 2 * (xy - wz); o[2] = 2 * (xz + wy); o[3] = 0;\n o[4] = 2 * (xy + wz); o[5] = 1 - 2 * (xx + zz); o[6] = 2 * (yz - wx); o[7] = 0;\n o[8] = 2 * (xz - wy); o[9] = 2 * (yz + wx); o[10] = 1 - 2 * (xx + yy); o[11] = 0;\n o[12] = tx; o[13] = ty; o[14] = tz; o[15] = 1;\n}\nfunction trackLocal(t: XrayTrack, f0: number, f1: number, a: number, invQ: boolean, o: M16): void {\n const R = t.rot;\n let x: number, y: number, z: number, w: number;\n if (R.length === 4) { x = R[0]! * Q; y = R[1]! * Q; z = R[2]! * Q; w = R[3]! * Q; }\n else {\n const i = f0 * 4, j = f1 * 4;\n const x0 = R[i]! * Q, y0 = R[i + 1]! * Q, z0 = R[i + 2]! * Q, w0 = R[i + 3]! * Q;\n let x1 = R[j]! * Q, y1 = R[j + 1]! * Q, z1 = R[j + 2]! * Q, w1 = R[j + 3]! * Q;\n if (x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1 < 0) { x1 = -x1; y1 = -y1; z1 = -z1; w1 = -w1; }\n x = x0 + (x1 - x0) * a; y = y0 + (y1 - y0) * a; z = z0 + (z1 - z0) * a; w = w0 + (w1 - w0) * a;\n }\n const l = Math.hypot(x, y, z, w) || 1;\n x /= l; y /= l; z /= l; w /= l;\n if (invQ) { x = -x; y = -y; z = -z; }\n const T = t.trans;\n let tx: number, ty: number, tz: number;\n if (T.length === 3) { tx = T[0]!; ty = T[1]!; tz = T[2]!; }\n else { const i = f0 * 3, j = f1 * 3; tx = T[i]! + (T[j]! - T[i]!) * a; ty = T[i + 1]! + (T[j + 1]! - T[i + 1]!) * a; tz = T[i + 2]! + (T[j + 2]! - T[i + 2]!) * a; }\n quatLocal(x, y, z, w, tx, ty, tz, o);\n}\n\nexport class XraySkeleton implements SkeletonRuntime {\n readonly n: number;\n readonly parent: number[];\n readonly order: number[];\n readonly bindLocal: Float32Array; readonly bindWorld: Float32Array; readonly invBind: Float32Array;\n readonly world: Float32Array; readonly skin: Float32Array;\n readonly bindLocalV: M16[]; readonly bindWorldV: M16[]; readonly invV: M16[]; readonly worldV: M16[]; readonly skinV: M16[];\n private readonly scratch = new Float32Array(16);\n bindVariant = '';\n bindErr: [number, number] = [0, 0];\n\n constructor(bones: OgfBone[], readonly transposeBind = false) {\n const n = (this.n = bones.length);\n this.parent = bones.map((b) => b.parent);\n const order: number[] = [], seen = new Uint8Array(n);\n const visit = (i: number) => { if (seen[i]) return; seen[i] = 1; if (this.parent[i]! >= 0) visit(this.parent[i]!); order.push(i); };\n for (let i = 0; i < n; i++) visit(i);\n this.order = order;\n const mk = () => new Float32Array(16 * n), view = (arr: Float32Array) => Array.from({ length: n }, (_, i) => arr.subarray(16 * i, 16 * i + 16));\n this.bindLocal = mk(); this.bindWorld = mk(); this.invBind = mk(); this.world = mk(); this.skin = mk();\n this.bindLocalV = view(this.bindLocal); this.bindWorldV = view(this.bindWorld); this.invV = view(this.invBind);\n this.worldV = view(this.world); this.skinV = view(this.skin);\n for (const i of order) {\n eulerLocal(bones[i]!.rot, bones[i]!.pos, this.bindLocalV[i]!, transposeBind);\n const p = this.parent[i]!;\n if (p < 0) this.bindWorldV[i]!.set(this.bindLocalV[i]!); else mulRow(this.bindLocalV[i]!, this.bindWorldV[p]!, this.bindWorldV[i]!);\n rigidInv(this.bindWorldV[i]!, this.invV[i]!);\n }\n for (let k = 0; k < 16 * n; k++) this.skin[k] = (k % 16) % 5 === 0 ? 1 : 0;\n this.world.set(this.bindWorld);\n }\n\n /** Матрица кости по углам setXYZi и позиции — в той же трактовке, что bind-поза (для анимаций SDK). */\n local(rot: number[], pos: number[], o: Float32Array): void { eulerLocal(rot, pos, o, this.transposeBind); }\n\n /** Выбор варианта трактовки углов: вершины должны лежать рядом со своими костями. */\n static auto(bones: OgfBone[], meshes: readonly Mesh[]): XraySkeleton {\n const a = new XraySkeleton(bones, false);\n if (!a.n) return a;\n const b = new XraySkeleton(bones, true);\n const err = (s: XraySkeleton) => {\n let e = 0, c = 0;\n for (const m of meshes) {\n if (!m.skinned) continue;\n const step = Math.max(1, Math.floor(m.vertexCount / 1500));\n for (let v = 0; v < m.vertexCount; v += step) {\n let bi = m.bone[v * 4]!, bw = m.weight[v * 4]!;\n for (let k = 1; k < 4; k++) if (m.weight[v * 4 + k]! > bw) { bw = m.weight[v * 4 + k]!; bi = m.bone[v * 4 + k]!; }\n const W = s.bindWorldV[bi]!;\n e += Math.hypot(m.pos[v * 3]! - W[12]!, m.pos[v * 3 + 1]! - W[13]!, m.pos[v * 3 + 2]! - W[14]!);\n c++;\n }\n }\n return c ? e / c : 0;\n };\n const ea = err(a), eb = err(b);\n const s = eb < ea * 0.98 ? b : a;\n s.bindVariant = s === b ? 'B (транспонированный)' : 'A';\n s.bindErr = [ea, eb];\n return s;\n }\n\n pose(motion: Motion | null, time: number, loop: boolean, invQ = false): void {\n if (!this.n) return;\n const m = motion && motion.kind === 'xray' ? motion : null;\n // кадры основной анимации и слоёв (у каждого своя длина; слои всегда по кругу)\n const frame = (x: XrayMotion, lp: boolean): [number, number, number] => {\n const F = x.frames;\n let fr = time * FPS;\n fr = lp ? ((fr % F) + F) % F : Math.min(Math.max(fr, 0), F - 1);\n const f0 = Math.floor(fr);\n return [f0, lp ? (f0 + 1) % F : Math.min(f0 + 1, F - 1), fr - f0];\n };\n const list: Array<[XrayMotion, number, number, number]> = m ? [[m, ...frame(m, loop)], ...(m.layers ?? []).map((x): [XrayMotion, number, number, number] => [x, ...frame(x, true)])] : [];\n for (const i of this.order) {\n const L = this.scratch;\n let done = false;\n for (const [x, f0, f1, a] of list) { const tr = x.tracks[i]; if (tr) { trackLocal(tr, f0, f1, a, invQ, L); done = true; break; } }\n if (!done) L.set(this.bindLocalV[i]!);\n const p = this.parent[i]!;\n if (p < 0) this.worldV[i]!.set(L); else mulRow(L, this.worldV[p]!, this.worldV[i]!);\n mulRow(this.invV[i]!, this.worldV[i]!, this.skinV[i]!);\n }\n }\n\n /** Автоподбор знака кватерниона: у верного варианта длины рёбер меша почти не меняются. */\n detectInvert(meshes: readonly Mesh[], motion: XrayMotion): boolean {\n const s0 = this.motionError(meshes, motion, false), s1 = this.motionError(meshes, motion, true);\n return s1 < s0 * 0.9;\n }\n\n /** Искажение меша анимацией: средняя относительная ошибка длин рёбер на трёх кадрах (0 — рёбра не тянутся). */\n motionError(meshes: readonly Mesh[], motion: XrayMotion, inv: boolean): number {\n const edges: Array<[Mesh, number, number]> = [];\n for (const m of meshes) {\n if (!m.skinned) continue;\n const tc = m.indices.length / 3, step = Math.max(1, Math.floor(tc / 1500));\n for (let t = 0; t < tc; t += step) edges.push([m, m.indices[t * 3]!, m.indices[t * 3 + 1]!]);\n }\n if (!edges.length) return 0;\n const sk = (m: Mesh, v: number, o: number[]) => {\n const x = m.pos[v * 3]!, y = m.pos[v * 3 + 1]!, z = m.pos[v * 3 + 2]!;\n o[0] = o[1] = o[2] = 0;\n for (let k = 0; k < 4; k++) {\n const w = m.weight[v * 4 + k]!;\n if (!w) continue;\n const M = this.skinV[m.bone[v * 4 + k]!]!;\n o[0] += w * (x * M[0]! + y * M[4]! + z * M[8]! + M[12]!); o[1] += w * (x * M[1]! + y * M[5]! + z * M[9]! + M[13]!); o[2] += w * (x * M[2]! + y * M[6]! + z * M[10]! + M[14]!);\n }\n };\n const A = [0, 0, 0], B = [0, 0, 0];\n const score = (inv: boolean) => {\n let d = 0, l = 0;\n for (const fr of [0, Math.floor(motion.frames / 2), motion.frames - 1]) {\n this.pose(motion, fr / FPS, false, inv);\n for (const [m, a, b] of edges) {\n sk(m, a, A); sk(m, b, B);\n const l1 = Math.hypot(A[0]! - B[0]!, A[1]! - B[1]!, A[2]! - B[2]!);\n const l0 = Math.hypot(m.pos[a * 3]! - m.pos[b * 3]!, m.pos[a * 3 + 1]! - m.pos[b * 3 + 1]!, m.pos[a * 3 + 2]! - m.pos[b * 3 + 2]!);\n d += Math.abs(l1 - l0); l += l0;\n }\n }\n return d / (l || 1);\n };\n return score(inv);\n }\n}\n\n"],"names":["s","inv"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { XrayMotion } from './motion';
2
+ export interface EnvKey {
3
+ value: number;
4
+ time: number;
5
+ shape: number;
6
+ tension: number;
7
+ continuity: number;
8
+ bias: number;
9
+ param: number[];
10
+ }
11
+ export interface Envelope {
12
+ behavior: [number, number];
13
+ keys: EnvKey[];
14
+ }
15
+ export interface SMotion {
16
+ name: string;
17
+ frameStart: number;
18
+ frameEnd: number;
19
+ fps: number;
20
+ version: number;
21
+ flags: number;
22
+ boneOrPart: number;
23
+ speed: number;
24
+ bones: Array<{
25
+ name: string;
26
+ flags: number;
27
+ envs: Envelope[];
28
+ }>;
29
+ }
30
+ /** Список анимаций: u32 число + анимации (.skls, чанк 0x0916 .object). */
31
+ export declare function parseSMotions(u: Uint8Array): SMotion[];
32
+ /** .skl: чанк 0x1200 с одной анимацией. */
33
+ export declare function parseSkl(u: Uint8Array): SMotion;
34
+ export declare function evalEnvelope(env: Envelope, time: number): number;
35
+ /** Кватернион из матрицы поворота (строки, как в quatLocal скелета X-Ray). */
36
+ export declare function quatOfRows(o: ArrayLike<number>): [number, number, number, number];
37
+ /**
38
+ * Анимация SDK → треки X-Ray по костям модели (по именам): огибающие считаются на каждом кадре, поворот (setXYZi)
39
+ * переводится в кватернион. local(rot, pos, out) — матрица кости по углам, как у bind-позы этого скелета.
40
+ */
41
+ export declare function smotionToXray(sm: SMotion, boneNames: readonly string[], local: (rot: number[], pos: number[], o: Float32Array) => void, parts?: Array<{
42
+ name: string;
43
+ bones: string[];
44
+ }>): XrayMotion;