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