13#include <simplesquirrel/simplesquirrel.hpp>
20constexpr float kPi = 3.14159265f;
21constexpr float kMaxElevationDeg = 70.f;
22constexpr float kSkyCubeSize = 64;
23constexpr int kMaxFireflies = 8;
26const char *kNames[] = {
"moonlight",
"starlight",
"fire",
"fireflies"};
28inline float deg2rad(
float d) {
return d * kPi / 180.f; }
32inline void sunDirection(
float elevDeg,
float azimDeg,
float &dx,
float &dy,
float &dz) {
33 const float el = deg2rad(elevDeg);
34 const float az = deg2rad(azimDeg);
35 const float he = std::cos(el);
36 dx = he * std::sin(az);
38 dz = he * std::cos(az);
42inline uint32_t hash13(uint32_t
x) {
50inline float hashUnit(uint32_t
x) {
return float(hash13(
x) % 10000u) / 9999.f; }
54void fillSkyFace(std::vector<uint8_t> &
px,
int size,
int face,
55 const float sunDir[3],
float sunEnergy,
float nightAmount,
56 void (*dirAt)(
int face,
int size,
int x,
int y,
float out[3])) {
58 for (
int y = 0;
y <
n; ++
y) {
59 for (
int x = 0;
x <
n; ++
x) {
61 dirAt(face,
n,
x,
y,
d);
62 const float len = std::sqrt(
d[0] *
d[0] +
d[1] *
d[1] +
d[2] *
d[2]);
63 if (len < 1e-6f) {
d[0] = 0.f;
d[1] = 1.f;
d[2] = 0.f; }
64 else {
d[0] /= len;
d[1] /= len;
d[2] /= len; }
66 const float up =
d[1];
69 if (nightAmount > 0.5f) {
71 const float t = 0.5f + 0.5f * up;
72 r = 0.015f + 0.03f * t;
73 g = 0.025f + 0.045f * t;
74 b = 0.07f + 0.10f * t;
76 const float t = 0.5f + 0.5f * up;
77 r = 0.35f + 0.30f * t;
78 g = 0.55f + 0.25f * t;
79 b = 0.75f + 0.20f * t;
82 if (nightAmount > 0.f && nightAmount < 1.f) {
83 float nr = 0.015f + 0.03f * (0.5f + 0.5f * up);
84 float ng = 0.025f + 0.045f * (0.5f + 0.5f * up);
85 float nb = 0.07f + 0.10f * (0.5f + 0.5f * up);
86 r = r * (1.f - nightAmount) + nr * nightAmount;
87 g = g * (1.f - nightAmount) + ng * nightAmount;
88 b =
b * (1.f - nightAmount) + nb * nightAmount;
92 const float dot =
d[0] * sunDir[0] +
d[1] * sunDir[1] +
d[2] * sunDir[2];
93 const float disc = std::pow(std::max(0.f, dot), 400.f) * sunEnergy;
98 const float glow = std::pow(std::max(0.f, dot), 8.f) * sunEnergy * 0.25f;
99 r += glow * 1.0f; g += glow * 0.9f;
b += glow * 0.7f;
103 if (nightAmount > 0.5f && up > 0.05f && dot < 0.98f) {
104 uint32_t
h = hash13(uint32_t((
x * 73856093) ^ (
y * 19349663) ^ (face * 83492791)));
106 const float tw = 0.6f + 0.4f * hashUnit(
h + 1u);
107 star = nightAmount * tw;
110 r += star * 0.9f; g += star * 0.95f;
b += star * 1.0f;
112 const int i = (
y *
n +
x) * 4;
113 px[i + 0] = uint8_t(std::min(255.f, r * 255.f));
114 px[i + 1] = uint8_t(std::min(255.f, g * 255.f));
115 px[i + 2] = uint8_t(std::min(255.f,
b * 255.f));
122void cubeDir(
int face,
int size,
int x,
int y,
float out[3]) {
123 const float u = (2.f * (float(
x) + 0.5f) /
float(size)) - 1.f;
124 const float v = (2.f * (float(
y) + 0.5f) /
float(size)) - 1.f;
126 case 0: out[0] = 1.f; out[1] = -
v; out[2] = -
u;
break;
127 case 1: out[0] = -1.f; out[1] = -
v; out[2] =
u;
break;
128 case 2: out[0] =
u; out[1] = 1.f; out[2] =
v;
break;
129 case 3: out[0] =
u; out[1] = -1.f; out[2] = -
v;
break;
130 case 4: out[0] =
u; out[1] = -
v; out[2] = 1.f;
break;
131 default: out[0] = -
u; out[1] = -
v; out[2] = -1.f;
break;
182 float h = std::fmod(hours, 24.f);
183 if (
h < 0.f)
h += 24.f;
207 const float night = impl_->
nightLight[1] ? 1.0f : 0.6f;
208 return 0.05f * night + impl_->
sunEnergy * 0.5f;
216 return ab * (0.95f + 0.05f * impl_->
sunEnergy);
220 return ab * (0.95f + 0.15f * impl_->
sunEnergy);
262 if (
int(impl_->
flies.size()) >= kMaxFireflies)
return;
265 f.seed = float(impl_->
flies.size()) * 1.7f;
266 impl_->
flies.push_back(
f);
269 l->
setColor(0.6f, 0.9f, 0.3f, 0.9f);
277 impl_->
flies.clear();
287 if (impl_->
built)
return;
306 for (
const auto &
f : impl_->
flies) {
308 l->
setColor(0.6f, 0.9f, 0.3f, 0.9f);
318 if (!impl_->
gfx)
return;
328 const float frac = (hours - 6.f) / 12.f;
329 const float elevDeg = kMaxElevationDeg * std::sin(kPi * frac);
330 const float azimDeg = (hours / 24.f) * 360.f;
335 impl_->
sunEnergy = std::clamp((elevDeg + 6.f) / 14.f, 0.f, 1.f);
336 const float nightAmount = std::clamp((-elevDeg) / 12.f, 0.f, 1.f);
353 const int bucket = int(elevDeg) + int(azimDeg / 4.f) * 1000;
356 std::vector<uint8_t> faces(
size_t(kSkyCubeSize) * kSkyCubeSize * 4 * 6);
357 for (
int f = 0;
f < 6; ++
f) {
358 std::vector<uint8_t> face(
size_t(kSkyCubeSize) * kSkyCubeSize * 4);
359 fillSkyFace(face,
int(kSkyCubeSize),
f, impl_->
sunDir,
361 std::memcpy(faces.data() +
size_t(
f) * face.size(), face.data(), face.size());
370 const bool night = elevDeg < 0.f;
374 const bool on = night && impl_->
nightLight[0];
389 for (
size_t i = 0; i < impl_->
flies.size(); ++i) {
392 const bool on = night && impl_->
nightLight[3];
397 f.y + std::sin(t * 1.3f + 1.7f) * 0.4f,
398 f.z + std::cos(t * 0.8f) * 0.6f);
408void DayNight::expose(ssq::Table &table) {
409 auto cls = table.addClass(
name, DayNight::create,
false);
413void DayNight::expose(ssq::Class &
cls) {
#define Module_IMPL(ModuleName, newExpr)
virtual std::string getName() const =0
DayNight module — a time-of-day cycle that drives the sun, sky and light.
int getFireflyCount() const
bool isNight() const
True when the sun is below the horizon (night).
void setPaused(bool paused)
void setSkyboxEnabled(bool enabled)
void init(graphics::Graphics *gfx)
Idempotent; builds lights / sky cubemap on first call.
static const int kNamedLightCount
float getSunIntensity() const
0..1 sun energy; ramps to 0 below the horizon.
float getTimeOfDay() const
float getAmbientR() const
bool isNightLight(const std::string &name) const
float getAmbientB() const
float getSunAzimuth() const
Solar azimuth in degrees, measured clockwise from +Z.
bool isSkyboxEnabled() const
void setFirePosition(float x, float y, float z)
Position of the campfire point light (fire system).
void addFirefly(float x, float y, float z)
Add one firefly anchor (world space); up to kMaxFireflies.
float getAmbientG() const
static const char *const kNamedLights[]
void setSpeed(float hoursPerRealHour)
float getSunElevation() const
Solar elevation in degrees (max at local noon, ~70°).
void setNightLight(const std::string &name, bool enabled)
Enable a named light system: "moonlight"|"starlight"|"fire"|"fireflies".
float getAmbientBrightness() const
float getSunDirX() const
World-space direction pointing AT the sun (normalized).
void setTimeOfDay(float hours)
void update(float dt, graphics::Graphics *gfx)
Advance the clock and push sun/sky/light state; before gfx.render3D().
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)
virtual void setBackgroundColorRGBA(float r, float g, float b, float a=1.f)
virtual void setMesh3DEnv(Texture *cube, float intensity)=0
Specular IBL environment for subsequent default mesh draws. cube must be from newCubemap (or nullptr ...
Declarative 3D light. Collected by RenderSystem3D (max 8 per frame). type: "point" | "dir" (≤15 chars...
void setEnabled(bool enabled)
void setDirection(float dx, float dy, float dz)
void setPosition(float x, float y, float z)
static Light3D * createLight(const std::string &type="point")
void setRadius(float radius)
void setColor(float r, float g, float b, float intensity=1.f)
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
std::vector< graphics::Light3D * > flyLights
graphics::Light3D * moonLight
graphics::Light3D * fireLight
graphics::Texture * skyCube