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