Minor tweaks to geom.c.
[neverball] / ball / game_draw.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 "vec3.h"
16 #include "glext.h"
17 #include "ball.h"
18 #include "item.h"
19 #include "part.h"
20 #include "geom.h"
21 #include "config.h"
22 #include "video.h"
23
24 #include "solid_draw.h"
25
26 #include "game_draw.h"
27
28 /*---------------------------------------------------------------------------*/
29
30 static void game_draw_balls(const struct s_vary *vary,
31                             const float *bill_M, float t)
32 {
33     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
34
35     float ball_M[16];
36     float pend_M[16];
37
38     m_basis(ball_M, vary->uv[0].e[0], vary->uv[0].e[1], vary->uv[0].e[2]);
39     m_basis(pend_M, vary->uv[0].E[0], vary->uv[0].E[1], vary->uv[0].E[2]);
40
41     glPushAttrib(GL_LIGHTING_BIT);
42     glPushMatrix();
43     {
44         glTranslatef(vary->uv[0].p[0],
45                      vary->uv[0].p[1] + BALL_FUDGE,
46                      vary->uv[0].p[2]);
47         glScalef(vary->uv[0].r,
48                  vary->uv[0].r,
49                  vary->uv[0].r);
50
51         glColor4fv(c);
52         ball_draw(ball_M, pend_M, bill_M, t);
53     }
54     glPopMatrix();
55     glPopAttrib();
56 }
57
58 static void game_draw_items(const struct s_vary *vary, float t)
59 {
60     float r = 360.f * t;
61     int hi;
62
63     glPushAttrib(GL_LIGHTING_BIT);
64     {
65         item_push(ITEM_COIN);
66         {
67             for (hi = 0; hi < vary->hc; hi++)
68
69                 if (vary->hv[hi].t == ITEM_COIN && vary->hv[hi].n > 0)
70                 {
71                     glPushMatrix();
72                     {
73                         glTranslatef(vary->hv[hi].p[0],
74                                      vary->hv[hi].p[1],
75                                      vary->hv[hi].p[2]);
76                         glRotatef(r, 0.0f, 1.0f, 0.0f);
77                         item_draw(&vary->hv[hi], r);
78                     }
79                     glPopMatrix();
80                 }
81         }
82         item_pull();
83
84         item_push(ITEM_SHRINK);
85         {
86             for (hi = 0; hi < vary->hc; hi++)
87
88                 if (vary->hv[hi].t == ITEM_SHRINK)
89                 {
90                     glPushMatrix();
91                     {
92                         glTranslatef(vary->hv[hi].p[0],
93                                      vary->hv[hi].p[1],
94                                      vary->hv[hi].p[2]);
95                         glRotatef(r, 0.0f, 1.0f, 0.0f);
96                         item_draw(&vary->hv[hi], r);
97                     }
98                     glPopMatrix();
99                 }
100         }
101         item_pull();
102
103         item_push(ITEM_GROW);
104         {
105             for (hi = 0; hi < vary->hc; hi++)
106
107                 if (vary->hv[hi].t == ITEM_GROW)
108                 {
109                     glPushMatrix();
110                     {
111                         glTranslatef(vary->hv[hi].p[0],
112                                      vary->hv[hi].p[1],
113                                      vary->hv[hi].p[2]);
114                         glRotatef(r, 0.0f, 1.0f, 0.0f);
115                         item_draw(&vary->hv[hi], r);
116                     }
117                     glPopMatrix();
118                 }
119         }
120         item_pull();
121     }
122     glPopAttrib();
123 }
124
125 static void game_draw_goals(const struct game_draw *gd,
126                             const float *M, float t)
127 {
128     const struct s_base *base = gd->vary.base;
129
130     if (gd->goal_e)
131     {
132         int zi;
133
134         /* Draw the goal particles. */
135 /*
136         for (zi = 0; zi < base->zc; zi++)
137         {
138             glPushMatrix();
139             {
140                 glTranslatef(base->zv[zi].p[0],
141                              base->zv[zi].p[1],
142                              base->zv[zi].p[2]);
143
144                 part_draw_goal(M, base->zv[zi].r, gd->goal_k, t);
145             }
146             glPopMatrix();
147         }
148 */
149         /* Draw the goal column. */
150
151         for (zi = 0; zi < base->zc; zi++)
152         {
153             glPushMatrix();
154             {
155                 glTranslatef(base->zv[zi].p[0],
156                              base->zv[zi].p[1],
157                              base->zv[zi].p[2]);
158
159                 glScalef(base->zv[zi].r,
160                          gd->goal_k,
161                          base->zv[zi].r);
162
163                 goal_draw(t);
164             }
165             glPopMatrix();
166         }
167     }
168 }
169
170 static void game_draw_jumps(const struct game_draw *gd,
171                             const float *M, float t)
172 {
173     const struct s_base *base = gd->vary.base;
174
175     int ji;
176 /*
177     for (ji = 0; ji < base->jc; ji++)
178     {
179         glPushMatrix();
180         {
181             glTranslatef(base->jv[ji].p[0],
182                          base->jv[ji].p[1],
183                          base->jv[ji].p[2]);
184
185             part_draw_jump(M, base->jv[ji].r, 1.0f, t);
186         }
187         glPopMatrix();
188     }
189 */
190     for (ji = 0; ji < base->jc; ji++)
191     {
192         glPushMatrix();
193         {
194             glTranslatef(base->jv[ji].p[0],
195                          base->jv[ji].p[1],
196                          base->jv[ji].p[2]);
197             glScalef(base->jv[ji].r,
198                      1.0f,
199                      base->jv[ji].r);
200
201             jump_draw(t, !gd->jump_e);
202         }
203         glPopMatrix();
204     }
205 }
206
207 static void game_draw_swchs(const struct s_vary *vary)
208 {
209     int xi;
210
211     for (xi = 0; xi < vary->xc; xi++)
212     {
213         struct v_swch *xp = vary->xv + xi;
214
215         if (xp->base->i)
216             continue;
217
218         glPushMatrix();
219         {
220             glTranslatef(xp->base->p[0],
221                          xp->base->p[1],
222                          xp->base->p[2]);
223             glScalef(xp->base->r,
224                      1.0f,
225                      xp->base->r);
226
227             swch_draw(xp->f, xp->e);
228         }
229         glPopMatrix();
230     }
231 }
232
233 /*---------------------------------------------------------------------------*/
234
235 static void game_draw_tilt(const struct game_draw *gd, int d)
236 {
237     const struct game_tilt *tilt = &gd->tilt;
238     const float *ball_p = gd->vary.uv[0].p;
239
240     /* Rotate the environment about the position of the ball. */
241
242     glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
243     glRotatef(-tilt->rz * d, tilt->z[0], tilt->z[1], tilt->z[2]);
244     glRotatef(-tilt->rx * d, tilt->x[0], tilt->x[1], tilt->x[2]);
245     glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
246 }
247
248 static void game_refl_all(const struct game_draw *gd)
249 {
250     glPushMatrix();
251     {
252         game_draw_tilt(gd, 1);
253
254         /* Draw the floor. */
255
256         sol_refl(&gd->draw);
257     }
258     glPopMatrix();
259 }
260
261 /*---------------------------------------------------------------------------*/
262
263 static void game_draw_light(void)
264 {
265     const float light_p[2][4] = {
266         { -8.0f, +32.0f, -8.0f, 0.0f },
267         { +8.0f, +32.0f, +8.0f, 0.0f },
268     };
269     const float light_c[2][4] = {
270         { 1.0f, 0.8f, 0.8f, 1.0f },
271         { 0.8f, 1.0f, 0.8f, 1.0f },
272     };
273
274     /* Configure the lighting. */
275
276     glEnable(GL_LIGHT0);
277     glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
278     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_c[0]);
279     glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
280
281     glEnable(GL_LIGHT1);
282     glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
283     glLightfv(GL_LIGHT1, GL_DIFFUSE,  light_c[1]);
284     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
285 }
286
287 static void game_draw_back(const struct game_draw *gd, int pose, int d, float t)
288 {
289     if (pose == POSE_BALL)
290         return;
291
292     glPushMatrix();
293     {
294         const struct game_view *view = &gd->view;
295
296         if (d < 0)
297         {
298             const struct game_tilt *tilt = &gd->tilt;
299
300             glRotatef(tilt->rz * 2, tilt->z[0], tilt->z[1], tilt->z[2]);
301             glRotatef(tilt->rx * 2, tilt->x[0], tilt->x[1], tilt->x[2]);
302         }
303
304         glTranslatef(view->p[0], view->p[1] * d, view->p[2]);
305
306         if (config_get_d(CONFIG_BACKGROUND))
307         {
308             /* Draw all background layers back to front. */
309
310             sol_back(&gd->back.draw, BACK_DIST, FAR_DIST,  t);
311             back_draw(0);
312             sol_back(&gd->back.draw,         0, BACK_DIST, t);
313         }
314         else back_draw(0);
315     }
316     glPopMatrix();
317 }
318
319 static void game_clip_refl(int d)
320 {
321     /* Fudge to eliminate the floor from reflection. */
322
323     GLdouble e[4], k = -0.00001;
324
325     e[0] = 0;
326     e[1] = 1;
327     e[2] = 0;
328     e[3] = k;
329
330     glClipPlane(GL_CLIP_PLANE0, e);
331 }
332
333 static void game_clip_ball(const struct game_draw *gd, int d, const float *p)
334 {
335     GLdouble r, c[3], pz[4], nz[4];
336
337     /* Compute the plane giving the front of the ball, as seen from view.p. */
338
339     c[0] = p[0];
340     c[1] = p[1] * d;
341     c[2] = p[2];
342
343     pz[0] = gd->view.p[0] - c[0];
344     pz[1] = gd->view.p[1] - c[1];
345     pz[2] = gd->view.p[2] - c[2];
346
347     r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
348
349     pz[0] /= r;
350     pz[1] /= r;
351     pz[2] /= r;
352     pz[3] = -(pz[0] * c[0] +
353               pz[1] * c[1] +
354               pz[2] * c[2]);
355
356     /* Find the plane giving the back of the ball, as seen from view.p. */
357
358     nz[0] = -pz[0];
359     nz[1] = -pz[1];
360     nz[2] = -pz[2];
361     nz[3] = -pz[3];
362
363     /* Reflect these planes as necessary, and store them in the GL state. */
364
365     pz[1] *= d;
366     nz[1] *= d;
367
368     glClipPlane(GL_CLIP_PLANE1, nz);
369     glClipPlane(GL_CLIP_PLANE2, pz);
370 }
371
372 static void game_draw_fore(const struct game_draw *gd,
373                            int pose, const float *M,
374                            int d, float t)
375 {
376     const float *ball_p = gd->vary.uv[0].p;
377
378     const struct s_draw *draw = &gd->draw;
379
380     glPushMatrix();
381     {
382         /* Rotate the environment about the position of the ball. */
383
384         game_draw_tilt(gd, d);
385
386         /* Compute clipping planes for reflection and ball facing. */
387
388         game_clip_refl(d);
389         game_clip_ball(gd, d, ball_p);
390
391         if (d < 0)
392             glEnable(GL_CLIP_PLANE0);
393
394         switch (pose)
395         {
396         case POSE_LEVEL:
397             sol_draw(draw, 0, 1);
398             break;
399
400         case POSE_NONE:
401             /* Draw the coins. */
402
403             game_draw_items(draw->vary, t);
404
405             /* Draw the floor. */
406
407             sol_draw(draw, 0, 1);
408
409             /* Fall through. */
410
411         case POSE_BALL:
412
413             /* Draw the ball shadow. */
414
415             if (d > 0 && config_get_d(CONFIG_SHADOW))
416             {
417                 shad_draw_set();
418                 sol_shad(draw, 0);
419                 shad_draw_clr();
420             }
421
422             /* Draw the ball. */
423
424             game_draw_balls(draw->vary, M, t);
425
426             break;
427         }
428
429         /* Draw the particles and light columns. */
430
431         glEnable(GL_COLOR_MATERIAL);
432         glDisable(GL_LIGHTING);
433         glDepthMask(GL_FALSE);
434         {
435             sol_bill(draw, M, t);
436             part_draw_coin(M, t);
437
438             game_draw_goals(gd, M, t);
439             game_draw_jumps(gd, M, t);
440             game_draw_swchs(draw->vary);
441         }
442         glDepthMask(GL_TRUE);
443         glEnable(GL_LIGHTING);
444         glDisable(GL_COLOR_MATERIAL);
445
446         if (d < 0)
447             glDisable(GL_CLIP_PLANE0);
448     }
449     glPopMatrix();
450 }
451
452 /*---------------------------------------------------------------------------*/
453
454 void game_draw(const struct game_draw *gd, int pose, float t)
455 {
456     float fov = (float) config_get_d(CONFIG_VIEW_FOV);
457
458     if (gd->jump_b) fov *= 2.f * fabsf(gd->jump_dt - 0.5);
459
460     if (gd->state)
461     {
462         const struct game_view *view = &gd->view;
463
464         video_push_persp(fov, 0.1f, FAR_DIST);
465         glPushMatrix();
466         {
467             float T[16], U[16], M[16], v[3];
468
469             /* Compute direct and reflected view bases. */
470
471             v[0] = +view->p[0];
472             v[1] = -view->p[1];
473             v[2] = +view->p[2];
474
475             m_view(T, view->c, view->p, view->e[1]);
476             m_view(U, view->c, v,       view->e[1]);
477
478             m_xps(M, T);
479
480             /* Apply the current view. */
481
482             v_sub(v, view->c, view->p);
483
484             glTranslatef(0.f, 0.f, -v_len(v));
485             glMultMatrixf(M);
486             glTranslatef(-view->c[0], -view->c[1], -view->c[2]);
487
488             if (gd->draw.reflective && config_get_d(CONFIG_REFLECTION))
489             {
490                 glEnable(GL_STENCIL_TEST);
491                 {
492                     /* Draw the mirrors only into the stencil buffer. */
493
494                     glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
495                     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
496                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
497                     glDepthMask(GL_FALSE);
498
499                     game_refl_all(gd);
500
501                     glDepthMask(GL_TRUE);
502                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
503                     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
504                     glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
505
506                     /* Draw the scene reflected into color and depth buffers. */
507
508                     glFrontFace(GL_CW);
509                     glPushMatrix();
510                     {
511                         glScalef(+1.0f, -1.0f, +1.0f);
512
513                         game_draw_light();
514                         game_draw_back(gd, pose,    -1, t);
515                         game_draw_fore(gd, pose, U, -1, t);
516                     }
517                     glPopMatrix();
518                     glFrontFace(GL_CCW);
519                 }
520                 glDisable(GL_STENCIL_TEST);
521             }
522
523             /* Draw the scene normally. */
524
525             game_draw_light();
526
527             if (gd->draw.reflective)
528             {
529                 if (config_get_d(CONFIG_REFLECTION))
530                 {
531                     /* Draw background while preserving reflections. */
532
533                     glEnable(GL_STENCIL_TEST);
534                     {
535                         glStencilFunc(GL_NOTEQUAL, 1, 0xFFFFFFFF);
536                         game_draw_back(gd, pose, +1, t);
537                     }
538                     glDisable(GL_STENCIL_TEST);
539
540                     /* Draw mirrors. */
541
542                     game_refl_all(gd);
543                 }
544                 else
545                 {
546                     /* Draw background. */
547
548                     game_draw_back(gd, pose, +1, t);
549
550                     /*
551                      * Draw mirrors, first fully opaque with a custom
552                      * material color, then blending normally with the
553                      * opaque surfaces using their original material
554                      * properties.  (Keeps background from showing
555                      * through.)
556                      */
557
558                     glEnable(GL_COLOR_MATERIAL);
559                     {
560                         glColor4f(0.0, 0.0, 0.05, 1.0);
561                         game_refl_all(gd);
562                         glColor4f(1.0,  1.0,  1.0,  1.0);
563                     }
564                     glDisable(GL_COLOR_MATERIAL);
565
566                     game_refl_all(gd);
567                 }
568             }
569             else
570             {
571                 game_draw_back(gd, pose, +1, t);
572                 game_refl_all(gd);
573             }
574
575             game_draw_fore(gd, pose, T, +1, t);
576         }
577         glPopMatrix();
578         video_pop_matrix();
579
580         /* Draw the fade overlay. */
581
582         sol_fade(&gd->draw, gd->fade_k);
583     }
584 }
585
586 /*---------------------------------------------------------------------------*/
587
588 #define CURR 0
589 #define PREV 1
590
591 void game_lerp_init(struct game_lerp *gl, struct game_draw *gd)
592 {
593     gl->alpha = 1.0f;
594
595     sol_load_lerp(&gl->lerp, &gd->vary);
596
597     gl->tilt[PREV] = gl->tilt[CURR] = gd->tilt;
598     gl->view[PREV] = gl->view[CURR] = gd->view;
599
600     gl->goal_k[PREV] = gl->goal_k[CURR] = gd->goal_k;
601     gl->jump_dt[PREV] = gl->jump_dt[CURR] = gd->jump_dt;
602 }
603
604 void game_lerp_free(struct game_lerp *gl)
605 {
606     sol_free_lerp(&gl->lerp);
607 }
608
609 void game_lerp_copy(struct game_lerp *gl)
610 {
611     sol_lerp_copy(&gl->lerp);
612
613     gl->tilt[PREV] = gl->tilt[CURR];
614     gl->view[PREV] = gl->view[CURR];
615
616     gl->goal_k[PREV] = gl->goal_k[CURR];
617     gl->jump_dt[PREV] = gl->jump_dt[CURR];
618 }
619
620 void game_lerp_apply(struct game_lerp *gl, struct game_draw *gd)
621 {
622     float a = gl->alpha;
623
624     /* Solid. */
625
626     sol_lerp_apply(&gl->lerp, a);
627
628     /* Particles. */
629
630     part_lerp_apply(a);
631
632     /* Tilt. */
633
634     v_lerp(gd->tilt.x, gl->tilt[PREV].x, gl->tilt[CURR].x, a);
635     v_lerp(gd->tilt.z, gl->tilt[PREV].z, gl->tilt[CURR].z, a);
636
637     gd->tilt.rx = (gl->tilt[PREV].rx * (1.0f - a) + gl->tilt[CURR].rx * a);
638     gd->tilt.rz = (gl->tilt[PREV].rz * (1.0f - a) + gl->tilt[CURR].rz * a);
639
640     /* View. */
641
642     v_lerp(gd->view.c, gl->view[PREV].c, gl->view[CURR].c, a);
643     v_lerp(gd->view.p, gl->view[PREV].p, gl->view[CURR].p, a);
644     e_lerp(gd->view.e, gl->view[PREV].e, gl->view[CURR].e, a);
645
646     /* Effects. */
647
648     gd->goal_k = (gl->goal_k[PREV] * (1.0f - a) + gl->goal_k[CURR] * a);
649     gd->jump_dt = (gl->jump_dt[PREV] * (1.0f - a) + gl->jump_dt[CURR] * a);
650 }
651
652 /*---------------------------------------------------------------------------*/