15#include <simplesquirrel/simplesquirrel.hpp>
17#include "weather/shaders/weather_vert_spv.inc"
18#include "weather/shaders/weather_frag_spv.inc"
19#include "weather/shaders/bolt_vert_spv.inc"
20#include "weather/shaders/bolt_frag_spv.inc"
28 uint32_t
s = 0x9e3779b9u;
30 s =
s * 1664525u + 1013904223u;
33 float unit() {
return float(next() % 10000u) / 9999.f; }
34 float range(
float a,
float b) {
return a + (
b -
a) * unit(); }
37constexpr int kRainCount = 320;
38constexpr int kSnowCount = 260;
39constexpr int kBoltCount = 4;
41constexpr float kBoxXZ = 26.f;
42constexpr float kBoxY = 20.f;
53void genRainTexture(std::vector<uint8_t> &rgba) {
54 const int w = 8,
h = 32;
55 rgba.resize(
size_t(
w) *
h * 4);
56 for (
int y = 0;
y <
h; ++
y) {
57 const float v = (float(
y) + 0.5f) /
float(
h);
58 const float endFade = 1.f - 2.f * std::fabs(
v - 0.5f);
59 for (
int x = 0;
x <
w; ++
x) {
60 const float u = (float(
x) + 0.5f) /
float(
w);
61 const float edge = 1.f - 2.f * std::fabs(
u - 0.5f);
62 const float alpha = std::pow(edge, 2.f) * std::pow(endFade, 2.5f);
63 const int i = (
y *
w +
x) * 4;
67 rgba[i + 3] = uint8_t(std::min(255.f, alpha * 255.f));
73void genSnowTexture(std::vector<uint8_t> &rgba) {
74 const int w = 16,
h = 16;
75 rgba.resize(
size_t(
w) *
h * 4);
76 const float cx = (float(
w) - 1.f) * 0.5f;
77 const float cy = (float(
h) - 1.f) * 0.5f;
78 for (
int y = 0;
y <
h; ++
y) {
79 for (
int x = 0;
x <
w; ++
x) {
80 const float dx = (float(
x) -
cx) /
cx;
81 const float dy = (float(
y) -
cy) /
cy;
82 const float r = std::sqrt(dx * dx + dy * dy);
83 float alpha = 1.f - r;
84 if (alpha < 0.f) alpha = 0.f;
85 alpha = std::pow(alpha, 2.2f);
86 const int i = (
y *
w +
x) * 4;
90 rgba[i + 3] = uint8_t(std::min(255.f, alpha * 255.f));
101graphics::Mesh *buildFieldMesh(graphics::Graphics *gfx, Lcg &rng,
int count,
float len,
105 const int vertCount = count * 4;
106 const int idxCount = count * 6;
107 std::vector<float>
pos(vertCount * 3);
108 std::vector<float> nrm(vertCount * 3, 0.f);
109 std::vector<float> uv(vertCount * 2);
110 std::vector<uint32_t>
idx(idxCount);
112 for (
int i = 0; i < count; ++i) {
113 const float ax = rng.range(-kBoxXZ, kBoxXZ);
114 const float ay = rng.range(0.f, kBoxY);
115 const float az = rng.range(-kBoxXZ, kBoxXZ);
116 const int v0 = i * 4;
117 const int b = v0 * 3, t = v0 * 2;
125 nrm[
b + 1] = nrm[
b + 4] = nrm[
b + 7] = nrm[
b + 10] = 1.f;
128 uv[t + 0] = -0.5f; uv[t + 1] = 0.f;
129 uv[t + 2] = -0.5f; uv[t + 3] = 1.f;
130 uv[t + 4] = 0.5f; uv[t + 5] = 1.f;
131 uv[t + 6] = 0.5f; uv[t + 7] = 0.f;
133 const uint32_t base = uint32_t(v0);
134 idx[i * 6 + 0] = base + 0;
135 idx[i * 6 + 1] = base + 1;
136 idx[i * 6 + 2] = base + 2;
137 idx[i * 6 + 3] = base + 0;
138 idx[i * 6 + 4] = base + 2;
139 idx[i * 6 + 5] = base + 3;
142 return gfx->newMeshFromArrays(
pos.data(), nrm.data(), uv.data(), vertCount,
idx.data(),
147 float x = 0,
y = 0,
z = 0;
151graphics::Mesh *buildBoltMesh(graphics::Graphics *gfx, Lcg &rng, Pt top, Pt ground) {
157 float stepY = (top.y - ground.y) /
float(segs);
158 for (
int i = 1; i < segs; ++i) {
160 float f = float(i) / float(segs);
161 float jitter = (1.f -
f) * 0.9f;
163 p.x = ground.x + (top.x - ground.x) * (1.f -
f) + rng.range(-jitter, jitter) * 1.2f;
165 p.z = ground.z + (top.z - ground.z) * (1.f -
f) + rng.range(-jitter, jitter) * 1.2f;
168 pts.push_back(ground);
171 std::vector<Pt> branch;
172 int bStart = int(
float(segs) * 0.35f);
173 branch.push_back(pts[bStart]);
175 for (
int i = 1; i <= 8; ++i) {
176 float f = float(i) / 8.f;
177 bp.x += rng.range(-0.5f, 0.5f);
178 bp.y -= (pts[bStart].y - ground.y) * 0.12f;
179 bp.z += rng.range(-0.5f, 0.5f);
180 branch.push_back(bp);
184 auto emitStrip = [&](
const std::vector<Pt> &path,
float wTop,
float wBottom) {
185 const int n = int(path.size()) - 1;
186 for (
int i = 0; i <
n; ++i) {
187 Pt
a = path[i],
b = path[i + 1];
189 float dx =
b.x -
a.x, dz =
b.z -
a.z;
190 float len = std::sqrt(dx * dx + dz * dz) + 1e-4f;
191 float sx = -dz / len, sz = dx / len;
192 float w = wBottom + (wTop - wBottom) * (
float(i) / float(
n));
193 Pt p0 = {
a.x + sx *
w,
a.y,
a.z + sz *
w};
194 Pt p1 = {
a.x - sx *
w,
a.y,
a.z - sz *
w};
195 Pt p2 = {
b.x - sx *
w,
b.y,
b.z - sz *
w};
196 Pt p3 = {
b.x + sx *
w,
b.y,
b.z + sz *
w};
204 const float wMain = 0.07f;
205 emitStrip(pts, wMain, wMain * 0.15f);
206 emitStrip(branch, wMain * 0.8f, wMain * 0.15f);
208 const int vertCount = int(all.size());
209 const int idxCount = vertCount / 4 * 6;
210 std::vector<float>
pos(vertCount * 3);
211 std::vector<float> nrm(vertCount * 3, 0.f);
212 std::vector<float> uv(vertCount * 2, 0.f);
213 std::vector<uint32_t>
idx(idxCount);
215 for (
int i = 0; i < vertCount; ++i) {
216 pos[i * 3 + 0] = all[i].x;
217 pos[i * 3 + 1] = all[i].y;
218 pos[i * 3 + 2] = all[i].z;
219 nrm[i * 3 + 1] = 1.f;
220 uv[i * 2 + 1] = float(i / 4) * 0.25f;
222 for (
int q = 0; q < vertCount / 4; ++q) {
223 const uint32_t base = uint32_t(q * 4);
224 idx[q * 6 + 0] = base + 0;
225 idx[q * 6 + 1] = base + 1;
226 idx[q * 6 + 2] = base + 2;
227 idx[q * 6 + 3] = base + 0;
228 idx[q * 6 + 4] = base + 2;
229 idx[q * 6 + 5] = base + 3;
232 return gfx->newMeshFromArrays(
pos.data(), nrm.data(), uv.data(), vertCount,
idx.data(),
265 std::vector<graphics::Renderable3D *>
bolts;
277 "storm",
"snow",
"fog"};
290 float speed,
float length,
float width,
float intensity,
291 float fogR,
float fogG,
float fogB,
float fogDensity,
float flash) {
298 mat->
setFloat(
"uIntensity", intensity);
302 mat->
setFloat(
"uFogDensity", fogDensity);
308 shader->declareFloat(
"uTime");
309 shader->declareFloat(
"uWindX");
310 shader->declareFloat(
"uWindZ");
311 shader->declareFloat(
"uSpeed");
312 shader->declareFloat(
"uLength");
313 shader->declareFloat(
"uWidth");
314 shader->declareFloat(
"uIntensity");
315 shader->declareFloat(
"uFogR");
316 shader->declareFloat(
"uFogG");
317 shader->declareFloat(
"uFogB");
318 shader->declareFloat(
"uFogDensity");
319 shader->declareFloat(
"uFlash");
324 if (impl_->
built)
return;
330 std::vector<uint8_t> rainRgba, snowRgba;
331 genRainTexture(rainRgba);
332 genSnowTexture(snowRgba);
337 std::vector<uint32_t> wv(weather_vert_spv, weather_vert_spv + weather_vert_spv_count);
338 std::vector<uint32_t> wf(weather_frag_spv, weather_frag_spv + weather_frag_spv_count);
339 std::vector<uint32_t> bv(bolt_vert_spv, bolt_vert_spv + bolt_vert_spv_count);
340 std::vector<uint32_t> bf(bolt_frag_spv, bolt_frag_spv + bolt_frag_spv_count);
343 declareWeatherParams(weatherVert);
344 declareWeatherParams(boltShader);
347 graphics::Mesh *rainMesh = buildFieldMesh(gfx, impl_->
rng, kRainCount, 1.4f, 0.07f);
348 impl_->
rain = graphics::Renderable3D::create();
362 graphics::Mesh *snowMesh = buildFieldMesh(gfx, impl_->
rng, kSnowCount, 0.22f, 0.10f);
363 impl_->
snow = graphics::Renderable3D::create();
377 for (
int i = 0; i < kBoltCount; ++i) {
378 Pt top{impl_->
rng.range(-4.f, 4.f), kBoxY, impl_->
rng.range(-4.f, 4.f)};
379 Pt ground{impl_->
rng.range(-3.f, 3.f), 0.f, impl_->
rng.range(-3.f, 3.f)};
388 mm->
setTint(1.0f, 1.0f, 1.0f, 1.0f);
391 b->setVisible(
false);
392 impl_->
bolts.push_back(
b);
396 pushWeatherParams(impl_->
rainMat, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
397 pushWeatherParams(impl_->
snowMat, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
430 if (!impl_->
built || impl_->
bolts.empty())
return;
471 if (!impl_->
gfx)
return;
480 const float rad = impl_->
windDirDeg * 0.0174532925f;
481 const float windX = -std::sin(rad) * impl_->
windSpeed;
482 const float windZ = std::cos(rad) * impl_->
windSpeed;
484 const float time = impl_->
time;
485 const float fogR = impl_->
fogR, fogG = impl_->
fogG, fogB = impl_->
fogB;
490 const bool rainOn = impl_->
preset >= 1 && impl_->
preset <= 3;
493 const float speed = 26.f;
494 pushWeatherParams(impl_->
rainMat, time, windX, windZ, speed, 1.4f, 0.07f, intensity,
495 fogR, fogG, fogB, fogD, 0.f);
499 const bool snowOn = impl_->
preset == 4;
502 pushWeatherParams(impl_->
snowMat, time, windX, windZ, 2.2f, 0.22f, 0.10f, intensity,
503 fogR, fogG, fogB, fogD, 0.f);
507 const bool storm = impl_->
preset == 3;
521 }
else if (impl_->
boltLife < 0.20f) {
523 f = 0.25f + 0.75f * std::fabs(std::sin(impl_->
boltLife * 120.f));
525 f = std::max(0.f, 1.f - (impl_->
boltLife - 0.20f) / 0.15f);
527 impl_->
flash = std::clamp(
f, 0.f, 1.f);
530 impl_->
bolts[bi]->setVisible(impl_->
flash > 0.02f);
531 pushWeatherParams(impl_->
boltMats[bi], time, windX, windZ, 0.f, 0.f, 0.f, 1.f, fogR, fogG,
532 fogB, fogD, impl_->
flash);
535 impl_->
bolts[bi]->setVisible(
false);
540 impl_->
flash *= std::max(0.f, 1.f - dt * 8.f);
547 impl_->
skyB * dark, 1.f);
559void Weather::expose(ssq::Table &table) {
560 auto cls = table.addClass(
name, Weather::create,
false);
564void Weather::expose(ssq::Class &
cls) {
#define Module_IMPL(ModuleName, newExpr)
virtual std::string getName() const =0
virtual void setDirectionalLight(float dx, float dy, float dz, float r=1.f, float g=1.f, float b=1.f)
virtual Shader * newMeshShaderFromSpv(const std::vector< uint32_t > &vertSpv, const std::vector< uint32_t > &fragSpv)=0
Create a Mesh3D custom shader (MeshVertex + Frame UBO + albedo). Empty vert → default mesh3d....
virtual void setBackgroundColorRGBA(float r, float g, float b, float a=1.f)
virtual Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false)=0
Material * newMaterial()
Create a Material asset (shading model + textures + PBR knobs). Caller owns Material*; not tracked by...
Packages shading method + surface parameters into one attachable asset.
void setCastShadow(bool cast)
void setReceiveShadow(bool receive)
void setReceiveLight(bool receive)
void setShadingModel(const std::string &model)
"pbr" | "unlit" | "hair" | "custom" (unknown → pbr).
void setTint(float r, float g, float b, float a=1.f)
void setFloat(const std::string &name, float value)
void setShader(Shader *shader)
Optional Mesh3D / hair Shader. nullptr → built-in path for the shading model.
GPU mesh handle (+ optional CPU morph targets).
void setTexture(Texture *texture)
void setMaterial(Material *material)
Attach a Material that packages shading method + surface params.
void setVisible(bool visible)
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Weather module — real-time precipitation / lightning / wind system.
static const char *const kPresetNames[]
void setSunIntensity(float v)
float getFogColorB() const
static const int kPresetCount
float getFogDensity() const
void setWindSpeed(float v)
void init(graphics::Graphics *gfx)
Idempotent; builds meshes/shaders on first call.
void setLightningEnabled(bool on)
void setIntensity(float v)
float getFogColorG() const
void strike()
Force a bolt strike this frame (useful for manual testing).
void setSkyColor(float r, float g, float b)
float getWindDirection() const
void setFogDensity(float v)
std::string getPreset() const
float getFogColorR() const
float getSunIntensity() const
float getSkyColorG() const
float getSkyColorB() const
void setPreset(const std::string &name)
bool isLightningEnabled() const
float getWindSpeed() const
float getFlash() const
How bright the current flash is (0..1), sampled by the scene for a key light.
float getAmbientBrightness() const
Ambient multiplier that the example should feed into camera.setAmbient().
void update(float dt, graphics::Graphics *gfx)
Advance sim + push per-frame uniforms; must run before gfx.render3D().
void setFogColor(float r, float g, float b)
float getIntensity() const
void setWindDirection(float degrees)
Wind direction in degrees; 0 = toward +Z, 90 = toward -X.
float getSkyColorR() const
graphics::Renderable3D * rain
graphics::Renderable3D * snow
graphics::Material * rainMat
std::vector< graphics::Renderable3D * > bolts
std::vector< graphics::Material * > boltMats
graphics::Material * snowMat