textpsys

annotate src/psys.cc @ 3:b1c8d2784c72

made the timer internal to the effect, fx_draw doesn't take a time value any more
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 20 Aug 2015 06:40:23 +0300
parents a4ffd9e6984c
children
rev   line source
nuclear@0 1 #include <stdio.h>
nuclear@0 2 #include <stdlib.h>
nuclear@0 3 #include <vector>
nuclear@0 4 #include <algorithm>
nuclear@0 5 #include "opengl.h"
nuclear@0 6 #include "psys.h"
nuclear@0 7
nuclear@0 8 #define MAX_SPAWNMAP_SAMPLES 2048
nuclear@0 9
nuclear@0 10 static double frand();
nuclear@0 11 static float rndval(float x, float range);
nuclear@0 12 static Particle *palloc();
nuclear@0 13 static void pfree(Particle *p);
nuclear@0 14 static void pfreelist(Particle *p);
nuclear@0 15
nuclear@0 16 void psys_default(PSysParam *pp)
nuclear@0 17 {
nuclear@0 18 // default parameters
nuclear@0 19 pp->spawn_rate = 10.0;
nuclear@0 20 pp->spawn_range = 0.0;
nuclear@0 21 pp->life = 1.0;
nuclear@0 22 pp->life_range = 0.0;
nuclear@0 23 pp->size = 1.0;
nuclear@0 24 pp->size_range = 0.0;
nuclear@0 25 pp->spawn_map = 0;
nuclear@0 26 pp->spawn_map_speed = 0.0;
nuclear@0 27
nuclear@0 28 pp->gravity = Vector3(0, -9.2, 0);
nuclear@0 29
nuclear@0 30 pp->pimg = 0;
nuclear@0 31 pp->pcolor_start = pp->pcolor_mid = pp->pcolor_end = Vector3(1, 1, 1);
nuclear@0 32 pp->palpha_start = 1.0;
nuclear@0 33 pp->palpha_mid = 0.5;
nuclear@0 34 pp->palpha_end = 0.0;
nuclear@0 35 pp->pscale_start = pp->pscale_mid = pp->pscale_end = 1.0;
nuclear@0 36 }
nuclear@0 37
nuclear@0 38 ParticleSystem::ParticleSystem()
nuclear@0 39 {
nuclear@0 40 active = true;
nuclear@0 41 active_time = 0.0f;
nuclear@0 42 spawn_pending = 0.0f;
nuclear@0 43 plist = 0;
nuclear@0 44 pcount = 0;
nuclear@0 45 smcache = 0;
nuclear@0 46
nuclear@0 47 expl = false;
nuclear@0 48 expl_force = expl_dur = 0.0f;
nuclear@0 49 expl_life = 0.0f;
nuclear@0 50
nuclear@0 51 psys_default(&pp);
nuclear@0 52 }
nuclear@0 53
nuclear@0 54 ParticleSystem::~ParticleSystem()
nuclear@0 55 {
nuclear@0 56 pfreelist(plist);
nuclear@0 57 delete [] smcache;
nuclear@0 58 }
nuclear@0 59
nuclear@2 60 void ParticleSystem::reset()
nuclear@2 61 {
nuclear@2 62 pfreelist(plist);
nuclear@2 63 delete [] smcache;
nuclear@2 64 smcache = 0;
nuclear@2 65
nuclear@2 66 active = true;
nuclear@2 67 active_time = 0.0f;
nuclear@2 68 spawn_pending = 0.0f;
nuclear@2 69
nuclear@2 70 psys_default(&pp);
nuclear@2 71 }
nuclear@2 72
nuclear@0 73 void ParticleSystem::explode(const Vector3 &c, float force, float dur, float life)
nuclear@0 74 {
nuclear@2 75 if(expl) return;
nuclear@2 76
nuclear@0 77 expl_dur = dur;
nuclear@0 78 expl_force = force;
nuclear@0 79 expl_cent = c;
nuclear@0 80 expl_life = life;
nuclear@0 81 expl = true;
nuclear@0 82 }
nuclear@0 83
nuclear@0 84 bool ParticleSystem::alive() const
nuclear@0 85 {
nuclear@0 86 return active || pcount > 0;
nuclear@0 87 }
nuclear@0 88
nuclear@0 89 void ParticleSystem::update(float dt)
nuclear@0 90 {
nuclear@0 91 if(pp.spawn_map && !smcache) {
nuclear@0 92 gen_spawnmap(MAX_SPAWNMAP_SAMPLES);
nuclear@0 93 }
nuclear@0 94
nuclear@0 95 if(active) {
nuclear@0 96 active_time += dt;
nuclear@0 97 }
nuclear@0 98
nuclear@0 99 if(expl) {
nuclear@0 100 expl = false;
nuclear@0 101 //active = false;
nuclear@0 102
nuclear@0 103 Vector3 cent = expl_cent + pos;
nuclear@0 104
nuclear@0 105 Particle *p = plist;
nuclear@0 106 while(p) {
nuclear@0 107 p->max_life = expl_dur;
nuclear@0 108 Vector3 dir = p->pos - cent;
nuclear@0 109 p->vel += (normalize(dir + Vector3((frand() - 0.5) * 0.5, frand() - 0.5, (frand() - 0.5) * 0.5))) * expl_force;
nuclear@0 110 p = p->next;
nuclear@0 111 }
nuclear@0 112 }
nuclear@0 113
nuclear@0 114 if(expl_life > 0.0) {
nuclear@0 115 expl_life -= dt;
nuclear@0 116 if(expl_life <= 0.0) {
nuclear@0 117 expl_life = 0.0;
nuclear@0 118 active = false;
nuclear@0 119 }
nuclear@0 120 }
nuclear@0 121
nuclear@0 122 // update active particles
nuclear@0 123 Particle *p = plist;
nuclear@0 124 while(p) {
nuclear@0 125 p->life += dt;
nuclear@0 126 if(p->life < p->max_life) {
nuclear@0 127 float t = p->life / p->max_life;
nuclear@0 128
nuclear@0 129 p->pos = p->pos + p->vel * dt;
nuclear@0 130 p->vel = p->vel + pp.gravity * dt;
nuclear@0 131
nuclear@0 132 if(t < 0.5) {
nuclear@0 133 t *= 2.0;
nuclear@0 134 p->color = lerp(pp.pcolor_start, pp.pcolor_mid, t);
nuclear@0 135 p->alpha = lerp(pp.palpha_start, pp.palpha_mid, t);
nuclear@0 136 p->scale = lerp(pp.pscale_start, pp.pscale_mid, t);
nuclear@0 137 } else {
nuclear@0 138 t = (t - 0.5) * 2.0;
nuclear@0 139 p->color = lerp(pp.pcolor_mid, pp.pcolor_end, t);
nuclear@0 140 p->alpha = lerp(pp.palpha_mid, pp.palpha_end, t);
nuclear@0 141 p->scale = lerp(pp.pscale_mid, pp.pscale_end, t);
nuclear@0 142 }
nuclear@0 143
nuclear@0 144 } else {
nuclear@0 145 p->life = -1.0;
nuclear@0 146 }
nuclear@0 147 p = p->next;
nuclear@0 148 }
nuclear@0 149
nuclear@0 150 // remove dead particles
nuclear@0 151 Particle dummy;
nuclear@0 152 dummy.next = plist;
nuclear@0 153 p = &dummy;
nuclear@0 154 while(p->next) {
nuclear@0 155 if(p->next->life < 0.0) {
nuclear@0 156 Particle *tmp = p->next;
nuclear@0 157 p->next = tmp->next;
nuclear@0 158 pfree(tmp);
nuclear@0 159 --pcount;
nuclear@0 160 } else {
nuclear@0 161 p = p->next;
nuclear@0 162 }
nuclear@0 163 }
nuclear@0 164 plist = dummy.next;
nuclear@0 165
nuclear@0 166 float spawn_rate = pp.spawn_rate;
nuclear@0 167 if(pp.spawn_map && pp.spawn_map_speed > 0.0) {
nuclear@0 168 float s = active_time * pp.spawn_map_speed;
nuclear@0 169 if(s > 1.0) s = 1.0;
nuclear@0 170 spawn_rate *= s;
nuclear@0 171 }
nuclear@0 172
nuclear@0 173 // spawn particles as needed
nuclear@0 174 if(active) {
nuclear@0 175 spawn_pending += spawn_rate * dt;
nuclear@0 176
nuclear@0 177 while(spawn_pending >= 1.0f) {
nuclear@0 178 spawn_pending -= 1.0f;
nuclear@0 179 spawn_particle();
nuclear@0 180 }
nuclear@0 181 }
nuclear@0 182 }
nuclear@0 183
nuclear@0 184 void ParticleSystem::draw() const
nuclear@0 185 {
nuclear@0 186 int cur_sdr = 0;
nuclear@0 187 glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
nuclear@0 188 if(cur_sdr) {
nuclear@0 189 glUseProgram(0);
nuclear@0 190 }
nuclear@0 191
nuclear@0 192 glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
nuclear@0 193 glDisable(GL_LIGHTING);
nuclear@0 194 glEnable(GL_BLEND);
nuclear@0 195 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
nuclear@0 196
nuclear@0 197 if(pp.pimg) {
nuclear@0 198 if(!pp.pimg->texture) {
nuclear@0 199 pp.pimg->gen_texture();
nuclear@0 200 }
nuclear@0 201 glEnable(GL_TEXTURE_2D);
nuclear@0 202 glBindTexture(GL_TEXTURE_2D, pp.pimg->texture);
nuclear@0 203 }
nuclear@0 204
nuclear@0 205 glBegin(GL_QUADS);
nuclear@0 206 Particle *p = plist;
nuclear@0 207 while(p) {
nuclear@0 208 float hsz = p->size * p->scale * 0.5;
nuclear@0 209 glColor4f(p->color.x, p->color.y, p->color.z, p->alpha);
nuclear@0 210 glTexCoord2f(0, 0); glVertex3f(p->pos.x - hsz, p->pos.y - hsz, p->pos.z);
nuclear@0 211 glTexCoord2f(1, 0); glVertex3f(p->pos.x + hsz, p->pos.y - hsz, p->pos.z);
nuclear@0 212 glTexCoord2f(1, 1); glVertex3f(p->pos.x + hsz, p->pos.y + hsz, p->pos.z);
nuclear@0 213 glTexCoord2f(0, 1); glVertex3f(p->pos.x - hsz, p->pos.y + hsz, p->pos.z);
nuclear@0 214 p = p->next;
nuclear@0 215 }
nuclear@0 216 glEnd();
nuclear@0 217
nuclear@0 218 glPopAttrib();
nuclear@0 219
nuclear@0 220 if(cur_sdr) {
nuclear@0 221 glUseProgram(cur_sdr);
nuclear@0 222 }
nuclear@0 223 }
nuclear@0 224
nuclear@0 225 void ParticleSystem::gen_spawnmap(int count)
nuclear@0 226 {
nuclear@0 227 Image *img = pp.spawn_map;
nuclear@0 228 if(!img) return;
nuclear@0 229
nuclear@0 230 delete [] smcache;
nuclear@0 231 smcache = new Vector3[count];
nuclear@0 232
nuclear@0 233 float umax = (float)img->width;
nuclear@0 234 float vmax = (float)img->height;
nuclear@0 235 float aspect = umax / vmax;
nuclear@0 236
nuclear@0 237 // first generate a bunch of random samples by rejection sampling
nuclear@0 238 printf("generating %d random spawnmap samples\n", count);
nuclear@0 239 for(int i=0; i<count; i++) {
nuclear@0 240 float u, v;
nuclear@0 241 unsigned char val, ord;
nuclear@0 242
nuclear@0 243 do {
nuclear@0 244 u = (double)rand() / (double)RAND_MAX;
nuclear@0 245 v = (double)rand() / (double)RAND_MAX;
nuclear@0 246
nuclear@0 247 int x = (int)(u * umax);
nuclear@0 248 int y = (int)(v * vmax);
nuclear@0 249
nuclear@0 250 unsigned char *pptr = img->pixels + (y * img->width + x) * 3;
nuclear@0 251 val = pptr[0];
nuclear@0 252 ord = pptr[1];
nuclear@0 253 } while(val < 192);
nuclear@0 254
nuclear@0 255 smcache[i] = Vector3(u * 2.0 - 1.0, (1.0 - v * 2.0) / aspect, ord / 255.0);
nuclear@0 256 }
nuclear@0 257
nuclear@0 258 // then order by z
nuclear@0 259 std::sort(smcache, smcache + count,
nuclear@0 260 [](const Vector3 &a, const Vector3 &b) { return a.z < b.z; });
nuclear@0 261
nuclear@0 262 // precalculate the bounds of each slot
nuclear@0 263 smcache_max[0] = 0;
nuclear@0 264 for(int i=1; i<255; i++) {
nuclear@0 265 float maxval = (float)i / 255.0;
nuclear@0 266
nuclear@0 267 int idx = smcache_max[i - 1];
nuclear@0 268 while(++idx < count && smcache[idx].z < maxval);
nuclear@0 269 smcache_max[i] = idx;
nuclear@0 270 }
nuclear@0 271 smcache_max[255] = count;
nuclear@0 272 }
nuclear@0 273
nuclear@0 274 static double frand()
nuclear@0 275 {
nuclear@0 276 return (double)rand() / (double)RAND_MAX;
nuclear@0 277 }
nuclear@0 278
nuclear@0 279 static float rndval(float x, float range)
nuclear@0 280 {
nuclear@0 281 if(fabs(range) < 1e-6) {
nuclear@0 282 return x;
nuclear@0 283 }
nuclear@0 284 return x + (frand() * range - range * 0.5);
nuclear@0 285 }
nuclear@0 286
nuclear@0 287 void ParticleSystem::spawn_particle()
nuclear@0 288 {
nuclear@0 289 Particle *p = palloc();
nuclear@0 290 p->pos = Vector3(rndval(pos.x, pp.spawn_range),
nuclear@0 291 rndval(pos.y, pp.spawn_range),
nuclear@0 292 rndval(pos.z, pp.spawn_range));
nuclear@0 293 p->vel = Vector3(0, 0, 0);
nuclear@0 294 p->color = pp.pcolor_start;
nuclear@0 295 p->alpha = pp.palpha_start;
nuclear@0 296 p->life = 0.0;
nuclear@0 297 p->max_life = rndval(pp.life, pp.life_range);
nuclear@0 298 p->size = rndval(pp.size, pp.size_range);
nuclear@0 299 p->scale = pp.pscale_start;
nuclear@0 300
nuclear@0 301 if(pp.spawn_map) {
nuclear@0 302 float maxz = pp.spawn_map_speed > 0.0 ? active_time * pp.spawn_map_speed : 1.0;
nuclear@0 303 int max_idx = (int)(maxz * 255.0);
nuclear@0 304 if(max_idx > 255) max_idx = 255;
nuclear@0 305 if(max_idx < 1) max_idx = 1;
nuclear@0 306
nuclear@0 307 int idx = rand() % smcache_max[max_idx];
nuclear@0 308
nuclear@0 309 p->pos.x += smcache[idx].x;
nuclear@0 310 p->pos.y += smcache[idx].y;
nuclear@0 311 }
nuclear@0 312
nuclear@0 313 p->next = plist;
nuclear@0 314 plist = p;
nuclear@0 315 ++pcount;
nuclear@0 316 }
nuclear@0 317
nuclear@0 318 // particle allocator
nuclear@0 319 #define MAX_POOL_SIZE 8192
nuclear@0 320 static Particle *ppool;
nuclear@0 321 static int ppool_size;
nuclear@0 322
nuclear@0 323 static Particle *palloc()
nuclear@0 324 {
nuclear@0 325 if(ppool) {
nuclear@0 326 Particle *p = ppool;
nuclear@0 327 ppool = ppool->next;
nuclear@0 328 --ppool_size;
nuclear@0 329 return p;
nuclear@0 330 }
nuclear@0 331 return new Particle;
nuclear@0 332 }
nuclear@0 333
nuclear@0 334 static void pfree(Particle *p)
nuclear@0 335 {
nuclear@0 336 if(!p) return;
nuclear@0 337
nuclear@0 338 if(ppool_size < MAX_POOL_SIZE) {
nuclear@0 339 p->next = ppool;
nuclear@0 340 ppool = p;
nuclear@0 341 ++ppool_size;
nuclear@0 342 } else {
nuclear@0 343 delete p;
nuclear@0 344 }
nuclear@0 345 }
nuclear@0 346
nuclear@0 347 static void pfreelist(Particle *p)
nuclear@0 348 {
nuclear@0 349 if(!p) return;
nuclear@0 350
nuclear@0 351 Particle *it = p;
nuclear@0 352 int new_pool_size = ppool_size;
nuclear@0 353
nuclear@0 354 while(it->next && new_pool_size < MAX_POOL_SIZE) {
nuclear@0 355 it = it->next;
nuclear@0 356 ++new_pool_size;
nuclear@0 357 }
nuclear@0 358
nuclear@0 359 Particle *last = it;
nuclear@0 360 it = it->next;
nuclear@0 361
nuclear@0 362 // add the first lot to the pool
nuclear@0 363 last->next = ppool;
nuclear@0 364 ppool = p;
nuclear@0 365 ppool_size = new_pool_size;
nuclear@0 366
nuclear@0 367 // delete the rest;
nuclear@0 368 while(it) {
nuclear@0 369 p = it;
nuclear@0 370 it = it->next;
nuclear@0 371 delete p;
nuclear@0 372 }
nuclear@0 373 }