diff options
Diffstat (limited to 'src/crepe/system')
-rw-r--r-- | src/crepe/system/ParticleSystem.cpp | 157 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.h | 64 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.cpp | 56 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.h | 18 |
4 files changed, 243 insertions, 52 deletions
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index 397b586..4a25b47 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -1,62 +1,143 @@ #include <cmath> +#include <cstdlib> #include <ctime> -#include "../ComponentManager.h" -#include "../api/ParticleEmitter.h" +#include "api/ParticleEmitter.h" +#include "api/Transform.h" +#include "api/Vector2.h" +#include "ComponentManager.h" #include "ParticleSystem.h" using namespace crepe; -ParticleSystem::ParticleSystem() : elapsed_time(0.0f) {} +ParticleSystem::ParticleSystem() {} void ParticleSystem::update() { + // Get all emitters ComponentManager & mgr = ComponentManager::get_instance(); std::vector<std::reference_wrapper<ParticleEmitter>> emitters = mgr.get_components_by_type<ParticleEmitter>(); - float delta_time = 0.10; + for (ParticleEmitter & emitter : emitters) { - float update_amount = 1 / static_cast<float>(emitter.emission_rate); - for (float i = 0; i < delta_time; i += update_amount) { - emit_particle(emitter); + // Get transform linked to emitter + const Transform & transform + = mgr.get_components_by_id<Transform>(emitter.game_object_id) + .front() + .get(); + + // Emit particles based on emission_rate + int updates + = calculate_update(this->update_count, emitter.data.emission_rate); + for (size_t i = 0; i < updates; i++) { + emit_particle(emitter, transform); } - for (size_t j = 0; j < emitter.particles.size(); j++) { - if (emitter.particles[j].active) { - emitter.particles[j].update(delta_time); + + // Update all particles + for (Particle & particle : emitter.data.particles) { + if (particle.active) { + particle.update(); } } + + // Check if within boundary + check_bounds(emitter, transform); } + + this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT; } -void ParticleSystem::emit_particle(ParticleEmitter & emitter) { - Position initial_position = {emitter.position.x, emitter.position.y}; - float random_angle = 0.0f; - if (emitter.max_angle < emitter.min_angle) { - random_angle = ((emitter.min_angle - + (std::rand() - % (static_cast<uint32_t>(emitter.max_angle + 360 - - emitter.min_angle + 1)))) - % 360); - } else { - random_angle = emitter.min_angle - + (std::rand() - % (static_cast<uint32_t>(emitter.max_angle - - emitter.min_angle + 1))); - } - float angle_in_radians = random_angle * (M_PI / 180.0f); - float random_speed_offset = (static_cast<float>(std::rand()) / RAND_MAX) - * (2 * emitter.speed_offset) - - emitter.speed_offset; - float velocity_x - = (emitter.speed + random_speed_offset) * std::cos(angle_in_radians); - float velocity_y - = (emitter.speed + random_speed_offset) * std::sin(angle_in_radians); - Position initial_velocity = {velocity_x, velocity_y}; - for (size_t i = 0; i < emitter.particles.size(); i++) { - if (!emitter.particles[i].active) { - emitter.particles[i].reset(emitter.end_lifespan, initial_position, - initial_velocity); +void ParticleSystem::emit_particle(ParticleEmitter & emitter, + const Transform & transform) { + constexpr double DEG_TO_RAD = M_PI / 180.0; + + Vector2 initial_position = emitter.data.position + transform.position; + double random_angle + = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); + + double random_speed + = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); + double angle_radians = random_angle * DEG_TO_RAD; + + Vector2 velocity = {random_speed * std::cos(angle_radians), + random_speed * std::sin(angle_radians)}; + + for (Particle & particle : emitter.data.particles) { + if (!particle.active) { + particle.reset(emitter.data.end_lifespan, initial_position, + velocity, random_angle); break; } } } + +int ParticleSystem::calculate_update(int count, double emission) const { + double integer_part = std::floor(emission); + double fractional_part = emission - integer_part; + + if (fractional_part > 0) { + int denominator = static_cast<int>(1.0 / fractional_part); + return (count % denominator == 0) ? 1 : 0; + } + + return static_cast<int>(emission); +} + +void ParticleSystem::check_bounds(ParticleEmitter & emitter, + const Transform & transform) { + Vector2 offset = emitter.data.boundary.offset + transform.position + + emitter.data.position; + double half_width = emitter.data.boundary.width / 2.0; + double half_height = emitter.data.boundary.height / 2.0; + + const double LEFT = offset.x - half_width; + const double RIGHT = offset.x + half_width; + const double TOP = offset.y - half_height; + const double BOTTOM = offset.y + half_height; + + for (Particle & particle : emitter.data.particles) { + const Vector2 & position = particle.position; + bool within_bounds = (position.x >= LEFT && position.x <= RIGHT + && position.y >= TOP && position.y <= BOTTOM); + + if (!within_bounds) { + if (emitter.data.boundary.reset_on_exit) { + particle.active = false; + } else { + particle.velocity = {0, 0}; + if (position.x < LEFT) particle.position.x = LEFT; + else if (position.x > RIGHT) particle.position.x = RIGHT; + if (position.y < TOP) particle.position.y = TOP; + else if (position.y > BOTTOM) particle.position.y = BOTTOM; + } + } + } +} + +double ParticleSystem::generate_random_angle(double min_angle, + double max_angle) const { + if (min_angle == max_angle) { + return min_angle; + } else if (min_angle < max_angle) { + return min_angle + + static_cast<double>(std::rand() + % static_cast<int>(max_angle - min_angle)); + } else { + double angle_offset = (360 - min_angle) + max_angle; + double random_angle = min_angle + + static_cast<double>( + std::rand() % static_cast<int>(angle_offset)); + return (random_angle >= 360) ? random_angle - 360 : random_angle; + } +} + +double ParticleSystem::generate_random_speed(double min_speed, + double max_speed) const { + if (min_speed == max_speed) { + return min_speed; + } else { + return min_speed + + static_cast<double>(std::rand() + % static_cast<int>(max_speed - min_speed)); + } +} diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 3ac1d3f..3155df1 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -1,18 +1,74 @@ #pragma once -#include "../api/ParticleEmitter.h" +#include <cstdint> namespace crepe { - +class ParticleEmitter; +class Transform; +/** + * \brief ParticleSystem class responsible for managing particle emission, updates, and bounds checking. + */ class ParticleSystem { public: + /** + * \brief Default constructor. + */ ParticleSystem(); + + /** + * \brief Updates all particle emitters by emitting particles, updating particle states, and checking bounds. + */ void update(); private: - void emit_particle(ParticleEmitter & emitter); //emits a new particle + /** + * \brief Emits a particle from the specified emitter based on its emission properties. + * + * \param emitter Reference to the ParticleEmitter. + * \param transform Const reference to the Transform component associated with the emitter. + */ + void emit_particle(ParticleEmitter & emitter, const Transform & transform); + + /** + * \brief Calculates the number of times particles should be emitted based on emission rate and update count. + * + * \param count Current update count. + * \param emission Emission rate. + * \return The number of particles to emit. + */ + int calculate_update(int count, double emission) const; + + /** + * \brief Checks whether particles are within the emitter’s boundary, resets or stops particles if they exit. + * + * \param emitter Reference to the ParticleEmitter. + * \param transform Const reference to the Transform component associated with the emitter. + */ + void check_bounds(ParticleEmitter & emitter, const Transform & transform); - float elapsed_time; //elapsed time since the last emission + /** + * \brief Generates a random angle for particle emission within the specified range. + * + * \param min_angle Minimum emission angle in degrees. + * \param max_angle Maximum emission angle in degrees. + * \return Random angle in degrees. + */ + double generate_random_angle(double min_angle, double max_angle) const; + + /** + * \brief Generates a random speed for particle emission within the specified range. + * + * \param min_speed Minimum emission speed. + * \param max_speed Maximum emission speed. + * \return Random speed. + */ + double generate_random_speed(double min_speed, double max_speed) const; + +private: + //! Counter to count updates to determine how many times emit_particle is called. + uint32_t update_count = 0; + //! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 updates). + static constexpr uint32_t MAX_UPDATE_COUNT = 100; }; } // namespace crepe diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 10211a3..17a2337 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -1,3 +1,4 @@ +#include <cmath> #include <functional> #include <vector> @@ -6,7 +7,10 @@ #include "../api/Transform.h" #include "../facade/SDLContext.h" #include "../util/log.h" +#include "../api/ParticleEmitter.h" +#include "../api/Vector2.h" +#include "Particle.h" #include "RenderSystem.h" using namespace crepe; @@ -38,24 +42,62 @@ void RenderSystem::update_camera() { this->curr_cam = &cam; } } -void RenderSystem::render_sprites() const { + +bool RenderSystem::render_particle(const Sprite & sprite, + const Transform & tm) { ComponentManager & mgr = ComponentManager::get_instance(); + SDLContext & render = SDLContext::get_instance(); + + auto emitters = mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id); + + bool rendering_particles = false; + + Transform tmp(0, Vector2{0, 0}, 0, 0); + tmp.scale = tm.scale; + for (const ParticleEmitter & em : emitters) { + if (!em.active) continue; + if (!(em.data.sprite.game_object_id == sprite.game_object_id)) continue; - std::vector<std::reference_wrapper<Sprite>> sprites - = mgr.get_components_by_type<Sprite>(); + rendering_particles = true; + for (const Particle & p : em.data.particles) { + if (!p.active) continue; + tmp.position = p.position; + tmp.rotation = p.angle; + render.draw(em.data.sprite, tmp, *curr_cam); + } + } + return rendering_particles; +} +void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) { + + ComponentManager & mgr = ComponentManager::get_instance(); SDLContext & render = SDLContext::get_instance(); + + render.draw(sprite, tm, *curr_cam); +} + +void RenderSystem::render() { + + ComponentManager & mgr = ComponentManager::get_instance(); + + auto sprites = mgr.get_components_by_type<Sprite>(); for (const Sprite & sprite : sprites) { - auto transforms - = mgr.get_components_by_id<Transform>(sprite.game_object_id); - render.draw(sprite, transforms[0], *curr_cam); + if (!sprite.active) continue; + auto transform = mgr.get_components_by_id<Transform>(sprite.game_object_id); + + bool rendered_particles = this->render_particle(sprite, transform[0].get()); + + if (rendered_particles) continue; + + this->render_normal(sprite, transform[0].get()); } } void RenderSystem::update() { this->clear_screen(); this->update_camera(); - this->render_sprites(); + this->render(); this->present_screen(); } diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index 70db21a..468f79b 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -1,6 +1,8 @@ #pragma once #include "api/Camera.h" +#include "api/Sprite.h" +#include "api/Transform.h" #include "System.h" @@ -43,12 +45,22 @@ private: //! Updates the active camera used for rendering. void update_camera(); - //! Renders all active sprites to the screen. - void render_sprites() const; + //! Renders the whole screen + void render(); + + /** + * \brief Renders all the particles on the screen from a given sprite. + * + * \param sprite renders the particles with given texture + * \param tm the Transform component for scale + * \return true if particles have been rendered + */ + bool render_particle(const Sprite &, const Transform & tm); + + void render_normal(const Sprite &, const Transform & tm); /** * \todo Include color handling for sprites. - * \todo Implement particle emitter rendering with sprites. * \todo Add text rendering using SDL_ttf for text components. * \todo Implement a text component and a button component. * \todo Ensure each sprite is checked for active status before rendering. |