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