Fixed the level fade-out.
[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 /*---------------------------------------------------------------------------*/
27 /*
28 #define PARTICLEVBO 1
29 */
30
31 struct part_vary
32 {
33     GLfloat v[3];             /* Velocity                                    */
34 };
35
36 struct part_draw
37 {
38     GLfloat p[3];             /* Position                                    */
39     GLfloat c[3];             /* Color                                       */
40     GLfloat t;                /* Time until death. Doubles as opacity.       */
41 };
42
43 static struct part_vary coin_vary[PART_MAX_COIN];
44 static struct part_draw coin_draw[PART_MAX_COIN];
45
46 static GLuint coin_vbo;
47
48 /*---------------------------------------------------------------------------*/
49
50 static struct b_mtrl coin_base_mtrl =
51 {
52     { 0.8f, 0.8f, 0.8f, 1.0f },
53     { 0.2f, 0.2f, 0.2f, 1.0f },
54     { 0.0f, 0.0f, 0.0f, 1.0f },
55     { 0.0f, 0.0f, 0.0f, 1.0f },
56     { 0.0f }, 0.0f, M_TRANSPARENT, ""
57 };
58
59 static struct d_mtrl coin_draw_mtrl =
60 {
61     &coin_base_mtrl, 0
62 };
63
64 /*---------------------------------------------------------------------------*/
65
66 #define PI 3.1415927f
67
68 static float rnd(float l, float h)
69 {
70     return l + (h - l) * rand() / RAND_MAX;
71 }
72
73 /*---------------------------------------------------------------------------*/
74
75 #define CURR 0
76 #define PREV 1
77
78 struct part_lerp
79 {
80     float p[2][3];
81 };
82
83 static struct part_lerp part_lerp_coin[PART_MAX_COIN];
84
85 void part_lerp_copy(void)
86 {
87     int i;
88
89     for (i = 0; i < PART_MAX_COIN; i++)
90         v_cpy(part_lerp_coin[i].p[PREV],
91               part_lerp_coin[i].p[CURR]);
92 }
93
94 void part_lerp_init(void)
95 {
96 }
97
98 void part_lerp_burst(int i)
99 {
100     if (coin_draw[i].t >= 1.0f)
101     {
102         v_cpy(part_lerp_coin[i].p[PREV], coin_draw[i].p);
103         v_cpy(part_lerp_coin[i].p[CURR], coin_draw[i].p);
104     }
105 }
106
107 void part_lerp_apply(float a)
108 {
109     int i;
110
111     for (i = 0; i < PART_MAX_COIN; i++)
112         if (coin_draw[i].t > 0.0f)
113             v_lerp(coin_draw[i].p,
114                    part_lerp_coin[i].p[PREV],
115                    part_lerp_coin[i].p[CURR], a);
116
117     /* Upload the current state of the particles. It would be best to limit  */
118     /* this upload to only active particles, but it's more important to do   */
119     /* it all in a single call.                                              */
120
121 #ifdef PARTICLEVBO
122     glBindBuffer   (GL_ARRAY_BUFFER, coin_vbo);
123     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof (coin_draw), coin_draw);
124     glBindBuffer   (GL_ARRAY_BUFFER, 0);
125 #endif
126 }
127
128 /*---------------------------------------------------------------------------*/
129
130 void part_reset(void)
131 {
132     int i;
133
134     for (i = 0; i < PART_MAX_COIN; i++)
135         coin_draw[i].t = 0.0f;
136
137     part_lerp_init();
138 }
139
140 void part_init(void)
141 {
142     coin_draw_mtrl.o = make_image_from_file(IMG_PART_STAR);
143
144     memset(coin_vary, 0, PART_MAX_COIN * sizeof (struct part_vary));
145     memset(coin_draw, 0, PART_MAX_COIN * sizeof (struct part_draw));
146
147 #ifdef PARTICLEVBO
148     glGenBuffers(1,              &coin_vbo);
149     glBindBuffer(GL_ARRAY_BUFFER, coin_vbo);
150     glBufferData(GL_ARRAY_BUFFER, sizeof (coin_draw),
151                                           coin_draw, GL_DYNAMIC_DRAW);
152     glBindBuffer(GL_ARRAY_BUFFER, 0);
153 #endif
154
155     part_reset();
156 }
157
158 void part_free(void)
159 {
160     if (glIsBuffer_(coin_vbo))
161         glDeleteBuffers_(1, &coin_vbo);
162
163     if (glIsTexture(coin_draw_mtrl.o))
164         glDeleteTextures(1, &coin_draw_mtrl.o);
165 }
166
167 /*---------------------------------------------------------------------------*/
168
169 void part_burst(const float *p, const float *c)
170 {
171     int i, n = 0;
172
173     for (i = 0; n < 10 && i < PART_MAX_COIN; i++)
174         if (coin_draw[i].t <= 0.f)
175         {
176             float a = rnd(-1.0f * PI, +1.0f * PI);
177             float b = rnd(+0.3f * PI, +0.5f * PI);
178
179             coin_draw[i].c[0] = c[0];
180             coin_draw[i].c[1] = c[1];
181             coin_draw[i].c[2] = c[2];
182
183             coin_draw[i].p[0] = p[0];
184             coin_draw[i].p[1] = p[1];
185             coin_draw[i].p[2] = p[2];
186
187             coin_vary[i].v[0] = 4.f * fcosf(a) * fcosf(b);
188             coin_vary[i].v[1] = 4.f *            fsinf(b);
189             coin_vary[i].v[2] = 4.f * fsinf(a) * fcosf(b);
190
191             coin_draw[i].t = 1.f;
192
193             part_lerp_burst(i);
194
195             n++;
196         }
197 }
198
199 /*---------------------------------------------------------------------------*/
200
201 static void part_fall(struct part_lerp *lerp,
202                       struct part_vary *vary,
203                       struct part_draw *draw,
204                       int n, const float *g, float dt)
205 {
206     int i;
207
208     for (i = 0; i < n; i++)
209         if (draw[i].t > 0.f)
210         {
211             draw[i].t -= dt;
212
213             v_mad(vary[i].v, vary[i].v, g, dt);
214
215             v_mad(lerp[i].p[CURR], lerp[i].p[CURR], vary[i].v, dt);
216         }
217         else draw[i].t = 0.0f;
218 }
219
220 void part_step(const float *g, float dt)
221 {
222     part_lerp_copy();
223     part_fall(part_lerp_coin, coin_vary, coin_draw, PART_MAX_COIN, g, dt);
224 }
225
226 /*---------------------------------------------------------------------------*/
227
228 const struct d_mtrl *part_draw_coin(const struct d_mtrl *mq)
229 {
230     const GLfloat c[3] = { 0.0f, 1.0f, 0.0f };
231     GLint s = config_get_d(CONFIG_HEIGHT) / 8;
232
233     mq = sol_apply_mtrl(&coin_draw_mtrl, mq);
234
235     /* Draw the entire buffer.  Dead particles have zero opacity anyway. */
236
237 #ifdef PARTICLEVBO
238     glBindBuffer_(GL_ARRAY_BUFFER, coin_vbo);
239 #else
240     glBindBuffer_(GL_ARRAY_BUFFER, 0);
241 #endif
242
243     glClientActiveTexture_(GL_TEXTURE1);
244     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
245     glClientActiveTexture_(GL_TEXTURE0);
246     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
247
248     glDisableClientState(GL_NORMAL_ARRAY);
249     glEnableClientState(GL_COLOR_ARRAY);
250     {
251 #ifdef PARTICLEVBO
252         glColorPointer (4, GL_FLOAT, sizeof (struct part_draw),
253                         (GLvoid *) offsetof (struct part_draw, c));
254         glVertexPointer(3, GL_FLOAT, sizeof (struct part_draw),
255                         (GLvoid *) offsetof (struct part_draw, p));
256 #else
257         glColorPointer (4, GL_FLOAT, sizeof (struct part_draw), coin_draw[0].c);
258         glVertexPointer(3, GL_FLOAT, sizeof (struct part_draw), coin_draw[0].p);
259 #endif
260
261         glEnable(GL_POINT_SPRITE);
262         {
263             glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
264             glPointParameterfv_(GL_POINT_DISTANCE_ATTENUATION, c);
265             glPointSize(s);
266
267             glDrawArrays(GL_POINTS, 0, PART_MAX_COIN);
268         }
269         glDisable(GL_POINT_SPRITE);
270     }
271     glDisableClientState(GL_COLOR_ARRAY);
272     glEnableClientState(GL_NORMAL_ARRAY);
273
274     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
275     glClientActiveTexture_(GL_TEXTURE1);
276     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
277     glClientActiveTexture_(GL_TEXTURE0);
278
279     return mq;
280 }
281
282 /*---------------------------------------------------------------------------*/