@adatechnology/game-vehicles 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.global.js +1723 -135
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +36 -4
- package/dist/index.js.map +1 -1
- package/package.json +9 -6
package/dist/index.global.js
CHANGED
|
@@ -51,6 +51,1523 @@ var GameVehicles = (() => {
|
|
|
51
51
|
wrapThreeForLod: () => wrapThreeForLod
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
// ../game-shapes/dist/chunk-R4RY23RH.js
|
|
55
|
+
function canonicalJson(value) {
|
|
56
|
+
if (value === null) return "null";
|
|
57
|
+
if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
|
|
58
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
59
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
60
|
+
if (typeof value === "function") throw new Error("game-shapes: a function cannot be part of a shape (register it as a generator)");
|
|
61
|
+
if (ArrayBuffer.isView(value)) return canonicalJson(Array.from(value));
|
|
62
|
+
if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalJson(item)).join(",")}]`;
|
|
63
|
+
if (typeof value === "object") {
|
|
64
|
+
const record = value;
|
|
65
|
+
const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
|
|
66
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
67
|
+
}
|
|
68
|
+
return "null";
|
|
69
|
+
}
|
|
70
|
+
function needsUint32(array) {
|
|
71
|
+
for (let i = array.length - 1; i >= 0; --i) if (array[i] >= 65535) return true;
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
var indexArray = (indices) => needsUint32(indices) ? new Uint32Array(indices) : new Uint16Array(indices);
|
|
75
|
+
function pack(type, parameters, a) {
|
|
76
|
+
return {
|
|
77
|
+
type,
|
|
78
|
+
parameters,
|
|
79
|
+
positions: new Float32Array(a.vertices),
|
|
80
|
+
normals: new Float32Array(a.normals),
|
|
81
|
+
uvs: new Float32Array(a.uvs),
|
|
82
|
+
...a.indices ? { indices: indexArray(a.indices) } : {},
|
|
83
|
+
groups: a.groups
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
var normalize3 = (x, y, z) => {
|
|
87
|
+
const length = Math.sqrt(x * x + y * y + z * z);
|
|
88
|
+
const s = 1 / (length || 1);
|
|
89
|
+
return [x * s, y * s, z * s];
|
|
90
|
+
};
|
|
91
|
+
function box(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
|
|
92
|
+
const parameters = { width, height, depth, widthSegments, heightSegments, depthSegments };
|
|
93
|
+
widthSegments = Math.floor(widthSegments);
|
|
94
|
+
heightSegments = Math.floor(heightSegments);
|
|
95
|
+
depthSegments = Math.floor(depthSegments);
|
|
96
|
+
const indices = [];
|
|
97
|
+
const vertices = [];
|
|
98
|
+
const normals = [];
|
|
99
|
+
const uvs = [];
|
|
100
|
+
const groups = [];
|
|
101
|
+
let numberOfVertices = 0;
|
|
102
|
+
let groupStart = 0;
|
|
103
|
+
const buildPlane = (u, v, w, udir, vdir, width2, height2, depth2, gridX, gridY, materialIndex) => {
|
|
104
|
+
const segmentWidth = width2 / gridX;
|
|
105
|
+
const segmentHeight = height2 / gridY;
|
|
106
|
+
const widthHalf = width2 / 2;
|
|
107
|
+
const heightHalf = height2 / 2;
|
|
108
|
+
const depthHalf = depth2 / 2;
|
|
109
|
+
const gridX1 = gridX + 1;
|
|
110
|
+
const gridY1 = gridY + 1;
|
|
111
|
+
let vertexCounter = 0;
|
|
112
|
+
let groupCount = 0;
|
|
113
|
+
const vector = { x: 0, y: 0, z: 0 };
|
|
114
|
+
for (let iy = 0; iy < gridY1; iy++) {
|
|
115
|
+
const y = iy * segmentHeight - heightHalf;
|
|
116
|
+
for (let ix = 0; ix < gridX1; ix++) {
|
|
117
|
+
const x = ix * segmentWidth - widthHalf;
|
|
118
|
+
vector[u] = x * udir;
|
|
119
|
+
vector[v] = y * vdir;
|
|
120
|
+
vector[w] = depthHalf;
|
|
121
|
+
vertices.push(vector.x, vector.y, vector.z);
|
|
122
|
+
vector[u] = 0;
|
|
123
|
+
vector[v] = 0;
|
|
124
|
+
vector[w] = depth2 > 0 ? 1 : -1;
|
|
125
|
+
normals.push(vector.x, vector.y, vector.z);
|
|
126
|
+
uvs.push(ix / gridX);
|
|
127
|
+
uvs.push(1 - iy / gridY);
|
|
128
|
+
vertexCounter += 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (let iy = 0; iy < gridY; iy++) {
|
|
132
|
+
for (let ix = 0; ix < gridX; ix++) {
|
|
133
|
+
const a = numberOfVertices + ix + gridX1 * iy;
|
|
134
|
+
const b = numberOfVertices + ix + gridX1 * (iy + 1);
|
|
135
|
+
const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
|
|
136
|
+
const d = numberOfVertices + (ix + 1) + gridX1 * iy;
|
|
137
|
+
indices.push(a, b, d);
|
|
138
|
+
indices.push(b, c, d);
|
|
139
|
+
groupCount += 6;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex });
|
|
143
|
+
groupStart += groupCount;
|
|
144
|
+
numberOfVertices += vertexCounter;
|
|
145
|
+
};
|
|
146
|
+
buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0);
|
|
147
|
+
buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1);
|
|
148
|
+
buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2);
|
|
149
|
+
buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3);
|
|
150
|
+
buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4);
|
|
151
|
+
buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5);
|
|
152
|
+
return pack("BoxGeometry", parameters, { vertices, normals, uvs, indices, groups });
|
|
153
|
+
}
|
|
154
|
+
function cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
|
|
155
|
+
radialSegments = Math.floor(radialSegments);
|
|
156
|
+
heightSegments = Math.floor(heightSegments);
|
|
157
|
+
const indices = [];
|
|
158
|
+
const vertices = [];
|
|
159
|
+
const normals = [];
|
|
160
|
+
const uvs = [];
|
|
161
|
+
const groups = [];
|
|
162
|
+
let index = 0;
|
|
163
|
+
const indexArray2 = [];
|
|
164
|
+
const halfHeight = height / 2;
|
|
165
|
+
let groupStart = 0;
|
|
166
|
+
const generateTorso = () => {
|
|
167
|
+
let groupCount = 0;
|
|
168
|
+
const slope = (radiusBottom - radiusTop) / height;
|
|
169
|
+
for (let y = 0; y <= heightSegments; y++) {
|
|
170
|
+
const indexRow = [];
|
|
171
|
+
const v = y / heightSegments;
|
|
172
|
+
const radius = v * (radiusBottom - radiusTop) + radiusTop;
|
|
173
|
+
for (let x = 0; x <= radialSegments; x++) {
|
|
174
|
+
const u = x / radialSegments;
|
|
175
|
+
const theta = u * thetaLength + thetaStart;
|
|
176
|
+
const sinTheta = Math.sin(theta);
|
|
177
|
+
const cosTheta = Math.cos(theta);
|
|
178
|
+
vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
|
|
179
|
+
const n = normalize3(sinTheta, slope, cosTheta);
|
|
180
|
+
normals.push(n[0], n[1], n[2]);
|
|
181
|
+
uvs.push(u, 1 - v);
|
|
182
|
+
indexRow.push(index++);
|
|
183
|
+
}
|
|
184
|
+
indexArray2.push(indexRow);
|
|
185
|
+
}
|
|
186
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
187
|
+
for (let y = 0; y < heightSegments; y++) {
|
|
188
|
+
const a = indexArray2[y][x];
|
|
189
|
+
const b = indexArray2[y + 1][x];
|
|
190
|
+
const c = indexArray2[y + 1][x + 1];
|
|
191
|
+
const d = indexArray2[y][x + 1];
|
|
192
|
+
indices.push(a, b, d);
|
|
193
|
+
indices.push(b, c, d);
|
|
194
|
+
groupCount += 6;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex: 0 });
|
|
198
|
+
groupStart += groupCount;
|
|
199
|
+
};
|
|
200
|
+
const generateCap = (top) => {
|
|
201
|
+
const centerIndexStart = index;
|
|
202
|
+
let groupCount = 0;
|
|
203
|
+
const radius = top === true ? radiusTop : radiusBottom;
|
|
204
|
+
const sign2 = top === true ? 1 : -1;
|
|
205
|
+
for (let x = 1; x <= radialSegments; x++) {
|
|
206
|
+
vertices.push(0, halfHeight * sign2, 0);
|
|
207
|
+
normals.push(0, sign2, 0);
|
|
208
|
+
uvs.push(0.5, 0.5);
|
|
209
|
+
index++;
|
|
210
|
+
}
|
|
211
|
+
const centerIndexEnd = index;
|
|
212
|
+
for (let x = 0; x <= radialSegments; x++) {
|
|
213
|
+
const u = x / radialSegments;
|
|
214
|
+
const theta = u * thetaLength + thetaStart;
|
|
215
|
+
const cosTheta = Math.cos(theta);
|
|
216
|
+
const sinTheta = Math.sin(theta);
|
|
217
|
+
vertices.push(radius * sinTheta, halfHeight * sign2, radius * cosTheta);
|
|
218
|
+
normals.push(0, sign2, 0);
|
|
219
|
+
uvs.push(cosTheta * 0.5 + 0.5, sinTheta * 0.5 * sign2 + 0.5);
|
|
220
|
+
index++;
|
|
221
|
+
}
|
|
222
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
223
|
+
const c = centerIndexStart + x;
|
|
224
|
+
const i = centerIndexEnd + x;
|
|
225
|
+
if (top === true) indices.push(i, i + 1, c);
|
|
226
|
+
else indices.push(i + 1, i, c);
|
|
227
|
+
groupCount += 3;
|
|
228
|
+
}
|
|
229
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2 });
|
|
230
|
+
groupStart += groupCount;
|
|
231
|
+
};
|
|
232
|
+
generateTorso();
|
|
233
|
+
if (openEnded === false) {
|
|
234
|
+
if (radiusTop > 0) generateCap(true);
|
|
235
|
+
if (radiusBottom > 0) generateCap(false);
|
|
236
|
+
}
|
|
237
|
+
return { vertices, normals, uvs, indices, groups };
|
|
238
|
+
}
|
|
239
|
+
function cylinder(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
240
|
+
const parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
|
|
241
|
+
return pack("CylinderGeometry", parameters, cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
|
|
242
|
+
}
|
|
243
|
+
function cone(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
244
|
+
const parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
|
|
245
|
+
return pack("ConeGeometry", parameters, cylinderArrays(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
|
|
246
|
+
}
|
|
247
|
+
function sphere(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) {
|
|
248
|
+
const parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength };
|
|
249
|
+
widthSegments = Math.max(3, Math.floor(widthSegments));
|
|
250
|
+
heightSegments = Math.max(2, Math.floor(heightSegments));
|
|
251
|
+
const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI);
|
|
252
|
+
let index = 0;
|
|
253
|
+
const grid = [];
|
|
254
|
+
const indices = [];
|
|
255
|
+
const vertices = [];
|
|
256
|
+
const normals = [];
|
|
257
|
+
const uvs = [];
|
|
258
|
+
for (let iy = 0; iy <= heightSegments; iy++) {
|
|
259
|
+
const verticesRow = [];
|
|
260
|
+
const v = iy / heightSegments;
|
|
261
|
+
let uOffset = 0;
|
|
262
|
+
if (iy === 0 && thetaStart === 0) uOffset = 0.5 / widthSegments;
|
|
263
|
+
else if (iy === heightSegments && thetaEnd === Math.PI) uOffset = -0.5 / widthSegments;
|
|
264
|
+
for (let ix = 0; ix <= widthSegments; ix++) {
|
|
265
|
+
const u = ix / widthSegments;
|
|
266
|
+
const x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
267
|
+
const y = radius * Math.cos(thetaStart + v * thetaLength);
|
|
268
|
+
const z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
269
|
+
vertices.push(x, y, z);
|
|
270
|
+
const n = normalize3(x, y, z);
|
|
271
|
+
normals.push(n[0], n[1], n[2]);
|
|
272
|
+
uvs.push(u + uOffset, 1 - v);
|
|
273
|
+
verticesRow.push(index++);
|
|
274
|
+
}
|
|
275
|
+
grid.push(verticesRow);
|
|
276
|
+
}
|
|
277
|
+
for (let iy = 0; iy < heightSegments; iy++) {
|
|
278
|
+
for (let ix = 0; ix < widthSegments; ix++) {
|
|
279
|
+
const a = grid[iy][ix + 1];
|
|
280
|
+
const b = grid[iy][ix];
|
|
281
|
+
const c = grid[iy + 1][ix];
|
|
282
|
+
const d = grid[iy + 1][ix + 1];
|
|
283
|
+
if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
|
|
284
|
+
if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return pack("SphereGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
288
|
+
}
|
|
289
|
+
function plane(width = 1, height = 1, widthSegments = 1, heightSegments = 1) {
|
|
290
|
+
const parameters = { width, height, widthSegments, heightSegments };
|
|
291
|
+
const widthHalf = width / 2;
|
|
292
|
+
const heightHalf = height / 2;
|
|
293
|
+
const gridX = Math.floor(widthSegments);
|
|
294
|
+
const gridY = Math.floor(heightSegments);
|
|
295
|
+
const gridX1 = gridX + 1;
|
|
296
|
+
const gridY1 = gridY + 1;
|
|
297
|
+
const segmentWidth = width / gridX;
|
|
298
|
+
const segmentHeight = height / gridY;
|
|
299
|
+
const indices = [];
|
|
300
|
+
const vertices = [];
|
|
301
|
+
const normals = [];
|
|
302
|
+
const uvs = [];
|
|
303
|
+
for (let iy = 0; iy < gridY1; iy++) {
|
|
304
|
+
const y = iy * segmentHeight - heightHalf;
|
|
305
|
+
for (let ix = 0; ix < gridX1; ix++) {
|
|
306
|
+
const x = ix * segmentWidth - widthHalf;
|
|
307
|
+
vertices.push(x, -y, 0);
|
|
308
|
+
normals.push(0, 0, 1);
|
|
309
|
+
uvs.push(ix / gridX);
|
|
310
|
+
uvs.push(1 - iy / gridY);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
for (let iy = 0; iy < gridY; iy++) {
|
|
314
|
+
for (let ix = 0; ix < gridX; ix++) {
|
|
315
|
+
const a = ix + gridX1 * iy;
|
|
316
|
+
const b = ix + gridX1 * (iy + 1);
|
|
317
|
+
const c = ix + 1 + gridX1 * (iy + 1);
|
|
318
|
+
const d = ix + 1 + gridX1 * iy;
|
|
319
|
+
indices.push(a, b, d);
|
|
320
|
+
indices.push(b, c, d);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return pack("PlaneGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
324
|
+
}
|
|
325
|
+
function circle(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
326
|
+
const parameters = { radius, segments, thetaStart, thetaLength };
|
|
327
|
+
segments = Math.max(3, segments);
|
|
328
|
+
const indices = [];
|
|
329
|
+
const vertices = [];
|
|
330
|
+
const normals = [];
|
|
331
|
+
const uvs = [];
|
|
332
|
+
vertices.push(0, 0, 0);
|
|
333
|
+
normals.push(0, 0, 1);
|
|
334
|
+
uvs.push(0.5, 0.5);
|
|
335
|
+
for (let s = 0, i = 3; s <= segments; s++, i += 3) {
|
|
336
|
+
const segment = thetaStart + s / segments * thetaLength;
|
|
337
|
+
vertices.push(radius * Math.cos(segment), radius * Math.sin(segment), 0);
|
|
338
|
+
normals.push(0, 0, 1);
|
|
339
|
+
uvs.push((vertices[i] / radius + 1) / 2, (vertices[i + 1] / radius + 1) / 2);
|
|
340
|
+
}
|
|
341
|
+
for (let i = 1; i <= segments; i++) indices.push(i, i + 1, 0);
|
|
342
|
+
return pack("CircleGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
343
|
+
}
|
|
344
|
+
function ring(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
345
|
+
const parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength };
|
|
346
|
+
thetaSegments = Math.max(3, thetaSegments);
|
|
347
|
+
phiSegments = Math.max(1, phiSegments);
|
|
348
|
+
const indices = [];
|
|
349
|
+
const vertices = [];
|
|
350
|
+
const normals = [];
|
|
351
|
+
const uvs = [];
|
|
352
|
+
let radius = innerRadius;
|
|
353
|
+
const radiusStep = (outerRadius - innerRadius) / phiSegments;
|
|
354
|
+
for (let j = 0; j <= phiSegments; j++) {
|
|
355
|
+
for (let i = 0; i <= thetaSegments; i++) {
|
|
356
|
+
const segment = thetaStart + i / thetaSegments * thetaLength;
|
|
357
|
+
const x = radius * Math.cos(segment);
|
|
358
|
+
const y = radius * Math.sin(segment);
|
|
359
|
+
vertices.push(x, y, 0);
|
|
360
|
+
normals.push(0, 0, 1);
|
|
361
|
+
uvs.push((x / outerRadius + 1) / 2, (y / outerRadius + 1) / 2);
|
|
362
|
+
}
|
|
363
|
+
radius += radiusStep;
|
|
364
|
+
}
|
|
365
|
+
for (let j = 0; j < phiSegments; j++) {
|
|
366
|
+
const thetaSegmentLevel = j * (thetaSegments + 1);
|
|
367
|
+
for (let i = 0; i < thetaSegments; i++) {
|
|
368
|
+
const segment = i + thetaSegmentLevel;
|
|
369
|
+
const a = segment;
|
|
370
|
+
const b = segment + thetaSegments + 1;
|
|
371
|
+
const c = segment + thetaSegments + 2;
|
|
372
|
+
const d = segment + 1;
|
|
373
|
+
indices.push(a, b, d);
|
|
374
|
+
indices.push(b, c, d);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return pack("RingGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
378
|
+
}
|
|
379
|
+
function lathe(points, segments = 12, phiStart = 0, phiLength = Math.PI * 2) {
|
|
380
|
+
const parameters = { points: points.map(([x, y]) => ({ x, y })), segments, phiStart, phiLength };
|
|
381
|
+
segments = Math.floor(segments);
|
|
382
|
+
phiLength = Math.max(0, Math.min(Math.PI * 2, phiLength));
|
|
383
|
+
const indices = [];
|
|
384
|
+
const vertices = [];
|
|
385
|
+
const uvs = [];
|
|
386
|
+
const initNormals = [];
|
|
387
|
+
const normals = [];
|
|
388
|
+
const inverseSegments = 1 / segments;
|
|
389
|
+
const normal = { x: 0, y: 0, z: 0 };
|
|
390
|
+
const curNormal = { x: 0, y: 0, z: 0 };
|
|
391
|
+
const prevNormal = { x: 0, y: 0, z: 0 };
|
|
392
|
+
let dx = 0;
|
|
393
|
+
let dy = 0;
|
|
394
|
+
const last = points.length - 1;
|
|
395
|
+
const px = (j) => points[j][0];
|
|
396
|
+
const py = (j) => points[j][1];
|
|
397
|
+
const setNormalized = () => {
|
|
398
|
+
const n = normalize3(normal.x, normal.y, normal.z);
|
|
399
|
+
normal.x = n[0];
|
|
400
|
+
normal.y = n[1];
|
|
401
|
+
normal.z = n[2];
|
|
402
|
+
};
|
|
403
|
+
for (let j = 0; j <= last; j++) {
|
|
404
|
+
if (j === 0) {
|
|
405
|
+
dx = px(j + 1) - px(j);
|
|
406
|
+
dy = py(j + 1) - py(j);
|
|
407
|
+
normal.x = dy * 1;
|
|
408
|
+
normal.y = -dx;
|
|
409
|
+
normal.z = dy * 0;
|
|
410
|
+
prevNormal.x = normal.x;
|
|
411
|
+
prevNormal.y = normal.y;
|
|
412
|
+
prevNormal.z = normal.z;
|
|
413
|
+
setNormalized();
|
|
414
|
+
initNormals.push(normal.x, normal.y, normal.z);
|
|
415
|
+
} else if (j === last) {
|
|
416
|
+
initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);
|
|
417
|
+
} else {
|
|
418
|
+
dx = px(j + 1) - px(j);
|
|
419
|
+
dy = py(j + 1) - py(j);
|
|
420
|
+
normal.x = dy * 1;
|
|
421
|
+
normal.y = -dx;
|
|
422
|
+
normal.z = dy * 0;
|
|
423
|
+
curNormal.x = normal.x;
|
|
424
|
+
curNormal.y = normal.y;
|
|
425
|
+
curNormal.z = normal.z;
|
|
426
|
+
normal.x += prevNormal.x;
|
|
427
|
+
normal.y += prevNormal.y;
|
|
428
|
+
normal.z += prevNormal.z;
|
|
429
|
+
setNormalized();
|
|
430
|
+
initNormals.push(normal.x, normal.y, normal.z);
|
|
431
|
+
prevNormal.x = curNormal.x;
|
|
432
|
+
prevNormal.y = curNormal.y;
|
|
433
|
+
prevNormal.z = curNormal.z;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
for (let i = 0; i <= segments; i++) {
|
|
437
|
+
const phi = phiStart + i * inverseSegments * phiLength;
|
|
438
|
+
const sin = Math.sin(phi);
|
|
439
|
+
const cos = Math.cos(phi);
|
|
440
|
+
for (let j = 0; j <= last; j++) {
|
|
441
|
+
vertices.push(px(j) * sin, py(j), px(j) * cos);
|
|
442
|
+
uvs.push(i / segments, j / (points.length - 1));
|
|
443
|
+
const x = initNormals[3 * j + 0] * sin;
|
|
444
|
+
const y = initNormals[3 * j + 1];
|
|
445
|
+
const z = initNormals[3 * j + 0] * cos;
|
|
446
|
+
normals.push(x, y, z);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
for (let i = 0; i < segments; i++) {
|
|
450
|
+
for (let j = 0; j < points.length - 1; j++) {
|
|
451
|
+
const base = j + i * points.length;
|
|
452
|
+
const a = base;
|
|
453
|
+
const b = base + points.length;
|
|
454
|
+
const c = base + points.length + 1;
|
|
455
|
+
const d = base + 1;
|
|
456
|
+
indices.push(a, b, d);
|
|
457
|
+
indices.push(c, d, b);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return pack("LatheGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
461
|
+
}
|
|
462
|
+
function torus(radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) {
|
|
463
|
+
const parameters = { radius, tube, radialSegments, tubularSegments, arc };
|
|
464
|
+
radialSegments = Math.floor(radialSegments);
|
|
465
|
+
tubularSegments = Math.floor(tubularSegments);
|
|
466
|
+
const indices = [];
|
|
467
|
+
const vertices = [];
|
|
468
|
+
const normals = [];
|
|
469
|
+
const uvs = [];
|
|
470
|
+
for (let j = 0; j <= radialSegments; j++) {
|
|
471
|
+
for (let i = 0; i <= tubularSegments; i++) {
|
|
472
|
+
const u = i / tubularSegments * arc;
|
|
473
|
+
const v = j / radialSegments * Math.PI * 2;
|
|
474
|
+
const x = (radius + tube * Math.cos(v)) * Math.cos(u);
|
|
475
|
+
const y = (radius + tube * Math.cos(v)) * Math.sin(u);
|
|
476
|
+
const z = tube * Math.sin(v);
|
|
477
|
+
vertices.push(x, y, z);
|
|
478
|
+
const cx = radius * Math.cos(u);
|
|
479
|
+
const cy = radius * Math.sin(u);
|
|
480
|
+
const n = normalize3(x - cx, y - cy, z - 0);
|
|
481
|
+
normals.push(n[0], n[1], n[2]);
|
|
482
|
+
uvs.push(i / tubularSegments);
|
|
483
|
+
uvs.push(j / radialSegments);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
for (let j = 1; j <= radialSegments; j++) {
|
|
487
|
+
for (let i = 1; i <= tubularSegments; i++) {
|
|
488
|
+
const a = (tubularSegments + 1) * j + i - 1;
|
|
489
|
+
const b = (tubularSegments + 1) * (j - 1) + i - 1;
|
|
490
|
+
const c = (tubularSegments + 1) * (j - 1) + i;
|
|
491
|
+
const d = (tubularSegments + 1) * j + i;
|
|
492
|
+
indices.push(a, b, d);
|
|
493
|
+
indices.push(b, c, d);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return pack("TorusGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
497
|
+
}
|
|
498
|
+
function difference(size, subtract) {
|
|
499
|
+
const [w, h, d] = size;
|
|
500
|
+
const hw = w / 2;
|
|
501
|
+
const hh = h / 2;
|
|
502
|
+
const hd = d / 2;
|
|
503
|
+
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);
|
|
504
|
+
const cuts = (lo, hi, values) => [.../* @__PURE__ */ new Set([lo, hi, ...values.filter((v) => v > lo && v < hi)])].sort((a, b) => a - b);
|
|
505
|
+
const xs = cuts(-hw, hw, holes.flatMap((hole) => [hole.x0, hole.x1]));
|
|
506
|
+
const ys = cuts(-hh, hh, holes.flatMap((hole) => [hole.y0, hole.y1]));
|
|
507
|
+
const nx = xs.length - 1;
|
|
508
|
+
const ny = ys.length - 1;
|
|
509
|
+
const solid = (i, j) => {
|
|
510
|
+
if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
|
|
511
|
+
const cx = (xs[i] + xs[i + 1]) / 2;
|
|
512
|
+
const cy = (ys[j] + ys[j + 1]) / 2;
|
|
513
|
+
return !holes.some((hole) => cx > hole.x0 && cx < hole.x1 && cy > hole.y0 && cy < hole.y1);
|
|
514
|
+
};
|
|
515
|
+
const positions = [];
|
|
516
|
+
const normals = [];
|
|
517
|
+
const uvs = [];
|
|
518
|
+
const indices = [];
|
|
519
|
+
const quad = (corners, normal, uv) => {
|
|
520
|
+
const base = positions.length / 3;
|
|
521
|
+
for (let k = 0; k < 4; k++) {
|
|
522
|
+
positions.push(...corners[k]);
|
|
523
|
+
normals.push(...normal);
|
|
524
|
+
uvs.push(...uv[k]);
|
|
525
|
+
}
|
|
526
|
+
indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
|
|
527
|
+
};
|
|
528
|
+
const fu = (x) => (x + hw) / w;
|
|
529
|
+
const fv = (y) => (y + hh) / h;
|
|
530
|
+
for (let j = 0; j < ny; j++) {
|
|
531
|
+
for (let i = 0; i < nx; i++) {
|
|
532
|
+
if (!solid(i, j)) continue;
|
|
533
|
+
const x0 = xs[i];
|
|
534
|
+
const x1 = xs[i + 1];
|
|
535
|
+
const y0 = ys[j];
|
|
536
|
+
const y1 = ys[j + 1];
|
|
537
|
+
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)]]);
|
|
538
|
+
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)]]);
|
|
539
|
+
const zu = (z) => (z + hd) / d;
|
|
540
|
+
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)]]);
|
|
541
|
+
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)]]);
|
|
542
|
+
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)]]);
|
|
543
|
+
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)]]);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
type: "BufferGeometry",
|
|
548
|
+
parameters: { size: [w, h, d], subtract: holes.map((hole) => ({ min: [hole.x0, hole.y0], max: [hole.x1, hole.y1] })) },
|
|
549
|
+
positions: new Float32Array(positions),
|
|
550
|
+
normals: new Float32Array(normals),
|
|
551
|
+
uvs: new Float32Array(uvs),
|
|
552
|
+
indices: indexArray(indices),
|
|
553
|
+
groups: []
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
var Earcut = {
|
|
557
|
+
triangulate: function(data, holeIndices, dim = 2) {
|
|
558
|
+
const hasHoles = holeIndices && holeIndices.length;
|
|
559
|
+
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
|
|
560
|
+
let outerNode = linkedList(data, 0, outerLen, dim, true);
|
|
561
|
+
const triangles = [];
|
|
562
|
+
if (!outerNode || outerNode.next === outerNode.prev) return triangles;
|
|
563
|
+
let minX, minY, maxX, maxY, x, y, invSize;
|
|
564
|
+
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
|
|
565
|
+
if (data.length > 80 * dim) {
|
|
566
|
+
minX = maxX = data[0];
|
|
567
|
+
minY = maxY = data[1];
|
|
568
|
+
for (let i = dim; i < outerLen; i += dim) {
|
|
569
|
+
x = data[i];
|
|
570
|
+
y = data[i + 1];
|
|
571
|
+
if (x < minX) minX = x;
|
|
572
|
+
if (y < minY) minY = y;
|
|
573
|
+
if (x > maxX) maxX = x;
|
|
574
|
+
if (y > maxY) maxY = y;
|
|
575
|
+
}
|
|
576
|
+
invSize = Math.max(maxX - minX, maxY - minY);
|
|
577
|
+
invSize = invSize !== 0 ? 32767 / invSize : 0;
|
|
578
|
+
}
|
|
579
|
+
earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
|
|
580
|
+
return triangles;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
function linkedList(data, start, end, dim, clockwise) {
|
|
584
|
+
let i, last;
|
|
585
|
+
if (clockwise === signedArea(data, start, end, dim) > 0) {
|
|
586
|
+
for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
587
|
+
} else {
|
|
588
|
+
for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
589
|
+
}
|
|
590
|
+
if (last && equals(last, last.next)) {
|
|
591
|
+
removeNode(last);
|
|
592
|
+
last = last.next;
|
|
593
|
+
}
|
|
594
|
+
return last;
|
|
595
|
+
}
|
|
596
|
+
function filterPoints(start, end) {
|
|
597
|
+
if (!start) return start;
|
|
598
|
+
if (!end) end = start;
|
|
599
|
+
let p = start, again;
|
|
600
|
+
do {
|
|
601
|
+
again = false;
|
|
602
|
+
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
|
|
603
|
+
removeNode(p);
|
|
604
|
+
p = end = p.prev;
|
|
605
|
+
if (p === p.next) break;
|
|
606
|
+
again = true;
|
|
607
|
+
} else {
|
|
608
|
+
p = p.next;
|
|
609
|
+
}
|
|
610
|
+
} while (again || p !== end);
|
|
611
|
+
return end;
|
|
612
|
+
}
|
|
613
|
+
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
|
|
614
|
+
if (!ear) return;
|
|
615
|
+
if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
|
|
616
|
+
let stop = ear, prev, next;
|
|
617
|
+
while (ear.prev !== ear.next) {
|
|
618
|
+
prev = ear.prev;
|
|
619
|
+
next = ear.next;
|
|
620
|
+
if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
|
|
621
|
+
triangles.push(prev.i / dim | 0);
|
|
622
|
+
triangles.push(ear.i / dim | 0);
|
|
623
|
+
triangles.push(next.i / dim | 0);
|
|
624
|
+
removeNode(ear);
|
|
625
|
+
ear = next.next;
|
|
626
|
+
stop = next.next;
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
ear = next;
|
|
630
|
+
if (ear === stop) {
|
|
631
|
+
if (!pass) {
|
|
632
|
+
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
|
|
633
|
+
} else if (pass === 1) {
|
|
634
|
+
ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
|
|
635
|
+
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
|
|
636
|
+
} else if (pass === 2) {
|
|
637
|
+
splitEarcut(ear, triangles, dim, minX, minY, invSize);
|
|
638
|
+
}
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function isEar(ear) {
|
|
644
|
+
const a = ear.prev, b = ear, c = ear.next;
|
|
645
|
+
if (area(a, b, c) >= 0) return false;
|
|
646
|
+
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
647
|
+
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;
|
|
648
|
+
let p = c.next;
|
|
649
|
+
while (p !== a) {
|
|
650
|
+
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;
|
|
651
|
+
p = p.next;
|
|
652
|
+
}
|
|
653
|
+
return true;
|
|
654
|
+
}
|
|
655
|
+
function isEarHashed(ear, minX, minY, invSize) {
|
|
656
|
+
const a = ear.prev, b = ear, c = ear.next;
|
|
657
|
+
if (area(a, b, c) >= 0) return false;
|
|
658
|
+
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
659
|
+
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;
|
|
660
|
+
const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
|
|
661
|
+
let p = ear.prevZ, n = ear.nextZ;
|
|
662
|
+
while (p && p.z >= minZ && n && n.z <= maxZ) {
|
|
663
|
+
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;
|
|
664
|
+
p = p.prevZ;
|
|
665
|
+
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;
|
|
666
|
+
n = n.nextZ;
|
|
667
|
+
}
|
|
668
|
+
while (p && p.z >= minZ) {
|
|
669
|
+
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;
|
|
670
|
+
p = p.prevZ;
|
|
671
|
+
}
|
|
672
|
+
while (n && n.z <= maxZ) {
|
|
673
|
+
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;
|
|
674
|
+
n = n.nextZ;
|
|
675
|
+
}
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
function cureLocalIntersections(start, triangles, dim) {
|
|
679
|
+
let p = start;
|
|
680
|
+
do {
|
|
681
|
+
const a = p.prev, b = p.next.next;
|
|
682
|
+
if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
|
|
683
|
+
triangles.push(a.i / dim | 0);
|
|
684
|
+
triangles.push(p.i / dim | 0);
|
|
685
|
+
triangles.push(b.i / dim | 0);
|
|
686
|
+
removeNode(p);
|
|
687
|
+
removeNode(p.next);
|
|
688
|
+
p = start = b;
|
|
689
|
+
}
|
|
690
|
+
p = p.next;
|
|
691
|
+
} while (p !== start);
|
|
692
|
+
return filterPoints(p);
|
|
693
|
+
}
|
|
694
|
+
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
|
|
695
|
+
let a = start;
|
|
696
|
+
do {
|
|
697
|
+
let b = a.next.next;
|
|
698
|
+
while (b !== a.prev) {
|
|
699
|
+
if (a.i !== b.i && isValidDiagonal(a, b)) {
|
|
700
|
+
let c = splitPolygon(a, b);
|
|
701
|
+
a = filterPoints(a, a.next);
|
|
702
|
+
c = filterPoints(c, c.next);
|
|
703
|
+
earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
|
|
704
|
+
earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
b = b.next;
|
|
708
|
+
}
|
|
709
|
+
a = a.next;
|
|
710
|
+
} while (a !== start);
|
|
711
|
+
}
|
|
712
|
+
function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
713
|
+
const queue = [];
|
|
714
|
+
let i, len, start, end, list2;
|
|
715
|
+
for (i = 0, len = holeIndices.length; i < len; i++) {
|
|
716
|
+
start = holeIndices[i] * dim;
|
|
717
|
+
end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
|
|
718
|
+
list2 = linkedList(data, start, end, dim, false);
|
|
719
|
+
if (list2 === list2.next) list2.steiner = true;
|
|
720
|
+
queue.push(getLeftmost(list2));
|
|
721
|
+
}
|
|
722
|
+
queue.sort(compareX);
|
|
723
|
+
for (i = 0; i < queue.length; i++) {
|
|
724
|
+
outerNode = eliminateHole(queue[i], outerNode);
|
|
725
|
+
}
|
|
726
|
+
return outerNode;
|
|
727
|
+
}
|
|
728
|
+
function compareX(a, b) {
|
|
729
|
+
return a.x - b.x;
|
|
730
|
+
}
|
|
731
|
+
function eliminateHole(hole, outerNode) {
|
|
732
|
+
const bridge = findHoleBridge(hole, outerNode);
|
|
733
|
+
if (!bridge) {
|
|
734
|
+
return outerNode;
|
|
735
|
+
}
|
|
736
|
+
const bridgeReverse = splitPolygon(bridge, hole);
|
|
737
|
+
filterPoints(bridgeReverse, bridgeReverse.next);
|
|
738
|
+
return filterPoints(bridge, bridge.next);
|
|
739
|
+
}
|
|
740
|
+
function findHoleBridge(hole, outerNode) {
|
|
741
|
+
let p = outerNode, qx = -Infinity, m;
|
|
742
|
+
const hx = hole.x, hy = hole.y;
|
|
743
|
+
do {
|
|
744
|
+
if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
|
|
745
|
+
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
|
|
746
|
+
if (x <= hx && x > qx) {
|
|
747
|
+
qx = x;
|
|
748
|
+
m = p.x < p.next.x ? p : p.next;
|
|
749
|
+
if (x === hx) return m;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
p = p.next;
|
|
753
|
+
} while (p !== outerNode);
|
|
754
|
+
if (!m) return null;
|
|
755
|
+
const stop = m, mx = m.x, my = m.y;
|
|
756
|
+
let tanMin = Infinity, tan;
|
|
757
|
+
p = m;
|
|
758
|
+
do {
|
|
759
|
+
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)) {
|
|
760
|
+
tan = Math.abs(hy - p.y) / (hx - p.x);
|
|
761
|
+
if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
|
|
762
|
+
m = p;
|
|
763
|
+
tanMin = tan;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
p = p.next;
|
|
767
|
+
} while (p !== stop);
|
|
768
|
+
return m;
|
|
769
|
+
}
|
|
770
|
+
function sectorContainsSector(m, p) {
|
|
771
|
+
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
|
|
772
|
+
}
|
|
773
|
+
function indexCurve(start, minX, minY, invSize) {
|
|
774
|
+
let p = start;
|
|
775
|
+
do {
|
|
776
|
+
if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
|
|
777
|
+
p.prevZ = p.prev;
|
|
778
|
+
p.nextZ = p.next;
|
|
779
|
+
p = p.next;
|
|
780
|
+
} while (p !== start);
|
|
781
|
+
p.prevZ.nextZ = null;
|
|
782
|
+
p.prevZ = null;
|
|
783
|
+
sortLinked(p);
|
|
784
|
+
}
|
|
785
|
+
function sortLinked(list2) {
|
|
786
|
+
let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
|
|
787
|
+
do {
|
|
788
|
+
p = list2;
|
|
789
|
+
list2 = null;
|
|
790
|
+
tail = null;
|
|
791
|
+
numMerges = 0;
|
|
792
|
+
while (p) {
|
|
793
|
+
numMerges++;
|
|
794
|
+
q = p;
|
|
795
|
+
pSize = 0;
|
|
796
|
+
for (i = 0; i < inSize; i++) {
|
|
797
|
+
pSize++;
|
|
798
|
+
q = q.nextZ;
|
|
799
|
+
if (!q) break;
|
|
800
|
+
}
|
|
801
|
+
qSize = inSize;
|
|
802
|
+
while (pSize > 0 || qSize > 0 && q) {
|
|
803
|
+
if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
|
|
804
|
+
e = p;
|
|
805
|
+
p = p.nextZ;
|
|
806
|
+
pSize--;
|
|
807
|
+
} else {
|
|
808
|
+
e = q;
|
|
809
|
+
q = q.nextZ;
|
|
810
|
+
qSize--;
|
|
811
|
+
}
|
|
812
|
+
if (tail) tail.nextZ = e;
|
|
813
|
+
else list2 = e;
|
|
814
|
+
e.prevZ = tail;
|
|
815
|
+
tail = e;
|
|
816
|
+
}
|
|
817
|
+
p = q;
|
|
818
|
+
}
|
|
819
|
+
tail.nextZ = null;
|
|
820
|
+
inSize *= 2;
|
|
821
|
+
} while (numMerges > 1);
|
|
822
|
+
return list2;
|
|
823
|
+
}
|
|
824
|
+
function zOrder(x, y, minX, minY, invSize) {
|
|
825
|
+
x = (x - minX) * invSize | 0;
|
|
826
|
+
y = (y - minY) * invSize | 0;
|
|
827
|
+
x = (x | x << 8) & 16711935;
|
|
828
|
+
x = (x | x << 4) & 252645135;
|
|
829
|
+
x = (x | x << 2) & 858993459;
|
|
830
|
+
x = (x | x << 1) & 1431655765;
|
|
831
|
+
y = (y | y << 8) & 16711935;
|
|
832
|
+
y = (y | y << 4) & 252645135;
|
|
833
|
+
y = (y | y << 2) & 858993459;
|
|
834
|
+
y = (y | y << 1) & 1431655765;
|
|
835
|
+
return x | y << 1;
|
|
836
|
+
}
|
|
837
|
+
function getLeftmost(start) {
|
|
838
|
+
let p = start, leftmost = start;
|
|
839
|
+
do {
|
|
840
|
+
if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
|
|
841
|
+
p = p.next;
|
|
842
|
+
} while (p !== start);
|
|
843
|
+
return leftmost;
|
|
844
|
+
}
|
|
845
|
+
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
846
|
+
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);
|
|
847
|
+
}
|
|
848
|
+
function isValidDiagonal(a, b) {
|
|
849
|
+
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
|
|
850
|
+
(locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
|
|
851
|
+
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
|
|
852
|
+
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
|
|
853
|
+
}
|
|
854
|
+
function area(p, q, r) {
|
|
855
|
+
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
856
|
+
}
|
|
857
|
+
function equals(p1, p2) {
|
|
858
|
+
return p1.x === p2.x && p1.y === p2.y;
|
|
859
|
+
}
|
|
860
|
+
function intersects(p1, q1, p2, q2) {
|
|
861
|
+
const o1 = sign(area(p1, q1, p2));
|
|
862
|
+
const o2 = sign(area(p1, q1, q2));
|
|
863
|
+
const o3 = sign(area(p2, q2, p1));
|
|
864
|
+
const o4 = sign(area(p2, q2, q1));
|
|
865
|
+
if (o1 !== o2 && o3 !== o4) return true;
|
|
866
|
+
if (o1 === 0 && onSegment(p1, p2, q1)) return true;
|
|
867
|
+
if (o2 === 0 && onSegment(p1, q2, q1)) return true;
|
|
868
|
+
if (o3 === 0 && onSegment(p2, p1, q2)) return true;
|
|
869
|
+
if (o4 === 0 && onSegment(p2, q1, q2)) return true;
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
function onSegment(p, q, r) {
|
|
873
|
+
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);
|
|
874
|
+
}
|
|
875
|
+
function sign(num2) {
|
|
876
|
+
return num2 > 0 ? 1 : num2 < 0 ? -1 : 0;
|
|
877
|
+
}
|
|
878
|
+
function intersectsPolygon(a, b) {
|
|
879
|
+
let p = a;
|
|
880
|
+
do {
|
|
881
|
+
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;
|
|
882
|
+
p = p.next;
|
|
883
|
+
} while (p !== a);
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
function locallyInside(a, b) {
|
|
887
|
+
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;
|
|
888
|
+
}
|
|
889
|
+
function middleInside(a, b) {
|
|
890
|
+
let p = a, inside = false;
|
|
891
|
+
const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
|
|
892
|
+
do {
|
|
893
|
+
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)
|
|
894
|
+
inside = !inside;
|
|
895
|
+
p = p.next;
|
|
896
|
+
} while (p !== a);
|
|
897
|
+
return inside;
|
|
898
|
+
}
|
|
899
|
+
function splitPolygon(a, b) {
|
|
900
|
+
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;
|
|
901
|
+
a.next = b;
|
|
902
|
+
b.prev = a;
|
|
903
|
+
a2.next = an;
|
|
904
|
+
an.prev = a2;
|
|
905
|
+
b2.next = a2;
|
|
906
|
+
a2.prev = b2;
|
|
907
|
+
bp.next = b2;
|
|
908
|
+
b2.prev = bp;
|
|
909
|
+
return b2;
|
|
910
|
+
}
|
|
911
|
+
function insertNode(i, x, y, last) {
|
|
912
|
+
const p = new Node(i, x, y);
|
|
913
|
+
if (!last) {
|
|
914
|
+
p.prev = p;
|
|
915
|
+
p.next = p;
|
|
916
|
+
} else {
|
|
917
|
+
p.next = last.next;
|
|
918
|
+
p.prev = last;
|
|
919
|
+
last.next.prev = p;
|
|
920
|
+
last.next = p;
|
|
921
|
+
}
|
|
922
|
+
return p;
|
|
923
|
+
}
|
|
924
|
+
function removeNode(p) {
|
|
925
|
+
p.next.prev = p.prev;
|
|
926
|
+
p.prev.next = p.next;
|
|
927
|
+
if (p.prevZ) p.prevZ.nextZ = p.nextZ;
|
|
928
|
+
if (p.nextZ) p.nextZ.prevZ = p.prevZ;
|
|
929
|
+
}
|
|
930
|
+
function Node(i, x, y) {
|
|
931
|
+
this.i = i;
|
|
932
|
+
this.x = x;
|
|
933
|
+
this.y = y;
|
|
934
|
+
this.prev = null;
|
|
935
|
+
this.next = null;
|
|
936
|
+
this.z = 0;
|
|
937
|
+
this.prevZ = null;
|
|
938
|
+
this.nextZ = null;
|
|
939
|
+
this.steiner = false;
|
|
940
|
+
}
|
|
941
|
+
function signedArea(data, start, end, dim) {
|
|
942
|
+
let sum = 0;
|
|
943
|
+
for (let i = start, j = end - dim; i < end; i += dim) {
|
|
944
|
+
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
|
945
|
+
j = i;
|
|
946
|
+
}
|
|
947
|
+
return sum;
|
|
948
|
+
}
|
|
949
|
+
var equals2 = (a, b) => a.x === b.x && a.y === b.y;
|
|
950
|
+
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 };
|
|
951
|
+
function pathPoints(points) {
|
|
952
|
+
const out = [];
|
|
953
|
+
let last;
|
|
954
|
+
for (let i = 1; i < points.length; i++) {
|
|
955
|
+
const v1 = { x: points[i - 1][0], y: points[i - 1][1] };
|
|
956
|
+
const v2 = { x: points[i][0], y: points[i][1] };
|
|
957
|
+
for (let d = 0; d <= 1; d++) {
|
|
958
|
+
const point = linePoint(v1, v2, d / 1);
|
|
959
|
+
if (last && equals2(last, point)) continue;
|
|
960
|
+
out.push(point);
|
|
961
|
+
last = point;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return out;
|
|
965
|
+
}
|
|
966
|
+
function area2(contour) {
|
|
967
|
+
const n = contour.length;
|
|
968
|
+
let a = 0;
|
|
969
|
+
for (let p = n - 1, q = 0; q < n; p = q++) a += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
|
|
970
|
+
return a * 0.5;
|
|
971
|
+
}
|
|
972
|
+
var isClockWise = (pts) => area2(pts) < 0;
|
|
973
|
+
function removeDupEndPts(points) {
|
|
974
|
+
const l = points.length;
|
|
975
|
+
if (l > 2 && equals2(points[l - 1], points[0])) points.pop();
|
|
976
|
+
}
|
|
977
|
+
function triangulateShape(contour, holes) {
|
|
978
|
+
const vertices = [];
|
|
979
|
+
const holeIndices = [];
|
|
980
|
+
const faces = [];
|
|
981
|
+
removeDupEndPts(contour);
|
|
982
|
+
for (const p of contour) vertices.push(p.x, p.y);
|
|
983
|
+
let holeIndex = contour.length;
|
|
984
|
+
holes.forEach(removeDupEndPts);
|
|
985
|
+
for (let i = 0; i < holes.length; i++) {
|
|
986
|
+
holeIndices.push(holeIndex);
|
|
987
|
+
holeIndex += holes[i].length;
|
|
988
|
+
for (const p of holes[i]) vertices.push(p.x, p.y);
|
|
989
|
+
}
|
|
990
|
+
const triangles = Earcut.triangulate(vertices, holeIndices);
|
|
991
|
+
for (let i = 0; i < triangles.length; i += 3) faces.push(triangles.slice(i, i + 3));
|
|
992
|
+
return faces;
|
|
993
|
+
}
|
|
994
|
+
function computeVertexNormals(positions, indices) {
|
|
995
|
+
const normals = new Float32Array(positions.length);
|
|
996
|
+
const get = (array, i) => [array[i * 3], array[i * 3 + 1], array[i * 3 + 2]];
|
|
997
|
+
const face = (vA, vB, vC) => {
|
|
998
|
+
const pA = get(positions, vA);
|
|
999
|
+
const pB = get(positions, vB);
|
|
1000
|
+
const pC = get(positions, vC);
|
|
1001
|
+
const cb = [pC[0] - pB[0], pC[1] - pB[1], pC[2] - pB[2]];
|
|
1002
|
+
const ab = [pA[0] - pB[0], pA[1] - pB[1], pA[2] - pB[2]];
|
|
1003
|
+
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]];
|
|
1004
|
+
};
|
|
1005
|
+
const set = (i, x, y, z) => {
|
|
1006
|
+
normals[i * 3] = x;
|
|
1007
|
+
normals[i * 3 + 1] = y;
|
|
1008
|
+
normals[i * 3 + 2] = z;
|
|
1009
|
+
};
|
|
1010
|
+
if (indices) {
|
|
1011
|
+
for (let i = 0; i < indices.length; i += 3) {
|
|
1012
|
+
const vA = indices[i];
|
|
1013
|
+
const vB = indices[i + 1];
|
|
1014
|
+
const vC = indices[i + 2];
|
|
1015
|
+
const cb = face(vA, vB, vC);
|
|
1016
|
+
for (const v of [vA, vB, vC]) {
|
|
1017
|
+
const n = get(normals, v);
|
|
1018
|
+
set(v, n[0] + cb[0], n[1] + cb[1], n[2] + cb[2]);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
} else {
|
|
1022
|
+
for (let i = 0; i < positions.length / 3; i += 3) {
|
|
1023
|
+
const cb = face(i, i + 1, i + 2);
|
|
1024
|
+
set(i, cb[0], cb[1], cb[2]);
|
|
1025
|
+
set(i + 1, cb[0], cb[1], cb[2]);
|
|
1026
|
+
set(i + 2, cb[0], cb[1], cb[2]);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
for (let i = 0; i < normals.length / 3; i++) {
|
|
1030
|
+
const [x, y, z] = get(normals, i);
|
|
1031
|
+
const s = 1 / (Math.sqrt(x * x + y * y + z * z) || 1);
|
|
1032
|
+
set(i, x * s, y * s, z * s);
|
|
1033
|
+
}
|
|
1034
|
+
return normals;
|
|
1035
|
+
}
|
|
1036
|
+
function extrude(outline, depth = 1, steps = 1, bevel = void 0) {
|
|
1037
|
+
const parameters = { shape: outline, depth, steps, bevel: bevel ?? {} };
|
|
1038
|
+
const verticesArray = [];
|
|
1039
|
+
const uvArray = [];
|
|
1040
|
+
const groups = [];
|
|
1041
|
+
const placeholder = [];
|
|
1042
|
+
const bevelEnabled = bevel !== false;
|
|
1043
|
+
const spec = bevel === false || bevel === void 0 ? {} : bevel;
|
|
1044
|
+
let bevelThickness = spec.thickness !== void 0 ? spec.thickness : 0.2;
|
|
1045
|
+
let bevelSize = spec.size !== void 0 ? spec.size : bevelThickness - 0.1;
|
|
1046
|
+
let bevelOffset = spec.offset !== void 0 ? spec.offset : 0;
|
|
1047
|
+
let bevelSegments = spec.segments !== void 0 ? spec.segments : 3;
|
|
1048
|
+
if (!bevelEnabled) {
|
|
1049
|
+
bevelSegments = 0;
|
|
1050
|
+
bevelThickness = 0;
|
|
1051
|
+
bevelSize = 0;
|
|
1052
|
+
bevelOffset = 0;
|
|
1053
|
+
}
|
|
1054
|
+
let vertices = pathPoints(outline.outer);
|
|
1055
|
+
const holes = (outline.holes ?? []).map(pathPoints);
|
|
1056
|
+
const reverse = !isClockWise(vertices);
|
|
1057
|
+
if (reverse) {
|
|
1058
|
+
vertices = vertices.reverse();
|
|
1059
|
+
for (let h = 0; h < holes.length; h++) {
|
|
1060
|
+
const ahole = holes[h];
|
|
1061
|
+
if (isClockWise(ahole)) holes[h] = ahole.reverse();
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
const faces = triangulateShape(vertices, holes);
|
|
1065
|
+
const contour = vertices;
|
|
1066
|
+
for (const ahole of holes) vertices = vertices.concat(ahole);
|
|
1067
|
+
const scalePt2 = (pt, vec, size) => ({ x: pt.x + vec.x * size, y: pt.y + vec.y * size });
|
|
1068
|
+
const vlen = vertices.length;
|
|
1069
|
+
const flen = faces.length;
|
|
1070
|
+
const getBevelVec = (inPt, inPrev, inNext) => {
|
|
1071
|
+
let vTransX;
|
|
1072
|
+
let vTransY;
|
|
1073
|
+
let shrinkBy;
|
|
1074
|
+
const vPrevX = inPt.x - inPrev.x;
|
|
1075
|
+
const vPrevY = inPt.y - inPrev.y;
|
|
1076
|
+
const vNextX = inNext.x - inPt.x;
|
|
1077
|
+
const vNextY = inNext.y - inPt.y;
|
|
1078
|
+
const vPrevLensq = vPrevX * vPrevX + vPrevY * vPrevY;
|
|
1079
|
+
const collinear0 = vPrevX * vNextY - vPrevY * vNextX;
|
|
1080
|
+
if (Math.abs(collinear0) > Number.EPSILON) {
|
|
1081
|
+
const vPrevLen = Math.sqrt(vPrevLensq);
|
|
1082
|
+
const vNextLen = Math.sqrt(vNextX * vNextX + vNextY * vNextY);
|
|
1083
|
+
const ptPrevShiftX = inPrev.x - vPrevY / vPrevLen;
|
|
1084
|
+
const ptPrevShiftY = inPrev.y + vPrevX / vPrevLen;
|
|
1085
|
+
const ptNextShiftX = inNext.x - vNextY / vNextLen;
|
|
1086
|
+
const ptNextShiftY = inNext.y + vNextX / vNextLen;
|
|
1087
|
+
const sf = ((ptNextShiftX - ptPrevShiftX) * vNextY - (ptNextShiftY - ptPrevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
|
|
1088
|
+
vTransX = ptPrevShiftX + vPrevX * sf - inPt.x;
|
|
1089
|
+
vTransY = ptPrevShiftY + vPrevY * sf - inPt.y;
|
|
1090
|
+
const vTransLensq = vTransX * vTransX + vTransY * vTransY;
|
|
1091
|
+
if (vTransLensq <= 2) return { x: vTransX, y: vTransY };
|
|
1092
|
+
shrinkBy = Math.sqrt(vTransLensq / 2);
|
|
1093
|
+
} else {
|
|
1094
|
+
let directionEq = false;
|
|
1095
|
+
if (vPrevX > Number.EPSILON) {
|
|
1096
|
+
if (vNextX > Number.EPSILON) directionEq = true;
|
|
1097
|
+
} else if (vPrevX < -Number.EPSILON) {
|
|
1098
|
+
if (vNextX < -Number.EPSILON) directionEq = true;
|
|
1099
|
+
} else if (Math.sign(vPrevY) === Math.sign(vNextY)) {
|
|
1100
|
+
directionEq = true;
|
|
1101
|
+
}
|
|
1102
|
+
if (directionEq) {
|
|
1103
|
+
vTransX = -vPrevY;
|
|
1104
|
+
vTransY = vPrevX;
|
|
1105
|
+
shrinkBy = Math.sqrt(vPrevLensq);
|
|
1106
|
+
} else {
|
|
1107
|
+
vTransX = vPrevX;
|
|
1108
|
+
vTransY = vPrevY;
|
|
1109
|
+
shrinkBy = Math.sqrt(vPrevLensq / 2);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return { x: vTransX / shrinkBy, y: vTransY / shrinkBy };
|
|
1113
|
+
};
|
|
1114
|
+
const movements = (ring2) => {
|
|
1115
|
+
const out = [];
|
|
1116
|
+
for (let i = 0, il = ring2.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) {
|
|
1117
|
+
if (j === il) j = 0;
|
|
1118
|
+
if (k === il) k = 0;
|
|
1119
|
+
out[i] = getBevelVec(ring2[i], ring2[j], ring2[k]);
|
|
1120
|
+
}
|
|
1121
|
+
return out;
|
|
1122
|
+
};
|
|
1123
|
+
const contourMovements = movements(contour);
|
|
1124
|
+
const holesMovements = [];
|
|
1125
|
+
let verticesMovements = contourMovements.concat();
|
|
1126
|
+
for (const ahole of holes) {
|
|
1127
|
+
const oneHoleMovements = movements(ahole);
|
|
1128
|
+
holesMovements.push(oneHoleMovements);
|
|
1129
|
+
verticesMovements = verticesMovements.concat(oneHoleMovements);
|
|
1130
|
+
}
|
|
1131
|
+
const v = (x, y, z) => {
|
|
1132
|
+
placeholder.push(x);
|
|
1133
|
+
placeholder.push(y);
|
|
1134
|
+
placeholder.push(z);
|
|
1135
|
+
};
|
|
1136
|
+
const bevelLayer = (b, zOf) => {
|
|
1137
|
+
const t = b / bevelSegments;
|
|
1138
|
+
const z = bevelThickness * Math.cos(t * Math.PI / 2);
|
|
1139
|
+
const bs2 = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset;
|
|
1140
|
+
for (let i = 0; i < contour.length; i++) {
|
|
1141
|
+
const vert = scalePt2(contour[i], contourMovements[i], bs2);
|
|
1142
|
+
v(vert.x, vert.y, zOf(z));
|
|
1143
|
+
}
|
|
1144
|
+
for (let h = 0; h < holes.length; h++) {
|
|
1145
|
+
const ahole = holes[h];
|
|
1146
|
+
const oneHoleMovements = holesMovements[h];
|
|
1147
|
+
for (let i = 0; i < ahole.length; i++) {
|
|
1148
|
+
const vert = scalePt2(ahole[i], oneHoleMovements[i], bs2);
|
|
1149
|
+
v(vert.x, vert.y, zOf(z));
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
for (let b = 0; b < bevelSegments; b++) bevelLayer(b, (z) => -z);
|
|
1154
|
+
const bs = bevelSize + bevelOffset;
|
|
1155
|
+
for (let i = 0; i < vlen; i++) {
|
|
1156
|
+
const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
|
|
1157
|
+
v(vert.x, vert.y, 0);
|
|
1158
|
+
}
|
|
1159
|
+
for (let s = 1; s <= steps; s++) {
|
|
1160
|
+
for (let i = 0; i < vlen; i++) {
|
|
1161
|
+
const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
|
|
1162
|
+
v(vert.x, vert.y, depth / steps * s);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
for (let b = bevelSegments - 1; b >= 0; b--) bevelLayer(b, (z) => depth + z);
|
|
1166
|
+
const addVertex = (index) => {
|
|
1167
|
+
verticesArray.push(placeholder[index * 3 + 0]);
|
|
1168
|
+
verticesArray.push(placeholder[index * 3 + 1]);
|
|
1169
|
+
verticesArray.push(placeholder[index * 3 + 2]);
|
|
1170
|
+
};
|
|
1171
|
+
const addUV = (x, y) => {
|
|
1172
|
+
uvArray.push(x);
|
|
1173
|
+
uvArray.push(y);
|
|
1174
|
+
};
|
|
1175
|
+
const at = (index, axis) => verticesArray[index * 3 + axis];
|
|
1176
|
+
const f3 = (a, b, c) => {
|
|
1177
|
+
addVertex(a);
|
|
1178
|
+
addVertex(b);
|
|
1179
|
+
addVertex(c);
|
|
1180
|
+
const nextIndex = verticesArray.length / 3;
|
|
1181
|
+
for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
|
|
1182
|
+
};
|
|
1183
|
+
const f4 = (a, b, c, d) => {
|
|
1184
|
+
addVertex(a);
|
|
1185
|
+
addVertex(b);
|
|
1186
|
+
addVertex(d);
|
|
1187
|
+
addVertex(b);
|
|
1188
|
+
addVertex(c);
|
|
1189
|
+
addVertex(d);
|
|
1190
|
+
const nextIndex = verticesArray.length / 3;
|
|
1191
|
+
const iA = nextIndex - 6;
|
|
1192
|
+
const iB = nextIndex - 3;
|
|
1193
|
+
const iC = nextIndex - 2;
|
|
1194
|
+
const iD = nextIndex - 1;
|
|
1195
|
+
const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
|
|
1196
|
+
const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
|
|
1197
|
+
const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
|
|
1198
|
+
for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
|
|
1199
|
+
};
|
|
1200
|
+
let start = verticesArray.length / 3;
|
|
1201
|
+
if (bevelEnabled) {
|
|
1202
|
+
let offset = 0;
|
|
1203
|
+
for (let i = 0; i < flen; i++) {
|
|
1204
|
+
const face = faces[i];
|
|
1205
|
+
f3(face[2] + offset, face[1] + offset, face[0] + offset);
|
|
1206
|
+
}
|
|
1207
|
+
const layer = steps + bevelSegments * 2;
|
|
1208
|
+
offset = vlen * layer;
|
|
1209
|
+
for (let i = 0; i < flen; i++) {
|
|
1210
|
+
const face = faces[i];
|
|
1211
|
+
f3(face[0] + offset, face[1] + offset, face[2] + offset);
|
|
1212
|
+
}
|
|
1213
|
+
} else {
|
|
1214
|
+
for (let i = 0; i < flen; i++) {
|
|
1215
|
+
const face = faces[i];
|
|
1216
|
+
f3(face[2], face[1], face[0]);
|
|
1217
|
+
}
|
|
1218
|
+
for (let i = 0; i < flen; i++) {
|
|
1219
|
+
const face = faces[i];
|
|
1220
|
+
f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
|
|
1224
|
+
start = verticesArray.length / 3;
|
|
1225
|
+
const sidewalls = (ring2, layeroffset2) => {
|
|
1226
|
+
let i = ring2.length;
|
|
1227
|
+
while (--i >= 0) {
|
|
1228
|
+
const j = i;
|
|
1229
|
+
let k = i - 1;
|
|
1230
|
+
if (k < 0) k = ring2.length - 1;
|
|
1231
|
+
for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) {
|
|
1232
|
+
const slen1 = vlen * s;
|
|
1233
|
+
const slen2 = vlen * (s + 1);
|
|
1234
|
+
f4(layeroffset2 + j + slen1, layeroffset2 + k + slen1, layeroffset2 + k + slen2, layeroffset2 + j + slen2);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
let layeroffset = 0;
|
|
1239
|
+
sidewalls(contour, layeroffset);
|
|
1240
|
+
layeroffset += contour.length;
|
|
1241
|
+
for (const ahole of holes) {
|
|
1242
|
+
sidewalls(ahole, layeroffset);
|
|
1243
|
+
layeroffset += ahole.length;
|
|
1244
|
+
}
|
|
1245
|
+
groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
|
|
1246
|
+
const positions = new Float32Array(verticesArray);
|
|
1247
|
+
return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
|
|
1248
|
+
}
|
|
1249
|
+
var PARAMETER_KEYS = {
|
|
1250
|
+
box: ["size", "segments"],
|
|
1251
|
+
cylinder: ["radiusTop", "radiusBottom", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
|
|
1252
|
+
cone: ["radius", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
|
|
1253
|
+
sphere: ["radius", "widthSegments", "heightSegments", "phiStart", "phiLength", "thetaStart", "thetaLength"],
|
|
1254
|
+
plane: ["width", "height", "widthSegments", "heightSegments"],
|
|
1255
|
+
circle: ["radius", "segments", "thetaStart", "thetaLength"],
|
|
1256
|
+
ring: ["innerRadius", "outerRadius", "thetaSegments", "phiSegments", "thetaStart", "thetaLength"],
|
|
1257
|
+
lathe: ["points", "segments", "phiStart", "phiLength"],
|
|
1258
|
+
torus: ["radius", "tube", "radialSegments", "tubularSegments", "arc"],
|
|
1259
|
+
extrude: ["shape", "depth", "steps", "bevel"],
|
|
1260
|
+
difference: ["size", "subtract"],
|
|
1261
|
+
mesh: ["positions", "normals", "uvs", "indices"]
|
|
1262
|
+
};
|
|
1263
|
+
var SEGMENT_KEYS = {
|
|
1264
|
+
cylinder: ["radialSegments"],
|
|
1265
|
+
cone: ["radialSegments"],
|
|
1266
|
+
sphere: ["widthSegments", "heightSegments"],
|
|
1267
|
+
circle: ["segments"],
|
|
1268
|
+
ring: ["thetaSegments"],
|
|
1269
|
+
lathe: ["segments"],
|
|
1270
|
+
torus: ["radialSegments", "tubularSegments"]
|
|
1271
|
+
};
|
|
1272
|
+
var SEGMENT_DEFAULTS = { radialSegments: 32, widthSegments: 32, heightSegments: 16, segments: 12, thetaSegments: 32, tubularSegments: 48 };
|
|
1273
|
+
var segmentDefault = (kind, key) => kind === "circle" && key === "segments" ? 32 : kind === "torus" && key === "radialSegments" ? 12 : SEGMENT_DEFAULTS[key];
|
|
1274
|
+
function primitiveParameters(node, options = {}) {
|
|
1275
|
+
const out = {};
|
|
1276
|
+
const source = node;
|
|
1277
|
+
for (const key of PARAMETER_KEYS[node.kind]) if (source[key] !== void 0) out[key] = source[key];
|
|
1278
|
+
const detail = options.detail ?? 1;
|
|
1279
|
+
if (detail !== 1) {
|
|
1280
|
+
const floor = options.minSegments ?? 3;
|
|
1281
|
+
for (const key of SEGMENT_KEYS[node.kind] ?? []) {
|
|
1282
|
+
const given = out[key] ?? segmentDefault(node.kind, key);
|
|
1283
|
+
out[key] = Math.max(floor, Math.round(given * detail));
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
return out;
|
|
1287
|
+
}
|
|
1288
|
+
function mesh(p) {
|
|
1289
|
+
const positions = new Float32Array(p.positions);
|
|
1290
|
+
const indices = p.indices ? indexArray(p.indices) : void 0;
|
|
1291
|
+
return {
|
|
1292
|
+
type: "BufferGeometry",
|
|
1293
|
+
parameters: {},
|
|
1294
|
+
positions,
|
|
1295
|
+
normals: p.normals ? new Float32Array(p.normals) : computeVertexNormals(positions, indices),
|
|
1296
|
+
uvs: p.uvs ? new Float32Array(p.uvs) : new Float32Array(positions.length / 3 * 2),
|
|
1297
|
+
...indices ? { indices } : {},
|
|
1298
|
+
groups: []
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
function make(kind, p) {
|
|
1302
|
+
switch (kind) {
|
|
1303
|
+
case "box": {
|
|
1304
|
+
const size = p.size ?? [1, 1, 1];
|
|
1305
|
+
const segments = p.segments ?? [1, 1, 1];
|
|
1306
|
+
return box(size[0], size[1], size[2], segments[0], segments[1], segments[2]);
|
|
1307
|
+
}
|
|
1308
|
+
case "cylinder":
|
|
1309
|
+
return cylinder(p.radiusTop, p.radiusBottom, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
|
|
1310
|
+
case "cone":
|
|
1311
|
+
return cone(p.radius, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
|
|
1312
|
+
case "sphere":
|
|
1313
|
+
return sphere(p.radius, p.widthSegments, p.heightSegments, p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
|
|
1314
|
+
case "plane":
|
|
1315
|
+
return plane(p.width, p.height, p.widthSegments, p.heightSegments);
|
|
1316
|
+
case "circle":
|
|
1317
|
+
return circle(p.radius, p.segments, p.thetaStart, p.thetaLength);
|
|
1318
|
+
case "ring":
|
|
1319
|
+
return ring(p.innerRadius, p.outerRadius, p.thetaSegments, p.phiSegments, p.thetaStart, p.thetaLength);
|
|
1320
|
+
case "lathe":
|
|
1321
|
+
return lathe(p.points, p.segments, p.phiStart, p.phiLength);
|
|
1322
|
+
case "torus":
|
|
1323
|
+
return torus(p.radius, p.tube, p.radialSegments, p.tubularSegments, p.arc);
|
|
1324
|
+
case "extrude":
|
|
1325
|
+
return extrude(p.shape, p.depth, p.steps, p.bevel);
|
|
1326
|
+
case "difference":
|
|
1327
|
+
return difference(p.size, p.subtract ?? []);
|
|
1328
|
+
case "mesh":
|
|
1329
|
+
return mesh(p);
|
|
1330
|
+
default:
|
|
1331
|
+
throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
var templates = /* @__PURE__ */ new Map();
|
|
1335
|
+
var templateLimit = 4096;
|
|
1336
|
+
var stats = { hits: 0, misses: 0 };
|
|
1337
|
+
function buildPrimitive(node, options = {}) {
|
|
1338
|
+
const parameters = primitiveParameters(node, options);
|
|
1339
|
+
if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
|
|
1340
|
+
const key = `${node.kind}:${canonicalJson(parameters)}`;
|
|
1341
|
+
const cached = templates.get(key);
|
|
1342
|
+
if (cached) {
|
|
1343
|
+
stats.hits++;
|
|
1344
|
+
templates.delete(key);
|
|
1345
|
+
templates.set(key, cached);
|
|
1346
|
+
return cached;
|
|
1347
|
+
}
|
|
1348
|
+
stats.misses++;
|
|
1349
|
+
const built = make(node.kind, parameters);
|
|
1350
|
+
templates.set(key, built);
|
|
1351
|
+
if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
|
|
1352
|
+
return built;
|
|
1353
|
+
}
|
|
1354
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
|
|
1355
|
+
var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
|
|
1356
|
+
var str = (v) => typeof v === "string" ? null : "expected a string";
|
|
1357
|
+
var int = (min) => (v) => typeof v === "number" && Number.isInteger(v) && v >= min ? null : `expected an integer >= ${min}`;
|
|
1358
|
+
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}]`;
|
|
1359
|
+
var tuple = (n, item = num) => (v) => {
|
|
1360
|
+
if (!Array.isArray(v) || v.length !== n) return `expected an array of ${n}`;
|
|
1361
|
+
for (const x of v) if (item(x)) return `expected an array of ${n} numbers`;
|
|
1362
|
+
return null;
|
|
1363
|
+
};
|
|
1364
|
+
var list = (item, min = 0) => (v) => {
|
|
1365
|
+
if (!Array.isArray(v)) return "expected an array";
|
|
1366
|
+
if (v.length < min) return `expected at least ${min} items`;
|
|
1367
|
+
for (let i = 0; i < v.length; i++) {
|
|
1368
|
+
const e = item(v[i]);
|
|
1369
|
+
if (e) return `[${i}]: ${e}`;
|
|
1370
|
+
}
|
|
1371
|
+
return null;
|
|
1372
|
+
};
|
|
1373
|
+
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"';
|
|
1374
|
+
var oneOf = (values) => (v) => values.includes(v) ? null : `expected one of ${values.map((x) => JSON.stringify(x)).join(", ")}`;
|
|
1375
|
+
var object = (fields, required = []) => (v) => {
|
|
1376
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return "expected an object";
|
|
1377
|
+
const record = v;
|
|
1378
|
+
for (const key of required) if (record[key] === void 0) return `${key}: required`;
|
|
1379
|
+
for (const key of Object.keys(record)) {
|
|
1380
|
+
if (record[key] === void 0) continue;
|
|
1381
|
+
const check = fields[key];
|
|
1382
|
+
if (!check) return `${key}: unknown field`;
|
|
1383
|
+
const e = check(record[key]);
|
|
1384
|
+
if (e) return `${key}: ${e}`;
|
|
1385
|
+
}
|
|
1386
|
+
return null;
|
|
1387
|
+
};
|
|
1388
|
+
var ORDERS = ["XYZ", "YXZ", "ZXY", "ZYX", "YZX", "XZY"];
|
|
1389
|
+
var TRANSFORM = {
|
|
1390
|
+
position: tuple(3),
|
|
1391
|
+
rotation: tuple(3),
|
|
1392
|
+
rotationOrder: oneOf(ORDERS),
|
|
1393
|
+
scale: (v) => typeof v === "number" ? num(v) : tuple(3)(v),
|
|
1394
|
+
heading: num,
|
|
1395
|
+
matrix: tuple(16)
|
|
1396
|
+
};
|
|
1397
|
+
var UV = object({ u0: num, v0: num, u1: num, v1: num }, ["u0", "v0", "u1", "v1"]);
|
|
1398
|
+
var INSTANCE = object({ ...TRANSFORM, color });
|
|
1399
|
+
var COMMON = {
|
|
1400
|
+
...TRANSFORM,
|
|
1401
|
+
kind: str,
|
|
1402
|
+
name: str,
|
|
1403
|
+
part: str,
|
|
1404
|
+
material: str,
|
|
1405
|
+
color,
|
|
1406
|
+
uv: UV,
|
|
1407
|
+
instances: list(INSTANCE),
|
|
1408
|
+
minDetail: range(0, 1, true),
|
|
1409
|
+
tags: list(str)
|
|
1410
|
+
};
|
|
1411
|
+
var vec2 = tuple(2);
|
|
1412
|
+
var BEVEL = (v) => v === false ? null : object({ thickness: num, size: num, offset: num, segments: int(1) })(v);
|
|
1413
|
+
var KINDS = {
|
|
1414
|
+
group: { fields: {} },
|
|
1415
|
+
box: { fields: { size: tuple(3), segments: tuple(3, int(1)) } },
|
|
1416
|
+
cylinder: { fields: { radiusTop: num, radiusBottom: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
|
|
1417
|
+
cone: { fields: { radius: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
|
|
1418
|
+
sphere: { fields: { radius: num, widthSegments: int(3), heightSegments: int(2), phiStart: num, phiLength: num, thetaStart: num, thetaLength: num } },
|
|
1419
|
+
plane: { fields: { width: num, height: num, widthSegments: int(1), heightSegments: int(1) } },
|
|
1420
|
+
circle: { fields: { radius: num, segments: int(3), thetaStart: num, thetaLength: num } },
|
|
1421
|
+
ring: { fields: { innerRadius: num, outerRadius: num, thetaSegments: int(3), phiSegments: int(1), thetaStart: num, thetaLength: num } },
|
|
1422
|
+
lathe: { fields: { points: list(vec2, 2), segments: int(1), phiStart: num, phiLength: num }, required: ["points"] },
|
|
1423
|
+
torus: { fields: { radius: num, tube: num, radialSegments: int(1), tubularSegments: int(1), arc: num } },
|
|
1424
|
+
extrude: {
|
|
1425
|
+
fields: { shape: object({ outer: list(vec2, 3), holes: list(list(vec2, 3)) }, ["outer"]), depth: num, steps: int(1), bevel: BEVEL },
|
|
1426
|
+
required: ["shape"]
|
|
1427
|
+
},
|
|
1428
|
+
difference: { fields: { size: tuple(3), subtract: list(object({ min: vec2, max: vec2 }, ["min", "max"])) }, required: ["size", "subtract"] },
|
|
1429
|
+
mesh: {
|
|
1430
|
+
fields: {
|
|
1431
|
+
positions: (v) => list(num)(v) ?? (v.length % 3 === 0 ? null : "expected a multiple of 3 numbers"),
|
|
1432
|
+
normals: list(num),
|
|
1433
|
+
uvs: list(num),
|
|
1434
|
+
indices: list(int(0))
|
|
1435
|
+
},
|
|
1436
|
+
required: ["positions"]
|
|
1437
|
+
},
|
|
1438
|
+
point: { fields: { data: () => null } },
|
|
1439
|
+
custom: { fields: { generator: str, params: () => null }, required: ["generator"] }
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
// ../game-shapes-three/dist/index.js
|
|
1443
|
+
var UV_BEFORE_NORMAL = /* @__PURE__ */ new Set(["LatheGeometry", "ExtrudeGeometry"]);
|
|
1444
|
+
var INDEX_FIRST = /* @__PURE__ */ new Set(["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry"]);
|
|
1445
|
+
var isPrimitive = (value) => typeof value.type === "string";
|
|
1446
|
+
function threeParameters(THREE, p) {
|
|
1447
|
+
if (p.type === "LatheGeometry") {
|
|
1448
|
+
const points = p.parameters.points.map(({ x, y }) => new THREE.Vector2(x, y));
|
|
1449
|
+
return { ...p.parameters, points };
|
|
1450
|
+
}
|
|
1451
|
+
return { ...p.parameters };
|
|
1452
|
+
}
|
|
1453
|
+
function toGeometry(THREE, source, options = {}) {
|
|
1454
|
+
const copy = options.copy !== false;
|
|
1455
|
+
const primitive = isPrimitive(source) ? source : null;
|
|
1456
|
+
const ThreeClass = primitive && primitive.type !== "BufferGeometry" ? THREE[primitive.type] : void 0;
|
|
1457
|
+
const geometry = ThreeClass ? Reflect.construct(THREE.BufferGeometry, [], ThreeClass) : new THREE.BufferGeometry();
|
|
1458
|
+
if (primitive && ThreeClass) {
|
|
1459
|
+
Object.assign(geometry, { type: primitive.type, parameters: threeParameters(THREE, primitive) });
|
|
1460
|
+
}
|
|
1461
|
+
const attribute = (array, size) => copy ? new THREE.Float32BufferAttribute(array, size) : new THREE.BufferAttribute(array, size);
|
|
1462
|
+
const index = source.indices;
|
|
1463
|
+
const setIndex = () => {
|
|
1464
|
+
if (!index) return;
|
|
1465
|
+
geometry.setIndex(copy ? index instanceof Uint32Array ? new THREE.Uint32BufferAttribute(index, 1) : new THREE.Uint16BufferAttribute(index, 1) : new THREE.BufferAttribute(index, 1));
|
|
1466
|
+
};
|
|
1467
|
+
const type = primitive?.type ?? "BufferGeometry";
|
|
1468
|
+
if (INDEX_FIRST.has(type)) setIndex();
|
|
1469
|
+
geometry.setAttribute("position", attribute(source.positions, 3));
|
|
1470
|
+
if (UV_BEFORE_NORMAL.has(type)) {
|
|
1471
|
+
geometry.setAttribute("uv", attribute(source.uvs, 2));
|
|
1472
|
+
geometry.setAttribute("normal", attribute(source.normals, 3));
|
|
1473
|
+
} else {
|
|
1474
|
+
geometry.setAttribute("normal", attribute(source.normals, 3));
|
|
1475
|
+
geometry.setAttribute("uv", attribute(source.uvs, 2));
|
|
1476
|
+
}
|
|
1477
|
+
if (!primitive && source.colors) geometry.setAttribute("color", attribute(source.colors, 3));
|
|
1478
|
+
if (!INDEX_FIRST.has(type)) setIndex();
|
|
1479
|
+
if (primitive) for (const group of primitive.groups) geometry.addGroup(group.start, group.count, group.materialIndex);
|
|
1480
|
+
return geometry;
|
|
1481
|
+
}
|
|
1482
|
+
var SHAPE_GEOMETRY_TYPES = ["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry", "ExtrudeGeometry"];
|
|
1483
|
+
function linePoints(path) {
|
|
1484
|
+
if (path.autoClose || path.curves.length === 0) return null;
|
|
1485
|
+
const points = [];
|
|
1486
|
+
for (const [index, curve] of path.curves.entries()) {
|
|
1487
|
+
if (!curve.isLineCurve || !curve.v1 || !curve.v2) return null;
|
|
1488
|
+
if (index === 0) points.push([curve.v1.x, curve.v1.y]);
|
|
1489
|
+
else {
|
|
1490
|
+
const last = points[points.length - 1];
|
|
1491
|
+
if (last[0] !== curve.v1.x || last[1] !== curve.v1.y) return null;
|
|
1492
|
+
}
|
|
1493
|
+
points.push([curve.v2.x, curve.v2.y]);
|
|
1494
|
+
}
|
|
1495
|
+
return points;
|
|
1496
|
+
}
|
|
1497
|
+
function extrudeOf(shapes, options = {}) {
|
|
1498
|
+
if (Array.isArray(shapes) || !shapes || options.extrudePath !== void 0 || options.UVGenerator !== void 0) return null;
|
|
1499
|
+
const shape = shapes;
|
|
1500
|
+
const outer = linePoints(shape);
|
|
1501
|
+
if (!outer) return null;
|
|
1502
|
+
const holes = [];
|
|
1503
|
+
for (const hole of shape.holes ?? []) {
|
|
1504
|
+
const points = linePoints(hole);
|
|
1505
|
+
if (!points) return null;
|
|
1506
|
+
holes.push(points);
|
|
1507
|
+
}
|
|
1508
|
+
const outline = holes.length ? { outer, holes } : { outer };
|
|
1509
|
+
const bevel = options.bevelEnabled === false ? false : {
|
|
1510
|
+
...options.bevelThickness !== void 0 ? { thickness: options.bevelThickness } : {},
|
|
1511
|
+
...options.bevelSize !== void 0 ? { size: options.bevelSize } : {},
|
|
1512
|
+
...options.bevelOffset !== void 0 ? { offset: options.bevelOffset } : {},
|
|
1513
|
+
...options.bevelSegments !== void 0 ? { segments: options.bevelSegments } : {}
|
|
1514
|
+
};
|
|
1515
|
+
return extrude(outline, options.depth, options.steps, bevel);
|
|
1516
|
+
}
|
|
1517
|
+
var namespaces = /* @__PURE__ */ new WeakMap();
|
|
1518
|
+
var shims = /* @__PURE__ */ new WeakSet();
|
|
1519
|
+
function shapeGeometries(THREE, options = {}) {
|
|
1520
|
+
if (shims.has(THREE)) return THREE;
|
|
1521
|
+
const cache = options.cache !== false;
|
|
1522
|
+
const key = cache ? "cache" : "nocache";
|
|
1523
|
+
let byKey = namespaces.get(THREE);
|
|
1524
|
+
if (!byKey) {
|
|
1525
|
+
byKey = /* @__PURE__ */ new Map();
|
|
1526
|
+
namespaces.set(THREE, byKey);
|
|
1527
|
+
}
|
|
1528
|
+
const existing = byKey.get(key);
|
|
1529
|
+
if (existing) return existing;
|
|
1530
|
+
const made = (p) => toGeometry(THREE, p);
|
|
1531
|
+
const viaCache = (node, direct) => cache ? buildPrimitive(node) : direct();
|
|
1532
|
+
const defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
1533
|
+
const constructors = {
|
|
1534
|
+
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))),
|
|
1535
|
+
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))),
|
|
1536
|
+
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))),
|
|
1537
|
+
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))),
|
|
1538
|
+
PlaneGeometry: (width, height, widthSegments, heightSegments) => made(viaCache({ kind: "plane", ...defined({ width, height, widthSegments, heightSegments }) }, () => plane(width, height, widthSegments, heightSegments))),
|
|
1539
|
+
CircleGeometry: (radius, segments, thetaStart, thetaLength) => made(viaCache({ kind: "circle", ...defined({ radius, segments, thetaStart, thetaLength }) }, () => circle(radius, segments, thetaStart, thetaLength))),
|
|
1540
|
+
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))),
|
|
1541
|
+
LatheGeometry: (points, segments, phiStart, phiLength) => {
|
|
1542
|
+
if (!points) return new THREE.LatheGeometry(void 0, segments, phiStart, phiLength);
|
|
1543
|
+
const profile = points.map((p) => [p.x, p.y]);
|
|
1544
|
+
return made(viaCache({ kind: "lathe", points: profile, ...defined({ segments, phiStart, phiLength }) }, () => lathe(profile, segments, phiStart, phiLength)));
|
|
1545
|
+
},
|
|
1546
|
+
TorusGeometry: (radius, tube, radialSegments, tubularSegments, arc) => made(viaCache({ kind: "torus", ...defined({ radius, tube, radialSegments, tubularSegments, arc }) }, () => torus(radius, tube, radialSegments, tubularSegments, arc))),
|
|
1547
|
+
ExtrudeGeometry: (shapes, extrudeOptions) => {
|
|
1548
|
+
const buffers = shapes === void 0 ? null : extrudeOf(shapes, extrudeOptions);
|
|
1549
|
+
if (!buffers) return new THREE.ExtrudeGeometry(shapes, extrudeOptions);
|
|
1550
|
+
const geometry = toGeometry(THREE, buffers);
|
|
1551
|
+
geometry.parameters = { shapes, options: extrudeOptions ?? {} };
|
|
1552
|
+
return geometry;
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
const namespace = { ...THREE };
|
|
1556
|
+
for (const type of SHAPE_GEOMETRY_TYPES) {
|
|
1557
|
+
const make2 = constructors[type];
|
|
1558
|
+
const Constructor = function(...args) {
|
|
1559
|
+
return make2(...args);
|
|
1560
|
+
};
|
|
1561
|
+
Constructor.prototype = THREE[type].prototype;
|
|
1562
|
+
Object.defineProperty(Constructor, "name", { value: type });
|
|
1563
|
+
namespace[type] = Constructor;
|
|
1564
|
+
}
|
|
1565
|
+
const result = namespace;
|
|
1566
|
+
byKey.set(key, result);
|
|
1567
|
+
shims.add(result);
|
|
1568
|
+
return result;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
54
1571
|
// src/locales/en.json
|
|
55
1572
|
var en_default = {
|
|
56
1573
|
plate: {
|
|
@@ -746,9 +2263,9 @@ var GameVehicles = (() => {
|
|
|
746
2263
|
return envTex;
|
|
747
2264
|
}
|
|
748
2265
|
kit.studioEnvironment = studioEnvironment;
|
|
749
|
-
function makeMaterial(
|
|
2266
|
+
function makeMaterial(color2, o) {
|
|
750
2267
|
o = o || {};
|
|
751
|
-
const c =
|
|
2268
|
+
const c = color2 instanceof THREE.Color ? color2 : new THREE.Color(color2);
|
|
752
2269
|
const key = "s" + c.getHexString() + JSON.stringify(o, function(k, v) {
|
|
753
2270
|
return v && v.isColor ? "#" + v.getHexString() : v && v.isTexture ? v.uuid : v;
|
|
754
2271
|
});
|
|
@@ -764,8 +2281,8 @@ var GameVehicles = (() => {
|
|
|
764
2281
|
}
|
|
765
2282
|
return cache[key];
|
|
766
2283
|
}
|
|
767
|
-
function makeFlatMaterial(
|
|
768
|
-
return makeMaterial(
|
|
2284
|
+
function makeFlatMaterial(color2, o) {
|
|
2285
|
+
return makeMaterial(color2, Object.assign({ flatShading: true, roughness: 0.92 }, o || {}));
|
|
769
2286
|
}
|
|
770
2287
|
const DOUBLE_SIDE = THREE.DoubleSide;
|
|
771
2288
|
function toColor(hex) {
|
|
@@ -1013,10 +2530,10 @@ var GameVehicles = (() => {
|
|
|
1013
2530
|
const s = new V3();
|
|
1014
2531
|
for (let i = 0; i < n; i++) {
|
|
1015
2532
|
const fi = i / 3 | 0;
|
|
1016
|
-
const
|
|
2533
|
+
const list2 = map[keys[i]];
|
|
1017
2534
|
s.set(0, 0, 0);
|
|
1018
|
-
for (let j = 0; j <
|
|
1019
|
-
const g = FN[
|
|
2535
|
+
for (let j = 0; j < list2.length; j++) {
|
|
2536
|
+
const g = FN[list2[j]];
|
|
1020
2537
|
if (g.dot(FN[fi]) >= cs) s.add(g);
|
|
1021
2538
|
}
|
|
1022
2539
|
if (s.lengthSq() < 1e-20) s.copy(FN[fi]);
|
|
@@ -1082,7 +2599,7 @@ var GameVehicles = (() => {
|
|
|
1082
2599
|
const pos = [];
|
|
1083
2600
|
const col = [];
|
|
1084
2601
|
const idx = [];
|
|
1085
|
-
const
|
|
2602
|
+
const ring2 = seg + 1;
|
|
1086
2603
|
st.forEach(function(s) {
|
|
1087
2604
|
for (let j = 0; j <= seg; j++) {
|
|
1088
2605
|
const a = j / seg * PI * 2;
|
|
@@ -1100,8 +2617,8 @@ var GameVehicles = (() => {
|
|
|
1100
2617
|
});
|
|
1101
2618
|
for (let i = 0; i < st.length - 1; i++)
|
|
1102
2619
|
for (let k = 0; k < seg; k++) {
|
|
1103
|
-
const a0 = i *
|
|
1104
|
-
const b0 = a0 +
|
|
2620
|
+
const a0 = i * ring2 + k;
|
|
2621
|
+
const b0 = a0 + ring2;
|
|
1105
2622
|
idx.push(a0, a0 + 1, b0, a0 + 1, b0 + 1, b0);
|
|
1106
2623
|
}
|
|
1107
2624
|
;
|
|
@@ -1115,7 +2632,7 @@ var GameVehicles = (() => {
|
|
|
1115
2632
|
col.push(cc.r, cc.g, cc.b);
|
|
1116
2633
|
}
|
|
1117
2634
|
for (let k = 0; k < seg; k++) {
|
|
1118
|
-
const a = si *
|
|
2635
|
+
const a = si * ring2 + k;
|
|
1119
2636
|
if (e === 0) idx.push(ci, a + 1, a);
|
|
1120
2637
|
else idx.push(ci, a, a + 1);
|
|
1121
2638
|
}
|
|
@@ -1253,13 +2770,13 @@ var GameVehicles = (() => {
|
|
|
1253
2770
|
});
|
|
1254
2771
|
tris = next;
|
|
1255
2772
|
});
|
|
1256
|
-
function build(
|
|
2773
|
+
function build(list2) {
|
|
1257
2774
|
const g = new THREE.BufferGeometry();
|
|
1258
2775
|
let off = 0;
|
|
1259
2776
|
names.forEach(function(nm, a) {
|
|
1260
|
-
const arr = new Float32Array(
|
|
2777
|
+
const arr = new Float32Array(list2.length * 3 * sizes[a]);
|
|
1261
2778
|
let w = 0;
|
|
1262
|
-
|
|
2779
|
+
list2.forEach(function(t) {
|
|
1263
2780
|
for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) arr[w++] = t[k][off + q];
|
|
1264
2781
|
});
|
|
1265
2782
|
g.setAttribute(nm, new THREE.BufferAttribute(arr, sizes[a]));
|
|
@@ -1274,10 +2791,10 @@ var GameVehicles = (() => {
|
|
|
1274
2791
|
});
|
|
1275
2792
|
return out;
|
|
1276
2793
|
}
|
|
1277
|
-
function mergeGeometries(
|
|
2794
|
+
function mergeGeometries(list2) {
|
|
1278
2795
|
const pos = [];
|
|
1279
2796
|
const nor = [];
|
|
1280
|
-
|
|
2797
|
+
list2.forEach(function(g2) {
|
|
1281
2798
|
const n = g2.index ? g2.toNonIndexed() : g2;
|
|
1282
2799
|
if (!n.attributes.normal) n.computeVertexNormals();
|
|
1283
2800
|
const pa = n.attributes.position.array;
|
|
@@ -1336,13 +2853,13 @@ var GameVehicles = (() => {
|
|
|
1336
2853
|
};
|
|
1337
2854
|
kit.holeSide = function(poly, o) {
|
|
1338
2855
|
o = o || {};
|
|
1339
|
-
let
|
|
2856
|
+
let area3 = 0;
|
|
1340
2857
|
for (let i = 0; i < poly.length; i++) {
|
|
1341
2858
|
const a = poly[i];
|
|
1342
2859
|
const b = poly[(i + 1) % poly.length];
|
|
1343
|
-
|
|
2860
|
+
area3 += a[0] * b[1] - b[0] * a[1];
|
|
1344
2861
|
}
|
|
1345
|
-
const sg =
|
|
2862
|
+
const sg = area3 > 0 ? 1 : -1;
|
|
1346
2863
|
const E2 = poly.map(function(a, i) {
|
|
1347
2864
|
const b = poly[(i + 1) % poly.length];
|
|
1348
2865
|
const dx = b[0] - a[0];
|
|
@@ -1645,11 +3162,11 @@ var GameVehicles = (() => {
|
|
|
1645
3162
|
return d.clone().applyQuaternion(this.vehicle.quaternion);
|
|
1646
3163
|
}
|
|
1647
3164
|
/** Declares or refines a damage zone: `c.zone(id, label, box, parts, options)`. Pass `label === false` to remove an auto-derived zone. */
|
|
1648
|
-
zone(id, label,
|
|
3165
|
+
zone(id, label, box2, parts, o) {
|
|
1649
3166
|
const d = Object.assign({ id }, o || {});
|
|
1650
3167
|
if (label === false) d.remove = true;
|
|
1651
3168
|
else if (label) d.name = label;
|
|
1652
|
-
if (
|
|
3169
|
+
if (box2) d.box = I.normalizeBox(box2);
|
|
1653
3170
|
if (parts) d.objs = Array.isArray(parts) ? parts.slice() : [parts];
|
|
1654
3171
|
this.zoneDefs.push(d);
|
|
1655
3172
|
return d;
|
|
@@ -1800,7 +3317,7 @@ var GameVehicles = (() => {
|
|
|
1800
3317
|
const seg = o.seg || 24;
|
|
1801
3318
|
const pos = [];
|
|
1802
3319
|
const idx = [];
|
|
1803
|
-
const
|
|
3320
|
+
const ring2 = seg + 1;
|
|
1804
3321
|
const axisY = o.axis === "y";
|
|
1805
3322
|
st.forEach(function(s) {
|
|
1806
3323
|
const a0 = s[1];
|
|
@@ -1822,8 +3339,8 @@ var GameVehicles = (() => {
|
|
|
1822
3339
|
});
|
|
1823
3340
|
for (let i = 0; i < st.length - 1; i++)
|
|
1824
3341
|
for (let k = 0; k < seg; k++) {
|
|
1825
|
-
const A = i *
|
|
1826
|
-
const B = A +
|
|
3342
|
+
const A = i * ring2 + k;
|
|
3343
|
+
const B = A + ring2;
|
|
1827
3344
|
if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
|
|
1828
3345
|
else idx.push(A, A + 1, B, A + 1, B + 1, B);
|
|
1829
3346
|
}
|
|
@@ -1836,7 +3353,7 @@ var GameVehicles = (() => {
|
|
|
1836
3353
|
if (axisY) pos.push(cc, s[0], s[5] || 0);
|
|
1837
3354
|
else pos.push(s[0], cc, s[5] || 0);
|
|
1838
3355
|
for (let k = 0; k < seg; k++) {
|
|
1839
|
-
const a = si *
|
|
3356
|
+
const a = si * ring2 + k;
|
|
1840
3357
|
const flip = e === 0 !== axisY;
|
|
1841
3358
|
if (flip) idx.push(ci, a + 1, a);
|
|
1842
3359
|
else idx.push(ci, a, a + 1);
|
|
@@ -2068,15 +3585,15 @@ var GameVehicles = (() => {
|
|
|
2068
3585
|
c.part("glass", L, m, pos, o.reach || 0.9);
|
|
2069
3586
|
return m;
|
|
2070
3587
|
};
|
|
2071
|
-
kit.asGlass = function(c,
|
|
3588
|
+
kit.asGlass = function(c, mesh2, label, type, o) {
|
|
2072
3589
|
o = o || {};
|
|
2073
|
-
const i = c.deform.indexOf(
|
|
3590
|
+
const i = c.deform.indexOf(mesh2);
|
|
2074
3591
|
if (i >= 0) c.deform.splice(i, 1);
|
|
2075
|
-
if (
|
|
2076
|
-
const geo =
|
|
2077
|
-
geo.applyMatrix4(new THREE.Matrix4().compose(
|
|
3592
|
+
if (mesh2.parent) mesh2.parent.remove(mesh2);
|
|
3593
|
+
const geo = mesh2.geometry;
|
|
3594
|
+
geo.applyMatrix4(new THREE.Matrix4().compose(mesh2.position, mesh2.quaternion, mesh2.scale));
|
|
2078
3595
|
if (o.reuv !== false) o.reuv = true;
|
|
2079
|
-
return kit.addGlass(c, geo, label, type, Object.assign({ mat:
|
|
3596
|
+
return kit.addGlass(c, geo, label, type, Object.assign({ mat: mesh2.material }, o));
|
|
2080
3597
|
};
|
|
2081
3598
|
kit.sideWindows = function(c, polys, Wg, T, label, o) {
|
|
2082
3599
|
o = o || {};
|
|
@@ -2122,12 +3639,12 @@ var GameVehicles = (() => {
|
|
|
2122
3639
|
});
|
|
2123
3640
|
});
|
|
2124
3641
|
};
|
|
2125
|
-
kit.cutMesh = function(
|
|
2126
|
-
const src = o && o.sub ? subdivide(
|
|
3642
|
+
kit.cutMesh = function(mesh2, tests, deg, o) {
|
|
3643
|
+
const src = o && o.sub ? subdivide(mesh2.geometry, o.sub) : mesh2.geometry;
|
|
2127
3644
|
const r = cutGeometry(src, tests);
|
|
2128
3645
|
crease(r.geo, deg || 42);
|
|
2129
|
-
|
|
2130
|
-
|
|
3646
|
+
mesh2.geometry.dispose();
|
|
3647
|
+
mesh2.geometry = r.geo;
|
|
2131
3648
|
return r.cut;
|
|
2132
3649
|
};
|
|
2133
3650
|
kit.quadDef = function(pb, pt, Wg, T, which, bev, o) {
|
|
@@ -2327,8 +3844,8 @@ var GameVehicles = (() => {
|
|
|
2327
3844
|
const pz = nR === 1 ? 0 : (i ? 1 : -1) * w / 4;
|
|
2328
3845
|
const back = gm(g, new THREE.CylinderGeometry(r * 1.06, r * 1.06, d * 0.8, 22), MATERIALS.housing, 0, 0, pz);
|
|
2329
3846
|
back.rotation.z = PI / 2;
|
|
2330
|
-
const
|
|
2331
|
-
|
|
3847
|
+
const ring2 = gm(g, new THREE.TorusGeometry(r * 1.02, 0.013, 8, 30), MATERIALS.chrome, d / 2 - 6e-3, 0, pz);
|
|
3848
|
+
ring2.rotation.y = PI / 2;
|
|
2332
3849
|
const bowl = gm(g, new THREE.SphereGeometry(r * 0.96, 18, 10, 0, PI * 2, 0, PI / 2), MATERIALS.reflectorLit, d / 2 - 0.012, 0, pz);
|
|
2333
3850
|
bowl.rotation.z = PI / 2;
|
|
2334
3851
|
bowl.scale.y = 0.55;
|
|
@@ -2405,8 +3922,8 @@ var GameVehicles = (() => {
|
|
|
2405
3922
|
const e = gm(g, new THREE.CylinderGeometry(r * 0.9, r * 0.9, 0.012, 20), q * ls > 0 ? MATERIALS.tail : MATERIALS.tail, d / 2 - 0.02, 0, pz);
|
|
2406
3923
|
e.rotation.z = PI / 2;
|
|
2407
3924
|
inner.push(e);
|
|
2408
|
-
const
|
|
2409
|
-
|
|
3925
|
+
const ring2 = gm(g, new THREE.TorusGeometry(r, 0.01, 6, 24), MATERIALS.chrome, d / 2 - 4e-3, 0, pz);
|
|
3926
|
+
ring2.rotation.y = PI / 2;
|
|
2410
3927
|
const ln = gm(g, new THREE.SphereGeometry(r, 16, 8, 0, PI * 2, 0, PI / 2), MATERIALS.lensRed, d / 2 - 0.01, 0, pz);
|
|
2411
3928
|
ln.rotation.z = -PI / 2;
|
|
2412
3929
|
ln.scale.y = 0.25;
|
|
@@ -2499,7 +4016,7 @@ var GameVehicles = (() => {
|
|
|
2499
4016
|
} else if (st === "mesh") {
|
|
2500
4017
|
gm(g, roundedBoxGeometry(0.05, h, w, Math.min(0.03, h * 0.3)), MATERIALS.housing);
|
|
2501
4018
|
const cell = 0.026;
|
|
2502
|
-
const
|
|
4019
|
+
const list2 = [];
|
|
2503
4020
|
const rows = Math.floor(h * 0.85 / (cell * 1.6));
|
|
2504
4021
|
const cols = Math.floor(w * 0.9 / (cell * 1.8));
|
|
2505
4022
|
for (let rI = 0; rI < rows; rI++)
|
|
@@ -2508,9 +4025,9 @@ var GameVehicles = (() => {
|
|
|
2508
4025
|
hg.rotateZ(PI / 2);
|
|
2509
4026
|
hg.rotateX(PI / 6);
|
|
2510
4027
|
hg.translate(0.026, -h * 0.42 + (rI + 0.5) * h * 0.85 / rows, -w * 0.45 + (cI + 0.5 + rI % 2 * 0.5 - 0.25) * w * 0.9 / cols);
|
|
2511
|
-
|
|
4028
|
+
list2.push(hg);
|
|
2512
4029
|
}
|
|
2513
|
-
if (
|
|
4030
|
+
if (list2.length) gm(g, mergeGeometries(list2), MATERIALS.trim);
|
|
2514
4031
|
gm(g, roundedBoxGeometry(0.022, 0.014, w, 6e-3), MATERIALS.chrome, 0.03, h / 2 - 8e-3, 0);
|
|
2515
4032
|
} else if (st === "big") {
|
|
2516
4033
|
const fr = roundedBoxGeometry(0.06, h, w, 0.03);
|
|
@@ -3351,7 +4868,7 @@ var GameVehicles = (() => {
|
|
|
3351
4868
|
m.underbodyGroup = ug;
|
|
3352
4869
|
const DK = makeMaterial("#1a1b1c", { roughness: 0.75, metalness: 0.3 });
|
|
3353
4870
|
const ST = MATERIALS.steel;
|
|
3354
|
-
function
|
|
4871
|
+
function box2(w, h, d, mat, x, y, z) {
|
|
3355
4872
|
return c.roundBox(w, h, d, Math.min(0.02, h * 0.4), mat, x, y, z, { parent: ug });
|
|
3356
4873
|
}
|
|
3357
4874
|
function rod(a, b, r, mat) {
|
|
@@ -3368,9 +4885,9 @@ var GameVehicles = (() => {
|
|
|
3368
4885
|
rod(new V3(rx, rr, -zW), new V3(rx, rr, zW), 0.045, ST);
|
|
3369
4886
|
c.sphere(0.13, ST, rx, rr, 0, 1, 0.85, 1, { parent: ug });
|
|
3370
4887
|
rod(new V3(rx + 0.1, rr, 0), new V3(fx - 0.6, yU + 0.04, 0), 0.035, ST);
|
|
3371
|
-
} else
|
|
4888
|
+
} else box2(0.09, 0.08, zW * 2, DK, rx + 0.08, Math.max(yU, rr - 0.12), 0);
|
|
3372
4889
|
if (!ev) {
|
|
3373
|
-
|
|
4890
|
+
box2(Math.min(0.5, (fx - rx) * 0.2), 0.1, 0.38, DK, fx - 0.25, yU + 0.01, -0.05);
|
|
3374
4891
|
const tz = W * 0.3;
|
|
3375
4892
|
const mx = rx - 0.15;
|
|
3376
4893
|
const ty = Math.max(0.14, by - 0.06);
|
|
@@ -3387,10 +4904,10 @@ var GameVehicles = (() => {
|
|
|
3387
4904
|
{ parent: ug, seg: 30, rs: 6 }
|
|
3388
4905
|
);
|
|
3389
4906
|
c.cylinder(0.1, 0.1, 0.42, ST, mx, ty + 0.02, tz, "x", { parent: ug });
|
|
3390
|
-
|
|
3391
|
-
|
|
4907
|
+
box2(0.24, 0.06, 0.2, DK, (fx + rx) / 2 + 0.1, ty, 0.16);
|
|
4908
|
+
box2(0.55, 0.18, W * 0.5, makeMaterial("#232527", { roughness: 0.7 }), rx + 0.62, Math.max(0.16, by - 0.02), -W * 0.1);
|
|
3392
4909
|
} else {
|
|
3393
|
-
|
|
4910
|
+
box2(
|
|
3394
4911
|
(fx - rx) * 0.72,
|
|
3395
4912
|
0.1,
|
|
3396
4913
|
W - 0.4,
|
|
@@ -3399,7 +4916,7 @@ var GameVehicles = (() => {
|
|
|
3399
4916
|
Math.max(0.14, by - 0.03),
|
|
3400
4917
|
0
|
|
3401
4918
|
);
|
|
3402
|
-
for (let k = 0; k < 6; k++)
|
|
4919
|
+
for (let k = 0; k < 6; k++) box2(0.03, 0.02, W - 0.45, ST, rx + 0.4 + k * (fx - rx - 0.8) / 5, Math.max(0.14, by - 0.03) - 0.06, 0);
|
|
3403
4920
|
}
|
|
3404
4921
|
if (o.spare && m.rear < rx - 0.7) {
|
|
3405
4922
|
c.cylinder(0.32, 0.32, 0.2, MATERIALS.rubber, rx - 0.55, Math.max(0.2, by - 0.05), 0, null, { parent: ug });
|
|
@@ -3727,16 +5244,16 @@ var GameVehicles = (() => {
|
|
|
3727
5244
|
}
|
|
3728
5245
|
} else {
|
|
3729
5246
|
const ns = o.spokes || 28;
|
|
3730
|
-
const
|
|
5247
|
+
const list2 = [];
|
|
3731
5248
|
for (let jj = 0; jj < ns; jj++) {
|
|
3732
5249
|
const a2 = jj / ns * PI * 2;
|
|
3733
5250
|
const cg = new THREE.CylinderGeometry(25e-4, 25e-4, sl, 4);
|
|
3734
5251
|
cg.applyMatrix4(
|
|
3735
5252
|
new THREE.Matrix4().makeRotationZ(a2 - PI / 2).setPosition(Math.cos(a2) * sl / 2, Math.sin(a2) * sl / 2, (jj % 2 ? 1 : -1) * 0.012)
|
|
3736
5253
|
);
|
|
3737
|
-
|
|
5254
|
+
list2.push(cg);
|
|
3738
5255
|
}
|
|
3739
|
-
g.add(new THREE.Mesh(mergeGeometries(
|
|
5256
|
+
g.add(new THREE.Mesh(mergeGeometries(list2), MATERIALS.chrome));
|
|
3740
5257
|
}
|
|
3741
5258
|
const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.1, 12), MATERIALS.steel);
|
|
3742
5259
|
hub.rotation.x = PI / 2;
|
|
@@ -3918,9 +5435,9 @@ var GameVehicles = (() => {
|
|
|
3918
5435
|
return puffTexture(k * 17, true);
|
|
3919
5436
|
});
|
|
3920
5437
|
}
|
|
3921
|
-
const
|
|
5438
|
+
const list2 = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
|
|
3922
5439
|
const mat = new THREE.SpriteMaterial({
|
|
3923
|
-
map:
|
|
5440
|
+
map: list2[Math.floor(Math.random() * list2.length)],
|
|
3924
5441
|
color: new THREE.Color(col),
|
|
3925
5442
|
transparent: true,
|
|
3926
5443
|
depthWrite: false,
|
|
@@ -5229,7 +6746,7 @@ var GameVehicles = (() => {
|
|
|
5229
6746
|
}
|
|
5230
6747
|
});
|
|
5231
6748
|
const dset = new Set(c.deform);
|
|
5232
|
-
const
|
|
6749
|
+
const box2 = new THREE.Box3();
|
|
5233
6750
|
c.vehicle.children.forEach(function(o) {
|
|
5234
6751
|
if (dset.has(o) || o.userData.noFollow) return;
|
|
5235
6752
|
if (!(o.isMesh || o.isGroup && o.userData.rigid)) return;
|
|
@@ -5240,10 +6757,10 @@ var GameVehicles = (() => {
|
|
|
5240
6757
|
if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
|
|
5241
6758
|
o.userData.bb0 = o.geometry.boundingBox.clone().applyMatrix4(o.matrix);
|
|
5242
6759
|
} else {
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
o.userData.c0 =
|
|
5246
|
-
o.userData.bb0 =
|
|
6760
|
+
box2.setFromObject(o);
|
|
6761
|
+
box2.applyMatrix4(gInv);
|
|
6762
|
+
o.userData.c0 = box2.getCenter(new V3());
|
|
6763
|
+
o.userData.bb0 = box2.clone();
|
|
5247
6764
|
}
|
|
5248
6765
|
o.userData.pos0 = o.position.clone();
|
|
5249
6766
|
}
|
|
@@ -5539,11 +7056,11 @@ var GameVehicles = (() => {
|
|
|
5539
7056
|
[12, "#d8322a", kit.t("blast.lethal", { m: 12 })],
|
|
5540
7057
|
[30, "#e0a548", kit.t("blast.injury", { m: 30 })]
|
|
5541
7058
|
].forEach(function(r) {
|
|
5542
|
-
const
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
g.add(
|
|
7059
|
+
const ring2 = new THREE.Mesh(new THREE.RingGeometry(r[0] - 0.25, r[0], 96), boomMat(r[1], 0.85));
|
|
7060
|
+
ring2.rotation.x = -PI / 2;
|
|
7061
|
+
ring2.position.y = 0.06;
|
|
7062
|
+
ring2.renderOrder = 5;
|
|
7063
|
+
g.add(ring2);
|
|
5547
7064
|
const fill = new THREE.Mesh(new THREE.CircleGeometry(r[0], 96), boomMat(r[1], 0.07));
|
|
5548
7065
|
fill.rotation.x = -PI / 2;
|
|
5549
7066
|
fill.position.y = 0.05;
|
|
@@ -5560,7 +7077,7 @@ var GameVehicles = (() => {
|
|
|
5560
7077
|
lb.rotation.x = -PI / 2;
|
|
5561
7078
|
lb.position.set(0, 0.08, r[0] + r[0] * 0.06);
|
|
5562
7079
|
g.add(lb);
|
|
5563
|
-
c.bigFx.rings.push(
|
|
7080
|
+
c.bigFx.rings.push(ring2, fill, lb);
|
|
5564
7081
|
});
|
|
5565
7082
|
}
|
|
5566
7083
|
function detonate(c) {
|
|
@@ -5835,7 +7352,7 @@ var GameVehicles = (() => {
|
|
|
5835
7352
|
const I = kit._;
|
|
5836
7353
|
const { MATERIALS } = I;
|
|
5837
7354
|
function rollMeshes(c) {
|
|
5838
|
-
const
|
|
7355
|
+
const list2 = [];
|
|
5839
7356
|
const thin = [];
|
|
5840
7357
|
c.root.updateMatrixWorld(true);
|
|
5841
7358
|
const inv = new THREE.Matrix4().copy(c.vehicle.matrixWorld).invert();
|
|
@@ -5853,9 +7370,9 @@ var GameVehicles = (() => {
|
|
|
5853
7370
|
thin.push({ o: ob, M });
|
|
5854
7371
|
return;
|
|
5855
7372
|
}
|
|
5856
|
-
|
|
7373
|
+
list2.push({ o: ob, M });
|
|
5857
7374
|
});
|
|
5858
|
-
return { list, thin };
|
|
7375
|
+
return { list: list2, thin };
|
|
5859
7376
|
}
|
|
5860
7377
|
function convexHull(pts) {
|
|
5861
7378
|
pts.sort(function(a, b) {
|
|
@@ -5938,7 +7455,7 @@ var GameVehicles = (() => {
|
|
|
5938
7455
|
};
|
|
5939
7456
|
function rollContact(c, A, q) {
|
|
5940
7457
|
const m = c.meta;
|
|
5941
|
-
const
|
|
7458
|
+
const list2 = [];
|
|
5942
7459
|
const dy = A.cen[0] - q[0];
|
|
5943
7460
|
const dz = A.cen[1] - q[1];
|
|
5944
7461
|
const dl = Math.hypot(dy, dz) || 1;
|
|
@@ -5946,17 +7463,17 @@ var GameVehicles = (() => {
|
|
|
5946
7463
|
const f = 0.75;
|
|
5947
7464
|
const bike = m.kind === "bike";
|
|
5948
7465
|
(bike ? [0] : [-0.28, 0, 0.28]).forEach(function(k) {
|
|
5949
|
-
|
|
7466
|
+
list2.push([new V3(A.x + k * A.L, q[0], q[1]), dir]);
|
|
5950
7467
|
});
|
|
5951
7468
|
if (!bike && Math.abs(dir.z) > 0.75 && m.top - m.belt > 0.25 && A.hits < 3) {
|
|
5952
7469
|
const sd = -Math.sign(dir.z);
|
|
5953
7470
|
const yw = m.belt + (m.top - m.belt) * 0.45;
|
|
5954
7471
|
[-0.25, 0.2].forEach(function(k) {
|
|
5955
7472
|
const h = I.probe(c, new V3(A.x + k * A.L, yw, sd * (m.width / 2 + 2)), new V3(0, 0, -sd), 2 + m.width);
|
|
5956
|
-
if (h)
|
|
7473
|
+
if (h) list2.push([h.p, new V3(0, 0, -sd)]);
|
|
5957
7474
|
});
|
|
5958
7475
|
}
|
|
5959
|
-
|
|
7476
|
+
list2.forEach(function(e) {
|
|
5960
7477
|
I.impact(c, e[0], e[1], f, { noStain: true, maxDepth: 0.1 });
|
|
5961
7478
|
const pf = c.toEffects(e[0]);
|
|
5962
7479
|
pf.y = Math.max(0.03, pf.y);
|
|
@@ -5966,15 +7483,15 @@ var GameVehicles = (() => {
|
|
|
5966
7483
|
});
|
|
5967
7484
|
A.hits++;
|
|
5968
7485
|
A.bounce = A.hits === 1 ? 0.06 : 0.1;
|
|
5969
|
-
if (m.kind === "boat") I.burst(c, "water", c.toEffects(
|
|
7486
|
+
if (m.kind === "boat") I.burst(c, "water", c.toEffects(list2[0][0]).setY(0.05), 30, { speed: 3 });
|
|
5970
7487
|
}
|
|
5971
|
-
function exactMinY(c,
|
|
7488
|
+
function exactMinY(c, list2) {
|
|
5972
7489
|
c.root.updateMatrixWorld(true);
|
|
5973
7490
|
const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert();
|
|
5974
7491
|
const M = new THREE.Matrix4();
|
|
5975
7492
|
const v = new V3();
|
|
5976
7493
|
let mn = 1e9;
|
|
5977
|
-
|
|
7494
|
+
list2.forEach(function(e) {
|
|
5978
7495
|
const ob = e.o;
|
|
5979
7496
|
if (!ob.parent || !I.shown(c, ob)) return;
|
|
5980
7497
|
M.multiplyMatrices(inv, ob.matrixWorld);
|
|
@@ -8170,9 +9687,9 @@ var GameVehicles = (() => {
|
|
|
8170
9687
|
down: new V3(0, 1, 0)
|
|
8171
9688
|
}[d] || null;
|
|
8172
9689
|
}
|
|
8173
|
-
function resolveObjs(c,
|
|
9690
|
+
function resolveObjs(c, list2, z) {
|
|
8174
9691
|
const out = [];
|
|
8175
|
-
(
|
|
9692
|
+
(list2 || []).forEach(function(o) {
|
|
8176
9693
|
if (!o) return;
|
|
8177
9694
|
if (typeof o === "string") {
|
|
8178
9695
|
c.parts.forEach(function(p) {
|
|
@@ -8200,22 +9717,22 @@ var GameVehicles = (() => {
|
|
|
8200
9717
|
}
|
|
8201
9718
|
function deriveZones(c) {
|
|
8202
9719
|
const m = c.meta;
|
|
8203
|
-
let
|
|
9720
|
+
let list2 = [];
|
|
8204
9721
|
const byId = {};
|
|
8205
9722
|
(m.zones === false ? [] : I.autoZones(c)).forEach(function(z) {
|
|
8206
9723
|
byId[z.id] = z;
|
|
8207
|
-
|
|
9724
|
+
list2.push(z);
|
|
8208
9725
|
});
|
|
8209
9726
|
(c.zoneDefs || []).forEach(function(d) {
|
|
8210
9727
|
let z = byId[d.id];
|
|
8211
9728
|
if (d.remove) {
|
|
8212
9729
|
if (z) {
|
|
8213
|
-
|
|
9730
|
+
list2.splice(list2.indexOf(z), 1);
|
|
8214
9731
|
delete byId[d.id];
|
|
8215
9732
|
}
|
|
8216
|
-
|
|
9733
|
+
list2.slice().forEach(function(q) {
|
|
8217
9734
|
if (q.id.indexOf(d.id + "_") === 0 && /^\d+$/.test(q.id.slice(d.id.length + 1))) {
|
|
8218
|
-
|
|
9735
|
+
list2.splice(list2.indexOf(q), 1);
|
|
8219
9736
|
delete byId[q.id];
|
|
8220
9737
|
}
|
|
8221
9738
|
});
|
|
@@ -8228,7 +9745,7 @@ var GameVehicles = (() => {
|
|
|
8228
9745
|
} else {
|
|
8229
9746
|
z = Object.assign({ type: "panel" }, d);
|
|
8230
9747
|
byId[d.id] = z;
|
|
8231
|
-
|
|
9748
|
+
list2.push(z);
|
|
8232
9749
|
}
|
|
8233
9750
|
});
|
|
8234
9751
|
const inv = I.worldInverseOf(c);
|
|
@@ -8261,10 +9778,10 @@ var GameVehicles = (() => {
|
|
|
8261
9778
|
}
|
|
8262
9779
|
z.box = I.normalizeBox(bb);
|
|
8263
9780
|
});
|
|
8264
|
-
|
|
9781
|
+
list2 = list2.filter(function(z) {
|
|
8265
9782
|
return z.box && z.name;
|
|
8266
9783
|
});
|
|
8267
|
-
|
|
9784
|
+
list2.forEach(function(z) {
|
|
8268
9785
|
z.glass = (z.glass || []).slice();
|
|
8269
9786
|
z.lights = (z.lights || []).slice();
|
|
8270
9787
|
z.wheels = (z.wheels || []).slice();
|
|
@@ -8301,7 +9818,7 @@ var GameVehicles = (() => {
|
|
|
8301
9818
|
if (owner.has(g) || !g.parent || !g.userData.glass || g.userData.ghost) return;
|
|
8302
9819
|
const cg = I.localBox(c, g, inv).getCenter(new V3());
|
|
8303
9820
|
let best = null;
|
|
8304
|
-
|
|
9821
|
+
list2.forEach(function(z) {
|
|
8305
9822
|
if (z.internal || z.type === "wheel" || z.type === "light") return;
|
|
8306
9823
|
if (I.boxContains(z.box, cg.x, cg.y, cg.z, 0.05) && (!best || I.boxVolume(z.box) < I.boxVolume(best.box))) best = z;
|
|
8307
9824
|
});
|
|
@@ -8310,7 +9827,7 @@ var GameVehicles = (() => {
|
|
|
8310
9827
|
owner.set(g, best);
|
|
8311
9828
|
}
|
|
8312
9829
|
});
|
|
8313
|
-
const looseParts =
|
|
9830
|
+
const looseParts = list2.filter(function(z) {
|
|
8314
9831
|
return z.type === "part";
|
|
8315
9832
|
});
|
|
8316
9833
|
if (looseParts.length)
|
|
@@ -8335,7 +9852,7 @@ var GameVehicles = (() => {
|
|
|
8335
9852
|
});
|
|
8336
9853
|
}
|
|
8337
9854
|
});
|
|
8338
|
-
|
|
9855
|
+
list2.forEach(function(z) {
|
|
8339
9856
|
if (!z.mirror) return;
|
|
8340
9857
|
z.mirrors = [];
|
|
8341
9858
|
const B = z.box;
|
|
@@ -8367,21 +9884,21 @@ var GameVehicles = (() => {
|
|
|
8367
9884
|
});
|
|
8368
9885
|
}
|
|
8369
9886
|
});
|
|
8370
|
-
|
|
9887
|
+
list2.forEach(function(z) {
|
|
8371
9888
|
const B = z.box;
|
|
8372
9889
|
z.map = z.map || [Math.max(B.x0, R - 0.3), Math.min(B.x1, F + 0.3), Math.max(B.z0, -W / 2 - 0.35), Math.min(B.z1, W / 2 + 0.35)];
|
|
8373
9890
|
});
|
|
8374
|
-
carMap(c,
|
|
9891
|
+
carMap(c, list2);
|
|
8375
9892
|
c.zoneOwners = owner;
|
|
8376
|
-
return
|
|
9893
|
+
return list2;
|
|
8377
9894
|
}
|
|
8378
|
-
function carMap(c,
|
|
9895
|
+
function carMap(c, list2) {
|
|
8379
9896
|
const m = c.meta;
|
|
8380
9897
|
const W = m.width || 1.7;
|
|
8381
9898
|
const hw = W / 2;
|
|
8382
9899
|
const gh = m.glasshouse;
|
|
8383
9900
|
if ((m.kind || "car") !== "car" || !gh || gh.fb == null) return;
|
|
8384
|
-
|
|
9901
|
+
list2.forEach(function(z) {
|
|
8385
9902
|
if (z.mapSet) return;
|
|
8386
9903
|
const B = z.box;
|
|
8387
9904
|
const s = z.side;
|
|
@@ -8446,8 +9963,8 @@ var GameVehicles = (() => {
|
|
|
8446
9963
|
const top = m.top || belt + 0.5;
|
|
8447
9964
|
const hw = W / 2;
|
|
8448
9965
|
const WH = I.wheelInfo(c);
|
|
8449
|
-
function add(id, name, type,
|
|
8450
|
-
const z = Object.assign({ id, name, type, box: I.normalizeBox(
|
|
9966
|
+
function add(id, name, type, box2, o) {
|
|
9967
|
+
const z = Object.assign({ id, name, type, box: I.normalizeBox(box2) }, o || {});
|
|
8451
9968
|
Z.push(z);
|
|
8452
9969
|
return z;
|
|
8453
9970
|
}
|
|
@@ -9951,9 +11468,9 @@ var GameVehicles = (() => {
|
|
|
9951
11468
|
const kind = kindOf2(zone);
|
|
9952
11469
|
if (!kind || !zone.box) continue;
|
|
9953
11470
|
const b = zone.box;
|
|
9954
|
-
const
|
|
9955
|
-
if (matrix)
|
|
9956
|
-
out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", box });
|
|
11471
|
+
const box2 = new THREE.Box3(new THREE.Vector3(b.x0, b.y0, b.z0), new THREE.Vector3(b.x1, b.y1, b.z1));
|
|
11472
|
+
if (matrix) box2.applyMatrix4(matrix);
|
|
11473
|
+
out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", box: box2 });
|
|
9957
11474
|
}
|
|
9958
11475
|
return out;
|
|
9959
11476
|
};
|
|
@@ -9964,7 +11481,7 @@ var GameVehicles = (() => {
|
|
|
9964
11481
|
const opts = options || {};
|
|
9965
11482
|
if (!opts.THREE || !opts.THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
|
|
9966
11483
|
const lod = createLod(opts.lod);
|
|
9967
|
-
const THREE = wrapThreeForLod(opts.THREE, lod);
|
|
11484
|
+
const THREE = wrapThreeForLod(shapeGeometries(opts.THREE), lod);
|
|
9968
11485
|
const strings = resolveStrings(opts.locale);
|
|
9969
11486
|
const t = createTranslate(strings);
|
|
9970
11487
|
const doc = opts.document || (typeof document !== "undefined" ? document : void 0);
|
|
@@ -10225,6 +11742,51 @@ var GameVehicles = (() => {
|
|
|
10225
11742
|
return kit;
|
|
10226
11743
|
}
|
|
10227
11744
|
|
|
11745
|
+
// ../game-contracts/dist/index.js
|
|
11746
|
+
var DEFAULT_IGNITION_RULES = {
|
|
11747
|
+
fillCurve: [
|
|
11748
|
+
[0, 0.6],
|
|
11749
|
+
[0.1, 1],
|
|
11750
|
+
[0.35, 0.6],
|
|
11751
|
+
[0.65, 0.3],
|
|
11752
|
+
[0.9, 0.15],
|
|
11753
|
+
[1, 0.12]
|
|
11754
|
+
],
|
|
11755
|
+
flameFillInfluence: 0.5,
|
|
11756
|
+
flameDefaults: { incendiary: 0.7, tracer: 0.2 },
|
|
11757
|
+
sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },
|
|
11758
|
+
sparkMetalDefault: 0.5,
|
|
11759
|
+
sparkFluid: { diesel: 0.15 },
|
|
11760
|
+
fireNear: { radius: 1.5, chancePerSecond: 0.5 }
|
|
11761
|
+
};
|
|
11762
|
+
var resolveIgnitionRules = (rules) => ({
|
|
11763
|
+
...DEFAULT_IGNITION_RULES,
|
|
11764
|
+
...rules,
|
|
11765
|
+
flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },
|
|
11766
|
+
sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },
|
|
11767
|
+
sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },
|
|
11768
|
+
fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear }
|
|
11769
|
+
});
|
|
11770
|
+
var fillFactor = (fill, curve = DEFAULT_IGNITION_RULES.fillCurve) => {
|
|
11771
|
+
if (curve.length === 0) return 1;
|
|
11772
|
+
const f = Math.min(1, Math.max(0, fill));
|
|
11773
|
+
if (f <= curve[0][0]) return curve[0][1];
|
|
11774
|
+
for (let i = 1; i < curve.length; i++) {
|
|
11775
|
+
const [x1, y1] = curve[i];
|
|
11776
|
+
const [x0, y0] = curve[i - 1];
|
|
11777
|
+
if (f <= x1) return x1 === x0 ? y1 : y0 + (y1 - y0) * (f - x0) / (x1 - x0);
|
|
11778
|
+
}
|
|
11779
|
+
return curve[curve.length - 1][1];
|
|
11780
|
+
};
|
|
11781
|
+
var sparkTargetFactor = (materialId, metal, rules = DEFAULT_IGNITION_RULES) => !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault;
|
|
11782
|
+
var ignitionChance = (hit, target, rules = DEFAULT_IGNITION_RULES) => {
|
|
11783
|
+
const k = fillFactor(target.fill, rules.fillCurve);
|
|
11784
|
+
const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === void 0 ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k;
|
|
11785
|
+
const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0);
|
|
11786
|
+
const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
|
|
11787
|
+
return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
|
|
11788
|
+
};
|
|
11789
|
+
|
|
10228
11790
|
// src/destructible/model.ts
|
|
10229
11791
|
var JOINT_RULES = {
|
|
10230
11792
|
hinge: { strength: 5e3, tools: ["wrench", "screwdriver"], seconds: 3 },
|
|
@@ -10352,19 +11914,19 @@ var GameVehicles = (() => {
|
|
|
10352
11914
|
for (const z of zones) {
|
|
10353
11915
|
let role = roleOfZone(z);
|
|
10354
11916
|
if (z === hubZone) role = "frame";
|
|
10355
|
-
const
|
|
10356
|
-
const
|
|
11917
|
+
const box2 = boxOf(z);
|
|
11918
|
+
const area3 = areaOf(box2.size);
|
|
10357
11919
|
const id = z.id;
|
|
10358
11920
|
const label = String(z.name ?? id);
|
|
10359
11921
|
const tags = [...z.tags ?? []];
|
|
10360
11922
|
if (role === "rim") {
|
|
10361
11923
|
const wheels = z.wheels ?? [];
|
|
10362
11924
|
const r = Math.max(0.2, ...wheels.map((w) => w.userData?.r ?? 0.3));
|
|
10363
|
-
const sz = Math.sign(
|
|
10364
|
-
const rimBox = { center: [
|
|
11925
|
+
const sz = Math.sign(box2.center[2]) || 1;
|
|
11926
|
+
const rimBox = { center: [box2.center[0], box2.center[1], box2.center[2] - sz * box2.size[2] * 0.25], size: [box2.size[0] * 0.7, box2.size[1] * 0.7, box2.size[2] * 0.5] };
|
|
10365
11927
|
add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box: rimBox, mass: 22 * r * Math.max(1, wheels.length), hp: 12e3 * armour, zoneId: id, visual: kind !== "plane" && !/landing/.test(id), tags: ["wheel", ...tags] });
|
|
10366
11928
|
const tyreId = id.replace(/^wheels?/, "tyre").replace(/^(?!tyre)/, "tyre_");
|
|
10367
|
-
const tyreBox = { center:
|
|
11929
|
+
const tyreBox = { center: box2.center, size: [box2.size[0] * 0.98, box2.size[1] * 0.98, box2.size[2]] };
|
|
10368
11930
|
add({
|
|
10369
11931
|
id: tyreId,
|
|
10370
11932
|
role: "tyre",
|
|
@@ -10384,29 +11946,29 @@ var GameVehicles = (() => {
|
|
|
10384
11946
|
if (role === "glass") {
|
|
10385
11947
|
const glass = (z.glass ?? [])[0];
|
|
10386
11948
|
const laminated = glass?.userData?.glass?.type === "laminated" || m.bullet;
|
|
10387
|
-
add({ id, role, name: label, material: "glass", box, mass: 10 *
|
|
11949
|
+
add({ id, role, name: label, material: "glass", box: box2, mass: 10 * area3, hp: laminated ? 4e3 : 450, zoneId: id, visual: false, tags: ["glass", ...tags] });
|
|
10388
11950
|
continue;
|
|
10389
11951
|
}
|
|
10390
11952
|
if (role === "light") {
|
|
10391
|
-
add({ id, role, name: label, material: "glass", box, mass: 1.2 + 4 *
|
|
11953
|
+
add({ id, role, name: label, material: "glass", box: box2, mass: 1.2 + 4 * area3, hp: 320, zoneId: id, visual: true, tags: ["light", ...tags] });
|
|
10392
11954
|
continue;
|
|
10393
11955
|
}
|
|
10394
11956
|
if (role === "bumper") {
|
|
10395
|
-
add({ id, role, name: label, material: "plastic", box, mass: 6 + 5 *
|
|
11957
|
+
add({ id, role, name: label, material: "plastic", box: box2, mass: 6 + 5 * area3, hp: 9e3, ignitionC: 400, zoneId: id, visual: true, tags: ["bumper", ...tags] });
|
|
10396
11958
|
continue;
|
|
10397
11959
|
}
|
|
10398
11960
|
if (role === "engine") {
|
|
10399
11961
|
const kg2 = { bike: 55, quad: 90, car: 150, tractor: 450, truck: 600, bus: 450, tank: 900, boat: 90, plane: 160, heli: 150 }[kind];
|
|
10400
|
-
add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box, mass: kg2, hp: 6e4 * armour, ignitionC: 260, zoneId: id, visual: false, tags: ["engine", ...tags] });
|
|
11962
|
+
add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box: box2, mass: kg2, hp: 6e4 * armour, ignitionC: 260, zoneId: id, visual: false, tags: ["engine", ...tags] });
|
|
10401
11963
|
continue;
|
|
10402
11964
|
}
|
|
10403
11965
|
if (role === "radiator") {
|
|
10404
|
-
add({ id, role, name: label, material: "aluminium", box, mass: 8, hp: 3e3, zoneId: id, visual: false, contains: { fluid: "coolant", liters: kind === "truck" || kind === "bus" ? 18 : 7, pressurized: true }, tags: ["radiator", ...tags] });
|
|
11966
|
+
add({ id, role, name: label, material: "aluminium", box: box2, mass: 8, hp: 3e3, zoneId: id, visual: false, contains: { fluid: "coolant", liters: kind === "truck" || kind === "bus" ? 18 : 7, pressurized: true }, tags: ["radiator", ...tags] });
|
|
10405
11967
|
continue;
|
|
10406
11968
|
}
|
|
10407
11969
|
if (role === "fuel_tank") {
|
|
10408
11970
|
if (fuelless) {
|
|
10409
|
-
add({ id, role: "other", name: label, material: "steel", box, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
|
|
11971
|
+
add({ id, role: "other", name: label, material: "steel", box: box2, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
|
|
10410
11972
|
continue;
|
|
10411
11973
|
}
|
|
10412
11974
|
const liters = { bike: 14, quad: 16, car: 55, tractor: 150, truck: 300, bus: 200, tank: 600, boat: 90, plane: 120, heli: 300 }[kind];
|
|
@@ -10416,7 +11978,7 @@ var GameVehicles = (() => {
|
|
|
10416
11978
|
role,
|
|
10417
11979
|
name: label,
|
|
10418
11980
|
material: "steel",
|
|
10419
|
-
box,
|
|
11981
|
+
box: box2,
|
|
10420
11982
|
mass: 14 + liters * 0.8,
|
|
10421
11983
|
hp: 25e3 * armour,
|
|
10422
11984
|
zoneId: id,
|
|
@@ -10429,21 +11991,21 @@ var GameVehicles = (() => {
|
|
|
10429
11991
|
continue;
|
|
10430
11992
|
}
|
|
10431
11993
|
if (role === "battery") {
|
|
10432
|
-
add({ id, role, name: label, material: "plastic", box, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
|
|
11994
|
+
add({ id, role, name: label, material: "plastic", box: box2, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
|
|
10433
11995
|
continue;
|
|
10434
11996
|
}
|
|
10435
11997
|
if (role === "frame") {
|
|
10436
|
-
add({ id, role, name: label, material: "steel", box: floorPan(
|
|
11998
|
+
add({ id, role, name: label, material: "steel", box: floorPan(box2), mass: mass * 0.25, hp: 15e4 * armour, zoneId: id, visual: false, tags: ["frame", ...tags] });
|
|
10437
11999
|
continue;
|
|
10438
12000
|
}
|
|
10439
12001
|
const roofLike = role === "roof";
|
|
10440
|
-
const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) *
|
|
12002
|
+
const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) * area3 * (role === "door" ? 1.6 : 1);
|
|
10441
12003
|
add({
|
|
10442
12004
|
id,
|
|
10443
12005
|
role,
|
|
10444
12006
|
name: label,
|
|
10445
12007
|
material: skin,
|
|
10446
|
-
box,
|
|
12008
|
+
box: box2,
|
|
10447
12009
|
mass: Math.max(2, kg),
|
|
10448
12010
|
hp: (roofLike ? 3e4 : 2e4) * armour,
|
|
10449
12011
|
zoneId: id,
|
|
@@ -10665,7 +12227,7 @@ var GameVehicles = (() => {
|
|
|
10665
12227
|
const my = kind === "bike" || kind === "quad" ? (m.top ?? belt + 0.25) - 0.1 : belt + 0.12;
|
|
10666
12228
|
for (const s of [-1, 1]) {
|
|
10667
12229
|
const door = zones.find((z) => z.type === "door" && z.side === s && (z.mirrors ?? []).length);
|
|
10668
|
-
let
|
|
12230
|
+
let box2 = { center: [mx, my, s * half], size: [0.14, 0.12, 0.2] };
|
|
10669
12231
|
if (door) {
|
|
10670
12232
|
const bb = door.mirrors.reduce(
|
|
10671
12233
|
(acc, o) => {
|
|
@@ -10678,14 +12240,14 @@ var GameVehicles = (() => {
|
|
|
10678
12240
|
},
|
|
10679
12241
|
null
|
|
10680
12242
|
);
|
|
10681
|
-
if (bb)
|
|
12243
|
+
if (bb) box2 = { center: [(bb[0] + bb[1]) / 2, (bb[2] + bb[3]) / 2, (bb[4] + bb[5]) / 2], size: [Math.max(0.06, bb[1] - bb[0]), Math.max(0.06, bb[3] - bb[2]), Math.max(0.06, bb[5] - bb[4])] };
|
|
10682
12244
|
}
|
|
10683
12245
|
add({
|
|
10684
12246
|
id: `mirror_${side(s)}`,
|
|
10685
12247
|
role: "mirror",
|
|
10686
12248
|
name: t("mechanics.mirror", { side: t(`sides.${side(s)}`), sideM: t(`sides.${side(s)}M`) }),
|
|
10687
12249
|
material: "plastic",
|
|
10688
|
-
box,
|
|
12250
|
+
box: box2,
|
|
10689
12251
|
mass: 1.1,
|
|
10690
12252
|
hp: 1500,
|
|
10691
12253
|
ignitionC: 400,
|
|
@@ -10875,13 +12437,13 @@ var GameVehicles = (() => {
|
|
|
10875
12437
|
(parent ?? this.c.vehicle).add(m);
|
|
10876
12438
|
return m;
|
|
10877
12439
|
}
|
|
10878
|
-
surface(axis,
|
|
12440
|
+
surface(axis, sign2, B, samples) {
|
|
10879
12441
|
const V3 = this.THREE.Vector3;
|
|
10880
12442
|
const hits = [];
|
|
10881
12443
|
const far = 3;
|
|
10882
12444
|
for (const [u, v] of samples) {
|
|
10883
|
-
const o = axis === "z" ? new V3(u, v,
|
|
10884
|
-
const d = axis === "z" ? new V3(0, 0, -
|
|
12445
|
+
const o = axis === "z" ? new V3(u, v, sign2 * (Math.max(Math.abs(B.z0), Math.abs(B.z1)) + 1)) : new V3(sign2 * (Math.max(Math.abs(B.x0), Math.abs(B.x1)) + 1), u, v);
|
|
12446
|
+
const d = axis === "z" ? new V3(0, 0, -sign2) : new V3(-sign2, 0, 0);
|
|
10885
12447
|
const h = this.I.probe(this.c, o, d, far);
|
|
10886
12448
|
if (h) hits.push(axis === "z" ? h.p.z : h.p.x);
|
|
10887
12449
|
}
|
|
@@ -11031,12 +12593,12 @@ var GameVehicles = (() => {
|
|
|
11031
12593
|
const z = this.zone(p);
|
|
11032
12594
|
if (!z) return false;
|
|
11033
12595
|
const V3 = this.THREE.Vector3;
|
|
11034
|
-
const
|
|
11035
|
-
if (!
|
|
12596
|
+
const list2 = z.glass.filter((g) => g.parent && g.visible);
|
|
12597
|
+
if (!list2.length) return false;
|
|
11036
12598
|
const pt = new V3(...point);
|
|
11037
|
-
let best =
|
|
12599
|
+
let best = list2[0];
|
|
11038
12600
|
let bd = Infinity;
|
|
11039
|
-
for (const g of
|
|
12601
|
+
for (const g of list2) {
|
|
11040
12602
|
g.geometry.computeBoundingBox();
|
|
11041
12603
|
const bb = g.geometry.boundingBox.clone().applyMatrix4(g.matrix);
|
|
11042
12604
|
const d = bb.distanceToPoint(pt);
|
|
@@ -11337,6 +12899,9 @@ var GameVehicles = (() => {
|
|
|
11337
12899
|
const headless = !!options.headless;
|
|
11338
12900
|
const seedBase = strHash(`${c.key}|${id}|${options.seed ?? 0}`);
|
|
11339
12901
|
let rand = rng(seedBase);
|
|
12902
|
+
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
12903
|
+
const fuelFraction = clamp(options.fuel ?? 1, 0, 1);
|
|
12904
|
+
let igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
|
|
11340
12905
|
let clock = 0;
|
|
11341
12906
|
let model;
|
|
11342
12907
|
let vis = null;
|
|
@@ -11404,7 +12969,7 @@ var GameVehicles = (() => {
|
|
|
11404
12969
|
dents: [],
|
|
11405
12970
|
holes: [],
|
|
11406
12971
|
cracks: [],
|
|
11407
|
-
liters: d.contains
|
|
12972
|
+
liters: d.contains ? d.role === "fuel_tank" ? d.contains.liters * fuelFraction : d.contains.liters : void 0,
|
|
11408
12973
|
lost: false,
|
|
11409
12974
|
status: "intact",
|
|
11410
12975
|
flat: false,
|
|
@@ -11422,6 +12987,7 @@ var GameVehicles = (() => {
|
|
|
11422
12987
|
wasLit = true;
|
|
11423
12988
|
deadEngine = false;
|
|
11424
12989
|
rand = rng(seedBase);
|
|
12990
|
+
igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
|
|
11425
12991
|
steerSign = rand() < 0.5 ? -1 : 1;
|
|
11426
12992
|
vis = headless ? null : new VehicleVisuals(kit, c, model);
|
|
11427
12993
|
recomputeGroups();
|
|
@@ -11443,8 +13009,8 @@ var GameVehicles = (() => {
|
|
|
11443
13009
|
comps.get(r).push(p);
|
|
11444
13010
|
}
|
|
11445
13011
|
const hubRoot = parent.has(model.hub) ? find(model.hub) : "";
|
|
11446
|
-
const
|
|
11447
|
-
groupsCache =
|
|
13012
|
+
const list2 = [...comps.entries()].sort((a, b) => a[0] === hubRoot ? -1 : b[0] === hubRoot ? 1 : 0);
|
|
13013
|
+
groupsCache = list2.map(([, v]) => v);
|
|
11448
13014
|
groupOf = /* @__PURE__ */ new Map();
|
|
11449
13015
|
groupsCache.forEach((g, i) => g.forEach((p) => groupOf.set(p, i)));
|
|
11450
13016
|
return groupsCache;
|
|
@@ -11716,8 +13282,19 @@ var GameVehicles = (() => {
|
|
|
11716
13282
|
if (!p.burning && p.temp > AMBIENT) p.temp = Math.max(AMBIENT, p.temp - dt * 6);
|
|
11717
13283
|
}
|
|
11718
13284
|
for (const f of flames) heat(centreOf(f.def), 0.8, 500, dt * 0.5, out, 0);
|
|
13285
|
+
fireNearLeak(flames, dt, out);
|
|
11719
13286
|
thermal(out);
|
|
11720
13287
|
};
|
|
13288
|
+
const fireNearLeak = (flames, dt, out) => {
|
|
13289
|
+
const tank = tankPart();
|
|
13290
|
+
const leak = tank ? leaks.get(tank.def.id) : void 0;
|
|
13291
|
+
if (!tank || !leak || tank.lost || tank.burning || exploded || leak.fluid !== "fuel" && leak.fluid !== "diesel") return;
|
|
13292
|
+
if (leak.pud?.fire) return light(tank, out);
|
|
13293
|
+
const { radius, chancePerSecond } = ignitionRules.fireNear;
|
|
13294
|
+
if (chancePerSecond <= 0) return;
|
|
13295
|
+
const near = flames.some((f) => f !== tank && distToBox(tank.def, centreOf(f.def)) - Math.max(...f.def.box.size) / 2 <= radius);
|
|
13296
|
+
if (near && igniteRand() < 1 - Math.exp(-chancePerSecond * dt)) light(tank, out);
|
|
13297
|
+
};
|
|
11721
13298
|
const normalOf = (d, v) => faceNormal(d, v);
|
|
11722
13299
|
const ballistic = (h, out) => {
|
|
11723
13300
|
sync();
|
|
@@ -11785,10 +13362,21 @@ var GameVehicles = (() => {
|
|
|
11785
13362
|
let amount = through ? E * 0.6 : E * 0.25;
|
|
11786
13363
|
if (through && (d.role === "battery" || d.role === "alternator")) amount = Math.max(amount, p.hp * 0.65);
|
|
11787
13364
|
damage(p, amount, "ballistic", h.source, out, worldPoint);
|
|
11788
|
-
if (d.role === "fuel_tank"
|
|
13365
|
+
if (through && d.role === "fuel_tank") rollIgnition(p, h, metal, out);
|
|
11789
13366
|
}
|
|
11790
13367
|
}
|
|
11791
13368
|
};
|
|
13369
|
+
const light = (tank, out) => {
|
|
13370
|
+
tank.temp = Math.max(tank.temp, (tank.def.ignitionC ?? 260) + 1);
|
|
13371
|
+
if ((tank.liters ?? 0) > 0.5) ignite(tank, out);
|
|
13372
|
+
};
|
|
13373
|
+
const rollIgnition = (tank, h, metal, out) => {
|
|
13374
|
+
const d = tank.def;
|
|
13375
|
+
if (!d.contains || tank.burning || (tank.liters ?? 0) <= 0.05) return;
|
|
13376
|
+
const fill = d.contains.liters > 0 ? (tank.liters ?? 0) / d.contains.liters : 0;
|
|
13377
|
+
const chance = ignitionChance(h, { sparkTarget: sparkTargetFactor(d.material, metal, ignitionRules), fill, fluid: d.contains.fluid }, ignitionRules);
|
|
13378
|
+
if (chance > 0 && igniteRand() < chance) light(tank, out);
|
|
13379
|
+
};
|
|
11792
13380
|
const blunt = (h, out) => {
|
|
11793
13381
|
sync();
|
|
11794
13382
|
const M = toFrameMatrix();
|
|
@@ -11852,7 +13440,7 @@ var GameVehicles = (() => {
|
|
|
11852
13440
|
const verdict = kit.blastDamage(c, E / (1 + dist * dist * 4) / 50);
|
|
11853
13441
|
if (verdict === "explosion") explode(tank, h.source, out);
|
|
11854
13442
|
else if (verdict === "fire") {
|
|
11855
|
-
|
|
13443
|
+
light(tank, out);
|
|
11856
13444
|
startLeak(tank, centreOf(tank.def).setY(tank.def.box.center[1] - tank.def.box.size[1] / 2 + 0.05), 12, out, dir);
|
|
11857
13445
|
}
|
|
11858
13446
|
}
|
|
@@ -11866,9 +13454,9 @@ var GameVehicles = (() => {
|
|
|
11866
13454
|
thermal(out);
|
|
11867
13455
|
};
|
|
11868
13456
|
const sound = (role) => {
|
|
11869
|
-
const
|
|
11870
|
-
if (!
|
|
11871
|
-
return
|
|
13457
|
+
const list2 = [...ps.values()].filter((p) => p.def.role === role);
|
|
13458
|
+
if (!list2.length) return true;
|
|
13459
|
+
return list2.every((p) => !p.lost && attached(p.def.id) && p.hp >= p.def.hp * 0.5);
|
|
11872
13460
|
};
|
|
11873
13461
|
const tankPart = () => [...ps.values()].find((p) => p.def.role === "fuel_tank" && p.def.contains);
|
|
11874
13462
|
const computeStatus = () => {
|
|
@@ -12060,8 +13648,8 @@ var GameVehicles = (() => {
|
|
|
12060
13648
|
queue = [];
|
|
12061
13649
|
const hood = c.hood;
|
|
12062
13650
|
if (hood) {
|
|
12063
|
-
const
|
|
12064
|
-
const cur = hood.piv.rotation.z /
|
|
13651
|
+
const sign2 = hood.sign || 1;
|
|
13652
|
+
const cur = hood.piv.rotation.z / sign2;
|
|
12065
13653
|
if (Math.abs(cur - hood.ang) > 1e-4) {
|
|
12066
13654
|
hood.ang = cur;
|
|
12067
13655
|
hood.target = cur;
|