Oops! Fixed newly envmapped glass being scheduled as opaque.
[neverball] / share / mapc.c
index 83b566c..d79984f 100644 (file)
 
 /*---------------------------------------------------------------------------*/
 
-#ifdef WIN32
-#pragma comment(lib, "SDL_ttf.lib")
-#pragma comment(lib, "SDL_image.lib")
-#pragma comment(lib, "SDL_mixer.lib")
-#pragma comment(lib, "SDL.lib")
-#pragma comment(lib, "SDLmain.lib")
-#pragma comment(lib, "opengl32.lib")
-#endif
-
-/*---------------------------------------------------------------------------*/
-
-#include <SDL.h>
-#include <SDL_image.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +21,7 @@
 
 #include "vec3.h"
 #include "solid.h"
+#include "base_image.h"
 #include "base_config.h"
 
 #define MAXSTR 256
 
 /*---------------------------------------------------------------------------*/
 
+static int debug_output = 0;
+
+/*---------------------------------------------------------------------------*/
+
 /* Ohhhh... arbitrary! */
 
-#define MAXM    256
-#define MAXV    32767
-#define MAXE    32767
-#define MAXS    32767
-#define MAXT    32767
-#define MAXG    32767
-#define MAXL    1024
-#define MAXN    1024
-#define MAXP    512
-#define MAXB    512
-#define MAXH    1024
-#define MAXZ    16
-#define MAXJ    32
-#define MAXX    256
-#define MAXR    1024
-#define MAXU    16
-#define MAXW    32
-#define MAXD    128
-#define MAXA    8192
-#define MAXI    32767
+#define MAXM    1024
+#define MAXV    65534
+#define MAXE    65534
+#define MAXS    65534
+#define MAXT    65534
+#define MAXG    65534
+#define MAXL    2048
+#define MAXN    2048
+#define MAXP    1024
+#define MAXB    1024
+#define MAXH    2048
+#define MAXZ    1024
+#define MAXJ    1024
+#define MAXX    1024
+#define MAXR    2048
+#define MAXU    1024
+#define MAXW    1024
+#define MAXD    1024
+#define MAXA    16384
+#define MAXI    65534
 
 static int overflow(const char *s)
 {
@@ -138,7 +130,7 @@ static int inch(struct s_file *fp)
 
 static int incz(struct s_file *fp)
 {
-    return (fp->zc < MAXZ) ? fp->zc++ : overflow("geol");
+    return (fp->zc < MAXZ) ? fp->zc++ : overflow("goal");
 }
 
 static int incj(struct s_file *fp)
@@ -166,6 +158,11 @@ static int incw(struct s_file *fp)
     return (fp->wc < MAXW) ? fp->wc++ : overflow("view");
 }
 
+static int incd(struct s_file *fp)
+{
+    return (fp->dc < MAXD) ? fp->dc++ : overflow("dict");
+}
+
 static int inci(struct s_file *fp)
 {
     return (fp->ic < MAXI) ? fp->ic++ : overflow("indx");
@@ -190,6 +187,7 @@ static void init_file(struct s_file *fp)
     fp->rc = 0;
     fp->uc = 0;
     fp->wc = 0;
+    fp->dc = 0;
     fp->ac = 0;
     fp->ic = 0;
 
@@ -210,6 +208,7 @@ static void init_file(struct s_file *fp)
     fp->rv = (struct s_bill *) calloc(MAXR, sizeof (struct s_bill));
     fp->uv = (struct s_ball *) calloc(MAXU, sizeof (struct s_ball));
     fp->wv = (struct s_view *) calloc(MAXW, sizeof (struct s_view));
+    fp->dv = (struct s_dict *) calloc(MAXD, sizeof (struct s_dict));
     fp->av = (char          *) calloc(MAXA, sizeof (char));
     fp->iv = (int           *) calloc(MAXI, sizeof (int));
 }
@@ -291,7 +290,7 @@ static void targets(struct s_file *fp)
  * The following code caches  image sizes.  Textures are referenced by
  * name,  but  their  sizes   are  necessary  when  computing  texture
  * coordinates.  This code  allows each file to be  accessed only once
- * regardless of the number of surfaces refering to it.
+ * regardless of the number of surfaces referring to it.
  */
 
 struct _imagedata
