vrshoot

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