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"
31 /*---------------------------------------------------------------------------*/
33 static int sol_enum_mtrl(const struct s_file *fp,
34 const struct s_body *bp, int mi)
38 /* Count all lump geoms with this material. */
40 for (li = 0; li < bp->lc; li++)
42 int g0 = fp->lv[bp->l0 + li].g0;
43 int gc = fp->lv[bp->l0 + li].gc;
45 for (gi = 0; gi < gc; gi++)
46 if (fp->gv[fp->iv[g0 + gi]].mi == mi)
50 /* Count all body geoms with this material. */
52 for (gi = 0; gi < bp->gc; gi++)
53 if (fp->gv[fp->iv[bp->g0 + gi]].mi == mi)
59 static int sol_enum_body(const struct s_file *fp,
60 const struct s_body *bp, int fl)
64 /* Count all geoms with this flag. */
66 for (mi = 0; mi < fp->mc; mi++)
67 if (fp->mv[mi].fl & fl)
68 c = c + sol_enum_mtrl(fp, bp, mi);
73 /*---------------------------------------------------------------------------*/
75 #define tobyte(f) ((GLubyte) (f * 255.0f))
77 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
78 tobyte((a)[1]) == tobyte((b)[1]) && \
79 tobyte((a)[2]) == tobyte((b)[2]) && \
80 tobyte((a)[3]) == tobyte((b)[3]))
82 static struct s_mtrl default_mtrl =
84 { 0.8f, 0.8f, 0.8f, 1.0f },
85 { 0.2f, 0.2f, 0.2f, 1.0f },
86 { 0.0f, 0.0f, 0.0f, 1.0f },
87 { 0.0f, 0.0f, 0.0f, 1.0f },
88 { 0.0f, }, 0.0f, M_OPAQUE, 0, ""
91 static const struct s_mtrl *sol_draw_mtrl(const struct s_file *fp,
92 const struct s_mtrl *mp,
93 const struct s_mtrl *mq)
95 /* Change material properties only as needed. */
97 if (!color_cmp(mp->a, mq->a))
98 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp->a);
99 if (!color_cmp(mp->d, mq->d))
100 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp->d);
101 if (!color_cmp(mp->s, mq->s))
102 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp->s);
103 if (!color_cmp(mp->e, mq->e))
104 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp->e);
105 if (tobyte(mp->h[0]) != tobyte(mq->h[0]))
106 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp->h);
108 /* Bind the texture. */
111 glBindTexture(GL_TEXTURE_2D, mp->o);
113 /* Enable environment mapping. */
115 if ((mp->fl & M_ENVIRONMENT) && !(mq->fl & M_ENVIRONMENT))
117 glEnable(GL_TEXTURE_GEN_S);
118 glEnable(GL_TEXTURE_GEN_T);
120 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
121 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
124 /* Disable environment mapping. */
126 if ((mq->fl & M_ENVIRONMENT) && !(mp->fl & M_ENVIRONMENT))
128 glDisable(GL_TEXTURE_GEN_S);
129 glDisable(GL_TEXTURE_GEN_T);
132 /* Enable additive blending. */
134 if ((mp->fl & M_ADDITIVE) && !(mq->fl & M_ADDITIVE))
135 glBlendFunc(GL_ONE, GL_ONE);
137 /* Enable standard blending. */
139 if ((mq->fl & M_ADDITIVE) && !(mp->fl & M_ADDITIVE))
140 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
142 /* Enable visibility-from-behind. */
144 if ((mp->fl & M_TWO_SIDED) && !(mq->fl & M_TWO_SIDED))
146 glDisable(GL_CULL_FACE);
147 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
150 /* Disable visibility-from-behind. */
152 if ((mq->fl & M_TWO_SIDED) && !(mp->fl & M_TWO_SIDED))
154 glEnable(GL_CULL_FACE);
155 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
158 /* Enable decal offset. */
160 if ((mp->fl & M_DECAL) && !(mq->fl & M_DECAL))
162 glEnable(GL_POLYGON_OFFSET_FILL);
163 glPolygonOffset(-1.0f, -2.0f);
166 /* Disable decal offset. */
168 if ((mq->fl & M_DECAL) && !(mp->fl & M_DECAL))
169 glDisable(GL_POLYGON_OFFSET_FILL);
174 static const struct s_mtrl *sol_back_bill(const struct s_file *fp,
175 const struct s_bill *rp,
176 const struct s_mtrl *mp, float t)
178 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0.0f;
180 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
181 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
185 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
186 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
187 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
191 float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
192 float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
194 glRotatef(ry, 0.0f, 1.0f, 0.0f);
195 glRotatef(rx, 1.0f, 0.0f, 0.0f);
196 glTranslatef(0.0f, 0.0f, -rp->d);
200 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
201 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
204 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
206 glRotatef(rz, 0.0f, 0.0f, 1.0f);
208 mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
212 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y0);
213 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y0);
214 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y1);
215 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y1);
225 /*---------------------------------------------------------------------------*/
227 void sol_back(const struct s_file *fp, float n, float f, float t)
229 const struct s_mtrl *mp = &default_mtrl;
233 /* Render all billboards in the given range. */
235 glDisable(GL_LIGHTING);
236 glDepthMask(GL_FALSE);
238 for (ri = 0; ri < fp->rc; ri++)
239 if (n <= fp->rv[ri].d && fp->rv[ri].d < f)
240 mp = sol_back_bill(fp, fp->rv + ri, mp, t);
242 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
244 glDepthMask(GL_TRUE);
245 glEnable(GL_LIGHTING);
248 /*---------------------------------------------------------------------------*/
250 * The following code renders a body in a ludicrously inefficient
251 * manner. It iterates the materials and scans the data structure for
252 * geometry using each. This has the effect of absolutely minimizing
253 * material changes, texture bindings, and Begin/End pairs, but
254 * maximizing trips through the data.
256 * However, this is only done once for each level. The results are
257 * stored in display lists. Thus, it is well worth it.
260 static void sol_draw_geom(const struct s_file *fp,
261 const struct s_geom *gp, int mi)
265 const float *ui = fp->tv[gp->ti].u;
266 const float *uj = fp->tv[gp->tj].u;
267 const float *uk = fp->tv[gp->tk].u;
269 const float *ni = fp->sv[gp->si].n;
270 const float *nj = fp->sv[gp->sj].n;
271 const float *nk = fp->sv[gp->sk].n;
273 const float *vi = fp->vv[gp->vi].p;
274 const float *vj = fp->vv[gp->vj].p;
275 const float *vk = fp->vv[gp->vk].p;
291 static void sol_draw_lump(const struct s_file *fp,
292 const struct s_lump *lp, int mi)
296 for (i = 0; i < lp->gc; i++)
297 sol_draw_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
300 static const struct s_mtrl *sol_draw_body(const struct s_file *fp,
301 const struct s_body *bp,
302 const struct s_mtrl *mp,
307 /* Iterate all materials of the correct opacity. */
309 for (mi = 0; mi < fp->mc; mi++)
310 if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
312 if (sol_enum_mtrl(fp, bp, mi))
314 /* Set the material state. */
316 mp = sol_draw_mtrl(fp, fp->mv + mi, mp);
318 /* Render all geometry of that material. */
320 glBegin(GL_TRIANGLES);
322 for (li = 0; li < bp->lc; li++)
323 sol_draw_lump(fp, fp->lv + bp->l0 + li, mi);
324 for (gi = 0; gi < bp->gc; gi++)
325 sol_draw_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
334 static void sol_draw_list(const struct s_file *fp,
335 const struct s_body *bp, GLuint list)
339 sol_body_p(p, fp, bp);
343 /* Translate a moving body. */
345 glTranslatef(p[0], p[1], p[2]);
354 void sol_draw(const struct s_file *fp, int depthmask, int depthtest)
358 /* Render all opaque geometry into the color and depth buffers. */
360 for (bi = 0; bi < fp->bc; bi++)
362 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].ol);
364 /* Render all translucent geometry into only the color buffer. */
366 if (depthtest == 0) glDisable(GL_DEPTH_TEST);
367 if (depthmask == 0) glDepthMask(GL_FALSE);
369 for (bi = 0; bi < fp->bc; bi++)
371 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].tl);
373 if (depthmask == 0) glDepthMask(GL_TRUE);
374 if (depthtest == 0) glEnable(GL_DEPTH_TEST);
377 void sol_bill(const struct s_file *fp, const float *M, float t)
379 const struct s_mtrl *mp = &default_mtrl;
383 for (ri = 0; ri < fp->rc; ++ri)
385 const struct s_bill *rp = fp->rv + ri;
390 float w = rp->w [0] + rp->w [1] * T + rp->w [2] * S;
391 float h = rp->h [0] + rp->h [1] * T + rp->h [2] * S;
392 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * S;
393 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * S;
394 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * S;
396 mp = sol_draw_mtrl(fp, fp->mv + rp->mi, mp);
400 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
402 if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
404 if (fabsf(rx) > 0.0f) glRotatef(rx, 1.0f, 0.0f, 0.0f);
405 if (fabsf(ry) > 0.0f) glRotatef(ry, 0.0f, 1.0f, 0.0f);
406 if (fabsf(rz) > 0.0f) glRotatef(rz, 0.0f, 0.0f, 1.0f);
410 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, -h / 2);
411 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, -h / 2);
412 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, +h / 2);
413 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, +h / 2);
420 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
423 void sol_refl(const struct s_file *fp)
427 /* Render all reflective geometry into the color and depth buffers. */
429 for (bi = 0; bi < fp->bc; bi++)
431 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].rl);
434 /*---------------------------------------------------------------------------*/
436 static void sol_shad_geom(const struct s_file *fp,
437 const struct s_geom *gp, int mi)
441 const float *vi = fp->vv[gp->vi].p;
442 const float *vj = fp->vv[gp->vj].p;
443 const float *vk = fp->vv[gp->vk].p;
445 glTexCoord2f(vi[0], vi[2]);
448 glTexCoord2f(vj[0], vj[2]);
451 glTexCoord2f(vk[0], vk[2]);
456 static void sol_shad_lump(const struct s_file *fp,
457 const struct s_lump *lp, int mi)
461 for (i = 0; i < lp->gc; i++)
462 sol_shad_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
465 static void sol_shad_body(const struct s_file *fp,
466 const struct s_body *bp,
473 glEnable(GL_POLYGON_OFFSET_FILL);
474 glPolygonOffset(-1.0f, -2.0f);
477 glBegin(GL_TRIANGLES);
479 for (mi = 0; mi < fp->mc; mi++)
480 if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
482 for (li = 0; li < bp->lc; li++)
483 sol_shad_lump(fp, fp->lv + bp->l0 + li, mi);
484 for (gi = 0; gi < bp->gc; gi++)
485 sol_shad_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
491 glDisable(GL_POLYGON_OFFSET_FILL);
494 static void sol_shad_list(const struct s_file *fp,
495 const struct s_body *bp, GLuint list)
499 sol_body_p(p, fp, bp);
503 /* Translate a moving body. */
505 glTranslatef(p[0], p[1], p[2]);
507 /* Translate the shadow on a moving body. */
509 glMatrixMode(GL_TEXTURE);
512 glTranslatef(p[0], p[2], 0.0f);
514 glMatrixMode(GL_MODELVIEW);
520 /* Pop the shadow translation. */
522 glMatrixMode(GL_TEXTURE);
526 glMatrixMode(GL_MODELVIEW);
531 void sol_shad(const struct s_file *fp)
535 /* Render all shadowed geometry. */
537 glDepthMask(GL_FALSE);
539 for (bi = 0; bi < fp->bc; bi++)
541 sol_shad_list(fp, fp->bv + bi, fp->bv[bi].sl);
543 glDepthMask(GL_TRUE);
546 /*---------------------------------------------------------------------------*/
548 static void sol_load_objects(struct s_file *fp, int s)
552 /* Here we sort geometry into display lists by material type. */
554 for (i = 0; i < fp->bc; i++)
556 struct s_body *bp = fp->bv + i;
558 int on = sol_enum_body(fp, bp, M_OPAQUE);
559 int tn = sol_enum_body(fp, bp, M_TRANSPARENT);
560 int rn = sol_enum_body(fp, bp, M_REFLECTIVE);
561 int dn = sol_enum_body(fp, bp, M_DECAL);
563 /* Draw all opaque geometry, decals last. */
567 fp->bv[i].ol = glGenLists(1);
569 glNewList(fp->bv[i].ol, GL_COMPILE);
571 const struct s_mtrl *mp = &default_mtrl;
573 mp = sol_draw_body(fp, fp->bv + i, mp, M_OPAQUE, 0);
574 mp = sol_draw_body(fp, fp->bv + i, mp, M_OPAQUE, M_DECAL);
575 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
579 else fp->bv[i].ol = 0;
581 /* Draw all translucent geometry, decals first. */
585 fp->bv[i].tl = glGenLists(1);
587 glNewList(fp->bv[i].tl, GL_COMPILE);
589 const struct s_mtrl *mp = &default_mtrl;
591 mp = sol_draw_body(fp, fp->bv + i, mp, M_TRANSPARENT, M_DECAL);
592 mp = sol_draw_body(fp, fp->bv + i, mp, M_TRANSPARENT, 0);
593 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
597 else fp->bv[i].tl = 0;
599 /* Draw all reflective geometry. */
603 fp->bv[i].rl = glGenLists(1);
605 glNewList(fp->bv[i].rl, GL_COMPILE);
607 const struct s_mtrl *mp = &default_mtrl;
609 mp = sol_draw_body(fp, fp->bv + i, mp, M_REFLECTIVE, 0);
610 mp = sol_draw_mtrl(fp, &default_mtrl, mp);
614 else fp->bv[i].rl = 0;
616 /* Draw all shadowed geometry. */
620 fp->bv[i].sl = glGenLists(1);
622 glNewList(fp->bv[i].sl, GL_COMPILE);
624 if (on) sol_shad_body(fp, fp->bv + i, M_OPAQUE, 0);
625 if (rn) sol_shad_body(fp, fp->bv + i, M_REFLECTIVE, 0);
626 if (dn) sol_shad_body(fp, fp->bv + i, M_OPAQUE, M_DECAL);
630 else fp->bv[i].sl = 0;
634 static GLuint sol_find_texture(const char *name)
641 /* Prefer a lossless copy of the texture over a lossy compression. */
643 strncpy(png, name, PATHMAX); strcat(png, ".png");
644 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
646 /* Check for a PNG. */
648 if ((o = make_image_from_file(png)))
651 /* Check for a JPG. */
653 if ((o = make_image_from_file(jpg)))
659 static void sol_load_textures(struct s_file *fp, int k)
663 /* Load the image referenced by each material. */
665 for (i = 0; i < fp->mc; i++)
666 if ((fp->mv[i].o = sol_find_texture(_(fp->mv[i].f))))
668 /* Set the texture to clamp or repeat based on material type. */
670 if (fp->mv[i].fl & M_CLAMPED)
672 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
673 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
677 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
678 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
683 /*---------------------------------------------------------------------------*/
685 int sol_load_gl(struct s_file *fp, const char *filename, int k, int s)
687 if (sol_load_only_file(fp, filename))
689 sol_load_textures(fp, k);
690 sol_load_objects (fp, s);
696 /*---------------------------------------------------------------------------*/
698 void sol_free_gl(struct s_file *fp)
702 for (i = 0; i < fp->mc; i++)
704 if (glIsTexture(fp->mv[i].o))
705 glDeleteTextures(1, &fp->mv[i].o);
708 for (i = 0; i < fp->bc; i++)
710 if (glIsList(fp->bv[i].ol))
711 glDeleteLists(fp->bv[i].ol, 1);
712 if (glIsList(fp->bv[i].tl))
713 glDeleteLists(fp->bv[i].tl, 1);
714 if (glIsList(fp->bv[i].rl))
715 glDeleteLists(fp->bv[i].rl, 1);
721 /*---------------------------------------------------------------------------*/