@adatechnology/game-weather-three 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-SOEOSGSU.js +621 -0
- package/dist/chunk-SOEOSGSU.js.map +1 -0
- package/dist/index.d.ts +6 -354
- package/dist/index.global.js +206 -24
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +38 -537
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +379 -0
- package/dist/testing.js +9 -0
- package/dist/testing.js.map +1 -0
- package/package.json +10 -5
package/dist/index.js
CHANGED
|
@@ -1,540 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
function createFog(THREE, scene, options = {}) {
|
|
41
|
-
const original = scene.fog;
|
|
42
|
-
const kept = original && original.isFog ? original.clone() : original;
|
|
43
|
-
return {
|
|
44
|
-
update(sample, sky, zone) {
|
|
45
|
-
const values = fogOf(sample, zone, options.expFrom);
|
|
46
|
-
const current = scene.fog;
|
|
47
|
-
if (values.mode === "linear") {
|
|
48
|
-
if (current?.isFog) {
|
|
49
|
-
current.color.copy(sky);
|
|
50
|
-
current.near = values.near;
|
|
51
|
-
current.far = values.far;
|
|
52
|
-
} else scene.fog = new THREE.Fog(sky.getHex(), values.near, values.far);
|
|
53
|
-
} else if (current?.isFogExp2) {
|
|
54
|
-
current.color.copy(sky);
|
|
55
|
-
current.density = values.density;
|
|
56
|
-
} else scene.fog = new THREE.FogExp2(sky.getHex(), values.density);
|
|
57
|
-
const fog = scene.fog;
|
|
58
|
-
fog.color.copy(sky);
|
|
59
|
-
},
|
|
60
|
-
clear() {
|
|
61
|
-
scene.fog = kept && kept.isFog ? kept.clone() : kept ?? null;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
var MIST = { box: { x: [-14, 10], y: [0.2, 1.1], z: [-3, 8] }, drift: [0.15, 0.35], life: [9, 13], size: [3, 5], grow: 1.4, color: 8227488, alpha: 0.13, fadeIn: 0.35 };
|
|
66
|
-
var ASH = { box: { x: [-10, 8], y: [4, 6], z: [-3, 7] }, fall: -0.35, life: [8, 12], size: [0.025, 0.04], color: 10525328, alpha: 0.75, fadeIn: 0.1 };
|
|
67
|
-
function emitAirborne(engine, sample, dt, camera) {
|
|
68
|
-
const { random } = engine;
|
|
69
|
-
const around = (box) => [
|
|
70
|
-
camera[0] + random.between(box.x[0], box.x[1]),
|
|
71
|
-
random.between(box.y[0], box.y[1]),
|
|
72
|
-
camera[2] + random.between(box.z[0], box.z[1])
|
|
73
|
-
];
|
|
74
|
-
if (sample.mistPerSecond > 0) {
|
|
75
|
-
engine.emit("weather-mist", "normal", sample.mistPerSecond, dt, () => ({
|
|
76
|
-
p: around(MIST.box),
|
|
77
|
-
v: [random.between(MIST.drift[0], MIST.drift[1]), 0, random.between(-0.05, 0.05)],
|
|
78
|
-
sp: 0,
|
|
79
|
-
life: MIST.life,
|
|
80
|
-
size: MIST.size,
|
|
81
|
-
grow: MIST.grow,
|
|
82
|
-
c0: MIST.color,
|
|
83
|
-
a: MIST.alpha,
|
|
84
|
-
fi: MIST.fadeIn
|
|
85
|
-
}));
|
|
86
|
-
}
|
|
87
|
-
if (sample.ashPerSecond > 0) {
|
|
88
|
-
engine.emit("weather-ash", "normal", sample.ashPerSecond, dt, () => ({
|
|
89
|
-
p: around(ASH.box),
|
|
90
|
-
v: [random.between(0.1, 0.4), ASH.fall, random.between(-0.1, 0.1)],
|
|
91
|
-
sp: [0.05, 0.15],
|
|
92
|
-
life: ASH.life,
|
|
93
|
-
size: ASH.size,
|
|
94
|
-
c0: ASH.color,
|
|
95
|
-
a: ASH.alpha,
|
|
96
|
-
fi: ASH.fadeIn,
|
|
97
|
-
gnd: 1
|
|
98
|
-
}));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// src/headlights.ts
|
|
103
|
-
var HEADLIGHTS = {
|
|
104
|
-
spot: { color: 16773584, intensity: 2.4, distance: 26, angle: 0.33, penumbra: 0.5, decay: 1.6 },
|
|
105
|
-
/** Lamp position on the car (x is mirrored), where the spot points, and the cone drawn in the air. */
|
|
106
|
-
lamp: [0.65, 0.74, 2.15],
|
|
107
|
-
aim: [0.65, 0.2, 12],
|
|
108
|
-
cone: { radius: 2.4, length: 9, segments: 18, color: 16773584, opacity: 0.06 },
|
|
109
|
-
lens: { on: 16773584, off: 3815988 },
|
|
110
|
-
/** Lit drops a second, thrown into the beam 1 to 13 m ahead (W1176). */
|
|
111
|
-
lit: { rate: 600, depth: [1, 13], spread: 0.33, color: 13622512, alpha: 0.9, life: 0.06, len: 0.035, velocity: [0.8, -14, 0] },
|
|
112
|
-
/** Beyond this many metres a car keeps only the cone: no real light (W-b text of `farol`). */
|
|
113
|
-
lightRange: 40
|
|
114
|
-
};
|
|
115
|
-
var defaultHeadlightRule = (sample) => sample.nightFactor > 0.3 || sample.rain > 0 || sample.fogDensity > 0.02 || sample.visibility < 150;
|
|
116
|
-
function createHeadlights(THREE, rules = {}) {
|
|
117
|
-
const rigs = /* @__PURE__ */ new Set();
|
|
118
|
-
const cone = HEADLIGHTS.cone;
|
|
119
|
-
const lampWorld = new THREE.Vector3();
|
|
120
|
-
const aimWorld = new THREE.Vector3();
|
|
121
|
-
const forward = new THREE.Vector3();
|
|
122
|
-
const rule = rules.lit ?? defaultHeadlightRule;
|
|
123
|
-
const range = rules.lightRange ?? HEADLIGHTS.lightRange;
|
|
124
|
-
function attach(car, lens) {
|
|
125
|
-
const spots = [];
|
|
126
|
-
const cones = [];
|
|
127
|
-
const targets = [];
|
|
128
|
-
for (const side of [-1, 1]) {
|
|
129
|
-
const spot = new THREE.SpotLight(HEADLIGHTS.spot.color, 0, HEADLIGHTS.spot.distance, HEADLIGHTS.spot.angle, HEADLIGHTS.spot.penumbra, HEADLIGHTS.spot.decay);
|
|
130
|
-
spot.position.set(side * HEADLIGHTS.lamp[0], HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2]);
|
|
131
|
-
const target = new THREE.Object3D();
|
|
132
|
-
target.position.set(side * HEADLIGHTS.aim[0], HEADLIGHTS.aim[1], HEADLIGHTS.aim[2]);
|
|
133
|
-
car.add(spot, target);
|
|
134
|
-
spot.target = target;
|
|
135
|
-
const geometry = new THREE.ConeGeometry(cone.radius, cone.length, cone.segments, 1, true);
|
|
136
|
-
geometry.translate(0, -cone.length / 2, 0);
|
|
137
|
-
geometry.rotateX(-Math.PI / 2);
|
|
138
|
-
const material = new THREE.MeshBasicMaterial({ color: cone.color, transparent: true, opacity: cone.opacity, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide, fog: false });
|
|
139
|
-
const beam = new THREE.Mesh(geometry, material);
|
|
140
|
-
beam.position.set(side * HEADLIGHTS.lamp[0], HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2]);
|
|
141
|
-
beam.visible = false;
|
|
142
|
-
car.add(beam);
|
|
143
|
-
spots.push(spot);
|
|
144
|
-
cones.push(beam);
|
|
145
|
-
targets.push(target);
|
|
146
|
-
}
|
|
147
|
-
const rig = { car, spots, cones, lens, targets };
|
|
148
|
-
rigs.add(rig);
|
|
149
|
-
return () => {
|
|
150
|
-
rigs.delete(rig);
|
|
151
|
-
for (const object of [...spots, ...cones, ...targets]) car.remove(object);
|
|
152
|
-
for (const beam of cones) {
|
|
153
|
-
beam.geometry.dispose();
|
|
154
|
-
beam.material.dispose();
|
|
155
|
-
}
|
|
156
|
-
for (const spot of spots) spot.dispose();
|
|
157
|
-
lens?.color.setHex(HEADLIGHTS.lens.off);
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
attach,
|
|
162
|
-
update(sample, dt, camera, engine, enabled = true) {
|
|
163
|
-
const on = enabled && rule(sample);
|
|
164
|
-
let index = 0;
|
|
165
|
-
for (const rig of rigs) {
|
|
166
|
-
rig.car.updateWorldMatrix(true, false);
|
|
167
|
-
lampWorld.set(0, HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2]).applyMatrix4(rig.car.matrixWorld);
|
|
168
|
-
const near = Math.hypot(lampWorld.x - camera[0], lampWorld.z - camera[2]) <= range;
|
|
169
|
-
for (const spot of rig.spots) spot.intensity = on && near ? HEADLIGHTS.spot.intensity : 0;
|
|
170
|
-
for (const beam of rig.cones) beam.visible = on;
|
|
171
|
-
rig.lens?.color.setHex(on ? HEADLIGHTS.lens.on : HEADLIGHTS.lens.off);
|
|
172
|
-
if (!on || !engine || sample.rain <= 0 || !near) {
|
|
173
|
-
index++;
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
aimWorld.set(0, HEADLIGHTS.lamp[1], HEADLIGHTS.lamp[2] + 1).applyMatrix4(rig.car.matrixWorld);
|
|
177
|
-
forward.copy(aimWorld).sub(lampWorld).normalize();
|
|
178
|
-
const side = [forward.z, -forward.x];
|
|
179
|
-
const { random } = engine;
|
|
180
|
-
const lit = HEADLIGHTS.lit;
|
|
181
|
-
engine.emit(`weather-lit-${index}`, "streak", lit.rate, dt, () => {
|
|
182
|
-
const depth = random.between(lit.depth[0], lit.depth[1]);
|
|
183
|
-
const spread = depth * lit.spread * Math.sqrt(random.next());
|
|
184
|
-
const turn = random.between(0, 6.283);
|
|
185
|
-
return {
|
|
186
|
-
p: [lampWorld.x + forward.x * depth + side[0] * Math.cos(turn) * spread, lampWorld.y + forward.y * depth + Math.sin(turn) * spread * 0.6, lampWorld.z + forward.z * depth + side[1] * Math.cos(turn) * spread],
|
|
187
|
-
v: lit.velocity,
|
|
188
|
-
sp: 0,
|
|
189
|
-
life: lit.life,
|
|
190
|
-
len: lit.len,
|
|
191
|
-
c0: lit.color,
|
|
192
|
-
a: lit.alpha
|
|
193
|
-
};
|
|
194
|
-
});
|
|
195
|
-
index++;
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
count: () => rigs.size,
|
|
199
|
-
dispose() {
|
|
200
|
-
for (const rig of [...rigs]) {
|
|
201
|
-
rigs.delete(rig);
|
|
202
|
-
for (const object of [...rig.spots, ...rig.cones, ...rig.targets]) rig.car.remove(object);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// src/heat-haze.ts
|
|
209
|
-
var HEAT_HAZE = { opacity: 0.09, bands: 38, rise: 1.4, color: [1, 0.93, 0.82] };
|
|
210
|
-
var HEAT_HAZE_VERTEX = "varying vec2 vUv; void main(){ vUv = uv; gl_Position = vec4(position.xy, 0., 1.); }";
|
|
211
|
-
var HEAT_HAZE_FRAGMENT = "uniform float uTime; uniform float uHaze; uniform vec3 uColor; varying vec2 vUv; void main(){ float low = 1. - smoothstep(0.15, 0.7, vUv.y); float wave = sin(vUv.y*38. - uTime*1.4 + sin(vUv.x*11. + uTime*.7)*2.2) * .5 + .5; gl_FragColor = vec4(uColor, uHaze*low*wave*.09); }";
|
|
212
|
-
function createHeatHaze(THREE) {
|
|
213
|
-
const uniforms = { uTime: { value: 0 }, uHaze: { value: 0 }, uColor: { value: new THREE.Vector3(...HEAT_HAZE.color) } };
|
|
214
|
-
const material = new THREE.ShaderMaterial({ uniforms, vertexShader: HEAT_HAZE_VERTEX, fragmentShader: HEAT_HAZE_FRAGMENT, transparent: true, depthTest: false, depthWrite: false });
|
|
215
|
-
const object = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
|
|
216
|
-
object.frustumCulled = false;
|
|
217
|
-
object.renderOrder = 999;
|
|
218
|
-
object.name = "weather-heat-haze";
|
|
219
|
-
object.visible = false;
|
|
220
|
-
return {
|
|
221
|
-
object,
|
|
222
|
-
update(sample, time) {
|
|
223
|
-
uniforms.uHaze.value = sample.haze;
|
|
224
|
-
uniforms.uTime.value = time;
|
|
225
|
-
object.visible = sample.haze > 0;
|
|
226
|
-
},
|
|
227
|
-
dispose() {
|
|
228
|
-
object.geometry.dispose();
|
|
229
|
-
material.dispose();
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// src/lightning.ts
|
|
235
|
-
var LIGHTNING = { light: 3.2, background: [0.25, 0.27, 0.32], white: 0.12, skyColor: 3820650, groundColor: 723724 };
|
|
236
|
-
function createLightning(THREE, scene, vfx) {
|
|
237
|
-
const light = new THREE.HemisphereLight(LIGHTNING.skyColor, LIGHTNING.groundColor, 0);
|
|
238
|
-
light.name = "weather-lightning";
|
|
239
|
-
scene.add(light);
|
|
240
|
-
return {
|
|
241
|
-
light,
|
|
242
|
-
update(sample, background) {
|
|
243
|
-
const flash = sample.flash;
|
|
244
|
-
light.intensity = flash * LIGHTNING.light;
|
|
245
|
-
if (flash <= 0) return;
|
|
246
|
-
if (background) {
|
|
247
|
-
background.r += flash * LIGHTNING.background[0];
|
|
248
|
-
background.g += flash * LIGHTNING.background[1];
|
|
249
|
-
background.b += flash * LIGHTNING.background[2];
|
|
250
|
-
}
|
|
251
|
-
const screen = vfx?.workshop().screen;
|
|
252
|
-
if (screen) screen.white = Math.max(screen.white, flash * LIGHTNING.white);
|
|
253
|
-
},
|
|
254
|
-
dispose() {
|
|
255
|
-
scene.remove(light);
|
|
256
|
-
light.dispose();
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/rain-near.ts
|
|
262
|
-
import { inPuddle } from "@adatechnology/game-vfx";
|
|
263
|
-
var SPLASH_FROM_MM = 5;
|
|
264
|
-
function createNearRain(vfx, options = {}) {
|
|
265
|
-
const puddles = options.puddles ?? [];
|
|
266
|
-
return {
|
|
267
|
-
update(sample, dt, camera) {
|
|
268
|
-
const raining = sample.rain > 0;
|
|
269
|
-
const engine = raining ? vfx?.workshop() : vfx?.peekWorkshop();
|
|
270
|
-
if (!engine) return;
|
|
271
|
-
const field = engine.rain;
|
|
272
|
-
if (!raining) {
|
|
273
|
-
if (field.on) field.clear();
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
field.puddleAt = options.puddleAt ?? ((x, z) => inPuddle(puddles, x, z));
|
|
277
|
-
field.drive(sample.drops, sample.windX, camera[0], camera[2]);
|
|
278
|
-
field.update(dt, sample.rain >= (options.splashFrom ?? SPLASH_FROM_MM), camera);
|
|
279
|
-
},
|
|
280
|
-
count: () => vfx?.peekWorkshop()?.rain.count ?? 0,
|
|
281
|
-
clear() {
|
|
282
|
-
vfx?.peekWorkshop()?.rain.clear();
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// src/rain-curtain.ts
|
|
288
|
-
var RAIN_CURTAIN = { count: 14e3, width: 70, height: 25, speed: 13, pointSize: 1.6 };
|
|
289
|
-
var RAIN_CURTAIN_VERTEX = "uniform float uTime; uniform vec3 uCam; void main(){ vec3 p = position; p.y = mod(p.y - uTime*13., 25.); p.xz = uCam.xz + mod(p.xz - uCam.xz + 35., 70.) - 35.; gl_Position = projectionMatrix*viewMatrix*vec4(p,1.); gl_PointSize = 1.6; }";
|
|
290
|
-
var RAIN_CURTAIN_FRAGMENT = "uniform float uDay; void main(){ gl_FragColor = vec4(vec3(.68,.72,.78)*(0.25+0.75*uDay), .22+.1*uDay); }";
|
|
291
|
-
var RAIN_CURTAIN_PROFILES = { low: 0.35, medium: 0.7, high: 1 };
|
|
292
|
-
var RAIN_CURTAIN_FULL_AT = 4e3;
|
|
293
|
-
function rainCurtainPositions(random = Math.random, count = RAIN_CURTAIN.count) {
|
|
294
|
-
const positions = new Float32Array(count * 3);
|
|
295
|
-
for (let index = 0; index < count; index++) {
|
|
296
|
-
positions[index * 3] = (random() - 0.5) * RAIN_CURTAIN.width;
|
|
297
|
-
positions[index * 3 + 1] = random() * RAIN_CURTAIN.height;
|
|
298
|
-
positions[index * 3 + 2] = (random() - 0.5) * RAIN_CURTAIN.width;
|
|
299
|
-
}
|
|
300
|
-
return positions;
|
|
301
|
-
}
|
|
302
|
-
function createRainCurtain(THREE, options = {}) {
|
|
303
|
-
const count = options.count ?? RAIN_CURTAIN.count;
|
|
304
|
-
const geometry = new THREE.BufferGeometry();
|
|
305
|
-
geometry.setAttribute("position", new THREE.BufferAttribute(rainCurtainPositions(options.random, count), 3));
|
|
306
|
-
const uniforms = { uTime: { value: 0 }, uCam: { value: new THREE.Vector3() }, uDay: { value: 1 } };
|
|
307
|
-
const material = new THREE.ShaderMaterial({ uniforms, transparent: true, depthWrite: false, vertexShader: RAIN_CURTAIN_VERTEX, fragmentShader: RAIN_CURTAIN_FRAGMENT });
|
|
308
|
-
const object = new THREE.Points(geometry, material);
|
|
309
|
-
object.frustumCulled = false;
|
|
310
|
-
object.name = "weather-rain-curtain";
|
|
311
|
-
object.visible = false;
|
|
312
|
-
let share = RAIN_CURTAIN_PROFILES[options.profile ?? "high"];
|
|
313
|
-
let drawn = 0;
|
|
314
|
-
return {
|
|
315
|
-
object,
|
|
316
|
-
update(sample, camera, time, daylight) {
|
|
317
|
-
const fullAt = options.fullAt ?? RAIN_CURTAIN_FULL_AT;
|
|
318
|
-
drawn = sample.rain > 0 ? Math.round(count * share * Math.min(1, sample.drops / fullAt)) : 0;
|
|
319
|
-
object.visible = drawn > 0;
|
|
320
|
-
geometry.setDrawRange(0, drawn);
|
|
321
|
-
uniforms.uTime.value = time;
|
|
322
|
-
uniforms.uDay.value = daylight;
|
|
323
|
-
uniforms.uCam.value.set(camera.x, camera.y, camera.z);
|
|
324
|
-
},
|
|
325
|
-
setProfile(profile) {
|
|
326
|
-
share = RAIN_CURTAIN_PROFILES[profile];
|
|
327
|
-
},
|
|
328
|
-
drawn: () => drawn,
|
|
329
|
-
dispose() {
|
|
330
|
-
geometry.dispose();
|
|
331
|
-
material.dispose();
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// src/renderer.ts
|
|
337
|
-
var WEATHER_LAYERS = ["far_rain", "near_rain", "mist", "fog", "sky", "lightning", "headlights", "haze"];
|
|
338
|
-
function createWeatherRenderer(THREE, options) {
|
|
339
|
-
const { scene, camera, weather, vfx } = options;
|
|
340
|
-
const group = new THREE.Group();
|
|
341
|
-
group.name = "weather";
|
|
342
|
-
scene.add(group);
|
|
343
|
-
const curtain = createRainCurtain(THREE, { ...options.random ? { random: options.random } : {}, ...options.profile ? { profile: options.profile } : {} });
|
|
344
|
-
const haze = createHeatHaze(THREE);
|
|
345
|
-
group.add(curtain.object, haze.object);
|
|
346
|
-
const sky = createSky(THREE, scene);
|
|
347
|
-
const fog = createFog(THREE, scene);
|
|
348
|
-
const lightning = createLightning(THREE, scene, vfx);
|
|
349
|
-
const headlights = createHeadlights(THREE, options.headlights);
|
|
350
|
-
const nearRain = createNearRain(vfx, { ...options.puddles ? { puddles: options.puddles } : {}, ...options.puddleAt ? { puddleAt: options.puddleAt } : {} });
|
|
351
|
-
const on = new Map(WEATHER_LAYERS.map((id) => [id, options.layers?.[id] ?? true]));
|
|
352
|
-
let zone = options.zone;
|
|
353
|
-
let time = 0;
|
|
354
|
-
const eye = { x: 0, y: 0, z: 0 };
|
|
355
|
-
const switchedOff = (id) => {
|
|
356
|
-
if (id === "far_rain") curtain.object.visible = false;
|
|
357
|
-
else if (id === "near_rain") nearRain.clear();
|
|
358
|
-
else if (id === "fog") fog.clear();
|
|
359
|
-
else if (id === "lightning") lightning.light.intensity = 0;
|
|
360
|
-
else if (id === "haze") haze.object.visible = false;
|
|
361
|
-
};
|
|
362
|
-
return {
|
|
363
|
-
curtain,
|
|
364
|
-
nearRain,
|
|
365
|
-
fog,
|
|
366
|
-
sky,
|
|
367
|
-
lightning,
|
|
368
|
-
headlights,
|
|
369
|
-
haze,
|
|
370
|
-
update(dt) {
|
|
371
|
-
time += dt;
|
|
372
|
-
const sample = weather.sample();
|
|
373
|
-
const position = camera.position;
|
|
374
|
-
eye.x = position.x;
|
|
375
|
-
eye.y = position.y;
|
|
376
|
-
eye.z = position.z;
|
|
377
|
-
const where = [eye.x, eye.y, eye.z];
|
|
378
|
-
const background = scene.background ?? void 0;
|
|
379
|
-
const ownSky = on.get("sky") === true;
|
|
380
|
-
const color = ownSky ? sky.update(sample, zone) : background && background.isColor ? background : sky.color;
|
|
381
|
-
if (on.get("fog")) fog.update(sample, color, zone);
|
|
382
|
-
if (on.get("lightning")) lightning.update(sample, ownSky ? scene.background : void 0);
|
|
383
|
-
if (on.get("far_rain")) curtain.update(sample, eye, time, sample.sun);
|
|
384
|
-
if (on.get("haze")) haze.update(sample, time);
|
|
385
|
-
if (vfx) {
|
|
386
|
-
if (on.get("near_rain")) nearRain.update(sample, dt, where);
|
|
387
|
-
if (on.get("mist")) emitAirborne(vfx.workshop(), sample, dt, where);
|
|
388
|
-
}
|
|
389
|
-
headlights.update(sample, dt, where, vfx && sample.rain > 0 ? vfx.workshop() : void 0, on.get("headlights"));
|
|
390
|
-
},
|
|
391
|
-
setLayer(id, value) {
|
|
392
|
-
on.set(id, value);
|
|
393
|
-
if (!value) switchedOff(id);
|
|
394
|
-
},
|
|
395
|
-
layer: (id) => on.get(id) === true,
|
|
396
|
-
setZoneFog(next) {
|
|
397
|
-
zone = next;
|
|
398
|
-
},
|
|
399
|
-
setProfile(profile) {
|
|
400
|
-
curtain.setProfile(profile);
|
|
401
|
-
},
|
|
402
|
-
stats: () => ({ rainCurtain: curtain.drawn(), rainNear: nearRain.count(), cars: headlights.count() }),
|
|
403
|
-
dispose() {
|
|
404
|
-
scene.remove(group);
|
|
405
|
-
curtain.dispose();
|
|
406
|
-
haze.dispose();
|
|
407
|
-
lightning.dispose();
|
|
408
|
-
headlights.dispose();
|
|
409
|
-
nearRain.clear();
|
|
410
|
-
}
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// src/bench/visual.ts
|
|
415
|
-
import { footprintBlocker } from "@adatechnology/game-contracts";
|
|
416
|
-
|
|
417
|
-
// src/bench/text.ts
|
|
418
|
-
var TEXTS = {
|
|
419
|
-
en: { layers: { far_rain: "Rain curtain", near_rain: "Near rain and splashes", mist: "Mist and ash", sky: "Sky", lightning: "Lightning", headlights: "Headlights", haze: "Heat haze" } },
|
|
420
|
-
"pt-BR": { layers: { far_rain: "Cortina de chuva", near_rain: "Chuva perto e respingos", mist: "N\xE9voa e cinza", sky: "C\xE9u", lightning: "Rel\xE2mpago", headlights: "Far\xF3is", haze: "N\xE9voa de calor" } }
|
|
421
|
-
};
|
|
422
|
-
var weatherVisualTextOf = (locale) => TEXTS[locale] ?? TEXTS[locale.split("-")[0] ?? "en"] ?? TEXTS.en;
|
|
423
|
-
|
|
424
|
-
// src/bench/visual.ts
|
|
425
|
-
var RIG_VEHICLE = "midsize_sedan";
|
|
426
|
-
var LAYER_ORDER = ["far_rain", "near_rain", "mist", "sky", "lightning", "headlights", "haze"];
|
|
427
|
-
var RIG_DEFAULT = [1.6, -2.5, 0];
|
|
428
|
-
function buildRig(THREE, vehicles, key) {
|
|
429
|
-
const group = new THREE.Group();
|
|
430
|
-
group.name = "weather-lamp-rig";
|
|
431
|
-
const car = vehicles?.build(key, { quality: "lite" });
|
|
432
|
-
if (car) {
|
|
433
|
-
car.group.position.z = HEADLIGHTS.lamp[2] + 0.1 - car.length / 2;
|
|
434
|
-
group.add(car.group);
|
|
435
|
-
}
|
|
436
|
-
const footprint = car ? footprintOf(car) : void 0;
|
|
437
|
-
return {
|
|
438
|
-
group,
|
|
439
|
-
...footprint ? { footprint } : {},
|
|
440
|
-
dispose() {
|
|
441
|
-
car?.dispose();
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
function createWeatherVisual(options = {}) {
|
|
446
|
-
return {
|
|
447
|
-
layers(locale) {
|
|
448
|
-
const text = weatherVisualTextOf(locale);
|
|
449
|
-
return LAYER_ORDER.map((id) => ({ id, name: text.layers[id], default: options.layers?.[id] ?? true }));
|
|
450
|
-
},
|
|
451
|
-
create({ host, weather }) {
|
|
452
|
-
const THREE = host.THREE;
|
|
453
|
-
const renderer = createWeatherRenderer(THREE, {
|
|
454
|
-
scene: host.scene,
|
|
455
|
-
camera: host.camera,
|
|
456
|
-
weather,
|
|
457
|
-
...options.vfx ? { vfx: options.vfx } : {},
|
|
458
|
-
...options.profile ? { profile: options.profile } : {},
|
|
459
|
-
...options.random ? { random: options.random } : {},
|
|
460
|
-
...options.zone ? { zone: options.zone } : {},
|
|
461
|
-
...options.puddles ? { puddles: options.puddles } : {},
|
|
462
|
-
...options.puddleAt ? { puddleAt: options.puddleAt } : {},
|
|
463
|
-
...options.headlights ? { headlights: options.headlights } : {},
|
|
464
|
-
...options.layers ? { layers: options.layers } : {}
|
|
465
|
-
});
|
|
466
|
-
const item = options.rigItem && host.addObject ? options.rigItem : void 0;
|
|
467
|
-
return attachRig(THREE, host, renderer, options.rig === void 0 ? RIG_DEFAULT : options.rig, item ? void 0 : options.vehicles?.(THREE), options.rigVehicle ?? RIG_VEHICLE, item);
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
function footprintOf(car) {
|
|
472
|
-
if (!car.width) return void 0;
|
|
473
|
-
const z = car.group.position.z;
|
|
474
|
-
return { minX: -car.width / 2, maxX: car.width / 2, minZ: z - car.length / 2, maxZ: z + car.length / 2 };
|
|
475
|
-
}
|
|
476
|
-
function lengthOf(THREE, root) {
|
|
477
|
-
root.updateMatrixWorld(true);
|
|
478
|
-
const inverse = new THREE.Matrix4().copy(root.matrixWorld).invert();
|
|
479
|
-
const box = new THREE.Box3();
|
|
480
|
-
const piece = new THREE.Box3();
|
|
481
|
-
const matrix = new THREE.Matrix4();
|
|
482
|
-
root.traverse((mesh) => {
|
|
483
|
-
const m = mesh;
|
|
484
|
-
if (!m.isMesh || !m.geometry) return;
|
|
485
|
-
if (!m.geometry.boundingBox) m.geometry.computeBoundingBox();
|
|
486
|
-
piece.copy(m.geometry.boundingBox).applyMatrix4(matrix.multiplyMatrices(inverse, m.matrixWorld));
|
|
487
|
-
box.union(piece);
|
|
488
|
-
});
|
|
489
|
-
return box.isEmpty() ? 0 : box.max.z - box.min.z;
|
|
490
|
-
}
|
|
491
|
-
function attachRig(THREE, host, renderer, place, vehicles, key, item) {
|
|
492
|
-
const scene = host.scene;
|
|
493
|
-
const rig = place === false ? void 0 : buildRig(THREE, vehicles, key);
|
|
494
|
-
let detach;
|
|
495
|
-
let unblock;
|
|
496
|
-
if (rig && place) {
|
|
497
|
-
rig.group.position.set(place[0], 0, place[1]);
|
|
498
|
-
rig.group.rotation.y = place[2];
|
|
499
|
-
scene.add(rig.group);
|
|
500
|
-
detach = renderer.headlights.attach(rig.group);
|
|
501
|
-
if (rig.footprint) unblock = host.addBlocker?.(footprintBlocker(rig.group, rig.footprint));
|
|
502
|
-
}
|
|
503
|
-
let car = null;
|
|
504
|
-
let stopWaiting;
|
|
505
|
-
if (item && place) {
|
|
506
|
-
const tryPlace = () => {
|
|
507
|
-
if (car || !host.addObject) return;
|
|
508
|
-
const placed = host.addObject(item, { position: [place[0], 0, place[1]], rotationY: place[2] });
|
|
509
|
-
if (!placed) return;
|
|
510
|
-
car = placed;
|
|
511
|
-
stopWaiting?.();
|
|
512
|
-
stopWaiting = void 0;
|
|
513
|
-
const ahead = lengthOf(THREE, placed.root) / 2 - HEADLIGHTS.lamp[2] - 0.1;
|
|
514
|
-
rig.group.position.set(place[0] + Math.sin(place[2]) * ahead, 0, place[1] + Math.cos(place[2]) * ahead);
|
|
515
|
-
};
|
|
516
|
-
tryPlace();
|
|
517
|
-
if (!car) stopWaiting = host.onFrame(tryPlace);
|
|
518
|
-
}
|
|
519
|
-
return {
|
|
520
|
-
update: (dt) => renderer.update(dt),
|
|
521
|
-
setLayer(id, on) {
|
|
522
|
-
if (WEATHER_IDS.includes(id)) renderer.setLayer(id, on);
|
|
523
|
-
},
|
|
524
|
-
dispose() {
|
|
525
|
-
detach?.();
|
|
526
|
-
unblock?.();
|
|
527
|
-
stopWaiting?.();
|
|
528
|
-
car?.remove();
|
|
529
|
-
if (rig) {
|
|
530
|
-
scene.remove(rig.group);
|
|
531
|
-
rig.dispose();
|
|
532
|
-
}
|
|
533
|
-
renderer.dispose();
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
var WEATHER_IDS = ["far_rain", "near_rain", "mist", "fog", "sky", "lightning", "headlights", "haze"];
|
|
1
|
+
import {
|
|
2
|
+
ASH,
|
|
3
|
+
FOG_EXP_FROM,
|
|
4
|
+
FOG_NEAR,
|
|
5
|
+
FOG_ZONE_NEAR,
|
|
6
|
+
HEADLIGHTS,
|
|
7
|
+
HEAT_HAZE,
|
|
8
|
+
HEAT_HAZE_FRAGMENT,
|
|
9
|
+
HEAT_HAZE_VERTEX,
|
|
10
|
+
LIGHTNING,
|
|
11
|
+
MIST,
|
|
12
|
+
RAIN_CURTAIN,
|
|
13
|
+
RAIN_CURTAIN_FRAGMENT,
|
|
14
|
+
RAIN_CURTAIN_FULL_AT,
|
|
15
|
+
RAIN_CURTAIN_PROFILES,
|
|
16
|
+
RAIN_CURTAIN_VERTEX,
|
|
17
|
+
SKY_DAY,
|
|
18
|
+
SKY_NIGHT,
|
|
19
|
+
SPLASH_FROM_MM,
|
|
20
|
+
WEATHER_LAYERS,
|
|
21
|
+
ZONE_FOG_DEFAULTS,
|
|
22
|
+
ZONE_LIGHT,
|
|
23
|
+
createFog,
|
|
24
|
+
createHeadlights,
|
|
25
|
+
createHeatHaze,
|
|
26
|
+
createLightning,
|
|
27
|
+
createNearRain,
|
|
28
|
+
createRainCurtain,
|
|
29
|
+
createSky,
|
|
30
|
+
createWeatherRenderer,
|
|
31
|
+
createWeatherVisual,
|
|
32
|
+
defaultHeadlightRule,
|
|
33
|
+
densityForFar,
|
|
34
|
+
emitAirborne,
|
|
35
|
+
fogOf,
|
|
36
|
+
rainCurtainPositions,
|
|
37
|
+
weatherVisualTextOf
|
|
38
|
+
} from "./chunk-SOEOSGSU.js";
|
|
538
39
|
export {
|
|
539
40
|
ASH,
|
|
540
41
|
FOG_EXP_FROM,
|