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