goat3dgfx

view src/psyspp.cc @ 29:9d581abd0bfb

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