vrshoot
diff src/psyspp.cc @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/psyspp.cc Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,460 @@ 1.4 +#include <limits.h> 1.5 +#include "psyspp.h" 1.6 +#include "sdrman.h" 1.7 +#include "logger.h" 1.8 +#include "mesh.h" // just for the attrib enums 1.9 +#include "unistate.h" 1.10 +#include "datapath.h" 1.11 +#include "texman.h" 1.12 +#include "texgen.h" 1.13 +#include "image.h" 1.14 + 1.15 +static void pdraw_start(const psys_emitter *em, void *cls); 1.16 +static void pdraw(const psys_emitter *em, const psys_particle *part, void *cls); 1.17 +static void pdraw_end(const psys_emitter *em, void *cls); 1.18 + 1.19 +static unsigned int psys_load_texture(const char *fname, void *cls); 1.20 + 1.21 +ParticleSystemAttributes::ParticleSystemAttributes() 1.22 +{ 1.23 + tex = 0; 1.24 + own_psattr = true; 1.25 + psattr = new psys_attributes; 1.26 + psys_init_attr(psattr); 1.27 +} 1.28 + 1.29 +ParticleSystemAttributes::ParticleSystemAttributes(psys_attributes *psattr) 1.30 +{ 1.31 + tex = 0; 1.32 + own_psattr = false; 1.33 + this->psattr = psattr; 1.34 +} 1.35 + 1.36 +ParticleSystemAttributes::~ParticleSystemAttributes() 1.37 +{ 1.38 + if(own_psattr) { 1.39 + psys_destroy_attr(psattr); 1.40 + delete psattr; 1.41 + } 1.42 +} 1.43 + 1.44 +ParticleSystemAttributes::ParticleSystemAttributes(const ParticleSystemAttributes &rhs) 1.45 +{ 1.46 + own_psattr = true; 1.47 + tex = rhs.tex; 1.48 + psattr = new psys_attributes; 1.49 + psys_init_attr(psattr); 1.50 + 1.51 + psys_copy_attr(psattr, rhs.psattr); 1.52 +} 1.53 + 1.54 +ParticleSystemAttributes &ParticleSystemAttributes::operator =(const ParticleSystemAttributes &rhs) 1.55 +{ 1.56 + if(&rhs != this) { 1.57 + tex = rhs.tex; 1.58 + psys_copy_attr(psattr, rhs.psattr); 1.59 + } 1.60 + return *this; 1.61 +} 1.62 + 1.63 +bool ParticleSystemAttributes::load(const char *fname) 1.64 +{ 1.65 + psys_texture_loader(psys_load_texture, 0, this); 1.66 + return psys_load_attr(psattr, datafile_path(fname).c_str()) != -1; 1.67 +} 1.68 + 1.69 +bool ParticleSystemAttributes::load(FILE *fp) 1.70 +{ 1.71 + psys_texture_loader(psys_load_texture, 0, this); 1.72 + return psys_load_attr_stream(psattr, fp) != -1; 1.73 +} 1.74 + 1.75 +bool ParticleSystemAttributes::save(const char *fname) const 1.76 +{ 1.77 + return psys_save_attr(psattr, fname) != -1; 1.78 +} 1.79 + 1.80 +bool ParticleSystemAttributes::save(FILE *fp) const 1.81 +{ 1.82 + return psys_save_attr_stream(psattr, fp) != -1; 1.83 +} 1.84 + 1.85 +void ParticleSystemAttributes::set_part_color(const Vector3 &color, float t) 1.86 +{ 1.87 + psys_set_value3(&psattr->part_attr.color, (anm_time_t)(t * 1000.0), v3_cons(color.x, color.y, color.z)); 1.88 +} 1.89 + 1.90 +void ParticleSystemAttributes::set_part_alpha(float alpha, float t) 1.91 +{ 1.92 + psys_set_value(&psattr->part_attr.alpha, (anm_time_t)(t * 1000.0), alpha); 1.93 +} 1.94 + 1.95 +void ParticleSystemAttributes::set_part_scale(float size, float t) 1.96 +{ 1.97 + psys_set_value(&psattr->part_attr.size, (anm_time_t)(t * 1000.0), size); 1.98 +} 1.99 + 1.100 + 1.101 +// emmiter attributes 1.102 +void ParticleSystemAttributes::set_texture(Texture *tex) 1.103 +{ 1.104 + this->tex = tex; 1.105 + psattr->tex = tex->get_id(); 1.106 +} 1.107 + 1.108 +Texture *ParticleSystemAttributes::get_texture() const 1.109 +{ 1.110 + return tex; 1.111 +} 1.112 + 1.113 +void ParticleSystemAttributes::set_spawn_range(const Vector3 &range, long tm) 1.114 +{ 1.115 + psys_set_value3(&psattr->spawn_range, ANM_MSEC2TM(tm), v3_cons(range.x, range.y, range.z)); 1.116 +} 1.117 + 1.118 +void ParticleSystemAttributes::set_spawn_rate(float rate, long tm) 1.119 +{ 1.120 + psys_set_value(&psattr->rate, ANM_MSEC2TM(tm), rate); 1.121 +} 1.122 + 1.123 +float ParticleSystemAttributes::get_spawn_rate(long tm) const 1.124 +{ 1.125 + return psys_get_value(&psattr->rate, ANM_MSEC2TM(tm)); 1.126 +} 1.127 + 1.128 +void ParticleSystemAttributes::set_life(float life, float range, long tm) 1.129 +{ 1.130 + psys_set_anm_rnd(&psattr->life, ANM_MSEC2TM(tm), life, range); 1.131 +} 1.132 + 1.133 +void ParticleSystemAttributes::set_size(float sz, float range, long tm) 1.134 +{ 1.135 + psys_set_anm_rnd(&psattr->size, ANM_MSEC2TM(tm), sz, range); 1.136 +} 1.137 + 1.138 +void ParticleSystemAttributes::set_spawn_dir(const Vector3 &dir, const Vector3 &range, long tm) 1.139 +{ 1.140 + psys_set_anm_rnd3(&psattr->dir, ANM_MSEC2TM(tm), v3_cons(dir.x, dir.y, dir.z), v3_cons(range.x, range.y, range.z)); 1.141 +} 1.142 + 1.143 +void ParticleSystemAttributes::set_gravity(const Vector3 &grav, long tm) 1.144 +{ 1.145 + psys_set_value3(&psattr->grav, ANM_MSEC2TM(tm), v3_cons(grav.x, grav.y, grav.z)); 1.146 +} 1.147 + 1.148 +void ParticleSystemAttributes::set_drag(float drag) 1.149 +{ 1.150 + psattr->drag = drag; 1.151 +} 1.152 + 1.153 +void ParticleSystemAttributes::set_particle_limit(int lim) 1.154 +{ 1.155 + psattr->max_particles = lim; 1.156 +} 1.157 + 1.158 + 1.159 +// ---- ParticleSystem ---- 1.160 + 1.161 +ParticleSystem::ParticleSystem() 1.162 + : attr(&psys.attr) 1.163 +{ 1.164 + psys_init(&psys); 1.165 + psys_draw_func(&psys, pdraw, pdraw_start, pdraw_end, (void*)this); 1.166 + start_time = LONG_MIN; 1.167 + last_upd_time = LONG_MIN; 1.168 +} 1.169 + 1.170 +ParticleSystem::~ParticleSystem() 1.171 +{ 1.172 + psys_destroy(&psys); 1.173 +} 1.174 + 1.175 +void ParticleSystem::set_start_time(long tm) 1.176 +{ 1.177 + start_time = tm; 1.178 +} 1.179 + 1.180 +bool ParticleSystem::is_active() const 1.181 +{ 1.182 + float rate = attr.get_spawn_rate(last_upd_time); 1.183 + return psys.pcount > 0 || last_upd_time == 0;// || rate > 0.0; 1.184 +} 1.185 + 1.186 +ParticleSystemAttributes *ParticleSystem::get_attr() 1.187 +{ 1.188 + return &attr; 1.189 +} 1.190 + 1.191 +const ParticleSystemAttributes *ParticleSystem::get_attr() const 1.192 +{ 1.193 + return &attr; 1.194 +} 1.195 + 1.196 +void ParticleSystem::set_attr(const ParticleSystemAttributes &pattr) 1.197 +{ 1.198 + attr = pattr; 1.199 +} 1.200 + 1.201 +bool ParticleSystem::load(const char *fname) 1.202 +{ 1.203 + psys_texture_loader(psys_load_texture, 0, &attr); 1.204 + return attr.load(fname); 1.205 +} 1.206 + 1.207 +bool ParticleSystem::save(const char *fname) const 1.208 +{ 1.209 + return attr.save(fname); 1.210 +} 1.211 + 1.212 +void ParticleSystem::update(long tm) 1.213 +{ 1.214 + if(start_time > LONG_MIN) { 1.215 + tm -= start_time; 1.216 + } 1.217 + 1.218 + Matrix4x4 xform; 1.219 + get_xform(tm, &xform); 1.220 + 1.221 + Vector3 pos = Vector3(0, 0, 0).transformed(xform); 1.222 + 1.223 + psys_set_pos(&psys, v3_cons(pos.x, pos.y, pos.z), 0); 1.224 + psys_update(&psys, (double)tm / 1000.0); 1.225 + 1.226 + last_upd_time = tm; 1.227 +} 1.228 + 1.229 +void ParticleSystem::draw() const 1.230 +{ 1.231 + psys_draw(&psys); 1.232 +} 1.233 + 1.234 +// ---- particle drawing ---- 1.235 +struct PVertex { 1.236 + Vector4 color; 1.237 + Vector3 pos; 1.238 + Vector2 texcoord; 1.239 +}; 1.240 + 1.241 + 1.242 +#define USE_VBO 1.243 +#define USE_IBO 1.244 + 1.245 + 1.246 +#define MAX_DRAW_PART 256 1.247 +#define MAX_PVERTS (MAX_DRAW_PART * 4) 1.248 +static PVertex *pvarr, *pvptr; 1.249 + 1.250 +// double-buffered vbo set, write on one, while drawing from the other 1.251 + 1.252 +#ifdef USE_VBO 1.253 +static unsigned int vbo[2]; 1.254 +static int cur_buf; // current write vbo 1.255 +#endif 1.256 +static int num_buffered; // number of particles bufferd, will flush when >= MAX_DRAW_PART 1.257 + 1.258 +// ok so the index array is constant, regardless of the particle system 1.259 +// so this is a static index buffer created in init_particle_draw which should 1.260 +// be called once. 1.261 +#define MAX_PVIDX (MAX_DRAW_PART * 6) 1.262 +#ifdef USE_IBO 1.263 +static unsigned int ibo; 1.264 +#endif 1.265 +unsigned int *pvidx; 1.266 + 1.267 +static ShaderProg *psdr; // particle shader 1.268 +static Texture2D *blank_tex; 1.269 + 1.270 +static inline void init_particle_draw() 1.271 +{ 1.272 + static bool done_init; 1.273 + if(done_init) { 1.274 + return; // once 1.275 + } 1.276 + 1.277 + pvidx = new unsigned int[MAX_PVIDX]; 1.278 + unsigned int *ptr = pvidx; 1.279 + 1.280 + static const unsigned int idxoffs[] = { 0, 1, 2, 0, 2, 3 }; 1.281 + 1.282 + for(int i=0; i<MAX_DRAW_PART; i++) { 1.283 + for(int j=0; j<6; j++) { 1.284 + *ptr++ = i * 4 + idxoffs[j]; 1.285 + } 1.286 + } 1.287 + 1.288 +#ifdef USE_IBO 1.289 + glGenBuffers(1, &ibo); 1.290 + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 1.291 + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_PVIDX * sizeof *pvidx, pvidx, GL_STATIC_DRAW); 1.292 + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1.293 + 1.294 + delete [] pvidx; 1.295 +#endif 1.296 + 1.297 +#ifdef USE_VBO 1.298 + // create the stream vertex buffers (double buffering) 1.299 + glGenBuffers(2, vbo); 1.300 + for(int i=0; i<2; i++) { 1.301 + glBindBuffer(GL_ARRAY_BUFFER, vbo[i]); 1.302 + glBufferData(GL_ARRAY_BUFFER, MAX_PVERTS * sizeof(PVertex), 0, GL_STREAM_DRAW); 1.303 + } 1.304 + glBindBuffer(GL_ARRAY_BUFFER, 0); 1.305 +#else 1.306 + pvarr = new PVertex[MAX_PVERTS]; 1.307 + pvptr = pvarr; 1.308 +#endif 1.309 + 1.310 + // load shader program 1.311 + if(!(psdr = get_sdrprog("psdr.v.glsl", "psdr.p.glsl"))) { 1.312 + error_log("failed to load particle shader!\n"); 1.313 + } 1.314 + 1.315 + // create empty texture 1.316 + Image *img = texgen_solid(8, 8, Vector4(1, 1, 1, 1)); 1.317 + blank_tex = new Texture2D; 1.318 + blank_tex->set_image(*img); 1.319 + delete img; 1.320 + 1.321 + done_init = true; 1.322 +} 1.323 + 1.324 +static void pdraw_flush() 1.325 +{ 1.326 +#ifdef USE_VBO 1.327 + // assuming vbo[cur_buf] is bound 1.328 + glUnmapBuffer(GL_ARRAY_BUFFER); 1.329 +#endif 1.330 + 1.331 + // draw from the bound buffer 6 indices per particle 1.332 +#ifdef USE_IBO 1.333 + glDrawElements(GL_TRIANGLES, num_buffered * 6, GL_UNSIGNED_INT, 0); 1.334 +#else 1.335 + glDrawElements(GL_TRIANGLES, num_buffered * 6, GL_UNSIGNED_INT, pvidx); 1.336 +#endif 1.337 + num_buffered = 0; 1.338 + 1.339 +#ifdef USE_VBO 1.340 + // map the next buffer (write buffer) while the previous is drawing 1.341 + cur_buf = (cur_buf + 1) & 1; 1.342 + glBindBuffer(GL_ARRAY_BUFFER, vbo[cur_buf]); 1.343 + pvarr = (PVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); 1.344 +#endif 1.345 + pvptr = pvarr; 1.346 +} 1.347 + 1.348 +static void pdraw_start(const psys_emitter *em, void *cls) 1.349 +{ 1.350 + ParticleSystem *ps = (ParticleSystem*)cls; 1.351 + 1.352 + init_particle_draw(); 1.353 + 1.354 + num_buffered = 0; 1.355 + 1.356 +#ifdef USE_IBO 1.357 + // bind the particle index buffer which is static 1.358 + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 1.359 +#endif 1.360 + 1.361 +#ifdef USE_VBO 1.362 + // map the current write buffer 1.363 + glBindBuffer(GL_ARRAY_BUFFER, vbo[cur_buf]); 1.364 + pvarr = (PVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); 1.365 +#endif 1.366 + pvptr = pvarr; 1.367 + 1.368 + Texture *tex = ps->get_attr()->get_texture(); 1.369 + if(tex) { 1.370 + tex->bind(); 1.371 + } else { 1.372 + blank_tex->bind(); 1.373 + } 1.374 + psdr->bind(); 1.375 + 1.376 + glDisable(GL_DEPTH_TEST); 1.377 + glDisable(GL_CULL_FACE); 1.378 + glEnable(GL_BLEND); 1.379 + glBlendFunc(GL_SRC_ALPHA, GL_ONE); 1.380 + glDepthMask(0); 1.381 + 1.382 + glEnableVertexAttribArray(MESH_ATTR_VERTEX); 1.383 + glEnableVertexAttribArray(MESH_ATTR_COLOR); 1.384 + glEnableVertexAttribArray(MESH_ATTR_TEXCOORD); 1.385 + 1.386 +#ifdef USE_VBO 1.387 + glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)((char*)&pvarr->pos - (char*)pvarr)); 1.388 + glVertexAttribPointer(MESH_ATTR_COLOR, 4, GL_FLOAT, GL_TRUE, sizeof *pvarr, (void*)((char*)&pvarr->color - (char*)pvarr)); 1.389 + glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)((char*)&pvarr->texcoord - (char*)pvarr)); 1.390 +#else 1.391 + glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)&pvarr->pos); 1.392 + glVertexAttribPointer(MESH_ATTR_COLOR, 4, GL_FLOAT, GL_TRUE, sizeof *pvarr, (void*)&pvarr->color); 1.393 + glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)&pvarr->texcoord); 1.394 +#endif 1.395 +} 1.396 + 1.397 +static void pdraw(const psys_emitter *em, const psys_particle *part, void *cls) 1.398 +{ 1.399 + ParticleSystem *ps = (ParticleSystem*)cls; 1.400 + 1.401 + static const Vector3 pv[] = { 1.402 + Vector3(-0.5, -0.5, 0), 1.403 + Vector3(0.5, -0.5, 0), 1.404 + Vector3(0.5, 0.5, 0), 1.405 + Vector3(-0.5, 0.5, 0) 1.406 + }; 1.407 + static const Vector2 tex[] = { 1.408 + Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0) 1.409 + }; 1.410 + Vector4 color = Vector4(part->color.x, part->color.y, part->color.z, part->alpha); 1.411 + 1.412 + for(int i=0; i<4; i++) { 1.413 + pvptr->color = color; 1.414 + pvptr->pos = pv[i] * part->size + part->pos; 1.415 + pvptr->texcoord = tex[i]; 1.416 + pvptr++; 1.417 + } 1.418 + // XXX we don't need billboarding for this game, so don't bother 1.419 + 1.420 + // if we reached the maximum number of buffered particles, draw them 1.421 + if(++num_buffered >= MAX_DRAW_PART) { 1.422 + pdraw_flush(); // this will reset the counter 1.423 + } 1.424 +} 1.425 + 1.426 +static void pdraw_end(const psys_emitter *em, void *cls) 1.427 +{ 1.428 + // if we have leftover particles buffered, draw them before returning 1.429 + if(num_buffered) { 1.430 + pdraw_flush(); 1.431 + } 1.432 + 1.433 + // cleanup 1.434 + glDisableVertexAttribArray(MESH_ATTR_VERTEX); 1.435 + glDisableVertexAttribArray(MESH_ATTR_COLOR); 1.436 + glDisableVertexAttribArray(MESH_ATTR_TEXCOORD); 1.437 + 1.438 +#ifdef USE_VBO 1.439 + glUnmapBuffer(GL_ARRAY_BUFFER); 1.440 + glBindBuffer(GL_ARRAY_BUFFER, 0); 1.441 +#endif 1.442 +#ifdef USE_IBO 1.443 + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1.444 +#endif 1.445 + 1.446 + glDepthMask(1); 1.447 + glDisable(GL_BLEND); 1.448 + 1.449 + glEnable(GL_DEPTH_TEST); 1.450 + glEnable(GL_CULL_FACE); 1.451 +} 1.452 + 1.453 +static unsigned int psys_load_texture(const char *fname, void *cls) 1.454 +{ 1.455 + ParticleSystemAttributes *attr = (ParticleSystemAttributes*)cls; 1.456 + 1.457 + Texture *tex = texset.get(fname); 1.458 + if(tex) { 1.459 + attr->set_texture(tex); 1.460 + return tex->get_id(); 1.461 + } 1.462 + return 0; 1.463 +}