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