@adatechnology/game-animals-three 0.2.0 → 0.3.0

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.
@@ -92,6 +92,1523 @@ var GameAnimalsThree = (() => {
92
92
  return outcome === "dies" ? "dead" : "alive";
93
93
  };
94
94
 
95
+ // ../game-shapes/dist/chunk-R4RY23RH.js
96
+ function canonicalJson(value) {
97
+ if (value === null) return "null";
98
+ if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
99
+ if (typeof value === "string") return JSON.stringify(value);
100
+ if (typeof value === "boolean") return value ? "true" : "false";
101
+ if (typeof value === "function") throw new Error("game-shapes: a function cannot be part of a shape (register it as a generator)");
102
+ if (ArrayBuffer.isView(value)) return canonicalJson(Array.from(value));
103
+ if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalJson(item)).join(",")}]`;
104
+ if (typeof value === "object") {
105
+ const record = value;
106
+ const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
107
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
108
+ }
109
+ return "null";
110
+ }
111
+ function needsUint32(array) {
112
+ for (let i = array.length - 1; i >= 0; --i) if (array[i] >= 65535) return true;
113
+ return false;
114
+ }
115
+ var indexArray = (indices) => needsUint32(indices) ? new Uint32Array(indices) : new Uint16Array(indices);
116
+ function pack(type, parameters, a) {
117
+ return {
118
+ type,
119
+ parameters,
120
+ positions: new Float32Array(a.vertices),
121
+ normals: new Float32Array(a.normals),
122
+ uvs: new Float32Array(a.uvs),
123
+ ...a.indices ? { indices: indexArray(a.indices) } : {},
124
+ groups: a.groups
125
+ };
126
+ }
127
+ var normalize3 = (x, y, z) => {
128
+ const length = Math.sqrt(x * x + y * y + z * z);
129
+ const s = 1 / (length || 1);
130
+ return [x * s, y * s, z * s];
131
+ };
132
+ function box(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
133
+ const parameters = { width, height, depth, widthSegments, heightSegments, depthSegments };
134
+ widthSegments = Math.floor(widthSegments);
135
+ heightSegments = Math.floor(heightSegments);
136
+ depthSegments = Math.floor(depthSegments);
137
+ const indices = [];
138
+ const vertices = [];
139
+ const normals = [];
140
+ const uvs = [];
141
+ const groups = [];
142
+ let numberOfVertices = 0;
143
+ let groupStart = 0;
144
+ const buildPlane = (u, v, w, udir, vdir, width2, height2, depth2, gridX, gridY, materialIndex) => {
145
+ const segmentWidth = width2 / gridX;
146
+ const segmentHeight = height2 / gridY;
147
+ const widthHalf = width2 / 2;
148
+ const heightHalf = height2 / 2;
149
+ const depthHalf = depth2 / 2;
150
+ const gridX1 = gridX + 1;
151
+ const gridY1 = gridY + 1;
152
+ let vertexCounter = 0;
153
+ let groupCount = 0;
154
+ const vector = { x: 0, y: 0, z: 0 };
155
+ for (let iy = 0; iy < gridY1; iy++) {
156
+ const y = iy * segmentHeight - heightHalf;
157
+ for (let ix = 0; ix < gridX1; ix++) {
158
+ const x = ix * segmentWidth - widthHalf;
159
+ vector[u] = x * udir;
160
+ vector[v] = y * vdir;
161
+ vector[w] = depthHalf;
162
+ vertices.push(vector.x, vector.y, vector.z);
163
+ vector[u] = 0;
164
+ vector[v] = 0;
165
+ vector[w] = depth2 > 0 ? 1 : -1;
166
+ normals.push(vector.x, vector.y, vector.z);
167
+ uvs.push(ix / gridX);
168
+ uvs.push(1 - iy / gridY);
169
+ vertexCounter += 1;
170
+ }
171
+ }
172
+ for (let iy = 0; iy < gridY; iy++) {
173
+ for (let ix = 0; ix < gridX; ix++) {
174
+ const a = numberOfVertices + ix + gridX1 * iy;
175
+ const b = numberOfVertices + ix + gridX1 * (iy + 1);
176
+ const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
177
+ const d = numberOfVertices + (ix + 1) + gridX1 * iy;
178
+ indices.push(a, b, d);
179
+ indices.push(b, c, d);
180
+ groupCount += 6;
181
+ }
182
+ }
183
+ groups.push({ start: groupStart, count: groupCount, materialIndex });
184
+ groupStart += groupCount;
185
+ numberOfVertices += vertexCounter;
186
+ };
187
+ buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0);
188
+ buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1);
189
+ buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2);
190
+ buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3);
191
+ buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4);
192
+ buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5);
193
+ return pack("BoxGeometry", parameters, { vertices, normals, uvs, indices, groups });
194
+ }
195
+ function cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
196
+ radialSegments = Math.floor(radialSegments);
197
+ heightSegments = Math.floor(heightSegments);
198
+ const indices = [];
199
+ const vertices = [];
200
+ const normals = [];
201
+ const uvs = [];
202
+ const groups = [];
203
+ let index = 0;
204
+ const indexArray2 = [];
205
+ const halfHeight = height / 2;
206
+ let groupStart = 0;
207
+ const generateTorso = () => {
208
+ let groupCount = 0;
209
+ const slope = (radiusBottom - radiusTop) / height;
210
+ for (let y = 0; y <= heightSegments; y++) {
211
+ const indexRow = [];
212
+ const v = y / heightSegments;
213
+ const radius = v * (radiusBottom - radiusTop) + radiusTop;
214
+ for (let x = 0; x <= radialSegments; x++) {
215
+ const u = x / radialSegments;
216
+ const theta = u * thetaLength + thetaStart;
217
+ const sinTheta = Math.sin(theta);
218
+ const cosTheta = Math.cos(theta);
219
+ vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
220
+ const n = normalize3(sinTheta, slope, cosTheta);
221
+ normals.push(n[0], n[1], n[2]);
222
+ uvs.push(u, 1 - v);
223
+ indexRow.push(index++);
224
+ }
225
+ indexArray2.push(indexRow);
226
+ }
227
+ for (let x = 0; x < radialSegments; x++) {
228
+ for (let y = 0; y < heightSegments; y++) {
229
+ const a = indexArray2[y][x];
230
+ const b = indexArray2[y + 1][x];
231
+ const c = indexArray2[y + 1][x + 1];
232
+ const d = indexArray2[y][x + 1];
233
+ indices.push(a, b, d);
234
+ indices.push(b, c, d);
235
+ groupCount += 6;
236
+ }
237
+ }
238
+ groups.push({ start: groupStart, count: groupCount, materialIndex: 0 });
239
+ groupStart += groupCount;
240
+ };
241
+ const generateCap = (top) => {
242
+ const centerIndexStart = index;
243
+ let groupCount = 0;
244
+ const radius = top === true ? radiusTop : radiusBottom;
245
+ const sign2 = top === true ? 1 : -1;
246
+ for (let x = 1; x <= radialSegments; x++) {
247
+ vertices.push(0, halfHeight * sign2, 0);
248
+ normals.push(0, sign2, 0);
249
+ uvs.push(0.5, 0.5);
250
+ index++;
251
+ }
252
+ const centerIndexEnd = index;
253
+ for (let x = 0; x <= radialSegments; x++) {
254
+ const u = x / radialSegments;
255
+ const theta = u * thetaLength + thetaStart;
256
+ const cosTheta = Math.cos(theta);
257
+ const sinTheta = Math.sin(theta);
258
+ vertices.push(radius * sinTheta, halfHeight * sign2, radius * cosTheta);
259
+ normals.push(0, sign2, 0);
260
+ uvs.push(cosTheta * 0.5 + 0.5, sinTheta * 0.5 * sign2 + 0.5);
261
+ index++;
262
+ }
263
+ for (let x = 0; x < radialSegments; x++) {
264
+ const c = centerIndexStart + x;
265
+ const i = centerIndexEnd + x;
266
+ if (top === true) indices.push(i, i + 1, c);
267
+ else indices.push(i + 1, i, c);
268
+ groupCount += 3;
269
+ }
270
+ groups.push({ start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2 });
271
+ groupStart += groupCount;
272
+ };
273
+ generateTorso();
274
+ if (openEnded === false) {
275
+ if (radiusTop > 0) generateCap(true);
276
+ if (radiusBottom > 0) generateCap(false);
277
+ }
278
+ return { vertices, normals, uvs, indices, groups };
279
+ }
280
+ function cylinder(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
281
+ const parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
282
+ return pack("CylinderGeometry", parameters, cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
283
+ }
284
+ function cone(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
285
+ const parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
286
+ return pack("ConeGeometry", parameters, cylinderArrays(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
287
+ }
288
+ function sphere(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) {
289
+ const parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength };
290
+ widthSegments = Math.max(3, Math.floor(widthSegments));
291
+ heightSegments = Math.max(2, Math.floor(heightSegments));
292
+ const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI);
293
+ let index = 0;
294
+ const grid = [];
295
+ const indices = [];
296
+ const vertices = [];
297
+ const normals = [];
298
+ const uvs = [];
299
+ for (let iy = 0; iy <= heightSegments; iy++) {
300
+ const verticesRow = [];
301
+ const v = iy / heightSegments;
302
+ let uOffset = 0;
303
+ if (iy === 0 && thetaStart === 0) uOffset = 0.5 / widthSegments;
304
+ else if (iy === heightSegments && thetaEnd === Math.PI) uOffset = -0.5 / widthSegments;
305
+ for (let ix = 0; ix <= widthSegments; ix++) {
306
+ const u = ix / widthSegments;
307
+ const x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
308
+ const y = radius * Math.cos(thetaStart + v * thetaLength);
309
+ const z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
310
+ vertices.push(x, y, z);
311
+ const n = normalize3(x, y, z);
312
+ normals.push(n[0], n[1], n[2]);
313
+ uvs.push(u + uOffset, 1 - v);
314
+ verticesRow.push(index++);
315
+ }
316
+ grid.push(verticesRow);
317
+ }
318
+ for (let iy = 0; iy < heightSegments; iy++) {
319
+ for (let ix = 0; ix < widthSegments; ix++) {
320
+ const a = grid[iy][ix + 1];
321
+ const b = grid[iy][ix];
322
+ const c = grid[iy + 1][ix];
323
+ const d = grid[iy + 1][ix + 1];
324
+ if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
325
+ if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
326
+ }
327
+ }
328
+ return pack("SphereGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
329
+ }
330
+ function plane(width = 1, height = 1, widthSegments = 1, heightSegments = 1) {
331
+ const parameters = { width, height, widthSegments, heightSegments };
332
+ const widthHalf = width / 2;
333
+ const heightHalf = height / 2;
334
+ const gridX = Math.floor(widthSegments);
335
+ const gridY = Math.floor(heightSegments);
336
+ const gridX1 = gridX + 1;
337
+ const gridY1 = gridY + 1;
338
+ const segmentWidth = width / gridX;
339
+ const segmentHeight = height / gridY;
340
+ const indices = [];
341
+ const vertices = [];
342
+ const normals = [];
343
+ const uvs = [];
344
+ for (let iy = 0; iy < gridY1; iy++) {
345
+ const y = iy * segmentHeight - heightHalf;
346
+ for (let ix = 0; ix < gridX1; ix++) {
347
+ const x = ix * segmentWidth - widthHalf;
348
+ vertices.push(x, -y, 0);
349
+ normals.push(0, 0, 1);
350
+ uvs.push(ix / gridX);
351
+ uvs.push(1 - iy / gridY);
352
+ }
353
+ }
354
+ for (let iy = 0; iy < gridY; iy++) {
355
+ for (let ix = 0; ix < gridX; ix++) {
356
+ const a = ix + gridX1 * iy;
357
+ const b = ix + gridX1 * (iy + 1);
358
+ const c = ix + 1 + gridX1 * (iy + 1);
359
+ const d = ix + 1 + gridX1 * iy;
360
+ indices.push(a, b, d);
361
+ indices.push(b, c, d);
362
+ }
363
+ }
364
+ return pack("PlaneGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
365
+ }
366
+ function circle(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) {
367
+ const parameters = { radius, segments, thetaStart, thetaLength };
368
+ segments = Math.max(3, segments);
369
+ const indices = [];
370
+ const vertices = [];
371
+ const normals = [];
372
+ const uvs = [];
373
+ vertices.push(0, 0, 0);
374
+ normals.push(0, 0, 1);
375
+ uvs.push(0.5, 0.5);
376
+ for (let s = 0, i = 3; s <= segments; s++, i += 3) {
377
+ const segment = thetaStart + s / segments * thetaLength;
378
+ vertices.push(radius * Math.cos(segment), radius * Math.sin(segment), 0);
379
+ normals.push(0, 0, 1);
380
+ uvs.push((vertices[i] / radius + 1) / 2, (vertices[i + 1] / radius + 1) / 2);
381
+ }
382
+ for (let i = 1; i <= segments; i++) indices.push(i, i + 1, 0);
383
+ return pack("CircleGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
384
+ }
385
+ function ring(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) {
386
+ const parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength };
387
+ thetaSegments = Math.max(3, thetaSegments);
388
+ phiSegments = Math.max(1, phiSegments);
389
+ const indices = [];
390
+ const vertices = [];
391
+ const normals = [];
392
+ const uvs = [];
393
+ let radius = innerRadius;
394
+ const radiusStep = (outerRadius - innerRadius) / phiSegments;
395
+ for (let j = 0; j <= phiSegments; j++) {
396
+ for (let i = 0; i <= thetaSegments; i++) {
397
+ const segment = thetaStart + i / thetaSegments * thetaLength;
398
+ const x = radius * Math.cos(segment);
399
+ const y = radius * Math.sin(segment);
400
+ vertices.push(x, y, 0);
401
+ normals.push(0, 0, 1);
402
+ uvs.push((x / outerRadius + 1) / 2, (y / outerRadius + 1) / 2);
403
+ }
404
+ radius += radiusStep;
405
+ }
406
+ for (let j = 0; j < phiSegments; j++) {
407
+ const thetaSegmentLevel = j * (thetaSegments + 1);
408
+ for (let i = 0; i < thetaSegments; i++) {
409
+ const segment = i + thetaSegmentLevel;
410
+ const a = segment;
411
+ const b = segment + thetaSegments + 1;
412
+ const c = segment + thetaSegments + 2;
413
+ const d = segment + 1;
414
+ indices.push(a, b, d);
415
+ indices.push(b, c, d);
416
+ }
417
+ }
418
+ return pack("RingGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
419
+ }
420
+ function lathe(points, segments = 12, phiStart = 0, phiLength = Math.PI * 2) {
421
+ const parameters = { points: points.map(([x, y]) => ({ x, y })), segments, phiStart, phiLength };
422
+ segments = Math.floor(segments);
423
+ phiLength = Math.max(0, Math.min(Math.PI * 2, phiLength));
424
+ const indices = [];
425
+ const vertices = [];
426
+ const uvs = [];
427
+ const initNormals = [];
428
+ const normals = [];
429
+ const inverseSegments = 1 / segments;
430
+ const normal = { x: 0, y: 0, z: 0 };
431
+ const curNormal = { x: 0, y: 0, z: 0 };
432
+ const prevNormal = { x: 0, y: 0, z: 0 };
433
+ let dx = 0;
434
+ let dy = 0;
435
+ const last = points.length - 1;
436
+ const px = (j) => points[j][0];
437
+ const py = (j) => points[j][1];
438
+ const setNormalized = () => {
439
+ const n = normalize3(normal.x, normal.y, normal.z);
440
+ normal.x = n[0];
441
+ normal.y = n[1];
442
+ normal.z = n[2];
443
+ };
444
+ for (let j = 0; j <= last; j++) {
445
+ if (j === 0) {
446
+ dx = px(j + 1) - px(j);
447
+ dy = py(j + 1) - py(j);
448
+ normal.x = dy * 1;
449
+ normal.y = -dx;
450
+ normal.z = dy * 0;
451
+ prevNormal.x = normal.x;
452
+ prevNormal.y = normal.y;
453
+ prevNormal.z = normal.z;
454
+ setNormalized();
455
+ initNormals.push(normal.x, normal.y, normal.z);
456
+ } else if (j === last) {
457
+ initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);
458
+ } else {
459
+ dx = px(j + 1) - px(j);
460
+ dy = py(j + 1) - py(j);
461
+ normal.x = dy * 1;
462
+ normal.y = -dx;
463
+ normal.z = dy * 0;
464
+ curNormal.x = normal.x;
465
+ curNormal.y = normal.y;
466
+ curNormal.z = normal.z;
467
+ normal.x += prevNormal.x;
468
+ normal.y += prevNormal.y;
469
+ normal.z += prevNormal.z;
470
+ setNormalized();
471
+ initNormals.push(normal.x, normal.y, normal.z);
472
+ prevNormal.x = curNormal.x;
473
+ prevNormal.y = curNormal.y;
474
+ prevNormal.z = curNormal.z;
475
+ }
476
+ }
477
+ for (let i = 0; i <= segments; i++) {
478
+ const phi = phiStart + i * inverseSegments * phiLength;
479
+ const sin = Math.sin(phi);
480
+ const cos = Math.cos(phi);
481
+ for (let j = 0; j <= last; j++) {
482
+ vertices.push(px(j) * sin, py(j), px(j) * cos);
483
+ uvs.push(i / segments, j / (points.length - 1));
484
+ const x = initNormals[3 * j + 0] * sin;
485
+ const y = initNormals[3 * j + 1];
486
+ const z = initNormals[3 * j + 0] * cos;
487
+ normals.push(x, y, z);
488
+ }
489
+ }
490
+ for (let i = 0; i < segments; i++) {
491
+ for (let j = 0; j < points.length - 1; j++) {
492
+ const base = j + i * points.length;
493
+ const a = base;
494
+ const b = base + points.length;
495
+ const c = base + points.length + 1;
496
+ const d = base + 1;
497
+ indices.push(a, b, d);
498
+ indices.push(c, d, b);
499
+ }
500
+ }
501
+ return pack("LatheGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
502
+ }
503
+ function torus(radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) {
504
+ const parameters = { radius, tube, radialSegments, tubularSegments, arc };
505
+ radialSegments = Math.floor(radialSegments);
506
+ tubularSegments = Math.floor(tubularSegments);
507
+ const indices = [];
508
+ const vertices = [];
509
+ const normals = [];
510
+ const uvs = [];
511
+ for (let j = 0; j <= radialSegments; j++) {
512
+ for (let i = 0; i <= tubularSegments; i++) {
513
+ const u = i / tubularSegments * arc;
514
+ const v = j / radialSegments * Math.PI * 2;
515
+ const x = (radius + tube * Math.cos(v)) * Math.cos(u);
516
+ const y = (radius + tube * Math.cos(v)) * Math.sin(u);
517
+ const z = tube * Math.sin(v);
518
+ vertices.push(x, y, z);
519
+ const cx = radius * Math.cos(u);
520
+ const cy = radius * Math.sin(u);
521
+ const n = normalize3(x - cx, y - cy, z - 0);
522
+ normals.push(n[0], n[1], n[2]);
523
+ uvs.push(i / tubularSegments);
524
+ uvs.push(j / radialSegments);
525
+ }
526
+ }
527
+ for (let j = 1; j <= radialSegments; j++) {
528
+ for (let i = 1; i <= tubularSegments; i++) {
529
+ const a = (tubularSegments + 1) * j + i - 1;
530
+ const b = (tubularSegments + 1) * (j - 1) + i - 1;
531
+ const c = (tubularSegments + 1) * (j - 1) + i;
532
+ const d = (tubularSegments + 1) * j + i;
533
+ indices.push(a, b, d);
534
+ indices.push(b, c, d);
535
+ }
536
+ }
537
+ return pack("TorusGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
538
+ }
539
+ function difference(size, subtract) {
540
+ const [w, h, d] = size;
541
+ const hw = w / 2;
542
+ const hh = h / 2;
543
+ const hd = d / 2;
544
+ const holes = subtract.map((hole) => ({ x0: Math.max(-hw, Math.min(hole.min[0], hole.max[0])), x1: Math.min(hw, Math.max(hole.min[0], hole.max[0])), y0: Math.max(-hh, Math.min(hole.min[1], hole.max[1])), y1: Math.min(hh, Math.max(hole.min[1], hole.max[1])) })).filter((hole) => hole.x1 > hole.x0 && hole.y1 > hole.y0);
545
+ const cuts = (lo, hi, values) => [.../* @__PURE__ */ new Set([lo, hi, ...values.filter((v) => v > lo && v < hi)])].sort((a, b) => a - b);
546
+ const xs = cuts(-hw, hw, holes.flatMap((hole) => [hole.x0, hole.x1]));
547
+ const ys = cuts(-hh, hh, holes.flatMap((hole) => [hole.y0, hole.y1]));
548
+ const nx = xs.length - 1;
549
+ const ny = ys.length - 1;
550
+ const solid = (i, j) => {
551
+ if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
552
+ const cx = (xs[i] + xs[i + 1]) / 2;
553
+ const cy = (ys[j] + ys[j + 1]) / 2;
554
+ return !holes.some((hole) => cx > hole.x0 && cx < hole.x1 && cy > hole.y0 && cy < hole.y1);
555
+ };
556
+ const positions = [];
557
+ const normals = [];
558
+ const uvs = [];
559
+ const indices = [];
560
+ const quad = (corners, normal, uv) => {
561
+ const base = positions.length / 3;
562
+ for (let k = 0; k < 4; k++) {
563
+ positions.push(...corners[k]);
564
+ normals.push(...normal);
565
+ uvs.push(...uv[k]);
566
+ }
567
+ indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
568
+ };
569
+ const fu = (x) => (x + hw) / w;
570
+ const fv = (y) => (y + hh) / h;
571
+ for (let j = 0; j < ny; j++) {
572
+ for (let i = 0; i < nx; i++) {
573
+ if (!solid(i, j)) continue;
574
+ const x0 = xs[i];
575
+ const x1 = xs[i + 1];
576
+ const y0 = ys[j];
577
+ const y1 = ys[j + 1];
578
+ quad([[x0, y0, hd], [x1, y0, hd], [x1, y1, hd], [x0, y1, hd]], [0, 0, 1], [[fu(x0), fv(y0)], [fu(x1), fv(y0)], [fu(x1), fv(y1)], [fu(x0), fv(y1)]]);
579
+ quad([[x1, y0, -hd], [x0, y0, -hd], [x0, y1, -hd], [x1, y1, -hd]], [0, 0, -1], [[1 - fu(x1), fv(y0)], [1 - fu(x0), fv(y0)], [1 - fu(x0), fv(y1)], [1 - fu(x1), fv(y1)]]);
580
+ const zu = (z) => (z + hd) / d;
581
+ if (!solid(i - 1, j)) quad([[x0, y0, -hd], [x0, y0, hd], [x0, y1, hd], [x0, y1, -hd]], [-1, 0, 0], [[zu(-hd), fv(y0)], [zu(hd), fv(y0)], [zu(hd), fv(y1)], [zu(-hd), fv(y1)]]);
582
+ if (!solid(i + 1, j)) quad([[x1, y0, hd], [x1, y0, -hd], [x1, y1, -hd], [x1, y1, hd]], [1, 0, 0], [[zu(hd), fv(y0)], [zu(-hd), fv(y0)], [zu(-hd), fv(y1)], [zu(hd), fv(y1)]]);
583
+ if (!solid(i, j - 1)) quad([[x0, y0, -hd], [x1, y0, -hd], [x1, y0, hd], [x0, y0, hd]], [0, -1, 0], [[fu(x0), zu(-hd)], [fu(x1), zu(-hd)], [fu(x1), zu(hd)], [fu(x0), zu(hd)]]);
584
+ if (!solid(i, j + 1)) quad([[x0, y1, hd], [x1, y1, hd], [x1, y1, -hd], [x0, y1, -hd]], [0, 1, 0], [[fu(x0), zu(hd)], [fu(x1), zu(hd)], [fu(x1), zu(-hd)], [fu(x0), zu(-hd)]]);
585
+ }
586
+ }
587
+ return {
588
+ type: "BufferGeometry",
589
+ parameters: { size: [w, h, d], subtract: holes.map((hole) => ({ min: [hole.x0, hole.y0], max: [hole.x1, hole.y1] })) },
590
+ positions: new Float32Array(positions),
591
+ normals: new Float32Array(normals),
592
+ uvs: new Float32Array(uvs),
593
+ indices: indexArray(indices),
594
+ groups: []
595
+ };
596
+ }
597
+ var Earcut = {
598
+ triangulate: function(data, holeIndices, dim = 2) {
599
+ const hasHoles = holeIndices && holeIndices.length;
600
+ const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
601
+ let outerNode = linkedList(data, 0, outerLen, dim, true);
602
+ const triangles = [];
603
+ if (!outerNode || outerNode.next === outerNode.prev) return triangles;
604
+ let minX, minY, maxX, maxY, x, y, invSize;
605
+ if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
606
+ if (data.length > 80 * dim) {
607
+ minX = maxX = data[0];
608
+ minY = maxY = data[1];
609
+ for (let i = dim; i < outerLen; i += dim) {
610
+ x = data[i];
611
+ y = data[i + 1];
612
+ if (x < minX) minX = x;
613
+ if (y < minY) minY = y;
614
+ if (x > maxX) maxX = x;
615
+ if (y > maxY) maxY = y;
616
+ }
617
+ invSize = Math.max(maxX - minX, maxY - minY);
618
+ invSize = invSize !== 0 ? 32767 / invSize : 0;
619
+ }
620
+ earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
621
+ return triangles;
622
+ }
623
+ };
624
+ function linkedList(data, start, end, dim, clockwise) {
625
+ let i, last;
626
+ if (clockwise === signedArea(data, start, end, dim) > 0) {
627
+ for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
628
+ } else {
629
+ for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
630
+ }
631
+ if (last && equals(last, last.next)) {
632
+ removeNode(last);
633
+ last = last.next;
634
+ }
635
+ return last;
636
+ }
637
+ function filterPoints(start, end) {
638
+ if (!start) return start;
639
+ if (!end) end = start;
640
+ let p = start, again;
641
+ do {
642
+ again = false;
643
+ if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
644
+ removeNode(p);
645
+ p = end = p.prev;
646
+ if (p === p.next) break;
647
+ again = true;
648
+ } else {
649
+ p = p.next;
650
+ }
651
+ } while (again || p !== end);
652
+ return end;
653
+ }
654
+ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
655
+ if (!ear) return;
656
+ if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
657
+ let stop = ear, prev, next;
658
+ while (ear.prev !== ear.next) {
659
+ prev = ear.prev;
660
+ next = ear.next;
661
+ if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
662
+ triangles.push(prev.i / dim | 0);
663
+ triangles.push(ear.i / dim | 0);
664
+ triangles.push(next.i / dim | 0);
665
+ removeNode(ear);
666
+ ear = next.next;
667
+ stop = next.next;
668
+ continue;
669
+ }
670
+ ear = next;
671
+ if (ear === stop) {
672
+ if (!pass) {
673
+ earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
674
+ } else if (pass === 1) {
675
+ ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
676
+ earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
677
+ } else if (pass === 2) {
678
+ splitEarcut(ear, triangles, dim, minX, minY, invSize);
679
+ }
680
+ break;
681
+ }
682
+ }
683
+ }
684
+ function isEar(ear) {
685
+ const a = ear.prev, b = ear, c = ear.next;
686
+ if (area(a, b, c) >= 0) return false;
687
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
688
+ const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
689
+ let p = c.next;
690
+ while (p !== a) {
691
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
692
+ p = p.next;
693
+ }
694
+ return true;
695
+ }
696
+ function isEarHashed(ear, minX, minY, invSize) {
697
+ const a = ear.prev, b = ear, c = ear.next;
698
+ if (area(a, b, c) >= 0) return false;
699
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
700
+ const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
701
+ const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
702
+ let p = ear.prevZ, n = ear.nextZ;
703
+ while (p && p.z >= minZ && n && n.z <= maxZ) {
704
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
705
+ p = p.prevZ;
706
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
707
+ n = n.nextZ;
708
+ }
709
+ while (p && p.z >= minZ) {
710
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
711
+ p = p.prevZ;
712
+ }
713
+ while (n && n.z <= maxZ) {
714
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
715
+ n = n.nextZ;
716
+ }
717
+ return true;
718
+ }
719
+ function cureLocalIntersections(start, triangles, dim) {
720
+ let p = start;
721
+ do {
722
+ const a = p.prev, b = p.next.next;
723
+ if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
724
+ triangles.push(a.i / dim | 0);
725
+ triangles.push(p.i / dim | 0);
726
+ triangles.push(b.i / dim | 0);
727
+ removeNode(p);
728
+ removeNode(p.next);
729
+ p = start = b;
730
+ }
731
+ p = p.next;
732
+ } while (p !== start);
733
+ return filterPoints(p);
734
+ }
735
+ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
736
+ let a = start;
737
+ do {
738
+ let b = a.next.next;
739
+ while (b !== a.prev) {
740
+ if (a.i !== b.i && isValidDiagonal(a, b)) {
741
+ let c = splitPolygon(a, b);
742
+ a = filterPoints(a, a.next);
743
+ c = filterPoints(c, c.next);
744
+ earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
745
+ earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
746
+ return;
747
+ }
748
+ b = b.next;
749
+ }
750
+ a = a.next;
751
+ } while (a !== start);
752
+ }
753
+ function eliminateHoles(data, holeIndices, outerNode, dim) {
754
+ const queue = [];
755
+ let i, len, start, end, list2;
756
+ for (i = 0, len = holeIndices.length; i < len; i++) {
757
+ start = holeIndices[i] * dim;
758
+ end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
759
+ list2 = linkedList(data, start, end, dim, false);
760
+ if (list2 === list2.next) list2.steiner = true;
761
+ queue.push(getLeftmost(list2));
762
+ }
763
+ queue.sort(compareX);
764
+ for (i = 0; i < queue.length; i++) {
765
+ outerNode = eliminateHole(queue[i], outerNode);
766
+ }
767
+ return outerNode;
768
+ }
769
+ function compareX(a, b) {
770
+ return a.x - b.x;
771
+ }
772
+ function eliminateHole(hole, outerNode) {
773
+ const bridge = findHoleBridge(hole, outerNode);
774
+ if (!bridge) {
775
+ return outerNode;
776
+ }
777
+ const bridgeReverse = splitPolygon(bridge, hole);
778
+ filterPoints(bridgeReverse, bridgeReverse.next);
779
+ return filterPoints(bridge, bridge.next);
780
+ }
781
+ function findHoleBridge(hole, outerNode) {
782
+ let p = outerNode, qx = -Infinity, m;
783
+ const hx = hole.x, hy = hole.y;
784
+ do {
785
+ if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
786
+ const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
787
+ if (x <= hx && x > qx) {
788
+ qx = x;
789
+ m = p.x < p.next.x ? p : p.next;
790
+ if (x === hx) return m;
791
+ }
792
+ }
793
+ p = p.next;
794
+ } while (p !== outerNode);
795
+ if (!m) return null;
796
+ const stop = m, mx = m.x, my = m.y;
797
+ let tanMin = Infinity, tan;
798
+ p = m;
799
+ do {
800
+ if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
801
+ tan = Math.abs(hy - p.y) / (hx - p.x);
802
+ if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
803
+ m = p;
804
+ tanMin = tan;
805
+ }
806
+ }
807
+ p = p.next;
808
+ } while (p !== stop);
809
+ return m;
810
+ }
811
+ function sectorContainsSector(m, p) {
812
+ return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
813
+ }
814
+ function indexCurve(start, minX, minY, invSize) {
815
+ let p = start;
816
+ do {
817
+ if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
818
+ p.prevZ = p.prev;
819
+ p.nextZ = p.next;
820
+ p = p.next;
821
+ } while (p !== start);
822
+ p.prevZ.nextZ = null;
823
+ p.prevZ = null;
824
+ sortLinked(p);
825
+ }
826
+ function sortLinked(list2) {
827
+ let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
828
+ do {
829
+ p = list2;
830
+ list2 = null;
831
+ tail = null;
832
+ numMerges = 0;
833
+ while (p) {
834
+ numMerges++;
835
+ q = p;
836
+ pSize = 0;
837
+ for (i = 0; i < inSize; i++) {
838
+ pSize++;
839
+ q = q.nextZ;
840
+ if (!q) break;
841
+ }
842
+ qSize = inSize;
843
+ while (pSize > 0 || qSize > 0 && q) {
844
+ if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
845
+ e = p;
846
+ p = p.nextZ;
847
+ pSize--;
848
+ } else {
849
+ e = q;
850
+ q = q.nextZ;
851
+ qSize--;
852
+ }
853
+ if (tail) tail.nextZ = e;
854
+ else list2 = e;
855
+ e.prevZ = tail;
856
+ tail = e;
857
+ }
858
+ p = q;
859
+ }
860
+ tail.nextZ = null;
861
+ inSize *= 2;
862
+ } while (numMerges > 1);
863
+ return list2;
864
+ }
865
+ function zOrder(x, y, minX, minY, invSize) {
866
+ x = (x - minX) * invSize | 0;
867
+ y = (y - minY) * invSize | 0;
868
+ x = (x | x << 8) & 16711935;
869
+ x = (x | x << 4) & 252645135;
870
+ x = (x | x << 2) & 858993459;
871
+ x = (x | x << 1) & 1431655765;
872
+ y = (y | y << 8) & 16711935;
873
+ y = (y | y << 4) & 252645135;
874
+ y = (y | y << 2) & 858993459;
875
+ y = (y | y << 1) & 1431655765;
876
+ return x | y << 1;
877
+ }
878
+ function getLeftmost(start) {
879
+ let p = start, leftmost = start;
880
+ do {
881
+ if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
882
+ p = p.next;
883
+ } while (p !== start);
884
+ return leftmost;
885
+ }
886
+ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
887
+ return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
888
+ }
889
+ function isValidDiagonal(a, b) {
890
+ return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
891
+ (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
892
+ (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
893
+ equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
894
+ }
895
+ function area(p, q, r) {
896
+ return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
897
+ }
898
+ function equals(p1, p2) {
899
+ return p1.x === p2.x && p1.y === p2.y;
900
+ }
901
+ function intersects(p1, q1, p2, q2) {
902
+ const o1 = sign(area(p1, q1, p2));
903
+ const o2 = sign(area(p1, q1, q2));
904
+ const o3 = sign(area(p2, q2, p1));
905
+ const o4 = sign(area(p2, q2, q1));
906
+ if (o1 !== o2 && o3 !== o4) return true;
907
+ if (o1 === 0 && onSegment(p1, p2, q1)) return true;
908
+ if (o2 === 0 && onSegment(p1, q2, q1)) return true;
909
+ if (o3 === 0 && onSegment(p2, p1, q2)) return true;
910
+ if (o4 === 0 && onSegment(p2, q1, q2)) return true;
911
+ return false;
912
+ }
913
+ function onSegment(p, q, r) {
914
+ return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
915
+ }
916
+ function sign(num2) {
917
+ return num2 > 0 ? 1 : num2 < 0 ? -1 : 0;
918
+ }
919
+ function intersectsPolygon(a, b) {
920
+ let p = a;
921
+ do {
922
+ if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true;
923
+ p = p.next;
924
+ } while (p !== a);
925
+ return false;
926
+ }
927
+ function locallyInside(a, b) {
928
+ return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
929
+ }
930
+ function middleInside(a, b) {
931
+ let p = a, inside = false;
932
+ const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
933
+ do {
934
+ if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)
935
+ inside = !inside;
936
+ p = p.next;
937
+ } while (p !== a);
938
+ return inside;
939
+ }
940
+ function splitPolygon(a, b) {
941
+ const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev;
942
+ a.next = b;
943
+ b.prev = a;
944
+ a2.next = an;
945
+ an.prev = a2;
946
+ b2.next = a2;
947
+ a2.prev = b2;
948
+ bp.next = b2;
949
+ b2.prev = bp;
950
+ return b2;
951
+ }
952
+ function insertNode(i, x, y, last) {
953
+ const p = new Node(i, x, y);
954
+ if (!last) {
955
+ p.prev = p;
956
+ p.next = p;
957
+ } else {
958
+ p.next = last.next;
959
+ p.prev = last;
960
+ last.next.prev = p;
961
+ last.next = p;
962
+ }
963
+ return p;
964
+ }
965
+ function removeNode(p) {
966
+ p.next.prev = p.prev;
967
+ p.prev.next = p.next;
968
+ if (p.prevZ) p.prevZ.nextZ = p.nextZ;
969
+ if (p.nextZ) p.nextZ.prevZ = p.prevZ;
970
+ }
971
+ function Node(i, x, y) {
972
+ this.i = i;
973
+ this.x = x;
974
+ this.y = y;
975
+ this.prev = null;
976
+ this.next = null;
977
+ this.z = 0;
978
+ this.prevZ = null;
979
+ this.nextZ = null;
980
+ this.steiner = false;
981
+ }
982
+ function signedArea(data, start, end, dim) {
983
+ let sum = 0;
984
+ for (let i = start, j = end - dim; i < end; i += dim) {
985
+ sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
986
+ j = i;
987
+ }
988
+ return sum;
989
+ }
990
+ var equals2 = (a, b) => a.x === b.x && a.y === b.y;
991
+ var linePoint = (v1, v2, t) => t === 1 ? { x: v2.x, y: v2.y } : { x: (v2.x - v1.x) * t + v1.x, y: (v2.y - v1.y) * t + v1.y };
992
+ function pathPoints(points) {
993
+ const out = [];
994
+ let last;
995
+ for (let i = 1; i < points.length; i++) {
996
+ const v1 = { x: points[i - 1][0], y: points[i - 1][1] };
997
+ const v2 = { x: points[i][0], y: points[i][1] };
998
+ for (let d = 0; d <= 1; d++) {
999
+ const point = linePoint(v1, v2, d / 1);
1000
+ if (last && equals2(last, point)) continue;
1001
+ out.push(point);
1002
+ last = point;
1003
+ }
1004
+ }
1005
+ return out;
1006
+ }
1007
+ function area2(contour) {
1008
+ const n = contour.length;
1009
+ let a = 0;
1010
+ for (let p = n - 1, q = 0; q < n; p = q++) a += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
1011
+ return a * 0.5;
1012
+ }
1013
+ var isClockWise = (pts) => area2(pts) < 0;
1014
+ function removeDupEndPts(points) {
1015
+ const l = points.length;
1016
+ if (l > 2 && equals2(points[l - 1], points[0])) points.pop();
1017
+ }
1018
+ function triangulateShape(contour, holes) {
1019
+ const vertices = [];
1020
+ const holeIndices = [];
1021
+ const faces = [];
1022
+ removeDupEndPts(contour);
1023
+ for (const p of contour) vertices.push(p.x, p.y);
1024
+ let holeIndex = contour.length;
1025
+ holes.forEach(removeDupEndPts);
1026
+ for (let i = 0; i < holes.length; i++) {
1027
+ holeIndices.push(holeIndex);
1028
+ holeIndex += holes[i].length;
1029
+ for (const p of holes[i]) vertices.push(p.x, p.y);
1030
+ }
1031
+ const triangles = Earcut.triangulate(vertices, holeIndices);
1032
+ for (let i = 0; i < triangles.length; i += 3) faces.push(triangles.slice(i, i + 3));
1033
+ return faces;
1034
+ }
1035
+ function computeVertexNormals(positions, indices) {
1036
+ const normals = new Float32Array(positions.length);
1037
+ const get = (array, i) => [array[i * 3], array[i * 3 + 1], array[i * 3 + 2]];
1038
+ const face = (vA, vB, vC) => {
1039
+ const pA = get(positions, vA);
1040
+ const pB = get(positions, vB);
1041
+ const pC = get(positions, vC);
1042
+ const cb = [pC[0] - pB[0], pC[1] - pB[1], pC[2] - pB[2]];
1043
+ const ab = [pA[0] - pB[0], pA[1] - pB[1], pA[2] - pB[2]];
1044
+ return [cb[1] * ab[2] - cb[2] * ab[1], cb[2] * ab[0] - cb[0] * ab[2], cb[0] * ab[1] - cb[1] * ab[0]];
1045
+ };
1046
+ const set = (i, x, y, z) => {
1047
+ normals[i * 3] = x;
1048
+ normals[i * 3 + 1] = y;
1049
+ normals[i * 3 + 2] = z;
1050
+ };
1051
+ if (indices) {
1052
+ for (let i = 0; i < indices.length; i += 3) {
1053
+ const vA = indices[i];
1054
+ const vB = indices[i + 1];
1055
+ const vC = indices[i + 2];
1056
+ const cb = face(vA, vB, vC);
1057
+ for (const v of [vA, vB, vC]) {
1058
+ const n = get(normals, v);
1059
+ set(v, n[0] + cb[0], n[1] + cb[1], n[2] + cb[2]);
1060
+ }
1061
+ }
1062
+ } else {
1063
+ for (let i = 0; i < positions.length / 3; i += 3) {
1064
+ const cb = face(i, i + 1, i + 2);
1065
+ set(i, cb[0], cb[1], cb[2]);
1066
+ set(i + 1, cb[0], cb[1], cb[2]);
1067
+ set(i + 2, cb[0], cb[1], cb[2]);
1068
+ }
1069
+ }
1070
+ for (let i = 0; i < normals.length / 3; i++) {
1071
+ const [x, y, z] = get(normals, i);
1072
+ const s = 1 / (Math.sqrt(x * x + y * y + z * z) || 1);
1073
+ set(i, x * s, y * s, z * s);
1074
+ }
1075
+ return normals;
1076
+ }
1077
+ function extrude(outline, depth = 1, steps = 1, bevel = void 0) {
1078
+ const parameters = { shape: outline, depth, steps, bevel: bevel ?? {} };
1079
+ const verticesArray = [];
1080
+ const uvArray = [];
1081
+ const groups = [];
1082
+ const placeholder = [];
1083
+ const bevelEnabled = bevel !== false;
1084
+ const spec = bevel === false || bevel === void 0 ? {} : bevel;
1085
+ let bevelThickness = spec.thickness !== void 0 ? spec.thickness : 0.2;
1086
+ let bevelSize = spec.size !== void 0 ? spec.size : bevelThickness - 0.1;
1087
+ let bevelOffset = spec.offset !== void 0 ? spec.offset : 0;
1088
+ let bevelSegments = spec.segments !== void 0 ? spec.segments : 3;
1089
+ if (!bevelEnabled) {
1090
+ bevelSegments = 0;
1091
+ bevelThickness = 0;
1092
+ bevelSize = 0;
1093
+ bevelOffset = 0;
1094
+ }
1095
+ let vertices = pathPoints(outline.outer);
1096
+ const holes = (outline.holes ?? []).map(pathPoints);
1097
+ const reverse = !isClockWise(vertices);
1098
+ if (reverse) {
1099
+ vertices = vertices.reverse();
1100
+ for (let h = 0; h < holes.length; h++) {
1101
+ const ahole = holes[h];
1102
+ if (isClockWise(ahole)) holes[h] = ahole.reverse();
1103
+ }
1104
+ }
1105
+ const faces = triangulateShape(vertices, holes);
1106
+ const contour = vertices;
1107
+ for (const ahole of holes) vertices = vertices.concat(ahole);
1108
+ const scalePt2 = (pt, vec, size) => ({ x: pt.x + vec.x * size, y: pt.y + vec.y * size });
1109
+ const vlen = vertices.length;
1110
+ const flen = faces.length;
1111
+ const getBevelVec = (inPt, inPrev, inNext) => {
1112
+ let vTransX;
1113
+ let vTransY;
1114
+ let shrinkBy;
1115
+ const vPrevX = inPt.x - inPrev.x;
1116
+ const vPrevY = inPt.y - inPrev.y;
1117
+ const vNextX = inNext.x - inPt.x;
1118
+ const vNextY = inNext.y - inPt.y;
1119
+ const vPrevLensq = vPrevX * vPrevX + vPrevY * vPrevY;
1120
+ const collinear0 = vPrevX * vNextY - vPrevY * vNextX;
1121
+ if (Math.abs(collinear0) > Number.EPSILON) {
1122
+ const vPrevLen = Math.sqrt(vPrevLensq);
1123
+ const vNextLen = Math.sqrt(vNextX * vNextX + vNextY * vNextY);
1124
+ const ptPrevShiftX = inPrev.x - vPrevY / vPrevLen;
1125
+ const ptPrevShiftY = inPrev.y + vPrevX / vPrevLen;
1126
+ const ptNextShiftX = inNext.x - vNextY / vNextLen;
1127
+ const ptNextShiftY = inNext.y + vNextX / vNextLen;
1128
+ const sf = ((ptNextShiftX - ptPrevShiftX) * vNextY - (ptNextShiftY - ptPrevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
1129
+ vTransX = ptPrevShiftX + vPrevX * sf - inPt.x;
1130
+ vTransY = ptPrevShiftY + vPrevY * sf - inPt.y;
1131
+ const vTransLensq = vTransX * vTransX + vTransY * vTransY;
1132
+ if (vTransLensq <= 2) return { x: vTransX, y: vTransY };
1133
+ shrinkBy = Math.sqrt(vTransLensq / 2);
1134
+ } else {
1135
+ let directionEq = false;
1136
+ if (vPrevX > Number.EPSILON) {
1137
+ if (vNextX > Number.EPSILON) directionEq = true;
1138
+ } else if (vPrevX < -Number.EPSILON) {
1139
+ if (vNextX < -Number.EPSILON) directionEq = true;
1140
+ } else if (Math.sign(vPrevY) === Math.sign(vNextY)) {
1141
+ directionEq = true;
1142
+ }
1143
+ if (directionEq) {
1144
+ vTransX = -vPrevY;
1145
+ vTransY = vPrevX;
1146
+ shrinkBy = Math.sqrt(vPrevLensq);
1147
+ } else {
1148
+ vTransX = vPrevX;
1149
+ vTransY = vPrevY;
1150
+ shrinkBy = Math.sqrt(vPrevLensq / 2);
1151
+ }
1152
+ }
1153
+ return { x: vTransX / shrinkBy, y: vTransY / shrinkBy };
1154
+ };
1155
+ const movements = (ring2) => {
1156
+ const out = [];
1157
+ for (let i = 0, il = ring2.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) {
1158
+ if (j === il) j = 0;
1159
+ if (k === il) k = 0;
1160
+ out[i] = getBevelVec(ring2[i], ring2[j], ring2[k]);
1161
+ }
1162
+ return out;
1163
+ };
1164
+ const contourMovements = movements(contour);
1165
+ const holesMovements = [];
1166
+ let verticesMovements = contourMovements.concat();
1167
+ for (const ahole of holes) {
1168
+ const oneHoleMovements = movements(ahole);
1169
+ holesMovements.push(oneHoleMovements);
1170
+ verticesMovements = verticesMovements.concat(oneHoleMovements);
1171
+ }
1172
+ const v = (x, y, z) => {
1173
+ placeholder.push(x);
1174
+ placeholder.push(y);
1175
+ placeholder.push(z);
1176
+ };
1177
+ const bevelLayer = (b, zOf) => {
1178
+ const t = b / bevelSegments;
1179
+ const z = bevelThickness * Math.cos(t * Math.PI / 2);
1180
+ const bs2 = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset;
1181
+ for (let i = 0; i < contour.length; i++) {
1182
+ const vert = scalePt2(contour[i], contourMovements[i], bs2);
1183
+ v(vert.x, vert.y, zOf(z));
1184
+ }
1185
+ for (let h = 0; h < holes.length; h++) {
1186
+ const ahole = holes[h];
1187
+ const oneHoleMovements = holesMovements[h];
1188
+ for (let i = 0; i < ahole.length; i++) {
1189
+ const vert = scalePt2(ahole[i], oneHoleMovements[i], bs2);
1190
+ v(vert.x, vert.y, zOf(z));
1191
+ }
1192
+ }
1193
+ };
1194
+ for (let b = 0; b < bevelSegments; b++) bevelLayer(b, (z) => -z);
1195
+ const bs = bevelSize + bevelOffset;
1196
+ for (let i = 0; i < vlen; i++) {
1197
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1198
+ v(vert.x, vert.y, 0);
1199
+ }
1200
+ for (let s = 1; s <= steps; s++) {
1201
+ for (let i = 0; i < vlen; i++) {
1202
+ const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
1203
+ v(vert.x, vert.y, depth / steps * s);
1204
+ }
1205
+ }
1206
+ for (let b = bevelSegments - 1; b >= 0; b--) bevelLayer(b, (z) => depth + z);
1207
+ const addVertex = (index) => {
1208
+ verticesArray.push(placeholder[index * 3 + 0]);
1209
+ verticesArray.push(placeholder[index * 3 + 1]);
1210
+ verticesArray.push(placeholder[index * 3 + 2]);
1211
+ };
1212
+ const addUV = (x, y) => {
1213
+ uvArray.push(x);
1214
+ uvArray.push(y);
1215
+ };
1216
+ const at = (index, axis) => verticesArray[index * 3 + axis];
1217
+ const f3 = (a, b, c) => {
1218
+ addVertex(a);
1219
+ addVertex(b);
1220
+ addVertex(c);
1221
+ const nextIndex = verticesArray.length / 3;
1222
+ for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
1223
+ };
1224
+ const f4 = (a, b, c, d) => {
1225
+ addVertex(a);
1226
+ addVertex(b);
1227
+ addVertex(d);
1228
+ addVertex(b);
1229
+ addVertex(c);
1230
+ addVertex(d);
1231
+ const nextIndex = verticesArray.length / 3;
1232
+ const iA = nextIndex - 6;
1233
+ const iB = nextIndex - 3;
1234
+ const iC = nextIndex - 2;
1235
+ const iD = nextIndex - 1;
1236
+ const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
1237
+ const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
1238
+ const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
1239
+ for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
1240
+ };
1241
+ let start = verticesArray.length / 3;
1242
+ if (bevelEnabled) {
1243
+ let offset = 0;
1244
+ for (let i = 0; i < flen; i++) {
1245
+ const face = faces[i];
1246
+ f3(face[2] + offset, face[1] + offset, face[0] + offset);
1247
+ }
1248
+ const layer = steps + bevelSegments * 2;
1249
+ offset = vlen * layer;
1250
+ for (let i = 0; i < flen; i++) {
1251
+ const face = faces[i];
1252
+ f3(face[0] + offset, face[1] + offset, face[2] + offset);
1253
+ }
1254
+ } else {
1255
+ for (let i = 0; i < flen; i++) {
1256
+ const face = faces[i];
1257
+ f3(face[2], face[1], face[0]);
1258
+ }
1259
+ for (let i = 0; i < flen; i++) {
1260
+ const face = faces[i];
1261
+ f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
1262
+ }
1263
+ }
1264
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
1265
+ start = verticesArray.length / 3;
1266
+ const sidewalls = (ring2, layeroffset2) => {
1267
+ let i = ring2.length;
1268
+ while (--i >= 0) {
1269
+ const j = i;
1270
+ let k = i - 1;
1271
+ if (k < 0) k = ring2.length - 1;
1272
+ for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) {
1273
+ const slen1 = vlen * s;
1274
+ const slen2 = vlen * (s + 1);
1275
+ f4(layeroffset2 + j + slen1, layeroffset2 + k + slen1, layeroffset2 + k + slen2, layeroffset2 + j + slen2);
1276
+ }
1277
+ }
1278
+ };
1279
+ let layeroffset = 0;
1280
+ sidewalls(contour, layeroffset);
1281
+ layeroffset += contour.length;
1282
+ for (const ahole of holes) {
1283
+ sidewalls(ahole, layeroffset);
1284
+ layeroffset += ahole.length;
1285
+ }
1286
+ groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
1287
+ const positions = new Float32Array(verticesArray);
1288
+ return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
1289
+ }
1290
+ var PARAMETER_KEYS = {
1291
+ box: ["size", "segments"],
1292
+ cylinder: ["radiusTop", "radiusBottom", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1293
+ cone: ["radius", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
1294
+ sphere: ["radius", "widthSegments", "heightSegments", "phiStart", "phiLength", "thetaStart", "thetaLength"],
1295
+ plane: ["width", "height", "widthSegments", "heightSegments"],
1296
+ circle: ["radius", "segments", "thetaStart", "thetaLength"],
1297
+ ring: ["innerRadius", "outerRadius", "thetaSegments", "phiSegments", "thetaStart", "thetaLength"],
1298
+ lathe: ["points", "segments", "phiStart", "phiLength"],
1299
+ torus: ["radius", "tube", "radialSegments", "tubularSegments", "arc"],
1300
+ extrude: ["shape", "depth", "steps", "bevel"],
1301
+ difference: ["size", "subtract"],
1302
+ mesh: ["positions", "normals", "uvs", "indices"]
1303
+ };
1304
+ var SEGMENT_KEYS = {
1305
+ cylinder: ["radialSegments"],
1306
+ cone: ["radialSegments"],
1307
+ sphere: ["widthSegments", "heightSegments"],
1308
+ circle: ["segments"],
1309
+ ring: ["thetaSegments"],
1310
+ lathe: ["segments"],
1311
+ torus: ["radialSegments", "tubularSegments"]
1312
+ };
1313
+ var SEGMENT_DEFAULTS = { radialSegments: 32, widthSegments: 32, heightSegments: 16, segments: 12, thetaSegments: 32, tubularSegments: 48 };
1314
+ var segmentDefault = (kind, key) => kind === "circle" && key === "segments" ? 32 : kind === "torus" && key === "radialSegments" ? 12 : SEGMENT_DEFAULTS[key];
1315
+ function primitiveParameters(node, options = {}) {
1316
+ const out = {};
1317
+ const source = node;
1318
+ for (const key of PARAMETER_KEYS[node.kind]) if (source[key] !== void 0) out[key] = source[key];
1319
+ const detail = options.detail ?? 1;
1320
+ if (detail !== 1) {
1321
+ const floor = options.minSegments ?? 3;
1322
+ for (const key of SEGMENT_KEYS[node.kind] ?? []) {
1323
+ const given = out[key] ?? segmentDefault(node.kind, key);
1324
+ out[key] = Math.max(floor, Math.round(given * detail));
1325
+ }
1326
+ }
1327
+ return out;
1328
+ }
1329
+ function mesh(p) {
1330
+ const positions = new Float32Array(p.positions);
1331
+ const indices = p.indices ? indexArray(p.indices) : void 0;
1332
+ return {
1333
+ type: "BufferGeometry",
1334
+ parameters: {},
1335
+ positions,
1336
+ normals: p.normals ? new Float32Array(p.normals) : computeVertexNormals(positions, indices),
1337
+ uvs: p.uvs ? new Float32Array(p.uvs) : new Float32Array(positions.length / 3 * 2),
1338
+ ...indices ? { indices } : {},
1339
+ groups: []
1340
+ };
1341
+ }
1342
+ function make(kind, p) {
1343
+ switch (kind) {
1344
+ case "box": {
1345
+ const size = p.size ?? [1, 1, 1];
1346
+ const segments = p.segments ?? [1, 1, 1];
1347
+ return box(size[0], size[1], size[2], segments[0], segments[1], segments[2]);
1348
+ }
1349
+ case "cylinder":
1350
+ return cylinder(p.radiusTop, p.radiusBottom, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1351
+ case "cone":
1352
+ return cone(p.radius, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
1353
+ case "sphere":
1354
+ return sphere(p.radius, p.widthSegments, p.heightSegments, p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
1355
+ case "plane":
1356
+ return plane(p.width, p.height, p.widthSegments, p.heightSegments);
1357
+ case "circle":
1358
+ return circle(p.radius, p.segments, p.thetaStart, p.thetaLength);
1359
+ case "ring":
1360
+ return ring(p.innerRadius, p.outerRadius, p.thetaSegments, p.phiSegments, p.thetaStart, p.thetaLength);
1361
+ case "lathe":
1362
+ return lathe(p.points, p.segments, p.phiStart, p.phiLength);
1363
+ case "torus":
1364
+ return torus(p.radius, p.tube, p.radialSegments, p.tubularSegments, p.arc);
1365
+ case "extrude":
1366
+ return extrude(p.shape, p.depth, p.steps, p.bevel);
1367
+ case "difference":
1368
+ return difference(p.size, p.subtract ?? []);
1369
+ case "mesh":
1370
+ return mesh(p);
1371
+ default:
1372
+ throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
1373
+ }
1374
+ }
1375
+ var templates = /* @__PURE__ */ new Map();
1376
+ var templateLimit = 4096;
1377
+ var stats = { hits: 0, misses: 0 };
1378
+ function buildPrimitive(node, options = {}) {
1379
+ const parameters = primitiveParameters(node, options);
1380
+ if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
1381
+ const key = `${node.kind}:${canonicalJson(parameters)}`;
1382
+ const cached = templates.get(key);
1383
+ if (cached) {
1384
+ stats.hits++;
1385
+ templates.delete(key);
1386
+ templates.set(key, cached);
1387
+ return cached;
1388
+ }
1389
+ stats.misses++;
1390
+ const built = make(node.kind, parameters);
1391
+ templates.set(key, built);
1392
+ if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
1393
+ return built;
1394
+ }
1395
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
1396
+ var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
1397
+ var str = (v) => typeof v === "string" ? null : "expected a string";
1398
+ var int = (min) => (v) => typeof v === "number" && Number.isInteger(v) && v >= min ? null : `expected an integer >= ${min}`;
1399
+ var range = (lo, hi, loOpen = false) => (v) => typeof v === "number" && (loOpen ? v > lo : v >= lo) && v <= hi ? null : `expected a number in ${loOpen ? "(" : "["}${lo}, ${hi}]`;
1400
+ var tuple = (n, item = num) => (v) => {
1401
+ if (!Array.isArray(v) || v.length !== n) return `expected an array of ${n}`;
1402
+ for (const x of v) if (item(x)) return `expected an array of ${n} numbers`;
1403
+ return null;
1404
+ };
1405
+ var list = (item, min = 0) => (v) => {
1406
+ if (!Array.isArray(v)) return "expected an array";
1407
+ if (v.length < min) return `expected at least ${min} items`;
1408
+ for (let i = 0; i < v.length; i++) {
1409
+ const e = item(v[i]);
1410
+ if (e) return `[${i}]: ${e}`;
1411
+ }
1412
+ return null;
1413
+ };
1414
+ var color = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 16777215 || typeof v === "string" && /^#[0-9a-fA-F]{6}$/.test(v) ? null : 'expected 0xRRGGBB or "#rrggbb"';
1415
+ var oneOf = (values) => (v) => values.includes(v) ? null : `expected one of ${values.map((x) => JSON.stringify(x)).join(", ")}`;
1416
+ var object = (fields, required = []) => (v) => {
1417
+ if (!v || typeof v !== "object" || Array.isArray(v)) return "expected an object";
1418
+ const record = v;
1419
+ for (const key of required) if (record[key] === void 0) return `${key}: required`;
1420
+ for (const key of Object.keys(record)) {
1421
+ if (record[key] === void 0) continue;
1422
+ const check = fields[key];
1423
+ if (!check) return `${key}: unknown field`;
1424
+ const e = check(record[key]);
1425
+ if (e) return `${key}: ${e}`;
1426
+ }
1427
+ return null;
1428
+ };
1429
+ var ORDERS = ["XYZ", "YXZ", "ZXY", "ZYX", "YZX", "XZY"];
1430
+ var TRANSFORM = {
1431
+ position: tuple(3),
1432
+ rotation: tuple(3),
1433
+ rotationOrder: oneOf(ORDERS),
1434
+ scale: (v) => typeof v === "number" ? num(v) : tuple(3)(v),
1435
+ heading: num,
1436
+ matrix: tuple(16)
1437
+ };
1438
+ var UV = object({ u0: num, v0: num, u1: num, v1: num }, ["u0", "v0", "u1", "v1"]);
1439
+ var INSTANCE = object({ ...TRANSFORM, color });
1440
+ var COMMON = {
1441
+ ...TRANSFORM,
1442
+ kind: str,
1443
+ name: str,
1444
+ part: str,
1445
+ material: str,
1446
+ color,
1447
+ uv: UV,
1448
+ instances: list(INSTANCE),
1449
+ minDetail: range(0, 1, true),
1450
+ tags: list(str)
1451
+ };
1452
+ var vec2 = tuple(2);
1453
+ var BEVEL = (v) => v === false ? null : object({ thickness: num, size: num, offset: num, segments: int(1) })(v);
1454
+ var KINDS = {
1455
+ group: { fields: {} },
1456
+ box: { fields: { size: tuple(3), segments: tuple(3, int(1)) } },
1457
+ cylinder: { fields: { radiusTop: num, radiusBottom: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
1458
+ cone: { fields: { radius: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
1459
+ sphere: { fields: { radius: num, widthSegments: int(3), heightSegments: int(2), phiStart: num, phiLength: num, thetaStart: num, thetaLength: num } },
1460
+ plane: { fields: { width: num, height: num, widthSegments: int(1), heightSegments: int(1) } },
1461
+ circle: { fields: { radius: num, segments: int(3), thetaStart: num, thetaLength: num } },
1462
+ ring: { fields: { innerRadius: num, outerRadius: num, thetaSegments: int(3), phiSegments: int(1), thetaStart: num, thetaLength: num } },
1463
+ lathe: { fields: { points: list(vec2, 2), segments: int(1), phiStart: num, phiLength: num }, required: ["points"] },
1464
+ torus: { fields: { radius: num, tube: num, radialSegments: int(1), tubularSegments: int(1), arc: num } },
1465
+ extrude: {
1466
+ fields: { shape: object({ outer: list(vec2, 3), holes: list(list(vec2, 3)) }, ["outer"]), depth: num, steps: int(1), bevel: BEVEL },
1467
+ required: ["shape"]
1468
+ },
1469
+ difference: { fields: { size: tuple(3), subtract: list(object({ min: vec2, max: vec2 }, ["min", "max"])) }, required: ["size", "subtract"] },
1470
+ mesh: {
1471
+ fields: {
1472
+ positions: (v) => list(num)(v) ?? (v.length % 3 === 0 ? null : "expected a multiple of 3 numbers"),
1473
+ normals: list(num),
1474
+ uvs: list(num),
1475
+ indices: list(int(0))
1476
+ },
1477
+ required: ["positions"]
1478
+ },
1479
+ point: { fields: { data: () => null } },
1480
+ custom: { fields: { generator: str, params: () => null }, required: ["generator"] }
1481
+ };
1482
+
1483
+ // ../game-shapes-three/dist/index.js
1484
+ var UV_BEFORE_NORMAL = /* @__PURE__ */ new Set(["LatheGeometry", "ExtrudeGeometry"]);
1485
+ var INDEX_FIRST = /* @__PURE__ */ new Set(["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry"]);
1486
+ var isPrimitive = (value) => typeof value.type === "string";
1487
+ function threeParameters(THREE, p) {
1488
+ if (p.type === "LatheGeometry") {
1489
+ const points = p.parameters.points.map(({ x, y }) => new THREE.Vector2(x, y));
1490
+ return { ...p.parameters, points };
1491
+ }
1492
+ return { ...p.parameters };
1493
+ }
1494
+ function toGeometry(THREE, source, options = {}) {
1495
+ const copy = options.copy !== false;
1496
+ const primitive = isPrimitive(source) ? source : null;
1497
+ const ThreeClass = primitive && primitive.type !== "BufferGeometry" ? THREE[primitive.type] : void 0;
1498
+ const geometry = ThreeClass ? Reflect.construct(THREE.BufferGeometry, [], ThreeClass) : new THREE.BufferGeometry();
1499
+ if (primitive && ThreeClass) {
1500
+ Object.assign(geometry, { type: primitive.type, parameters: threeParameters(THREE, primitive) });
1501
+ }
1502
+ const attribute = (array, size) => copy ? new THREE.Float32BufferAttribute(array, size) : new THREE.BufferAttribute(array, size);
1503
+ const index = source.indices;
1504
+ const setIndex = () => {
1505
+ if (!index) return;
1506
+ geometry.setIndex(copy ? index instanceof Uint32Array ? new THREE.Uint32BufferAttribute(index, 1) : new THREE.Uint16BufferAttribute(index, 1) : new THREE.BufferAttribute(index, 1));
1507
+ };
1508
+ const type = primitive?.type ?? "BufferGeometry";
1509
+ if (INDEX_FIRST.has(type)) setIndex();
1510
+ geometry.setAttribute("position", attribute(source.positions, 3));
1511
+ if (UV_BEFORE_NORMAL.has(type)) {
1512
+ geometry.setAttribute("uv", attribute(source.uvs, 2));
1513
+ geometry.setAttribute("normal", attribute(source.normals, 3));
1514
+ } else {
1515
+ geometry.setAttribute("normal", attribute(source.normals, 3));
1516
+ geometry.setAttribute("uv", attribute(source.uvs, 2));
1517
+ }
1518
+ if (!primitive && source.colors) geometry.setAttribute("color", attribute(source.colors, 3));
1519
+ if (!INDEX_FIRST.has(type)) setIndex();
1520
+ if (primitive) for (const group of primitive.groups) geometry.addGroup(group.start, group.count, group.materialIndex);
1521
+ return geometry;
1522
+ }
1523
+ var SHAPE_GEOMETRY_TYPES = ["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry", "ExtrudeGeometry"];
1524
+ function linePoints(path) {
1525
+ if (path.autoClose || path.curves.length === 0) return null;
1526
+ const points = [];
1527
+ for (const [index, curve] of path.curves.entries()) {
1528
+ if (!curve.isLineCurve || !curve.v1 || !curve.v2) return null;
1529
+ if (index === 0) points.push([curve.v1.x, curve.v1.y]);
1530
+ else {
1531
+ const last = points[points.length - 1];
1532
+ if (last[0] !== curve.v1.x || last[1] !== curve.v1.y) return null;
1533
+ }
1534
+ points.push([curve.v2.x, curve.v2.y]);
1535
+ }
1536
+ return points;
1537
+ }
1538
+ function extrudeOf(shapes, options = {}) {
1539
+ if (Array.isArray(shapes) || !shapes || options.extrudePath !== void 0 || options.UVGenerator !== void 0) return null;
1540
+ const shape = shapes;
1541
+ const outer = linePoints(shape);
1542
+ if (!outer) return null;
1543
+ const holes = [];
1544
+ for (const hole of shape.holes ?? []) {
1545
+ const points = linePoints(hole);
1546
+ if (!points) return null;
1547
+ holes.push(points);
1548
+ }
1549
+ const outline = holes.length ? { outer, holes } : { outer };
1550
+ const bevel = options.bevelEnabled === false ? false : {
1551
+ ...options.bevelThickness !== void 0 ? { thickness: options.bevelThickness } : {},
1552
+ ...options.bevelSize !== void 0 ? { size: options.bevelSize } : {},
1553
+ ...options.bevelOffset !== void 0 ? { offset: options.bevelOffset } : {},
1554
+ ...options.bevelSegments !== void 0 ? { segments: options.bevelSegments } : {}
1555
+ };
1556
+ return extrude(outline, options.depth, options.steps, bevel);
1557
+ }
1558
+ var namespaces = /* @__PURE__ */ new WeakMap();
1559
+ var shims = /* @__PURE__ */ new WeakSet();
1560
+ function shapeGeometries(THREE, options = {}) {
1561
+ if (shims.has(THREE)) return THREE;
1562
+ const cache = options.cache !== false;
1563
+ const key = cache ? "cache" : "nocache";
1564
+ let byKey = namespaces.get(THREE);
1565
+ if (!byKey) {
1566
+ byKey = /* @__PURE__ */ new Map();
1567
+ namespaces.set(THREE, byKey);
1568
+ }
1569
+ const existing = byKey.get(key);
1570
+ if (existing) return existing;
1571
+ const made = (p) => toGeometry(THREE, p);
1572
+ const viaCache = (node, direct) => cache ? buildPrimitive(node) : direct();
1573
+ const defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
1574
+ const constructors = {
1575
+ BoxGeometry: (width, height, depth, ws = 1, hs = 1, ds = 1) => made(viaCache({ kind: "box", size: [width ?? 1, height ?? 1, depth ?? 1], segments: [ws, hs, ds] }, () => box(width, height, depth, ws, hs, ds))),
1576
+ CylinderGeometry: (radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cylinder", ...defined({ radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cylinder(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
1577
+ ConeGeometry: (radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cone", ...defined({ radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cone(radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
1578
+ SphereGeometry: (radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength) => made(viaCache({ kind: "sphere", ...defined({ radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength }) }, () => sphere(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength))),
1579
+ PlaneGeometry: (width, height, widthSegments, heightSegments) => made(viaCache({ kind: "plane", ...defined({ width, height, widthSegments, heightSegments }) }, () => plane(width, height, widthSegments, heightSegments))),
1580
+ CircleGeometry: (radius, segments, thetaStart, thetaLength) => made(viaCache({ kind: "circle", ...defined({ radius, segments, thetaStart, thetaLength }) }, () => circle(radius, segments, thetaStart, thetaLength))),
1581
+ RingGeometry: (innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) => made(viaCache({ kind: "ring", ...defined({ innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength }) }, () => ring(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength))),
1582
+ LatheGeometry: (points, segments, phiStart, phiLength) => {
1583
+ if (!points) return new THREE.LatheGeometry(void 0, segments, phiStart, phiLength);
1584
+ const profile = points.map((p) => [p.x, p.y]);
1585
+ return made(viaCache({ kind: "lathe", points: profile, ...defined({ segments, phiStart, phiLength }) }, () => lathe(profile, segments, phiStart, phiLength)));
1586
+ },
1587
+ TorusGeometry: (radius, tube, radialSegments, tubularSegments, arc) => made(viaCache({ kind: "torus", ...defined({ radius, tube, radialSegments, tubularSegments, arc }) }, () => torus(radius, tube, radialSegments, tubularSegments, arc))),
1588
+ ExtrudeGeometry: (shapes, extrudeOptions) => {
1589
+ const buffers = shapes === void 0 ? null : extrudeOf(shapes, extrudeOptions);
1590
+ if (!buffers) return new THREE.ExtrudeGeometry(shapes, extrudeOptions);
1591
+ const geometry = toGeometry(THREE, buffers);
1592
+ geometry.parameters = { shapes, options: extrudeOptions ?? {} };
1593
+ return geometry;
1594
+ }
1595
+ };
1596
+ const namespace = { ...THREE };
1597
+ for (const type of SHAPE_GEOMETRY_TYPES) {
1598
+ const make2 = constructors[type];
1599
+ const Constructor = function(...args) {
1600
+ return make2(...args);
1601
+ };
1602
+ Constructor.prototype = THREE[type].prototype;
1603
+ Object.defineProperty(Constructor, "name", { value: type });
1604
+ namespace[type] = Constructor;
1605
+ }
1606
+ const result = namespace;
1607
+ byKey.set(key, result);
1608
+ shims.add(result);
1609
+ return result;
1610
+ }
1611
+
95
1612
  // ../game-props3d/dist/index.js
96
1613
  var T = (id, interior, w, d, h, y, solid, group) => ({ id, interior, w, d, h, y, solid, group });
97
1614
  var TYPE_DEFS = [
@@ -172,19 +1689,19 @@ var GameAnimalsThree = (() => {
172
1689
  for (const key in owner) if (!skip.includes(key)) walk(owner[key], 0);
173
1690
  return found;
174
1691
  };
175
- var isVisibleInTree = (object) => {
176
- for (let node = object; node; node = node.parent) if (!node.visible) return false;
1692
+ var isVisibleInTree = (object2) => {
1693
+ for (let node = object2; node; node = node.parent) if (!node.visible) return false;
177
1694
  return true;
178
1695
  };
179
1696
  var createNodeSkin = (THREE, root, options = {}) => {
180
1697
  const referenced = new Set(options.nodes ?? []);
181
1698
  const nodes = [];
182
1699
  const indexOf = /* @__PURE__ */ new Map();
183
- root.traverse((object) => {
184
- const mesh2 = object;
185
- if (!mesh2.isMesh || object.children.length > 0 || referenced.has(object)) {
186
- indexOf.set(object, nodes.length);
187
- nodes.push(object);
1700
+ root.traverse((object2) => {
1701
+ const mesh22 = object2;
1702
+ if (!mesh22.isMesh || object2.children.length > 0 || referenced.has(object2)) {
1703
+ indexOf.set(object2, nodes.length);
1704
+ nodes.push(object2);
188
1705
  }
189
1706
  });
190
1707
  const positions = [];
@@ -193,7 +1710,7 @@ var GameAnimalsThree = (() => {
193
1710
  const surfaces = [];
194
1711
  const nodeIndices = [];
195
1712
  const samples = /* @__PURE__ */ new Map();
196
- const color = new THREE.Color();
1713
+ const color2 = new THREE.Color();
197
1714
  const vertex = new THREE.Vector3();
198
1715
  const normal = new THREE.Vector3();
199
1716
  const sampleOf = (texture) => {
@@ -207,22 +1724,22 @@ var GameAnimalsThree = (() => {
207
1724
  samples.set(texture, sample);
208
1725
  return sample;
209
1726
  };
210
- root.traverse((object) => {
211
- const mesh2 = object;
212
- if (!mesh2.isMesh || !isVisibleInTree(object)) return;
213
- const material2 = Array.isArray(mesh2.material) ? mesh2.material[0] : mesh2.material;
214
- const geometry2 = mesh2.geometry;
1727
+ root.traverse((object2) => {
1728
+ const mesh22 = object2;
1729
+ if (!mesh22.isMesh || !isVisibleInTree(object2)) return;
1730
+ const material2 = Array.isArray(mesh22.material) ? mesh22.material[0] : mesh22.material;
1731
+ const geometry2 = mesh22.geometry;
215
1732
  const position = geometry2.attributes.position;
216
1733
  const normalAttribute = geometry2.attributes.normal;
217
1734
  const uv = geometry2.attributes.uv;
218
1735
  const matrix = new THREE.Matrix4();
219
- let nodeIndex = indexOf.get(object);
1736
+ let nodeIndex = indexOf.get(object2);
220
1737
  if (nodeIndex === void 0) {
221
- nodeIndex = indexOf.get(object.parent) ?? 0;
222
- object.updateMatrix();
223
- matrix.copy(object.matrix);
1738
+ nodeIndex = indexOf.get(object2.parent) ?? 0;
1739
+ object2.updateMatrix();
1740
+ matrix.copy(object2.matrix);
224
1741
  }
225
- const normalMatrix = new THREE.Matrix3().getNormalMatrix(matrix);
1742
+ const normalMatrix2 = new THREE.Matrix3().getNormalMatrix(matrix);
226
1743
  const sample = material2.map ? sampleOf(material2.map) : void 0;
227
1744
  const emissive = material2.emissive ? (material2.emissive.r + material2.emissive.g + material2.emissive.b) * (material2.emissiveIntensity ?? 1) : 0;
228
1745
  const index = geometry2.index ? geometry2.index.array : null;
@@ -231,10 +1748,10 @@ var GameAnimalsThree = (() => {
231
1748
  const at = index ? index[item] : item;
232
1749
  vertex.fromBufferAttribute(position, at).applyMatrix4(matrix);
233
1750
  positions.push(vertex.x, vertex.y, vertex.z);
234
- if (normalAttribute) normal.fromBufferAttribute(normalAttribute, at).applyMatrix3(normalMatrix).normalize();
1751
+ if (normalAttribute) normal.fromBufferAttribute(normalAttribute, at).applyMatrix3(normalMatrix2).normalize();
235
1752
  else normal.set(0, 1, 0);
236
1753
  normals.push(normal.x, normal.y, normal.z);
237
- color.copy(material2.color || color.set(1, 1, 1)).convertLinearToSRGB();
1754
+ color2.copy(material2.color || color2.set(1, 1, 1)).convertLinearToSRGB();
238
1755
  if (sample && uv && material2.map) {
239
1756
  const repeat = material2.map.repeat;
240
1757
  const u = (uv.getX(at) * repeat.x % 1 + 1) % 1;
@@ -242,10 +1759,10 @@ var GameAnimalsThree = (() => {
242
1759
  const pixelX = Math.min(sample.width - 1, u * sample.width | 0);
243
1760
  const pixelY = Math.min(sample.height - 1, (1 - v) * sample.height | 0);
244
1761
  const offset = (pixelY * sample.width + pixelX) * 4;
245
- color.multiply(new THREE.Color().setRGB(sample.data[offset] / 255, sample.data[offset + 1] / 255, sample.data[offset + 2] / 255, THREE.SRGBColorSpace));
1762
+ color2.multiply(new THREE.Color().setRGB(sample.data[offset] / 255, sample.data[offset + 1] / 255, sample.data[offset + 2] / 255, THREE.SRGBColorSpace));
246
1763
  }
247
- if (emissive > 0.3) color.lerp(material2.emissive, 0.7);
248
- tints.push(color.r, color.g, color.b);
1764
+ if (emissive > 0.3) color2.lerp(material2.emissive, 0.7);
1765
+ tints.push(color2.r, color2.g, color2.b);
249
1766
  surfaces.push(material2.roughness ?? 0.8, material2.metalness ?? 0, emissive > 0.3 ? 1 : 0);
250
1767
  nodeIndices.push(nodeIndex);
251
1768
  }
@@ -275,12 +1792,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
275
1792
  const depthMaterial = new THREE.MeshDepthMaterial({ depthPacking: THREE.RGBADepthPacking });
276
1793
  depthMaterial.onBeforeCompile = skinVertex;
277
1794
  depthMaterial.customProgramCacheKey = () => `nodeSkinDepth${nodes.length}`;
278
- const mesh = new THREE.Mesh(geometry, material);
279
- mesh.customDepthMaterial = depthMaterial;
280
- mesh.frustumCulled = false;
1795
+ const mesh2 = new THREE.Mesh(geometry, material);
1796
+ mesh2.customDepthMaterial = depthMaterial;
1797
+ mesh2.frustumCulled = false;
281
1798
  root.updateMatrixWorld(true);
282
1799
  return {
283
- mesh,
1800
+ mesh: mesh2,
284
1801
  nodes,
285
1802
  matrices,
286
1803
  update() {
@@ -289,6 +1806,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
289
1806
  }
290
1807
  };
291
1808
  };
1809
+ var LEAF = (() => {
1810
+ const f = [0, 0, 0, -0.5, 0.4, 0, 0.5, 0.4, 0, 0, 1, 0];
1811
+ const positions = [];
1812
+ const normals = [];
1813
+ const uvs = [];
1814
+ const indices = [];
1815
+ for (const side of [1, -1]) {
1816
+ const base = positions.length / 3;
1817
+ for (let i = 0; i < 4; i++) {
1818
+ positions.push(f[i * 3], f[i * 3 + 1], f[i * 3 + 2]);
1819
+ normals.push(0, 0, side);
1820
+ uvs.push(f[i * 3] + 0.5, f[i * 3 + 1]);
1821
+ }
1822
+ if (side === 1) indices.push(base, base + 2, base + 1, base + 1, base + 2, base + 3);
1823
+ else indices.push(base, base + 1, base + 2, base + 1, base + 3, base + 2);
1824
+ }
1825
+ return { positions, normals, uvs, indices };
1826
+ })();
292
1827
 
293
1828
  // src/animate-bird.ts
294
1829
  var { PI } = Math;
@@ -469,26 +2004,26 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
469
2004
  };
470
2005
  };
471
2006
  var addMesh = (THREE, parent, geometry, material, x, y, z, rx, ry, rz) => {
472
- const mesh = new THREE.Mesh(geometry, material);
473
- mesh.position.set(x || 0, y || 0, z || 0);
474
- if (rx || ry || rz) mesh.rotation.set(rx || 0, ry || 0, rz || 0);
475
- mesh.castShadow = true;
476
- mesh.receiveShadow = true;
477
- parent.add(mesh);
478
- return mesh;
2007
+ const mesh2 = new THREE.Mesh(geometry, material);
2008
+ mesh2.position.set(x || 0, y || 0, z || 0);
2009
+ if (rx || ry || rz) mesh2.rotation.set(rx || 0, ry || 0, rz || 0);
2010
+ mesh2.castShadow = true;
2011
+ mesh2.receiveShadow = true;
2012
+ parent.add(mesh2);
2013
+ return mesh2;
479
2014
  };
480
2015
  var createRigTools = (THREE, rig) => {
481
2016
  const tools = {
482
- material(color, extras) {
483
- const material = new THREE.MeshStandardMaterial(Object.assign({ color, roughness: 0.8, metalness: 0, flatShading: false }, extras || {}));
2017
+ material(color2, extras) {
2018
+ const material = new THREE.MeshStandardMaterial(Object.assign({ color: color2, roughness: 0.8, metalness: 0, flatShading: false }, extras || {}));
484
2019
  material.userData.base = material.color.clone();
485
2020
  rig.materials.push(material);
486
2021
  return material;
487
2022
  },
488
2023
  ball(parent, radius, material, x, y, z, sx, sy, sz) {
489
- const mesh = addMesh(THREE, parent, new THREE.SphereGeometry(radius, 16, 12), material, x, y, z);
490
- if (sx) mesh.scale.set(sx, sy, sz);
491
- return mesh;
2024
+ const mesh2 = addMesh(THREE, parent, new THREE.SphereGeometry(radius, 16, 12), material, x, y, z);
2025
+ if (sx) mesh2.scale.set(sx, sy, sz);
2026
+ return mesh2;
492
2027
  },
493
2028
  limb: (parent, radius1, radius2, length, material) => addMesh(THREE, parent, new THREE.CylinderGeometry(radius2, radius1, length, 14, 1).translate(0, -length / 2, 0), material),
494
2029
  joint(parent, radius, material, x, y, z) {
@@ -711,7 +2246,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
711
2246
  outline.push(new THREE.Vector2(Math.max(1e-3, point.y), -length / 2 + point.x * length));
712
2247
  }
713
2248
  outline.push(new THREE.Vector2(1e-3, length / 2));
714
- return new THREE.LatheGeometry(outline, segments || 22).rotateX(Math.PI / 2);
2249
+ return new (shapeGeometries(THREE)).LatheGeometry(outline, segments || 22).rotateX(Math.PI / 2);
715
2250
  };
716
2251
 
717
2252
  // src/quadruped.ts
@@ -836,19 +2371,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
836
2371
  const { L, R: radius } = spec;
837
2372
  const sideX = radius * (spec.sx || 1);
838
2373
  const sideY = radius * (spec.sy || 1);
839
- const list = [blood];
2374
+ const list2 = [blood];
840
2375
  const wound = (parent, size, x, y, z, sx, sy, sz) => tools.ball(parent, size, rig.woundMaterial || (rig.woundMaterial = new THREE.MeshStandardMaterial({ color: 5902862, roughness: 0.45 })), x, y, z, sx || 1, sy || 1, sz || 1);
841
- list.push(wound(bodyGroup, radius * 0.28, sideX * 0.92, sideY * 0.15, L * 0.05, 0.35, 1, 1.3));
842
- list.push(wound(bodyGroup, radius * 0.2, -sideX * 0.9, sideY * 0.3, -L * 0.2, 0.35, 1, 1.1));
843
- list.push(wound(neck, neckSpec.r * 0.5, neckSpec.r * 0.85, neckSpec.len * 0.5, 0, 0.4, 1, 1));
2376
+ list2.push(wound(bodyGroup, radius * 0.28, sideX * 0.92, sideY * 0.15, L * 0.05, 0.35, 1, 1.3));
2377
+ list2.push(wound(bodyGroup, radius * 0.2, -sideX * 0.9, sideY * 0.3, -L * 0.2, 0.35, 1, 1.1));
2378
+ list2.push(wound(neck, neckSpec.r * 0.5, neckSpec.r * 0.85, neckSpec.len * 0.5, 0, 0.4, 1, 1));
844
2379
  const bone = tools.material(14274488, { roughness: 0.6 });
845
2380
  for (let index = 0; index < RIB_COUNT; index++) {
846
2381
  const rib = addMesh(THREE, bodyGroup, new THREE.TorusGeometry(radius * 0.95, radius * 0.035, 6, 16, Math.PI * 0.42), bone, sideX * 0.12, sideY * 0.05, L * 0.12 - index * L * 0.07, 0, Math.PI / 2, -0.3);
847
2382
  rib.scale.set(1, 1.02 * (spec.sy || 1), spec.sx || 1);
848
- list.push(rib);
2383
+ list2.push(rib);
849
2384
  }
850
- rig.infectionParts.push(...list);
851
- list.forEach((part) => part.visible = false);
2385
+ rig.infectionParts.push(...list2);
2386
+ list2.forEach((part) => part.visible = false);
852
2387
  };
853
2388
  var buildQuadruped = (THREE, rig, options) => {
854
2389
  const spec = rig.species.body;
@@ -888,9 +2423,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
888
2423
  const material = index % 4 === 1 || index % 4 === 2 ? secondary : primary;
889
2424
  const group = new THREE.Group();
890
2425
  rig.trunk.add(group);
891
- const sphere = tools.ball(group, radius, material, 0, radius, 0, 1, 0.78, 1.5);
892
- sphere.userData.joint = index % 3 === 0 ? 1 : 0;
893
- if (index % 3 === 0) rig.joints.push(sphere);
2426
+ const sphere2 = tools.ball(group, radius, material, 0, radius, 0, 1, 0.78, 1.5);
2427
+ sphere2.userData.joint = index % 3 === 0 ? 1 : 0;
2428
+ if (index % 3 === 0) rig.joints.push(sphere2);
894
2429
  rig.segments.push({ group, radius });
895
2430
  }
896
2431
  const head = new THREE.Group();
@@ -1379,7 +2914,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
1379
2914
  animals?.setVisualState(entry.rig, visualStateOf(entry.rig.species.outcome, true, state === "dead"));
1380
2915
  };
1381
2916
  const clearRings = () => {
1382
- for (const ring of rings) ring.parent?.remove(ring);
2917
+ for (const ring2 of rings) ring2.parent?.remove(ring2);
1383
2918
  rings = [];
1384
2919
  };
1385
2920
  const refreshLayers = () => {
@@ -1389,18 +2924,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
1389
2924
  animals.setJointView(entry.rig, host.bench.layer("joints"));
1390
2925
  animals.setSplit(entry.rig, host.bench.layer("split"));
1391
2926
  }
1392
- const ring = (radius, color, parent) => {
1393
- const mesh = new three.Mesh(new three.RingGeometry(radius - 0.15, radius, 96).rotateX(-Math.PI / 2), new three.MeshBasicMaterial({ color, transparent: true, opacity: 0.55, depthWrite: false }));
1394
- mesh.position.y = 0.03;
1395
- parent.add(mesh);
1396
- rings.push(mesh);
2927
+ const ring2 = (radius, color2, parent) => {
2928
+ const mesh2 = new three.Mesh(new three.RingGeometry(radius - 0.15, radius, 96).rotateX(-Math.PI / 2), new three.MeshBasicMaterial({ color: color2, transparent: true, opacity: 0.55, depthWrite: false }));
2929
+ mesh2.position.y = 0.03;
2930
+ parent.add(mesh2);
2931
+ rings.push(mesh2);
1397
2932
  };
1398
2933
  for (const entry of placed.values()) {
1399
2934
  if (!entry.rig.root.parent) continue;
1400
2935
  const radius = entry.rig.species.soundRangeMetres;
1401
- if (host.bench.layer("sound_range") && radius) ring(radius, 4500223, entry.rig.root);
2936
+ if (host.bench.layer("sound_range") && radius) ring2(radius, 4500223, entry.rig.root);
1402
2937
  const lure = entry.rig.species.outbreak?.carcass?.lureRadiusMetres;
1403
- if (host.bench.layer("carcass_lure") && lure) ring(lure, 16733491, entry.rig.root);
2938
+ if (host.bench.layer("carcass_lure") && lure) ring2(lure, 16733491, entry.rig.root);
1404
2939
  }
1405
2940
  };
1406
2941
  const addSkins = () => {
@@ -1412,8 +2947,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
1412
2947
  const rig = animals.build(animal);
1413
2948
  const skin = animals.createSkin(rig);
1414
2949
  rig.root.position.set(SKIN_FIELD.x0 + index % SKIN_FIELD.columns * SKIN_FIELD.gap, 0, SKIN_FIELD.z0 - Math.floor(index / SKIN_FIELD.columns) * SKIN_FIELD.gap);
1415
- rig.root.traverse((object) => {
1416
- if (object.isMesh) object.visible = false;
2950
+ rig.root.traverse((object2) => {
2951
+ if (object2.isMesh) object2.visible = false;
1417
2952
  });
1418
2953
  skinGroup.add(rig.root, skin.mesh);
1419
2954
  skins.push({ rig, skin });
@@ -1498,11 +3033,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
1498
3033
  const countMeshes = (only) => {
1499
3034
  let drawCalls = 0;
1500
3035
  let triangles = 0;
1501
- const count = (root) => root.traverse((object) => {
1502
- const mesh = object;
1503
- if (!mesh.isMesh || !mesh.visible) return;
3036
+ const count = (root) => root.traverse((object2) => {
3037
+ const mesh2 = object2;
3038
+ if (!mesh2.isMesh || !mesh2.visible) return;
1504
3039
  drawCalls += 1;
1505
- const geometry = mesh.geometry;
3040
+ const geometry = mesh2.geometry;
1506
3041
  triangles += geometry.index ? geometry.index.count / 3 : geometry.attributes.position.count / 3;
1507
3042
  });
1508
3043
  if (only !== "skins") for (const entry of placed.values()) count(entry.rig.root);
@@ -1558,7 +3093,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
1558
3093
  { kind: "action", id: "bite_all", name: text.actions.bite_all },
1559
3094
  { kind: "action", id: "recover", name: text.actions.recover },
1560
3095
  { kind: "action", id: "call", name: text.actions.call },
1561
- { kind: "action", id: "skin_40", name: text.actions.skin_40 },
3096
+ { kind: "action", id: "skin_40", name: text.actions.skin_40, slow: true },
1562
3097
  { kind: "action", id: "clear_skins", name: text.actions.clear_skins }
1563
3098
  ],
1564
3099
  layers: () => ["joints", "split", "sound_range", "carcass_lure"].map((id) => ({ id, name: text.layers[id], default: false })),
@@ -1594,15 +3129,16 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
1594
3129
  // src/index.ts
1595
3130
  var countTriangles = (root) => {
1596
3131
  let triangles = 0;
1597
- root.traverse((object) => {
1598
- const mesh = object;
1599
- if (!mesh.isMesh) return;
1600
- const geometry = mesh.geometry;
3132
+ root.traverse((object2) => {
3133
+ const mesh2 = object2;
3134
+ if (!mesh2.isMesh) return;
3135
+ const geometry = mesh2.geometry;
1601
3136
  triangles += geometry.index ? geometry.index.count / 3 : geometry.attributes.position.count / 3;
1602
3137
  });
1603
3138
  return Math.round(triangles);
1604
3139
  };
1605
- var createAnimalsThree = (THREE, options = {}) => {
3140
+ var createAnimalsThree = (host, options = {}) => {
3141
+ const THREE = shapeGeometries(host);
1606
3142
  const random = options.random ?? createStream(options.seed ?? 7);
1607
3143
  const documentOf = () => {
1608
3144
  const found = options.document ?? globalThis.document;
@@ -1619,8 +3155,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
1619
3155
  else if (species.body.kind === "bird") buildBird(THREE, rig, { extra: BIRD_EXTRAS[species.id] });
1620
3156
  else buildQuadruped(THREE, rig, { document: species.body.texture ? documentOf() : void 0, extra: QUADRUPED_EXTRAS[species.id] });
1621
3157
  rig.triangles = countTriangles(rig.root);
1622
- rig.root.traverse((object) => {
1623
- if (object.isMesh) object.userData.speciesId = species.id;
3158
+ rig.root.traverse((object2) => {
3159
+ if (object2.isMesh) object2.userData.speciesId = species.id;
1624
3160
  });
1625
3161
  return rig;
1626
3162
  },