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