@gmvjs/core 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +41 -35
  2. package/dist/archives/common.d.ts +9 -9
  3. package/dist/archives/common.js.map +1 -1
  4. package/dist/archives/folder.d.ts +1 -1
  5. package/dist/archives/folder.js +1 -1
  6. package/dist/archives/folder.js.map +1 -1
  7. package/dist/archives/zip.js.map +1 -1
  8. package/dist/binary.d.ts +7 -7
  9. package/dist/binary.js.map +1 -1
  10. package/dist/builtin-en.js.map +1 -1
  11. package/dist/builtin.js +1 -1
  12. package/dist/builtin.js.map +1 -1
  13. package/dist/codecs/zlib.d.ts +2 -2
  14. package/dist/codecs/zlib.js.map +1 -1
  15. package/dist/diagnostics.js.map +1 -1
  16. package/dist/geometry.d.ts +5 -5
  17. package/dist/geometry.js.map +1 -1
  18. package/dist/i18n.d.ts +4 -4
  19. package/dist/i18n.js.map +1 -1
  20. package/dist/images/bc45.d.ts +1 -1
  21. package/dist/images/bc45.js.map +1 -1
  22. package/dist/images/bc7.d.ts +1 -1
  23. package/dist/images/bc7.js.map +1 -1
  24. package/dist/images/dds.d.ts +1 -1
  25. package/dist/images/dds.js.map +1 -1
  26. package/dist/images/pcx.js.map +1 -1
  27. package/dist/images/tga.js.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/levelbuilder.d.ts +3 -3
  31. package/dist/levelbuilder.js +1 -1
  32. package/dist/levelbuilder.js.map +1 -1
  33. package/dist/math.d.ts +6 -6
  34. package/dist/math.js.map +1 -1
  35. package/dist/normals.d.ts +5 -5
  36. package/dist/normals.js.map +1 -1
  37. package/dist/plugin.d.ts +57 -57
  38. package/dist/plugin.js.map +1 -1
  39. package/dist/registry.d.ts +14 -14
  40. package/dist/registry.js +13 -13
  41. package/dist/registry.js.map +1 -1
  42. package/dist/scene.d.ts +14 -14
  43. package/dist/scene.js.map +1 -1
  44. package/dist/session.d.ts +34 -34
  45. package/dist/session.js +33 -33
  46. package/dist/session.js.map +1 -1
  47. package/dist/skeleton.d.ts +3 -3
  48. package/dist/skeleton.js.map +1 -1
  49. package/dist/surface.d.ts +4 -4
  50. package/dist/surface.js.map +1 -1
  51. package/dist/textures.d.ts +12 -12
  52. package/dist/textures.js +9 -9
  53. package/dist/textures.js.map +1 -1
  54. package/dist/types.d.ts +103 -103
  55. package/dist/types.js.map +1 -1
  56. package/dist/writers/dds.d.ts +1 -1
  57. package/dist/writers/dds.js.map +1 -1
  58. package/dist/writers/obj.js.map +1 -1
  59. package/dist/writers/zip.js.map +1 -1
  60. package/package.json +3 -3
package/dist/types.d.ts CHANGED
@@ -4,7 +4,7 @@ export interface Bounds {
4
4
  min: number[];
5
5
  max: number[];
6
6
  }
