Clip shadow above ball with a texture
[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 "image.h"
26 #include "base_image.h"
27 #include "base_config.h"
28 #include "lang.h"
29
30 #include "solid_draw.h"
31 #include "solid_all.h"
32
33 /*---------------------------------------------------------------------------*/
34
35 static int sol_enum_mtrl(const struct s_base *base,
36                          const struct b_body *bp, int mi)
37 {
38     int li, gi, c = 0;
39
40     /* Count all lump geoms with this material. */
41
42     for (li = 0; li < bp->lc; li++)
43     {
44         int g0 = base->lv[bp->l0 + li].g0;
45         int gc = base->lv[bp->l0 + li].gc;
46
47         for (gi = 0; gi < gc; gi++)
48             if (base->gv[base->iv[g0 + gi]].mi == mi)
49                 c++;
50     }
51
52     /* Count all body geoms with this material. */
53
54     for (gi = 0; gi < bp->gc; gi++)
55         if (base->gv[base->iv[bp->g0 + gi]].mi == mi)
56             c++;
57
58     return c;
59 }
60
61 static int sol_enum_body(const struct s_base *base,
62                          const struct b_body *bp, int fl)
63 {
64     int mi, c = 0;
65
66     /* Count all geoms with this flag. */
67
68     for (mi = 0; mi < base->mc; mi++)
69         if (base->mv[mi].fl & fl)
70             c = c + sol_enum_mtrl(base, bp, mi);
71
72     return c;
73 }
74
75 /*---------------------------------------------------------------------------*/
76
77 #define tobyte(f) ((GLubyte) (f * 255.0f))
78
79 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
80                          tobyte((a)[1]) == tobyte((b)[1]) && \
81                          tobyte((a)[2]) == tobyte((b)[2]) && \
82                          tobyte((a)[3]) == tobyte((b)[3]))
83
84 static struct b_mtrl default_base_mtrl =
85 {
86     { 0.8f, 0.8f, 0.8f, 1.0f },
87     { 0.2f, 0.2f, 0.2f, 1.0f },
88     { 0.0f, 0.0f, 0.0f, 1.0f },
89     { 0.0f, 0.0f, 0.0f, 1.0f },
90     { 0.0f }, 0.0f, M_OPAQUE, ""
91 };
92
93 static struct d_mtrl default_draw_mtrl =
94 {
95     &default_base_mtrl, 0
96 };
97
98 static const struct d_mtrl *sol_draw_mtrl(const struct s_draw *draw,
99                                           const struct d_mtrl *mp_draw,
100                                           const struct d_mtrl *mq_draw)
101 {
102     const struct b_mtrl *mp_base = mp_draw->base;
103     const struct b_mtrl *mq_base = mq_draw->base;
104
105     /* Change material properties only as needed. */
106
107     if (!color_cmp(mp_base->a, mq_base->a))
108         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   mp_base->a);
109     if (!color_cmp(mp_base->d, mq_base->d))
110         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   mp_base->d);
111     if (!color_cmp(mp_base->s, mq_base->s))
112         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  mp_base->s);
113     if (!color_cmp(mp_base->e, mq_base->e))
114         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  mp_base->e);
115     if (tobyte(mp_base->h[0]) != tobyte(mq_base->h[0]))
116         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp_base->h);
117
118     /* Bind the texture. */
119
120     if (mp_draw->o != mq_draw->o)
121         glBindTexture(GL_TEXTURE_2D, mp_draw->o);
122
123     /* Enable environment mapping. */
124
125     if ((mp_base->fl & M_ENVIRONMENT) && !(mq_base->fl & M_ENVIRONMENT))
126     {
127         glEnable(GL_TEXTURE_GEN_S);
128         glEnable(GL_TEXTURE_GEN_T);
129
130         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
131         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
132     }
133
134     /* Disable environment mapping. */
135
136     if ((mq_base->fl & M_ENVIRONMENT) && !(mp_base->fl & M_ENVIRONMENT))
137     {
138         glDisable(GL_TEXTURE_GEN_S);
139         glDisable(GL_TEXTURE_GEN_T);
140     }
141
142     /* Enable additive blending. */
143
144     if ((mp_base->fl & M_ADDITIVE) && !(mq_base->fl & M_ADDITIVE))
145         glBlendFunc(GL_ONE, GL_ONE);
146
147     /* Enable standard blending. */
148
149     if ((mq_base->fl & M_ADDITIVE) && !(mp_base->fl & M_ADDITIVE))
150         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
151
152     /* Enable visibility-from-behind. */
153
154     if ((mp_base->fl & M_TWO_SIDED) && !(mq_base->fl & M_TWO_SIDED))
155     {
156         glDisable(GL_CULL_FACE);
157         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
158     }
159
160     /* Disable visibility-from-behind. */
161
162     if ((mq_base->fl & M_TWO_SIDED) && !(mp_base->fl & M_TWO_SIDED))
163     {
164         glEnable(GL_CULL_FACE);
165         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
166     }
167
168     /* Enable decal offset. */
169
170     if ((mp_base->fl & M_DECAL) && !(mq_base->fl & M_DECAL))
171     {
172         glEnable(GL_POLYGON_OFFSET_FILL);
173         glPolygonOffset(-1.0f, -2.0f);
174     }
175
176     /* Disable decal offset. */
177
178     if ((mq_base->fl & M_DECAL) && !(mp_base->fl & M_DECAL))
179         glDisable(GL_POLYGON_OFFSET_FILL);
180
181     return mp_draw;
182 }
183
184 static const struct d_mtrl *sol_back_bill(const struct s_draw *draw,
185                                            const struct b_bill *rp,
186                                            const struct d_mtrl *mp,
187                                            float t)
188 {
189     float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0.0f;
190
191     float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
192     float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
193
194     if (w > 0 && h > 0)
195     {
196         float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
197         float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
198         float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
199
200         glPushMatrix();
201         {
202             float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
203             float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
204
205             glRotatef(ry, 0.0f, 1.0f, 0.0f);
206             glRotatef(rx, 1.0f, 0.0f, 0.0f);
207             glTranslatef(0.0f, 0.0f, -rp->d);
208
209             if (rp->fl & B_FLAT)
210             {
211                 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
212                 glRotatef(-ry,         0.0f, 0.0f, 1.0f);
213             }
214             if (rp->fl & B_EDGE)
215                 glRotatef(-rx,         1.0f, 0.0f, 0.0f);
216
217             glRotatef(rz, 0.0f, 0.0f, 1.0f);
218
219             mp = sol_draw_mtrl(draw, draw->mv + rp->mi, mp);
220
221             glBegin(GL_QUADS);
222             {
223                 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y0);
224                 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y0);
225                 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y1);
226                 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y1);
227             }
228             glEnd();
229         }
230         glPopMatrix();
231     }
232
233     return mp;
234 }
235
236 /*---------------------------------------------------------------------------*/
237
238 void sol_back(const struct s_draw *draw, float n, float f, float t)
239 {
240     const struct d_mtrl *mp = &default_draw_mtrl;
241
242     int ri;
243
244     /* Render all billboards in the given range. */
245
246     if (draw && draw->base)
247     {
248         glDisable(GL_LIGHTING);
249         glDepthMask(GL_FALSE);
250         {
251             for (ri = 0; ri < draw->base->rc; ri++)
252                 if (n <= draw->base->rv[ri].d && draw->base->rv[ri].d < f)
253                     mp = sol_back_bill(draw, draw->base->rv + ri, mp, t);
254
255             mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
256         }
257         glDepthMask(GL_TRUE);
258         glEnable(GL_LIGHTING);
259     }
260 }
261
262 /*---------------------------------------------------------------------------*/
263 /*
264  * The  following code  renders a  body in  a  ludicrously inefficient
265  * manner.  It iterates the materials and scans the data structure for
266  * geometry using each.  This  has the effect of absolutely minimizing
267  * material  changes,  texture  bindings,  and  Begin/End  pairs,  but
268  * maximizing trips through the data.
269  *
270  * However, this  is only done once  for each level.   The results are
271  * stored in display lists.  Thus, it is well worth it.
272  */
273
274 static void sol_draw_geom(const struct s_base *base,
275                           const struct b_geom *gp, int mi)
276 {
277     if (gp->mi == mi)
278     {
279         const float *ui = base->tv[gp->ti].u;
280         const float *uj = base->tv[gp->tj].u;
281         const float *uk = base->tv[gp->tk].u;
282
283         const float *ni = base->sv[gp->si].n;
284         const float *nj = base->sv[gp->sj].n;
285         const float *nk = base->sv[gp->sk].n;
286
287         const float *vi = base->vv[gp->vi].p;
288         const float *vj = base->vv[gp->vj].p;
289         const float *vk = base->vv[gp->vk].p;
290
291         glTexCoord2fv(ui);
292         glNormal3fv(ni);
293         glVertex3fv(vi);
294
295         glTexCoord2fv(uj);
296         glNormal3fv(nj);
297         glVertex3fv(vj);
298
299         glTexCoord2fv(uk);
300         glNormal3fv(nk);
301         glVertex3fv(vk);
302     }
303 }
304
305 static void sol_draw_lump(const struct s_base *base,
306                           const struct b_lump *lp, int mi)
307 {
308     int i;
309
310     for (i = 0; i < lp->gc; i++)
311         sol_draw_geom(base, base->gv + base->iv[lp->g0 + i], mi);
312 }
313
314 static const struct d_mtrl *sol_draw_body(const struct s_draw *draw,
315                                           const struct b_body *bp,
316                                           const struct d_mtrl *mp,
317                                           int fl, int decal)
318 {
319     const struct s_base *base = draw->base;
320
321     int mi, li, gi;
322
323     /* Iterate all materials of the correct opacity. */
324
325     for (mi = 0; mi < draw->mc; mi++)
326     {
327         struct d_mtrl *mq = draw->mv + mi;
328
329         if ((mq->base->fl & fl) && (mq->base->fl & M_DECAL) == decal)
330         {
331             if (sol_enum_mtrl(draw->base, bp, mi))
332             {
333                 /* Set the material state. */
334
335                 mp = sol_draw_mtrl(draw, mq, mp);
336
337                 /* Render all geometry of that material. */
338
339                 glBegin(GL_TRIANGLES);
340                 {
341                     for (li = 0; li < bp->lc; li++)
342                         sol_draw_lump(draw->base,
343                                       base->lv + bp->l0 + li,
344                                       mi);
345                     for (gi = 0; gi < bp->gc; gi++)
346                         sol_draw_geom(draw->base,
347                                       base->gv + base->iv[bp->g0 + gi],
348                                       mi);
349                 }
350                 glEnd();
351             }
352         }
353     }
354
355     return mp;
356 }
357
358 static void sol_draw_list(const struct s_vary *vary,
359                           const struct v_body *bp, GLuint list)
360 {
361     float p[3], e[4], u[3], a;
362
363     sol_body_p(p, vary, bp->pi, bp->t);
364     sol_body_e(e, vary, bp, 0);
365
366     q_as_axisangle(e, u, &a);
367     a = V_DEG(a);
368
369     glPushMatrix();
370     {
371         /* Translate and rotate a moving body. */
372
373         glTranslatef(p[0], p[1], p[2]);
374         glRotatef(a, u[0], u[1], u[2]);
375
376         /* Draw the body. */
377
378         glCallList(list);
379     }
380     glPopMatrix();
381 }
382
383 void sol_draw(const struct s_draw *draw, int depthmask, int depthtest)
384 {
385     int bi;
386
387     /* Render all opaque geometry into the color and depth buffers. */
388
389     for (bi = 0; bi < draw->bc; bi++)
390         if (draw->bv[bi].ol)
391             sol_draw_list(draw->vary, draw->vary->bv + bi, draw->bv[bi].ol);
392
393     /* Render all translucent geometry into only the color buffer. */
394
395     if (depthtest == 0) glDisable(GL_DEPTH_TEST);
396     if (depthmask == 0) glDepthMask(GL_FALSE);
397     {
398         for (bi = 0; bi < draw->bc; bi++)
399             if (draw->bv[bi].tl)
400                 sol_draw_list(draw->vary, draw->vary->bv + bi, draw->bv[bi].tl);
401     }
402     if (depthmask == 0) glDepthMask(GL_TRUE);
403     if (depthtest == 0) glEnable(GL_DEPTH_TEST);
404 }
405
406 void sol_bill(const struct s_draw *draw, const float *M, float t)
407 {
408     const struct d_mtrl *mp = &default_draw_mtrl;
409
410     int ri;
411
412     for (ri = 0; ri < draw->base->rc; ++ri)
413     {
414         const struct b_bill *rp = draw->base->rv + ri;
415
416         float T = rp->t * t;
417         float S = fsinf(T);
418
419         float w  = rp->w [0] + rp->w [1] * T + rp->w [2] * S;
420         float h  = rp->h [0] + rp->h [1] * T + rp->h [2] * S;
421         float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * S;
422         float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * S;
423         float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * S;
424
425         mp = sol_draw_mtrl(draw, draw->mv + rp->mi, mp);
426
427         glPushMatrix();
428         {
429             glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
430
431             if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
432
433             if (fabsf(rx) > 0.0f) glRotatef(rx, 1.0f, 0.0f, 0.0f);
434             if (fabsf(ry) > 0.0f) glRotatef(ry, 0.0f, 1.0f, 0.0f);
435             if (fabsf(rz) > 0.0f) glRotatef(rz, 0.0f, 0.0f, 1.0f);
436
437             glBegin(GL_QUADS);
438             {
439                 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, -h / 2);
440                 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, -h / 2);
441                 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, +h / 2);
442                 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, +h / 2);
443             }
444             glEnd();
445         }
446         glPopMatrix();
447     }
448
449     mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
450 }
451
452 void sol_refl(const struct s_draw *draw)
453 {
454     int bi;
455
456     /* Render all reflective geometry into the color and depth buffers. */
457
458     for (bi = 0; bi < draw->bc; bi++)
459         if (draw->bv[bi].rl)
460             sol_draw_list(draw->vary, draw->vary->bv + bi, draw->bv[bi].rl);
461 }
462
463 /*---------------------------------------------------------------------------*/
464
465 static void sol_shad_geom(const struct s_base *base,
466                           const struct b_geom *gp, int mi)
467 {
468     if (gp->mi == mi)
469     {
470         const float *vi = base->vv[gp->vi].p;
471         const float *vj = base->vv[gp->vj].p;
472         const float *vk = base->vv[gp->vk].p;
473
474         glVertex3fv(vi);
475         glVertex3fv(vj);
476         glVertex3fv(vk);
477     }
478 }
479
480 static void sol_shad_lump(const struct s_base *base,
481                           const struct b_lump *lp, int mi)
482 {
483     int i;
484
485     for (i = 0; i < lp->gc; i++)
486         sol_shad_geom(base, base->gv + base->iv[lp->g0 + i], mi);
487 }
488
489 static void sol_shad_body(const struct s_base *base,
490                           const struct b_body *bp,
491                           int fl, int decal)
492 {
493     int mi, li, gi;
494
495     if (decal)
496     {
497         glEnable(GL_POLYGON_OFFSET_FILL);
498         glPolygonOffset(-1.0f, -2.0f);
499     }
500
501     glBegin(GL_TRIANGLES);
502     {
503         for (mi = 0; mi < base->mc; mi++)
504         {
505             struct b_mtrl *mp = base->mv + mi;
506
507             if ((mp->fl & fl) && (mp->fl & M_DECAL) == decal)
508             {
509                 for (li = 0; li < bp->lc; li++)
510                     sol_shad_lump(base, base->lv + bp->l0 + li, mi);
511                 for (gi = 0; gi < bp->gc; gi++)
512                     sol_shad_geom(base, base->gv + base->iv[bp->g0 + gi], mi);
513             }
514         }
515     }
516     glEnd();
517
518     if (decal)
519         glDisable(GL_POLYGON_OFFSET_FILL);
520 }
521
522 static void sol_shad_list(const struct s_vary *vary,
523                           const struct v_ball *up,
524                           const struct v_body *bp, GLuint list)
525 {
526     float p[3], e[4], u[3], a;
527     float d[3];
528
529     float X[] = { 1.0f, 0.0f, 0.0f, 0.0f };
530     float Y[] = { 0.0f, 1.0f, 0.0f, 0.0f };
531     float Z[] = { 0.0f, 0.0f, 1.0f, 0.0f };
532
533     sol_body_p(p, vary, bp->pi, bp->t);
534     sol_body_e(e, vary, bp, 0);
535
536     v_sub(d, up->p, p);
537
538     Y[3] = 0.5f - v_dot(Y, d);
539
540     if (e[0] != 1.0f)
541     {
542         q_as_axisangle(e, u, &a);
543         a = V_DEG(a);
544
545         q_conj(e, e);
546
547         q_rot(X, e, X);
548         q_rot(Y, e, Y);
549         q_rot(Z, e, Z);
550     }
551     else
552     {
553         u[0] = 0.0f;
554         u[1] = 0.0f;
555         u[2] = 0.0f;
556
557         a = 0.0f;
558     }
559
560     glTexGenfv(GL_S, GL_OBJECT_PLANE, X);
561     glTexGenfv(GL_T, GL_OBJECT_PLANE, Z);
562
563     /* Translate the shadow on a moving body. */
564
565     glMatrixMode(GL_TEXTURE);
566     {
567         float k = 0.25f / up->r;
568
569         glPushMatrix();
570         glTranslatef(0.5f - k * d[0],
571                      0.5f - k * d[2], 0.0f);
572         glScalef(k, k, 0.0f);
573     }
574     glMatrixMode(GL_MODELVIEW);
575
576     /* Set up shadow clipping. */
577
578     if (glActiveTextureARB_)
579     {
580         glActiveTextureARB_(GL_TEXTURE1_ARB);
581         glTexGenfv(GL_S, GL_OBJECT_PLANE, Y);
582         glActiveTextureARB_(GL_TEXTURE0_ARB);
583     }
584
585     /* Draw the body. */
586
587     glPushMatrix();
588     {
589         glTranslatef(p[0], p[1], p[2]);
590         glRotatef(a, u[0], u[1], u[2]);
591
592         glCallList(list);
593     }
594     glPopMatrix();
595
596     /* Pop the shadow translation. */
597
598     glMatrixMode(GL_TEXTURE);
599     {
600         glPopMatrix();
601     }
602     glMatrixMode(GL_MODELVIEW);
603 }
604
605 void sol_shad(const struct s_draw *draw, int ui)
606 {
607     int bi;
608
609     /* Render all shadowed geometry. */
610
611     glDepthMask(GL_FALSE);
612     {
613         for (bi = 0; bi < draw->bc; bi++)
614             if (draw->bv[bi].sl)
615                 sol_shad_list(draw->vary,
616                               draw->vary->uv + ui,
617                               draw->vary->bv + bi, draw->bv[bi].sl);
618     }
619     glDepthMask(GL_TRUE);
620 }
621
622 /*---------------------------------------------------------------------------*/
623
624 static void sol_load_objects(struct s_draw *draw, int s)
625 {
626     int i;
627
628     /* Here we sort geometry into display lists by material type. */
629
630     for (i = 0; i < draw->bc; i++)
631     {
632         struct d_body *bp = draw->bv + i;
633
634         int on = sol_enum_body(draw->base, bp->base, M_OPAQUE);
635         int tn = sol_enum_body(draw->base, bp->base, M_TRANSPARENT);
636         int rn = sol_enum_body(draw->base, bp->base, M_REFLECTIVE);
637         int dn = sol_enum_body(draw->base, bp->base, M_DECAL);
638         int sn = sol_enum_body(draw->base, bp->base, M_SHADOWED);
639
640         /* Draw all opaque geometry, decals last. */
641
642         if (on)
643         {
644             bp->ol = glGenLists(1);
645
646             glNewList(bp->ol, GL_COMPILE);
647             {
648                 const struct d_mtrl *mp = &default_draw_mtrl;
649
650                 mp = sol_draw_body(draw, bp->base, mp, M_OPAQUE, 0);
651                 mp = sol_draw_body(draw, bp->base, mp, M_OPAQUE, M_DECAL);
652                 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
653             }
654             glEndList();
655         }
656         else bp->ol = 0;
657
658         /* Draw all translucent geometry, decals first. */
659
660         if (tn)
661         {
662             bp->tl = glGenLists(1);
663
664             glNewList(bp->tl, GL_COMPILE);
665             {
666                 const struct d_mtrl *mp = &default_draw_mtrl;
667
668                 mp = sol_draw_body(draw, bp->base, mp, M_TRANSPARENT, M_DECAL);
669                 mp = sol_draw_body(draw, bp->base, mp, M_TRANSPARENT, 0);
670                 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
671             }
672             glEndList();
673         }
674         else bp->tl = 0;
675
676         /* Draw all reflective geometry. */
677
678         if (rn)
679         {
680             bp->rl = glGenLists(1);
681
682             glNewList(bp->rl, GL_COMPILE);
683             {
684                 const struct d_mtrl *mp = &default_draw_mtrl;
685
686                 mp = sol_draw_body(draw, bp->base, mp, M_REFLECTIVE, 0);
687                 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
688             }
689             glEndList();
690
691             draw->reflective = 1;
692         }
693         else bp->rl = 0;
694
695         /* Draw all shadowed geometry. */
696
697         if (s && (on || rn || sn))
698         {
699             bp->sl = glGenLists(1);
700
701             glNewList(bp->sl, GL_COMPILE);
702             {
703                 if (on) sol_shad_body(draw->base, bp->base, M_OPAQUE, 0);
704                 if (rn) sol_shad_body(draw->base, bp->base, M_REFLECTIVE, 0);
705                 if (dn) sol_shad_body(draw->base, bp->base, M_OPAQUE, M_DECAL);
706                 if (sn)
707                 {
708                     /* Transparent shadowed geometry hack. */
709
710                     if (dn)
711                         sol_shad_body(draw->base, bp->base, M_SHADOWED, M_DECAL);
712
713                     sol_shad_body(draw->base, bp->base, M_SHADOWED, 0);
714                 }
715             }
716             glEndList();
717         }
718         else bp->sl = 0;
719     }
720 }
721
722 static GLuint sol_find_texture(const char *name)
723 {
724     char png[MAXSTR];
725     char jpg[MAXSTR];
726
727     GLuint o;
728
729     /* Prefer a lossless copy of the texture over a lossy compression. */
730
731     strncpy(png, name, PATHMAX); strcat(png, ".png");
732     strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
733
734     /* Check for a PNG. */
735
736     if ((o = make_image_from_file(png)))
737         return o;
738
739     /* Check for a JPG. */
740
741     if ((o = make_image_from_file(jpg)))
742         return o;
743
744     return 0;
745 }
746
747 static void sol_load_textures(struct s_draw *draw)
748 {
749     int i;
750
751     /* Load the image referenced by each material. */
752
753     for (i = 0; i < draw->mc; i++)
754     {
755         struct d_mtrl *mp = draw->mv + i;
756
757         if ((mp->o = sol_find_texture(_(mp->base->f))))
758         {
759             /* Set the texture to clamp or repeat based on material type. */
760
761             if (mp->base->fl & M_CLAMPED)
762             {
763                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
764                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
765             }
766             else
767             {
768                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
769                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
770             }
771         }
772     }
773 }
774
775 /*---------------------------------------------------------------------------*/
776
777 int sol_load_draw(struct s_draw *draw, const struct s_vary *vary, int s)
778 {
779     int i;
780
781     memset(draw, 0, sizeof (*draw));
782
783     draw->vary = vary;
784     draw->base = draw->vary->base;
785
786     if (draw->base->mc)
787     {
788         draw->mv = calloc(draw->base->mc, sizeof (*draw->mv));
789         draw->mc = draw->base->mc;
790
791         for (i = 0; i < draw->base->mc; i++)
792         {
793             struct d_mtrl *mp = draw->mv + i;
794             struct b_mtrl *mq = draw->base->mv + i;
795
796             mp->base = mq;
797         }
798     }
799
800     if (draw->base->bc)
801     {
802         draw->bv = calloc(draw->base->bc, sizeof (*draw->bv));
803         draw->bc = draw->base->bc;
804
805         for (i = 0; i < draw->base->bc; i++)
806         {
807             struct d_body *bp = draw->bv + i;
808             struct b_body *bq = draw->base->bv + i;
809
810             bp->base = bq;
811         }
812     }
813
814     sol_load_textures(draw);
815     sol_load_objects (draw, s);
816
817     return 1;
818 }
819
820 void sol_free_draw(struct s_draw *draw)
821 {
822     int i;
823
824     for (i = 0; i < draw->mc; i++)
825     {
826         if (glIsTexture(draw->mv[i].o))
827             glDeleteTextures(1, &draw->mv[i].o);
828     }
829
830     for (i = 0; i < draw->bc; i++)
831     {
832         if (glIsList(draw->bv[i].ol))
833             glDeleteLists(draw->bv[i].ol, 1);
834         if (glIsList(draw->bv[i].tl))
835             glDeleteLists(draw->bv[i].tl, 1);
836         if (glIsList(draw->bv[i].rl))
837             glDeleteLists(draw->bv[i].rl, 1);
838         if (glIsList(draw->bv[i].sl))
839             glDeleteLists(draw->bv[i].sl, 1);
840     }
841
842     free(draw->mv);
843     free(draw->bv);
844
845     memset(draw, 0, sizeof (*draw));
846 }
847
848 /*---------------------------------------------------------------------------*/
849
850 int sol_load_full(struct s_full *full, const char *filename, int s)
851 {
852     if (sol_load_base(&full->base, filename))
853     {
854         sol_load_vary(&full->vary, &full->base);
855         sol_load_draw(&full->draw, &full->vary, s);
856
857         return 1;
858     }
859
860     return 0;
861 }
862
863 void sol_free_full(struct s_full *full)
864 {
865     sol_free_draw(&full->draw);
866     sol_free_vary(&full->vary);
867     sol_free_base(&full->base);
868 }
869
870 /*---------------------------------------------------------------------------*/