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