1 #include <iostream>
2 #include <deque>
3 #include <boost/cstdint.hpp>
4 #include <math.h>
5
6 #include "asserts.hpp"
7 #include "color_utils.hpp"
8 #include "entity.hpp"
9 #include "foreach.hpp"
10 #include "formula.hpp"
11 #include "frame.hpp"
12 #include "particle_system.hpp"
13 #include "preferences.hpp"
14 #include "string_utils.hpp"
15 #include "texture.hpp"
16 #include "variant_utils.hpp"
17 #include "weather_particle_system.hpp"
18 #include "water_particle_system.hpp"
19 #include "raster.hpp"
20
21 namespace {
22
23 class particle_animation {
24 public:
particle_animation(variant node)25 explicit particle_animation(variant node) :
26 id_(node["id"].as_string()),
27 texture_(graphics::texture::get(node["image"].as_string())),
28 duration_(node["duration"].as_int()),
29 reverse_frame_(node["reverse"].as_bool()),
30 loops_(node["loops"].as_bool(false))
31 {
32 rect base_area(node.has_key("rect") ? rect(node["rect"]) :
33 rect(node["x"].as_int(),
34 node["y"].as_int(),
35 node["w"].as_int(),
36 node["h"].as_int()));
37 width_ = base_area.w()*node["scale"].as_int(2);
38 height_ = base_area.h()*node["scale"].as_int(2);
39 int nframes = node["frames"].as_int(1);
40 if(nframes < 1) {
41 nframes = 1;
42 }
43
44 const int nframes_per_row = node["frames_per_row"].as_int(-1);
45 const int pad = node["pad"].as_int();
46
47 frame frame_obj(node);
48
49 int row = 0, col = 0;
50 for(int n = 0; n != nframes; ++n) {
51 const frame::frame_info& info = frame_obj.frame_layout()[n];
52 const rect& area = info.area;
53
54 frame_area a;
55 a.u1 = GLfloat(area.x())/GLfloat(texture_.width());
56 a.u2 = GLfloat(area.x2())/GLfloat(texture_.width());
57 a.v1 = GLfloat(area.y())/GLfloat(texture_.height());
58 a.v2 = GLfloat(area.y2())/GLfloat(texture_.height());
59
60 a.x_adjust = info.x_adjust*2;
61 a.y_adjust = info.y_adjust*2;
62 a.x2_adjust = info.x2_adjust*2;
63 a.y2_adjust = info.y2_adjust*2;
64
65 frames_.push_back(a);
66
67 ++col;
68 if(col == nframes_per_row) {
69 col = 0;
70 ++row;
71 }
72 }
73 }
74
75 struct frame_area {
76 GLfloat u1, v1, u2, v2;
77 int x_adjust, y_adjust, x2_adjust, y2_adjust;
78 };
79
get_frame(int t) const80 const frame_area& get_frame(int t) const {
81 int index = t/duration_;
82 if(index < 0) {
83 index = 0;
84 } else if(index >= frames_.size()) {
85 if(loops_ && !reverse_frame_) {
86 index = index%frames_.size();
87 } else if (loops_ && reverse_frame_){
88 index = (running_in_reverse(index)? frames_.size()- 1 - index%frames_.size(): index%frames_.size());
89 } else {
90 index = frames_.size() - 1;
91 }
92 }
93
94 return frames_[index];
95 }
running_in_reverse(int current_frame) const96 bool running_in_reverse(int current_frame) const
97 {
98 return current_frame % (2* frames_.size()) >= frames_.size();
99 }
100
101
set_texture() const102 void set_texture() const {
103 texture_.set_as_current_texture();
104 }
105
width() const106 int width() const { return width_; }
height() const107 int height() const { return height_; }
108 private:
109 std::string id_;
110 graphics::texture texture_;
111
112 std::vector<frame_area> frames_;
113 int duration_;
114 int reverse_frame_;
115 int width_, height_;
116 bool loops_;
117 };
118
119 struct simple_particle_system_info {
simple_particle_system_info__anon54335c4e0111::simple_particle_system_info120 simple_particle_system_info(variant node)
121 : spawn_rate_(node["spawn_rate"].as_int(1)),
122 spawn_rate_random_(node["spawn_rate_random"].as_int()),
123 system_time_to_live_(node["system_time_to_live"].as_int(-1)),
124 time_to_live_(node["time_to_live"].as_int(50)),
125 min_x_(node["min_x"].as_int(0)),
126 max_x_(node["max_x"].as_int(0)),
127 min_y_(node["min_y"].as_int(0)),
128 max_y_(node["max_y"].as_int(0)),
129 velocity_x_(node["velocity_x"].as_int(0)),
130 velocity_y_(node["velocity_y"].as_int(0)),
131 velocity_x_rand_(node["velocity_x_random"].as_int(0)),
132 velocity_y_rand_(node["velocity_y_random"].as_int(0)),
133 velocity_magnitude_(node["velocity_magnitude"].as_int(0)),
134 velocity_magnitude_rand_(node["velocity_magnitude_random"].as_int(0)),
135 velocity_rotate_(node["velocity_rotate"].as_int(0)),
136 velocity_rotate_rand_(node["velocity_rotate_random"].as_int(0)),
137 accel_x_(node["accel_x"].as_int(0)),
138 accel_y_(node["accel_y"].as_int(0)),
139 pre_pump_cycles_(node["pre_pump_cycles"].as_int(0)),
140 delta_r_(node["delta_r"].as_int(0)),
141 delta_g_(node["delta_g"].as_int(0)),
142 delta_b_(node["delta_b"].as_int(0)),
143 delta_a_(node["delta_a"].as_int(0)),
144 random_schedule_(false)
145
146 {
147 if(node.has_key("velocity_x_schedule")) {
148 velocity_x_schedule_ = node["velocity_x_schedule"].as_list_int();
149 }
150
151 if(node.has_key("velocity_y_schedule")) {
152 velocity_y_schedule_ = node["velocity_y_schedule"].as_list_int();
153 }
154
155 random_schedule_ = node["random_schedule"].as_bool(velocity_x_schedule_.empty() == false || velocity_y_schedule_.empty() == false);
156 }
157 int spawn_rate_, spawn_rate_random_;
158 int system_time_to_live_;
159 int time_to_live_;
160 int min_x_, max_x_, min_y_, max_y_;
161 int velocity_x_, velocity_y_;
162 int velocity_x_rand_, velocity_y_rand_;
163 int velocity_magnitude_, velocity_magnitude_rand_;
164 int velocity_rotate_, velocity_rotate_rand_;
165 int accel_x_, accel_y_;
166
167 int pre_pump_cycles_; //# of cycles to pre-emptively simulate so the particle system appears to have been running for a while, rather than visibly starting to emit particles just when the player walks onscreen
168
169 int delta_r_, delta_g_, delta_b_, delta_a_;
170
171 std::vector<int> velocity_x_schedule_, velocity_y_schedule_;
172
173 bool random_schedule_;
174 };
175
176 class simple_particle_system_factory : public particle_system_factory {
177 public:
178 explicit simple_particle_system_factory(variant node);
~simple_particle_system_factory()179 ~simple_particle_system_factory() {}
180
181 particle_system_ptr create(const entity& e) const;
182
183 std::vector<particle_animation> frames_;
184
185 simple_particle_system_info info_;
186 };
187
simple_particle_system_factory(variant node)188 simple_particle_system_factory::simple_particle_system_factory(variant node)
189 : info_(node)
190 {
191 foreach(variant frame_node, node["animation"].as_list()) {
192 frames_.push_back(particle_animation(frame_node));
193 }
194 }
195
196 class simple_particle_system : public particle_system
197 {
198 public:
199 simple_particle_system(const entity& e, const simple_particle_system_factory& factory);
~simple_particle_system()200 ~simple_particle_system() {}
201
is_destroyed() const202 bool is_destroyed() const { return info_.system_time_to_live_ == 0 || info_.spawn_rate_ < 0 && particles_.empty(); }
should_save() const203 bool should_save() const { return info_.spawn_rate_ >= 0; }
204 void process(const entity& e);
205 void draw(const rect& area, const entity& e) const;
206
207 private:
208 void prepump(const entity& e);
209
get_value(const std::string & key) const210 variant get_value(const std::string& key) const {
211 if(key == "spawn_rate") {
212 return variant(info_.spawn_rate_);
213 } else if(key == "spawn_rate_random") {
214 return variant(info_.spawn_rate_random_);
215 } else if(key == "system_time_to_live") {
216 return variant(info_.system_time_to_live_);
217 } else if(key == "time_to_live") {
218 return variant(info_.time_to_live_);
219 } else if(key == "min_x") {
220 return variant(info_.min_x_);
221 } else if(key == "max_x") {
222 return variant(info_.max_x_);
223 } else if(key == "min_y") {
224 return variant(info_.min_y_);
225 } else if(key == "max_y") {
226 return variant(info_.max_y_);
227 } else if(key == "velocity_x") {
228 return variant(info_.velocity_x_);
229 } else if(key == "velocity_y") {
230 return variant(info_.velocity_y_);
231 } else if(key == "velocity_x_random") {
232 return variant(info_.velocity_x_rand_);
233 } else if(key == "velocity_y_random") {
234 return variant(info_.velocity_y_rand_);
235 } else if(key == "velocity_magnitude") {
236 return variant(info_.velocity_magnitude_);
237 } else if(key == "velocity_magnitude_random") {
238 return variant(info_.velocity_magnitude_rand_);
239 } else if(key == "velocity_rotate") {
240 return variant(info_.velocity_rotate_);
241 } else if(key == "velocity_rotate_random") {
242 return variant(info_.velocity_rotate_rand_);
243 } else if(key == "velocity_rotate") {
244 return variant(info_.velocity_rotate_);
245 } else if(key == "velocity_rotate_random") {
246 return variant(info_.velocity_rotate_rand_);
247 } else if(key == "accel_x") {
248 return variant(info_.accel_x_);
249 } else if(key == "accel_y") {
250 return variant(info_.accel_y_);
251 } else if(key == "pre_pump_cycles") {
252 return variant(info_.pre_pump_cycles_);
253 } else if(key == "delta_r") {
254 return variant(info_.delta_r_);
255 } else if(key == "delta_g") {
256 return variant(info_.delta_g_);
257 } else if(key == "delta_b") {
258 return variant(info_.delta_b_);
259 } else if(key == "delta_a") {
260 return variant(info_.delta_a_);
261 } else {
262 return variant();
263 }
264 }
265
set_value(const std::string & key,const variant & value)266 void set_value(const std::string& key, const variant& value) {
267 if(key == "spawn_rate") {
268 info_.spawn_rate_ = value.as_int();
269 } else if(key == "spawn_rate_random") {
270 info_.spawn_rate_random_ = value.as_int();
271 } else if(key == "system_time_to_live") {
272 info_.system_time_to_live_ = value.as_int();
273 } else if(key == "time_to_live") {
274 info_.time_to_live_ = value.as_int();
275 } else if(key == "min_x") {
276 info_.min_x_ = value.as_int();
277 } else if(key == "max_x") {
278 info_.max_x_ = value.as_int();
279 } else if(key == "min_y") {
280 info_.min_y_ = value.as_int();
281 } else if(key == "max_y") {
282 info_.max_y_ = value.as_int();
283 } else if(key == "velocity_x") {
284 info_.velocity_x_ = value.as_int();
285 } else if(key == "velocity_y") {
286 info_.velocity_y_ = value.as_int();
287 } else if(key == "velocity_x_random") {
288 info_.velocity_x_rand_ = value.as_int();
289 } else if(key == "velocity_y_random") {
290 info_.velocity_y_rand_ = value.as_int();
291 } else if(key == "velocity_magnitude") {
292 info_.velocity_magnitude_ = value.as_int();
293 } else if(key == "velocity_magnitude_random") {
294 info_.velocity_magnitude_rand_ = value.as_int();
295 } else if(key == "velocity_rotate") {
296 info_.velocity_rotate_ = value.as_int();
297 } else if(key == "velocity_rotate_random") {
298 info_.velocity_rotate_rand_ = value.as_int();
299 } else if(key == "velocity_rotate") {
300 info_.velocity_rotate_ = value.as_int();
301 } else if(key == "velocity_rotate_random") {
302 info_.velocity_rotate_rand_ = value.as_int();
303 } else if(key == "accel_x") {
304 info_.accel_x_ = value.as_int();
305 } else if(key == "accel_y") {
306 info_.accel_y_ = value.as_int();
307 } else if(key == "pre_pump_cycles") {
308 info_.pre_pump_cycles_ = value.as_int();
309 } else if(key == "delta_r") {
310 info_.delta_r_ = value.as_int();
311 } else if(key == "delta_g") {
312 info_.delta_g_ = value.as_int();
313 } else if(key == "delta_b") {
314 info_.delta_b_ = value.as_int();
315 } else if(key == "delta_a") {
316 info_.delta_a_ = value.as_int();
317 }
318
319 }
320
321 const simple_particle_system_factory& factory_;
322 simple_particle_system_info info_;
323
324 int cycle_;
325
326 struct particle {
327 GLfloat pos[2];
328 GLfloat velocity[2];
329 const particle_animation* anim;
330 int random;
331 };
332
333 struct generation {
334 int members;
335 int created_at;
336 };
337
338 std::deque<particle> particles_;
339 std::deque<generation> generations_;
340
341 int spawn_buildup_;
342 };
343
simple_particle_system(const entity & e,const simple_particle_system_factory & factory)344 simple_particle_system::simple_particle_system(const entity& e, const simple_particle_system_factory& factory)
345 : factory_(factory), info_(factory.info_), cycle_(0), spawn_buildup_(0)
346 {
347 }
348
prepump(const entity & e)349 void simple_particle_system::prepump(const entity& e)
350 {
351 //cosmetic thing for very slow-moving particles:
352 //it looks weird when you walk into a scene, with, say, a column of smoke that's presumably been rising for quite some time,
353 //but it only begins rising the moment you arrive. To overcome this, we can optionally have particle systems pre-simulate their particles
354 //for the short period of time (often as low as 4 seconds) needed to eliminate that implementation artifact
355 for( int i = 0; i < info_.pre_pump_cycles_; ++i)
356 {
357 process(e);
358 }
359
360 }
361
process(const entity & e)362 void simple_particle_system::process(const entity& e)
363 {
364 --info_.system_time_to_live_;
365 ++cycle_;
366
367 if(cycle_ == 1) {
368 prepump(e);
369 }
370
371 while(!generations_.empty() && cycle_ - generations_.front().created_at == info_.time_to_live_) {
372 particles_.erase(particles_.begin(), particles_.begin() + generations_.front().members);
373 generations_.pop_front();
374 }
375
376 std::deque<particle>::iterator p = particles_.begin();
377 foreach(generation& gen, generations_) {
378 for(int n = 0; n != gen.members; ++n) {
379 p->pos[0] += p->velocity[0];
380 p->pos[1] += p->velocity[1];
381 if(e.face_right()) {
382 p->velocity[0] += info_.accel_x_/1000.0;
383 } else {
384 p->velocity[0] -= info_.accel_x_/1000.0;
385 }
386 p->velocity[1] += info_.accel_y_/1000.0;
387 ++p;
388 }
389 }
390
391 if(info_.velocity_x_schedule_.empty() == false) {
392 std::deque<particle>::iterator p = particles_.begin();
393 foreach(generation& gen, generations_) {
394
395 for(int n = 0; n != gen.members; ++n) {
396 const int ncycle = p->random + cycle_ - gen.created_at - 1;
397 p->velocity[0] += info_.velocity_x_schedule_[ncycle%info_.velocity_x_schedule_.size()];
398 if(cycle_ - gen.created_at > 1) {
399 p->velocity[0] -= info_.velocity_x_schedule_[(ncycle-1)%info_.velocity_x_schedule_.size()];
400 }
401
402 ++p;
403 }
404 }
405 }
406
407 if(info_.velocity_y_schedule_.empty() == false) {
408 std::deque<particle>::iterator p = particles_.begin();
409 foreach(generation& gen, generations_) {
410 for(int n = 0; n != gen.members; ++n) {
411 const int ncycle = p->random + cycle_ - gen.created_at - 1;
412 p->velocity[1] += info_.velocity_y_schedule_[ncycle%info_.velocity_y_schedule_.size()];
413 if(cycle_ - gen.created_at > 1) {
414 p->velocity[1] -= info_.velocity_y_schedule_[(ncycle-1)%info_.velocity_y_schedule_.size()];
415 }
416
417 ++p;
418 }
419 }
420 }
421
422 int nspawn = info_.spawn_rate_;
423 if(info_.spawn_rate_random_ > 0) {
424 nspawn += rand()%info_.spawn_rate_random_;
425 }
426
427 if(nspawn > 0) {
428 nspawn += spawn_buildup_;
429 }
430
431 spawn_buildup_ = nspawn%1000;
432 nspawn /= 1000;
433
434 if(nspawn == 0) {
435 return;
436 }
437
438 generation new_gen;
439 new_gen.members = nspawn;
440 new_gen.created_at = cycle_;
441
442 generations_.push_back(new_gen);
443
444 while(nspawn-- > 0) {
445 particle p;
446 p.pos[0] = e.face_right() ? (e.x() + info_.min_x_) : (e.x() + e.current_frame().width() - info_.max_x_);
447 p.pos[1] = e.y() + info_.min_y_;
448 p.velocity[0] = info_.velocity_x_/1000.0;
449 p.velocity[1] = info_.velocity_y_/1000.0;
450
451 if(info_.velocity_x_rand_ > 0) {
452 p.velocity[0] += (rand()%info_.velocity_x_rand_)/1000.0;
453 }
454
455 if(info_.velocity_y_rand_ > 0) {
456 p.velocity[1] += (rand()%info_.velocity_y_rand_)/1000.0;
457 }
458
459 int velocity_magnitude = info_.velocity_magnitude_;
460 if(info_.velocity_magnitude_rand_ > 0) {
461 velocity_magnitude += rand()%info_.velocity_magnitude_rand_;
462 }
463
464 if(velocity_magnitude) {
465 int rotate_velocity = info_.velocity_rotate_;
466 if(info_.velocity_rotate_rand_) {
467 rotate_velocity += rand()%info_.velocity_rotate_rand_;
468 }
469
470 const GLfloat rotate_radians = (GLfloat(rotate_velocity)/360.0)*3.14*2.0;
471 const GLfloat magnitude = velocity_magnitude/1000.0;
472 p.velocity[0] += sin(rotate_radians)*magnitude;
473 p.velocity[1] += cos(rotate_radians)*magnitude;
474 }
475
476 ASSERT_GT(factory_.frames_.size(), 0);
477 p.anim = &factory_.frames_[rand()%factory_.frames_.size()];
478
479 const int diff_x = info_.max_x_ - info_.min_x_;
480 if(diff_x > 0) {
481 p.pos[0] += (rand()%(diff_x*1000))/1000.0;
482 }
483
484 const int diff_y = info_.max_y_ - info_.min_y_;
485 if(diff_y > 0) {
486 p.pos[1] += (rand()%(diff_y*1000))/1000.0;
487 }
488
489 if(!e.face_right()) {
490 p.velocity[0] = -p.velocity[0];
491 }
492
493 if(info_.random_schedule_) {
494 p.random = rand();
495 } else {
496 p.random = 0;
497 }
498
499 particles_.push_back(p);
500 }
501 }
502
draw(const rect & area,const entity & e) const503 void simple_particle_system::draw(const rect& area, const entity& e) const
504 {
505 if(particles_.empty()) {
506 return;
507 }
508
509 std::deque<particle>::const_iterator p = particles_.begin();
510
511 //all particles must have the same texture, so just set it once.
512 p->anim->set_texture();
513 std::vector<GLfloat>& varray = graphics::global_vertex_array();
514 std::vector<GLfloat>& tcarray = graphics::global_texcoords_array();
515 std::vector<GLbyte>& carray = graphics::global_vertex_color_array();
516
517 const int facing = e.face_right() ? 1 : -1;
518
519
520 carray.clear();
521 varray.clear();
522 tcarray.clear();
523 foreach(const generation& gen, generations_) {
524 for(int n = 0; n != gen.members; ++n) {
525 const particle_animation* anim = p->anim;
526 const particle_animation::frame_area& f = anim->get_frame(cycle_ - gen.created_at);
527
528 if(info_.delta_a_){
529 //Spare the bandwidth if we're opaque
530 const int alpha_level = std::max(256 - info_.delta_a_*(cycle_ - gen.created_at), 0);
531 const int red = 255;
532 const int green = 255;
533 const int blue = 255;
534 for( int i = 0; i < 6; ++i){
535 carray.push_back(red); carray.push_back(green); carray.push_back(blue); carray.push_back(alpha_level);
536 }
537
538 }
539 //draw the first point twice, to allow drawing all particles
540 //in one drawing operation.
541
542 tcarray.push_back(graphics::texture::get_coord_x(f.u1));
543 tcarray.push_back(graphics::texture::get_coord_y(f.v1));
544 varray.push_back(p->pos[0] + f.x_adjust*facing);
545 varray.push_back(p->pos[1] + f.y_adjust);
546 tcarray.push_back(graphics::texture::get_coord_x(f.u1));
547 tcarray.push_back(graphics::texture::get_coord_y(f.v1));
548 varray.push_back(p->pos[0] + f.x_adjust*facing);
549 varray.push_back(p->pos[1] + f.y_adjust);
550
551 tcarray.push_back(graphics::texture::get_coord_x(f.u2));
552 tcarray.push_back(graphics::texture::get_coord_y(f.v1));
553 varray.push_back(p->pos[0] + (anim->width() - f.x2_adjust)*facing);
554 varray.push_back(p->pos[1] + f.y_adjust);
555 tcarray.push_back(graphics::texture::get_coord_x(f.u1));
556 tcarray.push_back(graphics::texture::get_coord_y(f.v2));
557 varray.push_back(p->pos[0] + f.x_adjust*facing);
558 varray.push_back(p->pos[1] + anim->height() - f.y2_adjust);
559
560 //draw the last point twice.
561 tcarray.push_back(graphics::texture::get_coord_x(f.u2));
562 tcarray.push_back(graphics::texture::get_coord_y(f.v2));
563 varray.push_back(p->pos[0] + (anim->width() - f.x2_adjust)*facing);
564 varray.push_back(p->pos[1] + anim->height() - f.y2_adjust);
565 tcarray.push_back(graphics::texture::get_coord_x(f.u2));
566 tcarray.push_back(graphics::texture::get_coord_y(f.v2));
567 varray.push_back(p->pos[0] + (anim->width() - f.x2_adjust)*facing);
568 varray.push_back(p->pos[1] + anim->height() - f.y2_adjust);
569 ++p;
570 }
571 }
572
573 #if defined(USE_GLES2)
574 if(info_.delta_a_) {
575 gles2::manager gles2_manager(gles2::get_texcol_shader());
576 gles2::active_shader()->shader()->color_array(4, GL_UNSIGNED_BYTE, GL_TRUE, 0, &carray.front());
577 gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, GL_FALSE, 0, &varray.front());
578 gles2::active_shader()->shader()->texture_array(2, GL_FLOAT, GL_FALSE, 0, &tcarray.front());
579 glDrawArrays(GL_TRIANGLE_STRIP, 0, varray.size()/2);
580 } else {
581 gles2::active_shader()->prepare_draw();
582 gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, GL_FALSE, 0, &varray.front());
583 gles2::active_shader()->shader()->texture_array(2, GL_FLOAT, GL_FALSE, 0, &tcarray.front());
584 glDrawArrays(GL_TRIANGLE_STRIP, 0, varray.size()/2);
585 }
586 #else
587 if(info_.delta_a_){
588 glEnableClientState(GL_COLOR_ARRAY);
589 glColorPointer(4, GL_UNSIGNED_BYTE, 0, &carray.front());
590 }
591
592 glVertexPointer(2, GL_FLOAT, 0, &varray.front());
593 glTexCoordPointer(2, GL_FLOAT, 0, &tcarray.front());
594 glDrawArrays(GL_TRIANGLE_STRIP, 0, varray.size()/2);
595
596 if(info_.delta_a_){
597 glDisableClientState(GL_COLOR_ARRAY);
598 }
599 #endif
600 glColor4f(1.0, 1.0, 1.0, 1.0);
601 }
602
create(const entity & e) const603 particle_system_ptr simple_particle_system_factory::create(const entity& e) const
604 {
605 return particle_system_ptr(new simple_particle_system(e, *this));
606 }
607
608 struct point_particle_info
609 {
point_particle_info__anon54335c4e0111::point_particle_info610 explicit point_particle_info(variant node)
611 : generation_rate_millis(node["generation_rate_millis"].as_int()),
612 pos_x(node["pos_x"].as_int()*1024),
613 pos_y(node["pos_y"].as_int()*1024),
614 pos_x_rand(node["pos_x_rand"].as_int()*1024),
615 pos_y_rand(node["pos_y_rand"].as_int()*1024),
616 velocity_x(node["velocity_x"].as_int()),
617 velocity_y(node["velocity_y"].as_int()),
618 accel_x(node["accel_x"].as_int()),
619 accel_y(node["accel_y"].as_int()),
620 velocity_x_rand(node["velocity_x_rand"].as_int()),
621 velocity_y_rand(node["velocity_y_rand"].as_int()),
622 dot_size(node["dot_size"].as_int(1)*(preferences::double_scale() ? 2 : 1)),
623 dot_rounded(node["dot_rounded"].as_bool(false)),
624 time_to_live(node["time_to_live"].as_int()),
625 time_to_live_max(node["time_to_live_rand"].as_int() + time_to_live) {
626
627 if(node.has_key("colors")) {
628 const std::vector<variant> colors_vec = node["colors"].as_list();
629 foreach(const variant& col, colors_vec) {
630 unsigned int val = strtoul(col.as_string().c_str(), NULL, 16);
631 unsigned char* c = reinterpret_cast<unsigned char*>(&val);
632 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
633 std::reverse(c, c+4);
634 #endif
635 colors.push_back(val);
636 }
637 }
638
639 if(node.has_key("colors_expression")) {
640 const variant v = game_logic::formula(node["colors_expression"]).execute();
641 for(int n = 0; n != v.num_elements(); ++n) {
642 const variant u = v[n];
643 ASSERT_LOG(u.num_elements() == 4, "UNEXPECTED colors_expression: " << u.to_debug_string());
644 const unsigned int r = u[0].as_int();
645 const unsigned int g = u[1].as_int();
646 const unsigned int b = u[2].as_int();
647 const unsigned int a = u[3].as_int();
648 unsigned int val = (r << 24) + (g << 16) + (b << 8) + a;
649
650 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
651 unsigned char* c = reinterpret_cast<unsigned char*>(&val);
652 std::reverse(c, c+4);
653 #endif
654 colors.push_back(val);
655 }
656 }
657
658 std::reverse(colors.begin(), colors.end());
659
660 ttl_divisor = time_to_live_max/(colors.size()-1);
661
662 rgba[0] = node["red"].as_int();
663 rgba[1] = node["green"].as_int();
664 rgba[2] = node["blue"].as_int();
665 rgba[3] = node["alpha"].as_int(255);
666 rgba_rand[0] = node["red_rand"].as_int();
667 rgba_rand[1] = node["green_rand"].as_int();
668 rgba_rand[2] = node["blue_rand"].as_int();
669 rgba_rand[3] = node["alpha_rand"].as_int();
670 rgba_delta[0] = node["red_delta"].as_int();
671 rgba_delta[1] = node["green_delta"].as_int();
672 rgba_delta[2] = node["blue_delta"].as_int();
673 rgba_delta[3] = node["alpha_delta"].as_int();
674 }
675
676 int generation_rate_millis;
677 int pos_x, pos_y, pos_x_rand, pos_y_rand;
678 int velocity_x, velocity_y, velocity_x_rand, velocity_y_rand;
679 int accel_x, accel_y;
680 int time_to_live, time_to_live_max;
681 unsigned char rgba[4];
682 unsigned char rgba_rand[4];
683 char rgba_delta[4];
684 int dot_size;
685 bool dot_rounded;
686
687 std::vector<unsigned int> colors;
688 int ttl_divisor;
689 };
690
691 class point_particle_system : public particle_system
692 {
693 public:
point_particle_system(const entity & obj,const point_particle_info & info)694 point_particle_system(const entity& obj, const point_particle_info& info) : obj_(obj), info_(info), particle_generation_(0), generation_rate_millis_(info.generation_rate_millis), pos_x_(info.pos_x), pos_x_rand_(info.pos_x_rand), pos_y_(info.pos_y), pos_y_rand_(info.pos_y_rand) {
695 }
696
process(const entity & e)697 void process(const entity& e) {
698 particle_generation_ += generation_rate_millis_;
699
700 particles_.erase(std::remove_if(particles_.begin(), particles_.end(), particle_destroyed), particles_.end());
701
702 for(std::vector<particle>::iterator p = particles_.begin();
703 p != particles_.end(); ++p) {
704 p->pos_x += p->velocity_x;
705 p->pos_y += p->velocity_y;
706 if(e.face_right()) {
707 p->velocity_x += info_.accel_x/1000.0;
708 } else {
709 p->velocity_x -= info_.accel_x/1000.0;
710 }
711 p->velocity_y += info_.accel_y/1000.0;
712 p->rgba[0] += info_.rgba_delta[0];
713 p->rgba[1] += info_.rgba_delta[1];
714 p->rgba[2] += info_.rgba_delta[2];
715 p->rgba[3] += info_.rgba_delta[3];
716 p->ttl--;
717 }
718
719 while(particle_generation_ >= 1000) {
720 //std::cerr << "PARTICLE X ORIGIN: " << pos_x_;
721 particles_.push_back(particle());
722 particle& p = particles_.back();
723 p.ttl = info_.time_to_live;
724 if(info_.time_to_live_max != info_.time_to_live) {
725 p.ttl += rand()%(info_.time_to_live_max - info_.time_to_live);
726 }
727
728 p.velocity_x = info_.velocity_x;
729 p.velocity_y = info_.velocity_y;
730
731 if(info_.velocity_x_rand) {
732 p.velocity_x += rand()%info_.velocity_x_rand;
733 }
734
735 if(info_.velocity_y_rand) {
736 p.velocity_y += rand()%info_.velocity_y_rand;
737 }
738
739 p.pos_x = e.x()*1024 + pos_x_;
740 p.pos_y = e.y()*1024 + pos_y_;
741
742 if(pos_x_rand_) {
743 p.pos_x += rand()%pos_x_rand_;
744 }
745
746 if(pos_y_rand_) {
747 p.pos_y += rand()%pos_y_rand_;
748 }
749
750 p.rgba[0] = info_.rgba[0];
751 p.rgba[1] = info_.rgba[1];
752 p.rgba[2] = info_.rgba[2];
753 p.rgba[3] = info_.rgba[3];
754
755 if(info_.rgba_rand[0]) {
756 p.rgba[0] += rand()%info_.rgba_rand[0];
757 }
758
759 if(info_.rgba_rand[1]) {
760 p.rgba[1] += rand()%info_.rgba_rand[1];
761 }
762
763 if(info_.rgba_rand[2]) {
764 p.rgba[2] += rand()%info_.rgba_rand[2];
765 }
766
767 if(info_.rgba_rand[3]) {
768 p.rgba[3] += rand()%info_.rgba_rand[3];
769 }
770
771 particle_generation_ -= 1000;
772 }
773 }
774
draw(const rect & area,const entity & e) const775 void draw(const rect& area, const entity& e) const {
776 if(particles_.empty()) {
777 return;
778 }
779
780 static std::vector<GLshort> vertex;
781 static std::vector<unsigned int> colors;
782 vertex.resize(particles_.size()*2);
783 colors.resize(particles_.size());
784
785 unsigned int* c = &colors[0];
786 GLshort* v = &vertex[0];
787 for(std::vector<particle>::const_iterator p = particles_.begin();
788 p != particles_.end(); ++p) {
789 *v++ = p->pos_x/1024;
790 *v++ = p->pos_y/1024;
791 if(info_.colors.size() >= 2) {
792 *c++ = info_.colors[p->ttl/info_.ttl_divisor];
793 } else {
794 *c++ = p->color;
795 }
796 }
797
798 glColor4f(1.0, 1.0, 1.0, 1.0);
799
800 #if defined(USE_GLES2)
801 // Not dealing with GL_POINT_SMOOTH right now -- this would probably be better as a frgament shader.
802 glPointSize(info_.dot_size);
803 gles2::manager gles2_manager(gles2::get_simple_col_shader());
804 gles2::active_shader()->shader()->vertex_array(2, GL_SHORT, GL_FALSE, 0, &vertex[0]);
805 gles2::active_shader()->shader()->color_array(4, GL_UNSIGNED_BYTE, GL_TRUE, 0, &colors[0]);
806 glDrawArrays(GL_POINTS, 0, particles_.size());
807 #else
808 glDisable(GL_TEXTURE_2D);
809 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
810 glEnableClientState(GL_COLOR_ARRAY);
811 if(info_.dot_rounded){
812 glEnable( GL_POINT_SMOOTH );
813 }
814 glPointSize(info_.dot_size);
815
816 glVertexPointer(2, GL_SHORT, 0, &vertex[0]);
817 glColorPointer(4, GL_UNSIGNED_BYTE, 0, &colors[0]);
818 glDrawArrays(GL_POINTS, 0, particles_.size());
819
820 glDisableClientState(GL_COLOR_ARRAY);
821 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
822 glEnable(GL_TEXTURE_2D);
823 if(info_.dot_rounded){
824 glDisable( GL_POINT_SMOOTH );
825 }
826 #endif
827 glColor4f(1.0, 1.0, 1.0, 1.0);
828 }
829 private:
830 const entity& obj_;
831 const point_particle_info& info_;
832
833 struct particle {
834 GLshort velocity_x, velocity_y;
835 int pos_x, pos_y;
836 union { unsigned int color; unsigned char rgba[4]; };
837 int ttl;
838 };
839
particle_destroyed(const particle & p)840 static bool particle_destroyed(const particle& p) { return p.ttl <= 0; }
841
842 int particle_generation_;
843 int generation_rate_millis_;
844 int pos_x_, pos_x_rand_, pos_y_, pos_y_rand_;
845 std::vector<particle> particles_;
846
get_value(const std::string & key) const847 variant get_value(const std::string& key) const {
848 return variant();
849 }
850
set_value(const std::string & key,const variant & value)851 void set_value(const std::string& key, const variant& value) {
852 if(key == "generation_rate") {
853 generation_rate_millis_ = value.as_int();
854 } else if (key == "pos_x") {
855 pos_x_ = value.as_int()*1024;
856 } else if (key == "pos_x_rand") {
857 pos_x_rand_ = value.as_int()*1024;
858 } else if (key == "pos_y") {
859 pos_y_ = value.as_int()*1024;
860 } else if (key == "pos_y_rand") {
861 pos_y_rand_ = value.as_int()*1024;
862 }
863 }
864 };
865
866 class point_particle_system_factory : public particle_system_factory
867 {
868 public:
point_particle_system_factory(variant node)869 explicit point_particle_system_factory(variant node)
870 : info_(node)
871 {}
872
create(const entity & e) const873 particle_system_ptr create(const entity& e) const {
874 return particle_system_ptr(new point_particle_system(e, info_));
875 }
876
877 private:
878 point_particle_info info_;
879 };
880
881 }
882
create_factory(variant node)883 const_particle_system_factory_ptr particle_system_factory::create_factory(variant node)
884 {
885 const std::string& type = node["type"].as_string();
886 if(type == "simple") {
887 return const_particle_system_factory_ptr(new simple_particle_system_factory(node));
888 } else if (type == "weather") {
889 return const_particle_system_factory_ptr(new weather_particle_system_factory(node));
890 } else if (type == "water") {
891 return const_particle_system_factory_ptr(new water_particle_system_factory(node));
892 } else if(type == "point") {
893 return const_particle_system_factory_ptr(new point_particle_system_factory(node));
894 }
895
896 ASSERT_LOG(false, "Unrecognized particle system type: " << node["type"].as_string());
897 }
898
~particle_system_factory()899 particle_system_factory::~particle_system_factory()
900 {
901 }
902
~particle_system()903 particle_system::~particle_system()
904 {
905 }
906