f543391ffef0332721c81fe25dcc50f31e5530e4
[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] = { 64.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
180         glCallList(mark_list);
181     }
182     glDepthMask(GL_TRUE);
183     glEnable(GL_TEXTURE_2D);
184     glDisable(GL_COLOR_MATERIAL);
185 }
186
187 void mark_free(void)
188 {
189     if (glIsList(mark_list))
190         glDeleteLists(mark_list, 1);
191
192     mark_list = 0;
193 }
194
195 /*---------------------------------------------------------------------------*/
196
197 static void coin_head(int n, float radius, float thick)
198 {
199     int i;
200
201     glBegin(GL_TRIANGLE_FAN);
202     {
203         glNormal3f(0.f, 0.f, +1.f);
204
205         for (i = 0; i < n; i++)
206         {
207             float x = fcosf(+2.f * PI * i / n);
208             float y = fsinf(+2.f * PI * i / n);
209
210             glTexCoord2f(-x * 0.5f + 0.5f, +y * 0.5f + 0.5f);
211             glVertex3f(radius * x, radius * y, +thick);
212         }
213     }
214     glEnd();
215 }
216
217 static void coin_tail(int n, float radius, float thick)
218 {
219     int i;
220
221     glBegin(GL_TRIANGLE_FAN);
222     {
223         glNormal3f(0.f, 0.f, -1.f);
224
225         for (i = 0; i < n; i++)
226         {
227             float x = fcosf(-2.f * PI * i / n);
228             float y = fsinf(-2.f * PI * i / n);
229
230             glTexCoord2f(+x * 0.5f + 0.5f, +y * 0.5f + 0.5f);
231             glVertex3f(radius * x, radius * y, -thick);
232         }
233     }
234     glEnd();
235 }
236
237 static void coin_edge(int n, float radius, float thick)
238 {
239     int i;
240
241     glBegin(GL_QUAD_STRIP);
242     {
243         for (i = 0; i <= n; i++)
244         {
245             float x = fcosf(2.f * PI * i / n);
246             float y = fsinf(2.f * PI * i / n);
247
248             glNormal3f(x, y, 0.f);
249             glVertex3f(radius * x, radius * y, +thick);
250             glVertex3f(radius * x, radius * y, -thick);
251         }
252     }
253     glEnd();
254 }
255
256 /*---------------------------------------------------------------------------*/
257
258 static GLuint item_coin_text;
259 static GLuint item_grow_text;
260 static GLuint item_shrink_text;
261 static GLuint item_list;
262
263 void item_color(const struct s_item *hp, float *c)
264 {
265     switch (hp->t)
266     {
267
268     case ITEM_COIN:
269
270         if (hp->n >= 1)
271         {
272             c[0] = 1.0f;
273             c[1] = 1.0f;
274             c[2] = 0.2f;
275         }
276         if (hp->n >= 5)
277         {
278             c[0] = 1.0f;
279             c[1] = 0.2f;
280             c[2] = 0.2f;
281         }
282         if (hp->n >= 10)
283         {
284             c[0] = 0.2f;
285             c[1] = 0.2f;
286             c[2] = 1.0f;
287         }
288         break;
289
290     case ITEM_GROW:
291     case ITEM_SHRINK:
292
293     default:
294
295         c[0] = 1.0f;
296         c[1] = 1.0f;
297         c[2] = 1.0f;
298
299         break;
300     }
301 }
302
303 void item_init(int b)
304 {
305     int n = b ? 32 : 8;
306
307     item_coin_text   = make_image_from_file(IMG_ITEM_COIN);
308     item_grow_text   = make_image_from_file(IMG_ITEM_GROW);
309     item_shrink_text = make_image_from_file(IMG_ITEM_SHRINK);
310
311     item_list = glGenLists(1);
312
313     glNewList(item_list, GL_COMPILE);
314     {
315         coin_edge(n, COIN_RADIUS, COIN_THICK);
316         coin_head(n, COIN_RADIUS, COIN_THICK);
317         coin_tail(n, COIN_RADIUS, COIN_THICK);
318     }
319     glEndList();
320 }
321
322 void item_free(void)
323 {
324     if (glIsList(item_list))
325         glDeleteLists(item_list, 1);
326
327     if (glIsTexture(item_coin_text))
328         glDeleteTextures(1, &item_coin_text);
329
330     if (glIsTexture(item_grow_text))
331         glDeleteTextures(1, &item_grow_text);
332
333     if (glIsTexture(item_shrink_text))
334         glDeleteTextures(1, &item_shrink_text);
335
336     item_list = 0;
337     item_coin_text = 0;
338     item_grow_text = 0;
339     item_shrink_text = 0;
340 }
341
342 void item_push(int type)
343 {
344     static const float  a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
345     static const float  s[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
346     static const float  e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
347     static const float  h[1] = { 32.0f };
348
349     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   a);
350     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  s);
351     glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  e);
352     glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
353
354     glEnable(GL_COLOR_MATERIAL);
355
356     switch (type)
357     {
358     case ITEM_COIN:
359         glBindTexture(GL_TEXTURE_2D, item_coin_text);
360         break;
361
362     case ITEM_GROW:
363         glBindTexture(GL_TEXTURE_2D, item_grow_text);
364         break;
365
366     case ITEM_SHRINK:
367         glBindTexture(GL_TEXTURE_2D, item_shrink_text);
368         break;
369     }
370 }
371
372 void item_draw(const struct s_item *hp, float r)
373 {
374     float c[3];
375
376     item_color(hp, c);
377
378     glColor3fv(c);
379     glCallList(item_list);
380 }
381
382 void item_pull(void)
383 {
384     glColor3f(1.0f, 1.0f, 1.0f);
385     glDisable(GL_COLOR_MATERIAL);
386 }
387
388 /*---------------------------------------------------------------------------*/
389
390 static GLuint goal_list;
391
392 void goal_init(int b)
393 {
394     int i, n = b ? 32 : 8;
395
396     goal_list = glGenLists(1);
397
398     glNewList(goal_list, GL_COMPILE);
399     {
400         glBegin(GL_QUAD_STRIP);
401         {
402             for (i = 0; i <= n; i++)
403             {
404                 float x = fcosf(2.f * PI * i / n);
405                 float y = fsinf(2.f * PI * i / n);
406
407                 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
408                 glVertex3f(x, 0.0f, y);
409
410                 glColor4f(1.0f, 1.0f, 0.0f, 0.0f);
411                 glVertex3f(x, GOAL_HEIGHT, y);
412             }
413         }
414         glEnd();
415     }
416     glEndList();
417 }
418
419 void goal_free(void)
420 {
421     if (glIsList(goal_list))
422         glDeleteLists(goal_list, 1);
423
424     goal_list = 0;
425 }
426
427 void goal_draw(void)
428 {
429     glCallList(goal_list);
430 }
431
432 /*---------------------------------------------------------------------------*/
433
434 static GLuint jump_list;
435
436 void jump_init(int b)
437 {
438     int k, i, n = b ? 32 : 8;
439
440     jump_list = glGenLists(2);
441
442     for (k = 0; k < 2; k++)
443     {
444         glNewList(jump_list + k, GL_COMPILE);
445         {
446             glBegin(GL_QUAD_STRIP);
447             {
448                 for (i = 0; i <= n; i++)
449                 {
450                     float x = fcosf(2.f * PI * i / n);
451                     float y = fsinf(2.f * PI * i / n);
452
453                     glColor4f(1.0f, 1.0f, 1.0f, (k == 0 ? 0.5f : 0.8f));
454                     glVertex3f(x, 0.0f, y);
455
456                     glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
457                     glVertex3f(x, JUMP_HEIGHT, y);
458                 }
459             }
460             glEnd();
461         }
462         glEndList();
463     }
464 }
465
466 void jump_free(void)
467 {
468     glDeleteLists(jump_list, 2);
469     jump_list = 0;
470 }
471
472 void jump_draw(int highlight)
473 {
474     glCallList(jump_list + highlight);
475 }
476
477 /*---------------------------------------------------------------------------*/
478
479 static GLuint swch_list;
480
481 static GLfloat swch_colors[8][4] = {
482     { 1.0f, 0.0f, 0.0f, 0.5f }, /* red out */
483     { 1.0f, 0.0f, 0.0f, 0.0f },
484     { 1.0f, 0.0f, 0.0f, 0.8f }, /* red in */
485     { 1.0f, 0.0f, 0.0f, 0.0f },
486     { 0.0f, 1.0f, 0.0f, 0.5f }, /* green out */
487     { 0.0f, 1.0f, 0.0f, 0.0f },
488     { 0.0f, 1.0f, 0.0f, 0.8f }, /* green in */
489     { 0.0f, 1.0f, 0.0f, 0.0f }};
490
491 void swch_init(int b)
492 {
493     int k, i, n = b ? 32 : 8;
494
495     swch_list = glGenLists(4);
496
497     /* Create the display lists. */
498
499     for (k = 0; k < 4; k++)
500     {
501         glNewList(swch_list + k, GL_COMPILE);
502         {
503             glBegin(GL_QUAD_STRIP);
504             {
505                 for (i = 0; i <= n; i++)
506                 {
507                     float x = fcosf(2.f * PI * i / n);
508                     float y = fsinf(2.f * PI * i / n);
509
510                     glColor4fv(swch_colors[2 * k + 0]);
511                     glVertex3f(x, 0.0f, y);
512
513                     glColor4fv(swch_colors[2 * k + 1]);
514                     glVertex3f(x, SWCH_HEIGHT, y);
515                 }
516             }
517             glEnd();
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         glEnable(GL_COLOR_MATERIAL);
549         glDisable(GL_LIGHTING);
550         glDisable(GL_TEXTURE_2D);
551         {
552             glBegin(GL_QUAD_STRIP);
553             {
554                 for (i = 0; i <= n; i++)
555                 {
556                     float x = fcosf(2.f * PI * i / n) * 0.01f;
557                     float y = fsinf(2.f * PI * i / n) * 0.01f;
558
559                     glColor3f(1.0f, 1.0f, 1.0f);
560                     glVertex3f(x, 0.0f,        y);
561                     glVertex3f(x, GOAL_HEIGHT, y);
562                 }
563             }
564             glEnd();
565
566             glBegin(GL_TRIANGLES);
567             {
568                 glColor3f(1.0f, 0.0f, 0.0f);
569
570                 glVertex3f(              0.0f, GOAL_HEIGHT,        0.0f);
571                 glVertex3f(GOAL_HEIGHT * 0.2f, GOAL_HEIGHT * 0.9f, 0.0f);
572                 glVertex3f(              0.0f, GOAL_HEIGHT * 0.8f, 0.0f);
573
574                 glVertex3f(              0.0f, GOAL_HEIGHT,        0.0f);
575                 glVertex3f(              0.0f, GOAL_HEIGHT * 0.8f, 0.0f);
576                 glVertex3f(GOAL_HEIGHT * 0.2f, GOAL_HEIGHT * 0.9f, 0.0f);
577             }
578             glEnd();
579         }
580         glEnable(GL_TEXTURE_2D);
581         glEnable(GL_LIGHTING);
582         glDisable(GL_COLOR_MATERIAL);
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(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     if (k > 0.0f)
663     {
664         int w = config_get_d(CONFIG_WIDTH);
665         int h = config_get_d(CONFIG_HEIGHT);
666
667         config_push_ortho();
668         {
669             glEnable(GL_COLOR_MATERIAL);
670             glDisable(GL_LIGHTING);
671             glDisable(GL_DEPTH_TEST);
672             glDisable(GL_TEXTURE_2D);
673
674             glColor4f(0.0f, 0.0f, 0.0f, k);
675
676             glBegin(GL_QUADS);
677             {
678                 glVertex2i(0, 0);
679                 glVertex2i(w, 0);
680                 glVertex2i(w, h);
681                 glVertex2i(0, h);
682             }
683             glEnd();
684
685             glEnable(GL_TEXTURE_2D);
686             glEnable(GL_DEPTH_TEST);
687             glEnable(GL_LIGHTING);
688             glDisable(GL_COLOR_MATERIAL);
689         }
690         config_pop_matrix();
691     }
692 }
693
694 /*---------------------------------------------------------------------------*/