goat3dgfx

annotate src/psyspp.cc @ 15:7d6b667821cf

wrapped everything in the goatgfx namespace
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 30 Nov 2013 20:52:21 +0200
parents 1873dfd13f2d
children dc5918c62a64
rev   line source
nuclear@0 1 #include <limits.h>
nuclear@0 2 #include "psyspp.h"
nuclear@0 3 #include "sdrman.h"
nuclear@0 4 #include "logger.h"
nuclear@0 5 #include "mesh.h" // just for the attrib enums
nuclear@0 6 #include "unistate.h"
nuclear@0 7 #include "datapath.h"
nuclear@0 8 #include "texman.h"
nuclear@0 9 #include "texgen.h"
nuclear@0 10
nuclear@15 11 using namespace goatgfx;
nuclear@15 12
nuclear@0 13 static void pdraw_start(const psys_emitter *em, void *cls);
nuclear@0 14 static void pdraw(const psys_emitter *em, const psys_particle *part, void *cls);
nuclear@0 15 static void pdraw_end(const psys_emitter *em, void *cls);
nuclear@0 16
nuclear@0 17 static unsigned int psys_load_texture(const char *fname, void *cls);
nuclear@0 18
nuclear@0 19 ParticleSystemAttributes::ParticleSystemAttributes()
nuclear@0 20 {
nuclear@0 21 tex = 0;
nuclear@0 22 own_psattr = true;
nuclear@0 23 psattr = new psys_attributes;
nuclear@0 24 psys_init_attr(psattr);
nuclear@0 25 }
nuclear@0 26
nuclear@0 27 ParticleSystemAttributes::ParticleSystemAttributes(psys_attributes *psattr)
nuclear@0 28 {
nuclear@0 29 tex = 0;
nuclear@0 30 own_psattr = false;
nuclear@0 31 this->psattr = psattr;
nuclear@0 32 }
nuclear@0 33
nuclear@0 34 ParticleSystemAttributes::~ParticleSystemAttributes()
nuclear@0 35 {
nuclear@0 36 if(own_psattr) {
nuclear@0 37 psys_destroy_attr(psattr);
nuclear@0 38 delete psattr;
nuclear@0 39 }
nuclear@0 40 }
nuclear@0 41
nuclear@0 42 ParticleSystemAttributes::ParticleSystemAttributes(const ParticleSystemAttributes &rhs)
nuclear@0 43 {
nuclear@0 44 own_psattr = true;
nuclear@0 45 tex = rhs.tex;
nuclear@0 46 psattr = new psys_attributes;
nuclear@0 47 psys_init_attr(psattr);
nuclear@0 48
nuclear@0 49 psys_copy_attr(psattr, rhs.psattr);
nuclear@0 50 }
nuclear@0 51
nuclear@0 52 ParticleSystemAttributes &ParticleSystemAttributes::operator =(const ParticleSystemAttributes &rhs)
nuclear@0 53 {
nuclear@0 54 if(&rhs != this) {
nuclear@0 55 tex = rhs.tex;
nuclear@0 56 psys_copy_attr(psattr, rhs.psattr);
nuclear@0 57 }
nuclear@0 58 return *this;
nuclear@0 59 }
nuclear@0 60
nuclear@0 61 bool ParticleSystemAttributes::load(const char *fname)
nuclear@0 62 {
nuclear@0 63 psys_texture_loader(psys_load_texture, 0, this);
nuclear@15 64 return psys_load_attr(psattr, goatgfx::datafile_path(fname).c_str()) != -1;
nuclear@0 65 }
nuclear@0 66
nuclear@0 67 bool ParticleSystemAttributes::load(FILE *fp)
nuclear@0 68 {
nuclear@0 69 psys_texture_loader(psys_load_texture, 0, this);
nuclear@0 70 return psys_load_attr_stream(psattr, fp) != -1;
nuclear@0 71 }
nuclear@0 72
nuclear@0 73 bool ParticleSystemAttributes::save(const char *fname) const
nuclear@0 74 {
nuclear@0 75 return psys_save_attr(psattr, fname) != -1;
nuclear@0 76 }
nuclear@0 77
nuclear@0 78 bool ParticleSystemAttributes::save(FILE *fp) const
nuclear@0 79 {
nuclear@0 80 return psys_save_attr_stream(psattr, fp) != -1;
nuclear@0 81 }
nuclear@0 82
nuclear@0 83 void ParticleSystemAttributes::set_part_color(const Vector3 &color, float t)
nuclear@0 84 {
nuclear@0 85 psys_set_value3(&psattr->part_attr.color, (anm_time_t)(t * 1000.0), v3_cons(color.x, color.y, color.z));
nuclear@0 86 }
nuclear@0 87
nuclear@0 88 void ParticleSystemAttributes::set_part_alpha(float alpha, float t)
nuclear@0 89 {
nuclear@0 90 psys_set_value(&psattr->part_attr.alpha, (anm_time_t)(t * 1000.0), alpha);
nuclear@0 91 }
nuclear@0 92
nuclear@0 93 void ParticleSystemAttributes::set_part_scale(float size, float t)
nuclear@0 94 {
nuclear@0 95 psys_set_value(&psattr->part_attr.size, (anm_time_t)(t * 1000.0), size);
nuclear@0 96 }
nuclear@0 97
nuclear@0 98
nuclear@0 99 // emmiter attributes
nuclear@0 100 void ParticleSystemAttributes::set_texture(Texture *tex)
nuclear@0 101 {
nuclear@0 102 this->tex = tex;
nuclear@0 103 psattr->tex = tex->get_id();
nuclear@0 104 }
nuclear@0 105
nuclear@0 106 Texture *ParticleSystemAttributes::get_texture() const
nuclear@0 107 {
nuclear@0 108 return tex;
nuclear@0 109 }
nuclear@0 110
nuclear@0 111 void ParticleSystemAttributes::set_spawn_range(const Vector3 &range, long tm)
nuclear@0 112 {
nuclear@0 113 psys_set_value3(&psattr->spawn_range, ANM_MSEC2TM(tm), v3_cons(range.x, range.y, range.z));
nuclear@0 114 }
nuclear@0 115
nuclear@0 116 void ParticleSystemAttributes::set_spawn_rate(float rate, long tm)
nuclear@0 117 {
nuclear@0 118 psys_set_value(&psattr->rate, ANM_MSEC2TM(tm), rate);
nuclear@0 119 }
nuclear@0 120
nuclear@0 121 float ParticleSystemAttributes::get_spawn_rate(long tm) const
nuclear@0 122 {
nuclear@0 123 return psys_get_value(&psattr->rate, ANM_MSEC2TM(tm));
nuclear@0 124 }
nuclear@0 125
nuclear@0 126 void ParticleSystemAttributes::set_life(float life, float range, long tm)
nuclear@0 127 {
nuclear@0 128 psys_set_anm_rnd(&psattr->life, ANM_MSEC2TM(tm), life, range);
nuclear@0 129 }
nuclear@0 130
nuclear@0 131 void ParticleSystemAttributes::set_size(float sz, float range, long tm)
nuclear@0 132 {
nuclear@0 133 psys_set_anm_rnd(&psattr->size, ANM_MSEC2TM(tm), sz, range);
nuclear@0 134 }
nuclear@0 135
nuclear@0 136 void ParticleSystemAttributes::set_spawn_dir(const Vector3 &dir, const Vector3 &range, long tm)
nuclear@0 137 {
nuclear@0 138 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));
nuclear@0 139 }
nuclear@0 140
nuclear@0 141 void ParticleSystemAttributes::set_gravity(const Vector3 &grav, long tm)
nuclear@0 142 {
nuclear@0 143 psys_set_value3(&psattr->grav, ANM_MSEC2TM(tm), v3_cons(grav.x, grav.y, grav.z));
nuclear@0 144 }
nuclear@0 145
nuclear@0 146 void ParticleSystemAttributes::set_drag(float drag)
nuclear@0 147 {
nuclear@0 148 psattr->drag = drag;
nuclear@0 149 }
nuclear@0 150
nuclear@0 151 void ParticleSystemAttributes::set_particle_limit(int lim)
nuclear@0 152 {
nuclear@0 153 psattr->max_particles = lim;
nuclear@0 154 }
nuclear@0 155
nuclear@0 156
nuclear@0 157 // ---- ParticleSystem ----
nuclear@0 158
nuclear@0 159 ParticleSystem::ParticleSystem()
nuclear@0 160 : attr(&psys.attr)
nuclear@0 161 {
nuclear@0 162 psys_init(&psys);
nuclear@0 163 psys_draw_func(&psys, pdraw, pdraw_start, pdraw_end, (void*)this);
nuclear@0 164 start_time = LONG_MIN;
nuclear@0 165 last_upd_time = LONG_MIN;
nuclear@0 166 }
nuclear@0 167
nuclear@0 168 ParticleSystem::~ParticleSystem()
nuclear@0 169 {
nuclear@0 170 psys_destroy(&psys);
nuclear@0 171 }
nuclear@0 172
nuclear@0 173 void ParticleSystem::set_start_time(long tm)
nuclear@0 174 {
nuclear@0 175 start_time = tm;
nuclear@0 176 }
nuclear@0 177
nuclear@0 178 bool ParticleSystem::is_active() const
nuclear@0 179 {
nuclear@0 180 float rate = attr.get_spawn_rate(last_upd_time);
nuclear@0 181 return psys.pcount > 0 || last_upd_time == 0;// || rate > 0.0;
nuclear@0 182 }
nuclear@0 183
nuclear@0 184 ParticleSystemAttributes *ParticleSystem::get_attr()
nuclear@0 185 {
nuclear@0 186 return &attr;
nuclear@0 187 }
nuclear@0 188
nuclear@0 189 const ParticleSystemAttributes *ParticleSystem::get_attr() const
nuclear@0 190 {
nuclear@0 191 return &attr;
nuclear@0 192 }
nuclear@0 193
nuclear@0 194 void ParticleSystem::set_attr(const ParticleSystemAttributes &pattr)
nuclear@0 195 {
nuclear@0 196 attr = pattr;
nuclear@0 197 }
nuclear@0 198
nuclear@0 199 bool ParticleSystem::load(const char *fname)
nuclear@0 200 {
nuclear@0 201 psys_texture_loader(psys_load_texture, 0, &attr);
nuclear@0 202 return attr.load(fname);
nuclear@0 203 }
nuclear@0 204
nuclear@0 205 bool ParticleSystem::save(const char *fname) const
nuclear@0 206 {
nuclear@0 207 return attr.save(fname);
nuclear@0 208 }
nuclear@0 209
nuclear@0 210 void ParticleSystem::update(long tm)
nuclear@0 211 {
nuclear@0 212 if(start_time > LONG_MIN) {
nuclear@0 213 tm -= start_time;
nuclear@0 214 }
nuclear@0 215
nuclear@0 216 Matrix4x4 xform;
nuclear@0 217 get_xform(tm, &xform);
nuclear@0 218
nuclear@0 219 Vector3 pos = Vector3(0, 0, 0).transformed(xform);
nuclear@0 220
nuclear@0 221 psys_set_pos(&psys, v3_cons(pos.x, pos.y, pos.z), 0);
nuclear@0 222 psys_update(&psys, (double)tm / 1000.0);
nuclear@0 223
nuclear@0 224 last_upd_time = tm;
nuclear@0 225 }
nuclear@0 226
nuclear@0 227 void ParticleSystem::draw() const
nuclear@0 228 {
nuclear@0 229 psys_draw(&psys);
nuclear@0 230 }
nuclear@0 231
nuclear@0 232 // ---- particle drawing ----
nuclear@0 233 struct PVertex {
nuclear@0 234 Vector4 color;
nuclear@0 235 Vector3 pos;
nuclear@0 236 Vector2 texcoord;
nuclear@0 237 };
nuclear@0 238
nuclear@0 239
nuclear@0 240 #define USE_VBO
nuclear@0 241 #define USE_IBO
nuclear@0 242
nuclear@0 243
nuclear@0 244 #define MAX_DRAW_PART 256
nuclear@0 245 #define MAX_PVERTS (MAX_DRAW_PART * 4)
nuclear@0 246 static PVertex *pvarr, *pvptr;
nuclear@0 247
nuclear@0 248 // double-buffered vbo set, write on one, while drawing from the other
nuclear@0 249
nuclear@0 250 #ifdef USE_VBO
nuclear@0 251 static unsigned int vbo[2];
nuclear@0 252 static int cur_buf; // current write vbo
nuclear@0 253 #endif
nuclear@0 254 static int num_buffered; // number of particles bufferd, will flush when >= MAX_DRAW_PART
nuclear@0 255
nuclear@0 256 // ok so the index array is constant, regardless of the particle system
nuclear@0 257 // so this is a static index buffer created in init_particle_draw which should
nuclear@0 258 // be called once.
nuclear@0 259 #define MAX_PVIDX (MAX_DRAW_PART * 6)
nuclear@0 260 #ifdef USE_IBO
nuclear@0 261 static unsigned int ibo;
nuclear@0 262 #endif
nuclear@0 263 unsigned int *pvidx;
nuclear@0 264
nuclear@0 265 static ShaderProg *psdr; // particle shader
nuclear@0 266 static Texture2D *blank_tex;
nuclear@0 267
nuclear@0 268 static inline void init_particle_draw()
nuclear@0 269 {
nuclear@0 270 static bool done_init;
nuclear@0 271 if(done_init) {
nuclear@0 272 return; // once
nuclear@0 273 }
nuclear@0 274
nuclear@0 275 pvidx = new unsigned int[MAX_PVIDX];
nuclear@0 276 unsigned int *ptr = pvidx;
nuclear@0 277
nuclear@0 278 static const unsigned int idxoffs[] = { 0, 1, 2, 0, 2, 3 };
nuclear@0 279
nuclear@0 280 for(int i=0; i<MAX_DRAW_PART; i++) {
nuclear@0 281 for(int j=0; j<6; j++) {
nuclear@0 282 *ptr++ = i * 4 + idxoffs[j];
nuclear@0 283 }
nuclear@0 284 }
nuclear@0 285
nuclear@0 286 #ifdef USE_IBO
nuclear@0 287 glGenBuffers(1, &ibo);
nuclear@0 288 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
nuclear@0 289 glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_PVIDX * sizeof *pvidx, pvidx, GL_STATIC_DRAW);
nuclear@0 290 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
nuclear@0 291
nuclear@0 292 delete [] pvidx;
nuclear@0 293 #endif
nuclear@0 294
nuclear@0 295 #ifdef USE_VBO
nuclear@0 296 // create the stream vertex buffers (double buffering)
nuclear@0 297 glGenBuffers(2, vbo);
nuclear@0 298 for(int i=0; i<2; i++) {
nuclear@0 299 glBindBuffer(GL_ARRAY_BUFFER, vbo[i]);
nuclear@0 300 glBufferData(GL_ARRAY_BUFFER, MAX_PVERTS * sizeof(PVertex), 0, GL_STREAM_DRAW);
nuclear@0 301 }
nuclear@0 302 glBindBuffer(GL_ARRAY_BUFFER, 0);
nuclear@0 303 #else
nuclear@0 304 pvarr = new PVertex[MAX_PVERTS];
nuclear@0 305 pvptr = pvarr;
nuclear@0 306 #endif
nuclear@0 307
nuclear@0 308 // load shader program
nuclear@0 309 if(!(psdr = get_sdrprog("psdr.v.glsl", "psdr.p.glsl"))) {
nuclear@0 310 error_log("failed to load particle shader!\n");
nuclear@0 311 }
nuclear@0 312
nuclear@0 313 // create empty texture
nuclear@0 314 Image *img = texgen_solid(8, 8, Vector4(1, 1, 1, 1));
nuclear@0 315 blank_tex = new Texture2D;
nuclear@0 316 blank_tex->set_image(*img);
nuclear@0 317 delete img;
nuclear@0 318
nuclear@0 319 done_init = true;
nuclear@0 320 }
nuclear@0 321
nuclear@0 322 static void pdraw_flush()
nuclear@0 323 {
nuclear@0 324 #ifdef USE_VBO
nuclear@0 325 // assuming vbo[cur_buf] is bound
nuclear@0 326 glUnmapBuffer(GL_ARRAY_BUFFER);
nuclear@0 327 #endif
nuclear@0 328
nuclear@0 329 // draw from the bound buffer 6 indices per particle
nuclear@0 330 #ifdef USE_IBO
nuclear@0 331 glDrawElements(GL_TRIANGLES, num_buffered * 6, GL_UNSIGNED_INT, 0);
nuclear@0 332 #else
nuclear@0 333 glDrawElements(GL_TRIANGLES, num_buffered * 6, GL_UNSIGNED_INT, pvidx);
nuclear@0 334 #endif
nuclear@0 335 num_buffered = 0;
nuclear@0 336
nuclear@0 337 #ifdef USE_VBO
nuclear@0 338 // map the next buffer (write buffer) while the previous is drawing
nuclear@0 339 cur_buf = (cur_buf + 1) & 1;
nuclear@0 340 glBindBuffer(GL_ARRAY_BUFFER, vbo[cur_buf]);
nuclear@0 341 pvarr = (PVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
nuclear@0 342 #endif
nuclear@0 343 pvptr = pvarr;
nuclear@0 344 }
nuclear@0 345
nuclear@0 346 static void pdraw_start(const psys_emitter *em, void *cls)
nuclear@0 347 {
nuclear@0 348 ParticleSystem *ps = (ParticleSystem*)cls;
nuclear@0 349
nuclear@0 350 init_particle_draw();
nuclear@0 351
nuclear@0 352 num_buffered = 0;
nuclear@0 353
nuclear@0 354 #ifdef USE_IBO
nuclear@0 355 // bind the particle index buffer which is static
nuclear@0 356 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
nuclear@0 357 #endif
nuclear@0 358
nuclear@0 359 #ifdef USE_VBO
nuclear@0 360 // map the current write buffer
nuclear@0 361 glBindBuffer(GL_ARRAY_BUFFER, vbo[cur_buf]);
nuclear@0 362 pvarr = (PVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
nuclear@0 363 #endif
nuclear@0 364 pvptr = pvarr;
nuclear@0 365
nuclear@0 366 Texture *tex = ps->get_attr()->get_texture();
nuclear@0 367 if(tex) {
nuclear@0 368 tex->bind();
nuclear@0 369 } else {
nuclear@0 370 blank_tex->bind();
nuclear@0 371 }
nuclear@0 372 psdr->bind();
nuclear@0 373
nuclear@0 374 glDisable(GL_DEPTH_TEST);
nuclear@0 375 glDisable(GL_CULL_FACE);
nuclear@0 376 glEnable(GL_BLEND);
nuclear@0 377 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
nuclear@0 378 glDepthMask(0);
nuclear@0 379
nuclear@0 380 glEnableVertexAttribArray(MESH_ATTR_VERTEX);
nuclear@0 381 glEnableVertexAttribArray(MESH_ATTR_COLOR);
nuclear@0 382 glEnableVertexAttribArray(MESH_ATTR_TEXCOORD);
nuclear@0 383
nuclear@0 384 #ifdef USE_VBO
nuclear@0 385 glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)((char*)&pvarr->pos - (char*)pvarr));
nuclear@0 386 glVertexAttribPointer(MESH_ATTR_COLOR, 4, GL_FLOAT, GL_TRUE, sizeof *pvarr, (void*)((char*)&pvarr->color - (char*)pvarr));
nuclear@0 387 glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)((char*)&pvarr->texcoord - (char*)pvarr));
nuclear@0 388 #else
nuclear@0 389 glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)&pvarr->pos);
nuclear@0 390 glVertexAttribPointer(MESH_ATTR_COLOR, 4, GL_FLOAT, GL_TRUE, sizeof *pvarr, (void*)&pvarr->color);
nuclear@0 391 glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)&pvarr->texcoord);
nuclear@0 392 #endif
nuclear@0 393 }
nuclear@0 394
nuclear@0 395 static void pdraw(const psys_emitter *em, const psys_particle *part, void *cls)
nuclear@0 396 {
nuclear@0 397 ParticleSystem *ps = (ParticleSystem*)cls;
nuclear@0 398
nuclear@0 399 static const Vector3 pv[] = {
nuclear@0 400 Vector3(-0.5, -0.5, 0),
nuclear@0 401 Vector3(0.5, -0.5, 0),
nuclear@0 402 Vector3(0.5, 0.5, 0),
nuclear@0 403 Vector3(-0.5, 0.5, 0)
nuclear@0 404 };
nuclear@0 405 static const Vector2 tex[] = {
nuclear@0 406 Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)
nuclear@0 407 };
nuclear@0 408 Vector4 color = Vector4(part->color.x, part->color.y, part->color.z, part->alpha);
nuclear@0 409
nuclear@0 410 for(int i=0; i<4; i++) {
nuclear@0 411 pvptr->color = color;
nuclear@0 412 pvptr->pos = pv[i] * part->size + part->pos;
nuclear@0 413 pvptr->texcoord = tex[i];
nuclear@0 414 pvptr++;
nuclear@0 415 }
nuclear@0 416 // XXX we don't need billboarding for this game, so don't bother
nuclear@0 417
nuclear@0 418 // if we reached the maximum number of buffered particles, draw them
nuclear@0 419 if(++num_buffered >= MAX_DRAW_PART) {
nuclear@0 420 pdraw_flush(); // this will reset the counter
nuclear@0 421 }
nuclear@0 422 }
nuclear@0 423
nuclear@0 424 static void pdraw_end(const psys_emitter *em, void *cls)
nuclear@0 425 {
nuclear@0 426 // if we have leftover particles buffered, draw them before returning
nuclear@0 427 if(num_buffered) {
nuclear@0 428 pdraw_flush();
nuclear@0 429 }
nuclear@0 430
nuclear@0 431 // cleanup
nuclear@0 432 glDisableVertexAttribArray(MESH_ATTR_VERTEX);
nuclear@0 433 glDisableVertexAttribArray(MESH_ATTR_COLOR);
nuclear@0 434 glDisableVertexAttribArray(MESH_ATTR_TEXCOORD);
nuclear@0 435
nuclear@0 436 #ifdef USE_VBO
nuclear@0 437 glUnmapBuffer(GL_ARRAY_BUFFER);
nuclear@0 438 glBindBuffer(GL_ARRAY_BUFFER, 0);
nuclear@0 439 #endif
nuclear@0 440 #ifdef USE_IBO
nuclear@0 441 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
nuclear@0 442 #endif
nuclear@0 443
nuclear@0 444 glDepthMask(1);
nuclear@0 445 glDisable(GL_BLEND);
nuclear@0 446
nuclear@0 447 glEnable(GL_DEPTH_TEST);
nuclear@0 448 glEnable(GL_CULL_FACE);
nuclear@0 449 }
nuclear@0 450
nuclear@0 451 static unsigned int psys_load_texture(const char *fname, void *cls)
nuclear@0 452 {
nuclear@0 453 ParticleSystemAttributes *attr = (ParticleSystemAttributes*)cls;
nuclear@0 454
nuclear@0 455 Texture *tex = texset.get(fname);
nuclear@0 456 if(tex) {
nuclear@0 457 attr->set_texture(tex);
nuclear@0 458 return tex->get_id();
nuclear@0 459 }
nuclear@0 460 return 0;
nuclear@0 461 }