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 "solid_all.h"
29 #include "base_config.h"
32 /*---------------------------------------------------------------------------*/
34 static int sol_enum_mtrl(const struct s_file *fp,
35 const struct s_body *bp, int mi)
39 /* Count all lump geoms with this material. */
41 for (li = 0; li < bp->lc; li++)
43 int g0 = fp->lv[bp->l0 + li].g0;
44 int gc = fp->lv[bp->l0 + li].gc;
46 for (gi = 0; gi < gc; gi++)
47 if (fp->gv[fp->iv[g0 + gi]].mi == mi)
51 /* Count all body geoms with this material. */
53 for (gi = 0; gi < bp->gc; gi++)
54 if (fp->gv[fp->iv[bp->g0 + gi]].mi == mi)
60 static int sol_enum_body(const struct s_file *fp,
61 const struct s_body *bp, int fl)
65 /* Count all geoms with this flag. */
67 for (mi = 0; mi < fp->mc; mi++)
68 if (fp->mv[mi].fl & fl)
69 c = c + sol_enum_mtrl(fp, bp, mi);
74 /*---------------------------------------------------------------------------*/
76 int sol_reflective(const struct s_file *fp)
80 for (bi = 0; bi < fp->bc; bi++)
87 /*---------------------------------------------------------------------------*/
89 #define tobyte(f) ((GLubyte) (f * 255.0f))
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]))
96 static struct s_mtrl default_mtrl =
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, ""
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)
109 /* Change material properties only as needed. */
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);
122 /* Bind the texture. */
125 glBindTexture(GL_TEXTURE_2D, mp->o);
127 /* Enable environment mapping. */
129 if ((mp->fl & M_ENVIRONMENT) && !(mq->fl & M_ENVIRONMENT))
131 glEnable(GL_TEXTURE_GEN_S);
132 glEnable(GL_TEXTURE_GEN_T);
134 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
135 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
138 /* Disable environment mapping. */
140 if ((mq->fl & M_ENVIRONMENT) && !(mp->fl & M_ENVIRONMENT))
142 glDisable(GL_TEXTURE_GEN_S);
143 glDisable(GL_TEXTURE_GEN_T);
146 /* Enable additive blending. */
148 if ((mp->fl & M_ADDITIVE) && !(mq->fl & M_ADDITIVE))
149 glBlendFunc(GL_ONE, GL_ONE);
151 /* Enable standard blending. */
153 if ((mq->fl & M_ADDITIVE) && !(mp->fl & M_ADDITIVE))
154 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
156 /* Enable visibility-from-behind. */
158 if ((mp->fl & M_TWO_SIDED) && !(mq->fl & M_TWO_SIDED))
160 glDisable(GL_CULL_FACE);
161 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
164 /* Disable visibility-from-behind. */
166 if ((mq->fl & M_TWO_SIDED) && !(mp->fl & M_TWO_SIDED))
168 glEnable(GL_CULL_FACE);
169 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
172 /* Enable decal offset. */
174 if ((mp->fl & M_DECAL) && !(mq->fl & M_DECAL))
176 glEnable(GL_POLYGON_OFFSET_FILL);
177 glPolygonOffset(-1.0f, -2.0f);
180 /* Disable decal offset. */
182 if ((mq->fl & M_DECAL) && !(mp->fl & M_DECAL))
183 glDisable(GL_POLYGON_OFFSET_FILL);
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)
192 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0.0f;
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;
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;
205 float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
206 float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
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);
214 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
215 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
218 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
220 glRotatef(rz, 0.0f, 0.0f, 1.0f);
222 mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
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);
239 /*---------------------------------------------------------------------------*/
241 void sol_back(const struct s_file *fp, float n, float f, float t)
243 const struct s_mtrl *mp = &default_mtrl;
247 /* Render all billboards in the given range. */
249 glDisable(GL_LIGHTING);
250 glDepthMask(GL_FALSE);
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);
256 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
258 glDepthMask(GL_TRUE);
259 glEnable(GL_LIGHTING);
262 /*---------------------------------------------------------------------------*/
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.
270 * However, this is only done once for each level. The results are
271 * stored in display lists. Thus, it is well worth it.
274 static void sol_draw_geom(const struct s_file *fp,
275 const struct s_geom *gp, int mi)
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;
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;
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;
305 static void sol_draw_lump(const struct s_file *fp,
306 const struct s_lump *lp, int mi)
310 for (i = 0; i < lp->gc; i++)
311 sol_draw_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
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,
321 /* Iterate all materials of the correct opacity. */
323 for (mi = 0; mi < fp->mc; mi++)
324 if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
326 if (sol_enum_mtrl(fp, bp, mi))
328 /* Set the material state. */
330 mp = sol_draw_mtrl(fp, fp->mv + mi, mp);
332 /* Render all geometry of that material. */
334 glBegin(GL_TRIANGLES);
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);
348 static void sol_draw_list(const struct s_file *fp,
349 const struct s_body *bp, GLuint list)
351 float p[3], e[4], u[3], a;
353 sol_body_p(p, fp, bp->pi, bp->t);
354 sol_body_e(e, fp, bp, 0);
356 q_as_axisangle(e, u, &a);
361 /* Translate and rotate a moving body. */
363 glTranslatef(p[0], p[1], p[2]);
364 glRotatef(a, u[0], u[1], u[2]);
373 void sol_draw(const struct s_file *fp, int depthmask, int depthtest)
377 /* Render all opaque geometry into the color and depth buffers. */
379 for (bi = 0; bi < fp->bc; bi++)
381 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].ol);
383 /* Render all translucent geometry into only the color buffer. */
385 if (depthtest == 0) glDisable(GL_DEPTH_TEST);
386 if (depthmask == 0) glDepthMask(GL_FALSE);
388 for (bi = 0; bi < fp->bc; bi++)
390 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].tl);
392 if (depthmask == 0) glDepthMask(GL_TRUE);
393 if (depthtest == 0) glEnable(GL_DEPTH_TEST);
396 void sol_bill(const struct s_file *fp, const float *M, float t)
398 const struct s_mtrl *mp = &default_mtrl;
402 for (ri = 0; ri < fp->rc; ++ri)
404 const struct s_bill *rp = fp->rv + ri;
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;
415 mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
419 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
421 if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
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);
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);
439 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
442 void sol_refl(const struct s_file *fp)
446 /* Render all reflective geometry into the color and depth buffers. */
448 for (bi = 0; bi < fp->bc; bi++)
450 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].rl);
453 /*---------------------------------------------------------------------------*/
455 static void sol_shad_geom(const struct s_file *fp,
456 const struct s_geom *gp, int mi)
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;
464 glTexCoord2f(vi[0], vi[2]);
467 glTexCoord2f(vj[0], vj[2]);
470 glTexCoord2f(vk[0], vk[2]);
475 static void sol_shad_lump(const struct s_file *fp,
476 const struct s_lump *lp, int mi)
480 for (i = 0; i < lp->gc; i++)
481 sol_shad_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
484 static void sol_shad_body(const struct s_file *fp,
485 const struct s_body *bp,
492 glEnable(GL_POLYGON_OFFSET_FILL);
493 glPolygonOffset(-1.0f, -2.0f);
496 glBegin(GL_TRIANGLES);
498 for (mi = 0; mi < fp->mc; mi++)
499 if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
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);
510 glDisable(GL_POLYGON_OFFSET_FILL);
513 static void sol_shad_list(const struct s_file *fp,
514 const struct s_body *bp, GLuint list)
516 float p[3], e[4], u[3], a;
518 sol_body_p(p, fp, bp->pi, bp->t);
519 sol_body_e(e, fp, bp, 0);
521 q_as_axisangle(e, u, &a);
526 /* Translate and rotate a moving body. */
528 glTranslatef(p[0], p[1], p[2]);
529 glRotatef(a, u[0], u[1], u[2]);
531 /* Translate the shadow on a moving body. */
533 glMatrixMode(GL_TEXTURE);
536 glTranslatef(p[0], p[2], 0.0f);
537 glRotatef(-a, u[0], u[2], u[1]);
539 glMatrixMode(GL_MODELVIEW);
545 /* Pop the shadow translation. */
547 glMatrixMode(GL_TEXTURE);
551 glMatrixMode(GL_MODELVIEW);
556 void sol_shad(const struct s_file *fp)
560 /* Render all shadowed geometry. */
562 glDepthMask(GL_FALSE);
564 for (bi = 0; bi < fp->bc; bi++)
566 sol_shad_list(fp, fp->bv + bi, fp->bv[bi].sl);
568 glDepthMask(GL_TRUE);
571 /*---------------------------------------------------------------------------*/
573 static void sol_load_objects(struct s_file *fp, int s)
577 /* Here we sort geometry into display lists by material type. */
579 for (i = 0; i < fp->bc; i++)
581 struct s_body *bp = fp->bv + i;
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);
589 /* Draw all opaque geometry, decals last. */
593 fp->bv[i].ol = glGenLists(1);
595 glNewList(fp->bv[i].ol, GL_COMPILE);
597 const struct s_mtrl *mp = &default_mtrl;
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);
605 else fp->bv[i].ol = 0;
607 /* Draw all translucent geometry, decals first. */
611 fp->bv[i].tl = glGenLists(1);
613 glNewList(fp->bv[i].tl, GL_COMPILE);
615 const struct s_mtrl *mp = &default_mtrl;
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);
623 else fp->bv[i].tl = 0;
625 /* Draw all reflective geometry. */
629 fp->bv[i].rl = glGenLists(1);
631 glNewList(fp->bv[i].rl, GL_COMPILE);
633 const struct s_mtrl *mp = &default_mtrl;
635 mp = sol_draw_body(fp, fp->bv + i, mp, M_REFLECTIVE, 0);
636 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
640 else fp->bv[i].rl = 0;
642 /* Draw all shadowed geometry. */
644 if (s && (on || rn || sn))
646 fp->bv[i].sl = glGenLists(1);
648 glNewList(fp->bv[i].sl, GL_COMPILE);
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);
655 /* Transparent shadowed geometry hack. */
658 sol_shad_body(fp, fp->bv + i, M_SHADOWED, M_DECAL);
660 sol_shad_body(fp, fp->bv + i, M_SHADOWED, 0);
665 else fp->bv[i].sl = 0;
669 static GLuint sol_find_texture(const char *name)
676 /* Prefer a lossless copy of the texture over a lossy compression. */
678 strncpy(png, name, PATHMAX); strcat(png, ".png");
679 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
681 /* Check for a PNG. */
683 if ((o = make_image_from_file(png)))
686 /* Check for a JPG. */
688 if ((o = make_image_from_file(jpg)))
694 static void sol_load_textures(struct s_file *fp)
698 /* Load the image referenced by each material. */
700 for (i = 0; i < fp->mc; i++)
701 if ((fp->mv[i].o = sol_find_texture(_(fp->mv[i].f))))
703 /* Set the texture to clamp or repeat based on material type. */
705 if (fp->mv[i].fl & M_CLAMPED)
707 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
708 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
712 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
713 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
718 /*---------------------------------------------------------------------------*/
720 int sol_load_gl(struct s_file *fp, const char *filename, int s)
722 if (sol_load_only_file(fp, filename))
724 sol_load_textures(fp);
725 sol_load_objects (fp, s);
731 /*---------------------------------------------------------------------------*/
733 void sol_free_gl(struct s_file *fp)
737 for (i = 0; i < fp->mc; i++)
739 if (glIsTexture(fp->mv[i].o))
740 glDeleteTextures(1, &fp->mv[i].o);
743 for (i = 0; i < fp->bc; i++)
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);
758 /*---------------------------------------------------------------------------*/