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