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