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