@quake2ts/test-utils 0.0.818 → 0.0.823

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quake2ts/test-utils",
3
- "version": "0.0.818",
3
+ "version": "0.0.823",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -55,10 +55,10 @@
55
55
  "serve-handler": "^6.1.6",
56
56
  "vitest": "^1.6.0",
57
57
  "webgpu": "^0.3.8",
58
- "@quake2ts/engine": "^0.0.818",
59
- "@quake2ts/game": "0.0.818",
60
- "@quake2ts/shared": "0.0.818",
61
- "@quake2ts/server": "0.0.818"
58
+ "@quake2ts/engine": "^0.0.823",
59
+ "@quake2ts/server": "0.0.823",
60
+ "@quake2ts/game": "0.0.823",
61
+ "@quake2ts/shared": "0.0.823"
62
62
  },
63
63
  "peerDependenciesMeta": {
64
64
  "@quake2ts/engine": {
@@ -104,20 +104,20 @@
104
104
  "@types/upng-js": "^2.1.5",
105
105
  "@webgpu/types": "^0.1.68",
106
106
  "fake-indexeddb": "^6.0.0",
107
- "jsdom": "^27.4.0",
108
107
  "gl-matrix": "^3.4.4",
108
+ "jsdom": "^27.4.0",
109
109
  "pixelmatch": "^7.1.0",
110
110
  "playwright": "^1.57.0",
111
111
  "pngjs": "^7.0.0",
112
+ "serve-handler": "^6.1.6",
112
113
  "tsup": "^8.5.1",
113
114
  "typescript": "^5.9.3",
114
115
  "vitest": "^4.0.16",
115
- "serve-handler": "^6.1.6",
116
116
  "webgpu": "^0.3.8",
117
- "@quake2ts/engine": "^0.0.818",
118
- "@quake2ts/game": "0.0.818",
119
- "@quake2ts/server": "0.0.818",
120
- "@quake2ts/shared": "0.0.818"
117
+ "@quake2ts/engine": "^0.0.823",
118
+ "@quake2ts/game": "0.0.823",
119
+ "@quake2ts/server": "0.0.823",
120
+ "@quake2ts/shared": "0.0.823"
121
121
  },
122
122
  "dependencies": {
123
123
  "upng-js": "^2.1.0"
@@ -77,6 +77,7 @@ export function createMockRenderer(overrides?: Partial<Renderer>): Renderer {
77
77
  setLodBias: vi.fn(),
78
78
  setAreaPortalState: vi.fn(),
79
79
  renderInstanced: vi.fn(),
80
+ uploadBspGeometry: vi.fn().mockReturnValue({ surfaces: [], lightmaps: [] }),
80
81
  dispose: vi.fn(),
81
82
  ...overrides,
82
83
  };
package/src/shared/bsp.ts CHANGED
@@ -5,9 +5,27 @@ import {
5
5
  type CollisionPlane,
6
6
  type CollisionNode,
7
7
  type CollisionLeaf,
8
- CONTENTS_SOLID,
9
- type Vec3
8
+ CONTENTS_SOLID
10
9
  } from '@quake2ts/shared';
10
+ // Avoid named imports for Vec3 from shared to avoid conflict with engine tuple type
11
+ import * as SharedMath from '@quake2ts/shared';
12
+
13
+ import {
14
+ type BspMap,
15
+ type BspFace,
16
+ type BspTexInfo,
17
+ type BspNode,
18
+ type BspLeaf,
19
+ type BspModel,
20
+ type BspEdge,
21
+ type BspEntity,
22
+ type BspHeader,
23
+ type BspEntities,
24
+ type BspPlane,
25
+ type BspLightmapInfo,
26
+ type BspLeafLists,
27
+ type BspData
28
+ } from '@quake2ts/engine';
11
29
 
12
30
  /**
13
31
  * Creates a collision plane with the specified normal and distance.
@@ -17,7 +35,7 @@ import {
17
35
  * @param dist - The distance from the origin.
18
36
  * @returns A CollisionPlane object.
19
37
  */
20
- export function makePlane(normal: Vec3, dist: number): CollisionPlane {
38
+ export function makePlane(normal: SharedMath.Vec3, dist: number): CollisionPlane {
21
39
  return {
22
40
  normal,
23
41
  dist,
@@ -128,7 +146,7 @@ export function makeLeafModel(brushes: CollisionBrush[]): CollisionModel {
128
146
  * @param contents - Content flags (default: CONTENTS_SOLID).
129
147
  * @returns A CollisionBrush object.
130
148
  */
131
- export function makeBrushFromMinsMaxs(mins: Vec3, maxs: Vec3, contents = CONTENTS_SOLID): CollisionBrush {
149
+ export function makeBrushFromMinsMaxs(mins: SharedMath.Vec3, maxs: SharedMath.Vec3, contents = CONTENTS_SOLID): CollisionBrush {
132
150
  const planes = [
133
151
  makePlane({ x: 1, y: 0, z: 0 }, maxs.x),
134
152
  makePlane({ x: -1, y: 0, z: 0 }, -mins.x),
@@ -143,3 +161,162 @@ export function makeBrushFromMinsMaxs(mins: Vec3, maxs: Vec3, contents = CONTENT
143
161
  sides: planes.map((plane) => ({ plane, surfaceFlags: 0 })),
144
162
  };
145
163
  }
164
+
165
+ // --- Visual Test Helpers ---
166
+
167
+ // BspVec3 matches the tuple type used in engine assets
168
+ export type BspVec3 = [number, number, number];
169
+
170
+ export interface TestBspSurface {
171
+ vertices: BspVec3[];
172
+ texInfo?: Partial<BspTexInfo>;
173
+ lightmap?: Uint8Array;
174
+ lightmapInfo?: BspLightmapInfo;
175
+ styles?: [number, number, number, number];
176
+ }
177
+
178
+ export interface TestBspMapOptions {
179
+ surfaces?: TestBspSurface[];
180
+ entities?: BspEntity[];
181
+ }
182
+
183
+ /**
184
+ * Creates a minimal valid BspMap for testing rendering.
185
+ *
186
+ * @param options - Configuration for the test BSP map.
187
+ * @returns A BspMap object populated with the requested surfaces.
188
+ */
189
+ export function createTestBspMap(options: TestBspMapOptions = {}): BspMap {
190
+ const vertices: BspVec3[] = [];
191
+ const edges: BspEdge[] = [];
192
+ const surfEdges: number[] = []; // Using number[] to match push usage, converted to Int32Array later
193
+ const faces: BspFace[] = [];
194
+ const texInfos: BspTexInfo[] = [];
195
+ const lightMapInfo: (BspLightmapInfo | undefined)[] = [];
196
+
197
+ // Aggregate lightmap data
198
+ let lightMapDataSize = 0;
199
+ if (options.surfaces) {
200
+ for (const surface of options.surfaces) {
201
+ if (surface.lightmap) {
202
+ lightMapDataSize += surface.lightmap.length;
203
+ }
204
+ }
205
+ }
206
+ const lightMaps = new Uint8Array(lightMapDataSize);
207
+ let currentLightMapOffset = 0;
208
+
209
+ if (options.surfaces) {
210
+ for (const surface of options.surfaces) {
211
+ // Add vertices and create edges
212
+ const firstEdge = surfEdges.length;
213
+ const startVertexIndex = vertices.length;
214
+
215
+ for (const v of surface.vertices) {
216
+ vertices.push(v);
217
+ }
218
+
219
+ for (let i = 0; i < surface.vertices.length; i++) {
220
+ const v1 = startVertexIndex + i;
221
+ const v2 = startVertexIndex + ((i + 1) % surface.vertices.length);
222
+ edges.push({ vertices: [v1, v2] });
223
+ surfEdges.push(edges.length - 1); // Positive index for standard winding
224
+ }
225
+
226
+ // Add TexInfo
227
+ const defaultTexInfo: BspTexInfo = {
228
+ s: [1, 0, 0], sOffset: 0,
229
+ t: [0, 1, 0], tOffset: 0,
230
+ flags: 0, value: 0,
231
+ texture: 'test_texture',
232
+ nextTexInfo: -1
233
+ };
234
+ const texInfo: BspTexInfo = { ...defaultTexInfo, ...surface.texInfo };
235
+ texInfos.push(texInfo);
236
+
237
+ // Handle Lightmap
238
+ let lightOffset = -1;
239
+ let info: BspLightmapInfo | undefined = undefined;
240
+
241
+ if (surface.lightmap) {
242
+ lightOffset = currentLightMapOffset;
243
+ lightMaps.set(surface.lightmap, lightOffset);
244
+ info = { offset: lightOffset, length: surface.lightmap.length };
245
+ currentLightMapOffset += surface.lightmap.length;
246
+ }
247
+ lightMapInfo.push(info);
248
+
249
+ faces.push({
250
+ planeIndex: 0, // Dummy plane
251
+ side: 0,
252
+ firstEdge,
253
+ numEdges: surface.vertices.length,
254
+ texInfo: texInfos.length - 1,
255
+ styles: surface.styles ?? [255, 255, 255, 255], // Default styles
256
+ lightOffset
257
+ });
258
+ }
259
+ }
260
+
261
+ // Minimal valid BSP structure
262
+ const header: BspHeader = { version: 38, lumps: new Map() };
263
+ const entities: BspEntities = {
264
+ raw: '',
265
+ entities: options.entities || [],
266
+ worldspawn: undefined,
267
+ getUniqueClassnames: () => []
268
+ };
269
+
270
+ const planes: BspPlane[] = [{ normal: [0, 0, 1], dist: 0, type: 0 }]; // Dummy plane
271
+ const nodes: BspNode[] = [];
272
+ const leafs: BspLeaf[] = [{
273
+ contents: 0, cluster: 0, area: 0,
274
+ mins: [-1000, -1000, -1000], maxs: [1000, 1000, 1000],
275
+ firstLeafFace: 0, numLeafFaces: faces.length,
276
+ firstLeafBrush: 0, numLeafBrushes: 0
277
+ }];
278
+ const leafLists: BspLeafLists = {
279
+ leafFaces: [faces.map((_, i) => i)],
280
+ leafBrushes: [[]]
281
+ };
282
+
283
+ const models: BspModel[] = [{
284
+ mins: [-1000, -1000, -1000], maxs: [1000, 1000, 1000], origin: [0,0,0],
285
+ headNode: 0, firstFace: 0, numFaces: faces.length
286
+ }];
287
+
288
+ const data: BspData = {
289
+ header, entities, planes, vertices, nodes, texInfo: texInfos, faces,
290
+ lightMaps, lightMapInfo, leafs, leafLists, edges, surfEdges: Int32Array.from(surfEdges),
291
+ models, brushes: [], brushSides: [], visibility: undefined, areas: [], areaPortals: []
292
+ };
293
+
294
+ // Create the BspMap with methods
295
+ return {
296
+ ...data,
297
+ pickEntity: () => null,
298
+ findLeaf: () => leafs[0],
299
+ calculatePVS: () => undefined
300
+ };
301
+ }
302
+
303
+ /**
304
+ * Creates a simple solid color lightmap of specified dimensions.
305
+ *
306
+ * @param width - Width of the lightmap (usually small, e.g. 16x16)
307
+ * @param height - Height of the lightmap
308
+ * @param r - Red component (0-255)
309
+ * @param g - Green component (0-255)
310
+ * @param b - Blue component (0-255)
311
+ * @returns Uint8Array containing RGB data
312
+ */
313
+ export function createTestLightmap(width: number, height: number, r: number, g: number, b: number): Uint8Array {
314
+ const size = width * height * 3;
315
+ const data = new Uint8Array(size);
316
+ for (let i = 0; i < size; i+=3) {
317
+ data[i] = r;
318
+ data[i+1] = g;
319
+ data[i+2] = b;
320
+ }
321
+ return data;
322
+ }