goat3dgfx

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