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