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