Optional use of vertex arrays in the particle system.
[neverball] / share / part.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <SDL.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <math.h>
19
20 #include "config.h"
21 #include "glext.h"
22 #include "part.h"
23 #include "vec3.h"
24 #include "image.h"
25
26 #define PARTICLEVBO 1
27
28 /*---------------------------------------------------------------------------*/
29
30 struct part_vary
31 {
32     GLfloat v[3];             /* Velocity                                    */
33 };
34
35 struct part_draw
36 {
37     GLfloat p[3];             /* Position                                    */
38     GLfloat c[3];             /* Color                                       */
39     GLfloat t;                /* Time until death. Doubles as opacity.       */
40 };
41
42 static struct part_vary coin_vary[PART_MAX_COIN];
43 static struct part_draw coin_draw[PART_MAX_COIN];
44
45 static GLuint coin_vbo;
46
47 /*---------------------------------------------------------------------------*/
48
49 static struct b_mtrl coin_base_mtrl =
50 {
51     { 0.8f, 0.8f, 0.8f, 1.0f },
52     { 0.2f, 0.2f, 0.2f, 1.0f },
53     { 0.0f, 0.0f, 0.0f, 1.0f },
54     { 0.0f, 0.0f, 0.0f, 1.0f },
55     { 0.0f }, 0.0f, M_TRANSPARENT, ""
56 };
57
58 static struct d_mtrl coin_draw_mtrl =
59 {
60     &coin_base_mtrl, 0
61 };
62
63 /*---------------------------------------------------------------------------*/
64
65 #define PI 3.1415927f
66
67 static float rnd(float l, float h)
68 {
69     return l + (h - l) * rand() / RAND_MAX;
70 }
71
72 /*---------------------------------------------------------------------------*/
73
74 #define CURR 0
75 #define PREV 1
76
77 struct part_lerp
78 {
79     float p[2][3];
80 };
81
82 static struct part_lerp part_lerp_coin[PART_MAX_COIN];
83
84 void part_lerp_copy(void)
85 {
86     int i;
87
88     for (i = 0; i < PART_MAX_COIN; i++)
89         v_cpy(part_lerp_coin[i].p[PREV],
90               part_lerp_coin[i].p[CURR]);
91 }
92
93 void part_lerp_init(void)
94 {
95 }
96
97 void part_lerp_burst(int i)
98 {
99     if (coin_draw[i].t >= 1.0f)
100     {
101         v_cpy(part_lerp_coin[i].p[PREV], coin_draw[i].p);
102         v_cpy(part_lerp_coin[i].p[CURR], coin_draw[i].p);
103     }
104 }
105
106 void part_lerp_apply(float a)
107 {
108     int i;
109
110     for (i = 0; i < PART_MAX_COIN; i++)
111         if (coin_draw[i].t > 0.0f)
112             v_lerp(coin_draw[i].p,
113                    part_lerp_coin[i].p[PREV],
114                    part_lerp_coin[i].p[CURR], a);
115
116     /* Upload the current state of the particles. It would be best to limit  */
117     /* this upload to only active particles, but it's more important to do   */
118     /* it all in a single call.                                              */
119
120 #ifdef PARTICLEVBO
121     glBindBuffer   (GL_ARRAY_BUFFER, coin_vbo);
122     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof (coin_draw), coin_draw);
123     glBindBuffer   (GL_ARRAY_BUFFER, 0);
124 #endif
125 }
126
127 /*---------------------------------------------------------------------------*/
128
129 void part_reset(void)
130 {
131     int i;
132
133     for (i = 0; i < PART_MAX_COIN; i++)
134         coin_draw[i].t = 0.0f;
135
136     part_lerp_init();
137 }
138
139 void part_init(void)
140 {
141     coin_draw_mtrl.o = make_image_from_file(IMG_PART_STAR);
142
143     memset(coin_vary, 0, PART_MAX_COIN * sizeof (struct part_vary));
144     memset(coin_draw, 0, PART_MAX_COIN * sizeof (struct part_draw));
145
146 #ifdef PARTICLEVBO
147     glGenBuffers(1,              &coin_vbo);
148     glBindBuffer(GL_ARRAY_BUFFER, coin_vbo);
149     glBufferData(GL_ARRAY_BUFFER, sizeof (coin_draw),
150                                           coin_draw, GL_DYNAMIC_DRAW);
151     glBindBuffer(GL_ARRAY_BUFFER, 0);
152 #endif
153
154     part_reset();
155 }
156
157 void part_free(void)
158 {
159     if (glIsBuffer(coin_vbo))
160         glDeleteBuffers(1, &coin_vbo);
161
162     if (glIsTexture(coin_draw_mtrl.o))
163         glDeleteTextures(1, &coin_draw_mtrl.o);
164 }
165
166 /*---------------------------------------------------------------------------*/
167
168 void part_burst(const float *p, const float *c)
169 {
170     int i, n = 0;
171
172     for (i = 0; n < 10 && i < PART_MAX_COIN; i++)
173         if (coin_draw[i].t <= 0.f)
174         {
175             float a = rnd(-1.0f * PI, +1.0f * PI);
176             float b = rnd(+0.3f * PI, +0.5f * PI);
177
178             coin_draw[i].c[0] = c[0];
179             coin_draw[i].c[1] = c[1];
180             coin_draw[i].c[2] = c[2];
181
182             coin_draw[i].p[0] = p[0];
183             coin_draw[i].p[1] = p[1];
184             coin_draw[i].p[2] = p[2];
185
186             coin_vary[i].v[0] = 4.f * fcosf(a) * fcosf(b);
187             coin_vary[i].v[1] = 4.f *            fsinf(b);
188             coin_vary[i].v[2] = 4.f * fsinf(a) * fcosf(b);
189
190             coin_draw[i].t = 1.f;
191
192             part_lerp_burst(i);
193
194             n++;
195         }
196 }
197
198 /*---------------------------------------------------------------------------*/
199
200 static void part_fall(struct part_lerp *lerp,
201                       struct part_vary *vary,
202                       struct part_draw *draw,
203                       int n, const float *g, float dt)
204 {
205     int i;
206
207     for (i = 0; i < n; i++)
208         if (draw[i].t > 0.f)
209         {
210             draw[i].t -= dt;
211
212             v_mad(vary[i].v, vary[i].v, g, dt);
213
214             v_mad(lerp[i].p[CURR], lerp[i].p[CURR], vary[i].v, dt);
215         }
216         else draw[i].t = 0.0f;
217 }
218
219 void part_step(const float *g, float dt)
220 {
221     part_lerp_copy();
222     part_fall(part_lerp_coin, coin_vary, coin_draw, PART_MAX_COIN, g, dt);
223 }
224
225 /*---------------------------------------------------------------------------*/
226
227 const struct d_mtrl *part_draw_coin(const struct d_mtrl *mq)
228 {
229     const GLfloat c[3] = { 0.0f, 1.0f, 0.0f };
230     GLint s = config_get_d(CONFIG_HEIGHT) / 8;
231
232     mq = sol_apply_mtrl(&coin_draw_mtrl, mq);
233
234     /* Draw the entire buffer.  Dead particles have zero opacity anyway. */
235
236 #ifdef PARTICLEVBO
237     glBindBuffer(GL_ARRAY_BUFFER, coin_vbo);
238 #else
239     glBindBuffer(GL_ARRAY_BUFFER, 0);
240 #endif
241
242     glClientActiveTexture(GL_TEXTURE1);
243     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
244     glClientActiveTexture(GL_TEXTURE0);
245     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
246
247     glDisableClientState(GL_NORMAL_ARRAY);
248     glEnableClientState(GL_COLOR_ARRAY);
249     {
250 #ifdef PARTICLEVBO
251         glColorPointer (4, GL_FLOAT, sizeof (struct part_draw),
252                         (GLvoid *) offsetof (struct part_draw, c));
253         glVertexPointer(3, GL_FLOAT, sizeof (struct part_draw),
254                         (GLvoid *) offsetof (struct part_draw, p));
255 #else
256         glColorPointer (4, GL_FLOAT, sizeof (struct part_draw), coin_draw[0].c);
257         glVertexPointer(3, GL_FLOAT, sizeof (struct part_draw), coin_draw[0].p);
258 #endif
259
260         glEnable(GL_POINT_SPRITE);
261         {
262             glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
263             glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, c);
264             glPointSize(s);
265
266             glDrawArrays(GL_POINTS, 0, PART_MAX_COIN);
267         }
268         glDisable(GL_POINT_SPRITE);
269     }
270     glDisableClientState(GL_COLOR_ARRAY);
271     glEnableClientState(GL_NORMAL_ARRAY);
272
273     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
274     glClientActiveTexture(GL_TEXTURE1);
275     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
276     glClientActiveTexture(GL_TEXTURE0);
277
278     return mq;
279 }
280
281 /*---------------------------------------------------------------------------*/