cloud_particle_system.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. #include "object/cloud_particle_system.hpp"
  17. #include "math/random.hpp"
  18. #include "object/camera.hpp"
  19. #include "supertux/sector.hpp"
  20. #include "supertux/globals.hpp"
  21. #include "util/reader_mapping.hpp"
  22. #include "video/drawing_context.hpp"
  23. #include "video/surface.hpp"
  24. #include "video/surface_batch.hpp"
  25. #include "video/video_system.hpp"
  26. #include "video/viewport.hpp"
  27. CloudParticleSystem::CloudParticleSystem() :
  28. ParticleSystem(128),
  29. ExposedObject<CloudParticleSystem, scripting::Clouds>(this),
  30. cloudimage(Surface::from_file("images/particles/cloud.png")),
  31. m_current_speed(1.f),
  32. m_target_speed(1.f),
  33. m_speed_fade_time_remaining(0.f),
  34. m_current_amount(15),
  35. m_current_real_amount(0)
  36. {
  37. init();
  38. }
  39. CloudParticleSystem::CloudParticleSystem(const ReaderMapping& reader) :
  40. ParticleSystem(reader, 128),
  41. ExposedObject<CloudParticleSystem, scripting::Clouds>(this),
  42. cloudimage(Surface::from_file("images/particles/cloud.png")),
  43. m_current_speed(1.f),
  44. m_target_speed(1.f),
  45. m_speed_fade_time_remaining(0.f),
  46. m_current_amount(15),
  47. m_current_real_amount(0)
  48. {
  49. reader.get("intensity", m_current_amount);
  50. init();
  51. }
  52. CloudParticleSystem::~CloudParticleSystem()
  53. {
  54. }
  55. void CloudParticleSystem::init()
  56. {
  57. virtual_width = 2000.0;
  58. // Create some random clouds.
  59. add_clouds(m_current_amount, 0.f);
  60. }
  61. ObjectSettings CloudParticleSystem::get_settings()
  62. {
  63. ObjectSettings result = ParticleSystem::get_settings();
  64. result.add_int(_("Intensity"), &m_current_amount, "intensity", 15);
  65. result.reorder({"intensity", "enabled", "name"});
  66. return result;
  67. }
  68. void CloudParticleSystem::update(float dt_sec)
  69. {
  70. if (!enabled)
  71. return;
  72. // Update speed.
  73. if (m_speed_fade_time_remaining > 0.f) {
  74. if (dt_sec >= m_speed_fade_time_remaining) {
  75. m_current_speed = m_target_speed;
  76. m_speed_fade_time_remaining = 0.f;
  77. } else {
  78. float amount = dt_sec / m_speed_fade_time_remaining;
  79. m_current_speed += (m_target_speed - m_current_speed) * amount;
  80. m_speed_fade_time_remaining -= dt_sec;
  81. }
  82. }
  83. auto& cam = Sector::get().get_singleton_by_type<Camera>();
  84. auto scale = cam.get_current_scale();
  85. auto screen_width = static_cast<float>(SCREEN_WIDTH) / scale;
  86. auto screen_height = static_cast<float>(SCREEN_HEIGHT) / scale;
  87. for (auto& particle : particles) {
  88. auto cloudParticle = dynamic_cast<CloudParticle*>(particle.get());
  89. if (!cloudParticle)
  90. continue;
  91. cloudParticle->pos.x += cloudParticle->speed * dt_sec * m_current_speed;
  92. while (cloudParticle->pos.x < cam.get_translation().x - static_cast<float>(cloudParticle->texture->get_width()))
  93. cloudParticle->pos.x += screen_width + static_cast<float>(cloudParticle->texture->get_width()) * 2.f;
  94. while (cloudParticle->pos.x > cam.get_translation().x + screen_width)
  95. cloudParticle->pos.x -= screen_width + static_cast<float>(cloudParticle->texture->get_width()) * 2.f;
  96. while (cloudParticle->pos.y < cam.get_translation().y - static_cast<float>(cloudParticle->texture->get_height()))
  97. cloudParticle->pos.y += screen_height + static_cast<float>(cloudParticle->texture->get_height()) * 2.f;
  98. while (cloudParticle->pos.y > cam.get_translation().y + screen_height)
  99. cloudParticle->pos.y -= screen_height + static_cast<float>(cloudParticle->texture->get_height()) * 2.f;
  100. // Update alpha.
  101. if (cloudParticle->target_time_remaining > 0.f) {
  102. if (dt_sec >= cloudParticle->target_time_remaining) {
  103. cloudParticle->alpha = cloudParticle->target_alpha;
  104. cloudParticle->target_time_remaining = 0.f;
  105. if (cloudParticle->alpha == 0.f) {
  106. // Remove this particle, but not at this point
  107. // as it would interfere with the iterator.
  108. }
  109. } else {
  110. float amount = dt_sec / cloudParticle->target_time_remaining;
  111. cloudParticle->alpha += (cloudParticle->target_alpha - cloudParticle->alpha) * amount;
  112. cloudParticle->target_time_remaining -= dt_sec;
  113. }
  114. }
  115. }
  116. // Clear dead clouds.
  117. // Iterate through the vector backwards to avoid affecting the index of elements
  118. // after removal, preventing buggy behavior.
  119. for (int i = static_cast<int>(particles.size()) - 1; i >= 0; --i) {
  120. auto particle = dynamic_cast<CloudParticle*>(particles.at(i).get());
  121. if (particle->target_alpha == 0.f && particle->target_time_remaining == 0.f)
  122. particles.erase(particles.begin()+i);
  123. }
  124. }
  125. int CloudParticleSystem::add_clouds(int amount, float fade_time)
  126. {
  127. int target_amount = m_current_real_amount + amount;
  128. if (target_amount > max_amount)
  129. target_amount = max_amount;
  130. int amount_to_add = target_amount - m_current_real_amount;
  131. for (int i = 0; i < amount_to_add; ++i) {
  132. auto particle = std::make_unique<CloudParticle>();
  133. // Don't consider the camera, because the Sector might not exist yet
  134. // Instead, rely on update() to correct this when it will be called.
  135. particle->pos.x = graphicsRandom.randf(virtual_width);
  136. particle->pos.y = graphicsRandom.randf(virtual_height);
  137. particle->texture = cloudimage;
  138. particle->speed = -graphicsRandom.randf(25.0, 54.0);
  139. particle->alpha = (fade_time == 0.f) ? 1.f : 0.f;
  140. particle->target_alpha = 1.f;
  141. particle->target_time_remaining = fade_time;
  142. particles.push_back(std::move(particle));
  143. }
  144. m_current_real_amount = target_amount;
  145. return m_current_real_amount;
  146. }
  147. int CloudParticleSystem::remove_clouds(int amount, float fade_time)
  148. {
  149. int target_amount = m_current_real_amount - amount;
  150. if (target_amount < min_amount)
  151. target_amount = min_amount;
  152. int amount_to_remove = m_current_real_amount - target_amount;
  153. int i = 0;
  154. for (; i < amount_to_remove && i < static_cast<int>(particles.size()); ++i) {
  155. auto particle = dynamic_cast<CloudParticle*>(particles.at(i).get());
  156. if (particle->target_alpha != 1.f || particle->target_time_remaining != 0.f) {
  157. // Skip that one, it doesn't count.
  158. --i;
  159. } else {
  160. particle->target_alpha = 0.f;
  161. particle->target_time_remaining = fade_time;
  162. }
  163. }
  164. return i;
  165. }
  166. void CloudParticleSystem::fade_speed(float new_speed, float fade_time)
  167. {
  168. // No check for enabled; change the fading even if it's disabled.
  169. // If fade_time is 0 or smaller, update() will never change m_current_speed.
  170. if (fade_time <= 0.f)
  171. {
  172. m_current_speed = new_speed;
  173. }
  174. m_target_speed = new_speed;
  175. m_speed_fade_time_remaining = fade_time;
  176. }
  177. void CloudParticleSystem::fade_amount(int new_amount, float fade_time, float time_between)
  178. {
  179. // No check for enabled; change the fading even if it's disabled.
  180. int delta = new_amount - m_current_real_amount;
  181. if (delta < 0)
  182. {
  183. remove_clouds(-delta, fade_time);
  184. }
  185. else if (delta > 0)
  186. {
  187. add_clouds(delta, fade_time);
  188. } // If delta is zero, there is nothing to do.
  189. }
  190. void CloudParticleSystem::draw(DrawingContext& context)
  191. {
  192. if (!enabled)
  193. return;
  194. const auto& region = Sector::current()->get_active_region();
  195. context.push_transform();
  196. std::unordered_map<SurfacePtr, SurfaceBatch> batches;
  197. for (const auto& particle : particles) {
  198. if(!region.contains(particle->pos))
  199. continue;
  200. if (particle->alpha != 1.f) {
  201. const auto& batch_it = batches.emplace(
  202. particle->texture->clone(),
  203. SurfaceBatch(
  204. particle->texture,
  205. Color(1.f, 1.f, 1.f, particle->alpha)
  206. ));
  207. batch_it.first->second.draw(particle->pos, particle->angle);
  208. } else {
  209. auto it = batches.find(particle->texture);
  210. if (it == batches.end()) {
  211. const auto& batch_it = batches.emplace(particle->texture,
  212. SurfaceBatch(particle->texture));
  213. batch_it.first->second.draw(particle->pos, particle->angle);
  214. } else {
  215. it->second.draw(particle->pos, particle->angle);
  216. }
  217. }
  218. }
  219. for(auto& it : batches) {
  220. auto& surface = it.first;
  221. auto& batch = it.second;
  222. // FIXME: What is the colour used for?
  223. // RESOLVED : That's the tint and the alpha.
  224. context.color().draw_surface_batch(surface, batch.move_srcrects(),
  225. batch.move_dstrects(), batch.move_angles(), batch.get_color(), z_pos);
  226. }
  227. context.pop_transform();
  228. }
  229. /* EOF */