Fixed the level fade-out.
[neverball] / share / solid_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 <SDL.h>
16 #include <SDL_rwops.h>
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <math.h>
22
23 #include "glext.h"
24 #include "vec3.h"
25 #include "geom.h"
26 #include "image.h"
27 #include "base_image.h"
28 #include "base_config.h"
29 #include "lang.h"
30
31 #include "solid_draw.h"
32 #include "solid_all.h"
33
34 /*---------------------------------------------------------------------------*/
35
36 /* EXCLUDED material flags for each rendering pass. */
37
38 static const int pass_ex[] = {
39     M_REFLECTIVE | M_TRANSPARENT | M_DECAL,
40     M_REFLECTIVE | M_TRANSPARENT,
41     M_REFLECTIVE,
42     M_REFLECTIVE | M_DECAL,
43     0,
44 };
45
46 /* INCLUDED material flags for each rendering pass. */
47
48 static const int pass_in[] = {
49     0,
50     M_DECAL,
51     M_DECAL | M_TRANSPARENT,
52     M_TRANSPARENT,
53     M_REFLECTIVE,
54 };
55
56 /*---------------------------------------------------------------------------*/
57
58 static void sol_transform(const struct s_vary *vary,
59                           const struct v_body *bp)
60 {
61     float a;
62     float e[4];
63     float p[3];
64     float v[3];
65
66     /* Apply the body position and rotation to the model-view matrix. */
67
68     sol_body_p(p, vary, bp->pi, bp->t);
69     sol_body_e(e, vary, bp, 0);
70
71     q_as_axisangle(e, v, &a);
72
73     glTranslatef(p[0], p[1], p[2]);
74     glRotatef(V_DEG(a), v[0], v[1], v[2]);
75
76     /* Apply the shadow transform to the texture matrix. */
77
78     if (vary->uc && vary->uv->r > 0.0)
79     {
80         glActiveTexture_(GL_TEXTURE1);
81         glMatrixMode(GL_TEXTURE);
82         {
83             float k = 0.25f / vary->uv->r;
84
85             glLoadIdentity();
86
87             /* Center the shadow texture on the ball. */
88
89             glTranslatef(0.5f, 0.5f, 0.0f);
90
91             /* Transform ball XZ position to ST texture coordinate. */
92
93             glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
94
95             /* Move the shadow texture under the ball. */
96
97             glTranslatef(-vary->uv->p[0],
98                          -vary->uv->p[1],
99                          -vary->uv->p[2]);
100
101             /* Scale the shadow texture to the radius of the ball. */
102
103             glScalef(k, k, k);
104
105             /* Apply the body position and rotation. */
106
107             glTranslatef(p[0], p[1], p[2]);
108             glRotatef(V_DEG(a), v[0], v[1], v[2]);
109         }
110         glMatrixMode(GL_MODELVIEW);
111         glActiveTexture_(GL_TEXTURE0);
112     }
113 }
114
115 /*---------------------------------------------------------------------------*/
116
117 static void sol_load_bill(struct s_draw *draw)
118 {
119     static const GLfloat data[] = {
120         0.0f,  0.0f, -1.0f, -1.0f,
121         1.0f,  0.0f,  1.0f, -1.0f,
122         0.0f,  1.0f, -1.0f,  1.0f,
123         1.0f,  1.0f,  1.0f,  1.0f,
124
125         0.0f,  0.0f, -0.5f,  0.0f,
126         1.0f,  0.0f,  0.5f,  0.0f,
127         0.0f,  1.0f, -0.5f,  1.0f,
128         1.0f,  1.0f,  0.5f,  1.0f,
129
130         0.0f,  0.0f, -0.5f, -0.5f,
131         1.0f,  0.0f,  0.5f, -0.5f,
132         0.0f,  1.0f, -0.5f,  0.5f,
133         1.0f,  1.0f,  0.5f,  0.5f,
134     };
135
136     /* Initialize a vertex buffer object for billboard drawing. */
137
138     glGenBuffers_(1,              &draw->bill);
139     glBindBuffer_(GL_ARRAY_BUFFER, draw->bill);
140     glBufferData_(GL_ARRAY_BUFFER, sizeof (data), data, GL_STATIC_DRAW);
141     glBindBuffer_(GL_ARRAY_BUFFER, 0);
142 }
143
144 static void sol_free_bill(struct s_draw *draw)
145 {
146     if (glIsBuffer_(draw->bill))
147         glDeleteBuffers_(1, &draw->bill);
148 }
149
150 static void sol_draw_bill(GLfloat w, GLfloat h, GLboolean edge)
151 {
152     glPushMatrix();
153     {
154         glScalef(0.5f * w, 0.5f * h, 1.0f);
155
156         if (edge)
157             glTranslatef(0.0f, 0.5f, 0.0f);
158
159         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
160     }
161     glPopMatrix();
162 }
163
164 /*---------------------------------------------------------------------------*/
165
166 /* NOTE: The state management here presumes that billboard rendering is      */
167 /* NESTED within a wider SOL rendering process. That is: sol_draw_enable     */
168 /* has been called and sol_draw_disable will be called in the future.        */
169 /* Thus the "default" VBO state retained by billboard rendering is the       */
170 /* state appropriate for normal SOL rendering.                               */
171
172 static void sol_bill_enable(const struct s_draw *draw)
173 {
174     const size_t s = sizeof (GLfloat);
175 /*
176     glDisableClientState(GL_NORMAL_ARRAY);
177     glClientActiveTexture_(GL_TEXTURE1);
178     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
179     glClientActiveTexture_(GL_TEXTURE0);
180 */
181     glBindBuffer_(GL_ARRAY_BUFFER, draw->bill);
182
183     glTexCoordPointer(2, GL_FLOAT, s * 4, (GLvoid *) (    0));
184     glVertexPointer  (2, GL_FLOAT, s * 4, (GLvoid *) (s * 2));
185 }
186
187 static void sol_bill_disable(void)
188 {
189 /*
190     glEnableClientState(GL_NORMAL_ARRAY);
191     glClientActiveTexture_(GL_TEXTURE1);
192     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
193     glClientActiveTexture_(GL_TEXTURE0);
194 */
195 }
196
197 /*---------------------------------------------------------------------------*/
198
199 #define tobyte(f) ((GLubyte) (f * 255.0f))
200
201 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
202                          tobyte((a)[1]) == tobyte((b)[1]) && \
203                          tobyte((a)[2]) == tobyte((b)[2]) && \
204                          tobyte((a)[3]) == tobyte((b)[3]))
205
206 static struct b_mtrl default_base_mtrl =
207 {
208     { 0.8f, 0.8f, 0.8f, 1.0f },
209     { 0.2f, 0.2f, 0.2f, 1.0f },
210     { 0.0f, 0.0f, 0.0f, 1.0f },
211     { 0.0f, 0.0f, 0.0f, 1.0f },
212     { 0.0f }, 0.0f, 0, ""
213 };
214
215 static struct d_mtrl default_draw_mtrl =
216 {
217     &default_base_mtrl, 0
218 };
219
220 const struct d_mtrl *sol_apply_mtrl(const struct d_mtrl *mp_draw,
221                                     const struct d_mtrl *mq_draw)
222 {
223     const struct b_mtrl *mp_base = mp_draw->base;
224     const struct b_mtrl *mq_base = mq_draw->base;
225
226     /* Bind the texture. */
227
228     if (mp_draw->o != mq_draw->o)
229         glBindTexture(GL_TEXTURE_2D, mp_draw->o);
230
231     /* Set material properties. */
232
233     if (!color_cmp(mp_base->a, mq_base->a))
234         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   mp_base->a);
235     if (!color_cmp(mp_base->d, mq_base->d))
236         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   mp_base->d);
237     if (!color_cmp(mp_base->s, mq_base->s))
238         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  mp_base->s);
239     if (!color_cmp(mp_base->e, mq_base->e))
240         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  mp_base->e);
241     if (tobyte(mp_base->h[0]) != tobyte(mq_base->h[0]))
242         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp_base->h);
243
244     /* Ball shadow. */
245
246     if ((mp_base->fl & M_SHADOWED) && !(mq_base->fl & M_SHADOWED))
247     {
248         shad_draw_set();
249     }
250
251     if (!(mp_base->fl & M_SHADOWED) && (mq_base->fl & M_SHADOWED))
252     {
253         shad_draw_clr();
254     }
255
256     /* Environment mapping. */
257
258 #ifndef CONF_OPENGLES
259     if ((mp_base->fl & M_ENVIRONMENT) && !(mq_base->fl & M_ENVIRONMENT))
260     {
261         glEnable(GL_TEXTURE_GEN_S);
262         glEnable(GL_TEXTURE_GEN_T);
263
264         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
265         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
266     }
267
268     if ((mq_base->fl & M_ENVIRONMENT) && !(mp_base->fl & M_ENVIRONMENT))
269     {
270         glDisable(GL_TEXTURE_GEN_S);
271         glDisable(GL_TEXTURE_GEN_T);
272     }
273 #endif
274
275     /* Additive blending. */
276
277     if ((mp_base->fl & M_ADDITIVE) && !(mq_base->fl & M_ADDITIVE))
278         glBlendFunc(GL_ONE, GL_ONE);
279
280     if ((mq_base->fl & M_ADDITIVE) && !(mp_base->fl & M_ADDITIVE))
281         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
282
283     /* Visibility-from-behind. */
284
285     if ((mp_base->fl & M_TWO_SIDED) && !(mq_base->fl & M_TWO_SIDED))
286     {
287         glDisable(GL_CULL_FACE);
288         glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1);
289     }
290
291     if ((mq_base->fl & M_TWO_SIDED) && !(mp_base->fl & M_TWO_SIDED))
292     {
293         glEnable(GL_CULL_FACE);
294         glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
295     }
296
297     /* Decal offset. */
298
299     if ((mp_base->fl & M_DECAL) && !(mq_base->fl & M_DECAL))
300     {
301         glEnable(GL_POLYGON_OFFSET_FILL);
302         glPolygonOffset(-1.0f, -2.0f);
303     }
304
305     if ((mq_base->fl & M_DECAL) && !(mp_base->fl & M_DECAL))
306         glDisable(GL_POLYGON_OFFSET_FILL);
307
308     return mp_draw;
309 }
310
311 static GLuint sol_find_texture(const char *name)
312 {
313     char png[MAXSTR];
314     char jpg[MAXSTR];
315
316     GLuint o;
317
318     /* Prefer a lossless copy of the texture over a lossy compression. */
319
320     strncpy(png, name, PATHMAX); strcat(png, ".png");
321     strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
322
323     /* Check for a PNG. */
324
325     if ((o = make_image_from_file(png)))
326         return o;
327
328     /* Check for a JPG. */
329
330     if ((o = make_image_from_file(jpg)))
331         return o;
332
333     return 0;
334 }
335
336 static void sol_load_mtrl(struct d_mtrl *mp,
337                     const struct b_mtrl *mq,
338                           struct s_draw *draw)
339 {
340     mp->base = mq;
341
342     if ((mp->o = sol_find_texture(_(mq->f))))
343     {
344         /* Set the texture to clamp or repeat based on material type. */
345
346         if (mq->fl & M_CLAMP_S)
347             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
348         else
349             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
350
351         if (mq->fl & M_CLAMP_T)
352             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
353         else
354             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
355
356         /* If at least one material is reflective, mark it in the SOL. */
357
358         if (mq->fl & M_REFLECTIVE)
359             draw->reflective = 1;
360     }
361 }
362
363 static void sol_free_mtrl(struct d_mtrl *mp)
364 {
365     if (glIsTexture(mp->o))
366         glDeleteTextures(1, &mp->o);
367 }
368
369 static int sol_test_mtrl(const struct d_mtrl *mp, int p)
370 {
371     /* Test whether the material flags exclude f0 and include f1. */
372
373     return ((mp->base->fl & pass_in[p]) == pass_in[p] &&
374             (mp->base->fl & pass_ex[p]) == 0);
375 }
376
377 /*---------------------------------------------------------------------------*/
378
379 static int sol_count_geom(const struct s_base *base, int g0, int gc, int mi)
380 {
381     int gi, c = 0;
382
383     /* The arguments g0 and gc specify a range of the index array. These     */
384     /* indices refer to geoms. Determine how many of these geoms use the     */
385     /* given material                                                        */
386
387     for (gi = 0; gi < gc; gi++)
388         if (base->gv[base->iv[g0 + gi]].mi == mi)
389             c++;
390
391     return c;
392 }
393
394 static int sol_count_body(const struct b_body *bp,
395                           const struct s_base *base, int mi)
396 {
397     int li, c = 0;
398
399     /* Count all lump geoms with the given material. */
400
401     for (li = 0; li < bp->lc; li++)
402         c += sol_count_geom(base, base->lv[bp->l0 + li].g0,
403                                   base->lv[bp->l0 + li].gc, mi);
404
405     /* Count all body geoms with the given material. */
406
407     c += sol_count_geom(base, bp->g0, bp->gc, mi);
408
409     return c;
410 }
411
412 static int sol_count_mesh(const struct d_body *bp, int p)
413 {
414     int mi, c = 0;
415
416     /* Count the body meshes matching the given material flags. */
417
418     for (mi = 0; mi < bp->mc; ++mi)
419         if (sol_test_mtrl(bp->mv[mi].mp, p))
420             c++;
421
422     return c;
423 }
424
425 /*---------------------------------------------------------------------------*/
426
427 static void sol_mesh_vert(struct d_vert *vp,
428                     const struct s_base *base, int oi)
429 {
430     /* Gather all vertex attributes for the given offs. */
431
432     const struct b_texc *tq = base->tv + base->ov[oi].ti;
433     const struct b_side *sq = base->sv + base->ov[oi].si;
434     const struct b_vert *vq = base->vv + base->ov[oi].vi;
435
436     vp->p[0] = vq->p[0];
437     vp->p[1] = vq->p[1];
438     vp->p[2] = vq->p[2];
439
440     vp->n[0] = sq->n[0];
441     vp->n[1] = sq->n[1];
442     vp->n[2] = sq->n[2];
443
444     vp->t[0] = tq->u[0];
445     vp->t[1] = tq->u[1];
446 }
447
448 static void sol_mesh_geom(struct d_vert *vv,   int *vn,
449                           struct d_geom *gv,   int *gn,
450                     const struct s_base *base, int *iv, int g0, int gc, int mi)
451 {
452     int gi;
453
454     /* Insert all geoms with material mi into the vertex and element data. */
455
456     for (gi = 0; gi < gc; gi++)
457     {
458         const struct b_geom *gq = base->gv + base->iv[g0 + gi];
459
460         if (gq->mi == mi)
461         {
462             /* Insert a d_vert into the VBO data for each referenced b_off. */
463
464             if (iv[gq->oi] == -1)
465             {
466                 iv[gq->oi] = *vn;
467                 sol_mesh_vert(vv + (*vn)++, base, gq->oi);
468             }
469             if (iv[gq->oj] == -1)
470             {
471                 iv[gq->oj] = *vn;
472                 sol_mesh_vert(vv + (*vn)++, base, gq->oj);
473             }
474             if (iv[gq->ok] == -1)
475             {
476                 iv[gq->ok] = *vn;
477                 sol_mesh_vert(vv + (*vn)++, base, gq->ok);
478             }
479
480             /* Populate the EBO data using remapped b_off indices. */
481
482             gv[*gn].i = iv[gq->oi];
483             gv[*gn].j = iv[gq->oj];
484             gv[*gn].k = iv[gq->ok];
485
486             (*gn)++;
487         }
488     }
489 }
490
491 static void sol_load_mesh(struct d_mesh *mp,
492                     const struct b_body *bp,
493                     const struct s_draw *draw, int mi)
494 {
495     const size_t vs = sizeof (struct d_vert);
496     const size_t gs = sizeof (struct d_geom);
497
498     struct d_vert *vv = 0;
499     struct d_geom *gv = 0;
500     int           *iv = 0;
501
502     int oc = draw->base->oc;
503     int vn = 0;
504     int gn = 0;
505
506     const int gc = sol_count_body(bp, draw->base, mi);
507
508     /* Get temporary storage for vertex and element array creation. */
509
510     if ((vv = (struct d_vert *) calloc(oc, vs)) &&
511         (gv = (struct d_geom *) calloc(gc, gs)) &&
512         (iv = (int           *) calloc(oc, sizeof (int))))
513     {
514         int li, i;
515
516         /* Initialize the index remapping. */
517
518         for (i = 0; i < oc; ++i) iv[i] = -1;
519
520         /* Include all matching lump geoms in the arrays. */
521
522         for (li = 0; li < bp->lc; li++)
523             sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv,
524                           draw->base->lv[bp->l0 + li].g0,
525                           draw->base->lv[bp->l0 + li].gc, mi);
526
527         /* Include all matching body geoms in the arrays. */
528
529         sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv, bp->g0, bp->gc, mi);
530
531         /* Initialize buffer objects for all data. */
532
533         glGenBuffers_(1, &mp->vbo);
534         glBindBuffer_(GL_ARRAY_BUFFER,         mp->vbo);
535         glBufferData_(GL_ARRAY_BUFFER,         vn * vs, vv, GL_STATIC_DRAW);
536         glBindBuffer_(GL_ARRAY_BUFFER,         0);
537
538         glGenBuffers_(1, &mp->ebo);
539         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
540         glBufferData_(GL_ELEMENT_ARRAY_BUFFER, gn * gs, gv, GL_STATIC_DRAW);
541         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
542
543         mp->mp  = draw->mv + mi;
544         mp->ebc = gn * 3;
545     }
546
547     free(iv);
548     free(gv);
549     free(vv);
550 }
551
552 static void sol_free_mesh(struct d_mesh *mp)
553 {
554     if (glIsBuffer_(mp->ebo))
555         glDeleteBuffers_(1, &mp->ebo);
556     if (glIsBuffer_(mp->vbo))
557         glDeleteBuffers_(1, &mp->vbo);
558 }
559
560 static const struct d_mtrl *sol_draw_mesh(const struct d_mesh *mp,
561                                           const struct d_mtrl *mq, int p)
562 {
563     /* If this mesh has material matching the given flags... */
564
565     if (sol_test_mtrl(mp->mp, p))
566     {
567         const size_t s = sizeof (struct d_vert);
568         const GLenum T = GL_FLOAT;
569
570         /* Apply the material state. */
571
572         mq = sol_apply_mtrl(mp->mp, mq);
573
574         /* Bind the mesh data. */
575
576         glBindBuffer_(GL_ARRAY_BUFFER,         mp->vbo);
577         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
578
579         glVertexPointer  (3, T, s, (GLvoid *) offsetof (struct d_vert, p));
580         glNormalPointer  (   T, s, (GLvoid *) offsetof (struct d_vert, n));
581
582         glClientActiveTexture_(GL_TEXTURE1);
583         glTexCoordPointer(3, T, s, (GLvoid *) offsetof (struct d_vert, p));
584         glClientActiveTexture_(GL_TEXTURE0);
585         glTexCoordPointer(2, T, s, (GLvoid *) offsetof (struct d_vert, t));
586
587         /* Draw the mesh. */
588
589         glDrawElements(GL_TRIANGLES, mp->ebc, GL_UNSIGNED_SHORT, 0);
590     }
591
592     return mq;
593 }
594
595 /*---------------------------------------------------------------------------*/
596
597 static void sol_load_body(struct d_body *bp,
598                     const struct b_body *bq,
599                     const struct s_draw *draw)
600 {
601     int mi;
602
603     bp->base = bq;
604     bp->mc   =  0;
605
606     /* Determine how many materials this body uses. */
607
608     for (mi = 0; mi < draw->mc; ++mi)
609         if (sol_count_body(bq, draw->base, mi))
610             bp->mc++;
611
612     /* Allocate and initialize a mesh for each material. */
613
614     if ((bp->mv = (struct d_mesh *) calloc(bp->mc, sizeof (struct d_mesh))))
615     {
616         int mj = 0;
617
618         for (mi = 0; mi < draw->mc; ++mi)
619             if (sol_count_body(bq, draw->base, mi))
620                 sol_load_mesh(bp->mv + mj++, bq, draw, mi);
621     }
622
623     /* Cache a mesh count for each pass. */
624
625     bp->pass[0] = sol_count_mesh(bp, 0);
626     bp->pass[1] = sol_count_mesh(bp, 1);
627     bp->pass[2] = sol_count_mesh(bp, 2); 
628     bp->pass[3] = sol_count_mesh(bp, 3); 
629     bp->pass[4] = sol_count_mesh(bp, 4);
630 }
631
632 static void sol_free_body(struct d_body *bp)
633 {
634     int mi;
635
636     for (mi = 0; mi < bp->mc; ++mi)
637         sol_free_mesh(bp->mv + mi);
638
639     free(bp->mv);
640 }
641
642 static const struct d_mtrl *sol_draw_body(const struct d_body *bp,
643                                           const struct d_mtrl *mq, int p)
644 {
645     int i;
646
647     for (i = 0; i < bp->mc; ++i)
648         mq = sol_draw_mesh(bp->mv + i, mq, p);
649
650     return mq;
651 }
652
653 /*---------------------------------------------------------------------------*/
654
655 int sol_load_draw(struct s_draw *draw, const struct s_vary *vary, int s)
656 {
657     int i;
658
659     memset(draw, 0, sizeof (struct s_draw));
660
661     draw->vary = vary;
662     draw->base = vary->base;
663
664     /* Initialize all materials for this file. */
665
666     if (draw->base->mc)
667     {
668         if ((draw->mv = calloc(draw->base->mc, sizeof (*draw->mv))))
669         {
670             draw->mc = draw->base->mc;
671
672             for (i = 0; i < draw->mc; i++)
673                 sol_load_mtrl(draw->mv + i, draw->base->mv + i, draw);
674         }
675     }
676
677     /* Initialize all bodies for this file. */
678
679     if (draw->base->bc)
680     {
681         if ((draw->bv = calloc(draw->base->bc, sizeof (*draw->bv))))
682         {
683             draw->bc = draw->base->bc;
684
685             for (i = 0; i < draw->bc; i++)
686                 sol_load_body(draw->bv + i, draw->base->bv + i, draw);
687         }
688     }
689
690     sol_load_bill(draw);
691
692     return 1;
693 }
694
695 void sol_free_draw(struct s_draw *draw)
696 {
697     int i;
698
699     sol_free_bill(draw);
700
701     for (i = 0; i < draw->bc; i++)
702         sol_free_body(draw->bv + i);
703     for (i = 0; i < draw->mc; i++)
704         sol_free_mtrl(draw->mv + i);
705 }
706
707 /*---------------------------------------------------------------------------*/
708
709 static const struct d_mtrl *sol_draw_all(const struct s_draw *draw,
710                                          const struct d_mtrl *mq, int p)
711 {
712     int bi;
713
714     /* Draw all meshes of all bodies matching the given material flags. */
715
716     for (bi = 0; bi < draw->bc; ++bi)
717
718         if (draw->bv[bi].pass[p])
719         {
720             glPushMatrix();
721             {
722                 sol_transform(draw->vary, draw->vary->bv + bi);
723                 mq = sol_draw_body(draw->bv + bi, mq, p);
724             }
725             glPopMatrix();
726         }
727
728     return mq;
729 }
730
731 const struct d_mtrl *sol_draw_enable(void)
732 {
733     glEnableClientState(GL_VERTEX_ARRAY);
734     glEnableClientState(GL_NORMAL_ARRAY);
735
736     glClientActiveTexture_(GL_TEXTURE1);
737     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
738     glClientActiveTexture_(GL_TEXTURE0);
739     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
740
741     return &default_draw_mtrl;
742 }
743
744 void sol_draw_disable(const struct d_mtrl *mq)
745 {
746     sol_apply_mtrl(&default_draw_mtrl, mq);
747
748     glClientActiveTexture_(GL_TEXTURE1);
749     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
750     glClientActiveTexture_(GL_TEXTURE0);
751     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
752
753     glDisableClientState(GL_NORMAL_ARRAY);
754     glDisableClientState(GL_VERTEX_ARRAY);
755 }
756
757 /*---------------------------------------------------------------------------*/
758
759 const struct d_mtrl *sol_draw(const struct s_draw *draw,
760                               const struct d_mtrl *mq, int mask, int test)
761 {
762     /* Render all opaque geometry, decals last. */
763
764     mq = sol_draw_all(draw, mq, 0);
765     mq = sol_draw_all(draw, mq, 1);
766
767     /* Render all transparent geometry, decals first. */
768
769     if (!test) glDisable(GL_DEPTH_TEST);
770     if (!mask) glDepthMask(GL_FALSE);
771     {
772         mq = sol_draw_all(draw, mq, 2);
773         mq = sol_draw_all(draw, mq, 3);
774     }
775     if (!mask) glDepthMask(GL_TRUE);
776     if (!test) glEnable(GL_DEPTH_TEST);
777
778     /* Revert the buffer object state. */
779
780     glBindBuffer_(GL_ARRAY_BUFFER,         0);
781     glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
782
783     return mq;
784 }
785
786 const struct d_mtrl *sol_refl(const struct s_draw *draw,
787                               const struct d_mtrl *mq)
788 {
789     /* Render all reflective geometry. */
790
791     mq = sol_draw_all(draw, mq, 4);
792
793     /* Revert the buffer object state. */
794
795     glBindBuffer_(GL_ARRAY_BUFFER,         0);
796     glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
797
798     return mq;
799 }
800
801 const struct d_mtrl *sol_back(const struct s_draw *draw,
802                               const struct d_mtrl *mq,
803                               float n, float f, float t)
804 {
805     glDisable(GL_LIGHTING);
806     glDepthMask(GL_FALSE);
807
808     sol_bill_enable(draw);
809     {
810         int ri;
811
812         /* Consider each billboard. */
813
814         for (ri = 0; ri < draw->base->rc; ri++)
815         {
816             const struct b_bill *rp = draw->base->rv + ri;
817
818             /* Render only billboards at distances between n and f. */
819
820             if (n <= rp->d && rp->d < f)
821             {
822                 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0;
823
824                 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
825                 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
826
827                 /* Render only billboards facing the viewer. */
828
829                 if (w > 0 && h > 0)
830                 {
831                     float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
832                     float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
833                     float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
834
835                     glPushMatrix();
836                     {
837                         if (ry) glRotatef(ry, 0.0f, 1.0f, 0.0f);
838                         if (rx) glRotatef(rx, 1.0f, 0.0f, 0.0f);
839
840                         glTranslatef(0.0f, 0.0f, -rp->d);
841
842                         if (rp->fl & B_FLAT)
843                         {
844                             glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
845                             glRotatef(-ry,         0.0f, 0.0f, 1.0f);
846                         }
847                         if (rp->fl & B_EDGE)
848                             glRotatef(-rx,         1.0f, 0.0f, 0.0f);
849
850                         if (rz) glRotatef(rz, 0.0f, 0.0f, 1.0f);
851
852                         glScalef(w, h, 1.0f);
853
854                         mq = sol_apply_mtrl(draw->mv + rp->mi, mq);
855
856                         if (rp->fl & B_EDGE)
857                             glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
858                         else
859                             glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
860                     }
861                     glPopMatrix();
862                 }
863             }
864         }
865     }
866     sol_bill_disable();
867
868     glDepthMask(GL_TRUE);
869     glEnable(GL_LIGHTING);
870
871     return mq;
872 }
873
874 const struct d_mtrl *sol_bill(const struct s_draw *draw,
875                               const struct d_mtrl *mq, const float *M, float t)
876 {
877     sol_bill_enable(draw);
878     {
879         int ri;
880
881         for (ri = 0; ri < draw->base->rc; ++ri)
882         {
883             const struct b_bill *rp = draw->base->rv + ri;
884
885             float T = rp->t * t;
886             float S = fsinf(T);
887
888             float w  = rp->w [0] + rp->w [1] * T + rp->w [2] * S;
889             float h  = rp->h [0] + rp->h [1] * T + rp->h [2] * S;
890             float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * S;
891             float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * S;
892             float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * S;
893
894             mq = sol_apply_mtrl(draw->mv + rp->mi, mq);
895
896             glPushMatrix();
897             {
898                 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
899
900                 if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
901
902                 if (fabsf(rx) > 0.0f) glRotatef(rx, 1.0f, 0.0f, 0.0f);
903                 if (fabsf(ry) > 0.0f) glRotatef(ry, 0.0f, 1.0f, 0.0f);
904                 if (fabsf(rz) > 0.0f) glRotatef(rz, 0.0f, 0.0f, 1.0f);
905
906                 sol_draw_bill(w, h, GL_FALSE);
907             }
908             glPopMatrix();
909         }
910     }
911     sol_bill_disable();
912
913     return mq;
914 }
915
916 void sol_fade(const struct s_draw *draw, float k)
917 {
918     if (k > 0.0f)
919     {
920         glMatrixMode(GL_PROJECTION);
921         glPushMatrix();
922         glLoadIdentity();
923         glMatrixMode(GL_MODELVIEW);
924         glPushMatrix();
925         glLoadIdentity();
926         {
927             glEnable(GL_COLOR_MATERIAL);
928             glDisable(GL_LIGHTING);
929             glDisable(GL_DEPTH_TEST);
930             glDisable(GL_TEXTURE_2D);
931
932             glColor4f(0.0f, 0.0f, 0.0f, k);
933
934             sol_bill_enable(draw);
935             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
936             sol_bill_disable();
937
938             glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
939
940             glEnable(GL_TEXTURE_2D);
941             glEnable(GL_DEPTH_TEST);
942             glEnable(GL_LIGHTING);
943             glDisable(GL_COLOR_MATERIAL);
944         }
945         glMatrixMode(GL_PROJECTION);
946         glPopMatrix();
947         glMatrixMode(GL_MODELVIEW);
948         glPopMatrix();
949     }
950 }
951
952 /*---------------------------------------------------------------------------*/
953
954 int sol_load_full(struct s_full *full, const char *filename, int s)
955 {
956     if (sol_load_base(&full->base, filename))
957     {
958         sol_load_vary(&full->vary, &full->base);
959         sol_load_draw(&full->draw, &full->vary, s);
960
961         return 1;
962     }
963
964     return 0;
965 }
966
967 void sol_free_full(struct s_full *full)
968 {
969     sol_free_draw(&full->draw);
970     sol_free_vary(&full->vary);
971     sol_free_base(&full->base);
972 }
973
974 /*---------------------------------------------------------------------------*/