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 #include "geom.h"
26 #include "hmd.h"
27 #include "video.h"
28
29 /*---------------------------------------------------------------------------*/
30 /*
31 #define PARTICLEVBO 1
32 */
33
34 struct part_vary
35 {
36 GLfloat v[3]; /* Velocity */
37 };
38
39 struct part_draw
40 {
41 GLfloat p[3]; /* Position */
42 GLfloat c[3]; /* Color */
43 GLfloat t; /* Time until death. Doubles as opacity. */
44 };
45
46 static struct part_vary coin_vary[PART_MAX_COIN];
47 static struct part_draw coin_draw[PART_MAX_COIN];
48
49 static GLuint coin_vbo;
50
51 /*---------------------------------------------------------------------------*/
52
53 static struct b_mtrl coin_base_mtrl =
54 {
55 { 0.8f, 0.8f, 0.8f, 1.0f },
56 { 0.2f, 0.2f, 0.2f, 1.0f },
57 { 0.0f, 0.0f, 0.0f, 1.0f },
58 { 0.0f, 0.0f, 0.0f, 1.0f },
59 { 0.0f }, 0.0f, M_TRANSPARENT, IMG_PART_STAR
60 };
61
62 static int coin_mtrl;
63
64 /*---------------------------------------------------------------------------*/
65
66 #define PI 3.1415927f
67
rnd(float l,float h)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
part_lerp_copy(void)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
part_lerp_init(void)94 void part_lerp_init(void)
95 {
96 }
97
part_lerp_burst(int i)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
part_lerp_apply(float a)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
part_reset(void)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
part_init(void)140 void part_init(void)
141 {
142 coin_mtrl = mtrl_cache(&coin_base_mtrl);
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
part_free(void)158 void part_free(void)
159 {
160 glDeleteBuffers_(1, &coin_vbo);
161
162 mtrl_free(coin_mtrl);
163 coin_mtrl = 0;
164 }
165
166 /*---------------------------------------------------------------------------*/
167
part_burst(const float * p,const float * c)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
part_fall(struct part_lerp * lerp,struct part_vary * vary,struct part_draw * draw,int n,const float * g,float dt)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
part_step(const float * g,float dt)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
part_draw_coin(struct s_rend * rend)227 void part_draw_coin(struct s_rend *rend)
228 {
229 GLfloat height = (hmd_stat() ? 0.3f : 1.0f) * video.device_h;
230
231 r_apply_mtrl(rend, coin_mtrl);
232
233 /* Draw the entire buffer. Dead particles have zero opacity anyway. */
234
235 #ifdef PARTICLEVBO
236 glBindBuffer_(GL_ARRAY_BUFFER, coin_vbo);
237 #else
238 glBindBuffer_(GL_ARRAY_BUFFER, 0);
239 #endif
240
241 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
242 glDisableClientState(GL_NORMAL_ARRAY);
243 glEnableClientState(GL_COLOR_ARRAY);
244 {
245 #ifdef PARTICLEVBO
246 glColorPointer (4, GL_FLOAT, sizeof (struct part_draw),
247 (GLvoid *) offsetof (struct part_draw, c));
248 glVertexPointer(3, GL_FLOAT, sizeof (struct part_draw),
249 (GLvoid *) offsetof (struct part_draw, p));
250 #else
251 glColorPointer (4, GL_FLOAT, sizeof (struct part_draw), coin_draw[0].c);
252 glVertexPointer(3, GL_FLOAT, sizeof (struct part_draw), coin_draw[0].p);
253 #endif
254
255 glEnable(GL_POINT_SPRITE);
256 {
257 const GLfloat c[3] = { 0.0f, 0.0f, 1.0f };
258
259 glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
260 glPointParameterfv_(GL_POINT_DISTANCE_ATTENUATION, c);
261 glPointSize(height / 6);
262
263 glDrawArrays(GL_POINTS, 0, PART_MAX_COIN);
264 }
265 glDisable(GL_POINT_SPRITE);
266 }
267 glDisableClientState(GL_COLOR_ARRAY);
268 glEnableClientState(GL_NORMAL_ARRAY);
269 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
270
271 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
272 }
273
274 /*---------------------------------------------------------------------------*/
275