Add missing credit for Esperanto translation
[neverball.git] / share / solid_draw.c
blob96d14bdb086249e795df729693d83f2cebd503dd
1 /*
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.
15 #include <SDL.h>
16 #include <SDL_rwops.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <math.h>
23 #include "glext.h"
24 #include "video.h"
25 #include "vec3.h"
26 #include "geom.h"
27 #include "image.h"
28 #include "base_image.h"
29 #include "config.h"
30 #include "base_config.h"
31 #include "lang.h"
33 #include "solid_draw.h"
34 #include "solid_all.h"
36 /*---------------------------------------------------------------------------*/
39 * Included and excluded material flags for each rendering pass.
42 static const struct
44 int in;
45 int ex;
46 } passes[PASS_MAX] = {
47 { 0, M_REFLECTIVE | M_TRANSPARENT | M_DECAL },
48 { M_DECAL, M_REFLECTIVE | M_TRANSPARENT },
49 { M_DECAL | M_TRANSPARENT, M_REFLECTIVE },
50 { M_TRANSPARENT, M_REFLECTIVE | M_DECAL },
51 { M_REFLECTIVE, 0 }
54 /*---------------------------------------------------------------------------*/
56 static void sol_transform(const struct s_vary *vary,
57 const struct v_body *bp, int ui)
59 float a;
60 float e[4];
61 float p[3];
62 float v[3];
64 /* Apply the body position and rotation to the model-view matrix. */
66 sol_body_p(p, vary, bp, 0.0f);
67 sol_body_e(e, vary, bp, 0.0f);
69 q_as_axisangle(e, v, &a);
71 if (!(p[0] == 0 && p[1] == 0 && p[2] == 0))
72 glTranslatef(p[0], p[1], p[2]);
74 if (!((v[0] == 0 && v[1] == 0 && v[2] == 0) || a == 0))
75 glRotatef(V_DEG(a), v[0], v[1], v[2]);
77 /* Apply the shadow transform to the texture matrix. */
79 if (ui >= 0 && ui < vary->uc && vary->uv[ui].r > 0.0f)
81 struct v_ball *up = vary->uv + ui;
83 if (tex_env_stage(TEX_STAGE_SHADOW))
85 glMatrixMode(GL_TEXTURE);
87 float k = 0.25f / up->r;
89 glLoadIdentity();
91 /* Center the shadow texture on the ball. */
93 glTranslatef(0.5f, 0.5f, 0.0f);
95 /* Transform ball XZ position to ST texture coordinate. */
97 glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
99 /* Scale the shadow texture to the radius of the ball. */
101 glScalef(k, k, k);
103 /* Move the shadow texture under the ball. */
105 glTranslatef(-up->p[0], -up->p[1], -up->p[2]);
107 /* Apply the body position and rotation. */
109 glTranslatef(p[0], p[1], p[2]);
110 glRotatef(V_DEG(a), v[0], v[1], v[2]);
112 /* Vertically center clipper texture on ball position. */
114 if (tex_env_stage(TEX_STAGE_CLIP))
116 glLoadIdentity();
117 glTranslatef(p[0] - up->p[0],
118 p[1] - up->p[1] + 0.5f,
119 p[2] - up->p[2]);
120 glRotatef(V_DEG(a), v[0], v[1], v[2]);
124 glMatrixMode(GL_MODELVIEW);
126 tex_env_stage(TEX_STAGE_TEXTURE);
131 /*---------------------------------------------------------------------------*/
133 static void sol_load_bill(struct s_draw *draw)
135 static const GLfloat data[] = {
136 0.0f, 0.0f, -0.5f, 0.0f,
137 1.0f, 0.0f, 0.5f, 0.0f,
138 0.0f, 1.0f, -0.5f, 1.0f,
139 1.0f, 1.0f, 0.5f, 1.0f,
141 0.0f, 0.0f, -0.5f, -0.5f,
142 1.0f, 0.0f, 0.5f, -0.5f,
143 0.0f, 1.0f, -0.5f, 0.5f,
144 1.0f, 1.0f, 0.5f, 0.5f,
147 /* Initialize a vertex buffer object for billboard drawing. */
149 glGenBuffers_(1, &draw->bill);
150 glBindBuffer_(GL_ARRAY_BUFFER, draw->bill);
151 glBufferData_(GL_ARRAY_BUFFER, sizeof (data), data, GL_STATIC_DRAW);
152 glBindBuffer_(GL_ARRAY_BUFFER, 0);
155 static void sol_free_bill(struct s_draw *draw)
157 glDeleteBuffers_(1, &draw->bill);
160 static void sol_draw_bill(GLboolean edge)
162 if (edge)
163 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
164 else
165 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
168 /*---------------------------------------------------------------------------*/
170 /* NOTE: The state management here presumes that billboard rendering is */
171 /* NESTED within a wider SOL rendering process. That is: r_draw_enable */
172 /* has been called and r_draw_disable will be called in the future. */
173 /* Thus the "default" VBO state retained by billboard rendering is the */
174 /* state appropriate for normal SOL rendering. */
176 static void sol_bill_enable(const struct s_draw *draw)
178 const size_t s = sizeof (GLfloat);
180 glBindBuffer_(GL_ARRAY_BUFFER, draw->bill);
182 glDisableClientState(GL_NORMAL_ARRAY);
184 glTexCoordPointer(2, GL_FLOAT, s * 4, (GLvoid *) ( 0));
185 glVertexPointer (2, GL_FLOAT, s * 4, (GLvoid *) (s * 2));
188 static void sol_bill_disable(void)
190 glEnableClientState(GL_NORMAL_ARRAY);
192 glBindBuffer_(GL_ARRAY_BUFFER, 0);
195 /*---------------------------------------------------------------------------*/
197 static int sol_test_mtrl(int mi, int p)
199 const struct mtrl *mp = mtrl_get(mi);
201 /* Test whether the material flags match inclusion rules. */
203 return ((mp->base.fl & passes[p].in) == passes[p].in &&
204 (mp->base.fl & passes[p].ex) == 0);
207 /*---------------------------------------------------------------------------*/
209 static int sol_count_geom(const struct s_base *base, int g0, int gc, int mi)
211 int gi, c = 0;
213 /* The arguments g0 and gc specify a range of the index array. These */
214 /* indices refer to geoms. Determine how many of these geoms use the */
215 /* given material */
217 for (gi = 0; gi < gc; gi++)
218 if (base->gv[base->iv[g0 + gi]].mi == mi)
219 c++;
221 return c;
224 static int sol_count_body(const struct b_body *bp,
225 const struct s_base *base, int mi)
227 int li, c = 0;
229 /* Count all lump geoms with the given material. */
231 for (li = 0; li < bp->lc; li++)
232 c += sol_count_geom(base, base->lv[bp->l0 + li].g0,
233 base->lv[bp->l0 + li].gc, mi);
235 /* Count all body geoms with the given material. */
237 c += sol_count_geom(base, bp->g0, bp->gc, mi);
239 return c;
242 static int sol_count_mesh(const struct d_body *bp, int p)
244 int mi, c = 0;
246 /* Count the body meshes matching the given material flags. */
248 for (mi = 0; mi < bp->mc; ++mi)
249 if (sol_test_mtrl(bp->mv[mi].mtrl, p))
250 c++;
252 return c;
255 /*---------------------------------------------------------------------------*/
257 static void sol_mesh_vert(struct d_vert *vp,
258 const struct s_base *base, int oi)
260 /* Gather all vertex attributes for the given offs. */
262 const struct b_texc *tq = base->tv + base->ov[oi].ti;
263 const struct b_side *sq = base->sv + base->ov[oi].si;
264 const struct b_vert *vq = base->vv + base->ov[oi].vi;
266 vp->p[0] = vq->p[0];
267 vp->p[1] = vq->p[1];
268 vp->p[2] = vq->p[2];
270 vp->n[0] = sq->n[0];
271 vp->n[1] = sq->n[1];
272 vp->n[2] = sq->n[2];
274 vp->t[0] = tq->u[0];
275 vp->t[1] = tq->u[1];
278 static void sol_mesh_geom(struct d_vert *vv, int *vn,
279 struct d_geom *gv, int *gn,
280 const struct s_base *base, int *iv, int g0, int gc, int mi)
282 int gi;
284 /* Insert all geoms with material mi into the vertex and element data. */
286 for (gi = 0; gi < gc; gi++)
288 const struct b_geom *gq = base->gv + base->iv[g0 + gi];
290 if (gq->mi == mi)
292 /* Insert a d_vert into the VBO data for each referenced b_off. */
294 if (iv[gq->oi] == -1)
296 iv[gq->oi] = *vn;
297 sol_mesh_vert(vv + (*vn)++, base, gq->oi);
299 if (iv[gq->oj] == -1)
301 iv[gq->oj] = *vn;
302 sol_mesh_vert(vv + (*vn)++, base, gq->oj);
304 if (iv[gq->ok] == -1)
306 iv[gq->ok] = *vn;
307 sol_mesh_vert(vv + (*vn)++, base, gq->ok);
310 /* Populate the EBO data using remapped b_off indices. */
312 gv[*gn].i = iv[gq->oi];
313 gv[*gn].j = iv[gq->oj];
314 gv[*gn].k = iv[gq->ok];
316 (*gn)++;
321 static void sol_load_mesh(struct d_mesh *mp,
322 const struct b_body *bp,
323 const struct s_draw *draw, int mi)
325 const size_t vs = sizeof (struct d_vert);
326 const size_t gs = sizeof (struct d_geom);
328 struct d_vert *vv = 0;
329 struct d_geom *gv = 0;
330 int *iv = 0;
332 int oc = draw->base->oc;
333 int vn = 0;
334 int gn = 0;
336 const int gc = sol_count_body(bp, draw->base, mi);
338 /* Get temporary storage for vertex and element array creation. */
340 if ((vv = (struct d_vert *) calloc(oc, vs)) &&
341 (gv = (struct d_geom *) calloc(gc, gs)) &&
342 (iv = (int *) calloc(oc, sizeof (int))))
344 int li, i;
346 /* Initialize the index remapping. */
348 for (i = 0; i < oc; ++i) iv[i] = -1;
350 /* Include all matching lump geoms in the arrays. */
352 for (li = 0; li < bp->lc; li++)
353 sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv,
354 draw->base->lv[bp->l0 + li].g0,
355 draw->base->lv[bp->l0 + li].gc, mi);
357 /* Include all matching body geoms in the arrays. */
359 sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv, bp->g0, bp->gc, mi);
361 /* Initialize buffer objects for all data. */
363 glGenBuffers_(1, &mp->vbo);
364 glBindBuffer_(GL_ARRAY_BUFFER, mp->vbo);
365 glBufferData_(GL_ARRAY_BUFFER, vn * vs, vv, GL_STATIC_DRAW);
366 glBindBuffer_(GL_ARRAY_BUFFER, 0);
368 glGenBuffers_(1, &mp->ebo);
369 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
370 glBufferData_(GL_ELEMENT_ARRAY_BUFFER, gn * gs, gv, GL_STATIC_DRAW);
371 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
373 /* Note cached material index. */
375 mp->mtrl = draw->base->mtrls[mi];
377 mp->ebc = gn * 3;
378 mp->vbc = vn;
381 free(iv);
382 free(gv);
383 free(vv);
386 static void sol_free_mesh(struct d_mesh *mp)
388 glDeleteBuffers_(1, &mp->ebo);
389 glDeleteBuffers_(1, &mp->vbo);
392 void sol_draw_mesh(const struct d_mesh *mp, struct s_rend *rend, int p)
394 /* If this mesh has material matching the given flags... */
396 if (sol_test_mtrl(mp->mtrl, p))
398 const size_t s = sizeof (struct d_vert);
399 const GLenum T = GL_FLOAT;
401 /* Apply the material state. */
403 r_apply_mtrl(rend, mp->mtrl);
405 /* Bind the mesh data. */
407 glBindBuffer_(GL_ARRAY_BUFFER, mp->vbo);
408 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
410 glVertexPointer (3, T, s, (GLvoid *) offsetof (struct d_vert, p));
411 glNormalPointer ( T, s, (GLvoid *) offsetof (struct d_vert, n));
413 if (tex_env_stage(TEX_STAGE_SHADOW))
415 glTexCoordPointer(3, T, s, (GLvoid *) offsetof (struct d_vert, p));
417 if (tex_env_stage(TEX_STAGE_CLIP))
418 glTexCoordPointer(3, T, s, (GLvoid *) offsetof (struct d_vert, p));
420 tex_env_stage(TEX_STAGE_TEXTURE);
422 glTexCoordPointer(2, T, s, (GLvoid *) offsetof (struct d_vert, t));
424 /* Draw the mesh. */
426 if (rend->curr_mtrl.base.fl & M_PARTICLE)
427 glDrawArrays(GL_POINTS, 0, mp->vbc);
428 else
429 glDrawElements(GL_TRIANGLES, mp->ebc, GL_UNSIGNED_SHORT, 0);
433 /*---------------------------------------------------------------------------*/
435 static void sol_load_body(struct d_body *bp,
436 const struct b_body *bq,
437 const struct s_draw *draw)
439 int mi;
441 bp->base = bq;
442 bp->mc = 0;
444 /* Determine how many materials this body uses. */
446 for (mi = 0; mi < draw->base->mc; ++mi)
447 if (sol_count_body(bq, draw->base, mi))
448 bp->mc++;
450 /* Allocate and initialize a mesh for each material. */
452 if ((bp->mv = (struct d_mesh *) calloc(bp->mc, sizeof (struct d_mesh))))
454 int mj = 0;
456 for (mi = 0; mi < draw->base->mc; ++mi)
457 if (sol_count_body(bq, draw->base, mi))
458 sol_load_mesh(bp->mv + mj++, bq, draw, mi);
461 /* Cache a mesh count for each pass. */
463 bp->pass[0] = sol_count_mesh(bp, 0);
464 bp->pass[1] = sol_count_mesh(bp, 1);
465 bp->pass[2] = sol_count_mesh(bp, 2);
466 bp->pass[3] = sol_count_mesh(bp, 3);
467 bp->pass[4] = sol_count_mesh(bp, 4);
470 static void sol_free_body(struct d_body *bp)
472 int mi;
474 for (mi = 0; mi < bp->mc; ++mi)
475 sol_free_mesh(bp->mv + mi);
477 free(bp->mv);
480 static void sol_draw_body(const struct d_body *bp, struct s_rend *rend, int p)
482 int i;
484 for (i = 0; i < bp->mc; ++i)
485 sol_draw_mesh(bp->mv + i, rend, p);
488 /*---------------------------------------------------------------------------*/
490 int sol_load_draw(struct s_draw *draw, struct s_vary *vary, int s)
492 int i;
494 memset(draw, 0, sizeof (struct s_draw));
496 draw->vary = vary;
497 draw->base = vary->base;
499 /* Determine whether this file has reflective materials. */
501 for (i = 0; i < draw->base->mc; i++)
502 if (draw->base->mv[i].fl & M_REFLECTIVE)
504 draw->reflective = 1;
505 break;
508 /* Cache all materials for this file. */
510 mtrl_cache_sol(draw->base);
512 /* Initialize shadow state. */
514 draw->shadow_ui = -1;
515 draw->shadowed = s;
517 /* Initialize all bodies for this file. */
519 if (draw->base->bc)
521 if ((draw->bv = calloc(draw->base->bc, sizeof (*draw->bv))))
523 draw->bc = draw->base->bc;
525 for (i = 0; i < draw->bc; i++)
526 sol_load_body(draw->bv + i, draw->base->bv + i, draw);
530 sol_load_bill(draw);
532 return 1;
535 void sol_free_draw(struct s_draw *draw)
537 int i;
539 mtrl_free_sol(draw->base);
541 sol_free_bill(draw);
543 for (i = 0; i < draw->bc; i++)
544 sol_free_body(draw->bv + i);
546 free(draw->bv);
549 /*---------------------------------------------------------------------------*/
551 static void sol_draw_all(const struct s_draw *draw, struct s_rend *rend, int p)
553 int bi;
555 /* Draw all meshes of all bodies matching the given material flags. */
557 for (bi = 0; bi < draw->bc; ++bi)
558 if (draw->bv[bi].pass[p])
560 glPushMatrix();
562 sol_transform(draw->vary, draw->vary->bv + bi, draw->shadow_ui);
563 sol_draw_body(draw->bv + bi, rend, p);
565 glPopMatrix();
569 /*---------------------------------------------------------------------------*/
571 void sol_draw(const struct s_draw *draw, struct s_rend *rend, int mask, int test)
573 /* Disable shadowed material setup if not requested. */
575 rend->skip_flags |= (draw->shadowed ? 0 : M_SHADOWED);
577 /* Render all opaque geometry, decals last. */
579 sol_draw_all(draw, rend, PASS_OPAQUE);
580 sol_draw_all(draw, rend, PASS_OPAQUE_DECAL);
582 /* Render all transparent geometry, decals first. */
584 if (!test) glDisable(GL_DEPTH_TEST);
585 if (!mask) glDepthMask(GL_FALSE);
587 sol_draw_all(draw, rend, PASS_TRANSPARENT_DECAL);
588 sol_draw_all(draw, rend, PASS_TRANSPARENT);
590 if (!mask) glDepthMask(GL_TRUE);
591 if (!test) glEnable(GL_DEPTH_TEST);
593 /* Revert the buffer object state. */
595 glBindBuffer_(GL_ARRAY_BUFFER, 0);
596 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
598 rend->skip_flags = 0;
601 void sol_refl(const struct s_draw *draw, struct s_rend *rend)
603 /* Disable shadowed material setup if not requested. */
605 rend->skip_flags |= (draw->shadowed ? 0 : M_SHADOWED);
607 /* Render all reflective geometry. */
609 sol_draw_all(draw, rend, PASS_REFLECTIVE);
611 /* Revert the buffer object state. */
613 glBindBuffer_(GL_ARRAY_BUFFER, 0);
614 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
616 rend->skip_flags = 0;
619 void sol_back(const struct s_draw *draw,
620 struct s_rend *rend,
621 float n, float f, float t)
623 if (!(draw && draw->base && draw->base->rc))
624 return;
626 glDisable(GL_LIGHTING);
627 glDepthMask(GL_FALSE);
629 sol_bill_enable(draw);
631 int ri;
633 /* Consider each billboard. */
635 for (ri = 0; ri < draw->base->rc; ri++)
637 const struct b_bill *rp = draw->base->rv + ri;
639 /* Render only billboards at distances between n and f. */
641 if (n <= rp->d && rp->d < f)
643 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0;
645 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
646 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
648 /* Render only billboards facing the viewer. */
650 if (w > 0 && h > 0)
652 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
653 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
654 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
656 r_apply_mtrl(rend, draw->base->mtrls[rp->mi]);
658 glPushMatrix();
660 if (ry) glRotatef(ry, 0.0f, 1.0f, 0.0f);
661 if (rx) glRotatef(rx, 1.0f, 0.0f, 0.0f);
663 glTranslatef(0.0f, 0.0f, -rp->d);
665 if (rp->fl & B_FLAT)
667 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
668 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
670 if (rp->fl & B_EDGE)
671 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
673 if (rz) glRotatef(rz, 0.0f, 0.0f, 1.0f);
675 glScalef(w, h, 1.0f);
677 sol_draw_bill(rp->fl & B_EDGE);
679 glPopMatrix();
684 sol_bill_disable();
686 glDepthMask(GL_TRUE);
687 glEnable(GL_LIGHTING);
690 void sol_bill(const struct s_draw *draw,
691 struct s_rend *rend, const float *M, float t)
693 if (!(draw && draw->base && draw->base->rc))
694 return;
696 sol_bill_enable(draw);
698 int ri;
700 for (ri = 0; ri < draw->base->rc; ++ri)
702 const struct b_bill *rp = draw->base->rv + ri;
704 float T = rp->t * t;
705 float S = fsinf(T);
707 float w = rp->w [0] + rp->w [1] * T + rp->w [2] * S;
708 float h = rp->h [0] + rp->h [1] * T + rp->h [2] * S;
709 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * S;
710 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * S;
711 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * S;
713 r_apply_mtrl(rend, draw->base->mtrls[rp->mi]);
715 glPushMatrix();
717 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
719 if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
721 if (fabsf(rx) > 0.0f) glRotatef(rx, 1.0f, 0.0f, 0.0f);
722 if (fabsf(ry) > 0.0f) glRotatef(ry, 0.0f, 1.0f, 0.0f);
723 if (fabsf(rz) > 0.0f) glRotatef(rz, 0.0f, 0.0f, 1.0f);
725 glScalef(w, h, 1.0f);
727 sol_draw_bill(GL_FALSE);
729 glPopMatrix();
732 sol_bill_disable();
735 void sol_fade(const struct s_draw *draw, struct s_rend *rend, float k)
737 if (k > 0.0f)
739 glMatrixMode(GL_PROJECTION);
740 glPushMatrix();
741 glLoadIdentity();
742 glMatrixMode(GL_MODELVIEW);
743 glPushMatrix();
744 glLoadIdentity();
746 glDisable(GL_LIGHTING);
747 glDisable(GL_DEPTH_TEST);
748 glDisable(GL_TEXTURE_2D);
750 glColor4f(0.0f, 0.0f, 0.0f, k);
752 sol_bill_enable(draw);
753 r_apply_mtrl(rend, default_mtrl);
754 glScalef(2.0f, 2.0f, 1.0f);
755 sol_draw_bill(GL_FALSE);
756 sol_bill_disable();
758 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
760 glEnable(GL_TEXTURE_2D);
761 glEnable(GL_DEPTH_TEST);
762 glEnable(GL_LIGHTING);
764 glMatrixMode(GL_PROJECTION);
765 glPopMatrix();
766 glMatrixMode(GL_MODELVIEW);
767 glPopMatrix();
771 /*---------------------------------------------------------------------------*/
773 int sol_load_full(struct s_full *full, const char *filename, int s)
775 if (full)
777 memset(full, 0, sizeof (*full));
779 if (sol_load_base(&full->base, filename))
781 sol_load_vary(&full->vary, &full->base);
782 sol_load_draw(&full->draw, &full->vary, s);
784 return 1;
788 return 0;
791 void sol_free_full(struct s_full *full)
793 sol_free_draw(&full->draw);
794 sol_free_vary(&full->vary);
795 sol_free_base(&full->base);
798 /*---------------------------------------------------------------------------*/
800 #if DEBUG_MTRL
801 static void check_mtrl(const char *name, GLenum pname, GLuint curr)
803 static char buff[64];
805 GLuint real;
806 GLfloat v[4];
808 glGetMaterialfv(GL_FRONT, pname, v);
810 if (pname != GL_SHININESS)
811 real = (tobyte(v[0]) |
812 tobyte(v[1]) << 8 |
813 tobyte(v[2]) << 16 |
814 tobyte(v[3]) << 24);
815 else
816 real = (tobyte(v[0]));
818 if (real != curr)
820 sprintf(buff, "%s mismatch (0x%08X -> 0x%08X)", name, real, curr);
821 glStringMarker_(buff);
825 static void assert_mtrl(const struct mtrl *mp)
827 if (glIsEnabled(GL_COLOR_MATERIAL))
828 return;
830 check_mtrl("ambient", GL_AMBIENT, mp->a);
831 check_mtrl("diffuse", GL_DIFFUSE, mp->d);
832 check_mtrl("specular", GL_SPECULAR, mp->s);
833 check_mtrl("emission", GL_EMISSION, mp->e);
834 check_mtrl("shininess", GL_SHININESS, mp->h);
836 #endif
838 void r_color_mtrl(struct s_rend *rend, int enable)
840 if (enable)
842 glEnable(GL_COLOR_MATERIAL);
844 rend->color_mtrl = 1;
846 else
848 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
850 glDisable(GL_COLOR_MATERIAL);
852 /* Keep material tracking synchronized with GL state. */
854 rend->curr_mtrl.d = 0xffffffff;
855 rend->curr_mtrl.a = 0xffffffff;
857 rend->color_mtrl = 0;
861 void r_apply_mtrl(struct s_rend *rend, int mi)
863 struct mtrl *mp = mtrl_get(mi);
864 struct mtrl *mq = &rend->curr_mtrl;
866 /* Mask ignored flags. */
868 int mp_flags = mp->base.fl & ~rend->skip_flags;
869 int mq_flags = mq->base.fl;
871 #if DEBUG_MTRL
872 assert_mtrl(&rend->curr_mtrl);
873 #endif
875 /* Bind the texture. */
877 if (mp->o != mq->o)
878 glBindTexture(GL_TEXTURE_2D, mp->o);
880 /* Set material properties. */
882 if (mp->d != mq->d && !rend->color_mtrl)
883 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp->base.d);
884 if (mp->a != mq->a && !rend->color_mtrl)
885 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp->base.a);
886 if (mp->s != mq->s)
887 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp->base.s);
888 if (mp->e != mq->e)
889 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp->base.e);
890 if (mp->h != mq->h)
891 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp->base.h);
893 /* Ball shadow. */
895 if ((mp_flags & M_SHADOWED) ^ (mq_flags & M_SHADOWED))
897 if (mp_flags & M_SHADOWED)
898 shad_draw_set();
899 else
900 shad_draw_clr();
903 /* Environment mapping. */
905 #if !ENABLE_OPENGLES
906 if ((mp_flags & M_ENVIRONMENT) ^ (mq_flags & M_ENVIRONMENT))
908 if (mp_flags & M_ENVIRONMENT)
910 glEnable(GL_TEXTURE_GEN_S);
911 glEnable(GL_TEXTURE_GEN_T);
913 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
914 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
916 else
918 glDisable(GL_TEXTURE_GEN_S);
919 glDisable(GL_TEXTURE_GEN_T);
922 #endif
924 /* Additive blending. */
926 if ((mp_flags & M_ADDITIVE) ^ (mq_flags & M_ADDITIVE))
928 if (mp_flags & M_ADDITIVE)
929 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
930 else
931 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
934 /* Visibility-from-behind. */
936 if ((mp_flags & M_TWO_SIDED) ^ (mq_flags & M_TWO_SIDED))
938 if (mp_flags & M_TWO_SIDED)
940 glDisable(GL_CULL_FACE);
941 glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1);
943 else
945 glEnable(GL_CULL_FACE);
946 glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
950 /* Decal offset. */
952 if ((mp_flags & M_DECAL) ^ (mq_flags & M_DECAL))
954 if (mp_flags & M_DECAL)
956 glEnable(GL_POLYGON_OFFSET_FILL);
957 glPolygonOffset(-1.0f, -2.0f);
959 else
960 glDisable(GL_POLYGON_OFFSET_FILL);
963 /* Alpha test. */
965 if ((mp_flags & M_ALPHA_TEST) ^ (mq_flags & M_ALPHA_TEST))
967 if (mp_flags & M_ALPHA_TEST)
969 glAlphaFunc(mtrl_func(mp->base.alpha_func), mp->base.alpha_ref);
971 glEnable(GL_ALPHA_TEST);
973 else
974 glDisable(GL_ALPHA_TEST);
977 if (((mp_flags & mq_flags) & M_ALPHA_TEST) && (mp->base.alpha_func !=
978 mq->base.alpha_func ||
979 mp->base.alpha_ref !=
980 mq->base.alpha_ref))
982 /* Update alpha function. */
984 glAlphaFunc(mtrl_func(mp->base.alpha_func), mp->base.alpha_ref);
987 /* Point sprite. */
989 if ((mp_flags & M_PARTICLE) ^ (mq_flags & M_PARTICLE))
991 if (mp_flags & M_PARTICLE)
993 const int s = video.device_h / 4;
994 const GLfloat c[3] = { 0.0f, 0.0f, 1.0f };
996 glEnable (GL_POINT_SPRITE);
997 glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
998 glPointParameterfv_(GL_POINT_DISTANCE_ATTENUATION, c);
999 glPointParameterf_ (GL_POINT_SIZE_MIN, 1);
1000 glPointParameterf_ (GL_POINT_SIZE_MAX, s);
1002 else
1004 glDisable(GL_POINT_SPRITE);
1008 /* Update current material state. */
1010 memcpy(mq, mp, sizeof (struct mtrl));
1012 mq->base.fl = mp_flags;
1015 void r_draw_enable(struct s_rend *rend)
1017 memset(rend, 0, sizeof (*rend));
1019 glEnableClientState(GL_VERTEX_ARRAY);
1020 glEnableClientState(GL_NORMAL_ARRAY);
1021 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1023 glBindTexture(GL_TEXTURE_2D, 0);
1025 rend->curr_mtrl = *mtrl_get(default_mtrl);
1028 void r_draw_disable(struct s_rend *rend)
1030 r_apply_mtrl(rend, default_mtrl);
1032 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1033 glDisableClientState(GL_NORMAL_ARRAY);
1034 glDisableClientState(GL_VERTEX_ARRAY);
1037 /*---------------------------------------------------------------------------*/