bonus ball for bonus levels. Fix #56
[neverball] / share / geom.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 "geom.h"
22 #include "part.h"
23 #include "vec3.h"
24 #include "solid.h"
25 #include "image.h"
26 #include "config.h"
27
28 #define PI 3.1415926535897932
29
30 /*---------------------------------------------------------------------------*/
31
32 static GLuint ball_list;
33 static GLuint ball_text[2];
34
35 void ball_init(int b)
36 {
37     char name[MAXSTR];
38     int i, slices = b ? 32 : 16;
39     int j, stacks = b ? 16 :  8;
40
41     config_get_s(CONFIG_BALL, name, MAXSTR);
42     ball_text[0] = make_image_from_file(NULL, NULL, NULL, NULL, name);
43     
44     config_get_s(CONFIG_BALL_BONUS, name, MAXSTR);
45     ball_text[1] = make_image_from_file(NULL, NULL, NULL, NULL, name);
46
47     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
48     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
49
50     ball_list = glGenLists(1);
51     
52     glNewList(ball_list, GL_COMPILE);
53     {
54         for (i = 0; i < stacks; i++)
55         {
56             float k0 = (float)  i      / stacks;
57             float k1 = (float) (i + 1) / stacks;
58
59             float s0 = fsinf(V_PI * (k0 - 0.5));
60             float c0 = fcosf(V_PI * (k0 - 0.5));
61             float s1 = fsinf(V_PI * (k1 - 0.5));
62             float c1 = fcosf(V_PI * (k1 - 0.5));
63
64             glBegin(GL_QUAD_STRIP);
65             {
66                 for (j = 0; j <= slices; j++)
67                 {
68                     float k = (float) j / slices;
69                     float s = fsinf(V_PI * k * 2.0);
70                     float c = fcosf(V_PI * k * 2.0);
71
72                     glTexCoord2f(k, k0);
73                     glNormal3f(s * c0, c * c0, s0);
74                     glVertex3f(s * c0, c * c0, s0);
75
76                     glTexCoord2f(k, k1);
77                     glNormal3f(s * c1, c * c1, s1);
78                     glVertex3f(s * c1, c * c1, s1);
79                 }
80             }
81             glEnd();
82         }
83     }
84     glEndList();
85 }
86
87 void ball_free(void)
88 {
89     if (glIsList(ball_list))
90         glDeleteLists(ball_list, 1);
91
92     if (glIsTexture(ball_text[0]))
93         glDeleteTextures(1, &ball_text[0]);
94
95     if (glIsTexture(ball_text[1]))
96         glDeleteTextures(1, &ball_text[1]);
97
98     ball_list    = 0;
99     ball_text[0] = 0;
100     ball_text[1] = 0;
101 }
102
103 void ball_draw(int i)
104 {
105     glPushAttrib(GL_POLYGON_BIT |
106                  GL_LIGHTING_BIT |
107                  GL_DEPTH_BUFFER_BIT);
108     {
109         static const float  s[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
110         static const float  e[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
111         static const float  h[1] = { 64.0f };
112
113         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  s);
114         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  e);
115         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
116
117         glEnable(GL_COLOR_MATERIAL);
118
119         glBindTexture(GL_TEXTURE_2D, ball_text[i]);
120
121         /* Render the ball back to front in case it is translucent. */
122
123         glDepthMask(GL_FALSE);
124
125         glCullFace(GL_FRONT);
126         glCallList(ball_list);
127         glCullFace(GL_BACK);
128         glCallList(ball_list);
129
130         /* Render the ball into the depth buffer. */
131
132         glDepthMask(GL_TRUE);
133         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
134         {
135             glCallList(ball_list);
136         }
137         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
138
139         /* Ensure the ball is visible even when obscured by geometry. */
140
141         glDisable(GL_DEPTH_TEST);
142
143         glColor4f(1.0f, 1.0f, 1.0f, 0.1f);
144         glCallList(ball_list);
145     }
146     glPopAttrib();
147 }
148
149 /*---------------------------------------------------------------------------*/
150
151 static GLuint mark_list;
152
153 void mark_init(int b)
154 {
155     int i, slices = b ? 32 : 16;
156
157     mark_list = glGenLists(1);
158     
159     glNewList(mark_list, GL_COMPILE);
160     {
161         glBegin(GL_TRIANGLE_FAN);
162         {
163             glNormal3f(0.f, 1.f, 0.f);
164
165             for (i = 0; i < slices; i++)
166             {
167                 float x = fcosf(-2.f * PI * i / slices);
168                 float y = fsinf(-2.f * PI * i / slices);
169
170                 glVertex3f(x, 0, y);
171             }
172         }
173         glEnd();
174     }
175     glEndList();
176 }
177
178 void mark_draw(void)
179 {
180     glPushAttrib(GL_TEXTURE_BIT);
181     glPushAttrib(GL_LIGHTING_BIT);
182     glPushAttrib(GL_DEPTH_BUFFER_BIT);
183     {
184         glEnable(GL_COLOR_MATERIAL);
185         glDisable(GL_TEXTURE_2D);
186         glDepthMask(GL_FALSE);
187
188         glCallList(mark_list);
189     }
190     glPopAttrib();
191     glPopAttrib();
192     glPopAttrib();
193 }
194
195 void mark_free(void)
196 {
197     if (glIsList(mark_list))
198         glDeleteLists(mark_list, 1);
199
200     mark_list = 0;
201 }
202
203 /*---------------------------------------------------------------------------*/
204
205 static GLuint coin_text;
206 static GLuint coin_list;
207
208 static void coin_head(int n, float radius, float thick)
209 {
210     int i;
211
212     glBegin(GL_TRIANGLE_FAN);
213     {
214         glNormal3f(0.f, 0.f, +1.f);
215
216         for (i = 0; i < n; i++)
217         {
218             float x = fcosf(+2.f * PI * i / n);
219             float y = fsinf(+2.f * PI * i / n);
220
221             glTexCoord2f(-x * 0.5f + 0.5f, +y * 0.5f + 0.5f);
222             glVertex3f(radius * x, radius * y, +thick);
223         }
224     }
225     glEnd();
226 }
227
228 static void coin_tail(int n, float radius, float thick)
229 {
230     int i;
231
232     glBegin(GL_TRIANGLE_FAN);
233     {
234         glNormal3f(0.f, 0.f, -1.f);
235
236         for (i = 0; i < n; i++)
237         {
238             float x = fcosf(-2.f * PI * i / n);
239             float y = fsinf(-2.f * PI * i / n);
240
241             glTexCoord2f(+x * 0.5f + 0.5f, +y * 0.5f + 0.5f);
242             glVertex3f(radius * x, radius * y, -thick);
243         }
244     }
245     glEnd();
246 }
247
248 static void coin_edge(int n, float radius, float thick)
249 {
250     int i;
251
252     glBegin(GL_QUAD_STRIP);
253     {
254         for (i = 0; i <= n; i++)
255         {
256             float x = fcosf(2.f * PI * i / n);
257             float y = fsinf(2.f * PI * i / n);
258
259             glNormal3f(x, y, 0.f);
260             glVertex3f(radius * x, radius * y, +thick);
261             glVertex3f(radius * x, radius * y, -thick);
262         }
263     }
264     glEnd();
265 }
266
267 void coin_color(float *c, int n)
268 {
269     if (n >= 1)
270     {
271         c[0] = 1.0f;
272         c[1] = 1.0f;
273         c[2] = 0.2f;
274     }
275     if (n >= 5)
276     {
277         c[0] = 1.0f;
278         c[1] = 0.2f;
279         c[2] = 0.2f;
280     }
281     if (n >= 10)
282     {
283         c[0] = 0.2f;
284         c[1] = 0.2f;
285         c[2] = 1.0f;
286     }
287 }
288
289 void coin_init(int b)
290 {
291     char name[MAXSTR];
292     int n = b ? 32 : 8;
293
294     config_get_s(CONFIG_COIN, name, MAXSTR);
295
296     coin_text = make_image_from_file(NULL, NULL, NULL, NULL, name);
297     coin_list = glGenLists(1);
298
299     glNewList(coin_list, GL_COMPILE);
300     {
301         coin_edge(n, COIN_RADIUS, COIN_THICK);
302         coin_head(n, COIN_RADIUS, COIN_THICK);
303         coin_tail(n, COIN_RADIUS, COIN_THICK);
304     }
305     glEndList();
306 }
307
308 void coin_free(void)
309 {
310     if (glIsList(coin_list))
311         glDeleteLists(coin_list, 1);
312
313     if (glIsTexture(coin_text))
314         glDeleteTextures(1, &coin_text);
315
316     coin_list = 0;
317     coin_text = 0;
318 }
319
320 void coin_push(void)
321 {
322     static const float  a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
323     static const float  s[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
324     static const float  e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
325     static const float  h[1] = { 32.0f };
326
327     glPushAttrib(GL_LIGHTING_BIT);
328
329     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   a);
330     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  s);
331     glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  e);
332     glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
333
334     glEnable(GL_COLOR_MATERIAL);
335     glBindTexture(GL_TEXTURE_2D, coin_text);
336 }
337
338 void coin_draw(int n, float r)
339 {
340     float c[3];
341
342     coin_color(c, n);
343
344     glColor3fv(c);
345     glCallList(coin_list);
346 }
347
348 void coin_pull(void)
349 {
350     glPopAttrib();
351 }
352
353 /*---------------------------------------------------------------------------*/
354
355 static GLuint goal_list;
356
357 void goal_init(int b)
358 {
359     int i, n = b ? 32 : 8;
360
361     goal_list = glGenLists(1);
362
363     glNewList(goal_list, GL_COMPILE);
364     {
365         glPushAttrib(GL_TEXTURE_BIT  |
366                      GL_LIGHTING_BIT |
367                      GL_DEPTH_BUFFER_BIT);
368         {
369             glEnable(GL_COLOR_MATERIAL);
370             glDisable(GL_LIGHTING);
371             glDisable(GL_TEXTURE_2D);
372             glDepthMask(GL_FALSE);
373
374             glBegin(GL_QUAD_STRIP);
375             {
376                 for (i = 0; i <= n; i++)
377                 {
378                     float x = fcosf(2.f * PI * i / n);
379                     float y = fsinf(2.f * PI * i / n);
380             
381                     glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
382                     glVertex3f(x, 0.0f, y);
383
384                     glColor4f(1.0f, 1.0f, 0.0f, 0.0f);
385                     glVertex3f(x, GOAL_HEIGHT, y);
386                 }
387             }
388             glEnd();
389         }
390         glPopAttrib();
391     }
392     glEndList();
393 }
394
395 void goal_free(void)
396 {
397     if (glIsList(goal_list))
398         glDeleteLists(goal_list, 1);
399
400     goal_list = 0;
401 }
402
403 void goal_draw(void)
404 {
405     glCallList(goal_list);
406 }
407
408 /*---------------------------------------------------------------------------*/
409
410 static GLuint jump_list;
411
412 void jump_init(int b)
413 {
414     int k, i, n = b ? 32 : 8;
415
416     jump_list = glGenLists(2);
417
418     for (k=0; k<12; k++)
419     {
420         glNewList(jump_list + k, GL_COMPILE);
421         {
422             glPushAttrib(GL_TEXTURE_BIT  |
423                     GL_LIGHTING_BIT |
424                     GL_DEPTH_BUFFER_BIT);
425             {
426                 glEnable(GL_COLOR_MATERIAL);
427                 glDisable(GL_LIGHTING);
428                 glDisable(GL_TEXTURE_2D);
429                 glDepthMask(GL_FALSE);
430
431                 glBegin(GL_QUAD_STRIP);
432                 {
433                     for (i = 0; i <= n; i++)
434                     {
435                         float x = fcosf(2.f * PI * i / n);
436                         float y = fsinf(2.f * PI * i / n);
437
438                         glColor4f(1.0f, 1.0f, 1.0f, (k==0 ? 0.5f : 0.8f));
439                         glVertex3f(x, 0.0f, y);
440
441                         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
442                         glVertex3f(x, JUMP_HEIGHT, y);
443                     }
444                 }
445                 glEnd();
446             }
447             glPopAttrib();
448         }
449         glEndList();
450     }
451 }
452
453 void jump_free(void)
454 {
455     if (glIsList(jump_list))
456         glDeleteLists(jump_list, 1);
457
458     jump_list = 0;
459 }
460
461 void jump_draw(int highlight)
462 {
463     glCallList(jump_list + highlight);
464 }
465
466 /*---------------------------------------------------------------------------*/
467
468 static GLuint swch_list;
469
470 static GLfloat swch_colors[8][4] = {
471     {1.0f, 0.0f, 0.0f, 0.5f}, /* red out */
472     {1.0f, 0.0f, 0.0f, 0.0f},
473     {1.0f, 0.0f, 0.0f, 0.8f}, /* red in */
474     {1.0f, 0.0f, 0.0f, 0.0f},
475     {0.0f, 1.0f, 0.0f, 0.5f}, /* green out */
476     {0.0f, 1.0f, 0.0f, 0.0f},
477     {0.0f, 1.0f, 0.0f, 0.8f}, /* green in */
478     {0.0f, 1.0f, 0.0f, 0.0f}};
479
480 void swch_init(int b)
481 {
482     int k, i, n = b ? 32 : 8;
483
484     swch_list = glGenLists(4);
485
486     /* Create the display lists. */
487
488     for (k = 0; k < 4; k++)
489     {
490         glNewList(swch_list + k, GL_COMPILE);
491         {
492             glPushAttrib(GL_TEXTURE_BIT  |
493                     GL_LIGHTING_BIT |
494                     GL_DEPTH_BUFFER_BIT);
495             {
496                 glEnable(GL_COLOR_MATERIAL);
497                 glDisable(GL_LIGHTING);
498                 glDisable(GL_TEXTURE_2D);
499                 glDepthMask(GL_FALSE);
500
501                 glBegin(GL_QUAD_STRIP);
502                 {
503                     for (i = 0; i <= n; i++)
504                     {
505                         float x = fcosf(2.f * PI * i / n);
506                         float y = fsinf(2.f * PI * i / n);
507
508                         glColor4fv(swch_colors[2*k]);
509                         glVertex3f(x, 0.0f, y);
510
511                         glColor4fv(swch_colors[2*k+1]);
512                         glVertex3f(x, SWCH_HEIGHT, y);
513                     }
514                 }
515                 glEnd();
516             }
517             glPopAttrib();
518         }
519         glEndList();
520     }
521 }
522
523 void swch_free(void)
524 {
525     if (glIsList(swch_list))
526         glDeleteLists(swch_list, 2);
527
528     swch_list = 0;
529 }
530
531 void swch_draw(int b, int e)
532 {
533     glCallList(swch_list + b*2 + e);
534 }
535
536 /*---------------------------------------------------------------------------*/
537
538 static GLuint flag_list;
539
540 void flag_init(int b)
541 {
542     int i, n = b ? 8 : 4;
543
544     flag_list = glGenLists(1);
545
546     glNewList(flag_list, GL_COMPILE);
547     {
548         glPushAttrib(GL_TEXTURE_BIT | GL_LIGHTING_BIT);
549         {
550             glEnable(GL_COLOR_MATERIAL);
551             glDisable(GL_LIGHTING);
552             glDisable(GL_TEXTURE_2D);
553
554             glBegin(GL_QUAD_STRIP);
555             {
556                 for (i = 0; i <= n; i++)
557                 {
558                     float x = fcosf(2.f * PI * i / n) * 0.01f;
559                     float y = fsinf(2.f * PI * i / n) * 0.01f;
560             
561                     glColor3f(1.0f, 1.0f, 1.0f);
562                     glVertex3f(x, 0.0f,        y);
563                     glVertex3f(x, GOAL_HEIGHT, y);
564                 }
565             }
566             glEnd();
567
568             glBegin(GL_TRIANGLES);
569             {
570                 glColor3f(1.0f, 0.0f, 0.0f);
571
572                 glVertex3f(              0.0f, GOAL_HEIGHT,        0.0f);
573                 glVertex3f(GOAL_HEIGHT * 0.2f, GOAL_HEIGHT * 0.9f, 0.0f);
574                 glVertex3f(              0.0f, GOAL_HEIGHT * 0.8f, 0.0f);
575
576                 glVertex3f(              0.0f, GOAL_HEIGHT,        0.0f);
577                 glVertex3f(              0.0f, GOAL_HEIGHT * 0.8f, 0.0f);
578                 glVertex3f(GOAL_HEIGHT * 0.2f, GOAL_HEIGHT * 0.9f, 0.0f);
579             }
580             glEnd();
581         }
582         glPopAttrib();
583     }
584     glEndList();
585 }
586
587 void flag_free(void)
588 {
589     if (glIsList(flag_list))
590         glDeleteLists(flag_list, 1);
591
592     flag_list = 0;
593 }
594
595 void flag_draw(void)
596 {
597     glCallList(flag_list);
598 }
599
600 /*---------------------------------------------------------------------------*/
601 /*
602  * A note about lighting and shadow: technically speaking, it's wrong.
603  * The  light  position  and   shadow  projection  behave  as  if  the
604  * light-source rotates with the  floor.  However, the skybox does not
605  * rotate, thus the light should also remain stationary.
606  *
607  * The  correct behavior  would eliminate  a significant  3D  cue: the
608  * shadow of  the ball indicates  the ball's position relative  to the
609  * floor even  when the ball is  in the air.  This  was the motivating
610  * idea  behind the  shadow  in  the first  place,  so correct  shadow
611  * projection would only magnify the problem.
612  */
613
614 static GLuint shad_text;
615
616 void shad_init(void)
617 {
618     shad_text = make_image_from_file(NULL, NULL, NULL, NULL, IMG_SHAD);
619
620     if (config_get_d(CONFIG_SHADOW) == 2)
621     {
622         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
623         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
624     }
625 }
626
627 void shad_free(void)
628 {
629     if (glIsTexture(shad_text))
630         glDeleteTextures(1, &shad_text);
631 }
632
633 void shad_draw_set(const float *p, float r)
634 {
635     glMatrixMode(GL_TEXTURE);
636     {
637         float k = 0.25f / r;
638
639         glBindTexture(GL_TEXTURE_2D, shad_text);
640
641         glLoadIdentity();
642         glTranslatef(0.5f - k * p[0],
643                      0.5f - k * p[2], 0.f);
644         glScalef(k, k, 1.0f);
645     }
646     glMatrixMode(GL_MODELVIEW);
647 }
648
649 void shad_draw_clr(void)
650 {
651     glMatrixMode(GL_TEXTURE);
652     {
653         glLoadIdentity();
654     }
655     glMatrixMode(GL_MODELVIEW);
656 }
657
658 /*---------------------------------------------------------------------------*/
659
660 void fade_draw(float k)
661 {
662     int w = config_get_d(CONFIG_WIDTH);
663     int h = config_get_d(CONFIG_HEIGHT);
664
665     if (k > 0.0f)
666     {
667         config_push_ortho();
668         glPushAttrib(GL_TEXTURE_BIT  |
669                      GL_LIGHTING_BIT |
670                      GL_COLOR_BUFFER_BIT |
671                      GL_DEPTH_BUFFER_BIT);
672         {
673             glEnable(GL_COLOR_MATERIAL);
674             glDisable(GL_LIGHTING);
675             glDisable(GL_DEPTH_TEST);
676             glDisable(GL_TEXTURE_2D);
677             
678             glColor4f(0.0f, 0.0f, 0.0f, k);
679
680             glBegin(GL_QUADS);
681             {
682                 glVertex2i(0, 0);
683                 glVertex2i(w, 0);
684                 glVertex2i(w, h);
685                 glVertex2i(0, h);
686             }
687             glEnd();
688         }
689         glPopAttrib();
690         config_pop_matrix();
691     }
692 }
693
694 /*---------------------------------------------------------------------------*/