7
- /** Декодированная текстура: либо RGBA-пиксели, либо ImageBitmap (PNG/JPG через браузер). */
7
+ /** Decoded texture: either RGBA pixels or an ImageBitmap (PNG/JPG via the browser). */
8
8
  export interface DecodedImage {
9
9
  width: number;
10
10
  height: number;
@@ -12,20 +12,20 @@ export interface DecodedImage {
12
12
  bitmap?: ImageBitmap;
13
13
  name?: string;
14
14
  }
15
- /** Источник байтов, который можно открыть как архив (File в браузере, Blob с именем в Node). */
15
+ /** A byte source that can be opened as an archive (File in the browser, a named Blob in Node). */
16
16
  export type BlobFile = Blob & {
17
17
  readonly name: string;
18
18
  readonly webkitRelativePath?: string;
19
19
  };
20
- /** «Файл-подобный» объект: обычный File, запись архива или уже декодированная текстура. */
20
+ /** File-like object: a regular File, an archive entry or an already decoded texture. */
21
21
  export interface VFile {
22
22
  readonly name: string;
23
- /** Путь внутри архива / папки (нижний регистр, «/»). */
23
+ /** Path inside an archive / folder (lower case, "/"). */
24
24
  readonly path?: string;
25
25
  readonly archive?: Archive;
26
26
  readonly webkitRelativePath?: string;
27
27
  arrayBuffer(): Promise<ArrayBuffer>;
28
- /** Если задано — это уже декодированная картинка (например, из TXD). */
28
+ /** If set, this is an already decoded image (for example from a TXD). */
29
29
  readonly decoded?: DecodedImage;
30
30
  }
31
31
  export interface ArchiveEntry {
@@ -34,33 +34,33 @@ export interface ArchiveEntry {
34
34
  }
35
35
  export interface Archive {
36
36
  readonly name: string;
37
- /** id плагина архива: 'zip', 'bsa', 'folder'… */
37
+ /** Archive plugin id: 'zip', 'bsa', 'folder'… */
38
38
  readonly kind: string;
39
39
  readonly version: number;
40
40
  readonly entries: ArchiveEntry[];
41
- /** Полный путь -> запись. */
41
+ /** Full path -> entry. */
42
42
  readonly index: Map<string, ArchiveEntry>;
43
- /** Путь без расширения -> запись текстуры. */
43
+ /** Path without extension -> texture entry. */
44
44
  readonly byKey: Map<string, ArchiveEntry>;
45
- /** Имя файла -> запись (для «Папки игры»). */
45
+ /** File name -> entry (for the "Game folder"). */
46
46
  readonly byBase?: Map<string, ArchiveEntry>;
47
47
  lookup(path: string): ArchiveEntry | null;
48
48
  read(entry: ArchiveEntry): Promise<Uint8Array>;
49
49
  fileOf(entry: ArchiveEntry): VFile;
50
- /** Исходный файл записи (только «Папка игры»): чтобы открыть лежащий в папке архив. */
50
+ /** Source file of the entry ("Game folder" only): to open an archive lying in the folder. */
51
51
  blobOf?(entry: ArchiveEntry): BlobFile | null;
52
- /** Текстура по точному пути из модели, даже если её нет в списке (архивы с именами-хешами: Far Cry 2). */
52
+ /** Texture at the exact path from the model, even if it is not in the list (archives with hashed names: Far Cry 2). */
53
53
  findTexture?(path: string): ArchiveEntry | null;
54
54
  }
55
- /** Канал карты: путь/имя файла и номер канала (0 R, 1 G, 2 B, 3 A). */
55
+ /** Map channel: path/file name and channel number (0 R, 1 G, 2 B, 3 A). */
56
56
  export interface ChannelRef {
57
57
  map: string;
58
58
  channel: 0 | 1 | 2 | 3;
59
59
  image?: DecodedImage;
60
60
  }
61
61
  /**
62
- * Поверхность для PBR. Схема metal/rough (glTF, BeamNG, SnowRunner) или spec/gloss (Arma _smdi, MudRunner __s_d):
63
- * gloss = 1 − roughness, specular — сила блика неметалла.
62
+ * PBR surface. The metal/rough scheme (glTF, BeamNG, SnowRunner) or spec/gloss (Arma _smdi, MudRunner __s_d):
63
+ * gloss = 1 − roughness, specular is the non-metal specular strength.
64
64
  */
65
65
  export interface SurfaceInfo {
66
66
  roughness?: ChannelRef;
@@ -70,156 +70,156 @@ export interface SurfaceInfo {
70
70
  specular?: ChannelRef;
71
71
  roughnessFactor?: number;
72
72
  metallicFactor?: number;
73
- /** Сила блика без карты (0..1; 0.5 ≈ F0 0.04). */
73
+ /** Specular strength without a map (0..1; 0.5 ≈ F0 0.04). */
74
74
  specularFactor?: number;
75
- /** Откуда взято (для подписи): «BeamNG», «Arma _smdi»… */
75
+ /** Where it came from (for the label): "BeamNG", "Arma _smdi"… */
76
76
  source?: string;
77
77
  }
78
78
  export interface Mesh {
79
79
  name: string;
80
- /** Имя текстуры/материала, как записано в модели. */
80
+ /** Texture/material name as written in the model. */
81
81
  texture: string;
82
82
  shader?: string;
83
- /** Имя материала (assimp/HPL). */
83
+ /** Material name (assimp/HPL). */
84
84
  material?: string;
85
85
  vertexCount: number;
86
86
  pos: Float32Array;
87
87
  nor: Float32Array;
88
88
  uv: Float32Array;
89
- /** 4 индекса костей на вершину (float — для атрибутов WebGL). */
89
+ /** 4 bone indices per vertex (float, for WebGL attributes). */
90
90
  bone: Float32Array;
91
91
  weight: Float32Array;
92
92
  indices: Uint16Array | Uint32Array;
93
93
  skinned: boolean;
94
- /** Цвет без текстуры (RenderWare). */
94
+ /** Color without a texture (RenderWare). */
95
95
  color?: number[];
96
- /** Встроенная текстура (Quake MDL, GoldSrc). */
96
+ /** Embedded texture (Quake MDL, GoldSrc). */
97
97
  image?: DecodedImage;
98
- /** Покадровая вершинная анимация: pos/nor меняются на месте. */
98
+ /** Per-frame vertex animation: pos/nor change in place. */
99
99
  dynamic?: boolean;
100
100
  frames?: {
101
101
  pos: Float32Array[];
102
102
  nor: Float32Array[];
103
103
  };
104
- /** Кандидаты путей материалов (Source .vmt). */
104
+ /** Candidate material paths (Source .vmt). */
105
105
  materialPaths?: string[];
106
- /** Номер подматериала (CryEngine). */
106
+ /** Submaterial number (CryEngine). */
107
107
  matId?: number;
108
- /** Карта нормалей, как она записана в модели или материале (путь/имя); пусто — ищется по имени диффузной. */
108
+ /** Normal map as written in the model or material (path/name); empty means it is looked up from the diffuse name. */
109
109
  normalMap?: string;
110
- /** Встроенная карта нормалей. */
110
+ /** Embedded normal map. */
111
111
  normalImage?: DecodedImage;
112
- /** PBR-поверхность: шероховатость, металличность, AO, блик (карты и множители). */
112
+ /** PBR surface: roughness, metalness, AO, specular (maps and multipliers). */
113
113
  surface?: SurfaceInfo;
114
- /** Прозрачность по альфе текстуры «вырезанием» (решётки, листва, наклейки) — независимо от общего переключателя. */
114
+ /** Alpha-tested transparency from the texture alpha (grates, foliage, decals), independent of the global toggle. */
115
115
  alphaTest?: boolean;
116
- /** Служебная геометрия (световые плоскости, экраны-рендер-цели): по умолчанию скрыта. */
116
+ /** Helper geometry (light planes, render-target screens): hidden by default. */
117
117
  defaultHidden?: boolean;
118
- /** Второй набор UV — координаты в карте освещения (Model.lightmap). */
118
+ /** Second UV set: coordinates in the lightmap (Model.lightmap). */
119
119
  uv2?: Float32Array;
120
- /** Прозрачность по вершинам (0..1): слой ландшафта поверх основы — рисуется после непрозрачных мешей, с наложением. */
120
+ /** Per-vertex opacity (0..1): a terrain layer over the base, drawn after opaque meshes with blending. */
121
121
  vertexAlpha?: Float32Array;
122
- /** Запечённое освещение по вершинам: RGB(A) байтами, 128 — без изменений (умножение ×2, как у Black Box). */
122
+ /** Baked per-vertex lighting: RGB(A) bytes, 128 = unchanged (×2 multiplication, as in Black Box). */
123
123
  vertexShade?: Uint8Array;
124
124
  /**
125
- * Карта слоёв ландшафта (номер слоя Model.terrainLayers на тексель, по UV меша 0..1): вблизи вместо одной цветовой
126
- * карты (image) рисуются повторяющиеся текстуры слоёв, подкрашенные ею.
125
+ * Terrain layer map (a Model.terrainLayers layer number per texel, over the mesh UV 0..1): up close, instead of a single color
126
+ * map (image), repeating layer textures tinted by it are drawn.
127
127
  */
128
128
  terrainIndex?: {
129
129
  width: number;
130
130
  height: number;
131
131
  data: Uint8Array;
132
132
  };
133
- /** Отражение и преломление по экрану (вода, преломляющее стекло Source): рисуется после сцены по её снимку. */
133
+ /** Screen-space reflection and refraction (water, Source refracting glass): drawn after the scene from its snapshot. */
134
134
  screenFx?: ScreenFx;
135
135
  }
136
- /** Параметры экранной поверхности: вода (плоское отражение, преломление, туман по глубине) или стекло (преломление). */
136
+ /** Screen surface parameters: water (planar reflection, refraction, depth fog) or glass (refraction). */
137
137
  export interface ScreenFx {
138
138
  kind: 'water' | 'glass';
139
- /** Плоскость поверхности в координатах модели: n·p = d (для отражения воды). */
139
+ /** Surface plane in model coordinates: n·p = d (for water reflection). */
140
140
  plane?: [number, number, number, number];
141
- /** Цвет преломления (то, что под водой / за стеклом) и отражения (у стекла — блик окружения $envmaptint), 0..1. */
141
+ /** Refraction color (what is under water / behind glass) and reflection color (for glass, the $envmaptint environment highlight), 0..1. */
142
142
  refractTint: number[];
143
143
  reflectTint?: number[];
144
- /** Сила искажения по карте нормалей: доля экрана. */
144
+ /** Distortion strength from the normal map: a fraction of the screen. */
145
145
  refract: number;
146
146
  reflect?: number;
147
- /** Туман под водой: цвет 0..1, начало и конец в единицах модели. */
147
+ /** Underwater fog: color 0..1, start and end in model units. */
148
148
  fog?: {
149
149
  color: number[];
150
150
  start: number;
151
151
  end: number;
152
152
  };
153
- /** Сдвиг карты нормалей во времени (UV/сек). */
153
+ /** Normal map scrolling over time (UV/sec). */
154
154
  scroll?: [number, number];
155
- /** Без поддержки: цвет и непрозрачность полупрозрачного слоя. */
155
+ /** Without support: color and opacity of a translucent layer. */
156
156
  fallback: {
157
157
  color: number[];
158
158
  opacity: number;
159
159
  };
160
160
  }
161
- /** Слои поверхности ландшафта: текстуры одного размера и параметры показа. */
161
+ /** Terrain surface layers: textures of one size and display parameters. */
162
162
  export interface TerrainLayers {
163
- /** Текстуры слоёв (одного размера, степень двойки). */
163
+ /** Layer textures (one size, power of two). */
164
164
  images: DecodedImage[];
165
- /** Период повтора текстуры в единицах модели (по X и Z координат меша). */
165
+ /** Texture repeat period in model units (along the mesh X and Z coordinates). */
166
166
  period: number[];
167
- /** 0..1: насколько слой подкрашивается цветовой картой меша. */
167
+ /** 0..1: how much the layer is tinted by the mesh color map. */
168
168
  tint: number[];
169
- /** Средний цвет текстуры слоя (0..1) — для подкраски. */
169
+ /** Average color of the layer texture (0..1), for tinting. */
170
170
  mean: Array<[number, number, number]>;
171
- /** Дальность, на которой слои переходят в цветовую карту. */
171
+ /** Distance at which layers fade into the color map. */
172
172
  fade: number;
173
- /** Нормали слоёв (тот же размер, что images): RG — XY касательной нормали (DirectX), A — AO. */
173
+ /** Layer normals (same size as images): RG = XY of the tangent normal (DirectX), A = AO. */
174
174
  normals?: DecodedImage[];
175
- /** Средний масштаб: текстуры изменчивости (свой размер), период, сила, средний цвет. */
175
+ /** Medium scale: variation textures (own size), period, strength, average color. */
176
176
  middle?: DecodedImage[];
177
177
  midPeriod?: number[];
178
178
  midBlend?: number[];
179
179
  midMean?: Array<[number, number, number]>;
180
- /** Имена (для сведений). */
180
+ /** Names (for details). */
181
181
  names?: string[];
182
182
  }
183
- /** Запечённое освещение уровня (одна карта на модель): яркость в R, UV — Mesh.uv2. */
183
+ /** Baked level lighting (one map per model): brightness in R, UV is Mesh.uv2. */
184
184
  export interface Lightmap {
185
185
  width: number;
186
186
  height: number;
187
- /** Яркость, 1 байт на пиксель (или RGB, 3 байта, при rgb). */
187
+ /** Brightness, 1 byte per pixel (or RGB, 3 bytes, with rgb). */
188
188
  data: Uint8Array;
189
- /** Цветная карта (Source): RGB8. */
189
+ /** Color map (Source): RGB8. */
190
190
  rgb?: boolean;
191
- /** Множитель яркости при показе. */
191
+ /** Brightness multiplier for display. */
192
192
  scale?: number;
193
193
  /**
194
- * Карты освещения по трём направлениям базиса рельефа Source (bump lightmaps), тот же формат и раскладка, что data:
195
- * свет смешивается по нормали из карты нормалей. У граней без рельефа — копия data.
194
+ * Lightmaps for the three directions of the Source bump basis (bump lightmaps), same format and layout as data:
195
+ * light is blended by the normal from the normal map. Faces without bump get a copy of data.
196
196
  */
197
197
  bump?: [Uint8Array, Uint8Array, Uint8Array];
198
198
  }
199
- /** Ключи анимации assimp-стиля: [время, значение]. */
199
+ /** assimp-style animation keys: [time, value]. */
200
200
  export type Keys<T> = Array<[number, T]>;
201
201
  export interface NodeChannel {
202
202
  pos: Keys<number[]> | null;
203
203
  rot: Keys<number[]> | null;
204
204
  scl: Keys<number[]> | null;
205
- /** Аддитивная дорожка как смещение (перенос + поворот [w,x,y,z]) — применяется так, когда анимация — слой поверх другой. */
205
+ /** Additive track as an offset (translation + rotation [w,x,y,z]): applied this way when the animation is a layer over another. */
206
206
  add?: {
207
207
  pos: Keys<number[]> | null;
208
208
  rot: Keys<number[]> | null;
209
209
  };
210
210
  }
211
- /** Слой поверх основной анимации узлов: аддитивные каналы складываются, остальные заменяют позу (в пределах маски). */
211
+ /** Layer over the main node animation: additive channels are added, others replace the pose (within the mask). */
212
212
  export interface NodeLayer {
213
213
  motion: NodeMotion;
214
- /** Постоянный кадр слоя (поза прицеливания — средний кадр сетки направлений); нет — играет по времени. */
214
+ /** Fixed frame of the layer (aim pose: the middle frame of the direction grid); none means it plays over time. */
215
215
  frame?: number;
216
- /** Номера узлов, которые слой может менять; нет — все. */
216
+ /** Node numbers the layer may change; none means all. */
217
217
  mask?: ReadonlySet<number>;
218
- /** Только дополнять: узлы, у которых есть канал в основной анимации, слой не трогает (хват оружия у NPC — свой). */
218
+ /** Only complement: the layer does not touch nodes that have a channel in the main animation (NPC weapon grip is its own). */
219
219
  fill?: boolean;
220
220
  /**
221
- * Слой — сетка поз (кадр = строка·cols + столбец), например направления прицела: x — столбец, y — строка
222
- * (дробные: смешиваются 4 соседних кадра). Перекрывает frame.
221
+ * The layer is a pose grid (frame = row·cols + column), for example aim directions: x is the column, y the row
222
+ * (fractional: 4 neighbouring frames are blended). Overrides frame.
223
223
  */
224
224
  grid?: {
225
225
  cols: number;
@@ -230,32 +230,32 @@ export interface NodeLayer {
230
230
  }
231
231
  export interface MotionBase {
232
232
  name: string;
233
- /** Длительность, сек. */
233
+ /** Duration, seconds. */
234
234
  duration: number;
235
- /** Число кадров (для подписи в списке). */
235
+ /** Frame count (for the list label). */
236
236
  frames?: number;
237
- /** Откуда анимация: 'model' или имя файла. */
237
+ /** Where the animation comes from: 'model' or a file name. */
238
238
  source?: string;
239
- /** Анимация из длинного списка (Metro: тысячи файлов): данные читаются при выборе. */
239
+ /** Animation from a long list (Metro: thousands of files): data is read on selection. */
240
240
  load?(): Promise<Motion>;
241
241
  }
242
- /** Анимация узлов (assimp, NIF, MD5, PSA, Cast, GoldSrc). */
242
+ /** Node animation (assimp, NIF, MD5, PSA, Cast, GoldSrc). */
243
243
  export interface NodeMotion extends MotionBase {
244
244
  kind: 'node';
245
245
  tps: number;
246
246
  chan: Map<number, NodeChannel>;
247
- /** Слои поверх (прицеливание, дыхание). */
247
+ /** Layers on top (aiming, breathing). */
248
248
  layers?: NodeLayer[];
249
- /** Анимации, которые можно положить слоем (для выбора в интерфейсе; могут загружаться при выборе). */
249
+ /** Animations that can be used as a layer (for selection in the UI; may be loaded on selection). */
250
250
  layerPool?: NodeMotion[];
251
- /** Имена из layerPool, подобранные слоем по умолчанию. */
251
+ /** Names from layerPool picked as the default layer. */
252
252
  layerAuto?: string[];
253
253
  /**
254
- * Смешивание анимаций по параметрам (последовательности Source: направление движения): сетка cols×rows анимаций
255
- * (индекс — строка·cols + столбец), x/y — дробное положение; фаза общая — анимации разной длины идут в такт.
254
+ * Parametric animation blending (Source sequences: movement direction): a cols×rows grid of animations
255
+ * (index = row·cols + column), x/y are fractional positions; the phase is shared, so animations of different length stay in step.
256
256
  */
257
257
  blend?: NodeBlend;
258
- /** Как класть эту анимацию слоем (кадр, маска) — задаёт движок. */
258
+ /** How to apply this animation as a layer (frame, mask): set by the engine. */
259
259
  asLayer?: (m: NodeMotion) => Omit<NodeLayer, 'motion'>;
260
260
  }
261
261
  export interface NodeBlend {
@@ -264,14 +264,14 @@ export interface NodeBlend {
264
264
  rows: number;
265
265
  x: number;
266
266
  y: number;
267
- /** Параметры по осям (для подписи ползунка): имя и значения на краях сетки. */
267
+ /** Axis parameters (for the slider label): name and values at the grid edges. */
268
268
  params?: Array<{
269
269
  name: string;
270
270
  start: number;
271
271
  end: number;
272
272
  }>;
273
273
  }
274
- /** Покадровая вершинная анимация (MD2/MD3/MDL). */
274
+ /** Per-frame vertex animation (MD2/MD3/MDL). */
275
275
  export interface VertexMotion extends MotionBase {
276
276
  kind: 'vertex';
277
277
  start: number;
@@ -279,15 +279,15 @@ export interface VertexMotion extends MotionBase {
279
279
  fps: number;
280
280
  }
281
281
  /**
282
- * Виды анимаций по полю kind. Движок добавляет свой вид расширением интерфейса, например
283
- * `declare module '@gmvjs/core/types' { interface MotionKinds { xray: XrayMotion } }` — и сужение по `m.kind` продолжает работать.
282
+ * Animation kinds by the kind field. An engine adds its own kind by extending the interface, for example
283
+ * `declare module '@gmvjs/core/types' { interface MotionKinds { xray: XrayMotion } }`, and narrowing by `m.kind` keeps working.
284
284
  */
285
285
  export interface MotionKinds {
286
286
  node: NodeMotion;
287
287
  vertex: VertexMotion;
288
288
  }
289
289
  export type Motion = MotionKinds[keyof MotionKinds];
290
- /** Скелет, который умеет считать позу. Матрицы — column-major Float32, 16 на кость. */
290
+ /** A skeleton that can compute a pose. Matrices are column-major Float32, 16 per bone. */
291
291
  export interface SkeletonRuntime {
292
292
  readonly n: number;
293
293
  readonly parent: number[];
@@ -296,11 +296,11 @@ export interface SkeletonRuntime {
296
296
  readonly skinV: Float32Array[];
297
297
  pose(motion: Motion | null, time: number, loop: boolean, invert?: boolean): void;
298
298
  }
299
- /** Сцена из повторяющихся моделей (объекты уровня): для экспорта ссылками вместо слитой геометрии. */
299
+ /** Scene of repeated models (level objects): for exporting as references instead of merged geometry. */
300
300
  export interface InstancedScene {
301
- /** Номера мешей модели, собранных из экземпляров (при экспорте заменяются ссылками). */
301
+ /** Numbers of model meshes built from instances (replaced by references on export). */
302
302
  meshes: number[];
303
- /** Прототипы: части с номером меша модели, по которому берутся текстура и материал. */
303
+ /** Prototypes: parts with the model mesh number used for texture and material. */
304
304
  protos: Array<{
305
305
  name: string;
306
306
  parts: Array<{
@@ -311,10 +311,10 @@ export interface InstancedScene {
311
311
  indices: Uint32Array;
312
312
  }>;
313
313
  }>;
314
- /** Поза — матрица 4×4 по строкам (вектор-строка × M, перенос в последней строке), в координатах модели. */
314
+ /** Pose: 4×4 matrix by rows (row vector × M, translation in the last row), in model coordinates. */
315
315
  placements: InstancePlacement[];
316
316
  }
317
- /** Где лежит часть экземпляра в слитом меше: номер меша, вершины и индексы. */
317
+ /** Where an instance part lives in the merged mesh: mesh number, vertices and indices. */
318
318
  export interface PlacementRange {
319
319
  mesh: number;
320
320
  part: number;
@@ -327,14 +327,14 @@ export interface InstancePlacement {
327
327
  proto: number;
328
328
  name: string;
329
329
  pose: number[];
330
- /** Группа в слоях сцены (класс объекта: STATICPROP, O_ENTITY…). */
330
+ /** Group in the scene layers (object class: STATICPROP, O_ENTITY…). */
331
331
  group?: string;
332
- /** Скрыт пользователем: не рисуется и не выгружается. */
332
+ /** Hidden by the user: not drawn and not exported. */
333
333
  hidden?: boolean;
334
- /** Части в слитых мешах (для скрытия, подсветки, выбора и поворота). */
334
+ /** Parts in merged meshes (for hiding, highlighting, picking and rotation). */
335
335
  ranges?: PlacementRange[];
336
336
  }
337
- /** Выбор для пересборки модели: id, подпись, варианты и текущее значение. */
337
+ /** A choice for rebuilding the model: id, label, options and current value. */
338
338
  export interface ModelOption {
339
339
  id: string;
340
340
  label: string;
@@ -346,38 +346,38 @@ export interface ModelOption {
346
346
  }
347
347
  export interface Model {
348
348
  meshes: Mesh[];
349
- /** Экземпляры моделей внутри сцены (Metro level.bin). */
349
+ /** Model instances inside the scene (Metro level.bin). */
350
350
  instances?: InstancedScene;
351
- /** Карта освещения уровня (Metro level.lmap_pc). */
351
+ /** Level lightmap (Metro level.lmap_pc). */
352
352
  lightmap?: Lightmap;
353
- /** Слои поверхности ландшафта (Mesh.terrainIndex). */
353
+ /** Terrain surface layers (Mesh.terrainIndex). */
354
354
  terrainLayers?: TerrainLayers;
355
355
  skeleton: SkeletonRuntime | null;
356
356
  motions: Motion[];
357
357
  bounds: Bounds;
358
- /** Левосторонняя система (X-Ray): отразить Z при показе. */
358
+ /** Left-handed system (X-Ray): mirror Z for display. */
359
359
  flipZ?: boolean;
360
360
  defaultZUp?: boolean;
361
361
  cameraYaw?: number;
362
- /** Строки для панели «Сведения». */
362
+ /** Lines for the "Details" panel. */
363
363
  info: string[];
364
- /** Вершинная анимация: обновить pos/nor мешей на время t. */
364
+ /** Vertex animation: update mesh pos/nor for time t. */
365
365
  animate?(motion: Motion | null, t: number, loop: boolean): void;
366
- /** Подбор знака кватернионов для анимации. */
366
+ /** Quaternion sign fitting for the animation. */
367
367
  detectInvert?(motion: Motion): boolean;
368
- /** Загрузка внешних анимаций (.omf/.psa/.md5anim/.cast). */
368
+ /** Loading external animations (.omf/.psa/.md5anim/.cast). */
369
369
  loadMotions?(file: VFile): Promise<Motion[]>;
370
- /** Произвольные данные для резолверов текстур (имя .mtl и т. п.). */
370
+ /** Arbitrary data for texture resolvers (.mtl name and the like). */
371
371
  meta?: Record<string, unknown>;
372
372
  /**
373
- * Настройки вида, которые формат умеет пересобрать (диски, винил, цвет окраски NFS World): список выборов с текущим
374
- * значением. Смена значения — повторная загрузка с LoadContext.options (камера сохраняется).
373
+ * View settings the format can rebuild (wheels, vinyl, NFS World paint color): a list of choices with their current
374
+ * value. Changing a value reloads with LoadContext.options (the camera is kept).
375
375
  */
376
376
  options?: ModelOption[];
377
- /** Направление зелёного канала карт нормалей игры: 'dx' (Y вниз, Direct3D — по умолчанию) или 'gl' (Y вверх). */
377
+ /** Green channel direction of the game's normal maps: 'dx' (Y down, Direct3D, the default) or 'gl' (Y up). */
378
378
  normalY?: 'gl' | 'dx';
379
379
  }
380
- /** Ошибка «файл содержит только анимацию» — сессия попробует добавить его как анимацию. */
380
+ /** "The file contains only an animation" error: the session will try to add it as an animation. */
381
381
  export declare class MotionOnlyError extends Error {
382
382
  readonly motionOnly = true;
383
383
  }
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../src/types.ts"],"sourcesContent":["// Публичные типы ядра gmv. Ядро не зависит от DOM: работает в странице, в Worker и в Node (тесты).\n\nexport type Vec3 = [number, number, number];\nexport type Quat = [number, number, number, number]; // [w, x, y, z]\n\nexport interface Bounds {\n min: number[];\n max: number[];\n}\n\n/** Декодированная текстура: либо RGBA-пиксели, либо ImageBitmap (PNG/JPG через браузер). */\nexport interface DecodedImage {\n width: number;\n height: number;\n rgba?: Uint8Array;\n bitmap?: ImageBitmap;\n name?: string;\n}\n\n/** Источник байтов, который можно открыть как архив (File в браузере, Blob с именем в Node). */\nexport type BlobFile = Blob & { readonly name: string; readonly webkitRelativePath?: string };\n\n/** «Файл-подобный» объект: обычный File, запись архива или уже декодированная текстура. */\nexport interface VFile {\n readonly name: string;\n /** Путь внутри архива / папки (нижний регистр, «/»). */\n readonly path?: string;\n readonly archive?: Archive;\n readonly webkitRelativePath?: string;\n arrayBuffer(): Promise<ArrayBuffer>;\n /** Если задано — это уже декодированная картинка (например, из TXD). */\n readonly decoded?: DecodedImage;\n}\n\nexport interface ArchiveEntry {\n path: string;\n size?: number;\n}\n\nexport interface Archive {\n readonly name: string;\n /** id плагина архива: 'zip', 'bsa', 'folder'… */\n readonly kind: string;\n readonly version: number;\n readonly entries: ArchiveEntry[];\n /** Полный путь -> запись. */\n readonly index: Map<string, ArchiveEntry>;\n /** Путь без расширения -> запись текстуры. */\n readonly byKey: Map<string, ArchiveEntry>;\n /** Имя файла -> запись (для «Папки игры»). */\n readonly byBase?: Map<string, ArchiveEntry>;\n lookup(path: string): ArchiveEntry | null;\n read(entry: ArchiveEntry): Promise<Uint8Array>;\n fileOf(entry: ArchiveEntry): VFile;\n /** Исходный файл записи (только «Папка игры»): чтобы открыть лежащий в папке архив. */\n blobOf?(entry: ArchiveEntry): BlobFile | null;\n /** Текстура по точному пути из модели, даже если её нет в списке (архивы с именами-хешами: Far Cry 2). */\n findTexture?(path: string): ArchiveEntry | null;\n}\n\n/** Канал карты: путь/имя файла и номер канала (0 R, 1 G, 2 B, 3 A). */\nexport interface ChannelRef { map: string; channel: 0 | 1 | 2 | 3; image?: DecodedImage }\n/**\n * Поверхность для PBR. Схема metal/rough (glTF, BeamNG, SnowRunner) или spec/gloss (Arma _smdi, MudRunner __s_d):\n * gloss = 1 − roughness, specular — сила блика неметалла.\n */\nexport interface SurfaceInfo {\n roughness?: ChannelRef;\n gloss?: ChannelRef;\n metallic?: ChannelRef;\n ao?: ChannelRef;\n specular?: ChannelRef;\n roughnessFactor?: number;\n metallicFactor?: number;\n /** Сила блика без карты (0..1; 0.5 ≈ F0 0.04). */\n specularFactor?: number;\n /** Откуда взято (для подписи): «BeamNG», «Arma _smdi»… */\n source?: string;\n}\n\nexport interface Mesh {\n name: string;\n /** Имя текстуры/материала, как записано в модели. */\n texture: string;\n shader?: string;\n /** Имя материала (assimp/HPL). */\n material?: string;\n vertexCount: number;\n pos: Float32Array;\n nor: Float32Array;\n uv: Float32Array;\n /** 4 индекса костей на вершину (float — для атрибутов WebGL). */\n bone: Float32Array;\n weight: Float32Array;\n indices: Uint16Array | Uint32Array;\n skinned: boolean;\n /** Цвет без текстуры (RenderWare). */\n color?: number[];\n /** Встроенная текстура (Quake MDL, GoldSrc). */\n image?: DecodedImage;\n /** Покадровая вершинная анимация: pos/nor меняются на месте. */\n dynamic?: boolean;\n frames?: { pos: Float32Array[]; nor: Float32Array[] };\n /** Кандидаты путей материалов (Source .vmt). */\n materialPaths?: string[];\n /** Номер подматериала (CryEngine). */\n matId?: number;\n /** Карта нормалей, как она записана в модели или материале (путь/имя); пусто — ищется по имени диффузной. */\n normalMap?: string;\n /** Встроенная карта нормалей. */\n normalImage?: DecodedImage;\n /** PBR-поверхность: шероховатость, металличность, AO, блик (карты и множители). */\n surface?: SurfaceInfo;\n /** Прозрачность по альфе текстуры «вырезанием» (решётки, листва, наклейки) — независимо от общего переключателя. */\n alphaTest?: boolean;\n /** Служебная геометрия (световые плоскости, экраны-рендер-цели): по умолчанию скрыта. */\n defaultHidden?: boolean;\n /** Второй набор UV — координаты в карте освещения (Model.lightmap). */\n uv2?: Float32Array;\n /** Прозрачность по вершинам (0..1): слой ландшафта поверх основы — рисуется после непрозрачных мешей, с наложением. */\n vertexAlpha?: Float32Array;\n /** Запечённое освещение по вершинам: RGB(A) байтами, 128 — без изменений (умножение ×2, как у Black Box). */\n vertexShade?: Uint8Array;\n /**\n * Карта слоёв ландшафта (номер слоя Model.terrainLayers на тексель, по UV меша 0..1): вблизи вместо одной цветовой\n * карты (image) рисуются повторяющиеся текстуры слоёв, подкрашенные ею.\n */\n terrainIndex?: { width: number; height: number; data: Uint8Array };\n /** Отражение и преломление по экрану (вода, преломляющее стекло Source): рисуется после сцены по её снимку. */\n screenFx?: ScreenFx;\n}\n\n/** Параметры экранной поверхности: вода (плоское отражение, преломление, туман по глубине) или стекло (преломление). */\nexport interface ScreenFx {\n kind: 'water' | 'glass';\n /** Плоскость поверхности в координатах модели: n·p = d (для отражения воды). */\n plane?: [number, number, number, number];\n /** Цвет преломления (то, что под водой / за стеклом) и отражения (у стекла — блик окружения $envmaptint), 0..1. */\n refractTint: number[];\n reflectTint?: number[];\n /** Сила искажения по карте нормалей: доля экрана. */\n refract: number;\n reflect?: number;\n /** Туман под водой: цвет 0..1, начало и конец в единицах модели. */\n fog?: { color: number[]; start: number; end: number };\n /** Сдвиг карты нормалей во времени (UV/сек). */\n scroll?: [number, number];\n /** Без поддержки: цвет и непрозрачность полупрозрачного слоя. */\n fallback: { color: number[]; opacity: number };\n}\n\n/** Слои поверхности ландшафта: текстуры одного размера и параметры показа. */\nexport interface TerrainLayers {\n /** Текстуры слоёв (одного размера, степень двойки). */\n images: DecodedImage[];\n /** Период повтора текстуры в единицах модели (по X и Z координат меша). */\n period: number[];\n /** 0..1: насколько слой подкрашивается цветовой картой меша. */\n tint: number[];\n /** Средний цвет текстуры слоя (0..1) — для подкраски. */\n mean: Array<[number, number, number]>;\n /** Дальность, на которой слои переходят в цветовую карту. */\n fade: number;\n /** Нормали слоёв (тот же размер, что images): RG — XY касательной нормали (DirectX), A — AO. */\n normals?: DecodedImage[];\n /** Средний масштаб: текстуры изменчивости (свой размер), период, сила, средний цвет. */\n middle?: DecodedImage[];\n midPeriod?: number[];\n midBlend?: number[];\n midMean?: Array<[number, number, number]>;\n /** Имена (для сведений). */\n names?: string[];\n}\n\n/** Запечённое освещение уровня (одна карта на модель): яркость в R, UV — Mesh.uv2. */\nexport interface Lightmap {\n width: number;\n height: number;\n /** Яркость, 1 байт на пиксель (или RGB, 3 байта, при rgb). */\n data: Uint8Array;\n /** Цветная карта (Source): RGB8. */\n rgb?: boolean;\n /** Множитель яркости при показе. */\n scale?: number;\n /**\n * Карты освещения по трём направлениям базиса рельефа Source (bump lightmaps), тот же формат и раскладка, что data:\n * свет смешивается по нормали из карты нормалей. У граней без рельефа — копия data.\n */\n bump?: [Uint8Array, Uint8Array, Uint8Array];\n}\n\n/** Ключи анимации assimp-стиля: [время, значение]. */\nexport type Keys<T> = Array<[number, T]>;\nexport interface NodeChannel {\n pos: Keys<number[]> | null;\n rot: Keys<number[]> | null;\n scl: Keys<number[]> | null;\n /** Аддитивная дорожка как смещение (перенос + поворот [w,x,y,z]) — применяется так, когда анимация — слой поверх другой. */\n add?: { pos: Keys<number[]> | null; rot: Keys<number[]> | null };\n}\n\n/** Слой поверх основной анимации узлов: аддитивные каналы складываются, остальные заменяют позу (в пределах маски). */\nexport interface NodeLayer {\n motion: NodeMotion;\n /** Постоянный кадр слоя (поза прицеливания — средний кадр сетки направлений); нет — играет по времени. */\n frame?: number;\n /** Номера узлов, которые слой может менять; нет — все. */\n mask?: ReadonlySet<number>;\n /** Только дополнять: узлы, у которых есть канал в основной анимации, слой не трогает (хват оружия у NPC — свой). */\n fill?: boolean;\n /**\n * Слой — сетка поз (кадр = строка·cols + столбец), например направления прицела: x — столбец, y — строка\n * (дробные: смешиваются 4 соседних кадра). Перекрывает frame.\n */\n grid?: { cols: number; rows: number; x: number; y: number };\n}\n\nexport interface MotionBase {\n name: string;\n /** Длительность, сек. */\n duration: number;\n /** Число кадров (для подписи в списке). */\n frames?: number;\n /** Откуда анимация: 'model' или имя файла. */\n source?: string;\n /** Анимация из длинного списка (Metro: тысячи файлов): данные читаются при выборе. */\n load?(): Promise<Motion>;\n}\n/** Анимация узлов (assimp, NIF, MD5, PSA, Cast, GoldSrc). */\nexport interface NodeMotion extends MotionBase {\n kind: 'node';\n tps: number;\n chan: Map<number, NodeChannel>;\n /** Слои поверх (прицеливание, дыхание). */\n layers?: NodeLayer[];\n /** Анимации, которые можно положить слоем (для выбора в интерфейсе; могут загружаться при выборе). */\n layerPool?: NodeMotion[];\n /** Имена из layerPool, подобранные слоем по умолчанию. */\n layerAuto?: string[];\n /**\n * Смешивание анимаций по параметрам (последовательности Source: направление движения): сетка cols×rows анимаций\n * (индекс — строка·cols + столбец), x/y — дробное положение; фаза общая — анимации разной длины идут в такт.\n */\n blend?: NodeBlend;\n /** Как класть эту анимацию слоем (кадр, маска) — задаёт движок. */\n asLayer?: (m: NodeMotion) => Omit<NodeLayer, 'motion'>;\n}\nexport interface NodeBlend {\n motions: NodeMotion[];\n cols: number;\n rows: number;\n x: number;\n y: number;\n /** Параметры по осям (для подписи ползунка): имя и значения на краях сетки. */\n params?: Array<{ name: string; start: number; end: number }>;\n}\n/** Покадровая вершинная анимация (MD2/MD3/MDL). */\nexport interface VertexMotion extends MotionBase {\n kind: 'vertex';\n start: number;\n count: number;\n fps: number;\n}\n/**\n * Виды анимаций по полю kind. Движок добавляет свой вид расширением интерфейса, например\n * `declare module '@gmvjs/core/types' { interface MotionKinds { xray: XrayMotion } }` — и сужение по `m.kind` продолжает работать.\n */\nexport interface MotionKinds { node: NodeMotion; vertex: VertexMotion }\nexport type Motion = MotionKinds[keyof MotionKinds];\n\n/** Скелет, который умеет считать позу. Матрицы — column-major Float32, 16 на кость. */\nexport interface SkeletonRuntime {\n readonly n: number;\n readonly parent: number[];\n readonly skin: Float32Array;\n readonly worldV: Float32Array[];\n readonly skinV: Float32Array[];\n pose(motion: Motion | null, time: number, loop: boolean, invert?: boolean): void;\n}\n\n/** Сцена из повторяющихся моделей (объекты уровня): для экспорта ссылками вместо слитой геометрии. */\nexport interface InstancedScene {\n /** Номера мешей модели, собранных из экземпляров (при экспорте заменяются ссылками). */\n meshes: number[];\n /** Прототипы: части с номером меша модели, по которому берутся текстура и материал. */\n protos: Array<{ name: string; parts: Array<{ mesh: number; pos: Float32Array; nor: Float32Array; uv: Float32Array; indices: Uint32Array }> }>;\n /** Поза — матрица 4×4 по строкам (вектор-строка × M, перенос в последней строке), в координатах модели. */\n placements: InstancePlacement[];\n}\n\n/** Где лежит часть экземпляра в слитом меше: номер меша, вершины и индексы. */\nexport interface PlacementRange { mesh: number; part: number; vStart: number; vCount: number; iStart: number; iCount: number }\n\nexport interface InstancePlacement {\n proto: number;\n name: string;\n pose: number[];\n /** Группа в слоях сцены (класс объекта: STATICPROP, O_ENTITY…). */\n group?: string;\n /** Скрыт пользователем: не рисуется и не выгружается. */\n hidden?: boolean;\n /** Части в слитых мешах (для скрытия, подсветки, выбора и поворота). */\n ranges?: PlacementRange[];\n}\n\n/** Выбор для пересборки модели: id, подпись, варианты и текущее значение. */\nexport interface ModelOption { id: string; label: string; values: Array<{ id: string; label: string }>; value: string }\n\nexport interface Model {\n meshes: Mesh[];\n /** Экземпляры моделей внутри сцены (Metro level.bin). */\n instances?: InstancedScene;\n /** Карта освещения уровня (Metro level.lmap_pc). */\n lightmap?: Lightmap;\n /** Слои поверхности ландшафта (Mesh.terrainIndex). */\n terrainLayers?: TerrainLayers;\n skeleton: SkeletonRuntime | null;\n motions: Motion[];\n bounds: Bounds;\n /** Левосторонняя система (X-Ray): отразить Z при показе. */\n flipZ?: boolean;\n defaultZUp?: boolean;\n cameraYaw?: number;\n /** Строки для панели «Сведения». */\n info: string[];\n /** Вершинная анимация: обновить pos/nor мешей на время t. */\n animate?(motion: Motion | null, t: number, loop: boolean): void;\n /** Подбор знака кватернионов для анимации. */\n detectInvert?(motion: Motion): boolean;\n /** Загрузка внешних анимаций (.omf/.psa/.md5anim/.cast). */\n loadMotions?(file: VFile): Promise<Motion[]>;\n /** Произвольные данные для резолверов текстур (имя .mtl и т. п.). */\n meta?: Record<string, unknown>;\n /**\n * Настройки вида, которые формат умеет пересобрать (диски, винил, цвет окраски NFS World): список выборов с текущим\n * значением. Смена значения — повторная загрузка с LoadContext.options (камера сохраняется).\n */\n options?: ModelOption[];\n /** Направление зелёного канала карт нормалей игры: 'dx' (Y вниз, Direct3D — по умолчанию) или 'gl' (Y вверх). */\n normalY?: 'gl' | 'dx';\n}\n\n/** Ошибка «файл содержит только анимацию» — сессия попробует добавить его как анимацию. */\nexport class MotionOnlyError extends Error {\n readonly motionOnly = true;\n}\n"],"names":[],"mappings":"AAuVO,MAAM,wBAAwB,MAAM;AAAA,EAChC,aAAa;AACxB;"}
1
+ {"version":3,"file":"types.js","sources":["../src/types.ts"],"sourcesContent":["// Public types of the gmv core. The core does not depend on the DOM: it runs in a page, in a Worker and in Node (tests).\n\nexport type Vec3 = [number, number, number];\nexport type Quat = [number, number, number, number]; // [w, x, y, z]\n\nexport interface Bounds {\n min: number[];\n max: number[];\n}\n\n/** Decoded texture: either RGBA pixels or an ImageBitmap (PNG/JPG via the browser). */\nexport interface DecodedImage {\n width: number;\n height: number;\n rgba?: Uint8Array;\n bitmap?: ImageBitmap;\n name?: string;\n}\n\n/** A byte source that can be opened as an archive (File in the browser, a named Blob in Node). */\nexport type BlobFile = Blob & { readonly name: string; readonly webkitRelativePath?: string };\n\n/** File-like object: a regular File, an archive entry or an already decoded texture. */\nexport interface VFile {\n readonly name: string;\n /** Path inside an archive / folder (lower case, \"/\"). */\n readonly path?: string;\n readonly archive?: Archive;\n readonly webkitRelativePath?: string;\n arrayBuffer(): Promise<ArrayBuffer>;\n /** If set, this is an already decoded image (for example from a TXD). */\n readonly decoded?: DecodedImage;\n}\n\nexport interface ArchiveEntry {\n path: string;\n size?: number;\n}\n\nexport interface Archive {\n readonly name: string;\n /** Archive plugin id: 'zip', 'bsa', 'folder'… */\n readonly kind: string;\n readonly version: number;\n readonly entries: ArchiveEntry[];\n /** Full path -> entry. */\n readonly index: Map<string, ArchiveEntry>;\n /** Path without extension -> texture entry. */\n readonly byKey: Map<string, ArchiveEntry>;\n /** File name -> entry (for the \"Game folder\"). */\n readonly byBase?: Map<string, ArchiveEntry>;\n lookup(path: string): ArchiveEntry | null;\n read(entry: ArchiveEntry): Promise<Uint8Array>;\n fileOf(entry: ArchiveEntry): VFile;\n /** Source file of the entry (\"Game folder\" only): to open an archive lying in the folder. */\n blobOf?(entry: ArchiveEntry): BlobFile | null;\n /** Texture at the exact path from the model, even if it is not in the list (archives with hashed names: Far Cry 2). */\n findTexture?(path: string): ArchiveEntry | null;\n}\n\n/** Map channel: path/file name and channel number (0 R, 1 G, 2 B, 3 A). */\nexport interface ChannelRef { map: string; channel: 0 | 1 | 2 | 3; image?: DecodedImage }\n/**\n * PBR surface. The metal/rough scheme (glTF, BeamNG, SnowRunner) or spec/gloss (Arma _smdi, MudRunner __s_d):\n * gloss = 1 − roughness, specular is the non-metal specular strength.\n */\nexport interface SurfaceInfo {\n roughness?: ChannelRef;\n gloss?: ChannelRef;\n metallic?: ChannelRef;\n ao?: ChannelRef;\n specular?: ChannelRef;\n roughnessFactor?: number;\n metallicFactor?: number;\n /** Specular strength without a map (0..1; 0.5 ≈ F0 0.04). */\n specularFactor?: number;\n /** Where it came from (for the label): \"BeamNG\", \"Arma _smdi\"… */\n source?: string;\n}\n\nexport interface Mesh {\n name: string;\n /** Texture/material name as written in the model. */\n texture: string;\n shader?: string;\n /** Material name (assimp/HPL). */\n material?: string;\n vertexCount: number;\n pos: Float32Array;\n nor: Float32Array;\n uv: Float32Array;\n /** 4 bone indices per vertex (float, for WebGL attributes). */\n bone: Float32Array;\n weight: Float32Array;\n indices: Uint16Array | Uint32Array;\n skinned: boolean;\n /** Color without a texture (RenderWare). */\n color?: number[];\n /** Embedded texture (Quake MDL, GoldSrc). */\n image?: DecodedImage;\n /** Per-frame vertex animation: pos/nor change in place. */\n dynamic?: boolean;\n frames?: { pos: Float32Array[]; nor: Float32Array[] };\n /** Candidate material paths (Source .vmt). */\n materialPaths?: string[];\n /** Submaterial number (CryEngine). */\n matId?: number;\n /** Normal map as written in the model or material (path/name); empty means it is looked up from the diffuse name. */\n normalMap?: string;\n /** Embedded normal map. */\n normalImage?: DecodedImage;\n /** PBR surface: roughness, metalness, AO, specular (maps and multipliers). */\n surface?: SurfaceInfo;\n /** Alpha-tested transparency from the texture alpha (grates, foliage, decals), independent of the global toggle. */\n alphaTest?: boolean;\n /** Helper geometry (light planes, render-target screens): hidden by default. */\n defaultHidden?: boolean;\n /** Second UV set: coordinates in the lightmap (Model.lightmap). */\n uv2?: Float32Array;\n /** Per-vertex opacity (0..1): a terrain layer over the base, drawn after opaque meshes with blending. */\n vertexAlpha?: Float32Array;\n /** Baked per-vertex lighting: RGB(A) bytes, 128 = unchanged (×2 multiplication, as in Black Box). */\n vertexShade?: Uint8Array;\n /**\n * Terrain layer map (a Model.terrainLayers layer number per texel, over the mesh UV 0..1): up close, instead of a single color\n * map (image), repeating layer textures tinted by it are drawn.\n */\n terrainIndex?: { width: number; height: number; data: Uint8Array };\n /** Screen-space reflection and refraction (water, Source refracting glass): drawn after the scene from its snapshot. */\n screenFx?: ScreenFx;\n}\n\n/** Screen surface parameters: water (planar reflection, refraction, depth fog) or glass (refraction). */\nexport interface ScreenFx {\n kind: 'water' | 'glass';\n /** Surface plane in model coordinates: n·p = d (for water reflection). */\n plane?: [number, number, number, number];\n /** Refraction color (what is under water / behind glass) and reflection color (for glass, the $envmaptint environment highlight), 0..1. */\n refractTint: number[];\n reflectTint?: number[];\n /** Distortion strength from the normal map: a fraction of the screen. */\n refract: number;\n reflect?: number;\n /** Underwater fog: color 0..1, start and end in model units. */\n fog?: { color: number[]; start: number; end: number };\n /** Normal map scrolling over time (UV/sec). */\n scroll?: [number, number];\n /** Without support: color and opacity of a translucent layer. */\n fallback: { color: number[]; opacity: number };\n}\n\n/** Terrain surface layers: textures of one size and display parameters. */\nexport interface TerrainLayers {\n /** Layer textures (one size, power of two). */\n images: DecodedImage[];\n /** Texture repeat period in model units (along the mesh X and Z coordinates). */\n period: number[];\n /** 0..1: how much the layer is tinted by the mesh color map. */\n tint: number[];\n /** Average color of the layer texture (0..1), for tinting. */\n mean: Array<[number, number, number]>;\n /** Distance at which layers fade into the color map. */\n fade: number;\n /** Layer normals (same size as images): RG = XY of the tangent normal (DirectX), A = AO. */\n normals?: DecodedImage[];\n /** Medium scale: variation textures (own size), period, strength, average color. */\n middle?: DecodedImage[];\n midPeriod?: number[];\n midBlend?: number[];\n midMean?: Array<[number, number, number]>;\n /** Names (for details). */\n names?: string[];\n}\n\n/** Baked level lighting (one map per model): brightness in R, UV is Mesh.uv2. */\nexport interface Lightmap {\n width: number;\n height: number;\n /** Brightness, 1 byte per pixel (or RGB, 3 bytes, with rgb). */\n data: Uint8Array;\n /** Color map (Source): RGB8. */\n rgb?: boolean;\n /** Brightness multiplier for display. */\n scale?: number;\n /**\n * Lightmaps for the three directions of the Source bump basis (bump lightmaps), same format and layout as data:\n * light is blended by the normal from the normal map. Faces without bump get a copy of data.\n */\n bump?: [Uint8Array, Uint8Array, Uint8Array];\n}\n\n/** assimp-style animation keys: [time, value]. */\nexport type Keys<T> = Array<[number, T]>;\nexport interface NodeChannel {\n pos: Keys<number[]> | null;\n rot: Keys<number[]> | null;\n scl: Keys<number[]> | null;\n /** Additive track as an offset (translation + rotation [w,x,y,z]): applied this way when the animation is a layer over another. */\n add?: { pos: Keys<number[]> | null; rot: Keys<number[]> | null };\n}\n\n/** Layer over the main node animation: additive channels are added, others replace the pose (within the mask). */\nexport interface NodeLayer {\n motion: NodeMotion;\n /** Fixed frame of the layer (aim pose: the middle frame of the direction grid); none means it plays over time. */\n frame?: number;\n /** Node numbers the layer may change; none means all. */\n mask?: ReadonlySet<number>;\n /** Only complement: the layer does not touch nodes that have a channel in the main animation (NPC weapon grip is its own). */\n fill?: boolean;\n /**\n * The layer is a pose grid (frame = row·cols + column), for example aim directions: x is the column, y the row\n * (fractional: 4 neighbouring frames are blended). Overrides frame.\n */\n grid?: { cols: number; rows: number; x: number; y: number };\n}\n\nexport interface MotionBase {\n name: string;\n /** Duration, seconds. */\n duration: number;\n /** Frame count (for the list label). */\n frames?: number;\n /** Where the animation comes from: 'model' or a file name. */\n source?: string;\n /** Animation from a long list (Metro: thousands of files): data is read on selection. */\n load?(): Promise<Motion>;\n}\n/** Node animation (assimp, NIF, MD5, PSA, Cast, GoldSrc). */\nexport interface NodeMotion extends MotionBase {\n kind: 'node';\n tps: number;\n chan: Map<number, NodeChannel>;\n /** Layers on top (aiming, breathing). */\n layers?: NodeLayer[];\n /** Animations that can be used as a layer (for selection in the UI; may be loaded on selection). */\n layerPool?: NodeMotion[];\n /** Names from layerPool picked as the default layer. */\n layerAuto?: string[];\n /**\n * Parametric animation blending (Source sequences: movement direction): a cols×rows grid of animations\n * (index = row·cols + column), x/y are fractional positions; the phase is shared, so animations of different length stay in step.\n */\n blend?: NodeBlend;\n /** How to apply this animation as a layer (frame, mask): set by the engine. */\n asLayer?: (m: NodeMotion) => Omit<NodeLayer, 'motion'>;\n}\nexport interface NodeBlend {\n motions: NodeMotion[];\n cols: number;\n rows: number;\n x: number;\n y: number;\n /** Axis parameters (for the slider label): name and values at the grid edges. */\n params?: Array<{ name: string; start: number; end: number }>;\n}\n/** Per-frame vertex animation (MD2/MD3/MDL). */\nexport interface VertexMotion extends MotionBase {\n kind: 'vertex';\n start: number;\n count: number;\n fps: number;\n}\n/**\n * Animation kinds by the kind field. An engine adds its own kind by extending the interface, for example\n * `declare module '@gmvjs/core/types' { interface MotionKinds { xray: XrayMotion } }`, and narrowing by `m.kind` keeps working.\n */\nexport interface MotionKinds { node: NodeMotion; vertex: VertexMotion }\nexport type Motion = MotionKinds[keyof MotionKinds];\n\n/** A skeleton that can compute a pose. Matrices are column-major Float32, 16 per bone. */\nexport interface SkeletonRuntime {\n readonly n: number;\n readonly parent: number[];\n readonly skin: Float32Array;\n readonly worldV: Float32Array[];\n readonly skinV: Float32Array[];\n pose(motion: Motion | null, time: number, loop: boolean, invert?: boolean): void;\n}\n\n/** Scene of repeated models (level objects): for exporting as references instead of merged geometry. */\nexport interface InstancedScene {\n /** Numbers of model meshes built from instances (replaced by references on export). */\n meshes: number[];\n /** Prototypes: parts with the model mesh number used for texture and material. */\n protos: Array<{ name: string; parts: Array<{ mesh: number; pos: Float32Array; nor: Float32Array; uv: Float32Array; indices: Uint32Array }> }>;\n /** Pose: 4×4 matrix by rows (row vector × M, translation in the last row), in model coordinates. */\n placements: InstancePlacement[];\n}\n\n/** Where an instance part lives in the merged mesh: mesh number, vertices and indices. */\nexport interface PlacementRange { mesh: number; part: number; vStart: number; vCount: number; iStart: number; iCount: number }\n\nexport interface InstancePlacement {\n proto: number;\n name: string;\n pose: number[];\n /** Group in the scene layers (object class: STATICPROP, O_ENTITY…). */\n group?: string;\n /** Hidden by the user: not drawn and not exported. */\n hidden?: boolean;\n /** Parts in merged meshes (for hiding, highlighting, picking and rotation). */\n ranges?: PlacementRange[];\n}\n\n/** A choice for rebuilding the model: id, label, options and current value. */\nexport interface ModelOption { id: string; label: string; values: Array<{ id: string; label: string }>; value: string }\n\nexport interface Model {\n meshes: Mesh[];\n /** Model instances inside the scene (Metro level.bin). */\n instances?: InstancedScene;\n /** Level lightmap (Metro level.lmap_pc). */\n lightmap?: Lightmap;\n /** Terrain surface layers (Mesh.terrainIndex). */\n terrainLayers?: TerrainLayers;\n skeleton: SkeletonRuntime | null;\n motions: Motion[];\n bounds: Bounds;\n /** Left-handed system (X-Ray): mirror Z for display. */\n flipZ?: boolean;\n defaultZUp?: boolean;\n cameraYaw?: number;\n /** Lines for the \"Details\" panel. */\n info: string[];\n /** Vertex animation: update mesh pos/nor for time t. */\n animate?(motion: Motion | null, t: number, loop: boolean): void;\n /** Quaternion sign fitting for the animation. */\n detectInvert?(motion: Motion): boolean;\n /** Loading external animations (.omf/.psa/.md5anim/.cast). */\n loadMotions?(file: VFile): Promise<Motion[]>;\n /** Arbitrary data for texture resolvers (.mtl name and the like). */\n meta?: Record<string, unknown>;\n /**\n * View settings the format can rebuild (wheels, vinyl, NFS World paint color): a list of choices with their current\n * value. Changing a value reloads with LoadContext.options (the camera is kept).\n */\n options?: ModelOption[];\n /** Green channel direction of the game's normal maps: 'dx' (Y down, Direct3D, the default) or 'gl' (Y up). */\n normalY?: 'gl' | 'dx';\n}\n\n/** \"The file contains only an animation\" error: the session will try to add it as an animation. */\nexport class MotionOnlyError extends Error {\n readonly motionOnly = true;\n}\n"],"names":[],"mappings":"AAuVO,MAAM,wBAAwB,MAAM;AAAA,EAChC,aAAa;AACxB;"}
@@ -4,5 +4,5 @@ export declare function encodeDdsRgba(img: {
4
4
  height: number;
5
5
  rgba: Uint8Array;
6
6
  }, mips?: boolean): Uint8Array;
7
- /** Есть ли в картинке полупрозрачные пиксели. */
7
+ /** Whether the image has semi-transparent pixels. */
8
8
  export declare function hasAlpha(img: DecodedImage): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"dds.js","sources":["../../src/writers/dds.ts"],"sourcesContent":["// DDS без сжатия (A8R8G8B8) с цепочкой mip-уровней (усреднение 2×2). Читается DirectX, движками и редакторами.\nimport type { DecodedImage } from '../types';\n\nexport function encodeDdsRgba(img: { width: number; height: number; rgba: Uint8Array }, mips = true): Uint8Array {\n const levels: Array<{ w: number; h: number; px: Uint8Array }> = [{ w: img.width, h: img.height, px: img.rgba }];\n while (mips) {\n const { w, h, px } = levels[levels.length - 1]!;\n if (w === 1 && h === 1) break;\n const nw = Math.max(1, w >> 1), nh = Math.max(1, h >> 1), out = new Uint8Array(nw * nh * 4);\n for (let y = 0; y < nh; y++) for (let x = 0; x < nw; x++) for (let c = 0; c < 4; c++) {\n let s = 0, n = 0;\n for (const [dx, dy] of [[0, 0], [1, 0], [0, 1], [1, 1]]) { const sx = Math.min(w - 1, x * 2 + dx!), sy = Math.min(h - 1, y * 2 + dy!); s += px[(sy * w + sx) * 4 + c]!; n++; }\n out[(y * nw + x) * 4 + c] = Math.round(s / n);\n }\n levels.push({ w: nw, h: nh, px: out });\n }\n const size = levels.reduce((a, l) => a + l.w * l.h * 4, 0), u = new Uint8Array(128 + size), dv = new DataView(u.buffer);\n dv.setUint32(0, 0x20534444, true); dv.setUint32(4, 124, true);\n dv.setUint32(8, 0x1 | 0x2 | 0x4 | 0x1000 | 0x8 | (levels.length > 1 ? 0x20000 : 0), true); // CAPS|HEIGHT|WIDTH|PIXELFORMAT|PITCH|MIPMAPCOUNT\n dv.setUint32(12, img.height, true); dv.setUint32(16, img.width, true); dv.setUint32(20, img.width * 4, true);\n dv.setUint32(28, levels.length, true);\n dv.setUint32(76, 32, true); dv.setUint32(80, 0x41, true); dv.setUint32(88, 32, true); // RGB | ALPHAPIXELS, 32 бита\n dv.setUint32(92, 0x00ff0000, true); dv.setUint32(96, 0x0000ff00, true); dv.setUint32(100, 0x000000ff, true); dv.setUint32(104, 0xff000000, true);\n dv.setUint32(108, 0x1000 | (levels.length > 1 ? 0x400008 : 0), true); // TEXTURE | MIPMAP | COMPLEX\n let p = 128;\n for (const l of levels) for (let i = 0; i < l.w * l.h; i++) { u[p++] = l.px[i * 4 + 2]!; u[p++] = l.px[i * 4 + 1]!; u[p++] = l.px[i * 4]!; u[p++] = l.px[i * 4 + 3]!; }\n return u;\n}\n\n/** Есть ли в картинке полупрозрачные пиксели. */\nexport function hasAlpha(img: DecodedImage): boolean {\n const d = img.rgba;\n if (!d) return false;\n for (let i = 3; i < d.length; i += 16) if (d[i]! < 250) return true;\n return false;\n}\n"],"names":[],"mappings":"AAGO,SAAS,cAAc,KAA0D,OAAO,MAAkB;AAC/G,QAAM,SAA0D,CAAC,EAAE,GAAG,IAAI,OAAO,GAAG,IAAI,QAAQ,IAAI,IAAI,KAAA,CAAM;AAC9G,SAAO,MAAM;AACX,UAAM,EAAE,GAAG,GAAG,GAAA,IAAO,OAAO,OAAO,SAAS,CAAC;AAC7C,QAAI,MAAM,KAAK,MAAM,EAAG;AACxB,UAAM,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC,GAAG,MAAM,IAAI,WAAW,KAAK,KAAK,CAAC;AAC1F,aAAS,IAAI,GAAG,IAAI,IAAI,cAAc,IAAI,GAAG,IAAI,IAAI,IAAK,UAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACpF,UAAI,IAAI,GAAG,IAAI;AACf,iBAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG;AAAE,cAAM,KAAK,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,EAAG,GAAG,KAAK,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,EAAG;AAAG,aAAK,IAAI,KAAK,IAAI,MAAM,IAAI,CAAC;AAAI;AAAA,MAAK;AAC7K,WAAK,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC;AAAA,IAC9C;AACA,WAAO,KAAK,EAAE,GAAG,IAAI,GAAG,IAAI,IAAI,KAAK;AAAA,EACvC;AACA,QAAM,OAAO,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,WAAW,MAAM,IAAI,GAAG,KAAK,IAAI,SAAS,EAAE,MAAM;AACtH,KAAG,UAAU,GAAG,WAAY,IAAI;AAAG,KAAG,UAAU,GAAG,KAAK,IAAI;AAC5D,KAAG,UAAU,GAAG,IAAM,IAAM,IAAM,OAAS,KAAO,OAAO,SAAS,IAAI,SAAU,IAAI,IAAI;AACxF,KAAG,UAAU,IAAI,IAAI,QAAQ,IAAI;AAAG,KAAG,UAAU,IAAI,IAAI,OAAO,IAAI;AAAG,KAAG,UAAU,IAAI,IAAI,QAAQ,GAAG,IAAI;AAC3G,KAAG,UAAU,IAAI,OAAO,QAAQ,IAAI;AACpC,KAAG,UAAU,IAAI,IAAI,IAAI;AAAG,KAAG,UAAU,IAAI,IAAM,IAAI;AAAG,KAAG,UAAU,IAAI,IAAI,IAAI;AACnF,KAAG,UAAU,IAAI,UAAY,IAAI;AAAG,KAAG,UAAU,IAAI,OAAY,IAAI;AAAG,KAAG,UAAU,KAAK,KAAY,IAAI;AAAG,KAAG,UAAU,KAAK,YAAY,IAAI;AAC/I,KAAG,UAAU,KAAK,QAAU,OAAO,SAAS,IAAI,UAAW,IAAI,IAAI;AACnE,MAAI,IAAI;AACR,aAAW,KAAK,OAAQ,UAAS,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK;AAAE,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC;AAAI,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC;AAAI,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;AAAI,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC;AAAA,EAAI;AACtK,SAAO;AACT;AAGO,SAAS,SAAS,KAA4B;AACnD,QAAM,IAAI,IAAI;AACd,MAAI,CAAC,EAAG,QAAO;AACf,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,GAAI,KAAI,EAAE,CAAC,IAAK,IAAK,QAAO;AAC/D,SAAO;AACT;"}
1
+ {"version":3,"file":"dds.js","sources":["../../src/writers/dds.ts"],"sourcesContent":["// Uncompressed DDS (A8R8G8B8) with a mip chain (2×2 averaging). Readable by DirectX, engines and editors.\nimport type { DecodedImage } from '../types';\n\nexport function encodeDdsRgba(img: { width: number; height: number; rgba: Uint8Array }, mips = true): Uint8Array {\n const levels: Array<{ w: number; h: number; px: Uint8Array }> = [{ w: img.width, h: img.height, px: img.rgba }];\n while (mips) {\n const { w, h, px } = levels[levels.length - 1]!;\n if (w === 1 && h === 1) break;\n const nw = Math.max(1, w >> 1), nh = Math.max(1, h >> 1), out = new Uint8Array(nw * nh * 4);\n for (let y = 0; y < nh; y++) for (let x = 0; x < nw; x++) for (let c = 0; c < 4; c++) {\n let s = 0, n = 0;\n for (const [dx, dy] of [[0, 0], [1, 0], [0, 1], [1, 1]]) { const sx = Math.min(w - 1, x * 2 + dx!), sy = Math.min(h - 1, y * 2 + dy!); s += px[(sy * w + sx) * 4 + c]!; n++; }\n out[(y * nw + x) * 4 + c] = Math.round(s / n);\n }\n levels.push({ w: nw, h: nh, px: out });\n }\n const size = levels.reduce((a, l) => a + l.w * l.h * 4, 0), u = new Uint8Array(128 + size), dv = new DataView(u.buffer);\n dv.setUint32(0, 0x20534444, true); dv.setUint32(4, 124, true);\n dv.setUint32(8, 0x1 | 0x2 | 0x4 | 0x1000 | 0x8 | (levels.length > 1 ? 0x20000 : 0), true); // CAPS|HEIGHT|WIDTH|PIXELFORMAT|PITCH|MIPMAPCOUNT\n dv.setUint32(12, img.height, true); dv.setUint32(16, img.width, true); dv.setUint32(20, img.width * 4, true);\n dv.setUint32(28, levels.length, true);\n dv.setUint32(76, 32, true); dv.setUint32(80, 0x41, true); dv.setUint32(88, 32, true); // RGB | ALPHAPIXELS, 32 bits\n dv.setUint32(92, 0x00ff0000, true); dv.setUint32(96, 0x0000ff00, true); dv.setUint32(100, 0x000000ff, true); dv.setUint32(104, 0xff000000, true);\n dv.setUint32(108, 0x1000 | (levels.length > 1 ? 0x400008 : 0), true); // TEXTURE | MIPMAP | COMPLEX\n let p = 128;\n for (const l of levels) for (let i = 0; i < l.w * l.h; i++) { u[p++] = l.px[i * 4 + 2]!; u[p++] = l.px[i * 4 + 1]!; u[p++] = l.px[i * 4]!; u[p++] = l.px[i * 4 + 3]!; }\n return u;\n}\n\n/** Whether the image has semi-transparent pixels. */\nexport function hasAlpha(img: DecodedImage): boolean {\n const d = img.rgba;\n if (!d) return false;\n for (let i = 3; i < d.length; i += 16) if (d[i]! < 250) return true;\n return false;\n}\n"],"names":[],"mappings":"AAGO,SAAS,cAAc,KAA0D,OAAO,MAAkB;AAC/G,QAAM,SAA0D,CAAC,EAAE,GAAG,IAAI,OAAO,GAAG,IAAI,QAAQ,IAAI,IAAI,KAAA,CAAM;AAC9G,SAAO,MAAM;AACX,UAAM,EAAE,GAAG,GAAG,GAAA,IAAO,OAAO,OAAO,SAAS,CAAC;AAC7C,QAAI,MAAM,KAAK,MAAM,EAAG;AACxB,UAAM,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC,GAAG,MAAM,IAAI,WAAW,KAAK,KAAK,CAAC;AAC1F,aAAS,IAAI,GAAG,IAAI,IAAI,cAAc,IAAI,GAAG,IAAI,IAAI,IAAK,UAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACpF,UAAI,IAAI,GAAG,IAAI;AACf,iBAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG;AAAE,cAAM,KAAK,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,EAAG,GAAG,KAAK,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,EAAG;AAAG,aAAK,IAAI,KAAK,IAAI,MAAM,IAAI,CAAC;AAAI;AAAA,MAAK;AAC7K,WAAK,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC;AAAA,IAC9C;AACA,WAAO,KAAK,EAAE,GAAG,IAAI,GAAG,IAAI,IAAI,KAAK;AAAA,EACvC;AACA,QAAM,OAAO,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,WAAW,MAAM,IAAI,GAAG,KAAK,IAAI,SAAS,EAAE,MAAM;AACtH,KAAG,UAAU,GAAG,WAAY,IAAI;AAAG,KAAG,UAAU,GAAG,KAAK,IAAI;AAC5D,KAAG,UAAU,GAAG,IAAM,IAAM,IAAM,OAAS,KAAO,OAAO,SAAS,IAAI,SAAU,IAAI,IAAI;AACxF,KAAG,UAAU,IAAI,IAAI,QAAQ,IAAI;AAAG,KAAG,UAAU,IAAI,IAAI,OAAO,IAAI;AAAG,KAAG,UAAU,IAAI,IAAI,QAAQ,GAAG,IAAI;AAC3G,KAAG,UAAU,IAAI,OAAO,QAAQ,IAAI;AACpC,KAAG,UAAU,IAAI,IAAI,IAAI;AAAG,KAAG,UAAU,IAAI,IAAM,IAAI;AAAG,KAAG,UAAU,IAAI,IAAI,IAAI;AACnF,KAAG,UAAU,IAAI,UAAY,IAAI;AAAG,KAAG,UAAU,IAAI,OAAY,IAAI;AAAG,KAAG,UAAU,KAAK,KAAY,IAAI;AAAG,KAAG,UAAU,KAAK,YAAY,IAAI;AAC/I,KAAG,UAAU,KAAK,QAAU,OAAO,SAAS,IAAI,UAAW,IAAI,IAAI;AACnE,MAAI,IAAI;AACR,aAAW,KAAK,OAAQ,UAAS,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK;AAAE,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC;AAAI,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC;AAAI,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;AAAI,MAAE,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC;AAAA,EAAI;AACtK,SAAO;AACT;AAGO,SAAS,SAAS,KAA4B;AACnD,QAAM,IAAI,IAAI;AACd,MAAI,CAAC,EAAG,QAAO;AACf,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,GAAI,KAAI,EAAE,CAAC,IAAK,IAAK,QAAO;AAC/D,SAAO;AACT;"}