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