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
|
#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();
// Check if within boundary
check_bounds(emitter,transform);
// 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();
}
}
}
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};
//todo add that particle goes back to boundary
}
}
}
}
|