@quake2ts/test-utils 0.0.838 → 0.0.839

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/dist/index.js CHANGED
@@ -4910,6 +4910,602 @@ function expectNoDoubleTransform(renderer) {
4910
4910
  expect2(warnings).toHaveLength(0);
4911
4911
  }
4912
4912
 
4913
+ // src/engine/builders/bspBuilder.ts
4914
+ import { BspLump } from "@quake2ts/engine";
4915
+ var HEADER_LUMPS = 19;
4916
+ var HEADER_SIZE = 4 + 4 + HEADER_LUMPS * 8;
4917
+ function allocBuffer(size) {
4918
+ return new DataView(new ArrayBuffer(size));
4919
+ }
4920
+ function writeVec3(view, offset, vec) {
4921
+ view.setFloat32(offset, vec[0], true);
4922
+ view.setFloat32(offset + 4, vec[1], true);
4923
+ view.setFloat32(offset + 8, vec[2], true);
4924
+ }
4925
+ function encodePlanes(planes) {
4926
+ const view = allocBuffer(planes.length * 20);
4927
+ planes.forEach((plane, index) => {
4928
+ const base = index * 20;
4929
+ writeVec3(view, base, plane.normal);
4930
+ view.setFloat32(base + 12, plane.dist, true);
4931
+ view.setInt32(base + 16, plane.type, true);
4932
+ });
4933
+ return new Uint8Array(view.buffer);
4934
+ }
4935
+ function encodeVertices(vertices) {
4936
+ const view = allocBuffer(vertices.length * 12);
4937
+ vertices.forEach((vertex, index) => writeVec3(view, index * 12, vertex));
4938
+ return new Uint8Array(view.buffer);
4939
+ }
4940
+ function encodeNodes(nodes) {
4941
+ const view = allocBuffer(nodes.length * 28);
4942
+ nodes.forEach((node, index) => {
4943
+ const base = index * 28;
4944
+ view.setInt32(base, node.planeIndex, true);
4945
+ view.setInt32(base + 4, node.children[0], true);
4946
+ view.setInt32(base + 8, node.children[1], true);
4947
+ view.setInt16(base + 12, node.mins[0], true);
4948
+ view.setInt16(base + 14, node.mins[1], true);
4949
+ view.setInt16(base + 16, node.mins[2], true);
4950
+ view.setInt16(base + 18, node.maxs[0], true);
4951
+ view.setInt16(base + 20, node.maxs[1], true);
4952
+ view.setInt16(base + 22, node.maxs[2], true);
4953
+ view.setUint16(base + 24, node.firstFace, true);
4954
+ view.setUint16(base + 26, node.numFaces, true);
4955
+ });
4956
+ return new Uint8Array(view.buffer);
4957
+ }
4958
+ function encodeTexInfo(texInfos) {
4959
+ const view = allocBuffer(texInfos.length * 76);
4960
+ texInfos.forEach((tex, index) => {
4961
+ const base = index * 76;
4962
+ writeVec3(view, base, tex.s);
4963
+ view.setFloat32(base + 12, tex.sOffset, true);
4964
+ writeVec3(view, base + 16, tex.t);
4965
+ view.setFloat32(base + 28, tex.tOffset, true);
4966
+ view.setInt32(base + 32, tex.flags, true);
4967
+ view.setInt32(base + 36, tex.value, true);
4968
+ const textureBytes = new TextEncoder().encode(tex.texture);
4969
+ new Uint8Array(view.buffer).set(textureBytes.slice(0, 32), base + 40);
4970
+ view.setInt32(base + 72, tex.nextTexInfo, true);
4971
+ });
4972
+ return new Uint8Array(view.buffer);
4973
+ }
4974
+ function encodeFaces(faces) {
4975
+ const view = allocBuffer(faces.length * 20);
4976
+ faces.forEach((face, index) => {
4977
+ const base = index * 20;
4978
+ view.setUint16(base, face.planeIndex, true);
4979
+ view.setInt16(base + 2, face.side, true);
4980
+ view.setInt32(base + 4, face.firstEdge, true);
4981
+ view.setInt16(base + 8, face.numEdges, true);
4982
+ view.setInt16(base + 10, face.texInfo, true);
4983
+ face.styles.forEach((style, sIndex) => view.setUint8(base + 12 + sIndex, style));
4984
+ view.setInt32(base + 16, face.lightOffset, true);
4985
+ });
4986
+ return new Uint8Array(view.buffer);
4987
+ }
4988
+ function encodeLeafs(leafs) {
4989
+ const view = allocBuffer(leafs.length * 28);
4990
+ leafs.forEach((leaf, index) => {
4991
+ const base = index * 28;
4992
+ view.setInt32(base, leaf.contents, true);
4993
+ view.setInt16(base + 4, leaf.cluster, true);
4994
+ view.setInt16(base + 6, leaf.area, true);
4995
+ view.setInt16(base + 8, leaf.mins[0], true);
4996
+ view.setInt16(base + 10, leaf.mins[1], true);
4997
+ view.setInt16(base + 12, leaf.mins[2], true);
4998
+ view.setInt16(base + 14, leaf.maxs[0], true);
4999
+ view.setInt16(base + 16, leaf.maxs[1], true);
5000
+ view.setInt16(base + 18, leaf.maxs[2], true);
5001
+ view.setUint16(base + 20, leaf.firstLeafFace, true);
5002
+ view.setUint16(base + 22, leaf.numLeafFaces, true);
5003
+ view.setUint16(base + 24, leaf.firstLeafBrush, true);
5004
+ view.setUint16(base + 26, leaf.numLeafBrushes, true);
5005
+ });
5006
+ return new Uint8Array(view.buffer);
5007
+ }
5008
+ function encodeEdges(edges) {
5009
+ const view = allocBuffer(edges.length * 4);
5010
+ edges.forEach((edge, index) => {
5011
+ const base = index * 4;
5012
+ view.setUint16(base, edge[0], true);
5013
+ view.setUint16(base + 2, edge[1], true);
5014
+ });
5015
+ return new Uint8Array(view.buffer);
5016
+ }
5017
+ function encodeModels(models) {
5018
+ const entrySize = 48;
5019
+ const view = allocBuffer(models.length * entrySize);
5020
+ models.forEach((model, index) => {
5021
+ const base = index * entrySize;
5022
+ writeVec3(view, base, model.mins);
5023
+ writeVec3(view, base + 12, model.maxs);
5024
+ writeVec3(view, base + 24, model.origin);
5025
+ view.setInt32(base + 36, model.headNode, true);
5026
+ view.setInt32(base + 40, model.firstFace, true);
5027
+ view.setInt32(base + 44, model.numFaces, true);
5028
+ });
5029
+ return new Uint8Array(view.buffer);
5030
+ }
5031
+ function encodeBrushes(brushes) {
5032
+ const view = allocBuffer(brushes.length * 12);
5033
+ brushes.forEach((brush, index) => {
5034
+ const base = index * 12;
5035
+ view.setInt32(base, brush.firstSide, true);
5036
+ view.setInt32(base + 4, brush.numSides, true);
5037
+ view.setInt32(base + 8, brush.contents, true);
5038
+ });
5039
+ return new Uint8Array(view.buffer);
5040
+ }
5041
+ function encodeBrushSides(sides) {
5042
+ const view = allocBuffer(sides.length * 4);
5043
+ sides.forEach((side, index) => {
5044
+ const base = index * 4;
5045
+ view.setUint16(base, side.planeIndex, true);
5046
+ view.setInt16(base + 2, side.texInfo, true);
5047
+ });
5048
+ return new Uint8Array(view.buffer);
5049
+ }
5050
+ function encodeVisibility(numClusters, pvsRows, phsRows) {
5051
+ const headerBytes = 4 + numClusters * 8;
5052
+ const header = allocBuffer(headerBytes);
5053
+ header.setInt32(0, numClusters, true);
5054
+ let cursor = headerBytes;
5055
+ const payloads = [];
5056
+ for (let i = 0; i < numClusters; i += 1) {
5057
+ const pvs = pvsRows[i];
5058
+ const phs = phsRows?.[i] ?? pvs;
5059
+ const pvsOffset = cursor - 0;
5060
+ const phsOffset = cursor + pvs.byteLength - 0;
5061
+ payloads.push(pvs, phs);
5062
+ header.setInt32(4 + i * 8, pvsOffset, true);
5063
+ header.setInt32(8 + i * 8, phsOffset, true);
5064
+ cursor += pvs.byteLength + phs.byteLength;
5065
+ }
5066
+ const result = new Uint8Array(cursor);
5067
+ result.set(new Uint8Array(header.buffer), 0);
5068
+ let payloadCursor = headerBytes;
5069
+ for (const payload of payloads) {
5070
+ result.set(payload, payloadCursor);
5071
+ payloadCursor += payload.byteLength;
5072
+ }
5073
+ return result;
5074
+ }
5075
+ function buildTestBsp(options) {
5076
+ const lumps = {};
5077
+ lumps[BspLump.Entities] = new TextEncoder().encode(options.entities ?? '{"classname" "worldspawn"}\n');
5078
+ lumps[BspLump.Planes] = options.planes ? encodePlanes(options.planes) : new Uint8Array();
5079
+ lumps[BspLump.Vertices] = options.vertices ? encodeVertices(options.vertices) : new Uint8Array();
5080
+ lumps[BspLump.Visibility] = options.visibility ?? new Uint8Array();
5081
+ lumps[BspLump.Nodes] = options.nodes ? encodeNodes(options.nodes) : new Uint8Array();
5082
+ lumps[BspLump.TexInfo] = options.texInfo ? encodeTexInfo(options.texInfo) : new Uint8Array();
5083
+ lumps[BspLump.Faces] = options.faces ? encodeFaces(options.faces) : new Uint8Array();
5084
+ lumps[BspLump.Lighting] = options.lighting ?? new Uint8Array();
5085
+ lumps[BspLump.Leafs] = options.leafs ? encodeLeafs(options.leafs) : new Uint8Array();
5086
+ lumps[BspLump.LeafFaces] = options.leafFaces ? new Uint8Array(options.leafFaces.buffer) : new Uint8Array();
5087
+ lumps[BspLump.LeafBrushes] = options.leafBrushes ? new Uint8Array(options.leafBrushes.buffer) : new Uint8Array();
5088
+ lumps[BspLump.Edges] = options.edges ? encodeEdges(options.edges) : new Uint8Array();
5089
+ lumps[BspLump.SurfEdges] = options.surfEdges ? new Uint8Array(options.surfEdges.buffer) : new Uint8Array();
5090
+ lumps[BspLump.Models] = options.models ? encodeModels(options.models) : new Uint8Array();
5091
+ lumps[BspLump.Brushes] = options.brushes ? encodeBrushes(options.brushes) : new Uint8Array();
5092
+ lumps[BspLump.BrushSides] = options.brushSides ? encodeBrushSides(options.brushSides) : new Uint8Array();
5093
+ lumps[BspLump.Areas] = options.areas ?? new Uint8Array();
5094
+ lumps[BspLump.AreaPortals] = options.areaPortals ?? new Uint8Array();
5095
+ let cursor = HEADER_SIZE;
5096
+ const ordered = [];
5097
+ for (let i = 0; i < HEADER_LUMPS; i += 1) {
5098
+ const data = lumps[i] ?? new Uint8Array();
5099
+ const info = { offset: cursor, length: data.byteLength };
5100
+ ordered.push({ info, data });
5101
+ cursor += data.byteLength;
5102
+ }
5103
+ const buffer = new ArrayBuffer(cursor);
5104
+ const header = new DataView(buffer);
5105
+ header.setUint8(0, 73);
5106
+ header.setUint8(1, 66);
5107
+ header.setUint8(2, 83);
5108
+ header.setUint8(3, 80);
5109
+ header.setInt32(4, 38, true);
5110
+ ordered.forEach((entry, index) => {
5111
+ header.setInt32(8 + index * 8, entry.info.offset, true);
5112
+ header.setInt32(12 + index * 8, entry.info.length, true);
5113
+ });
5114
+ const body = new Uint8Array(buffer);
5115
+ ordered.forEach((entry) => body.set(entry.data, entry.info.offset));
5116
+ return buffer;
5117
+ }
5118
+ function runLengthVisRow(values) {
5119
+ return new Uint8Array(values);
5120
+ }
5121
+ function encodedVisForClusters(numClusters, rows) {
5122
+ const rowBytes = Math.ceil(numClusters / 8);
5123
+ const encodedRows = rows.map((row) => new Uint8Array(row));
5124
+ return encodeVisibility(numClusters, encodedRows);
5125
+ }
5126
+
5127
+ // src/engine/builders/md2Builder.ts
5128
+ function allocBuffer2(size) {
5129
+ return new DataView(new ArrayBuffer(size));
5130
+ }
5131
+ function writeCString(view, offset, text, max) {
5132
+ const bytes = new TextEncoder().encode(text);
5133
+ const length = Math.min(bytes.length, max - 1);
5134
+ new Uint8Array(view.buffer, view.byteOffset + offset, length).set(bytes.slice(0, length));
5135
+ view.setUint8(offset + length, 0);
5136
+ }
5137
+ function encodeFrames(frames, numVertices) {
5138
+ const frameSize = 40 + numVertices * 4;
5139
+ const view = allocBuffer2(frames.length * frameSize);
5140
+ frames.forEach((frame, frameIndex) => {
5141
+ const base = frameIndex * frameSize;
5142
+ const scale = frame.scale ?? { x: 1, y: 1, z: 1 };
5143
+ const translate = frame.translate ?? { x: 0, y: 0, z: 0 };
5144
+ view.setFloat32(base, scale.x, true);
5145
+ view.setFloat32(base + 4, scale.y, true);
5146
+ view.setFloat32(base + 8, scale.z, true);
5147
+ view.setFloat32(base + 12, translate.x, true);
5148
+ view.setFloat32(base + 16, translate.y, true);
5149
+ view.setFloat32(base + 20, translate.z, true);
5150
+ writeCString(view, base + 24, frame.name, 16);
5151
+ frame.vertices.forEach((vertex, index) => {
5152
+ const offset = base + 40 + index * 4;
5153
+ view.setUint8(offset, Math.round((vertex.position.x - translate.x) / scale.x));
5154
+ view.setUint8(offset + 1, Math.round((vertex.position.y - translate.y) / scale.y));
5155
+ view.setUint8(offset + 2, Math.round((vertex.position.z - translate.z) / scale.z));
5156
+ view.setUint8(offset + 3, vertex.normalIndex);
5157
+ });
5158
+ });
5159
+ return new Uint8Array(view.buffer);
5160
+ }
5161
+ function encodeGlCommands(commands) {
5162
+ const bytes = [];
5163
+ (commands ?? []).forEach((command) => {
5164
+ const count = command.vertices.length * (command.mode === "strip" ? 1 : -1);
5165
+ bytes.push(count);
5166
+ command.vertices.forEach((vertex) => {
5167
+ const floatView = new DataView(new ArrayBuffer(4));
5168
+ floatView.setFloat32(0, vertex.s, true);
5169
+ bytes.push(floatView.getInt32(0, true));
5170
+ floatView.setFloat32(0, vertex.t, true);
5171
+ bytes.push(floatView.getInt32(0, true));
5172
+ bytes.push(vertex.vertexIndex);
5173
+ });
5174
+ });
5175
+ bytes.push(0);
5176
+ const data = new Uint8Array(bytes.length * 4);
5177
+ const view = new DataView(data.buffer);
5178
+ bytes.forEach((value, index) => view.setInt32(index * 4, value, true));
5179
+ return { data, count: bytes.length };
5180
+ }
5181
+ function buildMd2(options) {
5182
+ const numVertices = options.frames[0]?.vertices.length ?? 0;
5183
+ const frameSize = 40 + numVertices * 4;
5184
+ const skins = options.skins ?? [];
5185
+ const { data: glData, count: glCount } = encodeGlCommands(options.glCommands);
5186
+ const headerSize = 68;
5187
+ const skinsSize = skins.length * 64;
5188
+ const texCoordSize = options.texCoords.length * 4;
5189
+ const triangleSize = options.triangles.length * 12;
5190
+ const frameBlockSize = options.frames.length * frameSize;
5191
+ const glSize = glData.length;
5192
+ const offsetSkins = headerSize;
5193
+ const offsetTexCoords = offsetSkins + skinsSize;
5194
+ const offsetTriangles = offsetTexCoords + texCoordSize;
5195
+ const offsetFrames = offsetTriangles + triangleSize;
5196
+ const offsetGlCommands = offsetFrames + frameBlockSize;
5197
+ const offsetEnd = offsetGlCommands + glSize;
5198
+ const view = allocBuffer2(offsetEnd);
5199
+ view.setInt32(0, 844121161, true);
5200
+ view.setInt32(4, 8, true);
5201
+ view.setInt32(8, options.skinWidth ?? 64, true);
5202
+ view.setInt32(12, options.skinHeight ?? 64, true);
5203
+ view.setInt32(16, frameSize, true);
5204
+ view.setInt32(20, skins.length, true);
5205
+ view.setInt32(24, numVertices, true);
5206
+ view.setInt32(28, options.texCoords.length, true);
5207
+ view.setInt32(32, options.triangles.length, true);
5208
+ view.setInt32(36, glCount, true);
5209
+ view.setInt32(40, options.frames.length, true);
5210
+ view.setInt32(44, offsetSkins, true);
5211
+ view.setInt32(48, offsetTexCoords, true);
5212
+ view.setInt32(52, offsetTriangles, true);
5213
+ view.setInt32(56, offsetFrames, true);
5214
+ view.setInt32(60, offsetGlCommands, true);
5215
+ view.setInt32(64, offsetEnd, true);
5216
+ skins.forEach((skin, index) => writeCString(view, offsetSkins + index * 64, skin, 64));
5217
+ options.texCoords.forEach((coord, index) => {
5218
+ const base = offsetTexCoords + index * 4;
5219
+ view.setInt16(base, coord.s, true);
5220
+ view.setInt16(base + 2, coord.t, true);
5221
+ });
5222
+ options.triangles.forEach((tri, index) => {
5223
+ const base = offsetTriangles + index * 12;
5224
+ view.setUint16(base, tri.vertexIndices[0], true);
5225
+ view.setUint16(base + 2, tri.vertexIndices[1], true);
5226
+ view.setUint16(base + 4, tri.vertexIndices[2], true);
5227
+ view.setUint16(base + 6, tri.texCoordIndices[0], true);
5228
+ view.setUint16(base + 8, tri.texCoordIndices[1], true);
5229
+ view.setUint16(base + 10, tri.texCoordIndices[2], true);
5230
+ });
5231
+ new Uint8Array(view.buffer, offsetFrames, frameBlockSize).set(encodeFrames(options.frames, numVertices));
5232
+ new Uint8Array(view.buffer, offsetGlCommands, glSize).set(glData);
5233
+ return view.buffer;
5234
+ }
5235
+
5236
+ // src/engine/builders/md3Builder.ts
5237
+ var HEADER_SIZE2 = 108;
5238
+ var FRAME_SIZE = 56;
5239
+ var TAG_SIZE = 112;
5240
+ var SURFACE_HEADER_SIZE = 108;
5241
+ function writeString(target, offset, text, length) {
5242
+ const encoder = new TextEncoder();
5243
+ const encoded = encoder.encode(text);
5244
+ const bytes = new Uint8Array(target.buffer, target.byteOffset + offset, length);
5245
+ bytes.fill(0);
5246
+ bytes.set(encoded.subarray(0, length));
5247
+ }
5248
+ function buildSurface(buffer, offset, surface, numFrames) {
5249
+ const view = new DataView(buffer);
5250
+ writeString(view, offset + 4, surface.name, 64);
5251
+ view.setInt32(offset, 860898377, true);
5252
+ view.setInt32(offset + 72, numFrames, true);
5253
+ view.setInt32(offset + 76, surface.shaders?.length ?? 0, true);
5254
+ view.setInt32(offset + 80, surface.vertices[0]?.length ?? 0, true);
5255
+ view.setInt32(offset + 84, surface.triangles.length, true);
5256
+ let cursor = SURFACE_HEADER_SIZE;
5257
+ view.setInt32(offset + 88, cursor, true);
5258
+ for (const tri of surface.triangles) {
5259
+ view.setInt32(offset + cursor, tri[0], true);
5260
+ view.setInt32(offset + cursor + 4, tri[1], true);
5261
+ view.setInt32(offset + cursor + 8, tri[2], true);
5262
+ cursor += 12;
5263
+ }
5264
+ view.setInt32(offset + 92, cursor, true);
5265
+ for (const shader of surface.shaders ?? []) {
5266
+ writeString(view, offset + cursor, shader.name, 64);
5267
+ view.setInt32(offset + cursor + 64, shader.index, true);
5268
+ cursor += 68;
5269
+ }
5270
+ view.setInt32(offset + 96, cursor, true);
5271
+ for (const tex of surface.texCoords) {
5272
+ view.setFloat32(offset + cursor, tex.s, true);
5273
+ view.setFloat32(offset + cursor + 4, tex.t, true);
5274
+ cursor += 8;
5275
+ }
5276
+ view.setInt32(offset + 100, cursor, true);
5277
+ for (const frame of surface.vertices) {
5278
+ for (const vertex of frame) {
5279
+ view.setInt16(offset + cursor, Math.round(vertex.position.x * 64), true);
5280
+ view.setInt16(offset + cursor + 2, Math.round(vertex.position.y * 64), true);
5281
+ view.setInt16(offset + cursor + 4, Math.round(vertex.position.z * 64), true);
5282
+ view.setUint16(offset + cursor + 6, vertex.latLng, true);
5283
+ cursor += 8;
5284
+ }
5285
+ }
5286
+ view.setInt32(offset + 104, cursor, true);
5287
+ return cursor;
5288
+ }
5289
+ function buildMd3(options) {
5290
+ const numFrames = options.frames.length;
5291
+ const numTags = options.tags?.length ?? 0;
5292
+ const numSurfaces = options.surfaces.length;
5293
+ let size = HEADER_SIZE2;
5294
+ size += numFrames * FRAME_SIZE;
5295
+ size += numFrames * numTags * TAG_SIZE;
5296
+ for (const surface of options.surfaces) {
5297
+ const verts = surface.vertices[0]?.length ?? 0;
5298
+ const triangles = surface.triangles.length;
5299
+ const shaders = surface.shaders?.length ?? 0;
5300
+ const texCoordBytes = surface.texCoords.length * 8;
5301
+ const surfaceSize = SURFACE_HEADER_SIZE + triangles * 12 + shaders * 68 + texCoordBytes + verts * 8 * numFrames;
5302
+ size += surfaceSize;
5303
+ }
5304
+ const buffer = new ArrayBuffer(size);
5305
+ const view = new DataView(buffer);
5306
+ writeString(view, 8, options.name ?? "builder", 64);
5307
+ view.setInt32(0, 860898377, true);
5308
+ view.setInt32(4, 15, true);
5309
+ view.setInt32(72, 0, true);
5310
+ view.setInt32(76, numFrames, true);
5311
+ view.setInt32(80, numTags, true);
5312
+ view.setInt32(84, numSurfaces, true);
5313
+ view.setInt32(88, 0, true);
5314
+ let offset = HEADER_SIZE2;
5315
+ view.setInt32(92, offset, true);
5316
+ for (const frame of options.frames) {
5317
+ view.setFloat32(offset, frame.min.x, true);
5318
+ view.setFloat32(offset + 4, frame.min.y, true);
5319
+ view.setFloat32(offset + 8, frame.min.z, true);
5320
+ view.setFloat32(offset + 12, frame.max.x, true);
5321
+ view.setFloat32(offset + 16, frame.max.y, true);
5322
+ view.setFloat32(offset + 20, frame.max.z, true);
5323
+ view.setFloat32(offset + 24, frame.origin.x, true);
5324
+ view.setFloat32(offset + 28, frame.origin.y, true);
5325
+ view.setFloat32(offset + 32, frame.origin.z, true);
5326
+ view.setFloat32(offset + 36, frame.radius, true);
5327
+ writeString(view, offset + 40, frame.name, 16);
5328
+ offset += FRAME_SIZE;
5329
+ }
5330
+ view.setInt32(96, offset, true);
5331
+ for (let frame = 0; frame < numFrames; frame += 1) {
5332
+ for (const tag of options.tags ?? []) {
5333
+ writeString(view, offset, tag.name, 64);
5334
+ view.setFloat32(offset + 64, tag.origin.x, true);
5335
+ view.setFloat32(offset + 68, tag.origin.y, true);
5336
+ view.setFloat32(offset + 72, tag.origin.z, true);
5337
+ for (let axis = 0; axis < 3; axis += 1) {
5338
+ const v = tag.axis[axis];
5339
+ view.setFloat32(offset + 76 + axis * 12, v.x, true);
5340
+ view.setFloat32(offset + 80 + axis * 12, v.y, true);
5341
+ view.setFloat32(offset + 84 + axis * 12, v.z, true);
5342
+ }
5343
+ offset += TAG_SIZE;
5344
+ }
5345
+ }
5346
+ view.setInt32(100, offset, true);
5347
+ for (let i = 0; i < options.surfaces.length; i += 1) {
5348
+ const written = buildSurface(buffer, offset, options.surfaces[i], numFrames);
5349
+ offset += written;
5350
+ }
5351
+ view.setInt32(104, offset, true);
5352
+ return buffer;
5353
+ }
5354
+
5355
+ // src/engine/builders/pakBuilder.ts
5356
+ var HEADER_SIZE3 = 12;
5357
+ var DIRECTORY_ENTRY_SIZE = 64;
5358
+ function buildPak(entries) {
5359
+ let offset = HEADER_SIZE3;
5360
+ const fileBuffers = [];
5361
+ const directory = new Uint8Array(entries.length * DIRECTORY_ENTRY_SIZE);
5362
+ const dirView = new DataView(directory.buffer);
5363
+ entries.forEach((entry, index) => {
5364
+ const data = entry.data;
5365
+ fileBuffers.push(data);
5366
+ const nameBytes = new TextEncoder().encode(entry.path);
5367
+ directory.set(nameBytes.slice(0, 56), index * DIRECTORY_ENTRY_SIZE);
5368
+ dirView.setInt32(index * DIRECTORY_ENTRY_SIZE + 56, offset, true);
5369
+ dirView.setInt32(index * DIRECTORY_ENTRY_SIZE + 60, data.byteLength, true);
5370
+ offset += data.byteLength;
5371
+ });
5372
+ const directoryOffset = offset;
5373
+ const directoryLength = directory.byteLength;
5374
+ const totalSize = HEADER_SIZE3 + fileBuffers.reduce((sum, buf) => sum + buf.byteLength, 0) + directoryLength;
5375
+ const buffer = new ArrayBuffer(totalSize);
5376
+ const view = new DataView(buffer);
5377
+ const writer = new Uint8Array(buffer);
5378
+ writer.set([80, 65, 67, 75]);
5379
+ view.setInt32(4, directoryOffset, true);
5380
+ view.setInt32(8, directoryLength, true);
5381
+ let cursor = HEADER_SIZE3;
5382
+ for (const data of fileBuffers) {
5383
+ writer.set(data, cursor);
5384
+ cursor += data.byteLength;
5385
+ }
5386
+ writer.set(directory, directoryOffset);
5387
+ return buffer;
5388
+ }
5389
+ function textData(text) {
5390
+ return new TextEncoder().encode(text);
5391
+ }
5392
+
5393
+ // src/engine/builders/pcxBuilder.ts
5394
+ function buildPcx(options) {
5395
+ const { width, height } = options;
5396
+ const headerSize = 128;
5397
+ const paletteSize = 769;
5398
+ const encodedPixels = [];
5399
+ for (const value of options.pixels) {
5400
+ if (value >= 192) {
5401
+ encodedPixels.push(193, value);
5402
+ } else {
5403
+ encodedPixels.push(value);
5404
+ }
5405
+ }
5406
+ const imageSize = encodedPixels.length;
5407
+ const buffer = new ArrayBuffer(headerSize + imageSize + paletteSize);
5408
+ const view = new DataView(buffer);
5409
+ view.setUint8(0, 10);
5410
+ view.setUint8(1, 5);
5411
+ view.setUint8(2, 1);
5412
+ view.setUint8(3, 8);
5413
+ view.setUint16(4, 0, true);
5414
+ view.setUint16(6, 0, true);
5415
+ view.setUint16(8, width - 1, true);
5416
+ view.setUint16(10, height - 1, true);
5417
+ view.setUint16(66, width, true);
5418
+ const encoded = new Uint8Array(buffer, headerSize, imageSize);
5419
+ encoded.set(encodedPixels);
5420
+ const paletteMarkerOffset = headerSize + imageSize;
5421
+ view.setUint8(paletteMarkerOffset, 12);
5422
+ const palette = new Uint8Array(buffer, paletteMarkerOffset + 1, 768);
5423
+ palette.fill(0);
5424
+ if (options.palette) {
5425
+ palette.set(options.palette.subarray(0, 768));
5426
+ } else {
5427
+ for (let i = 0; i < 256; i += 1) {
5428
+ palette[i * 3] = i;
5429
+ palette[i * 3 + 1] = 255 - i;
5430
+ palette[i * 3 + 2] = i;
5431
+ }
5432
+ }
5433
+ return buffer;
5434
+ }
5435
+
5436
+ // src/engine/builders/walBuilder.ts
5437
+ function buildWal(options) {
5438
+ const { width, height } = options;
5439
+ const headerSize = 100;
5440
+ const mipSizes = [
5441
+ width * height,
5442
+ Math.max(1, (width >> 1) * (height >> 1)),
5443
+ Math.max(1, (width >> 2) * (height >> 2)),
5444
+ Math.max(1, (width >> 3) * (height >> 3))
5445
+ ];
5446
+ const totalSize = headerSize + mipSizes.reduce((a, b) => a + b, 0);
5447
+ const buffer = new ArrayBuffer(totalSize);
5448
+ const view = new DataView(buffer);
5449
+ const encoder = new TextEncoder();
5450
+ new Uint8Array(buffer, 0, 32).set(encoder.encode(options.name));
5451
+ view.setInt32(32, width, true);
5452
+ view.setInt32(36, height, true);
5453
+ let offset = headerSize;
5454
+ mipSizes.forEach((size, index) => {
5455
+ view.setInt32(40 + index * 4, offset, true);
5456
+ const data = new Uint8Array(buffer, offset, size);
5457
+ for (let i = 0; i < size; i += 1) {
5458
+ data[i] = (i + index) % 256;
5459
+ }
5460
+ offset += size;
5461
+ });
5462
+ new Uint8Array(buffer, 56, 32).set(encoder.encode(options.name + "_anim"));
5463
+ view.setInt32(88, 0, true);
5464
+ view.setInt32(92, 0, true);
5465
+ view.setInt32(96, 0, true);
5466
+ return buffer;
5467
+ }
5468
+
5469
+ // src/engine/builders/wavBuilder.ts
5470
+ function buildWav(options) {
5471
+ const bitsPerSample = options.bitsPerSample ?? 16;
5472
+ const bytesPerSample = bitsPerSample / 8;
5473
+ const frameCount = options.samples.length / options.channels;
5474
+ const dataSize = frameCount * options.channels * bytesPerSample;
5475
+ const buffer = new ArrayBuffer(44 + dataSize);
5476
+ const view = new DataView(buffer);
5477
+ const writeString2 = (offset2, text) => {
5478
+ new Uint8Array(buffer, offset2, text.length).set(new TextEncoder().encode(text));
5479
+ };
5480
+ writeString2(0, "RIFF");
5481
+ view.setUint32(4, 36 + dataSize, true);
5482
+ writeString2(8, "WAVE");
5483
+ writeString2(12, "fmt ");
5484
+ view.setUint32(16, 16, true);
5485
+ view.setUint16(20, 1, true);
5486
+ view.setUint16(22, options.channels, true);
5487
+ view.setUint32(24, options.sampleRate, true);
5488
+ view.setUint32(28, options.sampleRate * options.channels * bytesPerSample, true);
5489
+ view.setUint16(32, options.channels * bytesPerSample, true);
5490
+ view.setUint16(34, bitsPerSample, true);
5491
+ writeString2(36, "data");
5492
+ view.setUint32(40, dataSize, true);
5493
+ let offset = 44;
5494
+ for (let i = 0; i < options.samples.length; i += 1) {
5495
+ const sample = options.samples[i];
5496
+ if (bitsPerSample === 8) {
5497
+ view.setUint8(offset, Math.max(0, Math.min(255, Math.round(sample * 128 + 128))));
5498
+ offset += 1;
5499
+ } else if (bitsPerSample === 16) {
5500
+ view.setInt16(offset, Math.round(sample * 32767), true);
5501
+ offset += 2;
5502
+ } else {
5503
+ throw new Error("Unsupported bit depth for builder");
5504
+ }
5505
+ }
5506
+ return buffer;
5507
+ }
5508
+
4913
5509
  // src/client/helpers/view.ts
