aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/system/ParticleSystem.cpp
blob: e6dc6708d73f205d5d253629ca04076761af4b2c (plain)
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
#include <cmath>
#include <ctime>
#include <cstdlib>

#include "api/ParticleEmitter.h"
#include "api/Vector2.h"

#include "ComponentManager.h"
#include "ParticleSystem.h"

using namespace crepe;

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>();

	for (ParticleEmitter & emitter : emitters) {

		//	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);		
		}
		
		// Update all particles
		for (size_t j = 0; j < emitter.data.particles.size(); j++) {
			if (emitter.data.particles[j].active) {
				emitter.data.particles[j].update();
			}
		}

		//	Check if within boundary
		check_bounds(emitter,transform);
	}

	update_count++;
	if(update_count == MAX_UPDATE_COUNT) update_count = 0;
}

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 min_angle = emitter.data.min_angle;
	double max_angle = emitter.data.max_angle;
	double random_angle;
	if(min_angle == max_angle){
		random_angle = min_angle;
	}
	else if (min_angle <= max_angle) {
			// Standard range (e.g., 10 to 20 degrees)
			double angle_offset = max_angle - min_angle;
			random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
	} else {
			// Wrap-around range (e.g., 350 to 10 degrees)
			double angle_offset = (360 - min_angle) + max_angle;
			random_angle = min_angle + static_cast<double>(std::rand() % static_cast<uint32_t>(angle_offset));
			
			// Wrap around to keep random_angle within 0-360 degrees
			if (random_angle >= 360) {
					random_angle -= 360;
			}
	}

	// Generate a random speed between min_speed and max_speed
	double speed_offset = emitter.data.max_speed - emitter.data.min_speed;
	double random_speed = 0.0;
	if(emitter.data.max_speed  == emitter.data.min_speed)
	{
		random_speed = emitter.data.min_speed;
	}
	else {
		random_speed = emitter.data.min_speed + static_cast<double>(std::rand() % static_cast<uint32_t>(speed_offset));
	}
	 
	// Convert random_angle to radians
	double angle_radians = random_angle * DEG_TO_RAD;

	Vector2 velocity = {
        random_speed * std::cos(angle_radians),
        random_speed * std::sin(angle_radians)
    };


	for (size_t i = 0; i < emitter.data.particles.size(); i++) {
		if (!emitter.data.particles[i].active) {
			emitter.data.particles[i].reset(emitter.data.end_lifespan, initial_position,velocity,random_angle);
			break;
		}
	}
}

int ParticleSystem::calculate_update(int count, double emission) {

	//get interger part of the emission
	double integer_part = std::floor(emission);

	// Get the fractional part of the emission
	double fractional_part = emission - integer_part;

	// Convert the fractional part to a denominator value
	int denominator = static_cast<int>(1.0 / fractional_part);

	// For emissions like 0.01, 0.1, 0.5, etc., calculate the update frequency
	if (fractional_part > 0) {
		// Calculate how often the update should be triggered based on the fractional part
		if (count % denominator == 0) {
			return 1;
		} else {
			return 0;
		}
	}
	
	// For integer emissions, return the emission directly
	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;

	// Define boundary edges
	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;

	std::vector<Particle>& particles = emitter.data.particles;
	for (Particle& particle : particles)
	{
		const Vector2& position = particle.position;

		// Check if particle is within bounds
		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 (particle.position.x < left) particle.position.x = left;
        else if (particle.position.x > right) particle.position.x = right;
        if (particle.position.y < bottom) particle.position.y = bottom;
        else if (particle.position.y > top) particle.position.y = top;
			}
		}
	}
}