@adatechnology/game-fluids-three 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.global.js +489 -153
- package/dist/index.global.js.map +1 -1
- package/package.json +5 -5
package/dist/index.global.js
CHANGED
|
@@ -1325,11 +1325,231 @@ var GameFluidsThree = (() => {
|
|
|
1325
1325
|
glow.outIntensity = glow.i * Math.pow(1 - age, glow.curve) * flicker;
|
|
1326
1326
|
}
|
|
1327
1327
|
}
|
|
1328
|
+
var LEHMER_MULTIPLIER = 16807;
|
|
1329
|
+
var LEHMER_MODULUS = 2147483647;
|
|
1330
|
+
var HASH_MIX = 2654435761;
|
|
1331
|
+
function normalizeLehmerSeed(seed) {
|
|
1332
|
+
const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1);
|
|
1333
|
+
return whole === 0 ? 1 : whole;
|
|
1334
|
+
}
|
|
1335
|
+
function lehmer(seed) {
|
|
1336
|
+
let state = normalizeLehmerSeed(seed);
|
|
1337
|
+
const next = () => {
|
|
1338
|
+
state = state * LEHMER_MULTIPLIER % LEHMER_MODULUS;
|
|
1339
|
+
return (state - 1) / (LEHMER_MODULUS - 1);
|
|
1340
|
+
};
|
|
1341
|
+
return {
|
|
1342
|
+
next,
|
|
1343
|
+
between: (min2, max2) => min2 + (max2 - min2) * next(),
|
|
1344
|
+
direction(out) {
|
|
1345
|
+
const up = next() * 2 - 1;
|
|
1346
|
+
const angle = next() * Math.PI * 2;
|
|
1347
|
+
const ring2 = Math.sqrt(1 - up * up);
|
|
1348
|
+
out.x = ring2 * Math.cos(angle);
|
|
1349
|
+
out.y = up;
|
|
1350
|
+
out.z = ring2 * Math.sin(angle);
|
|
1351
|
+
return out;
|
|
1352
|
+
},
|
|
1353
|
+
get state() {
|
|
1354
|
+
return state;
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
function mixEventSeed(eventSeed) {
|
|
1359
|
+
let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 2246822507);
|
|
1360
|
+
mixed ^= mixed >>> 13;
|
|
1361
|
+
mixed = Math.imul(mixed, 3266489909);
|
|
1362
|
+
mixed ^= mixed >>> 16;
|
|
1363
|
+
return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1;
|
|
1364
|
+
}
|
|
1365
|
+
function streamFor(eventSeed) {
|
|
1366
|
+
return lehmer(mixEventSeed(eventSeed));
|
|
1367
|
+
}
|
|
1368
|
+
var SKIN = 5e-3;
|
|
1369
|
+
var MAX_PAD = 0.45;
|
|
1370
|
+
function rotation(q) {
|
|
1371
|
+
const [x, y, z, w] = q ?? [0, 0, 0, 1];
|
|
1372
|
+
const n = Math.hypot(x, y, z, w) || 1;
|
|
1373
|
+
const qx = x / n;
|
|
1374
|
+
const qy = y / n;
|
|
1375
|
+
const qz = z / n;
|
|
1376
|
+
const qw = w / n;
|
|
1377
|
+
return new Float64Array([
|
|
1378
|
+
1 - 2 * (qy * qy + qz * qz),
|
|
1379
|
+
2 * (qx * qy - qz * qw),
|
|
1380
|
+
2 * (qx * qz + qy * qw),
|
|
1381
|
+
2 * (qx * qy + qz * qw),
|
|
1382
|
+
1 - 2 * (qx * qx + qz * qz),
|
|
1383
|
+
2 * (qy * qz - qx * qw),
|
|
1384
|
+
2 * (qx * qz - qy * qw),
|
|
1385
|
+
2 * (qy * qz + qx * qw),
|
|
1386
|
+
1 - 2 * (qx * qx + qy * qy)
|
|
1387
|
+
]);
|
|
1388
|
+
}
|
|
1389
|
+
var Enclosures = class {
|
|
1390
|
+
constructor(seed = 20973) {
|
|
1391
|
+
__publicField2(this, "spaces", /* @__PURE__ */ new Map());
|
|
1392
|
+
__publicField2(this, "list", []);
|
|
1393
|
+
__publicField2(this, "random");
|
|
1394
|
+
__publicField2(this, "out", { x: 0, y: 0, z: 0, nx: 0, ny: 0, nz: 0 });
|
|
1395
|
+
__publicField2(this, "l0", new Float64Array(3));
|
|
1396
|
+
__publicField2(this, "l1", new Float64Array(3));
|
|
1397
|
+
__publicField2(this, "q", new Float64Array(3));
|
|
1398
|
+
this.random = lehmer(seed);
|
|
1399
|
+
}
|
|
1400
|
+
get empty() {
|
|
1401
|
+
return this.list.length === 0;
|
|
1402
|
+
}
|
|
1403
|
+
get size() {
|
|
1404
|
+
return this.list.length;
|
|
1405
|
+
}
|
|
1406
|
+
/** Declares, moves or (with `null`) ends the space `id`. */
|
|
1407
|
+
set(id, enclosure) {
|
|
1408
|
+
if (!enclosure) {
|
|
1409
|
+
if (this.spaces.delete(id)) this.list = [...this.spaces.values()];
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
const [hx, hy, hz] = enclosure.halfSize;
|
|
1413
|
+
const space = {
|
|
1414
|
+
cx: enclosure.center[0],
|
|
1415
|
+
cy: enclosure.center[1],
|
|
1416
|
+
cz: enclosure.center[2],
|
|
1417
|
+
hx: Math.abs(hx),
|
|
1418
|
+
hy: Math.abs(hy),
|
|
1419
|
+
hz: Math.abs(hz),
|
|
1420
|
+
r: rotation(enclosure.orientation),
|
|
1421
|
+
radius: Math.hypot(hx, hy, hz),
|
|
1422
|
+
openings: enclosure.openings ?? [],
|
|
1423
|
+
leak: Math.max(0, enclosure.leak ?? 0)
|
|
1424
|
+
};
|
|
1425
|
+
const old = this.spaces.get(id);
|
|
1426
|
+
this.spaces.set(id, space);
|
|
1427
|
+
const at3 = old ? this.list.indexOf(old) : -1;
|
|
1428
|
+
if (at3 >= 0) this.list[at3] = space;
|
|
1429
|
+
else this.list.push(space);
|
|
1430
|
+
}
|
|
1431
|
+
get(id) {
|
|
1432
|
+
const s = this.spaces.get(id);
|
|
1433
|
+
if (!s) return void 0;
|
|
1434
|
+
return { center: [s.cx, s.cy, s.cz], halfSize: [s.hx, s.hy, s.hz], openings: s.openings, leak: s.leak };
|
|
1435
|
+
}
|
|
1436
|
+
ids() {
|
|
1437
|
+
return [...this.spaces.keys()];
|
|
1438
|
+
}
|
|
1439
|
+
clear() {
|
|
1440
|
+
this.spaces.clear();
|
|
1441
|
+
this.list = [];
|
|
1442
|
+
}
|
|
1443
|
+
/** Whether a point is inside any space (on the inside of its walls). */
|
|
1444
|
+
inside(x, y, z) {
|
|
1445
|
+
for (const s of this.list) {
|
|
1446
|
+
this.toLocal(s, x, y, z, this.l0);
|
|
1447
|
+
if (Math.abs(this.l0[0]) <= s.hx && Math.abs(this.l0[1]) <= s.hy && Math.abs(this.l0[2]) <= s.hz) return true;
|
|
1448
|
+
}
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
toLocal(s, x, y, z, out) {
|
|
1452
|
+
const dx = x - s.cx;
|
|
1453
|
+
const dy = y - s.cy;
|
|
1454
|
+
const dz = z - s.cz;
|
|
1455
|
+
const r = s.r;
|
|
1456
|
+
out[0] = r[0] * dx + r[3] * dy + r[6] * dz;
|
|
1457
|
+
out[1] = r[1] * dx + r[4] * dy + r[7] * dz;
|
|
1458
|
+
out[2] = r[2] * dx + r[5] * dy + r[8] * dz;
|
|
1459
|
+
}
|
|
1460
|
+
/** Passes at `rate` per second. */
|
|
1461
|
+
chance(rate, dt) {
|
|
1462
|
+
return rate > 0 && this.random.next() < 1 - Math.exp(-rate * dt);
|
|
1463
|
+
}
|
|
1464
|
+
/** The opening the wall point `q` (local) is in lets it through now. */
|
|
1465
|
+
through(s, q, dt) {
|
|
1466
|
+
for (const o of s.openings) {
|
|
1467
|
+
if (Math.abs(q[0] - o.center[0]) > o.halfSize[0] || Math.abs(q[1] - o.center[1]) > o.halfSize[1] || Math.abs(q[2] - o.center[2]) > o.halfSize[2]) continue;
|
|
1468
|
+
if (o.rate === void 0 || this.chance(o.rate, dt)) return true;
|
|
1469
|
+
}
|
|
1470
|
+
return this.chance(s.leak, dt);
|
|
1471
|
+
}
|
|
1472
|
+
confine(x0, y0, z0, x1, y1, z1, pad, dt) {
|
|
1473
|
+
const list = this.list;
|
|
1474
|
+
if (list.length === 0) return false;
|
|
1475
|
+
const p = Math.min(Math.max(0, pad), MAX_PAD);
|
|
1476
|
+
const l0 = this.l0;
|
|
1477
|
+
const l1 = this.l1;
|
|
1478
|
+
const q = this.q;
|
|
1479
|
+
for (const s of list) {
|
|
1480
|
+
const reach = s.radius + p + 0.05;
|
|
1481
|
+
const dx = x1 - s.cx;
|
|
1482
|
+
const dy = y1 - s.cy;
|
|
1483
|
+
const dz = z1 - s.cz;
|
|
1484
|
+
if (dx * dx + dy * dy + dz * dz > reach * reach) {
|
|
1485
|
+
const ex = x0 - s.cx;
|
|
1486
|
+
const ey = y0 - s.cy;
|
|
1487
|
+
const ez = z0 - s.cz;
|
|
1488
|
+
if (ex * ex + ey * ey + ez * ez > reach * reach) continue;
|
|
1489
|
+
}
|
|
1490
|
+
this.toLocal(s, x0, y0, z0, l0);
|
|
1491
|
+
this.toLocal(s, x1, y1, z1, l1);
|
|
1492
|
+
const h = [s.hx, s.hy, s.hz];
|
|
1493
|
+
const in0 = Math.abs(l0[0]) <= s.hx && Math.abs(l0[1]) <= s.hy && Math.abs(l0[2]) <= s.hz;
|
|
1494
|
+
let axis2 = 0;
|
|
1495
|
+
let sign = 1;
|
|
1496
|
+
if (in0) {
|
|
1497
|
+
if (Math.abs(l1[0]) <= s.hx && Math.abs(l1[1]) <= s.hy && Math.abs(l1[2]) <= s.hz) continue;
|
|
1498
|
+
for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
|
|
1499
|
+
if (this.through(s, q, dt)) continue;
|
|
1500
|
+
let worst = -Infinity;
|
|
1501
|
+
for (let a = 0; a < 3; a++) {
|
|
1502
|
+
const over = Math.abs(l1[a]) - h[a];
|
|
1503
|
+
if (over > worst) {
|
|
1504
|
+
worst = over;
|
|
1505
|
+
axis2 = a;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
sign = l1[axis2] < 0 ? -1 : 1;
|
|
1509
|
+
for (let a = 0; a < 3; a++) l1[a] = Math.min(h[a] - SKIN, Math.max(-(h[a] - SKIN), l1[a]));
|
|
1510
|
+
sign = -sign;
|
|
1511
|
+
} else {
|
|
1512
|
+
const padded0 = Math.abs(l0[0]) <= s.hx + p && Math.abs(l0[1]) <= s.hy + p && Math.abs(l0[2]) <= s.hz + p;
|
|
1513
|
+
const bound = padded0 ? 0 : p;
|
|
1514
|
+
if (!(Math.abs(l1[0]) <= s.hx + bound && Math.abs(l1[1]) <= s.hy + bound && Math.abs(l1[2]) <= s.hz + bound)) continue;
|
|
1515
|
+
for (let a = 0; a < 3; a++) q[a] = Math.min(h[a], Math.max(-h[a], l1[a]));
|
|
1516
|
+
if (this.through(s, q, dt)) continue;
|
|
1517
|
+
let worst = -Infinity;
|
|
1518
|
+
for (let a = 0; a < 3; a++) {
|
|
1519
|
+
const over = Math.abs(l0[a]) - (h[a] + bound);
|
|
1520
|
+
if (over > worst) {
|
|
1521
|
+
worst = over;
|
|
1522
|
+
axis2 = a;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
sign = l0[axis2] < 0 ? -1 : 1;
|
|
1526
|
+
l1[axis2] = sign * (h[axis2] + bound + SKIN);
|
|
1527
|
+
}
|
|
1528
|
+
const r = s.r;
|
|
1529
|
+
const out = this.out;
|
|
1530
|
+
out.x = s.cx + r[0] * l1[0] + r[1] * l1[1] + r[2] * l1[2];
|
|
1531
|
+
out.y = s.cy + r[3] * l1[0] + r[4] * l1[1] + r[5] * l1[2];
|
|
1532
|
+
out.z = s.cz + r[6] * l1[0] + r[7] * l1[1] + r[8] * l1[2];
|
|
1533
|
+
out.nx = r[axis2] * sign;
|
|
1534
|
+
out.ny = r[3 + axis2] * sign;
|
|
1535
|
+
out.nz = r[6 + axis2] * sign;
|
|
1536
|
+
return true;
|
|
1537
|
+
}
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
function slideAlong(out, vx2, vy2, vz2) {
|
|
1542
|
+
const into = vx2 * out.nx + vy2 * out.ny + vz2 * out.nz;
|
|
1543
|
+
if (into >= 0) return [vx2, vy2, vz2];
|
|
1544
|
+
return [vx2 - into * out.nx, vy2 - into * out.ny, vz2 - into * out.nz];
|
|
1545
|
+
}
|
|
1328
1546
|
var FLOOR_FRACTION = 0.4;
|
|
1329
1547
|
var FLOOR_BOUNCE = -0.2;
|
|
1330
1548
|
var ARRIVAL_FRAME = 30;
|
|
1331
1549
|
var ARRIVAL_BASE = 0.3;
|
|
1332
1550
|
var GROWTH_CAP_SECONDS = 3.5;
|
|
1551
|
+
var CARRIED_KINDS = [true, true, false, true, false];
|
|
1552
|
+
var PAD_OF_SIZE = 0.35;
|
|
1333
1553
|
function createPuffPool(capacity) {
|
|
1334
1554
|
const placeholder = { next: () => 0, between: (min2) => min2, direction: (out) => out, state: 0 };
|
|
1335
1555
|
return new OrderedPool(capacity, () => ({
|
|
@@ -1424,13 +1644,24 @@ var GameFluidsThree = (() => {
|
|
|
1424
1644
|
if (puff3.vy < 0) puff3.vy *= FLOOR_BOUNCE;
|
|
1425
1645
|
}
|
|
1426
1646
|
}
|
|
1427
|
-
function updatePuffs(pool, dt, wind) {
|
|
1647
|
+
function updatePuffs(pool, dt, wind, confiner) {
|
|
1428
1648
|
pool.compact(ageOrKeep2, dt);
|
|
1649
|
+
const confining = confiner !== void 0 && !confiner.empty;
|
|
1429
1650
|
for (const puff3 of pool.items) {
|
|
1430
1651
|
const age = puff3.t / puff3.life;
|
|
1431
1652
|
const heat = puff3.h0 > 0 ? puff3.h0 * Math.pow(Math.max(0, 1 - puff3.t / puff3.hl), 1.25) : 0;
|
|
1653
|
+
const x0 = puff3.x;
|
|
1654
|
+
const y0 = puff3.y;
|
|
1655
|
+
const z0 = puff3.z;
|
|
1432
1656
|
if (puff3.ring) moveOnRing(puff3, puff3.ring, wind);
|
|
1433
1657
|
else moveFree(puff3, dt, age, heat, wind);
|
|
1658
|
+
if (confining && CARRIED_KINDS[puff3.kind] && confiner.confine(x0, y0, z0, puff3.x, puff3.y, puff3.z, puff3.size * PAD_OF_SIZE, dt)) {
|
|
1659
|
+
const out = confiner.out;
|
|
1660
|
+
puff3.x = out.x;
|
|
1661
|
+
puff3.y = out.y;
|
|
1662
|
+
puff3.z = out.z;
|
|
1663
|
+
[puff3.vx, puff3.vy, puff3.vz] = slideAlong(out, puff3.vx, puff3.vy, puff3.vz);
|
|
1664
|
+
}
|
|
1434
1665
|
const size = puff3.s0 + (puff3.s1 - puff3.s0) * (1 - Math.exp(-puff3.t * puff3.gk)) + puff3.sg * Math.min(puff3.t, GROWTH_CAP_SECONDS);
|
|
1435
1666
|
puff3.size = size * Math.min(1, puff3.t * ARRIVAL_FRAME + ARRIVAL_BASE);
|
|
1436
1667
|
puff3.age = age;
|
|
@@ -1956,10 +2187,10 @@ var GameFluidsThree = (() => {
|
|
|
1956
2187
|
])
|
|
1957
2188
|
]
|
|
1958
2189
|
};
|
|
1959
|
-
function localToWorld(translation,
|
|
2190
|
+
function localToWorld(translation, rotation2, local2) {
|
|
1960
2191
|
const [x, y, z] = local2;
|
|
1961
|
-
const [a, c, e] = [Math.cos(
|
|
1962
|
-
const [b, d, f] = [Math.sin(
|
|
2192
|
+
const [a, c, e] = [Math.cos(rotation2[0]), Math.cos(rotation2[1]), Math.cos(rotation2[2])];
|
|
2193
|
+
const [b, d, f] = [Math.sin(rotation2[0]), Math.sin(rotation2[1]), Math.sin(rotation2[2])];
|
|
1963
2194
|
const [ae, af, be, bf] = [a * e, a * f, b * e, b * f];
|
|
1964
2195
|
return [
|
|
1965
2196
|
c * e * x + -c * f * y + d * z + translation[0],
|
|
@@ -3622,46 +3853,6 @@ var GameFluidsThree = (() => {
|
|
|
3622
3853
|
var ALL_EFFECTS = [BLOOD_HIT, HEADSHOT, DISMEMBER, BLOATER_CLOUD, SCREAMER_SHRIEK, CORPSE_DECAY, REANIMATION, EYES_IN_FOG, MUZZLE_FLASH, IMPACT_BY_MATERIAL, PENETRATION, CASINGS_AND_MAGAZINE, MOLOTOV, GRENADE, EXPLOSIVE_BARREL, SHOPFRONT_GLASS, DOOR_STAGES, BUILDING_COLLAPSE, BLOCK_WALL, METAL_DOOR_EFFECT, IRON_RAILING, WOODEN_FENCE, HIT_POINT_BREAKAGE, PRESSURE_CYLINDERS, BUILDING_FIRE, RAIN_AND_PUDDLES, FOG_AND_ASH, SCRAPING_SPARKS, ENGINE_SMOKE, FUEL_LEAK, CAR_EXPLOSION, WINDSHIELD, HEADLIGHTS_IN_RAIN, DAMAGE_AND_BITE, INFECTION_PHASES, PANIC, BLOATER_BLINDNESS, CAMPFIRE, COUGH_AND_BREATH];
|
|
3623
3854
|
var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
|
|
3624
3855
|
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3625
|
-
var LEHMER_MULTIPLIER = 16807;
|
|
3626
|
-
var LEHMER_MODULUS = 2147483647;
|
|
3627
|
-
var HASH_MIX = 2654435761;
|
|
3628
|
-
function normalizeLehmerSeed(seed) {
|
|
3629
|
-
const whole = Math.floor(Math.abs(seed)) % (LEHMER_MODULUS - 1);
|
|
3630
|
-
return whole === 0 ? 1 : whole;
|
|
3631
|
-
}
|
|
3632
|
-
function lehmer(seed) {
|
|
3633
|
-
let state = normalizeLehmerSeed(seed);
|
|
3634
|
-
const next = () => {
|
|
3635
|
-
state = state * LEHMER_MULTIPLIER % LEHMER_MODULUS;
|
|
3636
|
-
return (state - 1) / (LEHMER_MODULUS - 1);
|
|
3637
|
-
};
|
|
3638
|
-
return {
|
|
3639
|
-
next,
|
|
3640
|
-
between: (min2, max2) => min2 + (max2 - min2) * next(),
|
|
3641
|
-
direction(out) {
|
|
3642
|
-
const up = next() * 2 - 1;
|
|
3643
|
-
const angle = next() * Math.PI * 2;
|
|
3644
|
-
const ring2 = Math.sqrt(1 - up * up);
|
|
3645
|
-
out.x = ring2 * Math.cos(angle);
|
|
3646
|
-
out.y = up;
|
|
3647
|
-
out.z = ring2 * Math.sin(angle);
|
|
3648
|
-
return out;
|
|
3649
|
-
},
|
|
3650
|
-
get state() {
|
|
3651
|
-
return state;
|
|
3652
|
-
}
|
|
3653
|
-
};
|
|
3654
|
-
}
|
|
3655
|
-
function mixEventSeed(eventSeed) {
|
|
3656
|
-
let mixed = Math.imul((eventSeed | 0) ^ HASH_MIX, 2246822507);
|
|
3657
|
-
mixed ^= mixed >>> 13;
|
|
3658
|
-
mixed = Math.imul(mixed, 3266489909);
|
|
3659
|
-
mixed ^= mixed >>> 16;
|
|
3660
|
-
return (mixed >>> 0) % (LEHMER_MODULUS - 1) + 1;
|
|
3661
|
-
}
|
|
3662
|
-
function streamFor(eventSeed) {
|
|
3663
|
-
return lehmer(mixEventSeed(eventSeed));
|
|
3664
|
-
}
|
|
3665
3856
|
var TABLE = {
|
|
3666
3857
|
concrete: { dust: 9078138, chip: 7236195, spark: 2 },
|
|
3667
3858
|
metal: { dust: 0, chip: 0, spark: 18 },
|
|
@@ -4965,8 +5156,8 @@ var GameFluidsThree = (() => {
|
|
|
4965
5156
|
function addScorch(store, random, x, z, radius) {
|
|
4966
5157
|
const variant = Math.floor(random.next() * SCORCH_VARIANTS);
|
|
4967
5158
|
const lift = 0.03 + nextLift(store) * 1e-3;
|
|
4968
|
-
const
|
|
4969
|
-
store.add({ kind: "scorch", x, z, radius, variant, lift, rotation });
|
|
5159
|
+
const rotation2 = random.next() * 6;
|
|
5160
|
+
store.add({ kind: "scorch", x, z, radius, variant, lift, rotation: rotation2 });
|
|
4970
5161
|
}
|
|
4971
5162
|
function addRimRocks(store, ground, random, x, z, radius) {
|
|
4972
5163
|
const count = Math.round(6 + radius * 7);
|
|
@@ -4995,8 +5186,8 @@ var GameFluidsThree = (() => {
|
|
|
4995
5186
|
function addCrater(store, ground, random, spec) {
|
|
4996
5187
|
ground?.dent(random, spec.x, spec.z, spec.radius, spec.depth);
|
|
4997
5188
|
if (!spec.marks) return;
|
|
4998
|
-
const
|
|
4999
|
-
store.add({ kind: "dirt", x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation });
|
|
5189
|
+
const rotation2 = random.next() * 6;
|
|
5190
|
+
store.add({ kind: "dirt", x: spec.x, z: spec.z, radius: spec.radius * 1.02, lift: 0.045, rotation: rotation2 });
|
|
5000
5191
|
addRimRocks(store, ground, random, spec.x, spec.z, spec.radius);
|
|
5001
5192
|
}
|
|
5002
5193
|
function addBloodSplat(store, ground, random, spec) {
|
|
@@ -5552,6 +5743,7 @@ var GameFluidsThree = (() => {
|
|
|
5552
5743
|
state.gas = Math.max(0, state.gas - dt / DECAY.gasSeconds);
|
|
5553
5744
|
state.beat *= Math.exp(-DECAY.beat * dt);
|
|
5554
5745
|
}
|
|
5746
|
+
var PAD_OF_SIZE2 = 0.35;
|
|
5555
5747
|
var clamp4 = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
5556
5748
|
var lerp4 = (a, b, t) => a + (b - a) * t;
|
|
5557
5749
|
function rgb(hex) {
|
|
@@ -5711,9 +5903,20 @@ var GameFluidsThree = (() => {
|
|
|
5711
5903
|
vy2 *= k;
|
|
5712
5904
|
vz2 *= k;
|
|
5713
5905
|
}
|
|
5714
|
-
|
|
5906
|
+
let x = this.position[i3] + vx2 * dt;
|
|
5715
5907
|
let y = this.position[i3 + 1] + vy2 * dt;
|
|
5716
|
-
|
|
5908
|
+
let z = this.position[i3 + 2] + vz2 * dt;
|
|
5909
|
+
const confiner = host.confiner;
|
|
5910
|
+
if (def.carried && confiner && !confiner.empty) {
|
|
5911
|
+
const pad = this.size0[i] * lerp4(1, def.grow, t) * PAD_OF_SIZE2;
|
|
5912
|
+
if (confiner.confine(this.position[i3], this.position[i3 + 1], this.position[i3 + 2], x, y, z, pad, dt)) {
|
|
5913
|
+
const out = confiner.out;
|
|
5914
|
+
x = out.x;
|
|
5915
|
+
y = out.y;
|
|
5916
|
+
z = out.z;
|
|
5917
|
+
[vx2, vy2, vz2] = slideAlong(out, vx2, vy2, vz2);
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5717
5920
|
if (def.ground !== "none") {
|
|
5718
5921
|
const own = this.floor[i];
|
|
5719
5922
|
const gy = Number.isNaN(own) ? host.groundAt(x, z) + 5e-3 : own;
|
|
@@ -5799,7 +6002,8 @@ var GameFluidsThree = (() => {
|
|
|
5799
6002
|
alpha: 0.35,
|
|
5800
6003
|
fadeIn: 0.15,
|
|
5801
6004
|
cone: 0.6,
|
|
5802
|
-
ground: "none"
|
|
6005
|
+
ground: "none",
|
|
6006
|
+
carried: true
|
|
5803
6007
|
},
|
|
5804
6008
|
sparks: {
|
|
5805
6009
|
id: "sparks",
|
|
@@ -5843,7 +6047,8 @@ var GameFluidsThree = (() => {
|
|
|
5843
6047
|
alpha: 0.4,
|
|
5844
6048
|
fadeIn: 0.1,
|
|
5845
6049
|
cone: 0.4,
|
|
5846
|
-
ground: "none"
|
|
6050
|
+
ground: "none",
|
|
6051
|
+
carried: true
|
|
5847
6052
|
},
|
|
5848
6053
|
shards: {
|
|
5849
6054
|
id: "shards",
|
|
@@ -6001,7 +6206,8 @@ var GameFluidsThree = (() => {
|
|
|
6001
6206
|
fadeIn: 0.1,
|
|
6002
6207
|
cone: 0.5,
|
|
6003
6208
|
ground: "stop",
|
|
6004
|
-
flutter: 0.6
|
|
6209
|
+
flutter: 0.6,
|
|
6210
|
+
carried: true
|
|
6005
6211
|
},
|
|
6006
6212
|
steam: {
|
|
6007
6213
|
id: "steam",
|
|
@@ -6023,7 +6229,8 @@ var GameFluidsThree = (() => {
|
|
|
6023
6229
|
alpha: 0.35,
|
|
6024
6230
|
fadeIn: 0.15,
|
|
6025
6231
|
cone: 0.6,
|
|
6026
|
-
ground: "none"
|
|
6232
|
+
ground: "none",
|
|
6233
|
+
carried: true
|
|
6027
6234
|
},
|
|
6028
6235
|
flash: {
|
|
6029
6236
|
id: "flash",
|
|
@@ -6156,7 +6363,8 @@ var GameFluidsThree = (() => {
|
|
|
6156
6363
|
const host = {
|
|
6157
6364
|
groundAt: this.options.groundAt,
|
|
6158
6365
|
gravity: this.options.gravity,
|
|
6159
|
-
land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag)
|
|
6366
|
+
land: (owner, x, y, z, color, tag) => this.listeners.get(owner)?.(x, y, z, color, tag),
|
|
6367
|
+
...this.options.confiner ? { confiner: this.options.confiner } : {}
|
|
6160
6368
|
};
|
|
6161
6369
|
if (this.additive.live > 0 || this.additive.drawCount > 0) this.additive.update(dt, host);
|
|
6162
6370
|
if (this.normal.live > 0 || this.normal.drawCount > 0) this.normal.update(dt, host);
|
|
@@ -6362,8 +6570,8 @@ var GameFluidsThree = (() => {
|
|
|
6362
6570
|
* Takes away the marks inside a box: world `center`, `half` extents along the axes of the world `rotation` (a quaternion, x y z w; none means the
|
|
6363
6571
|
* world axes). The surface they were on is gone (a brick came out of a wall with its bullet holes). Returns how many went.
|
|
6364
6572
|
*/
|
|
6365
|
-
clearInside(center, half,
|
|
6366
|
-
const qx = -
|
|
6573
|
+
clearInside(center, half, rotation2 = [0, 0, 0, 1]) {
|
|
6574
|
+
const qx = -rotation2[0], qy = -rotation2[1], qz = -rotation2[2], qw = rotation2[3];
|
|
6367
6575
|
let gone = 0;
|
|
6368
6576
|
for (const decal2 of this.decals) {
|
|
6369
6577
|
if (!decal2.on) continue;
|
|
@@ -6484,10 +6692,14 @@ var GameFluidsThree = (() => {
|
|
|
6484
6692
|
var DEFAULT_FADE_IN = 0.1;
|
|
6485
6693
|
var FLOATS_PER_VECTOR = 3;
|
|
6486
6694
|
var BYTE_MAX2 = 255;
|
|
6695
|
+
var CARRIED_GRAVITY = 3;
|
|
6696
|
+
var PAD_OF_SIZE3 = 0.35;
|
|
6487
6697
|
var SpritePool = class {
|
|
6488
|
-
|
|
6698
|
+
/** `carried`: the normal-blend pool (smoke, dust, steam), whose light sprites go round the closed spaces; the additive one (flames, sparks) does not. */
|
|
6699
|
+
constructor(capacity, context, carried = false) {
|
|
6489
6700
|
__publicField2(this, "capacity", capacity);
|
|
6490
6701
|
__publicField2(this, "context", context);
|
|
6702
|
+
__publicField2(this, "carried", carried);
|
|
6491
6703
|
__publicField2(this, "count", 0);
|
|
6492
6704
|
__publicField2(this, "position");
|
|
6493
6705
|
__publicField2(this, "velocity");
|
|
@@ -6587,6 +6799,7 @@ var GameFluidsThree = (() => {
|
|
|
6587
6799
|
}
|
|
6588
6800
|
update(dt, leaveDecal) {
|
|
6589
6801
|
const { position, velocity } = this;
|
|
6802
|
+
const confiner = this.context.confiner;
|
|
6590
6803
|
for (let index = this.count - 1; index >= 0; index--) {
|
|
6591
6804
|
this.life[index] -= dt;
|
|
6592
6805
|
if (this.life[index] <= 0) {
|
|
@@ -6600,9 +6813,25 @@ var GameFluidsThree = (() => {
|
|
|
6600
6813
|
velocity[offset] *= factor;
|
|
6601
6814
|
velocity[offset + 1] *= factor;
|
|
6602
6815
|
velocity[offset + 2] *= factor;
|
|
6816
|
+
const x0 = position[offset];
|
|
6817
|
+
const y0 = position[offset + 1];
|
|
6818
|
+
const z0 = position[offset + 2];
|
|
6603
6819
|
position[offset] += velocity[offset] * dt;
|
|
6604
6820
|
position[offset + 1] += velocity[offset + 1] * dt;
|
|
6605
6821
|
position[offset + 2] += velocity[offset + 2] * dt;
|
|
6822
|
+
if (this.carried && confiner && !confiner.empty && Math.abs(this.gravity[index]) < CARRIED_GRAVITY) {
|
|
6823
|
+
const size = this.startSize[index] * (1 + (this.growth[index] - 1) * age);
|
|
6824
|
+
if (confiner.confine(x0, y0, z0, position[offset], position[offset + 1], position[offset + 2], size * PAD_OF_SIZE3, dt)) {
|
|
6825
|
+
const out = confiner.out;
|
|
6826
|
+
position[offset] = out.x;
|
|
6827
|
+
position[offset + 1] = out.y;
|
|
6828
|
+
position[offset + 2] = out.z;
|
|
6829
|
+
const [vx2, vy2, vz2] = slideAlong(out, velocity[offset], velocity[offset + 1], velocity[offset + 2]);
|
|
6830
|
+
velocity[offset] = vx2;
|
|
6831
|
+
velocity[offset + 1] = vy2;
|
|
6832
|
+
velocity[offset + 2] = vz2;
|
|
6833
|
+
}
|
|
6834
|
+
}
|
|
6606
6835
|
const mode = this.groundMode[index];
|
|
6607
6836
|
if (mode) {
|
|
6608
6837
|
const groundHeight = this.context.groundAt(position[offset], position[offset + 2]);
|
|
@@ -6879,9 +7108,9 @@ var GameFluidsThree = (() => {
|
|
|
6879
7108
|
__publicField2(this, "placement");
|
|
6880
7109
|
__publicField2(this, "accumulators", /* @__PURE__ */ new Map());
|
|
6881
7110
|
const profile = options.profile ?? PROFILES.high;
|
|
6882
|
-
this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace };
|
|
7111
|
+
this.context = { random: options.random, groundAt: options.groundAt ?? workshopGround, quality: { level: 0, mult: 1 }, trace: options.trace, ...options.confiner ? { confiner: options.confiner } : {} };
|
|
6883
7112
|
this.add = new SpritePool(profile.sprites.add, this.context);
|
|
6884
|
-
this.normal = new SpritePool(profile.sprites.normal, this.context);
|
|
7113
|
+
this.normal = new SpritePool(profile.sprites.normal, this.context, true);
|
|
6885
7114
|
this.streaks = new StreakPool(profile.streaks, this.context);
|
|
6886
7115
|
this.debris = new DebrisPool(this.context);
|
|
6887
7116
|
this.decals = new DecalPool(this.context);
|
|
@@ -7069,12 +7298,14 @@ var GameFluidsThree = (() => {
|
|
|
7069
7298
|
const groundAt = options.groundAt ?? (ground ? (x, z) => ground.heightAt(x, z) : () => 0);
|
|
7070
7299
|
const handles = { next: 0, events: 0 };
|
|
7071
7300
|
const low = options.profile === "low";
|
|
7301
|
+
const enclosures = new Enclosures(20973 + (options.seed ?? 0));
|
|
7072
7302
|
const spriteSystem = new SpriteKindSystem({
|
|
7073
7303
|
capacity: options.sprites?.capacity ?? (low ? 1250 : 2500),
|
|
7074
7304
|
density: options.sprites?.density ?? (low ? 0.6 : 1),
|
|
7075
7305
|
seed: 2654435761 + (options.seed ?? 0),
|
|
7076
7306
|
groundAt,
|
|
7077
7307
|
gravity: options.gravity ?? DEFAULT_GRAVITY,
|
|
7308
|
+
confiner: enclosures,
|
|
7078
7309
|
...options.sprites?.kinds ? { kinds: options.sprites.kinds } : {}
|
|
7079
7310
|
});
|
|
7080
7311
|
const core = {
|
|
@@ -7124,7 +7355,7 @@ var GameFluidsThree = (() => {
|
|
|
7124
7355
|
return [camera2.x, camera2.y, camera2.z];
|
|
7125
7356
|
}, forward: (local2) => [local2[0] ?? 0, local2[1] ?? 0, local2[2] ?? 0] };
|
|
7126
7357
|
function workshop() {
|
|
7127
|
-
return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt }));
|
|
7358
|
+
return engine ?? (engine = new WorkshopEngine({ random: sharedRandom, profile, groundAt, confiner: enclosures }));
|
|
7128
7359
|
}
|
|
7129
7360
|
function effectPlayer() {
|
|
7130
7361
|
return player ?? (player = createEffectPlayer({ engine: workshop(), effects, view }));
|
|
@@ -7183,6 +7414,10 @@ var GameFluidsThree = (() => {
|
|
|
7183
7414
|
puff3.vz += dz * strength;
|
|
7184
7415
|
}
|
|
7185
7416
|
},
|
|
7417
|
+
enclose(id, enclosure) {
|
|
7418
|
+
enclosures.set(id, enclosure);
|
|
7419
|
+
},
|
|
7420
|
+
enclosures: () => enclosures,
|
|
7186
7421
|
stop(handle) {
|
|
7187
7422
|
player?.stop(handle);
|
|
7188
7423
|
const fires = core.fires;
|
|
@@ -7210,7 +7445,7 @@ var GameFluidsThree = (() => {
|
|
|
7210
7445
|
core.clock.time += dt;
|
|
7211
7446
|
core.schedule.update(core.clock.time);
|
|
7212
7447
|
updateRings(core.rings, dt);
|
|
7213
|
-
updatePuffs(core.puffs, dt, wind);
|
|
7448
|
+
updatePuffs(core.puffs, dt, wind, enclosures);
|
|
7214
7449
|
updateSparks(core.sparks, { dt, groundAt });
|
|
7215
7450
|
updateGlows(core.glows, dt);
|
|
7216
7451
|
updateShocks(core.shocks, core.puffs.items, dt);
|
|
@@ -7688,6 +7923,11 @@ void main(){
|
|
|
7688
7923
|
var MAX_CONFORM_STEPS = 48;
|
|
7689
7924
|
var MIN_CONFORM_STEPS = 16;
|
|
7690
7925
|
var BLOOD_GROW_RATE = 1.2;
|
|
7926
|
+
var BLOOD_START_GROWTH = 0.05;
|
|
7927
|
+
var DEFAULT_MAX_GROUND_MARKS = 64;
|
|
7928
|
+
var DEFAULT_MAX_ROCKS = 512;
|
|
7929
|
+
var DEFAULT_MAX_BLOOD = 256;
|
|
7930
|
+
var MAX_SPARE_GROUND_GEOMETRIES = 16;
|
|
7691
7931
|
function createMarkLayer(THREE, options) {
|
|
7692
7932
|
const group = new THREE.Group();
|
|
7693
7933
|
const textures = createMarkTextures(THREE, options.createCanvas);
|
|
@@ -7711,110 +7951,168 @@ void main(){
|
|
|
7711
7951
|
const bloodMaterial = new THREE.MeshStandardMaterial({ color: 6031882, roughness: 0.15, metalness: 0.1, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 });
|
|
7712
7952
|
const bloodGeometry = new THREE.CircleGeometry(0.5, 9).rotateX(-Math.PI / 2);
|
|
7713
7953
|
owned.push(bloodMaterial, bloodGeometry);
|
|
7714
|
-
const
|
|
7715
|
-
const
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
material = new THREE.MeshStandardMaterial({ color, roughness: 0.9, metalness: 0, flatShading: true });
|
|
7721
|
-
rockMaterials.set(color, material);
|
|
7722
|
-
owned.push(material);
|
|
7954
|
+
const maxGroundMarks = options.maxGroundMarks ?? DEFAULT_MAX_GROUND_MARKS;
|
|
7955
|
+
const instanced = {};
|
|
7956
|
+
function rockMaterial() {
|
|
7957
|
+
if (!instanced.rockMaterial) {
|
|
7958
|
+
instanced.rockMaterial = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.9, metalness: 0, flatShading: true });
|
|
7959
|
+
owned.push(instanced.rockMaterial);
|
|
7723
7960
|
}
|
|
7724
|
-
return
|
|
7961
|
+
return instanced.rockMaterial;
|
|
7962
|
+
}
|
|
7963
|
+
function bloodMesh() {
|
|
7964
|
+
return instanced.blood ?? (instanced.blood = attach(createInstanced(bloodGeometry, bloodMaterial, options.maxBlood ?? DEFAULT_MAX_BLOOD)));
|
|
7965
|
+
}
|
|
7966
|
+
function rockMesh() {
|
|
7967
|
+
return instanced.rock ?? (instanced.rock = attach(createInstanced(options.rockGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
7968
|
+
}
|
|
7969
|
+
function plateMesh() {
|
|
7970
|
+
return instanced.plate ?? (instanced.plate = attach(createInstanced(options.plateGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
7971
|
+
}
|
|
7972
|
+
function attach(mesh2, castShadow = false) {
|
|
7973
|
+
mesh2.castShadow = castShadow;
|
|
7974
|
+
group.add(mesh2);
|
|
7975
|
+
return mesh2;
|
|
7976
|
+
}
|
|
7977
|
+
const groundMarks = [];
|
|
7978
|
+
const spareGeometries = [];
|
|
7979
|
+
const blood = [];
|
|
7980
|
+
const placed = { blood: 0, rock: 0, plate: 0 };
|
|
7981
|
+
const matrix = new THREE.Matrix4();
|
|
7982
|
+
const position = new THREE.Vector3();
|
|
7983
|
+
const quaternion = new THREE.Quaternion();
|
|
7984
|
+
const euler = new THREE.Euler();
|
|
7985
|
+
const scale2 = new THREE.Vector3();
|
|
7986
|
+
const color = new THREE.Color();
|
|
7987
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
7988
|
+
function createInstanced(geometry, material, capacity) {
|
|
7989
|
+
const mesh2 = new THREE.InstancedMesh(geometry, material, capacity);
|
|
7990
|
+
mesh2.count = 0;
|
|
7991
|
+
mesh2.frustumCulled = false;
|
|
7992
|
+
mesh2.receiveShadow = true;
|
|
7993
|
+
return mesh2;
|
|
7994
|
+
}
|
|
7995
|
+
function takeSpare(vertexCount) {
|
|
7996
|
+
const found = spareGeometries.findIndex((geometry) => geometry.getAttribute("position").count === vertexCount);
|
|
7997
|
+
return found < 0 ? void 0 : spareGeometries.splice(found, 1)[0];
|
|
7998
|
+
}
|
|
7999
|
+
function evictOldestGroundMark() {
|
|
8000
|
+
const oldest = groundMarks.shift();
|
|
8001
|
+
group.remove(oldest.mesh);
|
|
8002
|
+
spareGeometries.push(oldest.mesh.geometry);
|
|
8003
|
+
if (spareGeometries.length > MAX_SPARE_GROUND_GEOMETRIES) spareGeometries.shift().dispose();
|
|
7725
8004
|
}
|
|
7726
8005
|
function conform(mark, material) {
|
|
7727
8006
|
const radius = mark.radius;
|
|
7728
8007
|
const steps = Math.max(MIN_CONFORM_STEPS, Math.min(MAX_CONFORM_STEPS, Math.round(radius * 8)));
|
|
7729
|
-
|
|
7730
|
-
const
|
|
8008
|
+
if (groundMarks.length >= maxGroundMarks) evictOldestGroundMark();
|
|
8009
|
+
const geometry = takeSpare((steps + 1) * (steps + 1)) ?? new THREE.PlaneGeometry(2, 2, steps, steps).rotateX(-Math.PI / 2);
|
|
8010
|
+
const grid = geometry.attributes.position;
|
|
8011
|
+
const cell = 2 / steps;
|
|
7731
8012
|
const cos2 = Math.cos(mark.rotation);
|
|
7732
8013
|
const sin2 = Math.sin(mark.rotation);
|
|
7733
|
-
for (let index = 0; index <
|
|
7734
|
-
const localX =
|
|
7735
|
-
const localZ =
|
|
8014
|
+
for (let index = 0; index < grid.count; index++) {
|
|
8015
|
+
const localX = Math.fround(index % (steps + 1) * cell - 1) * radius;
|
|
8016
|
+
const localZ = Math.fround(Math.floor(index / (steps + 1)) * cell - 1) * radius;
|
|
7736
8017
|
const worldX = mark.x + localX * cos2 - localZ * sin2;
|
|
7737
8018
|
const worldZ = mark.z + localX * sin2 + localZ * cos2;
|
|
7738
|
-
|
|
8019
|
+
grid.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ);
|
|
7739
8020
|
}
|
|
8021
|
+
grid.needsUpdate = true;
|
|
7740
8022
|
geometry.computeVertexNormals();
|
|
8023
|
+
geometry.attributes.normal.needsUpdate = true;
|
|
8024
|
+
geometry.boundingSphere = null;
|
|
8025
|
+
geometry.boundingBox = null;
|
|
7741
8026
|
const mesh2 = new THREE.Mesh(geometry, material);
|
|
7742
8027
|
mesh2.receiveShadow = true;
|
|
7743
|
-
|
|
8028
|
+
group.add(mesh2);
|
|
8029
|
+
groundMarks.push({ mesh: mesh2, steps });
|
|
7744
8030
|
}
|
|
7745
8031
|
function addRock(mark) {
|
|
7746
|
-
const mesh2 =
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
mesh2.
|
|
7751
|
-
mesh2.
|
|
7752
|
-
|
|
8032
|
+
const mesh2 = mark.plate ? plateMesh() : rockMesh();
|
|
8033
|
+
const slot = mark.plate ? "plate" : "rock";
|
|
8034
|
+
const index = placed[slot]++ % mesh2.instanceMatrix.count;
|
|
8035
|
+
quaternion.setFromEuler(euler.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]));
|
|
8036
|
+
mesh2.setMatrixAt(index, matrix.compose(position.set(mark.x, mark.y, mark.z), quaternion, scale2.set(mark.scale[0], mark.scale[1], mark.scale[2])));
|
|
8037
|
+
mesh2.setColorAt(index, color.set(mark.color));
|
|
8038
|
+
mesh2.count = Math.min(placed[slot], mesh2.instanceMatrix.count);
|
|
8039
|
+
mesh2.instanceMatrix.needsUpdate = true;
|
|
8040
|
+
mesh2.instanceColor.needsUpdate = true;
|
|
8041
|
+
}
|
|
8042
|
+
function writeBlood(index, drawn, growth) {
|
|
8043
|
+
quaternion.setFromAxisAngle(up, drawn.rotation);
|
|
8044
|
+
const mesh2 = bloodMesh();
|
|
8045
|
+
mesh2.setMatrixAt(index, matrix.compose(position.set(drawn.position[0], drawn.position[1], drawn.position[2]), quaternion, scale2.set(drawn.scaleX * growth, 1, drawn.scaleZ * growth)));
|
|
8046
|
+
mesh2.instanceMatrix.needsUpdate = true;
|
|
7753
8047
|
}
|
|
7754
8048
|
function addBlood(mark) {
|
|
7755
|
-
const
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
mesh2.scale.multiplyScalar(0.05);
|
|
7761
|
-
blood.push({ mesh: mesh2, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
|
|
7762
|
-
return mesh2;
|
|
8049
|
+
const index = placed.blood++ % bloodMesh().instanceMatrix.count;
|
|
8050
|
+
const drawn = { position: [mark.x, mark.y, mark.z], rotation: mark.rotation, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 };
|
|
8051
|
+
blood[index] = drawn;
|
|
8052
|
+
writeBlood(index, drawn, BLOOD_START_GROWTH);
|
|
8053
|
+
bloodMesh().count = Math.min(placed.blood, bloodMesh().instanceMatrix.count);
|
|
7763
8054
|
}
|
|
7764
8055
|
function build(mark) {
|
|
7765
|
-
if (mark.kind === "scorch")
|
|
7766
|
-
if (mark.kind === "dirt")
|
|
7767
|
-
if (mark.kind === "rock")
|
|
7768
|
-
|
|
7769
|
-
}
|
|
7770
|
-
function
|
|
7771
|
-
for (const
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
8056
|
+
if (mark.kind === "scorch") conform(mark, scorchMaterials[mark.variant]);
|
|
8057
|
+
else if (mark.kind === "dirt") conform(mark, dirtMaterial);
|
|
8058
|
+
else if (mark.kind === "rock") addRock(mark);
|
|
8059
|
+
else addBlood(mark);
|
|
8060
|
+
}
|
|
8061
|
+
function clearMarks() {
|
|
8062
|
+
for (const drawn of groundMarks) group.remove(drawn.mesh);
|
|
8063
|
+
for (const drawn of groundMarks) drawn.mesh.geometry.dispose();
|
|
8064
|
+
groundMarks.length = 0;
|
|
8065
|
+
for (const geometry of spareGeometries) geometry.dispose();
|
|
8066
|
+
spareGeometries.length = 0;
|
|
7776
8067
|
blood.length = 0;
|
|
7777
|
-
|
|
8068
|
+
placed.blood = placed.rock = placed.plate = 0;
|
|
8069
|
+
for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) if (mesh2) mesh2.count = 0;
|
|
7778
8070
|
}
|
|
7779
8071
|
return {
|
|
7780
8072
|
group,
|
|
7781
8073
|
handle(event) {
|
|
7782
|
-
if (event.type === "clear")
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
8074
|
+
if (event.type === "clear") clearMarks();
|
|
8075
|
+
else build(event.mark);
|
|
8076
|
+
},
|
|
8077
|
+
prepare() {
|
|
8078
|
+
bloodMesh();
|
|
8079
|
+
rockMesh();
|
|
8080
|
+
plateMesh();
|
|
7788
8081
|
},
|
|
7789
8082
|
update(dt) {
|
|
7790
|
-
for (
|
|
7791
|
-
|
|
8083
|
+
for (let index = 0; index < blood.length; index++) {
|
|
8084
|
+
const drawn = blood[index];
|
|
8085
|
+
if (!drawn || drawn.age > 1) continue;
|
|
7792
8086
|
drawn.age += dt * BLOOD_GROW_RATE;
|
|
7793
|
-
|
|
7794
|
-
drawn.mesh.scale.set(drawn.scaleX * Math.max(0.05, ease3), 1, drawn.scaleZ * Math.max(0.05, ease3));
|
|
8087
|
+
writeBlood(index, drawn, Math.max(BLOOD_START_GROWTH, 1 - Math.pow(1 - Math.min(1, drawn.age), 2.5)));
|
|
7795
8088
|
}
|
|
7796
8089
|
},
|
|
7797
|
-
count: () =>
|
|
8090
|
+
count: () => groundMarks.length + [instanced.blood, instanced.rock, instanced.plate].filter((mesh2) => mesh2 && mesh2.count > 0).length,
|
|
7798
8091
|
dispose() {
|
|
7799
|
-
|
|
8092
|
+
clearMarks();
|
|
8093
|
+
for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) mesh2?.dispose();
|
|
7800
8094
|
for (const item of owned) item.dispose();
|
|
7801
8095
|
}
|
|
7802
8096
|
};
|
|
7803
8097
|
}
|
|
7804
8098
|
var SPHERE_DEFAULT_WIDTH = 8;
|
|
7805
8099
|
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
8100
|
+
var MAX_PARKED_MESHES_PER_KIND = 256;
|
|
7806
8101
|
function geometryOf(THREE, particle) {
|
|
7807
8102
|
switch (particle.shape) {
|
|
7808
8103
|
case "cone":
|
|
7809
|
-
return new THREE.ConeGeometry(
|
|
8104
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, false);
|
|
7810
8105
|
case "cone_open":
|
|
7811
|
-
return new THREE.ConeGeometry(
|
|
8106
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, true);
|
|
7812
8107
|
case "sphere":
|
|
7813
|
-
return new THREE.SphereGeometry(
|
|
8108
|
+
return new THREE.SphereGeometry(1, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
7814
8109
|
case "icosahedron":
|
|
7815
|
-
return new THREE.IcosahedronGeometry(
|
|
8110
|
+
return new THREE.IcosahedronGeometry(1, particle.segments);
|
|
7816
8111
|
}
|
|
7817
8112
|
}
|
|
8113
|
+
function isCone(particle) {
|
|
8114
|
+
return particle.shape === "cone" || particle.shape === "cone_open";
|
|
8115
|
+
}
|
|
7818
8116
|
function materialOf(THREE, particle) {
|
|
7819
8117
|
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
7820
8118
|
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
@@ -7825,51 +8123,82 @@ void main(){
|
|
|
7825
8123
|
(kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
|
|
7826
8124
|
);
|
|
7827
8125
|
}
|
|
8126
|
+
function geometryKeyOf(particle) {
|
|
8127
|
+
return `${particle.shape}:${particle.segments}:${particle.segments2}`;
|
|
8128
|
+
}
|
|
7828
8129
|
function createMeshLayer(THREE, pool) {
|
|
7829
8130
|
const group = new THREE.Group();
|
|
7830
8131
|
group.name = "workshop-meshes";
|
|
7831
8132
|
const drawn = /* @__PURE__ */ new Map();
|
|
8133
|
+
const geometries = /* @__PURE__ */ new Map();
|
|
8134
|
+
const parked = /* @__PURE__ */ new Map();
|
|
7832
8135
|
const frame = new THREE.Quaternion();
|
|
7833
8136
|
const euler = new THREE.Euler();
|
|
7834
8137
|
const turn = new THREE.Quaternion();
|
|
7835
8138
|
const offset = new THREE.Vector3();
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
8139
|
+
let stamp = 0;
|
|
8140
|
+
function geometryFor(particle) {
|
|
8141
|
+
const key = geometryKeyOf(particle);
|
|
8142
|
+
let geometry = geometries.get(key);
|
|
8143
|
+
if (!geometry) {
|
|
8144
|
+
geometry = geometryOf(THREE, particle);
|
|
8145
|
+
geometries.set(key, geometry);
|
|
8146
|
+
}
|
|
8147
|
+
return geometry;
|
|
8148
|
+
}
|
|
8149
|
+
function acquire(particle) {
|
|
8150
|
+
const parkingKey = `${geometryKeyOf(particle)}|${particle.lit}|${particle.additive}`;
|
|
8151
|
+
const mesh2 = parked.get(parkingKey)?.pop() ?? new THREE.Mesh(geometryFor(particle), materialOf(THREE, particle));
|
|
8152
|
+
mesh2.material.color.set(particle.color);
|
|
8153
|
+
mesh2.frustumCulled = false;
|
|
8154
|
+
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
8155
|
+
group.add(mesh2);
|
|
8156
|
+
return { mesh: mesh2, parkingKey, stamp };
|
|
8157
|
+
}
|
|
8158
|
+
function park(entry) {
|
|
8159
|
+
entry.mesh.removeFromParent();
|
|
8160
|
+
let free = parked.get(entry.parkingKey);
|
|
8161
|
+
if (!free) {
|
|
8162
|
+
free = [];
|
|
8163
|
+
parked.set(entry.parkingKey, free);
|
|
8164
|
+
}
|
|
8165
|
+
if (free.length < MAX_PARKED_MESHES_PER_KIND) free.push(entry.mesh);
|
|
8166
|
+
else entry.mesh.material.dispose();
|
|
7840
8167
|
}
|
|
7841
8168
|
return {
|
|
7842
8169
|
group,
|
|
7843
8170
|
sync() {
|
|
7844
|
-
|
|
8171
|
+
stamp++;
|
|
7845
8172
|
for (const particle of pool.particles) {
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
mesh2.frustumCulled = false;
|
|
7851
|
-
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
7852
|
-
drawn.set(particle.id, mesh2);
|
|
7853
|
-
group.add(mesh2);
|
|
8173
|
+
let entry = drawn.get(particle.id);
|
|
8174
|
+
if (!entry) {
|
|
8175
|
+
entry = acquire(particle);
|
|
8176
|
+
drawn.set(particle.id, entry);
|
|
7854
8177
|
}
|
|
8178
|
+
entry.stamp = stamp;
|
|
8179
|
+
const mesh2 = entry.mesh;
|
|
7855
8180
|
frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
7856
8181
|
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
|
|
7857
8182
|
mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
7858
8183
|
turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
7859
8184
|
mesh2.quaternion.copy(frame).multiply(turn);
|
|
7860
|
-
mesh2.scale.
|
|
8185
|
+
mesh2.scale.set(particle.radius * particle.scale, (isCone(particle) ? particle.height : particle.radius) * particle.scale, particle.radius * particle.scale);
|
|
7861
8186
|
mesh2.material.opacity = particle.opacity;
|
|
7862
8187
|
}
|
|
7863
|
-
for (const [id,
|
|
7864
|
-
if (
|
|
7865
|
-
|
|
8188
|
+
for (const [id, entry] of drawn) {
|
|
8189
|
+
if (entry.stamp === stamp) continue;
|
|
8190
|
+
park(entry);
|
|
7866
8191
|
drawn.delete(id);
|
|
7867
8192
|
}
|
|
7868
8193
|
return pool.count;
|
|
7869
8194
|
},
|
|
7870
8195
|
dispose() {
|
|
7871
|
-
for (const
|
|
8196
|
+
for (const entry of drawn.values()) park(entry);
|
|
7872
8197
|
drawn.clear();
|
|
8198
|
+
for (const free of parked.values()) for (const mesh2 of free) mesh2.material.dispose();
|
|
8199
|
+
parked.clear();
|
|
8200
|
+
for (const geometry of geometries.values()) geometry.dispose();
|
|
8201
|
+
geometries.clear();
|
|
7873
8202
|
group.removeFromParent();
|
|
7874
8203
|
}
|
|
7875
8204
|
};
|
|
@@ -8437,7 +8766,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8437
8766
|
const shocks = createShockLayer(THREE, pools.shocks);
|
|
8438
8767
|
const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
|
|
8439
8768
|
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
|
|
8440
|
-
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry });
|
|
8769
|
+
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
|
|
8441
8770
|
root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
|
|
8442
8771
|
const stopMarks = world.onMarks((event) => marks.handle(event));
|
|
8443
8772
|
const overlay = options.overlay ? createScreenOverlay(options.overlay) : void 0;
|
|
@@ -8463,6 +8792,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8463
8792
|
const position = options.camera.position;
|
|
8464
8793
|
world.setCamera([position.x, position.y, position.z]);
|
|
8465
8794
|
}
|
|
8795
|
+
function attachWorkshopLayers() {
|
|
8796
|
+
const engine = world.peekWorkshop();
|
|
8797
|
+
if (!engine || engine === workshopEngine) return;
|
|
8798
|
+
workshopLayers?.dispose();
|
|
8799
|
+
if (workshopLayers) root.remove(workshopLayers.group);
|
|
8800
|
+
workshopEngine = engine;
|
|
8801
|
+
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
8802
|
+
root.add(workshopLayers.group);
|
|
8803
|
+
applyLayers();
|
|
8804
|
+
}
|
|
8466
8805
|
function syncAll() {
|
|
8467
8806
|
lastDrawn.puffs = puffs.sync();
|
|
8468
8807
|
lastDrawn.sparks = sparks.sync();
|
|
@@ -8471,15 +8810,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8471
8810
|
lastDrawn.shocks = shocks.sync();
|
|
8472
8811
|
lastDrawn.sprites = sprites.sync();
|
|
8473
8812
|
lights.sync();
|
|
8474
|
-
|
|
8475
|
-
if (engine && engine !== workshopEngine) {
|
|
8476
|
-
workshopLayers?.dispose();
|
|
8477
|
-
if (workshopLayers) root.remove(workshopLayers.group);
|
|
8478
|
-
workshopEngine = engine;
|
|
8479
|
-
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
8480
|
-
root.add(workshopLayers.group);
|
|
8481
|
-
applyLayers();
|
|
8482
|
-
}
|
|
8813
|
+
attachWorkshopLayers();
|
|
8483
8814
|
const workshopCalls = workshopLayers?.sync() ?? 0;
|
|
8484
8815
|
const glow = pools.lights.glow;
|
|
8485
8816
|
uniforms.uGlowPos.value.set(glow.x, glow.y, glow.z);
|
|
@@ -8507,6 +8838,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8507
8838
|
});
|
|
8508
8839
|
},
|
|
8509
8840
|
stop: (handle) => world.stop(handle),
|
|
8841
|
+
// a module's closed space (a shut car's cabin): the world's smoke, dust and steam go round it
|
|
8842
|
+
enclose: (id, enclosure) => world.enclose(id, enclosure),
|
|
8510
8843
|
configure(settings) {
|
|
8511
8844
|
const band = settings.lod_band;
|
|
8512
8845
|
if (typeof band === "string" && band in LOD_BANDS) world.forceLod(LOD_BANDS[band] ?? null);
|
|
@@ -8532,6 +8865,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8532
8865
|
},
|
|
8533
8866
|
warmUpProbes() {
|
|
8534
8867
|
releaseAnchors();
|
|
8868
|
+
marks.prepare();
|
|
8869
|
+
world.workshop();
|
|
8870
|
+
attachWorkshopLayers();
|
|
8535
8871
|
const meshes = createMeshLayerProbes(THREE);
|
|
8536
8872
|
for (const mesh2 of meshes) mesh2.frustumCulled = false;
|
|
8537
8873
|
const group = new THREE.Group();
|
|
@@ -10574,7 +10910,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10574
10910
|
const direction = new THREE.Vector3();
|
|
10575
10911
|
const location = new THREE.Vector3();
|
|
10576
10912
|
const scale2 = new THREE.Vector3();
|
|
10577
|
-
const
|
|
10913
|
+
const rotation2 = new THREE.Quaternion();
|
|
10578
10914
|
const matrix = new THREE.Matrix4();
|
|
10579
10915
|
return {
|
|
10580
10916
|
drops,
|
|
@@ -10594,9 +10930,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10594
10930
|
const o = i * 3;
|
|
10595
10931
|
const speed = Math.hypot(velocity[o], velocity[o + 1], velocity[o + 2]);
|
|
10596
10932
|
direction.set(velocity[o], velocity[o + 1], velocity[o + 2]).normalize();
|
|
10597
|
-
|
|
10933
|
+
rotation2.setFromUnitVectors(up, direction);
|
|
10598
10934
|
const size = radius[i] * METRES_PER_MM;
|
|
10599
|
-
matrix.compose(location.set(position[o], position[o + 1], position[o + 2]),
|
|
10935
|
+
matrix.compose(location.set(position[o], position[o + 1], position[o + 2]), rotation2, scale2.set(size, size * (1 + Math.min(MAX_STRETCH, speed * STRETCH_PER_SPEED)), size));
|
|
10600
10936
|
drops.setMatrixAt(i, matrix);
|
|
10601
10937
|
drawn++;
|
|
10602
10938
|
}
|