@@ -322,15 +321,11 @@ static void free_imagedata()
 
 static int size_load(const char *file, int *w, int *h)
 {
-    SDL_Surface *S;
+    void *p;
 
-    if ((S = IMG_Load(file)))
+    if ((p = image_load(file, w, h, NULL)))
     {
-        *w = S->w;
-        *h = S->h;
-
-        SDL_FreeSurface(S);
-
+        free(p);
         return 1;
     }
     return 0;
@@ -339,7 +334,6 @@ static int size_load(const char *file, int *w, int *h)
 static void size_image(const char *name, int *w, int *h)
 {
     char jpg[MAXSTR];
-    char tga[MAXSTR];
     char png[MAXSTR];
     int i;
 
@@ -357,11 +351,9 @@ static void size_image(const char *name, int *w, int *h)
     *h = 0;
 
     strcpy(jpg, name); strcat(jpg, ".jpg");
-    strcpy(tga, name); strcat(tga, ".tga");
     strcpy(png, name); strcat(png, ".png");
 
     if (size_load(config_data(png), w, h) ||
-        size_load(config_data(tga), w, h) ||
         size_load(config_data(jpg), w, h))
     {
 
@@ -416,6 +408,7 @@ static int read_mtrl(struct s_file *fp, const char *name)
     mp->e[0] = mp->e[1] = mp->e[2] = mp->e[3] = 1.0f;
     mp->h[0] = 0.0f;
     mp->fl   = 0;
+    mp->angle = 45.0f;
 
     if ((fin = fopen(config_data(name), "r")))
     {
@@ -424,12 +417,12 @@ static int read_mtrl(struct s_file *fp, const char *name)
                "%f %f %f %f "
                "%f %f %f %f "
                "%f %f %f %f "
-               "%f %d ",
+               "%f %d %f",
                mp->d, mp->d + 1, mp->d + 2, mp->d + 3,
                mp->a, mp->a + 1, mp->a + 2, mp->a + 3,
                mp->s, mp->s + 1, mp->s + 2, mp->s + 3,
                mp->e, mp->e + 1, mp->e + 2, mp->e + 3,
-               mp->h, &mp->fl);
+               mp->h, &mp->fl, &mp->angle);
         fclose(fin);
     }
 
@@ -470,10 +463,34 @@ static void move_lump(struct s_file *fp,
 static void move_body(struct s_file *fp,
                       struct s_body *bp)
 {
-    int i;
+    int i, *b;
+
+    /* Move the lumps. */
 
     for (i = 0; i < bp->lc; i++)
         move_lump(fp, fp->lv + bp->l0 + i, fp->pv[bp->pi].p);
+
+    /* Create an array to mark any verts referenced by moved geoms. */
+
+    if (bp->gc > 0 && (b = (int *) calloc(fp->vc, sizeof (int))))
+    {
+        /* Mark the verts. */
+
+        for (i = 0; i < bp->gc; i++)
+        {
+            b[fp->gv[fp->iv[bp->g0 + i]].vi] = 1;
+            b[fp->gv[fp->iv[bp->g0 + i]].vj] = 1;
+            b[fp->gv[fp->iv[bp->g0 + i]].vk] = 1;
+        }
+
+        /* Apply the motion to the marked vertices. */
+
+        for (i = 0; i < fp->vc; ++i)
+            if (b[i])
+                move_vert(fp->vv + i, fp->pv[bp->pi].p);
+
+        free(b);
+    }
 }
 
 static void move_file(struct s_file *fp)
@@ -690,7 +707,7 @@ static int map_token(FILE *fin, int pi, char key[MAXSTR], char val[MAXSTR])
 
         /* Scan a key-value pair. */
 
-        if (buf[0] == '"')
+        if (buf[0] == '\"')
         {
             strcpy(key, strtok(buf,  "\""));
             (void)      strtok(NULL, "\"");
@@ -776,6 +793,7 @@ static void make_path(struct s_file *fp,
     pp->t    = 1.f;
     pp->pi   = pi;
     pp->f    = 1;
+    pp->s    = 1;
 
     for (i = 0; i < c; i++)
     {
@@ -791,6 +809,9 @@ static void make_path(struct s_file *fp,
         if (strcmp(k[i], "speed") == 0)
             sscanf(v[i], "%f", &pp->t);
 
+        if (strcmp(k[i], "smooth") == 0)
+            pp->s = atoi(v[i]);
+
         if (strcmp(k[i], "origin") == 0)
         {
             int x = 0, y = 0, z = 0;
@@ -804,6 +825,33 @@ static void make_path(struct s_file *fp,
     }
 }
 
+static void make_dict(struct s_file *fp,
+                      const char *k,
+                      const char *v)
+{
+    int space_left, space_needed, di = incd(fp);
+
+    struct s_dict *dp = fp->dv + di;
+
+    space_left   = MAXA - fp->ac;
+    space_needed = strlen(k) + 1 + strlen(v) + 1;
+
+    if (space_needed > space_left)
+    {
+        fp->dc--;
+        return;
+    }
+
+    dp->ai = fp->ac;
+    dp->aj = dp->ai + strlen(k) + 1;
+    fp->ac = dp->aj + strlen(v) + 1;
+
+    strncpy(fp->av + dp->ai, k, space_left);
+    strncpy(fp->av + dp->aj, v, space_left - strlen(k) - 1);
+}
+
+static int read_dict_entries = 0;
+
 static void make_body(struct s_file *fp,
                       char k[][MAXSTR],
                       char v[][MAXSTR], int c, int l0)
@@ -839,15 +887,8 @@ static void make_body(struct s_file *fp,
         else if (strcmp(k[i], "origin") == 0)
             sscanf(v[i], "%d %d %d", &x, &y, &z);
 
-        else if (strcmp(k[i], "classname") != 0)
-        {
-            /* Considers other strings as metadata */
-            strcat(fp->av, k[i]);
-            strcat(fp->av, "=");
-            strcat(fp->av, v[i]);
-            strcat(fp->av, "\n");
-            fp->ac += (int) (strlen(v[i]) + (strlen(k[i])) + 2);
-        }
+        else if (read_dict_entries && strcmp(k[i], "classname") != 0)
+            make_dict(fp, k[i], v[i]);
     }
 
     bp->l0 = l0;
@@ -864,6 +905,8 @@ static void make_body(struct s_file *fp,
 
     for (i = v0; i < fp->vc; i++)
         v_add(fp->vv[i].p, fp->vv[i].p, p);
+
+    read_dict_entries = 0;
 }
 
 static void make_item(struct s_file *fp,
@@ -980,17 +1023,11 @@ static void make_goal(struct s_file *fp,
     zp->p[1] = 0.f;
     zp->p[2] = 0.f;
     zp->r    = 0.75;
-    zp->s    = 0;
-    zp->c    = 0;
 
     for (i = 0; i < c; i++)
     {
         if (strcmp(k[i], "radius") == 0)
             sscanf(v[i], "%f", &zp->r);
-        if (strcmp(k[i], "skip") == 0)
-            sscanf(v[i], "%d", &zp->s);
-        if (strcmp(k[i], "special") == 0)
-            sscanf(v[i], "%d", &zp->c);
 
         if (strcmp(k[i], "origin") == 0)
         {
@@ -1106,7 +1143,10 @@ static void make_swch(struct s_file *fp,
             sscanf(v[i], "%f", &xp->t0);
 
         if (strcmp(k[i], "state") == 0)
-            xp->f = atoi(v[i]);
+        {
+            xp->f  = atoi(v[i]);
+            xp->f0 = atoi(v[i]);
+        }
 
         if (strcmp(k[i], "invisible") == 0)
             xp->i = atoi(v[i]);
@@ -1237,7 +1277,11 @@ static void read_ent(struct s_file *fp, FILE *fin)
     if (!strcmp(v[i], "info_player_deathmatch"))   make_goal(fp, k, v, c);
     if (!strcmp(v[i], "target_teleporter"))        make_jump(fp, k, v, c);
     if (!strcmp(v[i], "target_position"))          make_targ(fp, k, v, c);
-    if (!strcmp(v[i], "worldspawn"))               make_body(fp, k, v, c, l0);
+    if (!strcmp(v[i], "worldspawn"))
+    {
+        read_dict_entries = 1;
+        make_body(fp, k, v, c, l0);
+    }
     if (!strcmp(v[i], "func_train"))               make_body(fp, k, v, c, l0);
     if (!strcmp(v[i], "misc_model"))               make_body(fp, k, v, c, l0);
 }
@@ -1384,7 +1428,7 @@ static void clip_edge(struct s_file *fp,
 /*
  * Find all verts that lie on  the given side of the lump.  Sort these
  * verts to  have a counter-clockwise winding about  the plane normal.
- * Create geoms to tessalate the resulting convex polygon.
+ * Create geoms to tessellate the resulting convex polygon.
  */
 static void clip_geom(struct s_file *fp,
                       struct s_lump *lp, int si)
@@ -1463,7 +1507,7 @@ static void clip_geom(struct s_file *fp,
 }
 
 /*
- * Iterate the sides of the lump, attemping to generate a new vert for
+ * Iterate the sides of the lump, attempting to generate a new vert for
  * each trio of planes, a new edge  for each pair of planes, and a new
  * set of geom for each visible plane.
  */
@@ -1517,7 +1561,7 @@ static void clip_file(struct s_file *fp)
 /*
  * For each body element type,  determine if element 'p' is equivalent
  * to element  'q'.  This  is more than  a simple memory  compare.  It
- * effectively  snaps mtrls and  verts togather,  and may  reverse the
+ * effectively  snaps mtrls and  verts together,  and may  reverse the
  * winding of  an edge or a geom.   This is done in  order to maximize
  * the number of elements that can be eliminated.
  */
@@ -1867,14 +1911,169 @@ static void uniq_side(struct s_file *fp)
 
 static void uniq_file(struct s_file *fp)
 {
-    uniq_mtrl(fp);
-    uniq_vert(fp);
-    uniq_edge(fp);
-    uniq_side(fp);
-    uniq_texc(fp);
-    uniq_geom(fp);
+    /* Debug mode skips optimization, producing oversized output files. */
+
+    if (debug_output == 0)
+    {
+        uniq_mtrl(fp);
+        uniq_vert(fp);
+        uniq_edge(fp);
+        uniq_side(fp);
+        uniq_texc(fp);
+        uniq_geom(fp);
+    }
+}
+
+/*---------------------------------------------------------------------------*/
+
+struct s_trip
+{
+    int vi;
+    int mi;
+    int si;
+    int gi;
+};
+
+static int comp_trip(const void *p, const void *q)
+{
+    const struct s_trip *tp = (const struct s_trip *) p;
+    const struct s_trip *tq = (const struct s_trip *) q;
+
+    if (tp->vi < tq->vi) return -1;
+    if (tp->vi > tq->vi) return +1;
+    if (tp->mi < tq->mi) return -1;
+    if (tp->mi > tq->mi) return +1;
+
+    return 0;
 }
 
+static void smth_file(struct s_file *fp)
+{
+    struct s_trip temp, *T;
+    
+    if (debug_output == 0)
+    {
+        if ((T = (struct s_trip *) malloc(fp->gc * 3 * sizeof (struct s_trip))))
+        {
+            int gi, i, j, k, l, c = 0;
+
+            /* Create a list of all non-faceted vertex triplets. */
+
+            for (gi = 0; gi < fp->gc; ++gi)
+            {
+                struct s_geom *gp = fp->gv + gi;
+
+                T[c].vi = gp->vi;
+                T[c].mi = gp->mi;
+                T[c].si = gp->si;
+                T[c].gi = gi;
+                c++;
+
+                T[c].vi = gp->vj;
+                T[c].mi = gp->mi;
+                T[c].si = gp->sj;
+                T[c].gi = gi;
+                c++;
+
+                T[c].vi = gp->vk;
+                T[c].mi = gp->mi;
+                T[c].si = gp->sk;
+                T[c].gi = gi;
+                c++;
+            }
+
+            /* Sort all triplets by vertex index and material. */
+
+            qsort(T, c, sizeof (struct s_trip), comp_trip);
+
+            /* For each set of triplets sharing vertex index and material... */
+
+            for (i = 0; i < c; i = l)
+            {
+                int acc = 0;
+
+                float N[3], angle = fp->mv[T[i].mi].angle;
+                const float   *Ni = fp->sv[T[i].si].n;
+
+                /* Sort the set by side similarity to the first. */
+
+                for (j = i + 1; j < c && (T[j].vi == T[i].vi &&
+                                          T[j].mi == T[i].mi); ++j)
+                {
+                    for (k = j + 1; k < c && (T[k].vi == T[i].vi &&
+                                              T[k].mi == T[i].mi); ++k)
+                    {
+                        const float *Nj = fp->sv[T[j].si].n;
+                        const float *Nk = fp->sv[T[k].si].n;
+
+                        if (T[j].si != T[k].si && v_dot(Nk, Ni) > v_dot(Nj, Ni))
+                        {
+                            temp = T[k];
+                            T[k] = T[j];
+                            T[j] = temp;
+                        }
+                    }
+                }
+
+                /* Accumulate all similar side normals. */
+
+                N[0] = Ni[0];
+                N[1] = Ni[1];
+                N[2] = Ni[2];
+
+                for (l = i + 1; l < c && (T[l].vi == T[i].vi &&
+                                          T[l].mi == T[i].mi); ++l)
+                    if (T[l].si != T[i].si)
+                    {
+                        const float *Nl = fp->sv[T[l].si].n;
+
+                        if (V_DEG(facosf(v_dot(Ni, Nl))) > angle)
+                            break;
+
+                        N[0] += Nl[0];
+                        N[1] += Nl[1];
+                        N[2] += Nl[2];
+
+                        acc++;
+                    }
+
+                /* If at least two normals have been accumulated... */
+
+                if (acc)
+                {
+                    /* Store the accumulated normal as a new side. */
+
+                    int ss = incs(fp);
+
+                    v_nrm(fp->sv[ss].n, N);
+                    fp->sv[ss].d = 0.0f;
+
+                    /* Assign the new normal to the merged triplets. */
+
+                    for (j = i; j < l; ++j)
+                        T[j].si = ss;
+                }
+            }
+
+            /* Assign the remapped normals to the original geoms. */
+
+            for (i = 0; i < c; ++i)
+            {
+                struct s_geom *gp = fp->gv + T[i].gi;
+
+                if (gp->vi == T[i].vi) gp->si = T[i].si;
+                if (gp->vj == T[i].vi) gp->sj = T[i].si;
+                if (gp->vk == T[i].vi) gp->sk = T[i].si;
+            }
+
+            free(T);
+        }
+
+        uniq_side(fp);
+    }
+}
+
+
 /*---------------------------------------------------------------------------*/
 
 static void sort_file(struct s_file *fp)
@@ -2001,11 +2200,26 @@ static int node_node(struct s_file *fp, int l0, int lc)
         /* Flag each lump with its position WRT the side. */
 
         for (li = 0; li < lc; li++)
-            switch (test_lump_side(fp, fp->lv + l0 + li, fp->sv + sj))
+            if (debug_output)
+            {
+                fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
+            }
+            else
             {
-            case +1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10; break;
-            case  0: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20; break;
-            case -1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40; break;
+                switch (test_lump_side(fp, fp->lv + l0 + li, fp->sv + sj))
+                {
+                case +1:
+                    fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10;
+                    break;
+
+                case  0:
+                    fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
+                    break;
+
+                case -1:
+                    fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40;
+                    break;
+                }
             }
 
         /* Sort all lumps in the range by their flag values. */
@@ -2110,8 +2324,8 @@ static void dump_file(struct s_file *p, const char *name)
            "  geom  lump  path  node  body\n"
            "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
            "  item  goal  view  jump  swch"
-           "  bill  ball  char  indx\n"
-           "%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
+           "  bill  ball  char  dict  indx\n"
+           "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
 #if 0
            name, n, m, c,
 #endif
@@ -2119,14 +2333,9 @@ static void dump_file(struct s_file *p, const char *name)
            p->mc, p->vc, p->ec, p->sc, p->tc,
            p->gc, p->lc, p->pc, p->nc, p->bc,
            p->hc, p->zc, p->wc, p->jc, p->xc,
-           p->rc, p->uc, p->ac, p->ic);
+           p->rc, p->uc, p->ac, p->dc, p->ic);
 }
 
-/* Skip the ugly SDL main substitution since we only need sdl_image. */
-#ifdef main
-#    undef main
-#endif
-
 int main(int argc, char *argv[])
 {
     char src[MAXSTR];
@@ -2136,6 +2345,9 @@ int main(int argc, char *argv[])
 
     if (argc > 2)
     {
+        if (argc > 3 && strcmp(argv[3], "--debug") == 0)
+            debug_output = 1;
+
         if (config_data_path(argv[2], NULL))
         {
             strncpy(src,  argv[1], MAXSTR);
@@ -2157,6 +2369,7 @@ int main(int argc, char *argv[])
                 clip_file(&f);
                 move_file(&f);
                 uniq_file(&f);
+                smth_file(&f);
                 sort_file(&f);
                 node_file(&f);
                 dump_file(&f, dst);
@@ -2170,7 +2383,7 @@ int main(int argc, char *argv[])
         }
         else fprintf(stderr, "Failure to establish data directory\n");
     }
-    else fprintf(stderr, "Usage: %s <map> [data]\n", argv[0]);
+    else fprintf(stderr, "Usage: %s <map> <data> [--debug]\n", argv[0]);
 
     return 0;
 }