sparselines.map: Re-texture level as per RlksNotes
[neverball] / share / solid_gl.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 "solid_gl.h"
28 #include "solid_all.h"
29 #include "base_config.h"
30 #include "lang.h"
31
32 /*---------------------------------------------------------------------------*/
33
34 static int sol_enum_mtrl(const struct s_file *fp,
35                          const struct s_body *bp, int mi)
36 {
37     int li, gi, c = 0;
38
39     /* Count all lump geoms with this material. */
40
41     for (li = 0; li < bp->lc; li++)
42     {
43         int g0 = fp->lv[bp->l0 + li].g0;
44         int gc = fp->lv[bp->l0 + li].gc;
45
46         for (gi = 0; gi < gc; gi++)
47             if (fp->gv[fp->iv[g0 + gi]].mi == mi)
48                 c++;
49     }
50
51     /* Count all body geoms with this material. */
52
53     for (gi = 0; gi < bp->gc; gi++)
54         if (fp->gv[fp->iv[bp->g0 + gi]].mi == mi)
55             c++;
56
57     return c;
58 }
59
60 static int sol_enum_body(const struct s_file *fp,
61                          const struct s_body *bp, int fl)
62 {
63     int mi, c = 0;
64
65     /* Count all geoms with this flag. */
66
67     for (mi = 0; mi < fp->mc; mi++)
68         if (fp->mv[mi].fl & fl)
69             c = c + sol_enum_mtrl(fp, bp, mi);
70
71     return c;
72 }
73
74 /*---------------------------------------------------------------------------*/
75
76 int sol_reflective(const struct s_file *fp)
77 {
78     int bi;
79
80     for (bi = 0; bi < fp->bc; bi++)
81         if (fp->bv[bi].rl)
82             return 1;
83
84     return 0;
85 }
86
87 /*---------------------------------------------------------------------------*/
88
89 #define tobyte(f) ((GLubyte) (f * 255.0f))
90
91 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
92                          tobyte((a)[1]) == tobyte((b)[1]) && \
93                          tobyte((a)[2]) == tobyte((b)[2]) && \
94                          tobyte((a)[3]) == tobyte((b)[3]))
95
96 static struct s_mtrl default_mtrl =
97 {
98     { 0.8f, 0.8f, 0.8f, 1.0f },
99     { 0.2f, 0.2f, 0.2f, 1.0f },
100     { 0.0f, 0.0f, 0.0f, 1.0f },
101     { 0.0f, 0.0f, 0.0f, 1.0f },
102     { 0.0f, }, 0.0f, M_OPAQUE, 0, ""
103 };
104
105 static const struct s_mtrl *sol_draw_mtrl(const struct s_file *fp,
106                                           const struct s_mtrl *mp,
107                                           const struct s_mtrl *mq)
108 {
109     /* Change material properties only as needed. */
110
111     if (!color_cmp(mp->a, mq->a))
112         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   mp->a);
113     if (!color_cmp(mp->d, mq->d))
114         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   mp->d);
115     if (!color_cmp(mp->s, mq->s))
116         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  mp->s);
117     if (!color_cmp(mp->e, mq->e))
118         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  mp->e);
119     if (tobyte(mp->h[0]) != tobyte(mq->h[0]))
120         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp->h);
121
122     /* Bind the texture. */
123
124     if (mp->o != mq->o)
125         glBindTexture(GL_TEXTURE_2D, mp->o);
126
127     /* Enable environment mapping. */
128
129     if ((mp->fl & M_ENVIRONMENT) && !(mq->fl & M_ENVIRONMENT))
130     {
131         glEnable(GL_TEXTURE_GEN_S);
132         glEnable(GL_TEXTURE_GEN_T);
133
134         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
135         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
136     }
137
138     /* Disable environment mapping. */
139
140     if ((mq->fl & M_ENVIRONMENT) && !(mp->fl & M_ENVIRONMENT))
141     {
142         glDisable(GL_TEXTURE_GEN_S);
143         glDisable(GL_TEXTURE_GEN_T);
144     }
145
146     /* Enable additive blending. */
147
148     if ((mp->fl & M_ADDITIVE) && !(mq->fl & M_ADDITIVE))
149         glBlendFunc(GL_ONE, GL_ONE);
150
151     /* Enable standard blending. */
152
153     if ((mq->fl & M_ADDITIVE) && !(mp->fl & M_ADDITIVE))
154         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
155
156     /* Enable visibility-from-behind. */
157
158     if ((mp->fl & M_TWO_SIDED) && !(mq->fl & M_TWO_SIDED))
159     {
160         glDisable(GL_CULL_FACE);
161         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
162     }
163
164     /* Disable visibility-from-behind. */
165
166     if ((mq->fl & M_TWO_SIDED) && !(mp->fl & M_TWO_SIDED))
167     {
168         glEnable(GL_CULL_FACE);
169         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
170     }
171
172     /* Enable decal offset. */
173
174     if ((mp->fl & M_DECAL) && !(mq->fl & M_DECAL))
175     {
176         glEnable(GL_POLYGON_OFFSET_FILL);
177         glPolygonOffset(-1.0f, -2.0f);
178     }
179
180     /* Disable decal offset. */
181
182     if ((mq->fl & M_DECAL) && !(mp->fl & M_DECAL))
183         glDisable(GL_POLYGON_OFFSET_FILL);
184
185     return mp;
186 }
187
188 static const struct s_mtrl *sol_back_bill(const struct s_file *fp,
189                                           const struct s_bill *rp,
190                                           const struct s_mtrl *mp, float t)
191 {
192     float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0.0f;
193
194     float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
195     float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
196
197     if (w > 0 && h > 0)
198     {
199         float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
200         float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
201         float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
202
203         glPushMatrix();
204         {
205             float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
206             float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
207
208             glRotatef(ry, 0.0f, 1.0f, 0.0f);
209             glRotatef(rx, 1.0f, 0.0f, 0.0f);
210             glTranslatef(0.0f, 0.0f, -rp->d);
211
212             if (rp->fl & B_FLAT)
213             {
214                 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
215                 glRotatef(-ry,         0.0f, 0.0f, 1.0f);
216             }
217             if (rp->fl & B_EDGE)
218                 glRotatef(-rx,         1.0f, 0.0f, 0.0f);
219
220             glRotatef(rz, 0.0f, 0.0f, 1.0f);
221
222             mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
223
224             glBegin(GL_QUADS);
225             {
226                 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y0);
227                 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y0);
228                 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y1);
229                 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y1);
230             }
231             glEnd();
232         }
233         glPopMatrix();
234     }
235
236     return mp;
237 }
238
239 /*---------------------------------------------------------------------------*/
240
241 void sol_back(const struct s_file *fp, float n, float f, float t)
242 {
243     const struct s_mtrl *mp = &default_mtrl;
244
245     int ri;
246
247     /* Render all billboards in the given range. */
248
249     glDisable(GL_LIGHTING);
250     glDepthMask(GL_FALSE);
251     {
252         for (ri = 0; ri < fp->rc; ri++)
253             if (n <= fp->rv[ri].d && fp->rv[ri].d < f)
254                 mp = sol_back_bill(fp, fp->rv + ri, mp, t);
255
256         mp = sol_draw_mtrl(fp, &default_mtrl, mp);
257     }
258     glDepthMask(GL_TRUE);
259     glEnable(GL_LIGHTING);
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_file *fp,
275                           const struct s_geom *gp, int mi)
276 {
277     if (gp->mi == mi)
278     {
279         const float *ui = fp->tv[gp->ti].u;
280         const float *uj = fp->tv[gp->tj].u;
281         const float *uk = fp->tv[gp->tk].u;
282
283         const float *ni = fp->sv[gp->si].n;
284         const float *nj = fp->sv[gp->sj].n;
285         const float *nk = fp->sv[gp->sk].n;
286
287         const float *vi = fp->vv[gp->vi].p;
288         const float *vj = fp->vv[gp->vj].p;
289         const float *vk = fp->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_file *fp,
306                           const struct s_lump *lp, int mi)
307 {
308     int i;
309
310     for (i = 0; i < lp->gc; i++)
311         sol_draw_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
312 }
313
314 static const struct s_mtrl *sol_draw_body(const struct s_file *fp,
315                                           const struct s_body *bp,
316                                           const struct s_mtrl *mp,
317                                           int fl, int decal)
318 {
319     int mi, li, gi;
320
321     /* Iterate all materials of the correct opacity. */
322
323     for (mi = 0; mi < fp->mc; mi++)
324         if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
325         {
326             if (sol_enum_mtrl(fp, bp, mi))
327             {
328                 /* Set the material state. */
329
330                 mp = sol_draw_mtrl(fp, fp->mv + mi, mp);
331
332                 /* Render all geometry of that material. */
333
334                 glBegin(GL_TRIANGLES);
335                 {
336                     for (li = 0; li < bp->lc; li++)
337                         sol_draw_lump(fp, fp->lv + bp->l0 + li, mi);
338                     for (gi = 0; gi < bp->gc; gi++)
339                         sol_draw_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
340                 }
341                 glEnd();
342             }
343         }
344
345     return mp;
346 }
347
348 static void sol_draw_list(const struct s_file *fp,
349                           const struct s_body *bp, GLuint list)
350 {
351     float p[3], e[4], u[3], a;
352
353     sol_body_p(p, fp, bp->pi, bp->t);
354     sol_body_e(e, fp, bp, 0);
355
356     q_as_axisangle(e, u, &a);
357     a = V_DEG(a);
358
359     glPushMatrix();
360     {
361         /* Translate and rotate a moving body. */
362
363         glTranslatef(p[0], p[1], p[2]);
364         glRotatef(a, u[0], u[1], u[2]);
365
366         /* Draw the body. */
367
368         glCallList(list);
369     }
370     glPopMatrix();
371 }
372
373 void sol_draw(const struct s_file *fp, int depthmask, int depthtest)
374 {
375     int bi;
376
377     /* Render all opaque geometry into the color and depth buffers. */
378
379     for (bi = 0; bi < fp->bc; bi++)
380         if (fp->bv[bi].ol)
381             sol_draw_list(fp, fp->bv + bi, fp->bv[bi].ol);
382
383     /* Render all translucent geometry into only the color buffer. */
384
385     if (depthtest == 0) glDisable(GL_DEPTH_TEST);
386     if (depthmask == 0) glDepthMask(GL_FALSE);
387     {
388         for (bi = 0; bi < fp->bc; bi++)
389             if (fp->bv[bi].tl)
390                 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].tl);
391     }
392     if (depthmask == 0) glDepthMask(GL_TRUE);
393     if (depthtest == 0) glEnable(GL_DEPTH_TEST);
394 }
395
396 void sol_bill(const struct s_file *fp, const float *M, float t)
397 {
398     const struct s_mtrl *mp = &default_mtrl;
399
400     int ri;
401
402     for (ri = 0; ri < fp->rc; ++ri)
403     {
404         const struct s_bill *rp = fp->rv + ri;
405
406         float T = rp->t * t;
407         float S = fsinf(T);
408
409         float w  = rp->w [0] + rp->w [1] * T + rp->w [2] * S;
410         float h  = rp->h [0] + rp->h [1] * T + rp->h [2] * S;
411         float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * S;
412         float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * S;
413         float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * S;
414
415         mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
416
417         glPushMatrix();
418         {
419             glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
420
421             if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
422
423             if (fabsf(rx) > 0.0f) glRotatef(rx, 1.0f, 0.0f, 0.0f);
424             if (fabsf(ry) > 0.0f) glRotatef(ry, 0.0f, 1.0f, 0.0f);
425             if (fabsf(rz) > 0.0f) glRotatef(rz, 0.0f, 0.0f, 1.0f);
426
427             glBegin(GL_QUADS);
428             {
429                 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, -h / 2);
430                 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, -h / 2);
431                 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, +h / 2);
432                 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, +h / 2);
433             }
434             glEnd();
435         }
436         glPopMatrix();
437     }
438
439     mp = sol_draw_mtrl(fp, &default_mtrl, mp);
440 }
441
442 void sol_refl(const struct s_file *fp)
443 {
444     int bi;
445
446     /* Render all reflective geometry into the color and depth buffers. */
447
448     for (bi = 0; bi < fp->bc; bi++)
449         if (fp->bv[bi].rl)
450             sol_draw_list(fp, fp->bv + bi, fp->bv[bi].rl);
451 }
452
453 /*---------------------------------------------------------------------------*/
454
455 static void sol_shad_geom(const struct s_file *fp,
456                           const struct s_geom *gp, int mi)
457 {
458     if (gp->mi == mi)
459     {
460         const float *vi = fp->vv[gp->vi].p;
461         const float *vj = fp->vv[gp->vj].p;
462         const float *vk = fp->vv[gp->vk].p;
463
464         glTexCoord2f(vi[0], vi[2]);
465         glVertex3fv(vi);
466
467         glTexCoord2f(vj[0], vj[2]);
468         glVertex3fv(vj);
469
470         glTexCoord2f(vk[0], vk[2]);
471         glVertex3fv(vk);
472     }
473 }
474
475 static void sol_shad_lump(const struct s_file *fp,
476                           const struct s_lump *lp, int mi)
477 {
478     int i;
479
480     for (i = 0; i < lp->gc; i++)
481         sol_shad_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
482 }
483
484 static void sol_shad_body(const struct s_file *fp,
485                           const struct s_body *bp,
486                           int fl, int decal)
487 {
488     int mi, li, gi;
489
490     if (decal)
491     {
492         glEnable(GL_POLYGON_OFFSET_FILL);
493         glPolygonOffset(-1.0f, -2.0f);
494     }
495
496     glBegin(GL_TRIANGLES);
497     {
498         for (mi = 0; mi < fp->mc; mi++)
499             if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
500             {
501                 for (li = 0; li < bp->lc; li++)
502                     sol_shad_lump(fp, fp->lv + bp->l0 + li, mi);
503                 for (gi = 0; gi < bp->gc; gi++)
504                     sol_shad_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
505             }
506     }
507     glEnd();
508
509     if (decal)
510         glDisable(GL_POLYGON_OFFSET_FILL);
511 }
512
513 static void sol_shad_list(const struct s_file *fp,
514                           const struct s_body *bp, GLuint list)
515 {
516     float p[3], e[4], u[3], a;
517
518     sol_body_p(p, fp, bp->pi, bp->t);
519     sol_body_e(e, fp, bp, 0);
520
521     q_as_axisangle(e, u, &a);
522     a = V_DEG(a);
523
524     glPushMatrix();
525     {
526         /* Translate and rotate a moving body. */
527
528         glTranslatef(p[0], p[1], p[2]);
529         glRotatef(a, u[0], u[1], u[2]);
530
531         /* Translate the shadow on a moving body. */
532
533         glMatrixMode(GL_TEXTURE);
534         {
535             glPushMatrix();
536             glTranslatef(p[0], p[2], 0.0f);
537             glRotatef(-a, u[0], u[2], u[1]);
538         }
539         glMatrixMode(GL_MODELVIEW);
540
541         /* Draw the body. */
542
543         glCallList(list);
544
545         /* Pop the shadow translation. */
546
547         glMatrixMode(GL_TEXTURE);
548         {
549             glPopMatrix();
550         }
551         glMatrixMode(GL_MODELVIEW);
552     }
553     glPopMatrix();
554 }
555
556 void sol_shad(const struct s_file *fp)
557 {
558     int bi;
559
560     /* Render all shadowed geometry. */
561
562     glDepthMask(GL_FALSE);
563     {
564         for (bi = 0; bi < fp->bc; bi++)
565             if (fp->bv[bi].sl)
566                 sol_shad_list(fp, fp->bv + bi, fp->bv[bi].sl);
567     }
568     glDepthMask(GL_TRUE);
569 }
570
571 /*---------------------------------------------------------------------------*/
572
573 static void sol_load_objects(struct s_file *fp, int s)
574 {
575     int i;
576
577     /* Here we sort geometry into display lists by material type. */
578
579     for (i = 0; i < fp->bc; i++)
580     {
581         struct s_body *bp = fp->bv + i;
582
583         int on = sol_enum_body(fp, bp, M_OPAQUE);
584         int tn = sol_enum_body(fp, bp, M_TRANSPARENT);
585         int rn = sol_enum_body(fp, bp, M_REFLECTIVE);
586         int dn = sol_enum_body(fp, bp, M_DECAL);
587         int sn = sol_enum_body(fp, bp, M_SHADOWED);
588
589         /* Draw all opaque geometry, decals last. */
590
591         if (on)
592         {
593             fp->bv[i].ol = glGenLists(1);
594
595             glNewList(fp->bv[i].ol, GL_COMPILE);
596             {
597                 const struct s_mtrl *mp = &default_mtrl;
598
599                 mp = sol_draw_body(fp, fp->bv + i, mp, M_OPAQUE, 0);
600                 mp = sol_draw_body(fp, fp->bv + i, mp, M_OPAQUE, M_DECAL);
601                 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
602             }
603             glEndList();
604         }
605         else fp->bv[i].ol = 0;
606
607         /* Draw all translucent geometry, decals first. */
608
609         if (tn)
610         {
611             fp->bv[i].tl = glGenLists(1);
612
613             glNewList(fp->bv[i].tl, GL_COMPILE);
614             {
615                 const struct s_mtrl *mp = &default_mtrl;
616
617                 mp = sol_draw_body(fp, fp->bv + i, mp, M_TRANSPARENT, M_DECAL);
618                 mp = sol_draw_body(fp, fp->bv + i, mp, M_TRANSPARENT, 0);
619                 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
620             }
621             glEndList();
622         }
623         else fp->bv[i].tl = 0;
624
625         /* Draw all reflective geometry. */
626
627         if (rn)
628         {
629             fp->bv[i].rl = glGenLists(1);
630
631             glNewList(fp->bv[i].rl, GL_COMPILE);
632             {
633                 const struct s_mtrl *mp = &default_mtrl;
634
635                 mp = sol_draw_body(fp, fp->bv + i, mp, M_REFLECTIVE, 0);
636                 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
637             }
638             glEndList();
639         }
640         else fp->bv[i].rl = 0;
641
642         /* Draw all shadowed geometry. */
643
644         if (s && (on || rn || sn))
645         {
646             fp->bv[i].sl = glGenLists(1);
647
648             glNewList(fp->bv[i].sl, GL_COMPILE);
649             {
650                 if (on) sol_shad_body(fp, fp->bv + i, M_OPAQUE, 0);
651                 if (rn) sol_shad_body(fp, fp->bv + i, M_REFLECTIVE, 0);
652                 if (dn) sol_shad_body(fp, fp->bv + i, M_OPAQUE, M_DECAL);
653                 if (sn)
654                 {
655                     /* Transparent shadowed geometry hack. */
656
657                     if (dn)
658                         sol_shad_body(fp, fp->bv + i, M_SHADOWED, M_DECAL);
659
660                     sol_shad_body(fp, fp->bv + i, M_SHADOWED, 0);
661                 }
662             }
663             glEndList();
664         }
665         else fp->bv[i].sl = 0;
666     }
667 }
668
669 static GLuint sol_find_texture(const char *name)
670 {
671     char png[MAXSTR];
672     char jpg[MAXSTR];
673
674     GLuint o;
675
676     /* Prefer a lossless copy of the texture over a lossy compression. */
677
678     strncpy(png, name, PATHMAX); strcat(png, ".png");
679     strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
680
681     /* Check for a PNG. */
682
683     if ((o = make_image_from_file(png)))
684         return o;
685
686     /* Check for a JPG. */
687
688     if ((o = make_image_from_file(jpg)))
689         return o;
690
691     return 0;
692 }
693
694 static void sol_load_textures(struct s_file *fp)
695 {
696     int i;
697
698     /* Load the image referenced by each material. */
699
700     for (i = 0; i < fp->mc; i++)
701         if ((fp->mv[i].o = sol_find_texture(_(fp->mv[i].f))))
702         {
703             /* Set the texture to clamp or repeat based on material type. */
704
705             if (fp->mv[i].fl & M_CLAMPED)
706             {
707                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
708                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
709             }
710             else
711             {
712                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
713                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
714             }
715         }
716 }
717
718 /*---------------------------------------------------------------------------*/
719
720 int sol_load_gl(struct s_file *fp, const char *filename, int s)
721 {
722     if (sol_load_only_file(fp, filename))
723     {
724         sol_load_textures(fp);
725         sol_load_objects (fp, s);
726         return 1;
727     }
728     return 0;
729 }
730
731 /*---------------------------------------------------------------------------*/
732
733 void sol_free_gl(struct s_file *fp)
734 {
735     int i;
736
737     for (i = 0; i < fp->mc; i++)
738     {
739         if (glIsTexture(fp->mv[i].o))
740             glDeleteTextures(1, &fp->mv[i].o);
741     }
742
743     for (i = 0; i < fp->bc; i++)
744     {
745         if (glIsList(fp->bv[i].ol))
746             glDeleteLists(fp->bv[i].ol, 1);
747         if (glIsList(fp->bv[i].tl))
748             glDeleteLists(fp->bv[i].tl, 1);
749         if (glIsList(fp->bv[i].rl))
750             glDeleteLists(fp->bv[i].rl, 1);
751         if (glIsList(fp->bv[i].sl))
752             glDeleteLists(fp->bv[i].sl, 1);
753     }
754
755     sol_free(fp);
756 }
757
758 /*---------------------------------------------------------------------------*/