1 | // SuperTux |
2 | // Copyright (C) 2006 Matthias Braun <matze@braunis.de> |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | #include "object/snow_particle_system.hpp" |
18 | |
19 | #include <assert.h> |
20 | #include <math.h> |
21 | |
22 | #include "math/random.hpp" |
23 | #include "supertux/sector.hpp" |
24 | #include "video/surface.hpp" |
25 | #include "video/video_system.hpp" |
26 | #include "video/viewport.hpp" |
27 | |
28 | // TODO: tweak values |
29 | namespace SNOW { |
30 | static const float SPIN_SPEED = 60.0f; |
31 | static const float WIND_SPEED = 30.0f; // max speed of wind will be randf(WIND_SPEED) * randf(STATE_LENGTH) |
32 | static const float STATE_LENGTH = 5.0f; |
33 | static const float DECAY_RATIO = 0.2f; // ratio of attack speed to decay speed |
34 | static const float EPSILON = 0.5f; //velocity changes by up to this much each tick |
35 | static const float WOBBLE_DECAY = 0.99f; //wobble decays exponentially by this much each tick |
36 | static const float WOBBLE_FACTOR = 4 * .005f; //wobble approaches drift_speed by this much each tick |
37 | } |
38 | |
39 | SnowParticleSystem::SnowParticleSystem() : |
40 | state(RELEASING), |
41 | timer(), |
42 | gust_onset(0), |
43 | gust_current_velocity(0) |
44 | { |
45 | init(); |
46 | } |
47 | |
48 | SnowParticleSystem::SnowParticleSystem(const ReaderMapping& reader) : |
49 | ParticleSystem(reader), |
50 | state(RELEASING), |
51 | timer(), |
52 | gust_onset(0), |
53 | gust_current_velocity(0) |
54 | { |
55 | init(); |
56 | } |
57 | |
58 | SnowParticleSystem::~SnowParticleSystem() |
59 | { |
60 | } |
61 | |
62 | void SnowParticleSystem::init() |
63 | { |
64 | snowimages[0] = Surface::from_file("images/objects/particles/snow2.png" ); |
65 | snowimages[1] = Surface::from_file("images/objects/particles/snow1.png" ); |
66 | snowimages[2] = Surface::from_file("images/objects/particles/snow0.png" ); |
67 | |
68 | virtual_width = static_cast<float>(SCREEN_WIDTH) * 2.0f; |
69 | |
70 | timer.start(.01f); |
71 | |
72 | // create some random snowflakes |
73 | int snowflakecount = static_cast<int>(virtual_width / 10.0f); |
74 | for (int i = 0; i < snowflakecount; ++i) { |
75 | auto particle = std::make_unique<SnowParticle>(); |
76 | int snowsize = graphicsRandom.rand(3); |
77 | |
78 | particle->pos.x = graphicsRandom.randf(virtual_width); |
79 | particle->pos.y = graphicsRandom.randf(static_cast<float>(SCREEN_HEIGHT)); |
80 | particle->anchorx = particle->pos.x + (graphicsRandom.randf(-0.5, 0.5) * 16); |
81 | // drift will change with wind gusts |
82 | particle->drift_speed = graphicsRandom.randf(-0.5f, 0.5f) * 0.3f; |
83 | particle->wobble = 0.0; |
84 | |
85 | particle->texture = snowimages[snowsize]; |
86 | particle->flake_size = static_cast<int>(powf(static_cast<float>(snowsize) + 3.0f, 4.0f)); // since it ranges from 0 to 2 |
87 | |
88 | particle->speed = 6.32f * (1.0f + (2.0f - static_cast<float>(snowsize)) / 2.0f + graphicsRandom.randf(1.8f)); |
89 | |
90 | // Spinning |
91 | particle->angle = graphicsRandom.randf(360.0); |
92 | particle->spin_speed = graphicsRandom.randf(-SNOW::SPIN_SPEED,SNOW::SPIN_SPEED); |
93 | |
94 | particles.push_back(std::move(particle)); |
95 | } |
96 | } |
97 | |
98 | void SnowParticleSystem::update(float dt_sec) |
99 | { |
100 | if (!enabled) |
101 | return; |
102 | |
103 | // Simple ADSR wind gusts |
104 | |
105 | if (timer.check()) { |
106 | // Change state |
107 | state = static_cast<State>((state + 1) % MAX_STATE); |
108 | |
109 | if (state == RESTING) { |
110 | // stop wind |
111 | gust_current_velocity = 0; |
112 | // new wind strength |
113 | gust_onset = graphicsRandom.randf(-SNOW::WIND_SPEED, SNOW::WIND_SPEED); |
114 | } |
115 | timer.start(graphicsRandom.randf(SNOW::STATE_LENGTH)); |
116 | } |
117 | |
118 | // Update velocities |
119 | switch (state) { |
120 | case ATTACKING: |
121 | gust_current_velocity += gust_onset * dt_sec; |
122 | break; |
123 | case DECAYING: |
124 | gust_current_velocity -= gust_onset * dt_sec * SNOW::DECAY_RATIO; |
125 | break; |
126 | case RELEASING: |
127 | // uses current time/velocity instead of constants |
128 | gust_current_velocity -= gust_current_velocity * dt_sec / timer.get_timeleft(); |
129 | break; |
130 | case SUSTAINING: |
131 | case RESTING: |
132 | //do nothing |
133 | break; |
134 | default: |
135 | assert(false); |
136 | } |
137 | |
138 | float sq_g = sqrtf(Sector::get().get_gravity()); |
139 | |
140 | for (auto& part : particles) { |
141 | auto particle = dynamic_cast<SnowParticle*>(part.get()); |
142 | if (!particle) |
143 | continue; |
144 | |
145 | float anchor_delta; |
146 | |
147 | // Falling |
148 | particle->pos.y += particle->speed * dt_sec * sq_g; |
149 | // Drifting (speed approaches wind at a rate dependent on flake size) |
150 | particle->drift_speed += (gust_current_velocity - particle->drift_speed) / static_cast<float>(particle->flake_size) + graphicsRandom.randf(-SNOW::EPSILON, SNOW::EPSILON); |
151 | particle->anchorx += particle->drift_speed * dt_sec; |
152 | // Wobbling (particle approaches anchorx) |
153 | particle->pos.x += particle->wobble * dt_sec * sq_g; |
154 | anchor_delta = (particle->anchorx - particle->pos.x); |
155 | particle->wobble += (SNOW::WOBBLE_FACTOR * anchor_delta) + graphicsRandom.randf(-SNOW::EPSILON, SNOW::EPSILON); |
156 | particle->wobble *= SNOW::WOBBLE_DECAY; |
157 | // Spinning |
158 | particle->angle += particle->spin_speed * dt_sec; |
159 | particle->angle = fmodf(particle->angle, 360.0); |
160 | } |
161 | } |
162 | |
163 | /* EOF */ |
164 | |