4914
5510
  import { vec3 } from "gl-matrix";
4915
5511
  import { Camera } from "@quake2ts/engine";
@@ -5587,6 +6183,13 @@ export {
5587
6183
  ShaderProgram,
5588
6184
  VertexBuffer,
5589
6185
  ZERO_VEC3,
6186
+ buildMd2,
6187
+ buildMd3,
6188
+ buildPak,
6189
+ buildPcx,
6190
+ buildTestBsp,
6191
+ buildWal,
6192
+ buildWav,
5590
6193
  captureAudioEvents,
5591
6194
  captureCanvasDrawCalls,
5592
6195
  captureFramebufferAsPNG,
@@ -5770,6 +6373,7 @@ export {
5770
6373
  createVisualTestScenario,
5771
6374
  createWebGLPlaywrightSetup,
5772
6375
  createWebGLRenderTestSetup,
6376
+ encodedVisForClusters,
5773
6377
  expectAnimationSnapshot,
5774
6378
  expectNoDoubleTransform,
5775
6379
  expectRendererCalls,
@@ -5800,6 +6404,7 @@ export {
5800
6404
  renderAndExpectSnapshot,
5801
6405
  restoreSaveGameSnapshot,
5802
6406
  runComputeAndReadback,
6407
+ runLengthVisRow,
5803
6408
  savePNG,
5804
6409
  serializeUserInfo,
5805
6410
  setupBrowserEnvironment,
@@ -5836,6 +6441,7 @@ export {
5836
6441
  testPipelineRendering,
5837
6442
  testWebGLAnimation,
5838
6443
  testWebGLRenderer,
6444
+ textData,
5839
6445
  throttleBandwidth,
5840
6446
  verifySmoothing,
5841
6447
  verifySnapshotConsistency,