Merge branch 'gles'
[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 "image.h"
25 #include "config.h"
26 #include "video.h"
27
28 #include "solid_draw.h"
29
30 /*---------------------------------------------------------------------------*/
31
32 static const struct tex_env *curr_tex_env;
33
34 static void tex_env_conf_default(int, int);
35 static void tex_env_conf_shadow(int, int);
36
37 const struct tex_env tex_env_default = {
38     tex_env_conf_default,
39     1,
40     {
41         { GL_TEXTURE0, TEX_STAGE_TEXTURE }
42     }
43 };
44
45 const struct tex_env tex_env_shadow = {
46     tex_env_conf_shadow,
47     2,
48     {
49         { GL_TEXTURE0, TEX_STAGE_SHADOW },
50         { GL_TEXTURE1, TEX_STAGE_TEXTURE }
51     }
52 };
53
54 const struct tex_env tex_env_shadow_clip = {
55     tex_env_conf_shadow,
56     3,
57     {
58         { GL_TEXTURE0, TEX_STAGE_SHADOW },
59         { GL_TEXTURE1, TEX_STAGE_CLIP },
60         { GL_TEXTURE2, TEX_STAGE_TEXTURE }
61     }
62 };
63
64 static void tex_env_conf_default(int stage, int enable)
65 {
66     switch (stage)
67     {
68     case TEX_STAGE_TEXTURE:
69         if (enable)
70         {
71             glEnable(GL_TEXTURE_2D);
72
73             /* Modulate is the default mode. */
74
75             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
76
77             glMatrixMode(GL_TEXTURE);
78             glLoadIdentity();
79             glMatrixMode(GL_MODELVIEW);
80         }
81         else
82         {
83             glDisable(GL_TEXTURE_2D);
84         }
85         break;
86     }
87 }
88
89 static void tex_env_conf_shadow(int stage, int enable)
90 {
91     switch (stage)
92     {
93     case TEX_STAGE_SHADOW:
94         if (enable)
95         {
96             glDisable(GL_TEXTURE_2D);
97
98             /* Modulate primary color and shadow alpha. */
99
100             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
101
102             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
103             glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
104             glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
105             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
106             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_ALPHA);
107
108             /* Copy incoming alpha. */
109
110             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
111             glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
112             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
113
114             glMatrixMode(GL_TEXTURE);
115             glLoadIdentity();
116             glMatrixMode(GL_MODELVIEW);
117         }
118         else
119         {
120             glDisable(GL_TEXTURE_2D);
121         }
122         break;
123
124     case TEX_STAGE_CLIP:
125         if (enable)
126         {
127             glDisable(GL_TEXTURE_2D);
128
129             /* Interpolate shadowed and non-shadowed primary color. */
130
131             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
132
133             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
134             glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
135             glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR);
136             glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_TEXTURE);
137             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
138             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
139             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
140
141             /* Copy incoming alpha. */
142
143             glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
144             glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
145             glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
146
147             glMatrixMode(GL_TEXTURE);
148             glLoadIdentity();
149             glMatrixMode(GL_MODELVIEW);
150         }
151         else
152         {
153             glDisable(GL_TEXTURE_2D);
154         }
155         break;
156
157     case TEX_STAGE_TEXTURE:
158         tex_env_conf_default(TEX_STAGE_TEXTURE, enable);
159         break;
160     }
161 }
162
163 static void tex_env_conf(const struct tex_env *env, int enable)
164 {
165     size_t i;
166
167     for (i = 0; i < env->count; i++)
168     {
169         const struct tex_stage *st = &env->stages[i];
170         glActiveTexture_(st->unit);
171         glClientActiveTexture_(st->unit);
172         env->conf(st->stage, enable);
173     }
174
175     /* Last stage remains selected. */
176 }
177
178 /*
179  * Set up current texture pipeline.
180  */
181 void tex_env_active(const struct tex_env *env)
182 {
183     if (curr_tex_env == env)
184         return;
185
186     if (curr_tex_env)
187     {
188         tex_env_conf(curr_tex_env, 0);
189         curr_tex_env = NULL;
190     }
191
192     tex_env_conf(env, 1);
193     curr_tex_env = env;
194 }
195
196 /*
197  * Select stage of the current texture pipeline.
198  */
199 int tex_env_stage(int stage)
200 {
201     size_t i;
202
203     if (curr_tex_env)
204     {
205         for (i = 0; i < curr_tex_env->count; i++)
206         {
207             const struct tex_stage *st = &curr_tex_env->stages[i];
208
209             if (st->stage == stage)
210             {
211                 glActiveTexture_(st->unit);
212                 glClientActiveTexture_(st->unit);
213                 return 1;
214             }
215         }
216     }
217     return 0;
218 }
219
220 /*---------------------------------------------------------------------------*/
221
222 static struct s_full beam;
223 static struct s_full jump;
224 static struct s_full goal;
225 static struct s_full flag;
226 static struct s_full mark;
227 static struct s_full vect;
228 static struct s_full back;
229
230 static int back_state = 0;
231
232 /*---------------------------------------------------------------------------*/
233
234 void geom_init(void)
235 {
236     sol_load_full(&beam, "geom/beam/beam.sol", 0);
237     sol_load_full(&jump, "geom/jump/jump.sol", 0);
238     sol_load_full(&goal, "geom/goal/goal.sol", 0);
239     sol_load_full(&flag, "geom/flag/flag.sol", 0);
240     sol_load_full(&mark, "geom/mark/mark.sol", 0);
241     sol_load_full(&vect, "geom/vect/vect.sol", 0);
242 }
243
244 void geom_free(void)
245 {
246     sol_free_full(&vect);
247     sol_free_full(&mark);
248     sol_free_full(&flag);
249     sol_free_full(&goal);
250     sol_free_full(&jump);
251     sol_free_full(&beam);
252 }
253
254 /*---------------------------------------------------------------------------*/
255
256 void back_init(const char *name)
257 {
258     if (back_state)
259         back_free();
260
261     /* Load the background SOL and modify its material in-place to use the   */
262     /* named gradient texture.                                               */
263
264     sol_load_full(&back, "geom/back/back.sol", 0);
265     back.draw.mv[0].o = make_image_from_file(name);
266
267     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
268
269     back_state = 1;
270 }
271
272 void back_free(void)
273 {
274     if (back_state)
275         sol_free_full(&back);
276
277     back_state = 0;
278 }
279
280 /*---------------------------------------------------------------------------*/
281
282 static void jump_part_draw(struct s_rend *rend, GLfloat s, GLfloat a)
283 {
284     glMatrixMode(GL_TEXTURE);
285     glTranslatef(s, 0.0f, 0.0f);
286     glMatrixMode(GL_MODELVIEW);
287
288     glRotatef(a, 0.0f, 1.0f, 0.0f);
289     sol_draw(&jump.draw, rend, 1, 1);
290     glScalef(0.9f, 0.9f, 0.9f);
291 }
292
293 static void goal_part_draw(struct s_rend *rend, GLfloat s)
294 {
295     glMatrixMode(GL_TEXTURE);
296     glTranslatef(0.0f, -s, 0.0f);
297     glMatrixMode(GL_MODELVIEW);
298
299     sol_draw(&goal.draw, rend, 1, 1);
300     glScalef(0.8f, 1.1f, 0.8f);
301 }
302
303 /*---------------------------------------------------------------------------*/
304
305 void goal_draw(struct s_rend *rend, float t)
306 {
307     glPushMatrix();
308     {
309         glScalef(1.0f, 3.0f, 1.0f);
310         glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
311
312         sol_draw(&beam.draw, rend, 1, 1);
313
314         goal_part_draw(rend, t * 0.10f);
315         goal_part_draw(rend, t * 0.10f);
316         goal_part_draw(rend, t * 0.10f);
317         goal_part_draw(rend, t * 0.10f);
318
319         glMatrixMode(GL_TEXTURE);
320         glLoadIdentity();
321         glMatrixMode(GL_MODELVIEW);
322
323         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
324     }
325     glPopMatrix();
326 }
327
328 void jump_draw(struct s_rend *rend, float t, int h)
329 {
330     static GLfloat c[4][4] = {
331         { 0.75f, 0.5f, 1.0f, 0.5f },
332         { 0.75f, 0.5f, 1.0f, 0.8f },
333     };
334
335     glPushMatrix();
336     {
337         glColor4f(c[h][0], c[h][1], c[h][2], c[h][3]);
338
339         glScalef(1.0f, 2.0f, 1.0f);
340
341         sol_draw(&beam.draw, rend, 1, 1);
342
343         jump_part_draw(rend, t * 0.15f, t * 360.0f);
344         jump_part_draw(rend, t * 0.20f, t * 360.0f);
345         jump_part_draw(rend, t * 0.25f, t * 360.0f);
346
347         glMatrixMode(GL_TEXTURE);
348         glLoadIdentity();
349         glMatrixMode(GL_MODELVIEW);
350
351         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
352     }
353     glPopMatrix();
354 }
355
356 void swch_draw(struct s_rend *rend, int b, int e)
357 {
358     static GLfloat c[4][4] = {
359         { 1.0f, 0.0f, 0.0f, 0.5f }, /* red out */
360         { 1.0f, 0.0f, 0.0f, 0.8f }, /* red in */
361         { 0.0f, 1.0f, 0.0f, 0.5f }, /* green out */
362         { 0.0f, 1.0f, 0.0f, 0.8f }, /* green in */
363     };
364
365     const int h = 2 * b + e;
366
367     glPushMatrix();
368     {
369         glScalef(1.0f, 2.0f, 1.0f);
370
371         glColor4f(c[h][0], c[h][1], c[h][2], c[h][3]);
372         sol_draw(&beam.draw, rend, 1, 1);
373         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
374     }
375     glPopMatrix();
376 }
377
378 void flag_draw(struct s_rend *rend)
379 {
380     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
381     sol_draw(&flag.draw, rend, 1, 1);
382 }
383
384 void mark_draw(struct s_rend *rend)
385 {
386     sol_draw(&mark.draw, rend, 1, 1);
387 }
388
389 void vect_draw(struct s_rend *rend)
390 {
391     sol_draw(&vect.draw, rend, 0, 1);
392     sol_draw(&vect.draw, rend, 0, 0);
393 }
394
395 void back_draw(struct s_rend *rend, float t)
396 {
397     glPushMatrix();
398     {
399         GLfloat dx =  60.0f * fsinf(t / 10.0f);
400         GLfloat dz = 180.0f * fsinf(t / 12.0f);
401
402         glDisable(GL_DEPTH_TEST);
403         glDisable(GL_CULL_FACE);
404         glDisable(GL_LIGHTING);
405         glDepthMask(GL_FALSE);
406         {
407             glScalef(-BACK_DIST, BACK_DIST, -BACK_DIST);
408             if (t) glRotatef(dz, 0.0f, 0.0f, 1.0f);
409             if (t) glRotatef(dx, 1.0f, 0.0f, 0.0f);
410
411             sol_draw(&back.draw, rend, 1, 1);
412         }
413         glDepthMask(GL_TRUE);
414         glEnable(GL_LIGHTING);
415         glEnable(GL_CULL_FACE);
416         glEnable(GL_DEPTH_TEST);
417     }
418     glPopMatrix();
419 }
420
421 void back_draw_easy(void)
422 {
423     struct s_rend rend = { NULL };
424
425     sol_draw_enable(&rend);
426     back_draw(&rend, 0.0f);
427     sol_draw_disable(&rend);
428 }
429
430 /*---------------------------------------------------------------------------*/
431
432 /*
433  * A note about lighting and shadow: technically speaking, it's wrong.
434  * The  light  position  and   shadow  projection  behave  as  if  the
435  * light-source rotates with the  floor.  However, the skybox does not
436  * rotate, thus the light should also remain stationary.
437  *
438  * The  correct behavior  would eliminate  a significant  3D  cue: the
439  * shadow of  the ball indicates  the ball's position relative  to the
440  * floor even  when the ball is  in the air.  This  was the motivating
441  * idea  behind the  shadow  in  the first  place,  so correct  shadow
442  * projection would only magnify the problem.
443  */
444
445 static GLuint shad_text;
446 static GLuint clip_text;
447
448 static GLubyte clip_data[] = { 0xff, 0xff, 0x0, 0x0 };
449
450 void shad_init(void)
451 {
452     shad_text = make_image_from_file(IMG_SHAD);
453
454     if (config_get_d(CONFIG_SHADOW) == 2)
455     {
456         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
457         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
458     }
459
460     /* Create the clip texture. */
461
462     glGenTextures(1, &clip_text);
463     glBindTexture(GL_TEXTURE_2D, clip_text);
464
465     glTexImage2D(GL_TEXTURE_2D, 0,
466                  GL_LUMINANCE_ALPHA, 1, 2, 0,
467                  GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, clip_data);
468
469     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
470     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
471
472     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
473     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
474 }
475
476 void shad_free(void)
477 {
478     glDeleteTextures(1, &shad_text);
479     glDeleteTextures(1, &clip_text);
480 }
481
482 void shad_draw_set(void)
483 {
484     if (tex_env_stage(TEX_STAGE_SHADOW))
485     {
486         glEnable(GL_TEXTURE_2D);
487         glBindTexture(GL_TEXTURE_2D, shad_text);
488
489         if (tex_env_stage(TEX_STAGE_CLIP))
490         {
491             glBindTexture(GL_TEXTURE_2D, clip_text);
492             glEnable(GL_TEXTURE_2D);
493         }
494
495         tex_env_stage(TEX_STAGE_TEXTURE);
496     }
497 }
498
499 void shad_draw_clr(void)
500 {
501     if (tex_env_stage(TEX_STAGE_SHADOW))
502     {
503         glBindTexture(GL_TEXTURE_2D, 0);
504         glDisable(GL_TEXTURE_2D);
505
506         if (tex_env_stage(TEX_STAGE_CLIP))
507         {
508             glBindTexture(GL_TEXTURE_2D, 0);
509             glDisable(GL_TEXTURE_2D);
510         }
511
512         tex_env_stage(TEX_STAGE_TEXTURE);
513     }
514 }
515
516 /*---------------------------------------------------------------------------*/