Line data Source code
1 : #include "Particles.hpp"
2 :
3 : #include "core/physics/Particles.hpp"
4 : #include "core/utility/Getline.hpp"
5 :
6 : #include "spdlog/spdlog.h"
7 :
8 : #include <filesystem>
9 : #include <fstream>
10 : #include <map>
11 :
12 : namespace Soldank
13 : {
14 0 : Particle::Particle(bool _active,
15 : glm::vec2 _position,
16 : glm::vec2 _old_position,
17 : glm::vec2 _velocity,
18 : glm::vec2 force,
19 : float one_over_mass,
20 : float timestep,
21 : float gravity,
22 : float e_damping,
23 0 : float v_damping)
24 0 : : active(_active)
25 0 : , position(_position)
26 0 : , old_position(_old_position)
27 0 : , velocity_(_velocity)
28 0 : , force_(force)
29 0 : , one_over_mass_(one_over_mass)
30 0 : , timestep_(timestep)
31 0 : , gravity_(gravity)
32 0 : , e_damping_(e_damping)
33 0 : , v_damping_(v_damping)
34 : {
35 0 : }
36 :
37 0 : void Particle::Euler()
38 : {
39 0 : old_position = position;
40 0 : force_.y += gravity_;
41 0 : glm::vec2 current_force = force_;
42 0 : current_force *= one_over_mass_ * std::pow(timestep_, 2);
43 0 : velocity_ += current_force;
44 0 : position += velocity_;
45 0 : velocity_ *= e_damping_;
46 0 : force_ = glm::vec2(0.0, 0.0);
47 0 : }
48 :
49 0 : void Particle::Verlet()
50 : {
51 0 : glm::vec2 a = position * (1.0F + v_damping_);
52 0 : glm::vec2 b = old_position * v_damping_;
53 :
54 0 : old_position = position;
55 0 : force_.y += gravity_;
56 0 : glm::vec2 current_force = force_;
57 0 : current_force *= one_over_mass_ * std::pow(timestep_, 2);
58 0 : position = a - b + current_force;
59 0 : force_ = glm::vec2(0.0, 0.0);
60 0 : }
61 :
62 0 : ParticleSystem::ParticleSystem(const std::vector<Particle>& particles,
63 0 : const std::vector<Constraint>& constraints)
64 0 : : particles_(particles)
65 0 : , constraints_(constraints)
66 : {
67 0 : }
68 :
69 0 : void ParticleSystem::DoVerletTimestep()
70 : {
71 0 : for (Particle& particle : particles_) {
72 0 : if (particle.active) {
73 0 : particle.Verlet();
74 : }
75 : }
76 0 : SatisfyConstraints();
77 0 : }
78 :
79 0 : void ParticleSystem::DoVerletTimestepFor(unsigned int particle_num, unsigned int constraint_num)
80 : {
81 0 : particles_[particle_num - 1].Verlet();
82 0 : SatisfyConstraintFor(constraint_num - 1);
83 0 : }
84 :
85 0 : void ParticleSystem::DoEulerTimestep()
86 : {
87 0 : for (Particle& particle : particles_) {
88 0 : if (particle.active) {
89 0 : particle.Euler();
90 : }
91 : }
92 0 : }
93 :
94 0 : void ParticleSystem::DoEulerTimestepFor(unsigned int particle_num)
95 : {
96 0 : particles_[particle_num - 1].Euler();
97 0 : }
98 :
99 0 : void ParticleSystem::SatisfyConstraints()
100 : {
101 0 : for (const Constraint& constraint : constraints_) {
102 0 : if (constraint.active) {
103 0 : SatisfyConstraint(constraint, particles_);
104 : }
105 : }
106 0 : }
107 :
108 0 : void ParticleSystem::SatisfyConstraintFor(unsigned int constraint_num)
109 : {
110 0 : SatisfyConstraint(constraints_[constraint_num - 1], particles_);
111 0 : }
112 :
113 0 : void ParticleSystem::SatisfyConstraint(const Constraint& constraint,
114 : std::vector<Particle>& particles)
115 : {
116 0 : unsigned int a = constraint.particle_num.x - 1;
117 0 : unsigned int b = constraint.particle_num.y - 1;
118 :
119 0 : glm::vec2 delta = particles[b].position - particles[a].position;
120 0 : auto length = glm::length(delta);
121 :
122 0 : if (length > 0.0) {
123 0 : auto diff = (length - constraint.rest_length) / length;
124 :
125 : // delta *= diff / 2.0f;
126 0 : if (particles[a].GetOneOverMass() > 0.0) {
127 0 : particles[a].position += delta * diff / 2.0F;
128 : }
129 :
130 0 : if (particles[b].GetOneOverMass() > 0.0) {
131 0 : particles[b].position -= delta * diff / 2.0F;
132 : }
133 : }
134 0 : }
135 :
136 0 : std::shared_ptr<ParticleSystem> ParticleSystem::Load(ParticleSystemType particle_system_type,
137 : float scale,
138 : const IFileReader& file_reader)
139 : {
140 : // TODO: const
141 0 : const float grav = 0.06F;
142 0 : switch (particle_system_type) {
143 0 : case ParticleSystemType::Soldier: {
144 : // TODO: load it at the application start
145 : static std::map<float, std::shared_ptr<ParticleSystem>>
146 0 : soldier_particle_system_by_scale;
147 0 : if (!soldier_particle_system_by_scale.contains(scale)) {
148 0 : soldier_particle_system_by_scale[scale] =
149 0 : LoadFromFile("gostek.po", scale, 1.0F, 1.06F * grav, 0.0F, 0.9945F, file_reader);
150 : }
151 0 : return std::make_shared<ParticleSystem>(*soldier_particle_system_by_scale.at(scale));
152 : }
153 0 : case ParticleSystemType::Flag: {
154 0 : static std::map<float, std::shared_ptr<ParticleSystem>> flag_particle_system_by_scale;
155 0 : if (!flag_particle_system_by_scale.contains(scale)) {
156 0 : flag_particle_system_by_scale[scale] =
157 0 : LoadFromFile("flag.po", scale, 1.0F, 1.06F * grav, 0.0F, 0.9945F, file_reader);
158 : }
159 0 : return std::make_shared<ParticleSystem>(*flag_particle_system_by_scale.at(scale));
160 : }
161 0 : case ParticleSystemType::Weapon: {
162 0 : static std::map<float, std::shared_ptr<ParticleSystem>> weapon_particle_system_by_scale;
163 0 : if (!weapon_particle_system_by_scale.contains(scale)) {
164 0 : weapon_particle_system_by_scale[scale] =
165 0 : LoadFromFile("karabin.po", scale, 1.0F, 1.06F * grav, 0.0F, 0.9945F, file_reader);
166 : }
167 0 : return std::make_shared<ParticleSystem>(*weapon_particle_system_by_scale.at(scale));
168 : }
169 0 : case ParticleSystemType::Kit: {
170 0 : static std::map<float, std::shared_ptr<ParticleSystem>> kit_particle_system_by_scale;
171 0 : if (!kit_particle_system_by_scale.contains(scale)) {
172 0 : kit_particle_system_by_scale[scale] =
173 0 : LoadFromFile("kit.po", scale, 1.0F, 1.06F * grav, 0.0F, 0.989F, file_reader);
174 : }
175 0 : return std::make_shared<ParticleSystem>(*kit_particle_system_by_scale.at(scale));
176 : }
177 0 : case ParticleSystemType::Parachute: {
178 : static std::map<float, std::shared_ptr<ParticleSystem>>
179 0 : parachute_particle_system_by_scale;
180 0 : if (!parachute_particle_system_by_scale.contains(scale)) {
181 0 : parachute_particle_system_by_scale[scale] =
182 0 : LoadFromFile("para.po", scale, 1.0F, 1.06F * grav, 0.0F, 0.9945F, file_reader);
183 : }
184 0 : return std::make_shared<ParticleSystem>(*parachute_particle_system_by_scale.at(scale));
185 : }
186 0 : case ParticleSystemType::StationaryGun: {
187 : static std::map<float, std::shared_ptr<ParticleSystem>>
188 0 : stat_gun_particle_system_by_scale;
189 0 : if (!stat_gun_particle_system_by_scale.contains(scale)) {
190 0 : stat_gun_particle_system_by_scale[scale] =
191 0 : LoadFromFile("stat.po", scale, 1.0F, 1.06F * grav, 0.0F, 0.9945F, file_reader);
192 : }
193 0 : return std::make_shared<ParticleSystem>(*stat_gun_particle_system_by_scale.at(scale));
194 : }
195 : }
196 : }
197 :
198 0 : std::shared_ptr<ParticleSystem> ParticleSystem::LoadFromFile(const std::string& file_name,
199 : float scale,
200 : float timestep,
201 : float gravity,
202 : float e_damping,
203 : float v_damping,
204 : const IFileReader& file_reader)
205 : {
206 0 : std::vector<Particle> particles;
207 0 : std::vector<Constraint> constraints;
208 :
209 0 : std::filesystem::path file_path = "objects/";
210 0 : file_path += file_name;
211 0 : auto file_data = file_reader.Read(file_path.string());
212 0 : if (!file_data.has_value()) {
213 0 : std::string message = "Could not open file: " + file_path.string();
214 0 : throw std::runtime_error(message.c_str());
215 0 : }
216 0 : std::stringstream data_buffer{ *file_data };
217 :
218 0 : auto read_float = [](std::stringstream& buffer) {
219 0 : std::string line;
220 0 : GetlineSafe(buffer, line);
221 0 : return std::stof(line);
222 0 : };
223 :
224 0 : std::string line;
225 0 : GetlineSafe(data_buffer, line);
226 :
227 0 : while (line != "CONSTRAINTS") {
228 0 : float x = read_float(data_buffer);
229 0 : read_float(data_buffer);
230 0 : float z = read_float(data_buffer);
231 0 : glm::vec2 p = glm::vec2(-x * scale / 1.2, -z * scale);
232 :
233 0 : particles.emplace_back(true,
234 : p,
235 : p,
236 0 : glm::vec2(0.0, 0.0),
237 0 : glm::vec2(0.0, 0.0),
238 0 : 1.0,
239 : timestep,
240 : gravity,
241 : e_damping,
242 : v_damping);
243 :
244 0 : GetlineSafe(data_buffer, line);
245 : }
246 :
247 0 : while (!data_buffer.eof()) {
248 0 : GetlineSafe(data_buffer, line);
249 :
250 0 : if (data_buffer.eof() || line.empty() || line == "ENDFILE") {
251 0 : break;
252 : }
253 0 : line.erase(0, 1); // first character is always P
254 0 : unsigned int pa_num = std::stoul(line);
255 :
256 0 : GetlineSafe(data_buffer, line);
257 0 : line.erase(0, 1); // first character is always P
258 0 : unsigned int pb_num = std::stoul(line);
259 :
260 0 : auto delta = particles[pa_num - 1].position - particles[pb_num - 1].position;
261 0 : constraints.push_back({ true, glm::uvec2(pa_num, pb_num), glm::length(delta) });
262 : }
263 :
264 0 : spdlog::info("Particle {}, loaded {} particles and {} constraints",
265 0 : file_path.string(),
266 0 : particles.size(),
267 0 : constraints.size());
268 :
269 0 : return std::make_shared<ParticleSystem>(particles, constraints);
270 0 : }
271 : } // namespace Soldank
|