2 * Copyright (C) 2003 Robert Kooima
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.
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.
16 #include <SDL_rwops.h>
26 #include "base_image.h"
28 #include "base_config.h"
30 /*---------------------------------------------------------------------------*/
32 static int sol_enum_mtrl(const struct s_file *fp,
33 const struct s_body *bp, int mi)
37 /* Count all lump geoms with this material. */
39 for (li = 0; li < bp->lc; li++)
41 int g0 = fp->lv[bp->l0 + li].g0;
42 int gc = fp->lv[bp->l0 + li].gc;
44 for (gi = 0; gi < gc; gi++)
45 if (fp->gv[fp->iv[g0 + gi]].mi == mi)
49 /* Count all body geoms with this material. */
51 for (gi = 0; gi < bp->gc; gi++)
52 if (fp->gv[fp->iv[bp->g0 + gi]].mi == mi)
58 static int sol_enum_body(const struct s_file *fp,
59 const struct s_body *bp, int fl)
63 /* Count all geoms with this flag. */
65 for (mi = 0; mi < fp->mc; mi++)
66 if (fp->mv[mi].fl & fl)
67 c = c + sol_enum_mtrl(fp, bp, mi);
72 /*---------------------------------------------------------------------------*/
74 #define tobyte(f) ((GLubyte) (f * 255.0f))
76 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
77 tobyte((a)[1]) == tobyte((b)[1]) && \
78 tobyte((a)[2]) == tobyte((b)[2]) && \
79 tobyte((a)[3]) == tobyte((b)[3]))
81 static struct s_mtrl default_mtrl =
83 { 0.8f, 0.8f, 0.8f, 1.0f },
84 { 0.2f, 0.2f, 0.2f, 1.0f },
85 { 0.0f, 0.0f, 0.0f, 1.0f },
86 { 0.0f, 0.0f, 0.0f, 1.0f },
87 { 0.0f, }, 0.0f, M_OPAQUE, 0, ""
90 static const struct s_mtrl *sol_draw_mtrl(const struct s_file *fp,
91 const struct s_mtrl *mp,
92 const struct s_mtrl *mq)
94 /* Change material properties only as needed. */
96 if (!color_cmp(mp->a, mq->a))
97 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp->a);
98 if (!color_cmp(mp->d, mq->d))
99 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp->d);
100 if (!color_cmp(mp->s, mq->s))
101 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp->s);
102 if (!color_cmp(mp->e, mq->e))
103 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp->e);
104 if (tobyte(mp->h[0]) != tobyte(mq->h[0]))
105 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp->h);
107 /* Bind the texture. */
110 glBindTexture(GL_TEXTURE_2D, mp->o);
112 /* Enable environment mapping. */
114 if ((mp->fl & M_ENVIRONMENT) && !(mq->fl & M_ENVIRONMENT))
116 glEnable(GL_TEXTURE_GEN_S);
117 glEnable(GL_TEXTURE_GEN_T);
119 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
120 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
123 /* Disable environment mapping. */
125 if ((mq->fl & M_ENVIRONMENT) && !(mp->fl & M_ENVIRONMENT))
127 glDisable(GL_TEXTURE_GEN_S);
128 glDisable(GL_TEXTURE_GEN_T);
130 glBindTexture(GL_TEXTURE_2D, mp->o);
133 /* Enable additive blending. */
135 if ((mp->fl & M_ADDITIVE) && !(mq->fl & M_ADDITIVE))
136 glBlendFunc(GL_ONE, GL_ONE);
138 /* Enable standard blending. */
140 if ((mq->fl & M_ADDITIVE) && !(mp->fl & M_ADDITIVE))
141 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
143 /* Enable decal offset. */
145 if ((mp->fl & M_DECAL) && !(mq->fl & M_DECAL))
147 glEnable(GL_POLYGON_OFFSET_FILL);
148 glPolygonOffset(-1.0f, -2.0f);
151 /* Disable decal offset. */
153 if ((mq->fl & M_DECAL) && !(mp->fl & M_DECAL))
154 glDisable(GL_POLYGON_OFFSET_FILL);
159 static const struct s_mtrl *sol_back_bill(const struct s_file *fp,
160 const struct s_bill *rp,
161 const struct s_mtrl *mp, float t)
163 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0.0f;
165 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
166 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
170 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
171 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
172 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
176 float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
177 float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
179 glRotatef(ry, 0.0f, 1.0f, 0.0f);
180 glRotatef(rx, 1.0f, 0.0f, 0.0f);
181 glTranslatef(0.0f, 0.0f, -rp->d);
185 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
186 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
189 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
191 glRotatef(rz, 0.0f, 0.0f, 1.0f);
193 mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
197 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y0);
198 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y0);
199 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y1);
200 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y1);
210 /*---------------------------------------------------------------------------*/
212 void sol_back(const struct s_file *fp, float n, float f, float t)
214 const struct s_mtrl *mp = &default_mtrl;
218 /* Render all billboards in the given range. */
220 glDisable(GL_LIGHTING);
221 glDepthMask(GL_FALSE);
223 for (ri = 0; ri < fp->rc; ri++)
224 if (n <= fp->rv[ri].d && fp->rv[ri].d < f)
225 mp = sol_back_bill(fp, fp->rv + ri, mp, t);
227 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
229 glDepthMask(GL_TRUE);
230 glEnable(GL_LIGHTING);
233 /*---------------------------------------------------------------------------*/
235 * The following code renders a body in a ludicrously inefficient
236 * manner. It iterates the materials and scans the data structure for
237 * geometry using each. This has the effect of absolutely minimizing
238 * material changes, texture bindings, and Begin/End pairs, but
239 * maximizing trips through the data.
241 * However, this is only done once for each level. The results are
242 * stored in display lists. Thus, it is well worth it.
245 static void sol_draw_geom(const struct s_file *fp,
246 const struct s_geom *gp, int mi)
250 const float *ui = fp->tv[gp->ti].u;
251 const float *uj = fp->tv[gp->tj].u;
252 const float *uk = fp->tv[gp->tk].u;
254 const float *ni = fp->sv[gp->si].n;
255 const float *nj = fp->sv[gp->sj].n;
256 const float *nk = fp->sv[gp->sk].n;
258 const float *vi = fp->vv[gp->vi].p;
259 const float *vj = fp->vv[gp->vj].p;
260 const float *vk = fp->vv[gp->vk].p;
276 static void sol_draw_lump(const struct s_file *fp,
277 const struct s_lump *lp, int mi)
281 for (i = 0; i < lp->gc; i++)
282 sol_draw_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
285 static const struct s_mtrl *sol_draw_body(const struct s_file *fp,
286 const struct s_body *bp,
287 const struct s_mtrl *mp,
292 /* Iterate all materials of the correct opacity. */
294 for (mi = 0; mi < fp->mc; mi++)
295 if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
297 if (sol_enum_mtrl(fp, bp, mi))
299 /* Set the material state. */
301 mp = sol_draw_mtrl(fp, fp->mv + mi, mp);
303 /* Render all geometry of that material. */
305 glBegin(GL_TRIANGLES);
307 for (li = 0; li < bp->lc; li++)
308 sol_draw_lump(fp, fp->lv + bp->l0 + li, mi);
309 for (gi = 0; gi < bp->gc; gi++)
310 sol_draw_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
319 static void sol_draw_list(const struct s_file *fp,
320 const struct s_body *bp, GLuint list)
324 sol_body_p(p, fp, bp);
328 /* Translate a moving body. */
330 glTranslatef(p[0], p[1], p[2]);
339 void sol_draw(const struct s_file *fp, float rx, float ry)
343 /* Render all opaque geometry into the color and depth buffers. */
345 for (bi = 0; bi < fp->bc; bi++)
347 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].ol);
349 /* Render all translucent geometry into only the color buffer. */
351 glDepthMask(GL_FALSE);
353 for (bi = 0; bi < fp->bc; bi++)
355 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].tl);
357 glDepthMask(GL_TRUE);
361 void sol_bill(const struct s_file *fp, float rx, float ry)
363 const struct s_mtrl *mp = &default_mtrl;
367 for (ri = 0; ri < fp->rc; ++ri)
369 const struct s_bill *rp = fp->rv + ri;
371 float w = (float) rp->w[0];
372 float h = (float) rp->h[0];
374 mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
378 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
379 glRotatef(ry, 0.f, 1.f, 0.f);
380 glRotatef(rx, 1.f, 0.f, 0.f);
384 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, -h / 2);
385 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, -h / 2);
386 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, +h / 2);
387 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, +h / 2);
394 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
397 void sol_refl(const struct s_file *fp)
401 /* Render all reflective geometry into the color and depth buffers. */
403 for (bi = 0; bi < fp->bc; bi++)
405 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].rl);
408 /*---------------------------------------------------------------------------*/
410 static void sol_shad_geom(const struct s_file *fp,
411 const struct s_geom *gp, int mi)
415 const float *vi = fp->vv[gp->vi].p;
416 const float *vj = fp->vv[gp->vj].p;
417 const float *vk = fp->vv[gp->vk].p;
419 glTexCoord2f(vi[0], vi[2]);
422 glTexCoord2f(vj[0], vj[2]);
425 glTexCoord2f(vk[0], vk[2]);
430 static void sol_shad_lump(const struct s_file *fp,
431 const struct s_lump *lp, int mi)
435 for (i = 0; i < lp->gc; i++)
436 sol_shad_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
439 static void sol_shad_body(const struct s_file *fp,
440 const struct s_body *bp,
447 glEnable(GL_POLYGON_OFFSET_FILL);
448 glPolygonOffset(-1.0f, -2.0f);
451 glBegin(GL_TRIANGLES);
453 for (mi = 0; mi < fp->mc; mi++)
454 if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
456 for (li = 0; li < bp->lc; li++)
457 sol_shad_lump(fp, fp->lv + bp->l0 + li, mi);
458 for (gi = 0; gi < bp->gc; gi++)
459 sol_shad_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
465 glDisable(GL_POLYGON_OFFSET_FILL);
468 static void sol_shad_list(const struct s_file *fp,
469 const struct s_body *bp, GLuint list)
473 sol_body_p(p, fp, bp);
477 /* Translate a moving body. */
479 glTranslatef(p[0], p[1], p[2]);
481 /* Translate the shadow on a moving body. */
483 glMatrixMode(GL_TEXTURE);
486 glTranslatef(p[0], p[2], 0.0f);
488 glMatrixMode(GL_MODELVIEW);
494 /* Pop the shadow translation. */
496 glMatrixMode(GL_TEXTURE);
500 glMatrixMode(GL_MODELVIEW);
505 void sol_shad(const struct s_file *fp)
509 /* Render all shadowed geometry. */
511 glDepthMask(GL_FALSE);
513 for (bi = 0; bi < fp->bc; bi++)
515 sol_shad_list(fp, fp->bv + bi, fp->bv[bi].sl);
517 glDepthMask(GL_TRUE);
520 /*---------------------------------------------------------------------------*/
522 static void sol_load_objects(struct s_file *fp, int s)
526 /* Here we sort geometry into display lists by material type. */
528 for (i = 0; i < fp->bc; i++)
530 struct s_body *bp = fp->bv + i;
532 int on = sol_enum_body(fp, bp, M_OPAQUE);
533 int tn = sol_enum_body(fp, bp, M_TRANSPARENT);
534 int rn = sol_enum_body(fp, bp, M_REFLECTIVE);
535 int dn = sol_enum_body(fp, bp, M_DECAL);
537 /* Draw all opaque geometry, decals last. */
541 fp->bv[i].ol = glGenLists(1);
543 glNewList(fp->bv[i].ol, GL_COMPILE);
545 const struct s_mtrl *mp = &default_mtrl;
547 mp = sol_draw_body(fp, fp->bv + i, mp, M_OPAQUE, 0);
548 mp = sol_draw_body(fp, fp->bv + i, mp, M_OPAQUE, M_DECAL);
549 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
553 else fp->bv[i].ol = 0;
555 /* Draw all translucent geometry, decals first. */
559 fp->bv[i].tl = glGenLists(1);
561 glNewList(fp->bv[i].tl, GL_COMPILE);
563 const struct s_mtrl *mp = &default_mtrl;
565 mp = sol_draw_body(fp, fp->bv + i, mp, M_TRANSPARENT, M_DECAL);
566 mp = sol_draw_body(fp, fp->bv + i, mp, M_TRANSPARENT, 0);
567 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
571 else fp->bv[i].tl = 0;
573 /* Draw all reflective geometry. */
577 fp->bv[i].rl = glGenLists(1);
579 glNewList(fp->bv[i].rl, GL_COMPILE);
581 const struct s_mtrl *mp = &default_mtrl;
583 mp = sol_draw_body(fp, fp->bv + i, mp, M_REFLECTIVE, 0);
584 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
588 else fp->bv[i].rl = 0;
590 /* Draw all shadowed geometry. */
594 fp->bv[i].sl = glGenLists(1);
596 glNewList(fp->bv[i].sl, GL_COMPILE);
598 if (on) sol_shad_body(fp, fp->bv + i, M_OPAQUE, 0);
599 if (rn) sol_shad_body(fp, fp->bv + i, M_REFLECTIVE, 0);
600 if (dn) sol_shad_body(fp, fp->bv + i, M_OPAQUE, M_DECAL);
604 else fp->bv[i].sl = 0;
608 static GLuint sol_find_texture(const char *name)
615 /* Prefer a lossless copy of the texture over a lossy compression. */
617 strncpy(png, name, PATHMAX); strcat(png, ".png");
618 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
620 /* Check for a PNG. */
622 if ((o = make_image_from_file(png)))
625 /* Check for a JPG. */
627 if ((o = make_image_from_file(jpg)))
633 static void sol_load_textures(struct s_file *fp, int k)
637 /* Load the image referenced by each material. */
639 for (i = 0; i < fp->mc; i++)
640 if ((fp->mv[i].o = sol_find_texture(fp->mv[i].f)))
642 /* Set the texture to clamp or repeat based on material type. */
644 if (fp->mv[i].fl & M_CLAMPED)
646 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
647 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
651 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
652 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
657 /*---------------------------------------------------------------------------*/
659 int sol_load_gl(struct s_file *fp, const char *filename, int k, int s)
661 if (sol_load_only_file(fp, filename))
663 sol_load_textures(fp, k);
664 sol_load_objects (fp, s);
670 /*---------------------------------------------------------------------------*/
672 void sol_free_gl(struct s_file *fp)
676 for (i = 0; i < fp->mc; i++)
678 if (glIsTexture(fp->mv[i].o))
679 glDeleteTextures(1, &fp->mv[i].o);
682 for (i = 0; i < fp->bc; i++)
684 if (glIsList(fp->bv[i].ol))
685 glDeleteLists(fp->bv[i].ol, 1);
686 if (glIsList(fp->bv[i].tl))
687 glDeleteLists(fp->bv[i].tl, 1);
688 if (glIsList(fp->bv[i].rl))
689 glDeleteLists(fp->bv[i].rl, 1);
695 /*---------------------------------------------------------------------------*/