updated URL of forum and table in readme file
[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 GLuint      part_text;
40 static GLuint      part_list;
41
42 /*---------------------------------------------------------------------------*/
43
44 #define PI 3.1415927f
45
46 static float rnd(float l, float h)
47 {
48     return l + (h - l) * rand() / RAND_MAX;
49 }
50
51 /*---------------------------------------------------------------------------*/
52
53 static GLfloat gcoltab[] = {0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};
54
55 void part_reset(float h)
56 {
57     int i;
58
59     for (i = 0; i < PART_MAX_GOAL; i++)
60     {
61         float t = rnd(+0.1f,      +1.0f);
62         float a = rnd(-1.0f * PI, +1.0f * PI);
63         float w = rnd(-2.0f * PI, +2.0f * PI);
64         int k = i % 6;
65
66         part_goal[i].t = t;
67         part_goal[i].a = V_DEG(a);
68         part_goal[i].w = V_DEG(w);
69
70         part_goal[i].c[0] = gcoltab[k];
71         part_goal[i].c[1] = gcoltab[k+1];
72         part_goal[i].c[2] = gcoltab[k+2];
73
74         part_goal[i].p[0] = fsinf(a);
75         part_goal[i].p[1] = (1.f - t) * h;
76         part_goal[i].p[2] = fcosf(a);
77
78         part_goal[i].v[0] = 0.f;
79         part_goal[i].v[1] = 0.f;
80         part_goal[i].v[2] = 0.f;
81
82         part_coin[i].t    = 0.0f;
83     }
84 }
85
86 void part_init(float h)
87 {
88     memset(part_coin, 0, PART_MAX_COIN * sizeof (struct part));
89     memset(part_goal, 0, PART_MAX_GOAL * sizeof (struct part));
90
91     part_text = make_image_from_file(NULL, NULL, NULL, NULL, IMG_PART);
92     part_list = glGenLists(1);
93
94     glNewList(part_list, GL_COMPILE);
95     {
96         glBegin(GL_QUADS);
97         {
98             glTexCoord2f(0.f, 0.f);
99             glVertex2f(-PART_SIZE, -PART_SIZE);
100
101             glTexCoord2f(1.f, 0.f);
102             glVertex2f(+PART_SIZE, -PART_SIZE);
103
104             glTexCoord2f(1.f, 1.f);
105             glVertex2f(+PART_SIZE, +PART_SIZE);
106
107             glTexCoord2f(0.f, 1.f);
108             glVertex2f(-PART_SIZE, +PART_SIZE);
109         }
110         glEnd();
111     }
112     glEndList();
113
114     part_reset(h);
115 }
116
117 void part_free(void)
118 {
119     if (glIsList(part_list))
120         glDeleteLists(part_list, 1);
121
122     if (glIsTexture(part_text))
123         glDeleteTextures(1, &part_text);
124 }
125
126 /*---------------------------------------------------------------------------*/
127
128 void part_burst(const float *p, const float *c)
129 {
130     int i, n = 0;
131
132     for (i = 0; n < 10 && i < PART_MAX_COIN; i++)
133         if (part_coin[i].t <= 0.f)
134         {
135             float a = rnd(-1.0f * PI, +1.0f * PI);
136             float b = rnd(+0.3f * PI, +0.5f * PI);
137             float w = rnd(-4.0f * PI, +4.0f * PI);
138
139             part_coin[i].p[0] = p[0];
140             part_coin[i].p[1] = p[1];
141             part_coin[i].p[2] = p[2];
142
143             part_coin[i].v[0] = 4.f * fcosf(a) * fcosf(b);
144             part_coin[i].v[1] = 4.f *            fsinf(b);
145             part_coin[i].v[2] = 4.f * fsinf(a) * fcosf(b);
146
147             part_coin[i].c[0] = c[0];
148             part_coin[i].c[1] = c[1];
149             part_coin[i].c[2] = c[2];
150
151             part_coin[i].t = 1.f;
152             part_coin[i].a = 0.f;
153             part_coin[i].w = V_DEG(w);
154
155             n++;
156         }
157 }
158
159 /*---------------------------------------------------------------------------*/
160
161 static void part_fall(struct part *part, int n, const float *g, float dt)
162 {
163     int i;
164
165     for (i = 0; i < n; i++)
166         if (part[i].t > 0.f)
167         {
168             part[i].t -= dt;
169
170             part[i].v[0] += (g[0] * dt);
171             part[i].v[1] += (g[1] * dt);
172             part[i].v[2] += (g[2] * dt);
173
174             part[i].p[0] += (part[i].v[0] * dt);
175             part[i].p[1] += (part[i].v[1] * dt);
176             part[i].p[2] += (part[i].v[2] * dt);
177         }
178 }
179
180 static void part_spin(struct part *part, int n, const float *g, float dt)
181 {
182     int i;
183
184     for (i = 0; i < n; i++)
185         if (part[i].t > 0.f)
186         {
187             part[i].a += 30.f * dt;
188
189             part[i].p[0] = fsinf(V_RAD(part[i].a));
190             part[i].p[2] = fcosf(V_RAD(part[i].a));
191         }
192 }
193
194 void part_step(const float *g, float dt)
195 {
196     part_fall(part_coin, PART_MAX_COIN, g, dt);
197
198     if (g[1] > 0.f)
199         part_fall(part_goal, PART_MAX_GOAL, g, dt);
200     else
201         part_spin(part_goal, PART_MAX_GOAL, g, dt);
202 }
203
204 /*---------------------------------------------------------------------------*/
205
206 static void part_draw(const float p[3], const float c[3],
207                       float a, float r, float rx, float ry, float rz)
208 {
209     glPushMatrix();
210     {
211         glTranslatef(r * p[0], p[1], r * p[2]);
212         glRotatef(ry, 0.f, 1.f, 0.f);
213         glRotatef(rx, 1.f, 0.f, 0.f);
214         glRotatef(rz, 0.f, 0.f, 1.f);
215
216         glColor4f(c[0], c[1], c[2], a);
217
218         glCallList(part_list);
219     }
220     glPopMatrix();
221 }
222
223 void part_draw_coin(float rx, float ry)
224 {
225     float r = (float) SDL_GetTicks() / 1000.f;
226     int i;
227
228     glPushAttrib(GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
229     {
230         glDisable(GL_LIGHTING);
231         glEnable(GL_COLOR_MATERIAL);
232
233         glDepthMask(GL_FALSE);
234         glBindTexture(GL_TEXTURE_2D, part_text);
235
236         for (i = 0; i < PART_MAX_COIN; i++)
237             if (part_coin[i].t > 0.f)
238                 part_draw(part_coin[i].p,
239                           part_coin[i].c,
240                           part_coin[i].t,
241                           1.f, rx, ry, r * part_coin[i].w);
242     }
243     glPopAttrib();
244 }
245
246 void part_draw_goal(float rx, float ry, float radius, float a, int spe)
247 {
248     float r = (float) SDL_GetTicks() / 1000.f;
249     int i;
250     GLfloat yel[3] = {1.0f, 1.0f, 0.0f};
251
252     glPushAttrib(GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
253     {
254         glDisable(GL_LIGHTING);
255
256         glEnable(GL_COLOR_MATERIAL);
257
258         glDepthMask(GL_FALSE);
259         glBindTexture(GL_TEXTURE_2D, part_text);
260
261         for (i = 0; i < PART_MAX_GOAL; i++)
262             if (part_goal[i].t > 0.f)
263                 part_draw(part_goal[i].p,
264                           spe ? part_goal[i].c : yel,
265                           a,
266                           radius - 0.05f, rx, ry, r * part_goal[i].w);
267     }
268     glPopAttrib();
269 }
270
271 /*---------------------------------------------------------------------------*/