@adatechnology/game-vehicles-three 0.3.7 → 0.5.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/dist/chunk-GAO7ZJP7.js +1456 -0
- package/dist/chunk-GAO7ZJP7.js.map +1 -0
- package/dist/index.d.ts +31 -181
- package/dist/index.global.js +1725 -115
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +26 -1180
- package/dist/index.js.map +1 -1
- package/dist/testing-DmGv7Gpb.d.ts +219 -0
- package/dist/testing.d.ts +3 -0
- package/dist/testing.js +25 -0
- package/dist/testing.js.map +1 -0
- package/package.json +15 -9
package/dist/index.global.js
CHANGED
|
@@ -28,9 +28,12 @@ var GameVehiclesThree = (() => {
|
|
|
28
28
|
PAGE_SCENARIO_ALIASES: () => PAGE_SCENARIO_ALIASES,
|
|
29
29
|
PRUNE_BELOW: () => PRUNE_BELOW,
|
|
30
30
|
VEHICLES_AREA_ID: () => VEHICLES_AREA_ID,
|
|
31
|
+
VEHICLE_LOD_DISTANCE: () => VEHICLE_LOD_DISTANCE,
|
|
31
32
|
VEHICLE_SCENARIO_IDS: () => VEHICLE_SCENARIO_IDS,
|
|
32
33
|
classColor: () => classColor,
|
|
33
34
|
classify: () => classify,
|
|
35
|
+
createCabinSmoke: () => createCabinSmoke,
|
|
36
|
+
createVehicleLod: () => createVehicleLod,
|
|
34
37
|
createVehiclesTestModule: () => createVehiclesTestModule,
|
|
35
38
|
createVehiclesThree: () => createVehiclesThree,
|
|
36
39
|
defaultEffectIds: () => defaultEffectIds,
|
|
@@ -42,6 +45,7 @@ var GameVehiclesThree = (() => {
|
|
|
42
45
|
});
|
|
43
46
|
|
|
44
47
|
// ../game-contracts/dist/index.js
|
|
48
|
+
var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
|
|
45
49
|
var DEFAULT_IGNITION_RULES = {
|
|
46
50
|
fillCurve: [
|
|
47
51
|
[0, 0.6],
|
|
@@ -85,6 +89,38 @@ var GameVehiclesThree = (() => {
|
|
|
85
89
|
const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
|
|
86
90
|
return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
|
|
87
91
|
};
|
|
92
|
+
var DEFERRED_QUEUE_ID = {
|
|
93
|
+
DESTRUCTION_EVENTS: "destruction.events",
|
|
94
|
+
BREACH_PAINT: "floorplan.breach-paint",
|
|
95
|
+
AUDIO_VOICES: "audio.voices",
|
|
96
|
+
VEHICLE_CREASE: "vehicles.crease"
|
|
97
|
+
};
|
|
98
|
+
var DEFERRED_PRIORITY = {
|
|
99
|
+
[DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
|
|
100
|
+
[DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
|
|
101
|
+
[DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
|
|
102
|
+
[DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
|
|
103
|
+
};
|
|
104
|
+
var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
|
|
105
|
+
var warnedKeys = () => {
|
|
106
|
+
const holder = globalThis;
|
|
107
|
+
return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
|
|
108
|
+
};
|
|
109
|
+
var isProduction = () => {
|
|
110
|
+
try {
|
|
111
|
+
return process.env.NODE_ENV === "production";
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
|
|
117
|
+
var warnOnce = (key, message) => {
|
|
118
|
+
if (isQuiet() || isProduction()) return;
|
|
119
|
+
const seen = warnedKeys();
|
|
120
|
+
if (seen.has(key)) return;
|
|
121
|
+
seen.add(key);
|
|
122
|
+
console.warn(`[game-kit] ${message}`);
|
|
123
|
+
};
|
|
88
124
|
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
89
125
|
var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
90
126
|
var deepMerge = (base, patch) => {
|
|
@@ -106,10 +142,13 @@ var GameVehiclesThree = (() => {
|
|
|
106
142
|
for (const [id, patch] of Object.entries(patches ?? {})) {
|
|
107
143
|
const entry = entries.get(id);
|
|
108
144
|
if (entry) entries.set(id, deepMerge(entry, patch));
|
|
145
|
+
else warnOnce(`registry.patch.${id}`, `patch for "${id}" ignored: no entry has that id`);
|
|
109
146
|
}
|
|
110
147
|
};
|
|
111
148
|
var removeIds = (entries, ids) => {
|
|
112
|
-
for (const id of ids ?? [])
|
|
149
|
+
for (const id of ids ?? []) {
|
|
150
|
+
if (!entries.delete(id)) warnOnce(`registry.remove.${id}`, `remove of "${id}" ignored: no entry has that id`);
|
|
151
|
+
}
|
|
113
152
|
};
|
|
114
153
|
var KIT_ORIGIN_KEY = "kitOrigin";
|
|
115
154
|
function markKitOrigin(object2, origin) {
|
|
@@ -134,6 +173,43 @@ var GameVehiclesThree = (() => {
|
|
|
134
173
|
return lx > fp.minX - radius && lx < fp.maxX + radius && lz > fp.minZ - radius && lz < fp.maxZ + radius;
|
|
135
174
|
};
|
|
136
175
|
}
|
|
176
|
+
var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
|
|
177
|
+
var GAIT_IDS = ["walk", "run"];
|
|
178
|
+
var LANDING_IDS = ["light", "heavy"];
|
|
179
|
+
var BODY_SOUND_IDS = [
|
|
180
|
+
"jump",
|
|
181
|
+
"cloth_rustle",
|
|
182
|
+
"body_fall",
|
|
183
|
+
"fall_damage",
|
|
184
|
+
"player_hurt",
|
|
185
|
+
"player_breath_run",
|
|
186
|
+
"player_death",
|
|
187
|
+
"bite",
|
|
188
|
+
"grab",
|
|
189
|
+
"punch_swing",
|
|
190
|
+
"punch_hit_body",
|
|
191
|
+
"punch_hit_face",
|
|
192
|
+
"punch_hit_object",
|
|
193
|
+
"kick_swing",
|
|
194
|
+
"kick_hit",
|
|
195
|
+
"kick_hit_object",
|
|
196
|
+
"shove_two_hands",
|
|
197
|
+
"knife_swing",
|
|
198
|
+
"knife_stab",
|
|
199
|
+
"knife_slash",
|
|
200
|
+
"knife_hit_object",
|
|
201
|
+
"blunt_swing",
|
|
202
|
+
"blunt_hit",
|
|
203
|
+
"blunt_hit_object",
|
|
204
|
+
"axe_swing",
|
|
205
|
+
"axe_chop",
|
|
206
|
+
"axe_hit_object"
|
|
207
|
+
];
|
|
208
|
+
var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
|
|
209
|
+
var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
|
|
210
|
+
var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing2) => `land_${surface}_${landing2}`));
|
|
211
|
+
var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
212
|
+
var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
|
|
137
213
|
|
|
138
214
|
// ../game-vehicles-fleet/dist/index.js
|
|
139
215
|
var military = [
|
|
@@ -24323,7 +24399,7 @@ var GameVehiclesThree = (() => {
|
|
|
24323
24399
|
c2.cylinder(0.03, 0.04, 0.08, MT.chrome, sx + 2.35, fl + 0.99, -0.75);
|
|
24324
24400
|
c2.part("fuel_tank", trl("labels.oxygen_cylinder"), null, new V3(sx + 2.35, fl + 0.5, -0.75), 0.5);
|
|
24325
24401
|
c2.roundBox(0.04, 0.26, 0.34, 0.02, MT.screen, 0.1, 1.7, 0.5);
|
|
24326
|
-
const
|
|
24402
|
+
const lightPanel = c2.roundBox(
|
|
24327
24403
|
2.3,
|
|
24328
24404
|
0.02,
|
|
24329
24405
|
W - 0.3,
|
|
@@ -24333,7 +24409,7 @@ var GameVehiclesThree = (() => {
|
|
|
24333
24409
|
m.top - 0.06,
|
|
24334
24410
|
0
|
|
24335
24411
|
);
|
|
24336
|
-
|
|
24412
|
+
lightPanel.castShadow = false;
|
|
24337
24413
|
m.roofItems = beaconBar;
|
|
24338
24414
|
}
|
|
24339
24415
|
});
|
|
@@ -27892,15 +27968,17 @@ var GameVehiclesThree = (() => {
|
|
|
27892
27968
|
}
|
|
27893
27969
|
function createVehiclesThree(THREE, options) {
|
|
27894
27970
|
const kit = options.kit;
|
|
27971
|
+
const fleet = options.fleet ?? { colorFromSeed, fleetDims, paintPalettes, paletteOf };
|
|
27972
|
+
const { colorFromSeed: colorFromSeed2, fleetDims: fleetDims2, paintPalettes: paintPalettes2, paletteOf: paletteOf2 } = fleet;
|
|
27895
27973
|
const groupOf = options.groupOf || ((key) => kit.find(key)?.group === kit.config.defaultGroup ? "cars" : String(kit.find(key)?.group));
|
|
27896
27974
|
const militaryKeys = /* @__PURE__ */ new Set();
|
|
27897
|
-
const isMilitary = (key) => militaryKeys.has(key) ||
|
|
27975
|
+
const isMilitary = (key) => militaryKeys.has(key) || paletteOf2(groupOf(key)) === paintPalettes2.military;
|
|
27898
27976
|
const build = (key, o = {}) => {
|
|
27899
27977
|
const quality = o.quality === "lite" ? "lite" : "high";
|
|
27900
27978
|
const spec = kit.find(key);
|
|
27901
27979
|
if (!spec) throw new Error(`createVehiclesThree: unknown vehicle "${key}"`);
|
|
27902
27980
|
const seed = o.seed == null ? null : o.seed;
|
|
27903
|
-
const color2 = o.color || (seed != null ?
|
|
27981
|
+
const color2 = o.color || (seed != null ? colorFromSeed2(seed, isMilitary(key) ? paintPalettes2.military : paintPalettes2.civil) : null);
|
|
27904
27982
|
const c = kit.build(key, o.state || "intact", void 0, { lod: quality === "lite" });
|
|
27905
27983
|
c.root.updateMatrixWorld(true);
|
|
27906
27984
|
const primary = primaryPaint(c);
|
|
@@ -27966,7 +28044,7 @@ var GameVehiclesThree = (() => {
|
|
|
27966
28044
|
width: +size.x.toFixed(3),
|
|
27967
28045
|
height: +size.y.toFixed(3),
|
|
27968
28046
|
pruned,
|
|
27969
|
-
expected:
|
|
28047
|
+
expected: fleetDims2(key),
|
|
27970
28048
|
context: quality === "high" ? c : null,
|
|
27971
28049
|
dispose() {
|
|
27972
28050
|
group.traverse((m) => {
|
|
@@ -27991,7 +28069,7 @@ var GameVehiclesThree = (() => {
|
|
|
27991
28069
|
}
|
|
27992
28070
|
}
|
|
27993
28071
|
|
|
27994
|
-
// ../game-shapes/dist/chunk-
|
|
28072
|
+
// ../game-shapes/dist/chunk-IFXQIDSA.js
|
|
27995
28073
|
function canonicalJson(value) {
|
|
27996
28074
|
if (value === null) return "null";
|
|
27997
28075
|
if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
|
|
@@ -29286,10 +29364,10 @@ var GameVehiclesThree = (() => {
|
|
|
29286
29364
|
return cached;
|
|
29287
29365
|
}
|
|
29288
29366
|
stats.misses++;
|
|
29289
|
-
const
|
|
29290
|
-
templates.set(key,
|
|
29367
|
+
const built2 = make(node.kind, parameters);
|
|
29368
|
+
templates.set(key, built2);
|
|
29291
29369
|
if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
|
|
29292
|
-
return
|
|
29370
|
+
return built2;
|
|
29293
29371
|
}
|
|
29294
29372
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
|
|
29295
29373
|
var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
|
|
@@ -30467,7 +30545,7 @@ var GameVehiclesThree = (() => {
|
|
|
30467
30545
|
}
|
|
30468
30546
|
return ti.builtBlocks === total;
|
|
30469
30547
|
}
|
|
30470
|
-
function start(geo) {
|
|
30548
|
+
function start(geo, store = cache) {
|
|
30471
30549
|
const pos = geo.attributes.position;
|
|
30472
30550
|
const triangles = trianglesOf(geo);
|
|
30473
30551
|
const ti = {
|
|
@@ -30480,7 +30558,7 @@ var GameVehiclesThree = (() => {
|
|
|
30480
30558
|
blocks: new Float64Array(blockCount(triangles) * 6),
|
|
30481
30559
|
builtBlocks: 0
|
|
30482
30560
|
};
|
|
30483
|
-
|
|
30561
|
+
store.set(geo, ti);
|
|
30484
30562
|
return ti;
|
|
30485
30563
|
}
|
|
30486
30564
|
function triIndexOf(geo) {
|
|
@@ -30532,6 +30610,106 @@ var GameVehiclesThree = (() => {
|
|
|
30532
30610
|
}
|
|
30533
30611
|
return { box: box2, version: ti.positionVersion };
|
|
30534
30612
|
}
|
|
30613
|
+
var rayCache = /* @__PURE__ */ new WeakMap();
|
|
30614
|
+
function rayIndexOf(geo) {
|
|
30615
|
+
const shared2 = cache.get(geo);
|
|
30616
|
+
if (shared2 && isCurrent(geo, shared2) && shared2.builtBlocks === blockCount(shared2.triangles)) return shared2;
|
|
30617
|
+
let ti = rayCache.get(geo);
|
|
30618
|
+
if (!ti || !isCurrent(geo, ti)) ti = start(geo, rayCache);
|
|
30619
|
+
advance(geo, ti, Infinity);
|
|
30620
|
+
return ti;
|
|
30621
|
+
}
|
|
30622
|
+
var BLOCK_SLACK = 1e-6;
|
|
30623
|
+
var scratch = /* @__PURE__ */ new WeakMap();
|
|
30624
|
+
function scratchOf(THREE) {
|
|
30625
|
+
let s = scratch.get(THREE);
|
|
30626
|
+
if (!s) {
|
|
30627
|
+
s = {
|
|
30628
|
+
inverse: new THREE.Matrix4(),
|
|
30629
|
+
local: new THREE.Ray(),
|
|
30630
|
+
box: new THREE.Box3(),
|
|
30631
|
+
a: new THREE.Vector3(),
|
|
30632
|
+
b: new THREE.Vector3(),
|
|
30633
|
+
c: new THREE.Vector3(),
|
|
30634
|
+
point: new THREE.Vector3(),
|
|
30635
|
+
world: new THREE.Vector3(),
|
|
30636
|
+
sphere: new THREE.Sphere()
|
|
30637
|
+
};
|
|
30638
|
+
scratch.set(THREE, s);
|
|
30639
|
+
}
|
|
30640
|
+
return s;
|
|
30641
|
+
}
|
|
30642
|
+
function indexable(THREE, mesh2) {
|
|
30643
|
+
const geo = mesh2.geometry;
|
|
30644
|
+
if (mesh2.raycast !== THREE.Mesh.prototype.raycast) return false;
|
|
30645
|
+
if (!mesh2.isMesh || mesh2.isSkinnedMesh || mesh2.isInstancedMesh || mesh2.isBatchedMesh || !geo || !geo.isBufferGeometry) return false;
|
|
30646
|
+
if (!mesh2.material || Array.isArray(mesh2.material)) return false;
|
|
30647
|
+
if (geo.morphAttributes && geo.morphAttributes.position && geo.morphAttributes.position.length) return false;
|
|
30648
|
+
return supports(geo);
|
|
30649
|
+
}
|
|
30650
|
+
function raycastMesh(THREE, raycaster, mesh2, out) {
|
|
30651
|
+
const geo = mesh2.geometry;
|
|
30652
|
+
const s = scratchOf(THREE);
|
|
30653
|
+
if (geo.boundingSphere === null) geo.computeBoundingSphere();
|
|
30654
|
+
s.sphere.copy(geo.boundingSphere).applyMatrix4(mesh2.matrixWorld);
|
|
30655
|
+
if (!raycaster.ray.intersectsSphere(s.sphere)) return;
|
|
30656
|
+
const ti = rayIndexOf(geo);
|
|
30657
|
+
if (!ti) return;
|
|
30658
|
+
s.inverse.copy(mesh2.matrixWorld).invert();
|
|
30659
|
+
s.local.copy(raycaster.ray).applyMatrix4(s.inverse);
|
|
30660
|
+
const material = mesh2.material;
|
|
30661
|
+
const back = material.side === THREE.BackSide;
|
|
30662
|
+
const cull = material.side === THREE.FrontSide;
|
|
30663
|
+
const P = geo.attributes.position;
|
|
30664
|
+
const ix = geo.index;
|
|
30665
|
+
const count2 = ix ? ix.count : P.count;
|
|
30666
|
+
const first = Math.max(0, geo.drawRange.start);
|
|
30667
|
+
const last = Math.min(count2, geo.drawRange.start + geo.drawRange.count);
|
|
30668
|
+
const t0 = Math.ceil(first / 3);
|
|
30669
|
+
const t1 = Math.floor(last / 3);
|
|
30670
|
+
const B = ti.boxes;
|
|
30671
|
+
const K = ti.blocks;
|
|
30672
|
+
for (let blk = Math.floor(t0 / TRI_BLOCK); blk * TRI_BLOCK < t1; blk++) {
|
|
30673
|
+
const o = blk * 6;
|
|
30674
|
+
s.box.min.set(K[o] - BLOCK_SLACK, K[o + 1] - BLOCK_SLACK, K[o + 2] - BLOCK_SLACK);
|
|
30675
|
+
s.box.max.set(K[o + 3] + BLOCK_SLACK, K[o + 4] + BLOCK_SLACK, K[o + 5] + BLOCK_SLACK);
|
|
30676
|
+
if (!s.local.intersectsBox(s.box)) continue;
|
|
30677
|
+
const end = Math.min(t1, (blk + 1) * TRI_BLOCK);
|
|
30678
|
+
for (let t = Math.max(t0, blk * TRI_BLOCK); t < end; t++) {
|
|
30679
|
+
const j = t * 6;
|
|
30680
|
+
s.box.min.set(B[j] - BLOCK_SLACK, B[j + 1] - BLOCK_SLACK, B[j + 2] - BLOCK_SLACK);
|
|
30681
|
+
s.box.max.set(B[j + 3] + BLOCK_SLACK, B[j + 4] + BLOCK_SLACK, B[j + 5] + BLOCK_SLACK);
|
|
30682
|
+
if (!s.local.intersectsBox(s.box)) continue;
|
|
30683
|
+
const a = ix ? ix.getX(t * 3) : t * 3;
|
|
30684
|
+
const b = ix ? ix.getX(t * 3 + 1) : t * 3 + 1;
|
|
30685
|
+
const c = ix ? ix.getX(t * 3 + 2) : t * 3 + 2;
|
|
30686
|
+
s.a.fromBufferAttribute(P, a);
|
|
30687
|
+
s.b.fromBufferAttribute(P, b);
|
|
30688
|
+
s.c.fromBufferAttribute(P, c);
|
|
30689
|
+
const hit = back ? s.local.intersectTriangle(s.c, s.b, s.a, true, s.point) : s.local.intersectTriangle(s.a, s.b, s.c, cull, s.point);
|
|
30690
|
+
if (hit === null) continue;
|
|
30691
|
+
s.world.copy(s.point).applyMatrix4(mesh2.matrixWorld);
|
|
30692
|
+
const distance = raycaster.ray.origin.distanceTo(s.world);
|
|
30693
|
+
if (distance < raycaster.near || distance > raycaster.far) continue;
|
|
30694
|
+
const normal = new THREE.Vector3();
|
|
30695
|
+
THREE.Triangle.getNormal(s.a, s.b, s.c, normal);
|
|
30696
|
+
out.push({ distance, point: s.world.clone(), object: mesh2, face: { a, b, c, normal, materialIndex: 0 }, faceIndex: Math.floor(t) });
|
|
30697
|
+
}
|
|
30698
|
+
}
|
|
30699
|
+
}
|
|
30700
|
+
function collect2(THREE, raycaster, object2, recursive, out) {
|
|
30701
|
+
if (object2.layers.test(raycaster.layers)) {
|
|
30702
|
+
if (indexable(THREE, object2)) raycastMesh(THREE, raycaster, object2, out);
|
|
30703
|
+
else object2.raycast(raycaster, out);
|
|
30704
|
+
}
|
|
30705
|
+
if (recursive) for (const child of object2.children) collect2(THREE, raycaster, child, true, out);
|
|
30706
|
+
}
|
|
30707
|
+
function raycastIndexed(THREE, raycaster, objects, recursive = false) {
|
|
30708
|
+
const out = [];
|
|
30709
|
+
for (const object2 of objects) collect2(THREE, raycaster, object2, recursive, out);
|
|
30710
|
+
out.sort((x, y) => x.distance - y.distance);
|
|
30711
|
+
return out;
|
|
30712
|
+
}
|
|
30535
30713
|
function installGeometry(kit) {
|
|
30536
30714
|
const THREE = kit.THREE;
|
|
30537
30715
|
const V3 = THREE.Vector3;
|
|
@@ -31856,6 +32034,7 @@ var GameVehiclesThree = (() => {
|
|
|
31856
32034
|
}
|
|
31857
32035
|
kit.profX = profX;
|
|
31858
32036
|
const _sr = new THREE.Raycaster();
|
|
32037
|
+
_sr.layers.enableAll();
|
|
31859
32038
|
kit.surfaceAt = function(c, org, dir, o) {
|
|
31860
32039
|
o = o || {};
|
|
31861
32040
|
c.root.updateMatrixWorld(true);
|
|
@@ -31872,7 +32051,7 @@ var GameVehiclesThree = (() => {
|
|
|
31872
32051
|
_sr.set(p0.clone().applyMatrix4(Mw), d.clone().transformDirection(Mw));
|
|
31873
32052
|
_sr.near = 0;
|
|
31874
32053
|
_sr.far = o.max || 20;
|
|
31875
|
-
const h =
|
|
32054
|
+
const h = raycastIndexed(THREE, _sr, cands)[0];
|
|
31876
32055
|
if (!h) return null;
|
|
31877
32056
|
const n = h.face ? h.face.normal.clone().transformDirection(h.object.matrixWorld).transformDirection(inv) : d.clone().negate();
|
|
31878
32057
|
if (n.dot(d) > 0) n.negate();
|
|
@@ -31892,6 +32071,7 @@ var GameVehiclesThree = (() => {
|
|
|
31892
32071
|
const V3 = THREE.Vector3;
|
|
31893
32072
|
const I = kit._;
|
|
31894
32073
|
const { MATERIALS, crease, cutGeometry, pathShape, subdivide, tumble, uvProject } = I;
|
|
32074
|
+
I.paneSeals = /* @__PURE__ */ new WeakMap();
|
|
31895
32075
|
kit.addGlass = function(c, geo, label, type, o) {
|
|
31896
32076
|
o = o || {};
|
|
31897
32077
|
if (!geo.attributes.uv || o.reuv) o.aspect = uvProject(geo, o.uvAxes);
|
|
@@ -31958,7 +32138,7 @@ var GameVehiclesThree = (() => {
|
|
|
31958
32138
|
cy /= poly.length;
|
|
31959
32139
|
const base = I.resolveLabel(label ? label(i, polys.length) : { key: "window" });
|
|
31960
32140
|
const lab = { key: base.key, label: kit.t("parts.glass_side", { label: base.label, s }) };
|
|
31961
|
-
kit.addGlass(c, geo, lab, o.type || "tempered", {
|
|
32141
|
+
const pane = kit.addGlass(c, geo, lab, o.type || "tempered", {
|
|
31962
32142
|
mat: o.mat || (o.see === false ? MATERIALS.glassDark : MATERIALS.glass),
|
|
31963
32143
|
pos: new V3(cx, cy, s * I.halfAt(Wg, T, cy)),
|
|
31964
32144
|
outline: shp.getSpacedPoints(56),
|
|
@@ -31974,6 +32154,7 @@ var GameVehiclesThree = (() => {
|
|
|
31974
32154
|
pts.pop();
|
|
31975
32155
|
const seal = c.tube(pts, 0.011, MATERIALS.trim, { closed: true, seg: 72, rs: 5 });
|
|
31976
32156
|
seal.castShadow = false;
|
|
32157
|
+
I.paneSeals.set(pane, seal);
|
|
31977
32158
|
});
|
|
31978
32159
|
});
|
|
31979
32160
|
};
|
|
@@ -33670,6 +33851,42 @@ var GameVehiclesThree = (() => {
|
|
|
33670
33851
|
};
|
|
33671
33852
|
Object.assign(I, {});
|
|
33672
33853
|
}
|
|
33854
|
+
var ROUTED_PARTICLE_TYPES = { smoke: "particle_puff", dust: "particle_puff", fire: "particle_glow" };
|
|
33855
|
+
var PUFF_LOOKS = {
|
|
33856
|
+
smokewhite: [10133927, 0.42],
|
|
33857
|
+
smokeblack: [1776412, 0.62],
|
|
33858
|
+
smokegray: [5593436, 0.5],
|
|
33859
|
+
smokesteam: [11975873, 0.3],
|
|
33860
|
+
dust: [7235160, 0.34],
|
|
33861
|
+
fire: [16777215, 0.9]
|
|
33862
|
+
};
|
|
33863
|
+
var LIFE = { dust: 1.6, smoke: 2.8, fire: 0.7 };
|
|
33864
|
+
var FIRE_COLOR = 16753968;
|
|
33865
|
+
var SMOKE_START_SCALE = 0.18;
|
|
33866
|
+
function routedParticleRequest(input) {
|
|
33867
|
+
const { type } = input;
|
|
33868
|
+
const look = PUFF_LOOKS[type + (input.variant ?? "")] ?? PUFF_LOOKS[type] ?? PUFF_LOOKS.smokewhite;
|
|
33869
|
+
const life = input.life ?? LIFE[type] ?? 99;
|
|
33870
|
+
const size = (input.size ?? 1) * (input.grow ?? 1);
|
|
33871
|
+
const isFire = type === "fire";
|
|
33872
|
+
const growth = isFire ? 1 : (SMOKE_START_SCALE + 1.5 * (1 - Math.exp(-life * 1.1)) + life * 0.12) / SMOKE_START_SCALE;
|
|
33873
|
+
return {
|
|
33874
|
+
effect: ROUTED_PARTICLE_TYPES[type],
|
|
33875
|
+
point: input.position,
|
|
33876
|
+
values: {
|
|
33877
|
+
vx: input.velocity[0],
|
|
33878
|
+
vy: input.velocity[1],
|
|
33879
|
+
vz: input.velocity[2],
|
|
33880
|
+
life,
|
|
33881
|
+
size: isFire ? size * 0.85 : size * SMOKE_START_SCALE,
|
|
33882
|
+
grow: growth,
|
|
33883
|
+
color: input.color ?? (isFire ? FIRE_COLOR : look[0]),
|
|
33884
|
+
alpha: input.alpha ?? look[1],
|
|
33885
|
+
gravity: isFire ? 0 : type === "dust" ? 0 : -0.08,
|
|
33886
|
+
drag: type === "dust" ? 2.5 : isFire ? 1.5 : 0.9
|
|
33887
|
+
}
|
|
33888
|
+
};
|
|
33889
|
+
}
|
|
33673
33890
|
function installParticles(kit) {
|
|
33674
33891
|
const THREE = kit.THREE;
|
|
33675
33892
|
const V3 = THREE.Vector3;
|
|
@@ -33760,7 +33977,7 @@ var GameVehiclesThree = (() => {
|
|
|
33760
33977
|
});
|
|
33761
33978
|
}
|
|
33762
33979
|
const list2 = fire ? FIRE_TEXTURES : SMOKE_TEXTURES;
|
|
33763
|
-
const
|
|
33980
|
+
const build = () => new THREE.SpriteMaterial({
|
|
33764
33981
|
map: list2[Math.floor(Math.random() * list2.length)],
|
|
33765
33982
|
color: new THREE.Color(col),
|
|
33766
33983
|
transparent: true,
|
|
@@ -33768,6 +33985,7 @@ var GameVehiclesThree = (() => {
|
|
|
33768
33985
|
opacity: op,
|
|
33769
33986
|
blending: fire ? THREE.AdditiveBlending : THREE.NormalBlending
|
|
33770
33987
|
});
|
|
33988
|
+
const mat = kit.particleMaterials ? kit.particleMaterials.own(build) : build();
|
|
33771
33989
|
const dis = { value: 0 };
|
|
33772
33990
|
mat.userData.dis = dis;
|
|
33773
33991
|
mat.onBeforeCompile = function(sh) {
|
|
@@ -33795,11 +34013,37 @@ var GameVehiclesThree = (() => {
|
|
|
33795
34013
|
function killParticle(c, i) {
|
|
33796
34014
|
const P = c.particles[i];
|
|
33797
34015
|
c.effects.remove(P.m);
|
|
33798
|
-
if (P.own)
|
|
34016
|
+
if (P.own) {
|
|
34017
|
+
if (kit.particleMaterials) kit.particleMaterials.release(P.m.material);
|
|
34018
|
+
else P.m.material.dispose();
|
|
34019
|
+
}
|
|
33799
34020
|
c.particles.splice(i, 1);
|
|
33800
34021
|
}
|
|
34022
|
+
function routeParticle(c, effects, type, pos, vel, o) {
|
|
34023
|
+
c.effects.updateWorldMatrix(true, false);
|
|
34024
|
+
const at = pos.clone().applyMatrix4(c.effects.matrixWorld);
|
|
34025
|
+
const to = vel.clone().transformDirection(c.effects.matrixWorld).multiplyScalar(vel.length());
|
|
34026
|
+
const colour = o.col == null ? void 0 : new THREE.Color(o.col).getHex();
|
|
34027
|
+
effects.play(
|
|
34028
|
+
routedParticleRequest({
|
|
34029
|
+
type,
|
|
34030
|
+
position: [at.x, at.y, at.z],
|
|
34031
|
+
velocity: [to.x, to.y, to.z],
|
|
34032
|
+
...o.variant ? { variant: o.variant } : {},
|
|
34033
|
+
...colour !== void 0 ? { color: colour } : {},
|
|
34034
|
+
...o.op != null ? { alpha: o.op } : {},
|
|
34035
|
+
...o.s ? { size: o.s } : {},
|
|
34036
|
+
...o.life ? { life: o.life } : {},
|
|
34037
|
+
...o.grow ? { grow: o.grow } : {}
|
|
34038
|
+
})
|
|
34039
|
+
);
|
|
34040
|
+
return null;
|
|
34041
|
+
}
|
|
33801
34042
|
kit.spawn = function(c, type, pos, vel, o) {
|
|
33802
34043
|
o = o || {};
|
|
34044
|
+
const effects = ROUTED_PARTICLE_TYPES[type] ? kit.effects?.() : void 0;
|
|
34045
|
+
if (effects) return routeParticle(c, effects, type, pos, vel, o);
|
|
34046
|
+
if (ROUTED_PARTICLE_TYPES[type] && !kit.effects) warnOnce("game-vehicles.own-particles", "game-vehicles drew its smoke, fire and dust with sprites of its own because the host lent no effects world (options.effects); pass the host effects so the puffs share one budget");
|
|
33803
34047
|
if (c.particles.length >= PARTICLE_LIMIT) {
|
|
33804
34048
|
const idx = c.particles.findIndex(function(q) {
|
|
33805
34049
|
return TRANSIENT_TYPES[q.type];
|
|
@@ -34069,6 +34313,7 @@ var GameVehiclesThree = (() => {
|
|
|
34069
34313
|
};
|
|
34070
34314
|
kit.defineFluid("stain", { fresh: "#4a4a44", dry: "#26241f", wipeable: false });
|
|
34071
34315
|
const _fr = new THREE.Raycaster();
|
|
34316
|
+
_fr.layers.enableAll();
|
|
34072
34317
|
kit.splat = function(c, p, n, r, o) {
|
|
34073
34318
|
o = o || {};
|
|
34074
34319
|
n = n.clone().normalize();
|
|
@@ -34508,31 +34753,55 @@ var GameVehiclesThree = (() => {
|
|
|
34508
34753
|
};
|
|
34509
34754
|
Object.assign(I, { _fr, buildDrip, DOWNHILL, PN, puddleGeometry, rimGeometry, puddle, shapePuddle, updateFluids });
|
|
34510
34755
|
}
|
|
34756
|
+
function crackedGlassMaterial(THREE, texture) {
|
|
34757
|
+
return new THREE.MeshStandardMaterial({
|
|
34758
|
+
color: 16777215,
|
|
34759
|
+
map: texture,
|
|
34760
|
+
emissive: 6976118,
|
|
34761
|
+
emissiveMap: texture,
|
|
34762
|
+
transparent: true,
|
|
34763
|
+
roughness: 0.25,
|
|
34764
|
+
metalness: 0.1,
|
|
34765
|
+
side: THREE.DoubleSide,
|
|
34766
|
+
depthWrite: false
|
|
34767
|
+
});
|
|
34768
|
+
}
|
|
34511
34769
|
function installGlassDamage(kit) {
|
|
34512
34770
|
const THREE = kit.THREE;
|
|
34513
34771
|
const V3 = THREE.Vector3;
|
|
34514
34772
|
const PI = Math.PI;
|
|
34515
34773
|
const I = kit._;
|
|
34516
34774
|
const document2 = kit.document;
|
|
34517
|
-
const {
|
|
34518
|
-
function glassUV(msh, pLocal) {
|
|
34775
|
+
const { MATERIALS, crease, cutGeometry, mergeGeometries, subdivide, tumble } = I;
|
|
34776
|
+
function glassUV(c, msh, pLocal) {
|
|
34519
34777
|
const pos = msh.geometry.attributes.position;
|
|
34520
34778
|
const uv = msh.geometry.attributes.uv;
|
|
34521
34779
|
if (!uv) return [0.5, 0.5];
|
|
34522
|
-
|
|
34523
|
-
const q = pLocal.clone()
|
|
34524
|
-
|
|
34525
|
-
|
|
34526
|
-
const
|
|
34527
|
-
|
|
34528
|
-
|
|
34529
|
-
|
|
34530
|
-
|
|
34531
|
-
|
|
34532
|
-
|
|
34533
|
-
|
|
34780
|
+
c.root.updateMatrixWorld(true);
|
|
34781
|
+
const q = msh.worldToLocal(c.vehicle.localToWorld(pLocal.clone()));
|
|
34782
|
+
const idx = msh.geometry.index;
|
|
34783
|
+
const tri = new THREE.Triangle();
|
|
34784
|
+
const near = new V3();
|
|
34785
|
+
const bary = new V3();
|
|
34786
|
+
let best = Infinity;
|
|
34787
|
+
const out = [0.5, 0.5];
|
|
34788
|
+
const count2 = idx ? idx.count : pos.count;
|
|
34789
|
+
for (let f = 0; f + 2 < count2; f += 3) {
|
|
34790
|
+
const ia = idx ? idx.getX(f) : f;
|
|
34791
|
+
const ib = idx ? idx.getX(f + 1) : f + 1;
|
|
34792
|
+
const ic = idx ? idx.getX(f + 2) : f + 2;
|
|
34793
|
+
tri.a.fromBufferAttribute(pos, ia);
|
|
34794
|
+
tri.b.fromBufferAttribute(pos, ib);
|
|
34795
|
+
tri.c.fromBufferAttribute(pos, ic);
|
|
34796
|
+
tri.closestPointToPoint(q, near);
|
|
34797
|
+
const d = near.distanceToSquared(q);
|
|
34798
|
+
if (d >= best) continue;
|
|
34799
|
+
best = d;
|
|
34800
|
+
tri.getBarycoord(near, bary);
|
|
34801
|
+
out[0] = uv.getX(ia) * bary.x + uv.getX(ib) * bary.y + uv.getX(ic) * bary.z;
|
|
34802
|
+
out[1] = uv.getY(ia) * bary.x + uv.getY(ib) * bary.y + uv.getY(ic) * bary.z;
|
|
34534
34803
|
}
|
|
34535
|
-
return
|
|
34804
|
+
return out;
|
|
34536
34805
|
}
|
|
34537
34806
|
function drawWeb(x, cx, cy, s, ax, rnd) {
|
|
34538
34807
|
const n = 16 + Math.floor(rnd() * 8);
|
|
@@ -34609,19 +34878,9 @@ var GameVehiclesThree = (() => {
|
|
|
34609
34878
|
x0.fillStyle = "rgba(48,66,72,0.5)";
|
|
34610
34879
|
x0.fillRect(0, 0, 256, 256);
|
|
34611
34880
|
G2.tex = new THREE.CanvasTexture(G2.cv);
|
|
34612
|
-
msh.material =
|
|
34613
|
-
color: 16777215,
|
|
34614
|
-
map: G2.tex,
|
|
34615
|
-
emissive: 6976118,
|
|
34616
|
-
emissiveMap: G2.tex,
|
|
34617
|
-
transparent: true,
|
|
34618
|
-
roughness: 0.25,
|
|
34619
|
-
metalness: 0.1,
|
|
34620
|
-
side: DOUBLE_SIDE,
|
|
34621
|
-
depthWrite: false
|
|
34622
|
-
});
|
|
34881
|
+
msh.material = crackedGlassMaterial(THREE, G2.tex);
|
|
34623
34882
|
}
|
|
34624
|
-
const uv = glassUV(msh, p);
|
|
34883
|
+
const uv = glassUV(c, msh, p);
|
|
34625
34884
|
const x = G2.cv.getContext("2d");
|
|
34626
34885
|
drawWeb(x, uv[0] * 256, (1 - uv[1]) * 256, sc != null ? sc : 0.6 + force * 0.5, Math.max(0.4, G2.aspect || 1), c.rand);
|
|
34627
34886
|
G2.tex.needsUpdate = true;
|
|
@@ -34766,6 +35025,13 @@ var GameVehiclesThree = (() => {
|
|
|
34766
35025
|
}
|
|
34767
35026
|
const seatGeo = [];
|
|
34768
35027
|
const d = dir.clone().normalize();
|
|
35028
|
+
if (msh.parent && msh.parent !== c.vehicle) {
|
|
35029
|
+
c.root.updateMatrixWorld(true);
|
|
35030
|
+
const toVehicle = new THREE.Matrix4().copy(c.vehicle.matrixWorld).invert().multiply(msh.parent.matrixWorld);
|
|
35031
|
+
cubesAt.forEach(function(q) {
|
|
35032
|
+
q.applyMatrix4(toVehicle);
|
|
35033
|
+
});
|
|
35034
|
+
}
|
|
34769
35035
|
cubesAt.forEach(function(p, i) {
|
|
34770
35036
|
if (i % 3 === 0 && c.seatTops.length && !c.rollState && !c.flipped) {
|
|
34771
35037
|
let best = null;
|
|
@@ -34966,6 +35232,7 @@ var GameVehiclesThree = (() => {
|
|
|
34966
35232
|
polygonOffsetUnits: -2
|
|
34967
35233
|
});
|
|
34968
35234
|
const _ray = new THREE.Raycaster();
|
|
35235
|
+
_ray.layers.enableAll();
|
|
34969
35236
|
function findSurface(c, p, dir) {
|
|
34970
35237
|
c.root.updateMatrixWorld(true);
|
|
34971
35238
|
const Mw = c.vehicle.matrixWorld;
|
|
@@ -35047,6 +35314,7 @@ var GameVehiclesThree = (() => {
|
|
|
35047
35314
|
const v0 = new V3();
|
|
35048
35315
|
const w = new V3();
|
|
35049
35316
|
const sc = new V3();
|
|
35317
|
+
const local = new V3();
|
|
35050
35318
|
const cab = m.cabin && !c.flipped && !c.rollState ? m.cabin : null;
|
|
35051
35319
|
c.deform.forEach(function(msh) {
|
|
35052
35320
|
if (!msh.parent || !msh.visible) return;
|
|
@@ -35061,7 +35329,15 @@ var GameVehiclesThree = (() => {
|
|
|
35061
35329
|
irel.copy(rel).invert();
|
|
35062
35330
|
if (!g.userData.p0) g.userData.p0 = new Float32Array(pos.array);
|
|
35063
35331
|
const p0 = g.userData.p0;
|
|
35332
|
+
local.copy(p).applyMatrix4(irel);
|
|
35333
|
+
const reach = H.R / Math.max(1e-6, Math.min(sc.x, sc.y, sc.z));
|
|
35334
|
+
const reach2 = reach * reach;
|
|
35335
|
+
const arr = pos.array;
|
|
35064
35336
|
for (let i = 0; i < pos.count; i++) {
|
|
35337
|
+
const dx = arr[3 * i] - local.x;
|
|
35338
|
+
const dy = arr[3 * i + 1] - local.y;
|
|
35339
|
+
const dz = arr[3 * i + 2] - local.z;
|
|
35340
|
+
if (dx * dx + dy * dy + dz * dz >= reach2) continue;
|
|
35065
35341
|
v.fromBufferAttribute(pos, i).applyMatrix4(rel);
|
|
35066
35342
|
const a = dentField(v, H);
|
|
35067
35343
|
if (a <= 0) continue;
|
|
@@ -35077,10 +35353,15 @@ var GameVehiclesThree = (() => {
|
|
|
35077
35353
|
}
|
|
35078
35354
|
if (changed) {
|
|
35079
35355
|
pos.needsUpdate = true;
|
|
35080
|
-
if (g.userData.crease)
|
|
35356
|
+
if (g.userData.crease && c.smoothLater) c.smoothLater(msh);
|
|
35357
|
+
else if (g.userData.crease) crease(g, 42);
|
|
35081
35358
|
else g.computeVertexNormals();
|
|
35082
|
-
g.computeBoundingSphere();
|
|
35083
35359
|
g.computeBoundingBox();
|
|
35360
|
+
const sphere2 = g.boundingSphere ?? (g.boundingSphere = new THREE.Sphere());
|
|
35361
|
+
g.boundingBox.getCenter(sphere2.center);
|
|
35362
|
+
let far2 = 0;
|
|
35363
|
+
for (let i = 0; i < pos.count; i++) far2 = Math.max(far2, sphere2.center.distanceToSquared(v.fromBufferAttribute(pos, i)));
|
|
35364
|
+
sphere2.radius = Math.sqrt(far2);
|
|
35084
35365
|
}
|
|
35085
35366
|
});
|
|
35086
35367
|
const dset = new Set(c.deform);
|
|
@@ -35164,13 +35445,14 @@ var GameVehiclesThree = (() => {
|
|
|
35164
35445
|
kit.impact = impact;
|
|
35165
35446
|
kit.partEffects = {};
|
|
35166
35447
|
const _pr = new THREE.Raycaster();
|
|
35448
|
+
_pr.layers.enableAll();
|
|
35167
35449
|
function probe(c, o, d, far) {
|
|
35168
35450
|
c.root.updateMatrixWorld(true);
|
|
35169
35451
|
const Mw = c.vehicle.matrixWorld;
|
|
35170
35452
|
const inv = new THREE.Matrix4().copy(Mw).invert();
|
|
35171
35453
|
_pr.set(o.clone().applyMatrix4(Mw), d.clone().transformDirection(Mw));
|
|
35172
35454
|
_pr.far = far || 10;
|
|
35173
|
-
const hits = _pr
|
|
35455
|
+
const hits = raycastIndexed(THREE, _pr, [c.vehicle], true);
|
|
35174
35456
|
for (let i = 0; i < hits.length; i++) {
|
|
35175
35457
|
const h = hits[i];
|
|
35176
35458
|
const ob = h.object;
|
|
@@ -36524,6 +36806,7 @@ var GameVehiclesThree = (() => {
|
|
|
36524
36806
|
return hole;
|
|
36525
36807
|
}
|
|
36526
36808
|
const _zr = new THREE.Raycaster();
|
|
36809
|
+
_zr.layers.enableAll();
|
|
36527
36810
|
const PROXY_MIN_TRIANGLES = 500;
|
|
36528
36811
|
const STROKE_REACH = 0.6;
|
|
36529
36812
|
const _pv = new V3();
|
|
@@ -36758,6 +37041,7 @@ var GameVehiclesThree = (() => {
|
|
|
36758
37041
|
return out;
|
|
36759
37042
|
}
|
|
36760
37043
|
const _rt = new THREE.Raycaster();
|
|
37044
|
+
_rt.layers.enableAll();
|
|
36761
37045
|
function scratchPath(c, s, len, ang, cands, B) {
|
|
36762
37046
|
const n = s.n.clone().normalize();
|
|
36763
37047
|
const t = Math.abs(n.y) < 0.9 ? new V3().crossVectors(new V3(0, 1, 0), n) : new V3().crossVectors(new V3(1, 0, 0), n);
|
|
@@ -40017,6 +40301,8 @@ var GameVehiclesThree = (() => {
|
|
|
40017
40301
|
V3: THREE.Vector3,
|
|
40018
40302
|
PI: Math.PI,
|
|
40019
40303
|
document: doc,
|
|
40304
|
+
particleMaterials: opts.particleMaterials,
|
|
40305
|
+
effects: opts.effects,
|
|
40020
40306
|
strings,
|
|
40021
40307
|
t,
|
|
40022
40308
|
matchers: createMatchers(strings),
|
|
@@ -40272,7 +40558,9 @@ var GameVehiclesThree = (() => {
|
|
|
40272
40558
|
assignDeep(strings, resolveStrings(opts.locale));
|
|
40273
40559
|
return kit;
|
|
40274
40560
|
};
|
|
40275
|
-
const { THREE: _three, lod: _lod, locale: _locale, document: _document, plugins, ...config } = opts;
|
|
40561
|
+
const { THREE: _three, lod: _lod, locale: _locale, document: _document, particleMaterials: _particleMaterials, effects: _effects, plugins, ...config } = opts;
|
|
40562
|
+
void _particleMaterials;
|
|
40563
|
+
void _effects;
|
|
40276
40564
|
void _three;
|
|
40277
40565
|
void _lod;
|
|
40278
40566
|
void _locale;
|
|
@@ -40366,6 +40654,14 @@ var GameVehiclesThree = (() => {
|
|
|
40366
40654
|
center: [b.center[0], b.center[1] - b.size[1] / 2 + Math.min(0.14, b.size[1]) / 2 + 0.04, b.center[2]],
|
|
40367
40655
|
size: [b.size[0] * 0.5, Math.min(0.14, b.size[1]), b.size[2] * 0.95]
|
|
40368
40656
|
});
|
|
40657
|
+
function paneBox(c, pane, inverse) {
|
|
40658
|
+
pane.geometry.computeBoundingBox();
|
|
40659
|
+
const box2 = pane.geometry.boundingBox.clone().applyMatrix4(pane.matrixWorld);
|
|
40660
|
+
box2.applyMatrix4(inverse);
|
|
40661
|
+
const center = [(box2.min.x + box2.max.x) / 2, (box2.min.y + box2.max.y) / 2, (box2.min.z + box2.max.z) / 2];
|
|
40662
|
+
const size = [box2.max.x - box2.min.x, box2.max.y - box2.min.y, box2.max.z - box2.min.z];
|
|
40663
|
+
return { center, size };
|
|
40664
|
+
}
|
|
40369
40665
|
function buildVehicleModel(kit, c, options = {}) {
|
|
40370
40666
|
const kind = kindOf(c);
|
|
40371
40667
|
const m = c.meta;
|
|
@@ -40506,6 +40802,16 @@ var GameVehiclesThree = (() => {
|
|
|
40506
40802
|
tags: [role, ...tags]
|
|
40507
40803
|
});
|
|
40508
40804
|
}
|
|
40805
|
+
const claimed = new Set(zones.filter((z) => roleOfZone(z) === "glass").flatMap((z) => z.glass ?? []));
|
|
40806
|
+
c.root.updateMatrixWorld(true);
|
|
40807
|
+
const inverse = c.vehicle.matrixWorld.clone().invert();
|
|
40808
|
+
for (const pane of c.glass ?? []) {
|
|
40809
|
+
if (claimed.has(pane)) continue;
|
|
40810
|
+
const G2 = pane.userData.glass;
|
|
40811
|
+
const bb = paneBox(c, pane, inverse);
|
|
40812
|
+
const laminated = G2?.type === "laminated" || m.bullet;
|
|
40813
|
+
add2({ id: `pane_${G2?.key ?? "glass"}`, role: "glass", name: String(G2?.label ?? G2?.key ?? "glass"), material: "glass", box: bb, mass: 10 * areaOf(bb.size), hp: laminated ? 4e3 : 450, panes: [pane], visual: false, tags: ["glass"] });
|
|
40814
|
+
}
|
|
40509
40815
|
if (!hubZone) {
|
|
40510
40816
|
const h = Math.max(0.15, belt - clearance);
|
|
40511
40817
|
add2({
|
|
@@ -40839,6 +41145,76 @@ var GameVehiclesThree = (() => {
|
|
|
40839
41145
|
void has;
|
|
40840
41146
|
return { parts, joints, hub: hubId, hitch };
|
|
40841
41147
|
}
|
|
41148
|
+
var HOLE_DECAL_SCALE = 2.4;
|
|
41149
|
+
var DOOR_DEPTH = 0.07;
|
|
41150
|
+
var DOOR_EDGE_PIECES = 16;
|
|
41151
|
+
var DOOR_CARD_COLOR = "#3b3e42";
|
|
41152
|
+
var DOOR_LINER_COLOR = "#5a5e64";
|
|
41153
|
+
var DOOR_LINER_DARK = 0.15;
|
|
41154
|
+
var DOOR_STRIP_THIN = 0.05;
|
|
41155
|
+
var DOOR_STRIP_NEAR = 0.04;
|
|
41156
|
+
var DOOR_STRIP_OVER = 0.3;
|
|
41157
|
+
var DOOR_TOP_OVER_PANE = 0.03;
|
|
41158
|
+
var DOOR_STRIP_SUB = 0.02;
|
|
41159
|
+
var HOLE_TEXTURE_SIZE = 64;
|
|
41160
|
+
var DOOR_HANDLE_INSET = 0.12;
|
|
41161
|
+
var DOOR_HANDLE_MIN = 0.07;
|
|
41162
|
+
var DOOR_HANDLE_MAX = 0.24;
|
|
41163
|
+
var DOOR_HANDLE_THIN = 0.07;
|
|
41164
|
+
var DOOR_HANDLE_OUTSIDE = 0.1;
|
|
41165
|
+
var PANE_SEAT_OUT = 0.05;
|
|
41166
|
+
var PANE_SEAT_IN = -0.05;
|
|
41167
|
+
var PANE_SEAT_REACH = 0.14;
|
|
41168
|
+
var PANE_SEAT_SAMPLES = 24;
|
|
41169
|
+
var PANE_SEAT_LIFT = 4e-3;
|
|
41170
|
+
var PANE_FRAME_NEAR = 0.07;
|
|
41171
|
+
var DRAWN_MIRROR_SIZE = 0.3;
|
|
41172
|
+
var DRAWN_MIRROR_REACH = 0.4;
|
|
41173
|
+
var DRAWN_MIRROR_MOUNT = 0.2;
|
|
41174
|
+
var MIRROR_GLASS_FIT = 0.76;
|
|
41175
|
+
var MIRROR_GLASS_THIN = 0.04;
|
|
41176
|
+
var MIRROR_GLASS_MIN = 0.05;
|
|
41177
|
+
var MIRROR_GLASS_COLOR = "#59636a";
|
|
41178
|
+
var HOLE_RIM = { steel: "#b7bcc0", aluminium: "#d3d6d8", plastic: "#d9d6cc", rubber: "#141414", glass: "#eef3f5" };
|
|
41179
|
+
var holeMaterials = /* @__PURE__ */ new WeakMap();
|
|
41180
|
+
var holeDisc = null;
|
|
41181
|
+
function holeDiscGeometry(THREE) {
|
|
41182
|
+
holeDisc = holeDisc ?? new THREE.CircleGeometry(1, 12);
|
|
41183
|
+
return holeDisc;
|
|
41184
|
+
}
|
|
41185
|
+
function holeMaterial(kit, material) {
|
|
41186
|
+
const key = kit;
|
|
41187
|
+
const cache2 = holeMaterials.get(key) ?? /* @__PURE__ */ new Map();
|
|
41188
|
+
holeMaterials.set(key, cache2);
|
|
41189
|
+
const known = cache2.get(material);
|
|
41190
|
+
if (known) return known;
|
|
41191
|
+
const I = kit._;
|
|
41192
|
+
const THREE = kit.THREE;
|
|
41193
|
+
const canvas = kit.document?.createElement?.("canvas");
|
|
41194
|
+
const x = canvas?.getContext?.("2d");
|
|
41195
|
+
let made = I.MATERIALS.dark;
|
|
41196
|
+
if (x) {
|
|
41197
|
+
canvas.width = canvas.height = HOLE_TEXTURE_SIZE;
|
|
41198
|
+
const mid = HOLE_TEXTURE_SIZE / 2;
|
|
41199
|
+
x.fillStyle = HOLE_RIM[material] ?? HOLE_RIM.steel;
|
|
41200
|
+
x.beginPath();
|
|
41201
|
+
for (let i = 0; i < 18; i++) {
|
|
41202
|
+
const a = i / 18 * Math.PI * 2;
|
|
41203
|
+
const r = mid * (i % 2 ? 0.98 : 0.76);
|
|
41204
|
+
x.lineTo(mid + Math.cos(a) * r, mid + Math.sin(a) * r);
|
|
41205
|
+
}
|
|
41206
|
+
x.closePath();
|
|
41207
|
+
x.fill();
|
|
41208
|
+
x.fillStyle = "#050505";
|
|
41209
|
+
x.beginPath();
|
|
41210
|
+
x.arc(mid, mid, mid * 0.42, 0, Math.PI * 2);
|
|
41211
|
+
x.fill();
|
|
41212
|
+
const map = new THREE.CanvasTexture(canvas);
|
|
41213
|
+
made = new THREE.MeshStandardMaterial({ map, transparent: true, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -2, roughness: 0.55, metalness: material === "steel" || material === "aluminium" ? 0.6 : 0 });
|
|
41214
|
+
}
|
|
41215
|
+
cache2.set(material, made);
|
|
41216
|
+
return made;
|
|
41217
|
+
}
|
|
40842
41218
|
var VehicleVisuals = class {
|
|
40843
41219
|
constructor(kit, c, model) {
|
|
40844
41220
|
__publicField(this, "kit");
|
|
@@ -40852,11 +41228,17 @@ var GameVehiclesThree = (() => {
|
|
|
40852
41228
|
__publicField(this, "gone", /* @__PURE__ */ new Set());
|
|
40853
41229
|
__publicField(this, "syncPlates", /* @__PURE__ */ new Map());
|
|
40854
41230
|
__publicField(this, "skins", /* @__PURE__ */ new Map());
|
|
41231
|
+
__publicField(this, "doorTops", /* @__PURE__ */ new Map());
|
|
40855
41232
|
__publicField(this, "lightsOn", true);
|
|
40856
41233
|
__publicField(this, "warming");
|
|
40857
41234
|
__publicField(this, "tornThisFrame", false);
|
|
41235
|
+
__publicField(this, "queued", false);
|
|
41236
|
+
__publicField(this, "holdArmed", false);
|
|
41237
|
+
__publicField(this, "holdFrame", -1);
|
|
40858
41238
|
__publicField(this, "smoothing", /* @__PURE__ */ new Map());
|
|
40859
41239
|
__publicField(this, "hood");
|
|
41240
|
+
;
|
|
41241
|
+
c.smoothLater = (mesh2) => this.smoothLater(mesh2);
|
|
40860
41242
|
this.kit = kit;
|
|
40861
41243
|
this.c = c;
|
|
40862
41244
|
this.model = model;
|
|
@@ -40865,6 +41247,8 @@ var GameVehiclesThree = (() => {
|
|
|
40865
41247
|
for (const p of model.parts) this.byId.set(p.id, p);
|
|
40866
41248
|
c.root.updateMatrixWorld(true);
|
|
40867
41249
|
this.hood = this.nameHood();
|
|
41250
|
+
this.seatPanes();
|
|
41251
|
+
this.seatDoorHandles();
|
|
40868
41252
|
this.warming = c.deform.filter((msh) => msh.geometry && msh.geometry.attributes.position && msh.geometry.attributes.position.count >= 1500);
|
|
40869
41253
|
if (!this.warming.length) this.warming = null;
|
|
40870
41254
|
for (const p of model.parts) {
|
|
@@ -40885,6 +41269,7 @@ var GameVehiclesThree = (() => {
|
|
|
40885
41269
|
const id = z ? z.id : this.model.parts.find((p) => p.role === "hood")?.id;
|
|
40886
41270
|
if (!id) return null;
|
|
40887
41271
|
c.hood.piv.name = id;
|
|
41272
|
+
c.hood.piv.userData.plateOf = id;
|
|
40888
41273
|
return { id, open: c.hood.open, sign: c.hood.sign || 1 };
|
|
40889
41274
|
}
|
|
40890
41275
|
mirrorObjects(p) {
|
|
@@ -40972,6 +41357,7 @@ var GameVehiclesThree = (() => {
|
|
|
40972
41357
|
recess.userData.noFollow = true;
|
|
40973
41358
|
const piv = new THREE.Group();
|
|
40974
41359
|
piv.name = p.id;
|
|
41360
|
+
piv.userData.plateOf = p.id;
|
|
40975
41361
|
piv.position.set(hx, cy, sk + s * 0.016);
|
|
40976
41362
|
piv.userData.rigid = true;
|
|
40977
41363
|
this.c.vehicle.add(piv);
|
|
@@ -40988,6 +41374,7 @@ var GameVehiclesThree = (() => {
|
|
|
40988
41374
|
recess.userData.noFollow = true;
|
|
40989
41375
|
const piv = new THREE.Group();
|
|
40990
41376
|
piv.name = p.id;
|
|
41377
|
+
piv.userData.plateOf = p.id;
|
|
40991
41378
|
piv.position.set(sk - 0.016, B.y1 - 0.05, 0);
|
|
40992
41379
|
piv.userData.rigid = true;
|
|
40993
41380
|
this.c.vehicle.add(piv);
|
|
@@ -41051,10 +41438,19 @@ var GameVehiclesThree = (() => {
|
|
|
41051
41438
|
try {
|
|
41052
41439
|
if (p.role === "door") {
|
|
41053
41440
|
const dirty = /* @__PURE__ */ new Set();
|
|
41054
|
-
const
|
|
41055
|
-
const
|
|
41441
|
+
const glassBox = new this.THREE.Box3();
|
|
41442
|
+
for (const g of I.windowsOver(this.c, z, inv)) glassBox.union(I.localBox(this.c, g, inv));
|
|
41443
|
+
const top = glassBox.isEmpty() ? B.y1 - 0.02 : Math.max(B.y1 - 0.02, glassBox.min.y + DOOR_TOP_OVER_PANE);
|
|
41444
|
+
this.doorTops.set(id, top);
|
|
41445
|
+
const rect = [B.x0 + 0.03, B.x1 - 0.03, B.y0 + Math.min(0.08, (B.y1 - B.y0) * 0.1), top];
|
|
41446
|
+
const pcs = I.cutPanel(this.c, z, rect, true, dirty, { anyN: true }).map((x) => x.mesh).filter((m) => !!m);
|
|
41056
41447
|
this.mend(dirty);
|
|
41057
|
-
if (pcs.length)
|
|
41448
|
+
if (pcs.length) {
|
|
41449
|
+
const panes = I.windowsOver(this.c, z, inv);
|
|
41450
|
+
const seals = panes.map((g) => I.paneSeals.get(g)).filter((s) => !!s);
|
|
41451
|
+
const mirrors = this.mirrorsOn(z);
|
|
41452
|
+
objs = [...pcs, ...this.doorCards(pcs, z), ...I.followersIn(this.c, z, inv), ...panes, ...seals, ...mirrors];
|
|
41453
|
+
}
|
|
41058
41454
|
} else {
|
|
41059
41455
|
const pcs = this.cutZone(z);
|
|
41060
41456
|
if (pcs.length) objs = [...pcs, ...I.followersIn(this.c, z, inv)];
|
|
@@ -41072,10 +41468,124 @@ var GameVehiclesThree = (() => {
|
|
|
41072
41468
|
const di = deform.indexOf(o);
|
|
41073
41469
|
if (di >= 0) deform.splice(di, 1);
|
|
41074
41470
|
}
|
|
41471
|
+
if (p.role === "door" && objs.length > 0) {
|
|
41472
|
+
this.doorEdges(piv, z, this.doorTops.get(id) ?? B.y1 - 0.02);
|
|
41473
|
+
this.liftDoorTrim(objs, z);
|
|
41474
|
+
this.cutStripsAcross(piv, z, inv);
|
|
41475
|
+
}
|
|
41075
41476
|
piv.quaternion.copy(q);
|
|
41076
41477
|
this.c.root.updateMatrixWorld(true);
|
|
41077
41478
|
return objs.length > 0;
|
|
41078
41479
|
}
|
|
41480
|
+
/** A door has edges: a band that follows the skin all round closes it to its card and hides where the cut followed the triangles of the shell; the same band, in the same paint as on a real car, lines the opening. */
|
|
41481
|
+
doorEdges(piv, z, y1) {
|
|
41482
|
+
const THREE = this.THREE;
|
|
41483
|
+
const B = z.box;
|
|
41484
|
+
const side2 = z.side < 0 ? -1 : 1;
|
|
41485
|
+
const flat = piv.position.z - side2 * 0.016;
|
|
41486
|
+
const x0 = B.x0 + 0.03;
|
|
41487
|
+
const x1 = B.x1 - 0.03;
|
|
41488
|
+
const y0 = B.y0 + Math.min(0.08, (B.y1 - B.y0) * 0.1);
|
|
41489
|
+
const loop = [];
|
|
41490
|
+
const run = (ax, ay, bx, by) => {
|
|
41491
|
+
for (let i = 0; i < DOOR_EDGE_PIECES; i++) loop.push([ax + (bx - ax) * i / DOOR_EDGE_PIECES, ay + (by - ay) * i / DOOR_EDGE_PIECES]);
|
|
41492
|
+
};
|
|
41493
|
+
run(x0, y0, x1, y0);
|
|
41494
|
+
run(x1, y0, x1, y1);
|
|
41495
|
+
run(x1, y1, x0, y1);
|
|
41496
|
+
run(x0, y1, x0, y0);
|
|
41497
|
+
const heights = loop.map(([x, y]) => this.surface("z", side2, B, [[x, y]]) ?? flat);
|
|
41498
|
+
const band = (lift) => {
|
|
41499
|
+
const position = [];
|
|
41500
|
+
const index = [];
|
|
41501
|
+
loop.forEach(([x, y], i) => {
|
|
41502
|
+
position.push(x, y, heights[i] + side2 * lift, x, y, heights[i] + side2 * (lift - DOOR_DEPTH));
|
|
41503
|
+
const a = i * 2;
|
|
41504
|
+
const b = (i + 1) % loop.length * 2;
|
|
41505
|
+
index.push(a, a + 1, b, b, a + 1, b + 1);
|
|
41506
|
+
});
|
|
41507
|
+
const g = new THREE.BufferGeometry();
|
|
41508
|
+
g.setAttribute("position", new THREE.Float32BufferAttribute(position, 3));
|
|
41509
|
+
g.setIndex(index);
|
|
41510
|
+
g.computeVertexNormals();
|
|
41511
|
+
return g;
|
|
41512
|
+
};
|
|
41513
|
+
const paint = this.paint().clone();
|
|
41514
|
+
paint.side = THREE.DoubleSide;
|
|
41515
|
+
const edges = new THREE.Mesh(band(2e-3), paint);
|
|
41516
|
+
const lining = new THREE.Mesh(band(2e-3), paint);
|
|
41517
|
+
for (const m of [edges, lining]) {
|
|
41518
|
+
m.userData.noFollow = true;
|
|
41519
|
+
m.castShadow = false;
|
|
41520
|
+
this.c.vehicle.add(m);
|
|
41521
|
+
}
|
|
41522
|
+
piv.attach(edges);
|
|
41523
|
+
}
|
|
41524
|
+
/** A front door carries the mirror drawn at its corner. */
|
|
41525
|
+
mirrorsOn(z) {
|
|
41526
|
+
if (!z.mirror) return [];
|
|
41527
|
+
const own = (z.mirrors ?? []).filter((o) => o.parent);
|
|
41528
|
+
if (own.length) return own;
|
|
41529
|
+
const part = this.model.parts.find((q) => q.role === "mirror" && Math.sign(q.box.center[2]) === Math.sign(z.side));
|
|
41530
|
+
return part ? (this.extra.get(part.id) ?? []).filter((o) => o.parent) : [];
|
|
41531
|
+
}
|
|
41532
|
+
/** The panel drawn inside the door is lit like the rest of a cabin, not a black slab from end to end. */
|
|
41533
|
+
liftDoorTrim(objs, z) {
|
|
41534
|
+
const card = this.I.makeMaterial(DOOR_LINER_COLOR, { roughness: 0.85 });
|
|
41535
|
+
const inv = this.I.worldInverseOf(this.c);
|
|
41536
|
+
for (const o of objs) {
|
|
41537
|
+
if (!o.isMesh || o.userData.glass) continue;
|
|
41538
|
+
const box2 = this.I.localBox(this.c, o, inv);
|
|
41539
|
+
const size = box2.getSize(new this.THREE.Vector3());
|
|
41540
|
+
if (size.x > (z.box.x1 - z.box.x0) * 0.4 && size.y > (z.box.y1 - z.box.y0) * 0.3 && size.z < DOOR_DEPTH * 1.5 && o.material?.color?.getHSL({ h: 0, s: 0, l: 0 }).l < DOOR_LINER_DARK) o.material = card;
|
|
41541
|
+
}
|
|
41542
|
+
}
|
|
41543
|
+
/** A long strip drawn along the side of the car (the chrome of the belt): the part over the door goes with the door, and nothing stays across the opening. */
|
|
41544
|
+
cutStripsAcross(piv, z, inv) {
|
|
41545
|
+
const THREE = this.THREE;
|
|
41546
|
+
const B = z.box;
|
|
41547
|
+
const x0 = B.x0 + 0.03;
|
|
41548
|
+
const x1 = B.x1 - 0.03;
|
|
41549
|
+
const y0 = B.y0 - DOOR_STRIP_NEAR;
|
|
41550
|
+
const y1 = B.y1 + DOOR_STRIP_NEAR;
|
|
41551
|
+
const deform = this.c.deform ?? [];
|
|
41552
|
+
for (const o of [...this.c.vehicle.children]) {
|
|
41553
|
+
if (!o.isMesh || o === piv || deform.includes(o) || o.userData.glass || o.userData.decal || !o.visible) continue;
|
|
41554
|
+
const box2 = this.I.localBox(this.c, o, inv);
|
|
41555
|
+
const size = box2.getSize(new THREE.Vector3());
|
|
41556
|
+
const mid = box2.getCenter(new THREE.Vector3());
|
|
41557
|
+
if (Math.sign(mid.z) !== Math.sign(z.side) || size.z > DOOR_STRIP_THIN || size.y > DOOR_STRIP_THIN || Math.min(box2.max.x, x1) - Math.max(box2.min.x, x0) < (x1 - x0) * DOOR_STRIP_OVER || mid.y < y0 || mid.y > y1) continue;
|
|
41558
|
+
const toVehicle = new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld);
|
|
41559
|
+
const v = new THREE.Vector3();
|
|
41560
|
+
const sdf = (x, y, zz) => {
|
|
41561
|
+
v.set(x, y, zz).applyMatrix4(toVehicle);
|
|
41562
|
+
return Math.max(x0 - v.x, v.x - x1);
|
|
41563
|
+
};
|
|
41564
|
+
this.kit.cutMesh(o, [{ sdf }], 42, { sub: DOOR_STRIP_SUB });
|
|
41565
|
+
const from = Math.max(box2.min.x, x0);
|
|
41566
|
+
const to = Math.min(box2.max.x, x1);
|
|
41567
|
+
const piece = this.box(to - from, size.y, size.z, o.material, (from + to) / 2, mid.y, mid.z);
|
|
41568
|
+
piece.userData.noFollow = true;
|
|
41569
|
+
piv.attach(piece);
|
|
41570
|
+
}
|
|
41571
|
+
}
|
|
41572
|
+
/** The inside of a door: its skin brought in by the depth of the door, in the trim's dark plastic, so the open door is a door and not one sheet of paint. */
|
|
41573
|
+
doorCards(skins, z) {
|
|
41574
|
+
const cardMaterial = this.I.makeMaterial(DOOR_CARD_COLOR, { roughness: 0.8 });
|
|
41575
|
+
return skins.map((skin) => {
|
|
41576
|
+
const card = new this.THREE.Mesh(skin.geometry, cardMaterial);
|
|
41577
|
+
card.position.copy(skin.position);
|
|
41578
|
+
card.quaternion.copy(skin.quaternion);
|
|
41579
|
+
card.scale.copy(skin.scale);
|
|
41580
|
+
const side2 = z.side < 0 ? -1 : 1;
|
|
41581
|
+
const inward = -side2 * DOOR_DEPTH;
|
|
41582
|
+
card.position.z += inward;
|
|
41583
|
+
card.userData.noFollow = true;
|
|
41584
|
+
card.castShadow = false;
|
|
41585
|
+
this.c.vehicle.add(card);
|
|
41586
|
+
return card;
|
|
41587
|
+
});
|
|
41588
|
+
}
|
|
41079
41589
|
mend(dirty) {
|
|
41080
41590
|
dirty.forEach((msh) => {
|
|
41081
41591
|
const g = msh.geometry;
|
|
@@ -41085,7 +41595,191 @@ var GameVehiclesThree = (() => {
|
|
|
41085
41595
|
g.computeBoundingBox();
|
|
41086
41596
|
});
|
|
41087
41597
|
}
|
|
41598
|
+
/**
|
|
41599
|
+
* A windshield or rear window is drawn as a flat quad, and the body under its edge is curved: the pane and its frame stand off the shell or rise past the roof. Each one
|
|
41600
|
+
* comes to the shell along its own normal, by how far the shell is just outside each of its vertices.
|
|
41601
|
+
*/
|
|
41602
|
+
seatPanes() {
|
|
41603
|
+
const THREE = this.THREE;
|
|
41604
|
+
const c = this.c;
|
|
41605
|
+
c.root.updateMatrixWorld(true);
|
|
41606
|
+
const shell = c.deform.filter((o) => o.isMesh && !o.userData.glass && c.paints?.has(o.material));
|
|
41607
|
+
if (!shell.length) return;
|
|
41608
|
+
const ray = new THREE.Raycaster();
|
|
41609
|
+
ray.layers.enableAll();
|
|
41610
|
+
for (const pane of c.glass) {
|
|
41611
|
+
if (pane.userData.glass?.outline) continue;
|
|
41612
|
+
const seat = this.paneSeat(pane, shell, ray);
|
|
41613
|
+
if (!seat) continue;
|
|
41614
|
+
const near = c.vehicle.children.filter((o) => o.isMesh && o !== pane && !o.userData.glass && !shell.includes(o) && this.onPane(o, pane, seat));
|
|
41615
|
+
for (const m of [pane, ...near]) this.movePane(m, seat);
|
|
41616
|
+
const past = this.shellPast(pane, shell, ray, seat.normal);
|
|
41617
|
+
if (past > 0) for (const m of [pane, ...near]) this.movePane(m, { normal: seat.normal, samples: [{ at: seat.samples[0].at, by: past }] });
|
|
41618
|
+
}
|
|
41619
|
+
}
|
|
41620
|
+
/** The outward normal of a pane and how far the shell is at each sampled point (world). */
|
|
41621
|
+
paneSeat(pane, shell, ray) {
|
|
41622
|
+
const THREE = this.THREE;
|
|
41623
|
+
const pos = pane.geometry.attributes.position;
|
|
41624
|
+
const box2 = new THREE.Box3().setFromObject(pane);
|
|
41625
|
+
const centre = box2.getCenter(new THREE.Vector3());
|
|
41626
|
+
const vehicleCentre = new THREE.Box3().setFromObject(this.c.root).getCenter(new THREE.Vector3());
|
|
41627
|
+
const tri = new THREE.Triangle();
|
|
41628
|
+
const normal = new THREE.Vector3();
|
|
41629
|
+
let largest = 0;
|
|
41630
|
+
const idx = pane.geometry.index;
|
|
41631
|
+
const count2 = idx ? idx.count : pos.count;
|
|
41632
|
+
for (let f = 0; f + 2 < count2; f += 3) {
|
|
41633
|
+
tri.a.fromBufferAttribute(pos, idx ? idx.getX(f) : f);
|
|
41634
|
+
tri.b.fromBufferAttribute(pos, idx ? idx.getX(f + 1) : f + 1);
|
|
41635
|
+
tri.c.fromBufferAttribute(pos, idx ? idx.getX(f + 2) : f + 2);
|
|
41636
|
+
if (tri.getArea() > largest) {
|
|
41637
|
+
largest = tri.getArea();
|
|
41638
|
+
tri.getNormal(normal);
|
|
41639
|
+
}
|
|
41640
|
+
}
|
|
41641
|
+
normal.transformDirection(pane.matrixWorld);
|
|
41642
|
+
if (normal.dot(centre.clone().sub(vehicleCentre)) < 0) normal.negate();
|
|
41643
|
+
const samples = [];
|
|
41644
|
+
const seen = /* @__PURE__ */ new Set();
|
|
41645
|
+
const v = new THREE.Vector3();
|
|
41646
|
+
const step = Math.max(1, Math.floor(pos.count / PANE_SEAT_SAMPLES));
|
|
41647
|
+
for (let i = 0; i < pos.count; i += step) {
|
|
41648
|
+
v.fromBufferAttribute(pos, i).applyMatrix4(pane.matrixWorld);
|
|
41649
|
+
const key = `${Math.round(v.x * 100)},${Math.round(v.y * 100)},${Math.round(v.z * 100)}`;
|
|
41650
|
+
if (seen.has(key)) continue;
|
|
41651
|
+
seen.add(key);
|
|
41652
|
+
const away = v.clone().sub(centre);
|
|
41653
|
+
away.addScaledVector(normal, -away.dot(normal)).normalize();
|
|
41654
|
+
let by = -Infinity;
|
|
41655
|
+
for (const off of [PANE_SEAT_OUT, PANE_SEAT_IN, PANE_SEAT_IN * 3]) {
|
|
41656
|
+
const at = v.clone().addScaledVector(away, off);
|
|
41657
|
+
ray.set(at.addScaledVector(normal, PANE_SEAT_REACH), normal.clone().negate());
|
|
41658
|
+
ray.far = PANE_SEAT_REACH * 2;
|
|
41659
|
+
const hit = raycastIndexed(THREE, ray, shell)[0];
|
|
41660
|
+
if (hit) by = Math.max(by, PANE_SEAT_REACH - hit.distance);
|
|
41661
|
+
}
|
|
41662
|
+
if (by > -Infinity) samples.push({ at: v.clone(), by });
|
|
41663
|
+
}
|
|
41664
|
+
return samples.length >= 3 ? { normal, samples } : null;
|
|
41665
|
+
}
|
|
41666
|
+
/** How far the shell still stands past a seated pane, over its triangles. */
|
|
41667
|
+
shellPast(pane, shell, ray, normal) {
|
|
41668
|
+
const THREE = this.THREE;
|
|
41669
|
+
const pos = pane.geometry.attributes.position;
|
|
41670
|
+
const idx = pane.geometry.index;
|
|
41671
|
+
const count2 = idx ? idx.count : pos.count;
|
|
41672
|
+
const tri = new THREE.Triangle();
|
|
41673
|
+
const step = Math.max(1, Math.floor(count2 / 3 / PANE_SEAT_SAMPLES)) * 3;
|
|
41674
|
+
let past = 0;
|
|
41675
|
+
for (let f = 0; f + 2 < count2; f += step) {
|
|
41676
|
+
tri.a.fromBufferAttribute(pos, idx ? idx.getX(f) : f);
|
|
41677
|
+
tri.b.fromBufferAttribute(pos, idx ? idx.getX(f + 1) : f + 1);
|
|
41678
|
+
tri.c.fromBufferAttribute(pos, idx ? idx.getX(f + 2) : f + 2);
|
|
41679
|
+
const mid = tri.getMidpoint(new THREE.Vector3()).applyMatrix4(pane.matrixWorld);
|
|
41680
|
+
ray.set(mid.addScaledVector(normal, PANE_SEAT_REACH), normal.clone().negate());
|
|
41681
|
+
ray.far = PANE_SEAT_REACH;
|
|
41682
|
+
const hit = raycastIndexed(THREE, ray, shell)[0];
|
|
41683
|
+
if (hit) past = Math.max(past, PANE_SEAT_REACH - hit.distance + PANE_SEAT_LIFT);
|
|
41684
|
+
}
|
|
41685
|
+
return past;
|
|
41686
|
+
}
|
|
41687
|
+
/** A frame or a seal drawn on the pane: small, and close to its plane. */
|
|
41688
|
+
onPane(o, pane, seat) {
|
|
41689
|
+
const THREE = this.THREE;
|
|
41690
|
+
const box2 = new THREE.Box3().setFromObject(o);
|
|
41691
|
+
if (box2.isEmpty() || !new THREE.Box3().setFromObject(pane).expandByScalar(PANE_FRAME_NEAR).containsBox(box2)) return false;
|
|
41692
|
+
const at = box2.getCenter(new THREE.Vector3());
|
|
41693
|
+
return Math.abs(at.clone().sub(seat.samples[0].at).dot(seat.normal)) < PANE_SEAT_REACH;
|
|
41694
|
+
}
|
|
41695
|
+
movePane(mesh2, seat) {
|
|
41696
|
+
const THREE = this.THREE;
|
|
41697
|
+
const g = mesh2.geometry.clone();
|
|
41698
|
+
const pos = g.attributes.position;
|
|
41699
|
+
const inverse = mesh2.matrixWorld.clone().invert();
|
|
41700
|
+
const v = new THREE.Vector3();
|
|
41701
|
+
for (let i = 0; i < pos.count; i++) {
|
|
41702
|
+
v.fromBufferAttribute(pos, i).applyMatrix4(mesh2.matrixWorld);
|
|
41703
|
+
let weight = 0;
|
|
41704
|
+
let by = 0;
|
|
41705
|
+
for (const s of seat.samples) {
|
|
41706
|
+
const w = 1 / (s.at.distanceToSquared(v) + 1e-4);
|
|
41707
|
+
weight += w;
|
|
41708
|
+
by += s.by * w;
|
|
41709
|
+
}
|
|
41710
|
+
v.addScaledVector(seat.normal, by / weight + PANE_SEAT_LIFT).applyMatrix4(inverse);
|
|
41711
|
+
pos.setXYZ(i, v.x, v.y, v.z);
|
|
41712
|
+
}
|
|
41713
|
+
pos.needsUpdate = true;
|
|
41714
|
+
g.computeBoundingBox();
|
|
41715
|
+
g.computeBoundingSphere();
|
|
41716
|
+
mesh2.geometry = g;
|
|
41717
|
+
}
|
|
41718
|
+
/** A handle is at the free edge of its door, the end away from the hinge. */
|
|
41719
|
+
seatDoorHandles() {
|
|
41720
|
+
const THREE = this.THREE;
|
|
41721
|
+
const c = this.c;
|
|
41722
|
+
const inv = this.I.worldInverseOf(c);
|
|
41723
|
+
for (const p of this.model.parts) {
|
|
41724
|
+
if (p.role !== "door") continue;
|
|
41725
|
+
const z = this.zone(p);
|
|
41726
|
+
if (!z) continue;
|
|
41727
|
+
const B = z.box;
|
|
41728
|
+
const free = z.hinge === "rear" ? B.x1 - DOOR_HANDLE_INSET : B.x0 + DOOR_HANDLE_INSET;
|
|
41729
|
+
for (const o of c.vehicle.children) {
|
|
41730
|
+
if (!o.isMesh || o.material !== this.I.MATERIALS.chrome || o.userData.glass) continue;
|
|
41731
|
+
const box2 = this.I.localBox(c, o, inv);
|
|
41732
|
+
const size = box2.getSize(new THREE.Vector3());
|
|
41733
|
+
const at = box2.getCenter(new THREE.Vector3());
|
|
41734
|
+
const isHandle = size.x > DOOR_HANDLE_MIN && size.x < DOOR_HANDLE_MAX && size.y < DOOR_HANDLE_THIN && size.z < DOOR_HANDLE_THIN;
|
|
41735
|
+
if (!isHandle || Math.sign(at.z) !== Math.sign(z.side) || at.x < B.x0 || at.x > B.x1 || at.y < B.y0 || at.y > B.y1) continue;
|
|
41736
|
+
const here = this.surface("z", z.side < 0 ? -1 : 1, B, [[at.x, at.y]]);
|
|
41737
|
+
if (here != null && Math.abs(here - at.z) > DOOR_HANDLE_OUTSIDE) continue;
|
|
41738
|
+
const skin = this.surface("z", z.side < 0 ? -1 : 1, B, [[free, at.y]]);
|
|
41739
|
+
o.position.x += free - at.x;
|
|
41740
|
+
if (skin != null) o.position.z += skin + (z.side < 0 ? -1 : 1) * (size.z / 2) - at.z;
|
|
41741
|
+
}
|
|
41742
|
+
}
|
|
41743
|
+
c.root.updateMatrixWorld(true);
|
|
41744
|
+
}
|
|
41745
|
+
/** The glass of a drawn mirror sits inside its rounded housing, without corners standing past it. */
|
|
41746
|
+
seatMirrorGlass(glass) {
|
|
41747
|
+
const g = glass.geometry.clone();
|
|
41748
|
+
g.computeBoundingBox();
|
|
41749
|
+
const mid = g.boundingBox.getCenter(new this.THREE.Vector3());
|
|
41750
|
+
g.translate(-mid.x, -mid.y, -mid.z);
|
|
41751
|
+
g.scale(MIRROR_GLASS_FIT, MIRROR_GLASS_FIT, MIRROR_GLASS_FIT);
|
|
41752
|
+
g.translate(mid.x, mid.y, mid.z);
|
|
41753
|
+
glass.geometry = g;
|
|
41754
|
+
glass.material = this.I.makeMaterial(MIRROR_GLASS_COLOR, { metalness: 0.95, roughness: 0.12 });
|
|
41755
|
+
}
|
|
41756
|
+
/** The mirror the model already draws around where the part says, if it draws one. */
|
|
41757
|
+
drawnMirror(p) {
|
|
41758
|
+
const THREE = this.THREE;
|
|
41759
|
+
const [cx, cy, cz] = p.box.center;
|
|
41760
|
+
this.c.root.updateMatrixWorld(true);
|
|
41761
|
+
const inv = this.I.worldInverseOf(this.c);
|
|
41762
|
+
const small = this.c.vehicle.children.flatMap((o) => {
|
|
41763
|
+
if (o.userData.glass || o.userData.decal || !(o.isMesh || o.isGroup) || !o.visible || this.c.deform?.includes(o)) return [];
|
|
41764
|
+
const box2 = this.I.localBox(this.c, o, inv);
|
|
41765
|
+
if (box2.isEmpty()) return [];
|
|
41766
|
+
const size = box2.getSize(new THREE.Vector3());
|
|
41767
|
+
return Math.max(size.x, size.y, size.z) < DRAWN_MIRROR_SIZE ? [{ o, at: box2.getCenter(new THREE.Vector3()) }] : [];
|
|
41768
|
+
});
|
|
41769
|
+
const housing = small.filter(({ at }) => Math.hypot(at.x - cx, at.y - cy, at.z - cz) < DRAWN_MIRROR_REACH && Math.abs(at.z) > Math.abs(cz) - DRAWN_MIRROR_REACH / 2);
|
|
41770
|
+
return small.filter(({ at }) => housing.some((h) => h.at.distanceTo(at) < DRAWN_MIRROR_MOUNT)).map(({ o }) => o);
|
|
41771
|
+
}
|
|
41088
41772
|
makeMirror(p) {
|
|
41773
|
+
const drawn = this.drawnMirror(p);
|
|
41774
|
+
if (drawn.length) {
|
|
41775
|
+
for (const o of drawn) {
|
|
41776
|
+
if (!o.isMesh || o.material !== this.I.MATERIALS.chrome) continue;
|
|
41777
|
+
const size = new this.THREE.Box3().setFromObject(o).getSize(new this.THREE.Vector3());
|
|
41778
|
+
if (size.x < MIRROR_GLASS_THIN && size.y > MIRROR_GLASS_MIN && size.z > MIRROR_GLASS_MIN) this.seatMirrorGlass(o);
|
|
41779
|
+
}
|
|
41780
|
+
this.extra.set(p.id, drawn);
|
|
41781
|
+
return;
|
|
41782
|
+
}
|
|
41089
41783
|
const [cx, cy, cz] = p.box.center;
|
|
41090
41784
|
const s = cz < 0 ? -1 : 1;
|
|
41091
41785
|
const mats = this.I.MATERIALS;
|
|
@@ -41126,17 +41820,28 @@ var GameVehiclesThree = (() => {
|
|
|
41126
41820
|
}
|
|
41127
41821
|
// ---- marks -------------------------------------------------------------------------------------------------------------------------------
|
|
41128
41822
|
/** A bullet hole: a dark disc lying on the surface at `point` (`c.vehicle` coordinates). */
|
|
41129
|
-
holeDecal(point, normal, diameter) {
|
|
41823
|
+
holeDecal(point, normal, diameter, part) {
|
|
41130
41824
|
const THREE = this.THREE;
|
|
41131
|
-
const
|
|
41825
|
+
const pane = part?.role === "glass" ? this.panesOf(part).find((g) => g.visible) : void 0;
|
|
41826
|
+
if (part?.role === "glass" && (!pane || pane.userData.glass?.broken)) return;
|
|
41827
|
+
const radius = Math.max(6e-3, diameter / 2) * HOLE_DECAL_SCALE;
|
|
41828
|
+
const m = new THREE.Mesh(holeDiscGeometry(THREE), holeMaterial(this.kit, part?.material ?? "steel"));
|
|
41829
|
+
m.scale.set(radius, radius, 1);
|
|
41132
41830
|
const n = new THREE.Vector3(...normal);
|
|
41133
|
-
m.position.set(...point).addScaledVector(n,
|
|
41134
|
-
|
|
41831
|
+
m.position.set(...point).addScaledVector(n, 3e-3);
|
|
41832
|
+
this.c.vehicle.add(m);
|
|
41833
|
+
m.lookAt(this.c.vehicle.localToWorld(m.position.clone().add(n)));
|
|
41135
41834
|
m.userData.decal = true;
|
|
41136
41835
|
m.userData.noFollow = true;
|
|
41836
|
+
m.renderOrder = 3;
|
|
41137
41837
|
m.raycast = () => {
|
|
41138
41838
|
};
|
|
41139
|
-
this.c.vehicle.
|
|
41839
|
+
const carrier = (part && (this.plates.get(part.id) ?? (part.id === this.hood?.id ? this.c.hood?.piv : void 0))) ?? (pane && pane.parent !== this.c.vehicle ? pane.parent : null);
|
|
41840
|
+
if (carrier) carrier.attach(m);
|
|
41841
|
+
}
|
|
41842
|
+
/** The door or lid a hinge group stands for. */
|
|
41843
|
+
plateIdOf(o) {
|
|
41844
|
+
return o.userData.plateOf;
|
|
41140
41845
|
}
|
|
41141
41846
|
spark(point, dir, count2 = 8) {
|
|
41142
41847
|
const V3 = this.THREE.Vector3;
|
|
@@ -41176,10 +41881,8 @@ var GameVehiclesThree = (() => {
|
|
|
41176
41881
|
}
|
|
41177
41882
|
/** Glass: cracks (laminated) or shatters (tempered); returns whether it broke. */
|
|
41178
41883
|
hitGlass(p, point, dir, force) {
|
|
41179
|
-
const z = this.zone(p);
|
|
41180
|
-
if (!z) return false;
|
|
41181
41884
|
const V3 = this.THREE.Vector3;
|
|
41182
|
-
const list2 =
|
|
41885
|
+
const list2 = this.panesOf(p).filter((g) => g.parent && g.visible);
|
|
41183
41886
|
if (!list2.length) return false;
|
|
41184
41887
|
const pt = new V3(...point);
|
|
41185
41888
|
let best = list2[0];
|
|
@@ -41196,9 +41899,16 @@ var GameVehiclesThree = (() => {
|
|
|
41196
41899
|
this.kit.hitGlass(this.c, best, pt, new V3(...dir), force);
|
|
41197
41900
|
return !!best.userData.glass?.broken;
|
|
41198
41901
|
}
|
|
41902
|
+
panesOf(p) {
|
|
41903
|
+
return p.panes ?? this.zone(p)?.glass ?? [];
|
|
41904
|
+
}
|
|
41199
41905
|
glassBreak(p) {
|
|
41200
41906
|
const z = this.zone(p);
|
|
41201
|
-
if (!z)
|
|
41907
|
+
if (!z) {
|
|
41908
|
+
for (const g of p.panes ?? []) if (g.parent && g.visible) this.kit.hitGlass(this.c, g, g.position.clone(), new this.THREE.Vector3(0, -1, 0), 0.6);
|
|
41909
|
+
this.gone.add(p.id);
|
|
41910
|
+
return;
|
|
41911
|
+
}
|
|
41202
41912
|
for (const g of z.glass) if (g.parent && g.visible) this.I.glassBand(this.c, z, g, 4);
|
|
41203
41913
|
this.gone.add(p.id);
|
|
41204
41914
|
}
|
|
@@ -41226,6 +41936,7 @@ var GameVehiclesThree = (() => {
|
|
|
41226
41936
|
smoothLater(msh) {
|
|
41227
41937
|
this.smoothing.set(msh, this.I.creaseTask(msh.geometry, 42));
|
|
41228
41938
|
this.tornThisFrame = true;
|
|
41939
|
+
this.holdArmed = true;
|
|
41229
41940
|
}
|
|
41230
41941
|
/** Spends at most `budgetMs` finishing the pending normal smoothing. Call once per frame. */
|
|
41231
41942
|
pump(budgetMs = 2.5) {
|
|
@@ -41233,6 +41944,7 @@ var GameVehiclesThree = (() => {
|
|
|
41233
41944
|
this.tornThisFrame = false;
|
|
41234
41945
|
return;
|
|
41235
41946
|
}
|
|
41947
|
+
if (this.queued) return;
|
|
41236
41948
|
if (!this.smoothing.size && !this.warming) return;
|
|
41237
41949
|
const deadline = performance.now() + budgetMs;
|
|
41238
41950
|
for (const [msh, task] of this.smoothing) {
|
|
@@ -41241,6 +41953,34 @@ var GameVehiclesThree = (() => {
|
|
|
41241
41953
|
}
|
|
41242
41954
|
this.warmIndexes(deadline);
|
|
41243
41955
|
}
|
|
41956
|
+
setQueued(isQueued) {
|
|
41957
|
+
this.queued = isQueued;
|
|
41958
|
+
}
|
|
41959
|
+
/** Units the shared queue may run now: 0 in the frame of the cut (the first ask after it holds the work for that frame, or `pump` closes it when there is no frame). */
|
|
41960
|
+
pendingUnits(frame) {
|
|
41961
|
+
if (this.holdArmed && frame !== void 0) {
|
|
41962
|
+
this.holdArmed = false;
|
|
41963
|
+
this.holdFrame = frame;
|
|
41964
|
+
}
|
|
41965
|
+
if (frame !== void 0 && frame === this.holdFrame) return 0;
|
|
41966
|
+
if (frame === void 0 && this.tornThisFrame) return 0;
|
|
41967
|
+
return this.smoothing.size + (this.warming ? 1 : 0);
|
|
41968
|
+
}
|
|
41969
|
+
/** One slice of the pending work, for the shared queue. */
|
|
41970
|
+
runStep(deadline) {
|
|
41971
|
+
const next = this.smoothing.entries().next();
|
|
41972
|
+
if (!next.done) {
|
|
41973
|
+
const [msh, task] = next.value;
|
|
41974
|
+
if (msh.geometry !== task.geo || task.step(deadline)) this.smoothing.delete(msh);
|
|
41975
|
+
return true;
|
|
41976
|
+
}
|
|
41977
|
+
if (!this.warming) return false;
|
|
41978
|
+
this.warmIndexes(deadline);
|
|
41979
|
+
return true;
|
|
41980
|
+
}
|
|
41981
|
+
finishAll() {
|
|
41982
|
+
while (this.runStep(Infinity)) ;
|
|
41983
|
+
}
|
|
41244
41984
|
/** Measures the triangles of the heavy body meshes once, a slice per frame, so the first panel torn off finds them ready (see `triIndexOf`). */
|
|
41245
41985
|
warmIndexes(deadline) {
|
|
41246
41986
|
if (!this.warming) return;
|
|
@@ -41412,6 +42152,7 @@ var GameVehiclesThree = (() => {
|
|
|
41412
42152
|
}
|
|
41413
42153
|
pieceTasks.forEach((task, mesh2) => this.smoothing.set(mesh2, task));
|
|
41414
42154
|
this.tornThisFrame = true;
|
|
42155
|
+
this.holdArmed = true;
|
|
41415
42156
|
dirty.forEach((msh) => {
|
|
41416
42157
|
const g = msh.geometry;
|
|
41417
42158
|
if (g.userData.crease || !g.index) this.smoothLater(msh);
|
|
@@ -41514,6 +42255,7 @@ var GameVehiclesThree = (() => {
|
|
|
41514
42255
|
let clock = 0;
|
|
41515
42256
|
let model;
|
|
41516
42257
|
let vis = null;
|
|
42258
|
+
let releaseCrease;
|
|
41517
42259
|
const ps = /* @__PURE__ */ new Map();
|
|
41518
42260
|
let js = [];
|
|
41519
42261
|
let leaks = /* @__PURE__ */ new Map();
|
|
@@ -41598,7 +42340,9 @@ var GameVehiclesThree = (() => {
|
|
|
41598
42340
|
rand = rng(seedBase);
|
|
41599
42341
|
igniteRand = rng(strHash(`${c.key}|${id}|ignition|${options.seed ?? 0}`));
|
|
41600
42342
|
steerSign = rand() < 0.5 ? -1 : 1;
|
|
42343
|
+
releaseCrease?.();
|
|
41601
42344
|
vis = headless ? null : new VehicleVisuals(kit, c, model);
|
|
42345
|
+
releaseCrease = vis && options.creaseQueue ? options.creaseQueue.track(vis) : void 0;
|
|
41602
42346
|
recomputeGroups();
|
|
41603
42347
|
};
|
|
41604
42348
|
const recomputeGroups = () => {
|
|
@@ -41909,13 +42653,13 @@ var GameVehiclesThree = (() => {
|
|
|
41909
42653
|
sync();
|
|
41910
42654
|
const M = toFrameMatrix();
|
|
41911
42655
|
const v = toLocal(h.point);
|
|
41912
|
-
const p = locate(v, 0.2);
|
|
42656
|
+
const p = (h.part ? ps.get(h.part) : void 0) ?? locate(v, 0.2);
|
|
41913
42657
|
if (!p) return;
|
|
41914
42658
|
const d = p.def;
|
|
41915
42659
|
const E = h.energy ?? 0;
|
|
41916
42660
|
const cal = h.caliber ?? 9;
|
|
41917
42661
|
const dir = dirToLocal(h.direction ?? [0, 0, -1]).normalize();
|
|
41918
|
-
const n = normalOf(d, v);
|
|
42662
|
+
const n = h.normal ? dirToLocal(h.normal).normalize() : normalOf(d, v);
|
|
41919
42663
|
const res = resist(d, p);
|
|
41920
42664
|
const through = E >= res;
|
|
41921
42665
|
const cosInc = Math.abs(dir.dot(n));
|
|
@@ -41934,7 +42678,7 @@ var GameVehiclesThree = (() => {
|
|
|
41934
42678
|
const hp = { point: r3(fromVehicle(v, M)), normal: r3(fromVehicle(n, new THREE.Matrix4().extractRotation(M))), diameter: r2(holeDiam), through };
|
|
41935
42679
|
p.holes.push(hp);
|
|
41936
42680
|
out.push(ev({ type: "hole", part: d.id, hole: hp, worldPoint, ...through ? { exitPoint: add3(worldPoint, r3(dirToWorld(dir.clone().multiplyScalar(Math.max(0.02, Math.min(d.box.size[0], d.box.size[1], d.box.size[2])))))) } : {} }));
|
|
41937
|
-
vis?.holeDecal([v.x, v.y, v.z], [n.x, n.y, n.z], holeDiam);
|
|
42681
|
+
vis?.holeDecal([v.x, v.y, v.z], [n.x, n.y, n.z], holeDiam, d);
|
|
41938
42682
|
startLeak(p, v, cal, out, dir);
|
|
41939
42683
|
};
|
|
41940
42684
|
switch (d.material) {
|
|
@@ -42235,6 +42979,46 @@ var GameVehiclesThree = (() => {
|
|
|
42235
42979
|
for (const p of ps.values()) if (!p.lost && (p.burning || p.temp > AMBIENT)) return true;
|
|
42236
42980
|
return false;
|
|
42237
42981
|
};
|
|
42982
|
+
const surfaceRays = new THREE.Raycaster();
|
|
42983
|
+
surfaceRays.layers.enableAll();
|
|
42984
|
+
surfaceRays.camera = new THREE.PerspectiveCamera();
|
|
42985
|
+
let meshParts = null;
|
|
42986
|
+
const paneParts = () => {
|
|
42987
|
+
if (meshParts) return meshParts;
|
|
42988
|
+
meshParts = /* @__PURE__ */ new Map();
|
|
42989
|
+
for (const d of model.parts) {
|
|
42990
|
+
if (d.role !== "glass") continue;
|
|
42991
|
+
const z = kit.zonesOf(c).find((q) => q.id === d.zoneId);
|
|
42992
|
+
for (const g of z?.glass ?? d.panes ?? []) meshParts.set(g, d.id);
|
|
42993
|
+
}
|
|
42994
|
+
return meshParts;
|
|
42995
|
+
};
|
|
42996
|
+
const isDrawnSurface = (o) => {
|
|
42997
|
+
for (let q = o; q && q !== c.vehicle; q = q.parent) if (!q.visible || q.userData.decal || q.userData.ghost) return false;
|
|
42998
|
+
return true;
|
|
42999
|
+
};
|
|
43000
|
+
const partOfSurface = (o, v) => {
|
|
43001
|
+
const pane = paneParts().get(o);
|
|
43002
|
+
if (pane) return ps.get(pane) ?? null;
|
|
43003
|
+
for (let q = o; q && q !== c.vehicle; q = q.parent) {
|
|
43004
|
+
if (vis?.plateIdOf(q)) return ps.get(vis.plateIdOf(q)) ?? null;
|
|
43005
|
+
}
|
|
43006
|
+
return locate(v, 0.08, (p) => p.def.role === "glass");
|
|
43007
|
+
};
|
|
43008
|
+
const surfaceRay = (ray) => {
|
|
43009
|
+
sync();
|
|
43010
|
+
surfaceRays.set(new V3(ray.origin[0], ray.origin[1], ray.origin[2]), new V3(ray.direction[0], ray.direction[1], ray.direction[2]));
|
|
43011
|
+
surfaceRays.far = ray.maxDistance;
|
|
43012
|
+
for (const h of surfaceRays.intersectObject(c.vehicle, true)) {
|
|
43013
|
+
if (!h.face || !isDrawnSurface(h.object)) continue;
|
|
43014
|
+
const p = partOfSurface(h.object, c.vehicle.worldToLocal(h.point.clone()));
|
|
43015
|
+
if (!p || p.lost || ray.ignored?.(p.def.id)) continue;
|
|
43016
|
+
const n = h.face.normal.clone().transformDirection(h.object.matrixWorld);
|
|
43017
|
+
if (n.dot(surfaceRays.ray.direction) > 0) n.negate();
|
|
43018
|
+
return { part: p.def.id, point: [h.point.x, h.point.y, h.point.z], normal: [n.x, n.y, n.z], distance: h.distance, pathLen: 0.02 };
|
|
43019
|
+
}
|
|
43020
|
+
return null;
|
|
43021
|
+
};
|
|
42238
43022
|
const hit = (h) => {
|
|
42239
43023
|
stateCache = void 0;
|
|
42240
43024
|
const out = [];
|
|
@@ -42357,9 +43141,14 @@ var GameVehiclesThree = (() => {
|
|
|
42357
43141
|
},
|
|
42358
43142
|
describe,
|
|
42359
43143
|
hit,
|
|
43144
|
+
surfaceRay,
|
|
42360
43145
|
update,
|
|
42361
43146
|
state,
|
|
42362
43147
|
reset,
|
|
43148
|
+
dispose: () => {
|
|
43149
|
+
releaseCrease?.();
|
|
43150
|
+
releaseCrease = void 0;
|
|
43151
|
+
},
|
|
42363
43152
|
disassemble,
|
|
42364
43153
|
extinguish,
|
|
42365
43154
|
serialize: () => ({
|
|
@@ -42395,6 +43184,387 @@ var GameVehiclesThree = (() => {
|
|
|
42395
43184
|
root.userData.vehicle = vehicle;
|
|
42396
43185
|
return { root, vehicle };
|
|
42397
43186
|
}
|
|
43187
|
+
function createCreaseQueue() {
|
|
43188
|
+
const tracked = [];
|
|
43189
|
+
let cursor = 0;
|
|
43190
|
+
let lastFrame;
|
|
43191
|
+
let attached = false;
|
|
43192
|
+
const deferred = {
|
|
43193
|
+
id: DEFERRED_QUEUE_ID.VEHICLE_CREASE,
|
|
43194
|
+
pending(frame) {
|
|
43195
|
+
lastFrame = frame;
|
|
43196
|
+
let units = 0;
|
|
43197
|
+
for (const work of tracked) units += work.pendingUnits(frame);
|
|
43198
|
+
return units;
|
|
43199
|
+
},
|
|
43200
|
+
run(deadline, clock) {
|
|
43201
|
+
let ran = 0;
|
|
43202
|
+
let skipped = 0;
|
|
43203
|
+
while (tracked.length > 0 && skipped < tracked.length) {
|
|
43204
|
+
if (ran > 0 && clock() >= deadline) break;
|
|
43205
|
+
const work = tracked[cursor % tracked.length];
|
|
43206
|
+
cursor = (cursor + 1) % tracked.length;
|
|
43207
|
+
if (work.pendingUnits(lastFrame) > 0 && work.runStep(deadline)) {
|
|
43208
|
+
ran++;
|
|
43209
|
+
skipped = 0;
|
|
43210
|
+
} else skipped++;
|
|
43211
|
+
}
|
|
43212
|
+
return ran;
|
|
43213
|
+
},
|
|
43214
|
+
flush() {
|
|
43215
|
+
for (const work of tracked) work.finishAll();
|
|
43216
|
+
},
|
|
43217
|
+
attach() {
|
|
43218
|
+
attached = true;
|
|
43219
|
+
for (const work of tracked) work.setQueued(true);
|
|
43220
|
+
},
|
|
43221
|
+
detach() {
|
|
43222
|
+
attached = false;
|
|
43223
|
+
for (const work of tracked) work.setQueued(false);
|
|
43224
|
+
}
|
|
43225
|
+
};
|
|
43226
|
+
return {
|
|
43227
|
+
deferredQueue: () => deferred,
|
|
43228
|
+
track(work) {
|
|
43229
|
+
tracked.push(work);
|
|
43230
|
+
work.setQueued(attached);
|
|
43231
|
+
return () => {
|
|
43232
|
+
const at = tracked.indexOf(work);
|
|
43233
|
+
if (at < 0) return;
|
|
43234
|
+
tracked.splice(at, 1);
|
|
43235
|
+
work.setQueued(false);
|
|
43236
|
+
};
|
|
43237
|
+
}
|
|
43238
|
+
};
|
|
43239
|
+
}
|
|
43240
|
+
var CABIN_LEAK = 0.02;
|
|
43241
|
+
var HOLE_RATE_PER_AREA = 3e3;
|
|
43242
|
+
var CRACK_GAP_PER_METRE = 0.5;
|
|
43243
|
+
var THROUGH = 0.18;
|
|
43244
|
+
var HOLE_PATCH = 0.12;
|
|
43245
|
+
var FULL_SWING = 1.15;
|
|
43246
|
+
var SHELL_ROLES = /* @__PURE__ */ new Set(["door", "glass", "roof", "lid", "panel", "pillar"]);
|
|
43247
|
+
var GONE = /* @__PURE__ */ new Set(["broken", "melted", "ashes"]);
|
|
43248
|
+
function cabinBoxOf(vehicle) {
|
|
43249
|
+
const cabin = vehicle.meta.cabin;
|
|
43250
|
+
if (!cabin || !(cabin.x1 > cabin.x0) || !(cabin.y1 > cabin.y0) || !(cabin.z > 0)) return null;
|
|
43251
|
+
return cabin;
|
|
43252
|
+
}
|
|
43253
|
+
function swingOf(vehicle, partId) {
|
|
43254
|
+
const pivot = vehicle.vehicle?.getObjectByName?.(partId);
|
|
43255
|
+
const q = pivot?.quaternion;
|
|
43256
|
+
if (!q) return 0;
|
|
43257
|
+
const angle = 2 * Math.acos(Math.min(1, Math.abs(q.w)));
|
|
43258
|
+
return Math.min(1, angle / FULL_SWING);
|
|
43259
|
+
}
|
|
43260
|
+
var overlaps = (b, c, margin) => Math.abs(b.center[0] - (c.x0 + c.x1) / 2) <= b.size[0] / 2 + (c.x1 - c.x0) / 2 + margin && Math.abs(b.center[1] - (c.y0 + c.y1) / 2) <= b.size[1] / 2 + (c.y1 - c.y0) / 2 + margin && Math.abs(b.center[2]) <= b.size[2] / 2 + c.z + margin;
|
|
43261
|
+
function cabinEnclosure(source, options = {}) {
|
|
43262
|
+
const c = source.vehicle;
|
|
43263
|
+
const cabin = cabinBoxOf(c);
|
|
43264
|
+
const node = c.vehicle;
|
|
43265
|
+
if (!cabin || !node?.localToWorld) return null;
|
|
43266
|
+
const state = source.state();
|
|
43267
|
+
if (state.destroyed) return null;
|
|
43268
|
+
const V3 = node.position.constructor;
|
|
43269
|
+
const Quat = node.quaternion.constructor;
|
|
43270
|
+
const mid = [(cabin.x0 + cabin.x1) / 2, (cabin.y0 + cabin.y1) / 2, 0];
|
|
43271
|
+
const half = [(cabin.x1 - cabin.x0) / 2, (cabin.y1 - cabin.y0) / 2, cabin.z];
|
|
43272
|
+
const ajar = options.ajar ?? 0.06;
|
|
43273
|
+
const byId = new Map(state.parts.map((p) => [p.id, p]));
|
|
43274
|
+
const mainGroup = state.groups[0] ? new Set(state.groups[0]) : null;
|
|
43275
|
+
const openings = [];
|
|
43276
|
+
let crackLength = 0;
|
|
43277
|
+
const toCabin = (b) => ({
|
|
43278
|
+
center: [b.center[0] - mid[0], b.center[1] - mid[1], b.center[2] - mid[2]],
|
|
43279
|
+
halfSize: [b.size[0] / 2 + THROUGH, b.size[1] / 2 + THROUGH, b.size[2] / 2 + THROUGH]
|
|
43280
|
+
});
|
|
43281
|
+
node.updateWorldMatrix?.(true, false);
|
|
43282
|
+
const frame = options.frame ?? c.root;
|
|
43283
|
+
frame?.updateWorldMatrix?.(true, false);
|
|
43284
|
+
const inverse = node.matrixWorld.clone().invert();
|
|
43285
|
+
for (const def of source.model.parts) {
|
|
43286
|
+
if (!SHELL_ROLES.has(def.role) || !overlaps(def.box, cabin, 0.12)) continue;
|
|
43287
|
+
const part = byId.get(def.id);
|
|
43288
|
+
if (!part) continue;
|
|
43289
|
+
const gone = GONE.has(part.status) || mainGroup !== null && !mainGroup.has(def.id);
|
|
43290
|
+
const swung = (def.role === "door" || def.role === "lid") && swingOf(c, def.id) > ajar;
|
|
43291
|
+
if (gone || swung) {
|
|
43292
|
+
openings.push(toCabin(def.box));
|
|
43293
|
+
continue;
|
|
43294
|
+
}
|
|
43295
|
+
for (const hole of part.holes) {
|
|
43296
|
+
if (!hole.through && def.material !== "glass") continue;
|
|
43297
|
+
const area3 = Math.PI * (hole.diameter / 2) ** 2;
|
|
43298
|
+
const p = new V3(hole.point[0], hole.point[1], hole.point[2]);
|
|
43299
|
+
if (frame?.localToWorld) frame.localToWorld(p);
|
|
43300
|
+
p.applyMatrix4(inverse);
|
|
43301
|
+
openings.push({
|
|
43302
|
+
center: [p.x - mid[0], p.y - mid[1], p.z - mid[2]],
|
|
43303
|
+
halfSize: [HOLE_PATCH, HOLE_PATCH, HOLE_PATCH],
|
|
43304
|
+
// the patch is far bigger than the hole: only a share of what crosses it passes, more for a bigger hole
|
|
43305
|
+
rate: Math.min(4, area3 * HOLE_RATE_PER_AREA)
|
|
43306
|
+
});
|
|
43307
|
+
}
|
|
43308
|
+
for (const crack of part.cracks) crackLength += crack.length;
|
|
43309
|
+
}
|
|
43310
|
+
const center = node.localToWorld(new V3(mid[0], mid[1], mid[2]));
|
|
43311
|
+
const q = node.getWorldQuaternion(new Quat());
|
|
43312
|
+
const scale = node.getWorldScale(new V3());
|
|
43313
|
+
const leak = (options.leak ?? CABIN_LEAK) * (1 + crackLength * CRACK_GAP_PER_METRE);
|
|
43314
|
+
return {
|
|
43315
|
+
center: [center.x, center.y, center.z],
|
|
43316
|
+
halfSize: [half[0] * Math.abs(scale.x), half[1] * Math.abs(scale.y), half[2] * Math.abs(scale.z)],
|
|
43317
|
+
orientation: [q.x, q.y, q.z, q.w],
|
|
43318
|
+
openings: openings.map((o) => ({ ...o, center: [o.center[0] * scale.x, o.center[1] * scale.y, o.center[2] * scale.z], halfSize: [o.halfSize[0] * Math.abs(scale.x), o.halfSize[1] * Math.abs(scale.y), o.halfSize[2] * Math.abs(scale.z)] })),
|
|
43319
|
+
leak
|
|
43320
|
+
};
|
|
43321
|
+
}
|
|
43322
|
+
|
|
43323
|
+
// src/cabin-smoke.ts
|
|
43324
|
+
function createCabinSmoke() {
|
|
43325
|
+
const tracked = /* @__PURE__ */ new Map();
|
|
43326
|
+
const end = (id, entry, effects) => {
|
|
43327
|
+
if (entry.declared) effects?.enclose?.(id, null);
|
|
43328
|
+
tracked.delete(id);
|
|
43329
|
+
};
|
|
43330
|
+
return {
|
|
43331
|
+
track(id, source, frame) {
|
|
43332
|
+
tracked.set(id, { source, frame, declared: false, placed: false });
|
|
43333
|
+
},
|
|
43334
|
+
untrack(id, effects) {
|
|
43335
|
+
const entry = tracked.get(id);
|
|
43336
|
+
if (entry) end(id, entry, effects);
|
|
43337
|
+
},
|
|
43338
|
+
sync(effects) {
|
|
43339
|
+
if (!effects?.enclose) return;
|
|
43340
|
+
for (const [id, entry] of tracked) {
|
|
43341
|
+
if (!entry.frame.parent) {
|
|
43342
|
+
if (entry.placed) end(id, entry, effects);
|
|
43343
|
+
continue;
|
|
43344
|
+
}
|
|
43345
|
+
entry.placed = true;
|
|
43346
|
+
const enclosure = cabinEnclosure(entry.source, { frame: entry.frame });
|
|
43347
|
+
if (enclosure || entry.declared) effects.enclose(id, enclosure);
|
|
43348
|
+
entry.declared = enclosure !== null;
|
|
43349
|
+
}
|
|
43350
|
+
},
|
|
43351
|
+
clear(effects) {
|
|
43352
|
+
for (const [id, entry] of [...tracked]) end(id, entry, effects);
|
|
43353
|
+
},
|
|
43354
|
+
ids: () => [...tracked.keys()]
|
|
43355
|
+
};
|
|
43356
|
+
}
|
|
43357
|
+
|
|
43358
|
+
// ../game-render-three/dist/index.js
|
|
43359
|
+
var DEFAULT_ANGLE = Math.PI / 3;
|
|
43360
|
+
var EFFECT_STEP_SECONDS = 1 / 50;
|
|
43361
|
+
function isRenderPools(value) {
|
|
43362
|
+
const candidate = value;
|
|
43363
|
+
return typeof candidate === "object" && candidate !== null && typeof candidate.geometries?.acquire === "function" && typeof candidate.materials?.acquire === "function";
|
|
43364
|
+
}
|
|
43365
|
+
var BATCHED_LAYER = 30;
|
|
43366
|
+
var MATRIX_EPSILON = 1e-6;
|
|
43367
|
+
var ATTRIBUTES = ["position", "normal", "uv", "color"];
|
|
43368
|
+
var isShown = (object2, root) => {
|
|
43369
|
+
for (let node = object2; node; node = node.parent) {
|
|
43370
|
+
if (node === root) return true;
|
|
43371
|
+
if (!node.visible) return false;
|
|
43372
|
+
}
|
|
43373
|
+
return false;
|
|
43374
|
+
};
|
|
43375
|
+
var isBatchable = (mesh2) => {
|
|
43376
|
+
const flags = mesh2;
|
|
43377
|
+
if (flags.isInstancedMesh === true || flags.isSkinnedMesh === true || Array.isArray(mesh2.material)) return false;
|
|
43378
|
+
const geometry = mesh2.geometry;
|
|
43379
|
+
return geometry.attributes.position !== void 0 && Object.keys(geometry.morphAttributes).length === 0;
|
|
43380
|
+
};
|
|
43381
|
+
var signatureOf = (mesh2) => {
|
|
43382
|
+
const { attributes } = mesh2.geometry;
|
|
43383
|
+
const present = ATTRIBUTES.filter((name) => attributes[name] !== void 0).map((name) => `${name}${attributes[name].itemSize}`);
|
|
43384
|
+
return `${mesh2.material.uuid}|${present.join(",")}|${mesh2.castShadow ? 1 : 0}${mesh2.receiveShadow ? 1 : 0}`;
|
|
43385
|
+
};
|
|
43386
|
+
var sameMatrix = (a, b) => a.elements.every((value, index) => Math.abs(value - b.elements[index]) < MATRIX_EPSILON);
|
|
43387
|
+
function createStaticBatcher(THREE, root, options = {}) {
|
|
43388
|
+
const include = options.include ?? (() => true);
|
|
43389
|
+
const follow = options.followMeshes === true;
|
|
43390
|
+
root.updateMatrixWorld(true);
|
|
43391
|
+
const inverse = new THREE.Matrix4().copy(root.matrixWorld).invert();
|
|
43392
|
+
const groups = /* @__PURE__ */ new Map();
|
|
43393
|
+
root.traverse((object2) => {
|
|
43394
|
+
const mesh2 = object2;
|
|
43395
|
+
if (!mesh2.isMesh || !isShown(mesh2, root) || !isBatchable(mesh2) || !include(mesh2)) return;
|
|
43396
|
+
const key = signatureOf(mesh2);
|
|
43397
|
+
groups.set(key, [...groups.get(key) ?? [], mesh2]);
|
|
43398
|
+
});
|
|
43399
|
+
const group = new THREE.Group();
|
|
43400
|
+
group.name = "static-batches";
|
|
43401
|
+
const sources = [];
|
|
43402
|
+
const pending = [...groups.values()].filter((meshes) => meshes.length >= 2);
|
|
43403
|
+
const mergeSome = (deadline) => {
|
|
43404
|
+
while (pending.length > 0) {
|
|
43405
|
+
const meshes = pending.shift();
|
|
43406
|
+
if (meshes.every((mesh2) => isShown(mesh2, root))) sources.push(...mergeGroup(THREE, { meshes, inverse: new THREE.Matrix4().copy(root.matrixWorld).invert(), group, follow }));
|
|
43407
|
+
if (performance.now() >= deadline) return;
|
|
43408
|
+
}
|
|
43409
|
+
};
|
|
43410
|
+
if (options.budgetMs === void 0) mergeSome(Number.POSITIVE_INFINITY);
|
|
43411
|
+
root.add(group);
|
|
43412
|
+
const scratch2 = new THREE.Matrix4();
|
|
43413
|
+
let released = 0;
|
|
43414
|
+
const release = (source) => {
|
|
43415
|
+
source.released = true;
|
|
43416
|
+
source.mesh.layers.mask = source.mask;
|
|
43417
|
+
const { index } = source.batch;
|
|
43418
|
+
const first = index.array[source.start];
|
|
43419
|
+
for (let at = source.start; at < source.start + source.count; at++) index.array[at] = first;
|
|
43420
|
+
index.needsUpdate = true;
|
|
43421
|
+
if (--source.batch.live === 0) source.batch.mesh.visible = false;
|
|
43422
|
+
released++;
|
|
43423
|
+
};
|
|
43424
|
+
const hasChanged = (source) => {
|
|
43425
|
+
const { mesh: mesh2 } = source;
|
|
43426
|
+
if (mesh2.geometry !== source.geometry || mesh2.material !== source.material || !isShown(mesh2, root)) return true;
|
|
43427
|
+
if (mesh2.geometry.attributes.position.version !== source.version) return true;
|
|
43428
|
+
if (follow || sameMatrix(mesh2.matrixWorld, source.world)) return false;
|
|
43429
|
+
if (!sameMatrix(scratch2.multiplyMatrices(inverse, mesh2.matrixWorld), source.local)) return true;
|
|
43430
|
+
source.world.copy(mesh2.matrixWorld);
|
|
43431
|
+
return false;
|
|
43432
|
+
};
|
|
43433
|
+
return {
|
|
43434
|
+
group,
|
|
43435
|
+
update() {
|
|
43436
|
+
if (pending.length > 0) mergeSome(performance.now() + (options.budgetMs ?? Number.POSITIVE_INFINITY));
|
|
43437
|
+
const before = released;
|
|
43438
|
+
inverse.copy(root.matrixWorld).invert();
|
|
43439
|
+
for (const source of sources) if (!source.released && hasChanged(source)) release(source);
|
|
43440
|
+
return released - before;
|
|
43441
|
+
},
|
|
43442
|
+
stats: () => ({ batches: group.children.length, batched: sources.filter((source) => !source.released).length, released }),
|
|
43443
|
+
dispose() {
|
|
43444
|
+
for (const source of sources) if (!source.released) source.mesh.layers.mask = source.mask;
|
|
43445
|
+
group.removeFromParent();
|
|
43446
|
+
for (const child of group.children) {
|
|
43447
|
+
;
|
|
43448
|
+
child.geometry.dispose();
|
|
43449
|
+
child.skeleton?.dispose();
|
|
43450
|
+
}
|
|
43451
|
+
}
|
|
43452
|
+
};
|
|
43453
|
+
}
|
|
43454
|
+
function copyAttribute(name, attribute, target, offset, matrix, normalMatrix2) {
|
|
43455
|
+
const size = attribute.itemSize;
|
|
43456
|
+
const source = attribute.array;
|
|
43457
|
+
const flat = !attribute.isInterleavedBufferAttribute && source.length === attribute.count * size;
|
|
43458
|
+
if (name !== "position" && name !== "normal") {
|
|
43459
|
+
if (flat) target.set(source, offset * size);
|
|
43460
|
+
else for (let at = 0; at < attribute.count; at++) for (let k = 0; k < size; k++) target[(offset + at) * size + k] = attribute.getComponent(at, k);
|
|
43461
|
+
return;
|
|
43462
|
+
}
|
|
43463
|
+
const e = (name === "position" ? matrix : normalMatrix2).elements;
|
|
43464
|
+
const isNormal = name === "normal";
|
|
43465
|
+
for (let at = 0; at < attribute.count; at++) {
|
|
43466
|
+
const x = attribute.getX(at);
|
|
43467
|
+
const y = attribute.getY(at);
|
|
43468
|
+
const z = attribute.getZ(at);
|
|
43469
|
+
const out = (offset + at) * 3;
|
|
43470
|
+
if (isNormal) {
|
|
43471
|
+
const nx = e[0] * x + e[3] * y + e[6] * z;
|
|
43472
|
+
const ny = e[1] * x + e[4] * y + e[7] * z;
|
|
43473
|
+
const nz = e[2] * x + e[5] * y + e[8] * z;
|
|
43474
|
+
const length = Math.hypot(nx, ny, nz) || 1;
|
|
43475
|
+
target[out] = nx / length;
|
|
43476
|
+
target[out + 1] = ny / length;
|
|
43477
|
+
target[out + 2] = nz / length;
|
|
43478
|
+
} else {
|
|
43479
|
+
target[out] = e[0] * x + e[4] * y + e[8] * z + e[12];
|
|
43480
|
+
target[out + 1] = e[1] * x + e[5] * y + e[9] * z + e[13];
|
|
43481
|
+
target[out + 2] = e[2] * x + e[6] * y + e[10] * z + e[14];
|
|
43482
|
+
}
|
|
43483
|
+
}
|
|
43484
|
+
}
|
|
43485
|
+
function skinnedBatch(THREE, merged, material, meshes) {
|
|
43486
|
+
const vertices = merged.attributes.position.count;
|
|
43487
|
+
const skinIndex = new Uint16Array(vertices * 4);
|
|
43488
|
+
const skinWeight = new Float32Array(vertices * 4);
|
|
43489
|
+
let at = 0;
|
|
43490
|
+
meshes.forEach((mesh22, bone) => {
|
|
43491
|
+
for (let k = 0; k < mesh22.geometry.attributes.position.count; k++, at++) {
|
|
43492
|
+
skinIndex[at * 4] = bone;
|
|
43493
|
+
skinWeight[at * 4] = 1;
|
|
43494
|
+
}
|
|
43495
|
+
});
|
|
43496
|
+
merged.setAttribute("skinIndex", new THREE.BufferAttribute(skinIndex, 4));
|
|
43497
|
+
merged.setAttribute("skinWeight", new THREE.BufferAttribute(skinWeight, 4));
|
|
43498
|
+
const mesh2 = new THREE.SkinnedMesh(merged, material);
|
|
43499
|
+
const identities = meshes.map(() => new THREE.Matrix4());
|
|
43500
|
+
mesh2.bind(new THREE.Skeleton(meshes, identities), new THREE.Matrix4());
|
|
43501
|
+
mesh2.frustumCulled = false;
|
|
43502
|
+
return mesh2;
|
|
43503
|
+
}
|
|
43504
|
+
function mergeGroup(THREE, { meshes, inverse: rootInverse, group, follow }) {
|
|
43505
|
+
const identity = new THREE.Matrix4();
|
|
43506
|
+
const inverse = follow ? identity : rootInverse;
|
|
43507
|
+
const first = meshes[0];
|
|
43508
|
+
const names = ATTRIBUTES.filter((name) => first.geometry.attributes[name] !== void 0);
|
|
43509
|
+
const pieces = meshes.map((mesh22) => {
|
|
43510
|
+
const local = new THREE.Matrix4().multiplyMatrices(inverse, mesh22.matrixWorld);
|
|
43511
|
+
const vertices = mesh22.geometry.attributes.position.count;
|
|
43512
|
+
const indexCount = mesh22.geometry.index ? mesh22.geometry.index.count : vertices;
|
|
43513
|
+
return { mesh: mesh22, local, vertices, indexCount };
|
|
43514
|
+
});
|
|
43515
|
+
const totalVertices = pieces.reduce((sum, piece) => sum + piece.vertices, 0);
|
|
43516
|
+
const totalIndices = pieces.reduce((sum, piece) => sum + piece.indexCount, 0);
|
|
43517
|
+
const merged = new THREE.BufferGeometry();
|
|
43518
|
+
const normalMatrix2 = new THREE.Matrix3();
|
|
43519
|
+
for (const name of names) {
|
|
43520
|
+
const size = first.geometry.attributes[name].itemSize;
|
|
43521
|
+
const array = new Float32Array(totalVertices * size);
|
|
43522
|
+
let offset = 0;
|
|
43523
|
+
for (const piece of pieces) {
|
|
43524
|
+
const matrix = follow ? identity : piece.local;
|
|
43525
|
+
copyAttribute(name, piece.mesh.geometry.attributes[name], array, offset, matrix, normalMatrix2.getNormalMatrix(matrix));
|
|
43526
|
+
offset += piece.vertices;
|
|
43527
|
+
}
|
|
43528
|
+
merged.setAttribute(name, new THREE.BufferAttribute(array, size));
|
|
43529
|
+
}
|
|
43530
|
+
const indexArray2 = totalVertices > 65535 ? new Uint32Array(totalIndices) : new Uint16Array(totalIndices);
|
|
43531
|
+
const indexAttribute = new THREE.BufferAttribute(indexArray2, 1);
|
|
43532
|
+
merged.setIndex(indexAttribute);
|
|
43533
|
+
const mesh2 = follow ? skinnedBatch(THREE, merged, first.material, meshes) : new THREE.Mesh(merged, first.material);
|
|
43534
|
+
mesh2.name = `batch:${first.material.name || first.material.type}`;
|
|
43535
|
+
mesh2.castShadow = first.castShadow;
|
|
43536
|
+
mesh2.receiveShadow = first.receiveShadow;
|
|
43537
|
+
mesh2.raycast = () => void 0;
|
|
43538
|
+
merged.computeBoundingBox();
|
|
43539
|
+
merged.computeBoundingSphere();
|
|
43540
|
+
group.add(mesh2);
|
|
43541
|
+
const batch = { mesh: mesh2, live: pieces.length, index: indexAttribute };
|
|
43542
|
+
const sources = [];
|
|
43543
|
+
let vertexOffset = 0;
|
|
43544
|
+
let indexOffset = 0;
|
|
43545
|
+
for (const piece of pieces) {
|
|
43546
|
+
const index = piece.mesh.geometry.index;
|
|
43547
|
+
if (index) for (let at = 0; at < piece.indexCount; at++) indexArray2[indexOffset + at] = index.getX(at) + vertexOffset;
|
|
43548
|
+
else for (let at = 0; at < piece.indexCount; at++) indexArray2[indexOffset + at] = at + vertexOffset;
|
|
43549
|
+
sources.push({
|
|
43550
|
+
mesh: piece.mesh,
|
|
43551
|
+
geometry: piece.mesh.geometry,
|
|
43552
|
+
material: piece.mesh.material,
|
|
43553
|
+
version: piece.mesh.geometry.attributes.position.version,
|
|
43554
|
+
local: piece.local,
|
|
43555
|
+
world: piece.mesh.matrixWorld.clone(),
|
|
43556
|
+
mask: piece.mesh.layers.mask,
|
|
43557
|
+
batch,
|
|
43558
|
+
start: indexOffset,
|
|
43559
|
+
count: piece.indexCount,
|
|
43560
|
+
released: false
|
|
43561
|
+
});
|
|
43562
|
+
piece.mesh.layers.set(BATCHED_LAYER);
|
|
43563
|
+
vertexOffset += piece.vertices;
|
|
43564
|
+
indexOffset += piece.indexCount;
|
|
43565
|
+
}
|
|
43566
|
+
return sources;
|
|
43567
|
+
}
|
|
42398
43568
|
|
|
42399
43569
|
// ../game-vehicles-driving/dist/index.js
|
|
42400
43570
|
var defaultArcadeSpec = {
|
|
@@ -42811,6 +43981,169 @@ var GameVehiclesThree = (() => {
|
|
|
42811
43981
|
return driver;
|
|
42812
43982
|
}
|
|
42813
43983
|
|
|
43984
|
+
// src/vehicle-lod.ts
|
|
43985
|
+
var VEHICLE_LOD_DISTANCE = 14;
|
|
43986
|
+
var BATCH_BUDGET_MS = 2;
|
|
43987
|
+
var isShown2 = (object2, top) => {
|
|
43988
|
+
for (let node = object2; node && node !== top; node = node.parent) if (node.visible === false) return false;
|
|
43989
|
+
return true;
|
|
43990
|
+
};
|
|
43991
|
+
function createVehicleLod(kit, context, root, full) {
|
|
43992
|
+
const THREE = kit.THREE;
|
|
43993
|
+
let isUploaded = false;
|
|
43994
|
+
let modelKey = "";
|
|
43995
|
+
const culled = /* @__PURE__ */ new Map();
|
|
43996
|
+
const keyOf = () => full.children.filter((child) => child.name !== "static-batches").map((child) => child.uuid).join();
|
|
43997
|
+
const armUpload = () => {
|
|
43998
|
+
for (const [mesh2, wasCulled] of culled) mesh2.frustumCulled = wasCulled;
|
|
43999
|
+
culled.clear();
|
|
44000
|
+
modelKey = keyOf();
|
|
44001
|
+
isUploaded = false;
|
|
44002
|
+
full.traverse((object2) => {
|
|
44003
|
+
if (!object2.isMesh || culled.has(object2)) return;
|
|
44004
|
+
culled.set(object2, object2.frustumCulled);
|
|
44005
|
+
object2.frustumCulled = false;
|
|
44006
|
+
});
|
|
44007
|
+
const probe = [...culled.keys()][0];
|
|
44008
|
+
if (!probe) {
|
|
44009
|
+
isUploaded = true;
|
|
44010
|
+
return;
|
|
44011
|
+
}
|
|
44012
|
+
const before = probe.onAfterRender;
|
|
44013
|
+
probe.onAfterRender = function(...args) {
|
|
44014
|
+
isUploaded = true;
|
|
44015
|
+
probe.onAfterRender = before;
|
|
44016
|
+
before?.apply(this, args);
|
|
44017
|
+
};
|
|
44018
|
+
};
|
|
44019
|
+
armUpload();
|
|
44020
|
+
let lite = null;
|
|
44021
|
+
let isFused = false;
|
|
44022
|
+
let isLocked = false;
|
|
44023
|
+
let batcher = null;
|
|
44024
|
+
let shadow = null;
|
|
44025
|
+
const castsShadow = /* @__PURE__ */ new Set();
|
|
44026
|
+
const shadowProxyOf = (fused) => {
|
|
44027
|
+
const positions = [];
|
|
44028
|
+
const indices = [];
|
|
44029
|
+
for (const mesh2 of fused.children) {
|
|
44030
|
+
if (!mesh2.isMesh || !mesh2.castShadow) continue;
|
|
44031
|
+
const geometry2 = mesh2.geometry.clone().applyMatrix4(mesh2.matrix);
|
|
44032
|
+
const offset = positions.length / 3;
|
|
44033
|
+
const position = geometry2.attributes.position;
|
|
44034
|
+
for (let at = 0; at < position.count; at++) positions.push(position.getX(at), position.getY(at), position.getZ(at));
|
|
44035
|
+
const index = geometry2.index ? Array.from(geometry2.index.array) : Array.from({ length: position.count }, (_, at) => at);
|
|
44036
|
+
for (const vertex of index) indices.push(vertex + offset);
|
|
44037
|
+
geometry2.dispose();
|
|
44038
|
+
}
|
|
44039
|
+
const geometry = new THREE.BufferGeometry();
|
|
44040
|
+
geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
|
|
44041
|
+
geometry.setIndex(indices);
|
|
44042
|
+
geometry.computeBoundingSphere();
|
|
44043
|
+
const proxy = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ colorWrite: false, depthWrite: false }));
|
|
44044
|
+
proxy.name = "vehicle-shadow";
|
|
44045
|
+
markKitOrigin(proxy, { owner: "game-vehicles-three", kind: "vehicle", id: root.name });
|
|
44046
|
+
proxy.castShadow = true;
|
|
44047
|
+
proxy.userData.skipLite = true;
|
|
44048
|
+
proxy.raycast = () => void 0;
|
|
44049
|
+
root.add(proxy);
|
|
44050
|
+
return proxy;
|
|
44051
|
+
};
|
|
44052
|
+
const drawFull = () => {
|
|
44053
|
+
if (!batcher && isLocked) return;
|
|
44054
|
+
if (!batcher) {
|
|
44055
|
+
lite ?? (lite = build());
|
|
44056
|
+
lite.visible = isFused;
|
|
44057
|
+
shadow ?? (shadow = shadowProxyOf(lite));
|
|
44058
|
+
shadow.visible = !isFused;
|
|
44059
|
+
full.traverse((object2) => {
|
|
44060
|
+
if (!object2.isMesh || !object2.castShadow) return;
|
|
44061
|
+
castsShadow.add(object2);
|
|
44062
|
+
object2.castShadow = false;
|
|
44063
|
+
});
|
|
44064
|
+
batcher = createStaticBatcher(THREE, full, { budgetMs: BATCH_BUDGET_MS });
|
|
44065
|
+
batcher.group.traverse((object2) => object2.userData.skipLite = true);
|
|
44066
|
+
markKitOrigin(batcher.group, { owner: "game-vehicles-three", kind: "vehicle", id: root.name });
|
|
44067
|
+
return;
|
|
44068
|
+
}
|
|
44069
|
+
batcher.update();
|
|
44070
|
+
if (!isLocked && batcher.stats().batched === 0 && batcher.stats().released > 0) {
|
|
44071
|
+
batcher.dispose();
|
|
44072
|
+
batcher = null;
|
|
44073
|
+
}
|
|
44074
|
+
};
|
|
44075
|
+
const build = () => {
|
|
44076
|
+
root.updateMatrixWorld(true);
|
|
44077
|
+
const inverse = new THREE.Matrix4().copy(root.matrixWorld).invert();
|
|
44078
|
+
const textured = [];
|
|
44079
|
+
full.traverse((object2) => {
|
|
44080
|
+
if (!object2.isMesh || object2.userData.skipLite || !isShown2(object2, full)) return;
|
|
44081
|
+
const materials = Array.isArray(object2.material) ? object2.material : [object2.material];
|
|
44082
|
+
if (materials.some((material) => material?.map && !/plate/i.test(material.name ?? "")) && !/plate/i.test(object2.name ?? "")) textured.push(object2);
|
|
44083
|
+
});
|
|
44084
|
+
for (const mesh2 of textured) mesh2.userData.skipLite = true;
|
|
44085
|
+
const fusion = fuse(THREE, { ...context, vehicle: full }, inverse, null, primaryPaint(context), kit.lodHiddenMaterials());
|
|
44086
|
+
for (const mesh2 of textured) delete mesh2.userData.skipLite;
|
|
44087
|
+
const group = new THREE.Group();
|
|
44088
|
+
group.name = "vehicle-lite";
|
|
44089
|
+
markKitOrigin(group, { owner: "game-vehicles-three", kind: "vehicle", id: root.name });
|
|
44090
|
+
for (const mesh2 of liteMeshes(THREE, fusion, liteMaterials(THREE, kit.studioEnvironment()))) group.add(mesh2);
|
|
44091
|
+
for (const mesh2 of textured) {
|
|
44092
|
+
const copy = new THREE.Mesh(mesh2.geometry, mesh2.material);
|
|
44093
|
+
copy.matrixAutoUpdate = false;
|
|
44094
|
+
copy.matrix.multiplyMatrices(inverse, mesh2.matrixWorld);
|
|
44095
|
+
copy.castShadow = mesh2.castShadow;
|
|
44096
|
+
copy.receiveShadow = mesh2.receiveShadow;
|
|
44097
|
+
group.add(copy);
|
|
44098
|
+
}
|
|
44099
|
+
root.add(group);
|
|
44100
|
+
return group;
|
|
44101
|
+
};
|
|
44102
|
+
const show = (shouldFuse) => {
|
|
44103
|
+
if (shouldFuse === isFused) return;
|
|
44104
|
+
isFused = shouldFuse;
|
|
44105
|
+
full.visible = !shouldFuse;
|
|
44106
|
+
if (lite) lite.visible = shouldFuse;
|
|
44107
|
+
if (shadow) shadow.visible = !shouldFuse;
|
|
44108
|
+
};
|
|
44109
|
+
const api = {
|
|
44110
|
+
update(distance, touched, posed = false) {
|
|
44111
|
+
isLocked || (isLocked = touched);
|
|
44112
|
+
if (keyOf() !== modelKey) {
|
|
44113
|
+
api.dispose();
|
|
44114
|
+
armUpload();
|
|
44115
|
+
}
|
|
44116
|
+
if (!isUploaded) return show(false);
|
|
44117
|
+
if (culled.size > 0) {
|
|
44118
|
+
for (const [mesh2, wasCulled] of culled) mesh2.frustumCulled = wasCulled;
|
|
44119
|
+
culled.clear();
|
|
44120
|
+
}
|
|
44121
|
+
const shouldFuse = !isLocked && !posed && distance > (isFused ? VEHICLE_LOD_DISTANCE * 0.9 : VEHICLE_LOD_DISTANCE);
|
|
44122
|
+
if (shouldFuse && !lite) lite = build();
|
|
44123
|
+
show(shouldFuse);
|
|
44124
|
+
if (!shouldFuse) drawFull();
|
|
44125
|
+
},
|
|
44126
|
+
get fused() {
|
|
44127
|
+
return isFused;
|
|
44128
|
+
},
|
|
44129
|
+
dispose() {
|
|
44130
|
+
show(false);
|
|
44131
|
+
batcher?.dispose();
|
|
44132
|
+
batcher = null;
|
|
44133
|
+
for (const mesh2 of castsShadow) mesh2.castShadow = true;
|
|
44134
|
+
castsShadow.clear();
|
|
44135
|
+
shadow?.removeFromParent();
|
|
44136
|
+
shadow?.geometry.dispose();
|
|
44137
|
+
shadow = null;
|
|
44138
|
+
if (!lite) return;
|
|
44139
|
+
lite.removeFromParent();
|
|
44140
|
+
lite.traverse((object2) => object2.geometry?.dispose());
|
|
44141
|
+
lite = null;
|
|
44142
|
+
}
|
|
44143
|
+
};
|
|
44144
|
+
return api;
|
|
44145
|
+
}
|
|
44146
|
+
|
|
42814
44147
|
// src/test-area/text.ts
|
|
42815
44148
|
var scenarioIds = [
|
|
42816
44149
|
"vehicles_9mm_windshield",
|
|
@@ -43202,7 +44535,7 @@ var GameVehiclesThree = (() => {
|
|
|
43202
44535
|
];
|
|
43203
44536
|
};
|
|
43204
44537
|
|
|
43205
|
-
// ../game-audio/dist/
|
|
44538
|
+
// ../game-audio/dist/chunk-DZITVFMJ.js
|
|
43206
44539
|
var DISTANCE_CURVES = {
|
|
43207
44540
|
/** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
|
|
43208
44541
|
game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
|
|
@@ -43217,31 +44550,6 @@ var GameVehiclesThree = (() => {
|
|
|
43217
44550
|
/** The game's animal calls: `max(0, 1 − d / 70) × 0.25`, silent at or below 0.005 (jogo L66627–66628). */
|
|
43218
44551
|
animals: { model: "linear", range: 70, scale: 0.25, minGain: 5e-3 }
|
|
43219
44552
|
};
|
|
43220
|
-
var mix_profiles_default = [
|
|
43221
|
-
{
|
|
43222
|
-
id: "game",
|
|
43223
|
-
master: 0.5,
|
|
43224
|
-
noise: { seconds: 1.5, create: "lazy" },
|
|
43225
|
-
sources: ["jogo:L62077-62081 master 0.5 to the output", "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"]
|
|
43226
|
-
},
|
|
43227
|
-
{
|
|
43228
|
-
id: "art_scene",
|
|
43229
|
-
master: 0.85,
|
|
43230
|
-
compressor: { threshold: -12, ratio: 5 },
|
|
43231
|
-
reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
|
|
43232
|
-
noise: { seconds: 2, create: "eager" },
|
|
43233
|
-
sources: [
|
|
43234
|
-
"cena:L1308 compressor -12 dB, ratio 5",
|
|
43235
|
-
"cena:L1309 master 0.85",
|
|
43236
|
-
"cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
|
|
43237
|
-
"cena:L1311 wet 0.4",
|
|
43238
|
-
"cena:L1312 noise 2 s made with the chain"
|
|
43239
|
-
]
|
|
43240
|
-
}
|
|
43241
|
-
];
|
|
43242
|
-
var MIX_PROFILES = Object.fromEntries(
|
|
43243
|
-
mix_profiles_default.map((profile) => [profile.id, profile])
|
|
43244
|
-
);
|
|
43245
44553
|
var game_sfx_default = {
|
|
43246
44554
|
sources: ["jogo:L62089-62107"],
|
|
43247
44555
|
note: "The game's sfx table P: [lowpass cutoff Hz, peak gain, seconds]. Played as a noise burst with the game's distance curve (falloff game_sfx) and a 0.05 s tail. Team names in `team`.",
|
|
@@ -43374,6 +44682,31 @@ var GameVehiclesThree = (() => {
|
|
|
43374
44682
|
var LIBRARY_SOUNDS = [horn_default, radio_static_default, siren_default];
|
|
43375
44683
|
var LIBRARY_LOOPS = [engine_saw_default, rotor_blades_default, crowd_rumble_default];
|
|
43376
44684
|
var LIBRARY_BANK = { id: "library", sounds: LIBRARY_SOUNDS, loops: LIBRARY_LOOPS };
|
|
44685
|
+
var mix_profiles_default = [
|
|
44686
|
+
{
|
|
44687
|
+
id: "game",
|
|
44688
|
+
master: 0.5,
|
|
44689
|
+
noise: { seconds: 1.5, create: "lazy" },
|
|
44690
|
+
sources: ["jogo:L62077-62081 master 0.5 to the output", "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"]
|
|
44691
|
+
},
|
|
44692
|
+
{
|
|
44693
|
+
id: "art_scene",
|
|
44694
|
+
master: 0.85,
|
|
44695
|
+
compressor: { threshold: -12, ratio: 5 },
|
|
44696
|
+
reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
|
|
44697
|
+
noise: { seconds: 2, create: "eager" },
|
|
44698
|
+
sources: [
|
|
44699
|
+
"cena:L1308 compressor -12 dB, ratio 5",
|
|
44700
|
+
"cena:L1309 master 0.85",
|
|
44701
|
+
"cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
|
|
44702
|
+
"cena:L1311 wet 0.4",
|
|
44703
|
+
"cena:L1312 noise 2 s made with the chain"
|
|
44704
|
+
]
|
|
44705
|
+
}
|
|
44706
|
+
];
|
|
44707
|
+
var MIX_PROFILES = Object.fromEntries(
|
|
44708
|
+
mix_profiles_default.map((profile) => [profile.id, profile])
|
|
44709
|
+
);
|
|
43377
44710
|
var BENCH_BANK = {
|
|
43378
44711
|
id: "audio_bench",
|
|
43379
44712
|
sounds: [
|
|
@@ -43420,6 +44753,236 @@ var GameVehiclesThree = (() => {
|
|
|
43420
44753
|
]
|
|
43421
44754
|
};
|
|
43422
44755
|
|
|
44756
|
+
// ../game-audio/dist/index.js
|
|
44757
|
+
var ATTACK = 2e-3;
|
|
44758
|
+
function lengthOf(d, attack) {
|
|
44759
|
+
if (typeof d === "number") return attack + d;
|
|
44760
|
+
if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
|
|
44761
|
+
return { ...d, add: (d.add ?? 0) + attack };
|
|
44762
|
+
}
|
|
44763
|
+
var envelope = (attack) => [
|
|
44764
|
+
[0, 1e-4, "set", "fixed"],
|
|
44765
|
+
[attack, 1, "exp"],
|
|
44766
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
44767
|
+
];
|
|
44768
|
+
function sceneNoise(o) {
|
|
44769
|
+
const a = o.a ?? ATTACK;
|
|
44770
|
+
const over = typeof o.d === "number" ? o.d : 0;
|
|
44771
|
+
return {
|
|
44772
|
+
filters: {
|
|
44773
|
+
mode: "serial",
|
|
44774
|
+
bank: [{ type: o.type ?? "lowpass", frequency: o.f2 === void 0 ? { set: o.f } : { set: o.f, to: o.f2, curve: "exp", over }, q: o.q ?? 0.7 }]
|
|
44775
|
+
},
|
|
44776
|
+
duration: lengthOf(o.d, a),
|
|
44777
|
+
gain: o.vol,
|
|
44778
|
+
...o.delay === void 0 ? {} : { at: o.delay },
|
|
44779
|
+
...o.wet === void 0 ? {} : { wet: o.wet },
|
|
44780
|
+
source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
|
|
44781
|
+
envelope: envelope(a)
|
|
44782
|
+
};
|
|
44783
|
+
}
|
|
44784
|
+
function sceneTone(o) {
|
|
44785
|
+
return {
|
|
44786
|
+
source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
|
|
44787
|
+
duration: lengthOf(o.d, ATTACK),
|
|
44788
|
+
gain: o.vol,
|
|
44789
|
+
...o.delay === void 0 ? {} : { at: o.delay },
|
|
44790
|
+
...o.wet === void 0 ? {} : { wet: o.wet },
|
|
44791
|
+
envelope: envelope(ATTACK)
|
|
44792
|
+
};
|
|
44793
|
+
}
|
|
44794
|
+
function sceneVoice(o) {
|
|
44795
|
+
return {
|
|
44796
|
+
duration: lengthOf(o.d, 0),
|
|
44797
|
+
gain: o.vol,
|
|
44798
|
+
wet: 0.45,
|
|
44799
|
+
source: {
|
|
44800
|
+
kind: "formant_voice",
|
|
44801
|
+
wave: o.wave ?? "sawtooth",
|
|
44802
|
+
frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
|
|
44803
|
+
vibrato: { rate: [4, 10], depth: 0.08 },
|
|
44804
|
+
formants: [
|
|
44805
|
+
{ frequency: o.fA, q: 4 },
|
|
44806
|
+
{ frequency: o.fB, q: 6 }
|
|
44807
|
+
],
|
|
44808
|
+
breath: { frequency: o.fB, q: 1.1, gain: o.noise },
|
|
44809
|
+
mix: 3.2
|
|
44810
|
+
},
|
|
44811
|
+
envelope: [
|
|
44812
|
+
[0, 1e-4, "set", "fixed"],
|
|
44813
|
+
[0.1, 1, "exp"],
|
|
44814
|
+
["0.6d", 1, "set"],
|
|
44815
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
44816
|
+
]
|
|
44817
|
+
};
|
|
44818
|
+
}
|
|
44819
|
+
var TAKES = [
|
|
44820
|
+
{ f: 1, t: 1, v: 1 },
|
|
44821
|
+
{ f: 1.08, t: 0.9, v: 0.92 },
|
|
44822
|
+
{ f: 0.93, t: 1.12, v: 1.05 },
|
|
44823
|
+
{ f: 1.03, t: 1.05, v: 0.88 }
|
|
44824
|
+
];
|
|
44825
|
+
var FOOTSTEP_FALLOFF = { model: "inverse_square", k: 0.03, cutoffK: 0.03, minGain: 4e-3 };
|
|
44826
|
+
var BODY_FALLOFF = { model: "inverse_square", k: 0.012, cutoffK: 0.015, minGain: 4e-3 };
|
|
44827
|
+
var SURFACES = {
|
|
44828
|
+
concrete: { heel: { f: 95, d: 0.05, vol: 0.55 }, roll: { type: "bandpass", f: 2300, q: 1.1, d: 0.05, vol: 0.38 }, grains: { count: 2, f: 4200, q: 2, spread: 0.04, vol: 0.08 } },
|
|
44829
|
+
asphalt: { heel: { f: 90, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 1700, q: 0.9, d: 0.06, vol: 0.36 }, grains: { count: 4, f: 3600, q: 2.5, spread: 0.06, vol: 0.1 } },
|
|
44830
|
+
wood: { heel: { f: 130, d: 0.08, vol: 0.55 }, roll: { type: "lowpass", f: 1300, q: 0.8, d: 0.06, vol: 0.3 }, ring: [{ f: 210, d: 0.1, vol: 0.18 }, { f: 470, d: 0.06, vol: 0.06 }] },
|
|
44831
|
+
tile: { heel: { f: 110, d: 0.04, vol: 0.45 }, roll: { type: "bandpass", f: 3600, q: 2, d: 0.03, vol: 0.42 }, ring: [{ f: 1900, d: 0.03, vol: 0.05 }] },
|
|
44832
|
+
dirt: { heel: { f: 75, d: 0.06, vol: 0.45 }, roll: { type: "lowpass", f: 750, q: 0.7, d: 0.09, vol: 0.4 }, grains: { count: 3, f: 1800, q: 1.5, spread: 0.07, vol: 0.06 } },
|
|
44833
|
+
grass: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "highpass", f: 2600, q: 0.7, d: 0.13, vol: 0.28 }, grains: { count: 4, f: 5200, q: 1.2, spread: 0.1, vol: 0.07 } },
|
|
44834
|
+
gravel: { heel: { f: 80, d: 0.05, vol: 0.4 }, roll: { type: "lowpass", f: 950, q: 0.8, d: 0.1, vol: 0.3 }, grains: { count: 8, f: 2600, q: 3, spread: 0.13, vol: 0.2 } },
|
|
44835
|
+
sand: { heel: { f: 65, d: 0.06, vol: 0.3 }, roll: { type: "bandpass", f: 1500, q: 0.6, d: 0.16, vol: 0.3 }, grains: { count: 5, f: 4800, q: 1.5, spread: 0.12, vol: 0.05 } },
|
|
44836
|
+
metal: { heel: { f: 120, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 2800, q: 1.5, d: 0.03, vol: 0.25 }, ring: [{ f: 420, d: 0.25, vol: 0.16 }, { f: 1130, d: 0.18, vol: 0.09 }, { f: 2470, d: 0.1, vol: 0.04 }] },
|
|
44837
|
+
water: { heel: { f: 70, d: 0.04, vol: 0.25 }, roll: { type: "bandpass", f: 3e3, q: 1.2, d: 0.1, vol: 0.18 }, splash: { f: 1100, f2: 380, d: 0.22, vol: 0.55 }, grains: { count: 4, f: 2400, q: 6, spread: 0.18, vol: 0.12 } },
|
|
44838
|
+
glass: { heel: { f: 90, d: 0.04, vol: 0.4 }, roll: { type: "highpass", f: 4500, q: 0.8, d: 0.06, vol: 0.2 }, grains: { count: 9, f: 6200, q: 5, spread: 0.12, vol: 0.22 }, ring: [{ f: 3900, d: 0.04, vol: 0.04 }, { f: 5300, d: 0.03, vol: 0.03 }] },
|
|
44839
|
+
carpet: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "lowpass", f: 420, q: 0.7, d: 0.07, vol: 0.3 } }
|
|
44840
|
+
};
|
|
44841
|
+
var GAITS = {
|
|
44842
|
+
walk: { vol: 1, bright: 1, toe: 0.075, short: 1 },
|
|
44843
|
+
run: { vol: 1.35, bright: 1.25, toe: 0.035, short: 0.75 }
|
|
44844
|
+
};
|
|
44845
|
+
var grainLayers = (g, take, start2, scale) => Array.from(
|
|
44846
|
+
{ length: g.count },
|
|
44847
|
+
(_, i) => sceneNoise({ type: "bandpass", f: [g.f * take.f * 0.85, g.f * take.f * 1.15], q: g.q, d: [8e-3, 0.02], vol: g.vol * scale * take.v, delay: start2 + (i + 0.5) / g.count * g.spread * take.t })
|
|
44848
|
+
);
|
|
44849
|
+
var ringLayers = (rings, take, scale, at = 0) => rings.map((r) => sceneTone({ f: r.f * take.f, d: r.d * take.t, vol: r.vol * scale * take.v, delay: at }));
|
|
44850
|
+
function footLayers(surface, take, gait, weight = 1) {
|
|
44851
|
+
const s = SURFACES[surface];
|
|
44852
|
+
const toe = gait.toe * take.t;
|
|
44853
|
+
return [
|
|
44854
|
+
sceneTone({ f: s.heel.f * take.f * weight ** -0.3, f2: s.heel.f * take.f * 0.6, d: s.heel.d * gait.short * take.t * weight ** 0.5, vol: s.heel.vol * gait.vol * take.v * weight ** 0.5 }),
|
|
44855
|
+
sceneNoise({ type: "lowpass", f: 500 * gait.bright, d: 0.02 * take.t, vol: 0.18 * gait.vol * take.v * weight ** 0.5 }),
|
|
44856
|
+
sceneNoise({ type: s.roll.type, f: [s.roll.f * take.f * gait.bright * 0.92, s.roll.f * take.f * gait.bright * 1.08], q: s.roll.q, d: s.roll.d * gait.short * take.t * weight ** 0.3, vol: s.roll.vol * gait.vol * take.v, delay: toe }),
|
|
44857
|
+
...s.grains ? grainLayers(s.grains, take, toe * 0.5, gait.vol * weight ** 0.5) : [],
|
|
44858
|
+
...s.ring ? ringLayers(s.ring, take, gait.vol * weight ** 0.5) : [],
|
|
44859
|
+
...s.splash ? [sceneNoise({ type: "bandpass", f: s.splash.f * take.f, f2: s.splash.f2 * take.f, q: 1.4, d: s.splash.d * take.t * weight ** 0.4, vol: s.splash.vol * gait.vol * take.v * weight ** 0.5 })] : []
|
|
44860
|
+
];
|
|
44861
|
+
}
|
|
44862
|
+
var quieter = (level, layers) => layers.map((layer) => {
|
|
44863
|
+
const gain = layer.gain ?? 1;
|
|
44864
|
+
if (typeof gain === "number") return { ...layer, gain: gain * level };
|
|
44865
|
+
if (Array.isArray(gain)) return { ...layer, gain: [gain[0] * level, gain[1] * level] };
|
|
44866
|
+
return layer;
|
|
44867
|
+
});
|
|
44868
|
+
var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
|
|
44869
|
+
var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
|
|
44870
|
+
function takes(id, layersOf, falloff, rate = [0.96, 1.04], bus) {
|
|
44871
|
+
const sounds = TAKES.map((take, index) => recipe(takeId(id, index), layersOf(take, index), falloff, bus));
|
|
44872
|
+
return { sounds, set: { id, variants: sounds.map((sound) => sound.id), rate } };
|
|
44873
|
+
}
|
|
44874
|
+
var footstep = (surface, gait) => takes(footstepSoundId(surface, gait), (take) => footLayers(surface, take, GAITS[gait]), FOOTSTEP_FALLOFF);
|
|
44875
|
+
var LANDINGS = { light: { weight: 1.4, cloth: 0.12 }, heavy: { weight: 2.4, cloth: 0.22 } };
|
|
44876
|
+
var LANDING_LEVEL = 0.65;
|
|
44877
|
+
var landing = (surface, landing2) => takes(
|
|
44878
|
+
`land_${surface}_${landing2}`,
|
|
44879
|
+
(take) => quieter(LANDING_LEVEL, [
|
|
44880
|
+
// both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
|
|
44881
|
+
...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
|
|
44882
|
+
...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer) => ({ ...layer, at: (typeof layer.at === "number" ? layer.at : 0) + 0.018 * take.t })),
|
|
44883
|
+
sceneTone({ f: 60 * take.f, f2: 38, d: 0.14 * LANDINGS[landing2].weight * take.t, vol: 0.22 * LANDINGS[landing2].weight ** 0.7 * take.v, delay: 0.02 }),
|
|
44884
|
+
sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
|
|
44885
|
+
]),
|
|
44886
|
+
FOOTSTEP_FALLOFF
|
|
44887
|
+
);
|
|
44888
|
+
var whoosh = (blow, from, to, d, vol, q = 1.6) => (take) => [
|
|
44889
|
+
// the air: a band sweeping up as the limb speeds up; it peaks `SWING_LEAD_SECONDS` in, the moment before the contact, then dies away
|
|
44890
|
+
sceneNoise({ type: "bandpass", f: from * take.f, f2: to * take.f, q, a: SWING_LEAD_SECONDS[blow], d: d * take.t, vol: vol * take.v }),
|
|
44891
|
+
sceneNoise({ type: "lowpass", f: 600 * take.f, a: SWING_LEAD_SECONDS[blow], d: d * take.t * 0.8, vol: vol * 0.35 * take.v })
|
|
44892
|
+
];
|
|
44893
|
+
var fleshHit = (o) => (take) => [
|
|
44894
|
+
sceneNoise({ type: "bandpass", f: o.slapF * take.f, q: 1.2, d: 0.03 * take.t, vol: o.slap * take.v }),
|
|
44895
|
+
sceneNoise({ type: "highpass", f: 3500 * take.f, d: 0.012, vol: o.slap * 0.4 * take.v }),
|
|
44896
|
+
sceneTone({ f: o.thudF * take.f, f2: o.thudF * 0.5, d: o.thudD * take.t, vol: o.thud * take.v }),
|
|
44897
|
+
sceneNoise({ type: "lowpass", f: 600 * take.f, d: o.thudD * 1.2 * take.t, vol: o.thud * 0.5 * take.v }),
|
|
44898
|
+
...o.crack ? [sceneNoise({ type: "highpass", f: 2500, d: 0.015, vol: o.crack * take.v, delay: 6e-3 }), sceneTone({ wave: "square", f: 900 * take.f, f2: 300, d: 0.02, vol: o.crack * 0.15 * take.v, delay: 6e-3 })] : [],
|
|
44899
|
+
...o.wet ? [sceneNoise({ type: "bandpass", f: 1400 * take.f, f2: 500, q: 2.5, d: 0.12 * take.t, vol: o.wet * take.v, delay: 0.01 })] : []
|
|
44900
|
+
];
|
|
44901
|
+
var knock = (o) => (take) => [
|
|
44902
|
+
sceneNoise({ type: "bandpass", f: o.clickF * take.f, q: 1.5, d: 0.018, vol: o.click * take.v }),
|
|
44903
|
+
...o.tones.map(([f, d, vol]) => sceneTone({ f: f * take.f, d: d * take.t, vol: vol * take.v }))
|
|
44904
|
+
];
|
|
44905
|
+
var voice = (o) => (take) => [
|
|
44906
|
+
sceneVoice({ f0: o.f0 * take.f, f1: o.f1 * take.f, d: o.d * take.t, fA: o.fA * take.f, fB: o.fB * take.f, noise: o.noise, vol: o.vol * take.v })
|
|
44907
|
+
];
|
|
44908
|
+
var BODY = {
|
|
44909
|
+
jump: (take) => [
|
|
44910
|
+
sceneNoise({ type: "bandpass", f: 900 * take.f, f2: 1700 * take.f, q: 1, a: 0.03, d: 0.12, vol: 0.18 * take.v }),
|
|
44911
|
+
sceneNoise({ type: "bandpass", f: 2200 * take.f, q: 0.8, d: 0.04, vol: 0.12 * take.v }),
|
|
44912
|
+
sceneTone({ f: 85 * take.f, f2: 60, d: 0.04, vol: 0.2 * take.v })
|
|
44913
|
+
],
|
|
44914
|
+
cloth_rustle: (take) => [
|
|
44915
|
+
{ ...sceneNoise({ type: "bandpass", f: 2400 * take.f, q: 0.8, a: 0.06, d: 0.22 * take.t, vol: 0.12 * take.v }), modulators: [{ target: "gain", wave: "triangle", rate: [9, 14], depth: 0.5 }] }
|
|
44916
|
+
],
|
|
44917
|
+
body_fall: (take) => [
|
|
44918
|
+
sceneTone({ f: 58 * take.f, f2: 35, d: 0.28 * take.t, vol: 0.9 * take.v }),
|
|
44919
|
+
sceneNoise({ type: "lowpass", f: 480 * take.f, d: 0.3 * take.t, vol: 0.55 * take.v }),
|
|
44920
|
+
sceneNoise({ type: "bandpass", f: 1300 * take.f, q: 1, d: 0.05, vol: 0.3 * take.v }),
|
|
44921
|
+
// the limbs a moment later, and the clothes
|
|
44922
|
+
sceneTone({ f: 75 * take.f, f2: 45, d: 0.12, vol: 0.4 * take.v, delay: 0.13 * take.t }),
|
|
44923
|
+
sceneNoise({ type: "bandpass", f: 2200, q: 0.8, d: 0.2, vol: 0.12 * take.v, delay: 0.05 })
|
|
44924
|
+
],
|
|
44925
|
+
fall_damage: (take) => [
|
|
44926
|
+
...BODY.body_fall(take),
|
|
44927
|
+
sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
|
|
44928
|
+
...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer) => ({ ...layer, at: 0.08 }))
|
|
44929
|
+
],
|
|
44930
|
+
player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
|
|
44931
|
+
player_breath_run: (take) => [
|
|
44932
|
+
sceneNoise({ type: "bandpass", f: 1200 * take.f, q: 1, a: 0.08, d: 0.22 * take.t, vol: 0.25 * take.v }),
|
|
44933
|
+
sceneNoise({ type: "bandpass", f: 900 * take.f, q: 1, a: 0.06, d: 0.26 * take.t, vol: 0.3 * take.v, delay: 0.3 * take.t })
|
|
44934
|
+
],
|
|
44935
|
+
player_death: voice({ f0: 135, f1: 68, d: 1, fA: 560, fB: 980, noise: 0.35, vol: 0.4 }),
|
|
44936
|
+
bite: (take) => [
|
|
44937
|
+
sceneNoise({ type: "highpass", f: 2600, d: 0.015, vol: 0.4 * take.v }),
|
|
44938
|
+
sceneNoise({ type: "bandpass", f: 1600 * take.f, f2: 450, q: 2.2, d: 0.16 * take.t, vol: 0.55 * take.v, delay: 0.01 }),
|
|
44939
|
+
sceneNoise({ type: "bandpass", f: 3200 * take.f, f2: 1800, q: 1.2, d: 0.22 * take.t, vol: 0.25 * take.v, delay: 0.09 }),
|
|
44940
|
+
sceneTone({ f: 120 * take.f, f2: 70, d: 0.06, vol: 0.3 * take.v })
|
|
44941
|
+
],
|
|
44942
|
+
grab: (take) => [
|
|
44943
|
+
sceneNoise({ type: "bandpass", f: 1900 * take.f, q: 0.9, a: 4e-3, d: 0.13 * take.t, vol: 0.32 * take.v }),
|
|
44944
|
+
sceneTone({ f: 95 * take.f, f2: 60, d: 0.07, vol: 0.3 * take.v }),
|
|
44945
|
+
sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 0.2 * take.v })
|
|
44946
|
+
],
|
|
44947
|
+
punch_swing: whoosh("punch", 450, 1500, 0.16, 0.9),
|
|
44948
|
+
punch_hit_body: fleshHit({ slap: 0.55, slapF: 1500, thud: 0.6, thudF: 115, thudD: 0.09 }),
|
|
44949
|
+
punch_hit_face: fleshHit({ slap: 0.75, slapF: 2300, thud: 0.45, thudF: 150, thudD: 0.07, crack: 0.25 }),
|
|
44950
|
+
punch_hit_object: knock({ clickF: 1800, click: 0.3, tones: [[210, 0.04, 0.25]] }),
|
|
44951
|
+
kick_swing: whoosh("kick", 280, 950, 0.22, 1, 1.3),
|
|
44952
|
+
kick_hit: fleshHit({ slap: 0.45, slapF: 1100, thud: 0.85, thudF: 82, thudD: 0.14 }),
|
|
44953
|
+
kick_hit_object: knock({ clickF: 1e3, click: 0.3, tones: [[92, 0.08, 0.5]] }),
|
|
44954
|
+
shove_two_hands: (take) => [
|
|
44955
|
+
sceneNoise({ type: "bandpass", f: 950 * take.f, q: 0.9, a: 0.01, d: 0.13 * take.t, vol: 0.38 * take.v }),
|
|
44956
|
+
sceneTone({ f: 100 * take.f, f2: 60, d: 0.1 * take.t, vol: 0.45 * take.v }),
|
|
44957
|
+
sceneNoise({ type: "bandpass", f: 2300, q: 0.8, d: 0.16, vol: 0.12 * take.v, delay: 0.03 })
|
|
44958
|
+
],
|
|
44959
|
+
knife_swing: whoosh("blade", 1900, 4600, 0.12, 1.1, 2.2),
|
|
44960
|
+
knife_stab: fleshHit({ slap: 0.2, slapF: 1200, thud: 0.4, thudF: 160, thudD: 0.06, wet: 0.5 }),
|
|
44961
|
+
knife_slash: (take) => [
|
|
44962
|
+
sceneNoise({ type: "highpass", f: 3200 * take.f, f2: 1800, d: 0.13 * take.t, vol: 0.35 * take.v }),
|
|
44963
|
+
sceneNoise({ type: "bandpass", f: 1300 * take.f, f2: 600, q: 2.5, d: 0.12 * take.t, vol: 0.35 * take.v, delay: 0.02 })
|
|
44964
|
+
],
|
|
44965
|
+
knife_hit_object: knock({ clickF: 3200, click: 0.25, tones: [[2800, 0.08, 0.07], [4150, 0.06, 0.05]] }),
|
|
44966
|
+
blunt_swing: whoosh("blunt", 220, 760, 0.28, 1.1, 1.5),
|
|
44967
|
+
blunt_hit: fleshHit({ slap: 0.35, slapF: 900, thud: 1, thudF: 70, thudD: 0.18, crack: 0.3 }),
|
|
44968
|
+
blunt_hit_object: knock({ clickF: 1400, click: 0.35, tones: [[300, 0.12, 0.25], [760, 0.08, 0.12]] }),
|
|
44969
|
+
axe_swing: whoosh("chop", 190, 620, 0.3, 1.2, 1.4),
|
|
44970
|
+
axe_chop: fleshHit({ slap: 0.3, slapF: 1400, thud: 0.75, thudF: 140, thudD: 0.1, crack: 0.35, wet: 0.45 }),
|
|
44971
|
+
axe_hit_object: knock({ clickF: 2200, click: 0.4, tones: [[1500, 0.15, 0.1], [2300, 0.1, 0.06], [140, 0.1, 0.35]] })
|
|
44972
|
+
};
|
|
44973
|
+
var BODY_BUS = { player_hurt: "voices", player_death: "voices", player_breath_run: "voices", fall_damage: "voices" };
|
|
44974
|
+
var built = [
|
|
44975
|
+
...SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstep(surface, gait))),
|
|
44976
|
+
...SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((l) => landing(surface, l))),
|
|
44977
|
+
...BODY_SOUND_IDS.map((id) => takes(id, BODY[id], BODY_FALLOFF, id.endsWith("_swing") ? [0.94, 1.06] : [0.96, 1.04], BODY_BUS[id]))
|
|
44978
|
+
];
|
|
44979
|
+
var BODY_FOLEY_BANK = {
|
|
44980
|
+
id: "body_foley",
|
|
44981
|
+
sounds: built.flatMap((entry) => entry.sounds),
|
|
44982
|
+
sets: built.map((entry) => entry.set)
|
|
44983
|
+
};
|
|
44984
|
+
var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
44985
|
+
|
|
43423
44986
|
// src/test-area/types.ts
|
|
43424
44987
|
var defaultEffectIds = {
|
|
43425
44988
|
ignite: "vehicle_fire",
|
|
@@ -43442,10 +45005,12 @@ var GameVehiclesThree = (() => {
|
|
|
43442
45005
|
var ITEM_OF = { compact_hatch: "hatch", midsize_sedan: "sedan", midsize_pickup: "pickup", sport_bike: "sport_bike", box_truck: "box_truck", city_bus: "city_bus", battle_tank: "battle_tank", attack_helicopter: "attack_helicopter" };
|
|
43443
45006
|
var itemId = (key) => ITEM_OF[key] ?? key;
|
|
43444
45007
|
var LANE = { z: 11, from: -12, wall: 13 };
|
|
45008
|
+
var LEAF_REST_RADIANS = 0.02;
|
|
43445
45009
|
var MATERIAL_COLOURS = { metal: 13421772, glass: 4500223, rubber: 16750848, fuel: 16724736 };
|
|
43446
45010
|
var ZONE_COLOURS = { door: 5627221, hood: 14540083, lid: 14522675, bumper: 14505437, glass: 4500223, light: 16777215, wheel: 16742195 };
|
|
43447
45011
|
var BODY_HEIGHT = 1.7;
|
|
43448
45012
|
function createVehiclesTestModule(options = {}) {
|
|
45013
|
+
const fleet = options.fleet ?? { colorFromSeed, fleetDims, installFleet, paintPalettes, paletteOf };
|
|
43449
45014
|
const locale = options.locale ?? "pt-BR";
|
|
43450
45015
|
const text = TEXT[locale] ?? TEXT["pt-BR"];
|
|
43451
45016
|
const keys = options.vehicles ?? DEFAULT_TEST_VEHICLES;
|
|
@@ -43456,6 +45021,7 @@ var GameVehiclesThree = (() => {
|
|
|
43456
45021
|
const settings = { quality: "high", paint: false, seed: 1, palette: "auto", driving: "arcade", surface: "dry_asphalt" };
|
|
43457
45022
|
const kits = /* @__PURE__ */ new WeakMap();
|
|
43458
45023
|
const placed = /* @__PURE__ */ new Map();
|
|
45024
|
+
const cabins = createCabinSmoke();
|
|
43459
45025
|
let host = null;
|
|
43460
45026
|
let three = null;
|
|
43461
45027
|
let counter = 0;
|
|
@@ -43469,23 +45035,29 @@ var GameVehiclesThree = (() => {
|
|
|
43469
45035
|
engineLoop?.stop();
|
|
43470
45036
|
engineLoop = void 0;
|
|
43471
45037
|
};
|
|
45038
|
+
const particleMaterials = {
|
|
45039
|
+
own: (build) => isRenderPools(host?.pools) ? host.pools.materials.own(build) : build(),
|
|
45040
|
+
release: (material) => isRenderPools(host?.pools) ? host.pools.materials.release(material) : material.dispose?.()
|
|
45041
|
+
};
|
|
43472
45042
|
const kitFor = (THREE) => {
|
|
43473
45043
|
three = THREE;
|
|
43474
45044
|
let kit = kits.get(THREE);
|
|
43475
45045
|
if (!kit) {
|
|
43476
45046
|
if (options.createKit) kit = options.createKit(THREE);
|
|
43477
45047
|
else {
|
|
43478
|
-
kit = createVehicleKit({ THREE, locale, ...options.document ? { document: options.document } : {} });
|
|
43479
|
-
installFleet(kit);
|
|
45048
|
+
kit = createVehicleKit({ THREE, locale, particleMaterials, effects: () => host?.effects, ...options.document ? { document: options.document } : {} });
|
|
45049
|
+
fleet.installFleet(kit);
|
|
43480
45050
|
}
|
|
43481
45051
|
kits.set(THREE, kit);
|
|
43482
45052
|
}
|
|
43483
45053
|
return kit;
|
|
43484
45054
|
};
|
|
43485
|
-
const paletteFor = (key) => settings.palette === "civil" ? paintPalettes.civil : settings.palette === "military" ? paintPalettes.military : paletteOf(key);
|
|
45055
|
+
const paletteFor = (key) => settings.palette === "civil" ? fleet.paintPalettes.civil : settings.palette === "military" ? fleet.paintPalettes.military : fleet.paletteOf(key);
|
|
43486
45056
|
const paintOf = (entry) => {
|
|
43487
45057
|
if (!settings.paint) return;
|
|
43488
|
-
|
|
45058
|
+
entry.lod?.dispose();
|
|
45059
|
+
delete entry.lod;
|
|
45060
|
+
const color2 = fleet.colorFromSeed(settings.seed + entry.index, paletteFor(entry.key));
|
|
43489
45061
|
if (!entry.paint) {
|
|
43490
45062
|
const vehicle = entry.item.vehicle;
|
|
43491
45063
|
const primary = primaryPaint(entry.item.vehicle);
|
|
@@ -43507,19 +45079,41 @@ var GameVehiclesThree = (() => {
|
|
|
43507
45079
|
slow: true,
|
|
43508
45080
|
build: (THREE) => {
|
|
43509
45081
|
const kit = kitFor(THREE);
|
|
43510
|
-
const
|
|
43511
|
-
|
|
43512
|
-
const entry = { root:
|
|
43513
|
-
placed.set(
|
|
45082
|
+
const built2 = buildVehicleItem(kit, key, { lod: settings.quality === "lite", ...scenario ? { scenario } : {} });
|
|
45083
|
+
built2.root.userData.kit = kit;
|
|
45084
|
+
const entry = { root: built2.root, key, ...scenario ? { scenario } : {}, item: built2, index: counter++, helpers: [] };
|
|
45085
|
+
placed.set(built2.root.uuid, entry);
|
|
43514
45086
|
measure(entry);
|
|
43515
45087
|
paintOf(entry);
|
|
43516
|
-
return
|
|
45088
|
+
return built2.root;
|
|
43517
45089
|
},
|
|
43518
45090
|
impl: (root, id) => {
|
|
43519
45091
|
const r = root;
|
|
43520
45092
|
const kit = r.userData.kit;
|
|
43521
|
-
const d = createVehicleDestructible(kit, r.userData.vehicle, { id, frame: root, name: text.items[itemKey] ?? key });
|
|
45093
|
+
const d = createVehicleDestructible(kit, r.userData.vehicle, { id, frame: root, name: text.items[itemKey] ?? key, creaseQueue });
|
|
43522
45094
|
tapEffects(d);
|
|
45095
|
+
const setPartPose = d.setPartPose?.bind(d);
|
|
45096
|
+
{
|
|
45097
|
+
const rests = new Map(d.describe().parts.map((part) => [part.id, part.rotation ?? [0, 0, 0]]));
|
|
45098
|
+
d.setPartPose = (partId, position, rotation) => {
|
|
45099
|
+
const entry = [...placed.values()].find((candidate) => candidate.root === root);
|
|
45100
|
+
const rest = rests.get(partId) ?? [0, 0, 0];
|
|
45101
|
+
const isOpen = (rotation ?? [0, 0, 0]).some((angle, axis) => Math.abs(angle - (rest[axis] ?? 0)) > LEAF_REST_RADIANS);
|
|
45102
|
+
if (entry) {
|
|
45103
|
+
entry.posedParts ?? (entry.posedParts = /* @__PURE__ */ new Set());
|
|
45104
|
+
if (isOpen) entry.posedParts.add(partId);
|
|
45105
|
+
else entry.posedParts.delete(partId);
|
|
45106
|
+
}
|
|
45107
|
+
return setPartPose?.(partId, position, rotation);
|
|
45108
|
+
};
|
|
45109
|
+
}
|
|
45110
|
+
const hitBody = d.hit.bind(d);
|
|
45111
|
+
d.hit = (h) => {
|
|
45112
|
+
const entry = [...placed.values()].find((candidate) => candidate.root === root);
|
|
45113
|
+
if (entry) entry.touched = true;
|
|
45114
|
+
return hitBody(h);
|
|
45115
|
+
};
|
|
45116
|
+
cabins.track(id, d, root);
|
|
43523
45117
|
r.userData.destructible = d;
|
|
43524
45118
|
return d;
|
|
43525
45119
|
},
|
|
@@ -43531,10 +45125,10 @@ var GameVehiclesThree = (() => {
|
|
|
43531
45125
|
if (!THREE) return void 0;
|
|
43532
45126
|
if (!clipCache.has(key)) {
|
|
43533
45127
|
const kit = kitFor(THREE);
|
|
43534
|
-
const
|
|
43535
|
-
const d = createVehicleDestructible(kit,
|
|
45128
|
+
const built2 = buildVehicleItem(kit, key);
|
|
45129
|
+
const d = createVehicleDestructible(kit, built2.vehicle, { id: `${itemId(key)}_clips`, frame: built2.root, headless: true });
|
|
43536
45130
|
clipCache.set(key, d.animations());
|
|
43537
|
-
kit.dispose(
|
|
45131
|
+
kit.dispose(built2.vehicle);
|
|
43538
45132
|
}
|
|
43539
45133
|
return clipCache.get(key);
|
|
43540
45134
|
};
|
|
@@ -43659,33 +45253,33 @@ var GameVehiclesThree = (() => {
|
|
|
43659
45253
|
if (!host || !three) return;
|
|
43660
45254
|
clearLane();
|
|
43661
45255
|
const kit = kitFor(three);
|
|
43662
|
-
const vehicles = createVehiclesThree(three, { kit });
|
|
43663
|
-
const
|
|
45256
|
+
const vehicles = createVehiclesThree(three, { kit, fleet });
|
|
45257
|
+
const built2 = vehicles.build("compact_hatch", { quality: "lite", seed: settings.seed });
|
|
43664
45258
|
const wall = new three.Mesh(new three.BoxGeometry(0.6, 2, 6), new three.MeshStandardMaterial({ color: 7829367 }));
|
|
43665
45259
|
wall.position.set(LANE.wall + 0.3, 1, LANE.z);
|
|
43666
45260
|
const scene = host.scene;
|
|
43667
|
-
scene.add(
|
|
45261
|
+
scene.add(built2.group, wall);
|
|
43668
45262
|
const mode = settings.driving === "semi" ? "semi" : "arcade";
|
|
43669
45263
|
const surfaces = createSurfaces();
|
|
43670
45264
|
const idle = { throttle: 0, brake: 0, steer: 0, handbrake: false };
|
|
43671
45265
|
const state = { time: 0, exhaust: 0, ended: false, wasBlocked: false };
|
|
43672
|
-
const arcade = createArcadeDriver({ length:
|
|
45266
|
+
const arcade = createArcadeDriver({ length: built2.length, width: built2.width });
|
|
43673
45267
|
const semi = createSemiSimDriver({}, { surfaces });
|
|
43674
45268
|
const driver = mode === "semi" ? semi : arcade;
|
|
43675
45269
|
driver.place(LANE.from, LANE.z, 0);
|
|
43676
|
-
const gap = () => LANE.wall - (driver.state.x +
|
|
45270
|
+
const gap = () => LANE.wall - (driver.state.x + built2.length / 2);
|
|
43677
45271
|
const decel = () => mode === "semi" ? 9 * surfaces.get(settings.surface).grip : arcade.spec.brakeAgainstMotion;
|
|
43678
45272
|
const brakeAt = () => lane ? lane.speed() * lane.speed() / (2 * decel()) + 3 : 3;
|
|
43679
45273
|
const pose = () => {
|
|
43680
|
-
|
|
43681
|
-
|
|
45274
|
+
built2.group.position.set(driver.state.x, 0, driver.state.z);
|
|
45275
|
+
built2.group.rotation.y = driver.state.yaw + Math.PI / 2;
|
|
43682
45276
|
};
|
|
43683
45277
|
pose();
|
|
43684
45278
|
engineLoop = audio()?.loop?.(soundIds.engine, { position: [driver.state.x, 0.5, driver.state.z] });
|
|
43685
45279
|
const log = (key, value) => host?.log(fill(text, locale, text.lane[key], value.toFixed(1)));
|
|
43686
45280
|
lane = {
|
|
43687
|
-
car:
|
|
43688
|
-
group:
|
|
45281
|
+
car: built2,
|
|
45282
|
+
group: built2.group,
|
|
43689
45283
|
wall,
|
|
43690
45284
|
mode,
|
|
43691
45285
|
done: false,
|
|
@@ -43721,7 +45315,7 @@ var GameVehiclesThree = (() => {
|
|
|
43721
45315
|
const fx = effects();
|
|
43722
45316
|
if (fx && state.exhaust <= 0 && Math.abs(lane?.speed() ?? 0) > 0.5) {
|
|
43723
45317
|
state.exhaust = 0.4;
|
|
43724
|
-
fx.play({ effect: effectIds.exhaust, point: [driver.state.x - Math.cos(driver.state.yaw) * (
|
|
45318
|
+
fx.play({ effect: effectIds.exhaust, point: [driver.state.x - Math.cos(driver.state.yaw) * (built2.length / 2), 0.3, driver.state.z + Math.sin(driver.state.yaw) * (built2.length / 2)], scale: Math.min(1, Math.abs(lane?.speed() ?? 0) / 17) });
|
|
43725
45319
|
}
|
|
43726
45320
|
if (!state.ended && state.time > 1 && Math.abs(lane?.speed() ?? 0) < 0.05) {
|
|
43727
45321
|
state.ended = true;
|
|
@@ -43765,6 +45359,16 @@ var GameVehiclesThree = (() => {
|
|
|
43765
45359
|
break;
|
|
43766
45360
|
}
|
|
43767
45361
|
};
|
|
45362
|
+
const creaseQueue = createCreaseQueue();
|
|
45363
|
+
const updateLod = (entry) => {
|
|
45364
|
+
if (!host || settings.quality === "lite") return;
|
|
45365
|
+
entry.lod ?? (entry.lod = createVehicleLod(entry.root.userData.kit, entry.item.vehicle, entry.root, entry.root.children[0]));
|
|
45366
|
+
const at = host.player().position;
|
|
45367
|
+
const distance = Math.hypot(at[0] - entry.root.position.x, at[2] - entry.root.position.z);
|
|
45368
|
+
const c = entry.item.vehicle;
|
|
45369
|
+
if (c && (c.rollState || c.flipped || c.blast || c.exploded)) entry.touched = true;
|
|
45370
|
+
entry.lod.update(distance, entry.touched === true, entry.scenario !== void 0 || (entry.posedParts?.size ?? 0) > 0);
|
|
45371
|
+
};
|
|
43768
45372
|
const frame = (dt) => {
|
|
43769
45373
|
for (const [id, entry] of placed) {
|
|
43770
45374
|
if (!entry.root.parent) {
|
|
@@ -43772,9 +45376,11 @@ var GameVehiclesThree = (() => {
|
|
|
43772
45376
|
continue;
|
|
43773
45377
|
}
|
|
43774
45378
|
measure(entry);
|
|
45379
|
+
updateLod(entry);
|
|
43775
45380
|
}
|
|
43776
45381
|
if (host?.bench && boxKey() !== helpersShown && (host.bench.layer("damageables") || host.bench.layer("zones") || helpersShown !== "")) refreshLayers();
|
|
43777
45382
|
lane?.step(dt);
|
|
45383
|
+
cabins.sync(effects());
|
|
43778
45384
|
};
|
|
43779
45385
|
const count2 = () => {
|
|
43780
45386
|
let drawCalls = 0;
|
|
@@ -43801,7 +45407,7 @@ var GameVehiclesThree = (() => {
|
|
|
43801
45407
|
const sizeError = () => {
|
|
43802
45408
|
let worst = 0;
|
|
43803
45409
|
for (const entry of placed.values()) {
|
|
43804
|
-
const want = fleetDims(entry.key);
|
|
45410
|
+
const want = fleet.fleetDims(entry.key);
|
|
43805
45411
|
const got = entry.size;
|
|
43806
45412
|
if (!want || !got || entry.scenario) continue;
|
|
43807
45413
|
worst = Math.max(worst, Math.abs(got.length - want.length), Math.abs(got.width - want.width), Math.abs(got.height - want.height));
|
|
@@ -43823,15 +45429,15 @@ var GameVehiclesThree = (() => {
|
|
|
43823
45429
|
const rowOf = (list2, z) => {
|
|
43824
45430
|
let cursor = 0;
|
|
43825
45431
|
const spots = list2.map((entry) => {
|
|
43826
|
-
const width = fleetDims(entry.key)?.width ?? 2;
|
|
45432
|
+
const width = fleet.fleetDims(entry.key)?.width ?? 2;
|
|
43827
45433
|
const x = cursor + width / 2;
|
|
43828
45434
|
cursor += width + gap;
|
|
43829
45435
|
return { id: entry.id, x };
|
|
43830
45436
|
});
|
|
43831
45437
|
return { spots, z, width: Math.max(0, cursor - gap) };
|
|
43832
45438
|
};
|
|
43833
|
-
const front = rowOf(entries.filter((e) => (fleetDims(e.key)?.width ?? 2) <= WIDE), 0);
|
|
43834
|
-
const back = rowOf(entries.filter((e) => (fleetDims(e.key)?.width ?? 2) > WIDE), -16);
|
|
45439
|
+
const front = rowOf(entries.filter((e) => (fleet.fleetDims(e.key)?.width ?? 2) <= WIDE), 0);
|
|
45440
|
+
const back = rowOf(entries.filter((e) => (fleet.fleetDims(e.key)?.width ?? 2) > WIDE), -16);
|
|
43835
45441
|
const placements = [front, back].flatMap((row) => row.spots.map(({ id, x }) => ({ item: id, position: [x - row.width / 2, 0, row.z], rotationY: 0 })));
|
|
43836
45442
|
return { placements, view: { eye: [0, 5, Math.max(14, front.width * 0.6)], target: [0, 1, -2] } };
|
|
43837
45443
|
},
|
|
@@ -43870,11 +45476,15 @@ var GameVehiclesThree = (() => {
|
|
|
43870
45476
|
const stopChange = lent.bench?.onChange(onChange);
|
|
43871
45477
|
const stopFrame = lent.onFrame(frame);
|
|
43872
45478
|
const stopBlocker = lent.addBlocker?.(blocked);
|
|
45479
|
+
const stopCrease = lent.defer?.(creaseQueue.deferredQueue(), DEFERRED_PRIORITY[DEFERRED_QUEUE_ID.VEHICLE_CREASE]);
|
|
45480
|
+
if (isRenderPools(lent.pools)) lent.pools.materials.register("vehicles.cracked-glass", () => crackedGlassMaterial(three, new three.DataTexture(new Uint8Array(4), 1, 1)));
|
|
43873
45481
|
lent.audio?.register?.(LIBRARY_BANK);
|
|
43874
45482
|
return () => {
|
|
43875
45483
|
stopChange?.();
|
|
43876
45484
|
stopFrame();
|
|
43877
45485
|
stopBlocker?.();
|
|
45486
|
+
stopCrease?.();
|
|
45487
|
+
cabins.clear(effects());
|
|
43878
45488
|
clearHelpers();
|
|
43879
45489
|
clearLane();
|
|
43880
45490
|
host = null;
|