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