textpsys
diff src/psys.cc @ 0:a4ffd9e6984c
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 19 Aug 2015 09:13:48 +0300 |
parents | |
children | 4b1360a5d54d |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/psys.cc Wed Aug 19 09:13:48 2015 +0300 1.3 @@ -0,0 +1,358 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <vector> 1.7 +#include <algorithm> 1.8 +#include "opengl.h" 1.9 +#include "psys.h" 1.10 + 1.11 +#define MAX_SPAWNMAP_SAMPLES 2048 1.12 + 1.13 +static double frand(); 1.14 +static float rndval(float x, float range); 1.15 +static Particle *palloc(); 1.16 +static void pfree(Particle *p); 1.17 +static void pfreelist(Particle *p); 1.18 + 1.19 +void psys_default(PSysParam *pp) 1.20 +{ 1.21 + // default parameters 1.22 + pp->spawn_rate = 10.0; 1.23 + pp->spawn_range = 0.0; 1.24 + pp->life = 1.0; 1.25 + pp->life_range = 0.0; 1.26 + pp->size = 1.0; 1.27 + pp->size_range = 0.0; 1.28 + pp->spawn_map = 0; 1.29 + pp->spawn_map_speed = 0.0; 1.30 + 1.31 + pp->gravity = Vector3(0, -9.2, 0); 1.32 + 1.33 + pp->pimg = 0; 1.34 + pp->pcolor_start = pp->pcolor_mid = pp->pcolor_end = Vector3(1, 1, 1); 1.35 + pp->palpha_start = 1.0; 1.36 + pp->palpha_mid = 0.5; 1.37 + pp->palpha_end = 0.0; 1.38 + pp->pscale_start = pp->pscale_mid = pp->pscale_end = 1.0; 1.39 +} 1.40 + 1.41 +ParticleSystem::ParticleSystem() 1.42 +{ 1.43 + active = true; 1.44 + active_time = 0.0f; 1.45 + spawn_pending = 0.0f; 1.46 + plist = 0; 1.47 + pcount = 0; 1.48 + smcache = 0; 1.49 + 1.50 + expl = false; 1.51 + expl_force = expl_dur = 0.0f; 1.52 + expl_life = 0.0f; 1.53 + 1.54 + psys_default(&pp); 1.55 +} 1.56 + 1.57 +ParticleSystem::~ParticleSystem() 1.58 +{ 1.59 + pfreelist(plist); 1.60 + delete [] smcache; 1.61 +} 1.62 + 1.63 +void ParticleSystem::explode(const Vector3 &c, float force, float dur, float life) 1.64 +{ 1.65 + expl_dur = dur; 1.66 + expl_force = force; 1.67 + expl_cent = c; 1.68 + expl_life = life; 1.69 + expl = true; 1.70 +} 1.71 + 1.72 +bool ParticleSystem::alive() const 1.73 +{ 1.74 + return active || pcount > 0; 1.75 +} 1.76 + 1.77 +void ParticleSystem::update(float dt) 1.78 +{ 1.79 + if(pp.spawn_map && !smcache) { 1.80 + gen_spawnmap(MAX_SPAWNMAP_SAMPLES); 1.81 + } 1.82 + 1.83 + if(active) { 1.84 + active_time += dt; 1.85 + } 1.86 + 1.87 + if(expl) { 1.88 + expl = false; 1.89 + //active = false; 1.90 + 1.91 + Vector3 cent = expl_cent + pos; 1.92 + 1.93 + Particle *p = plist; 1.94 + while(p) { 1.95 + p->max_life = expl_dur; 1.96 + Vector3 dir = p->pos - cent; 1.97 + p->vel += (normalize(dir + Vector3((frand() - 0.5) * 0.5, frand() - 0.5, (frand() - 0.5) * 0.5))) * expl_force; 1.98 + p = p->next; 1.99 + } 1.100 + } 1.101 + 1.102 + if(expl_life > 0.0) { 1.103 + expl_life -= dt; 1.104 + if(expl_life <= 0.0) { 1.105 + expl_life = 0.0; 1.106 + active = false; 1.107 + } 1.108 + } 1.109 + 1.110 + // update active particles 1.111 + Particle *p = plist; 1.112 + while(p) { 1.113 + p->life += dt; 1.114 + if(p->life < p->max_life) { 1.115 + float t = p->life / p->max_life; 1.116 + 1.117 + p->pos = p->pos + p->vel * dt; 1.118 + p->vel = p->vel + pp.gravity * dt; 1.119 + 1.120 + if(t < 0.5) { 1.121 + t *= 2.0; 1.122 + p->color = lerp(pp.pcolor_start, pp.pcolor_mid, t); 1.123 + p->alpha = lerp(pp.palpha_start, pp.palpha_mid, t); 1.124 + p->scale = lerp(pp.pscale_start, pp.pscale_mid, t); 1.125 + } else { 1.126 + t = (t - 0.5) * 2.0; 1.127 + p->color = lerp(pp.pcolor_mid, pp.pcolor_end, t); 1.128 + p->alpha = lerp(pp.palpha_mid, pp.palpha_end, t); 1.129 + p->scale = lerp(pp.pscale_mid, pp.pscale_end, t); 1.130 + } 1.131 + 1.132 + } else { 1.133 + p->life = -1.0; 1.134 + } 1.135 + p = p->next; 1.136 + } 1.137 + 1.138 + // remove dead particles 1.139 + Particle dummy; 1.140 + dummy.next = plist; 1.141 + p = &dummy; 1.142 + while(p->next) { 1.143 + if(p->next->life < 0.0) { 1.144 + Particle *tmp = p->next; 1.145 + p->next = tmp->next; 1.146 + pfree(tmp); 1.147 + --pcount; 1.148 + } else { 1.149 + p = p->next; 1.150 + } 1.151 + } 1.152 + plist = dummy.next; 1.153 + 1.154 + float spawn_rate = pp.spawn_rate; 1.155 + if(pp.spawn_map && pp.spawn_map_speed > 0.0) { 1.156 + float s = active_time * pp.spawn_map_speed; 1.157 + if(s > 1.0) s = 1.0; 1.158 + spawn_rate *= s; 1.159 + } 1.160 + 1.161 + // spawn particles as needed 1.162 + if(active) { 1.163 + spawn_pending += spawn_rate * dt; 1.164 + 1.165 + while(spawn_pending >= 1.0f) { 1.166 + spawn_pending -= 1.0f; 1.167 + spawn_particle(); 1.168 + } 1.169 + } 1.170 +} 1.171 + 1.172 +void ParticleSystem::draw() const 1.173 +{ 1.174 + int cur_sdr = 0; 1.175 + glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr); 1.176 + if(cur_sdr) { 1.177 + glUseProgram(0); 1.178 + } 1.179 + 1.180 + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT); 1.181 + glDisable(GL_LIGHTING); 1.182 + glEnable(GL_BLEND); 1.183 + glBlendFunc(GL_SRC_ALPHA, GL_ONE); 1.184 + 1.185 + if(pp.pimg) { 1.186 + if(!pp.pimg->texture) { 1.187 + pp.pimg->gen_texture(); 1.188 + } 1.189 + glEnable(GL_TEXTURE_2D); 1.190 + glBindTexture(GL_TEXTURE_2D, pp.pimg->texture); 1.191 + } 1.192 + 1.193 + glBegin(GL_QUADS); 1.194 + Particle *p = plist; 1.195 + while(p) { 1.196 + float hsz = p->size * p->scale * 0.5; 1.197 + glColor4f(p->color.x, p->color.y, p->color.z, p->alpha); 1.198 + glTexCoord2f(0, 0); glVertex3f(p->pos.x - hsz, p->pos.y - hsz, p->pos.z); 1.199 + glTexCoord2f(1, 0); glVertex3f(p->pos.x + hsz, p->pos.y - hsz, p->pos.z); 1.200 + glTexCoord2f(1, 1); glVertex3f(p->pos.x + hsz, p->pos.y + hsz, p->pos.z); 1.201 + glTexCoord2f(0, 1); glVertex3f(p->pos.x - hsz, p->pos.y + hsz, p->pos.z); 1.202 + p = p->next; 1.203 + } 1.204 + glEnd(); 1.205 + 1.206 + glPopAttrib(); 1.207 + 1.208 + if(cur_sdr) { 1.209 + glUseProgram(cur_sdr); 1.210 + } 1.211 +} 1.212 + 1.213 +void ParticleSystem::gen_spawnmap(int count) 1.214 +{ 1.215 + Image *img = pp.spawn_map; 1.216 + if(!img) return; 1.217 + 1.218 + delete [] smcache; 1.219 + smcache = new Vector3[count]; 1.220 + 1.221 + float umax = (float)img->width; 1.222 + float vmax = (float)img->height; 1.223 + float aspect = umax / vmax; 1.224 + 1.225 + // first generate a bunch of random samples by rejection sampling 1.226 + printf("generating %d random spawnmap samples\n", count); 1.227 + for(int i=0; i<count; i++) { 1.228 + float u, v; 1.229 + unsigned char val, ord; 1.230 + 1.231 + do { 1.232 + u = (double)rand() / (double)RAND_MAX; 1.233 + v = (double)rand() / (double)RAND_MAX; 1.234 + 1.235 + int x = (int)(u * umax); 1.236 + int y = (int)(v * vmax); 1.237 + 1.238 + unsigned char *pptr = img->pixels + (y * img->width + x) * 3; 1.239 + val = pptr[0]; 1.240 + ord = pptr[1]; 1.241 + } while(val < 192); 1.242 + 1.243 + smcache[i] = Vector3(u * 2.0 - 1.0, (1.0 - v * 2.0) / aspect, ord / 255.0); 1.244 + } 1.245 + 1.246 + // then order by z 1.247 + std::sort(smcache, smcache + count, 1.248 + [](const Vector3 &a, const Vector3 &b) { return a.z < b.z; }); 1.249 + 1.250 + // precalculate the bounds of each slot 1.251 + smcache_max[0] = 0; 1.252 + for(int i=1; i<255; i++) { 1.253 + float maxval = (float)i / 255.0; 1.254 + 1.255 + int idx = smcache_max[i - 1]; 1.256 + while(++idx < count && smcache[idx].z < maxval); 1.257 + smcache_max[i] = idx; 1.258 + } 1.259 + smcache_max[255] = count; 1.260 +} 1.261 + 1.262 +static double frand() 1.263 +{ 1.264 + return (double)rand() / (double)RAND_MAX; 1.265 +} 1.266 + 1.267 +static float rndval(float x, float range) 1.268 +{ 1.269 + if(fabs(range) < 1e-6) { 1.270 + return x; 1.271 + } 1.272 + return x + (frand() * range - range * 0.5); 1.273 +} 1.274 + 1.275 +void ParticleSystem::spawn_particle() 1.276 +{ 1.277 + Particle *p = palloc(); 1.278 + p->pos = Vector3(rndval(pos.x, pp.spawn_range), 1.279 + rndval(pos.y, pp.spawn_range), 1.280 + rndval(pos.z, pp.spawn_range)); 1.281 + p->vel = Vector3(0, 0, 0); 1.282 + p->color = pp.pcolor_start; 1.283 + p->alpha = pp.palpha_start; 1.284 + p->life = 0.0; 1.285 + p->max_life = rndval(pp.life, pp.life_range); 1.286 + p->size = rndval(pp.size, pp.size_range); 1.287 + p->scale = pp.pscale_start; 1.288 + 1.289 + if(pp.spawn_map) { 1.290 + float maxz = pp.spawn_map_speed > 0.0 ? active_time * pp.spawn_map_speed : 1.0; 1.291 + int max_idx = (int)(maxz * 255.0); 1.292 + if(max_idx > 255) max_idx = 255; 1.293 + if(max_idx < 1) max_idx = 1; 1.294 + 1.295 + int idx = rand() % smcache_max[max_idx]; 1.296 + 1.297 + p->pos.x += smcache[idx].x; 1.298 + p->pos.y += smcache[idx].y; 1.299 + } 1.300 + 1.301 + p->next = plist; 1.302 + plist = p; 1.303 + ++pcount; 1.304 +} 1.305 + 1.306 +// particle allocator 1.307 +#define MAX_POOL_SIZE 8192 1.308 +static Particle *ppool; 1.309 +static int ppool_size; 1.310 + 1.311 +static Particle *palloc() 1.312 +{ 1.313 + if(ppool) { 1.314 + Particle *p = ppool; 1.315 + ppool = ppool->next; 1.316 + --ppool_size; 1.317 + return p; 1.318 + } 1.319 + return new Particle; 1.320 +} 1.321 + 1.322 +static void pfree(Particle *p) 1.323 +{ 1.324 + if(!p) return; 1.325 + 1.326 + if(ppool_size < MAX_POOL_SIZE) { 1.327 + p->next = ppool; 1.328 + ppool = p; 1.329 + ++ppool_size; 1.330 + } else { 1.331 + delete p; 1.332 + } 1.333 +} 1.334 + 1.335 +static void pfreelist(Particle *p) 1.336 +{ 1.337 + if(!p) return; 1.338 + 1.339 + Particle *it = p; 1.340 + int new_pool_size = ppool_size; 1.341 + 1.342 + while(it->next && new_pool_size < MAX_POOL_SIZE) { 1.343 + it = it->next; 1.344 + ++new_pool_size; 1.345 + } 1.346 + 1.347 + Particle *last = it; 1.348 + it = it->next; 1.349 + 1.350 + // add the first lot to the pool 1.351 + last->next = ppool; 1.352 + ppool = p; 1.353 + ppool_size = new_pool_size; 1.354 + 1.355 + // delete the rest; 1.356 + while(it) { 1.357 + p = it; 1.358 + it = it->next; 1.359 + delete p; 1.360 + } 1.361 +}