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