载入中...
搜索中...
未找到
DayNight.cpp
浏览该文件的文档.
1#include "daynight/DayNight.h"
3
4#include "graphics/Graphics.h"
6#include "graphics/Light.h"
8#include "graphics/Texture.h"
9
10#include <cmath>
11#include <cstdint>
12#include <cstring>
13#include <string>
14#include <vector>
15
16#include <simplesquirrel/simplesquirrel.hpp>
17
18namespace eve::daynight {
19
20namespace {
21
22// Solar orbit constants.
23constexpr float kPi = 3.14159265f;
24constexpr float kMaxElevationDeg = 70.f; // solar elevation at local noon
25constexpr float kSkyCubeSize = 128; // per-face resolution of the procedural sky
26constexpr int kMaxFireflies = 8;
27
28// Night light names (script-facing) — index maps to the Impl flags array.
29
30inline float deg2rad(float d) { return d * kPi / 180.f; }
31inline float smoothstep(float e0, float e1, float x) {
32 if (std::fabs(e1 - e0) < 1e-7f) return x < e0 ? 0.f : 1.f;
33 const float t = std::clamp((x - e0) / (e1 - e0), 0.f, 1.f);
34 return t * t * (3.f - 2.f * t);
35}
36
37struct Vec3 {
38 float x, y, z;
39};
40
41inline Vec3 add(Vec3 a, Vec3 b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; }
42inline Vec3 mul(Vec3 a, Vec3 b) { return {a.x * b.x, a.y * b.y, a.z * b.z}; }
43inline Vec3 scale(Vec3 a, float s) { return {a.x * s, a.y * s, a.z * s}; }
44
45// Compact single-scattering atmosphere approximation. The wavelength-dependent
46// coefficients preserve the important physical relationships (blue Rayleigh sky,
47// neutral Mie haze, warm attenuated low sun) while remaining cheap enough to build
48// the IBL cubemap on the CPU. It is deliberately isolated so a future GPU LUT
49// implementation can consume the same public atmosphere parameters.
50Vec3 atmosphereRadiance(Vec3 view, Vec3 sun, float turbidity, float mieStrength) {
51 const float mu = std::clamp(view.x * sun.x + view.y * sun.y + view.z * sun.z, -1.f, 1.f);
52 const float horizonMass = 1.f / std::max(0.08f, view.y + 0.14f);
53 const float sunMass = 1.f / std::max(0.06f, sun.y + 0.12f);
54 const Vec3 betaR{0.055f, 0.130f, 0.285f};
55 const Vec3 betaM = scale(Vec3{0.18f, 0.17f, 0.15f}, mieStrength * (0.35f + turbidity * 0.09f));
56 const Vec3 extinction = add(betaR, betaM);
57 const Vec3 viewT{std::exp(-extinction.x * horizonMass),
58 std::exp(-extinction.y * horizonMass),
59 std::exp(-extinction.z * horizonMass)};
60 const Vec3 sunT{std::exp(-extinction.x * sunMass),
61 std::exp(-extinction.y * sunMass),
62 std::exp(-extinction.z * sunMass)};
63 const float rayleighPhase = 3.f / (16.f * kPi) * (1.f + mu * mu);
64 const float g = std::clamp(0.72f + turbidity * 0.008f, 0.72f, 0.82f);
65 const float gg = g * g;
66 const float miePhase = (1.f - gg) /
67 (4.f * kPi * std::pow(std::max(0.015f, 1.f + gg - 2.f * g * mu), 1.5f));
68 const Vec3 scatter = add(scale(betaR, rayleighPhase * 13.f),
69 scale(betaM, miePhase * 2.2f));
70 Vec3 sky = mul(mul(scatter, sunT), Vec3{1.f - viewT.x, 1.f - viewT.y, 1.f - viewT.z});
71 // Multiple-scattering floor prevents a black antisolar horizon and approximates
72 // light returned by the ground/atmosphere without an expensive integral.
73 sky = add(sky, scale(mul(sunT, Vec3{0.18f, 0.22f, 0.30f}),
74 0.12f + 0.18f * (1.f - std::max(view.y, 0.f))));
75 return sky;
76}
77
78Vec3 toneMapSky(Vec3 c, float exposure) {
79 c = scale(c, std::max(exposure, 0.01f));
80 // ACES fitted curve, followed by display gamma. Keeps a smooth solar halo in
81 // the current RGBA8 backend rather than clipping radiance before conversion.
82 auto channel = [](float x) {
83 x = std::clamp((x * (2.51f * x + 0.03f)) /
84 (x * (2.43f * x + 0.59f) + 0.14f), 0.f, 1.f);
85 return std::pow(x, 1.f / 2.2f);
86 };
87 return {channel(c.x), channel(c.y), channel(c.z)};
88}
89
90Vec3 attenuatedSunColor(float sunElevation, float turbidity, float mieStrength) {
91 const float mass = 1.f / std::max(0.06f, sunElevation + 0.12f);
92 const Vec3 extinction = add(Vec3{0.055f, 0.130f, 0.285f},
93 scale(Vec3{0.18f, 0.17f, 0.15f}, mieStrength * (0.35f + turbidity * 0.09f)));
94 Vec3 c{std::exp(-extinction.x * mass), std::exp(-extinction.y * mass),
95 std::exp(-extinction.z * mass)};
96 const float maxChannel = std::max({c.x, c.y, c.z, 1e-5f});
97 return scale(c, 1.f / maxChannel);
98}
99
100Vec3 correlatedColorTemperature(float kelvin) {
101 const float t = std::clamp(kelvin, 1000.f, 40000.f) / 100.f;
102 const float r = t <= 66.f ? 1.f : 1.2929362f * std::pow(t - 60.f, -0.13320476f);
103 const float g = t <= 66.f ? 0.39008158f * std::log(t) - 0.63184144f
104 : 1.1298909f * std::pow(t - 60.f, -0.07551485f);
105 const float b = t >= 66.f ? 1.f
106 : t <= 19.f ? 0.f
107 : 0.5432068f * std::log(t - 10.f) - 1.1962541f;
108 return {std::clamp(r, 0.f, 1.f), std::clamp(g, 0.f, 1.f),
109 std::clamp(b, 0.f, 1.f)};
110}
111
112Vec3 manualSunColor(const PcgManualSunState &sun) {
113 const Vec3 temperature = correlatedColorTemperature(sun.kelvin);
114 return {sun.red * temperature.x, sun.green * temperature.y,
115 sun.blue * temperature.z};
116}
117
118// Convert an elevation/azimuth to a unit direction pointing at the sun.
119// azimuth measured clockwise from +Z, elevation above the horizon.
120inline void sunDirection(float elevDeg, float azimDeg, float &dx, float &dy, float &dz) {
121 const float el = deg2rad(elevDeg);
122 const float az = deg2rad(azimDeg);
123 const float he = std::cos(el);
124 dx = he * std::sin(az);
125 dy = std::sin(el);
126 dz = he * std::cos(az);
127}
128
129// Tiny deterministic hash for stars (no <random> dependency).
130inline uint32_t hash13(uint32_t x) {
131 x ^= x >> 16;
132 x *= 0x7feb352du;
133 x ^= x >> 15;
134 x *= 0x846ca68bu;
135 x ^= x >> 16;
136 return x;
137}
138inline float hashUnit(uint32_t x) { return float(hash13(x) % 10000u) / 9999.f; }
139
140// Fill one cubemap face's RGBA. `face` in {0..5} order +X,-X,+Y,-Y,+Z,-Z.
141// dirAt(x,y) writes the world direction (unnormalized ok) for a pixel.
142void fillSkyFace(std::vector<uint8_t> &px, int size, int face,
143 const float sunDir[3], const Vec3 &directSunColor, float sunEnergy,
144 float nightAmount,
145 float turbidity, float mieStrength, float exposure, float cloudiness,
146 float rotationDegrees, const Vec3 &tint,
147 void (*dirAt)(int face, int size, int x, int y, float out[3])) {
148 const int n = size;
149 for (int y = 0; y < n; ++y) {
150 for (int x = 0; x < n; ++x) {
151 float d[3];
152 dirAt(face, n, x, y, d);
153 const float len = std::sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
154 if (len < 1e-6f) { d[0] = 0.f; d[1] = 1.f; d[2] = 0.f; }
155 else { d[0] /= len; d[1] /= len; d[2] /= len; }
156 if (rotationDegrees != 0.f) {
157 const float angle = deg2rad(rotationDegrees);
158 const float x = d[0] * std::cos(angle) + d[2] * std::sin(angle);
159 d[2] = -d[0] * std::sin(angle) + d[2] * std::cos(angle);
160 d[0] = x;
161 }
162
163 const float up = d[1];
164 const Vec3 view{d[0], std::max(d[1], 0.002f), d[2]};
165 const Vec3 sun{sunDir[0], sunDir[1], sunDir[2]};
166 Vec3 day = atmosphereRadiance(view, sun, turbidity, mieStrength);
167 Vec3 night{0.006f + 0.012f * std::max(up, 0.f),
168 0.010f + 0.020f * std::max(up, 0.f),
169 0.035f + 0.070f * std::max(up, 0.f)};
170 Vec3 color = add(scale(day, (1.f - nightAmount) * sunEnergy),
171 scale(night, nightAmount));
172
173 // Sun disc: a tight highlight around the sun direction.
174 const float dot = d[0] * sunDir[0] + d[1] * sunDir[1] + d[2] * sunDir[2];
175 // Physical solar angular radius is about 0.27 degrees. Slightly enlarge it
176 // to remain stable in a 128px cubemap; the Mie term supplies the broad halo.
177 const float disc = smoothstep(std::cos(deg2rad(0.65f)),
178 std::cos(deg2rad(0.35f)), dot) * sunEnergy;
179 const float sunMass = 1.f / std::max(0.06f, sun.y + 0.12f);
180 const Vec3 atmosphericSun{std::exp(-0.16f * sunMass),
181 std::exp(-0.28f * sunMass),
182 std::exp(-0.58f * sunMass)};
183 const Vec3 sunColor{atmosphericSun.x * directSunColor.x,
184 atmosphericSun.y * directSunColor.y,
185 atmosphericSun.z * directSunColor.z};
186 color = add(color, scale(sunColor, disc * 12.f));
187
188 // Stars (only at night, only in the sky hemisphere, avoid the sun).
189 float star = 0.f;
190 if (nightAmount > 0.5f && up > 0.05f && dot < 0.98f) {
191 uint32_t h = hash13(uint32_t((x * 73856093) ^ (y * 19349663) ^ (face * 83492791)));
192 if (h % 40u == 0u) {
193 const float tw = 0.6f + 0.4f * hashUnit(h + 1u);
194 star = nightAmount * tw;
195 }
196 }
197 color = add(color, scale(Vec3{0.9f, 0.95f, 1.f}, star * (1.f - cloudiness)));
198 const Vec3 overcast{0.075f + 0.035f * std::max(up, 0.f),
199 0.090f + 0.045f * std::max(up, 0.f),
200 0.125f + 0.060f * std::max(up, 0.f)};
201 color = add(scale(color, 1.f - cloudiness * 0.82f),
202 scale(overcast, cloudiness));
203 color = {color.x * tint.x, color.y * tint.y, color.z * tint.z};
204 color = toneMapSky(color, exposure);
205
206 const int i = (y * n + x) * 4;
207 px[i + 0] = uint8_t(std::clamp(color.x * 255.f, 0.f, 255.f));
208 px[i + 1] = uint8_t(std::clamp(color.y * 255.f, 0.f, 255.f));
209 px[i + 2] = uint8_t(std::clamp(color.z * 255.f, 0.f, 255.f));
210 px[i + 3] = 255;
211 }
212 }
213}
214
215// Standard cubemap direction mapping for each face (u,v in [0,size]).
216void cubeDir(int face, int size, int x, int y, float out[3]) {
217 const float u = (2.f * (float(x) + 0.5f) / float(size)) - 1.f; // -1..1
218 const float v = (2.f * (float(y) + 0.5f) / float(size)) - 1.f; // -1..1
219 switch (face) {
220 case 0: out[0] = 1.f; out[1] = -v; out[2] = -u; break; // +X
221 case 1: out[0] = -1.f; out[1] = -v; out[2] = u; break; // -X
222 case 2: out[0] = u; out[1] = 1.f; out[2] = v; break; // +Y
223 case 3: out[0] = u; out[1] = -1.f; out[2] = -v; break; // -Y
224 case 4: out[0] = u; out[1] = -v; out[2] = 1.f; break; // +Z
225 default: out[0] = -u; out[1] = -v; out[2] = -1.f; break; // -Z
226 }
227}
228
229} // namespace
230
233 bool built = false;
234
235 // clock
236 float timeOfDay = 9.f; // hours 0..24
237 float speed = 0.5f; // simulated hours per real second
238 bool paused = false;
239
240 // derived sun
241 float elevDeg = 0.f;
242 float azimDeg = 0.f;
243 float sunDir[3] = {0.f, 1.f, 0.f};
244 float sunEnergy = 1.f;
245 float turbidity = 2.5f;
246 float skyExposure = 1.f;
247 float mieStrength = 1.f;
248 float weatherCloudiness = 0.f;
249 float weatherFlash = 0.f;
255 bool pcgFogWasActive = false;
256 std::string pcgFogBaseMode = "screenspace";
257 std::string pcgFogBaseQuality = "medium";
258 float pcgFogBaseDensity = 0.85f;
259 float pcgFogBaseStart = 2.f;
260 float pcgFogBaseEnd = 40.f;
261
262 // sky cache (regenerate only when the sun bucket changes)
263 bool skyboxEnabled = true;
265 std::array<graphics::Texture *, 6> skyFaces{};
266 std::array<float, 6> skyFaceCenterLuminance{};
268
269 // night lights
270 bool nightLight[4] = {true, true, false, true}; // moonlight, starlight, fire, fireflies
271 float fireX = 0.f, fireY = 0.5f, fireZ = 0.f;
272
273 struct Fly {
274 float x, y, z; // base anchor
275 float seed; // animation phase
276 };
277 std::vector<Fly> flies;
280 std::vector<graphics::Light3D *> flyLights;
281};
282
283const char *const DayNight::kNamedLights[] = {"moonlight", "starlight", "fire", "fireflies"};
284const int DayNight::kNamedLightCount = 4;
285
286DayNight::DayNight() : impl_(new Impl()) {}
287DayNight::~DayNight() { delete impl_; }
288
289// ---------------------------------------------------------------------------
290// Clock / derived state
291// ---------------------------------------------------------------------------
292
293void DayNight::setTimeOfDay(float hours) {
294 float h = std::fmod(hours, 24.f);
295 if (h < 0.f) h += 24.f;
296 impl_->timeOfDay = h;
297}
298float DayNight::getTimeOfDay() const { return impl_->timeOfDay; }
299
300void DayNight::setSpeed(float hprs) { impl_->speed = hprs < 0.f ? 0.f : hprs; }
301float DayNight::getSpeed() const { return impl_->speed; }
302void DayNight::setPaused(bool p) { impl_->paused = p; }
303bool DayNight::isPaused() const { return impl_->paused; }
304
305bool DayNight::isNight() const { return impl_->elevDeg < 0.f; }
306
307float DayNight::getSunElevation() const { return impl_->elevDeg; }
308float DayNight::getSunAzimuth() const { return impl_->azimDeg; }
309float DayNight::getSunDirX() const { return impl_->sunDir[0]; }
310float DayNight::getSunDirY() const { return impl_->sunDir[1]; }
311float DayNight::getSunDirZ() const { return impl_->sunDir[2]; }
312float DayNight::getSunIntensity() const { return impl_->sunEnergy; }
313float DayNight::getSunR() const {
314 const Vec3 color = impl_->manualSun.enabled
315 ? manualSunColor(impl_->manualSun)
316 : attenuatedSunColor(impl_->sunDir[1], impl_->turbidity,
317 impl_->mieStrength);
318 const float multiplier = impl_->pcgAmbient.active
320 : 1.f;
321 return color.x * impl_->sunEnergy * (1.f - 0.82f * impl_->weatherCloudiness) * multiplier;
322}
323float DayNight::getSunG() const {
324 const Vec3 color = impl_->manualSun.enabled
325 ? manualSunColor(impl_->manualSun)
326 : attenuatedSunColor(impl_->sunDir[1], impl_->turbidity,
327 impl_->mieStrength);
328 const float multiplier = impl_->pcgAmbient.active
330 : 1.f;
331 return color.y * impl_->sunEnergy * (1.f - 0.82f * impl_->weatherCloudiness) * multiplier;
332}
333float DayNight::getSunB() const {
334 const Vec3 color = impl_->manualSun.enabled
335 ? manualSunColor(impl_->manualSun)
336 : attenuatedSunColor(impl_->sunDir[1], impl_->turbidity,
337 impl_->mieStrength);
338 const float multiplier = impl_->pcgAmbient.active
340 : 1.f;
341 return color.z * impl_->sunEnergy * (1.f - 0.82f * impl_->weatherCloudiness) * multiplier;
342}
343
345 const bool finite = std::isfinite(state.pitchDegrees) &&
346 std::isfinite(state.rotationDegrees) &&
347 std::isfinite(state.intensity) && std::isfinite(state.red) &&
348 std::isfinite(state.green) && std::isfinite(state.blue) &&
349 std::isfinite(state.kelvin);
350 if (!finite || state.pitchDegrees < 0.f || state.pitchDegrees > 360.f ||
351 state.rotationDegrees < 0.f || state.rotationDegrees > 360.f ||
352 state.intensity < 0.f || state.intensity > 8.f || state.red < 0.f ||
353 state.green < 0.f || state.blue < 0.f || state.kelvin < 1500.f ||
354 state.kelvin > 20000.f) {
356 DiagnosticCode::InvalidArgument, "invalid Pcg manual-sun state", {}, {},
357 "daynight.pcgManualSun"));
358 }
359 impl_->manualSun = state;
360 impl_->lastSkyBucket = -1;
361 return Result<void>::success();
362}
363
364PcgManualSunState DayNight::getPcgManualSun() const noexcept { return impl_->manualSun; }
365
367 const bool finite = std::isfinite(state.rotationDegrees) &&
368 std::isfinite(state.exposure) && std::isfinite(state.tintRed) &&
369 std::isfinite(state.tintGreen) && std::isfinite(state.tintBlue);
370 if (!finite || state.rotationDegrees < 0.f || state.rotationDegrees > 360.f ||
371 state.exposure < 0.f || state.exposure > 30.f || state.tintRed < 0.f ||
372 state.tintGreen < 0.f || state.tintBlue < 0.f) {
374 DiagnosticCode::InvalidArgument, "invalid Pcg skybox state", {}, {},
375 "daynight.pcgSkybox"));
376 }
377 impl_->pcgSkybox = state;
378 impl_->lastSkyBucket = -1;
379 return Result<void>::success();
380}
381
382PcgSkyboxState DayNight::getPcgSkybox() const noexcept { return impl_->pcgSkybox; }
383
385 const float values[] = {state.additionalLinearDistance, state.additionalExponentialDensity,
386 state.red, state.green, state.blue, state.density,
387 state.startDistance, state.endDistance,
388 state.globalDensityMultiplier, state.densityAlbedoRed,
389 state.densityAlbedoGreen, state.densityAlbedoBlue,
390 state.densityVolumeDistance};
391 for (float value : values)
392 if (!std::isfinite(value))
394 DiagnosticCode::InvalidArgument, "Pcg fog values must be finite", {}, {},
395 "daynight.pcgFog"));
396 const bool invalid = state.additionalLinearDistance < -5000.f ||
397 state.additionalLinearDistance > 5000.f ||
398 state.additionalExponentialDensity < 0.f ||
399 state.additionalExponentialDensity > 0.05f || state.mode < 0 ||
400 state.mode > 2 || state.red < 0.f || state.green < 0.f ||
401 state.blue < 0.f || state.density < 0.f || state.density > 0.05f ||
402 state.startDistance < 0.f || state.startDistance > 5000.f ||
403 state.endDistance < 0.f || state.endDistance > 5000.f ||
404 state.globalDensityMultiplier < 0.f ||
405 state.globalDensityMultiplier > 5.f ||
406 state.densityAlbedoRed < 0.f || state.densityAlbedoGreen < 0.f ||
407 state.densityAlbedoBlue < 0.f || state.densityVolumeDistance < 0.01f ||
408 state.densityVolumeDistance > 1500.f ||
409 state.densityVolumeEffect < 0 || state.densityVolumeEffect > 4 ||
410 state.densityVolumeTiling < 0 || state.densityVolumeTiling > 5;
411 if (invalid)
413 DiagnosticCode::InvalidArgument, "invalid Pcg fog state", {}, {},
414 "daynight.pcgFog"));
415 impl_->pcgFog = state;
416 return Result<void>::success();
417}
418
419PcgFogState DayNight::getPcgFog() const noexcept { return impl_->pcgFog; }
420
422 const float values[] = {state.intensity, state.skyRed, state.skyGreen, state.skyBlue,
423 state.equatorRed, state.equatorGreen, state.equatorBlue,
424 state.groundRed, state.groundGreen, state.groundBlue,
425 state.globalLightMultiplier};
426 for (float value : values) {
427 if (!std::isfinite(value) || value < 0.f)
430 "Pcg ambient-light values must be finite and non-negative", {}, {},
431 "daynight.pcgAmbientLight"));
432 }
433 if (state.intensity > 10.f || state.globalLightMultiplier > 5.f)
435 DiagnosticCode::InvalidArgument, "invalid Pcg ambient-light range", {}, {},
436 "daynight.pcgAmbientLight"));
437 impl_->pcgAmbient = state;
438 return Result<void>::success();
439}
440
442 return impl_->pcgAmbient;
443}
445 impl_->turbidity = std::clamp(v, 1.5f, 10.f);
446 impl_->lastSkyBucket = -1;
447}
448float DayNight::getTurbidity() const { return impl_->turbidity; }
450 impl_->skyExposure = std::clamp(v, 0.05f, 8.f);
451 impl_->lastSkyBucket = -1;
452}
453float DayNight::getSkyExposure() const { return impl_->skyExposure; }
455 impl_->mieStrength = std::clamp(v, 0.f, 4.f);
456 impl_->lastSkyBucket = -1;
457}
458float DayNight::getMieStrength() const { return impl_->mieStrength; }
459
460// Sky / ambient colors are functions of the sun energy and night amount.
461float DayNight::getSkyR() const {
462 const float angle = deg2rad(impl_->pcgSkybox.enabled ? impl_->pcgSkybox.rotationDegrees : 0.f);
463 const Vec3 c = toneMapSky(atmosphereRadiance({std::sin(angle), 0.04f, std::cos(angle)},
464 {impl_->sunDir[0], impl_->sunDir[1], impl_->sunDir[2]}, impl_->turbidity,
465 impl_->mieStrength), impl_->pcgSkybox.enabled ? impl_->pcgSkybox.exposure : impl_->skyExposure);
466 const float clear = c.x * impl_->sunEnergy + 0.012f * (1.f - impl_->sunEnergy);
467 const float result = clear * (1.f - impl_->weatherCloudiness * 0.72f) +
468 impl_->weatherCloudiness * 0.075f + impl_->weatherFlash * 0.38f;
469 return result * (impl_->pcgSkybox.enabled ? impl_->pcgSkybox.tintRed : 1.f);
470}
471float DayNight::getSkyG() const {
472 const float angle = deg2rad(impl_->pcgSkybox.enabled ? impl_->pcgSkybox.rotationDegrees : 0.f);
473 const Vec3 c = toneMapSky(atmosphereRadiance({std::sin(angle), 0.04f, std::cos(angle)},
474 {impl_->sunDir[0], impl_->sunDir[1], impl_->sunDir[2]}, impl_->turbidity,
475 impl_->mieStrength), impl_->pcgSkybox.enabled ? impl_->pcgSkybox.exposure : impl_->skyExposure);
476 const float clear = c.y * impl_->sunEnergy + 0.020f * (1.f - impl_->sunEnergy);
477 const float result = clear * (1.f - impl_->weatherCloudiness * 0.72f) +
478 impl_->weatherCloudiness * 0.090f + impl_->weatherFlash * 0.48f;
479 return result * (impl_->pcgSkybox.enabled ? impl_->pcgSkybox.tintGreen : 1.f);
480}
481float DayNight::getSkyB() const {
482 const float angle = deg2rad(impl_->pcgSkybox.enabled ? impl_->pcgSkybox.rotationDegrees : 0.f);
483 const Vec3 c = toneMapSky(atmosphereRadiance({std::sin(angle), 0.04f, std::cos(angle)},
484 {impl_->sunDir[0], impl_->sunDir[1], impl_->sunDir[2]}, impl_->turbidity,
485 impl_->mieStrength), impl_->pcgSkybox.enabled ? impl_->pcgSkybox.exposure : impl_->skyExposure);
486 const float clear = c.z * impl_->sunEnergy + 0.060f * (1.f - impl_->sunEnergy);
487 const float result = clear * (1.f - impl_->weatherCloudiness * 0.62f) +
488 impl_->weatherCloudiness * 0.125f + impl_->weatherFlash * 0.68f;
489 return result * (impl_->pcgSkybox.enabled ? impl_->pcgSkybox.tintBlue : 1.f);
490}
492 const float night = impl_->nightLight[1] ? 1.0f : 0.6f; // starlight boost
493 return 0.05f * night + impl_->sunEnergy * 0.5f *
494 (1.f - impl_->weatherCloudiness * 0.68f) + impl_->weatherFlash * 0.45f;
495}
496
497void DayNight::setWeatherInfluence(float cloudiness, float lightningFlash) {
498 const float nextCloudiness = std::clamp(cloudiness, 0.f, 1.f);
499 if (std::fabs(nextCloudiness - impl_->weatherCloudiness) >= 0.04f)
500 impl_->lastSkyBucket = -1;
501 impl_->weatherCloudiness = nextCloudiness;
502 impl_->weatherFlash = std::clamp(lightningFlash, 0.f, 1.f);
503}
504float DayNight::getWeatherCloudiness() const { return impl_->weatherCloudiness; }
505float DayNight::getWeatherFlash() const { return impl_->weatherFlash; }
507 if (impl_->pcgAmbient.active) {
508 const auto &a = impl_->pcgAmbient;
509 return (a.skyRed * 0.5f + a.equatorRed * 0.35f + a.groundRed * 0.15f) *
510 a.intensity;
511 }
512 const float ab = getAmbientBrightness();
513 return ab * 0.95f;
514}
516 if (impl_->pcgAmbient.active) {
517 const auto &a = impl_->pcgAmbient;
518 return (a.skyGreen * 0.5f + a.equatorGreen * 0.35f + a.groundGreen * 0.15f) *
519 a.intensity;
520 }
521 const float ab = getAmbientBrightness();
522 return ab * (0.95f + 0.05f * impl_->sunEnergy); // greener in daylight
523}
525 if (impl_->pcgAmbient.active) {
526 const auto &a = impl_->pcgAmbient;
527 return (a.skyBlue * 0.5f + a.equatorBlue * 0.35f + a.groundBlue * 0.15f) *
528 a.intensity;
529 }
530 const float ab = getAmbientBrightness();
531 return ab * (0.95f + 0.15f * impl_->sunEnergy); // bluer in daylight
532}
533
535 if (!fog) return;
537 fog->setFogColor(getSkyR(), getSkyG(), getSkyB());
538 fog->setCloudLightColor(getSunR() + impl_->weatherFlash * 0.5f,
539 getSunG() + impl_->weatherFlash * 0.65f,
540 getSunB() + impl_->weatherFlash * 0.9f);
541 fog->setIntensity(std::max(0.08f, impl_->sunEnergy + impl_->weatherFlash));
542 fog->setTime(impl_->timeOfDay * 18.f);
543 const PcgFogState &pcg = impl_->pcgFog;
544 const bool hasWeatherOffset = pcg.additionalLinearDistance != 0.f ||
546 const bool pcgFogActive = pcg.overrideDensityVolume || pcg.overrideFog ||
547 hasWeatherOffset;
548 if (impl_->pcgFogTarget != fog) {
549 impl_->pcgFogTarget = fog;
550 impl_->pcgFogWasActive = false;
551 }
552 if (pcgFogActive && !impl_->pcgFogWasActive) {
553 impl_->pcgFogBaseMode = fog->getMode();
554 impl_->pcgFogBaseQuality = fog->getQuality();
555 impl_->pcgFogBaseDensity = fog->getFloat("density");
556 impl_->pcgFogBaseStart = fog->getFloat("fogStart");
557 impl_->pcgFogBaseEnd = fog->getFloat("fogEnd");
558 } else if (!pcgFogActive && impl_->pcgFogWasActive) {
559 fog->setMode(impl_->pcgFogBaseMode);
560 fog->setQuality(impl_->pcgFogBaseQuality);
561 fog->setDensity(impl_->pcgFogBaseDensity);
562 fog->setFogStart(impl_->pcgFogBaseStart);
563 fog->setFogEnd(impl_->pcgFogBaseEnd);
564 }
565 impl_->pcgFogWasActive = pcgFogActive;
566 if (pcg.overrideDensityVolume) {
567 static constexpr float hazeDensity[] = {0.0025f, 0.005f, 0.01f, 0.02f, 0.04f};
568 fog->setMode("fog");
569 fog->setQuality(pcg.densityVolumeTiling < 2 ? "low" :
570 pcg.densityVolumeTiling < 4 ? "medium" : "high");
573 fog->setDensity(hazeDensity[pcg.densityVolumeEffect] *
575 fog->setFogStart(0.f);
577 } else if (pcg.overrideFog || hasWeatherOffset) {
578 fog->setMode("fog");
579 fog->setFogColor(pcg.red, pcg.green, pcg.blue);
580 const float modeScale = pcg.mode == 2 ? 1.5f : 1.f;
582 pcg.globalDensityMultiplier * modeScale);
583 const float start = std::max(0.f, pcg.startDistance + pcg.additionalLinearDistance);
584 fog->setFogStart(start);
585 fog->setFogEnd(std::max(start + 1.f,
587 }
588}
589
591 if (!probe) return;
592 static constexpr Vec3 directions[6] = {
593 {1.f, 0.f, 0.f}, {-1.f, 0.f, 0.f}, {0.f, 1.f, 0.f},
594 {0.f, -1.f, 0.f}, {0.f, 0.f, 1.f}, {0.f, 0.f, -1.f},
595 };
596 const Vec3 sun{impl_->sunDir[0], impl_->sunDir[1], impl_->sunDir[2]};
597 const float nightAmount = std::clamp((-impl_->elevDeg) / 12.f, 0.f, 1.f);
598 const float skyRotation = deg2rad(
599 impl_->pcgSkybox.enabled ? impl_->pcgSkybox.rotationDegrees : 0.f);
600 const Vec3 skyTint = impl_->pcgSkybox.enabled
601 ? Vec3{impl_->pcgSkybox.tintRed, impl_->pcgSkybox.tintGreen,
602 impl_->pcgSkybox.tintBlue}
603 : Vec3{1.f, 1.f, 1.f};
604 for (int face = 0; face < 6; ++face) {
605 Vec3 direction = directions[face];
606 const float rotatedX = direction.x * std::cos(skyRotation) +
607 direction.z * std::sin(skyRotation);
608 direction.z = -direction.x * std::sin(skyRotation) +
609 direction.z * std::cos(skyRotation);
610 direction.x = rotatedX;
611 const float up = direction.y;
612 const Vec3 view{direction.x, std::max(direction.y, 0.002f), direction.z};
613 const Vec3 day = atmosphereRadiance(view, sun, impl_->turbidity, impl_->mieStrength);
614 const Vec3 night{0.006f + 0.012f * std::max(up, 0.f),
615 0.010f + 0.020f * std::max(up, 0.f),
616 0.035f + 0.070f * std::max(up, 0.f)};
617 Vec3 color = add(scale(day, (1.f - nightAmount) * impl_->sunEnergy),
618 scale(night, nightAmount));
619 const Vec3 overcast{0.075f + 0.035f * std::max(up, 0.f),
620 0.090f + 0.045f * std::max(up, 0.f),
621 0.125f + 0.060f * std::max(up, 0.f)};
622 color = add(scale(color, 1.f - impl_->weatherCloudiness * 0.82f),
623 scale(overcast, impl_->weatherCloudiness));
624 color = add(color, scale(Vec3{0.75f, 0.90f, 1.20f}, impl_->weatherFlash));
626 : impl_->skyExposure);
627 color = {color.x * skyTint.x, color.y * skyTint.y, color.z * skyTint.z};
628 probe->setSkyFaceColor(face, color.x, color.y, color.z);
629 probe->setSkyFaceTexture(face, impl_->skyFaces[static_cast<size_t>(face)]);
630 const float linearLuminance =
631 color.x * 0.2126f + color.y * 0.7152f + color.z * 0.0722f;
632 const float encodedLuminance =
633 impl_->skyFaceCenterLuminance[static_cast<size_t>(face)];
635 face, encodedLuminance > 1e-4f ? linearLuminance / encodedLuminance : 1.f);
636 }
637 probe->setEnvironmentLighting(impl_->skyCube, 0.5f + 0.5f * impl_->sunEnergy);
638}
639
640// ---------------------------------------------------------------------------
641// Skybox
642// ---------------------------------------------------------------------------
643
645 impl_->skyboxEnabled = enabled;
646 impl_->lastSkyBucket = -1; // force regenerate if re-enabled
647}
648bool DayNight::isSkyboxEnabled() const { return impl_->skyboxEnabled; }
649
650// ---------------------------------------------------------------------------
651// Night lights
652// ---------------------------------------------------------------------------
653
654void DayNight::setNightLight(const std::string &name, bool enabled) {
655 for (int i = 0; i < kNamedLightCount; ++i) {
656 if (name == kNamedLights[i]) {
657 impl_->nightLight[i] = enabled;
658 return;
659 }
660 }
661}
662bool DayNight::isNightLight(const std::string &name) const {
663 for (int i = 0; i < kNamedLightCount; ++i) {
664 if (name == kNamedLights[i]) return impl_->nightLight[i];
665 }
666 return false;
667}
668
669void DayNight::setFirePosition(float x, float y, float z) {
670 impl_->fireX = x; impl_->fireY = y; impl_->fireZ = z;
671 if (impl_->fireLight) {
672 impl_->fireLight->setPosition(x, y, z);
673 impl_->fireLight->setColor(1.0f, 0.55f, 0.2f, 1.2f);
674 impl_->fireLight->setRadius(6.f);
675 }
676}
677
678void DayNight::addFirefly(float x, float y, float z) {
679 if (int(impl_->flies.size()) >= kMaxFireflies) return;
680 Impl::Fly f;
681 f.x = x; f.y = y; f.z = z;
682 f.seed = float(impl_->flies.size()) * 1.7f;
683 impl_->flies.push_back(f);
684 if (impl_->built) {
686 l->setColor(0.6f, 0.9f, 0.3f, 0.9f);
687 l->setRadius(2.5f);
688 l->setEnabled(false);
689 l->setPosition(x, y, z);
690 impl_->flyLights.push_back(l);
691 }
692}
694 impl_->flies.clear();
695 impl_->flyLights.clear();
696}
697int DayNight::getFireflyCount() const { return int(impl_->flies.size()); }
698
699// ---------------------------------------------------------------------------
700// init / update
701// ---------------------------------------------------------------------------
702
704 if (impl_->built) return;
705 impl_->built = true;
706 impl_->gfx = gfx;
707 if (!gfx) return;
708
709 // Moon: a cool directional light, driven at night.
711 impl_->moonLight->setColor(0.55f, 0.65f, 0.9f, 0.35f);
712 impl_->moonLight->setDirection(0.2f, -0.8f, 0.4f);
713 impl_->moonLight->setEnabled(false);
714
715 // Fire: a warm point light (position set by setFirePosition).
717 impl_->fireLight->setColor(1.0f, 0.55f, 0.2f, 1.2f);
718 impl_->fireLight->setRadius(6.f);
719 impl_->fireLight->setPosition(impl_->fireX, impl_->fireY, impl_->fireZ);
720 impl_->fireLight->setEnabled(false);
721
722 // Fireflies.
723 for (const auto &f : impl_->flies) {
725 l->setColor(0.6f, 0.9f, 0.3f, 0.9f);
726 l->setRadius(2.5f);
727 l->setEnabled(false);
728 l->setPosition(f.x, f.y, f.z);
729 impl_->flyLights.push_back(l);
730 }
731}
732
734 if (!impl_->built) init(gfx);
735 if (!impl_->gfx) return;
736
737 if (!impl_->paused) {
738 impl_->timeOfDay += dt * impl_->speed;
739 impl_->timeOfDay = std::fmod(impl_->timeOfDay, 24.f);
740 if (impl_->timeOfDay < 0.f) impl_->timeOfDay += 24.f;
741 }
742 const float hours = impl_->timeOfDay;
743
744 // Solar elevation: sine curve peaking at noon (hours=12).
745 const float frac = (hours - 6.f) / 12.f; // -1 at 6h, 0 at 12h, +1 at 18h
746 float elevDeg = kMaxElevationDeg * std::sin(kPi * frac);
747 float azimDeg = (hours / 24.f) * 360.f; // full rotation per day
748 if (impl_->manualSun.enabled) {
749 const float pitch = deg2rad(impl_->manualSun.pitchDegrees);
750 const float rotation = deg2rad(impl_->manualSun.rotationDegrees);
751 impl_->sunDir[0] = -std::cos(pitch) * std::sin(rotation);
752 impl_->sunDir[1] = std::sin(pitch);
753 impl_->sunDir[2] = -std::cos(pitch) * std::cos(rotation);
754 elevDeg = std::asin(std::clamp(impl_->sunDir[1], -1.f, 1.f)) * 180.f / kPi;
755 azimDeg = std::atan2(impl_->sunDir[0], impl_->sunDir[2]) * 180.f / kPi;
756 if (azimDeg < 0.f) azimDeg += 360.f;
757 } else {
758 sunDirection(elevDeg, azimDeg, impl_->sunDir[0], impl_->sunDir[1],
759 impl_->sunDir[2]);
760 }
761 impl_->elevDeg = elevDeg;
762 impl_->azimDeg = azimDeg;
763
764 // Sun energy: ramps up a few degrees above the horizon.
765 impl_->sunEnergy = impl_->manualSun.enabled
766 ? impl_->manualSun.intensity
767 : std::clamp((elevDeg + 6.f) / 14.f, 0.f, 1.f);
768 const float nightAmount = std::clamp((-elevDeg) / 12.f, 0.f, 1.f);
769
770 // Push the directional sun (replaces the legacy directional when no other
771 // dir Light3D is active; we keep moon as a Light3D instead so it can have
772 // different color/intensity than the sun slot).
773 const Vec3 directSun = impl_->manualSun.enabled
774 ? manualSunColor(impl_->manualSun)
775 : attenuatedSunColor(impl_->sunDir[1], impl_->turbidity,
776 impl_->mieStrength);
777 const float weatherSun = 1.f - 0.82f * impl_->weatherCloudiness;
778 const Vec3 flashLight{impl_->weatherFlash * 0.75f, impl_->weatherFlash * 0.90f,
779 impl_->weatherFlash * 1.20f};
780 const float globalLight = impl_->pcgAmbient.active
782 : 1.f;
783 gfx->setDirectionalLight(impl_->sunDir[0], impl_->sunDir[1], impl_->sunDir[2],
784 directSun.x * impl_->sunEnergy * weatherSun * globalLight + flashLight.x,
785 directSun.y * impl_->sunEnergy * weatherSun * globalLight + flashLight.y,
786 directSun.z * impl_->sunEnergy * weatherSun * globalLight + flashLight.z);
787
788 // Background matches the sky at the horizon for the clear color.
789 const float skyR = getSkyR(), skyG = getSkyG(), skyB = getSkyB();
790 gfx->setBackgroundColorRGBA(skyR, skyG, skyB, 1.f);
791
792 // --- procedural skybox (IBL env), regenerated per sun bucket ---
793 if (impl_->skyboxEnabled) {
794 const int bucket = int(elevDeg) + int(azimDeg / 4.f) * 1000 +
795 int(impl_->weatherCloudiness * 10.f) * 100000;
796 if (bucket != impl_->lastSkyBucket) {
797 impl_->lastSkyBucket = bucket;
798 std::vector<uint8_t> faces(
799 size_t(kSkyCubeSize) * size_t(kSkyCubeSize) * 4 * 6);
800 for (int f = 0; f < 6; ++f) {
801 std::vector<uint8_t> face(
802 size_t(kSkyCubeSize) * size_t(kSkyCubeSize) * 4);
803 const Vec3 tint = impl_->pcgSkybox.enabled
804 ? Vec3{impl_->pcgSkybox.tintRed, impl_->pcgSkybox.tintGreen,
805 impl_->pcgSkybox.tintBlue}
806 : Vec3{1.f, 1.f, 1.f};
807 fillSkyFace(face, int(kSkyCubeSize), f, impl_->sunDir, directSun,
808 impl_->sunEnergy, nightAmount, impl_->turbidity,
809 impl_->mieStrength,
810 impl_->pcgSkybox.enabled ? impl_->pcgSkybox.exposure : impl_->skyExposure,
811 impl_->weatherCloudiness,
812 impl_->pcgSkybox.enabled ? impl_->pcgSkybox.rotationDegrees : 0.f,
813 tint, cubeDir);
814 impl_->skyFaces[static_cast<size_t>(f)] =
815 gfx->newTexture(int(kSkyCubeSize), int(kSkyCubeSize), face.data());
816 const size_t center =
817 (size_t(kSkyCubeSize / 2) * size_t(kSkyCubeSize) + size_t(kSkyCubeSize / 2)) *
818 4u;
819 impl_->skyFaceCenterLuminance[static_cast<size_t>(f)] =
820 (float(face[center]) * 0.2126f + float(face[center + 1]) * 0.7152f +
821 float(face[center + 2]) * 0.0722f) /
822 255.f;
823 std::memcpy(faces.data() + size_t(f) * face.size(), face.data(), face.size());
824 }
825 // Replace the previous env cube; Graphics owns old textures.
826 impl_->skyCube = gfx->newCubemap(int(kSkyCubeSize), faces.data(),
828 gfx->setMesh3DEnv(impl_->skyCube, 0.5f + 0.5f * impl_->sunEnergy);
829 }
830 }
831
832 // --- night light systems (only meaningful below the horizon) ---
833 const bool night = elevDeg < 0.f;
834
835 // Moonlight: a directional light at the opposite-ish angle of the sun.
836 if (impl_->moonLight) {
837 const bool on = night && impl_->nightLight[0];
838 impl_->moonLight->setEnabled(on);
839 if (on) {
840 impl_->moonLight->setDirection(-impl_->sunDir[0], -impl_->sunDir[1],
841 -impl_->sunDir[2]);
842 }
843 }
844
845 // Fire.
846 if (impl_->fireLight) {
847 impl_->fireLight->setEnabled(night && impl_->nightLight[2]);
848 }
849
850 // Fireflies: gentle sinusoidal drift.
851 if (impl_->flyLights.size() == impl_->flies.size()) {
852 for (size_t i = 0; i < impl_->flies.size(); ++i) {
853 graphics::Light3D *l = impl_->flyLights[i];
854 const Impl::Fly &f = impl_->flies[i];
855 const bool on = night && impl_->nightLight[3];
856 l->setEnabled(on);
857 if (on) {
858 const float t = impl_->timeOfDay + f.seed;
859 l->setPosition(f.x + std::sin(t * 0.9f) * 0.6f,
860 f.y + std::sin(t * 1.3f + 1.7f) * 0.4f,
861 f.z + std::cos(t * 0.8f) * 0.6f);
862 }
863 }
864 }
865}
866
867// ---------------------------------------------------------------------------
868// Script binding
869// ---------------------------------------------------------------------------
870
871void DayNight::expose(ssq::Table &table) {
872 auto cls = table.addClass(name, DayNight::create, false);
873 expose(cls);
875}
876
877void DayNight::expose(ssq::Class &cls) {
878 cls.addFunc("getName", &DayNight::getName);
879 cls.addFunc("init", &DayNight::init);
880 cls.addFunc("update", &DayNight::update);
881 cls.addFunc("setTimeOfDay", &DayNight::setTimeOfDay);
882 cls.addFunc("getTimeOfDay", &DayNight::getTimeOfDay);
883 cls.addFunc("setSpeed", &DayNight::setSpeed);
884 cls.addFunc("getSpeed", &DayNight::getSpeed);
885 cls.addFunc("setPaused", &DayNight::setPaused);
886 cls.addFunc("isPaused", &DayNight::isPaused);
887 cls.addFunc("isNight", &DayNight::isNight);
888 cls.addFunc("getSunElevation", &DayNight::getSunElevation);
889 cls.addFunc("getSunAzimuth", &DayNight::getSunAzimuth);
890 cls.addFunc("getSunDirX", &DayNight::getSunDirX);
891 cls.addFunc("getSunDirY", &DayNight::getSunDirY);
892 cls.addFunc("getSunDirZ", &DayNight::getSunDirZ);
893 cls.addFunc("getSunIntensity", &DayNight::getSunIntensity);
894 cls.addFunc("getSunR", &DayNight::getSunR);
895 cls.addFunc("getSunG", &DayNight::getSunG);
896 cls.addFunc("getSunB", &DayNight::getSunB);
897 cls.addFunc("setTurbidity", &DayNight::setTurbidity);
898 cls.addFunc("getTurbidity", &DayNight::getTurbidity);
899 cls.addFunc("setSkyExposure", &DayNight::setSkyExposure);
900 cls.addFunc("getSkyExposure", &DayNight::getSkyExposure);
901 cls.addFunc("setMieStrength", &DayNight::setMieStrength);
902 cls.addFunc("getMieStrength", &DayNight::getMieStrength);
903 cls.addFunc("getSkyR", &DayNight::getSkyR);
904 cls.addFunc("getSkyG", &DayNight::getSkyG);
905 cls.addFunc("getSkyB", &DayNight::getSkyB);
906 cls.addFunc("getAmbientR", &DayNight::getAmbientR);
907 cls.addFunc("getAmbientG", &DayNight::getAmbientG);
908 cls.addFunc("getAmbientB", &DayNight::getAmbientB);
909 cls.addFunc("getAmbientBrightness", &DayNight::getAmbientBrightness);
910 cls.addFunc("applyAtmosphere", &DayNight::applyAtmosphere);
911 cls.addFunc("applyReflectionProbeSky", &DayNight::applyReflectionProbeSky);
912 cls.addFunc("setWeatherInfluence", &DayNight::setWeatherInfluence);
913 cls.addFunc("getWeatherCloudiness", &DayNight::getWeatherCloudiness);
914 cls.addFunc("getWeatherFlash", &DayNight::getWeatherFlash);
915 cls.addFunc("setSkyboxEnabled", &DayNight::setSkyboxEnabled);
916 cls.addFunc("isSkyboxEnabled", &DayNight::isSkyboxEnabled);
917 cls.addFunc("setNightLight", &DayNight::setNightLight);
918 cls.addFunc("isNightLight", &DayNight::isNightLight);
919 cls.addFunc("setFirePosition", &DayNight::setFirePosition);
920 cls.addFunc("addFirefly", &DayNight::addFirefly);
921 cls.addFunc("clearFireflies", &DayNight::clearFireflies);
922 cls.addFunc("getFireflyCount", &DayNight::getFireflyCount);
923}
924
926
927} // namespace eve::daynight
double value
Duration start
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
float rotationDegrees
int az
Definition CaveMesh.cpp:113
glm::vec4 p[6]
HSQOBJECT cls
Definition ECS.cpp:21
std::map< std::string, Var > values
tensor::Graph g
Definition GpuGraph.cpp:7
glm::vec4 tint
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
std::uint32_t ab
double r
float v
HexVec3 up
std::int32_t c
float temperature
int h
std::vector< Colorf > px
std::array< float, 4 > rotation
std::array< float, 3 > scale
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
size_t directions
Definition OnnxLstm.cpp:29
bool finite
float f
float d
float t
glm::mat4 view
RoadLaneDirection direction
float dz
float dy
float dx
Vec extinction
float size
Definition TreeMesh.cpp:156
float angle
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
virtual std::string getName() const =0
Returns the name.
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
DayNight module — a time-of-day cycle that drives the sun, sky and light.
Definition DayNight.h:94
float getSpeed() const
Returns the speed.
Definition DayNight.cpp:301
float getWeatherFlash() const
Returns the weather flash.
Definition DayNight.cpp:505
float getSunR() const
Atmosphere-attenuated direct sunlight red channel, including intensity.
Definition DayNight.cpp:313
bool isPaused() const
True when paused.
Definition DayNight.cpp:303
int getFireflyCount() const
Returns the firefly count.
Definition DayNight.cpp:697
float getSunG() const
Atmosphere-attenuated direct sunlight green channel, including intensity.
Definition DayNight.cpp:323
PcgManualSunState getPcgManualSun() const noexcept
Return the authoritative manual-sun settings by value.
Definition DayNight.cpp:364
void applyReflectionProbeSky(graphics::ReflectionProbeCapture *probe) const
Synchronize linear directional sky radiance and sky IBL to a reflection probe.
Definition DayNight.cpp:590
float getSunDirY() const
Returns the sun dir y.
Definition DayNight.cpp:310
float getSkyR() const
Returns the sky r.
Definition DayNight.cpp:461
void applyAtmosphere(graphics::Volumetric *fog) const
Synchronize sun direction and atmosphere-derived fog lighting.
Definition DayNight.cpp:534
float getSkyG() const
Returns the sky g.
Definition DayNight.cpp:471
float getSkyB() const
Returns the sky b.
Definition DayNight.cpp:481
bool isNight() const
True when the sun is below the horizon (night).
Definition DayNight.cpp:305
float getSunDirZ() const
Returns the sun dir z.
Definition DayNight.cpp:311
void setPaused(bool paused)
Sets the paused.
Definition DayNight.cpp:302
void setSkyboxEnabled(bool enabled)
Sets the skybox enabled.
Definition DayNight.cpp:644
void clearFireflies()
Clears fireflies.
Definition DayNight.cpp:693
Result< void > setPcgManualSun(const PcgManualSunState &state)
Atomically replace the persistent Pcg manual-sun state.
Definition DayNight.cpp:344
void init(graphics::Graphics *gfx)
Idempotent; builds lights / sky cubemap on first call.
Definition DayNight.cpp:703
static const int kNamedLightCount
Definition DayNight.h:251
float getSunIntensity() const
0..1 sun energy; ramps to 0 below the horizon.
Definition DayNight.cpp:312
float getTimeOfDay() const
Returns the time of day.
Definition DayNight.cpp:298
float getAmbientR() const
Returns the ambient r.
Definition DayNight.cpp:506
bool isNightLight(const std::string &name) const
True when night light.
Definition DayNight.cpp:662
PcgFogState getPcgFog() const noexcept
Return the authoritative Pcg fog state by value.
Definition DayNight.cpp:419
float getAmbientB() const
Returns the ambient b.
Definition DayNight.cpp:524
Result< void > setPcgSkybox(const PcgSkyboxState &state)
Atomically replace Pcg's persistent skybox override.
Definition DayNight.cpp:366
float getSunAzimuth() const
Solar azimuth in degrees, measured clockwise from +Z.
Definition DayNight.cpp:308
PcgSkyboxState getPcgSkybox() const noexcept
Return the authoritative Pcg skybox override by value.
Definition DayNight.cpp:382
float getSkyExposure() const
Returns the sky exposure.
Definition DayNight.cpp:453
Result< void > setPcgAmbientLight(const PcgAmbientLightState &state)
Atomically replace Pcg ambient colors, intensity and direct-light multiplier.
Definition DayNight.cpp:421
Result< void > setPcgFog(const PcgFogState &state)
Atomically replace all Pcg fog and density-volume controls.
Definition DayNight.cpp:384
bool isSkyboxEnabled() const
True when skybox enabled.
Definition DayNight.cpp:648
void setFirePosition(float x, float y, float z)
Position of the campfire point light (fire system).
Definition DayNight.cpp:669
void addFirefly(float x, float y, float z)
Add one firefly anchor (world space); up to kMaxFireflies.
Definition DayNight.cpp:678
float getMieStrength() const
Returns the mie strength.
Definition DayNight.cpp:458
float getSunB() const
Atmosphere-attenuated direct sunlight blue channel, including intensity.
Definition DayNight.cpp:333
void setTurbidity(float turbidity)
Set aerosol turbidity. 1.5 is very clear; 10 is hazy.
Definition DayNight.cpp:444
PcgAmbientLightState getPcgAmbientLight() const noexcept
Return the authoritative Pcg ambient-light state by value.
Definition DayNight.cpp:441
float getAmbientG() const
Returns the ambient g.
Definition DayNight.cpp:515
~DayNight() override
Day night.
Definition DayNight.cpp:287
void setWeatherInfluence(float cloudiness, float lightningFlash)
Apply cloud cover and lightning exposure to the unified sky/lighting model.
Definition DayNight.cpp:497
static const char *const kNamedLights[]
Definition DayNight.h:250
float getTurbidity() const
Returns the turbidity.
Definition DayNight.cpp:448
void setSkyExposure(float exposure)
Exposure used when mapping physical sky radiance to the RGBA8 sky cubemap.
Definition DayNight.cpp:449
void setSpeed(float hoursPerRealHour)
Sets the speed.
Definition DayNight.cpp:300
float getSunElevation() const
Solar elevation in degrees (max at local noon, ~70°).
Definition DayNight.cpp:307
void setNightLight(const std::string &name, bool enabled)
Enable a named light system: "moonlight"|"starlight"|"fire"|"fireflies".
Definition DayNight.cpp:654
void setMieStrength(float strength)
Relative Mie aerosol density; controls horizon haze and solar halo.
Definition DayNight.cpp:454
float getAmbientBrightness() const
Returns the ambient brightness.
Definition DayNight.cpp:491
float getSunDirX() const
World-space direction pointing AT the sun (normalized).
Definition DayNight.cpp:309
float getWeatherCloudiness() const
Returns the weather cloudiness.
Definition DayNight.cpp:504
void setTimeOfDay(float hours)
Sets the time of day.
Definition DayNight.cpp:293
void update(float dt, graphics::Graphics *gfx)
Advance the clock and push sun/sky/light state; before gfx.render3D().
Definition DayNight.cpp:733
virtual Texture * newCubemap(int faceSize, const uint8_t *rgbaFaces)=0
Create an RGBA8 cubemap from 6 faces packed as +X,-X,+Y,-Y,+Z,-Z (each faceSize×faceSize,...
virtual void setDirectionalLight(float dx, float dy, float dz, float r=1.f, float g=1.f, float b=1.f)
Sets the directional light.
Definition Graphics.cpp:248
virtual void setBackgroundColorRGBA(float r, float g, float b, float a=1.f)
Sets the background color rgba.
virtual void setMesh3DEnv(Texture *cube, float intensity)=0
Specular IBL environment for subsequent default mesh draws. cube must be from newCubemap (or nullptr ...
virtual Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false)=0
Creates a texture. @ownership Caller deletes unless documented otherwise.
Declarative 3D light. Collected by RenderSystem3D (max 8 per frame). type: "point" | "dir" | "spot" (...
Definition Light.h:191
void setEnabled(bool enabled)
Definition Light.cpp:164
void setDirection(float dx, float dy, float dz)
Definition Light.cpp:132
void setPosition(float x, float y, float z)
Definition Light.cpp:121
static Light3D * createLight(const std::string &type="point")
Definition Light.cpp:90
void setRadius(float radius)
Definition Light.cpp:151
void setColor(float r, float g, float b, float intensity=1.f)
Definition Light.cpp:143
Incremental six-face HDR scene capture for a runtime reflection probe.
void setEnvironmentLighting(Texture *environment, float intensity=1.f)
Set non-recursive global environment lighting for captured geometry.
void setSkyFaceTextureScale(int face, float scale)
Set the linear HDR radiance multiplier for one sky face texture.
void setSkyFaceTexture(int face, Texture *texture)
Set a directional per-pixel sky texture for one capture face.
void setSkyFaceColor(int face, float r, float g, float b)
Override one directional backup sky face in +X,-X,+Y,-Y,+Z,-Z order.
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
Volumetric light + fog.
Definition Volumetric.h:38
float getFloat(const std::string &name) const
Returns the float.
void setMode(const std::string &mode)
"screenspace" | "raymarch" | "fog" | "froxel" | "cloud".
void setCloudLightColor(float r, float g, float b)
Direct sunlight color used by cloud single scattering.
void setFogEnd(float endDistance)
Sets the fog end.
std::string getMode() const
Returns the mode.
Definition Volumetric.h:56
void setIntensity(float intensity)
Sets the intensity.
std::string getQuality() const
Returns the quality.
Definition Volumetric.h:51
void setLightDirection(float dx, float dy, float dz)
World-space direction toward the lit surface (ray march / phase).
void setTime(float seconds)
Sets the time.
void setQuality(const std::string &quality)
"low" | "medium" | "high" (unknown → medium).
void setDensity(float density)
Sets the density.
void setFogStart(float startDistance)
View-distance ramp where fog appears (world units along the ray).
void setFogColor(float r, float g, float b)
Sets the fog color.
void exposeSkyScriptBindings(ssq::Table &table, ssq::Class &daynightClass)
Register independent sky bindings on the VM/graphics owner thread.
double dot(const Vec2 &a, const Vec2 &b)
Dot.
Definition UrbanTypes.h:38
bool enabled
PcgManualSunState manualSun
Definition DayNight.cpp:250
std::vector< graphics::Light3D * > flyLights
Definition DayNight.cpp:280
std::array< float, 6 > skyFaceCenterLuminance
Definition DayNight.cpp:266
graphics::Volumetric * pcgFogTarget
Definition DayNight.cpp:254
std::vector< Fly > flies
Definition DayNight.cpp:277
graphics::Light3D * moonLight
Definition DayNight.cpp:278
graphics::Light3D * fireLight
Definition DayNight.cpp:279
graphics::Graphics * gfx
Definition DayNight.cpp:232
graphics::Texture * skyCube
Definition DayNight.cpp:264
std::array< graphics::Texture *, 6 > skyFaces
Definition DayNight.cpp:265
PcgAmbientLightState pcgAmbient
Definition DayNight.cpp:253
Pcg ambient-gradient colors and global direct-light multiplier.
Definition DayNight.h:61
Joint Pcg Photo Mode fog and density-volume state.
Definition DayNight.h:43
Persistent manual sun settings projected from Pcg Photo Mode.
Definition DayNight.h:21
Persistent Pcg skybox override settings.
Definition DayNight.h:33
static TextureCreateInfo withMipmaps(bool aniso=true, float maxAniso=16.f)
With mipmaps.
std::mutex mu
Definition Graphics.cpp:181
uint32_t bucket
glm::vec4 color