@adatechnology/game-fluids-three 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -0
- package/dist/index.global.js +211 -94
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as THREE_NS from 'three';
|
|
2
|
+
import { WarmUpProbes } from '@adatechnology/game-contracts';
|
|
2
3
|
import { VfxWorld } from '@adatechnology/game-vfx';
|
|
3
4
|
import { ThreeLib as ThreeLib$1 } from '@adatechnology/game-vfx-three';
|
|
4
5
|
import { Vec3, FluidWorld, Film, StainTint, FilmLayout, FilmProfile, FilmRandom, LiquidId, SurfaceSpec, FilmLook, FilmRegion } from '@adatechnology/game-fluids';
|
|
@@ -56,6 +57,8 @@ interface FluidRenderer {
|
|
|
56
57
|
/** Draw the liquid of a world container inside `object` (a volume clipped by the liquid surface, plus steam). */
|
|
57
58
|
bindContainer(containerId: string, object: THREE_NS.Object3D, options?: BindOptions): void;
|
|
58
59
|
unbindContainer(containerId: string): void;
|
|
60
|
+
/** Meshes that use the materials of the liquid bodies, streams and stains, to render once so that the first leak does not build their shader programs in the middle of a frame. */
|
|
61
|
+
warmUpProbes(): WarmUpProbes;
|
|
59
62
|
/** Advance the visuals (call it after `fluids.update(dt)`). */
|
|
60
63
|
update(dt: number): void;
|
|
61
64
|
/** Remove every stain, stream and particle (the bindings stay). */
|
package/dist/index.global.js
CHANGED
|
@@ -3088,6 +3088,17 @@ var GameFluidsThree = (() => {
|
|
|
3088
3088
|
])
|
|
3089
3089
|
]
|
|
3090
3090
|
};
|
|
3091
|
+
var FUEL_LEAK_ONLY = {
|
|
3092
|
+
id: "fuel_leak_only",
|
|
3093
|
+
pageId: "vazamento",
|
|
3094
|
+
team: "vehicles",
|
|
3095
|
+
budget: { particles: 200, lights: 0 },
|
|
3096
|
+
loop: 11,
|
|
3097
|
+
view: FUEL_LEAK.view,
|
|
3098
|
+
setup: FUEL_LEAK.setup,
|
|
3099
|
+
timeline: [decal(POOL[0], 0, POOL[2], 2.2, 2040368, { grow: 3, life: 60, ar: 0.6, fluid: "oil" })],
|
|
3100
|
+
tick: [emit("drip", "normal", 12, { p: TANK, box: [0.05, 0, 0.05], sp: 0, life: 1, size: 0.03, c0: 5921402, a: 0.9, g: 9.8, fi: 0, gnd: 1 })]
|
|
3101
|
+
};
|
|
3091
3102
|
var CAR_EXPLOSION = {
|
|
3092
3103
|
id: "car_explosion",
|
|
3093
3104
|
pageId: "explosao",
|
|
@@ -3441,6 +3452,8 @@ var GameFluidsThree = (() => {
|
|
|
3441
3452
|
];
|
|
3442
3453
|
var PI = Math.PI;
|
|
3443
3454
|
var SMOKE = { color: 9079430, width: 10, height: 8, gravity: -0.08, drag: 2.2, growth: 2.1 };
|
|
3455
|
+
var DEFAULT_SMOKE = { puffs: 2, radius: 0.01, opacity: 0.14, life: 0.7, drift: 0.2, rise: 0.06, growth: 2.1, flash: 1 };
|
|
3456
|
+
var DEFAULT_SPREAD = { drift: 0.1, rise: 0.04, side: 0.06 };
|
|
3444
3457
|
var FLASH_SECONDS = 0.055;
|
|
3445
3458
|
var FLASH_RAMP = [0.6, 0.9];
|
|
3446
3459
|
var FLASH_OPACITY = 0.95;
|
|
@@ -3448,8 +3461,10 @@ var GameFluidsThree = (() => {
|
|
|
3448
3461
|
var FLAME_ADDITIVE = 3;
|
|
3449
3462
|
var ORIGIN2 = [0, 0, 0];
|
|
3450
3463
|
var SIZE = variable("size");
|
|
3464
|
+
var FLASH_SCALE = variable("flash");
|
|
3465
|
+
var fallback = (name, defaultValue) => ite(ne(variable(name), void 0), variable(name), defaultValue);
|
|
3451
3466
|
var wide = (width) => mul(sub(rand(), 0.5), width);
|
|
3452
|
-
var puff2 = (point, velocity, world, radius, life, opacity) => mesh({
|
|
3467
|
+
var puff2 = (point, velocity, world, radius, life, opacity, growth = SMOKE.growth) => mesh({
|
|
3453
3468
|
shape: "sphere",
|
|
3454
3469
|
p: point,
|
|
3455
3470
|
v: velocity,
|
|
@@ -3464,7 +3479,7 @@ var GameFluidsThree = (() => {
|
|
|
3464
3479
|
lit: true,
|
|
3465
3480
|
g: SMOKE.gravity,
|
|
3466
3481
|
drag: SMOKE.drag,
|
|
3467
|
-
grow:
|
|
3482
|
+
grow: growth
|
|
3468
3483
|
});
|
|
3469
3484
|
var flameBit = (point, velocity, world, radius, life) => [
|
|
3470
3485
|
set("bitPoint", point),
|
|
@@ -3497,37 +3512,55 @@ var GameFluidsThree = (() => {
|
|
|
3497
3512
|
id: "viewmodel_muzzle_flash",
|
|
3498
3513
|
pageId: "event:viewmodel_muzzle_flash",
|
|
3499
3514
|
team: "weapons",
|
|
3500
|
-
budget: { particles:
|
|
3515
|
+
budget: { particles: 14, lights: 1 },
|
|
3501
3516
|
loop: 0.1,
|
|
3517
|
+
setup: [
|
|
3518
|
+
set("size", fallback("size", 1)),
|
|
3519
|
+
set("puffs", fallback("puffs", DEFAULT_SMOKE.puffs)),
|
|
3520
|
+
set("radius", fallback("radius", DEFAULT_SMOKE.radius)),
|
|
3521
|
+
set("opacity", fallback("opacity", DEFAULT_SMOKE.opacity)),
|
|
3522
|
+
set("life", fallback("life", DEFAULT_SMOKE.life)),
|
|
3523
|
+
set("drift", fallback("drift", DEFAULT_SMOKE.drift)),
|
|
3524
|
+
set("rise", fallback("rise", DEFAULT_SMOKE.rise)),
|
|
3525
|
+
set("growth", fallback("growth", DEFAULT_SMOKE.growth)),
|
|
3526
|
+
set("flash", fallback("flash", DEFAULT_SMOKE.flash)),
|
|
3527
|
+
set("driftSpread", fallback("driftSpread", DEFAULT_SPREAD.drift)),
|
|
3528
|
+
set("riseSpread", fallback("riseSpread", DEFAULT_SPREAD.rise)),
|
|
3529
|
+
set("sideSpread", fallback("sideSpread", DEFAULT_SPREAD.side)),
|
|
3530
|
+
set("lifeSpread", ite(ne(variable("lifeSpread"), void 0), variable("lifeSpread"), mul(variable("life"), 0.3)))
|
|
3531
|
+
],
|
|
3502
3532
|
timeline: [
|
|
3503
3533
|
set("angles", []),
|
|
3504
3534
|
repeat(4, append("angles", add(div(mul(INDEX, PI), 2), mul(rand(), 0.6)))),
|
|
3505
3535
|
set("roll", mul(rand(), PI)),
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3536
|
+
when(gt(FLASH_SCALE, 0), [
|
|
3537
|
+
flash(ORIGIN2, 16756832, mul(4, SIZE, FLASH_SCALE), FLASH_SECONDS, mul(2, SIZE, FLASH_SCALE), true, true),
|
|
3538
|
+
flashPiece("cone_open", mul(0.013, SIZE, FLASH_SCALE), mul(0.085, SIZE, FLASH_SCALE), 10, [mul(0.042, SIZE, FLASH_SCALE), 0, 0], [0, 0, -PI / 2], 16757575),
|
|
3539
|
+
repeat(
|
|
3540
|
+
4,
|
|
3541
|
+
set("angle", pick(variable("angles"), INDEX)),
|
|
3542
|
+
flashPiece(
|
|
3543
|
+
"cone",
|
|
3544
|
+
mul(5e-3, SIZE, FLASH_SCALE),
|
|
3545
|
+
mul(0.04, SIZE, FLASH_SCALE),
|
|
3546
|
+
6,
|
|
3547
|
+
[mul(0.012, SIZE, FLASH_SCALE), mul(cos(variable("angle")), 0.016, SIZE, FLASH_SCALE), mul(sin(variable("angle")), 0.016, SIZE, FLASH_SCALE)],
|
|
3548
|
+
[variable("angle"), 0, -PI / 2],
|
|
3549
|
+
16757575
|
|
3550
|
+
)
|
|
3551
|
+
),
|
|
3552
|
+
flashPiece("sphere", mul(0.016, SIZE, FLASH_SCALE), void 0, 12, [mul(8e-3, SIZE, FLASH_SCALE), 0, 0], [0, 0, 0], 16773568)
|
|
3553
|
+
]),
|
|
3522
3554
|
repeat(
|
|
3523
|
-
|
|
3555
|
+
variable("puffs"),
|
|
3524
3556
|
puff2(
|
|
3525
3557
|
ORIGIN2,
|
|
3526
|
-
[add(
|
|
3558
|
+
[add(variable("drift"), mul(rand(), variable("driftSpread"))), add(variable("rise"), mul(rand(), variable("riseSpread"))), mul(sub(rand(), 0.5), variable("sideSpread"))],
|
|
3527
3559
|
[0, 0, 0],
|
|
3528
|
-
mul(
|
|
3529
|
-
add(
|
|
3530
|
-
|
|
3560
|
+
mul(variable("radius"), SIZE),
|
|
3561
|
+
add(variable("life"), mul(rand(), variable("lifeSpread"))),
|
|
3562
|
+
variable("opacity"),
|
|
3563
|
+
variable("growth")
|
|
3531
3564
|
)
|
|
3532
3565
|
)
|
|
3533
3566
|
]
|
|
@@ -3575,9 +3608,20 @@ var GameFluidsThree = (() => {
|
|
|
3575
3608
|
]
|
|
3576
3609
|
};
|
|
3577
3610
|
var VIEWMODEL_EFFECTS = [MUZZLE_FLASH2, SMOKE_PUFF, BACKBLAST, FLAME_JET];
|
|
3611
|
+
var aliasOf = (base, id, changes) => ({ ...base, id, pageId: `vehicle:${id}`, anchor: CAR_ORIGIN2, ...changes });
|
|
3612
|
+
var VEHICLE_EFFECTS = [
|
|
3613
|
+
/** The engine on fire: `engine_smoke` at its worst condition (black smoke, flames in the hood, a light). */
|
|
3614
|
+
aliasOf(ENGINE_SMOKE, "vehicle_fire", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 3 }, loop: 12 }),
|
|
3615
|
+
/** The exhaust of a car under way: `engine_smoke` in the best condition, for half a second so a moving car asks again. */
|
|
3616
|
+
aliasOf(ENGINE_SMOKE, "vehicle_exhaust", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 0 }, loop: 0.5 }),
|
|
3617
|
+
/** A hole in the tank: the page's petrol leak without the fire and the blast (the pool and the drips). A normal bullet does only this. */
|
|
3618
|
+
aliasOf(FUEL_LEAK_ONLY, "vehicle_leak", {}),
|
|
3619
|
+
/** The leak caught fire (an incendiary or tracer round, a spark, a fire near): the page's whole sequence, the fire running along the pool to the tank and the blast. */
|
|
3620
|
+
aliasOf(FUEL_LEAK, "vehicle_leak_ignited", {})
|
|
3621
|
+
];
|
|
3578
3622
|
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];
|
|
3579
3623
|
var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
|
|
3580
|
-
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3624
|
+
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3581
3625
|
var LEHMER_MULTIPLIER = 16807;
|
|
3582
3626
|
var LEHMER_MODULUS = 2147483647;
|
|
3583
3627
|
var HASH_MIX = 2654435761;
|
|
@@ -7730,6 +7774,79 @@ void main(){
|
|
|
7730
7774
|
}
|
|
7731
7775
|
};
|
|
7732
7776
|
}
|
|
7777
|
+
var SPHERE_DEFAULT_WIDTH = 8;
|
|
7778
|
+
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
7779
|
+
function geometryOf(THREE, particle) {
|
|
7780
|
+
switch (particle.shape) {
|
|
7781
|
+
case "cone":
|
|
7782
|
+
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
|
|
7783
|
+
case "cone_open":
|
|
7784
|
+
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
|
|
7785
|
+
case "sphere":
|
|
7786
|
+
return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
7787
|
+
case "icosahedron":
|
|
7788
|
+
return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
|
|
7789
|
+
}
|
|
7790
|
+
}
|
|
7791
|
+
function materialOf(THREE, particle) {
|
|
7792
|
+
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
7793
|
+
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
7794
|
+
}
|
|
7795
|
+
function createMeshLayerProbes(THREE) {
|
|
7796
|
+
const geometry = new THREE.BoxGeometry(1e-3, 1e-3, 1e-3);
|
|
7797
|
+
return [{ lit: true, additive: false }, { lit: false, additive: false }, { lit: false, additive: true }].map(
|
|
7798
|
+
(kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
|
|
7799
|
+
);
|
|
7800
|
+
}
|
|
7801
|
+
function createMeshLayer(THREE, pool) {
|
|
7802
|
+
const group = new THREE.Group();
|
|
7803
|
+
group.name = "workshop-meshes";
|
|
7804
|
+
const drawn = /* @__PURE__ */ new Map();
|
|
7805
|
+
const frame = new THREE.Quaternion();
|
|
7806
|
+
const euler = new THREE.Euler();
|
|
7807
|
+
const turn = new THREE.Quaternion();
|
|
7808
|
+
const offset = new THREE.Vector3();
|
|
7809
|
+
function release(mesh2) {
|
|
7810
|
+
mesh2.removeFromParent();
|
|
7811
|
+
mesh2.geometry.dispose();
|
|
7812
|
+
mesh2.material.dispose();
|
|
7813
|
+
}
|
|
7814
|
+
return {
|
|
7815
|
+
group,
|
|
7816
|
+
sync() {
|
|
7817
|
+
const live = /* @__PURE__ */ new Set();
|
|
7818
|
+
for (const particle of pool.particles) {
|
|
7819
|
+
live.add(particle.id);
|
|
7820
|
+
let mesh2 = drawn.get(particle.id);
|
|
7821
|
+
if (!mesh2) {
|
|
7822
|
+
mesh2 = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
|
|
7823
|
+
mesh2.frustumCulled = false;
|
|
7824
|
+
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
7825
|
+
drawn.set(particle.id, mesh2);
|
|
7826
|
+
group.add(mesh2);
|
|
7827
|
+
}
|
|
7828
|
+
frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
7829
|
+
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
|
|
7830
|
+
mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
7831
|
+
turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
7832
|
+
mesh2.quaternion.copy(frame).multiply(turn);
|
|
7833
|
+
mesh2.scale.setScalar(particle.scale);
|
|
7834
|
+
mesh2.material.opacity = particle.opacity;
|
|
7835
|
+
}
|
|
7836
|
+
for (const [id, mesh2] of drawn) {
|
|
7837
|
+
if (live.has(id)) continue;
|
|
7838
|
+
release(mesh2);
|
|
7839
|
+
drawn.delete(id);
|
|
7840
|
+
}
|
|
7841
|
+
return pool.count;
|
|
7842
|
+
},
|
|
7843
|
+
dispose() {
|
|
7844
|
+
for (const mesh2 of drawn.values()) release(mesh2);
|
|
7845
|
+
drawn.clear();
|
|
7846
|
+
group.removeFromParent();
|
|
7847
|
+
}
|
|
7848
|
+
};
|
|
7849
|
+
}
|
|
7733
7850
|
var clamp5 = (value, min2, max2) => value < min2 ? min2 : value > max2 ? max2 : value;
|
|
7734
7851
|
function layer(container, name, blend) {
|
|
7735
7852
|
const element = container.ownerDocument.createElement("div");
|
|
@@ -7899,73 +8016,6 @@ void main() {
|
|
|
7899
8016
|
}
|
|
7900
8017
|
};
|
|
7901
8018
|
}
|
|
7902
|
-
var SPHERE_DEFAULT_WIDTH = 8;
|
|
7903
|
-
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
7904
|
-
function geometryOf(THREE, particle) {
|
|
7905
|
-
switch (particle.shape) {
|
|
7906
|
-
case "cone":
|
|
7907
|
-
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
|
|
7908
|
-
case "cone_open":
|
|
7909
|
-
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
|
|
7910
|
-
case "sphere":
|
|
7911
|
-
return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
7912
|
-
case "icosahedron":
|
|
7913
|
-
return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
|
|
7914
|
-
}
|
|
7915
|
-
}
|
|
7916
|
-
function materialOf(THREE, particle) {
|
|
7917
|
-
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
7918
|
-
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
7919
|
-
}
|
|
7920
|
-
function createMeshLayer(THREE, pool) {
|
|
7921
|
-
const group = new THREE.Group();
|
|
7922
|
-
group.name = "workshop-meshes";
|
|
7923
|
-
const drawn = /* @__PURE__ */ new Map();
|
|
7924
|
-
const frame = new THREE.Quaternion();
|
|
7925
|
-
const euler = new THREE.Euler();
|
|
7926
|
-
const turn = new THREE.Quaternion();
|
|
7927
|
-
const offset = new THREE.Vector3();
|
|
7928
|
-
function release(mesh2) {
|
|
7929
|
-
mesh2.removeFromParent();
|
|
7930
|
-
mesh2.geometry.dispose();
|
|
7931
|
-
mesh2.material.dispose();
|
|
7932
|
-
}
|
|
7933
|
-
return {
|
|
7934
|
-
group,
|
|
7935
|
-
sync() {
|
|
7936
|
-
const live = /* @__PURE__ */ new Set();
|
|
7937
|
-
for (const particle of pool.particles) {
|
|
7938
|
-
live.add(particle.id);
|
|
7939
|
-
let mesh2 = drawn.get(particle.id);
|
|
7940
|
-
if (!mesh2) {
|
|
7941
|
-
mesh2 = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
|
|
7942
|
-
mesh2.frustumCulled = false;
|
|
7943
|
-
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
7944
|
-
drawn.set(particle.id, mesh2);
|
|
7945
|
-
group.add(mesh2);
|
|
7946
|
-
}
|
|
7947
|
-
frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
7948
|
-
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
|
|
7949
|
-
mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
7950
|
-
turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
7951
|
-
mesh2.quaternion.copy(frame).multiply(turn);
|
|
7952
|
-
mesh2.scale.setScalar(particle.scale);
|
|
7953
|
-
mesh2.material.opacity = particle.opacity;
|
|
7954
|
-
}
|
|
7955
|
-
for (const [id, mesh2] of drawn) {
|
|
7956
|
-
if (live.has(id)) continue;
|
|
7957
|
-
release(mesh2);
|
|
7958
|
-
drawn.delete(id);
|
|
7959
|
-
}
|
|
7960
|
-
return pool.count;
|
|
7961
|
-
},
|
|
7962
|
-
dispose() {
|
|
7963
|
-
for (const mesh2 of drawn.values()) release(mesh2);
|
|
7964
|
-
drawn.clear();
|
|
7965
|
-
group.removeFromParent();
|
|
7966
|
-
}
|
|
7967
|
-
};
|
|
7968
|
-
}
|
|
7969
8019
|
var WORKSHOP_SPRITE_VERTEX = `attribute float size;attribute float alpha;attribute vec3 pcol;varying vec3 vC;varying float vA;uniform float uScale;
|
|
7970
8020
|
void main(){vC=pcol;vA=alpha;vec4 mv=modelViewMatrix*vec4(position,1.);gl_PointSize=clamp(size*uScale/-mv.z,1.,512.);gl_Position=projectionMatrix*mv;}`;
|
|
7971
8021
|
var WORKSHOP_SPRITE_FRAGMENT = `uniform float uSoft;varying vec3 vC;varying float vA;
|
|
@@ -8330,6 +8380,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8330
8380
|
const requested = options.vfx;
|
|
8331
8381
|
const world = isWorld(requested) ? requested : createVfx({ profile: options.quality ?? "medium", reducedMotion, ...requested });
|
|
8332
8382
|
const pools = world.pools();
|
|
8383
|
+
let anchors;
|
|
8384
|
+
const releaseAnchors = () => {
|
|
8385
|
+
if (!anchors) return;
|
|
8386
|
+
anchors.meshes[0]?.geometry.dispose();
|
|
8387
|
+
for (const mesh2 of anchors.meshes) mesh2.material.dispose();
|
|
8388
|
+
anchors = void 0;
|
|
8389
|
+
};
|
|
8333
8390
|
const root = new THREE.Group();
|
|
8334
8391
|
options.scene.add(root);
|
|
8335
8392
|
const direction = new THREE.Vector3(...options.lightDirection ?? LAB_LIGHT_DIRECTION).normalize();
|
|
@@ -8446,6 +8503,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8446
8503
|
syncAll();
|
|
8447
8504
|
overlay?.update(screenNow());
|
|
8448
8505
|
},
|
|
8506
|
+
warmUpProbes() {
|
|
8507
|
+
releaseAnchors();
|
|
8508
|
+
const meshes = createMeshLayerProbes(THREE);
|
|
8509
|
+
for (const mesh2 of meshes) mesh2.frustumCulled = false;
|
|
8510
|
+
const group = new THREE.Group();
|
|
8511
|
+
group.add(...meshes);
|
|
8512
|
+
anchors = { meshes, group };
|
|
8513
|
+
return { group, dispose: () => {
|
|
8514
|
+
} };
|
|
8515
|
+
},
|
|
8449
8516
|
applyCamera() {
|
|
8450
8517
|
const shake = screenNow().shake;
|
|
8451
8518
|
if (reducedMotion || !(shake > SHAKE_FLOOR)) return () => {
|
|
@@ -8464,6 +8531,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8464
8531
|
},
|
|
8465
8532
|
screen: screenNow,
|
|
8466
8533
|
dispose() {
|
|
8534
|
+
releaseAnchors();
|
|
8467
8535
|
stopMarks();
|
|
8468
8536
|
overlay?.dispose();
|
|
8469
8537
|
workshopLayers?.dispose();
|
|
@@ -8676,6 +8744,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8676
8744
|
this.vertices = 0;
|
|
8677
8745
|
return;
|
|
8678
8746
|
}
|
|
8747
|
+
if (state.closed && this.isShellOpaque()) {
|
|
8748
|
+
if (this.mesh) this.mesh.visible = false;
|
|
8749
|
+
this.vertices = 0;
|
|
8750
|
+
return;
|
|
8751
|
+
}
|
|
8679
8752
|
this.build(state, quality);
|
|
8680
8753
|
if (!this.mesh || !this.solid) return;
|
|
8681
8754
|
this.mesh.material = materials.liquidBody(def);
|
|
@@ -8701,6 +8774,17 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8701
8774
|
}
|
|
8702
8775
|
} else this.steamAcc = 0;
|
|
8703
8776
|
}
|
|
8777
|
+
/** Every mesh of the bound object (the liquid itself aside) is drawn with an opaque material. */
|
|
8778
|
+
isShellOpaque() {
|
|
8779
|
+
let opaque = true;
|
|
8780
|
+
this.object.traverse((child) => {
|
|
8781
|
+
const mesh2 = child;
|
|
8782
|
+
if (!opaque || !mesh2.isMesh || child === this.mesh) return;
|
|
8783
|
+
const materials = Array.isArray(mesh2.material) ? mesh2.material : [mesh2.material];
|
|
8784
|
+
if (materials.some((material) => material.transparent || material.opacity < 1)) opaque = false;
|
|
8785
|
+
});
|
|
8786
|
+
return opaque;
|
|
8787
|
+
}
|
|
8704
8788
|
get triangles() {
|
|
8705
8789
|
return this.visible ? Math.floor(this.vertices / 3) : 0;
|
|
8706
8790
|
}
|
|
@@ -8826,6 +8910,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8826
8910
|
}
|
|
8827
8911
|
return m;
|
|
8828
8912
|
}
|
|
8913
|
+
/** One material per role, in a clear and in an opaque liquid, which are the shader programs a leak will need (the colour is not part of a program). */
|
|
8914
|
+
probes() {
|
|
8915
|
+
const base = { id: "warm_up", color: 8421504, viscosity: 1, density: 1e3, dryTime: 60, gloss: 0.5 };
|
|
8916
|
+
const defs = [{ ...base, opacity: 0.5 }, { ...base, opacity: 1 }];
|
|
8917
|
+
return {
|
|
8918
|
+
plain: defs.flatMap((def) => [this.liquidBody(def), this.liquidStream(def)]),
|
|
8919
|
+
instanced: defs.map((def) => this.liquidStream(def)),
|
|
8920
|
+
coloured: defs.flatMap((def) => [this.stainFilm(def), this.stainWet(def)])
|
|
8921
|
+
};
|
|
8922
|
+
}
|
|
8829
8923
|
dispose() {
|
|
8830
8924
|
for (const map of [this.body, this.stream, this.film, this.wet]) {
|
|
8831
8925
|
for (const m of map.values()) m.dispose();
|
|
@@ -8901,9 +8995,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8901
8995
|
var dot2 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
8902
8996
|
var cross3 = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
8903
8997
|
var length = (a) => Math.hypot(a[0], a[1], a[2]);
|
|
8904
|
-
var normalize2 = (a,
|
|
8998
|
+
var normalize2 = (a, fallback2 = [0, 1, 0]) => {
|
|
8905
8999
|
const l = length(a);
|
|
8906
|
-
return l < 1e-9 ? [
|
|
9000
|
+
return l < 1e-9 ? [fallback2[0], fallback2[1], fallback2[2]] : [a[0] / l, a[1] / l, a[2] / l];
|
|
8907
9001
|
};
|
|
8908
9002
|
var ballistic = (origin, velocity, gravity, t) => [
|
|
8909
9003
|
origin[0] + velocity[0] * t,
|
|
@@ -10339,6 +10433,29 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10339
10433
|
body.dispose();
|
|
10340
10434
|
bodies.delete(containerId);
|
|
10341
10435
|
},
|
|
10436
|
+
warmUpProbes() {
|
|
10437
|
+
const plainGeometry = new THREE.BufferGeometry();
|
|
10438
|
+
plainGeometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(9), 3));
|
|
10439
|
+
plainGeometry.setAttribute("normal", new THREE.BufferAttribute(new Float32Array(9), 3));
|
|
10440
|
+
const stainGeometry = plainGeometry.clone();
|
|
10441
|
+
stainGeometry.setAttribute("color", new THREE.BufferAttribute(new Float32Array(12), 4));
|
|
10442
|
+
const { plain, instanced, coloured } = materials.probes();
|
|
10443
|
+
const meshes = [
|
|
10444
|
+
...plain.map((material) => new THREE.Mesh(plainGeometry, material)),
|
|
10445
|
+
...instanced.map((material) => new THREE.InstancedMesh(dropGeometry, material, 1)),
|
|
10446
|
+
...coloured.map((material) => new THREE.Mesh(stainGeometry, material))
|
|
10447
|
+
];
|
|
10448
|
+
const group = new THREE.Group();
|
|
10449
|
+
for (const mesh2 of meshes) mesh2.frustumCulled = false;
|
|
10450
|
+
group.add(...meshes);
|
|
10451
|
+
return {
|
|
10452
|
+
group,
|
|
10453
|
+
dispose() {
|
|
10454
|
+
plainGeometry.dispose();
|
|
10455
|
+
stainGeometry.dispose();
|
|
10456
|
+
}
|
|
10457
|
+
};
|
|
10458
|
+
},
|
|
10342
10459
|
update(dt) {
|
|
10343
10460
|
if (disposed) return;
|
|
10344
10461
|
time += dt;
|