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