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 static void sol_draw_mtrl(const struct s_file *fp, int i)
76 const struct s_mtrl *mp = fp->mv + i;
78 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp->a);
79 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp->d);
80 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp->s);
81 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp->e);
82 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp->h);
84 if (mp->fl & M_ENVIRONMENT)
86 glEnable(GL_TEXTURE_GEN_S);
87 glEnable(GL_TEXTURE_GEN_T);
89 glBindTexture(GL_TEXTURE_2D, mp->o);
91 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
92 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
96 glDisable(GL_TEXTURE_GEN_S);
97 glDisable(GL_TEXTURE_GEN_T);
99 glBindTexture(GL_TEXTURE_2D, mp->o);
102 if (mp->fl & M_ADDITIVE)
103 glBlendFunc(GL_ONE, GL_ONE);
105 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
108 static void sol_draw_bill(const struct s_file *fp,
109 const struct s_bill *rp, float t)
111 float T = fmodf(t, rp->t) - rp->t / 2;
113 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
114 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
118 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
119 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
120 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
124 float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
125 float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
127 glRotatef(ry, 0.0f, 1.0f, 0.0f);
128 glRotatef(rx, 1.0f, 0.0f, 0.0f);
129 glTranslatef(0.0f, 0.0f, -rp->d);
133 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
134 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
137 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
139 glRotatef(rz, 0.0f, 0.0f, 1.0f);
141 sol_draw_mtrl(fp, rp->mi);
145 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y0);
146 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y0);
147 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y1);
148 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y1);
156 void sol_back(const struct s_file *fp, float n, float f, float t)
160 glPushAttrib(GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
162 /* Render all billboards in the given range. */
164 glDisable(GL_LIGHTING);
165 glDepthMask(GL_FALSE);
167 for (ri = 0; ri < fp->rc; ri++)
168 if (n <= fp->rv[ri].d && fp->rv[ri].d < f)
169 sol_draw_bill(fp, fp->rv + ri, t);
174 /*---------------------------------------------------------------------------*/
176 * The following code renders a body in a ludicrously inefficient
177 * manner. It iterates the materials and scans the data structure for
178 * geometry using each. This has the effect of absolutely minimizing
179 * material changes, texture bindings, and Begin/End pairs, but
180 * maximizing trips through the data.
182 * However, this is only done once for each level. The results are
183 * stored in display lists. Thus, it is well worth it.
186 static void sol_draw_geom(const struct s_file *fp,
187 const struct s_geom *gp, int mi)
191 const float *ui = fp->tv[gp->ti].u;
192 const float *uj = fp->tv[gp->tj].u;
193 const float *uk = fp->tv[gp->tk].u;
195 const float *ni = fp->sv[gp->si].n;
196 const float *nj = fp->sv[gp->sj].n;
197 const float *nk = fp->sv[gp->sk].n;
199 const float *vi = fp->vv[gp->vi].p;
200 const float *vj = fp->vv[gp->vj].p;
201 const float *vk = fp->vv[gp->vk].p;
217 static void sol_draw_lump(const struct s_file *fp,
218 const struct s_lump *lp, int mi)
222 for (i = 0; i < lp->gc; i++)
223 sol_draw_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
226 static void sol_draw_body(const struct s_file *fp,
227 const struct s_body *bp, int fl, int decal)
233 glEnable(GL_POLYGON_OFFSET_FILL);
234 glPolygonOffset(-1.0f, -2.0f);
237 /* Iterate all materials of the correct opacity. */
239 for (mi = 0; mi < fp->mc; mi++)
240 if ((fp->mv[mi].fl & fl) && (fp->mv[mi].fl & M_DECAL) == decal)
242 if (sol_enum_mtrl(fp, bp, mi))
244 /* Set the material state. */
246 sol_draw_mtrl(fp, mi);
248 /* Render all geometry of that material. */
250 glBegin(GL_TRIANGLES);
252 for (li = 0; li < bp->lc; li++)
253 sol_draw_lump(fp, fp->lv + bp->l0 + li, mi);
254 for (gi = 0; gi < bp->gc; gi++)
255 sol_draw_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
262 glDisable(GL_POLYGON_OFFSET_FILL);
265 static void sol_draw_list(const struct s_file *fp,
266 const struct s_body *bp, GLuint list)
270 sol_body_p(p, fp, bp);
274 /* Translate a moving body. */
276 glTranslatef(p[0], p[1], p[2]);
285 void sol_draw(const struct s_file *fp)
289 glPushAttrib(GL_ENABLE_BIT |
292 GL_COLOR_BUFFER_BIT |
293 GL_DEPTH_BUFFER_BIT);
295 /* Render all opaque geometry into the color and depth buffers. */
297 for (bi = 0; bi < fp->bc; bi++)
299 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].ol);
301 /* Render all translucent geometry into only the color buffer. */
303 glDepthMask(GL_FALSE);
306 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
308 for (bi = 0; bi < fp->bc; bi++)
310 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].tl);
315 void sol_refl(const struct s_file *fp)
319 glPushAttrib(GL_LIGHTING_BIT);
321 /* Render all reflective geometry into the color and depth buffers. */
324 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
326 for (bi = 0; bi < fp->bc; bi++)
328 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].rl);
333 /*---------------------------------------------------------------------------*/
335 static void sol_shad_geom(const struct s_file *fp,
336 const struct s_geom *gp, int mi)
340 const float *vi = fp->vv[gp->vi].p;
341 const float *vj = fp->vv[gp->vj].p;
342 const float *vk = fp->vv[gp->vk].p;
344 glTexCoord2f(vi[0], vi[2]);
347 glTexCoord2f(vj[0], vj[2]);
350 glTexCoord2f(vk[0], vk[2]);
355 static void sol_shad_lump(const struct s_file *fp,
356 const struct s_lump *lp, int mi)
360 for (i = 0; i < lp->gc; i++)
361 sol_shad_geom(fp, fp->gv + fp->iv[lp->g0 + i], mi);
364 static void sol_shad_body(const struct s_file *fp,
365 const struct s_body *bp, int fl, int decal)
371 glEnable(GL_POLYGON_OFFSET_FILL);
372 glPolygonOffset(-1.0f, -2.0f);
375 glBegin(GL_TRIANGLES);
377 for (mi = 0; mi < fp->mc; mi++)
378 if (fp->mv[mi].fl & fl)
380 for (li = 0; li < bp->lc; li++)
381 sol_shad_lump(fp, fp->lv + bp->l0 + li, mi);
382 for (gi = 0; gi < bp->gc; gi++)
383 sol_shad_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi);
389 glDisable(GL_POLYGON_OFFSET_FILL);
392 static void sol_shad_list(const struct s_file *fp,
393 const struct s_body *bp, GLuint list)
397 sol_body_p(p, fp, bp);
401 /* Translate a moving body. */
403 glTranslatef(p[0], p[1], p[2]);
405 /* Translate the shadow on a moving body. */
407 glMatrixMode(GL_TEXTURE);
410 glTranslatef(p[0], p[2], 0.0f);
412 glMatrixMode(GL_MODELVIEW);
418 /* Pop the shadow translation. */
420 glMatrixMode(GL_TEXTURE);
424 glMatrixMode(GL_MODELVIEW);
429 void sol_shad(const struct s_file *fp)
433 glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT);
435 /* Render all shadowed geometry. */
438 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440 glDepthFunc(GL_LEQUAL);
441 glDepthMask(GL_FALSE);
443 for (bi = 0; bi < fp->bc; bi++)
445 sol_shad_list(fp, fp->bv + bi, fp->bv[bi].sl);
450 /*---------------------------------------------------------------------------*/
452 static void sol_load_objects(struct s_file *fp, int s)
456 /* Here we sort geometry into display lists by material type. */
458 for (i = 0; i < fp->bc; i++)
460 struct s_body *bp = fp->bv + i;
462 /* Draw all opaque geometry, decals last. */
464 if (sol_enum_body(fp, bp, M_OPAQUE | M_ENVIRONMENT))
466 fp->bv[i].ol = glGenLists(1);
468 glNewList(fp->bv[i].ol, GL_COMPILE);
470 sol_draw_body(fp, fp->bv+i, M_OPAQUE | M_ENVIRONMENT, 0);
471 sol_draw_body(fp, fp->bv+i, M_OPAQUE | M_ENVIRONMENT, M_DECAL);
475 else fp->bv[i].ol = 0;
477 /* Draw all translucent geometry, decals first. */
479 if (sol_enum_body(fp, bp, M_TRANSPARENT))
481 fp->bv[i].tl = glGenLists(1);
483 glNewList(fp->bv[i].tl, GL_COMPILE);
485 sol_draw_body(fp, fp->bv+i, M_TRANSPARENT, M_DECAL);
486 sol_draw_body(fp, fp->bv+i, M_TRANSPARENT, 0);
490 else fp->bv[i].tl = 0;
492 /* Draw all reflective geometry. */
494 if (sol_enum_body(fp, bp, M_REFLECTIVE))
496 fp->bv[i].rl = glGenLists(1);
498 glNewList(fp->bv[i].rl, GL_COMPILE);
500 sol_draw_body(fp, fp->bv+i, M_REFLECTIVE, 0);
504 else fp->bv[i].rl = 0;
506 /* Draw all shadowed geometry. */
508 if (s && sol_enum_body(fp, bp, M_SHADOWED))
510 fp->bv[i].sl = glGenLists(1);
512 glNewList(fp->bv[i].sl, GL_COMPILE);
514 sol_shad_body(fp, fp->bv+i, M_SHADOWED, 0);
515 sol_shad_body(fp, fp->bv+i, M_SHADOWED, M_DECAL);
519 else fp->bv[i].sl = 0;
523 static GLuint sol_find_texture(const char *name)
530 /* Prefer a lossless copy of the texture over a lossy compression. */
532 strncpy(png, name, PATHMAX); strcat(png, ".png");
533 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
535 /* Check for a PNG. */
537 if ((o = make_image_from_file(png)))
540 /* Check for a JPG. */
542 if ((o = make_image_from_file(jpg)))
548 static void sol_load_textures(struct s_file *fp, int k)
552 /* Load the image referenced by each material. */
554 for (i = 0; i < fp->mc; i++)
555 if ((fp->mv[i].o = sol_find_texture(fp->mv[i].f)))
557 /* Set the texture to clamp or repeat based on material type. */
559 if (fp->mv[i].fl & M_CLAMPED)
561 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
562 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
566 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
567 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
572 /*---------------------------------------------------------------------------*/
574 int sol_load_gl(struct s_file *fp, const char *filename, int k, int s)
576 if (sol_load_only_file(fp, filename))
578 sol_load_textures(fp, k);
579 sol_load_objects (fp, s);
585 /*---------------------------------------------------------------------------*/
587 void sol_free_gl(struct s_file *fp)
591 for (i = 0; i < fp->mc; i++)
593 if (glIsTexture(fp->mv[i].o))
594 glDeleteTextures(1, &fp->mv[i].o);
597 for (i = 0; i < fp->bc; i++)
599 if (glIsList(fp->bv[i].ol))
600 glDeleteLists(fp->bv[i].ol, 1);
601 if (glIsList(fp->bv[i].tl))
602 glDeleteLists(fp->bv[i].tl, 1);
603 if (glIsList(fp->bv[i].rl))
604 glDeleteLists(fp->bv[i].rl, 1);
610 /*---------------------------------------------------------------------------*/