f38a4e179f98d437ca8bd870562064e796ca7fcf
[neverball] / share / solid_draw.c
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 "vec3.h"
25 #include "geom.h"
26 #include "image.h"
27 #include "base_image.h"
28 #include "base_config.h"
29 #include "lang.h"
30
31 #include "solid_draw.h"
32 #include "solid_all.h"
33
34 /*---------------------------------------------------------------------------*/
35
36 static void sol_transform(const struct s_vary *vary,
37                           const struct v_body *bp)
38 {
39     float a;
40     float d[4];
41     float e[4];
42     float p[3];
43     float v[3];
44
45     /* Compute the body transform. */
46
47     sol_body_p(p, vary, bp->pi, bp->t);
48     sol_body_e(e, vary, bp, 0);
49
50     q_as_axisangle(e, v, &a);
51
52     glTranslatef(p[0], p[1], p[2]);
53     glRotatef(V_DEG(a), v[0], v[1], v[2]);
54
55     /* Compute the shadow texture transform */
56
57     v_sub(d, vary->uv->p, p);
58
59     glActiveTexture_(GL_TEXTURE1);
60     glMatrixMode(GL_TEXTURE);
61     {
62         float k = 0.25f / vary->uv->r;
63
64         glLoadIdentity();
65         glTranslatef(0.5f - k * d[0],
66                      0.5f - k * d[2], 0.0f);
67         glScalef(k, k, 0.0f);
68     }
69     glMatrixMode(GL_MODELVIEW);
70     glActiveTexture_(GL_TEXTURE0);
71 }
72
73 /*---------------------------------------------------------------------------*/
74
75 void sol_back(const struct s_draw *draw, float n, float f, float t)
76 {
77 }
78
79 /*---------------------------------------------------------------------------*/
80
81 void sol_bill(const struct s_draw *draw, const float *M, float t)
82 {
83 }
84
85 /*---------------------------------------------------------------------------*/
86
87 void sol_shad(const struct s_draw *draw, int ui)
88 {
89 }
90
91 /*---------------------------------------------------------------------------*/
92
93 #define tobyte(f) ((GLubyte) (f * 255.0f))
94
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]))
99
100 static struct b_mtrl default_base_mtrl =
101 {
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, ""
107 };
108
109 static struct d_mtrl default_draw_mtrl =
110 {
111     &default_base_mtrl, 0
112 };
113
114 static const struct d_mtrl *sol_apply_mtrl(const struct d_mtrl *mp_draw,
115                                            const struct d_mtrl *mq_draw)
116 {
117     const struct b_mtrl *mp_base = mp_draw->base;
118     const struct b_mtrl *mq_base = mq_draw->base;
119
120     /* Bind the texture. */
121
122     if (mp_draw->o != mq_draw->o)
123         glBindTexture(GL_TEXTURE_2D, mp_draw->o);
124
125     /* Set material properties. */
126
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);
137
138     /* Enable ball shadow. */
139
140     if ((mp_base->fl & M_SHADOWED) && !(mq_base->fl & M_SHADOWED))
141     {
142         shad_draw_set();
143     }
144
145     /* Disable ball shadow. */
146
147     if (!(mp_base->fl & M_SHADOWED) && (mq_base->fl & M_SHADOWED))
148     {
149         shad_draw_clr();
150     }
151
152     /* Enable environment mapping. */
153
154     if ((mp_base->fl & M_ENVIRONMENT) && !(mq_base->fl & M_ENVIRONMENT))
155     {
156         glEnable(GL_TEXTURE_GEN_S);
157         glEnable(GL_TEXTURE_GEN_T);
158
159         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
160         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
161     }
162
163     /* Disable environment mapping. */
164
165     if ((mq_base->fl & M_ENVIRONMENT) && !(mp_base->fl & M_ENVIRONMENT))
166     {
167         glDisable(GL_TEXTURE_GEN_S);
168         glDisable(GL_TEXTURE_GEN_T);
169     }
170
171     /* Enable additive blending. */
172
173     if ((mp_base->fl & M_ADDITIVE) && !(mq_base->fl & M_ADDITIVE))
174         glBlendFunc(GL_ONE, GL_ONE);
175
176     /* Enable standard blending. */
177
178     if ((mq_base->fl & M_ADDITIVE) && !(mp_base->fl & M_ADDITIVE))
179         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
180
181     /* Enable visibility-from-behind. */
182
183     if ((mp_base->fl & M_TWO_SIDED) && !(mq_base->fl & M_TWO_SIDED))
184     {
185         glDisable(GL_CULL_FACE);
186         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
187     }
188
189     /* Disable visibility-from-behind. */
190
191     if ((mq_base->fl & M_TWO_SIDED) && !(mp_base->fl & M_TWO_SIDED))
192     {
193         glEnable(GL_CULL_FACE);
194         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
195     }
196
197     /* Enable decal offset. */
198
199     if ((mp_base->fl & M_DECAL) && !(mq_base->fl & M_DECAL))
200     {
201         glEnable(GL_POLYGON_OFFSET_FILL);
202         glPolygonOffset(-1.0f, -2.0f);
203     }
204
205     /* Disable decal offset. */
206
207     if ((mq_base->fl & M_DECAL) && !(mp_base->fl & M_DECAL))
208         glDisable(GL_POLYGON_OFFSET_FILL);
209
210     return mp_draw;
211 }
212
213 static GLuint sol_find_texture(const char *name)
214 {
215     char png[MAXSTR];
216     char jpg[MAXSTR];
217
218     GLuint o;
219
220     /* Prefer a lossless copy of the texture over a lossy compression. */
221
222     strncpy(png, name, PATHMAX); strcat(png, ".png");
223     strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
224
225     /* Check for a PNG. */
226
227     if ((o = make_image_from_file(png)))
228         return o;
229
230     /* Check for a JPG. */
231
232     if ((o = make_image_from_file(jpg)))
233         return o;
234
235     return 0;
236 }
237
238 static void sol_load_mtrl(struct d_mtrl *mp,
239                     const struct b_mtrl *mq,
240                           struct s_draw *draw)
241 {
242     mp->base = mq;
243
244     if ((mp->o = sol_find_texture(_(mq->f))))
245     {
246         /* Set the texture to clamp or repeat based on material type. */
247
248         if (mq->fl & M_CLAMPED)
249         {
250             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
251             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
252         }
253         else
254         {
255             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
256             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
257         }
258
259         /* If at least one material is reflective, mark it in the SOL. */
260
261         if (mq->fl & M_REFLECTIVE)
262             draw->reflective = 1;
263     }
264 }
265
266 static void sol_free_mtrl(struct d_mtrl *mp)
267 {
268     if (glIsTexture(mp->o))
269         glDeleteTextures(1, &mp->o);
270 }
271
272 static int sol_test_mtrl(const struct d_mtrl *mp, int f0, int f1)
273 {
274     /* Test whether the material flags exclude f0 and include f1. */
275
276     return ((mp->base->fl & f1) == f1 &&
277             (mp->base->fl & f0) ==  0);
278 }
279
280 /*---------------------------------------------------------------------------*/
281
282 static int sol_count_geom(const struct s_base *base, int g0, int gc, int mi)
283 {
284     int gi, c = 0;
285
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     */
288     /* given material                                                        */
289
290     for (gi = 0; gi < gc; gi++)
291         if (base->gv[base->iv[g0 + gi]].mi == mi)
292             c++;
293
294     return c;
295 }
296
297 static int sol_count_body(const struct b_body *bp,
298                           const struct s_base *base, int mi)
299 {
300     int li, c = 0;
301
302     /* Count all lump geoms with the given material. */
303
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);
307
308     /* Count all body geoms with the given material. */
309
310     c += sol_count_geom(base, bp->g0, bp->gc, mi);
311
312     return c;
313 }
314
315 /*---------------------------------------------------------------------------*/
316
317 static void sol_mesh_vert(struct d_vert *vp,
318                     const struct s_base *base, int oi)
319 {
320     /* Gather all vertex attributes for the given offs. */
321
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;
325
326     vp->p[0] = vq->p[0];
327     vp->p[1] = vq->p[1];
328     vp->p[2] = vq->p[2];
329
330     vp->n[0] = sq->n[0];
331     vp->n[1] = sq->n[1];
332     vp->n[2] = sq->n[2];
333
334     vp->t[0] = tq->u[0];
335     vp->t[1] = tq->u[1];
336
337     vp->u[0] = vq->p[0];
338     vp->u[1] = vq->p[2];
339 }
340
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)
344 {
345     int gi;
346
347     /* Insert all geoms with material mi into the vertex and element data. */
348
349     for (gi = 0; gi < gc; gi++)
350     {
351         const struct b_geom *gq = base->gv + base->iv[g0 + gi];
352
353         if (gq->mi == mi)
354         {
355             /* Insert a d_vert into the VBO data for each referenced b_off. */
356
357             if (iv[gq->oi] == -1)
358             {
359                 iv[gq->oi] = *vn;
360                 sol_mesh_vert(vv + (*vn)++, base, gq->oi);
361             }
362             if (iv[gq->oj] == -1)
363             {
364                 iv[gq->oj] = *vn;
365                 sol_mesh_vert(vv + (*vn)++, base, gq->oj);
366             }
367             if (iv[gq->ok] == -1)
368             {
369                 iv[gq->ok] = *vn;
370                 sol_mesh_vert(vv + (*vn)++, base, gq->ok);
371             }
372
373             /* Populate the EBO data using remapped b_off indices. */
374
375             gv[*gn].i = iv[gq->oi];
376             gv[*gn].j = iv[gq->oj];
377             gv[*gn].k = iv[gq->ok];
378
379             (*gn)++;
380         }
381     }
382 }
383
384 static void sol_load_mesh(struct d_mesh *mp,
385                     const struct b_body *bp,
386                     const struct s_draw *draw, int mi)
387 {
388     const size_t vs = sizeof (struct d_vert);
389     const size_t gs = sizeof (struct d_geom);
390
391     struct d_vert *vv = 0;
392     struct d_geom *gv = 0;
393     int           *iv = 0;
394
395     int oc = draw->base->oc;
396     int vn = 0;
397     int gn = 0;
398
399     const int gc = sol_count_body(bp, draw->base, mi);
400
401     /* Get temporary storage for vertex and element array creation. */
402
403     if ((vv = (struct d_vert *) calloc(oc, vs)) &&
404         (gv = (struct d_geom *) calloc(gc, gs)) &&
405         (iv = (int           *) calloc(oc, sizeof (int))))
406     {
407         int li, i;
408
409         /* Initialize the index remapping. */
410
411         for (i = 0; i < oc; ++i) iv[i] = -1;
412
413         /* Include all matching lump geoms in the arrays. */
414
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);
419
420         /* Include all matching body geoms in the arrays. */
421
422         sol_mesh_geom(vv, &vn, gv, &gn, draw->base, iv, bp->g0, bp->gc, mi);
423
424         /* Initialize buffer objects for all data. */
425
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);
430
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);
435
436         mp->mp  = draw->mv + mi;
437         mp->ebc = gn * 3;
438     }
439
440     free(iv);
441     free(gv);
442     free(vv);
443 }
444
445 static void sol_free_mesh(struct d_mesh *mp)
446 {
447     if (glIsBuffer(mp->ebo))
448         glDeleteBuffers(1, &mp->ebo);
449     if (glIsBuffer(mp->vbo))
450         glDeleteBuffers(1, &mp->vbo);
451 }
452
453 static const struct d_mtrl *sol_draw_mesh(const struct d_mesh *mp,
454                                           const struct d_mtrl *mq,
455                                           int f0, int f1)
456 {
457     /* If this mesh has material matching the given flags... */
458
459     if (sol_test_mtrl(mp->mp, f0, f1))
460     {
461         const size_t s = sizeof (struct d_vert);
462         const GLenum T = GL_FLOAT;
463
464         /* Apply the material state. */
465
466         mq = sol_apply_mtrl(mp->mp, mq);
467
468         /* Bind the mesh data. */
469
470         glBindBuffer(GL_ARRAY_BUFFER,         mp->vbo);
471         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mp->ebo);
472
473         glVertexPointer  (3, T, s, (GLvoid *) offsetof (struct d_vert, p));
474         glNormalPointer  (   T, s, (GLvoid *) offsetof (struct d_vert, n));
475
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));
480
481         /* Draw the mesh. */
482
483         glDrawElements(GL_TRIANGLES, mp->ebc, GL_UNSIGNED_SHORT, 0);
484     }
485
486     return mq;
487 }
488
489 /*---------------------------------------------------------------------------*/
490
491 static void sol_load_body(struct d_body *bp,
492                     const struct b_body *bq,
493                     const struct s_draw *draw)
494 {
495     int mi;
496
497     bp->base = bq;
498     bp->mc   =  0;
499
500     /* Determine how many materials this body uses. */
501
502     for (mi = 0; mi < draw->mc; ++mi)
503         if (sol_count_body(bq, draw->base, mi))
504             bp->mc++;
505
506     /* Allocate and initialize a mesh for each material. */
507
508     if ((bp->mv = (struct d_mesh *) calloc(bp->mc, sizeof (struct d_mesh))))
509     {
510         int mj = 0;
511
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);
515     }
516 }
517
518 static void sol_free_body(struct d_body *bp)
519 {
520     int mi;
521
522     for (mi = 0; mi < bp->mc; ++mi)
523         sol_free_mesh(bp->mv + mi);
524
525     free(bp->mv);
526 }
527
528 static const struct d_mtrl *sol_draw_body(const struct d_body *bp,
529                                           const struct d_mtrl *mq,
530                                           int f0, int f1)
531 {
532     int i;
533
534     for (i = 0; i < bp->mc; ++i)
535         mq = sol_draw_mesh(bp->mv + i, mq, f0, f1);
536
537     return mq;
538 }
539
540 /*---------------------------------------------------------------------------*/
541
542 int sol_load_draw(struct s_draw *draw, const struct s_vary *vary, int s)
543 {
544     int i;
545
546     memset(draw, 0, sizeof (struct s_draw));
547
548     draw->vary = vary;
549     draw->base = vary->base;
550
551     /* Initialize all materials for this file. */
552
553     if (draw->base->mc)
554     {
555         if ((draw->mv = calloc(draw->base->mc, sizeof (*draw->mv))))
556         {
557             draw->mc = draw->base->mc;
558
559             for (i = 0; i < draw->mc; i++)
560                 sol_load_mtrl(draw->mv + i, draw->base->mv + i, draw);
561         }
562     }
563
564     /* Initialize all bodies for this file. */
565
566     if (draw->base->bc)
567     {
568         if ((draw->bv = calloc(draw->base->bc, sizeof (*draw->bv))))
569         {
570             draw->bc = draw->base->bc;
571
572             for (i = 0; i < draw->bc; i++)
573                 sol_load_body(draw->bv + i, draw->base->bv + i, draw);
574         }
575     }
576
577     return 1;
578 }
579
580 void sol_free_draw(struct s_draw *draw)
581 {
582     int i;
583
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);
588 }
589
590 /*---------------------------------------------------------------------------*/
591
592 static const struct d_mtrl *sol_draw_all(const struct s_draw *draw,
593                                          const struct d_mtrl *mq,
594                                          int f0, int f1)
595 {
596     int bi;
597
598     /* Draw all meshes of all bodies matching the given material flags. */
599
600     for (bi = 0; bi < draw->bc; ++bi)
601     {
602         glPushMatrix();
603         {
604             sol_transform(draw->vary, draw->vary->bv + bi);
605             mq = sol_draw_body(draw->bv + bi, mq, f0, f1);
606         }
607         glPopMatrix();
608     }
609     return mq;
610 }
611
612 void sol_draw_enable(void)
613 {
614     glEnableClientState(GL_VERTEX_ARRAY);
615     glEnableClientState(GL_NORMAL_ARRAY);
616
617     glClientActiveTexture(GL_TEXTURE1);
618     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
619     glClientActiveTexture(GL_TEXTURE0);
620     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
621 }
622
623 void sol_draw_disable(void)
624 {
625     glClientActiveTexture(GL_TEXTURE1);
626     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
627     glClientActiveTexture(GL_TEXTURE0);
628     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
629
630     glDisableClientState(GL_NORMAL_ARRAY);
631     glDisableClientState(GL_VERTEX_ARRAY);
632 }
633
634 void sol_draw(const struct s_draw *draw, int mask, int test)
635 {
636     sol_draw_enable();
637     {
638         const struct d_mtrl *mq = &default_draw_mtrl;
639
640         /* Render all opaque geometry. */
641
642         mq = sol_draw_all(draw, mq, M_TRANSPARENT | M_REFLECTIVE, 0);
643
644         /* Render all transparent geometry. */
645
646         if (!test) glDisable(GL_DEPTH_TEST);
647         if (!mask) glDepthMask(GL_FALSE);
648         {
649             sol_draw_all(draw, mq, M_REFLECTIVE, M_TRANSPARENT);
650         }
651         if (!mask) glDepthMask(GL_TRUE);
652         if (!test) glEnable(GL_DEPTH_TEST);
653
654         mq = sol_apply_mtrl(&default_draw_mtrl, mq);
655
656         /* Revert the buffer object state. */
657
658         glBindBuffer(GL_ARRAY_BUFFER,         0);
659         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
660     }
661     sol_draw_disable();
662 }
663
664 void sol_refl(const struct s_draw *draw)
665 {
666     sol_draw_enable();
667     {
668         const struct d_mtrl *mq = &default_draw_mtrl;
669
670         /* Render all reflective geometry. */
671
672         mq = sol_draw_all(draw, mq, 0, M_REFLECTIVE);
673         mq = sol_apply_mtrl(&default_draw_mtrl, mq);
674
675         /* Revert the buffer object state. */
676
677         glBindBuffer(GL_ARRAY_BUFFER,         0);
678         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
679     }
680     sol_draw_disable();
681 }
682
683 /*---------------------------------------------------------------------------*/
684
685 int sol_load_full(struct s_full *full, const char *filename, int s)
686 {
687     if (sol_load_base(&full->base, filename))
688     {
689         sol_load_vary(&full->vary, &full->base);
690         sol_load_draw(&full->draw, &full->vary, s);
691
692         return 1;
693     }
694
695     return 0;
696 }
697
698 void sol_free_full(struct s_full *full)
699 {
700     sol_free_draw(&full->draw);
701     sol_free_vary(&full->vary);
702     sol_free_base(&full->base);
703 }
704
705 /*---------------------------------------------------------------------------*/