Revert "share/solid: wrap all structs with typedefs"
[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 "solid.h"
16 #include "vec3.h"
17 #include "glext.h"
18 #include "ball.h"
19 #include "item.h"
20 #include "part.h"
21 #include "geom.h"
22 #include "solid_gl.h"
23 #include "config.h"
24 #include "back.h"
25 #include "video.h"
26
27 #include "game_draw.h"
28
29 /*---------------------------------------------------------------------------*/
30
31 static void game_draw_balls(const struct s_file *fp,
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, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
40     m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]);
41
42     glPushAttrib(GL_LIGHTING_BIT);
43     glPushMatrix();
44     {
45         glTranslatef(fp->uv[0].p[0],
46                      fp->uv[0].p[1] + BALL_FUDGE,
47                      fp->uv[0].p[2]);
48         glScalef(fp->uv[0].r,
49                  fp->uv[0].r,
50                  fp->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_file *fp, 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 < fp->hc; hi++)
69
70                 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
71                 {
72                     glPushMatrix();
73                     {
74                         glTranslatef(fp->hv[hi].p[0],
75                                      fp->hv[hi].p[1],
76                                      fp->hv[hi].p[2]);
77                         glRotatef(r, 0.0f, 1.0f, 0.0f);
78                         item_draw(&fp->hv[hi], r);
79                     }
80                     glPopMatrix();
81                 }
82         }
83         item_pull();
84
85         item_push(ITEM_SHRINK);
86         {
87             for (hi = 0; hi < fp->hc; hi++)
88
89                 if (fp->hv[hi].t == ITEM_SHRINK)
90                 {
91                     glPushMatrix();
92                     {
93                         glTranslatef(fp->hv[hi].p[0],
94                                      fp->hv[hi].p[1],
95                                      fp->hv[hi].p[2]);
96                         glRotatef(r, 0.0f, 1.0f, 0.0f);
97                         item_draw(&fp->hv[hi], r);
98                     }
99                     glPopMatrix();
100                 }
101         }
102         item_pull();
103
104         item_push(ITEM_GROW);
105         {
106             for (hi = 0; hi < fp->hc; hi++)
107
108                 if (fp->hv[hi].t == ITEM_GROW)
109                 {
110                     glPushMatrix();
111                     {
112                         glTranslatef(fp->hv[hi].p[0],
113                                      fp->hv[hi].p[1],
114                                      fp->hv[hi].p[2]);
115                         glRotatef(r, 0.0f, 1.0f, 0.0f);
116                         item_draw(&fp->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 *dr,
127                             const struct s_file *fp,
128                             const float *M, float t)
129 {
130     if (dr->goal_e)
131     {
132         int zi;
133
134         /* Draw the goal particles. */
135
136         glEnable(GL_TEXTURE_2D);
137         {
138             for (zi = 0; zi < fp->zc; zi++)
139             {
140                 glPushMatrix();
141                 {
142                     glTranslatef(fp->zv[zi].p[0],
143                                  fp->zv[zi].p[1],
144                                  fp->zv[zi].p[2]);
145
146                     part_draw_goal(M, fp->zv[zi].r, dr->goal_k, t);
147                 }
148                 glPopMatrix();
149             }
150         }
151         glDisable(GL_TEXTURE_2D);
152
153         /* Draw the goal column. */
154
155         for (zi = 0; zi < fp->zc; zi++)
156         {
157             glPushMatrix();
158             {
159                 glTranslatef(fp->zv[zi].p[0],
160                              fp->zv[zi].p[1],
161                              fp->zv[zi].p[2]);
162
163                 glScalef(fp->zv[zi].r,
164                          dr->goal_k,
165                          fp->zv[zi].r);
166
167                 goal_draw();
168             }
169             glPopMatrix();
170         }
171     }
172 }
173
174 static void game_draw_jumps(const struct game_draw *dr,
175                             const struct s_file *fp,
176                             const float *M, float t)
177 {
178     int ji;
179
180     glEnable(GL_TEXTURE_2D);
181     {
182         for (ji = 0; ji < fp->jc; ji++)
183         {
184             glPushMatrix();
185             {
186                 glTranslatef(fp->jv[ji].p[0],
187                              fp->jv[ji].p[1],
188                              fp->jv[ji].p[2]);
189
190                 part_draw_jump(M, fp->jv[ji].r, 1.0f, t);
191             }
192             glPopMatrix();
193         }
194     }
195     glDisable(GL_TEXTURE_2D);
196
197     for (ji = 0; ji < fp->jc; ji++)
198     {
199         glPushMatrix();
200         {
201             glTranslatef(fp->jv[ji].p[0],
202                          fp->jv[ji].p[1],
203                          fp->jv[ji].p[2]);
204             glScalef(fp->jv[ji].r,
205                      1.0f,
206                      fp->jv[ji].r);
207
208             jump_draw(!dr->jump_e);
209         }
210         glPopMatrix();
211     }
212 }
213
214 static void game_draw_swchs(const struct s_file *fp)
215 {
216     int xi;
217
218     for (xi = 0; xi < fp->xc; xi++)
219     {
220         if (fp->xv[xi].i)
221             continue;
222
223         glPushMatrix();
224         {
225             glTranslatef(fp->xv[xi].p[0],
226                          fp->xv[xi].p[1],
227                          fp->xv[xi].p[2]);
228             glScalef(fp->xv[xi].r,
229                      1.0f,
230                      fp->xv[xi].r);
231
232             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
233         }
234         glPopMatrix();
235     }
236 }
237
238 /*---------------------------------------------------------------------------*/
239
240 static void game_draw_tilt(const struct game_draw *dr, int d)
241 {
242     const struct game_tilt *tilt = &dr->tilt;
243     const float *ball_p = dr->file.uv->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 *dr)
254 {
255     glPushMatrix();
256     {
257         game_draw_tilt(dr, 1);
258
259         /* Draw the floor. */
260
261         sol_refl(&dr->file);
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 *dr, int pose, int d, float t)
293 {
294     if (pose == POSE_BALL)
295         return;
296
297     glPushMatrix();
298     {
299         if (d < 0)
300         {
301             const struct game_tilt *tilt = &dr->tilt;
302
303             glRotatef(tilt->rz * 2, tilt->z[0], tilt->z[1], tilt->z[2]);
304             glRotatef(tilt->rx * 2, tilt->x[0], tilt->x[1], tilt->x[2]);
305         }
306
307         glTranslatef(dr->view.p[0], dr->view.p[1] * d, dr->view.p[2]);
308
309         if (config_get_d(CONFIG_BACKGROUND))
310         {
311             /* Draw all background layers back to front. */
312
313             sol_back(&dr->back, BACK_DIST, FAR_DIST,  t);
314             back_draw(0);
315             sol_back(&dr->back,         0, BACK_DIST, t);
316         }
317         else back_draw(0);
318     }
319     glPopMatrix();
320 }
321
322 static void game_clip_refl(int d)
323 {
324     /* Fudge to eliminate the floor from reflection. */
325
326     GLdouble e[4], k = -0.00001;
327
328     e[0] = 0;
329     e[1] = 1;
330     e[2] = 0;
331     e[3] = k;
332
333     glClipPlane(GL_CLIP_PLANE0, e);
334 }
335
336 static void game_clip_ball(const struct game_draw *dr, int d, const float *p)
337 {
338     GLdouble r, c[3], pz[4], nz[4];
339
340     /* Compute the plane giving the front of the ball, as seen from view.p. */
341
342     c[0] = p[0];
343     c[1] = p[1] * d;
344     c[2] = p[2];
345
346     pz[0] = dr->view.p[0] - c[0];
347     pz[1] = dr->view.p[1] - c[1];
348     pz[2] = dr->view.p[2] - c[2];
349
350     r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
351
352     pz[0] /= r;
353     pz[1] /= r;
354     pz[2] /= r;
355     pz[3] = -(pz[0] * c[0] +
356               pz[1] * c[1] +
357               pz[2] * c[2]);
358
359     /* Find the plane giving the back of the ball, as seen from view.p. */
360
361     nz[0] = -pz[0];
362     nz[1] = -pz[1];
363     nz[2] = -pz[2];
364     nz[3] = -pz[3];
365
366     /* Reflect these planes as necessary, and store them in the GL state. */
367
368     pz[1] *= d;
369     nz[1] *= d;
370
371     glClipPlane(GL_CLIP_PLANE1, nz);
372     glClipPlane(GL_CLIP_PLANE2, pz);
373 }
374
375 static void game_draw_fore(const struct game_draw *dr,
376                            int pose, const float *M,
377                            int d, float t)
378 {
379     const float *ball_p = dr->file.uv->p;
380     const float  ball_r = dr->file.uv->r;
381
382     const struct s_file *fp = &dr->file;
383
384     glPushMatrix();
385     {
386         /* Rotate the environment about the position of the ball. */
387
388         game_draw_tilt(dr, d);
389
390         /* Compute clipping planes for reflection and ball facing. */
391
392         game_clip_refl(d);
393         game_clip_ball(dr, d, ball_p);
394
395         if (d < 0)
396             glEnable(GL_CLIP_PLANE0);
397
398         switch (pose)
399         {
400         case POSE_LEVEL:
401             sol_draw(fp, 0, 1);
402             break;
403
404         case POSE_NONE:
405             /* Draw the coins. */
406
407             game_draw_items(fp, t);
408
409             /* Draw the floor. */
410
411             sol_draw(fp, 0, 1);
412
413             /* Fall through. */
414
415         case POSE_BALL:
416
417             /* Draw the ball shadow. */
418
419             if (d > 0 && config_get_d(CONFIG_SHADOW))
420             {
421                 shad_draw_set(ball_p, ball_r);
422                 sol_shad(fp);
423                 shad_draw_clr();
424             }
425
426             /* Draw the ball. */
427
428             game_draw_balls(fp, M, t);
429
430             break;
431         }
432
433         /* Draw the particles and light columns. */
434
435         glEnable(GL_COLOR_MATERIAL);
436         glDisable(GL_LIGHTING);
437         glDepthMask(GL_FALSE);
438         {
439             glColor3f(1.0f, 1.0f, 1.0f);
440
441             sol_bill(fp, M, t);
442             part_draw_coin(M, t);
443
444             glDisable(GL_TEXTURE_2D);
445             {
446                 game_draw_goals(dr, fp, M, t);
447                 game_draw_jumps(dr, fp, M, t);
448                 game_draw_swchs(fp);
449             }
450             glEnable(GL_TEXTURE_2D);
451
452             glColor3f(1.0f, 1.0f, 1.0f);
453         }
454         glDepthMask(GL_TRUE);
455         glEnable(GL_LIGHTING);
456         glDisable(GL_COLOR_MATERIAL);
457
458         if (d < 0)
459             glDisable(GL_CLIP_PLANE0);
460     }
461     glPopMatrix();
462 }
463
464 /*---------------------------------------------------------------------------*/
465
466 void game_draw(const struct game_draw *dr, int pose, float t)
467 {
468     float fov = (float) config_get_d(CONFIG_VIEW_FOV);
469
470     if (dr->jump_b) fov *= 2.f * fabsf(dr->jump_dt - 0.5);
471
472     if (dr->state)
473     {
474         const struct game_view *view = &dr->view;
475
476         video_push_persp(fov, 0.1f, FAR_DIST);
477         glPushMatrix();
478         {
479             float T[16], U[16], M[16], v[3];
480
481
482             /* Compute direct and reflected view bases. */
483
484             v[0] = +view->p[0];
485             v[1] = -view->p[1];
486             v[2] = +view->p[2];
487
488             m_view(T, view->c, view->p, view->e[1]);
489             m_view(U, view->c, v,       view->e[1]);
490
491             m_xps(M, T);
492
493             /* Apply the current view. */
494
495             v_sub(v, view->c, view->p);
496
497             glTranslatef(0.f, 0.f, -v_len(v));
498             glMultMatrixf(M);
499             glTranslatef(-view->c[0], -view->c[1], -view->c[2]);
500
501             if (dr->reflective && config_get_d(CONFIG_REFLECTION))
502             {
503                 glEnable(GL_STENCIL_TEST);
504                 {
505                     /* Draw the mirrors only into the stencil buffer. */
506
507                     glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
508                     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
509                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
510                     glDepthMask(GL_FALSE);
511
512                     game_refl_all(dr);
513
514                     glDepthMask(GL_TRUE);
515                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
516                     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
517                     glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
518
519                     /* Draw the scene reflected into color and depth buffers. */
520
521                     glFrontFace(GL_CW);
522                     glPushMatrix();
523                     {
524                         glScalef(+1.0f, -1.0f, +1.0f);
525
526                         game_draw_light();
527                         game_draw_back(dr, pose,    -1, t);
528                         game_draw_fore(dr, pose, U, -1, t);
529                     }
530                     glPopMatrix();
531                     glFrontFace(GL_CCW);
532                 }
533                 glDisable(GL_STENCIL_TEST);
534             }
535
536             /* Draw the scene normally. */
537
538             game_draw_light();
539
540             if (dr->reflective)
541             {
542                 if (config_get_d(CONFIG_REFLECTION))
543                 {
544                     /* Draw background while preserving reflections. */
545
546                     glEnable(GL_STENCIL_TEST);
547                     {
548                         glStencilFunc(GL_NOTEQUAL, 1, 0xFFFFFFFF);
549                         game_draw_back(dr, pose, +1, t);
550                     }
551                     glDisable(GL_STENCIL_TEST);
552
553                     /* Draw mirrors. */
554
555                     game_refl_all(dr);
556                 }
557                 else
558                 {
559                     /* Draw background. */
560
561                     game_draw_back(dr, pose, +1, t);
562
563                     /*
564                      * Draw mirrors, first fully opaque with a custom
565                      * material color, then blending normally with the
566                      * opaque surfaces using their original material
567                      * properties.  (Keeps background from showing
568                      * through.)
569                      */
570
571                     glEnable(GL_COLOR_MATERIAL);
572                     {
573                         glColor4f(0.0, 0.0, 0.05, 1.0);
574                         game_refl_all(dr);
575                         glColor4f(1.0,  1.0,  1.0,  1.0);
576                     }
577                     glDisable(GL_COLOR_MATERIAL);
578
579                     game_refl_all(dr);
580                 }
581             }
582             else
583             {
584                 game_draw_back(dr, pose, +1, t);
585                 game_refl_all(dr);
586             }
587
588             game_draw_fore(dr, pose, T, +1, t);
589         }
590         glPopMatrix();
591         video_pop_matrix();
592
593         /* Draw the fade overlay. */
594
595         fade_draw(dr->fade_k);
596     }
597 }
598
599 /*---------------------------------------------------------------------------*/