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