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