@adatechnology/game-vehicles 0.1.12 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -1
- package/dist/index.d.ts +90 -3
- package/dist/index.global.js +1900 -139
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +216 -11
- package/dist/index.js.map +1 -1
- package/package.json +9 -6
package/dist/index.global.js
CHANGED
|
@@ -30,9 +30,12 @@ var GameVehicles = (() => {
|
|
|
30
30
|
buildVehicleItem: () => buildVehicleItem,
|
|
31
31
|
buildVehicleModel: () => buildVehicleModel,
|
|
32
32
|
clamp: () => clamp,
|
|
33
|
+
createLod: () => createLod,
|
|
33
34
|
createTranslate: () => createTranslate,
|
|
34
35
|
createVehicleDestructible: () => createVehicleDestructible,
|
|
35
36
|
createVehicleKit: () => createVehicleKit,
|
|
37
|
+
defaultDamageableKinds: () => defaultDamageableKinds,
|
|
38
|
+
defaultLodSettings: () => defaultLodSettings,
|
|
36
39
|
format: () => format,
|
|
37
40
|
hash: () => hash,
|
|
38
41
|
legacyGlassId: () => legacyGlassId,
|
|
@@ -44,9 +47,1527 @@ var GameVehicles = (() => {
|
|
|
44
47
|
resolveStrings: () => resolveStrings,
|
|
45
48
|
rng: () => rng,
|
|
46
49
|
smooth: () => smooth,
|
|
47
|
-
strHash: () => strHash
|
|
50
|
+
strHash: () => strHash,
|
|
51
|
+
wrapThreeForLod: () => wrapThreeForLod
|
|
48
52
|
});
|
|
49
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
|
+
|
|
50
1571
|
// src/locales/en.json
|
|
51
1572
|
var en_default = {
|
|
52
1573
|
plate: {
|
|
@@ -742,9 +2263,9 @@ var GameVehicles = (() => {
|
|
|
742
2263
|
return envTex;
|
|
743
2264
|
}
|
|
744
2265
|
kit.studioEnvironment = studioEnvironment;
|
|
745
|
-
function makeMaterial(
|
|
2266
|
+
function makeMaterial(color2, o) {
|
|
746
2267
|
o = o || {};
|
|
747
|
-
const c =
|
|
2268
|
+
const c = color2 instanceof THREE.Color ? color2 : new THREE.Color(color2);
|
|
748
2269
|
const key = "s" + c.getHexString() + JSON.stringify(o, function(k, v) {
|
|
749
2270
|
return v && v.isColor ? "#" + v.getHexString() : v && v.isTexture ? v.uuid : v;
|
|
750
2271
|
});
|
|
@@ -760,8 +2281,8 @@ var GameVehicles = (() => {
|
|
|
760
2281
|
}
|
|
761
2282
|
return cache[key];
|
|
762
2283
|
}
|
|
763
|
-
function makeFlatMaterial(
|
|
764
|
-
return makeMaterial(
|
|
2284
|
+
function makeFlatMaterial(color2, o) {
|
|
2285
|
+
return makeMaterial(color2, Object.assign({ flatShading: true, roughness: 0.92 }, o || {}));
|
|
765
2286
|
}
|
|
766
2287
|
const DOUBLE_SIDE = THREE.DoubleSide;
|
|
767
2288
|
function toColor(hex) {
|
|
@@ -882,9 +2403,11 @@ var GameVehicles = (() => {
|
|
|
882
2403
|
const V3 = THREE.Vector3;
|
|
883
2404
|
const PI = Math.PI;
|
|
884
2405
|
const I = kit._;
|
|
2406
|
+
const lod = kit.lod;
|
|
885
2407
|
function roundedBoxGeometry(w, h, d, r) {
|
|
886
2408
|
r = Math.max(2e-3, Math.min(r, w * 0.45, h * 0.45, d * 0.45));
|
|
887
|
-
|
|
2409
|
+
if (lod.on && (r < 0.012 || Math.max(w, h, d) < lod.flatBoxEdge || Math.min(w, h, d) < 0.08)) return new THREE.BoxGeometry(w, h, d);
|
|
2410
|
+
const nb = lod.on ? lod.roundingRings : 3;
|
|
888
2411
|
const n = 2 * nb + 1;
|
|
889
2412
|
const g = new THREE.BoxGeometry(1, 1, 1, n, n, n);
|
|
890
2413
|
const p = g.attributes.position;
|
|
@@ -929,6 +2452,7 @@ var GameVehicles = (() => {
|
|
|
929
2452
|
geo.computeVertexNormals();
|
|
930
2453
|
}
|
|
931
2454
|
function subdivide(geo, maxLen) {
|
|
2455
|
+
if (lod.on) maxLen = maxLen * lod.subdivisionFactor;
|
|
932
2456
|
const src = geo.index ? geo.toNonIndexed() : geo;
|
|
933
2457
|
const a = src.attributes.position.array;
|
|
934
2458
|
const out = [];
|
|
@@ -1006,10 +2530,10 @@ var GameVehicles = (() => {
|
|
|
1006
2530
|
const s = new V3();
|
|
1007
2531
|
for (let i = 0; i < n; i++) {
|
|
1008
2532
|
const fi = i / 3 | 0;
|
|
1009
|
-
const
|
|
2533
|
+
const list2 = map[keys[i]];
|
|
1010
2534
|
s.set(0, 0, 0);
|
|
1011
|
-
for (let j = 0; j <
|
|
1012
|
-
const g = FN[
|
|
2535
|
+
for (let j = 0; j < list2.length; j++) {
|
|
2536
|
+
const g = FN[list2[j]];
|
|
1013
2537
|
if (g.dot(FN[fi]) >= cs) s.add(g);
|
|
1014
2538
|
}
|
|
1015
2539
|
if (s.lengthSq() < 1e-20) s.copy(FN[fi]);
|
|
@@ -1075,7 +2599,7 @@ var GameVehicles = (() => {
|
|
|
1075
2599
|
const pos = [];
|
|
1076
2600
|
const col = [];
|
|
1077
2601
|
const idx = [];
|
|
1078
|
-
const
|
|
2602
|
+
const ring2 = seg + 1;
|
|
1079
2603
|
st.forEach(function(s) {
|
|
1080
2604
|
for (let j = 0; j <= seg; j++) {
|
|
1081
2605
|
const a = j / seg * PI * 2;
|
|
@@ -1093,8 +2617,8 @@ var GameVehicles = (() => {
|
|
|
1093
2617
|
});
|
|
1094
2618
|
for (let i = 0; i < st.length - 1; i++)
|
|
1095
2619
|
for (let k = 0; k < seg; k++) {
|
|
1096
|
-
const a0 = i *
|
|
1097
|
-
const b0 = a0 +
|
|
2620
|
+
const a0 = i * ring2 + k;
|
|
2621
|
+
const b0 = a0 + ring2;
|
|
1098
2622
|
idx.push(a0, a0 + 1, b0, a0 + 1, b0 + 1, b0);
|
|
1099
2623
|
}
|
|
1100
2624
|
;
|
|
@@ -1108,7 +2632,7 @@ var GameVehicles = (() => {
|
|
|
1108
2632
|
col.push(cc.r, cc.g, cc.b);
|
|
1109
2633
|
}
|
|
1110
2634
|
for (let k = 0; k < seg; k++) {
|
|
1111
|
-
const a = si *
|
|
2635
|
+
const a = si * ring2 + k;
|
|
1112
2636
|
if (e === 0) idx.push(ci, a + 1, a);
|
|
1113
2637
|
else idx.push(ci, a, a + 1);
|
|
1114
2638
|
}
|
|
@@ -1246,13 +2770,13 @@ var GameVehicles = (() => {
|
|
|
1246
2770
|
});
|
|
1247
2771
|
tris = next;
|
|
1248
2772
|
});
|
|
1249
|
-
function build(
|
|
2773
|
+
function build(list2) {
|
|
1250
2774
|
const g = new THREE.BufferGeometry();
|
|
1251
2775
|
let off = 0;
|
|
1252
2776
|
names.forEach(function(nm, a) {
|
|
1253
|
-
const arr = new Float32Array(
|
|
2777
|
+
const arr = new Float32Array(list2.length * 3 * sizes[a]);
|
|
1254
2778
|
let w = 0;
|
|
1255
|
-
|
|
2779
|
+
list2.forEach(function(t) {
|
|
1256
2780
|
for (let k = 0; k < 3; k++) for (let q = 0; q < sizes[a]; q++) arr[w++] = t[k][off + q];
|
|
1257
2781
|
});
|
|
1258
2782
|
g.setAttribute(nm, new THREE.BufferAttribute(arr, sizes[a]));
|
|
@@ -1267,10 +2791,10 @@ var GameVehicles = (() => {
|
|
|
1267
2791
|
});
|
|
1268
2792
|
return out;
|
|
1269
2793
|
}
|
|
1270
|
-
function mergeGeometries(
|
|
2794
|
+
function mergeGeometries(list2) {
|
|
1271
2795
|
const pos = [];
|
|
1272
2796
|
const nor = [];
|
|
1273
|
-
|
|
2797
|
+
list2.forEach(function(g2) {
|
|
1274
2798
|
const n = g2.index ? g2.toNonIndexed() : g2;
|
|
1275
2799
|
if (!n.attributes.normal) n.computeVertexNormals();
|
|
1276
2800
|
const pa = n.attributes.position.array;
|
|
@@ -1329,13 +2853,13 @@ var GameVehicles = (() => {
|
|
|
1329
2853
|
};
|
|
1330
2854
|
kit.holeSide = function(poly, o) {
|
|
1331
2855
|
o = o || {};
|
|
1332
|
-
let
|
|
2856
|
+
let area3 = 0;
|
|
1333
2857
|
for (let i = 0; i < poly.length; i++) {
|
|
1334
2858
|
const a = poly[i];
|
|
1335
2859
|
const b = poly[(i + 1) % poly.length];
|
|
1336
|
-
|
|
2860
|
+
area3 += a[0] * b[1] - b[0] * a[1];
|
|
1337
2861
|
}
|
|
1338
|
-
const sg =
|
|
2862
|
+
const sg = area3 > 0 ? 1 : -1;
|
|
1339
2863
|
const E2 = poly.map(function(a, i) {
|
|
1340
2864
|
const b = poly[(i + 1) % poly.length];
|
|
1341
2865
|
const dx = b[0] - a[0];
|
|
@@ -1638,11 +3162,11 @@ var GameVehicles = (() => {
|
|
|
1638
3162
|
return d.clone().applyQuaternion(this.vehicle.quaternion);
|
|
1639
3163
|
}
|
|
1640
3164
|
/** Declares or refines a damage zone: `c.zone(id, label, box, parts, options)`. Pass `label === false` to remove an auto-derived zone. */
|
|
1641
|
-
zone(id, label,
|
|
3165
|
+
zone(id, label, box2, parts, o) {
|
|
1642
3166
|
const d = Object.assign({ id }, o || {});
|
|
1643
3167
|
if (label === false) d.remove = true;
|
|
1644
3168
|
else if (label) d.name = label;
|
|
1645
|
-
if (
|
|
3169
|
+
if (box2) d.box = I.normalizeBox(box2);
|
|
1646
3170
|
if (parts) d.objs = Array.isArray(parts) ? parts.slice() : [parts];
|
|
1647
3171
|
this.zoneDefs.push(d);
|
|
1648
3172
|
return d;
|
|
@@ -1658,6 +3182,7 @@ var GameVehicles = (() => {
|
|
|
1658
3182
|
const V3 = THREE.Vector3;
|
|
1659
3183
|
const PI = Math.PI;
|
|
1660
3184
|
const I = kit._;
|
|
3185
|
+
const lod = kit.lod;
|
|
1661
3186
|
const { crease } = I;
|
|
1662
3187
|
function makeWarp(o) {
|
|
1663
3188
|
function sec(y) {
|
|
@@ -1755,6 +3280,7 @@ var GameVehicles = (() => {
|
|
|
1755
3280
|
});
|
|
1756
3281
|
}
|
|
1757
3282
|
function resample(st, n) {
|
|
3283
|
+
if (lod.on) n = Math.max(lod.minStations, Math.round(n * lod.stationFraction));
|
|
1758
3284
|
const out = [];
|
|
1759
3285
|
const L = st.length;
|
|
1760
3286
|
function P(i) {
|
|
@@ -1786,11 +3312,12 @@ var GameVehicles = (() => {
|
|
|
1786
3312
|
}
|
|
1787
3313
|
function smoothLoft(st, o) {
|
|
1788
3314
|
o = o || {};
|
|
3315
|
+
if (lod.on) o = Object.assign({}, o, { seg: Math.max(lod.minLoftSegments, Math.round((o.seg || 24) * lod.loftSegmentFraction)) });
|
|
1789
3316
|
if (o.smooth) st = resample(st, o.smooth);
|
|
1790
3317
|
const seg = o.seg || 24;
|
|
1791
3318
|
const pos = [];
|
|
1792
3319
|
const idx = [];
|
|
1793
|
-
const
|
|
3320
|
+
const ring2 = seg + 1;
|
|
1794
3321
|
const axisY = o.axis === "y";
|
|
1795
3322
|
st.forEach(function(s) {
|
|
1796
3323
|
const a0 = s[1];
|
|
@@ -1812,8 +3339,8 @@ var GameVehicles = (() => {
|
|
|
1812
3339
|
});
|
|
1813
3340
|
for (let i = 0; i < st.length - 1; i++)
|
|
1814
3341
|
for (let k = 0; k < seg; k++) {
|
|
1815
|
-
const A = i *
|
|
1816
|
-
const B = A +
|
|
3342
|
+
const A = i * ring2 + k;
|
|
3343
|
+
const B = A + ring2;
|
|
1817
3344
|
if (axisY) idx.push(A, B, A + 1, A + 1, B, B + 1);
|
|
1818
3345
|
else idx.push(A, A + 1, B, A + 1, B + 1, B);
|
|
1819
3346
|
}
|
|
@@ -1826,7 +3353,7 @@ var GameVehicles = (() => {
|
|
|
1826
3353
|
if (axisY) pos.push(cc, s[0], s[5] || 0);
|
|
1827
3354
|
else pos.push(s[0], cc, s[5] || 0);
|
|
1828
3355
|
for (let k = 0; k < seg; k++) {
|
|
1829
|
-
const a = si *
|
|
3356
|
+
const a = si * ring2 + k;
|
|
1830
3357
|
const flip = e === 0 !== axisY;
|
|
1831
3358
|
if (flip) idx.push(ci, a + 1, a);
|
|
1832
3359
|
else idx.push(ci, a, a + 1);
|
|
@@ -2058,15 +3585,15 @@ var GameVehicles = (() => {
|
|
|
2058
3585
|
c.part("glass", L, m, pos, o.reach || 0.9);
|
|
2059
3586
|
return m;
|
|
2060
3587
|
};
|
|
2061
|
-
kit.asGlass = function(c,
|
|
3588
|
+
kit.asGlass = function(c, mesh2, label, type, o) {
|
|
2062
3589
|
o = o || {};
|
|
2063
|
-
const i = c.deform.indexOf(
|
|
3590
|
+
const i = c.deform.indexOf(mesh2);
|
|
2064
3591
|
if (i >= 0) c.deform.splice(i, 1);
|
|
2065
|
-
if (
|
|
2066
|
-
const geo =
|
|
2067
|
-
geo.applyMatrix4(new THREE.Matrix4().compose(
|
|
3592
|
+
if (mesh2.parent) mesh2.parent.remove(mesh2);
|
|
3593
|
+
const geo = mesh2.geometry;
|
|
3594
|
+
geo.applyMatrix4(new THREE.Matrix4().compose(mesh2.position, mesh2.quaternion, mesh2.scale));
|
|
2068
3595
|
if (o.reuv !== false) o.reuv = true;
|
|
2069
|
-
return kit.addGlass(c, geo, label, type, Object.assign({ mat:
|
|
3596
|
+
return kit.addGlass(c, geo, label, type, Object.assign({ mat: mesh2.material }, o));
|
|
2070
3597
|
};
|
|
2071
3598
|
kit.sideWindows = function(c, polys, Wg, T, label, o) {
|
|
2072
3599
|
o = o || {};
|
|
@@ -2112,12 +3639,12 @@ var GameVehicles = (() => {
|
|
|
2112
3639
|
});
|
|
2113
3640
|
});
|
|
2114
3641
|
};
|
|
2115
|
-
kit.cutMesh = function(
|
|
2116
|
-
const src = o && o.sub ? subdivide(
|
|
3642
|
+
kit.cutMesh = function(mesh2, tests, deg, o) {
|
|
3643
|
+
const src = o && o.sub ? subdivide(mesh2.geometry, o.sub) : mesh2.geometry;
|
|
2117
3644
|
const r = cutGeometry(src, tests);
|
|
2118
3645
|
crease(r.geo, deg || 42);
|
|
2119
|
-
|
|
2120
|
-
|
|
3646
|
+
mesh2.geometry.dispose();
|
|
3647
|
+
mesh2.geometry = r.geo;
|
|
2121
3648
|
return r.cut;
|
|
2122
3649
|
};
|
|
2123
3650
|
kit.quadDef = function(pb, pt, Wg, T, which, bev, o) {
|
|
@@ -2317,8 +3844,8 @@ var GameVehicles = (() => {
|
|
|
2317
3844
|
const pz = nR === 1 ? 0 : (i ? 1 : -1) * w / 4;
|
|
2318
3845
|
const back = gm(g, new THREE.CylinderGeometry(r * 1.06, r * 1.06, d * 0.8, 22), MATERIALS.housing, 0, 0, pz);
|
|
2319
3846
|
back.rotation.z = PI / 2;
|
|
2320
|
-
const
|
|
2321
|
-
|
|
3847
|
+
const ring2 = gm(g, new THREE.TorusGeometry(r * 1.02, 0.013, 8, 30), MATERIALS.chrome, d / 2 - 6e-3, 0, pz);
|
|
3848
|
+
ring2.rotation.y = PI / 2;
|
|
2322
3849
|
const bowl = gm(g, new THREE.SphereGeometry(r * 0.96, 18, 10, 0, PI * 2, 0, PI / 2), MATERIALS.reflectorLit, d / 2 - 0.012, 0, pz);
|
|
2323
3850
|
bowl.rotation.z = PI / 2;
|
|
2324
3851
|
bowl.scale.y = 0.55;
|
|
@@ -2395,8 +3922,8 @@ var GameVehicles = (() => {
|
|
|
2395
3922
|
const e = gm(g, new THREE.CylinderGeometry(r * 0.9, r * 0.9, 0.012, 20), q * ls > 0 ? MATERIALS.tail : MATERIALS.tail, d / 2 - 0.02, 0, pz);
|
|
2396
3923
|
e.rotation.z = PI / 2;
|
|
2397
3924
|
inner.push(e);
|
|
2398
|
-
const
|
|
2399
|
-
|
|
3925
|
+
const ring2 = gm(g, new THREE.TorusGeometry(r, 0.01, 6, 24), MATERIALS.chrome, d / 2 - 4e-3, 0, pz);
|
|
3926
|
+
ring2.rotation.y = PI / 2;
|
|
2400
3927
|
const ln = gm(g, new THREE.SphereGeometry(r, 16, 8, 0, PI * 2, 0, PI / 2), MATERIALS.lensRed, d / 2 - 0.01, 0, pz);
|
|
2401
3928
|
ln.rotation.z = -PI / 2;
|
|
2402
3929
|
ln.scale.y = 0.25;
|
|
@@ -2489,7 +4016,7 @@ var GameVehicles = (() => {
|
|
|
2489
4016
|
} else if (st === "mesh") {
|
|
2490
4017
|
gm(g, roundedBoxGeometry(0.05, h, w, Math.min(0.03, h * 0.3)), MATERIALS.housing);
|
|
2491
4018
|
const cell = 0.026;
|
|
2492
|
-
const
|
|
4019
|
+
const list2 = [];
|
|
2493
4020
|
const rows = Math.floor(h * 0.85 / (cell * 1.6));
|
|
2494
4021
|
const cols = Math.floor(w * 0.9 / (cell * 1.8));
|
|
2495
4022
|
for (let rI = 0; rI < rows; rI++)
|
|
@@ -2498,9 +4025,9 @@ var GameVehicles = (() => {
|
|
|
2498
4025
|
hg.rotateZ(PI / 2);
|
|
2499
4026
|
hg.rotateX(PI / 6);
|
|
2500
4027
|
hg.translate(0.026, -h * 0.42 + (rI + 0.5) * h * 0.85 / rows, -w * 0.45 + (cI + 0.5 + rI % 2 * 0.5 - 0.25) * w * 0.9 / cols);
|
|
2501
|
-
|
|
4028
|
+
list2.push(hg);
|
|
2502
4029
|
}
|
|
2503
|
-
if (
|
|
4030
|
+
if (list2.length) gm(g, mergeGeometries(list2), MATERIALS.trim);
|
|
2504
4031
|
gm(g, roundedBoxGeometry(0.022, 0.014, w, 6e-3), MATERIALS.chrome, 0.03, h / 2 - 8e-3, 0);
|
|
2505
4032
|
} else if (st === "big") {
|
|
2506
4033
|
const fr = roundedBoxGeometry(0.06, h, w, 0.03);
|
|
@@ -3341,7 +4868,7 @@ var GameVehicles = (() => {
|
|
|
3341
4868
|
m.underbodyGroup = ug;
|
|
3342
4869
|
const DK = makeMaterial("#1a1b1c", { roughness: 0.75, metalness: 0.3 });
|
|
3343
4870
|
const ST = MATERIALS.steel;
|
|
3344
|
-
function
|
|
4871
|
+
function box2(w, h, d, mat, x, y, z) {
|
|
3345
4872
|
return c.roundBox(w, h, d, Math.min(0.02, h * 0.4), mat, x, y, z, { parent: ug });
|
|
3346
4873
|
}
|
|
3347
4874
|
function rod(a, b, r, mat) {
|
|
@@ -3358,9 +4885,9 @@ var GameVehicles = (() => {
|
|
|
3358
4885
|
rod(new V3(rx, rr, -zW), new V3(rx, rr, zW), 0.045, ST);
|
|
3359
4886
|
c.sphere(0.13, ST, rx, rr, 0, 1, 0.85, 1, { parent: ug });
|
|
3360
4887
|
rod(new V3(rx + 0.1, rr, 0), new V3(fx - 0.6, yU + 0.04, 0), 0.035, ST);
|
|
3361
|
-
} else
|
|
4888
|
+
} else box2(0.09, 0.08, zW * 2, DK, rx + 0.08, Math.max(yU, rr - 0.12), 0);
|
|
3362
4889
|
if (!ev) {
|
|
3363
|
-
|
|
4890
|
+
box2(Math.min(0.5, (fx - rx) * 0.2), 0.1, 0.38, DK, fx - 0.25, yU + 0.01, -0.05);
|
|
3364
4891
|
const tz = W * 0.3;
|
|
3365
4892
|
const mx = rx - 0.15;
|
|
3366
4893
|
const ty = Math.max(0.14, by - 0.06);
|
|
@@ -3377,10 +4904,10 @@ var GameVehicles = (() => {
|
|
|
3377
4904
|
{ parent: ug, seg: 30, rs: 6 }
|
|
3378
4905
|
);
|
|
3379
4906
|
c.cylinder(0.1, 0.1, 0.42, ST, mx, ty + 0.02, tz, "x", { parent: ug });
|
|
3380
|
-
|
|
3381
|
-
|
|
4907
|
+
box2(0.24, 0.06, 0.2, DK, (fx + rx) / 2 + 0.1, ty, 0.16);
|
|
4908
|
+
box2(0.55, 0.18, W * 0.5, makeMaterial("#232527", { roughness: 0.7 }), rx + 0.62, Math.max(0.16, by - 0.02), -W * 0.1);
|
|
3382
4909
|
} else {
|
|
3383
|
-
|
|
4910
|
+
box2(
|
|
3384
4911
|
(fx - rx) * 0.72,
|
|
3385
4912
|
0.1,
|
|
3386
4913
|
W - 0.4,
|
|
@@ -3389,7 +4916,7 @@ var GameVehicles = (() => {
|
|
|
3389
4916
|
Math.max(0.14, by - 0.03),
|
|
3390
4917
|
0
|
|
3391
4918
|
);
|
|
3392
|
-
for (let k = 0; k < 6; k++)
|
|
4919
|
+
for (let k = 0; k < 6; k++) box2(0.03, 0.02, W - 0.45, ST, rx + 0.4 + k * (fx - rx - 0.8) / 5, Math.max(0.14, by - 0.03) - 0.06, 0);
|
|
3393
4920
|
}
|
|
3394
4921
|
if (o.spare && m.rear < rx - 0.7) {
|
|
3395
4922
|
c.cylinder(0.32, 0.32, 0.2, MATERIALS.rubber, rx - 0.55, Math.max(0.2, by - 0.05), 0, null, { parent: ug });
|
|
@@ -3717,16 +5244,16 @@ var GameVehicles = (() => {
|
|
|
3717
5244
|
}
|
|
3718
5245
|
} else {
|
|
3719
5246
|
const ns = o.spokes || 28;
|
|
3720
|
-
const
|
|
5247
|
+
const list2 = [];
|
|
3721
5248
|
for (let jj = 0; jj < ns; jj++) {
|
|
3722
5249
|
const a2 = jj / ns * PI * 2;
|
|
3723
5250
|
const cg = new THREE.CylinderGeometry(25e-4, 25e-4, sl, 4);
|
|
3724
5251
|
cg.applyMatrix4(
|
|
3725
5252
|
new THREE.Matrix4().makeRotationZ(a2 - PI / 2).setPosition(Math.cos(a2) * sl / 2, Math.sin(a2) * sl / 2, (jj % 2 ? 1 : -1) * 0.012)
|
|
3726
5253
|
);
|
|
3727
|
-
|
|
5254
|
+
list2.push(cg);
|
|
3728
5255
|
}
|
|
3729
|
-
g.add(new THREE.Mesh(mergeGeometries(
|
|
5256
|
+
g.add(new THREE.Mesh(mergeGeometries(list2), MATERIALS.chrome));
|
|
3730
5257
|
}
|
|
3731
5258
|
const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.1, 12), MATERIALS.steel);
|
|
3732
5259
|
hub.rotation.x = PI / 2;
|
|
@@ -3908,9 +5435,9 @@ var GameVehicles = (() => {
|
|
|
3908
5435
|
return puffTexture(k * 17, true);
|
|
3909
5436
|
});
|
|
3910
5437
|
}
|
|
3911
|
-
const
|
|
5438
|
+
const list2 = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
|
|
3912
5439
|
const mat = new THREE.SpriteMaterial({
|
|
3913
|
-
map:
|
|
5440
|
+
map: list2[Math.floor(Math.random() * list2.length)],
|
|
3914
5441
|
color: new THREE.Color(col),
|
|
3915
5442
|
transparent: true,
|
|
3916
5443
|
depthWrite: false,
|
|
@@ -5219,7 +6746,7 @@ var GameVehicles = (() => {
|
|
|
5219
6746
|
}
|
|
5220
6747
|
});
|
|
5221
6748
|
const dset = new Set(c.deform);
|
|
5222
|
-
const
|
|
6749
|
+
const box2 = new THREE.Box3();
|
|
5223
6750
|
c.vehicle.children.forEach(function(o) {
|
|
5224
6751
|
if (dset.has(o) || o.userData.noFollow) return;
|
|
5225
6752
|
if (!(o.isMesh || o.isGroup && o.userData.rigid)) return;
|
|
@@ -5230,10 +6757,10 @@ var GameVehicles = (() => {
|
|
|
5230
6757
|
if (!o.geometry.boundingBox) o.geometry.computeBoundingBox();
|
|
5231
6758
|
o.userData.bb0 = o.geometry.boundingBox.clone().applyMatrix4(o.matrix);
|
|
5232
6759
|
} else {
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
o.userData.c0 =
|
|
5236
|
-
o.userData.bb0 =
|
|
6760
|
+
box2.setFromObject(o);
|
|
6761
|
+
box2.applyMatrix4(gInv);
|
|
6762
|
+
o.userData.c0 = box2.getCenter(new V3());
|
|
6763
|
+
o.userData.bb0 = box2.clone();
|
|
5237
6764
|
}
|
|
5238
6765
|
o.userData.pos0 = o.position.clone();
|
|
5239
6766
|
}
|
|
@@ -5529,11 +7056,11 @@ var GameVehicles = (() => {
|
|
|
5529
7056
|
[12, "#d8322a", kit.t("blast.lethal", { m: 12 })],
|
|
5530
7057
|
[30, "#e0a548", kit.t("blast.injury", { m: 30 })]
|
|
5531
7058
|
].forEach(function(r) {
|
|
5532
|
-
const
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
g.add(
|
|
7059
|
+
const ring2 = new THREE.Mesh(new THREE.RingGeometry(r[0] - 0.25, r[0], 96), boomMat(r[1], 0.85));
|
|
7060
|
+
ring2.rotation.x = -PI / 2;
|
|
7061
|
+
ring2.position.y = 0.06;
|
|
7062
|
+
ring2.renderOrder = 5;
|
|
7063
|
+
g.add(ring2);
|
|
5537
7064
|
const fill = new THREE.Mesh(new THREE.CircleGeometry(r[0], 96), boomMat(r[1], 0.07));
|
|
5538
7065
|
fill.rotation.x = -PI / 2;
|
|
5539
7066
|
fill.position.y = 0.05;
|
|
@@ -5550,7 +7077,7 @@ var GameVehicles = (() => {
|
|
|
5550
7077
|
lb.rotation.x = -PI / 2;
|
|
5551
7078
|
lb.position.set(0, 0.08, r[0] + r[0] * 0.06);
|
|
5552
7079
|
g.add(lb);
|
|
5553
|
-
c.bigFx.rings.push(
|
|
7080
|
+
c.bigFx.rings.push(ring2, fill, lb);
|
|
5554
7081
|
});
|
|
5555
7082
|
}
|
|
5556
7083
|
function detonate(c) {
|
|
@@ -5825,7 +7352,7 @@ var GameVehicles = (() => {
|
|
|
5825
7352
|
const I = kit._;
|
|
5826
7353
|
const { MATERIALS } = I;
|
|
5827
7354
|
function rollMeshes(c) {
|
|
5828
|
-
const
|
|
7355
|
+
const list2 = [];
|
|
5829
7356
|
const thin = [];
|
|
5830
7357
|
c.root.updateMatrixWorld(true);
|
|
5831
7358
|
const inv = new THREE.Matrix4().copy(c.vehicle.matrixWorld).invert();
|
|
@@ -5843,9 +7370,9 @@ var GameVehicles = (() => {
|
|
|
5843
7370
|
thin.push({ o: ob, M });
|
|
5844
7371
|
return;
|
|
5845
7372
|
}
|
|
5846
|
-
|
|
7373
|
+
list2.push({ o: ob, M });
|
|
5847
7374
|
});
|
|
5848
|
-
return { list, thin };
|
|
7375
|
+
return { list: list2, thin };
|
|
5849
7376
|
}
|
|
5850
7377
|
function convexHull(pts) {
|
|
5851
7378
|
pts.sort(function(a, b) {
|
|
@@ -5928,7 +7455,7 @@ var GameVehicles = (() => {
|
|
|
5928
7455
|
};
|
|
5929
7456
|
function rollContact(c, A, q) {
|
|
5930
7457
|
const m = c.meta;
|
|
5931
|
-
const
|
|
7458
|
+
const list2 = [];
|
|
5932
7459
|
const dy = A.cen[0] - q[0];
|
|
5933
7460
|
const dz = A.cen[1] - q[1];
|
|
5934
7461
|
const dl = Math.hypot(dy, dz) || 1;
|
|
@@ -5936,17 +7463,17 @@ var GameVehicles = (() => {
|
|
|
5936
7463
|
const f = 0.75;
|
|
5937
7464
|
const bike = m.kind === "bike";
|
|
5938
7465
|
(bike ? [0] : [-0.28, 0, 0.28]).forEach(function(k) {
|
|
5939
|
-
|
|
7466
|
+
list2.push([new V3(A.x + k * A.L, q[0], q[1]), dir]);
|
|
5940
7467
|
});
|
|
5941
7468
|
if (!bike && Math.abs(dir.z) > 0.75 && m.top - m.belt > 0.25 && A.hits < 3) {
|
|
5942
7469
|
const sd = -Math.sign(dir.z);
|
|
5943
7470
|
const yw = m.belt + (m.top - m.belt) * 0.45;
|
|
5944
7471
|
[-0.25, 0.2].forEach(function(k) {
|
|
5945
7472
|
const h = I.probe(c, new V3(A.x + k * A.L, yw, sd * (m.width / 2 + 2)), new V3(0, 0, -sd), 2 + m.width);
|
|
5946
|
-
if (h)
|
|
7473
|
+
if (h) list2.push([h.p, new V3(0, 0, -sd)]);
|
|
5947
7474
|
});
|
|
5948
7475
|
}
|
|
5949
|
-
|
|
7476
|
+
list2.forEach(function(e) {
|
|
5950
7477
|
I.impact(c, e[0], e[1], f, { noStain: true, maxDepth: 0.1 });
|
|
5951
7478
|
const pf = c.toEffects(e[0]);
|
|
5952
7479
|
pf.y = Math.max(0.03, pf.y);
|
|
@@ -5956,15 +7483,15 @@ var GameVehicles = (() => {
|
|
|
5956
7483
|
});
|
|
5957
7484
|
A.hits++;
|
|
5958
7485
|
A.bounce = A.hits === 1 ? 0.06 : 0.1;
|
|
5959
|
-
if (m.kind === "boat") I.burst(c, "water", c.toEffects(
|
|
7486
|
+
if (m.kind === "boat") I.burst(c, "water", c.toEffects(list2[0][0]).setY(0.05), 30, { speed: 3 });
|
|
5960
7487
|
}
|
|
5961
|
-
function exactMinY(c,
|
|
7488
|
+
function exactMinY(c, list2) {
|
|
5962
7489
|
c.root.updateMatrixWorld(true);
|
|
5963
7490
|
const inv = new THREE.Matrix4().copy(c.root.matrixWorld).invert();
|
|
5964
7491
|
const M = new THREE.Matrix4();
|
|
5965
7492
|
const v = new V3();
|
|
5966
7493
|
let mn = 1e9;
|
|
5967
|
-
|
|
7494
|
+
list2.forEach(function(e) {
|
|
5968
7495
|
const ob = e.o;
|
|
5969
7496
|
if (!ob.parent || !I.shown(c, ob)) return;
|
|
5970
7497
|
M.multiplyMatrices(inv, ob.matrixWorld);
|
|
@@ -8160,9 +9687,9 @@ var GameVehicles = (() => {
|
|
|
8160
9687
|
down: new V3(0, 1, 0)
|
|
8161
9688
|
}[d] || null;
|
|
8162
9689
|
}
|
|
8163
|
-
function resolveObjs(c,
|
|
9690
|
+
function resolveObjs(c, list2, z) {
|
|
8164
9691
|
const out = [];
|
|
8165
|
-
(
|
|
9692
|
+
(list2 || []).forEach(function(o) {
|
|
8166
9693
|
if (!o) return;
|
|
8167
9694
|
if (typeof o === "string") {
|
|
8168
9695
|
c.parts.forEach(function(p) {
|
|
@@ -8190,22 +9717,22 @@ var GameVehicles = (() => {
|
|
|
8190
9717
|
}
|
|
8191
9718
|
function deriveZones(c) {
|
|
8192
9719
|
const m = c.meta;
|
|
8193
|
-
let
|
|
9720
|
+
let list2 = [];
|
|
8194
9721
|
const byId = {};
|
|
8195
9722
|
(m.zones === false ? [] : I.autoZones(c)).forEach(function(z) {
|
|
8196
9723
|
byId[z.id] = z;
|
|
8197
|
-
|
|
9724
|
+
list2.push(z);
|
|
8198
9725
|
});
|
|
8199
9726
|
(c.zoneDefs || []).forEach(function(d) {
|
|
8200
9727
|
let z = byId[d.id];
|
|
8201
9728
|
if (d.remove) {
|
|
8202
9729
|
if (z) {
|
|
8203
|
-
|
|
9730
|
+
list2.splice(list2.indexOf(z), 1);
|
|
8204
9731
|
delete byId[d.id];
|
|
8205
9732
|
}
|
|
8206
|
-
|
|
9733
|
+
list2.slice().forEach(function(q) {
|
|
8207
9734
|
if (q.id.indexOf(d.id + "_") === 0 && /^\d+$/.test(q.id.slice(d.id.length + 1))) {
|
|
8208
|
-
|
|
9735
|
+
list2.splice(list2.indexOf(q), 1);
|
|
8209
9736
|
delete byId[q.id];
|
|
8210
9737
|
}
|
|
8211
9738
|
});
|
|
@@ -8218,7 +9745,7 @@ var GameVehicles = (() => {
|
|
|
8218
9745
|
} else {
|
|
8219
9746
|
z = Object.assign({ type: "panel" }, d);
|
|
8220
9747
|
byId[d.id] = z;
|
|
8221
|
-
|
|
9748
|
+
list2.push(z);
|
|
8222
9749
|
}
|
|
8223
9750
|
});
|
|
8224
9751
|
const inv = I.worldInverseOf(c);
|
|
@@ -8251,10 +9778,10 @@ var GameVehicles = (() => {
|
|
|
8251
9778
|
}
|
|
8252
9779
|
z.box = I.normalizeBox(bb);
|
|
8253
9780
|
});
|
|
8254
|
-
|
|
9781
|
+
list2 = list2.filter(function(z) {
|
|
8255
9782
|
return z.box && z.name;
|
|
8256
9783
|
});
|
|
8257
|
-
|
|
9784
|
+
list2.forEach(function(z) {
|
|
8258
9785
|
z.glass = (z.glass || []).slice();
|
|
8259
9786
|
z.lights = (z.lights || []).slice();
|
|
8260
9787
|
z.wheels = (z.wheels || []).slice();
|
|
@@ -8291,7 +9818,7 @@ var GameVehicles = (() => {
|
|
|
8291
9818
|
if (owner.has(g) || !g.parent || !g.userData.glass || g.userData.ghost) return;
|
|
8292
9819
|
const cg = I.localBox(c, g, inv).getCenter(new V3());
|
|
8293
9820
|
let best = null;
|
|
8294
|
-
|
|
9821
|
+
list2.forEach(function(z) {
|
|
8295
9822
|
if (z.internal || z.type === "wheel" || z.type === "light") return;
|
|
8296
9823
|
if (I.boxContains(z.box, cg.x, cg.y, cg.z, 0.05) && (!best || I.boxVolume(z.box) < I.boxVolume(best.box))) best = z;
|
|
8297
9824
|
});
|
|
@@ -8300,7 +9827,7 @@ var GameVehicles = (() => {
|
|
|
8300
9827
|
owner.set(g, best);
|
|
8301
9828
|
}
|
|
8302
9829
|
});
|
|
8303
|
-
const looseParts =
|
|
9830
|
+
const looseParts = list2.filter(function(z) {
|
|
8304
9831
|
return z.type === "part";
|
|
8305
9832
|
});
|
|
8306
9833
|
if (looseParts.length)
|
|
@@ -8325,7 +9852,7 @@ var GameVehicles = (() => {
|
|
|
8325
9852
|
});
|
|
8326
9853
|
}
|
|
8327
9854
|
});
|
|
8328
|
-
|
|
9855
|
+
list2.forEach(function(z) {
|
|
8329
9856
|
if (!z.mirror) return;
|
|
8330
9857
|
z.mirrors = [];
|
|
8331
9858
|
const B = z.box;
|
|
@@ -8357,21 +9884,21 @@ var GameVehicles = (() => {
|
|
|
8357
9884
|
});
|
|
8358
9885
|
}
|
|
8359
9886
|
});
|
|
8360
|
-
|
|
9887
|
+
list2.forEach(function(z) {
|
|
8361
9888
|
const B = z.box;
|
|
8362
9889
|
z.map = z.map || [Math.max(B.x0, R - 0.3), Math.min(B.x1, F + 0.3), Math.max(B.z0, -W / 2 - 0.35), Math.min(B.z1, W / 2 + 0.35)];
|
|
8363
9890
|
});
|
|
8364
|
-
carMap(c,
|
|
9891
|
+
carMap(c, list2);
|
|
8365
9892
|
c.zoneOwners = owner;
|
|
8366
|
-
return
|
|
9893
|
+
return list2;
|
|
8367
9894
|
}
|
|
8368
|
-
function carMap(c,
|
|
9895
|
+
function carMap(c, list2) {
|
|
8369
9896
|
const m = c.meta;
|
|
8370
9897
|
const W = m.width || 1.7;
|
|
8371
9898
|
const hw = W / 2;
|
|
8372
9899
|
const gh = m.glasshouse;
|
|
8373
9900
|
if ((m.kind || "car") !== "car" || !gh || gh.fb == null) return;
|
|
8374
|
-
|
|
9901
|
+
list2.forEach(function(z) {
|
|
8375
9902
|
if (z.mapSet) return;
|
|
8376
9903
|
const B = z.box;
|
|
8377
9904
|
const s = z.side;
|
|
@@ -8436,8 +9963,8 @@ var GameVehicles = (() => {
|
|
|
8436
9963
|
const top = m.top || belt + 0.5;
|
|
8437
9964
|
const hw = W / 2;
|
|
8438
9965
|
const WH = I.wheelInfo(c);
|
|
8439
|
-
function add(id, name, type,
|
|
8440
|
-
const z = Object.assign({ id, name, type, box: I.normalizeBox(
|
|
9966
|
+
function add(id, name, type, box2, o) {
|
|
9967
|
+
const z = Object.assign({ id, name, type, box: I.normalizeBox(box2) }, o || {});
|
|
8441
9968
|
Z.push(z);
|
|
8442
9969
|
return z;
|
|
8443
9970
|
}
|
|
@@ -9800,16 +11327,167 @@ var GameVehicles = (() => {
|
|
|
9800
11327
|
kit.ui = { damageMap, bandButtons, legend, healthBar, scenarioButtons, zoneLabel, bandRange, css, injectStyles };
|
|
9801
11328
|
}
|
|
9802
11329
|
|
|
11330
|
+
// src/lod.ts
|
|
11331
|
+
var defaultLodSettings = {
|
|
11332
|
+
on: false,
|
|
11333
|
+
segmentFraction: 0.5,
|
|
11334
|
+
roundingRings: 1,
|
|
11335
|
+
subdivisionFactor: 2.2,
|
|
11336
|
+
flatBoxEdge: 0.22,
|
|
11337
|
+
minLoftSegments: 14,
|
|
11338
|
+
loftSegmentFraction: 0.45,
|
|
11339
|
+
minStations: 20,
|
|
11340
|
+
stationFraction: 0.55,
|
|
11341
|
+
gridFactor: 2.6,
|
|
11342
|
+
hiddenMaterials: ["seat", "carpet", "headliner", "gauge", "screen", "bay", "hoodLiner", "block", "cover", "radiator", "coolant", "coolantLiq", "washer", "hv", "hose", "leather"]
|
|
11343
|
+
};
|
|
11344
|
+
function createLod(options) {
|
|
11345
|
+
return { ...defaultLodSettings, ...options || {}, on: false };
|
|
11346
|
+
}
|
|
11347
|
+
var fewer = (lod, n, min) => Math.max(min, Math.round(n * lod.segmentFraction));
|
|
11348
|
+
function wrapThreeForLod(TH, lod) {
|
|
11349
|
+
const T = Object.assign({}, TH);
|
|
11350
|
+
if (TH.SRGBColorSpace) {
|
|
11351
|
+
T.CanvasTexture = class extends TH.CanvasTexture {
|
|
11352
|
+
constructor(a, b, c, d, e, f, g, h, i) {
|
|
11353
|
+
super(a, b, c, d, e, f, g, h, i);
|
|
11354
|
+
this.colorSpace = TH.SRGBColorSpace;
|
|
11355
|
+
}
|
|
11356
|
+
};
|
|
11357
|
+
}
|
|
11358
|
+
T.CylinderGeometry = class extends TH.CylinderGeometry {
|
|
11359
|
+
constructor(rt, rb, h, rs, hs, open, ts, tl) {
|
|
11360
|
+
super(rt, rb, h, lod.on && rs != null ? fewer(lod, rs, 6) : rs, lod.on ? 1 : hs, open, ts, tl);
|
|
11361
|
+
}
|
|
11362
|
+
};
|
|
11363
|
+
T.SphereGeometry = class extends TH.SphereGeometry {
|
|
11364
|
+
constructor(r, ws, hs, ps, pl, ts, tl) {
|
|
11365
|
+
super(r, lod.on && ws != null ? fewer(lod, ws, 8) : ws, lod.on && hs != null ? fewer(lod, hs, 5) : hs, ps, pl, ts, tl);
|
|
11366
|
+
}
|
|
11367
|
+
};
|
|
11368
|
+
T.TorusGeometry = class extends TH.TorusGeometry {
|
|
11369
|
+
constructor(r, tube, rs, ts, arc) {
|
|
11370
|
+
super(r, tube, lod.on && rs != null ? fewer(lod, rs, 4) : rs, lod.on && ts != null ? fewer(lod, ts, 8) : ts, arc);
|
|
11371
|
+
}
|
|
11372
|
+
};
|
|
11373
|
+
T.LatheGeometry = class extends TH.LatheGeometry {
|
|
11374
|
+
constructor(pts, seg, a, b) {
|
|
11375
|
+
super(pts, lod.on && seg != null ? fewer(lod, seg, 10) : seg, a, b);
|
|
11376
|
+
}
|
|
11377
|
+
};
|
|
11378
|
+
T.TubeGeometry = class extends TH.TubeGeometry {
|
|
11379
|
+
constructor(path, ts, r, rs, closed) {
|
|
11380
|
+
if (lod.on) {
|
|
11381
|
+
let len = 1;
|
|
11382
|
+
try {
|
|
11383
|
+
len = path.getLength();
|
|
11384
|
+
} catch (e) {
|
|
11385
|
+
}
|
|
11386
|
+
ts = Math.max(6, Math.min(ts == null ? 64 : ts, Math.ceil(len / 0.14)));
|
|
11387
|
+
rs = r < 0.025 ? 3 : Math.max(4, fewer(lod, rs == null ? 8 : rs, 4));
|
|
11388
|
+
}
|
|
11389
|
+
super(path, ts, r, rs, closed);
|
|
11390
|
+
}
|
|
11391
|
+
};
|
|
11392
|
+
T.ExtrudeGeometry = class extends TH.ExtrudeGeometry {
|
|
11393
|
+
constructor(shape, o) {
|
|
11394
|
+
if (lod.on && o) {
|
|
11395
|
+
o = Object.assign({}, o);
|
|
11396
|
+
if (o.curveSegments) o.curveSegments = fewer(lod, o.curveSegments, 5);
|
|
11397
|
+
if (o.bevelSegments) o.bevelSegments = Math.max(1, Math.round(o.bevelSegments * 0.5));
|
|
11398
|
+
}
|
|
11399
|
+
super(shape, o);
|
|
11400
|
+
}
|
|
11401
|
+
};
|
|
11402
|
+
T.CircleGeometry = class extends TH.CircleGeometry {
|
|
11403
|
+
constructor(r, seg, a, b) {
|
|
11404
|
+
super(r, lod.on && seg != null ? fewer(lod, seg, 8) : seg, a, b);
|
|
11405
|
+
}
|
|
11406
|
+
};
|
|
11407
|
+
T.RingGeometry = class extends TH.RingGeometry {
|
|
11408
|
+
constructor(a, b, ts, ps, c, d) {
|
|
11409
|
+
super(a, b, lod.on && ts != null ? fewer(lod, ts, 8) : ts, ps, c, d);
|
|
11410
|
+
}
|
|
11411
|
+
};
|
|
11412
|
+
return T;
|
|
11413
|
+
}
|
|
11414
|
+
var INTERIOR_FUNCTIONS = ["interior", "engineBay", "seat", "dashboard", "steeringWheel", "yoke", "busRows"];
|
|
11415
|
+
function installLod(kit) {
|
|
11416
|
+
const lod = kit.lod;
|
|
11417
|
+
const meshesOf = (c) => {
|
|
11418
|
+
const set = /* @__PURE__ */ new Set();
|
|
11419
|
+
c.vehicle.traverse((o) => {
|
|
11420
|
+
if (o.isMesh) set.add(o);
|
|
11421
|
+
});
|
|
11422
|
+
return set;
|
|
11423
|
+
};
|
|
11424
|
+
for (const name of INTERIOR_FUNCTIONS) {
|
|
11425
|
+
const original = kit[name];
|
|
11426
|
+
if (typeof original !== "function") continue;
|
|
11427
|
+
kit[name] = function(...args) {
|
|
11428
|
+
const c = args[0];
|
|
11429
|
+
if (!lod.on || !c || !c.vehicle) return original.apply(this, args);
|
|
11430
|
+
const before = meshesOf(c);
|
|
11431
|
+
const result = original.apply(this, args);
|
|
11432
|
+
c.vehicle.traverse((o) => {
|
|
11433
|
+
if (o.isMesh && !before.has(o)) o.userData.skipLite = true;
|
|
11434
|
+
});
|
|
11435
|
+
return result;
|
|
11436
|
+
};
|
|
11437
|
+
}
|
|
11438
|
+
kit.lodHiddenMaterials = function() {
|
|
11439
|
+
const materials = kit.materials;
|
|
11440
|
+
return new Set(lod.hiddenMaterials.map((name) => materials[name]).filter(Boolean));
|
|
11441
|
+
};
|
|
11442
|
+
}
|
|
11443
|
+
|
|
11444
|
+
// src/damageables.ts
|
|
11445
|
+
var defaultDamageableKinds = {
|
|
11446
|
+
door: { material: "metal", part: "door" },
|
|
11447
|
+
hood: { material: "metal", part: "hood" },
|
|
11448
|
+
lid: { material: "metal", part: "lid" },
|
|
11449
|
+
bumper: { material: "metal", part: "bumper" },
|
|
11450
|
+
glass: { material: "glass", part: "window" },
|
|
11451
|
+
"glass:windshield": { material: "glass", part: "windshield" },
|
|
11452
|
+
light: { material: "glass", part: "headlight" },
|
|
11453
|
+
"light:taillight": { material: "glass", part: "taillight" },
|
|
11454
|
+
wheel: { material: "rubber", part: "wheel" },
|
|
11455
|
+
"internal:fuel_tank": { material: "fuel", part: "fuel_tank" }
|
|
11456
|
+
};
|
|
11457
|
+
function installDamageables(kit) {
|
|
11458
|
+
const THREE = kit.THREE;
|
|
11459
|
+
kit.damageableKinds = { ...defaultDamageableKinds };
|
|
11460
|
+
const kindOf2 = (zone) => {
|
|
11461
|
+
const kinds = kit.damageableKinds;
|
|
11462
|
+
if (zone.type === "internal") return zone.sub ? kinds[`internal:${zone.sub}`] || null : null;
|
|
11463
|
+
return kinds[`${zone.type}:${zone.fxKey}`] || kinds[zone.type] || null;
|
|
11464
|
+
};
|
|
11465
|
+
kit.damageables = function(c, matrix) {
|
|
11466
|
+
const out = [];
|
|
11467
|
+
for (const zone of c.zones || []) {
|
|
11468
|
+
const kind = kindOf2(zone);
|
|
11469
|
+
if (!kind || !zone.box) continue;
|
|
11470
|
+
const b = zone.box;
|
|
11471
|
+
const box2 = new THREE.Box3(new THREE.Vector3(b.x0, b.y0, b.z0), new THREE.Vector3(b.x1, b.y1, b.z1));
|
|
11472
|
+
if (matrix) box2.applyMatrix4(matrix);
|
|
11473
|
+
out.push({ material: kind.material, zone: zone.id, part: kind.part, side: zone.side === 0 ? "center" : zone.side < 0 ? "left" : "right", box: box2 });
|
|
11474
|
+
}
|
|
11475
|
+
return out;
|
|
11476
|
+
};
|
|
11477
|
+
}
|
|
11478
|
+
|
|
9803
11479
|
// src/kit.ts
|
|
9804
11480
|
function createVehicleKit(options) {
|
|
9805
11481
|
const opts = options || {};
|
|
9806
|
-
|
|
9807
|
-
|
|
11482
|
+
if (!opts.THREE || !opts.THREE.Vector3) throw new Error("createVehicleKit: options.THREE (the three.js namespace) is required");
|
|
11483
|
+
const lod = createLod(opts.lod);
|
|
11484
|
+
const THREE = wrapThreeForLod(shapeGeometries(opts.THREE), lod);
|
|
9808
11485
|
const strings = resolveStrings(opts.locale);
|
|
9809
11486
|
const t = createTranslate(strings);
|
|
9810
11487
|
const doc = opts.document || (typeof document !== "undefined" ? document : void 0);
|
|
9811
11488
|
const kit = {
|
|
9812
11489
|
THREE,
|
|
11490
|
+
lod,
|
|
9813
11491
|
V3: THREE.Vector3,
|
|
9814
11492
|
PI: Math.PI,
|
|
9815
11493
|
document: doc,
|
|
@@ -9881,6 +11559,8 @@ var GameVehicles = (() => {
|
|
|
9881
11559
|
installZoneApply(kit);
|
|
9882
11560
|
installScenarios(kit);
|
|
9883
11561
|
installUi(kit);
|
|
11562
|
+
installLod(kit);
|
|
11563
|
+
installDamageables(kit);
|
|
9884
11564
|
const I = kit._;
|
|
9885
11565
|
const HOOKS = {};
|
|
9886
11566
|
kit.on = function(ev, fn) {
|
|
@@ -9975,9 +11655,18 @@ var GameVehicles = (() => {
|
|
|
9975
11655
|
}
|
|
9976
11656
|
}
|
|
9977
11657
|
kit.defaultDamage = defaultDamage;
|
|
9978
|
-
kit.build = function(key, state, zoneState) {
|
|
11658
|
+
kit.build = function(key, state, zoneState, options2) {
|
|
9979
11659
|
const spec = kit.find(key);
|
|
9980
11660
|
if (!spec) throw new Error("Unknown vehicle: " + key);
|
|
11661
|
+
const lodBefore = lod.on;
|
|
11662
|
+
if (options2 && options2.lod != null) lod.on = !!options2.lod;
|
|
11663
|
+
try {
|
|
11664
|
+
return buildVehicle(spec, key, state, zoneState);
|
|
11665
|
+
} finally {
|
|
11666
|
+
lod.on = lodBefore;
|
|
11667
|
+
}
|
|
11668
|
+
};
|
|
11669
|
+
function buildVehicle(spec, key, state, zoneState) {
|
|
9981
11670
|
const st = state || "intact";
|
|
9982
11671
|
const seedKey = spec.seedKey || key;
|
|
9983
11672
|
const c = new kit.BuildContext(st, strHash(seedKey + (LEGACY_STATES[st] || st)));
|
|
@@ -10026,7 +11715,7 @@ var GameVehicles = (() => {
|
|
|
10026
11715
|
if (o.isMesh && !(o.material && o.material.transparent)) o.receiveShadow = true;
|
|
10027
11716
|
});
|
|
10028
11717
|
return c;
|
|
10029
|
-
}
|
|
11718
|
+
}
|
|
10030
11719
|
kit.dispose = function(c) {
|
|
10031
11720
|
const shared = Object.values(I.PARTICLE_GEOMETRIES);
|
|
10032
11721
|
c.root.traverse(function(o) {
|
|
@@ -10043,8 +11732,9 @@ var GameVehicles = (() => {
|
|
|
10043
11732
|
assignDeep(strings, resolveStrings(opts.locale));
|
|
10044
11733
|
return kit;
|
|
10045
11734
|
};
|
|
10046
|
-
const { THREE: _three, locale: _locale, document: _document, plugins, ...config } = opts;
|
|
11735
|
+
const { THREE: _three, lod: _lod, locale: _locale, document: _document, plugins, ...config } = opts;
|
|
10047
11736
|
void _three;
|
|
11737
|
+
void _lod;
|
|
10048
11738
|
void _locale;
|
|
10049
11739
|
void _document;
|
|
10050
11740
|
kit.configure(config);
|
|
@@ -10052,6 +11742,51 @@ var GameVehicles = (() => {
|
|
|
10052
11742
|
return kit;
|
|
10053
11743
|
}
|
|
10054
11744
|
|
|
11745
|
+
// ../game-contracts/dist/index.js
|
|
11746
|
+
var DEFAULT_IGNITION_RULES = {
|
|
11747
|
+
fillCurve: [
|
|
11748
|
+
[0, 0.6],
|
|
11749
|
+
[0.1, 1],
|
|
11750
|
+
[0.35, 0.6],
|
|
11751
|
+
[0.65, 0.3],
|
|
11752
|
+
[0.9, 0.15],
|
|
11753
|
+
[1, 0.12]
|
|
11754
|
+
],
|
|
11755
|
+
flameFillInfluence: 0.5,
|
|
11756
|
+
flameDefaults: { incendiary: 0.7, tracer: 0.2 },
|
|
11757
|
+
sparkTarget: { steel: 1, iron: 1, aluminium: 0.25 },
|
|
11758
|
+
sparkMetalDefault: 0.5,
|
|
11759
|
+
sparkFluid: { diesel: 0.15 },
|
|
11760
|
+
fireNear: { radius: 1.5, chancePerSecond: 0.5 }
|
|
11761
|
+
};
|
|
11762
|
+
var resolveIgnitionRules = (rules) => ({
|
|
11763
|
+
...DEFAULT_IGNITION_RULES,
|
|
11764
|
+
...rules,
|
|
11765
|
+
flameDefaults: { ...DEFAULT_IGNITION_RULES.flameDefaults, ...rules?.flameDefaults },
|
|
11766
|
+
sparkTarget: { ...DEFAULT_IGNITION_RULES.sparkTarget, ...rules?.sparkTarget },
|
|
11767
|
+
sparkFluid: { ...DEFAULT_IGNITION_RULES.sparkFluid, ...rules?.sparkFluid },
|
|
11768
|
+
fireNear: { ...DEFAULT_IGNITION_RULES.fireNear, ...rules?.fireNear }
|
|
11769
|
+
});
|
|
11770
|
+
var fillFactor = (fill, curve = DEFAULT_IGNITION_RULES.fillCurve) => {
|
|
11771
|
+
if (curve.length === 0) return 1;
|
|
11772
|
+
const f = Math.min(1, Math.max(0, fill));
|
|
11773
|
+
if (f <= curve[0][0]) return curve[0][1];
|
|
11774
|
+
for (let i = 1; i < curve.length; i++) {
|
|
11775
|
+
const [x1, y1] = curve[i];
|
|
11776
|
+
const [x0, y0] = curve[i - 1];
|
|
11777
|
+
if (f <= x1) return x1 === x0 ? y1 : y0 + (y1 - y0) * (f - x0) / (x1 - x0);
|
|
11778
|
+
}
|
|
11779
|
+
return curve[curve.length - 1][1];
|
|
11780
|
+
};
|
|
11781
|
+
var sparkTargetFactor = (materialId, metal, rules = DEFAULT_IGNITION_RULES) => !metal ? 0 : rules.sparkTarget[materialId] ?? rules.sparkMetalDefault;
|
|
11782
|
+
var ignitionChance = (hit, target, rules = DEFAULT_IGNITION_RULES) => {
|
|
11783
|
+
const k = fillFactor(target.fill, rules.fillCurve);
|
|
11784
|
+
const spark = (hit.ignition?.spark ?? 0) * target.sparkTarget * (target.fluid === void 0 ? 1 : rules.sparkFluid[target.fluid] ?? 1) * k;
|
|
11785
|
+
const flameBase = hit.ignition?.flame ?? Math.max(hit.incendiary ? rules.flameDefaults.incendiary : 0, hit.tracer ? rules.flameDefaults.tracer : 0);
|
|
11786
|
+
const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
|
|
11787
|
+
return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
|
|
11788
|
+
};
|
|
11789
|
+
|
|
10055
11790
|
// src/destructible/model.ts
|
|
10056
11791
|
var JOINT_RULES = {
|
|
10057
11792
|
hinge: { strength: 5e3, tools: ["wrench", "screwdriver"], seconds: 3 },
|
|
@@ -10179,19 +11914,19 @@ var GameVehicles = (() => {
|
|
|
10179
11914
|
for (const z of zones) {
|
|
10180
11915
|
let role = roleOfZone(z);
|
|
10181
11916
|
if (z === hubZone) role = "frame";
|
|
10182
|
-
const
|
|
10183
|
-
const
|
|
11917
|
+
const box2 = boxOf(z);
|
|
11918
|
+
const area3 = areaOf(box2.size);
|
|
10184
11919
|
const id = z.id;
|
|
10185
11920
|
const label = String(z.name ?? id);
|
|
10186
11921
|
const tags = [...z.tags ?? []];
|
|
10187
11922
|
if (role === "rim") {
|
|
10188
11923
|
const wheels = z.wheels ?? [];
|
|
10189
11924
|
const r = Math.max(0.2, ...wheels.map((w) => w.userData?.r ?? 0.3));
|
|
10190
|
-
const sz = Math.sign(
|
|
10191
|
-
const rimBox = { center: [
|
|
11925
|
+
const sz = Math.sign(box2.center[2]) || 1;
|
|
11926
|
+
const rimBox = { center: [box2.center[0], box2.center[1], box2.center[2] - sz * box2.size[2] * 0.25], size: [box2.size[0] * 0.7, box2.size[1] * 0.7, box2.size[2] * 0.5] };
|
|
10192
11927
|
add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box: rimBox, mass: 22 * r * Math.max(1, wheels.length), hp: 12e3 * armour, zoneId: id, visual: kind !== "plane" && !/landing/.test(id), tags: ["wheel", ...tags] });
|
|
10193
11928
|
const tyreId = id.replace(/^wheels?/, "tyre").replace(/^(?!tyre)/, "tyre_");
|
|
10194
|
-
const tyreBox = { center:
|
|
11929
|
+
const tyreBox = { center: box2.center, size: [box2.size[0] * 0.98, box2.size[1] * 0.98, box2.size[2]] };
|
|
10195
11930
|
add({
|
|
10196
11931
|
id: tyreId,
|
|
10197
11932
|
role: "tyre",
|
|
@@ -10211,29 +11946,29 @@ var GameVehicles = (() => {
|
|
|
10211
11946
|
if (role === "glass") {
|
|
10212
11947
|
const glass = (z.glass ?? [])[0];
|
|
10213
11948
|
const laminated = glass?.userData?.glass?.type === "laminated" || m.bullet;
|
|
10214
|
-
add({ id, role, name: label, material: "glass", box, mass: 10 *
|
|
11949
|
+
add({ id, role, name: label, material: "glass", box: box2, mass: 10 * area3, hp: laminated ? 4e3 : 450, zoneId: id, visual: false, tags: ["glass", ...tags] });
|
|
10215
11950
|
continue;
|
|
10216
11951
|
}
|
|
10217
11952
|
if (role === "light") {
|
|
10218
|
-
add({ id, role, name: label, material: "glass", box, mass: 1.2 + 4 *
|
|
11953
|
+
add({ id, role, name: label, material: "glass", box: box2, mass: 1.2 + 4 * area3, hp: 320, zoneId: id, visual: true, tags: ["light", ...tags] });
|
|
10219
11954
|
continue;
|
|
10220
11955
|
}
|
|
10221
11956
|
if (role === "bumper") {
|
|
10222
|
-
add({ id, role, name: label, material: "plastic", box, mass: 6 + 5 *
|
|
11957
|
+
add({ id, role, name: label, material: "plastic", box: box2, mass: 6 + 5 * area3, hp: 9e3, ignitionC: 400, zoneId: id, visual: true, tags: ["bumper", ...tags] });
|
|
10223
11958
|
continue;
|
|
10224
11959
|
}
|
|
10225
11960
|
if (role === "engine") {
|
|
10226
11961
|
const kg2 = { bike: 55, quad: 90, car: 150, tractor: 450, truck: 600, bus: 450, tank: 900, boat: 90, plane: 160, heli: 150 }[kind];
|
|
10227
|
-
add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box, mass: kg2, hp: 6e4 * armour, ignitionC: 260, zoneId: id, visual: false, tags: ["engine", ...tags] });
|
|
11962
|
+
add({ id, role, name: label, material: kind === "plane" || kind === "heli" ? "aluminium" : "steel", box: box2, mass: kg2, hp: 6e4 * armour, ignitionC: 260, zoneId: id, visual: false, tags: ["engine", ...tags] });
|
|
10228
11963
|
continue;
|
|
10229
11964
|
}
|
|
10230
11965
|
if (role === "radiator") {
|
|
10231
|
-
add({ id, role, name: label, material: "aluminium", box, mass: 8, hp: 3e3, zoneId: id, visual: false, contains: { fluid: "coolant", liters: kind === "truck" || kind === "bus" ? 18 : 7, pressurized: true }, tags: ["radiator", ...tags] });
|
|
11966
|
+
add({ id, role, name: label, material: "aluminium", box: box2, mass: 8, hp: 3e3, zoneId: id, visual: false, contains: { fluid: "coolant", liters: kind === "truck" || kind === "bus" ? 18 : 7, pressurized: true }, tags: ["radiator", ...tags] });
|
|
10232
11967
|
continue;
|
|
10233
11968
|
}
|
|
10234
11969
|
if (role === "fuel_tank") {
|
|
10235
11970
|
if (fuelless) {
|
|
10236
|
-
add({ id, role: "other", name: label, material: "steel", box, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
|
|
11971
|
+
add({ id, role: "other", name: label, material: "steel", box: box2, mass: 20, hp: 25e3, zoneId: id, visual: false, tags });
|
|
10237
11972
|
continue;
|
|
10238
11973
|
}
|
|
10239
11974
|
const liters = { bike: 14, quad: 16, car: 55, tractor: 150, truck: 300, bus: 200, tank: 600, boat: 90, plane: 120, heli: 300 }[kind];
|
|
@@ -10243,7 +11978,7 @@ var GameVehicles = (() => {
|
|
|
10243
11978
|
role,
|
|
10244
11979
|
name: label,
|
|
10245
11980
|
material: "steel",
|
|
10246
|
-
box,
|
|
11981
|
+
box: box2,
|
|
10247
11982
|
mass: 14 + liters * 0.8,
|
|
10248
11983
|
hp: 25e3 * armour,
|
|
10249
11984
|
zoneId: id,
|
|
@@ -10256,21 +11991,21 @@ var GameVehicles = (() => {
|
|
|
10256
11991
|
continue;
|
|
10257
11992
|
}
|
|
10258
11993
|
if (role === "battery") {
|
|
10259
|
-
add({ id, role, name: label, material: "plastic", box, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
|
|
11994
|
+
add({ id, role, name: label, material: "plastic", box: box2, mass: 14, hp: 1500, zoneId: id, visual: true, ignitionC: 200, tags: ["battery", ...tags] });
|
|
10260
11995
|
continue;
|
|
10261
11996
|
}
|
|
10262
11997
|
if (role === "frame") {
|
|
10263
|
-
add({ id, role, name: label, material: "steel", box: floorPan(
|
|
11998
|
+
add({ id, role, name: label, material: "steel", box: floorPan(box2), mass: mass * 0.25, hp: 15e4 * armour, zoneId: id, visual: false, tags: ["frame", ...tags] });
|
|
10264
11999
|
continue;
|
|
10265
12000
|
}
|
|
10266
12001
|
const roofLike = role === "roof";
|
|
10267
|
-
const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) *
|
|
12002
|
+
const kg = (skin === "steel" ? STEEL_KG_M2 : ALU_KG_M2) * area3 * (role === "door" ? 1.6 : 1);
|
|
10268
12003
|
add({
|
|
10269
12004
|
id,
|
|
10270
12005
|
role,
|
|
10271
12006
|
name: label,
|
|
10272
12007
|
material: skin,
|
|
10273
|
-
box,
|
|
12008
|
+
box: box2,
|
|
10274
12009
|
mass: Math.max(2, kg),
|
|
10275
12010
|
hp: (roofLike ? 3e4 : 2e4) * armour,
|
|
10276
12011
|
zoneId: id,
|
|
@@ -10492,7 +12227,7 @@ var GameVehicles = (() => {
|
|
|
10492
12227
|
const my = kind === "bike" || kind === "quad" ? (m.top ?? belt + 0.25) - 0.1 : belt + 0.12;
|
|
10493
12228
|
for (const s of [-1, 1]) {
|
|
10494
12229
|
const door = zones.find((z) => z.type === "door" && z.side === s && (z.mirrors ?? []).length);
|
|
10495
|
-
let
|
|
12230
|
+
let box2 = { center: [mx, my, s * half], size: [0.14, 0.12, 0.2] };
|
|
10496
12231
|
if (door) {
|
|
10497
12232
|
const bb = door.mirrors.reduce(
|
|
10498
12233
|
(acc, o) => {
|
|
@@ -10505,14 +12240,14 @@ var GameVehicles = (() => {
|
|
|
10505
12240
|
},
|
|
10506
12241
|
null
|
|
10507
12242
|
);
|
|
10508
|
-
if (bb)
|
|
12243
|
+
if (bb) box2 = { center: [(bb[0] + bb[1]) / 2, (bb[2] + bb[3]) / 2, (bb[4] + bb[5]) / 2], size: [Math.max(0.06, bb[1] - bb[0]), Math.max(0.06, bb[3] - bb[2]), Math.max(0.06, bb[5] - bb[4])] };
|
|
10509
12244
|
}
|
|
10510
12245
|
add({
|
|
10511
12246
|
id: `mirror_${side(s)}`,
|
|
10512
12247
|
role: "mirror",
|
|
10513
12248
|
name: t("mechanics.mirror", { side: t(`sides.${side(s)}`), sideM: t(`sides.${side(s)}M`) }),
|
|
10514
12249
|
material: "plastic",
|
|
10515
|
-
box,
|
|
12250
|
+
box: box2,
|
|
10516
12251
|
mass: 1.1,
|
|
10517
12252
|
hp: 1500,
|
|
10518
12253
|
ignitionC: 400,
|
|
@@ -10702,13 +12437,13 @@ var GameVehicles = (() => {
|
|
|
10702
12437
|
(parent ?? this.c.vehicle).add(m);
|
|
10703
12438
|
return m;
|
|
10704
12439
|
}
|
|
10705
|
-
surface(axis,
|
|
12440
|
+
surface(axis, sign2, B, samples) {
|
|
10706
12441
|
const V3 = this.THREE.Vector3;
|
|
10707
12442
|
const hits = [];
|
|
10708
12443
|
const far = 3;
|
|
10709
12444
|
for (const [u, v] of samples) {
|
|
10710
|
-
const o = axis === "z" ? new V3(u, v,
|
|
10711
|
-
const d = axis === "z" ? new V3(0, 0, -
|
|
12445
|
+
const o = axis === "z" ? new V3(u, v, sign2 * (Math.max(Math.abs(B.z0), Math.abs(B.z1)) + 1)) : new V3(sign2 * (Math.max(Math.abs(B.x0), Math.abs(B.x1)) + 1), u, v);
|
|
12446
|
+
const d = axis === "z" ? new V3(0, 0, -sign2) : new V3(-sign2, 0, 0);
|
|
10712
12447
|
const h = this.I.probe(this.c, o, d, far);
|
|
10713
12448
|
if (h) hits.push(axis === "z" ? h.p.z : h.p.x);
|
|
10714
12449
|
}
|
|
@@ -10858,12 +12593,12 @@ var GameVehicles = (() => {
|
|
|
10858
12593
|
const z = this.zone(p);
|
|
10859
12594
|
if (!z) return false;
|
|
10860
12595
|
const V3 = this.THREE.Vector3;
|
|
10861
|
-
const
|
|
10862
|
-
if (!
|
|
12596
|
+
const list2 = z.glass.filter((g) => g.parent && g.visible);
|
|
12597
|
+
if (!list2.length) return false;
|
|
10863
12598
|
const pt = new V3(...point);
|
|
10864
|
-
let best =
|
|
12599
|
+
let best = list2[0];
|
|
10865
12600
|
let bd = Infinity;
|
|
10866
|
-
for (const g of
|
|
12601
|
+
for (const g of list2) {
|
|
10867
12602
|
g.geometry.computeBoundingBox();
|
|
10868
12603
|
const bb = g.geometry.boundingBox.clone().applyMatrix4(g.matrix);
|
|
10869
12604
|
const d = bb.distanceToPoint(pt);
|
|
@@ -11164,6 +12899,9 @@ var GameVehicles = (() => {
|
|
|
11164
12899
|
const headless = !!options.headless;
|
|
11165
12900
|
const seedBase = strHash(`${c.key}|${id}|${options.seed ?? 0}`);
|
|
11166
12901
|
let rand = rng(seedBase);
|
|
12902
|
+
const ignitionRules = resolveIgnitionRules(options.ignition);
|
|
12903
|
+
const fuelFraction = clamp(options.fuel ?? 1, 0, 1);
|
|
12904
|
+
let igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
|
|
11167
12905
|
let clock = 0;
|
|
11168
12906
|
let model;
|
|
11169
12907
|
let vis = null;
|
|
@@ -11231,7 +12969,7 @@ var GameVehicles = (() => {
|
|
|
11231
12969
|
dents: [],
|
|
11232
12970
|
holes: [],
|
|
11233
12971
|
cracks: [],
|
|
11234
|
-
liters: d.contains
|
|
12972
|
+
liters: d.contains ? d.role === "fuel_tank" ? d.contains.liters * fuelFraction : d.contains.liters : void 0,
|
|
11235
12973
|
lost: false,
|
|
11236
12974
|
status: "intact",
|
|
11237
12975
|
flat: false,
|
|
@@ -11249,6 +12987,7 @@ var GameVehicles = (() => {
|
|
|
11249
12987
|
wasLit = true;
|
|
11250
12988
|
deadEngine = false;
|
|
11251
12989
|
rand = rng(seedBase);
|
|
12990
|
+
igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
|
|
11252
12991
|
steerSign = rand() < 0.5 ? -1 : 1;
|
|
11253
12992
|
vis = headless ? null : new VehicleVisuals(kit, c, model);
|
|
11254
12993
|
recomputeGroups();
|
|
@@ -11270,8 +13009,8 @@ var GameVehicles = (() => {
|
|
|
11270
13009
|
comps.get(r).push(p);
|
|
11271
13010
|
}
|
|
11272
13011
|
const hubRoot = parent.has(model.hub) ? find(model.hub) : "";
|
|
11273
|
-
const
|
|
11274
|
-
groupsCache =
|
|
13012
|
+
const list2 = [...comps.entries()].sort((a, b) => a[0] === hubRoot ? -1 : b[0] === hubRoot ? 1 : 0);
|
|
13013
|
+
groupsCache = list2.map(([, v]) => v);
|
|
11275
13014
|
groupOf = /* @__PURE__ */ new Map();
|
|
11276
13015
|
groupsCache.forEach((g, i) => g.forEach((p) => groupOf.set(p, i)));
|
|
11277
13016
|
return groupsCache;
|
|
@@ -11543,8 +13282,19 @@ var GameVehicles = (() => {
|
|
|
11543
13282
|
if (!p.burning && p.temp > AMBIENT) p.temp = Math.max(AMBIENT, p.temp - dt * 6);
|
|
11544
13283
|
}
|
|
11545
13284
|
for (const f of flames) heat(centreOf(f.def), 0.8, 500, dt * 0.5, out, 0);
|
|
13285
|
+
fireNearLeak(flames, dt, out);
|
|
11546
13286
|
thermal(out);
|
|
11547
13287
|
};
|
|
13288
|
+
const fireNearLeak = (flames, dt, out) => {
|
|
13289
|
+
const tank = tankPart();
|
|
13290
|
+
const leak = tank ? leaks.get(tank.def.id) : void 0;
|
|
13291
|
+
if (!tank || !leak || tank.lost || tank.burning || exploded || leak.fluid !== "fuel" && leak.fluid !== "diesel") return;
|
|
13292
|
+
if (leak.pud?.fire) return light(tank, out);
|
|
13293
|
+
const { radius, chancePerSecond } = ignitionRules.fireNear;
|
|
13294
|
+
if (chancePerSecond <= 0) return;
|
|
13295
|
+
const near = flames.some((f) => f !== tank && distToBox(tank.def, centreOf(f.def)) - Math.max(...f.def.box.size) / 2 <= radius);
|
|
13296
|
+
if (near && igniteRand() < 1 - Math.exp(-chancePerSecond * dt)) light(tank, out);
|
|
13297
|
+
};
|
|
11548
13298
|
const normalOf = (d, v) => faceNormal(d, v);
|
|
11549
13299
|
const ballistic = (h, out) => {
|
|
11550
13300
|
sync();
|
|
@@ -11612,10 +13362,21 @@ var GameVehicles = (() => {
|
|
|
11612
13362
|
let amount = through ? E * 0.6 : E * 0.25;
|
|
11613
13363
|
if (through && (d.role === "battery" || d.role === "alternator")) amount = Math.max(amount, p.hp * 0.65);
|
|
11614
13364
|
damage(p, amount, "ballistic", h.source, out, worldPoint);
|
|
11615
|
-
if (d.role === "fuel_tank"
|
|
13365
|
+
if (through && d.role === "fuel_tank") rollIgnition(p, h, metal, out);
|
|
11616
13366
|
}
|
|
11617
13367
|
}
|
|
11618
13368
|
};
|
|
13369
|
+
const light = (tank, out) => {
|
|
13370
|
+
tank.temp = Math.max(tank.temp, (tank.def.ignitionC ?? 260) + 1);
|
|
13371
|
+
if ((tank.liters ?? 0) > 0.5) ignite(tank, out);
|
|
13372
|
+
};
|
|
13373
|
+
const rollIgnition = (tank, h, metal, out) => {
|
|
13374
|
+
const d = tank.def;
|
|
13375
|
+
if (!d.contains || tank.burning || (tank.liters ?? 0) <= 0.05) return;
|
|
13376
|
+
const fill = d.contains.liters > 0 ? (tank.liters ?? 0) / d.contains.liters : 0;
|
|
13377
|
+
const chance = ignitionChance(h, { sparkTarget: sparkTargetFactor(d.material, metal, ignitionRules), fill, fluid: d.contains.fluid }, ignitionRules);
|
|
13378
|
+
if (chance > 0 && igniteRand() < chance) light(tank, out);
|
|
13379
|
+
};
|
|
11619
13380
|
const blunt = (h, out) => {
|
|
11620
13381
|
sync();
|
|
11621
13382
|
const M = toFrameMatrix();
|
|
@@ -11679,7 +13440,7 @@ var GameVehicles = (() => {
|
|
|
11679
13440
|
const verdict = kit.blastDamage(c, E / (1 + dist * dist * 4) / 50);
|
|
11680
13441
|
if (verdict === "explosion") explode(tank, h.source, out);
|
|
11681
13442
|
else if (verdict === "fire") {
|
|
11682
|
-
|
|
13443
|
+
light(tank, out);
|
|
11683
13444
|
startLeak(tank, centreOf(tank.def).setY(tank.def.box.center[1] - tank.def.box.size[1] / 2 + 0.05), 12, out, dir);
|
|
11684
13445
|
}
|
|
11685
13446
|
}
|
|
@@ -11693,9 +13454,9 @@ var GameVehicles = (() => {
|
|
|
11693
13454
|
thermal(out);
|
|
11694
13455
|
};
|
|
11695
13456
|
const sound = (role) => {
|
|
11696
|
-
const
|
|
11697
|
-
if (!
|
|
11698
|
-
return
|
|
13457
|
+
const list2 = [...ps.values()].filter((p) => p.def.role === role);
|
|
13458
|
+
if (!list2.length) return true;
|
|
13459
|
+
return list2.every((p) => !p.lost && attached(p.def.id) && p.hp >= p.def.hp * 0.5);
|
|
11699
13460
|
};
|
|
11700
13461
|
const tankPart = () => [...ps.values()].find((p) => p.def.role === "fuel_tank" && p.def.contains);
|
|
11701
13462
|
const computeStatus = () => {
|
|
@@ -11887,8 +13648,8 @@ var GameVehicles = (() => {
|
|
|
11887
13648
|
queue = [];
|
|
11888
13649
|
const hood = c.hood;
|
|
11889
13650
|
if (hood) {
|
|
11890
|
-
const
|
|
11891
|
-
const cur = hood.piv.rotation.z /
|
|
13651
|
+
const sign2 = hood.sign || 1;
|
|
13652
|
+
const cur = hood.piv.rotation.z / sign2;
|
|
11892
13653
|
if (Math.abs(cur - hood.ang) > 1e-4) {
|
|
11893
13654
|
hood.ang = cur;
|
|
11894
13655
|
hood.target = cur;
|
|
@@ -11998,7 +13759,7 @@ var GameVehicles = (() => {
|
|
|
11998
13759
|
// src/destructible/item.ts
|
|
11999
13760
|
function buildVehicleItem(kit, key, options = {}) {
|
|
12000
13761
|
const THREE = kit.THREE;
|
|
12001
|
-
const vehicle = kit.build(key, options.state ?? "intact");
|
|
13762
|
+
const vehicle = options.lod ? kit.build(key, options.state ?? "intact", void 0, { lod: true }) : kit.build(key, options.state ?? "intact");
|
|
12002
13763
|
const m = vehicle.meta;
|
|
12003
13764
|
const root = new THREE.Group();
|
|
12004
13765
|
root.name = key;
|