Removed extra whitespace
[neverball] / share / ball.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 <stdlib.h>
16 #include <string.h>
17
18 #include "vec3.h"
19 #include "glext.h"
20 #include "config.h"
21 #include "solid_gl.h"
22
23 /*---------------------------------------------------------------------------*/
24
25 static int has_solid = 0;
26 static int has_inner = 0;
27 static int has_outer = 0;
28
29 static struct s_file solid;
30 static struct s_file inner;
31 static struct s_file outer;
32
33 #define F_PENDULUM   1
34 #define F_DRAWBACK   2
35 #define F_DRAWCLIP   4
36 #define F_DEPTHMASK  8
37 #define F_DEPTHTEST 16
38
39 static int solid_flags;
40 static int inner_flags;
41 static int outer_flags;
42
43 static float solid_alpha;
44 static float inner_alpha;
45 static float outer_alpha;
46
47 /*---------------------------------------------------------------------------*/
48
49 #define SET(B, v, b) ((v) ? ((B) | (b)) : ((B) & ~(b)))
50
51 static int ball_opts(const struct s_file *fp, float *alpha)
52 {
53     int flags = F_DEPTHTEST;
54     int di;
55
56     for (di = 0; di < fp->dc; ++di)
57     {
58         char *k = fp->av + fp->dv[di].ai;
59         char *v = fp->av + fp->dv[di].aj;
60
61         if (strcmp(k, "pendulum")  == 0)
62             flags = SET(flags, atoi(v), F_PENDULUM);
63         if (strcmp(k, "drawback")  == 0)
64             flags = SET(flags, atoi(v), F_DRAWBACK);
65         if (strcmp(k, "drawclip")  == 0)
66             flags = SET(flags, atoi(v), F_DRAWCLIP);
67         if (strcmp(k, "depthmask") == 0)
68             flags = SET(flags, atoi(v), F_DEPTHMASK);
69         if (strcmp(k, "depthtest") == 0)
70             flags = SET(flags, atoi(v), F_DEPTHTEST);
71         if (strcmp(k, "alphatest") == 0)
72             sscanf(v, "%f", alpha);
73     }
74
75     return flags;
76 }
77
78 void ball_init(void)
79 {
80     int T = config_get_d(CONFIG_TEXTURES);
81
82     char solid_file[PATHMAX];
83     char inner_file[PATHMAX];
84     char outer_file[PATHMAX];
85
86     config_get_s(CONFIG_BALL, solid_file, PATHMAX - 12);
87     config_get_s(CONFIG_BALL, inner_file, PATHMAX - 12);
88     config_get_s(CONFIG_BALL, outer_file, PATHMAX - 12);
89
90     strcat(solid_file, "-solid.sol");
91     strcat(inner_file, "-inner.sol");
92     strcat(outer_file, "-outer.sol");
93
94     solid_flags = 0;
95     inner_flags = 0;
96     outer_flags = 0;
97
98     solid_alpha = 1.0f;
99     inner_alpha = 1.0f;
100     outer_alpha = 1.0f;
101
102     if ((has_solid = sol_load_gl(&solid, config_data(solid_file), T, 0)))
103         solid_flags = ball_opts(&solid, &solid_alpha);
104
105     if ((has_inner = sol_load_gl(&inner, config_data(inner_file), T, 0)))
106         inner_flags = ball_opts(&inner, &inner_alpha);
107
108     if ((has_outer = sol_load_gl(&outer, config_data(outer_file), T, 0)))
109         outer_flags = ball_opts(&outer, &outer_alpha);
110 }
111
112 void ball_free(void)
113 {
114     if (has_outer) sol_free_gl(&outer);
115     if (has_inner) sol_free_gl(&inner);
116     if (has_solid) sol_free_gl(&solid);
117
118     has_solid = has_inner = has_outer = 0;
119 }
120
121 /*---------------------------------------------------------------------------*/
122
123 static void ball_draw_solid(const float *ball_M,
124                             const float *ball_bill_M, float t)
125 {
126     if (has_solid)
127     {
128         const int mask = (solid_flags & F_DEPTHMASK);
129         const int test = (solid_flags & F_DEPTHTEST);
130
131         if (solid_alpha < 1.0f)
132         {
133             glEnable(GL_ALPHA_TEST);
134             glAlphaFunc(GL_GEQUAL, solid_alpha);
135         }
136
137         glPushMatrix();
138         {
139             /* Apply the ball rotation. */
140
141             glMultMatrixf(ball_M);
142
143             /* Draw the solid billboard geometry. */
144
145             if (solid.rc)
146             {
147                 if (test == 0) glDisable(GL_DEPTH_TEST);
148                 if (mask == 0) glDepthMask(GL_FALSE);
149                 glDisable(GL_LIGHTING);
150                 {
151                     sol_bill(&solid, ball_bill_M, t);
152                 }
153                 glEnable(GL_LIGHTING);
154                 if (mask == 0) glDepthMask(GL_TRUE);
155                 if (test == 0) glEnable(GL_DEPTH_TEST);
156             }
157
158             /* Draw the solid opaque and transparent geometry. */
159
160             sol_draw(&solid, mask, test);
161         }
162         glPopMatrix();
163
164         if (solid_alpha < 1.0f)
165             glDisable(GL_ALPHA_TEST);
166     }
167 }
168
169 static void ball_draw_inner(const float *pend_M,
170                             const float *bill_M,
171                             const float *pend_bill_M, float t)
172 {
173     if (has_inner)
174     {
175         const int pend = (inner_flags & F_PENDULUM);
176         const int mask = (inner_flags & F_DEPTHMASK);
177         const int test = (inner_flags & F_DEPTHTEST);
178
179         if (inner_alpha < 1.0f)
180         {
181             glEnable(GL_ALPHA_TEST);
182             glAlphaFunc(GL_GEQUAL, inner_alpha);
183         }
184
185         /* Apply the pendulum rotation. */
186
187         if (pend)
188         {
189             glPushMatrix();
190             glMultMatrixf(pend_M);
191         }
192
193         /* Draw the inner opaque and transparent geometry. */
194
195         sol_draw(&inner, mask, test);
196
197         /* Draw the inner billboard geometry. */
198
199         if (inner.rc)
200         {
201             if (test == 0) glDisable(GL_DEPTH_TEST);
202             if (mask == 0) glDepthMask(GL_FALSE);
203             glDisable(GL_LIGHTING);
204             {
205                 if (pend)
206                     sol_bill(&inner, pend_bill_M, t);
207                 else
208                     sol_bill(&inner, bill_M,      t);
209             }
210
211             glEnable(GL_LIGHTING);
212             if (mask == 0) glDepthMask(GL_TRUE);
213             if (test == 0) glEnable(GL_DEPTH_TEST);
214         }
215
216         if (pend)
217             glPopMatrix();
218
219         if (inner_alpha < 1.0f)
220             glDisable(GL_ALPHA_TEST);
221     }
222 }
223
224 static void ball_draw_outer(const float *pend_M,
225                             const float *bill_M,
226                             const float *pend_bill_M, float t)
227 {
228     if (has_outer)
229     {
230         const int pend = (outer_flags & F_PENDULUM);
231         const int mask = (outer_flags & F_DEPTHMASK);
232         const int test = (outer_flags & F_DEPTHTEST);
233
234         if (outer_alpha < 1.0f)
235         {
236             glEnable(GL_ALPHA_TEST);
237             glAlphaFunc(GL_GEQUAL, outer_alpha);
238         }
239
240        /* Apply the pendulum rotation. */
241
242         if (pend)
243         {
244             glPushMatrix();
245             glMultMatrixf(pend_M);
246         }
247
248         /* Draw the outer opaque and transparent geometry. */
249
250         sol_draw(&outer, mask, test);
251
252         /* Draw the outer billboard geometry. */
253
254         if (outer.rc)
255         {
256             if (test == 0) glDisable(GL_DEPTH_TEST);
257             if (mask == 0) glDepthMask(GL_FALSE);
258             glDisable(GL_LIGHTING);
259             {
260                 if (pend)
261                     sol_bill(&outer, pend_bill_M, t);
262                 else
263                     sol_bill(&outer, bill_M,      t);
264             }
265             glEnable(GL_LIGHTING);
266             if (mask == 0) glDepthMask(GL_TRUE);
267             if (test == 0) glEnable(GL_DEPTH_TEST);
268         }
269
270         if (pend)
271             glPopMatrix();
272
273         if (outer_alpha < 1.0f)
274             glDisable(GL_ALPHA_TEST);
275     }
276 }
277
278 /*---------------------------------------------------------------------------*/
279
280 static void ball_pass_inner(const float *ball_M,
281                             const float *pend_M,
282                             const float *bill_M,
283                             const float *ball_bill_M,
284                             const float *pend_bill_M, float t)
285 {
286     /* Sort the inner ball using clip planes. */
287
288     if      (inner_flags & F_DRAWCLIP)
289     {
290         glEnable(GL_CLIP_PLANE1);
291         ball_draw_inner(        pend_M, bill_M,              pend_bill_M, t);
292         glDisable(GL_CLIP_PLANE1);
293
294         glEnable(GL_CLIP_PLANE2);
295         ball_draw_inner(        pend_M, bill_M,              pend_bill_M, t);
296         glDisable(GL_CLIP_PLANE2);
297     }
298
299     /* Sort the inner ball using face culling. */
300
301     else if (inner_flags & F_DRAWBACK)
302     {
303         glCullFace(GL_FRONT);
304         ball_draw_inner(        pend_M, bill_M,              pend_bill_M, t);
305         glCullFace(GL_BACK);
306         ball_draw_inner(        pend_M, bill_M,              pend_bill_M, t);
307     }
308
309     /* Draw the inner ball normally. */
310
311     else
312     {
313         ball_draw_inner(        pend_M, bill_M,              pend_bill_M, t);
314     }
315 }
316
317 static void ball_pass_solid(const float *ball_M,
318                             const float *pend_M,
319                             const float *bill_M,
320                             const float *ball_bill_M,
321                             const float *pend_bill_M, float t)
322 {
323     /* Sort the solid ball with the inner ball using clip planes. */
324
325     if      (solid_flags & F_DRAWCLIP)
326     {
327         glEnable(GL_CLIP_PLANE1);
328         ball_draw_solid(ball_M,                 ball_bill_M, t);
329         glDisable(GL_CLIP_PLANE1);
330
331         ball_pass_inner(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
332
333         glEnable(GL_CLIP_PLANE2);
334         ball_draw_solid(ball_M,                 ball_bill_M, t);
335         glDisable(GL_CLIP_PLANE2);
336     }
337
338     /* Sort the solid ball with the inner ball using face culling. */
339
340     else if (solid_flags & F_DRAWBACK)
341     {
342         glCullFace(GL_FRONT);
343         ball_draw_solid(ball_M,                 ball_bill_M, t);
344         glCullFace(GL_BACK);
345
346         ball_pass_inner(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
347         ball_draw_solid(ball_M,                 ball_bill_M, t);
348     }
349
350     /* Draw the solid ball after the inner ball. */
351
352     else
353     {
354         ball_pass_inner(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
355         ball_draw_solid(ball_M,                 ball_bill_M, t);
356     }
357 }
358
359 static void ball_pass_outer(const float *ball_M,
360                             const float *pend_M,
361                             const float *bill_M,
362                             const float *ball_bill_M,
363                             const float *pend_bill_M, float t)
364 {
365     /* Sort the outer ball with the solid ball using clip planes. */
366
367     if      (outer_flags & F_DRAWCLIP)
368     {
369         glEnable(GL_CLIP_PLANE1);
370         ball_draw_outer(        pend_M, bill_M,              pend_bill_M, t);
371         glDisable(GL_CLIP_PLANE1);
372
373         ball_pass_solid(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
374
375         glEnable(GL_CLIP_PLANE2);
376         ball_draw_outer(        pend_M, bill_M,              pend_bill_M, t);
377         glDisable(GL_CLIP_PLANE2);
378     }
379
380     /* Sort the outer ball with the solid ball using face culling. */
381
382     else if (outer_flags & F_DRAWBACK)
383     {
384         glCullFace(GL_FRONT);
385         ball_draw_outer(        pend_M, bill_M,              pend_bill_M, t);
386         glCullFace(GL_BACK);
387
388         ball_pass_solid(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
389         ball_draw_outer(        pend_M, bill_M,              pend_bill_M, t);
390     }
391
392     /* Draw the outer ball after the solid ball. */
393
394     else
395     {
396         ball_pass_solid(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
397         ball_draw_outer(        pend_M, bill_M,              pend_bill_M, t);
398     }
399 }
400
401 /*---------------------------------------------------------------------------*/
402
403 void ball_draw(const float *ball_M,
404                const float *pend_M,
405                const float *bill_M, float t)
406 {
407     /* Compute transforms for ball and pendulum billboards. */
408
409     float ball_T[16], ball_bill_M[16];
410     float pend_T[16], pend_bill_M[16];
411
412     m_xps(ball_T, ball_M);
413     m_xps(pend_T, pend_M);
414     m_xps(pend_T, pend_M);
415
416     m_mult(ball_bill_M, ball_T, bill_M);
417     m_mult(pend_bill_M, pend_T, bill_M);
418
419     /* Go to GREAT pains to ensure all layers are drawn back-to-front. */
420
421     ball_pass_outer(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
422 }
423
424 /*---------------------------------------------------------------------------*/