2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
15 /*---------------------------------------------------------------------------*/
24 #include "base_image.h"
25 #include "base_config.h"
33 * The overall design of this map converter is very stupid, but very
34 * simple. It begins by assuming that every mtrl, vert, edge, side,
35 * and texc in the map is unique. It then makes an optimizing pass
36 * that discards redundant information. The result is optimal, though
37 * the process is terribly inefficient.
40 /*---------------------------------------------------------------------------*/
42 static int debug_output = 0;
44 /*---------------------------------------------------------------------------*/
46 /* Ohhhh... arbitrary! */
69 static int overflow(const char *s)
71 printf("%s overflow\n", s);
76 static int incm(struct s_file *fp)
78 return (fp->mc < MAXM) ? fp->mc++ : overflow("mtrl");
81 static int incv(struct s_file *fp)
83 return (fp->vc < MAXV) ? fp->vc++ : overflow("vert");
86 static int ince(struct s_file *fp)
88 return (fp->ec < MAXE) ? fp->ec++ : overflow("edge");
91 static int incs(struct s_file *fp)
93 return (fp->sc < MAXS) ? fp->sc++ : overflow("side");
96 static int inct(struct s_file *fp)
98 return (fp->tc < MAXT) ? fp->tc++ : overflow("texc");
101 static int incg(struct s_file *fp)
103 return (fp->gc < MAXG) ? fp->gc++ : overflow("geom");
106 static int incl(struct s_file *fp)
108 return (fp->lc < MAXL) ? fp->lc++ : overflow("lump");
111 static int incn(struct s_file *fp)
113 return (fp->nc < MAXN) ? fp->nc++ : overflow("node");
116 static int incp(struct s_file *fp)
118 return (fp->pc < MAXP) ? fp->pc++ : overflow("path");
121 static int incb(struct s_file *fp)
123 return (fp->bc < MAXB) ? fp->bc++ : overflow("body");
126 static int inch(struct s_file *fp)
128 return (fp->hc < MAXH) ? fp->hc++ : overflow("item");
131 static int incz(struct s_file *fp)
133 return (fp->zc < MAXZ) ? fp->zc++ : overflow("goal");
136 static int incj(struct s_file *fp)
138 return (fp->jc < MAXJ) ? fp->jc++ : overflow("jump");
141 static int incx(struct s_file *fp)
143 return (fp->xc < MAXX) ? fp->xc++ : overflow("swch");
146 static int incr(struct s_file *fp)
148 return (fp->rc < MAXR) ? fp->rc++ : overflow("bill");
151 static int incu(struct s_file *fp)
153 return (fp->uc < MAXU) ? fp->uc++ : overflow("ball");
156 static int incw(struct s_file *fp)
158 return (fp->wc < MAXW) ? fp->wc++ : overflow("view");
161 static int incd(struct s_file *fp)
163 return (fp->dc < MAXD) ? fp->dc++ : overflow("dict");
166 static int inci(struct s_file *fp)
168 return (fp->ic < MAXI) ? fp->ic++ : overflow("indx");
171 static void init_file(struct s_file *fp)
194 fp->mv = (struct s_mtrl *) calloc(MAXM, sizeof (struct s_mtrl));
195 fp->vv = (struct s_vert *) calloc(MAXV, sizeof (struct s_vert));
196 fp->ev = (struct s_edge *) calloc(MAXE, sizeof (struct s_edge));
197 fp->sv = (struct s_side *) calloc(MAXS, sizeof (struct s_side));
198 fp->tv = (struct s_texc *) calloc(MAXT, sizeof (struct s_texc));
199 fp->gv = (struct s_geom *) calloc(MAXG, sizeof (struct s_geom));
200 fp->lv = (struct s_lump *) calloc(MAXL, sizeof (struct s_lump));
201 fp->nv = (struct s_node *) calloc(MAXN, sizeof (struct s_node));
202 fp->pv = (struct s_path *) calloc(MAXP, sizeof (struct s_path));
203 fp->bv = (struct s_body *) calloc(MAXB, sizeof (struct s_body));
204 fp->hv = (struct s_item *) calloc(MAXH, sizeof (struct s_item));
205 fp->zv = (struct s_goal *) calloc(MAXZ, sizeof (struct s_goal));
206 fp->jv = (struct s_jump *) calloc(MAXJ, sizeof (struct s_jump));
207 fp->xv = (struct s_swch *) calloc(MAXX, sizeof (struct s_swch));
208 fp->rv = (struct s_bill *) calloc(MAXR, sizeof (struct s_bill));
209 fp->uv = (struct s_ball *) calloc(MAXU, sizeof (struct s_ball));
210 fp->wv = (struct s_view *) calloc(MAXW, sizeof (struct s_view));
211 fp->dv = (struct s_dict *) calloc(MAXD, sizeof (struct s_dict));
212 fp->av = (char *) calloc(MAXA, sizeof (char));
213 fp->iv = (int *) calloc(MAXI, sizeof (int));
216 /*---------------------------------------------------------------------------*/
219 * The following is a small symbol table data structure. Symbols and
220 * their integer values are collected in symv and valv. References
221 * and pointers to their unsatisfied integer values are collected in
222 * refv and pntv. The resolve procedure matches references to symbols
223 * and fills waiting ints with the proper values.
228 static char symv[MAXSYM][MAXSTR];
229 static int valv[MAXSYM];
231 static char refv[MAXSYM][MAXSTR];
232 static int *pntv[MAXSYM];
237 static void make_sym(const char *s, int v)
239 strncpy(symv[strc], s, MAXSTR - 1);
244 static void make_ref(const char *r, int *p)
246 strncpy(refv[refc], r, MAXSTR - 1);
251 static void resolve(void)
255 for (i = 0; i < refc; i++)
256 for (j = 0; j < strc; j++)
257 if (strncmp(refv[i], symv[j], MAXSTR) == 0)
259 *(pntv[i]) = valv[j];
264 /*---------------------------------------------------------------------------*/
267 * The following globals are used to cache target_positions. They are
268 * targeted by various entities and must be resolved in a second pass.
271 static float targ_p [MAXW][3];
272 static int targ_wi[MAXW];
273 static int targ_ji[MAXW];
276 static void targets(struct s_file *fp)
280 for (i = 0; i < fp->wc; i++)
281 v_cpy(fp->wv[i].q, targ_p[targ_wi[i]]);
283 for (i = 0; i < fp->jc; i++)
284 v_cpy(fp->jv[i].q, targ_p[targ_ji[i]]);
287 /*---------------------------------------------------------------------------*/
290 * The following code caches image sizes. Textures are referenced by
291 * name, but their sizes are necessary when computing texture
292 * coordinates. This code allows each file to be accessed only once
293 * regardless of the number of surfaces referring to it.
302 static struct _imagedata *imagedata = NULL;
303 static int image_n = 0;
304 static int image_alloc = 0;
306 #define IMAGE_REALLOC 32
308 static void free_imagedata()
314 for (i = 0; i < image_n; i++)
315 free(imagedata[i].s);
319 image_n = image_alloc = 0;
322 static int size_load(const char *file, int *w, int *h)
326 if ((p = image_load(file, w, h, NULL)))
334 static void size_image(const char *name, int *w, int *h)
341 for (i = 0; i < image_n; i++)
342 if (strncmp(imagedata[i].s, name, MAXSTR) == 0)
353 strcpy(jpg, name); strcat(jpg, ".jpg");
354 strcpy(png, name); strcat(png, ".png");
356 if (size_load(config_data(png), w, h) ||
357 size_load(config_data(jpg), w, h))
360 if (image_n + 1 >= image_alloc)
362 struct _imagedata *tmp =
363 (struct _imagedata *) malloc(sizeof(struct _imagedata) * (image_alloc + IMAGE_REALLOC));
366 printf("malloc error\n");
371 (void) memcpy(tmp, imagedata, sizeof(struct _imagedata) * image_alloc);
375 image_alloc += IMAGE_REALLOC;
378 imagedata[image_n].s = (char *) calloc(strlen(name) + 1, 1);
379 imagedata[image_n].w = *w;
380 imagedata[image_n].h = *h;
381 strcpy(imagedata[image_n].s, name);
387 /*---------------------------------------------------------------------------*/
389 /* Read the given material file, adding a new material to the solid. */
391 static int read_mtrl(struct s_file *fp, const char *name)
397 for (mi = 0; mi < fp->mc; mi++)
398 if (strncmp(name, fp->mv[mi].f, MAXSTR) == 0)
401 mp = fp->mv + incm(fp);
403 strncpy(mp->f, name, PATHMAX - 1);
405 mp->a[0] = mp->a[1] = mp->a[2] = 0.2f;
406 mp->d[0] = mp->d[1] = mp->d[2] = 0.8f;
407 mp->s[0] = mp->s[1] = mp->s[2] = 0.0f;
408 mp->e[0] = mp->e[1] = mp->e[2] = 0.0f;
409 mp->a[3] = mp->d[3] = mp->s[3] = mp->e[3] = 1.0f;
414 if ((fin = fopen(config_data(name), "r")))
422 mp->d, mp->d + 1, mp->d + 2, mp->d + 3,
423 mp->a, mp->a + 1, mp->a + 2, mp->a + 3,
424 mp->s, mp->s + 1, mp->s + 2, mp->s + 3,
425 mp->e, mp->e + 1, mp->e + 2, mp->e + 3,
426 mp->h, &mp->fl, &mp->angle);
433 /*---------------------------------------------------------------------------*/
436 * All bodies with an associated path are assumed to be positioned at
437 * the beginning of that path. These bodies must be moved to the
438 * origin in order for their path transforms to behave correctly.
439 * This is how we get away with defining func_trains with no origin
443 static void move_side(struct s_side *sp, const float p[3])
445 sp->d -= v_dot(sp->n, p);
448 static void move_vert(struct s_vert *vp, const float p[3])
450 v_sub(vp->p, vp->p, p);
453 static void move_lump(struct s_file *fp,
454 struct s_lump *lp, const float p[3])
458 for (i = 0; i < lp->sc; i++)
459 move_side(fp->sv + fp->iv[lp->s0 + i], p);
460 for (i = 0; i < lp->vc; i++)
461 move_vert(fp->vv + fp->iv[lp->v0 + i], p);
464 static void move_body(struct s_file *fp,
469 /* Move the lumps. */
471 for (i = 0; i < bp->lc; i++)
472 move_lump(fp, fp->lv + bp->l0 + i, fp->pv[bp->pi].p);
474 /* Create an array to mark any verts referenced by moved geoms. */
476 if (bp->gc > 0 && (b = (int *) calloc(fp->vc, sizeof (int))))
478 /* Mark the verts. */
480 for (i = 0; i < bp->gc; i++)
482 b[fp->gv[fp->iv[bp->g0 + i]].vi] = 1;
483 b[fp->gv[fp->iv[bp->g0 + i]].vj] = 1;
484 b[fp->gv[fp->iv[bp->g0 + i]].vk] = 1;
487 /* Apply the motion to the marked vertices. */
489 for (i = 0; i < fp->vc; ++i)
491 move_vert(fp->vv + i, fp->pv[bp->pi].p);
497 static void move_file(struct s_file *fp)
501 for (i = 0; i < fp->bc; i++)
502 if (fp->bv[i].pi >= 0)
503 move_body(fp, fp->bv + i);
506 /*---------------------------------------------------------------------------*/
509 * This is a basic OBJ loader. It is by no means fully compliant with
510 * the OBJ specification, but it works well with the output of
511 * Wings3D. All faces must be triangles and all vertices must include
512 * normals and texture coordinates. Material names are taken to be
513 * references to Neverball materials, rather than MTL definitions.
516 static void read_vt(struct s_file *fp, const char *line)
518 struct s_texc *tp = fp->tv + inct(fp);
520 sscanf(line, "%f %f", tp->u, tp->u + 1);
523 static void read_vn(struct s_file *fp, const char *line)
525 struct s_side *sp = fp->sv + incs(fp);
527 sscanf(line, "%f %f %f", sp->n, sp->n + 1, sp->n + 2);
530 static void read_v(struct s_file *fp, const char *line)
532 struct s_vert *vp = fp->vv + incv(fp);
534 sscanf(line, "%f %f %f", vp->p, vp->p + 1, vp->p + 2);
537 static void read_f(struct s_file *fp, const char *line,
538 int v0, int t0, int s0, int mi)
540 struct s_geom *gp = fp->gv + incg(fp);
545 sscanf(line, "%d%c%d%c%d %d%c%d%c%d %d%c%d%c%d",
546 &gp->vi, &c1, &gp->ti, &c2, &gp->si,
547 &gp->vj, &c1, &gp->tj, &c2, &gp->sj,
548 &gp->vk, &c1, &gp->tk, &c2, &gp->sk);
563 static void read_obj(struct s_file *fp, const char *name, int mi)
573 if ((fin = fopen(config_data(name), "r")))
575 while (fgets(line, MAXSTR, fin))
577 if (strncmp(line, "usemtl", 6) == 0)
579 sscanf(line + 6, "%s", mtrl);
580 mi = read_mtrl(fp, mtrl);
583 else if (strncmp(line, "f", 1) == 0)
585 if (fp->mv[mi].d[3] > 0.0f)
586 read_f(fp, line + 1, v0, t0, s0, mi);
589 else if (strncmp(line, "vt", 2) == 0) read_vt(fp, line + 2);
590 else if (strncmp(line, "vn", 2) == 0) read_vn(fp, line + 2);
591 else if (strncmp(line, "v", 1) == 0) read_v (fp, line + 1);
597 /*---------------------------------------------------------------------------*/
599 static float plane_d[MAXS];
600 static float plane_n[MAXS][3];
601 static float plane_p[MAXS][3];
602 static float plane_u[MAXS][3];
603 static float plane_v[MAXS][3];
604 static int plane_f[MAXS];
605 static int plane_m[MAXS];
607 static void make_plane(int pi, float x0, float y0, float z0,
608 float x1, float y1, float z1,
609 float x2, float y2, float z2,
610 float tu, float tv, float r,
611 float su, float sv, int fl, const char *s)
613 static const float base[6][3][3] = {
614 {{ 0, 0, 1 }, { 1, 0, 0 }, { 0, 1, 0 }},
615 {{ 0, 0, -1 }, { 1, 0, 0 }, { 0, 1, 0 }},
616 {{ 1, 0, 0 }, { 0, 0, -1 }, { 0, 1, 0 }},
617 {{ -1, 0, 0 }, { 0, 0, -1 }, { 0, 1, 0 }},
618 {{ 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, -1 }},
619 {{ 0, -1, 0 }, { 1, 0, 0 }, { 0, 0, -1 }},
623 float p0[3], p1[3], p2[3];
624 float u[3], v[3], p[3];
629 size_image(s, &w, &h);
631 plane_f[pi] = fl ? L_DETAIL : 0;
648 v_crs(plane_n[pi], u, v);
649 v_nrm(plane_n[pi], plane_n[pi]);
651 plane_d[pi] = v_dot(plane_n[pi], p1);
653 for (i = 0; i < 6; i++)
654 if ((k = v_dot(plane_n[pi], base[i][0])) >= d)
664 /* Always rotate around the positive axis */
666 m_rot(R, base[n - (n % 2)][0], V_RAD(r));
668 v_mad(p, p, base[n][1], +su * tu / SCALE);
669 v_mad(p, p, base[n][2], -sv * tv / SCALE);
671 m_vxfm(plane_u[pi], R, base[n][1]);
672 m_vxfm(plane_v[pi], R, base[n][2]);
673 m_vxfm(plane_p[pi], R, p);
675 v_scl(plane_u[pi], plane_u[pi], 64.f / w);
676 v_scl(plane_v[pi], plane_v[pi], 64.f / h);
678 v_scl(plane_u[pi], plane_u[pi], 1.f / su);
679 v_scl(plane_v[pi], plane_v[pi], 1.f / sv);
682 /*---------------------------------------------------------------------------*/
691 static int map_token(FILE *fin, int pi, char key[MAXSTR], char val[MAXSTR])
695 if (fgets(buf, MAXSTR, fin))
705 /* Scan the beginning or end of a block. */
707 if (buf[0] == '{') return T_BEG;
708 if (buf[0] == '}') return T_END;
710 /* Scan a key-value pair. */
714 strcpy(key, strtok(buf, "\""));
715 (void) strtok(NULL, "\"");
716 strcpy(val, strtok(NULL, "\""));
727 "%s %f %f %f %f %f %d",
728 &c, &x0, &y0, &z0, &c,
729 &c, &x1, &y1, &z1, &c,
730 &c, &x2, &y2, &z2, &c,
731 key, &tu, &tv, &r, &su, &sv, &fl) == 22)
733 make_plane(pi, x0, y0, z0,
736 tu, tv, r, su, sv, fl, key);
740 /* If it's not recognized, it must be uninteresting. */
747 /*---------------------------------------------------------------------------*/
749 /* Parse a lump from the given file and add it to the solid. */
751 static void read_lump(struct s_file *fp, FILE *fin)
757 struct s_lump *lp = fp->lv + incl(fp);
761 while ((t = map_token(fin, fp->sc, k, v)))
765 fp->sv[fp->sc].n[0] = plane_n[fp->sc][0];
766 fp->sv[fp->sc].n[1] = plane_n[fp->sc][1];
767 fp->sv[fp->sc].n[2] = plane_n[fp->sc][2];
768 fp->sv[fp->sc].d = plane_d[fp->sc];
770 plane_m[fp->sc] = read_mtrl(fp, k);
772 fp->iv[fp->ic] = fp->sc;
782 /*---------------------------------------------------------------------------*/
784 static void make_path(struct s_file *fp,
786 char v[][MAXSTR], int c)
788 int i, pi = incp(fp);
790 struct s_path *pp = fp->pv + pi;
800 for (i = 0; i < c; i++)
802 if (strcmp(k[i], "targetname") == 0)
805 if (strcmp(k[i], "target") == 0)
806 make_ref(v[i], &pp->pi);
808 if (strcmp(k[i], "state") == 0)
811 if (strcmp(k[i], "speed") == 0)
812 sscanf(v[i], "%f", &pp->t);
814 if (strcmp(k[i], "smooth") == 0)
817 if (strcmp(k[i], "origin") == 0)
819 float x = 0.f, y = 0.f, z = 0.f;
821 sscanf(v[i], "%f %f %f", &x, &y, &z);
823 pp->p[0] = +x / SCALE;
824 pp->p[1] = +z / SCALE;
825 pp->p[2] = -y / SCALE;
830 static void make_dict(struct s_file *fp,
834 int space_left, space_needed, di = incd(fp);
836 struct s_dict *dp = fp->dv + di;
838 space_left = MAXA - fp->ac;
839 space_needed = strlen(k) + 1 + strlen(v) + 1;
841 if (space_needed > space_left)
848 dp->aj = dp->ai + strlen(k) + 1;
849 fp->ac = dp->aj + strlen(v) + 1;
851 strncpy(fp->av + dp->ai, k, space_left);
852 strncpy(fp->av + dp->aj, v, space_left - strlen(k) - 1);
855 static int read_dict_entries = 0;
857 static void make_body(struct s_file *fp,
859 char v[][MAXSTR], int c, int l0)
861 int i, mi = 0, bi = incb(fp);
872 struct s_body *bp = fp->bv + bi;
878 for (i = 0; i < c; i++)
880 if (strcmp(k[i], "targetname") == 0)
883 else if (strcmp(k[i], "target") == 0)
884 make_ref(v[i], &bp->pi);
886 else if (strcmp(k[i], "material") == 0)
887 mi = read_mtrl(fp, v[i]);
889 else if (strcmp(k[i], "model") == 0)
890 read_obj(fp, v[i], mi);
892 else if (strcmp(k[i], "origin") == 0)
893 sscanf(v[i], "%f %f %f", &x, &y, &z);
895 else if (read_dict_entries && strcmp(k[i], "classname") != 0)
896 make_dict(fp, k[i], v[i]);
900 bp->lc = fp->lc - l0;
902 bp->gc = fp->gc - g0;
904 for (i = 0; i < bp->gc; i++)
905 fp->iv[inci(fp)] = g0++;
911 for (i = v0; i < fp->vc; i++)
912 v_add(fp->vv[i].p, fp->vv[i].p, p);
914 read_dict_entries = 0;
917 static void make_item(struct s_file *fp,
919 char v[][MAXSTR], int c)
921 int i, hi = inch(fp);
923 struct s_item *hp = fp->hv + hi;
932 for (i = 0; i < c; i++)
934 if (strcmp(k[i], "classname") == 0)
936 if (strcmp(v[i], "light") == 0)
938 else if (strcmp(v[i], "item_health_large") == 0)
940 else if (strcmp(v[i], "item_health_small") == 0)
944 if (strcmp(k[i], "light") == 0)
945 sscanf(v[i], "%d", &hp->n);
947 if (strcmp(k[i], "origin") == 0)
949 float x = 0.f, y = 0.f, z = 0.f;
951 sscanf(v[i], "%f %f %f", &x, &y, &z);
953 hp->p[0] = +x / SCALE;
954 hp->p[1] = +z / SCALE;
955 hp->p[2] = -y / SCALE;
960 static void make_bill(struct s_file *fp,
962 char v[][MAXSTR], int c)
964 int i, ri = incr(fp);
966 struct s_bill *rp = fp->rv + ri;
968 memset(rp, 0, sizeof (struct s_bill));
971 for (i = 0; i < c; i++)
973 if (strcmp(k[i], "width") == 0)
974 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
975 if (strcmp(k[i], "height") == 0)
976 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
978 if (strcmp(k[i], "xrot") == 0)
979 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
980 if (strcmp(k[i], "yrot") == 0)
981 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
982 if (strcmp(k[i], "zrot") == 0)
983 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
985 if (strcmp(k[i], "time") == 0)
986 sscanf(v[i], "%f", &rp->t);
987 if (strcmp(k[i], "dist") == 0)
988 sscanf(v[i], "%f", &rp->d);
989 if (strcmp(k[i], "flag") == 0)
990 sscanf(v[i], "%d", &rp->fl);
992 if (strcmp(k[i], "image") == 0)
994 rp->mi = read_mtrl(fp, v[i]);
995 fp->mv[rp->mi].fl |= M_CLAMPED;
998 if (strcmp(k[i], "origin") == 0)
1000 float x = 0.f, y = 0.f, z = 0.f;
1002 sscanf(v[i], "%f %f %f", &x, &y, &z);
1004 rp->p[0] = +x / SCALE;
1005 rp->p[1] = +z / SCALE;
1006 rp->p[2] = -y / SCALE;
1010 if (rp->fl & B_ADDITIVE)
1011 fp->mv[rp->mi].fl |= M_ADDITIVE;
1014 static void make_goal(struct s_file *fp,
1016 char v[][MAXSTR], int c)
1018 int i, zi = incz(fp);
1020 struct s_goal *zp = fp->zv + zi;
1027 for (i = 0; i < c; i++)
1029 if (strcmp(k[i], "radius") == 0)
1030 sscanf(v[i], "%f", &zp->r);
1032 if (strcmp(k[i], "origin") == 0)
1034 float x = 0.f, y = 0.f, z = 0.f;
1036 sscanf(v[i], "%f %f %f", &x, &y, &z);
1038 zp->p[0] = +(x) / SCALE;
1039 zp->p[1] = +(z - 24) / SCALE;
1040 zp->p[2] = -(y) / SCALE;
1045 static void make_view(struct s_file *fp,
1047 char v[][MAXSTR], int c)
1049 int i, wi = incw(fp);
1051 struct s_view *wp = fp->wv + wi;
1060 for (i = 0; i < c; i++)
1062 if (strcmp(k[i], "target") == 0)
1063 make_ref(v[i], targ_wi + wi);
1065 if (strcmp(k[i], "origin") == 0)
1067 float x = 0.f, y = 0.f, z = 0.f;
1069 sscanf(v[i], "%f %f %f", &x, &y, &z);
1071 wp->p[0] = +x / SCALE;
1072 wp->p[1] = +z / SCALE;
1073 wp->p[2] = -y / SCALE;
1078 static void make_jump(struct s_file *fp,
1080 char v[][MAXSTR], int c)
1082 int i, ji = incj(fp);
1084 struct s_jump *jp = fp->jv + ji;
1094 for (i = 0; i < c; i++)
1096 if (strcmp(k[i], "radius") == 0)
1097 sscanf(v[i], "%f", &jp->r);
1099 if (strcmp(k[i], "target") == 0)
1100 make_ref(v[i], targ_ji + ji);
1102 if (strcmp(k[i], "origin") == 0)
1104 float x = 0.f, y = 0.f, z = 0.f;
1106 sscanf(v[i], "%f %f %f", &x, &y, &z);
1108 jp->p[0] = +x / SCALE;
1109 jp->p[1] = +z / SCALE;
1110 jp->p[2] = -y / SCALE;
1115 static void make_swch(struct s_file *fp,
1117 char v[][MAXSTR], int c)
1119 int i, xi = incx(fp);
1121 struct s_swch *xp = fp->xv + xi;
1134 for (i = 0; i < c; i++)
1136 if (strcmp(k[i], "radius") == 0)
1137 sscanf(v[i], "%f", &xp->r);
1139 if (strcmp(k[i], "target") == 0)
1140 make_ref(v[i], &xp->pi);
1142 if (strcmp(k[i], "timer") == 0)
1143 sscanf(v[i], "%f", &xp->t0);
1145 if (strcmp(k[i], "state") == 0)
1148 xp->f0 = atoi(v[i]);
1151 if (strcmp(k[i], "invisible") == 0)
1154 if (strcmp(k[i], "origin") == 0)
1156 float x = 0.f, y = 0.f, z = 0.f;
1158 sscanf(v[i], "%f %f %f", &x, &y, &z);
1160 xp->p[0] = +x / SCALE;
1161 xp->p[1] = +z / SCALE;
1162 xp->p[2] = -y / SCALE;
1167 static void make_targ(struct s_file *fp,
1169 char v[][MAXSTR], int c)
1173 targ_p[targ_n][0] = 0.f;
1174 targ_p[targ_n][1] = 0.f;
1175 targ_p[targ_n][2] = 0.f;
1177 for (i = 0; i < c; i++)
1179 if (strcmp(k[i], "targetname") == 0)
1180 make_sym(v[i], targ_n);
1182 if (strcmp(k[i], "origin") == 0)
1184 float x = 0.f, y = 0.f, z = 0.f;
1186 sscanf(v[i], "%f %f %f", &x, &y, &z);
1188 targ_p[targ_n][0] = +x / SCALE;
1189 targ_p[targ_n][1] = +z / SCALE;
1190 targ_p[targ_n][2] = -y / SCALE;
1197 static void make_ball(struct s_file *fp,
1199 char v[][MAXSTR], int c)
1201 int i, ui = incu(fp);
1203 struct s_ball *up = fp->uv + ui;
1210 for (i = 0; i < c; i++)
1212 if (strcmp(k[i], "radius") == 0)
1213 sscanf(v[i], "%f", &up->r);
1215 if (strcmp(k[i], "origin") == 0)
1217 float x = 0.f, y = 0.f, z = 0.f;
1219 sscanf(v[i], "%f %f %f", &x, &y, &z);
1221 up->p[0] = +(x) / SCALE;
1222 up->p[1] = +(z - 24) / SCALE;
1223 up->p[2] = -(y) / SCALE;
1227 up->p[1] += up->r + SMALL;
1230 /*---------------------------------------------------------------------------*/
1232 static void read_ent(struct s_file *fp, FILE *fin)
1234 char k[MAXKEY][MAXSTR];
1235 char v[MAXKEY][MAXSTR];
1236 int t, i = 0, c = 0;
1240 while ((t = map_token(fin, -1, k[c], v[c])))
1244 if (strcmp(k[c], "classname") == 0)
1248 if (t == T_BEG) read_lump(fp, fin);
1249 if (t == T_END) break;
1252 if (!strcmp(v[i], "light")) make_item(fp, k, v, c);
1253 if (!strcmp(v[i], "item_health_large")) make_item(fp, k, v, c);
1254 if (!strcmp(v[i], "item_health_small")) make_item(fp, k, v, c);
1255 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1256 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1257 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1258 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1259 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1260 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1261 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1262 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1263 if (!strcmp(v[i], "worldspawn"))
1265 read_dict_entries = 1;
1266 make_body(fp, k, v, c, l0);
1268 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1269 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1272 static void read_map(struct s_file *fp, FILE *fin)
1278 while ((t = map_token(fin, -1, k, v)))
1283 /*---------------------------------------------------------------------------*/
1285 /* Test the location of a point with respect to a side plane. */
1287 static int fore_side(const float p[3], const struct s_side *sp)
1289 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1292 static int on_side(const float p[3], const struct s_side *sp)
1294 float d = v_dot(p, sp->n) - sp->d;
1296 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1299 /*---------------------------------------------------------------------------*/
1301 * Confirm that the addition of a vert would not result in degenerate
1305 static int ok_vert(const struct s_file *fp,
1306 const struct s_lump *lp, const float p[3])
1311 for (i = 0; i < lp->vc; i++)
1313 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1317 if (v_len(r) < SMALL)
1323 /*---------------------------------------------------------------------------*/
1326 * The following functions take the set of planes defining a lump and
1327 * find the verts, edges, and geoms that describe its boundaries. To
1328 * do this, they first find the verts, and then search these verts for
1329 * valid edges and geoms. It may be more efficient to compute edges
1330 * and geoms directly by clipping down infinite line segments and
1331 * planes, but this would be more complex and prone to numerical
1336 * Given 3 side planes, compute the point of intersection, if any.
1337 * Confirm that this point falls within the current lump, and that it
1338 * is unique. Add it as a vert of the solid.
1340 static void clip_vert(struct s_file *fp,
1341 struct s_lump *lp, int si, int sj, int sk)
1343 float M[16], X[16], I[16];
1347 d[0] = fp->sv[si].d;
1348 d[1] = fp->sv[sj].d;
1349 d[2] = fp->sv[sk].d;
1351 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1358 for (i = 0; i < lp->sc; i++)
1360 int si = fp->iv[lp->s0 + i];
1362 if (fore_side(p, fp->sv + si))
1366 if (ok_vert(fp, lp, p))
1368 v_cpy(fp->vv[fp->vc].p, p);
1370 fp->iv[fp->ic] = fp->vc;
1379 * Given two side planes, find an edge along their intersection by
1380 * finding a pair of vertices that fall on both planes. Add it to the
1383 static void clip_edge(struct s_file *fp,
1384 struct s_lump *lp, int si, int sj)
1388 for (i = 1; i < lp->vc; i++)
1390 int vi = fp->iv[lp->v0 + i];
1392 if (!on_side(fp->vv[vi].p, fp->sv + si) ||
1393 !on_side(fp->vv[vi].p, fp->sv + sj))
1396 for (j = 0; j < i; j++)
1398 int vj = fp->iv[lp->v0 + j];
1400 if (on_side(fp->vv[vj].p, fp->sv + si) &&
1401 on_side(fp->vv[vj].p, fp->sv + sj))
1403 fp->ev[fp->ec].vi = vi;
1404 fp->ev[fp->ec].vj = vj;
1406 fp->iv[fp->ic] = fp->ec;
1417 * Find all verts that lie on the given side of the lump. Sort these
1418 * verts to have a counter-clockwise winding about the plane normal.
1419 * Create geoms to tessellate the resulting convex polygon.
1421 static void clip_geom(struct s_file *fp,
1422 struct s_lump *lp, int si)
1424 int m[256], t[256], d, i, j, n = 0;
1429 struct s_side *sp = fp->sv + si;
1433 for (i = 0; i < lp->vc; i++)
1435 int vi = fp->iv[lp->v0 + i];
1437 if (on_side(fp->vv[vi].p, sp))
1442 v_add(v, fp->vv[vi].p, plane_p[si]);
1444 fp->tv[t[n]].u[0] = v_dot(v, plane_u[si]);
1445 fp->tv[t[n]].u[1] = v_dot(v, plane_v[si]);
1453 for (i = 1; i < n; i++)
1454 for (j = i + 1; j < n; j++)
1456 v_sub(u, fp->vv[m[i]].p, fp->vv[m[0]].p);
1457 v_sub(v, fp->vv[m[j]].p, fp->vv[m[0]].p);
1460 if (v_dot(w, sp->n) < 0.f)
1474 for (i = 0; i < n - 2; i++)
1476 fp->gv[fp->gc].mi = plane_m[si];
1478 fp->gv[fp->gc].ti = t[0];
1479 fp->gv[fp->gc].tj = t[i + 1];
1480 fp->gv[fp->gc].tk = t[i + 2];
1482 fp->gv[fp->gc].si = si;
1483 fp->gv[fp->gc].sj = si;
1484 fp->gv[fp->gc].sk = si;
1486 fp->gv[fp->gc].vi = m[0];
1487 fp->gv[fp->gc].vj = m[i + 1];
1488 fp->gv[fp->gc].vk = m[i + 2];
1490 fp->iv[fp->ic] = fp->gc;
1498 * Iterate the sides of the lump, attempting to generate a new vert for
1499 * each trio of planes, a new edge for each pair of planes, and a new
1500 * set of geom for each visible plane.
1502 static void clip_lump(struct s_file *fp, struct s_lump *lp)
1509 for (i = 2; i < lp->sc; i++)
1510 for (j = 1; j < i; j++)
1511 for (k = 0; k < j; k++)
1515 fp->iv[lp->s0 + k]);
1520 for (i = 1; i < lp->sc; i++)
1521 for (j = 0; j < i; j++)
1524 fp->iv[lp->s0 + j]);
1529 for (i = 0; i < lp->sc; i++)
1530 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0.0f)
1532 fp->iv[lp->s0 + i]);
1534 for (i = 0; i < lp->sc; i++)
1535 if (plane_f[fp->iv[lp->s0 + i]])
1539 static void clip_file(struct s_file *fp)
1543 for (i = 0; i < fp->lc; i++)
1544 clip_lump(fp, fp->lv + i);
1547 /*---------------------------------------------------------------------------*/
1550 * For each body element type, determine if element 'p' is equivalent
1551 * to element 'q'. This is more than a simple memory compare. It
1552 * effectively snaps mtrls and verts together, and may reverse the
1553 * winding of an edge or a geom. This is done in order to maximize
1554 * the number of elements that can be eliminated.
1557 static int comp_mtrl(const struct s_mtrl *mp, const struct s_mtrl *mq)
1559 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1560 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1561 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1562 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1564 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1565 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1566 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1567 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1569 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1570 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1571 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1572 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1574 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1575 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1576 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1577 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1579 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1581 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1586 static int comp_vert(const struct s_vert *vp, const struct s_vert *vq)
1588 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1589 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1590 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1595 static int comp_edge(const struct s_edge *ep, const struct s_edge *eq)
1597 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1598 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1603 static int comp_side(const struct s_side *sp, const struct s_side *sq)
1605 if (fabs(sp->d - sq->d) > SMALL) return 0;
1606 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1611 static int comp_texc(const struct s_texc *tp, const struct s_texc *tq)
1613 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1614 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1619 static int comp_geom(const struct s_geom *gp, const struct s_geom *gq)
1621 if (gp->mi != gq->mi) return 0;
1623 if (gp->ti != gq->ti) return 0;
1624 if (gp->si != gq->si) return 0;
1625 if (gp->vi != gq->vi) return 0;
1627 if (gp->tj != gq->tj) return 0;
1628 if (gp->sj != gq->sj) return 0;
1629 if (gp->vj != gq->vj) return 0;
1631 if (gp->tk != gq->tk) return 0;
1632 if (gp->sk != gq->sk) return 0;
1633 if (gp->vk != gq->vk) return 0;
1638 /*---------------------------------------------------------------------------*/
1641 * For each file element type, replace all references to element 'i'
1642 * with a reference to element 'j'. These are used when optimizing
1643 * and sorting the file.
1646 static void swap_mtrl(struct s_file *fp, int mi, int mj)
1650 for (i = 0; i < fp->gc; i++)
1651 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1652 for (i = 0; i < fp->rc; i++)
1653 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1656 static int vert_swaps[MAXV];
1658 static void apply_vert_swaps(struct s_file *fp)
1662 for (i = 0; i < fp->ec; i++)
1664 fp->ev[i].vi = vert_swaps[fp->ev[i].vi];
1665 fp->ev[i].vj = vert_swaps[fp->ev[i].vj];
1668 for (i = 0; i < fp->gc; i++)
1670 fp->gv[i].vi = vert_swaps[fp->gv[i].vi];
1671 fp->gv[i].vj = vert_swaps[fp->gv[i].vj];
1672 fp->gv[i].vk = vert_swaps[fp->gv[i].vk];
1675 for (i = 0; i < fp->lc; i++)
1676 for (j = 0; j < fp->lv[i].vc; j++)
1677 fp->iv[fp->lv[i].v0 + j] = vert_swaps[fp->iv[fp->lv[i].v0 + j]];
1680 static void swap_vert(struct s_file *fp, int vi, int vj)
1684 for (i = 0; i < fp->ec; i++)
1686 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1687 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1690 for (i = 0; i < fp->gc; i++)
1692 if (fp->gv[i].vi == vi) fp->gv[i].vi = vj;
1693 if (fp->gv[i].vj == vi) fp->gv[i].vj = vj;
1694 if (fp->gv[i].vk == vi) fp->gv[i].vk = vj;
1697 for (i = 0; i < fp->lc; i++)
1698 for (j = 0; j < fp->lv[i].vc; j++)
1699 if (fp->iv[fp->lv[i].v0 + j] == vi)
1700 fp->iv[fp->lv[i].v0 + j] = vj;
1703 static int edge_swaps[MAXE];
1705 static void apply_edge_swaps(struct s_file *fp)
1709 for (i = 0; i < fp->lc; i++)
1710 for (j = 0; j < fp->lv[i].ec; j++)
1711 fp->iv[fp->lv[i].e0 + j] = edge_swaps[fp->iv[fp->lv[i].e0 + j]];
1714 static int side_swaps[MAXS];
1716 static void apply_side_swaps(struct s_file *fp)
1720 for (i = 0; i < fp->gc; i++)
1722 fp->gv[i].si = side_swaps[fp->gv[i].si];
1723 fp->gv[i].sj = side_swaps[fp->gv[i].sj];
1724 fp->gv[i].sk = side_swaps[fp->gv[i].sk];
1726 for (i = 0; i < fp->nc; i++)
1727 fp->nv[i].si = side_swaps[fp->nv[i].si];
1729 for (i = 0; i < fp->lc; i++)
1730 for (j = 0; j < fp->lv[i].sc; j++)
1731 fp->iv[fp->lv[i].s0 + j] = side_swaps[fp->iv[fp->lv[i].s0 + j]];
1734 static int texc_swaps[MAXT];
1736 static void apply_texc_swaps(struct s_file *fp)
1740 for (i = 0; i < fp->gc; i++)
1742 fp->gv[i].ti = texc_swaps[fp->gv[i].ti];
1743 fp->gv[i].tj = texc_swaps[fp->gv[i].tj];
1744 fp->gv[i].tk = texc_swaps[fp->gv[i].tk];
1748 static int geom_swaps[MAXG];
1750 static void apply_geom_swaps(struct s_file *fp)
1754 for (i = 0; i < fp->lc; i++)
1755 for (j = 0; j < fp->lv[i].gc; j++)
1756 fp->iv[fp->lv[i].g0 + j] = geom_swaps[fp->iv[fp->lv[i].g0 + j]];
1758 for (i = 0; i < fp->bc; i++)
1759 for (j = 0; j < fp->bv[i].gc; j++)
1760 fp->iv[fp->bv[i].g0 + j] = geom_swaps[fp->iv[fp->bv[i].g0 + j]];
1763 /*---------------------------------------------------------------------------*/
1765 static void uniq_mtrl(struct s_file *fp)
1769 for (i = 0; i < fp->mc; i++)
1771 for (j = 0; j < k; j++)
1772 if (comp_mtrl(fp->mv + i, fp->mv + j))
1774 swap_mtrl(fp, i, j);
1782 fp->mv[k] = fp->mv[i];
1783 swap_mtrl(fp, i, k);
1792 static void uniq_vert(struct s_file *fp)
1796 for (i = 0; i < fp->vc; i++)
1798 for (j = 0; j < k; j++)
1799 if (comp_vert(fp->vv + i, fp->vv + j))
1807 fp->vv[k] = fp->vv[i];
1812 apply_vert_swaps(fp);
1817 static void uniq_edge(struct s_file *fp)
1821 for (i = 0; i < fp->ec; i++)
1823 for (j = 0; j < k; j++)
1824 if (comp_edge(fp->ev + i, fp->ev + j))
1832 fp->ev[k] = fp->ev[i];
1837 apply_edge_swaps(fp);
1842 static int geomlist[MAXV];
1843 static int nextgeom[MAXG];
1845 static void uniq_geom(struct s_file *fp)
1849 for (i = 0; i < MAXV; i++)
1852 for (i = 0; i < fp->gc; i++)
1854 int key = fp->gv[i].vj;
1856 for (j = geomlist[key]; j != -1; j = nextgeom[j])
1857 if (comp_geom(fp->gv + i, fp->gv + j))
1860 fp->gv[k] = fp->gv[i];
1862 nextgeom[k] = geomlist[key];
1872 apply_geom_swaps(fp);
1877 static void uniq_texc(struct s_file *fp)
1881 for (i = 0; i < fp->tc; i++)
1883 for (j = 0; j < k; j++)
1884 if (comp_texc(fp->tv + i, fp->tv + j))
1892 fp->tv[k] = fp->tv[i];
1897 apply_texc_swaps(fp);
1902 static void uniq_side(struct s_file *fp)
1906 for (i = 0; i < fp->sc; i++)
1908 for (j = 0; j < k; j++)
1909 if (comp_side(fp->sv + i, fp->sv + j))
1917 fp->sv[k] = fp->sv[i];
1922 apply_side_swaps(fp);
1927 static void uniq_file(struct s_file *fp)
1929 /* Debug mode skips optimization, producing oversized output files. */
1931 if (debug_output == 0)
1942 /*---------------------------------------------------------------------------*/
1952 static int comp_trip(const void *p, const void *q)
1954 const struct s_trip *tp = (const struct s_trip *) p;
1955 const struct s_trip *tq = (const struct s_trip *) q;
1957 if (tp->vi < tq->vi) return -1;
1958 if (tp->vi > tq->vi) return +1;
1959 if (tp->mi < tq->mi) return -1;
1960 if (tp->mi > tq->mi) return +1;
1965 static void smth_file(struct s_file *fp)
1967 struct s_trip temp, *T;
1969 if (debug_output == 0)
1971 if ((T = (struct s_trip *) malloc(fp->gc * 3 * sizeof (struct s_trip))))
1973 int gi, i, j, k, l, c = 0;
1975 /* Create a list of all non-faceted vertex triplets. */
1977 for (gi = 0; gi < fp->gc; ++gi)
1979 struct s_geom *gp = fp->gv + gi;
2000 /* Sort all triplets by vertex index and material. */
2002 qsort(T, c, sizeof (struct s_trip), comp_trip);
2004 /* For each set of triplets sharing vertex index and material... */
2006 for (i = 0; i < c; i = l)
2010 float N[3], angle = fp->mv[T[i].mi].angle;
2011 const float *Ni = fp->sv[T[i].si].n;
2013 /* Sort the set by side similarity to the first. */
2015 for (j = i + 1; j < c && (T[j].vi == T[i].vi &&
2016 T[j].mi == T[i].mi); ++j)
2018 for (k = j + 1; k < c && (T[k].vi == T[i].vi &&
2019 T[k].mi == T[i].mi); ++k)
2021 const float *Nj = fp->sv[T[j].si].n;
2022 const float *Nk = fp->sv[T[k].si].n;
2024 if (T[j].si != T[k].si && v_dot(Nk, Ni) > v_dot(Nj, Ni))
2033 /* Accumulate all similar side normals. */
2039 for (l = i + 1; l < c && (T[l].vi == T[i].vi &&
2040 T[l].mi == T[i].mi); ++l)
2041 if (T[l].si != T[i].si)
2043 const float *Nl = fp->sv[T[l].si].n;
2045 if (V_DEG(facosf(v_dot(Ni, Nl))) > angle)
2055 /* If at least two normals have been accumulated... */
2059 /* Store the accumulated normal as a new side. */
2063 v_nrm(fp->sv[ss].n, N);
2064 fp->sv[ss].d = 0.0f;
2066 /* Assign the new normal to the merged triplets. */
2068 for (j = i; j < l; ++j)
2073 /* Assign the remapped normals to the original geoms. */
2075 for (i = 0; i < c; ++i)
2077 struct s_geom *gp = fp->gv + T[i].gi;
2079 if (gp->vi == T[i].vi) gp->si = T[i].si;
2080 if (gp->vj == T[i].vi) gp->sj = T[i].si;
2081 if (gp->vk == T[i].vi) gp->sk = T[i].si;
2092 /*---------------------------------------------------------------------------*/
2094 static void sort_file(struct s_file *fp)
2098 /* Sort billboards by material within distance. */
2100 for (i = 0; i < fp->rc; i++)
2101 for (j = i + 1; j < fp->rc; j++)
2102 if ((fp->rv[j].d > fp->rv[i].d) ||
2103 (fp->rv[j].d == fp->rv[i].d &&
2104 fp->rv[j].mi > fp->rv[i].mi))
2109 fp->rv[i] = fp->rv[j];
2113 /* Ensure the first vertex is the lowest. */
2115 for (i = 0; i < fp->vc; i++)
2116 if (fp->vv[0].p[1] > fp->vv[i].p[1])
2121 fp->vv[0] = fp->vv[i];
2124 swap_vert(fp, 0, -1);
2125 swap_vert(fp, i, 0);
2126 swap_vert(fp, -1, i);
2130 /*---------------------------------------------------------------------------*/
2132 static int test_lump_side(const struct s_file *fp,
2133 const struct s_lump *lp,
2134 const struct s_side *sp,
2148 /* Check if the bounding sphere of the lump is completely on one side. */
2150 d = v_dot(bsphere, sp->n) - sp->d;
2152 if (fabs(d) > bsphere[3])
2153 return d > 0 ? 1 : -1;
2155 /* If the given side is part of the given lump, then the lump is behind. */
2157 for (si = 0; si < lp->sc; si++)
2158 if (fp->sv + fp->iv[lp->s0 + si] == sp)
2161 /* Check if each lump vertex is in front of, behind, on the side. */
2163 for (vi = 0; vi < lp->vc; vi++)
2165 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
2171 /* If no verts are behind, the lump is in front, and vice versa. */
2173 if (f > 0 && b == 0) return +1;
2174 if (b > 0 && f == 0) return -1;
2176 /* Else, the lump crosses the side. */
2181 static int node_node(struct s_file *fp, int l0, int lc, float bsphere[][4])
2185 /* Base case. Dump all given lumps into a leaf node. */
2187 fp->nv[fp->nc].si = -1;
2188 fp->nv[fp->nc].ni = -1;
2189 fp->nv[fp->nc].nj = -1;
2190 fp->nv[fp->nc].l0 = l0;
2191 fp->nv[fp->nc].lc = lc;
2201 int li = 0, lic = 0;
2202 int lj = 0, ljc = 0;
2203 int lk = 0, lkc = 0;
2206 /* Find the side that most evenly splits the given lumps. */
2208 for (si = 0; si < fp->sc; si++)
2214 for (li = 0; li < lc; li++)
2215 if ((k = test_lump_side(fp,
2225 if ((d < sjd) || (d == sjd && o < sjo))
2233 /* Flag each lump with its position WRT the side. */
2235 for (li = 0; li < lc; li++)
2238 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2242 switch (test_lump_side(fp,
2248 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10;
2252 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2256 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40;
2261 /* Sort all lumps in the range by their flag values. */
2263 for (li = 1; li < lc; li++)
2264 for (lj = 0; lj < li; lj++)
2265 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
2271 for (i = 0; i < 4; i++)
2273 f = bsphere[l0 + li][i];
2274 bsphere[l0 + li][i] = bsphere[l0 + lj][i];
2275 bsphere[l0 + lj][i] = f;
2278 l = fp->lv[l0 + li];
2279 fp->lv[l0 + li] = fp->lv[l0 + lj];
2280 fp->lv[l0 + lj] = l;
2283 /* Establish the in-front, on, and behind lump ranges. */
2289 for (i = lc - 1; i >= 0; i--)
2290 switch (fp->lv[l0 + i].fl & 0xf0)
2292 case 0x10: li = l0 + i; lic++; break;
2293 case 0x20: lj = l0 + i; ljc++; break;
2294 case 0x40: lk = l0 + i; lkc++; break;
2297 /* Add the lumps on the side to the node. */
2302 fp->nv[i].ni = node_node(fp, li, lic, bsphere);
2304 fp->nv[i].nj = node_node(fp, lk, lkc, bsphere);
2313 * Compute a bounding sphere for a lump (not optimal)
2315 static void lump_bounding_sphere(struct s_file *fp,
2326 bbox[0] = bbox[3] = fp->vv[fp->iv[lp->v0]].p[0];
2327 bbox[1] = bbox[4] = fp->vv[fp->iv[lp->v0]].p[1];
2328 bbox[2] = bbox[5] = fp->vv[fp->iv[lp->v0]].p[2];
2330 for (i = 1; i < lp->vc; i++)
2332 struct s_vert *vp = fp->vv + fp->iv[lp->v0 + i];
2335 for (j = 0; j < 3; j++)
2336 if (vp->p[j] < bbox[j])
2339 for (j = 0; j < 3; j++)
2340 if (vp->p[j] > bbox[j + 3])
2341 bbox[j + 3] = vp->p[j];
2346 for (i = 0; i < 3; i++)
2348 bsphere[i] = (bbox[i] + bbox[i + 3]) / 2;
2349 r += (bsphere[i] - bbox[i]) * (bsphere[i] - bbox[i]);
2352 bsphere[3] = fsqrtf(r);
2355 static void node_file(struct s_file *fp)
2357 float bsphere[MAXL][4];
2360 /* Compute a bounding sphere for each lump. */
2362 for (i = 0; i < fp->lc; i++)
2363 lump_bounding_sphere(fp, fp->lv + i, bsphere[i]);
2365 /* Sort the lumps of each body into BSP nodes. */
2367 for (i = 0; i < fp->bc; i++)
2368 fp->bv[i].ni = node_node(fp, fp->bv[i].l0, fp->bv[i].lc, bsphere);
2371 /*---------------------------------------------------------------------------*/
2373 static void dump_file(struct s_file *p, const char *name)
2375 /* FIXME: Count visible geoms.
2377 * I'm afraid items break this (not sure though) so leaving it out.
2387 int m = p->rc + p->cc * 128 + (p->zc * p->jc + p->xc) * 32;
2390 /* Count the number of solid lumps. */
2392 for (i = 0; i < p->lc; i++)
2393 if ((p->lv[i].fl & 1) == 0)
2397 /* Count the number of visible geoms. */
2399 for (i = 0; i < p->bc; i++)
2401 for (j = 0; j < p->bv[i].lc; j++)
2402 m += p->lv[p->bv[i].l0 + j].gc;
2407 /* Count the total value of all coins. */
2409 for (i = 0; i < p->hc; i++)
2410 if (p->hv[i].t == ITEM_COIN)
2414 printf("%s (%d/%d/$%d)\n"
2416 printf("%s (%d/$%d)\n"
2417 " mtrl vert edge side texc"
2418 " geom lump path node body\n"
2419 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2420 " item goal view jump swch"
2421 " bill ball char dict indx\n"
2422 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2427 p->mc, p->vc, p->ec, p->sc, p->tc,
2428 p->gc, p->lc, p->pc, p->nc, p->bc,
2429 p->hc, p->zc, p->wc, p->jc, p->xc,
2430 p->rc, p->uc, p->ac, p->dc, p->ic);
2433 int main(int argc, char *argv[])
2442 if (argc > 3 && strcmp(argv[3], "--debug") == 0)
2445 if (config_data_path(argv[2], NULL))
2447 strncpy(src, argv[1], MAXSTR);
2448 strncpy(dst, argv[1], MAXSTR);
2450 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2451 strcpy(dst + strlen(dst) - 4, ".sol");
2453 strcat(dst, ".sol");
2455 if ((fin = fopen(src, "r")))
2478 else fprintf(stderr, "Failure to establish data directory\n");
2480 else fprintf(stderr, "Usage: %s <map> <data> [--debug]\n", argv[0]);