@adatechnology/game-fluids-three 0.3.1 → 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 +203 -95
- 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
|
]
|
|
@@ -3581,8 +3614,10 @@ var GameFluidsThree = (() => {
|
|
|
3581
3614
|
aliasOf(ENGINE_SMOKE, "vehicle_fire", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 3 }, loop: 12 }),
|
|
3582
3615
|
/** The exhaust of a car under way: `engine_smoke` in the best condition, for half a second so a moving car asks again. */
|
|
3583
3616
|
aliasOf(ENGINE_SMOKE, "vehicle_exhaust", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 0 }, loop: 0.5 }),
|
|
3584
|
-
/** A hole in the tank: the page's
|
|
3585
|
-
aliasOf(
|
|
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", {})
|
|
3586
3621
|
];
|
|
3587
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];
|
|
3588
3623
|
var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
|
|
@@ -7739,6 +7774,79 @@ void main(){
|
|
|
7739
7774
|
}
|
|
7740
7775
|
};
|
|
7741
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
|
+
}
|
|
7742
7850
|
var clamp5 = (value, min2, max2) => value < min2 ? min2 : value > max2 ? max2 : value;
|
|
7743
7851
|
function layer(container, name, blend) {
|
|
7744
7852
|
const element = container.ownerDocument.createElement("div");
|
|
@@ -7908,73 +8016,6 @@ void main() {
|
|
|
7908
8016
|
}
|
|
7909
8017
|
};
|
|
7910
8018
|
}
|
|
7911
|
-
var SPHERE_DEFAULT_WIDTH = 8;
|
|
7912
|
-
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
7913
|
-
function geometryOf(THREE, particle) {
|
|
7914
|
-
switch (particle.shape) {
|
|
7915
|
-
case "cone":
|
|
7916
|
-
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
|
|
7917
|
-
case "cone_open":
|
|
7918
|
-
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
|
|
7919
|
-
case "sphere":
|
|
7920
|
-
return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
7921
|
-
case "icosahedron":
|
|
7922
|
-
return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
|
|
7923
|
-
}
|
|
7924
|
-
}
|
|
7925
|
-
function materialOf(THREE, particle) {
|
|
7926
|
-
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
7927
|
-
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
7928
|
-
}
|
|
7929
|
-
function createMeshLayer(THREE, pool) {
|
|
7930
|
-
const group = new THREE.Group();
|
|
7931
|
-
group.name = "workshop-meshes";
|
|
7932
|
-
const drawn = /* @__PURE__ */ new Map();
|
|
7933
|
-
const frame = new THREE.Quaternion();
|
|
7934
|
-
const euler = new THREE.Euler();
|
|
7935
|
-
const turn = new THREE.Quaternion();
|
|
7936
|
-
const offset = new THREE.Vector3();
|
|
7937
|
-
function release(mesh2) {
|
|
7938
|
-
mesh2.removeFromParent();
|
|
7939
|
-
mesh2.geometry.dispose();
|
|
7940
|
-
mesh2.material.dispose();
|
|
7941
|
-
}
|
|
7942
|
-
return {
|
|
7943
|
-
group,
|
|
7944
|
-
sync() {
|
|
7945
|
-
const live = /* @__PURE__ */ new Set();
|
|
7946
|
-
for (const particle of pool.particles) {
|
|
7947
|
-
live.add(particle.id);
|
|
7948
|
-
let mesh2 = drawn.get(particle.id);
|
|
7949
|
-
if (!mesh2) {
|
|
7950
|
-
mesh2 = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
|
|
7951
|
-
mesh2.frustumCulled = false;
|
|
7952
|
-
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
7953
|
-
drawn.set(particle.id, mesh2);
|
|
7954
|
-
group.add(mesh2);
|
|
7955
|
-
}
|
|
7956
|
-
frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
7957
|
-
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
|
|
7958
|
-
mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
7959
|
-
turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
7960
|
-
mesh2.quaternion.copy(frame).multiply(turn);
|
|
7961
|
-
mesh2.scale.setScalar(particle.scale);
|
|
7962
|
-
mesh2.material.opacity = particle.opacity;
|
|
7963
|
-
}
|
|
7964
|
-
for (const [id, mesh2] of drawn) {
|
|
7965
|
-
if (live.has(id)) continue;
|
|
7966
|
-
release(mesh2);
|
|
7967
|
-
drawn.delete(id);
|
|
7968
|
-
}
|
|
7969
|
-
return pool.count;
|
|
7970
|
-
},
|
|
7971
|
-
dispose() {
|
|
7972
|
-
for (const mesh2 of drawn.values()) release(mesh2);
|
|
7973
|
-
drawn.clear();
|
|
7974
|
-
group.removeFromParent();
|
|
7975
|
-
}
|
|
7976
|
-
};
|
|
7977
|
-
}
|
|
7978
8019
|
var WORKSHOP_SPRITE_VERTEX = `attribute float size;attribute float alpha;attribute vec3 pcol;varying vec3 vC;varying float vA;uniform float uScale;
|
|
7979
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;}`;
|
|
7980
8021
|
var WORKSHOP_SPRITE_FRAGMENT = `uniform float uSoft;varying vec3 vC;varying float vA;
|
|
@@ -8339,6 +8380,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8339
8380
|
const requested = options.vfx;
|
|
8340
8381
|
const world = isWorld(requested) ? requested : createVfx({ profile: options.quality ?? "medium", reducedMotion, ...requested });
|
|
8341
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
|
+
};
|
|
8342
8390
|
const root = new THREE.Group();
|
|
8343
8391
|
options.scene.add(root);
|
|
8344
8392
|
const direction = new THREE.Vector3(...options.lightDirection ?? LAB_LIGHT_DIRECTION).normalize();
|
|
@@ -8455,6 +8503,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8455
8503
|
syncAll();
|
|
8456
8504
|
overlay?.update(screenNow());
|
|
8457
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
|
+
},
|
|
8458
8516
|
applyCamera() {
|
|
8459
8517
|
const shake = screenNow().shake;
|
|
8460
8518
|
if (reducedMotion || !(shake > SHAKE_FLOOR)) return () => {
|
|
@@ -8473,6 +8531,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8473
8531
|
},
|
|
8474
8532
|
screen: screenNow,
|
|
8475
8533
|
dispose() {
|
|
8534
|
+
releaseAnchors();
|
|
8476
8535
|
stopMarks();
|
|
8477
8536
|
overlay?.dispose();
|
|
8478
8537
|
workshopLayers?.dispose();
|
|
@@ -8685,6 +8744,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8685
8744
|
this.vertices = 0;
|
|
8686
8745
|
return;
|
|
8687
8746
|
}
|
|
8747
|
+
if (state.closed && this.isShellOpaque()) {
|
|
8748
|
+
if (this.mesh) this.mesh.visible = false;
|
|
8749
|
+
this.vertices = 0;
|
|
8750
|
+
return;
|
|
8751
|
+
}
|
|
8688
8752
|
this.build(state, quality);
|
|
8689
8753
|
if (!this.mesh || !this.solid) return;
|
|
8690
8754
|
this.mesh.material = materials.liquidBody(def);
|
|
@@ -8710,6 +8774,17 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8710
8774
|
}
|
|
8711
8775
|
} else this.steamAcc = 0;
|
|
8712
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
|
+
}
|
|
8713
8788
|
get triangles() {
|
|
8714
8789
|
return this.visible ? Math.floor(this.vertices / 3) : 0;
|
|
8715
8790
|
}
|
|
@@ -8835,6 +8910,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8835
8910
|
}
|
|
8836
8911
|
return m;
|
|
8837
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
|
+
}
|
|
8838
8923
|
dispose() {
|
|
8839
8924
|
for (const map of [this.body, this.stream, this.film, this.wet]) {
|
|
8840
8925
|
for (const m of map.values()) m.dispose();
|
|
@@ -8910,9 +8995,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8910
8995
|
var dot2 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
8911
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]];
|
|
8912
8997
|
var length = (a) => Math.hypot(a[0], a[1], a[2]);
|
|
8913
|
-
var normalize2 = (a,
|
|
8998
|
+
var normalize2 = (a, fallback2 = [0, 1, 0]) => {
|
|
8914
8999
|
const l = length(a);
|
|
8915
|
-
return l < 1e-9 ? [
|
|
9000
|
+
return l < 1e-9 ? [fallback2[0], fallback2[1], fallback2[2]] : [a[0] / l, a[1] / l, a[2] / l];
|
|
8916
9001
|
};
|
|
8917
9002
|
var ballistic = (origin, velocity, gravity, t) => [
|
|
8918
9003
|
origin[0] + velocity[0] * t,
|
|
@@ -10348,6 +10433,29 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
10348
10433
|
body.dispose();
|
|
10349
10434
|
bodies.delete(containerId);
|
|
10350
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
|
+
},
|
|
10351
10459
|
update(dt) {
|
|
10352
10460
|
if (disposed) return;
|
|
10353
10461
|
time += dt;
|