d8ee64976e56329be2ef5ec05c3b5e87b05a8271
[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_grup;
207 static GLuint coin_grdn;
208 static GLuint coin_list;
209
210 static void coin_head(int n, float radius, float thick)
211 {
212     int i;
213
214     glBegin(GL_TRIANGLE_FAN);
215     {
216         glNormal3f(0.f, 0.f, +1.f);
217
218         for (i = 0; i < n; i++)
219         {
220             float x = fcosf(+2.f * PI * i / n);
221             float y = fsinf(+2.f * PI * i / n);
222
223             glTexCoord2f(-x * 0.5f + 0.5f, +y * 0.5f + 0.5f);
224             glVertex3f(radius * x, radius * y, +thick);
225         }
226     }
227     glEnd();
228 }
229
230 static void coin_tail(int n, float radius, float thick)
231 {
232     int i;
233
234     glBegin(GL_TRIANGLE_FAN);
235     {
236         glNormal3f(0.f, 0.f, -1.f);
237
238         for (i = 0; i < n; i++)
239         {
240             float x = fcosf(-2.f * PI * i / n);
241             float y = fsinf(-2.f * PI * i / n);
242
243             glTexCoord2f(+x * 0.5f + 0.5f, +y * 0.5f + 0.5f);
244             glVertex3f(radius * x, radius * y, -thick);
245         }
246     }
247     glEnd();
248 }
249
250 static void coin_edge(int n, float radius, float thick)
251 {
252     int i;
253
254     glBegin(GL_QUAD_STRIP);
255     {
256         for (i = 0; i <= n; i++)
257         {
258             float x = fcosf(2.f * PI * i / n);
259             float y = fsinf(2.f * PI * i / n);
260
261             glNormal3f(x, y, 0.f);
262             glVertex3f(radius * x, radius * y, +thick);
263             glVertex3f(radius * x, radius * y, -thick);
264         }
265     }
266     glEnd();
267 }
268
269 void coin_color(float *c, int n)
270 {
271     if (n >= 1)
272     {
273         c[0] = 1.0f;
274         c[1] = 1.0f;
275         c[2] = 0.2f;
276     }
277     if (n >= 5)
278     {
279         c[0] = 1.0f;
280         c[1] = 0.2f;
281         c[2] = 0.2f;
282     }
283     if (n >= 10)
284     {
285         c[0] = 0.2f;
286         c[1] = 0.2f;
287         c[2] = 1.0f;
288     }
289     if (n == 50) /*white's kind of boring, but you can do a colored png that way.*/
290     {
291         c[0] = 1.0f;
292         c[1] = 1.0f;
293         c[2] = 1.0f;
294     }
295     if (n == 150)
296     {
297         c[0] = 1.0f;
298         c[1] = 1.0f;
299         c[2] = 1.0f;
300     }
301 }
302
303 void coin_init(int b)
304 {
305     int n = b ? 32 : 8;
306
307     coin_text = make_image_from_file(NULL, NULL, NULL, NULL, IMG_COIN);
308     coin_grup = make_image_from_file(NULL, NULL, NULL, NULL, IMG_COIN_GRUP);
309     coin_grdn = make_image_from_file(NULL, NULL, NULL, NULL, IMG_COIN_GRDN);
310     coin_list = glGenLists(1);
311
312     glNewList(coin_list, GL_COMPILE);
313     {
314         coin_edge(n, COIN_RADIUS, COIN_THICK);
315         coin_head(n, COIN_RADIUS, COIN_THICK);
316         coin_tail(n, COIN_RADIUS, COIN_THICK);
317     }
318     glEndList();
319 }
320
321 void coin_free(void)
322 {
323     if (glIsList(coin_list))
324         glDeleteLists(coin_list, 1);
325
326     if (glIsTexture(coin_text))
327         glDeleteTextures(1, &coin_text);
328
329     if (glIsTexture(coin_grup))
330         glDeleteTextures(1, &coin_grup);
331
332     if (glIsTexture(coin_grdn))
333         glDeleteTextures(1, &coin_grdn);
334
335     coin_list = 0;
336     coin_text = 0;
337     coin_grup = 0;
338     coin_grdn = 0;
339 }
340
341 void coin_push(void)
342 {
343     static const float  a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
344     static const float  s[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
345     static const float  e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
346     static const float  h[1] = { 32.0f };
347
348     glPushAttrib(GL_LIGHTING_BIT);
349
350     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   a);
351     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  s);
352     glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  e);
353     glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
354 }
355
356 void coin_push_text(int n)
357 {
358     glEnable(GL_COLOR_MATERIAL);
359     if (n == 50)
360         glBindTexture(GL_TEXTURE_2D, coin_grdn);
361     else if (n == 150)
362         glBindTexture(GL_TEXTURE_2D, coin_grup);
363     else
364         glBindTexture(GL_TEXTURE_2D, coin_text);
365 }
366
367 void coin_draw(int n, float r)
368 {
369     float c[3];
370
371     coin_color(c, n);
372
373     glColor3fv(c);
374     glCallList(coin_list);
375 }
376
377 void coin_pull(void)
378 {
379     glPopAttrib();
380 }
381
382 /*---------------------------------------------------------------------------*/
383
384 static GLuint goal_list;
385
386 void goal_init(int b)
387 {
388     int i, n = b ? 32 : 8;
389
390     goal_list = glGenLists(1);
391
392     glNewList(goal_list, GL_COMPILE);
393     {
394         glPushAttrib(GL_TEXTURE_BIT  |
395                      GL_LIGHTING_BIT |
396                      GL_DEPTH_BUFFER_BIT);
397         {
398             glEnable(GL_COLOR_MATERIAL);
399             glDisable(GL_LIGHTING);
400             glDisable(GL_TEXTURE_2D);
401             glDepthMask(GL_FALSE);
402
403             glBegin(GL_QUAD_STRIP);
404             {
405                 for (i = 0; i <= n; i++)
406                 {
407                     float x = fcosf(2.f * PI * i / n);
408                     float y = fsinf(2.f * PI * i / n);
409
410                     glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
411                     glVertex3f(x, 0.0f, y);
412
413                     glColor4f(1.0f, 1.0f, 0.0f, 0.0f);
414                     glVertex3f(x, GOAL_HEIGHT, y);
415                 }
416             }
417             glEnd();
418         }
419         glPopAttrib();
420     }
421     glEndList();
422 }
423
424 void goal_free(void)
425 {
426     if (glIsList(goal_list))
427         glDeleteLists(goal_list, 1);
428
429     goal_list = 0;
430 }
431
432 void goal_draw(void)
433 {
434     glCallList(goal_list);
435 }
436
437 /*---------------------------------------------------------------------------*/
438
439 static GLuint jump_list;
440
441 void jump_init(int b)
442 {
443     int k, i, n = b ? 32 : 8;
444
445     jump_list = glGenLists(2);
446
447     for (k = 0; k < 12; k++)
448     {
449         glNewList(jump_list + k, GL_COMPILE);
450         {
451             glPushAttrib(GL_TEXTURE_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
452             {
453                 glEnable(GL_COLOR_MATERIAL);
454                 glDisable(GL_LIGHTING);
455                 glDisable(GL_TEXTURE_2D);
456                 glDepthMask(GL_FALSE);
457
458                 glBegin(GL_QUAD_STRIP);
459                 {
460                     for (i = 0; i <= n; i++)
461                     {
462                         float x = fcosf(2.f * PI * i / n);
463                         float y = fsinf(2.f * PI * i / n);
464
465                         glColor4f(1.0f, 1.0f, 1.0f, (k == 0 ? 0.5f : 0.8f));
466                         glVertex3f(x, 0.0f, y);
467
468                         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
469                         glVertex3f(x, JUMP_HEIGHT, y);
470                     }
471                 }
472                 glEnd();
473             }
474             glPopAttrib();
475         }
476         glEndList();
477     }
478 }
479
480 void jump_free(void)
481 {
482     if (glIsList(jump_list))
483         glDeleteLists(jump_list, 1);
484
485     jump_list = 0;
486 }
487
488 void jump_draw(int highlight)
489 {
490     glCallList(jump_list + highlight);
491 }
492
493 /*---------------------------------------------------------------------------*/
494
495 static GLuint swch_list;
496
497 static GLfloat swch_colors[8][4] = {
498     {1.0f, 0.0f, 0.0f, 0.5f}, /* red out */
499     {1.0f, 0.0f, 0.0f, 0.0f},
500     {1.0f, 0.0f, 0.0f, 0.8f}, /* red in */
501     {1.0f, 0.0f, 0.0f, 0.0f},
502     {0.0f, 1.0f, 0.0f, 0.5f}, /* green out */
503     {0.0f, 1.0f, 0.0f, 0.0f},
504     {0.0f, 1.0f, 0.0f, 0.8f}, /* green in */
505     {0.0f, 1.0f, 0.0f, 0.0f}};
506
507 void swch_init(int b)
508 {
509     int k, i, n = b ? 32 : 8;
510
511     swch_list = glGenLists(4);
512
513     /* Create the display lists. */
514
515     for (k = 0; k < 4; k++)
516     {
517         glNewList(swch_list + k, GL_COMPILE);
518         {
519             glPushAttrib(GL_TEXTURE_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
520             {
521                 glEnable(GL_COLOR_MATERIAL);
522                 glDisable(GL_LIGHTING);
523                 glDisable(GL_TEXTURE_2D);
524                 glDepthMask(GL_FALSE);
525
526                 glBegin(GL_QUAD_STRIP);
527                 {
528                     for (i = 0; i <= n; i++)
529                     {
530                         float x = fcosf(2.f * PI * i / n);
531                         float y = fsinf(2.f * PI * i / n);
532
533                         glColor4fv(swch_colors[2 * k]);
534                         glVertex3f(x, 0.0f, y);
535
536                         glColor4fv(swch_colors[2 * k + 1]);
537                         glVertex3f(x, SWCH_HEIGHT, y);
538                     }
539                 }
540                 glEnd();
541             }
542             glPopAttrib();
543         }
544         glEndList();
545     }
546 }
547
548 void swch_free(void)
549 {
550     if (glIsList(swch_list))
551         glDeleteLists(swch_list, 2);
552
553     swch_list = 0;
554 }
555
556 void swch_draw(int b, int e)
557 {
558     glCallList(swch_list + b * 2 + e);
559 }
560
561 /*---------------------------------------------------------------------------*/
562
563 static GLuint flag_list;
564
565 void flag_init(int b)
566 {
567     int i, n = b ? 8 : 4;
568
569     flag_list = glGenLists(1);
570
571     glNewList(flag_list, GL_COMPILE);
572     {
573         glPushAttrib(GL_TEXTURE_BIT | GL_LIGHTING_BIT);
574         {
575             glEnable(GL_COLOR_MATERIAL);
576             glDisable(GL_LIGHTING);
577             glDisable(GL_TEXTURE_2D);
578
579             glBegin(GL_QUAD_STRIP);
580             {
581                 for (i = 0; i <= n; i++)
582                 {
583                     float x = fcosf(2.f * PI * i / n) * 0.01f;
584                     float y = fsinf(2.f * PI * i / n) * 0.01f;
585
586                     glColor3f(1.0f, 1.0f, 1.0f);
587                     glVertex3f(x, 0.0f,        y);
588                     glVertex3f(x, GOAL_HEIGHT, y);
589                 }
590             }
591             glEnd();
592
593             glBegin(GL_TRIANGLES);
594             {
595                 glColor3f(1.0f, 0.0f, 0.0f);
596
597                 glVertex3f(              0.0f, GOAL_HEIGHT,        0.0f);
598                 glVertex3f(GOAL_HEIGHT * 0.2f, GOAL_HEIGHT * 0.9f, 0.0f);
599                 glVertex3f(              0.0f, GOAL_HEIGHT * 0.8f, 0.0f);
600
601                 glVertex3f(              0.0f, GOAL_HEIGHT,        0.0f);
602                 glVertex3f(              0.0f, GOAL_HEIGHT * 0.8f, 0.0f);
603                 glVertex3f(GOAL_HEIGHT * 0.2f, GOAL_HEIGHT * 0.9f, 0.0f);
604             }
605             glEnd();
606         }
607         glPopAttrib();
608     }
609     glEndList();
610 }
611
612 void flag_free(void)
613 {
614     if (glIsList(flag_list))
615         glDeleteLists(flag_list, 1);
616
617     flag_list = 0;
618 }
619
620 void flag_draw(void)
621 {
622     glCallList(flag_list);
623 }
624
625 /*---------------------------------------------------------------------------*/
626 /*
627  * A note about lighting and shadow: technically speaking, it's wrong.
628  * The  light  position  and   shadow  projection  behave  as  if  the
629  * light-source rotates with the  floor.  However, the skybox does not
630  * rotate, thus the light should also remain stationary.
631  *
632  * The  correct behavior  would eliminate  a significant  3D  cue: the
633  * shadow of  the ball indicates  the ball's position relative  to the
634  * floor even  when the ball is  in the air.  This  was the motivating
635  * idea  behind the  shadow  in  the first  place,  so correct  shadow
636  * projection would only magnify the problem.
637  */
638
639 static GLuint shad_text;
640
641 void shad_init(void)
642 {
643     shad_text = make_image_from_file(NULL, NULL, NULL, NULL, IMG_SHAD);
644
645     if (config_get_d(CONFIG_SHADOW) == 2)
646     {
647         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
648         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
649     }
650 }
651
652 void shad_free(void)
653 {
654     if (glIsTexture(shad_text))
655         glDeleteTextures(1, &shad_text);
656 }
657
658 void shad_draw_set(const float *p, float r)
659 {
660     glMatrixMode(GL_TEXTURE);
661     {
662         float k = 0.25f / r;
663
664         glBindTexture(GL_TEXTURE_2D, shad_text);
665
666         glLoadIdentity();
667         glTranslatef(0.5f - k * p[0],
668                      0.5f - k * p[2], 0.f);
669         glScalef(k, k, 1.0f);
670     }
671     glMatrixMode(GL_MODELVIEW);
672 }
673
674 void shad_draw_clr(void)
675 {
676     glMatrixMode(GL_TEXTURE);
677     {
678         glLoadIdentity();
679     }
680     glMatrixMode(GL_MODELVIEW);
681 }
682
683 /*---------------------------------------------------------------------------*/
684
685 void fade_draw(float k)
686 {
687     int w = config_get_d(CONFIG_WIDTH);
688     int h = config_get_d(CONFIG_HEIGHT);
689
690     if (k > 0.0f)
691     {
692         config_push_ortho();
693         glPushAttrib(GL_TEXTURE_BIT  |
694                      GL_LIGHTING_BIT |
695                      GL_COLOR_BUFFER_BIT |
696                      GL_DEPTH_BUFFER_BIT);
697         {
698             glEnable(GL_COLOR_MATERIAL);
699             glDisable(GL_LIGHTING);
700             glDisable(GL_DEPTH_TEST);
701             glDisable(GL_TEXTURE_2D);
702
703             glColor4f(0.0f, 0.0f, 0.0f, k);
704
705             glBegin(GL_QUADS);
706             {
707                 glVertex2i(0, 0);
708                 glVertex2i(w, 0);
709                 glVertex2i(w, h);
710                 glVertex2i(0, h);
711             }
712             glEnd();
713         }
714         glPopAttrib();
715         config_pop_matrix();
716     }
717 }
718
719 /*---------------------------------------------------------------------------*/