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