-
Notifications
You must be signed in to change notification settings - Fork 37
/
snow_demo.cpp
167 lines (144 loc) · 5 KB
/
snow_demo.cpp
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include "snow_demo.h"
#include "tools.h"
namespace
{
s9w::rng_state rng{static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())};
struct flake_pos{
double m_column{};
double m_row{};
auto get_indices() const -> std::pair<int, int>{
return { get_int(m_column - 0.5) , get_int(m_row - 0.5) };
}
};
struct snowflake{
flake_pos m_pos;
double m_frontality;
bool m_static = false;
};
enum class stick_state{no_stick, can_stick };
}
auto draw_partial_snowflake(
const double brightness,
const double ratio,
oof::pixel_screen& px,
const int column,
const int row
) -> void
{
if (px.is_in(column, row) == false)
return;
const uint8_t component = get_int<uint8_t>(brightness);
const uint8_t alpha = get_int<uint8_t>(ratio * 255.0);
const s9w::srgba_u snow_color{ component, component, component, alpha };
oof::color& target = px.get_color(column, row);
const s9w::srgb_u blend_result = s9w::blend(std::bit_cast<s9w::srgb_u>(target), snow_color);
target = std::bit_cast<oof::color>(blend_result);
}
auto draw_snowflake(
const snowflake& flake,
oof::pixel_screen& px
) -> void
{
constexpr int min_brightness = 50;
constexpr int delta_brightness = 255 - min_brightness;
const double target_brightness = min_brightness + delta_brightness * flake.m_frontality;
const int column = get_int(flake.m_pos.m_column - 0.5);
const int upper_row = get_int(flake.m_pos.m_row - 0.5);
const double second_ratio = std::fmod(flake.m_pos.m_row, 1.0);
draw_partial_snowflake(target_brightness, 1.0-second_ratio, px, column, upper_row);
draw_partial_snowflake(target_brightness, second_ratio, px, column, upper_row+1);
}
auto snow_demo() -> void
{
//const int width = get_screen_cell_dimensions()[0];
constexpr int width = 60;
const int height = 2 * get_screen_cell_dimensions()[1];
constexpr double max_speed = 20.0;
double dt{};
oof::pixel_screen px{ width, height };
std::vector<stick_state> neigh(width*height, stick_state::no_stick);
for(int column=0; column<width; ++column)
{
const int row = height - 1;
const int index = row * width + column;
neigh[index] = stick_state::can_stick;
}
std::vector<snowflake> snowflakes;
timer timer;
while (true) {
// draw bg
for(int row=0; row<height; ++row){
for (int column = 0; column < width; ++column){
const double height_progress = 1.0 * row / (height - 1);
const oof::color bg_color{
get_int<uint8_t>(30*height_progress),
get_int<uint8_t>(30*height_progress),
get_int<uint8_t>(100*height_progress)
};
px.get_color(column, row) = bg_color;
}
}
// draw snow
for(const snowflake& flake : snowflakes)
draw_snowflake(flake, px);
// move snow
for (snowflake& flake : snowflakes)
{
if (flake.m_static)
continue;
flake.m_pos.m_row += max_speed * flake.m_frontality * dt;
}
// sticking
for (snowflake& flake : snowflakes)
{
if(flake.m_frontality<0.5)
continue;
const int column = get_int(flake.m_pos.m_column - 0.5);
const int row = get_int(flake.m_pos.m_row - 0.5);
if(row<=0)
continue;
const int index = row * width + column;
const bool lower_left_ok = column == 0 || neigh[index-1] == stick_state::can_stick;
const bool lower_right_ok = column == (width-1) || neigh[index+1] == stick_state::can_stick;
if (neigh[index] == stick_state::can_stick && lower_left_ok && lower_right_ok)
{
flake.m_static = true;
neigh[index - width] = stick_state::can_stick;
}
}
// New snow
if(rng.get_flip(0.5 * px.get_width() * dt)){
snowflakes.push_back(
snowflake{
.m_pos = flake_pos{
rng.get_real(0.5, width-0.5),
-1.0
},
.m_frontality = rng.get_real(0.2, 1.0)
}
);
}
// Remove flakes that run out of the bottom (for performance)
remove_from_vector(
snowflakes,
[&](const snowflake& flake){ return flake.m_pos.m_row > height; }
);
// Remove flakes that touch a sticking spot but couldn't stick (so they don't fly over the sticky snow)
remove_from_vector(
snowflakes,
[&](const snowflake& flake) {
if (flake.m_static)
return false;
const auto& [column, row] = flake.m_pos.get_indices();
if (row < 0)
return false;
const int index = row * width + column;
return neigh[index] == stick_state::can_stick;
}
);
dt = timer.mark_frame();
fast_print(px.get_string());
if (const auto fps = timer.get_fps(); fps.has_value())
set_window_title("FPS: " + std::to_string(*fps));
}
}