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"
27 #include "base_config.h"
30 #include "solid_draw.h"
31 #include "solid_all.h"
33 /*---------------------------------------------------------------------------*/
35 static int sol_enum_mtrl(const struct s_base *base,
36 const struct b_body *bp, int mi)
40 /* Count all lump geoms with this material. */
42 for (li = 0; li < bp->lc; li++)
44 int g0 = base->lv[bp->l0 + li].g0;
45 int gc = base->lv[bp->l0 + li].gc;
47 for (gi = 0; gi < gc; gi++)
48 if (base->gv[base->iv[g0 + gi]].mi == mi)
52 /* Count all body geoms with this material. */
54 for (gi = 0; gi < bp->gc; gi++)
55 if (base->gv[base->iv[bp->g0 + gi]].mi == mi)
61 static int sol_enum_body(const struct s_base *base,
62 const struct b_body *bp, int fl)
66 /* Count all geoms with this flag. */
68 for (mi = 0; mi < base->mc; mi++)
69 if (base->mv[mi].fl & fl)
70 c = c + sol_enum_mtrl(base, bp, mi);
75 /*---------------------------------------------------------------------------*/
77 #define tobyte(f) ((GLubyte) (f * 255.0f))
79 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
80 tobyte((a)[1]) == tobyte((b)[1]) && \
81 tobyte((a)[2]) == tobyte((b)[2]) && \
82 tobyte((a)[3]) == tobyte((b)[3]))
84 static struct b_mtrl default_base_mtrl =
86 { 0.8f, 0.8f, 0.8f, 1.0f },
87 { 0.2f, 0.2f, 0.2f, 1.0f },
88 { 0.0f, 0.0f, 0.0f, 1.0f },
89 { 0.0f, 0.0f, 0.0f, 1.0f },
90 { 0.0f }, 0.0f, M_OPAQUE, ""
93 static struct d_mtrl default_draw_mtrl =
98 static const struct d_mtrl *sol_draw_mtrl(const struct s_draw *draw,
99 const struct d_mtrl *mp_draw,
100 const struct d_mtrl *mq_draw)
102 const struct b_mtrl *mp_base = mp_draw->base;
103 const struct b_mtrl *mq_base = mq_draw->base;
105 /* Change material properties only as needed. */
107 if (!color_cmp(mp_base->a, mq_base->a))
108 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp_base->a);
109 if (!color_cmp(mp_base->d, mq_base->d))
110 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp_base->d);
111 if (!color_cmp(mp_base->s, mq_base->s))
112 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp_base->s);
113 if (!color_cmp(mp_base->e, mq_base->e))
114 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp_base->e);
115 if (tobyte(mp_base->h[0]) != tobyte(mq_base->h[0]))
116 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp_base->h);
118 /* Bind the texture. */
120 if (mp_draw->o != mq_draw->o)
121 glBindTexture(GL_TEXTURE_2D, mp_draw->o);
123 /* Enable environment mapping. */
125 if ((mp_base->fl & M_ENVIRONMENT) && !(mq_base->fl & M_ENVIRONMENT))
127 glEnable(GL_TEXTURE_GEN_S);
128 glEnable(GL_TEXTURE_GEN_T);
130 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
131 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
134 /* Disable environment mapping. */
136 if ((mq_base->fl & M_ENVIRONMENT) && !(mp_base->fl & M_ENVIRONMENT))
138 glDisable(GL_TEXTURE_GEN_S);
139 glDisable(GL_TEXTURE_GEN_T);
142 /* Enable additive blending. */
144 if ((mp_base->fl & M_ADDITIVE) && !(mq_base->fl & M_ADDITIVE))
145 glBlendFunc(GL_ONE, GL_ONE);
147 /* Enable standard blending. */
149 if ((mq_base->fl & M_ADDITIVE) && !(mp_base->fl & M_ADDITIVE))
150 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
152 /* Enable visibility-from-behind. */
154 if ((mp_base->fl & M_TWO_SIDED) && !(mq_base->fl & M_TWO_SIDED))
156 glDisable(GL_CULL_FACE);
157 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
160 /* Disable visibility-from-behind. */
162 if ((mq_base->fl & M_TWO_SIDED) && !(mp_base->fl & M_TWO_SIDED))
164 glEnable(GL_CULL_FACE);
165 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
168 /* Enable decal offset. */
170 if ((mp_base->fl & M_DECAL) && !(mq_base->fl & M_DECAL))
172 glEnable(GL_POLYGON_OFFSET_FILL);
173 glPolygonOffset(-1.0f, -2.0f);
176 /* Disable decal offset. */
178 if ((mq_base->fl & M_DECAL) && !(mp_base->fl & M_DECAL))
179 glDisable(GL_POLYGON_OFFSET_FILL);
184 static const struct d_mtrl *sol_back_bill(const struct s_draw *draw,
185 const struct b_bill *rp,
186 const struct d_mtrl *mp,
189 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0.0f;
191 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
192 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
196 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
197 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
198 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
202 float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
203 float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
205 glRotatef(ry, 0.0f, 1.0f, 0.0f);
206 glRotatef(rx, 1.0f, 0.0f, 0.0f);
207 glTranslatef(0.0f, 0.0f, -rp->d);
211 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
212 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
215 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
217 glRotatef(rz, 0.0f, 0.0f, 1.0f);
219 mp = sol_draw_mtrl(draw, draw->mv + rp->mi, mp);
223 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y0);
224 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y0);
225 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y1);
226 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y1);
236 /*---------------------------------------------------------------------------*/
238 void sol_back(const struct s_draw *draw, float n, float f, float t)
240 const struct d_mtrl *mp = &default_draw_mtrl;
244 /* Render all billboards in the given range. */
246 if (draw && draw->base)
248 glDisable(GL_LIGHTING);
249 glDepthMask(GL_FALSE);
251 for (ri = 0; ri < draw->base->rc; ri++)
252 if (n <= draw->base->rv[ri].d && draw->base->rv[ri].d < f)
253 mp = sol_back_bill(draw, draw->base->rv + ri, mp, t);
255 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
257 glDepthMask(GL_TRUE);
258 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_base *base,
275 const struct b_geom *gp, int mi)
279 const float *ui = base->tv[gp->ti].u;
280 const float *uj = base->tv[gp->tj].u;
281 const float *uk = base->tv[gp->tk].u;
283 const float *ni = base->sv[gp->si].n;
284 const float *nj = base->sv[gp->sj].n;
285 const float *nk = base->sv[gp->sk].n;
287 const float *vi = base->vv[gp->vi].p;
288 const float *vj = base->vv[gp->vj].p;
289 const float *vk = base->vv[gp->vk].p;
305 static void sol_draw_lump(const struct s_base *base,
306 const struct b_lump *lp, int mi)
310 for (i = 0; i < lp->gc; i++)
311 sol_draw_geom(base, base->gv + base->iv[lp->g0 + i], mi);
314 static const struct d_mtrl *sol_draw_body(const struct s_draw *draw,
315 const struct b_body *bp,
316 const struct d_mtrl *mp,
319 const struct s_base *base = draw->base;
323 /* Iterate all materials of the correct opacity. */
325 for (mi = 0; mi < draw->mc; mi++)
327 struct d_mtrl *mq = draw->mv + mi;
329 if ((mq->base->fl & fl) && (mq->base->fl & M_DECAL) == decal)
331 if (sol_enum_mtrl(draw->base, bp, mi))
333 /* Set the material state. */
335 mp = sol_draw_mtrl(draw, mq, mp);
337 /* Render all geometry of that material. */
339 glBegin(GL_TRIANGLES);
341 for (li = 0; li < bp->lc; li++)
342 sol_draw_lump(draw->base,
343 base->lv + bp->l0 + li,
345 for (gi = 0; gi < bp->gc; gi++)
346 sol_draw_geom(draw->base,
347 base->gv + base->iv[bp->g0 + gi],
358 static void sol_draw_list(const struct s_vary *vary,
359 const struct v_body *bp, GLuint list)
361 float p[3], e[4], u[3], a;
363 sol_body_p(p, vary, bp->pi, bp->t);
364 sol_body_e(e, vary, bp, 0);
366 q_as_axisangle(e, u, &a);
371 /* Translate and rotate a moving body. */
373 glTranslatef(p[0], p[1], p[2]);
374 glRotatef(a, u[0], u[1], u[2]);
383 void sol_draw(const struct s_draw *draw, int depthmask, int depthtest)
387 /* Render all opaque geometry into the color and depth buffers. */
389 for (bi = 0; bi < draw->bc; bi++)
391 sol_draw_list(draw->vary, draw->vary->bv + bi, draw->bv[bi].ol);
393 /* Render all translucent geometry into only the color buffer. */
395 if (depthtest == 0) glDisable(GL_DEPTH_TEST);
396 if (depthmask == 0) glDepthMask(GL_FALSE);
398 for (bi = 0; bi < draw->bc; bi++)
400 sol_draw_list(draw->vary, draw->vary->bv + bi, draw->bv[bi].tl);
402 if (depthmask == 0) glDepthMask(GL_TRUE);
403 if (depthtest == 0) glEnable(GL_DEPTH_TEST);
406 void sol_bill(const struct s_draw *draw, const float *M, float t)
408 const struct d_mtrl *mp = &default_draw_mtrl;
412 for (ri = 0; ri < draw->base->rc; ++ri)
414 const struct b_bill *rp = draw->base->rv + ri;
419 float w = rp->w [0] + rp->w [1] * T + rp->w [2] * S;
420 float h = rp->h [0] + rp->h [1] * T + rp->h [2] * S;
421 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * S;
422 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * S;
423 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * S;
425 mp = sol_draw_mtrl(draw, draw->mv + rp->mi, mp);
429 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
431 if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
433 if (fabsf(rx) > 0.0f) glRotatef(rx, 1.0f, 0.0f, 0.0f);
434 if (fabsf(ry) > 0.0f) glRotatef(ry, 0.0f, 1.0f, 0.0f);
435 if (fabsf(rz) > 0.0f) glRotatef(rz, 0.0f, 0.0f, 1.0f);
439 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, -h / 2);
440 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, -h / 2);
441 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, +h / 2);
442 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, +h / 2);
449 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
452 void sol_refl(const struct s_draw *draw)
456 /* Render all reflective geometry into the color and depth buffers. */
458 for (bi = 0; bi < draw->bc; bi++)
460 sol_draw_list(draw->vary, draw->vary->bv + bi, draw->bv[bi].rl);
463 /*---------------------------------------------------------------------------*/
465 static void sol_shad_geom(const struct s_base *base,
466 const struct b_geom *gp, int mi)
470 const float *vi = base->vv[gp->vi].p;
471 const float *vj = base->vv[gp->vj].p;
472 const float *vk = base->vv[gp->vk].p;
480 static void sol_shad_lump(const struct s_base *base,
481 const struct b_lump *lp, int mi)
485 for (i = 0; i < lp->gc; i++)
486 sol_shad_geom(base, base->gv + base->iv[lp->g0 + i], mi);
489 static void sol_shad_body(const struct s_base *base,
490 const struct b_body *bp,
497 glEnable(GL_POLYGON_OFFSET_FILL);
498 glPolygonOffset(-1.0f, -2.0f);
501 glBegin(GL_TRIANGLES);
503 for (mi = 0; mi < base->mc; mi++)
505 struct b_mtrl *mp = base->mv + mi;
507 if ((mp->fl & fl) && (mp->fl & M_DECAL) == decal)
509 for (li = 0; li < bp->lc; li++)
510 sol_shad_lump(base, base->lv + bp->l0 + li, mi);
511 for (gi = 0; gi < bp->gc; gi++)
512 sol_shad_geom(base, base->gv + base->iv[bp->g0 + gi], mi);
519 glDisable(GL_POLYGON_OFFSET_FILL);
522 static void sol_shad_list(const struct s_vary *vary,
523 const struct v_ball *up,
524 const struct v_body *bp, GLuint list)
526 float p[3], e[4], u[3], a;
529 float X[] = { 1.0f, 0.0f, 0.0f, 0.0f };
530 float Y[] = { 0.0f, 1.0f, 0.0f, 0.0f };
531 float Z[] = { 0.0f, 0.0f, 1.0f, 0.0f };
533 sol_body_p(p, vary, bp->pi, bp->t);
534 sol_body_e(e, vary, bp, 0);
538 Y[3] = 0.5f - v_dot(Y, d);
542 q_as_axisangle(e, u, &a);
560 glTexGenfv(GL_S, GL_OBJECT_PLANE, X);
561 glTexGenfv(GL_T, GL_OBJECT_PLANE, Z);
563 /* Translate the shadow on a moving body. */
565 glMatrixMode(GL_TEXTURE);
567 float k = 0.25f / up->r;
570 glTranslatef(0.5f - k * d[0],
571 0.5f - k * d[2], 0.0f);
572 glScalef(k, k, 0.0f);
574 glMatrixMode(GL_MODELVIEW);
576 /* Set up shadow clipping. */
578 if (glActiveTextureARB_)
580 glActiveTextureARB_(GL_TEXTURE1_ARB);
581 glTexGenfv(GL_S, GL_OBJECT_PLANE, Y);
582 glActiveTextureARB_(GL_TEXTURE0_ARB);
589 glTranslatef(p[0], p[1], p[2]);
590 glRotatef(a, u[0], u[1], u[2]);
596 /* Pop the shadow translation. */
598 glMatrixMode(GL_TEXTURE);
602 glMatrixMode(GL_MODELVIEW);
605 void sol_shad(const struct s_draw *draw, int ui)
609 /* Render all shadowed geometry. */
611 glDepthMask(GL_FALSE);
613 for (bi = 0; bi < draw->bc; bi++)
615 sol_shad_list(draw->vary,
617 draw->vary->bv + bi, draw->bv[bi].sl);
619 glDepthMask(GL_TRUE);
622 /*---------------------------------------------------------------------------*/
624 static void sol_load_objects(struct s_draw *draw, int s)
628 /* Here we sort geometry into display lists by material type. */
630 for (i = 0; i < draw->bc; i++)
632 struct d_body *bp = draw->bv + i;
634 int on = sol_enum_body(draw->base, bp->base, M_OPAQUE);
635 int tn = sol_enum_body(draw->base, bp->base, M_TRANSPARENT);
636 int rn = sol_enum_body(draw->base, bp->base, M_REFLECTIVE);
637 int dn = sol_enum_body(draw->base, bp->base, M_DECAL);
638 int sn = sol_enum_body(draw->base, bp->base, M_SHADOWED);
640 /* Draw all opaque geometry, decals last. */
644 bp->ol = glGenLists(1);
646 glNewList(bp->ol, GL_COMPILE);
648 const struct d_mtrl *mp = &default_draw_mtrl;
650 mp = sol_draw_body(draw, bp->base, mp, M_OPAQUE, 0);
651 mp = sol_draw_body(draw, bp->base, mp, M_OPAQUE, M_DECAL);
652 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
658 /* Draw all translucent geometry, decals first. */
662 bp->tl = glGenLists(1);
664 glNewList(bp->tl, GL_COMPILE);
666 const struct d_mtrl *mp = &default_draw_mtrl;
668 mp = sol_draw_body(draw, bp->base, mp, M_TRANSPARENT, M_DECAL);
669 mp = sol_draw_body(draw, bp->base, mp, M_TRANSPARENT, 0);
670 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
676 /* Draw all reflective geometry. */
680 bp->rl = glGenLists(1);
682 glNewList(bp->rl, GL_COMPILE);
684 const struct d_mtrl *mp = &default_draw_mtrl;
686 mp = sol_draw_body(draw, bp->base, mp, M_REFLECTIVE, 0);
687 mp = sol_draw_mtrl(draw, &default_draw_mtrl, mp);
691 draw->reflective = 1;
695 /* Draw all shadowed geometry. */
697 if (s && (on || rn || sn))
699 bp->sl = glGenLists(1);
701 glNewList(bp->sl, GL_COMPILE);
703 if (on) sol_shad_body(draw->base, bp->base, M_OPAQUE, 0);
704 if (rn) sol_shad_body(draw->base, bp->base, M_REFLECTIVE, 0);
705 if (dn) sol_shad_body(draw->base, bp->base, M_OPAQUE, M_DECAL);
708 /* Transparent shadowed geometry hack. */
711 sol_shad_body(draw->base, bp->base, M_SHADOWED, M_DECAL);
713 sol_shad_body(draw->base, bp->base, M_SHADOWED, 0);
722 static GLuint sol_find_texture(const char *name)
729 /* Prefer a lossless copy of the texture over a lossy compression. */
731 strncpy(png, name, PATHMAX); strcat(png, ".png");
732 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
734 /* Check for a PNG. */
736 if ((o = make_image_from_file(png)))
739 /* Check for a JPG. */
741 if ((o = make_image_from_file(jpg)))
747 static void sol_load_textures(struct s_draw *draw)
751 /* Load the image referenced by each material. */
753 for (i = 0; i < draw->mc; i++)
755 struct d_mtrl *mp = draw->mv + i;
757 if ((mp->o = sol_find_texture(_(mp->base->f))))
759 /* Set the texture to clamp or repeat based on material type. */
761 if (mp->base->fl & M_CLAMPED)
763 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
764 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
768 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
769 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
775 /*---------------------------------------------------------------------------*/
777 int sol_load_draw(struct s_draw *draw, const struct s_vary *vary, int s)
781 memset(draw, 0, sizeof (*draw));
784 draw->base = draw->vary->base;
788 draw->mv = calloc(draw->base->mc, sizeof (*draw->mv));
789 draw->mc = draw->base->mc;
791 for (i = 0; i < draw->base->mc; i++)
793 struct d_mtrl *mp = draw->mv + i;
794 struct b_mtrl *mq = draw->base->mv + i;
802 draw->bv = calloc(draw->base->bc, sizeof (*draw->bv));
803 draw->bc = draw->base->bc;
805 for (i = 0; i < draw->base->bc; i++)
807 struct d_body *bp = draw->bv + i;
808 struct b_body *bq = draw->base->bv + i;
814 sol_load_textures(draw);
815 sol_load_objects (draw, s);
820 void sol_free_draw(struct s_draw *draw)
824 for (i = 0; i < draw->mc; i++)
826 if (glIsTexture(draw->mv[i].o))
827 glDeleteTextures(1, &draw->mv[i].o);
830 for (i = 0; i < draw->bc; i++)
832 if (glIsList(draw->bv[i].ol))
833 glDeleteLists(draw->bv[i].ol, 1);
834 if (glIsList(draw->bv[i].tl))
835 glDeleteLists(draw->bv[i].tl, 1);
836 if (glIsList(draw->bv[i].rl))
837 glDeleteLists(draw->bv[i].rl, 1);
838 if (glIsList(draw->bv[i].sl))
839 glDeleteLists(draw->bv[i].sl, 1);
845 memset(draw, 0, sizeof (*draw));
848 /*---------------------------------------------------------------------------*/
850 int sol_load_full(struct s_full *full, const char *filename, int s)
852 if (sol_load_base(&full->base, filename))
854 sol_load_vary(&full->vary, &full->base);
855 sol_load_draw(&full->draw, &full->vary, s);
863 void sol_free_full(struct s_full *full)
865 sol_free_draw(&full->draw);
866 sol_free_vary(&full->vary);
867 sol_free_base(&full->base);
870 /*---------------------------------------------------------------------------*/