@forgeax/engine-geometry 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +215 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/asset-owner.unit.test.d.ts +2 -0
  5. package/dist/__tests__/asset-owner.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/dim2.test.d.ts +2 -0
  7. package/dist/__tests__/dim2.test.d.ts.map +1 -0
  8. package/dist/__tests__/geometry.unit.test.d.ts +2 -0
  9. package/dist/__tests__/geometry.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/vertex-attribute-layout-owner.unit.test.d.ts +2 -0
  11. package/dist/__tests__/vertex-attribute-layout-owner.unit.test.d.ts.map +1 -0
  12. package/dist/assets/mesh-binary.d.ts +4 -0
  13. package/dist/assets/mesh-binary.d.ts.map +1 -0
  14. package/dist/assets/mesh-decoder.d.ts +6 -0
  15. package/dist/assets/mesh-decoder.d.ts.map +1 -0
  16. package/dist/assets/primitive-mesh.d.ts +5 -0
  17. package/dist/assets/primitive-mesh.d.ts.map +1 -0
  18. package/dist/box.d.ts +41 -0
  19. package/dist/box.d.ts.map +1 -0
  20. package/dist/capsule.d.ts +14 -0
  21. package/dist/capsule.d.ts.map +1 -0
  22. package/dist/cone.d.ts +4 -0
  23. package/dist/cone.d.ts.map +1 -0
  24. package/dist/cylinder.d.ts +4 -0
  25. package/dist/cylinder.d.ts.map +1 -0
  26. package/dist/dim2.d.ts +73 -0
  27. package/dist/dim2.d.ts.map +1 -0
  28. package/dist/index.d.ts +14 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.mjs +2000 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/dist/plane.d.ts +4 -0
  33. package/dist/plane.d.ts.map +1 -0
  34. package/dist/sphere.d.ts +4 -0
  35. package/dist/sphere.d.ts.map +1 -0
  36. package/dist/tangent.d.ts +34 -0
  37. package/dist/tangent.d.ts.map +1 -0
  38. package/dist/torus.d.ts +4 -0
  39. package/dist/torus.d.ts.map +1 -0
  40. package/dist/vertex-attribute-layout.d.ts +90 -0
  41. package/dist/vertex-attribute-layout.d.ts.map +1 -0
  42. package/package.json +59 -0
  43. package/src/__tests__/asset-owner.unit.test.ts +97 -0
  44. package/src/__tests__/dim2.test.ts +132 -0
  45. package/src/__tests__/geometry.unit.test.ts +1223 -0
  46. package/src/__tests__/vertex-attribute-layout-owner.unit.test.ts +181 -0
  47. package/src/assets/mesh-binary.ts +421 -0
  48. package/src/assets/mesh-decoder.ts +97 -0
  49. package/src/assets/primitive-mesh.ts +36 -0
  50. package/src/box.ts +437 -0
  51. package/src/capsule.ts +145 -0
  52. package/src/cone.ts +26 -0
  53. package/src/cylinder.ts +180 -0
  54. package/src/dim2.ts +505 -0
  55. package/src/index.ts +52 -0
  56. package/src/plane.ts +78 -0
  57. package/src/sphere.ts +82 -0
  58. package/src/tangent.ts +320 -0
  59. package/src/torus.ts +83 -0
  60. package/src/vertex-attribute-layout.ts +503 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,2000 @@
