载入中...
搜索中...
未找到
SnowField.cpp
浏览该文件的文档.
1#include "weather/SnowField.h"
2
3#include <algorithm>
4#include <cmath>
5
6namespace eve::weather {
7
8namespace {
9
11float smoothstep(float edge0, float edge1, float x) {
12 const float t = std::clamp((x - edge0) / (edge1 - edge0), 0.f, 1.f);
13 return t * t * (3.f - 2.f * t);
14}
15
17float cellNoise(int x, int y) {
18 const float v = std::sin(float(x) * 12.9898f + float(y) * 78.233f) * 43758.5453f;
19 return v - std::floor(v);
20}
21
22} // namespace
23
27
29 width_ = std::max(width, 0);
30 height_ = std::max(height, 0);
31 data_.assign(size_t(width_) * size_t(height_), 0.f);
32 dirty_ = true;
33}
34
35bool SnowField::inBounds(int x, int y) const {
36 return x >= 0 && y >= 0 && x < width_ && y < height_;
37}
38
39void SnowField::fill(float v) {
40 const float c = clamp01(v);
41 std::fill(data_.begin(), data_.end(), c);
42 dirty_ = true;
43}
44
45void SnowField::setHeight(int x, int y, float h) {
46 if (!inBounds(x, y)) return;
47 data_[size_t(y) * width_ + x] = clamp01(h);
48 dirty_ = true;
49}
50
51float SnowField::height(int x, int y) const {
52 if (!inBounds(x, y)) return 0.f;
53 return data_[size_t(y) * width_ + x];
54}
55
56void SnowField::stampFootprint(float cx, float cz, float dirX, float dirZ, float radius,
57 float depth) {
58 if (width_ <= 0 || height_ <= 0) return;
59
60 float len = std::sqrt(dirX * dirX + dirZ * dirZ);
61 if (len < 1e-5f) {
62 dirX = 1.f;
63 dirZ = 0.f;
64 } else {
65 dirX /= len;
66 dirZ /= len;
67 }
68
69 const float r = std::max(radius, 0.25f);
70 const float d = std::clamp(depth, 0.f, 1.f);
71 const float across = r * 0.55f; // footprints are longer than they are wide
72 const float margin = r * 1.25f;
73 const int x0 = int(std::floor(cx - margin));
74 const int x1 = int(std::floor(cx + margin));
75 const int z0 = int(std::floor(cz - margin));
76 const int z1 = int(std::floor(cz + margin));
77
78 for (int z = z0; z <= z1; ++z) {
79 for (int x = x0; x <= x1; ++x) {
80 if (!inBounds(x, z)) continue;
81 const float dx = float(x) - cx;
82 const float dz = float(z) - cz;
83 const float along = dx * dirX + dz * dirZ;
84 const float perp = -dx * dirZ + dz * dirX;
85 const float n = std::sqrt((along / r) * (along / r) +
86 (perp / across) * (perp / across));
87 if (n > 1.2f) continue;
88
89 // Flat-bottomed step profile: full depth out to ~72% of the
90 // footprint, then steep walls rising to the surface. Reads much
91 // clearer than a soft bowl once the mesh is smooth-shaded.
92 const float depression = d * (1.f - smoothstep(0.72f, 1.f, n));
93 // Kicked-snow mounds ringing the footprint: a per-cell hash makes
94 // the raised rim lumpy (snow thrown to the side while walking),
95 // instead of a perfect smooth ring.
96 const float ring = smoothstep(0.72f, 1.f, n) *
97 (1.f - smoothstep(1.f, 1.18f, n));
98 const float rnd = 0.35f + 0.65f * cellNoise(x, z);
99 const float rim = d * 0.5f * ring * rnd;
100 float &cell = data_[size_t(z) * width_ + x];
101 cell = clamp01(cell - depression + rim);
102 }
103 }
104 dirty_ = true;
105}
106
107void SnowField::stampImpact(float cx, float cz, float radius, float depth) {
108 if (width_ <= 0 || height_ <= 0) return;
109
110 const float r = std::max(radius, 0.25f);
111 const float d = std::clamp(depth, 0.f, 1.f);
112 const float margin = r * 1.45f;
113 const int x0 = int(std::floor(cx - margin));
114 const int x1 = int(std::floor(cx + margin));
115 const int z0 = int(std::floor(cz - margin));
116 const int z1 = int(std::floor(cz + margin));
117
118 for (int z = z0; z <= z1; ++z) {
119 for (int x = x0; x <= x1; ++x) {
120 if (!inBounds(x, z)) continue;
121 const float dx = float(x) - cx;
122 const float dz = float(z) - cz;
123 const float dist = std::sqrt(dx * dx + dz * dz);
124 const float n = dist / r;
125 if (n > 1.45f) continue;
126
127 const float bowl = d * (1.f - n) * (1.f - n);
128 // Random ejecta ring: snow thrown out of the crater piles up in
129 // lumpy mounds around the rim (hash-randomized height per cell).
130 const float ring = smoothstep(0.85f, 1.f, n) *
131 (1.f - smoothstep(1.f, 1.4f, n));
132 const float rnd = 0.3f + 0.7f * cellNoise(x, z);
133 const float rim = d * 0.75f * ring * rnd;
134 float &cell = data_[size_t(z) * width_ + x];
135 cell = clamp01(cell - bowl + rim);
136 }
137 }
138 dirty_ = true;
139}
140
141void SnowField::addSnowfall(float amount) {
142 if (amount <= 0.f || data_.empty()) return;
143 for (float &v : data_) {
144 v = std::min(1.f, v + amount);
145 }
146 dirty_ = true;
147}
148
149std::vector<uint8_t> SnowField::toHeightRGBA() const {
150 std::vector<uint8_t> rgba(size_t(width_) * size_t(height_) * 4, 0);
151 for (int z = 0; z < height_; ++z) {
152 for (int x = 0; x < width_; ++x) {
153 const float s = data_[size_t(z) * width_ + x];
154 const size_t i = (size_t(z) * width_ + x) * 4;
155 rgba[i + 0] = uint8_t(std::clamp(s * 255.f, 0.f, 255.f) + 0.5f);
156 rgba[i + 1] = 0;
157 rgba[i + 2] = 0;
158 rgba[i + 3] = 255;
159 }
160 }
161 return rgba;
162}
163
164std::vector<uint8_t> SnowField::toAlbedoRGBA() const {
165 constexpr float kSnow[3] = {0.93f, 0.96f, 1.00f}; // cool white
166 constexpr float kGround[3] = {0.36f, 0.30f, 0.24f}; // dark soil
167 std::vector<uint8_t> rgba(size_t(width_) * size_t(height_) * 4, 0);
168 for (int z = 0; z < height_; ++z) {
169 for (int x = 0; x < width_; ++x) {
170 const float s = data_[size_t(z) * width_ + x];
171 // Compression changes the snow's shade, not its material. Soil
172 // appears only when almost all of the snow layer is removed.
173 const float cover = smoothstep(0.015f, 0.14f, s);
174 const float powder = smoothstep(0.18f, 0.85f, s);
175 constexpr float kPacked[3] = {0.76f, 0.84f, 0.94f};
176 const float n = (cellNoise(x, z) - 0.5f) * 0.012f;
177 const size_t i = (size_t(z) * width_ + x) * 4;
178 for (int c = 0; c < 3; ++c) {
179 const float snow = kPacked[c] + (kSnow[c] - kPacked[c]) * powder;
180 const float v = kGround[c] + (snow - kGround[c]) * cover + n;
181 rgba[i + c] = uint8_t(std::clamp(v * 255.f, 0.f, 255.f) + 0.5f);
182 }
183 rgba[i + 3] = 255;
184 }
185 }
186 return rgba;
187}
188
189std::vector<uint8_t> SnowField::toNormalRGBA() const {
190 constexpr float kStrength = 9.f;
191 std::vector<uint8_t> rgba(size_t(width_) * size_t(height_) * 4, 0);
192 if (width_ <= 0 || height_ <= 0) return rgba;
193 auto sample = [&](int x, int z) -> float {
194 x = std::clamp(x, 0, width_ - 1);
195 z = std::clamp(z, 0, height_ - 1);
196 return data_[size_t(z) * width_ + x];
197 };
198 for (int z = 0; z < height_; ++z) {
199 for (int x = 0; x < width_; ++x) {
200 const float dhdu = (sample(x + 1, z) - sample(x - 1, z)) * 0.5f * kStrength;
201 const float dhdv = (sample(x, z + 1) - sample(x, z - 1)) * 0.5f * kStrength;
202 const float len = std::sqrt(dhdu * dhdu + dhdv * dhdv + 1.f);
203 const float nx = -dhdu / len;
204 const float ny = -dhdv / len;
205 const float nz = 1.f / len;
206 const size_t i = (size_t(z) * width_ + x) * 4;
207 rgba[i + 0] = uint8_t(std::clamp((nx * 0.5f + 0.5f) * 255.f, 0.f, 255.f) + 0.5f);
208 rgba[i + 1] = uint8_t(std::clamp((ny * 0.5f + 0.5f) * 255.f, 0.f, 255.f) + 0.5f);
209 rgba[i + 2] = uint8_t(std::clamp((nz * 0.5f + 0.5f) * 255.f, 0.f, 255.f) + 0.5f);
210 rgba[i + 3] = 255;
211 }
212 }
213 return rgba;
214}
215
216float SnowField::clamp01(float v) {
217 return std::clamp(v, 0.f, 1.f);
218}
219
220} // namespace eve::weather
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
float cx
Definition CardTypes.cpp:33
float nx
float nz
float ny
glm::vec3 n
Definition Grass.cpp:63
double r
float v
HexVec3 across
std::int32_t c
int h
std::uint32_t height
std::uint32_t width
float radius
float d
float t
float dz
float dx
Cell cell
int margin
std::uint32_t depth
std::vector< uint8_t > toAlbedoRGBA() const
Albedo as RGBA8: cool-white snow blended to dark ground by depth, plus a tiny per-cell hash,...
bool inBounds(int x, int y) const
Compatibility predicate returning whether a cell is inside the field.
Definition SnowField.cpp:35
void stampFootprint(float cx, float cz, float dirX, float dirZ, float radius, float depth)
Press a footprint into the snow: an ellipse along (dirX, dirZ) with a soft bowl depression and a rais...
Definition SnowField.cpp:56
std::vector< uint8_t > toHeightRGBA() const
POM height map as RGBA8 (width x height x 4): R = snow depth, G = B = 0, A = 255 (white = raised towa...
void resize(int width, int height)
Resize.
Definition SnowField.cpp:28
float height(int x, int y) const
Cell value in [0,1]; out-of-bounds reads 0.
Definition SnowField.cpp:51
std::vector< uint8_t > toNormalRGBA() const
Tangent-space normal map as RGBA8 derived from the snow-depth gradient (flat = 128,...
SnowField()=default
Snow field.
void addSnowfall(float amount)
Snowfall recovery: raise every cell toward 1 by amount. Call with dt * snowfallRate each frame; clamp...
void stampImpact(float cx, float cz, float radius, float depth)
Carve an impact crater: a parabolic bowl (deep center, shallow edge) plus a raised snow rim just outs...
void setHeight(int x, int y, float h)
Set one cell (clamped to [0,1]); out-of-bounds is a no-op.
Definition SnowField.cpp:45
void fill(float v)
Set every cell to v (clamped to [0,1]).
Definition SnowField.cpp:39