9db5a8d384a7386ee5daa76e9ee4d41b009add91
[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 "glext.h"
21 #include "part.h"
22 #include "vec3.h"
23 #include "image.h"
24
25 /*---------------------------------------------------------------------------*/
26
27 struct part
28 {
29     float t;
30     float a;
31     float w;
32     float c[3];
33     float p[3];
34     float v[3];
35 };
36
37 static struct part part_coin[PART_MAX_COIN];
38 static struct part part_goal[PART_MAX_GOAL];
39 static struct part part_jump[PART_MAX_JUMP];
40 static GLuint      part_text_star;
41 static GLuint      part_text_squiggle;
42 static GLuint      part_list;
43
44 static float goal_height;
45 static float jump_height;
46
47 /*---------------------------------------------------------------------------*/
48
49 #define PI 3.1415927f
50
51 static float rnd(float l, float h)
52 {
53     return l + (h - l) * rand() / RAND_MAX;
54 }
55
56 /*---------------------------------------------------------------------------*/
57
58 #define CURR 0
59 #define PREV 1
60
61 struct part_lerp
62 {
63     float p[2][3];
64 };
65
66 static struct part_lerp part_lerp_coin[PART_MAX_COIN];
67 static struct part_lerp part_lerp_goal[PART_MAX_GOAL];
68 static struct part_lerp part_lerp_jump[PART_MAX_JUMP];
69
70 void part_lerp_copy(void)
71 {
72     int i;
73
74     for (i = 0; i < PART_MAX_COIN; i++)
75         v_cpy(part_lerp_coin[i].p[PREV], part_lerp_coin[i].p[CURR]);
76
77     for (i = 0; i < PART_MAX_GOAL; i++)
78         v_cpy(part_lerp_goal[i].p[PREV], part_lerp_goal[i].p[CURR]);
79
80     for (i = 0; i < PART_MAX_JUMP; i++)
81         v_cpy(part_lerp_jump[i].p[PREV], part_lerp_jump[i].p[CURR]);
82 }
83
84 void part_lerp_init(void)
85 {
86     int i;
87
88     for (i = 0; i < PART_MAX_GOAL; i++)
89         if (part_goal[i].t > 0.0f)
90         {
91             v_cpy(part_lerp_goal[i].p[PREV], part_goal[i].p);
92             v_cpy(part_lerp_goal[i].p[CURR], part_goal[i].p);
93         }
94
95     for (i = 0; i < PART_MAX_JUMP; i++)
96         if (part_jump[i].t > 0.0f)
97         {
98             v_cpy(part_lerp_jump[i].p[PREV], part_jump[i].p);
99             v_cpy(part_lerp_jump[i].p[CURR], part_jump[i].p);
100         }
101 }
102
103 void part_lerp_burst(int i)
104 {
105     if (part_coin[i].t >= 1.0f)
106     {
107         v_cpy(part_lerp_coin[i].p[PREV], part_coin[i].p);
108         v_cpy(part_lerp_coin[i].p[CURR], part_coin[i].p);
109     }
110 }
111
112 void part_lerp_apply(float a)
113 {
114     int i;
115
116     for (i = 0; i < PART_MAX_COIN; i++)
117         if (part_coin[i].t > 0.0f)
118         {
119             v_lerp(part_coin[i].p,
120                    part_lerp_coin[i].p[PREV],
121                    part_lerp_coin[i].p[CURR], a);
122         }
123
124     for (i = 0; i < PART_MAX_GOAL; i++)
125         if (part_goal[i].t > 0.0f)
126         {
127             v_lerp(part_goal[i].p,
128                    part_lerp_goal[i].p[PREV],
129                    part_lerp_goal[i].p[CURR], a);
130         }
131
132     for (i = 0; i < PART_MAX_GOAL; i++)
133         if (part_jump[i].t > 0.0f)
134         {
135             v_lerp(part_jump[i].p,
136                    part_lerp_jump[i].p[PREV],
137                    part_lerp_jump[i].p[CURR], a);
138         }
139 }
140
141 /*---------------------------------------------------------------------------*/
142
143 void part_reset(float zh, float jh)
144 {
145     int i;
146
147     goal_height = zh;
148     jump_height = jh;
149
150     for (i = 0; i < PART_MAX_COIN; i++)
151         part_coin[i].t = 0.0f;
152
153     for (i = 0; i < PART_MAX_GOAL; i++)
154     {
155         float t = rnd(+0.1f,      +1.0f);
156         float a = rnd(-1.0f * PI, +1.0f * PI);
157         float w = rnd(-2.0f * PI, +2.0f * PI);
158
159         part_goal[i].t = t;
160         part_goal[i].a = V_DEG(a);
161         part_goal[i].w = V_DEG(w);
162
163         part_goal[i].c[0] = 1.0f;
164         part_goal[i].c[1] = 1.0f;
165         part_goal[i].c[2] = 0.0f;
166
167         part_goal[i].p[0] = fsinf(a);
168         part_goal[i].p[1] = (1.f - t) * goal_height;
169         part_goal[i].p[2] = fcosf(a);
170
171         part_goal[i].v[0] = 0.f;
172         part_goal[i].v[1] = 0.f;
173         part_goal[i].v[2] = 0.f;
174     }
175
176     for (i = 0; i < PART_MAX_JUMP; i++)
177     {
178         float t = rnd(+0.1f,      +1.0f);
179         float a = rnd(-1.0f * PI, +1.0f * PI);
180         float w = rnd(+0.5f,      +2.5f);
181
182         float vy = rnd(+0.025f, +0.25f);
183
184         part_jump[i].t = t;
185         part_jump[i].a = V_DEG(a);
186         part_jump[i].w = w;
187
188         part_jump[i].c[0] = 1.0f;
189         part_jump[i].c[1] = 1.0f;
190         part_jump[i].c[2] = 1.0f;
191
192         part_jump[i].p[0] = fsinf(a);
193         part_jump[i].p[1] = (1.f - t) * jump_height;
194         part_jump[i].p[2] = fcosf(a);
195
196         part_jump[i].v[0] = 0.f;
197         part_jump[i].v[1] = vy;
198         part_jump[i].v[2] = 0.f;
199     }
200
201     part_lerp_init();
202 }
203
204 void part_init(float zh, float jh)
205 {
206     memset(part_coin, 0, PART_MAX_COIN * sizeof (struct part));
207     memset(part_goal, 0, PART_MAX_GOAL * sizeof (struct part));
208     memset(part_jump, 0, PART_MAX_JUMP * sizeof (struct part));
209
210     part_text_star     = make_image_from_file(IMG_PART_STAR);
211     part_text_squiggle = make_image_from_file(IMG_PART_SQUIGGLE);
212
213     part_list = glGenLists(1);
214
215     glNewList(part_list, GL_COMPILE);
216     {
217         glBegin(GL_QUADS);
218         {
219             glTexCoord2f(0.f, 0.f);
220             glVertex2f(-PART_SIZE, -PART_SIZE);
221
222             glTexCoord2f(1.f, 0.f);
223             glVertex2f(+PART_SIZE, -PART_SIZE);
224
225             glTexCoord2f(1.f, 1.f);
226             glVertex2f(+PART_SIZE, +PART_SIZE);
227
228             glTexCoord2f(0.f, 1.f);
229             glVertex2f(-PART_SIZE, +PART_SIZE);
230         }
231         glEnd();
232     }
233     glEndList();
234
235     part_reset(zh, jh);
236 }
237
238 void part_free(void)
239 {
240     if (glIsList(part_list))
241         glDeleteLists(part_list, 1);
242
243     if (glIsTexture(part_text_star))
244         glDeleteTextures(1, &part_text_star);
245
246     if (glIsTexture(part_text_squiggle))
247         glDeleteTextures(1, &part_text_squiggle);
248 }
249
250 /*---------------------------------------------------------------------------*/
251
252 void part_burst(const float *p, const float *c)
253 {
254     int i, n = 0;
255
256     for (i = 0; n < 10 && i < PART_MAX_COIN; i++)
257         if (part_coin[i].t <= 0.f)
258         {
259             float a = rnd(-1.0f * PI, +1.0f * PI);
260             float b = rnd(+0.3f * PI, +0.5f * PI);
261             float w = rnd(-4.0f * PI, +4.0f * PI);
262
263             part_coin[i].p[0] = p[0];
264             part_coin[i].p[1] = p[1];
265             part_coin[i].p[2] = p[2];
266
267             part_coin[i].v[0] = 4.f * fcosf(a) * fcosf(b);
268             part_coin[i].v[1] = 4.f *            fsinf(b);
269             part_coin[i].v[2] = 4.f * fsinf(a) * fcosf(b);
270
271             part_coin[i].c[0] = c[0];
272             part_coin[i].c[1] = c[1];
273             part_coin[i].c[2] = c[2];
274
275             part_coin[i].t = 1.f;
276             part_coin[i].a = 0.f;
277             part_coin[i].w = V_DEG(w);
278
279             part_lerp_burst(i);
280
281             n++;
282         }
283 }
284
285 /*---------------------------------------------------------------------------*/
286
287 static void part_fall(struct part_lerp *lerp,
288                       struct part *part, int n,
289                       const float *g, float dt)
290 {
291     int i;
292
293     for (i = 0; i < n; i++)
294         if (part[i].t > 0.f)
295         {
296             part[i].t -= dt;
297
298             v_mad(part[i].v, part[i].v, g, dt);
299
300             v_mad(lerp[i].p[CURR], lerp[i].p[CURR], part[i].v, dt);
301         }
302 }
303
304 static void part_spin(struct part_lerp *lerp,
305                       struct part *part, int n,
306                       const float *g, float dt)
307 {
308     int i;
309
310     for (i = 0; i < n; i++)
311         if (part[i].t > 0.f)
312         {
313             part[i].a += 30.f * dt;
314
315             lerp[i].p[CURR][0] = fsinf(V_RAD(part[i].a));
316             lerp[i].p[CURR][2] = fcosf(V_RAD(part[i].a));
317         }
318 }
319
320 void part_step(const float *g, float dt)
321 {
322     int i;
323
324     part_lerp_copy();
325
326     part_fall(part_lerp_coin, part_coin, PART_MAX_COIN, g, dt);
327
328     if (g[1] > 0.f)
329         part_fall(part_lerp_goal, part_goal, PART_MAX_GOAL, g, dt);
330     else
331         part_spin(part_lerp_goal, part_goal, PART_MAX_GOAL, g, dt);
332
333     for (i = 0; i < PART_MAX_JUMP; i++)
334     {
335         part_lerp_jump[i].p[CURR][1] += part_jump[i].v[1] * dt;
336
337         if (part_lerp_jump[i].p[PREV][1] > jump_height)
338         {
339             part_lerp_jump[i].p[PREV][1] = 0.0f;
340             part_lerp_jump[i].p[CURR][1] = 0.0f;
341         }
342     }
343 }
344
345 /*---------------------------------------------------------------------------*/
346
347 static void part_draw(const float *M,
348                       const float *p, float r, float rz, float s)
349 {
350     glPushMatrix();
351     {
352         glTranslatef(r * p[0], p[1], r * p[2]);
353         glMultMatrixf(M);
354         glRotatef(rz, 0.f, 0.f, 1.f);
355         glScalef(s, s, 1.0f);
356
357         glCallList(part_list);
358     }
359     glPopMatrix();
360 }
361
362 void part_draw_coin(const float *M, float t)
363 {
364     int i;
365
366     glBindTexture(GL_TEXTURE_2D, part_text_star);
367
368     for (i = 0; i < PART_MAX_COIN; i++)
369         if (part_coin[i].t > 0.f)
370         {
371             glColor4f(part_coin[i].c[0],
372                       part_coin[i].c[1],
373                       part_coin[i].c[2],
374                       part_coin[i].t);
375
376             part_draw(M, part_coin[i].p, 1.0f, t * part_coin[i].w, 1.0f);
377         }
378
379     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
380 }
381
382 void part_draw_goal(const float *M, float radius, float a, float t)
383 {
384     int i;
385
386     glBindTexture(GL_TEXTURE_2D, part_text_star);
387
388     glColor4f(1.0f, 1.0f, 0.0f, a);
389
390     for (i = 0; i < PART_MAX_GOAL; i++)
391         if (part_goal[i].t > 0.0f)
392             part_draw(M, part_goal[i].p, radius - 0.05f,
393                       t * part_goal[i].w, 1.0f);
394
395     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
396 }
397
398 void part_draw_jump(const float *M, float radius, float a, float t)
399 {
400     int i;
401
402     glBindTexture(GL_TEXTURE_2D, part_text_squiggle);
403
404     for (i = 0; i < PART_MAX_JUMP; i++)
405     {
406         glColor4f(part_jump[i].c[0],
407                   part_jump[i].c[1],
408                   part_jump[i].c[2],
409                   1.0f - part_jump[i].p[1] / jump_height);
410
411         /*
412          * X is the current time since some Epoch, Y is the time it
413          * takes for a squiggle to grow to its full size and then
414          * shrink again.  F is the current scale of the squiggle in
415          * the interval [0.0, 1.0].
416          */
417
418 #define F(x, y) fabsf(fcosf(((x) / (y)) * PI))
419
420         part_draw(M, part_jump[i].p, radius - 0.05f,
421                   0.0f, F(t, part_jump[i].w));
422
423 #undef F
424     }
425
426     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
427 }
428
429 /*---------------------------------------------------------------------------*/