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>
27 #include "base_image.h"
28 #include "base_config.h"
31 #include "solid_draw.h"
32 #include "solid_all.h"
34 /*---------------------------------------------------------------------------*/
36 static void sol_transform(const struct s_vary *vary,
37 const struct v_body *bp)
45 /* Compute the body transform. */
47 sol_body_p(p, vary, bp->pi, bp->t);
48 sol_body_e(e, vary, bp, 0);
50 q_as_axisangle(e, v, &a);
52 glTranslatef(p[0], p[1], p[2]);
53 glRotatef(V_DEG(a), v[0], v[1], v[2]);
55 /* Compute the shadow texture transform */
57 v_sub(d, vary->uv->p, p);
59 glActiveTexture_(GL_TEXTURE1);
60 glMatrixMode(GL_TEXTURE);
62 float k = 0.25f / vary->uv->r;
65 glTranslatef(0.5f - k * d[0],
66 0.5f - k * d[2], 0.0f);
69 glMatrixMode(GL_MODELVIEW);
70 glActiveTexture_(GL_TEXTURE0);
73 /*---------------------------------------------------------------------------*/
75 void sol_back(const struct s_draw *draw, float n, float f, float t)
79 /*---------------------------------------------------------------------------*/
81 void sol_bill(const struct s_draw *draw, const float *M, float t)
85 /*---------------------------------------------------------------------------*/
87 void sol_shad(const struct s_draw *draw, int ui)
91 /*---------------------------------------------------------------------------*/
93 #define tobyte(f) ((GLubyte) (f * 255.0f))
95 #define color_cmp(a, b) (tobyte((a)[0]) == tobyte((b)[0]) && \
96 tobyte((a)[1]) == tobyte((b)[1]) && \
97 tobyte((a)[2]) == tobyte((b)[2]) && \
98 tobyte((a)[3]) == tobyte((b)[3]))
100 static struct b_mtrl default_base_mtrl =
102 { 0.8f, 0.8f, 0.8f, 1.0f },
103 { 0.2f, 0.2f, 0.2f, 1.0f },
104 { 0.0f, 0.0f, 0.0f, 1.0f },
105 { 0.0f, 0.0f, 0.0f, 1.0f },
106 { 0.0f }, 0.0f, M_OPAQUE, ""
109 static struct d_mtrl default_draw_mtrl =
111 &default_base_mtrl, 0
114 static const struct d_mtrl *sol_apply_mtrl(const struct d_mtrl *mp_draw,
115 const struct d_mtrl *mq_draw)
117 const struct b_mtrl *mp_base = mp_draw->base;
118 const struct b_mtrl *mq_base = mq_draw->base;
120 /* Bind the texture. */
122 if (mp_draw->o != mq_draw->o)
123 glBindTexture(GL_TEXTURE_2D, mp_draw->o);
125 /* Set material properties. */
127 if (!color_cmp(mp_base->a, mq_base->a))
128 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp_base->a);
129 if (!color_cmp(mp_base->d, mq_base->d))
130 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp_base->d);
131 if (!color_cmp(mp_base->s, mq_base->s))
132 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp_base->s);
133 if (!color_cmp(mp_base->e, mq_base->e))
134 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp_base->e);
135 if (tobyte(mp_base->h[0]) != tobyte(mq_base->h[0]))
136 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp_base->h);
138 /* Enable ball shadow. */
140 if ((mp_base->fl & M_SHADOWED) && !(mq_base->fl & M_SHADOWED))
145 /* Disable ball shadow. */
147 if (!(mp_base->fl & M_SHADOWED) && (mq_base->fl & M_SHADOWED))
152 /* Enable environment mapping. */
154 if ((mp_base->fl & M_ENVIRONMENT) && !(mq_base->fl & M_ENVIRONMENT))
156 glEnable(GL_TEXTURE_GEN_S);
157 glEnable(GL_TEXTURE_GEN_T);
159 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
160 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
163 /* Disable environment mapping. */
165 if ((mq_base->fl & M_ENVIRONMENT) && !(mp_base->fl & M_ENVIRONMENT))
167 glDisable(GL_TEXTURE_GEN_S);
168 glDisable(GL_TEXTURE_GEN_T);
171 /* Enable additive blending. */
173 if ((mp_base->fl & M_ADDITIVE) && !(mq_base->fl & M_ADDITIVE))
174 glBlendFunc(GL_ONE, GL_ONE);
176 /* Enable standard blending. */
178 if ((mq_base->fl & M_ADDITIVE) && !(mp_base->fl & M_ADDITIVE))
179 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
181 /* Enable visibility-from-behind. */
183 if ((mp_base->fl & M_TWO_SIDED) && !(mq_base->fl & M_TWO_SIDED))
185 glDisable(GL_CULL_FACE);
186 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
189 /* Disable visibility-from-behind. */
191 if ((mq_base->fl & M_TWO_SIDED) && !(mp_base->fl & M_TWO_SIDED))
193 glEnable(GL_CULL_FACE);
194 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
197 /* Enable decal offset. */
199 if ((mp_base->fl & M_DECAL) && !(mq_base->fl & M_DECAL))
201 glEnable(GL_POLYGON_OFFSET_FILL);
202 glPolygonOffset(-1.0f, -2.0f);
205 /* Disable decal offset. */
207 if ((mq_base->fl & M_DECAL) && !(mp_base->fl & M_DECAL))
208 glDisable(GL_POLYGON_OFFSET_FILL);
213 static GLuint sol_find_texture(const char *name)
220 /* Prefer a lossless copy of the texture over a lossy compression. */
222 strncpy(png, name, PATHMAX); strcat(png, ".png");
223 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
225 /* Check for a PNG. */
227 if ((o = make_image_from_file(png)))
230 /* Check for a JPG. */
232 if ((o = make_image_from_file(jpg)))
238 static void sol_load_mtrl(struct d_mtrl *mp,
239 const struct b_mtrl *mq,
244 if ((mp->o = sol_find_texture(_(mq->f))))
246 /* Set the texture to clamp or repeat based on material type. */
248 if (mq->fl & M_CLAMPED)
250 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
251 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
259 /* If at least one material is reflective, mark it in the SOL. */
261 if (mq->fl & M_REFLECTIVE)
262 draw->reflective = 1;
266 static void sol_free_mtrl(struct d_mtrl *mp)
268 if (glIsTexture(mp->o))
269 glDeleteTextures(1, &mp->o);
272 static int sol_test_mtrl(const struct d_mtrl *mp, int f0, int f1)
274 /* Test whether the material flags exclude f0 and include f1. */
276 return ((mp->base->fl & f1) == f1 &&
277 (mp->base->fl & f0) == 0);
280 /*---------------------------------------------------------------------------*/
282 static int sol_count_geom(const struct s_base *base, int g0, int gc, int mi)
286 /* The arguments g0 and gc specify a range of the index array. These */
287 /* indices refer to geoms. Determine how many of these geoms use the */
290 for (gi = 0; gi < gc; gi++)
291 if (base->gv[base->iv[g0 + gi]].mi == mi)
297 static int sol_count_body(const struct b_body *bp,
298 const struct s_base *base, int mi)
302 /* Count all lump geoms with the given material. */
304 for (li = 0; li < bp->lc; li++)
305 c += sol_count_geom(base, base->lv[bp->l0 + li].g0,
306 base->lv[bp->l0 + li].gc, mi);
308 /* Count all body geoms with the given material. */
310 c += sol_count_geom(base, bp->g0, bp->gc, mi);
315 /*---------------------------------------------------------------------------*/
317 static void sol_mesh_vert(struct d_vert *vp,
318 const struct s_base *base, int oi)
320 /* Gather all vertex attributes for the given offs. */
322 const struct b_texc *tq = base->tv + base->ov[oi].ti;
323 const struct b_side *sq = base->sv + base->ov[oi].si;
324 const struct b_vert *vq = base->vv + base->ov[oi].vi;
341 static void sol_mesh_geom(struct d_vert *vv, int *vn,
342 struct d_geom *gv, int *gn,
343 const struct s_base *base, int *iv, int g0, int gc, int mi)
347 /* Insert all geoms with material mi into the vertex and element data. */
349 for (gi = 0; gi < gc; gi++)
351 const struct b_geom *gq = base->gv + base->iv[g0 + gi];
355 /* Insert a d_vert into the VBO data for each referenced b_off. */
357 if (iv[gq->oi] == -1)
360 sol_mesh_vert(vv + (*vn)++, base, gq->oi);
362 if (iv[gq->oj] == -1)
365 sol_mesh_vert(vv + (*vn)++, base, gq->oj);
367 if (iv[gq->ok] == -1)
370 sol_mesh_vert(vv + (*vn)++, base, gq->ok);
373 /* Populate the EBO data using remapped b_off indices. */
375 gv[*gn].i = iv[gq->oi];
376 gv[*gn].j = iv[gq->oj];
377 gv[*gn].k = iv[gq->ok];
384 static void sol_load_mesh(struct d_mesh *mp,
385 const struct b_body *bp,
386 const struct s_draw *draw, int mi)
388 const size_t vs = sizeof (struct d_vert);
389 const size_t gs = sizeof (struct d_geom);
391 struct d_vert *vv = 0;
392 struct d_geom *gv = 0;
395 int oc = draw->base->oc;
399 const int gc = sol_count_body(bp, draw->base, mi);
401 /* Get temporary storage for vertex and element array creation. */
403 if ((vv = (struct d_vert *) calloc(oc, vs)) &&
404 (gv = (struct d_geom *) calloc(gc, gs)) &&
405 (iv = (int *) calloc(oc, sizeof (int))))
409 /* Initialize the index remapping. */
411 for (i = 0; i < oc; ++i) iv[i] = -1;
413 /* Include all matching lump geoms in the arrays. */
415 for (li = 0; li < bp->lc; li++)
416 sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv,
417 draw->base->lv[bp->l0 + li].g0,
418 draw->base->lv[bp->l0 + li].gc, mi);
420 /* Include all matching body geoms in the arrays. */
422 sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv, bp->g0, bp->gc, mi);
424 /* Initialize buffer objects for all data. */
426 glGenBuffers(1, &mp->vbo);
427 glBindBuffer(GL_ARRAY_BUFFER, mp->vbo);
428 glBufferData(GL_ARRAY_BUFFER, vn * vs, vv, GL_STATIC_DRAW);
429 glBindBuffer(GL_ARRAY_BUFFER, 0);
431 glGenBuffers(1, &mp->ebo);
432 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
433 glBufferData(GL_ELEMENT_ARRAY_BUFFER, gn * gs, gv, GL_STATIC_DRAW);
434 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
436 mp->mp = draw->mv + mi;
445 static void sol_free_mesh(struct d_mesh *mp)
447 if (glIsBuffer(mp->ebo))
448 glDeleteBuffers(1, &mp->ebo);
449 if (glIsBuffer(mp->vbo))
450 glDeleteBuffers(1, &mp->vbo);
453 static const struct d_mtrl *sol_draw_mesh(const struct d_mesh *mp,
454 const struct d_mtrl *mq,
457 /* If this mesh has material matching the given flags... */
459 if (sol_test_mtrl(mp->mp, f0, f1))
461 const size_t s = sizeof (struct d_vert);
462 const GLenum T = GL_FLOAT;
464 /* Apply the material state. */
466 mq = sol_apply_mtrl(mp->mp, mq);
468 /* Bind the mesh data. */
470 glBindBuffer(GL_ARRAY_BUFFER, mp->vbo);
471 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
473 glVertexPointer (3, T, s, (GLvoid *) offsetof (struct d_vert, p));
474 glNormalPointer ( T, s, (GLvoid *) offsetof (struct d_vert, n));
476 glClientActiveTexture(GL_TEXTURE1);
477 glTexCoordPointer(2, T, s, (GLvoid *) offsetof (struct d_vert, u));
478 glClientActiveTexture(GL_TEXTURE0);
479 glTexCoordPointer(2, T, s, (GLvoid *) offsetof (struct d_vert, t));
483 glDrawElements(GL_TRIANGLES, mp->ebc, GL_UNSIGNED_SHORT, 0);
489 /*---------------------------------------------------------------------------*/
491 static void sol_load_body(struct d_body *bp,
492 const struct b_body *bq,
493 const struct s_draw *draw)
500 /* Determine how many materials this body uses. */
502 for (mi = 0; mi < draw->mc; ++mi)
503 if (sol_count_body(bq, draw->base, mi))
506 /* Allocate and initialize a mesh for each material. */
508 if ((bp->mv = (struct d_mesh *) calloc(bp->mc, sizeof (struct d_mesh))))
512 for (mi = 0; mi < draw->mc; ++mi)
513 if (sol_count_body(bq, draw->base, mi))
514 sol_load_mesh(bp->mv + mj++, bq, draw, mi);
518 static void sol_free_body(struct d_body *bp)
522 for (mi = 0; mi < bp->mc; ++mi)
523 sol_free_mesh(bp->mv + mi);
528 static const struct d_mtrl *sol_draw_body(const struct d_body *bp,
529 const struct d_mtrl *mq,
534 for (i = 0; i < bp->mc; ++i)
535 mq = sol_draw_mesh(bp->mv + i, mq, f0, f1);
540 /*---------------------------------------------------------------------------*/
542 int sol_load_draw(struct s_draw *draw, const struct s_vary *vary, int s)
546 memset(draw, 0, sizeof (struct s_draw));
549 draw->base = vary->base;
551 /* Initialize all materials for this file. */
555 if ((draw->mv = calloc(draw->base->mc, sizeof (*draw->mv))))
557 draw->mc = draw->base->mc;
559 for (i = 0; i < draw->mc; i++)
560 sol_load_mtrl(draw->mv + i, draw->base->mv + i, draw);
564 /* Initialize all bodies for this file. */
568 if ((draw->bv = calloc(draw->base->bc, sizeof (*draw->bv))))
570 draw->bc = draw->base->bc;
572 for (i = 0; i < draw->bc; i++)
573 sol_load_body(draw->bv + i, draw->base->bv + i, draw);
580 void sol_free_draw(struct s_draw *draw)
584 for (i = 0; i < draw->bc; i++)
585 sol_free_body(draw->bv + i);
586 for (i = 0; i < draw->mc; i++)
587 sol_free_mtrl(draw->mv + i);
590 /*---------------------------------------------------------------------------*/
592 static const struct d_mtrl *sol_draw_all(const struct s_draw *draw,
593 const struct d_mtrl *mq,
598 /* Draw all meshes of all bodies matching the given material flags. */
600 for (bi = 0; bi < draw->bc; ++bi)
604 sol_transform(draw->vary, draw->vary->bv + bi);
605 mq = sol_draw_body(draw->bv + bi, mq, f0, f1);
612 void sol_draw_enable(void)
614 glEnableClientState(GL_VERTEX_ARRAY);
615 glEnableClientState(GL_NORMAL_ARRAY);
617 glClientActiveTexture(GL_TEXTURE1);
618 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
619 glClientActiveTexture(GL_TEXTURE0);
620 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
623 void sol_draw_disable(void)
625 glClientActiveTexture(GL_TEXTURE1);
626 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
627 glClientActiveTexture(GL_TEXTURE0);
628 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
630 glDisableClientState(GL_NORMAL_ARRAY);
631 glDisableClientState(GL_VERTEX_ARRAY);
634 void sol_draw(const struct s_draw *draw, int mask, int test)
638 const struct d_mtrl *mq = &default_draw_mtrl;
640 /* Render all opaque geometry. */
642 mq = sol_draw_all(draw, mq, M_TRANSPARENT | M_REFLECTIVE, 0);
644 /* Render all transparent geometry. */
646 if (!test) glDisable(GL_DEPTH_TEST);
647 if (!mask) glDepthMask(GL_FALSE);
649 sol_draw_all(draw, mq, M_REFLECTIVE, M_TRANSPARENT);
651 if (!mask) glDepthMask(GL_TRUE);
652 if (!test) glEnable(GL_DEPTH_TEST);
654 mq = sol_apply_mtrl(&default_draw_mtrl, mq);
656 /* Revert the buffer object state. */
658 glBindBuffer(GL_ARRAY_BUFFER, 0);
659 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
664 void sol_refl(const struct s_draw *draw)
668 const struct d_mtrl *mq = &default_draw_mtrl;
670 /* Render all reflective geometry. */
672 mq = sol_draw_all(draw, mq, 0, M_REFLECTIVE);
673 mq = sol_apply_mtrl(&default_draw_mtrl, mq);
675 /* Revert the buffer object state. */
677 glBindBuffer(GL_ARRAY_BUFFER, 0);
678 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
683 /*---------------------------------------------------------------------------*/
685 int sol_load_full(struct s_full *full, const char *filename, int s)
687 if (sol_load_base(&full->base, filename))
689 sol_load_vary(&full->vary, &full->base);
690 sol_load_draw(&full->draw, &full->vary, s);
698 void sol_free_full(struct s_full *full)
700 sol_free_draw(&full->draw);
701 sol_free_vary(&full->vary);
702 sol_free_base(&full->base);
705 /*---------------------------------------------------------------------------*/