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>
28 #include "base_image.h"
30 #include "base_config.h"
33 #include "solid_draw.h"
34 #include "solid_all.h"
36 /*---------------------------------------------------------------------------*/
39 * Included and excluded material flags for each rendering pass.
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
},
54 /*---------------------------------------------------------------------------*/
56 static void sol_transform(const struct s_vary
*vary
,
57 const struct v_body
*bp
, int ui
)
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
;
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. */
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
))
117 glTranslatef(p
[0] - up
->p
[0],
118 p
[1] - up
->p
[1] + 0.5f
,
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
)
163 glDrawArrays(GL_TRIANGLE_STRIP
, 0, 4);
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
)
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 */
217 for (gi
= 0; gi
< gc
; gi
++)
218 if (base
->gv
[base
->iv
[g0
+ gi
]].mi
== mi
)
224 static int sol_count_body(const struct b_body
*bp
,
225 const struct s_base
*base
, int mi
)
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
);
242 static int sol_count_mesh(const struct d_body
*bp
, int p
)
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
))
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
;
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
)
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
];
292 /* Insert a d_vert into the VBO data for each referenced b_off. */
294 if (iv
[gq
->oi
] == -1)
297 sol_mesh_vert(vv
+ (*vn
)++, base
, gq
->oi
);
299 if (iv
[gq
->oj
] == -1)
302 sol_mesh_vert(vv
+ (*vn
)++, base
, gq
->oj
);
304 if (iv
[gq
->ok
] == -1)
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
];
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;
332 int oc
= draw
->base
->oc
;
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))))
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
];
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
));
426 if (rend
->curr_mtrl
.base
.fl
& M_PARTICLE
)
427 glDrawArrays(GL_POINTS
, 0, mp
->vbc
);
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
)
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
))
450 /* Allocate and initialize a mesh for each material. */
452 if ((bp
->mv
= (struct d_mesh
*) calloc(bp
->mc
, sizeof (struct d_mesh
))))
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
)
474 for (mi
= 0; mi
< bp
->mc
; ++mi
)
475 sol_free_mesh(bp
->mv
+ mi
);
480 static void sol_draw_body(const struct d_body
*bp
, struct s_rend
*rend
, int p
)
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
)
494 memset(draw
, 0, sizeof (struct s_draw
));
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;
508 /* Cache all materials for this file. */
510 mtrl_cache_sol(draw
->base
);
512 /* Initialize shadow state. */
514 draw
->shadow_ui
= -1;
517 /* Initialize all bodies for this file. */
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
);
535 void sol_free_draw(struct s_draw
*draw
)
539 mtrl_free_sol(draw
->base
);
543 for (i
= 0; i
< draw
->bc
; i
++)
544 sol_free_body(draw
->bv
+ i
);
549 /*---------------------------------------------------------------------------*/
551 static void sol_draw_all(const struct s_draw
*draw
, struct s_rend
*rend
, int p
)
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
])
562 sol_transform(draw
->vary
, draw
->vary
->bv
+ bi
, draw
->shadow_ui
);
563 sol_draw_body(draw
->bv
+ bi
, rend
, p
);
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
,
621 float n
, float f
, float t
)
623 if (!(draw
&& draw
->base
&& draw
->base
->rc
))
626 glDisable(GL_LIGHTING
);
627 glDepthMask(GL_FALSE
);
629 sol_bill_enable(draw
);
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. */
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
]);
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
);
667 glRotatef(-rx
- 90.0f
, 1.0f
, 0.0f
, 0.0f
);
668 glRotatef(-ry
, 0.0f
, 0.0f
, 1.0f
);
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
);
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
))
696 sol_bill_enable(draw
);
700 for (ri
= 0; ri
< draw
->base
->rc
; ++ri
)
702 const struct b_bill
*rp
= draw
->base
->rv
+ ri
;
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
]);
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
);
735 void sol_fade(const struct s_draw
*draw
, struct s_rend
*rend
, float k
)
739 glMatrixMode(GL_PROJECTION
);
742 glMatrixMode(GL_MODELVIEW
);
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
);
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
);
766 glMatrixMode(GL_MODELVIEW
);
771 /*---------------------------------------------------------------------------*/
773 int sol_load_full(struct s_full
*full
, const char *filename
, int s
)
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
);
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 /*---------------------------------------------------------------------------*/
801 static void check_mtrl(const char *name
, GLenum pname
, GLuint curr
)
803 static char buff
[64];
808 glGetMaterialfv(GL_FRONT
, pname
, v
);
810 if (pname
!= GL_SHININESS
)
811 real
= (tobyte(v
[0]) |
816 real
= (tobyte(v
[0]));
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
))
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
);
838 void r_color_mtrl(struct s_rend
*rend
, int enable
)
842 glEnable(GL_COLOR_MATERIAL
);
844 rend
->color_mtrl
= 1;
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
;
872 assert_mtrl(&rend
->curr_mtrl
);
875 /* Bind the texture. */
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
);
887 glMaterialfv(GL_FRONT_AND_BACK
, GL_SPECULAR
, mp
->base
.s
);
889 glMaterialfv(GL_FRONT_AND_BACK
, GL_EMISSION
, mp
->base
.e
);
891 glMaterialfv(GL_FRONT_AND_BACK
, GL_SHININESS
, mp
->base
.h
);
895 if ((mp_flags
& M_SHADOWED
) ^ (mq_flags
& M_SHADOWED
))
897 if (mp_flags
& M_SHADOWED
)
903 /* Environment mapping. */
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
);
918 glDisable(GL_TEXTURE_GEN_S
);
919 glDisable(GL_TEXTURE_GEN_T
);
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
);
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);
945 glEnable(GL_CULL_FACE
);
946 glLightModelf(GL_LIGHT_MODEL_TWO_SIDE
, 0);
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
);
960 glDisable(GL_POLYGON_OFFSET_FILL
);
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
);
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
!=
982 /* Update alpha function. */
984 glAlphaFunc(mtrl_func(mp
->base
.alpha_func
), mp
->base
.alpha_ref
);
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
);
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 /*---------------------------------------------------------------------------*/