1
+ import { ok, countUvSets, AssetError, ASSET_ERROR_HINTS, err } from '@forgeax/engine-types';
2
+ import { decodeMeshBinHeader, MESH_BIN_HEADER_V4_BYTES, MESH_BIN_VERSION } from '@forgeax/engine-pack';
3
+ import { box3, circle2, box2 } from '@forgeax/engine-math';
4
+
5
+ // src/assets/mesh-decoder.ts
6
+ var EPSILON = 1e-8;
7
+ function tangentInputError(field, value, reason) {
8
+ return new AssetError({
9
+ code: "asset-parse-failed",
10
+ expected: `valid tangent topology: ${reason}`,
11
+ hint: ASSET_ERROR_HINTS["asset-parse-failed"],
12
+ detail: { field, value, reason }
13
+ });
14
+ }
15
+ function preflightTangentInput(positions, normals, uvs, indices) {
16
+ if (positions.length % 3 !== 0) {
17
+ return err(
18
+ tangentInputError(
19
+ "positions",
20
+ positions.length,
21
+ "positions.length must be divisible by the position stride of 3"
22
+ )
23
+ );
24
+ }
25
+ const vertexCount = positions.length / 3;
26
+ if (normals.length !== vertexCount * 3) {
27
+ return err(
28
+ tangentInputError(
29
+ "normals",
30
+ normals.length,
31
+ `normals.length must equal vertexCount * 3 (${vertexCount * 3})`
32
+ )
33
+ );
34
+ }
35
+ if (uvs.length !== vertexCount * 2) {
36
+ return err(
37
+ tangentInputError(
38
+ "uvs",
39
+ uvs.length,
40
+ `uvs.length must equal vertexCount * 2 (${vertexCount * 2})`
41
+ )
42
+ );
43
+ }
44
+ if (indices === void 0) {
45
+ if (vertexCount % 3 !== 0) {
46
+ return err(
47
+ tangentInputError(
48
+ "positions",
49
+ vertexCount,
50
+ "non-indexed vertexCount must be divisible by the triangle size of 3"
51
+ )
52
+ );
53
+ }
54
+ return ok(vertexCount);
55
+ }
56
+ if (indices.length % 3 !== 0) {
57
+ return err(
58
+ tangentInputError(
59
+ "indices",
60
+ indices.length,
61
+ "indices.length must be divisible by the triangle size of 3"
62
+ )
63
+ );
64
+ }
65
+ for (let indexPosition = 0; indexPosition < indices.length; indexPosition++) {
66
+ const index = indices[indexPosition];
67
+ if (index === void 0 || !Number.isInteger(index) || index < 0 || index >= vertexCount) {
68
+ return err(
69
+ tangentInputError(
70
+ "indices",
71
+ index ?? -1,
72
+ `indices[${indexPosition}] must be an integer in [0, ${vertexCount})`
73
+ )
74
+ );
75
+ }
76
+ }
77
+ return ok(vertexCount);
78
+ }
79
+ function computeTangentVec4(positions, normals, uvs, indices) {
80
+ const preflight = preflightTangentInput(positions, normals, uvs, indices);
81
+ if (!preflight.ok) return preflight;
82
+ const vertexCount = preflight.value;
83
+ const accumT = new Float32Array(vertexCount * 3);
84
+ const accumSign = new Float32Array(vertexCount);
85
+ const triangleCount = indices !== void 0 ? indices.length / 3 : vertexCount / 3;
86
+ for (let tri = 0; tri < triangleCount; tri++) {
87
+ const i0 = indices !== void 0 ? indices[tri * 3] ?? 0 : tri * 3;
88
+ const i1 = indices !== void 0 ? indices[tri * 3 + 1] ?? 0 : tri * 3 + 1;
89
+ const i2 = indices !== void 0 ? indices[tri * 3 + 2] ?? 0 : tri * 3 + 2;
90
+ const p0x = positions[i0 * 3] ?? 0;
91
+ const p0y = positions[i0 * 3 + 1] ?? 0;
92
+ const p0z = positions[i0 * 3 + 2] ?? 0;
93
+ const p1x = positions[i1 * 3] ?? 0;
94
+ const p1y = positions[i1 * 3 + 1] ?? 0;
95
+ const p1z = positions[i1 * 3 + 2] ?? 0;
96
+ const p2x = positions[i2 * 3] ?? 0;
97
+ const p2y = positions[i2 * 3 + 1] ?? 0;
98
+ const p2z = positions[i2 * 3 + 2] ?? 0;
99
+ const u0 = uvs[i0 * 2] ?? 0;
100
+ const v0 = uvs[i0 * 2 + 1] ?? 0;
101
+ const u1 = uvs[i1 * 2] ?? 0;
102
+ const v1 = uvs[i1 * 2 + 1] ?? 0;
103
+ const u2 = uvs[i2 * 2] ?? 0;
104
+ const v2 = uvs[i2 * 2 + 1] ?? 0;
105
+ const dP1x = p1x - p0x;
106
+ const dP1y = p1y - p0y;
107
+ const dP1z = p1z - p0z;
108
+ const dP2x = p2x - p0x;
109
+ const dP2y = p2y - p0y;
110
+ const dP2z = p2z - p0z;
111
+ const dU1 = u1 - u0;
112
+ const dV1 = v1 - v0;
113
+ const dU2 = u2 - u0;
114
+ const dV2 = v2 - v0;
115
+ const det = dU1 * dV2 - dU2 * dV1;
116
+ if (Math.abs(det) < EPSILON) {
117
+ continue;
118
+ }
119
+ const invDet = 1 / det;
120
+ const tx = invDet * (dV2 * dP1x - dV1 * dP2x);
121
+ const ty = invDet * (dV2 * dP1y - dV1 * dP2y);
122
+ const tz = invDet * (dV2 * dP1z - dV1 * dP2z);
123
+ const cx = dP1y * dP2z - dP1z * dP2y;
124
+ const cy = dP1z * dP2x - dP1x * dP2z;
125
+ const cz = dP1x * dP2y - dP1y * dP2x;
126
+ const faceArea = 0.5 * Math.sqrt(cx * cx + cy * cy + cz * cz);
127
+ if (faceArea < EPSILON) continue;
128
+ const signDet = det >= 0 ? 1 : -1;
129
+ const signedWeight = faceArea * signDet;
130
+ for (const vi of [i0, i1, i2]) {
131
+ accumT[vi * 3] = (accumT[vi * 3] ?? 0) + tx * faceArea;
132
+ accumT[vi * 3 + 1] = (accumT[vi * 3 + 1] ?? 0) + ty * faceArea;
133
+ accumT[vi * 3 + 2] = (accumT[vi * 3 + 2] ?? 0) + tz * faceArea;
134
+ accumSign[vi] = (accumSign[vi] ?? 0) + signedWeight;
135
+ }
136
+ }
137
+ const out = new Float32Array(vertexCount * 4);
138
+ for (let v = 0; v < vertexCount; v++) {
139
+ let tx = accumT[v * 3] ?? 0;
140
+ let ty = accumT[v * 3 + 1] ?? 0;
141
+ let tz = accumT[v * 3 + 2] ?? 0;
142
+ const nx = normals[v * 3] ?? 0;
143
+ const ny = normals[v * 3 + 1] ?? 0;
144
+ const nz = normals[v * 3 + 2] ?? 0;
145
+ const tLen = Math.sqrt(tx * tx + ty * ty + tz * tz);
146
+ if (tLen < EPSILON) {
147
+ const ax = Math.abs(nx);
148
+ const ay = Math.abs(ny);
149
+ const az = Math.abs(nz);
150
+ let rx = 1;
151
+ let ry = 0;
152
+ let rz = 0;
153
+ if (ax <= ay && ax <= az) {
154
+ rx = 1;
155
+ ry = 0;
156
+ rz = 0;
157
+ } else if (ay <= ax && ay <= az) {
158
+ rx = 0;
159
+ ry = 1;
160
+ rz = 0;
161
+ } else {
162
+ rx = 0;
163
+ ry = 0;
164
+ rz = 1;
165
+ }
166
+ tx = ny * rz - nz * ry;
167
+ ty = nz * rx - nx * rz;
168
+ tz = nx * ry - ny * rx;
169
+ const fLen = Math.sqrt(tx * tx + ty * ty + tz * tz) || 1;
170
+ tx /= fLen;
171
+ ty /= fLen;
172
+ tz /= fLen;
173
+ } else {
174
+ tx /= tLen;
175
+ ty /= tLen;
176
+ tz /= tLen;
177
+ }
178
+ const dotTN = tx * nx + ty * ny + tz * nz;
179
+ let gx = tx - dotTN * nx;
180
+ let gy = ty - dotTN * ny;
181
+ let gz = tz - dotTN * nz;
182
+ const gLen = Math.sqrt(gx * gx + gy * gy + gz * gz);
183
+ if (gLen < EPSILON) {
184
+ const ax = Math.abs(nx);
185
+ const ay = Math.abs(ny);
186
+ const az = Math.abs(nz);
187
+ let rx = 1;
188
+ let ry = 0;
189
+ let rz = 0;
190
+ if (ax <= ay && ax <= az) {
191
+ rx = 1;
192
+ ry = 0;
193
+ rz = 0;
194
+ } else if (ay <= ax && ay <= az) {
195
+ rx = 0;
196
+ ry = 1;
197
+ rz = 0;
198
+ } else {
199
+ rx = 0;
200
+ ry = 0;
201
+ rz = 1;
202
+ }
203
+ gx = ny * rz - nz * ry;
204
+ gy = nz * rx - nx * rz;
205
+ gz = nx * ry - ny * rx;
206
+ const fLen = Math.sqrt(gx * gx + gy * gy + gz * gz) || 1;
207
+ gx /= fLen;
208
+ gy /= fLen;
209
+ gz /= fLen;
210
+ } else {
211
+ gx /= gLen;
212
+ gy /= gLen;
213
+ gz /= gLen;
214
+ }
215
+ const accSign = accumSign[v] ?? 0;
216
+ const w = accSign >= 0 ? 1 : -1;
217
+ out[v * 4] = gx;
218
+ out[v * 4 + 1] = gy;
219
+ out[v * 4 + 2] = gz;
220
+ out[v * 4 + 3] = w;
221
+ }
222
+ return ok(out);
223
+ }
224
+ var ATTRIBUTE_FORMAT_MAP = {
225
+ position: "float32x3",
226
+ normal: "float32x3",
227
+ uv: "float32x2",
228
+ tangent: "float32x4",
229
+ skinIndex: "uint16x4",
230
+ skinWeight: "float32x4",
231
+ uv1: "float32x2",
232
+ uv2: "float32x2",
233
+ uv3: "float32x2",
234
+ uv4: "float32x2",
235
+ uv5: "float32x2",
236
+ uv6: "float32x2",
237
+ uv7: "float32x2",
238
+ color: "float32x4"
239
+ };
240
+ var ATTRIBUTE_BYTE_STRIDE = {
241
+ position: 12,
242
+ normal: 12,
243
+ uv: 8,
244
+ tangent: 16,
245
+ skinIndex: 8,
246
+ skinWeight: 16,
247
+ uv1: 8,
248
+ uv2: 8,
249
+ uv3: 8,
250
+ uv4: 8,
251
+ uv5: 8,
252
+ uv6: 8,
253
+ uv7: 8,
254
+ color: 16
255
+ };
256
+ var isAttributeKey = (key) => key in ATTRIBUTE_FORMAT_MAP;
257
+ var CANONICAL_KEYS = Object.keys(ATTRIBUTE_FORMAT_MAP).filter(isAttributeKey);
258
+ var UV_KEYS = CANONICAL_KEYS.filter(
259
+ (key) => key === "uv" || key.startsWith("uv")
260
+ );
261
+ function emitAliasEntries(entries, fromIndex, toIndex, aliasOffset, currentStride) {
262
+ for (let k = fromIndex; k < toIndex && k < UV_KEYS.length; k++) {
263
+ const uvKey = UV_KEYS[k];
264
+ entries.push({
265
+ key: uvKey,
266
+ shaderLocation: CANONICAL_KEYS.indexOf(uvKey),
267
+ offset: aliasOffset,
268
+ format: ATTRIBUTE_FORMAT_MAP[uvKey]
269
+ });
270
+ }
271
+ return fromIndex === 0 ? currentStride + ATTRIBUTE_BYTE_STRIDE.uv : currentStride;
272
+ }
273
+ function buildMeshAttributeMapForUvSets(uvSetCount) {
274
+ const map = {
275
+ position: new Float32Array(0),
276
+ normal: new Float32Array(0),
277
+ uv: new Float32Array(0),
278
+ tangent: new Float32Array(0)
279
+ };
280
+ for (let set = 1; set < uvSetCount && set < UV_KEYS.length; set++) {
281
+ map[UV_KEYS[set]] = new Float32Array(0);
282
+ }
283
+ return map;
284
+ }
285
+ function deriveVertexBufferLayout(map, opts) {
286
+ const shaderUvSetCount = opts?.shaderUvSetCount ?? 0;
287
+ const entries = [];
288
+ let offset = 0;
289
+ for (const key of CANONICAL_KEYS) {
290
+ if (map[key] === void 0) continue;
291
+ entries.push({
292
+ key,
293
+ shaderLocation: CANONICAL_KEYS.indexOf(key),
294
+ offset,
295
+ format: ATTRIBUTE_FORMAT_MAP[key]
296
+ });
297
+ offset += ATTRIBUTE_BYTE_STRIDE[key];
298
+ }
299
+ const present = entries.length;
300
+ if (shaderUvSetCount > 0) {
301
+ const meshUvSetCount = countUvSets(map);
302
+ if (shaderUvSetCount > meshUvSetCount) {
303
+ const lastUvIndex = meshUvSetCount - 1;
304
+ const lastUvKey = lastUvIndex >= 0 && lastUvIndex < UV_KEYS.length ? UV_KEYS[lastUvIndex] : void 0;
305
+ const aliasOffset = lastUvKey !== void 0 ? CANONICAL_KEYS.indexOf(lastUvKey) : -1;
306
+ const aliasByteOffset = aliasOffset >= 0 ? entries.find((e) => e.shaderLocation === aliasOffset)?.offset ?? 0 : offset;
307
+ offset = emitAliasEntries(entries, meshUvSetCount, shaderUvSetCount, aliasByteOffset, offset);
308
+ }
309
+ }
310
+ if (present === 0 && shaderUvSetCount === 0) return [];
311
+ entries.sort((a, b) => a.shaderLocation - b.shaderLocation);
312
+ return [
313
+ {
314
+ arrayStride: offset,
315
+ attributes: entries.map(({ shaderLocation, offset: entryOffset, format }) => ({
316
+ shaderLocation,
317
+ offset: entryOffset,
318
+ format
319
+ }))
320
+ }
321
+ ];
322
+ }
323
+ function deriveVertexBufferLayoutFromProjection(projection, opts) {
324
+ const entries = projection.attributes.map(({ key, shaderLocation, offset, format }) => ({
325
+ key,
326
+ shaderLocation,
327
+ offset,
328
+ format
329
+ }));
330
+ let arrayStride = projection.arrayStride;
331
+ const shaderUvSetCount = opts?.shaderUvSetCount ?? 0;
332
+ if (shaderUvSetCount > 0) {
333
+ const meshUvSetCount = entries.filter(
334
+ (entry) => entry.key === "uv" || entry.key.startsWith("uv")
335
+ ).length;
336
+ if (shaderUvSetCount > meshUvSetCount) {
337
+ const lastUv = entries.filter((entry) => entry.key === "uv" || entry.key.startsWith("uv")).at(-1);
338
+ const aliasOffset = lastUv?.offset ?? arrayStride;
339
+ for (let index = meshUvSetCount; index < shaderUvSetCount && index < UV_KEYS.length; index += 1) {
340
+ const key = UV_KEYS[index];
341
+ if (key === void 0) continue;
342
+ entries.push({
343
+ key,
344
+ shaderLocation: CANONICAL_KEYS.indexOf(key),
345
+ offset: aliasOffset,
346
+ format: ATTRIBUTE_FORMAT_MAP[key]
347
+ });
348
+ }
349
+ if (meshUvSetCount === 0) arrayStride += ATTRIBUTE_BYTE_STRIDE.uv;
350
+ }
351
+ }
352
+ entries.sort((a, b) => a.shaderLocation - b.shaderLocation);
353
+ return projection.attributes.length === 0 ? [] : [
354
+ {
355
+ arrayStride,
356
+ attributes: entries.map(({ shaderLocation, offset, format }) => ({
357
+ shaderLocation,
358
+ offset,
359
+ format
360
+ }))
361
+ }
362
+ ];
363
+ }
364
+ function bytesForFormat(format) {
365
+ if (format === "uint16x4" || format === "float32x2") return 8;
366
+ if (format === "float32x3") return 12;
367
+ return 16;
368
+ }
369
+ function storageOf(value) {
370
+ if (value instanceof ArrayBuffer) return "array-buffer";
371
+ if (value instanceof Float32Array) return "float32";
372
+ if (value instanceof Uint16Array) return "uint16";
373
+ return "other";
374
+ }
375
+ function elementView(value, format) {
376
+ if (value instanceof Float32Array || value instanceof Uint16Array) return value;
377
+ if (value instanceof ArrayBuffer) {
378
+ return format === "uint16x4" ? new Uint16Array(value) : new Float32Array(value);
379
+ }
380
+ return void 0;
381
+ }
382
+ function fnv1a(input) {
383
+ let hash = 2166136261;
384
+ for (let i = 0; i < input.length; i++) {
385
+ hash ^= input.charCodeAt(i);
386
+ hash = Math.imul(hash, 16777619);
387
+ }
388
+ return `vlp-v1-${(hash >>> 0).toString(16).padStart(8, "0")}`;
389
+ }
390
+ function deriveVertexLayoutProjection(map) {
391
+ const attributes = [];
392
+ let offset = 0;
393
+ let mask = 0;
394
+ for (const key of CANONICAL_KEYS) {
395
+ const value = map[key];
396
+ if (value === void 0) continue;
397
+ const format = ATTRIBUTE_FORMAT_MAP[key];
398
+ const byteLength = bytesForFormat(format);
399
+ attributes.push({
400
+ key,
401
+ shaderLocation: CANONICAL_KEYS.indexOf(key),
402
+ offset,
403
+ format,
404
+ byteLength
405
+ });
406
+ mask |= 1 << CANONICAL_KEYS.indexOf(key);
407
+ offset += byteLength;
408
+ }
409
+ const digestInput = [
410
+ "1",
411
+ String(mask),
412
+ String(offset),
413
+ ...attributes.map(
414
+ (entry) => `${entry.key},${entry.shaderLocation},${entry.offset},${entry.format}`
415
+ )
416
+ ].join("|");
417
+ return Object.freeze({
418
+ schemaVersion: 1,
419
+ attributes: Object.freeze(attributes),
420
+ mask,
421
+ arrayStride: offset,
422
+ digest: fnv1a(digestInput)
423
+ });
424
+ }
425
+ var VertexAttributePackError = class extends AssetError {
426
+ constructor(detail) {
427
+ super({
428
+ code: "asset-invalid-value",
429
+ expected: "canonical vertex attributes with matching storage and cardinality",
430
+ hint: ASSET_ERROR_HINTS["asset-invalid-value"],
431
+ detail
432
+ });
433
+ }
434
+ };
435
+ function deriveVertexLayoutProjectionFromMask(mask) {
436
+ const knownMask = (1 << CANONICAL_KEYS.length) - 1;
437
+ const unsignedMask = Number.isInteger(mask) && mask >= 0 ? mask >>> 0 : 4294967295;
438
+ const unknownMask = unsignedMask & ~knownMask;
439
+ if (mask === 0) {
440
+ return err({
441
+ code: "vertex-layout-mask-invalid",
442
+ expected: "a non-empty canonical vertex attribute mask",
443
+ hint: "re-cook the mesh-bin payload from MeshAsset.attributes",
444
+ detail: { mask, knownMask, unknownMask, reason: "empty" }
445
+ });
446
+ }
447
+ if (!Number.isInteger(mask) || mask < 0 || unknownMask !== 0) {
448
+ return err({
449
+ code: "vertex-layout-mask-invalid",
450
+ expected: `a mask using only canonical bits 0..${CANONICAL_KEYS.length - 1}`,
451
+ hint: "re-cook the mesh-bin payload from MeshAsset.attributes",
452
+ detail: { mask, knownMask, unknownMask, reason: "unknown-bits" }
453
+ });
454
+ }
455
+ const map = {};
456
+ for (let index = 0; index < CANONICAL_KEYS.length; index += 1) {
457
+ if ((mask & 1 << index) === 0) continue;
458
+ const key = CANONICAL_KEYS[index];
459
+ if (key !== void 0)
460
+ map[key] = key === "skinIndex" ? new Uint16Array(0) : new Float32Array(0);
461
+ }
462
+ return ok(deriveVertexLayoutProjection(map));
463
+ }
464
+ function packInterleavedVertexAttributes(map, vertexCount) {
465
+ const invalid2 = (detail) => err(new VertexAttributePackError(detail));
466
+ if (!Number.isInteger(vertexCount) || vertexCount < 0) {
467
+ return invalid2({ field: "vertexCount", reason: "vertex-count-invalid", actual: vertexCount });
468
+ }
469
+ const projection = deriveVertexLayoutProjection(map);
470
+ if (projection.attributes.length === 0) {
471
+ return invalid2({ field: "attributes", reason: "attributes-empty", actualCount: 0 });
472
+ }
473
+ const output = new ArrayBuffer(projection.arrayStride * vertexCount);
474
+ const outputFloats = new Float32Array(output);
475
+ const outputU16 = new Uint16Array(output);
476
+ for (const entry of projection.attributes) {
477
+ const sourceValue = map[entry.key];
478
+ if (sourceValue === void 0) continue;
479
+ const uint16 = entry.format === "uint16x4";
480
+ const components = entry.byteLength / (uint16 ? 2 : 4);
481
+ const bytesPerComponent = uint16 ? 2 : 4;
482
+ if (sourceValue instanceof ArrayBuffer && sourceValue.byteLength % bytesPerComponent !== 0) {
483
+ return invalid2({
484
+ field: entry.key,
485
+ reason: "attribute-cardinality-mismatch",
486
+ vertexCount,
487
+ componentsPerVertex: components,
488
+ expectedLength: vertexCount * components,
489
+ actualLength: sourceValue.byteLength / bytesPerComponent
490
+ });
491
+ }
492
+ const source = elementView(sourceValue, entry.format);
493
+ if (source === void 0 || (uint16 ? storageOf(sourceValue) !== "uint16" && storageOf(sourceValue) !== "array-buffer" : storageOf(sourceValue) !== "float32" && storageOf(sourceValue) !== "array-buffer")) {
494
+ return invalid2({
495
+ field: entry.key,
496
+ reason: "attribute-storage-invalid",
497
+ expectedStorage: uint16 ? "uint16" : "float32",
498
+ actualStorage: storageOf(sourceValue)
499
+ });
500
+ }
501
+ const expectedLength = vertexCount * components;
502
+ if (source.length !== expectedLength) {
503
+ return invalid2({
504
+ field: entry.key,
505
+ reason: "attribute-cardinality-mismatch",
506
+ vertexCount,
507
+ componentsPerVertex: components,
508
+ expectedLength,
509
+ actualLength: source.length
510
+ });
511
+ }
512
+ if (entry.key === "color") {
513
+ for (let elementIndex = 0; elementIndex < source.length; elementIndex += 1) {
514
+ const component = source[elementIndex];
515
+ if (!Number.isFinite(component)) {
516
+ return invalid2({
517
+ field: "color",
518
+ reason: "attribute-non-finite",
519
+ elementIndex,
520
+ actual: Number.isNaN(component) ? "nan" : component === Number.POSITIVE_INFINITY ? "positive-infinity" : "negative-infinity"
521
+ });
522
+ }
523
+ }
524
+ }
525
+ for (let vertex = 0; vertex < vertexCount; vertex += 1) {
526
+ for (let component = 0; component < components; component += 1) {
527
+ const sourceIndex = vertex * components + component;
528
+ const byteOffset = vertex * projection.arrayStride + entry.offset + component * (uint16 ? 2 : 4);
529
+ if (uint16) outputU16[byteOffset / 2] = Number(source[sourceIndex] ?? 0);
530
+ else outputFloats[byteOffset / 4] = Number(source[sourceIndex] ?? 0);
531
+ }
532
+ }
533
+ }
534
+ return ok(Object.freeze({ projection, vertices: outputFloats }));
535
+ }
536
+
537
+ // src/box.ts
538
+ var FACTORY_FLOATS_PER_VERTEX = 8;
539
+ var PROCEDURAL_FLOATS_PER_VERTEX = 12;
540
+ function interleavedInputError(field, value, reason) {
541
+ return new AssetError({
542
+ code: "asset-parse-failed",
543
+ expected: `valid interleaved triangle topology: ${reason}`,
544
+ hint: ASSET_ERROR_HINTS["asset-parse-failed"],
545
+ detail: { field, value, reason }
546
+ });
547
+ }
548
+ function buildAttributes(vertices, vertexCount) {
549
+ const positions = new Float32Array(vertexCount * 3);
550
+ const normals = new Float32Array(vertexCount * 3);
551
+ const uvs = new Float32Array(vertexCount * 2);
552
+ const tangents = new Float32Array(vertexCount * 4);
553
+ for (let i = 0; i < vertexCount; i++) {
554
+ const base = i * PROCEDURAL_FLOATS_PER_VERTEX;
555
+ positions[i * 3 + 0] = vertices[base + 0];
556
+ positions[i * 3 + 1] = vertices[base + 1];
557
+ positions[i * 3 + 2] = vertices[base + 2];
558
+ normals[i * 3 + 0] = vertices[base + 3];
559
+ normals[i * 3 + 1] = vertices[base + 4];
560
+ normals[i * 3 + 2] = vertices[base + 5];
561
+ uvs[i * 2 + 0] = vertices[base + 6];
562
+ uvs[i * 2 + 1] = vertices[base + 7];
563
+ tangents[i * 4 + 0] = vertices[base + 8];
564
+ tangents[i * 4 + 1] = vertices[base + 9];
565
+ tangents[i * 4 + 2] = vertices[base + 10];
566
+ tangents[i * 4 + 3] = vertices[base + 11];
567
+ }
568
+ const attrs = {
569
+ position: positions,
570
+ normal: normals,
571
+ uv: uvs,
572
+ tangent: tangents
573
+ };
574
+ deriveVertexBufferLayout(attrs);
575
+ return attrs;
576
+ }
577
+ function meshFromInterleaved(vertices, indices) {
578
+ if (vertices.length % FACTORY_FLOATS_PER_VERTEX !== 0) {
579
+ return err(
580
+ interleavedInputError(
581
+ "vertices",
582
+ vertices.length,
583
+ `vertices.length must be divisible by the interleaved stride of ${FACTORY_FLOATS_PER_VERTEX}`
584
+ )
585
+ );
586
+ }
587
+ const vertexCount = vertices.length / FACTORY_FLOATS_PER_VERTEX;
588
+ if (indices.length % 3 !== 0) {
589
+ return err(
590
+ interleavedInputError(
591
+ "indices",
592
+ indices.length,
593
+ "indices.length must be divisible by the triangle size of 3"
594
+ )
595
+ );
596
+ }
597
+ for (let indexPosition = 0; indexPosition < indices.length; indexPosition++) {
598
+ const index = indices[indexPosition];
599
+ if (index === void 0 || !Number.isInteger(index) || index < 0 || index >= vertexCount) {
600
+ return err(
601
+ interleavedInputError(
602
+ "indices",
603
+ index ?? -1,
604
+ `indices[${indexPosition}] must be an integer in [0, ${vertexCount})`
605
+ )
606
+ );
607
+ }
608
+ }
609
+ const positions = new Float32Array(vertexCount * 3);
610
+ const normals = new Float32Array(vertexCount * 3);
611
+ const uvs = new Float32Array(vertexCount * 2);
612
+ for (let i = 0; i < vertexCount; i++) {
613
+ const base = i * FACTORY_FLOATS_PER_VERTEX;
614
+ positions[i * 3 + 0] = vertices[base + 0];
615
+ positions[i * 3 + 1] = vertices[base + 1];
616
+ positions[i * 3 + 2] = vertices[base + 2];
617
+ normals[i * 3 + 0] = vertices[base + 3];
618
+ normals[i * 3 + 1] = vertices[base + 4];
619
+ normals[i * 3 + 2] = vertices[base + 5];
620
+ uvs[i * 2 + 0] = vertices[base + 6];
621
+ uvs[i * 2 + 1] = vertices[base + 7];
622
+ }
623
+ const tangentResult = computeTangentVec4(positions, normals, uvs, indices);
624
+ if (!tangentResult.ok) return tangentResult;
625
+ const tangents = tangentResult.value;
626
+ const expanded = new Float32Array(vertexCount * PROCEDURAL_FLOATS_PER_VERTEX);
627
+ for (let i = 0; i < vertexCount; i++) {
628
+ const dst = i * PROCEDURAL_FLOATS_PER_VERTEX;
629
+ const src = i * FACTORY_FLOATS_PER_VERTEX;
630
+ expanded[dst + 0] = vertices[src + 0];
631
+ expanded[dst + 1] = vertices[src + 1];
632
+ expanded[dst + 2] = vertices[src + 2];
633
+ expanded[dst + 3] = vertices[src + 3];
634
+ expanded[dst + 4] = vertices[src + 4];
635
+ expanded[dst + 5] = vertices[src + 5];
636
+ expanded[dst + 6] = vertices[src + 6];
637
+ expanded[dst + 7] = vertices[src + 7];
638
+ expanded[dst + 8] = tangents[i * 4];
639
+ expanded[dst + 9] = tangents[i * 4 + 1];
640
+ expanded[dst + 10] = tangents[i * 4 + 2];
641
+ expanded[dst + 11] = tangents[i * 4 + 3];
642
+ }
643
+ return ok({
644
+ kind: "mesh",
645
+ vertices: expanded,
646
+ indices,
647
+ attributes: buildAttributes(expanded, vertexCount),
648
+ submeshes: [
649
+ {
650
+ indexOffset: 0,
651
+ indexCount: indices.length,
652
+ vertexCount,
653
+ topology: "triangle-list",
654
+ materialSlot: 0
655
+ }
656
+ ],
657
+ materialSlots: [{ slotName: "Default" }],
658
+ // Procedural meshes carry their own local-space AABB: after feat-20260614
659
+ // (D-15) `allocSharedRef` stores the payload verbatim -- there is no
660
+ // `withMeshAabb` pass like the old `register`/`catalog` path -- so the cull
661
+ // + pick path can only read an AABB the POD already holds.
662
+ aabb: box3.fromPositions(box3.create(), positions)
663
+ });
664
+ }
665
+ function degenerate(detail) {
666
+ return new AssetError({
667
+ code: "asset-parse-failed",
668
+ expected: `all dimensions > 0; segments >= 1 (${detail})`,
669
+ hint: ASSET_ERROR_HINTS["asset-parse-failed"]
670
+ });
671
+ }
672
+ function createBoxGeometry(width, height, depth, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
673
+ if (width <= 0 || height <= 0 || depth <= 0) {
674
+ return err(degenerate(`width=${width}, height=${height}, depth=${depth}`));
675
+ }
676
+ const ws = widthSegments | 0;
677
+ const hs = heightSegments | 0;
678
+ const ds = depthSegments | 0;
679
+ if (ws < 1 || hs < 1 || ds < 1) {
680
+ return err(degenerate(`widthSegments=${ws}, heightSegments=${hs}, depthSegments=${ds}`));
681
+ }
682
+ const hw = width / 2;
683
+ const hh = height / 2;
684
+ const hd = depth / 2;
685
+ const faces = [
686
+ // +X face
687
+ {
688
+ uAxis: 2,
689
+ vAxis: 1,
690
+ wAxis: 0,
691
+ uSign: -1,
692
+ vSign: 1,
693
+ wSign: 1,
694
+ uSegs: ds,
695
+ vSegs: hs,
696
+ uSize: depth,
697
+ vSize: height,
698
+ wSize: width
699
+ },
700
+ // -X face
701
+ {
702
+ uAxis: 2,
703
+ vAxis: 1,
704
+ wAxis: 0,
705
+ uSign: 1,
706
+ vSign: 1,
707
+ wSign: -1,
708
+ uSegs: ds,
709
+ vSegs: hs,
710
+ uSize: depth,
711
+ vSize: height,
712
+ wSize: width
713
+ },
714
+ // +Y face
715
+ {
716
+ uAxis: 0,
717
+ vAxis: 2,
718
+ wAxis: 1,
719
+ uSign: 1,
720
+ vSign: 1,
721
+ wSign: 1,
722
+ uSegs: ws,
723
+ vSegs: ds,
724
+ uSize: width,
725
+ vSize: depth,
726
+ wSize: height
727
+ },
728
+ // -Y face
729
+ {
730
+ uAxis: 0,
731
+ vAxis: 2,
732
+ wAxis: 1,
733
+ uSign: 1,
734
+ vSign: -1,
735
+ wSign: -1,
736
+ uSegs: ws,
737
+ vSegs: ds,
738
+ uSize: width,
739
+ vSize: depth,
740
+ wSize: height
741
+ },
742
+ // +Z face
743
+ {
744
+ uAxis: 0,
745
+ vAxis: 1,
746
+ wAxis: 2,
747
+ uSign: 1,
748
+ vSign: 1,
749
+ wSign: 1,
750
+ uSegs: ws,
751
+ vSegs: hs,
752
+ uSize: width,
753
+ vSize: height,
754
+ wSize: depth
755
+ },
756
+ // -Z face
757
+ {
758
+ uAxis: 0,
759
+ vAxis: 1,
760
+ wAxis: 2,
761
+ uSign: -1,
762
+ vSign: 1,
763
+ wSign: -1,
764
+ uSegs: ws,
765
+ vSegs: hs,
766
+ uSize: width,
767
+ vSize: height,
768
+ wSize: depth
769
+ }
770
+ ];
771
+ let vertexCount = 0;
772
+ let indexCount = 0;
773
+ for (const f of faces) {
774
+ vertexCount += (f.uSegs + 1) * (f.vSegs + 1);
775
+ indexCount += f.uSegs * f.vSegs * 6;
776
+ }
777
+ const vertices = new Float32Array(vertexCount * FACTORY_FLOATS_PER_VERTEX);
778
+ const indices = new Uint32Array(indexCount);
779
+ let vIdx = 0;
780
+ let iIdx = 0;
781
+ const halves = [hw, hh, hd];
782
+ for (const f of faces) {
783
+ const vStart = vIdx;
784
+ const halfU = halves[f.uAxis];
785
+ const halfV = halves[f.vAxis];
786
+ const halfW = halves[f.wAxis];
787
+ for (let j = 0; j <= f.vSegs; j++) {
788
+ for (let i = 0; i <= f.uSegs; i++) {
789
+ const uCoord = (i / f.uSegs * f.uSize - f.uSize / 2) * f.uSign;
790
+ const vCoord = (j / f.vSegs * f.vSize - f.vSize / 2) * f.vSign;
791
+ const pos = [0, 0, 0];
792
+ pos[f.uAxis] = uCoord / f.uSize * halfU * 2;
793
+ pos[f.vAxis] = vCoord / f.vSize * halfV * 2;
794
+ pos[f.wAxis] = halfW * f.wSign;
795
+ const normal = [0, 0, 0];
796
+ normal[f.wAxis] = f.wSign;
797
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
798
+ vertices[base + 0] = pos[0];
799
+ vertices[base + 1] = pos[1];
800
+ vertices[base + 2] = pos[2];
801
+ vertices[base + 3] = normal[0];
802
+ vertices[base + 4] = normal[1];
803
+ vertices[base + 5] = normal[2];
804
+ vertices[base + 6] = i / f.uSegs;
805
+ vertices[base + 7] = j / f.vSegs;
806
+ vIdx++;
807
+ }
808
+ }
809
+ const isCyclic = (f.vAxis - f.uAxis + 3) % 3 === 1 && (f.wAxis - f.vAxis + 3) % 3 === 1;
810
+ const levi = isCyclic ? 1 : -1;
811
+ const ccwOutward = levi * f.uSign * f.vSign * f.wSign > 0;
812
+ for (let j = 0; j < f.vSegs; j++) {
813
+ for (let i = 0; i < f.uSegs; i++) {
814
+ const a = vStart + j * (f.uSegs + 1) + i;
815
+ const b = vStart + j * (f.uSegs + 1) + i + 1;
816
+ const c = vStart + (j + 1) * (f.uSegs + 1) + i;
817
+ const d = vStart + (j + 1) * (f.uSegs + 1) + i + 1;
818
+ if (ccwOutward) {
819
+ indices[iIdx++] = a;
820
+ indices[iIdx++] = b;
821
+ indices[iIdx++] = d;
822
+ indices[iIdx++] = a;
823
+ indices[iIdx++] = d;
824
+ indices[iIdx++] = c;
825
+ } else {
826
+ indices[iIdx++] = a;
827
+ indices[iIdx++] = d;
828
+ indices[iIdx++] = b;
829
+ indices[iIdx++] = a;
830
+ indices[iIdx++] = c;
831
+ indices[iIdx++] = d;
832
+ }
833
+ }
834
+ }
835
+ }
836
+ return meshFromInterleaved(vertices, indices);
837
+ }
838
+
839
+ // src/assets/mesh-binary.ts
840
+ function parseGuid(value) {
841
+ const compact = value.replaceAll("-", "");
842
+ if (!/^[0-9a-f]{32}$/i.test(compact)) return void 0;
843
+ const bytes = new Uint8Array(16);
844
+ for (let index = 0; index < bytes.length; index += 1) {
845
+ const pair = compact.slice(index * 2, index * 2 + 2);
846
+ const parsed = Number.parseInt(pair, 16);
847
+ if (!Number.isInteger(parsed)) return void 0;
848
+ bytes[index] = parsed;
849
+ }
850
+ return bytes;
851
+ }
852
+ function floatArray(value) {
853
+ if (value instanceof Float32Array) return value;
854
+ if (Array.isArray(value)) return new Float32Array(value);
855
+ return void 0;
856
+ }
857
+ function indexArray(value) {
858
+ if (value instanceof Uint16Array || value instanceof Uint32Array) {
859
+ return value.length === 0 ? void 0 : value;
860
+ }
861
+ if (!Array.isArray(value)) return void 0;
862
+ if (value.length === 0) return void 0;
863
+ return value.some((entry) => entry > 65535) ? new Uint32Array(value) : new Uint16Array(value);
864
+ }
865
+ function integerArray(value) {
866
+ if (value instanceof Uint16Array) return value;
867
+ if (Array.isArray(value)) return new Uint16Array(value);
868
+ return void 0;
869
+ }
870
+ function morphTargets(value) {
871
+ if (value === void 0) return void 0;
872
+ if (!Array.isArray(value) || value.length === 0 || value.length > 8) return void 0;
873
+ const targets = [];
874
+ for (const raw of value) {
875
+ if (raw === null || typeof raw !== "object") return void 0;
876
+ const target = {};
877
+ for (const [key, stream] of Object.entries(raw)) {
878
+ if (key !== "position" && key !== "normal" && key !== "tangent") return void 0;
879
+ const typed = floatArray(stream);
880
+ if (typed === void 0) return void 0;
881
+ if (key === "position") target.position = typed;
882
+ else if (key === "normal") target.normal = typed;
883
+ else target.tangent = typed;
884
+ }
885
+ if (Object.keys(target).length === 0) return void 0;
886
+ targets.push(target);
887
+ }
888
+ return targets;
889
+ }
890
+ function unpackMeshBinary(bytes) {
891
+ const headerResult = decodeMeshBinHeader(bytes);
892
+ if (!headerResult.ok) return void 0;
893
+ const header = headerResult.value;
894
+ const projectionResult = deriveVertexLayoutProjectionFromMask(header.mask);
895
+ if (!projectionResult.ok) return void 0;
896
+ const projection = projectionResult.value;
897
+ if (projection.schemaVersion !== header.projectionVersion || projection.arrayStride !== header.stride || projection.digest !== header.digest) {
898
+ return void 0;
899
+ }
900
+ const payloadBytes = header.vertexBytes + header.indexBytes + header.jsonBytes;
901
+ if (MESH_BIN_HEADER_V4_BYTES + payloadBytes !== bytes.byteLength) return void 0;
902
+ let offset = MESH_BIN_HEADER_V4_BYTES;
903
+ const vertexBytes = bytes.subarray(offset, offset + header.vertexBytes);
904
+ const vertices = new Float32Array(vertexBytes.byteLength / 4);
905
+ new Uint8Array(vertices.buffer).set(vertexBytes);
906
+ offset += header.vertexBytes;
907
+ let indices;
908
+ if (header.indexCount > 0) {
909
+ const indexBytes = bytes.subarray(offset, offset + header.indexBytes);
910
+ if (header.indexWidth === 2) {
911
+ indices = new Uint16Array(header.indexCount);
912
+ new Uint8Array(indices.buffer).set(indexBytes);
913
+ } else if (header.indexWidth === 4) {
914
+ indices = new Uint32Array(header.indexCount);
915
+ new Uint8Array(indices.buffer).set(indexBytes);
916
+ } else {
917
+ return void 0;
918
+ }
919
+ offset += header.indexBytes;
920
+ }
921
+ let metadata;
922
+ try {
923
+ metadata = JSON.parse(
924
+ new TextDecoder().decode(bytes.subarray(offset, offset + header.jsonBytes))
925
+ );
926
+ } catch {
927
+ return void 0;
928
+ }
929
+ if (metadata.submeshes === void 0 || metadata.submeshes.length === 0 || metadata.materialSlots === void 0) {
930
+ return void 0;
931
+ }
932
+ const attributes = {};
933
+ const view = new DataView(vertices.buffer);
934
+ for (const entry of projection.attributes) {
935
+ const components = entry.byteLength / (entry.format === "uint16x4" ? 2 : 4);
936
+ const target = entry.format === "uint16x4" ? new Uint16Array(header.vertexCount * components) : new Float32Array(header.vertexCount * components);
937
+ for (let vertex = 0; vertex < header.vertexCount; vertex += 1) {
938
+ for (let component = 0; component < components; component += 1) {
939
+ const sourceOffset = vertex * header.stride + entry.offset + component * (entry.format === "uint16x4" ? 2 : 4);
940
+ if (sourceOffset + (entry.format === "uint16x4" ? 2 : 4) > vertices.byteLength) {
941
+ return void 0;
942
+ }
943
+ if (target instanceof Uint16Array) {
944
+ target[vertex * components + component] = view.getUint16(sourceOffset, true);
945
+ } else {
946
+ const value = view.getFloat32(sourceOffset, true);
947
+ if (!Number.isFinite(value)) return void 0;
948
+ target[vertex * components + component] = value;
949
+ }
950
+ }
951
+ }
952
+ attributes[entry.key] = target;
953
+ }
954
+ const decodedMorphTargets = morphTargets(metadata.morphTargets);
955
+ const morphWeightValues = metadata.morphWeights === void 0 ? void 0 : new Float32Array(metadata.morphWeights);
956
+ if (decodedMorphTargets !== void 0 && morphWeightValues !== void 0 && decodedMorphTargets.length !== morphWeightValues.length) {
957
+ return void 0;
958
+ }
959
+ return {
960
+ version: MESH_BIN_VERSION,
961
+ vertices,
962
+ attributes,
963
+ projection,
964
+ ...indices === void 0 ? {} : { indices },
965
+ submeshes: metadata.submeshes,
966
+ materialSlots: metadata.materialSlots,
967
+ ...metadata.aabb === void 0 ? {} : { aabb: new Float32Array(metadata.aabb) },
968
+ // MeshAsset.vertices remains a Float32Array, so translate the wire stride
969
+ // (bytes) back to the legacy float-count input expected by meshFromParts.
970
+ floatsPerVertex: header.stride / Float32Array.BYTES_PER_ELEMENT,
971
+ uvSetCount: 1,
972
+ ...decodedMorphTargets === void 0 ? {} : { morphTargets: decodedMorphTargets },
973
+ ...morphWeightValues === void 0 ? {} : { morphWeights: morphWeightValues }
974
+ };
975
+ }
976
+ function materialSlotsFor(raw, submeshes, refs) {
977
+ if (raw === void 0)
978
+ return submeshes.map((_submesh, index) => ({ slotName: `LegacySlot_${index}` }));
979
+ return raw.map((slot, index) => {
980
+ const reference = slot.defaultMaterialRef === void 0 ? void 0 : refs[slot.defaultMaterialRef];
981
+ const defaultMaterial = reference === void 0 ? void 0 : parseGuid(reference);
982
+ return {
983
+ slotName: typeof slot.slotName === "string" && slot.slotName.trim().length > 0 ? slot.slotName : `LegacySlot_${index}`,
984
+ ...typeof slot.sourceKey === "string" ? { sourceKey: slot.sourceKey } : {},
985
+ ...defaultMaterial === void 0 ? {} : { defaultMaterial }
986
+ };
987
+ });
988
+ }
989
+ function submeshesFor(raw, vertexCount, indexCount) {
990
+ const values = raw === void 0 || raw.length === 0 ? [{ indexOffset: 0, indexCount, vertexCount, topology: "triangle-list" }] : raw;
991
+ return values.map((value, index) => ({
992
+ indexOffset: typeof value.indexOffset === "number" ? value.indexOffset : 0,
993
+ indexCount: typeof value.indexCount === "number" ? value.indexCount : indexCount,
994
+ vertexCount: typeof value.vertexCount === "number" ? value.vertexCount : vertexCount,
995
+ topology: value.topology === "line-list" || value.topology === "line-strip" || value.topology === "point-list" || value.topology === "triangle-strip" ? value.topology : "triangle-list",
996
+ materialSlot: typeof value.materialSlot === "number" ? value.materialSlot : index
997
+ }));
998
+ }
999
+ function deriveAabb(vertices, stride) {
1000
+ if (stride < 3 || vertices.length < 3 || vertices.length % stride !== 0) return void 0;
1001
+ let minX = Number.POSITIVE_INFINITY;
1002
+ let minY = Number.POSITIVE_INFINITY;
1003
+ let minZ = Number.POSITIVE_INFINITY;
1004
+ let maxX = Number.NEGATIVE_INFINITY;
1005
+ let maxY = Number.NEGATIVE_INFINITY;
1006
+ let maxZ = Number.NEGATIVE_INFINITY;
1007
+ for (let offset = 0; offset < vertices.length; offset += stride) {
1008
+ const x = vertices[offset];
1009
+ const y = vertices[offset + 1];
1010
+ const z = vertices[offset + 2];
1011
+ if (x === void 0 || y === void 0 || z === void 0) return void 0;
1012
+ minX = Math.min(minX, x);
1013
+ minY = Math.min(minY, y);
1014
+ minZ = Math.min(minZ, z);
1015
+ maxX = Math.max(maxX, x);
1016
+ maxY = Math.max(maxY, y);
1017
+ maxZ = Math.max(maxZ, z);
1018
+ }
1019
+ return Float32Array.of(minX, minY, minZ, maxX, maxY, maxZ);
1020
+ }
1021
+ function meshFromParts(parts, refs) {
1022
+ const vertices = floatArray(parts.vertices);
1023
+ if (vertices === void 0 || vertices.length === 0) return void 0;
1024
+ const indices = indexArray(parts.indices);
1025
+ const sourceAttributes = parts.attributes !== null && typeof parts.attributes === "object" ? parts.attributes : {};
1026
+ const attributes = {};
1027
+ for (const key of [
1028
+ "position",
1029
+ "normal",
1030
+ "uv",
1031
+ "tangent",
1032
+ "uv1",
1033
+ "uv2",
1034
+ "uv3",
1035
+ "uv4",
1036
+ "uv5",
1037
+ "uv6",
1038
+ "uv7",
1039
+ "color"
1040
+ ]) {
1041
+ const value = floatArray(sourceAttributes[key]);
1042
+ if (value !== void 0) attributes[key] = value;
1043
+ }
1044
+ const skinIndex = integerArray(sourceAttributes.skinIndex);
1045
+ const skinWeight = floatArray(sourceAttributes.skinWeight);
1046
+ if (skinIndex !== void 0) attributes.skinIndex = skinIndex;
1047
+ if (skinWeight !== void 0) attributes.skinWeight = skinWeight;
1048
+ const stride = parts.floatsPerVertex ?? 0;
1049
+ const vertexCount = stride > 0 ? vertices.length / stride : vertices.length;
1050
+ const submeshes = submeshesFor(parts.submeshes, vertexCount, indices?.length ?? 0);
1051
+ const materialSlots = materialSlotsFor(parts.materialSlots, submeshes, refs);
1052
+ const aabb = floatArray(parts.aabb) ?? deriveAabb(vertices, stride);
1053
+ if (aabb === void 0) return void 0;
1054
+ const morph = morphTargets(parts.morphTargets);
1055
+ const weights = floatArray(parts.morphWeights);
1056
+ if (morph !== void 0 && weights !== void 0 && morph.length !== weights.length)
1057
+ return void 0;
1058
+ const uvSetCount = parts.uvSetCount ?? 1;
1059
+ if (stride > 0 && uvSetCount > 1) {
1060
+ const hasSkin = stride === 18 + (uvSetCount - 1) * 2;
1061
+ const firstOffset = hasSkin ? 18 : PROCEDURAL_FLOATS_PER_VERTEX;
1062
+ const extraUvKeys = ["uv1", "uv2", "uv3", "uv4", "uv5", "uv6", "uv7"];
1063
+ for (let set = 1; set < uvSetCount; set += 1) {
1064
+ const key = extraUvKeys[set - 1];
1065
+ if (key === void 0) return void 0;
1066
+ const values = new Float32Array(vertexCount * 2);
1067
+ const sourceOffset = firstOffset + (set - 1) * 2;
1068
+ for (let vertex = 0; vertex < vertexCount; vertex += 1) {
1069
+ const source = vertex * stride + sourceOffset;
1070
+ values[vertex * 2] = vertices[source] ?? 0;
1071
+ values[vertex * 2 + 1] = vertices[source + 1] ?? 0;
1072
+ }
1073
+ attributes[key] = values;
1074
+ }
1075
+ }
1076
+ return {
1077
+ kind: "mesh",
1078
+ vertices,
1079
+ ...indices === void 0 ? {} : { indices },
1080
+ attributes,
1081
+ aabb,
1082
+ submeshes,
1083
+ materialSlots,
1084
+ ...morph === void 0 ? {} : { morphTargets: morph },
1085
+ ...weights === void 0 ? {} : { morphWeights: weights }
1086
+ };
1087
+ }
1088
+ function decodeMeshBinary(bytes, refs) {
1089
+ const decoded = unpackMeshBinary(bytes);
1090
+ if (decoded === void 0) return void 0;
1091
+ return meshFromParts(
1092
+ {
1093
+ vertices: decoded.vertices,
1094
+ ...decoded.attributes === void 0 ? {} : { attributes: decoded.attributes },
1095
+ ...decoded.indices === void 0 ? {} : { indices: decoded.indices },
1096
+ ...decoded.aabb === void 0 ? {} : { aabb: decoded.aabb },
1097
+ ...decoded.submeshes === void 0 ? {} : { submeshes: decoded.submeshes },
1098
+ ...decoded.materialSlots === void 0 ? {} : { materialSlots: decoded.materialSlots },
1099
+ ...decoded.morphTargets === void 0 ? {} : { morphTargets: decoded.morphTargets },
1100
+ ...decoded.morphWeights === void 0 ? {} : { morphWeights: decoded.morphWeights },
1101
+ ...decoded.skinIndex === void 0 && decoded.skinWeight === void 0 ? {} : {
1102
+ attributes: {
1103
+ ...decoded.skinIndex === void 0 ? {} : { skinIndex: decoded.skinIndex },
1104
+ ...decoded.skinWeight === void 0 ? {} : { skinWeight: decoded.skinWeight }
1105
+ }
1106
+ },
1107
+ floatsPerVertex: decoded.floatsPerVertex,
1108
+ uvSetCount: decoded.uvSetCount
1109
+ },
1110
+ refs
1111
+ );
1112
+ }
1113
+ function normalizeMeshPayload(payload, refs) {
1114
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return void 0;
1115
+ const source = payload;
1116
+ if (source.kind !== "mesh") return void 0;
1117
+ return meshFromParts(
1118
+ {
1119
+ vertices: source.vertices,
1120
+ ...source.indices === void 0 ? {} : { indices: source.indices },
1121
+ ...source.attributes === void 0 ? {} : { attributes: source.attributes },
1122
+ ...source.aabb === void 0 ? {} : { aabb: source.aabb },
1123
+ submeshes: Array.isArray(source.submeshes) ? source.submeshes : [],
1124
+ ...Array.isArray(source.materialSlots) ? { materialSlots: source.materialSlots } : {},
1125
+ ...source.morphTargets === void 0 ? {} : { morphTargets: source.morphTargets },
1126
+ ...source.morphWeights === void 0 ? {} : { morphWeights: source.morphWeights },
1127
+ floatsPerVertex: PROCEDURAL_FLOATS_PER_VERTEX
1128
+ },
1129
+ refs
1130
+ );
1131
+ }
1132
+ function createCylinderGeometry(radiusTop, radiusBottom, height, radialSegments = 16, heightSegments = 1) {
1133
+ if (radiusTop < 0 || radiusBottom < 0 || height <= 0) {
1134
+ return err(
1135
+ degenerate(`radiusTop=${radiusTop}, radiusBottom=${radiusBottom}, height=${height}`)
1136
+ );
1137
+ }
1138
+ if (radiusTop === 0 && radiusBottom === 0) {
1139
+ return err(degenerate(`radiusTop=0 and radiusBottom=0; at least one must be > 0`));
1140
+ }
1141
+ const rs = radialSegments | 0;
1142
+ const hs = heightSegments | 0;
1143
+ if (rs < 3) return err(degenerate(`radialSegments=${rs}; minimum 3`));
1144
+ if (hs < 1) return err(degenerate(`heightSegments=${hs}; minimum 1`));
1145
+ const halfHeight = height / 2;
1146
+ const sideVertexCount = (rs + 1) * (hs + 1);
1147
+ const topCap = radiusTop > 0;
1148
+ const bottomCap = radiusBottom > 0;
1149
+ const topCount = topCap ? rs + 2 : 0;
1150
+ const bottomCount = bottomCap ? rs + 2 : 0;
1151
+ const vertexCount = sideVertexCount + topCount + bottomCount;
1152
+ const sideIndexCount = rs * hs * 6;
1153
+ const topIdxCount = topCap ? rs * 3 : 0;
1154
+ const bottomIdxCount = bottomCap ? rs * 3 : 0;
1155
+ const indexCount = sideIndexCount + topIdxCount + bottomIdxCount;
1156
+ const vertices = new Float32Array(vertexCount * FACTORY_FLOATS_PER_VERTEX);
1157
+ const indices = new Uint32Array(indexCount);
1158
+ let vIdx = 0;
1159
+ const slope = (radiusBottom - radiusTop) / height;
1160
+ for (let iy = 0; iy <= hs; iy++) {
1161
+ const v = iy / hs;
1162
+ const y = halfHeight - v * height;
1163
+ const radius = v * (radiusBottom - radiusTop) + radiusTop;
1164
+ for (let ix = 0; ix <= rs; ix++) {
1165
+ const u = ix / rs;
1166
+ const theta = u * Math.PI * 2;
1167
+ const sinT = Math.sin(theta);
1168
+ const cosT = Math.cos(theta);
1169
+ const x = radius * sinT;
1170
+ const z = radius * cosT;
1171
+ const nx = sinT;
1172
+ const ny = slope;
1173
+ const nz = cosT;
1174
+ const nlen = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
1175
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
1176
+ vertices[base + 0] = x;
1177
+ vertices[base + 1] = y;
1178
+ vertices[base + 2] = z;
1179
+ vertices[base + 3] = nx / nlen;
1180
+ vertices[base + 4] = ny / nlen;
1181
+ vertices[base + 5] = nz / nlen;
1182
+ vertices[base + 6] = u;
1183
+ vertices[base + 7] = v;
1184
+ vIdx++;
1185
+ }
1186
+ }
1187
+ let iIdx = 0;
1188
+ for (let iy = 0; iy < hs; iy++) {
1189
+ for (let ix = 0; ix < rs; ix++) {
1190
+ const a = (rs + 1) * iy + ix;
1191
+ const b = (rs + 1) * (iy + 1) + ix;
1192
+ const c = (rs + 1) * (iy + 1) + ix + 1;
1193
+ const d = (rs + 1) * iy + ix + 1;
1194
+ indices[iIdx++] = a;
1195
+ indices[iIdx++] = b;
1196
+ indices[iIdx++] = d;
1197
+ indices[iIdx++] = b;
1198
+ indices[iIdx++] = c;
1199
+ indices[iIdx++] = d;
1200
+ }
1201
+ }
1202
+ if (topCap) {
1203
+ const centerIdx = vIdx;
1204
+ const cBase = vIdx * FACTORY_FLOATS_PER_VERTEX;
1205
+ vertices[cBase + 0] = 0;
1206
+ vertices[cBase + 1] = halfHeight;
1207
+ vertices[cBase + 2] = 0;
1208
+ vertices[cBase + 3] = 0;
1209
+ vertices[cBase + 4] = 1;
1210
+ vertices[cBase + 5] = 0;
1211
+ vertices[cBase + 6] = 0.5;
1212
+ vertices[cBase + 7] = 0.5;
1213
+ vIdx++;
1214
+ const ringStart = vIdx;
1215
+ for (let ix = 0; ix <= rs; ix++) {
1216
+ const u = ix / rs;
1217
+ const theta = u * Math.PI * 2;
1218
+ const sinT = Math.sin(theta);
1219
+ const cosT = Math.cos(theta);
1220
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
1221
+ vertices[base + 0] = radiusTop * sinT;
1222
+ vertices[base + 1] = halfHeight;
1223
+ vertices[base + 2] = radiusTop * cosT;
1224
+ vertices[base + 3] = 0;
1225
+ vertices[base + 4] = 1;
1226
+ vertices[base + 5] = 0;
1227
+ vertices[base + 6] = sinT * 0.5 + 0.5;
1228
+ vertices[base + 7] = cosT * 0.5 + 0.5;
1229
+ vIdx++;
1230
+ }
1231
+ for (let ix = 0; ix < rs; ix++) {
1232
+ indices[iIdx++] = centerIdx;
1233
+ indices[iIdx++] = ringStart + ix;
1234
+ indices[iIdx++] = ringStart + ix + 1;
1235
+ }
1236
+ }
1237
+ if (bottomCap) {
1238
+ const centerIdx = vIdx;
1239
+ const cBase = vIdx * FACTORY_FLOATS_PER_VERTEX;
1240
+ vertices[cBase + 0] = 0;
1241
+ vertices[cBase + 1] = -halfHeight;
1242
+ vertices[cBase + 2] = 0;
1243
+ vertices[cBase + 3] = 0;
1244
+ vertices[cBase + 4] = -1;
1245
+ vertices[cBase + 5] = 0;
1246
+ vertices[cBase + 6] = 0.5;
1247
+ vertices[cBase + 7] = 0.5;
1248
+ vIdx++;
1249
+ const ringStart = vIdx;
1250
+ for (let ix = 0; ix <= rs; ix++) {
1251
+ const u = ix / rs;
1252
+ const theta = u * Math.PI * 2;
1253
+ const sinT = Math.sin(theta);
1254
+ const cosT = Math.cos(theta);
1255
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
1256
+ vertices[base + 0] = radiusBottom * sinT;
1257
+ vertices[base + 1] = -halfHeight;
1258
+ vertices[base + 2] = radiusBottom * cosT;
1259
+ vertices[base + 3] = 0;
1260
+ vertices[base + 4] = -1;
1261
+ vertices[base + 5] = 0;
1262
+ vertices[base + 6] = sinT * 0.5 + 0.5;
1263
+ vertices[base + 7] = cosT * 0.5 + 0.5;
1264
+ vIdx++;
1265
+ }
1266
+ for (let ix = 0; ix < rs; ix++) {
1267
+ indices[iIdx++] = centerIdx;
1268
+ indices[iIdx++] = ringStart + ix + 1;
1269
+ indices[iIdx++] = ringStart + ix;
1270
+ }
1271
+ }
1272
+ return meshFromInterleaved(vertices, indices);
1273
+ }
1274
+ function createPlaneGeometry(width, height, widthSegments = 1, heightSegments = 1) {
1275
+ if (width <= 0 || height <= 0) {
1276
+ return err(degenerate(`width=${width}, height=${height}`));
1277
+ }
1278
+ const ws = widthSegments | 0;
1279
+ const hs = heightSegments | 0;
1280
+ if (ws < 1 || hs < 1) {
1281
+ return err(degenerate(`widthSegments=${ws}, heightSegments=${hs}`));
1282
+ }
1283
+ const halfW = width / 2;
1284
+ const halfH = height / 2;
1285
+ const gridX1 = ws + 1;
1286
+ const gridY1 = hs + 1;
1287
+ const segW = width / ws;
1288
+ const segH = height / hs;
1289
+ const vertexCount = gridX1 * gridY1;
1290
+ const indexCount = ws * hs * 6;
1291
+ const vertices = new Float32Array(vertexCount * FACTORY_FLOATS_PER_VERTEX);
1292
+ const indices = new Uint32Array(indexCount);
1293
+ let vIdx = 0;
1294
+ for (let iy = 0; iy < gridY1; iy++) {
1295
+ const y = iy * segH - halfH;
1296
+ for (let ix = 0; ix < gridX1; ix++) {
1297
+ const x = ix * segW - halfW;
1298
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
1299
+ vertices[base + 0] = x;
1300
+ vertices[base + 1] = -y;
1301
+ vertices[base + 2] = 0;
1302
+ vertices[base + 3] = 0;
1303
+ vertices[base + 4] = 0;
1304
+ vertices[base + 5] = 1;
1305
+ vertices[base + 6] = ix / ws;
1306
+ vertices[base + 7] = iy / hs;
1307
+ vIdx++;
1308
+ }
1309
+ }
1310
+ let iIdx = 0;
1311
+ for (let iy = 0; iy < hs; iy++) {
1312
+ for (let ix = 0; ix < ws; ix++) {
1313
+ const a = ix + gridX1 * iy;
1314
+ const b = ix + gridX1 * (iy + 1);
1315
+ const c = ix + 1 + gridX1 * (iy + 1);
1316
+ const d = ix + 1 + gridX1 * iy;
1317
+ indices[iIdx++] = a;
1318
+ indices[iIdx++] = b;
1319
+ indices[iIdx++] = d;
1320
+ indices[iIdx++] = b;
1321
+ indices[iIdx++] = c;
1322
+ indices[iIdx++] = d;
1323
+ }
1324
+ }
1325
+ return meshFromInterleaved(vertices, indices);
1326
+ }
1327
+ function createSphereGeometry(radius, widthSegments = 16, heightSegments = 12) {
1328
+ if (radius <= 0) return err(degenerate(`radius=${radius}`));
1329
+ const ws = widthSegments | 0;
1330
+ const hs = heightSegments | 0;
1331
+ if (ws < 3) return err(degenerate(`widthSegments=${ws}; minimum 3`));
1332
+ if (hs < 2) return err(degenerate(`heightSegments=${hs}; minimum 2`));
1333
+ const vertexCount = (ws + 1) * (hs + 1);
1334
+ const indexCount = ws * hs * 6;
1335
+ const vertices = new Float32Array(vertexCount * FACTORY_FLOATS_PER_VERTEX);
1336
+ const indices = new Uint32Array(indexCount);
1337
+ let vIdx = 0;
1338
+ for (let iy = 0; iy <= hs; iy++) {
1339
+ const v = iy / hs;
1340
+ const phi = v * Math.PI;
1341
+ for (let ix = 0; ix <= ws; ix++) {
1342
+ const u = ix / ws;
1343
+ const theta = u * Math.PI * 2;
1344
+ const x = -radius * Math.cos(theta) * Math.sin(phi);
1345
+ const y = radius * Math.cos(phi);
1346
+ const z = radius * Math.sin(theta) * Math.sin(phi);
1347
+ const nx = x / radius;
1348
+ const ny = y / radius;
1349
+ const nz = z / radius;
1350
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
1351
+ vertices[base + 0] = x;
1352
+ vertices[base + 1] = y;
1353
+ vertices[base + 2] = z;
1354
+ vertices[base + 3] = nx;
1355
+ vertices[base + 4] = ny;
1356
+ vertices[base + 5] = nz;
1357
+ vertices[base + 6] = u;
1358
+ vertices[base + 7] = v;
1359
+ vIdx++;
1360
+ }
1361
+ }
1362
+ let iIdx = 0;
1363
+ const stride = ws + 1;
1364
+ for (let iy = 0; iy < hs; iy++) {
1365
+ for (let ix = 0; ix < ws; ix++) {
1366
+ const a = iy * stride + ix + 1;
1367
+ const b = iy * stride + ix;
1368
+ const c = (iy + 1) * stride + ix;
1369
+ const d = (iy + 1) * stride + ix + 1;
1370
+ if (iy !== 0) {
1371
+ indices[iIdx++] = a;
1372
+ indices[iIdx++] = b;
1373
+ indices[iIdx++] = d;
1374
+ }
1375
+ if (iy !== hs - 1) {
1376
+ indices[iIdx++] = b;
1377
+ indices[iIdx++] = c;
1378
+ indices[iIdx++] = d;
1379
+ }
1380
+ }
1381
+ }
1382
+ const trimmed = indices.slice(0, iIdx);
1383
+ return meshFromInterleaved(vertices, trimmed);
1384
+ }
1385
+
1386
+ // src/assets/primitive-mesh.ts
1387
+ function createPrimitiveMesh(kind) {
1388
+ switch (kind) {
1389
+ case "cube":
1390
+ return createBoxGeometry(1, 1, 1);
1391
+ case "triangle":
1392
+ return meshFromInterleaved(
1393
+ new Float32Array([
1394
+ 0,
1395
+ 0.7,
1396
+ 0,
1397
+ 0,
1398
+ 0,
1399
+ 1,
1400
+ 0.5,
1401
+ 1,
1402
+ -0.7,
1403
+ -0.6,
1404
+ 0,
1405
+ 0,
1406
+ 0,
1407
+ 1,
1408
+ 0,
1409
+ 0,
1410
+ 0.7,
1411
+ -0.6,
1412
+ 0,
1413
+ 0,
1414
+ 0,
1415
+ 1,
1416
+ 1,
1417
+ 0
1418
+ ]),
1419
+ new Uint16Array([0, 1, 2])
1420
+ );
1421
+ case "quad":
1422
+ return createPlaneGeometry(1, 1);
1423
+ case "sphere":
1424
+ return createSphereGeometry(1, 16, 12);
1425
+ case "cylinder":
1426
+ return createCylinderGeometry(0.5, 0.5, 1, 16, 1);
1427
+ case "nine-slice-quad":
1428
+ return createPlaneGeometry(1, 1, 3, 3);
1429
+ }
1430
+ }
1431
+
1432
+ // src/assets/mesh-decoder.ts
1433
+ var meshAssetKind = {
1434
+ kind: "mesh"
1435
+ };
1436
+ var PROCEDURAL_MESH_KINDS = {
1437
+ "procedural-cube": "cube",
1438
+ "procedural-triangle": "triangle",
1439
+ "procedural-quad": "quad",
1440
+ "procedural-sphere": "sphere",
1441
+ "procedural-cylinder": "cylinder",
1442
+ "procedural-nine-slice-quad": "nine-slice-quad"
1443
+ };
1444
+ function proceduralMesh(payload) {
1445
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return void 0;
1446
+ const geometry = payload.geometry;
1447
+ if (typeof geometry !== "string") return void 0;
1448
+ const kind = PROCEDURAL_MESH_KINDS[geometry];
1449
+ if (kind === void 0) return void 0;
1450
+ const result = createPrimitiveMesh(kind);
1451
+ if (result.ok) return result;
1452
+ return err({
1453
+ code: "asset-package-invalid",
1454
+ expected: `a valid procedural ${kind} mesh payload`,
1455
+ hint: "recook the authored procedural mesh descriptor",
1456
+ detail: { guid: "", reason: "procedural mesh creation failed" }
1457
+ });
1458
+ }
1459
+ var meshAssetDecoder = {
1460
+ async decode({ envelope, artifacts }) {
1461
+ const payload = envelope.payload;
1462
+ const procedural = proceduralMesh(payload);
1463
+ if (procedural !== void 0) {
1464
+ if (!procedural.ok) {
1465
+ return err({
1466
+ code: "asset-package-invalid",
1467
+ expected: procedural.error.expected,
1468
+ hint: procedural.error.hint,
1469
+ detail: { guid: envelope.guid, reason: "procedural mesh creation failed" }
1470
+ });
1471
+ }
1472
+ return procedural;
1473
+ }
1474
+ const body = envelope.artifacts.body;
1475
+ if (body !== void 0) {
1476
+ const bytes = await artifacts.read(body);
1477
+ if (!bytes.ok) return err(bytes.error);
1478
+ const decoded = decodeMeshBinary(bytes.value, envelope.refs);
1479
+ if (decoded === void 0) {
1480
+ return err({
1481
+ code: "asset-package-invalid",
1482
+ expected: "a valid mesh-binary/4 body artifact with a local-space AABB",
1483
+ hint: "recook the mesh binary and publish its validated geometry payload",
1484
+ detail: { guid: envelope.guid, reason: "mesh binary decode failed" }
1485
+ });
1486
+ }
1487
+ return ok(decoded);
1488
+ }
1489
+ const normalized = normalizeMeshPayload(payload, envelope.refs);
1490
+ if (normalized !== void 0) return ok(normalized);
1491
+ if (payload.kind !== "mesh" || !(payload.vertices instanceof Float32Array) || payload.vertices.length === 0 || payload.aabb === void 0) {
1492
+ return err({
1493
+ code: "asset-package-invalid",
1494
+ expected: "a mesh payload with vertices and a local-space AABB",
1495
+ hint: "recook the mesh binary and publish its validated geometry payload",
1496
+ detail: { guid: envelope.guid, reason: "mesh payload failed geometry validation" }
1497
+ });
1498
+ }
1499
+ return ok(payload);
1500
+ }
1501
+ };
1502
+ var meshAssetContribution = {
1503
+ kind: meshAssetKind,
1504
+ decoder: meshAssetDecoder,
1505
+ consumer: "Geometry"
1506
+ };
1507
+ function createCapsuleGeometry(radius, length, capSegments = 4, radialSegments = 8) {
1508
+ if (radius <= 0) return err(degenerate(`radius=${radius}`));
1509
+ if (length < 0) return err(degenerate(`length=${length}`));
1510
+ const cs = capSegments | 0;
1511
+ const rs = radialSegments | 0;
1512
+ if (cs < 1) return err(degenerate(`capSegments=${cs}; minimum 1`));
1513
+ if (rs < 3) return err(degenerate(`radialSegments=${rs}; minimum 3`));
1514
+ const halfLength = length / 2;
1515
+ const latRows = 2 * (cs + 1);
1516
+ const vertexCount = latRows * (rs + 1);
1517
+ const indexCount = (latRows - 1) * rs * 6;
1518
+ const vertices = new Float32Array(vertexCount * FACTORY_FLOATS_PER_VERTEX);
1519
+ const indices = new Uint32Array(indexCount);
1520
+ let vIdx = 0;
1521
+ for (let row = 0; row < latRows; row++) {
1522
+ const topHemi = row <= cs;
1523
+ let ringR;
1524
+ let y;
1525
+ let centerY;
1526
+ if (topHemi) {
1527
+ const t = row / cs;
1528
+ const a = t * (Math.PI / 2);
1529
+ ringR = radius * Math.sin(a);
1530
+ centerY = halfLength;
1531
+ y = centerY + radius * Math.cos(a);
1532
+ } else {
1533
+ const t = (row - (cs + 1)) / cs;
1534
+ const a = t * (Math.PI / 2);
1535
+ ringR = radius * Math.cos(a);
1536
+ centerY = -halfLength;
1537
+ y = centerY - radius * Math.sin(a);
1538
+ }
1539
+ const v = row / (latRows - 1);
1540
+ for (let ix = 0; ix <= rs; ix++) {
1541
+ const u = ix / rs;
1542
+ const theta = u * Math.PI * 2;
1543
+ const sinT = Math.sin(theta);
1544
+ const cosT = Math.cos(theta);
1545
+ const x = ringR * sinT;
1546
+ const z = ringR * cosT;
1547
+ const nx = x;
1548
+ const ny = y - centerY;
1549
+ const nz = z;
1550
+ const nlen = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
1551
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
1552
+ vertices[base + 0] = x;
1553
+ vertices[base + 1] = y;
1554
+ vertices[base + 2] = z;
1555
+ vertices[base + 3] = nx / nlen;
1556
+ vertices[base + 4] = ny / nlen;
1557
+ vertices[base + 5] = nz / nlen;
1558
+ vertices[base + 6] = u;
1559
+ vertices[base + 7] = v;
1560
+ vIdx++;
1561
+ }
1562
+ }
1563
+ let iIdx = 0;
1564
+ const stride = rs + 1;
1565
+ for (let row = 0; row < latRows - 1; row++) {
1566
+ for (let ix = 0; ix < rs; ix++) {
1567
+ const a = row * stride + ix + 1;
1568
+ const b = row * stride + ix;
1569
+ const c = (row + 1) * stride + ix;
1570
+ const d = (row + 1) * stride + ix + 1;
1571
+ const northPoleRow = row === 0;
1572
+ const southPoleRow = row === latRows - 2;
1573
+ if (!northPoleRow) {
1574
+ indices[iIdx++] = a;
1575
+ indices[iIdx++] = b;
1576
+ indices[iIdx++] = d;
1577
+ }
1578
+ if (!southPoleRow) {
1579
+ indices[iIdx++] = b;
1580
+ indices[iIdx++] = c;
1581
+ indices[iIdx++] = d;
1582
+ }
1583
+ }
1584
+ }
1585
+ const trimmed = indices.slice(0, iIdx);
1586
+ return meshFromInterleaved(vertices, trimmed);
1587
+ }
1588
+ function createConeGeometry(radius, height, radialSegments = 16, heightSegments = 1) {
1589
+ if (radius <= 0) return err(degenerate(`radius=${radius}`));
1590
+ if (height <= 0) return err(degenerate(`height=${height}`));
1591
+ return createCylinderGeometry(0, radius, height, radialSegments, heightSegments);
1592
+ }
1593
+ var DEFAULT_RESOLUTION = 32;
1594
+ function invalid(detail) {
1595
+ return err(degenerate(detail));
1596
+ }
1597
+ function resolution(value) {
1598
+ const resolved = value ?? DEFAULT_RESOLUTION;
1599
+ const integer = resolved | 0;
1600
+ return integer >= 3 && integer === resolved ? integer : void 0;
1601
+ }
1602
+ function finitePoint(point) {
1603
+ return Number.isFinite(point[0]) && Number.isFinite(point[1]);
1604
+ }
1605
+ function maskRadius(shape) {
1606
+ switch (shape.kind) {
1607
+ case "circle":
1608
+ case "circular-sector":
1609
+ case "circular-segment":
1610
+ return shape.radius;
1611
+ case "ellipse":
1612
+ case "annulus":
1613
+ case "capsule":
1614
+ case "rhombus":
1615
+ case "rectangle":
1616
+ case "regular-polygon":
1617
+ case "triangle":
1618
+ case "segment":
1619
+ case "polyline":
1620
+ return void 0;
1621
+ }
1622
+ }
1623
+ function validateMeshOptions(shape, options) {
1624
+ const uv = options?.uv;
1625
+ if (uv === void 0) return ok(void 0);
1626
+ const radius = maskRadius(shape);
1627
+ return radius !== void 0 && radius > 0 && Number.isFinite(uv.angle) ? ok({ kind: "circular-mask", radius, angle: uv.angle }) : invalid(`circular-mask UV requires a circular shape and finite angle`);
1628
+ }
1629
+ function area(points) {
1630
+ let value = 0;
1631
+ for (let i = 0; i < points.length; i++) {
1632
+ const a = points[i];
1633
+ const b = points[(i + 1) % points.length];
1634
+ if (!a || !b) continue;
1635
+ value += a[0] * b[1] - b[0] * a[1];
1636
+ }
1637
+ return value / 2;
1638
+ }
1639
+ function centroid(points) {
1640
+ let x = 0;
1641
+ let y = 0;
1642
+ for (const point of points) {
1643
+ x += point[0];
1644
+ y += point[1];
1645
+ }
1646
+ const count = Math.max(1, points.length);
1647
+ return [x / count, y / count];
1648
+ }
1649
+ function counterClockwise(points) {
1650
+ return area(points) < 0 ? [...points].reverse() : [...points];
1651
+ }
1652
+ function circleBoundary(radius, count, start = -Math.PI / 2) {
1653
+ return Array.from({ length: count }, (_, i) => {
1654
+ const angle = start + i / count * Math.PI * 2;
1655
+ return [Math.cos(angle) * radius, Math.sin(angle) * radius];
1656
+ });
1657
+ }
1658
+ function arcBoundary(radius, angle, count, start) {
1659
+ return Array.from({ length: count + 1 }, (_, i) => {
1660
+ const t = i / count;
1661
+ const current = start + angle * t;
1662
+ return [Math.cos(current) * radius, Math.sin(current) * radius];
1663
+ });
1664
+ }
1665
+ function ellipseBoundary(halfWidth, halfHeight, count) {
1666
+ return Array.from({ length: count }, (_, i) => {
1667
+ const angle = -Math.PI / 2 + i / count * Math.PI * 2;
1668
+ return [Math.cos(angle) * halfWidth, Math.sin(angle) * halfHeight];
1669
+ });
1670
+ }
1671
+ function capsuleBoundary(radius, halfLength, count) {
1672
+ const points = [];
1673
+ for (let i = 0; i <= count; i++) {
1674
+ const angle = i / count * Math.PI;
1675
+ points.push([Math.cos(angle) * radius, halfLength + Math.sin(angle) * radius]);
1676
+ }
1677
+ for (let i = 0; i <= count; i++) {
1678
+ const angle = Math.PI + i / count * Math.PI;
1679
+ points.push([Math.cos(angle) * radius, -halfLength + Math.sin(angle) * radius]);
1680
+ }
1681
+ points.pop();
1682
+ points.shift();
1683
+ return points;
1684
+ }
1685
+ function buildMesh(points, indices, topology, uvProjection) {
1686
+ const positions = new Float32Array(points.length * 3);
1687
+ const normals = new Float32Array(points.length * 3);
1688
+ const uvs = new Float32Array(points.length * 2);
1689
+ const tangents = new Float32Array(points.length * 4);
1690
+ let minX = Infinity;
1691
+ let minY = Infinity;
1692
+ let maxX = -Infinity;
1693
+ let maxY = -Infinity;
1694
+ for (const point of points) {
1695
+ minX = Math.min(minX, point[0]);
1696
+ minY = Math.min(minY, point[1]);
1697
+ maxX = Math.max(maxX, point[0]);
1698
+ maxY = Math.max(maxY, point[1]);
1699
+ }
1700
+ const width = Math.max(maxX - minX, 1);
1701
+ const height = Math.max(maxY - minY, 1);
1702
+ const vertices = new Float32Array(points.length * PROCEDURAL_FLOATS_PER_VERTEX);
1703
+ for (let i = 0; i < points.length; i++) {
1704
+ const point = points[i];
1705
+ if (!point) continue;
1706
+ const [x, y] = point;
1707
+ const positionOffset = i * 3;
1708
+ const uvOffset = i * 2;
1709
+ const tangentOffset = i * 4;
1710
+ const vertexOffset = i * PROCEDURAL_FLOATS_PER_VERTEX;
1711
+ positions[positionOffset] = x;
1712
+ positions[positionOffset + 1] = y;
1713
+ normals[positionOffset + 2] = 1;
1714
+ const cos = uvProjection === void 0 ? 1 : Math.cos(uvProjection.angle);
1715
+ const sin = uvProjection === void 0 ? 0 : Math.sin(uvProjection.angle);
1716
+ const projectedX = x * cos - y * sin;
1717
+ const projectedY = x * sin + y * cos;
1718
+ const u = uvProjection === void 0 ? (x - minX) / width : 0.5 + projectedX / (2 * uvProjection.radius);
1719
+ const v = uvProjection === void 0 ? (maxY - y) / height : 0.5 - projectedY / (2 * uvProjection.radius);
1720
+ uvs[uvOffset] = u;
1721
+ uvs[uvOffset + 1] = v;
1722
+ tangents[tangentOffset] = 1;
1723
+ tangents[tangentOffset + 3] = 1;
1724
+ vertices[vertexOffset] = x;
1725
+ vertices[vertexOffset + 1] = y;
1726
+ vertices[vertexOffset + 5] = 1;
1727
+ vertices[vertexOffset + 6] = u;
1728
+ vertices[vertexOffset + 7] = v;
1729
+ vertices[vertexOffset + 8] = 1;
1730
+ vertices[vertexOffset + 11] = 1;
1731
+ }
1732
+ return {
1733
+ kind: "mesh",
1734
+ vertices,
1735
+ indices: new Uint32Array(indices),
1736
+ attributes: { position: positions, normal: normals, uv: uvs, tangent: tangents },
1737
+ submeshes: [
1738
+ {
1739
+ indexOffset: 0,
1740
+ indexCount: indices.length,
1741
+ vertexCount: points.length,
1742
+ topology,
1743
+ materialSlot: 0
1744
+ }
1745
+ ],
1746
+ materialSlots: [{ slotName: "Default" }],
1747
+ aabb: box3.fromPositions(box3.create(), positions)
1748
+ };
1749
+ }
1750
+ function fill(points, uvProjection) {
1751
+ const boundary = counterClockwise(points);
1752
+ const center = centroid(boundary);
1753
+ const vertices = [center, ...boundary];
1754
+ const indices = [];
1755
+ for (let i = 0; i < boundary.length; i++) {
1756
+ const next = (i + 1) % boundary.length;
1757
+ indices.push(0, i + 1, next + 1);
1758
+ }
1759
+ return buildMesh(vertices, indices, "triangle-list", uvProjection);
1760
+ }
1761
+ function ring(outer, inner) {
1762
+ const outside = counterClockwise(outer);
1763
+ const inside = counterClockwise(inner);
1764
+ const count = Math.min(outside.length, inside.length);
1765
+ const vertices = [...outside.slice(0, count), ...inside.slice(0, count)];
1766
+ const indices = [];
1767
+ for (let i = 0; i < count; i++) {
1768
+ const next = (i + 1) % count;
1769
+ indices.push(i, next, count + i, next, count + next, count + i);
1770
+ }
1771
+ return buildMesh(vertices, indices, "triangle-list");
1772
+ }
1773
+ function segment(points) {
1774
+ const indices = [];
1775
+ for (let i = 0; i + 1 < points.length; i += 1) indices.push(i, i + 1);
1776
+ return buildMesh(points, indices, "line-list");
1777
+ }
1778
+ function shapeBoundary(shape, count) {
1779
+ switch (shape.kind) {
1780
+ case "circle":
1781
+ return circleBoundary(shape.radius, count);
1782
+ case "circular-sector":
1783
+ return [
1784
+ ...arcBoundary(shape.radius, shape.angle, count, -Math.PI / 2 - shape.angle / 2),
1785
+ [0, 0]
1786
+ ];
1787
+ case "circular-segment":
1788
+ return arcBoundary(shape.radius, shape.angle, count, -Math.PI / 2 - shape.angle / 2);
1789
+ case "ellipse":
1790
+ return ellipseBoundary(shape.halfWidth, shape.halfHeight, count);
1791
+ case "capsule":
1792
+ return capsuleBoundary(shape.radius, shape.halfLength, Math.max(2, Math.floor(count / 2)));
1793
+ case "rhombus":
1794
+ return [
1795
+ [0, shape.halfHeight],
1796
+ [shape.halfWidth, 0],
1797
+ [0, -shape.halfHeight],
1798
+ [-shape.halfWidth, 0]
1799
+ ];
1800
+ case "rectangle":
1801
+ return [
1802
+ [-shape.width / 2, -shape.height / 2],
1803
+ [shape.width / 2, -shape.height / 2],
1804
+ [shape.width / 2, shape.height / 2],
1805
+ [-shape.width / 2, shape.height / 2]
1806
+ ];
1807
+ case "regular-polygon":
1808
+ return Array.from({ length: shape.sides }, (_, i) => {
1809
+ const angle = -Math.PI / 2 + i / shape.sides * Math.PI * 2;
1810
+ return [Math.cos(angle) * shape.radius, Math.sin(angle) * shape.radius];
1811
+ });
1812
+ case "triangle":
1813
+ return [...shape.vertices];
1814
+ case "annulus":
1815
+ case "segment":
1816
+ case "polyline":
1817
+ return void 0;
1818
+ }
1819
+ }
1820
+ function scaleAround(points, factor) {
1821
+ const center = centroid(points);
1822
+ return points.map(([x, y]) => [
1823
+ center[0] + (x - center[0]) * factor,
1824
+ center[1] + (y - center[1]) * factor
1825
+ ]);
1826
+ }
1827
+ function withResolution(value, valid, detail) {
1828
+ const count = resolution(value);
1829
+ return valid && count !== void 0 ? ok(count) : invalid(detail);
1830
+ }
1831
+ function validateCommon(shape) {
1832
+ switch (shape.kind) {
1833
+ case "circle":
1834
+ return withResolution(
1835
+ shape.resolution,
1836
+ shape.radius > 0 && Number.isFinite(shape.radius),
1837
+ `circle radius=${shape.radius}, resolution=${shape.resolution}`
1838
+ );
1839
+ case "circular-sector":
1840
+ case "circular-segment":
1841
+ return withResolution(
1842
+ shape.resolution,
1843
+ shape.radius > 0 && shape.angle > 0 && shape.angle <= Math.PI * 2 && Number.isFinite(shape.radius) && Number.isFinite(shape.angle),
1844
+ `${shape.kind} radius=${shape.radius}, angle=${shape.angle}, resolution=${shape.resolution}`
1845
+ );
1846
+ case "ellipse":
1847
+ return withResolution(
1848
+ shape.resolution,
1849
+ shape.halfWidth > 0 && shape.halfHeight > 0 && Number.isFinite(shape.halfWidth) && Number.isFinite(shape.halfHeight),
1850
+ `ellipse halfWidth=${shape.halfWidth}, halfHeight=${shape.halfHeight}, resolution=${shape.resolution}`
1851
+ );
1852
+ case "annulus":
1853
+ return withResolution(
1854
+ shape.resolution,
1855
+ shape.innerRadius >= 0 && shape.outerRadius > shape.innerRadius && Number.isFinite(shape.innerRadius) && Number.isFinite(shape.outerRadius),
1856
+ `annulus innerRadius=${shape.innerRadius}, outerRadius=${shape.outerRadius}, resolution=${shape.resolution}`
1857
+ );
1858
+ case "capsule":
1859
+ return withResolution(
1860
+ shape.resolution,
1861
+ shape.radius > 0 && shape.halfLength >= 0 && Number.isFinite(shape.radius) && Number.isFinite(shape.halfLength),
1862
+ `capsule radius=${shape.radius}, halfLength=${shape.halfLength}, resolution=${shape.resolution}`
1863
+ );
1864
+ case "rhombus":
1865
+ return shape.halfWidth > 0 && shape.halfHeight > 0 && Number.isFinite(shape.halfWidth) && Number.isFinite(shape.halfHeight) ? ok(0) : invalid(`rhombus halfWidth=${shape.halfWidth}, halfHeight=${shape.halfHeight}`);
1866
+ case "rectangle":
1867
+ return shape.width > 0 && shape.height > 0 && Number.isFinite(shape.width) && Number.isFinite(shape.height) ? ok(0) : invalid(`rectangle width=${shape.width}, height=${shape.height}`);
1868
+ case "regular-polygon":
1869
+ return shape.radius > 0 && Number.isFinite(shape.radius) && Number.isInteger(shape.sides) && shape.sides >= 3 ? ok(0) : invalid(`regular-polygon radius=${shape.radius}, sides=${shape.sides}`);
1870
+ case "triangle":
1871
+ return shape.vertices.every(finitePoint) && Math.abs(area(shape.vertices)) > 1e-7 ? ok(0) : invalid("triangle vertices must be finite and non-collinear");
1872
+ case "segment":
1873
+ return shape.vertices.every(finitePoint) && (shape.vertices[0][0] !== shape.vertices[1][0] || shape.vertices[0][1] !== shape.vertices[1][1]) ? ok(0) : invalid("segment endpoints must be finite and distinct");
1874
+ case "polyline":
1875
+ return shape.vertices.length >= 2 && shape.vertices.every(finitePoint) ? ok(0) : invalid("polyline needs at least two finite vertices");
1876
+ }
1877
+ }
1878
+ function create2dGeometry(shape, options) {
1879
+ const checked = validateCommon(shape);
1880
+ if (!checked.ok) return checked;
1881
+ const meshOptions = validateMeshOptions(shape, options);
1882
+ if (!meshOptions.ok) return meshOptions;
1883
+ if (shape.kind === "segment" || shape.kind === "polyline") return ok(segment(shape.vertices));
1884
+ if (shape.kind === "annulus") {
1885
+ const outer = circleBoundary(shape.outerRadius, checked.value);
1886
+ const inner = circleBoundary(shape.innerRadius, checked.value);
1887
+ return ok(ring(outer, inner));
1888
+ }
1889
+ const points = shapeBoundary(shape, checked.value);
1890
+ if (!points) return invalid(`unsupported 2d shape kind=${shape.kind}`);
1891
+ return ok(fill(points, meshOptions.value));
1892
+ }
1893
+ function boundsPoints(shape, count) {
1894
+ if (shape.kind === "annulus") return circleBoundary(shape.outerRadius, count);
1895
+ if (shape.kind === "segment" || shape.kind === "polyline") return [...shape.vertices];
1896
+ return shapeBoundary(shape, count);
1897
+ }
1898
+ function compute2dBounds(shape, pose = {}) {
1899
+ const checked = validateCommon(shape);
1900
+ if (!checked.ok) return checked;
1901
+ const translation = pose.translation ?? [0, 0];
1902
+ const rotation = pose.rotation ?? 0;
1903
+ if (!finitePoint(translation) || !Number.isFinite(rotation)) {
1904
+ return invalid("2d bounds pose must contain finite translation and rotation");
1905
+ }
1906
+ const points = boundsPoints(shape, checked.value);
1907
+ if (points === void 0 || points.length === 0)
1908
+ return invalid(`unsupported 2d bounds kind=${shape.kind}`);
1909
+ const cos = Math.cos(rotation);
1910
+ const sin = Math.sin(rotation);
1911
+ const transformed = points.map(
1912
+ ([x, y]) => [translation[0] + x * cos - y * sin, translation[1] + x * sin + y * cos]
1913
+ );
1914
+ let radius = 0;
1915
+ for (const [x, y] of points) radius = Math.max(radius, Math.hypot(x, y));
1916
+ return ok({
1917
+ aabb: box2.fromPoints(box2.create(), transformed),
1918
+ circle: circle2.create(translation[0], translation[1], radius)
1919
+ });
1920
+ }
1921
+ function create2dRingGeometry(shape, thickness, resolutionOverride) {
1922
+ if (!(thickness > 0) || !Number.isFinite(thickness))
1923
+ return invalid(`ring thickness=${thickness}`);
1924
+ const checked = validateCommon(shape);
1925
+ if (!checked.ok) return checked;
1926
+ if (shape.kind === "segment" || shape.kind === "polyline" || shape.kind === "annulus") {
1927
+ return invalid(`rings require a closed non-annulus shape, got ${shape.kind}`);
1928
+ }
1929
+ const count = resolutionOverride === void 0 ? checked.value : resolution(resolutionOverride);
1930
+ if (count === void 0) return invalid(`ring resolution=${resolutionOverride}`);
1931
+ const outer = shapeBoundary(shape, count);
1932
+ if (!outer) return invalid(`unsupported ring shape kind=${shape.kind}`);
1933
+ const extent = Math.max(...outer.map(([x, y]) => Math.hypot(x, y)));
1934
+ const factor = (extent - thickness) / extent;
1935
+ if (!(factor > 1e-5))
1936
+ return invalid(`ring thickness=${thickness} exceeds shape extent=${extent}`);
1937
+ return ok(ring(outer, scaleAround(outer, factor)));
1938
+ }
1939
+ function createTorusGeometry(radius, tube, radialSegments = 8, tubularSegments = 24) {
1940
+ if (radius <= 0) return err(degenerate(`radius=${radius}`));
1941
+ if (tube <= 0) return err(degenerate(`tube=${tube}`));
1942
+ const rs = radialSegments | 0;
1943
+ const ts = tubularSegments | 0;
1944
+ if (rs < 3) return err(degenerate(`radialSegments=${rs}; minimum 3`));
1945
+ if (ts < 3) return err(degenerate(`tubularSegments=${ts}; minimum 3`));
1946
+ const vertexCount = (rs + 1) * (ts + 1);
1947
+ const indexCount = rs * ts * 6;
1948
+ const vertices = new Float32Array(vertexCount * FACTORY_FLOATS_PER_VERTEX);
1949
+ const indices = new Uint32Array(indexCount);
1950
+ let vIdx = 0;
1951
+ for (let j = 0; j <= rs; j++) {
1952
+ const v = j / rs * Math.PI * 2;
1953
+ const cosV = Math.cos(v);
1954
+ const sinV = Math.sin(v);
1955
+ for (let i = 0; i <= ts; i++) {
1956
+ const u = i / ts * Math.PI * 2;
1957
+ const cosU = Math.cos(u);
1958
+ const sinU = Math.sin(u);
1959
+ const x = (radius + tube * cosV) * cosU;
1960
+ const y = (radius + tube * cosV) * sinU;
1961
+ const z = tube * sinV;
1962
+ const centerX = radius * cosU;
1963
+ const centerY = radius * sinU;
1964
+ const nx = x - centerX;
1965
+ const ny = y - centerY;
1966
+ const nz = z;
1967
+ const nlen = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
1968
+ const base = vIdx * FACTORY_FLOATS_PER_VERTEX;
1969
+ vertices[base + 0] = x;
1970
+ vertices[base + 1] = y;
1971
+ vertices[base + 2] = z;
1972
+ vertices[base + 3] = nx / nlen;
1973
+ vertices[base + 4] = ny / nlen;
1974
+ vertices[base + 5] = nz / nlen;
1975
+ vertices[base + 6] = i / ts;
1976
+ vertices[base + 7] = j / rs;
1977
+ vIdx++;
1978
+ }
1979
+ }
1980
+ let iIdx = 0;
1981
+ for (let j = 1; j <= rs; j++) {
1982
+ for (let i = 1; i <= ts; i++) {
1983
+ const a = (ts + 1) * j + i - 1;
1984
+ const b = (ts + 1) * (j - 1) + i - 1;
1985
+ const c = (ts + 1) * (j - 1) + i;
1986
+ const d = (ts + 1) * j + i;
1987
+ indices[iIdx++] = a;
1988
+ indices[iIdx++] = b;
1989
+ indices[iIdx++] = d;
1990
+ indices[iIdx++] = b;
1991
+ indices[iIdx++] = c;
1992
+ indices[iIdx++] = d;
1993
+ }
1994
+ }
1995
+ return meshFromInterleaved(vertices, indices);
1996
+ }
1997
+
1998
+ export { PROCEDURAL_FLOATS_PER_VERTEX, VertexAttributePackError, buildMeshAttributeMapForUvSets, compute2dBounds, computeTangentVec4, create2dGeometry, create2dRingGeometry, createBoxGeometry, createCapsuleGeometry, createConeGeometry, createCylinderGeometry, createPlaneGeometry, createPrimitiveMesh, createSphereGeometry, createTorusGeometry, deriveVertexBufferLayout, deriveVertexBufferLayoutFromProjection, deriveVertexLayoutProjection, deriveVertexLayoutProjectionFromMask, meshAssetContribution, meshAssetDecoder, meshAssetKind, meshFromInterleaved, packInterleavedVertexAttributes };
1999
+ //# sourceMappingURL=index.mjs.map
2000
+ //# sourceMappingURL=index.mjs.map