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"
35 * The overall design of this map converter is very stupid, but very
36 * simple. It begins by assuming that every mtrl, vert, edge, side,
37 * and texc in the map is unique. It then makes an optimizing pass
38 * that discards redundant information. The result is optimal, though
39 * the process is terribly inefficient.
42 /*---------------------------------------------------------------------------*/
44 static const char *input_file;
45 static int debug_output = 0;
47 /*---------------------------------------------------------------------------*/
49 /* Ohhhh... arbitrary! */
72 static int overflow(const char *s)
74 printf("%s overflow\n", s);
79 static int incm(struct s_file *fp)
81 return (fp->mc < MAXM) ? fp->mc++ : overflow("mtrl");
84 static int incv(struct s_file *fp)
86 return (fp->vc < MAXV) ? fp->vc++ : overflow("vert");
89 static int ince(struct s_file *fp)
91 return (fp->ec < MAXE) ? fp->ec++ : overflow("edge");
94 static int incs(struct s_file *fp)
96 return (fp->sc < MAXS) ? fp->sc++ : overflow("side");
99 static int inct(struct s_file *fp)
101 return (fp->tc < MAXT) ? fp->tc++ : overflow("texc");
104 static int incg(struct s_file *fp)
106 return (fp->gc < MAXG) ? fp->gc++ : overflow("geom");
109 static int incl(struct s_file *fp)
111 return (fp->lc < MAXL) ? fp->lc++ : overflow("lump");
114 static int incn(struct s_file *fp)
116 return (fp->nc < MAXN) ? fp->nc++ : overflow("node");
119 static int incp(struct s_file *fp)
121 return (fp->pc < MAXP) ? fp->pc++ : overflow("path");
124 static int incb(struct s_file *fp)
126 return (fp->bc < MAXB) ? fp->bc++ : overflow("body");
129 static int inch(struct s_file *fp)
131 return (fp->hc < MAXH) ? fp->hc++ : overflow("item");
134 static int incz(struct s_file *fp)
136 return (fp->zc < MAXZ) ? fp->zc++ : overflow("goal");
139 static int incj(struct s_file *fp)
141 return (fp->jc < MAXJ) ? fp->jc++ : overflow("jump");
144 static int incx(struct s_file *fp)
146 return (fp->xc < MAXX) ? fp->xc++ : overflow("swch");
149 static int incr(struct s_file *fp)
151 return (fp->rc < MAXR) ? fp->rc++ : overflow("bill");
154 static int incu(struct s_file *fp)
156 return (fp->uc < MAXU) ? fp->uc++ : overflow("ball");
159 static int incw(struct s_file *fp)
161 return (fp->wc < MAXW) ? fp->wc++ : overflow("view");
164 static int incd(struct s_file *fp)
166 return (fp->dc < MAXD) ? fp->dc++ : overflow("dict");
169 static int inci(struct s_file *fp)
171 return (fp->ic < MAXI) ? fp->ic++ : overflow("indx");
174 static void init_file(struct s_file *fp)
197 fp->mv = (struct s_mtrl *) calloc(MAXM, sizeof (struct s_mtrl));
198 fp->vv = (struct s_vert *) calloc(MAXV, sizeof (struct s_vert));
199 fp->ev = (struct s_edge *) calloc(MAXE, sizeof (struct s_edge));
200 fp->sv = (struct s_side *) calloc(MAXS, sizeof (struct s_side));
201 fp->tv = (struct s_texc *) calloc(MAXT, sizeof (struct s_texc));
202 fp->gv = (struct s_geom *) calloc(MAXG, sizeof (struct s_geom));
203 fp->lv = (struct s_lump *) calloc(MAXL, sizeof (struct s_lump));
204 fp->nv = (struct s_node *) calloc(MAXN, sizeof (struct s_node));
205 fp->pv = (struct s_path *) calloc(MAXP, sizeof (struct s_path));
206 fp->bv = (struct s_body *) calloc(MAXB, sizeof (struct s_body));
207 fp->hv = (struct s_item *) calloc(MAXH, sizeof (struct s_item));
208 fp->zv = (struct s_goal *) calloc(MAXZ, sizeof (struct s_goal));
209 fp->jv = (struct s_jump *) calloc(MAXJ, sizeof (struct s_jump));
210 fp->xv = (struct s_swch *) calloc(MAXX, sizeof (struct s_swch));
211 fp->rv = (struct s_bill *) calloc(MAXR, sizeof (struct s_bill));
212 fp->uv = (struct s_ball *) calloc(MAXU, sizeof (struct s_ball));
213 fp->wv = (struct s_view *) calloc(MAXW, sizeof (struct s_view));
214 fp->dv = (struct s_dict *) calloc(MAXD, sizeof (struct s_dict));
215 fp->av = (char *) calloc(MAXA, sizeof (char));
216 fp->iv = (int *) calloc(MAXI, sizeof (int));
219 /*---------------------------------------------------------------------------*/
222 * The following is a small symbol table data structure. Symbols and
223 * their integer values are collected in symv and valv. References
224 * and pointers to their unsatisfied integer values are collected in
225 * refv and pntv. The resolve procedure matches references to symbols
226 * and fills waiting ints with the proper values.
231 static char symv[MAXSYM][MAXSTR];
232 static int valv[MAXSYM];
234 static char refv[MAXSYM][MAXSTR];
235 static int *pntv[MAXSYM];
240 static void make_sym(const char *s, int v)
242 strncpy(symv[strc], s, MAXSTR - 1);
247 static void make_ref(const char *r, int *p)
249 strncpy(refv[refc], r, MAXSTR - 1);
254 static void resolve(void)
258 for (i = 0; i < refc; i++)
259 for (j = 0; j < strc; j++)
260 if (strncmp(refv[i], symv[j], MAXSTR) == 0)
262 *(pntv[i]) = valv[j];
267 /*---------------------------------------------------------------------------*/
270 * The following globals are used to cache target_positions. They are
271 * targeted by various entities and must be resolved in a second pass.
274 static float targ_p [MAXW][3];
275 static int targ_wi[MAXW];
276 static int targ_ji[MAXW];
279 static void targets(struct s_file *fp)
283 for (i = 0; i < fp->wc; i++)
284 v_cpy(fp->wv[i].q, targ_p[targ_wi[i]]);
286 for (i = 0; i < fp->jc; i++)
287 v_cpy(fp->jv[i].q, targ_p[targ_ji[i]]);
290 /*---------------------------------------------------------------------------*/
293 * The following code caches image sizes. Textures are referenced by
294 * name, but their sizes are necessary when computing texture
295 * coordinates. This code allows each file to be accessed only once
296 * regardless of the number of surfaces referring to it.
305 static struct _imagedata *imagedata = NULL;
306 static int image_n = 0;
307 static int image_alloc = 0;
309 #define IMAGE_REALLOC 32
311 static void free_imagedata()
317 for (i = 0; i < image_n; i++)
318 free(imagedata[i].s);
322 image_n = image_alloc = 0;
325 static int size_load(const char *file, int *w, int *h)
329 if ((p = image_load(file, w, h, NULL)))
337 static void size_image(const char *name, int *w, int *h)
344 for (i = 0; i < image_n; i++)
345 if (strncmp(imagedata[i].s, name, MAXSTR) == 0)
356 strcpy(jpg, name); strcat(jpg, ".jpg");
357 strcpy(png, name); strcat(png, ".png");
359 if (size_load(png, w, h) ||
360 size_load(jpg, w, h))
363 if (image_n + 1 >= image_alloc)
365 struct _imagedata *tmp =
366 (struct _imagedata *) malloc(sizeof(struct _imagedata) * (image_alloc + IMAGE_REALLOC));
369 printf("malloc error\n");
374 (void) memcpy(tmp, imagedata, sizeof(struct _imagedata) * image_alloc);
378 image_alloc += IMAGE_REALLOC;
381 imagedata[image_n].s = (char *) calloc(strlen(name) + 1, 1);
382 imagedata[image_n].w = *w;
383 imagedata[image_n].h = *h;
384 strcpy(imagedata[image_n].s, name);
390 /*---------------------------------------------------------------------------*/
392 /* Read the given material file, adding a new material to the solid. */
394 #define scan_vec4(f, s, v) \
395 if (fs_gets((s), sizeof (s), (f))) \
396 sscanf((s), "%f %f %f %f", (v), (v) + 1, (v) + 2, (v) + 3)
398 static int read_mtrl(struct s_file *fp, const char *name)
400 static char line[MAXSTR];
405 for (mi = 0; mi < fp->mc; mi++)
406 if (strncmp(name, fp->mv[mi].f, MAXSTR) == 0)
409 mp = fp->mv + incm(fp);
411 strncpy(mp->f, name, PATHMAX - 1);
413 mp->a[0] = mp->a[1] = mp->a[2] = 0.2f;
414 mp->d[0] = mp->d[1] = mp->d[2] = 0.8f;
415 mp->s[0] = mp->s[1] = mp->s[2] = 0.0f;
416 mp->e[0] = mp->e[1] = mp->e[2] = 0.0f;
417 mp->a[3] = mp->d[3] = mp->s[3] = mp->e[3] = 1.0f;
422 if ((fin = fs_open(name, "r")))
424 scan_vec4(fin, line, mp->d);
425 scan_vec4(fin, line, mp->a);
426 scan_vec4(fin, line, mp->s);
427 scan_vec4(fin, line, mp->e);
429 if (fs_gets(line, sizeof (line), fin))
430 mp->h[0] = strtod(line, NULL);
432 if (fs_gets(line, sizeof (line), fin))
433 mp->fl = strtol(line, NULL, 10);
435 if (fs_gets(line, sizeof (line), fin))
436 mp->angle = strtod(line, NULL);
441 fprintf(stderr, "%s: unknown material \"%s\"\n", input_file, name);
448 /*---------------------------------------------------------------------------*/
451 * All bodies with an associated path are assumed to be positioned at
452 * the beginning of that path. These bodies must be moved to the
453 * origin in order for their path transforms to behave correctly.
454 * This is how we get away with defining func_trains with no origin
458 static void move_side(struct s_side *sp, const float p[3])
460 sp->d -= v_dot(sp->n, p);
463 static void move_vert(struct s_vert *vp, const float p[3])
465 v_sub(vp->p, vp->p, p);
468 static void move_lump(struct s_file *fp,
469 struct s_lump *lp, const float p[3])
473 for (i = 0; i < lp->sc; i++)
474 move_side(fp->sv + fp->iv[lp->s0 + i], p);
475 for (i = 0; i < lp->vc; i++)
476 move_vert(fp->vv + fp->iv[lp->v0 + i], p);
479 static void move_body(struct s_file *fp,
484 /* Move the lumps. */
486 for (i = 0; i < bp->lc; i++)
487 move_lump(fp, fp->lv + bp->l0 + i, fp->pv[bp->pi].p);
489 /* Create an array to mark any verts referenced by moved geoms. */
491 if (bp->gc > 0 && (b = (int *) calloc(fp->vc, sizeof (int))))
493 /* Mark the verts. */
495 for (i = 0; i < bp->gc; i++)
497 b[fp->gv[fp->iv[bp->g0 + i]].vi] = 1;
498 b[fp->gv[fp->iv[bp->g0 + i]].vj] = 1;
499 b[fp->gv[fp->iv[bp->g0 + i]].vk] = 1;
502 /* Apply the motion to the marked vertices. */
504 for (i = 0; i < fp->vc; ++i)
506 move_vert(fp->vv + i, fp->pv[bp->pi].p);
512 static void move_file(struct s_file *fp)
516 for (i = 0; i < fp->bc; i++)
517 if (fp->bv[i].pi >= 0)
518 move_body(fp, fp->bv + i);
521 /*---------------------------------------------------------------------------*/
524 * This is a basic OBJ loader. It is by no means fully compliant with
525 * the OBJ specification, but it works well with the output of
526 * Wings3D. All faces must be triangles and all vertices must include
527 * normals and texture coordinates. Material names are taken to be
528 * references to Neverball materials, rather than MTL definitions.
531 static void read_vt(struct s_file *fp, const char *line)
533 struct s_texc *tp = fp->tv + inct(fp);
535 sscanf(line, "%f %f", tp->u, tp->u + 1);
538 static void read_vn(struct s_file *fp, const char *line)
540 struct s_side *sp = fp->sv + incs(fp);
542 sscanf(line, "%f %f %f", sp->n, sp->n + 1, sp->n + 2);
545 static void read_v(struct s_file *fp, const char *line)
547 struct s_vert *vp = fp->vv + incv(fp);
549 sscanf(line, "%f %f %f", vp->p, vp->p + 1, vp->p + 2);
552 static void read_f(struct s_file *fp, const char *line,
553 int v0, int t0, int s0, int mi)
555 struct s_geom *gp = fp->gv + incg(fp);
560 sscanf(line, "%d%c%d%c%d %d%c%d%c%d %d%c%d%c%d",
561 &gp->vi, &c1, &gp->ti, &c2, &gp->si,
562 &gp->vj, &c1, &gp->tj, &c2, &gp->sj,
563 &gp->vk, &c1, &gp->tk, &c2, &gp->sk);
578 static void read_obj(struct s_file *fp, const char *name, int mi)
588 if ((fin = fs_open(name, "r")))
590 while (fs_gets(line, MAXSTR, fin))
592 if (strncmp(line, "usemtl", 6) == 0)
594 sscanf(line + 6, "%s", mtrl);
595 mi = read_mtrl(fp, mtrl);
598 else if (strncmp(line, "f", 1) == 0)
600 if (fp->mv[mi].d[3] > 0.0f)
601 read_f(fp, line + 1, v0, t0, s0, mi);
604 else if (strncmp(line, "vt", 2) == 0) read_vt(fp, line + 2);
605 else if (strncmp(line, "vn", 2) == 0) read_vn(fp, line + 2);
606 else if (strncmp(line, "v", 1) == 0) read_v (fp, line + 1);
612 /*---------------------------------------------------------------------------*/
614 static float plane_d[MAXS];
615 static float plane_n[MAXS][3];
616 static float plane_p[MAXS][3];
617 static float plane_u[MAXS][3];
618 static float plane_v[MAXS][3];
619 static int plane_f[MAXS];
620 static int plane_m[MAXS];
622 static void make_plane(int pi, float x0, float y0, float z0,
623 float x1, float y1, float z1,
624 float x2, float y2, float z2,
625 float tu, float tv, float r,
626 float su, float sv, int fl, const char *s)
628 static const float base[6][3][3] = {
629 {{ 0, 0, 1 }, { 1, 0, 0 }, { 0, 1, 0 }},
630 {{ 0, 0, -1 }, { 1, 0, 0 }, { 0, 1, 0 }},
631 {{ 1, 0, 0 }, { 0, 0, -1 }, { 0, 1, 0 }},
632 {{ -1, 0, 0 }, { 0, 0, -1 }, { 0, 1, 0 }},
633 {{ 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, -1 }},
634 {{ 0, -1, 0 }, { 1, 0, 0 }, { 0, 0, -1 }},
638 float p0[3], p1[3], p2[3];
639 float u[3], v[3], p[3];
644 size_image(s, &w, &h);
646 plane_f[pi] = fl ? L_DETAIL : 0;
663 v_crs(plane_n[pi], u, v);
664 v_nrm(plane_n[pi], plane_n[pi]);
666 plane_d[pi] = v_dot(plane_n[pi], p1);
668 for (i = 0; i < 6; i++)
669 if ((k = v_dot(plane_n[pi], base[i][0])) >= d)
679 /* Always rotate around the positive axis */
681 m_rot(R, base[n - (n % 2)][0], V_RAD(r));
683 v_mad(p, p, base[n][1], +su * tu / SCALE);
684 v_mad(p, p, base[n][2], -sv * tv / SCALE);
686 m_vxfm(plane_u[pi], R, base[n][1]);
687 m_vxfm(plane_v[pi], R, base[n][2]);
688 m_vxfm(plane_p[pi], R, p);
690 v_scl(plane_u[pi], plane_u[pi], 64.f / w);
691 v_scl(plane_v[pi], plane_v[pi], 64.f / h);
693 v_scl(plane_u[pi], plane_u[pi], 1.f / su);
694 v_scl(plane_v[pi], plane_v[pi], 1.f / sv);
697 /*---------------------------------------------------------------------------*/
706 static int map_token(fs_file fin, int pi, char key[MAXSTR], char val[MAXSTR])
710 if (fs_gets(buf, MAXSTR, fin))
720 /* Scan the beginning or end of a block. */
722 if (buf[0] == '{') return T_BEG;
723 if (buf[0] == '}') return T_END;
725 /* Scan a key-value pair. */
729 strcpy(key, strtok(buf, "\""));
730 (void) strtok(NULL, "\"");
731 strcpy(val, strtok(NULL, "\""));
742 "%s %f %f %f %f %f %d",
743 &c, &x0, &y0, &z0, &c,
744 &c, &x1, &y1, &z1, &c,
745 &c, &x2, &y2, &z2, &c,
746 key, &tu, &tv, &r, &su, &sv, &fl) == 22)
748 make_plane(pi, x0, y0, z0,
751 tu, tv, r, su, sv, fl, key);
755 /* If it's not recognized, it must be uninteresting. */
762 /*---------------------------------------------------------------------------*/
764 /* Parse a lump from the given file and add it to the solid. */
766 static void read_lump(struct s_file *fp, fs_file fin)
772 struct s_lump *lp = fp->lv + incl(fp);
776 while ((t = map_token(fin, fp->sc, k, v)))
780 fp->sv[fp->sc].n[0] = plane_n[fp->sc][0];
781 fp->sv[fp->sc].n[1] = plane_n[fp->sc][1];
782 fp->sv[fp->sc].n[2] = plane_n[fp->sc][2];
783 fp->sv[fp->sc].d = plane_d[fp->sc];
785 plane_m[fp->sc] = read_mtrl(fp, k);
787 fp->iv[fp->ic] = fp->sc;
797 /*---------------------------------------------------------------------------*/
799 static void make_path(struct s_file *fp,
801 char v[][MAXSTR], int c)
803 int i, pi = incp(fp);
805 struct s_path *pp = fp->pv + pi;
815 for (i = 0; i < c; i++)
817 if (strcmp(k[i], "targetname") == 0)
820 if (strcmp(k[i], "target") == 0)
821 make_ref(v[i], &pp->pi);
823 if (strcmp(k[i], "state") == 0)
826 if (strcmp(k[i], "speed") == 0)
827 sscanf(v[i], "%f", &pp->t);
829 if (strcmp(k[i], "smooth") == 0)
832 if (strcmp(k[i], "origin") == 0)
834 float x = 0.f, y = 0.f, z = 0.f;
836 sscanf(v[i], "%f %f %f", &x, &y, &z);
838 pp->p[0] = +x / SCALE;
839 pp->p[1] = +z / SCALE;
840 pp->p[2] = -y / SCALE;
843 if (strcmp(k[i], "angles") == 0)
845 float x = 0.0f, y = 0.0f, z = 0.0f;
847 /* Pitch, yaw and roll. */
849 sscanf(v[i], "%f %f %f", &x, &y, &z);
852 * Find the direction vector from pitch and yaw, use it as
859 pp->e[1] = fcosf(y) * fcosf(x);
861 pp->e[3] = -fsinf(y) * fcosf(x);
863 /* Use roll as the rotation angle. */
865 z = V_RAD(+z) * 0.5f;
868 v_scl(pp->e + 1, pp->e + 1, fsinf(z));
870 pp->fl |= P_ORIENTED;
875 static void make_dict(struct s_file *fp,
879 int space_left, space_needed, di = incd(fp);
881 struct s_dict *dp = fp->dv + di;
883 space_left = MAXA - fp->ac;
884 space_needed = strlen(k) + 1 + strlen(v) + 1;
886 if (space_needed > space_left)
893 dp->aj = dp->ai + strlen(k) + 1;
894 fp->ac = dp->aj + strlen(v) + 1;
896 strncpy(fp->av + dp->ai, k, space_left);
897 strncpy(fp->av + dp->aj, v, space_left - strlen(k) - 1);
900 static int read_dict_entries = 0;
902 static void make_body(struct s_file *fp,
904 char v[][MAXSTR], int c, int l0)
906 int i, mi = 0, bi = incb(fp);
917 struct s_body *bp = fp->bv + bi;
923 for (i = 0; i < c; i++)
925 if (strcmp(k[i], "targetname") == 0)
928 else if (strcmp(k[i], "target") == 0)
929 make_ref(v[i], &bp->pi);
931 else if (strcmp(k[i], "material") == 0)
932 mi = read_mtrl(fp, v[i]);
934 else if (strcmp(k[i], "model") == 0)
935 read_obj(fp, v[i], mi);
937 else if (strcmp(k[i], "origin") == 0)
938 sscanf(v[i], "%f %f %f", &x, &y, &z);
940 else if (read_dict_entries && strcmp(k[i], "classname") != 0)
941 make_dict(fp, k[i], v[i]);
945 bp->lc = fp->lc - l0;
947 bp->gc = fp->gc - g0;
949 for (i = 0; i < bp->gc; i++)
950 fp->iv[inci(fp)] = g0++;
956 for (i = v0; i < fp->vc; i++)
957 v_add(fp->vv[i].p, fp->vv[i].p, p);
959 read_dict_entries = 0;
962 static void make_item(struct s_file *fp,
964 char v[][MAXSTR], int c)
966 int i, hi = inch(fp);
968 struct s_item *hp = fp->hv + hi;
977 for (i = 0; i < c; i++)
979 if (strcmp(k[i], "classname") == 0)
981 if (strcmp(v[i], "light") == 0)
983 else if (strcmp(v[i], "item_health_large") == 0)
985 else if (strcmp(v[i], "item_health_small") == 0)
989 if (strcmp(k[i], "light") == 0)
990 sscanf(v[i], "%d", &hp->n);
992 if (strcmp(k[i], "origin") == 0)
994 float x = 0.f, y = 0.f, z = 0.f;
996 sscanf(v[i], "%f %f %f", &x, &y, &z);
998 hp->p[0] = +x / SCALE;
999 hp->p[1] = +z / SCALE;
1000 hp->p[2] = -y / SCALE;
1005 static void make_bill(struct s_file *fp,
1007 char v[][MAXSTR], int c)
1009 int i, ri = incr(fp);
1011 struct s_bill *rp = fp->rv + ri;
1013 memset(rp, 0, sizeof (struct s_bill));
1016 for (i = 0; i < c; i++)
1018 if (strcmp(k[i], "width") == 0)
1019 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
1020 if (strcmp(k[i], "height") == 0)
1021 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
1023 if (strcmp(k[i], "xrot") == 0)
1024 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
1025 if (strcmp(k[i], "yrot") == 0)
1026 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
1027 if (strcmp(k[i], "zrot") == 0)
1028 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
1030 if (strcmp(k[i], "time") == 0)
1031 sscanf(v[i], "%f", &rp->t);
1032 if (strcmp(k[i], "dist") == 0)
1033 sscanf(v[i], "%f", &rp->d);
1034 if (strcmp(k[i], "flag") == 0)
1035 sscanf(v[i], "%d", &rp->fl);
1037 if (strcmp(k[i], "image") == 0)
1039 rp->mi = read_mtrl(fp, v[i]);
1040 fp->mv[rp->mi].fl |= M_CLAMPED;
1043 if (strcmp(k[i], "origin") == 0)
1045 float x = 0.f, y = 0.f, z = 0.f;
1047 sscanf(v[i], "%f %f %f", &x, &y, &z);
1049 rp->p[0] = +x / SCALE;
1050 rp->p[1] = +z / SCALE;
1051 rp->p[2] = -y / SCALE;
1055 if (rp->fl & B_ADDITIVE)
1056 fp->mv[rp->mi].fl |= M_ADDITIVE;
1059 static void make_goal(struct s_file *fp,
1061 char v[][MAXSTR], int c)
1063 int i, zi = incz(fp);
1065 struct s_goal *zp = fp->zv + zi;
1072 for (i = 0; i < c; i++)
1074 if (strcmp(k[i], "radius") == 0)
1075 sscanf(v[i], "%f", &zp->r);
1077 if (strcmp(k[i], "origin") == 0)
1079 float x = 0.f, y = 0.f, z = 0.f;
1081 sscanf(v[i], "%f %f %f", &x, &y, &z);
1083 zp->p[0] = +(x) / SCALE;
1084 zp->p[1] = +(z - 24) / SCALE;
1085 zp->p[2] = -(y) / SCALE;
1090 static void make_view(struct s_file *fp,
1092 char v[][MAXSTR], int c)
1094 int i, wi = incw(fp);
1096 struct s_view *wp = fp->wv + wi;
1105 for (i = 0; i < c; i++)
1107 if (strcmp(k[i], "target") == 0)
1108 make_ref(v[i], targ_wi + wi);
1110 if (strcmp(k[i], "origin") == 0)
1112 float x = 0.f, y = 0.f, z = 0.f;
1114 sscanf(v[i], "%f %f %f", &x, &y, &z);
1116 wp->p[0] = +x / SCALE;
1117 wp->p[1] = +z / SCALE;
1118 wp->p[2] = -y / SCALE;
1123 static void make_jump(struct s_file *fp,
1125 char v[][MAXSTR], int c)
1127 int i, ji = incj(fp);
1129 struct s_jump *jp = fp->jv + ji;
1139 for (i = 0; i < c; i++)
1141 if (strcmp(k[i], "radius") == 0)
1142 sscanf(v[i], "%f", &jp->r);
1144 if (strcmp(k[i], "target") == 0)
1145 make_ref(v[i], targ_ji + ji);
1147 if (strcmp(k[i], "origin") == 0)
1149 float x = 0.f, y = 0.f, z = 0.f;
1151 sscanf(v[i], "%f %f %f", &x, &y, &z);
1153 jp->p[0] = +x / SCALE;
1154 jp->p[1] = +z / SCALE;
1155 jp->p[2] = -y / SCALE;
1160 static void make_swch(struct s_file *fp,
1162 char v[][MAXSTR], int c)
1164 int i, xi = incx(fp);
1166 struct s_swch *xp = fp->xv + xi;
1179 for (i = 0; i < c; i++)
1181 if (strcmp(k[i], "radius") == 0)
1182 sscanf(v[i], "%f", &xp->r);
1184 if (strcmp(k[i], "target") == 0)
1185 make_ref(v[i], &xp->pi);
1187 if (strcmp(k[i], "timer") == 0)
1189 sscanf(v[i], "%f", &xp->t0);
1193 if (strcmp(k[i], "state") == 0)
1196 xp->f0 = atoi(v[i]);
1199 if (strcmp(k[i], "invisible") == 0)
1202 if (strcmp(k[i], "origin") == 0)
1204 float x = 0.f, y = 0.f, z = 0.f;
1206 sscanf(v[i], "%f %f %f", &x, &y, &z);
1208 xp->p[0] = +x / SCALE;
1209 xp->p[1] = +z / SCALE;
1210 xp->p[2] = -y / SCALE;
1215 static void make_targ(struct s_file *fp,
1217 char v[][MAXSTR], int c)
1221 targ_p[targ_n][0] = 0.f;
1222 targ_p[targ_n][1] = 0.f;
1223 targ_p[targ_n][2] = 0.f;
1225 for (i = 0; i < c; i++)
1227 if (strcmp(k[i], "targetname") == 0)
1228 make_sym(v[i], targ_n);
1230 if (strcmp(k[i], "origin") == 0)
1232 float x = 0.f, y = 0.f, z = 0.f;
1234 sscanf(v[i], "%f %f %f", &x, &y, &z);
1236 targ_p[targ_n][0] = +x / SCALE;
1237 targ_p[targ_n][1] = +z / SCALE;
1238 targ_p[targ_n][2] = -y / SCALE;
1245 static void make_ball(struct s_file *fp,
1247 char v[][MAXSTR], int c)
1249 int i, ui = incu(fp);
1251 struct s_ball *up = fp->uv + ui;
1258 for (i = 0; i < c; i++)
1260 if (strcmp(k[i], "radius") == 0)
1261 sscanf(v[i], "%f", &up->r);
1263 if (strcmp(k[i], "origin") == 0)
1265 float x = 0.f, y = 0.f, z = 0.f;
1267 sscanf(v[i], "%f %f %f", &x, &y, &z);
1269 up->p[0] = +(x) / SCALE;
1270 up->p[1] = +(z - 24) / SCALE;
1271 up->p[2] = -(y) / SCALE;
1275 up->p[1] += up->r + SMALL;
1278 /*---------------------------------------------------------------------------*/
1280 static void read_ent(struct s_file *fp, fs_file fin)
1282 char k[MAXKEY][MAXSTR];
1283 char v[MAXKEY][MAXSTR];
1284 int t, i = 0, c = 0;
1288 while ((t = map_token(fin, -1, k[c], v[c])))
1292 if (strcmp(k[c], "classname") == 0)
1296 if (t == T_BEG) read_lump(fp, fin);
1297 if (t == T_END) break;
1300 if (!strcmp(v[i], "light")) make_item(fp, k, v, c);
1301 if (!strcmp(v[i], "item_health_large")) make_item(fp, k, v, c);
1302 if (!strcmp(v[i], "item_health_small")) make_item(fp, k, v, c);
1303 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1304 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1305 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1306 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1307 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1308 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1309 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1310 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1311 if (!strcmp(v[i], "worldspawn"))
1313 read_dict_entries = 1;
1314 make_body(fp, k, v, c, l0);
1316 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1317 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1320 static void read_map(struct s_file *fp, fs_file fin)
1326 while ((t = map_token(fin, -1, k, v)))
1331 /*---------------------------------------------------------------------------*/
1333 /* Test the location of a point with respect to a side plane. */
1335 static int fore_side(const float p[3], const struct s_side *sp)
1337 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1340 static int on_side(const float p[3], const struct s_side *sp)
1342 float d = v_dot(p, sp->n) - sp->d;
1344 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1347 /*---------------------------------------------------------------------------*/
1349 * Confirm that the addition of a vert would not result in degenerate
1353 static int ok_vert(const struct s_file *fp,
1354 const struct s_lump *lp, const float p[3])
1359 for (i = 0; i < lp->vc; i++)
1361 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1365 if (v_len(r) < SMALL)
1371 /*---------------------------------------------------------------------------*/
1374 * The following functions take the set of planes defining a lump and
1375 * find the verts, edges, and geoms that describe its boundaries. To
1376 * do this, they first find the verts, and then search these verts for
1377 * valid edges and geoms. It may be more efficient to compute edges
1378 * and geoms directly by clipping down infinite line segments and
1379 * planes, but this would be more complex and prone to numerical
1384 * Given 3 side planes, compute the point of intersection, if any.
1385 * Confirm that this point falls within the current lump, and that it
1386 * is unique. Add it as a vert of the solid.
1388 static void clip_vert(struct s_file *fp,
1389 struct s_lump *lp, int si, int sj, int sk)
1391 float M[16], X[16], I[16];
1395 d[0] = fp->sv[si].d;
1396 d[1] = fp->sv[sj].d;
1397 d[2] = fp->sv[sk].d;
1399 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1406 for (i = 0; i < lp->sc; i++)
1408 int si = fp->iv[lp->s0 + i];
1410 if (fore_side(p, fp->sv + si))
1414 if (ok_vert(fp, lp, p))
1416 v_cpy(fp->vv[fp->vc].p, p);
1418 fp->iv[fp->ic] = fp->vc;
1427 * Given two side planes, find an edge along their intersection by
1428 * finding a pair of vertices that fall on both planes. Add it to the
1431 static void clip_edge(struct s_file *fp,
1432 struct s_lump *lp, int si, int sj)
1436 for (i = 1; i < lp->vc; i++)
1438 int vi = fp->iv[lp->v0 + i];
1440 if (!on_side(fp->vv[vi].p, fp->sv + si) ||
1441 !on_side(fp->vv[vi].p, fp->sv + sj))
1444 for (j = 0; j < i; j++)
1446 int vj = fp->iv[lp->v0 + j];
1448 if (on_side(fp->vv[vj].p, fp->sv + si) &&
1449 on_side(fp->vv[vj].p, fp->sv + sj))
1451 fp->ev[fp->ec].vi = vi;
1452 fp->ev[fp->ec].vj = vj;
1454 fp->iv[fp->ic] = fp->ec;
1465 * Find all verts that lie on the given side of the lump. Sort these
1466 * verts to have a counter-clockwise winding about the plane normal.
1467 * Create geoms to tessellate the resulting convex polygon.
1469 static void clip_geom(struct s_file *fp,
1470 struct s_lump *lp, int si)
1472 int m[256], t[256], d, i, j, n = 0;
1477 struct s_side *sp = fp->sv + si;
1481 for (i = 0; i < lp->vc; i++)
1483 int vi = fp->iv[lp->v0 + i];
1485 if (on_side(fp->vv[vi].p, sp))
1490 v_add(v, fp->vv[vi].p, plane_p[si]);
1492 fp->tv[t[n]].u[0] = v_dot(v, plane_u[si]);
1493 fp->tv[t[n]].u[1] = v_dot(v, plane_v[si]);
1501 for (i = 1; i < n; i++)
1502 for (j = i + 1; j < n; j++)
1504 v_sub(u, fp->vv[m[i]].p, fp->vv[m[0]].p);
1505 v_sub(v, fp->vv[m[j]].p, fp->vv[m[0]].p);
1508 if (v_dot(w, sp->n) < 0.f)
1522 for (i = 0; i < n - 2; i++)
1524 fp->gv[fp->gc].mi = plane_m[si];
1526 fp->gv[fp->gc].ti = t[0];
1527 fp->gv[fp->gc].tj = t[i + 1];
1528 fp->gv[fp->gc].tk = t[i + 2];
1530 fp->gv[fp->gc].si = si;
1531 fp->gv[fp->gc].sj = si;
1532 fp->gv[fp->gc].sk = si;
1534 fp->gv[fp->gc].vi = m[0];
1535 fp->gv[fp->gc].vj = m[i + 1];
1536 fp->gv[fp->gc].vk = m[i + 2];
1538 fp->iv[fp->ic] = fp->gc;
1546 * Iterate the sides of the lump, attempting to generate a new vert for
1547 * each trio of planes, a new edge for each pair of planes, and a new
1548 * set of geom for each visible plane.
1550 static void clip_lump(struct s_file *fp, struct s_lump *lp)
1557 for (i = 2; i < lp->sc; i++)
1558 for (j = 1; j < i; j++)
1559 for (k = 0; k < j; k++)
1563 fp->iv[lp->s0 + k]);
1568 for (i = 1; i < lp->sc; i++)
1569 for (j = 0; j < i; j++)
1572 fp->iv[lp->s0 + j]);
1577 for (i = 0; i < lp->sc; i++)
1578 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0.0f)
1580 fp->iv[lp->s0 + i]);
1582 for (i = 0; i < lp->sc; i++)
1583 if (plane_f[fp->iv[lp->s0 + i]])
1587 static void clip_file(struct s_file *fp)
1591 for (i = 0; i < fp->lc; i++)
1592 clip_lump(fp, fp->lv + i);
1595 /*---------------------------------------------------------------------------*/
1598 * For each body element type, determine if element 'p' is equivalent
1599 * to element 'q'. This is more than a simple memory compare. It
1600 * effectively snaps mtrls and verts together, and may reverse the
1601 * winding of an edge or a geom. This is done in order to maximize
1602 * the number of elements that can be eliminated.
1605 static int comp_mtrl(const struct s_mtrl *mp, const struct s_mtrl *mq)
1607 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1608 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1609 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1610 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1612 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1613 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1614 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1615 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1617 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1618 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1619 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1620 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1622 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1623 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1624 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1625 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1627 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1629 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1634 static int comp_vert(const struct s_vert *vp, const struct s_vert *vq)
1636 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1637 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1638 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1643 static int comp_edge(const struct s_edge *ep, const struct s_edge *eq)
1645 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1646 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1651 static int comp_side(const struct s_side *sp, const struct s_side *sq)
1653 if (fabs(sp->d - sq->d) > SMALL) return 0;
1654 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1659 static int comp_texc(const struct s_texc *tp, const struct s_texc *tq)
1661 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1662 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1667 static int comp_geom(const struct s_geom *gp, const struct s_geom *gq)
1669 if (gp->mi != gq->mi) return 0;
1671 if (gp->ti != gq->ti) return 0;
1672 if (gp->si != gq->si) return 0;
1673 if (gp->vi != gq->vi) return 0;
1675 if (gp->tj != gq->tj) return 0;
1676 if (gp->sj != gq->sj) return 0;
1677 if (gp->vj != gq->vj) return 0;
1679 if (gp->tk != gq->tk) return 0;
1680 if (gp->sk != gq->sk) return 0;
1681 if (gp->vk != gq->vk) return 0;
1686 /*---------------------------------------------------------------------------*/
1689 * For each file element type, replace all references to element 'i'
1690 * with a reference to element 'j'. These are used when optimizing
1691 * and sorting the file.
1694 static void swap_mtrl(struct s_file *fp, int mi, int mj)
1698 for (i = 0; i < fp->gc; i++)
1699 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1700 for (i = 0; i < fp->rc; i++)
1701 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1704 static int vert_swaps[MAXV];
1706 static void apply_vert_swaps(struct s_file *fp)
1710 for (i = 0; i < fp->ec; i++)
1712 fp->ev[i].vi = vert_swaps[fp->ev[i].vi];
1713 fp->ev[i].vj = vert_swaps[fp->ev[i].vj];
1716 for (i = 0; i < fp->gc; i++)
1718 fp->gv[i].vi = vert_swaps[fp->gv[i].vi];
1719 fp->gv[i].vj = vert_swaps[fp->gv[i].vj];
1720 fp->gv[i].vk = vert_swaps[fp->gv[i].vk];
1723 for (i = 0; i < fp->lc; i++)
1724 for (j = 0; j < fp->lv[i].vc; j++)
1725 fp->iv[fp->lv[i].v0 + j] = vert_swaps[fp->iv[fp->lv[i].v0 + j]];
1728 static void swap_vert(struct s_file *fp, int vi, int vj)
1732 for (i = 0; i < fp->ec; i++)
1734 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1735 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1738 for (i = 0; i < fp->gc; i++)
1740 if (fp->gv[i].vi == vi) fp->gv[i].vi = vj;
1741 if (fp->gv[i].vj == vi) fp->gv[i].vj = vj;
1742 if (fp->gv[i].vk == vi) fp->gv[i].vk = vj;
1745 for (i = 0; i < fp->lc; i++)
1746 for (j = 0; j < fp->lv[i].vc; j++)
1747 if (fp->iv[fp->lv[i].v0 + j] == vi)
1748 fp->iv[fp->lv[i].v0 + j] = vj;
1751 static int edge_swaps[MAXE];
1753 static void apply_edge_swaps(struct s_file *fp)
1757 for (i = 0; i < fp->lc; i++)
1758 for (j = 0; j < fp->lv[i].ec; j++)
1759 fp->iv[fp->lv[i].e0 + j] = edge_swaps[fp->iv[fp->lv[i].e0 + j]];
1762 static int side_swaps[MAXS];
1764 static void apply_side_swaps(struct s_file *fp)
1768 for (i = 0; i < fp->gc; i++)
1770 fp->gv[i].si = side_swaps[fp->gv[i].si];
1771 fp->gv[i].sj = side_swaps[fp->gv[i].sj];
1772 fp->gv[i].sk = side_swaps[fp->gv[i].sk];
1774 for (i = 0; i < fp->nc; i++)
1775 fp->nv[i].si = side_swaps[fp->nv[i].si];
1777 for (i = 0; i < fp->lc; i++)
1778 for (j = 0; j < fp->lv[i].sc; j++)
1779 fp->iv[fp->lv[i].s0 + j] = side_swaps[fp->iv[fp->lv[i].s0 + j]];
1782 static int texc_swaps[MAXT];
1784 static void apply_texc_swaps(struct s_file *fp)
1788 for (i = 0; i < fp->gc; i++)
1790 fp->gv[i].ti = texc_swaps[fp->gv[i].ti];
1791 fp->gv[i].tj = texc_swaps[fp->gv[i].tj];
1792 fp->gv[i].tk = texc_swaps[fp->gv[i].tk];
1796 static int geom_swaps[MAXG];
1798 static void apply_geom_swaps(struct s_file *fp)
1802 for (i = 0; i < fp->lc; i++)
1803 for (j = 0; j < fp->lv[i].gc; j++)
1804 fp->iv[fp->lv[i].g0 + j] = geom_swaps[fp->iv[fp->lv[i].g0 + j]];
1806 for (i = 0; i < fp->bc; i++)
1807 for (j = 0; j < fp->bv[i].gc; j++)
1808 fp->iv[fp->bv[i].g0 + j] = geom_swaps[fp->iv[fp->bv[i].g0 + j]];
1811 /*---------------------------------------------------------------------------*/
1813 static void uniq_mtrl(struct s_file *fp)
1817 for (i = 0; i < fp->mc; i++)
1819 for (j = 0; j < k; j++)
1820 if (comp_mtrl(fp->mv + i, fp->mv + j))
1822 swap_mtrl(fp, i, j);
1830 fp->mv[k] = fp->mv[i];
1831 swap_mtrl(fp, i, k);
1840 static void uniq_vert(struct s_file *fp)
1844 for (i = 0; i < fp->vc; i++)
1846 for (j = 0; j < k; j++)
1847 if (comp_vert(fp->vv + i, fp->vv + j))
1855 fp->vv[k] = fp->vv[i];
1860 apply_vert_swaps(fp);
1865 static void uniq_edge(struct s_file *fp)
1869 for (i = 0; i < fp->ec; i++)
1871 for (j = 0; j < k; j++)
1872 if (comp_edge(fp->ev + i, fp->ev + j))
1880 fp->ev[k] = fp->ev[i];
1885 apply_edge_swaps(fp);
1890 static int geomlist[MAXV];
1891 static int nextgeom[MAXG];
1893 static void uniq_geom(struct s_file *fp)
1897 for (i = 0; i < MAXV; i++)
1900 for (i = 0; i < fp->gc; i++)
1902 int key = fp->gv[i].vj;
1904 for (j = geomlist[key]; j != -1; j = nextgeom[j])
1905 if (comp_geom(fp->gv + i, fp->gv + j))
1908 fp->gv[k] = fp->gv[i];
1910 nextgeom[k] = geomlist[key];
1920 apply_geom_swaps(fp);
1925 static void uniq_texc(struct s_file *fp)
1929 for (i = 0; i < fp->tc; i++)
1931 for (j = 0; j < k; j++)
1932 if (comp_texc(fp->tv + i, fp->tv + j))
1940 fp->tv[k] = fp->tv[i];
1945 apply_texc_swaps(fp);
1950 static void uniq_side(struct s_file *fp)
1954 for (i = 0; i < fp->sc; i++)
1956 for (j = 0; j < k; j++)
1957 if (comp_side(fp->sv + i, fp->sv + j))
1965 fp->sv[k] = fp->sv[i];
1970 apply_side_swaps(fp);
1975 static void uniq_file(struct s_file *fp)
1977 /* Debug mode skips optimization, producing oversized output files. */
1979 if (debug_output == 0)
1990 /*---------------------------------------------------------------------------*/
2000 static int comp_trip(const void *p, const void *q)
2002 const struct s_trip *tp = (const struct s_trip *) p;
2003 const struct s_trip *tq = (const struct s_trip *) q;
2005 if (tp->vi < tq->vi) return -1;
2006 if (tp->vi > tq->vi) return +1;
2007 if (tp->mi < tq->mi) return -1;
2008 if (tp->mi > tq->mi) return +1;
2013 static void smth_file(struct s_file *fp)
2015 struct s_trip temp, *T;
2017 if (debug_output == 0)
2019 if ((T = (struct s_trip *) malloc(fp->gc * 3 * sizeof (struct s_trip))))
2021 int gi, i, j, k, l, c = 0;
2023 /* Create a list of all non-faceted vertex triplets. */
2025 for (gi = 0; gi < fp->gc; ++gi)
2027 struct s_geom *gp = fp->gv + gi;
2048 /* Sort all triplets by vertex index and material. */
2050 qsort(T, c, sizeof (struct s_trip), comp_trip);
2052 /* For each set of triplets sharing vertex index and material... */
2054 for (i = 0; i < c; i = l)
2058 float N[3], angle = fp->mv[T[i].mi].angle;
2059 const float *Ni = fp->sv[T[i].si].n;
2061 /* Sort the set by side similarity to the first. */
2063 for (j = i + 1; j < c && (T[j].vi == T[i].vi &&
2064 T[j].mi == T[i].mi); ++j)
2066 for (k = j + 1; k < c && (T[k].vi == T[i].vi &&
2067 T[k].mi == T[i].mi); ++k)
2069 const float *Nj = fp->sv[T[j].si].n;
2070 const float *Nk = fp->sv[T[k].si].n;
2072 if (T[j].si != T[k].si && v_dot(Nk, Ni) > v_dot(Nj, Ni))
2081 /* Accumulate all similar side normals. */
2087 for (l = i + 1; l < c && (T[l].vi == T[i].vi &&
2088 T[l].mi == T[i].mi); ++l)
2089 if (T[l].si != T[i].si)
2091 const float *Nl = fp->sv[T[l].si].n;
2093 if (V_DEG(facosf(v_dot(Ni, Nl))) > angle)
2103 /* If at least two normals have been accumulated... */
2107 /* Store the accumulated normal as a new side. */
2111 v_nrm(fp->sv[ss].n, N);
2112 fp->sv[ss].d = 0.0f;
2114 /* Assign the new normal to the merged triplets. */
2116 for (j = i; j < l; ++j)
2121 /* Assign the remapped normals to the original geoms. */
2123 for (i = 0; i < c; ++i)
2125 struct s_geom *gp = fp->gv + T[i].gi;
2127 if (gp->vi == T[i].vi) gp->si = T[i].si;
2128 if (gp->vj == T[i].vi) gp->sj = T[i].si;
2129 if (gp->vk == T[i].vi) gp->sk = T[i].si;
2140 /*---------------------------------------------------------------------------*/
2142 static void sort_file(struct s_file *fp)
2146 /* Sort billboards by material within distance. */
2148 for (i = 0; i < fp->rc; i++)
2149 for (j = i + 1; j < fp->rc; j++)
2150 if ((fp->rv[j].d > fp->rv[i].d) ||
2151 (fp->rv[j].d == fp->rv[i].d &&
2152 fp->rv[j].mi > fp->rv[i].mi))
2157 fp->rv[i] = fp->rv[j];
2161 /* Ensure the first vertex is the lowest. */
2163 for (i = 0; i < fp->vc; i++)
2164 if (fp->vv[0].p[1] > fp->vv[i].p[1])
2169 fp->vv[0] = fp->vv[i];
2172 swap_vert(fp, 0, -1);
2173 swap_vert(fp, i, 0);
2174 swap_vert(fp, -1, i);
2178 /*---------------------------------------------------------------------------*/
2180 static int test_lump_side(const struct s_file *fp,
2181 const struct s_lump *lp,
2182 const struct s_side *sp,
2196 /* Check if the bounding sphere of the lump is completely on one side. */
2198 d = v_dot(bsphere, sp->n) - sp->d;
2200 if (fabs(d) > bsphere[3])
2201 return d > 0 ? 1 : -1;
2203 /* If the given side is part of the given lump, then the lump is behind. */
2205 for (si = 0; si < lp->sc; si++)
2206 if (fp->sv + fp->iv[lp->s0 + si] == sp)
2209 /* Check if each lump vertex is in front of, behind, on the side. */
2211 for (vi = 0; vi < lp->vc; vi++)
2213 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
2219 /* If no verts are behind, the lump is in front, and vice versa. */
2221 if (f > 0 && b == 0) return +1;
2222 if (b > 0 && f == 0) return -1;
2224 /* Else, the lump crosses the side. */
2229 static int node_node(struct s_file *fp, int l0, int lc, float bsphere[][4])
2233 /* Base case. Dump all given lumps into a leaf node. */
2235 fp->nv[fp->nc].si = -1;
2236 fp->nv[fp->nc].ni = -1;
2237 fp->nv[fp->nc].nj = -1;
2238 fp->nv[fp->nc].l0 = l0;
2239 fp->nv[fp->nc].lc = lc;
2249 int li = 0, lic = 0;
2250 int lj = 0, ljc = 0;
2251 int lk = 0, lkc = 0;
2254 /* Find the side that most evenly splits the given lumps. */
2256 for (si = 0; si < fp->sc; si++)
2262 for (li = 0; li < lc; li++)
2263 if ((k = test_lump_side(fp,
2273 if ((d < sjd) || (d == sjd && o < sjo))
2281 /* Flag each lump with its position WRT the side. */
2283 for (li = 0; li < lc; li++)
2286 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2290 switch (test_lump_side(fp,
2296 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10;
2300 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2304 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40;
2309 /* Sort all lumps in the range by their flag values. */
2311 for (li = 1; li < lc; li++)
2312 for (lj = 0; lj < li; lj++)
2313 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
2319 for (i = 0; i < 4; i++)
2321 f = bsphere[l0 + li][i];
2322 bsphere[l0 + li][i] = bsphere[l0 + lj][i];
2323 bsphere[l0 + lj][i] = f;
2326 l = fp->lv[l0 + li];
2327 fp->lv[l0 + li] = fp->lv[l0 + lj];
2328 fp->lv[l0 + lj] = l;
2331 /* Establish the in-front, on, and behind lump ranges. */
2337 for (i = lc - 1; i >= 0; i--)
2338 switch (fp->lv[l0 + i].fl & 0xf0)
2340 case 0x10: li = l0 + i; lic++; break;
2341 case 0x20: lj = l0 + i; ljc++; break;
2342 case 0x40: lk = l0 + i; lkc++; break;
2345 /* Add the lumps on the side to the node. */
2350 fp->nv[i].ni = node_node(fp, li, lic, bsphere);
2352 fp->nv[i].nj = node_node(fp, lk, lkc, bsphere);
2361 * Compute a bounding sphere for a lump (not optimal)
2363 static void lump_bounding_sphere(struct s_file *fp,
2374 bbox[0] = bbox[3] = fp->vv[fp->iv[lp->v0]].p[0];
2375 bbox[1] = bbox[4] = fp->vv[fp->iv[lp->v0]].p[1];
2376 bbox[2] = bbox[5] = fp->vv[fp->iv[lp->v0]].p[2];
2378 for (i = 1; i < lp->vc; i++)
2380 struct s_vert *vp = fp->vv + fp->iv[lp->v0 + i];
2383 for (j = 0; j < 3; j++)
2384 if (vp->p[j] < bbox[j])
2387 for (j = 0; j < 3; j++)
2388 if (vp->p[j] > bbox[j + 3])
2389 bbox[j + 3] = vp->p[j];
2394 for (i = 0; i < 3; i++)
2396 bsphere[i] = (bbox[i] + bbox[i + 3]) / 2;
2397 r += (bsphere[i] - bbox[i]) * (bsphere[i] - bbox[i]);
2400 bsphere[3] = fsqrtf(r);
2403 static void node_file(struct s_file *fp)
2405 float bsphere[MAXL][4];
2408 /* Compute a bounding sphere for each lump. */
2410 for (i = 0; i < fp->lc; i++)
2411 lump_bounding_sphere(fp, fp->lv + i, bsphere[i]);
2413 /* Sort the lumps of each body into BSP nodes. */
2415 for (i = 0; i < fp->bc; i++)
2416 fp->bv[i].ni = node_node(fp, fp->bv[i].l0, fp->bv[i].lc, bsphere);
2419 /*---------------------------------------------------------------------------*/
2421 static void dump_file(struct s_file *p, const char *name)
2428 /* Count the number of solid lumps. */
2430 for (i = 0; i < p->lc; i++)
2431 if ((p->lv[i].fl & 1) == 0)
2434 /* Count the number of visible geoms. */
2436 m = p->rc + (p->zc + p->jc + p->xc) * 32;
2438 for (i = 0; i < p->hc; i++)
2439 if (p->hv[i].t == ITEM_COIN)
2444 for (i = 0; i < p->bc; i++)
2446 for (j = 0; j < p->bv[i].lc; j++)
2447 m += p->lv[p->bv[i].l0 + j].gc;
2451 /* Count the total value of all coins. */
2453 for (i = 0; i < p->hc; i++)
2454 if (p->hv[i].t == ITEM_COIN)
2457 printf("%s (%d/%d/$%d)\n"
2458 " mtrl vert edge side texc"
2459 " geom lump path node body\n"
2460 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2461 " item goal view jump swch"
2462 " bill ball char dict indx\n"
2463 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2465 p->mc, p->vc, p->ec, p->sc, p->tc,
2466 p->gc, p->lc, p->pc, p->nc, p->bc,
2467 p->hc, p->zc, p->wc, p->jc, p->xc,
2468 p->rc, p->uc, p->ac, p->dc, p->ic);
2471 int main(int argc, char *argv[])
2473 char src[MAXSTR] = "";
2474 char dst[MAXSTR] = "";
2478 if (!fs_init(argv[0]))
2480 fprintf(stderr, "Failure to initialize virtual file system: %s\n",
2487 input_file = argv[1];
2489 if (argc > 3 && strcmp(argv[3], "--debug") == 0)
2492 strncpy(src, argv[1], MAXSTR - 1);
2493 strncpy(dst, argv[1], MAXSTR - 1);
2495 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2496 strcpy(dst + strlen(dst) - 4, ".sol");
2498 strcat(dst, ".sol");
2500 fs_add_path (dir_name(src));
2501 fs_set_write_dir(dir_name(dst));
2503 if ((fin = fs_open(base_name(src, NULL), "r")))
2505 if (!fs_add_path_with_archives(argv[2]))
2507 fprintf(stderr, "Failure to establish data directory\n");
2527 sol_stor(&f, base_name(dst, NULL));
2534 else fprintf(stderr, "Usage: %s <map> <data> [--debug]\n", argv[0]);