@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.
- package/README.md +41 -35
- package/dist/archives/common.d.ts +9 -9
- package/dist/archives/common.js.map +1 -1
- package/dist/archives/folder.d.ts +1 -1
- package/dist/archives/folder.js +1 -1
- package/dist/archives/folder.js.map +1 -1
- package/dist/archives/zip.js.map +1 -1
- package/dist/binary.d.ts +7 -7
- package/dist/binary.js.map +1 -1
- package/dist/builtin-en.js.map +1 -1
- package/dist/builtin.js +1 -1
- package/dist/builtin.js.map +1 -1
- package/dist/codecs/zlib.d.ts +2 -2
- package/dist/codecs/zlib.js.map +1 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/geometry.d.ts +5 -5
- package/dist/geometry.js.map +1 -1
- package/dist/i18n.d.ts +4 -4
- package/dist/i18n.js.map +1 -1
- package/dist/images/bc45.d.ts +1 -1
- package/dist/images/bc45.js.map +1 -1
- package/dist/images/bc7.d.ts +1 -1
- package/dist/images/bc7.js.map +1 -1
- package/dist/images/dds.d.ts +1 -1
- package/dist/images/dds.js.map +1 -1
- package/dist/images/pcx.js.map +1 -1
- package/dist/images/tga.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/levelbuilder.d.ts +3 -3
- package/dist/levelbuilder.js +1 -1
- package/dist/levelbuilder.js.map +1 -1
- package/dist/math.d.ts +6 -6
- package/dist/math.js.map +1 -1
- package/dist/normals.d.ts +5 -5
- package/dist/normals.js.map +1 -1
- package/dist/plugin.d.ts +57 -57
- package/dist/plugin.js.map +1 -1
- package/dist/registry.d.ts +14 -14
- package/dist/registry.js +13 -13
- package/dist/registry.js.map +1 -1
- package/dist/scene.d.ts +14 -14
- package/dist/scene.js.map +1 -1
- package/dist/session.d.ts +34 -34
- package/dist/session.js +33 -33
- package/dist/session.js.map +1 -1
- package/dist/skeleton.d.ts +3 -3
- package/dist/skeleton.js.map +1 -1
- package/dist/surface.d.ts +4 -4
- package/dist/surface.js.map +1 -1
- package/dist/textures.d.ts +12 -12
- package/dist/textures.js +9 -9
- package/dist/textures.js.map +1 -1
- package/dist/types.d.ts +103 -103
- package/dist/types.js.map +1 -1
- package/dist/writers/dds.d.ts +1 -1
- package/dist/writers/dds.js.map +1 -1
- package/dist/writers/obj.js.map +1 -1
- package/dist/writers/zip.js.map +1 -1
- 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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
73
|
+
/** Specular strength without a map (0..1; 0.5 ≈ F0 0.04). */
|
|
74
74
|
specularFactor?: number;
|
|
75
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
/**
|
|
94
|
+
/** Color without a texture (RenderWare). */
|
|
95
95
|
color?: number[];
|
|
96
|
-
/**
|
|
96
|
+
/** Embedded texture (Quake MDL, GoldSrc). */
|
|
97
97
|
image?: DecodedImage;
|
|
98
|
-
/**
|
|
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
|
-
/**
|
|
104
|
+
/** Candidate material paths (Source .vmt). */
|
|
105
105
|
materialPaths?: string[];
|
|
106
|
-
/**
|
|
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
|
|
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
|
-
/**
|
|
118
|
+
/** Second UV set: coordinates in the lightmap (Model.lightmap). */
|
|
119
119
|
uv2?: Float32Array;
|
|
120
|
-
/**
|
|
120
|
+
/** Per-vertex opacity (0..1): a terrain layer over the base, drawn after opaque meshes with blending. */
|
|
121
121
|
vertexAlpha?: Float32Array;
|
|
122
|
-
/**
|
|
122
|
+
/** Baked per-vertex lighting: RGB(A) bytes, 128 = unchanged (×2 multiplication, as in Black Box). */
|
|
123
123
|
vertexShade?: Uint8Array;
|
|
124
124
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
139
|
+
/** Surface plane in model coordinates: n·p = d (for water reflection). */
|
|
140
140
|
plane?: [number, number, number, number];
|
|
141
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
187
|
+
/** Brightness, 1 byte per pixel (or RGB, 3 bytes, with rgb). */
|
|
188
188
|
data: Uint8Array;
|
|
189
|
-
/**
|
|
189
|
+
/** Color map (Source): RGB8. */
|
|
190
190
|
rgb?: boolean;
|
|
191
|
-
/**
|
|
191
|
+
/** Brightness multiplier for display. */
|
|
192
192
|
scale?: number;
|
|
193
193
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
222
|
-
* (
|
|
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
|
-
/**
|
|
237
|
+
/** Where the animation comes from: 'model' or a file name. */
|
|
238
238
|
source?: string;
|
|
239
|
-
/**
|
|
239
|
+
/** Animation from a long list (Metro: thousands of files): data is read on selection. */
|
|
240
240
|
load?(): Promise<Motion>;
|
|
241
241
|
}
|
|
242
|
-
/**
|
|
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
|
-
/**
|
|
251
|
+
/** Names from layerPool picked as the default layer. */
|
|
252
252
|
layerAuto?: string[];
|
|
253
253
|
/**
|
|
254
|
-
*
|
|
255
|
-
* (
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
283
|
-
* `declare module '@gmvjs/core/types' { interface MotionKinds { xray: XrayMotion } }
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
349
|
+
/** Model instances inside the scene (Metro level.bin). */
|
|
350
350
|
instances?: InstancedScene;
|
|
351
|
-
/**
|
|
351
|
+
/** Level lightmap (Metro level.lmap_pc). */
|
|
352
352
|
lightmap?: Lightmap;
|
|
353
|
-
/**
|
|
353
|
+
/** Terrain surface layers (Mesh.terrainIndex). */
|
|
354
354
|
terrainLayers?: TerrainLayers;
|
|
355
355
|
skeleton: SkeletonRuntime | null;
|
|
356
356
|
motions: Motion[];
|
|
357
357
|
bounds: Bounds;
|
|
358
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
368
|
+
/** Loading external animations (.omf/.psa/.md5anim/.cast). */
|
|
369
369
|
loadMotions?(file: VFile): Promise<Motion[]>;
|
|
370
|
-
/**
|
|
370
|
+
/** Arbitrary data for texture resolvers (.mtl name and the like). */
|
|
371
371
|
meta?: Record<string, unknown>;
|
|
372
372
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
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
|
-
/**
|
|
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;"}
|
package/dist/writers/dds.d.ts
CHANGED
|
@@ -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;
|
package/dist/writers/dds.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dds.js","sources":["../../src/writers/dds.ts"],"sourcesContent":["// DDS
|
|
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;"}
|