1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
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.
8  *
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.
13  */
14 
15 #include <SDL.h>
16 #include <SDL_rwops.h>
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <math.h>
22 
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"
32 
33 #include "solid_draw.h"
34 #include "solid_all.h"
35 
36 /*---------------------------------------------------------------------------*/
37 
38 /*
39  * Included and excluded material flags for each rendering pass.
40  */
41 
42 static const struct
43 {
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 }
52 };
53 
54 /*---------------------------------------------------------------------------*/
55 
sol_transform(const struct s_vary * vary,const struct v_body * bp,int ui)56 static void sol_transform(const struct s_vary *vary,
57                           const struct v_body *bp, int ui)
58 {
59     float a;
60     float e[4];
61     float p[3];
62     float v[3];
63 
64     /* Apply the body position and rotation to the model-view matrix. */
65 
66     sol_body_p(p, vary, bp, 0.0f);
67     sol_body_e(e, vary, bp, 0.0f);
68 
69     q_as_axisangle(e, v, &a);
70 
71     if (!(p[0] == 0 && p[1] == 0 && p[2] == 0))
72         glTranslatef(p[0], p[1], p[2]);
73 
74     if (!((v[0] == 0 && v[1] == 0 && v[2] == 0) || a == 0))
75         glRotatef(V_DEG(a), v[0], v[1], v[2]);
76 
77     /* Apply the shadow transform to the texture matrix. */
78 
79     if (ui >= 0 && ui < vary->uc && vary->uv[ui].r > 0.0f)
80     {
81         struct v_ball *up = vary->uv + ui;
82 
83         if (tex_env_stage(TEX_STAGE_SHADOW))
84         {
85             glMatrixMode(GL_TEXTURE);
86             {
87                 float k = 0.25f / up->r;
88 
89                 glLoadIdentity();
90 
91                 /* Center the shadow texture on the ball. */
92 
93                 glTranslatef(0.5f, 0.5f, 0.0f);
94 
95                 /* Transform ball XZ position to ST texture coordinate. */
96 
97                 glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
98 
99                 /* Scale the shadow texture to the radius of the ball. */
100 
101                 glScalef(k, k, k);
102 
103                 /* Move the shadow texture under the ball. */
104 
105                 glTranslatef(-up->p[0], -up->p[1], -up->p[2]);
106 
107                 /* Apply the body position and rotation. */
108 
109                 glTranslatef(p[0], p[1], p[2]);
110                 glRotatef(V_DEG(a), v[0], v[1], v[2]);
111 
112                 /* Vertically center clipper texture on ball position. */
113 
114                 if (tex_env_stage(TEX_STAGE_CLIP))
115                 {
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]);
121 
122                 }
123             }
124             glMatrixMode(GL_MODELVIEW);
125 
126             tex_env_stage(TEX_STAGE_TEXTURE);
127         }
128     }
129 }
130 
131 /*---------------------------------------------------------------------------*/
132 
sol_load_bill(struct s_draw * draw)133 static void sol_load_bill(struct s_draw *draw)
134 {
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,
140 
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,
145     };
146 
147     /* Initialize a vertex buffer object for billboard drawing. */
148 
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);
153 }
154 
sol_free_bill(struct s_draw * draw)155 static void sol_free_bill(struct s_draw *draw)
156 {
157     glDeleteBuffers_(1, &draw->bill);
158 }
159 
sol_draw_bill(GLboolean edge)160 static void sol_draw_bill(GLboolean edge)
161 {
162     if (edge)
163         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
164     else
165         glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
166 }
167 
168 /*---------------------------------------------------------------------------*/
169 
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.                               */
175 
sol_bill_enable(const struct s_draw * draw)176 static void sol_bill_enable(const struct s_draw *draw)
177 {
178     const size_t s = sizeof (GLfloat);
179 
180     glBindBuffer_(GL_ARRAY_BUFFER, draw->bill);
181 
182     glDisableClientState(GL_NORMAL_ARRAY);
183 
184     glTexCoordPointer(2, GL_FLOAT, s * 4, (GLvoid *) (    0));
185     glVertexPointer  (2, GL_FLOAT, s * 4, (GLvoid *) (s * 2));
186 }
187 
sol_bill_disable(void)188 static void sol_bill_disable(void)
189 {
190     glEnableClientState(GL_NORMAL_ARRAY);
191 
192     glBindBuffer_(GL_ARRAY_BUFFER, 0);
193 }
194 
195 /*---------------------------------------------------------------------------*/
196 
sol_test_mtrl(int mi,int p)197 static int sol_test_mtrl(int mi, int p)
198 {
199     const struct mtrl *mp = mtrl_get(mi);
200 
201     /* Test whether the material flags match inclusion rules. */
202 
203     return ((mp->base.fl & passes[p].in) == passes[p].in &&
204             (mp->base.fl & passes[p].ex) == 0);
205 }
206 
207 /*---------------------------------------------------------------------------*/
208 
sol_count_geom(const struct s_base * base,int g0,int gc,int mi)209 static int sol_count_geom(const struct s_base *base, int g0, int gc, int mi)
210 {
211     int gi, c = 0;
212 
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                                                        */
216 
217     for (gi = 0; gi < gc; gi++)
218         if (base->gv[base->iv[g0 + gi]].mi == mi)
219             c++;
220 
221     return c;
222 }
223 
sol_count_body(const struct b_body * bp,const struct s_base * base,int mi)224 static int sol_count_body(const struct b_body *bp,
225                           const struct s_base *base, int mi)
226 {
227     int li, c = 0;
228 
229     /* Count all lump geoms with the given material. */
230 
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);
234 
235     /* Count all body geoms with the given material. */
236 
237     c += sol_count_geom(base, bp->g0, bp->gc, mi);
238 
239     return c;
240 }
241 
sol_count_mesh(const struct d_body * bp,int p)242 static int sol_count_mesh(const struct d_body *bp, int p)
243 {
244     int mi, c = 0;
245 
246     /* Count the body meshes matching the given material flags. */
247 
248     for (mi = 0; mi < bp->mc; ++mi)
249         if (sol_test_mtrl(bp->mv[mi].mtrl, p))
250             c++;
251 
252     return c;
253 }
254 
255 /*---------------------------------------------------------------------------*/
256 
sol_mesh_vert(struct d_vert * vp,const struct s_base * base,int oi)257 static void sol_mesh_vert(struct d_vert *vp,
258                           const struct s_base *base, int oi)
259 {
260     /* Gather all vertex attributes for the given offs. */
261 
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;
265 
266     vp->p[0] = vq->p[0];
267     vp->p[1] = vq->p[1];
268     vp->p[2] = vq->p[2];
269 
270     vp->n[0] = sq->n[0];
271     vp->n[1] = sq->n[1];
272     vp->n[2] = sq->n[2];
273 
274     vp->t[0] = tq->u[0];
275     vp->t[1] = tq->u[1];
276 }
277 
sol_mesh_geom(struct d_vert * vv,int * vn,struct d_geom * gv,int * gn,const struct s_base * base,int * iv,int g0,int gc,int mi)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)
281 {
282     int gi;
283 
284     /* Insert all geoms with material mi into the vertex and element data. */
285 
286     for (gi = 0; gi < gc; gi++)
287     {
288         const struct b_geom *gq = base->gv + base->iv[g0 + gi];
289 
290         if (gq->mi == mi)
291         {
292             /* Insert a d_vert into the VBO data for each referenced b_off. */
293 
294             if (iv[gq->oi] == -1)
295             {
296                 iv[gq->oi] = *vn;
297                 sol_mesh_vert(vv + (*vn)++, base, gq->oi);
298             }
299             if (iv[gq->oj] == -1)
300             {
301                 iv[gq->oj] = *vn;
302                 sol_mesh_vert(vv + (*vn)++, base, gq->oj);
303             }
304             if (iv[gq->ok] == -1)
305             {
306                 iv[gq->ok] = *vn;
307                 sol_mesh_vert(vv + (*vn)++, base, gq->ok);
308             }
309 
310             /* Populate the EBO data using remapped b_off indices. */
311 
312             gv[*gn].i = iv[gq->oi];
313             gv[*gn].j = iv[gq->oj];
314             gv[*gn].k = iv[gq->ok];
315 
316             (*gn)++;
317         }
318     }
319 }
320 
sol_load_mesh(struct d_mesh * mp,const struct b_body * bp,const struct s_draw * draw,int mi)321 static void sol_load_mesh(struct d_mesh *mp,
322                           const struct b_body *bp,
323                           const struct s_draw *draw, int mi)
324 {
325     const size_t vs = sizeof (struct d_vert);
326     const size_t gs = sizeof (struct d_geom);
327 
328     struct d_vert *vv = 0;
329     struct d_geom *gv = 0;
330     int           *iv = 0;
331 
332     int oc = draw->base->oc;
333     int vn = 0;
334     int gn = 0;
335 
336     const int gc = sol_count_body(bp, draw->base, mi);
337 
338     /* Get temporary storage for vertex and element array creation. */
339 
340     if ((vv = (struct d_vert *) calloc(oc, vs)) &&
341         (gv = (struct d_geom *) calloc(gc, gs)) &&
342         (iv = (int           *) calloc(oc, sizeof (int))))
343     {
344         int li, i;
345 
346         /* Initialize the index remapping. */
347 
348         for (i = 0; i < oc; ++i) iv[i] = -1;
349 
350         /* Include all matching lump geoms in the arrays. */
351 
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);
356 
357         /* Include all matching body geoms in the arrays. */
358 
359         sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv, bp->g0, bp->gc, mi);
360 
361         /* Initialize buffer objects for all data. */
362 
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);
367 
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);
372 
373         /* Note cached material index. */
374 
375         mp->mtrl = draw->base->mtrls[mi];
376 
377         mp->ebc = gn * 3;
378         mp->vbc = vn;
379     }
380 
381     free(iv);
382     free(gv);
383     free(vv);
384 }
385 
sol_free_mesh(struct d_mesh * mp)386 static void sol_free_mesh(struct d_mesh *mp)
387 {
388     glDeleteBuffers_(1, &mp->ebo);
389     glDeleteBuffers_(1, &mp->vbo);
390 }
391 
sol_draw_mesh(const struct d_mesh * mp,struct s_rend * rend,int p)392 void sol_draw_mesh(const struct d_mesh *mp, struct s_rend *rend, int p)
393 {
394     /* If this mesh has material matching the given flags... */
395 
396     if (sol_test_mtrl(mp->mtrl, p))
397     {
398         const size_t s = sizeof (struct d_vert);
399         const GLenum T = GL_FLOAT;
400 
401         /* Apply the material state. */
402 
403         r_apply_mtrl(rend, mp->mtrl);
404 
405         /* Bind the mesh data. */
406 
407         glBindBuffer_(GL_ARRAY_BUFFER,         mp->vbo);
408         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
409 
410         glVertexPointer  (3, T, s, (GLvoid *) offsetof (struct d_vert, p));
411         glNormalPointer  (   T, s, (GLvoid *) offsetof (struct d_vert, n));
412 
413         if (tex_env_stage(TEX_STAGE_SHADOW))
414         {
415             glTexCoordPointer(3, T, s, (GLvoid *) offsetof (struct d_vert, p));
416 
417             if (tex_env_stage(TEX_STAGE_CLIP))
418                 glTexCoordPointer(3, T, s, (GLvoid *) offsetof (struct d_vert, p));
419 
420             tex_env_stage(TEX_STAGE_TEXTURE);
421         }
422         glTexCoordPointer(2, T, s, (GLvoid *) offsetof (struct d_vert, t));
423 
424         /* Draw the mesh. */
425 
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);
430     }
431 }
432 
433 /*---------------------------------------------------------------------------*/
434 
sol_load_body(struct d_body * bp,const struct b_body * bq,const struct s_draw * draw)435 static void sol_load_body(struct d_body *bp,
436                           const struct b_body *bq,
437                           const struct s_draw *draw)
438 {
439     int mi;
440 
441     bp->base = bq;
442     bp->mc   =  0;
443 
444     /* Determine how many materials this body uses. */
445 
446     for (mi = 0; mi < draw->base->mc; ++mi)
447         if (sol_count_body(bq, draw->base, mi))
448             bp->mc++;
449 
450     /* Allocate and initialize a mesh for each material. */
451 
452     if ((bp->mv = (struct d_mesh *) calloc(bp->mc, sizeof (struct d_mesh))))
453     {
454         int mj = 0;
455 
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);
459     }
460 
461     /* Cache a mesh count for each pass. */
462 
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);
468 }
469 
sol_free_body(struct d_body * bp)470 static void sol_free_body(struct d_body *bp)
471 {
472     int mi;
473 
474     for (mi = 0; mi < bp->mc; ++mi)
475         sol_free_mesh(bp->mv + mi);
476 
477     free(bp->mv);
478 }
479 
sol_draw_body(const struct d_body * bp,struct s_rend * rend,int p)480 static void sol_draw_body(const struct d_body *bp, struct s_rend *rend, int p)
481 {
482     int i;
483 
484     for (i = 0; i < bp->mc; ++i)
485         sol_draw_mesh(bp->mv + i, rend, p);
486 }
487 
488 /*---------------------------------------------------------------------------*/
489 
sol_load_draw(struct s_draw * draw,struct s_vary * vary,int s)490 int sol_load_draw(struct s_draw *draw, struct s_vary *vary, int s)
491 {
492     int i;
493 
494     memset(draw, 0, sizeof (struct s_draw));
495 
496     draw->vary = vary;
497     draw->base = vary->base;
498 
499     /* Determine whether this file has reflective materials. */
500 
501     for (i = 0; i < draw->base->mc; i++)
502         if (draw->base->mv[i].fl & M_REFLECTIVE)
503         {
504             draw->reflective = 1;
505             break;
506         }
507 
508     /* Cache all materials for this file. */
509 
510     mtrl_cache_sol(draw->base);
511 
512     /* Initialize shadow state. */
513 
514     draw->shadow_ui = -1;
515     draw->shadowed = s;
516 
517     /* Initialize all bodies for this file. */
518 
519     if (draw->base->bc)
520     {
521         if ((draw->bv = calloc(draw->base->bc, sizeof (*draw->bv))))
522         {
523             draw->bc = draw->base->bc;
524 
525             for (i = 0; i < draw->bc; i++)
526                 sol_load_body(draw->bv + i, draw->base->bv + i, draw);
527         }
528     }
529 
530     sol_load_bill(draw);
531 
532     return 1;
533 }
534 
sol_free_draw(struct s_draw * draw)535 void sol_free_draw(struct s_draw *draw)
536 {
537     int i;
538 
539     mtrl_free_sol(draw->base);
540 
541     sol_free_bill(draw);
542 
543     for (i = 0; i < draw->bc; i++)
544         sol_free_body(draw->bv + i);
545 
546     free(draw->bv);
547 }
548 
549 /*---------------------------------------------------------------------------*/
550 
sol_draw_all(const struct s_draw * draw,struct s_rend * rend,int p)551 static void sol_draw_all(const struct s_draw *draw, struct s_rend *rend, int p)
552 {
553     int bi;
554 
555     /* Draw all meshes of all bodies matching the given material flags. */
556 
557     for (bi = 0; bi < draw->bc; ++bi)
558         if (draw->bv[bi].pass[p])
559         {
560             glPushMatrix();
561             {
562                 sol_transform(draw->vary, draw->vary->bv + bi, draw->shadow_ui);
563                 sol_draw_body(draw->bv + bi, rend, p);
564             }
565             glPopMatrix();
566         }
567 }
568 
569 /*---------------------------------------------------------------------------*/
570 
sol_draw(const struct s_draw * draw,struct s_rend * rend,int mask,int test)571 void sol_draw(const struct s_draw *draw, struct s_rend *rend, int mask, int test)
572 {
573     /* Disable shadowed material setup if not requested. */
574 
575     rend->skip_flags |= (draw->shadowed ? 0 : M_SHADOWED);
576 
577     /* Render all opaque geometry, decals last. */
578 
579     sol_draw_all(draw, rend, PASS_OPAQUE);
580     sol_draw_all(draw, rend, PASS_OPAQUE_DECAL);
581 
582     /* Render all transparent geometry, decals first. */
583 
584     if (!test) glDisable(GL_DEPTH_TEST);
585     if (!mask) glDepthMask(GL_FALSE);
586     {
587         sol_draw_all(draw, rend, PASS_TRANSPARENT_DECAL);
588         sol_draw_all(draw, rend, PASS_TRANSPARENT);
589     }
590     if (!mask) glDepthMask(GL_TRUE);
591     if (!test) glEnable(GL_DEPTH_TEST);
592 
593     /* Revert the buffer object state. */
594 
595     glBindBuffer_(GL_ARRAY_BUFFER,         0);
596     glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
597 
598     rend->skip_flags = 0;
599 }
600 
sol_refl(const struct s_draw * draw,struct s_rend * rend)601 void sol_refl(const struct s_draw *draw, struct s_rend *rend)
602 {
603     /* Disable shadowed material setup if not requested. */
604 
605     rend->skip_flags |= (draw->shadowed ? 0 : M_SHADOWED);
606 
607     /* Render all reflective geometry. */
608 
609     sol_draw_all(draw, rend, PASS_REFLECTIVE);
610 
611     /* Revert the buffer object state. */
612 
613     glBindBuffer_(GL_ARRAY_BUFFER,         0);
614     glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER, 0);
615 
616     rend->skip_flags = 0;
617 }
618 
sol_back(const struct s_draw * draw,struct s_rend * rend,float n,float f,float t)619 void sol_back(const struct s_draw *draw,
620               struct s_rend *rend,
621               float n, float f, float t)
622 {
623     if (!(draw && draw->base && draw->base->rc))
624         return;
625 
626     glDisable(GL_LIGHTING);
627     glDepthMask(GL_FALSE);
628 
629     sol_bill_enable(draw);
630     {
631         int ri;
632 
633         /* Consider each billboard. */
634 
635         for (ri = 0; ri < draw->base->rc; ri++)
636         {
637             const struct b_bill *rp = draw->base->rv + ri;
638 
639             /* Render only billboards at distances between n and f. */
640 
641             if (n <= rp->d && rp->d < f)
642             {
643                 float T = (rp->t > 0.0f) ? (fmodf(t, rp->t) - rp->t / 2) : 0;
644 
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;
647 
648                 /* Render only billboards facing the viewer. */
649 
650                 if (w > 0 && h > 0)
651                 {
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;
655 
656                     r_apply_mtrl(rend, draw->base->mtrls[rp->mi]);
657 
658                     glPushMatrix();
659                     {
660                         if (ry) glRotatef(ry, 0.0f, 1.0f, 0.0f);
661                         if (rx) glRotatef(rx, 1.0f, 0.0f, 0.0f);
662 
663                         glTranslatef(0.0f, 0.0f, -rp->d);
664 
665                         if (rp->fl & B_FLAT)
666                         {
667                             glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
668                             glRotatef(-ry,         0.0f, 0.0f, 1.0f);
669                         }
670                         if (rp->fl & B_EDGE)
671                             glRotatef(-rx,         1.0f, 0.0f, 0.0f);
672 
673                         if (rz) glRotatef(rz, 0.0f, 0.0f, 1.0f);
674 
675                         glScalef(w, h, 1.0f);
676 
677                         sol_draw_bill(rp->fl & B_EDGE);
678                     }
679                     glPopMatrix();
680                 }
681             }
682         }
683     }
684     sol_bill_disable();
685 
686     glDepthMask(GL_TRUE);
687     glEnable(GL_LIGHTING);
688 }
689 
sol_bill(const struct s_draw * draw,struct s_rend * rend,const float * M,float t)690 void sol_bill(const struct s_draw *draw,
691               struct s_rend *rend, const float *M, float t)
692 {
693     if (!(draw && draw->base && draw->base->rc))
694         return;
695 
696     sol_bill_enable(draw);
697     {
698         int ri;
699 
700         for (ri = 0; ri < draw->base->rc; ++ri)
701         {
702             const struct b_bill *rp = draw->base->rv + ri;
703 
704             float T = rp->t * t;
705             float S = fsinf(T);
706 
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;
712 
713             r_apply_mtrl(rend, draw->base->mtrls[rp->mi]);
714 
715             glPushMatrix();
716             {
717                 glTranslatef(rp->p[0], rp->p[1], rp->p[2]);
718 
719                 if (M && ((rp->fl & B_NOFACE) == 0)) glMultMatrixf(M);
720 
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);
724 
725                 glScalef(w, h, 1.0f);
726 
727                 sol_draw_bill(GL_FALSE);
728             }
729             glPopMatrix();
730         }
731     }
732     sol_bill_disable();
733 }
734 
sol_fade(const struct s_draw * draw,struct s_rend * rend,float k)735 void sol_fade(const struct s_draw *draw, struct s_rend *rend, float k)
736 {
737     if (k > 0.0f)
738     {
739         glMatrixMode(GL_PROJECTION);
740         glPushMatrix();
741         glLoadIdentity();
742         glMatrixMode(GL_MODELVIEW);
743         glPushMatrix();
744         glLoadIdentity();
745         {
746             glDisable(GL_LIGHTING);
747             glDisable(GL_DEPTH_TEST);
748             glDisable(GL_TEXTURE_2D);
749 
750             glColor4f(0.0f, 0.0f, 0.0f, k);
751 
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();
757 
758             glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
759 
760             glEnable(GL_TEXTURE_2D);
761             glEnable(GL_DEPTH_TEST);
762             glEnable(GL_LIGHTING);
763         }
764         glMatrixMode(GL_PROJECTION);
765         glPopMatrix();
766         glMatrixMode(GL_MODELVIEW);
767         glPopMatrix();
768     }
769 }
770 
771 /*---------------------------------------------------------------------------*/
772 
sol_load_full(struct s_full * full,const char * filename,int s)773 int sol_load_full(struct s_full *full, const char *filename, int s)
774 {
775     if (full)
776     {
777         memset(full, 0, sizeof (*full));
778 
779         if (sol_load_base(&full->base, filename))
780         {
781             sol_load_vary(&full->vary, &full->base);
782             sol_load_draw(&full->draw, &full->vary, s);
783 
784             return 1;
785         }
786     }
787 
788     return 0;
789 }
790 
sol_free_full(struct s_full * full)791 void sol_free_full(struct s_full *full)
792 {
793     sol_free_draw(&full->draw);
794     sol_free_vary(&full->vary);
795     sol_free_base(&full->base);
796 }
797 
798 /*---------------------------------------------------------------------------*/
799 
800 #if DEBUG_MTRL
check_mtrl(const char * name,GLenum pname,GLuint curr)801 static void check_mtrl(const char *name, GLenum pname, GLuint curr)
802 {
803     static char buff[64];
804 
805     GLuint real;
806     GLfloat v[4];
807 
808     glGetMaterialfv(GL_FRONT, pname, v);
809 
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]));
817 
818     if (real != curr)
819     {
820         sprintf(buff, "%s mismatch (0x%08X -> 0x%08X)", name, real, curr);
821         glStringMarker_(buff);
822     }
823 }
824 
assert_mtrl(const struct mtrl * mp)825 static void assert_mtrl(const struct mtrl *mp)
826 {
827     if (glIsEnabled(GL_COLOR_MATERIAL))
828         return;
829 
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);
835 }
836 #endif
837 
r_color_mtrl(struct s_rend * rend,int enable)838 void r_color_mtrl(struct s_rend *rend, int enable)
839 {
840     if (enable)
841     {
842         glEnable(GL_COLOR_MATERIAL);
843 
844         rend->color_mtrl = 1;
845     }
846     else
847     {
848         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
849 
850         glDisable(GL_COLOR_MATERIAL);
851 
852         /* Keep material tracking synchronized with GL state. */
853 
854         rend->curr_mtrl.d = 0xffffffff;
855         rend->curr_mtrl.a = 0xffffffff;
856 
857         rend->color_mtrl = 0;
858     }
859 }
860 
r_apply_mtrl(struct s_rend * rend,int mi)861 void r_apply_mtrl(struct s_rend *rend, int mi)
862 {
863     struct mtrl *mp = mtrl_get(mi);
864     struct mtrl *mq = &rend->curr_mtrl;
865 
866     /* Mask ignored flags. */
867 
868     int mp_flags = mp->base.fl & ~rend->skip_flags;
869     int mq_flags = mq->base.fl;
870 
871 #if DEBUG_MTRL
872     assert_mtrl(&rend->curr_mtrl);
873 #endif
874 
875     /* Bind the texture. */
876 
877     if (mp->o != mq->o)
878         glBindTexture(GL_TEXTURE_2D, mp->o);
879 
880     /* Set material properties. */
881 
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);
892 
893     /* Ball shadow. */
894 
895     if ((mp_flags & M_SHADOWED) ^ (mq_flags & M_SHADOWED))
896     {
897         if (mp_flags & M_SHADOWED)
898             shad_draw_set();
899         else
900             shad_draw_clr();
901     }
902 
903     /* Environment mapping. */
904 
905 #if !ENABLE_OPENGLES
906     if ((mp_flags & M_ENVIRONMENT) ^ (mq_flags & M_ENVIRONMENT))
907     {
908         if (mp_flags & M_ENVIRONMENT)
909         {
910             glEnable(GL_TEXTURE_GEN_S);
911             glEnable(GL_TEXTURE_GEN_T);
912 
913             glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
914             glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
915         }
916         else
917         {
918             glDisable(GL_TEXTURE_GEN_S);
919             glDisable(GL_TEXTURE_GEN_T);
920         }
921     }
922 #endif
923 
924     /* Additive blending. */
925 
926     if ((mp_flags & M_ADDITIVE) ^ (mq_flags & M_ADDITIVE))
927     {
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);
932     }
933 
934     /* Visibility-from-behind. */
935 
936     if ((mp_flags & M_TWO_SIDED) ^ (mq_flags & M_TWO_SIDED))
937     {
938         if (mp_flags & M_TWO_SIDED)
939         {
940             glDisable(GL_CULL_FACE);
941             glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1);
942         }
943         else
944         {
945             glEnable(GL_CULL_FACE);
946             glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
947         }
948     }
949 
950     /* Decal offset. */
951 
952     if ((mp_flags & M_DECAL) ^ (mq_flags & M_DECAL))
953     {
954         if (mp_flags & M_DECAL)
955         {
956             glEnable(GL_POLYGON_OFFSET_FILL);
957             glPolygonOffset(-1.0f, -2.0f);
958         }
959         else
960             glDisable(GL_POLYGON_OFFSET_FILL);
961     }
962 
963     /* Alpha test. */
964 
965     if ((mp_flags & M_ALPHA_TEST) ^ (mq_flags & M_ALPHA_TEST))
966     {
967         if (mp_flags & M_ALPHA_TEST)
968         {
969             glAlphaFunc(mtrl_func(mp->base.alpha_func), mp->base.alpha_ref);
970 
971             glEnable(GL_ALPHA_TEST);
972         }
973         else
974             glDisable(GL_ALPHA_TEST);
975     }
976 
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))
981     {
982         /* Update alpha function. */
983 
984         glAlphaFunc(mtrl_func(mp->base.alpha_func), mp->base.alpha_ref);
985     }
986 
987     /* Point sprite. */
988 
989     if ((mp_flags & M_PARTICLE) ^ (mq_flags & M_PARTICLE))
990     {
991         if (mp_flags & M_PARTICLE)
992         {
993             const int s = video.device_h / 4;
994             const GLfloat c[3] = { 0.0f, 0.0f, 1.0f };
995 
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);
1001         }
1002         else
1003         {
1004             glDisable(GL_POINT_SPRITE);
1005         }
1006     }
1007 
1008     /* Update current material state. */
1009 
1010     memcpy(mq, mp, sizeof (struct mtrl));
1011 
1012     mq->base.fl = mp_flags;
1013 }
1014 
r_draw_enable(struct s_rend * rend)1015 void r_draw_enable(struct s_rend *rend)
1016 {
1017     memset(rend, 0, sizeof (*rend));
1018 
1019     glEnableClientState(GL_VERTEX_ARRAY);
1020     glEnableClientState(GL_NORMAL_ARRAY);
1021     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1022 
1023     glBindTexture(GL_TEXTURE_2D, 0);
1024 
1025     rend->curr_mtrl = *mtrl_get(default_mtrl);
1026 }
1027 
r_draw_disable(struct s_rend * rend)1028 void r_draw_disable(struct s_rend *rend)
1029 {
1030     r_apply_mtrl(rend, default_mtrl);
1031 
1032     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1033     glDisableClientState(GL_NORMAL_ARRAY);
1034     glDisableClientState(GL_VERTEX_ARRAY);
1035 }
1036 
1037 /*---------------------------------------------------------------------------*/
1038