@adatechnology/game-vehicles 0.2.0 → 0.4.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 +20 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.global.js +2006 -194
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +320 -64
- 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) {
|
|
@@ -984,46 +2501,79 @@ var GameVehicles = (() => {
|
|
|
984
2501
|
}
|
|
985
2502
|
const p = geo.attributes.position;
|
|
986
2503
|
const n = p.count;
|
|
987
|
-
const
|
|
988
|
-
const
|
|
989
|
-
const
|
|
990
|
-
const
|
|
991
|
-
const C = new V3();
|
|
992
|
-
const e1 = new V3();
|
|
993
|
-
const e2 = new V3();
|
|
2504
|
+
const faces = n / 3;
|
|
2505
|
+
const FN = new Float64Array(faces * 3);
|
|
2506
|
+
const map = /* @__PURE__ */ new Map();
|
|
2507
|
+
const RANGE = 65536;
|
|
994
2508
|
function key(i) {
|
|
995
|
-
|
|
2509
|
+
const x = Math.round(p.getX(i) * 2e3);
|
|
2510
|
+
const y = Math.round(p.getY(i) * 2e3);
|
|
2511
|
+
const z = Math.round(p.getZ(i) * 2e3);
|
|
2512
|
+
if (Math.abs(x) < RANGE && Math.abs(y) < RANGE && Math.abs(z) < RANGE) return ((x + RANGE) * 131072 + (y + RANGE)) * 131072 + (z + RANGE);
|
|
2513
|
+
return x + "," + y + "," + z;
|
|
996
2514
|
}
|
|
997
2515
|
const keys = new Array(n);
|
|
998
|
-
for (let f = 0; f <
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1004
|
-
|
|
2516
|
+
for (let f = 0; f < faces; f++) {
|
|
2517
|
+
const ax = p.getX(3 * f);
|
|
2518
|
+
const ay = p.getY(3 * f);
|
|
2519
|
+
const az = p.getZ(3 * f);
|
|
2520
|
+
const bx = p.getX(3 * f + 1);
|
|
2521
|
+
const by = p.getY(3 * f + 1);
|
|
2522
|
+
const bz = p.getZ(3 * f + 1);
|
|
2523
|
+
const cx = p.getX(3 * f + 2);
|
|
2524
|
+
const cy = p.getY(3 * f + 2);
|
|
2525
|
+
const cz = p.getZ(3 * f + 2);
|
|
2526
|
+
const e1x = cx - bx;
|
|
2527
|
+
const e1y = cy - by;
|
|
2528
|
+
const e1z = cz - bz;
|
|
2529
|
+
const e2x = ax - bx;
|
|
2530
|
+
const e2y = ay - by;
|
|
2531
|
+
const e2z = az - bz;
|
|
2532
|
+
let nx = e1y * e2z - e1z * e2y;
|
|
2533
|
+
let ny = e1z * e2x - e1x * e2z;
|
|
2534
|
+
let nz = e1x * e2y - e1y * e2x;
|
|
2535
|
+
if (nx * nx + ny * ny + nz * nz > 1e-20) {
|
|
2536
|
+
const inv = 1 / (Math.sqrt(nx * nx + ny * ny + nz * nz) || 1);
|
|
2537
|
+
nx *= inv;
|
|
2538
|
+
ny *= inv;
|
|
2539
|
+
nz *= inv;
|
|
2540
|
+
}
|
|
2541
|
+
FN[3 * f] = nx;
|
|
2542
|
+
FN[3 * f + 1] = ny;
|
|
2543
|
+
FN[3 * f + 2] = nz;
|
|
1005
2544
|
for (let k = 0; k < 3; k++) {
|
|
1006
2545
|
const kk = key(3 * f + k);
|
|
1007
2546
|
keys[3 * f + k] = kk;
|
|
1008
|
-
|
|
2547
|
+
const list2 = map.get(kk);
|
|
2548
|
+
if (list2) list2.push(f);
|
|
2549
|
+
else map.set(kk, [f]);
|
|
1009
2550
|
}
|
|
1010
2551
|
}
|
|
1011
2552
|
const cs = Math.cos(deg * PI / 180);
|
|
1012
2553
|
const out = new Float32Array(n * 3);
|
|
1013
|
-
const s = new V3();
|
|
1014
2554
|
for (let i = 0; i < n; i++) {
|
|
1015
|
-
const fi = i / 3 | 0;
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
2555
|
+
const fi = (i / 3 | 0) * 3;
|
|
2556
|
+
const list2 = map.get(keys[i]);
|
|
2557
|
+
let sx = 0;
|
|
2558
|
+
let sy = 0;
|
|
2559
|
+
let sz = 0;
|
|
2560
|
+
for (let j = 0; j < list2.length; j++) {
|
|
2561
|
+
const g = list2[j] * 3;
|
|
2562
|
+
if (FN[g] * FN[fi] + FN[g + 1] * FN[fi + 1] + FN[g + 2] * FN[fi + 2] >= cs) {
|
|
2563
|
+
sx += FN[g];
|
|
2564
|
+
sy += FN[g + 1];
|
|
2565
|
+
sz += FN[g + 2];
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
if (sx * sx + sy * sy + sz * sz < 1e-20) {
|
|
2569
|
+
sx = FN[fi];
|
|
2570
|
+
sy = FN[fi + 1];
|
|
2571
|
+
sz = FN[fi + 2];
|
|
2572
|
+
}
|
|
2573
|
+
const inv = 1 / (Math.sqrt(sx * sx + sy * sy + sz * sz) || 1);
|
|
2574
|
+
out[3 * i] = sx * inv;
|
|
2575
|
+
out[3 * i + 1] = sy * inv;
|
|
2576
|
+
out[3 * i + 2] = sz * inv;
|
|
1027
2577
|
}
|
|
1028
2578
|
geo.setAttribute("normal", new THREE.BufferAttribute(out, 3));
|
|
1029
2579
|
geo.userData.crease = true;
|
|
@@ -1082,7 +2632,7 @@ var GameVehicles = (() => {
|
|
|
1082
2632
|
const pos = [];
|
|
1083
2633
|
const col = [];
|
|
1084
2634
|
const idx = [];
|
|
1085
|
-
const
|
|
2635
|
+
const ring2 = seg + 1;
|
|
1086
2636
|
st.forEach(function(s) {
|
|
1087
2637
|
for (let j = 0; j <= seg; j++) {
|
|
1088
2638
|
const a = j / seg * PI * 2;
|
|
@@ -1100,8 +2650,8 @@ var GameVehicles = (() => {
|
|
|
1100
2650
|
});
|
|
1101
2651
|
for (let i = 0; i < st.length - 1; i++)
|
|
1102
2652
|
for (let k = 0; k < seg; k++) {
|
|
1103
|
-
const a0 = i *
|
|
1104
|
-
const b0 = a0 +
|
|
2653
|
+
const a0 = i * ring2 + k;
|
|
2654
|
+
const b0 = a0 + ring2;
|
|
1105
2655
|
idx.push(a0, a0 + 1, b0, a0 + 1, b0 + 1, b0);
|
|
1106
2656
|
}
|
|
1107
2657
|
;
|
|
@@ -1115,7 +2665,7 @@ var GameVehicles = (() => {
|
|
|
1115
2665
|
col.push(cc.r, cc.g, cc.b);
|
|
1116
2666
|
}
|
|
1117
2667
|
for (let k = 0; k < seg; k++) {
|
|
1118
|
-
const a = si *
|
|
2668
|
+
const a = si * ring2 + k;
|
|
1119
2669
|
if (e === 0) idx.push(ci, a + 1, a);
|
|
1120
2670
|
else idx.push(ci, a, a + 1);
|
|
1121
2671
|
}
|
|
@@ -1253,13 +2803,13 @@ var GameVehicles = (() => {
|
|
|
1253
2803
|
});
|
|
1254
2804
|
tris = next;
|
|
1255
2805
|
});
|
|
1256
|
-
function build(
|
|
2806
|
+
function build(list2) {
|
|
1257
2807
|
const g = new THREE.BufferGeometry();
|
|
1258
2808
|
let off = 0;
|
|
1259
2809
|
names.forEach(function(nm, a) {
|
|
1260
|
-
const arr = new Float32Array(
|
|
2810
|
+
const arr = new Float32Array(list2.length * 3 * sizes[a]);
|
|
1261
2811
|
let w = 0;
|
|
1262
|
-
|
|
2812
|
+
list2.forEach(function(t) {
|
|
1263
2813
|
for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) arr[w++] = t[k][off + q];
|
|
1264
2814
|
});
|
|
1265
2815
|
g.setAttribute(nm, new THREE.BufferAttribute(arr, sizes[a]));
|
|
@@ -1274,10 +2824,10 @@ var GameVehicles = (() => {
|
|
|
1274
2824
|
});
|
|
1275
2825
|
return out;
|
|
1276
2826
|
}
|
|
1277
|
-
function mergeGeometries(
|
|
2827
|
+
function mergeGeometries(list2) {
|
|
1278
2828
|
const pos = [];
|
|
1279
2829
|
const nor = [];
|
|
1280
|
-
|
|
2830
|
+
list2.forEach(function(g2) {
|
|
1281
2831
|
const n = g2.index ? g2.toNonIndexed() : g2;
|
|
1282
2832
|
if (!n.attributes.normal) n.computeVertexNormals();
|
|
1283
2833
|
const pa = n.attributes.position.array;
|
|
@@ -1336,13 +2886,13 @@ var GameVehicles = (() => {
|
|
|
1336
2886
|
};
|
|
1337
2887
|
kit.holeSide = function(poly, o) {
|
|
1338
2888
|
o = o || {};
|
|
1339
|
-
let
|
|
2889
|
+
let area3 = 0;
|
|
1340
2890
|
for (let i = 0; i < poly.length; i++) {
|
|
1341
2891
|
const a = poly[i];
|
|
1342
2892
|
const b = poly[(i + 1) % poly.length];
|
|
1343
|
-
|
|
2893
|
+
area3 += a[0] * b[1] - b[0] * a[1];
|
|
1344
2894
|
}
|
|
1345
|
-
const sg =
|
|
2895
|
+
const sg = area3 > 0 ? 1 : -1;
|
|
1346
2896
|
const E2 = poly.map(function(a, i) {
|
|
1347
2897
|
const b = poly[(i + 1) % poly.length];
|
|
1348
2898
|
const dx = b[0] - a[0];
|
|
@@ -1645,11 +3195,11 @@ var GameVehicles = (() => {
|
|
|
1645
3195
|
return d.clone().applyQuaternion(this.vehicle.quaternion);
|
|
1646
3196
|
}
|
|
1647
3197
|
/** 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,
|
|
3198
|
+
zone(id, label, box2, parts, o) {
|
|
1649
3199
|
const d = Object.assign({ id }, o || {});
|
|
1650
3200
|
if (label === false) d.remove = true;
|
|
1651
3201
|
else if (label) d.name = label;
|
|
1652
|
-
if (
|
|
3202
|
+
if (box2) d.box = I.normalizeBox(box2);
|
|
1653
3203
|
if (parts) d.objs = Array.isArray(parts) ? parts.slice() : [parts];
|
|
1654
3204
|
this.zoneDefs.push(d);
|
|
1655
3205
|
return d;
|
|
@@ -1800,7 +3350,7 @@ var GameVehicles = (() => {
|
|
|
1800
3350
|
const seg = o.seg || 24;
|
|
1801
3351
|
const pos = [];
|
|
1802
3352
|
const idx = [];
|
|
1803
|
-
const
|
|
3353
|
+
const ring2 = seg + 1;
|
|
1804
3354
|
const axisY = o.axis === "y";
|
|
1805
3355
|
st.forEach(function(s) {
|
|
1806
3356
|
const a0 = s[1];
|
|
@@ -1822,8 +3372,8 @@ var GameVehicles = (() => {
|
|
|
1822
3372
|
});
|
|
1823
3373
|
for (let i = 0; i < st.length - 1; i++)
|
|
1824
3374
|
for (let k = 0; k < seg; k++) {
|
|
1825
|
-
const A = i *
|
|
1826
|
-
const B = A +
|
|
3375
|
+
const A = i * ring2 + k;
|
|
3376
|
+
const B = A + ring2;
|
|
1827
3377
|
if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
|
|
1828
3378
|
else idx.push(A, A + 1, B, A + 1, B + 1, B);
|
|
1829
3379
|
}
|
|
@@ -1836,7 +3386,7 @@ var GameVehicles = (() => {
|
|
|
1836
3386
|
if (axisY) pos.push(cc, s[0], s[5] || 0);
|
|
1837
3387
|
else pos.push(s[0], cc, s[5] || 0);
|
|
1838
3388
|
for (let k = 0; k < seg; k++) {
|
|
1839
|
-
const a = si *
|
|
3389
|
+
const a = si * ring2 + k;
|
|
1840
3390
|
const flip = e === 0 !== axisY;
|
|
1841
3391
|
if (flip) idx.push(ci, a + 1, a);
|
|
1842
3392
|
else idx.push(ci, a, a + 1);
|
|
@@ -2068,15 +3618,15 @@ var GameVehicles = (() => {
|
|
|
2068
3618
|
c.part("glass", L, m, pos, o.reach || 0.9);
|
|
2069
3619
|
return m;
|
|
2070
3620
|
};
|
|
2071
|
-
kit.asGlass = function(c,
|
|
3621
|
+
kit.asGlass = function(c, mesh2, label, type, o) {
|
|
2072
3622
|
o = o || {};
|
|
2073
|
-
const i = c.deform.indexOf(
|
|
3623
|
+
const i = c.deform.indexOf(mesh2);
|
|
2074
3624
|
if (i >= 0) c.deform.splice(i, 1);
|
|
2075
|
-
if (
|
|
2076
|
-
const geo =
|
|
2077
|
-
geo.applyMatrix4(new THREE.Matrix4().compose(
|
|
3625
|
+
if (mesh2.parent) mesh2.parent.remove(mesh2);
|
|
3626
|
+
const geo = mesh2.geometry;
|
|
3627
|
+
geo.applyMatrix4(new THREE.Matrix4().compose(mesh2.position, mesh2.quaternion, mesh2.scale));
|
|
2078
3628
|
if (o.reuv !== false) o.reuv = true;
|
|
2079
|
-
return kit.addGlass(c, geo, label, type, Object.assign({ mat:
|
|
3629
|
+
return kit.addGlass(c, geo, label, type, Object.assign({ mat: mesh2.material }, o));
|
|
2080
3630
|
};
|
|
2081
3631
|
kit.sideWindows = function(c, polys, Wg, T, label, o) {
|
|
2082
3632
|
o = o || {};
|
|
@@ -2122,12 +3672,12 @@ var GameVehicles = (() => {
|
|
|
2122
3672
|
});
|
|
2123
3673
|
});
|
|
2124
3674
|
};
|
|
2125
|
-
kit.cutMesh = function(
|
|
2126
|
-
const src = o && o.sub ? subdivide(
|
|
3675
|
+
kit.cutMesh = function(mesh2, tests, deg, o) {
|
|
3676
|
+
const src = o && o.sub ? subdivide(mesh2.geometry, o.sub) : mesh2.geometry;
|
|
2127
3677
|
const r = cutGeometry(src, tests);
|
|
2128
3678
|
crease(r.geo, deg || 42);
|
|
2129
|
-
|
|
2130
|
-
|
|
3679
|
+
mesh2.geometry.dispose();
|
|
3680
|
+
mesh2.geometry = r.geo;
|
|
2131
3681
|
return r.cut;
|
|
2132
3682
|
};
|
|
2133
3683
|
kit.quadDef = function(pb, pt, Wg, T, which, bev, o) {
|
|
@@ -2327,8 +3877,8 @@ var GameVehicles = (() => {
|
|
|
2327
3877
|
const pz = nR === 1 ? 0 : (i ? 1 : -1) * w / 4;
|
|
2328
3878
|
const back = gm(g, new THREE.CylinderGeometry(r * 1.06, r * 1.06, d * 0.8, 22), MATERIALS.housing, 0, 0, pz);
|
|
2329
3879
|
back.rotation.z = PI / 2;
|
|
2330
|
-
const
|
|
2331
|
-
|
|
3880
|
+
const ring2 = gm(g, new THREE.TorusGeometry(r * 1.02, 0.013, 8, 30), MATERIALS.chrome, d / 2 - 6e-3, 0, pz);
|
|
3881
|
+
ring2.rotation.y = PI / 2;
|
|
2332
3882
|
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
3883
|
bowl.rotation.z = PI / 2;
|
|
2334
3884
|
bowl.scale.y = 0.55;
|
|
@@ -2405,8 +3955,8 @@ var GameVehicles = (() => {
|
|
|
2405
3955
|
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
3956
|
e.rotation.z = PI / 2;
|
|
2407
3957
|
inner.push(e);
|
|
2408
|
-
const
|
|
2409
|
-
|
|
3958
|
+
const ring2 = gm(g, new THREE.TorusGeometry(r, 0.01, 6, 24), MATERIALS.chrome, d / 2 - 4e-3, 0, pz);
|
|
3959
|
+
ring2.rotation.y = PI / 2;
|
|
2410
3960
|
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
3961
|
ln.rotation.z = -PI / 2;
|
|
2412
3962
|
ln.scale.y = 0.25;
|
|
@@ -2499,7 +4049,7 @@ var GameVehicles = (() => {
|
|
|
2499
4049
|
} else if (st === "mesh") {
|
|
2500
4050
|
gm(g, roundedBoxGeometry(0.05, h, w, Math.min(0.03, h * 0.3)), MATERIALS.housing);
|
|
2501
4051
|
const cell = 0.026;
|
|
2502
|
-
const
|
|
4052
|
+
const list2 = [];
|
|
2503
4053
|
const rows = Math.floor(h * 0.85 / (cell * 1.6));
|
|
2504
4054
|
const cols = Math.floor(w * 0.9 / (cell * 1.8));
|
|
2505
4055
|
for (let rI = 0; rI < rows; rI++)
|
|
@@ -2508,9 +4058,9 @@ var GameVehicles = (() => {
|
|
|
2508
4058
|
hg.rotateZ(PI / 2);
|
|
2509
4059
|
hg.rotateX(PI / 6);
|
|
2510
4060
|
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
|
-
|
|
4061
|
+
list2.push(hg);
|
|
2512
4062
|
}
|
|
2513
|
-
if (
|
|
4063
|
+
if (list2.length) gm(g, mergeGeometries(list2), MATERIALS.trim);
|
|
2514
4064
|
gm(g, roundedBoxGeometry(0.022, 0.014, w, 6e-3), MATERIALS.chrome, 0.03, h / 2 - 8e-3, 0);
|
|
2515
4065
|
} else if (st === "big") {
|
|
2516
4066
|
const fr = roundedBoxGeometry(0.06, h, w, 0.03);
|
|
@@ -3351,7 +4901,7 @@ var GameVehicles = (() => {
|
|
|
3351
4901
|
m.underbodyGroup = ug;
|
|
3352
4902
|
const DK = makeMaterial("#1a1b1c", { roughness: 0.75, metalness: 0.3 });
|
|
3353
4903
|
const ST = MATERIALS.steel;
|
|
3354
|
-
function
|
|
4904
|
+
function box2(w, h, d, mat, x, y, z) {
|
|
3355
4905
|
return c.roundBox(w, h, d, Math.min(0.02, h * 0.4), mat, x, y, z, { parent: ug });
|
|
3356
4906
|
}
|
|
3357
4907
|
function rod(a, b, r, mat) {
|
|
@@ -3368,9 +4918,9 @@ var GameVehicles = (() => {
|
|
|
3368
4918
|
rod(new V3(rx, rr, -zW), new V3(rx, rr, zW), 0.045, ST);
|
|
3369
4919
|
c.sphere(0.13, ST, rx, rr, 0, 1, 0.85, 1, { parent: ug });
|
|
3370
4920
|
rod(new V3(rx + 0.1, rr, 0), new V3(fx - 0.6, yU + 0.04, 0), 0.035, ST);
|
|
3371
|
-
} else
|
|
4921
|
+
} else box2(0.09, 0.08, zW * 2, DK, rx + 0.08, Math.max(yU, rr - 0.12), 0);
|
|
3372
4922
|
if (!ev) {
|
|
3373
|
-
|
|
4923
|
+
box2(Math.min(0.5, (fx - rx) * 0.2), 0.1, 0.38, DK, fx - 0.25, yU + 0.01, -0.05);
|
|
3374
4924
|
const tz = W * 0.3;
|
|
3375
4925
|
const mx = rx - 0.15;
|
|
3376
4926
|
const ty = Math.max(0.14, by - 0.06);
|
|
@@ -3387,10 +4937,10 @@ var GameVehicles = (() => {
|
|
|
3387
4937
|
{ parent: ug, seg: 30, rs: 6 }
|
|
3388
4938
|
);
|
|
3389
4939
|
c.cylinder(0.1, 0.1, 0.42, ST, mx, ty + 0.02, tz, "x", { parent: ug });
|
|
3390
|
-
|
|
3391
|
-
|
|
4940
|
+
box2(0.24, 0.06, 0.2, DK, (fx + rx) / 2 + 0.1, ty, 0.16);
|
|
4941
|
+
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
4942
|
} else {
|
|
3393
|
-
|
|
4943
|
+
box2(
|
|
3394
4944
|
(fx - rx) * 0.72,
|
|
3395
4945
|
0.1,
|
|
3396
4946
|
W - 0.4,
|
|
@@ -3399,7 +4949,7 @@ var GameVehicles = (() => {
|
|
|
3399
4949
|
Math.max(0.14, by - 0.03),
|
|
3400
4950
|
0
|
|
3401
4951
|
);
|
|
3402
|
-
for (let k = 0; k < 6; k++)
|
|
4952
|
+
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
4953
|
}
|
|
3404
4954
|
if (o.spare && m.rear < rx - 0.7) {
|
|
3405
4955
|
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 +5277,16 @@ var GameVehicles = (() => {
|
|
|
3727
5277
|
}
|
|
3728
5278
|
} else {
|
|
3729
5279
|
const ns = o.spokes || 28;
|
|
3730
|
-
const
|
|
5280
|
+
const list2 = [];
|
|
3731
5281
|
for (let jj = 0; jj < ns; jj++) {
|
|
3732
5282
|
const a2 = jj / ns * PI * 2;
|
|
3733
5283
|
const cg = new THREE.CylinderGeometry(25e-4, 25e-4, sl, 4);
|
|
3734
5284
|
cg.applyMatrix4(
|
|
3735
5285
|
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
5286
|
);
|
|
3737
|
-
|
|
5287
|
+
list2.push(cg);
|
|
3738
5288
|
}
|
|
3739
|
-
g.add(new THREE.Mesh(mergeGeometries(
|
|
5289
|
+
g.add(new THREE.Mesh(mergeGeometries(list2), MATERIALS.chrome));
|
|
3740
5290
|
}
|
|
3741
5291
|
const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.1, 12), MATERIALS.steel);
|
|
3742
5292
|
hub.rotation.x = PI / 2;
|
|
@@ -3918,9 +5468,9 @@ var GameVehicles = (() => {
|
|
|
3918
5468
|
return puffTexture(k * 17, true);
|
|
3919
5469
|
});
|
|
3920
5470
|
}
|
|
3921
|
-
const
|
|
5471
|
+
const list2 = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
|
|
3922
5472
|
const mat = new THREE.SpriteMaterial({
|
|
3923
|
-
map:
|
|
5473
|
+
map: list2[Math.floor(Math.random() * list2.length)],
|
|
3924
5474
|
color: new THREE.Color(col),
|
|
3925
5475
|
transparent: true,
|
|
3926
5476
|
depthWrite: false,
|
|
@@ -5229,7 +6779,7 @@ var GameVehicles = (() => {
|
|
|
5229
6779
|
}
|
|
5230
6780
|
});
|
|
5231
6781
|
const dset = new Set(c.deform);
|
|
5232
|
-
const
|
|
6782
|
+
const box2 = new THREE.Box3();
|
|
5233
6783
|
c.vehicle.children.forEach(function(o) {
|
|
5234
6784
|
if (dset.has(o) || o.userData.noFollow) return;
|
|
5235
6785
|
if (!(o.isMesh || o.isGroup && o.userData.rigid)) return;
|
|
@@ -5240,10 +6790,10 @@ var GameVehicles = (() => {
|
|
|
5240
6790
|
if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
|
|
5241
6791
|
o.userData.bb0 = o.geometry.boundingBox.clone().applyMatrix4(o.matrix);
|
|
5242
6792
|
} else {
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
o.userData.c0 =
|
|
5246
|
-
o.userData.bb0 =
|
|
6793
|
+
box2.setFromObject(o);
|
|
6794
|
+
box2.applyMatrix4(gInv);
|
|
6795
|
+
o.userData.c0 = box2.getCenter(new V3());
|
|
6796
|
+
o.userData.bb0 = box2.clone();
|
|
5247
6797
|
}
|
|
5248
6798
|
o.userData.pos0 = o.position.clone();
|
|
5249
6799
|
}
|
|
@@ -5539,11 +7089,11 @@ var GameVehicles = (() => {
|
|
|
5539
7089
|
[12, "#d8322a", kit.t("blast.lethal", { m: 12 })],
|
|
5540
7090
|
[30, "#e0a548", kit.t("blast.injury", { m: 30 })]
|
|
5541
7091
|
].forEach(function(r) {
|
|
5542
|
-
const
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
g.add(
|
|
7092
|
+
const ring2 = new THREE.Mesh(new THREE.RingGeometry(r[0] - 0.25, r[0], 96), boomMat(r[1], 0.85));
|
|
7093
|
+
ring2.rotation.x = -PI / 2;
|
|
7094
|
+
ring2.position.y = 0.06;
|
|
7095
|
+
ring2.renderOrder = 5;
|
|
7096
|
+
g.add(ring2);
|
|
5547
7097
|
const fill = new THREE.Mesh(new THREE.CircleGeometry(r[0], 96), boomMat(r[1], 0.07));
|
|
5548
7098
|
fill.rotation.x = -PI / 2;
|
|
5549
7099
|
fill.position.y = 0.05;
|
|
@@ -5560,7 +7110,7 @@ var GameVehicles = (() => {
|
|
|
5560
7110
|
lb.rotation.x = -PI / 2;
|
|
5561
7111
|
lb.position.set(0, 0.08, r[0] + r[0] * 0.06);
|
|
5562
7112
|
g.add(lb);
|
|
5563
|
-
c.bigFx.rings.push(
|
|
7113
|
+
c.bigFx.rings.push(ring2, fill, lb);
|
|
5564
7114
|
});
|
|
5565
7115
|
}
|
|
5566
7116
|
function detonate(c) {
|
|
@@ -5835,7 +7385,7 @@ var GameVehicles = (() => {
|
|
|
5835
7385
|
const I = kit._;
|
|
5836
7386
|
const { MATERIALS } = I;
|
|
5837
7387
|
function rollMeshes(c) {
|
|
5838
|
-
const
|
|
7388
|
+
const list2 = [];
|
|
5839
7389
|
const thin = [];
|
|
5840
7390
|
c.root.updateMatrixWorld(true);
|
|
5841
7391
|
const inv = new THREE.Matrix4().copy(c.vehicle.matrixWorld).invert();
|
|
@@ -5853,9 +7403,9 @@ var GameVehicles = (() => {
|
|
|
5853
7403
|
thin.push({ o: ob, M });
|
|
5854
7404
|
return;
|
|
5855
7405
|
}
|
|
5856
|
-
|
|
7406
|
+
list2.push({ o: ob, M });
|
|
5857
7407
|
});
|
|
5858
|
-
return { list, thin };
|
|
7408
|
+
return { list: list2, thin };
|
|
5859
7409
|
}
|
|
5860
7410
|
function convexHull(pts) {
|
|
5861
7411
|
pts.sort(function(a, b) {
|
|
@@ -5938,7 +7488,7 @@ var GameVehicles = (() => {
|
|
|
5938
7488
|
};
|
|
5939
7489
|
function rollContact(c, A, q) {
|
|
5940
7490
|
const m = c.meta;
|
|
5941
|
-
const
|
|
7491
|
+
const list2 = [];
|
|
5942
7492
|
const dy = A.cen[0] - q[0];
|
|
5943
7493
|
const dz = A.cen[1] - q[1];
|
|
5944
7494
|
const dl = Math.hypot(dy, dz) || 1;
|
|
@@ -5946,17 +7496,17 @@ var GameVehicles = (() => {
|
|
|
5946
7496
|
const f = 0.75;
|
|
5947
7497
|
const bike = m.kind === "bike";
|
|
5948
7498
|
(bike ? [0] : [-0.28, 0, 0.28]).forEach(function(k) {
|
|
5949
|
-
|
|
7499
|
+
list2.push([new V3(A.x + k * A.L, q[0], q[1]), dir]);
|
|
5950
7500
|
});
|
|
5951
7501
|
if (!bike && Math.abs(dir.z) > 0.75 && m.top - m.belt > 0.25 && A.hits < 3) {
|
|
5952
7502
|
const sd = -Math.sign(dir.z);
|
|
5953
7503
|
const yw = m.belt + (m.top - m.belt) * 0.45;
|
|
5954
7504
|
[-0.25, 0.2].forEach(function(k) {
|
|
5955
7505
|
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)
|
|
7506
|
+
if (h) list2.push([h.p, new V3(0, 0, -sd)]);
|
|
5957
7507
|
});
|
|
5958
7508
|
}
|
|
5959
|
-
|
|
7509
|
+
list2.forEach(function(e) {
|
|
5960
7510
|
I.impact(c, e[0], e[1], f, { noStain: true, maxDepth: 0.1 });
|
|
5961
7511
|
const pf = c.toEffects(e[0]);
|
|
5962
7512
|
pf.y = Math.max(0.03, pf.y);
|
|
@@ -5966,15 +7516,15 @@ var GameVehicles = (() => {
|
|
|
5966
7516
|
});
|
|
5967
7517
|
A.hits++;
|
|
5968
7518
|
A.bounce = A.hits === 1 ? 0.06 : 0.1;
|
|
5969
|
-
if (m.kind === "boat") I.burst(c, "water", c.toEffects(
|
|
7519
|
+
if (m.kind === "boat") I.burst(c, "water", c.toEffects(list2[0][0]).setY(0.05), 30, { speed: 3 });
|
|
5970
7520
|
}
|
|
5971
|
-
function exactMinY(c,
|
|
7521
|
+
function exactMinY(c, list2) {
|
|
5972
7522
|
c.root.updateMatrixWorld(true);
|
|
5973
7523
|
const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert();
|
|
5974
7524
|
const M = new THREE.Matrix4();
|
|
5975
7525
|
const v = new V3();
|
|
5976
7526
|
let mn = 1e9;
|
|
5977
|
-
|
|
7527
|
+
list2.forEach(function(e) {
|
|
5978
7528
|
const ob = e.o;
|
|
5979
7529
|
if (!ob.parent || !I.shown(c, ob)) return;
|
|
5980
7530
|
M.multiplyMatrices(inv, ob.matrixWorld);
|
|
@@ -6681,6 +8231,153 @@ var GameVehicles = (() => {
|
|
|
6681
8231
|
return hole;
|
|
6682
8232
|
}
|
|
6683
8233
|
const _zr = new THREE.Raycaster();
|
|
8234
|
+
const PROXY_MIN_TRIANGLES = 500;
|
|
8235
|
+
const STROKE_REACH = 0.6;
|
|
8236
|
+
const _pv = new V3();
|
|
8237
|
+
const _pm = new THREE.Matrix4();
|
|
8238
|
+
function decalRegion(c, z) {
|
|
8239
|
+
const B = z.box;
|
|
8240
|
+
const lo = [0, 1, 2].map(function(k) {
|
|
8241
|
+
return I.boxLo(B, k) - STROKE_REACH;
|
|
8242
|
+
});
|
|
8243
|
+
const hi = [0, 1, 2].map(function(k) {
|
|
8244
|
+
return I.boxHi(B, k) + STROKE_REACH;
|
|
8245
|
+
});
|
|
8246
|
+
const a = axisOf(z.dir);
|
|
8247
|
+
const dn = z.dir.clone().normalize();
|
|
8248
|
+
const sg = z.dir.getComponent(a) > 0 ? 1 : -1;
|
|
8249
|
+
const outer = sg > 0 ? I.boxLo(B, a) - 0.6 : I.boxHi(B, a) + 0.6;
|
|
8250
|
+
const far = 0.6 + (I.boxHi(B, a) - I.boxLo(B, a)) + 0.05;
|
|
8251
|
+
for (let i = 0; i < 8; i++) {
|
|
8252
|
+
const p = new V3();
|
|
8253
|
+
p.setComponent(a, outer);
|
|
8254
|
+
[0, 1, 2].filter(function(k) {
|
|
8255
|
+
return k !== a;
|
|
8256
|
+
}).forEach(function(k, j) {
|
|
8257
|
+
p.setComponent(k, i >> j & 1 ? I.boxHi(B, k) : I.boxLo(B, k));
|
|
8258
|
+
});
|
|
8259
|
+
for (let e = 0; e < 2; e++) {
|
|
8260
|
+
const q = e ? p.clone().addScaledVector(dn, far) : p;
|
|
8261
|
+
for (let k = 0; k < 3; k++) {
|
|
8262
|
+
lo[k] = Math.min(lo[k], q.getComponent(k) - 0.05);
|
|
8263
|
+
hi[k] = Math.max(hi[k], q.getComponent(k) + 0.05);
|
|
8264
|
+
}
|
|
8265
|
+
}
|
|
8266
|
+
}
|
|
8267
|
+
return { lo, hi };
|
|
8268
|
+
}
|
|
8269
|
+
function regionCands(c, z, cands) {
|
|
8270
|
+
if (!cands.some(heavy)) return cands;
|
|
8271
|
+
const R = decalRegion(c, z);
|
|
8272
|
+
const Mw = c.vehicle.matrixWorld;
|
|
8273
|
+
return cands.map(function(o) {
|
|
8274
|
+
if (!heavy(o)) return o;
|
|
8275
|
+
if (!o.geometry.boundingSphere) o.geometry.computeBoundingSphere();
|
|
8276
|
+
if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
|
|
8277
|
+
return proxyIn(o, Mw, R);
|
|
8278
|
+
});
|
|
8279
|
+
}
|
|
8280
|
+
function heavy(o) {
|
|
8281
|
+
const g = o.geometry;
|
|
8282
|
+
const pos = g && g.attributes && g.attributes.position;
|
|
8283
|
+
return o.raycast === THREE.Mesh.prototype.raycast && !o.isSkinnedMesh && !Array.isArray(o.material) && pos && !pos.isInterleavedBufferAttribute && pos.itemSize === 3 && !g.groups.length && !(g.morphAttributes && g.morphAttributes.position) && (g.index ? g.index.count : pos.count) / 3 >= PROXY_MIN_TRIANGLES;
|
|
8284
|
+
}
|
|
8285
|
+
function proxyIn(o, Mw, R) {
|
|
8286
|
+
_pm.copy(o.matrixWorld).invert().multiply(Mw);
|
|
8287
|
+
const lo = [Infinity, Infinity, Infinity];
|
|
8288
|
+
const hi = [-Infinity, -Infinity, -Infinity];
|
|
8289
|
+
for (let i = 0; i < 8; i++) {
|
|
8290
|
+
_pv.set(i & 1 ? R.hi[0] : R.lo[0], i & 2 ? R.hi[1] : R.lo[1], i & 4 ? R.hi[2] : R.lo[2]).applyMatrix4(_pm);
|
|
8291
|
+
for (let k = 0; k < 3; k++) {
|
|
8292
|
+
lo[k] = Math.min(lo[k], _pv.getComponent(k));
|
|
8293
|
+
hi[k] = Math.max(hi[k], _pv.getComponent(k));
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
8296
|
+
const g = o.geometry;
|
|
8297
|
+
const P = g.attributes.position.array;
|
|
8298
|
+
const ix = g.index ? g.index.array : null;
|
|
8299
|
+
const n = ix ? ix.length : P.length / 3;
|
|
8300
|
+
const keep = new Uint32Array(n - n % 3);
|
|
8301
|
+
let nk = 0;
|
|
8302
|
+
for (let t = 0; t + 2 < n; t += 3) {
|
|
8303
|
+
const a = (ix ? ix[t] : t) * 3;
|
|
8304
|
+
const b = (ix ? ix[t + 1] : t + 1) * 3;
|
|
8305
|
+
const d = (ix ? ix[t + 2] : t + 2) * 3;
|
|
8306
|
+
let out = false;
|
|
8307
|
+
for (let k = 0; k < 3 && !out; k++) {
|
|
8308
|
+
const u = P[a + k];
|
|
8309
|
+
const v = P[b + k];
|
|
8310
|
+
const w = P[d + k];
|
|
8311
|
+
out = Math.max(u, v, w) < lo[k] || Math.min(u, v, w) > hi[k];
|
|
8312
|
+
}
|
|
8313
|
+
if (!out) {
|
|
8314
|
+
keep[nk++] = ix ? ix[t] : t;
|
|
8315
|
+
keep[nk++] = ix ? ix[t + 1] : t + 1;
|
|
8316
|
+
keep[nk++] = ix ? ix[t + 2] : t + 2;
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
if (!nk) return null;
|
|
8320
|
+
const kept = keep.slice(0, nk);
|
|
8321
|
+
const box2 = new Float32Array(kept.length / 3 * 6);
|
|
8322
|
+
for (let t = 0, j = 0; t < kept.length; t += 3, j += 6) {
|
|
8323
|
+
const a = kept[t] * 3;
|
|
8324
|
+
const b = kept[t + 1] * 3;
|
|
8325
|
+
const d = kept[t + 2] * 3;
|
|
8326
|
+
for (let k = 0; k < 3; k++) {
|
|
8327
|
+
box2[j + k] = Math.min(P[a + k], P[b + k], P[d + k]);
|
|
8328
|
+
box2[j + 3 + k] = Math.max(P[a + k], P[b + k], P[d + k]);
|
|
8329
|
+
}
|
|
8330
|
+
}
|
|
8331
|
+
const pg = new THREE.BufferGeometry();
|
|
8332
|
+
pg.setAttribute("position", g.attributes.position);
|
|
8333
|
+
pg.setIndex(new THREE.BufferAttribute(new Uint32Array(kept.length), 1));
|
|
8334
|
+
pg.boundingSphere = g.boundingSphere;
|
|
8335
|
+
pg.boundingBox = g.boundingBox;
|
|
8336
|
+
const proxy = new THREE.Mesh(pg, o.material);
|
|
8337
|
+
proxy.layers.mask = o.layers.mask;
|
|
8338
|
+
proxy.proxyOf = o;
|
|
8339
|
+
proxy.kept = kept;
|
|
8340
|
+
proxy.keptBox = box2;
|
|
8341
|
+
return proxy;
|
|
8342
|
+
}
|
|
8343
|
+
function narrowTo(rc, proxy) {
|
|
8344
|
+
const o = proxy.proxyOf;
|
|
8345
|
+
const ray = rc.ray;
|
|
8346
|
+
_pm.copy(o.matrixWorld).invert();
|
|
8347
|
+
_pv.copy(ray.origin).applyMatrix4(_pm);
|
|
8348
|
+
const x0 = _pv.x;
|
|
8349
|
+
const y0 = _pv.y;
|
|
8350
|
+
const z0 = _pv.z;
|
|
8351
|
+
_pv.copy(ray.direction).multiplyScalar(rc.far).add(ray.origin).applyMatrix4(_pm);
|
|
8352
|
+
const lo0 = Math.min(x0, _pv.x) - 1e-4;
|
|
8353
|
+
const lo1 = Math.min(y0, _pv.y) - 1e-4;
|
|
8354
|
+
const lo2 = Math.min(z0, _pv.z) - 1e-4;
|
|
8355
|
+
const hi0 = Math.max(x0, _pv.x) + 1e-4;
|
|
8356
|
+
const hi1 = Math.max(y0, _pv.y) + 1e-4;
|
|
8357
|
+
const hi2 = Math.max(z0, _pv.z) + 1e-4;
|
|
8358
|
+
const K = proxy.kept;
|
|
8359
|
+
const X = proxy.keptBox;
|
|
8360
|
+
const dst = proxy.geometry.index.array;
|
|
8361
|
+
let m = 0;
|
|
8362
|
+
for (let t = 0, j = 0; t < K.length; t += 3, j += 6) {
|
|
8363
|
+
if (X[j + 3] < lo0 || X[j] > hi0 || X[j + 4] < lo1 || X[j + 1] > hi1 || X[j + 5] < lo2 || X[j + 2] > hi2) continue;
|
|
8364
|
+
dst[m++] = K[t];
|
|
8365
|
+
dst[m++] = K[t + 1];
|
|
8366
|
+
dst[m++] = K[t + 2];
|
|
8367
|
+
}
|
|
8368
|
+
proxy.geometry.setDrawRange(0, m);
|
|
8369
|
+
}
|
|
8370
|
+
function castAt(rc, cands) {
|
|
8371
|
+
for (let i = 0; i < cands.length; i++) {
|
|
8372
|
+
const q = cands[i];
|
|
8373
|
+
if (!q.proxyOf) continue;
|
|
8374
|
+
q.matrixWorld.copy(q.proxyOf.matrixWorld);
|
|
8375
|
+
narrowTo(rc, q);
|
|
8376
|
+
}
|
|
8377
|
+
const hits = rc.intersectObjects(cands, false);
|
|
8378
|
+
for (let i = 0; i < hits.length; i++) if (hits[i].object.proxyOf) hits[i].object = hits[i].object.proxyOf;
|
|
8379
|
+
return hits;
|
|
8380
|
+
}
|
|
6684
8381
|
function zoneCands(c, z, inv, glassToo) {
|
|
6685
8382
|
const out = [];
|
|
6686
8383
|
c.vehicle.traverse(function(o) {
|
|
@@ -6733,7 +8430,7 @@ var GameVehicles = (() => {
|
|
|
6733
8430
|
});
|
|
6734
8431
|
_zr.set(org.clone().applyMatrix4(Mw), d.clone().transformDirection(Mw));
|
|
6735
8432
|
_zr.far = 0.6 + (I.boxHi(B, a) - I.boxLo(B, a)) + 0.05;
|
|
6736
|
-
const hits = _zr
|
|
8433
|
+
const hits = castAt(_zr, cands);
|
|
6737
8434
|
for (let i = 0; i < hits.length; i++) {
|
|
6738
8435
|
const h = hits[i];
|
|
6739
8436
|
if (!h.face) continue;
|
|
@@ -6761,7 +8458,7 @@ var GameVehicles = (() => {
|
|
|
6761
8458
|
const q = s.p.clone().addScaledVector(t, len * 0.5 * k / M);
|
|
6762
8459
|
_rt.set(q.clone().addScaledVector(n, 0.08).applyMatrix4(Mw), n.clone().negate().transformDirection(Mw));
|
|
6763
8460
|
_rt.far = 0.2;
|
|
6764
|
-
const h = _rt
|
|
8461
|
+
const h = castAt(_rt, cands).find(function(x) {
|
|
6765
8462
|
return x.face;
|
|
6766
8463
|
});
|
|
6767
8464
|
if (!h) return null;
|
|
@@ -6832,6 +8529,7 @@ var GameVehicles = (() => {
|
|
|
6832
8529
|
const R = c.rand;
|
|
6833
8530
|
const need = (spec.fine || 0) + (spec.deep || 0) + (spec.chip || 0) + (spec.rust || 0) + (spec.tear || 0) + (spec.ding || 0) + (spec.soot || 0) + (spec.crater || 0);
|
|
6834
8531
|
if (!need) return [];
|
|
8532
|
+
cands = regionCands(c, z, cands).filter(Boolean);
|
|
6835
8533
|
const S = zoneSurf(c, z, need + 2, R, cands);
|
|
6836
8534
|
let k = 0;
|
|
6837
8535
|
const ang0 = R() * PI;
|
|
@@ -6912,8 +8610,25 @@ var GameVehicles = (() => {
|
|
|
6912
8610
|
}
|
|
6913
8611
|
function subdivIn(geo, rel, B, maxLen) {
|
|
6914
8612
|
const a = geo.attributes.position.array;
|
|
6915
|
-
|
|
6916
|
-
|
|
8613
|
+
let out = new Float32Array(a.length + 4096);
|
|
8614
|
+
let used = 0;
|
|
8615
|
+
const push = function(a0, a1, a2, b0, b1, b2, c0, c1, c2) {
|
|
8616
|
+
if (used + 9 > out.length) {
|
|
8617
|
+
const bigger = new Float32Array(out.length * 2);
|
|
8618
|
+
bigger.set(out);
|
|
8619
|
+
out = bigger;
|
|
8620
|
+
}
|
|
8621
|
+
out[used] = a0;
|
|
8622
|
+
out[used + 1] = a1;
|
|
8623
|
+
out[used + 2] = a2;
|
|
8624
|
+
out[used + 3] = b0;
|
|
8625
|
+
out[used + 4] = b1;
|
|
8626
|
+
out[used + 5] = b2;
|
|
8627
|
+
out[used + 6] = c0;
|
|
8628
|
+
out[used + 7] = c1;
|
|
8629
|
+
out[used + 8] = c2;
|
|
8630
|
+
used += 9;
|
|
8631
|
+
};
|
|
6917
8632
|
const e = 0.15;
|
|
6918
8633
|
function d(p, q) {
|
|
6919
8634
|
return Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);
|
|
@@ -6927,7 +8642,7 @@ var GameVehicles = (() => {
|
|
|
6927
8642
|
const ca = d(C, A);
|
|
6928
8643
|
const mx = Math.max(ab, bc, ca);
|
|
6929
8644
|
if (mx <= maxLen || depth > 9) {
|
|
6930
|
-
|
|
8645
|
+
push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
|
|
6931
8646
|
return;
|
|
6932
8647
|
}
|
|
6933
8648
|
if (mx === ab) {
|
|
@@ -6944,31 +8659,26 @@ var GameVehicles = (() => {
|
|
|
6944
8659
|
tri(F2, Bv, C, depth + 1);
|
|
6945
8660
|
}
|
|
6946
8661
|
}
|
|
8662
|
+
const M = rel.elements;
|
|
8663
|
+
const at = function(x, y, z, k) {
|
|
8664
|
+
const w = 1 / (M[3] * x + M[7] * y + M[11] * z + M[15]);
|
|
8665
|
+
return k === 0 ? (M[0] * x + M[4] * y + M[8] * z + M[12]) * w : k === 1 ? (M[1] * x + M[5] * y + M[9] * z + M[13]) * w : (M[2] * x + M[6] * y + M[10] * z + M[14]) * w;
|
|
8666
|
+
};
|
|
8667
|
+
const lo = [B.x0 - e, B.y0 - e, B.z0 - e];
|
|
8668
|
+
const hi = [B.x1 + e, B.y1 + e, B.z1 + e];
|
|
6947
8669
|
for (let i = 0; i < a.length; i += 9) {
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
[A, Bv, C].forEach(function(p) {
|
|
6958
|
-
v.set(p[0], p[1], p[2]).applyMatrix4(rel);
|
|
6959
|
-
x0 = Math.min(x0, v.x);
|
|
6960
|
-
x1 = Math.max(x1, v.x);
|
|
6961
|
-
y0 = Math.min(y0, v.y);
|
|
6962
|
-
y1 = Math.max(y1, v.y);
|
|
6963
|
-
z0 = Math.min(z0, v.z);
|
|
6964
|
-
z1 = Math.max(z1, v.z);
|
|
6965
|
-
});
|
|
6966
|
-
if (x1 < B.x0 - e || x0 > B.x1 + e || y1 < B.y0 - e || y0 > B.y1 + e || z1 < B.z0 - e || z0 > B.z1 + e)
|
|
6967
|
-
out.push(A[0], A[1], A[2], Bv[0], Bv[1], Bv[2], C[0], C[1], C[2]);
|
|
6968
|
-
else tri(A, Bv, C, 0);
|
|
8670
|
+
let outside = false;
|
|
8671
|
+
for (let k = 0; k < 3 && !outside; k++) {
|
|
8672
|
+
const p = at(a[i], a[i + 1], a[i + 2], k);
|
|
8673
|
+
const q = at(a[i + 3], a[i + 4], a[i + 5], k);
|
|
8674
|
+
const r = at(a[i + 6], a[i + 7], a[i + 8], k);
|
|
8675
|
+
outside = Math.max(p, q, r) < lo[k] || Math.min(p, q, r) > hi[k];
|
|
8676
|
+
}
|
|
8677
|
+
if (outside) push(a[i], a[i + 1], a[i + 2], a[i + 3], a[i + 4], a[i + 5], a[i + 6], a[i + 7], a[i + 8]);
|
|
8678
|
+
else tri([a[i], a[i + 1], a[i + 2]], [a[i + 3], a[i + 4], a[i + 5]], [a[i + 6], a[i + 7], a[i + 8]], 0);
|
|
6969
8679
|
}
|
|
6970
8680
|
const g = new THREE.BufferGeometry();
|
|
6971
|
-
g.setAttribute("position", new THREE.
|
|
8681
|
+
g.setAttribute("position", new THREE.BufferAttribute(out.slice(0, used), 3));
|
|
6972
8682
|
g.userData.crease = true;
|
|
6973
8683
|
g.userData.p0 = null;
|
|
6974
8684
|
return g;
|
|
@@ -6991,6 +8701,17 @@ var GameVehicles = (() => {
|
|
|
6991
8701
|
});
|
|
6992
8702
|
return out;
|
|
6993
8703
|
}
|
|
8704
|
+
function boxInFrame(B, m, tmp) {
|
|
8705
|
+
const r = [Infinity, Infinity, Infinity, -Infinity, -Infinity, -Infinity];
|
|
8706
|
+
for (let i = 0; i < 8; i++) {
|
|
8707
|
+
tmp.set(i & 1 ? B.x1 : B.x0, i & 2 ? B.y1 : B.y0, i & 4 ? B.z1 : B.z0).applyMatrix4(m);
|
|
8708
|
+
for (let k = 0; k < 3; k++) {
|
|
8709
|
+
r[k] = Math.min(r[k], tmp.getComponent(k) - 1e-4);
|
|
8710
|
+
r[k + 3] = Math.max(r[k + 3], tmp.getComponent(k) + 1e-4);
|
|
8711
|
+
}
|
|
8712
|
+
}
|
|
8713
|
+
return r;
|
|
8714
|
+
}
|
|
6994
8715
|
function zoneDent(c, z, dents, dirty, opt) {
|
|
6995
8716
|
opt = opt || {};
|
|
6996
8717
|
const B = z.box;
|
|
@@ -7045,6 +8766,7 @@ var GameVehicles = (() => {
|
|
|
7045
8766
|
g = msh.geometry = g.clone();
|
|
7046
8767
|
msh.userData.zCloned = true;
|
|
7047
8768
|
}
|
|
8769
|
+
let boundsDue = false;
|
|
7048
8770
|
const nonIdx = !g.index;
|
|
7049
8771
|
const onlyPos = Object.keys(g.attributes).every(function(k) {
|
|
7050
8772
|
return k === "position" || k === "normal";
|
|
@@ -7053,15 +8775,17 @@ var GameVehicles = (() => {
|
|
|
7053
8775
|
const ng = subdivIn(g, rel, B, opt.sub || 0.075);
|
|
7054
8776
|
g.dispose();
|
|
7055
8777
|
g = msh.geometry = ng;
|
|
7056
|
-
|
|
7057
|
-
ng.computeBoundingBox();
|
|
8778
|
+
boundsDue = true;
|
|
7058
8779
|
dirty.add(msh);
|
|
7059
8780
|
}
|
|
7060
8781
|
irel.copy(rel).invert();
|
|
7061
8782
|
const pos = g.attributes.position;
|
|
7062
8783
|
let changed = false;
|
|
8784
|
+
const near = boxInFrame(B, irel, v);
|
|
7063
8785
|
for (let i = 0; i < pos.count; i++) {
|
|
7064
|
-
v.fromBufferAttribute(pos, i)
|
|
8786
|
+
v.fromBufferAttribute(pos, i);
|
|
8787
|
+
if (v.x < near[0] || v.x > near[3] || v.y < near[1] || v.y > near[4] || v.z < near[2] || v.z > near[5]) continue;
|
|
8788
|
+
v.applyMatrix4(rel);
|
|
7065
8789
|
const f = field(v);
|
|
7066
8790
|
if (f <= 1e-5) continue;
|
|
7067
8791
|
v.addScaledVector(d, f);
|
|
@@ -7082,9 +8806,11 @@ var GameVehicles = (() => {
|
|
|
7082
8806
|
}
|
|
7083
8807
|
if (changed) {
|
|
7084
8808
|
pos.needsUpdate = true;
|
|
8809
|
+
dirty.add(msh);
|
|
8810
|
+
}
|
|
8811
|
+
if (changed || boundsDue) {
|
|
7085
8812
|
g.computeBoundingSphere();
|
|
7086
8813
|
g.computeBoundingBox();
|
|
7087
|
-
dirty.add(msh);
|
|
7088
8814
|
}
|
|
7089
8815
|
});
|
|
7090
8816
|
const dset = new Set(c.deform);
|
|
@@ -8170,9 +9896,9 @@ var GameVehicles = (() => {
|
|
|
8170
9896
|
down: new V3(0, 1, 0)
|
|
8171
9897
|
}[d] || null;
|
|
8172
9898
|
}
|
|
8173
|
-
function resolveObjs(c,
|
|
9899
|
+
function resolveObjs(c, list2, z) {
|
|
8174
9900
|
const out = [];
|
|
8175
|
-
(
|
|
9901
|
+
(list2 || []).forEach(function(o) {
|
|
8176
9902
|
if (!o) return;
|
|
8177
9903
|
if (typeof o === "string") {
|
|
8178
9904
|
c.parts.forEach(function(p) {
|
|
@@ -8200,22 +9926,22 @@ var GameVehicles = (() => {
|
|
|
8200
9926
|
}
|
|
8201
9927
|
function deriveZones(c) {
|
|
8202
9928
|
const m = c.meta;
|
|
8203
|
-
let
|
|
9929
|
+
let list2 = [];
|
|
8204
9930
|
const byId = {};
|
|
8205
9931
|
(m.zones === false ? [] : I.autoZones(c)).forEach(function(z) {
|
|
8206
9932
|
byId[z.id] = z;
|
|
8207
|
-
|
|
9933
|
+
list2.push(z);
|
|
8208
9934
|
});
|
|
8209
9935
|
(c.zoneDefs || []).forEach(function(d) {
|
|
8210
9936
|
let z = byId[d.id];
|
|
8211
9937
|
if (d.remove) {
|
|
8212
9938
|
if (z) {
|
|
8213
|
-
|
|
9939
|
+
list2.splice(list2.indexOf(z), 1);
|
|
8214
9940
|
delete byId[d.id];
|
|
8215
9941
|
}
|
|
8216
|
-
|
|
9942
|
+
list2.slice().forEach(function(q) {
|
|
8217
9943
|
if (q.id.indexOf(d.id + "_") === 0 && /^\d+$/.test(q.id.slice(d.id.length + 1))) {
|
|
8218
|
-
|
|
9944
|
+
list2.splice(list2.indexOf(q), 1);
|
|
8219
9945
|
delete byId[q.id];
|
|
8220
9946
|
}
|
|
8221
9947
|
});
|
|
@@ -8228,7 +9954,7 @@ var GameVehicles = (() => {
|
|
|
8228
9954
|
} else {
|
|
8229
9955
|
z = Object.assign({ type: "panel" }, d);
|
|
8230
9956
|
byId[d.id] = z;
|
|
8231
|
-
|
|
9957
|
+
list2.push(z);
|
|
8232
9958
|
}
|
|
8233
9959
|
});
|
|
8234
9960
|
const inv = I.worldInverseOf(c);
|
|
@@ -8261,10 +9987,10 @@ var GameVehicles = (() => {
|
|
|
8261
9987
|
}
|
|
8262
9988
|
z.box = I.normalizeBox(bb);
|
|
8263
9989
|
});
|
|
8264
|
-
|
|
9990
|
+
list2 = list2.filter(function(z) {
|
|
8265
9991
|
return z.box && z.name;
|
|
8266
9992
|
});
|
|
8267
|
-
|
|
9993
|
+
list2.forEach(function(z) {
|
|
8268
9994
|
z.glass = (z.glass || []).slice();
|
|
8269
9995
|
z.lights = (z.lights || []).slice();
|
|
8270
9996
|
z.wheels = (z.wheels || []).slice();
|
|
@@ -8301,7 +10027,7 @@ var GameVehicles = (() => {
|
|
|
8301
10027
|
if (owner.has(g) || !g.parent || !g.userData.glass || g.userData.ghost) return;
|
|
8302
10028
|
const cg = I.localBox(c, g, inv).getCenter(new V3());
|
|
8303
10029
|
let best = null;
|
|
8304
|
-
|
|
10030
|
+
list2.forEach(function(z) {
|
|
8305
10031
|
if (z.internal || z.type === "wheel" || z.type === "light") return;
|
|
8306
10032
|
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
10033
|
});
|
|
@@ -8310,7 +10036,7 @@ var GameVehicles = (() => {
|
|
|
8310
10036
|
owner.set(g, best);
|
|
8311
10037
|
}
|
|
8312
10038
|
});
|
|
8313
|
-
const looseParts =
|
|
10039
|
+
const looseParts = list2.filter(function(z) {
|
|
8314
10040
|
return z.type === "part";
|
|
8315
10041
|
});
|
|
8316
10042
|
if (looseParts.length)
|
|
@@ -8335,7 +10061,7 @@ var GameVehicles = (() => {
|
|
|
8335
10061
|
});
|
|
8336
10062
|
}
|
|
8337
10063
|
});
|
|
8338
|
-
|
|
10064
|
+
list2.forEach(function(z) {
|
|
8339
10065
|
if (!z.mirror) return;
|
|
8340
10066
|
z.mirrors = [];
|
|
8341
10067
|
const B = z.box;
|
|
@@ -8367,21 +10093,21 @@ var GameVehicles = (() => {
|
|
|
8367
10093
|
});
|
|
8368
10094
|
}
|
|
8369
10095
|
});
|
|
8370
|
-
|
|
10096
|
+
list2.forEach(function(z) {
|
|
8371
10097
|
const B = z.box;
|
|
8372
10098
|
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
10099
|
});
|
|
8374
|
-
carMap(c,
|
|
10100
|
+
carMap(c, list2);
|
|
8375
10101
|
c.zoneOwners = owner;
|
|
8376
|
-
return
|
|
10102
|
+
return list2;
|
|
8377
10103
|
}
|
|
8378
|
-
function carMap(c,
|
|
10104
|
+
function carMap(c, list2) {
|
|
8379
10105
|
const m = c.meta;
|
|
8380
10106
|
const W = m.width || 1.7;
|
|
8381
10107
|
const hw = W / 2;
|
|
8382
10108
|
const gh = m.glasshouse;
|
|
8383
10109
|
if ((m.kind || "car") !== "car" || !gh || gh.fb == null) return;
|
|
8384
|
-
|
|
10110
|
+
list2.forEach(function(z) {
|
|
8385
10111
|
if (z.mapSet) return;
|
|
8386
10112
|
const B = z.box;
|
|
8387
10113
|
const s = z.side;
|
|
@@ -8446,8 +10172,8 @@ var GameVehicles = (() => {
|
|
|
8446
10172
|
const top = m.top || belt + 0.5;
|
|
8447
10173
|
const hw = W / 2;
|
|
8448
10174
|
const WH = I.wheelInfo(c);
|
|
8449
|
-
function add(id, name, type,
|
|
8450
|
-
const z = Object.assign({ id, name, type, box: I.normalizeBox(
|
|
10175
|
+
function add(id, name, type, box2, o) {
|
|
10176
|
+
const z = Object.assign({ id, name, type, box: I.normalizeBox(box2) }, o || {});
|
|
8451
10177
|
Z.push(z);
|
|
8452
10178
|
return z;
|
|
8453
10179
|
}
|
|
@@ -9951,9 +11677,9 @@ var GameVehicles = (() => {
|
|
|
9951
11677
|
const kind = kindOf2(zone);
|
|
9952
11678
|
if (!kind || !zone.box) continue;
|
|
9953
11679
|
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 });
|
|
11680
|
+
const box2 = new THREE.Box3(new THREE.Vector3(b.x0, b.y0, b.z0), new THREE.Vector3(b.x1, b.y1, b.z1));
|
|
11681
|
+
if (matrix) box2.applyMatrix4(matrix);
|
|
11682
|
+
out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", box: box2 });
|
|
9957
11683
|
}
|
|
9958
11684
|
return out;
|
|
9959
11685
|
};
|
|
@@ -9964,7 +11690,7 @@ var GameVehicles = (() => {
|
|
|
9964
11690
|
const opts = options || {};
|
|
9965
11691
|
if (!opts.THREE || !opts.THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
|
|
9966
11692
|
const lod = createLod(opts.lod);
|
|
9967
|
-
const THREE = wrapThreeForLod(opts.THREE, lod);
|
|
11693
|
+
const THREE = wrapThreeForLod(shapeGeometries(opts.THREE), lod);
|
|
9968
11694
|
const strings = resolveStrings(opts.locale);
|
|
9969
11695
|
const t = createTranslate(strings);
|
|
9970
11696
|
const doc = opts.document || (typeof document !== "undefined" ? document : void 0);
|
|
@@ -10187,6 +11913,20 @@ var GameVehicles = (() => {
|
|
|
10187
11913
|
else kit.applyArmor(c);
|
|
10188
11914
|
}
|
|
10189
11915
|
if (zoneState == null && st === "damaged" && kit.config.damagedScenario) zoneState = kit.zoneScenario(c, kit.config.damagedScenario);
|
|
11916
|
+
else if (zoneState && zoneState.scenario != null) {
|
|
11917
|
+
c.scenario = zoneState.scenario;
|
|
11918
|
+
let fromScenario = { health: {}, flags: {} };
|
|
11919
|
+
try {
|
|
11920
|
+
fromScenario = kit.zoneScenario(c, zoneState.scenario);
|
|
11921
|
+
} catch (e) {
|
|
11922
|
+
;
|
|
11923
|
+
(c.zoneErrors = c.zoneErrors || []).push("scenario: " + (e && e.message));
|
|
11924
|
+
}
|
|
11925
|
+
zoneState = {
|
|
11926
|
+
health: Object.assign({}, fromScenario.health, zoneState.health),
|
|
11927
|
+
flags: Object.assign({}, fromScenario.flags, zoneState.flags)
|
|
11928
|
+
};
|
|
11929
|
+
}
|
|
10190
11930
|
if (zoneState) kit.applyZones(c, zoneState);
|
|
10191
11931
|
else {
|
|
10192
11932
|
c.zoneState = { health: {}, flags: {} };
|
|
@@ -10225,6 +11965,51 @@ var GameVehicles = (() => {
|
|
|
10225
11965
|
return kit;
|
|
10226
11966
|
}
|
|
10227
11967
|
|
|
11968
|
+
// ../game-contracts/dist/index.js
|
|
11969
|
+
var DEFAULT_IGNITION_RULES = {
|
|
11970
|
+
fillCurve: [
|
|
11971
|
+
[0, 0.6],
|
|
11972
|
+
[0.1, 1],
|
|
11973
|
+
[0.35, 0.6],
|
|
11974
|
+
[0.65, 0.3],
|
|
11975
|
+
[0.9, 0.15],
|
|
11976
|
+
[1, 0.12]
|
|
11977
|
+
],
|
|
11978
|
+
flameFillInfluence: 0.5,
|
|
11979
|
+
flameDefaults: { incendiary: 0.7, tracer: 0.2 },
|
|
11980
|
+
sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },
|
|
11981
|
+
sparkMetalDefault: 0.5,
|
|
11982
|
+
sparkFluid: { diesel: 0.15 },
|
|
11983
|
+
fireNear: { radius: 1.5, chancePerSecond: 0.5 }
|
|
11984
|
+
};
|
|
11985
|
+
var resolveIgnitionRules = (rules) => ({
|
|
11986
|
+
...DEFAULT_IGNITION_RULES,
|
|
11987
|
+
...rules,
|
|
11988
|
+
flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },
|
|
11989
|
+
sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },
|
|
11990
|
+
sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },
|
|
11991
|
+
fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear }
|
|
11992
|
+
});
|
|
11993
|
+
var fillFactor = (fill, curve = DEFAULT_IGNITION_RULES.fillCurve) => {
|
|
11994
|
+
if (curve.length === 0) return 1;
|
|
11995
|
+
const f = Math.min(1, Math.max(0, fill));
|
|
11996
|
+
if (f <= curve[0][0]) return curve[0][1];
|
|
11997
|
+
for (let i = 1; i < curve.length; i++) {
|
|
11998
|
+
const [x1, y1] = curve[i];
|
|
11999
|
+
const [x0, y0] = curve[i - 1];
|
|
12000
|
+
if (f <= x1) return x1 === x0 ? y1 : y0 + (y1 - y0) * (f - x0) / (x1 - x0);
|
|
12001
|
+
}
|
|
12002
|
+
return curve[curve.length - 1][1];
|
|
12003
|
+
};
|
|
12004
|
+
var sparkTargetFactor = (materialId, metal, rules = DEFAULT_IGNITION_RULES) => !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault;
|
|
12005
|
+
var ignitionChance = (hit, target, rules = DEFAULT_IGNITION_RULES) => {
|
|
12006
|
+
const k = fillFactor(target.fill, rules.fillCurve);
|
|
12007
|
+
const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === void 0 ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k;
|
|
12008
|
+
const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0);
|
|
12009
|
+
const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
|
|
12010
|
+
return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
|
|
12011
|
+
};
|
|
12012
|
+
|
|
10228
12013
|
// src/destructible/model.ts
|
|
10229
12014
|
var JOINT_RULES = {
|
|
10230
12015
|
hinge: { strength: 5e3, tools: ["wrench", "screwdriver"], seconds: 3 },
|
|
@@ -10352,19 +12137,19 @@ var GameVehicles = (() => {
|
|
|
10352
12137
|
for (const z of zones) {
|
|
10353
12138
|
let role = roleOfZone(z);
|
|
10354
12139
|
if (z === hubZone) role = "frame";
|
|
10355
|
-
const
|
|
10356
|
-
const
|
|
12140
|
+
const box2 = boxOf(z);
|
|
12141
|
+
const area3 = areaOf(box2.size);
|
|
10357
12142
|
const id = z.id;
|
|
10358
12143
|
const label = String(z.name ?? id);
|
|
10359
12144
|
const tags = [...z.tags ?? []];
|
|
10360
12145
|
if (role === "rim") {
|
|
10361
12146
|
const wheels = z.wheels ?? [];
|
|
10362
12147
|
const r = Math.max(0.2, ...wheels.map((w) => w.userData?.r ?? 0.3));
|
|
10363
|
-
const sz = Math.sign(
|
|
10364
|
-
const rimBox = { center: [
|
|
12148
|
+
const sz = Math.sign(box2.center[2]) || 1;
|
|
12149
|
+
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
12150
|
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
12151
|
const tyreId = id.replace(/^wheels?/, "tyre").replace(/^(?!tyre)/, "tyre_");
|
|
10367
|
-
const tyreBox = { center:
|
|
12152
|
+
const tyreBox = { center: box2.center, size: [box2.size[0] * 0.98, box2.size[1] * 0.98, box2.size[2]] };
|
|
10368
12153
|
add({
|
|
10369
12154
|
id: tyreId,
|
|
10370
12155
|
role: "tyre",
|
|
@@ -10384,29 +12169,29 @@ var GameVehicles = (() => {
|
|
|
10384
12169
|
if (role === "glass") {
|
|
10385
12170
|
const glass = (z.glass ?? [])[0];
|
|
10386
12171
|
const laminated = glass?.userData?.glass?.type === "laminated" || m.bullet;
|
|
10387
|
-
add({ id, role, name: label, material: "glass", box, mass: 10 *
|
|
12172
|
+
add({ id, role, name: label, material: "glass", box: box2, mass: 10 * area3, hp: laminated ? 4e3 : 450, zoneId: id, visual: false, tags: ["glass", ...tags] });
|
|
10388
12173
|
continue;
|
|
10389
12174
|
}
|
|
10390
12175
|
if (role === "light") {
|
|
10391
|
-
add({ id, role, name: label, material: "glass", box, mass: 1.2 + 4 *
|
|
12176
|
+
add({ id, role, name: label, material: "glass", box: box2, mass: 1.2 + 4 * area3, hp: 320, zoneId: id, visual: true, tags: ["light", ...tags] });
|
|
10392
12177
|
continue;
|
|
10393
12178
|
}
|
|
10394
12179
|
if (role === "bumper") {
|
|
10395
|
-
add({ id, role, name: label, material: "plastic", box, mass: 6 + 5 *
|
|
12180
|
+
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
12181
|
continue;
|
|
10397
12182
|
}
|
|
10398
12183
|
if (role === "engine") {
|
|
10399
12184
|
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] });
|
|
12185
|
+
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
12186
|
continue;
|
|
10402
12187
|
}
|
|
10403
12188
|
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] });
|
|
12189
|
+
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
12190
|
continue;
|
|
10406
12191
|
}
|
|
10407
12192
|
if (role === "fuel_tank") {
|
|
10408
12193
|
if (fuelless) {
|
|
10409
|
-
add({ id, role: "other", name: label, material: "steel", box, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
|
|
12194
|
+
add({ id, role: "other", name: label, material: "steel", box: box2, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
|
|
10410
12195
|
continue;
|
|
10411
12196
|
}
|
|
10412
12197
|
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 +12201,7 @@ var GameVehicles = (() => {
|
|
|
10416
12201
|
role,
|
|
10417
12202
|
name: label,
|
|
10418
12203
|
material: "steel",
|
|
10419
|
-
box,
|
|
12204
|
+
box: box2,
|
|
10420
12205
|
mass: 14 + liters * 0.8,
|
|
10421
12206
|
hp: 25e3 * armour,
|
|
10422
12207
|
zoneId: id,
|
|
@@ -10429,21 +12214,21 @@ var GameVehicles = (() => {
|
|
|
10429
12214
|
continue;
|
|
10430
12215
|
}
|
|
10431
12216
|
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] });
|
|
12217
|
+
add({ id, role, name: label, material: "plastic", box: box2, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
|
|
10433
12218
|
continue;
|
|
10434
12219
|
}
|
|
10435
12220
|
if (role === "frame") {
|
|
10436
|
-
add({ id, role, name: label, material: "steel", box: floorPan(
|
|
12221
|
+
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
12222
|
continue;
|
|
10438
12223
|
}
|
|
10439
12224
|
const roofLike = role === "roof";
|
|
10440
|
-
const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) *
|
|
12225
|
+
const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) * area3 * (role === "door" ? 1.6 : 1);
|
|
10441
12226
|
add({
|
|
10442
12227
|
id,
|
|
10443
12228
|
role,
|
|
10444
12229
|
name: label,
|
|
10445
12230
|
material: skin,
|
|
10446
|
-
box,
|
|
12231
|
+
box: box2,
|
|
10447
12232
|
mass: Math.max(2, kg),
|
|
10448
12233
|
hp: (roofLike ? 3e4 : 2e4) * armour,
|
|
10449
12234
|
zoneId: id,
|
|
@@ -10665,7 +12450,7 @@ var GameVehicles = (() => {
|
|
|
10665
12450
|
const my = kind === "bike" || kind === "quad" ? (m.top ?? belt + 0.25) - 0.1 : belt + 0.12;
|
|
10666
12451
|
for (const s of [-1, 1]) {
|
|
10667
12452
|
const door = zones.find((z) => z.type === "door" && z.side === s && (z.mirrors ?? []).length);
|
|
10668
|
-
let
|
|
12453
|
+
let box2 = { center: [mx, my, s * half], size: [0.14, 0.12, 0.2] };
|
|
10669
12454
|
if (door) {
|
|
10670
12455
|
const bb = door.mirrors.reduce(
|
|
10671
12456
|
(acc, o) => {
|
|
@@ -10678,14 +12463,14 @@ var GameVehicles = (() => {
|
|
|
10678
12463
|
},
|
|
10679
12464
|
null
|
|
10680
12465
|
);
|
|
10681
|
-
if (bb)
|
|
12466
|
+
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
12467
|
}
|
|
10683
12468
|
add({
|
|
10684
12469
|
id: `mirror_${side(s)}`,
|
|
10685
12470
|
role: "mirror",
|
|
10686
12471
|
name: t("mechanics.mirror", { side: t(`sides.${side(s)}`), sideM: t(`sides.${side(s)}M`) }),
|
|
10687
12472
|
material: "plastic",
|
|
10688
|
-
box,
|
|
12473
|
+
box: box2,
|
|
10689
12474
|
mass: 1.1,
|
|
10690
12475
|
hp: 1500,
|
|
10691
12476
|
ignitionC: 400,
|
|
@@ -10875,13 +12660,13 @@ var GameVehicles = (() => {
|
|
|
10875
12660
|
(parent ?? this.c.vehicle).add(m);
|
|
10876
12661
|
return m;
|
|
10877
12662
|
}
|
|
10878
|
-
surface(axis,
|
|
12663
|
+
surface(axis, sign2, B, samples) {
|
|
10879
12664
|
const V3 = this.THREE.Vector3;
|
|
10880
12665
|
const hits = [];
|
|
10881
12666
|
const far = 3;
|
|
10882
12667
|
for (const [u, v] of samples) {
|
|
10883
|
-
const o = axis === "z" ? new V3(u, v,
|
|
10884
|
-
const d = axis === "z" ? new V3(0, 0, -
|
|
12668
|
+
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);
|
|
12669
|
+
const d = axis === "z" ? new V3(0, 0, -sign2) : new V3(-sign2, 0, 0);
|
|
10885
12670
|
const h = this.I.probe(this.c, o, d, far);
|
|
10886
12671
|
if (h) hits.push(axis === "z" ? h.p.z : h.p.x);
|
|
10887
12672
|
}
|
|
@@ -11031,12 +12816,12 @@ var GameVehicles = (() => {
|
|
|
11031
12816
|
const z = this.zone(p);
|
|
11032
12817
|
if (!z) return false;
|
|
11033
12818
|
const V3 = this.THREE.Vector3;
|
|
11034
|
-
const
|
|
11035
|
-
if (!
|
|
12819
|
+
const list2 = z.glass.filter((g) => g.parent && g.visible);
|
|
12820
|
+
if (!list2.length) return false;
|
|
11036
12821
|
const pt = new V3(...point);
|
|
11037
|
-
let best =
|
|
12822
|
+
let best = list2[0];
|
|
11038
12823
|
let bd = Infinity;
|
|
11039
|
-
for (const g of
|
|
12824
|
+
for (const g of list2) {
|
|
11040
12825
|
g.geometry.computeBoundingBox();
|
|
11041
12826
|
const bb = g.geometry.boundingBox.clone().applyMatrix4(g.matrix);
|
|
11042
12827
|
const d = bb.distanceToPoint(pt);
|
|
@@ -11337,6 +13122,9 @@ var GameVehicles = (() => {
|
|
|
11337
13122
|
const headless = !!options.headless;
|
|
11338
13123
|
const seedBase = strHash(`${c.key}|${id}|${options.seed ?? 0}`);
|
|
11339
13124
|
let rand = rng(seedBase);
|
|
13125
|
+
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
13126
|
+
const fuelFraction = clamp(options.fuel ?? 1, 0, 1);
|
|
13127
|
+
let igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
|
|
11340
13128
|
let clock = 0;
|
|
11341
13129
|
let model;
|
|
11342
13130
|
let vis = null;
|
|
@@ -11404,7 +13192,7 @@ var GameVehicles = (() => {
|
|
|
11404
13192
|
dents: [],
|
|
11405
13193
|
holes: [],
|
|
11406
13194
|
cracks: [],
|
|
11407
|
-
liters: d.contains
|
|
13195
|
+
liters: d.contains ? d.role === "fuel_tank" ? d.contains.liters * fuelFraction : d.contains.liters : void 0,
|
|
11408
13196
|
lost: false,
|
|
11409
13197
|
status: "intact",
|
|
11410
13198
|
flat: false,
|
|
@@ -11422,6 +13210,7 @@ var GameVehicles = (() => {
|
|
|
11422
13210
|
wasLit = true;
|
|
11423
13211
|
deadEngine = false;
|
|
11424
13212
|
rand = rng(seedBase);
|
|
13213
|
+
igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
|
|
11425
13214
|
steerSign = rand() < 0.5 ? -1 : 1;
|
|
11426
13215
|
vis = headless ? null : new VehicleVisuals(kit, c, model);
|
|
11427
13216
|
recomputeGroups();
|
|
@@ -11443,8 +13232,8 @@ var GameVehicles = (() => {
|
|
|
11443
13232
|
comps.get(r).push(p);
|
|
11444
13233
|
}
|
|
11445
13234
|
const hubRoot = parent.has(model.hub) ? find(model.hub) : "";
|
|
11446
|
-
const
|
|
11447
|
-
groupsCache =
|
|
13235
|
+
const list2 = [...comps.entries()].sort((a, b) => a[0] === hubRoot ? -1 : b[0] === hubRoot ? 1 : 0);
|
|
13236
|
+
groupsCache = list2.map(([, v]) => v);
|
|
11448
13237
|
groupOf = /* @__PURE__ */ new Map();
|
|
11449
13238
|
groupsCache.forEach((g, i) => g.forEach((p) => groupOf.set(p, i)));
|
|
11450
13239
|
return groupsCache;
|
|
@@ -11716,8 +13505,19 @@ var GameVehicles = (() => {
|
|
|
11716
13505
|
if (!p.burning && p.temp > AMBIENT) p.temp = Math.max(AMBIENT, p.temp - dt * 6);
|
|
11717
13506
|
}
|
|
11718
13507
|
for (const f of flames) heat(centreOf(f.def), 0.8, 500, dt * 0.5, out, 0);
|
|
13508
|
+
fireNearLeak(flames, dt, out);
|
|
11719
13509
|
thermal(out);
|
|
11720
13510
|
};
|
|
13511
|
+
const fireNearLeak = (flames, dt, out) => {
|
|
13512
|
+
const tank = tankPart();
|
|
13513
|
+
const leak = tank ? leaks.get(tank.def.id) : void 0;
|
|
13514
|
+
if (!tank || !leak || tank.lost || tank.burning || exploded || leak.fluid !== "fuel" && leak.fluid !== "diesel") return;
|
|
13515
|
+
if (leak.pud?.fire) return light(tank, out);
|
|
13516
|
+
const { radius, chancePerSecond } = ignitionRules.fireNear;
|
|
13517
|
+
if (chancePerSecond <= 0) return;
|
|
13518
|
+
const near = flames.some((f) => f !== tank && distToBox(tank.def, centreOf(f.def)) - Math.max(...f.def.box.size) / 2 <= radius);
|
|
13519
|
+
if (near && igniteRand() < 1 - Math.exp(-chancePerSecond * dt)) light(tank, out);
|
|
13520
|
+
};
|
|
11721
13521
|
const normalOf = (d, v) => faceNormal(d, v);
|
|
11722
13522
|
const ballistic = (h, out) => {
|
|
11723
13523
|
sync();
|
|
@@ -11785,10 +13585,21 @@ var GameVehicles = (() => {
|
|
|
11785
13585
|
let amount = through ? E * 0.6 : E * 0.25;
|
|
11786
13586
|
if (through && (d.role === "battery" || d.role === "alternator")) amount = Math.max(amount, p.hp * 0.65);
|
|
11787
13587
|
damage(p, amount, "ballistic", h.source, out, worldPoint);
|
|
11788
|
-
if (d.role === "fuel_tank"
|
|
13588
|
+
if (through && d.role === "fuel_tank") rollIgnition(p, h, metal, out);
|
|
11789
13589
|
}
|
|
11790
13590
|
}
|
|
11791
13591
|
};
|
|
13592
|
+
const light = (tank, out) => {
|
|
13593
|
+
tank.temp = Math.max(tank.temp, (tank.def.ignitionC ?? 260) + 1);
|
|
13594
|
+
if ((tank.liters ?? 0) > 0.5) ignite(tank, out);
|
|
13595
|
+
};
|
|
13596
|
+
const rollIgnition = (tank, h, metal, out) => {
|
|
13597
|
+
const d = tank.def;
|
|
13598
|
+
if (!d.contains || tank.burning || (tank.liters ?? 0) <= 0.05) return;
|
|
13599
|
+
const fill = d.contains.liters > 0 ? (tank.liters ?? 0) / d.contains.liters : 0;
|
|
13600
|
+
const chance = ignitionChance(h, { sparkTarget: sparkTargetFactor(d.material, metal, ignitionRules), fill, fluid: d.contains.fluid }, ignitionRules);
|
|
13601
|
+
if (chance > 0 && igniteRand() < chance) light(tank, out);
|
|
13602
|
+
};
|
|
11792
13603
|
const blunt = (h, out) => {
|
|
11793
13604
|
sync();
|
|
11794
13605
|
const M = toFrameMatrix();
|
|
@@ -11852,7 +13663,7 @@ var GameVehicles = (() => {
|
|
|
11852
13663
|
const verdict = kit.blastDamage(c, E / (1 + dist * dist * 4) / 50);
|
|
11853
13664
|
if (verdict === "explosion") explode(tank, h.source, out);
|
|
11854
13665
|
else if (verdict === "fire") {
|
|
11855
|
-
|
|
13666
|
+
light(tank, out);
|
|
11856
13667
|
startLeak(tank, centreOf(tank.def).setY(tank.def.box.center[1] - tank.def.box.size[1] / 2 + 0.05), 12, out, dir);
|
|
11857
13668
|
}
|
|
11858
13669
|
}
|
|
@@ -11866,9 +13677,9 @@ var GameVehicles = (() => {
|
|
|
11866
13677
|
thermal(out);
|
|
11867
13678
|
};
|
|
11868
13679
|
const sound = (role) => {
|
|
11869
|
-
const
|
|
11870
|
-
if (!
|
|
11871
|
-
return
|
|
13680
|
+
const list2 = [...ps.values()].filter((p) => p.def.role === role);
|
|
13681
|
+
if (!list2.length) return true;
|
|
13682
|
+
return list2.every((p) => !p.lost && attached(p.def.id) && p.hp >= p.def.hp * 0.5);
|
|
11872
13683
|
};
|
|
11873
13684
|
const tankPart = () => [...ps.values()].find((p) => p.def.role === "fuel_tank" && p.def.contains);
|
|
11874
13685
|
const computeStatus = () => {
|
|
@@ -12060,8 +13871,8 @@ var GameVehicles = (() => {
|
|
|
12060
13871
|
queue = [];
|
|
12061
13872
|
const hood = c.hood;
|
|
12062
13873
|
if (hood) {
|
|
12063
|
-
const
|
|
12064
|
-
const cur = hood.piv.rotation.z /
|
|
13874
|
+
const sign2 = hood.sign || 1;
|
|
13875
|
+
const cur = hood.piv.rotation.z / sign2;
|
|
12065
13876
|
if (Math.abs(cur - hood.ang) > 1e-4) {
|
|
12066
13877
|
hood.ang = cur;
|
|
12067
13878
|
hood.target = cur;
|
|
@@ -12114,7 +13925,7 @@ var GameVehicles = (() => {
|
|
|
12114
13925
|
const reset = () => {
|
|
12115
13926
|
const old = c;
|
|
12116
13927
|
const parent = old.root.parent;
|
|
12117
|
-
const next = kit.build(old.key, old.state);
|
|
13928
|
+
const next = kit.build(old.key, old.state, old.scenario != null ? { scenario: old.scenario } : void 0);
|
|
12118
13929
|
if (parent) {
|
|
12119
13930
|
next.root.position.copy(old.root.position);
|
|
12120
13931
|
next.root.quaternion.copy(old.root.quaternion);
|
|
@@ -12171,7 +13982,8 @@ var GameVehicles = (() => {
|
|
|
12171
13982
|
// src/destructible/item.ts
|
|
12172
13983
|
function buildVehicleItem(kit, key, options = {}) {
|
|
12173
13984
|
const THREE = kit.THREE;
|
|
12174
|
-
const
|
|
13985
|
+
const zoneState = options.scenario != null ? { scenario: options.scenario } : void 0;
|
|
13986
|
+
const vehicle = options.lod ? kit.build(key, options.state ?? "intact", zoneState, { lod: true }) : kit.build(key, options.state ?? "intact", zoneState);
|
|
12175
13987
|
const m = vehicle.meta;
|
|
12176
13988
|
const root = new THREE.Group();
|
|
12177
13989
|
root.name = key;
|