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;
845 static void make_dict(struct s_file *fp,
849 int space_left, space_needed, di = incd(fp);
851 struct s_dict *dp = fp->dv + di;
853 space_left = MAXA - fp->ac;
854 space_needed = strlen(k) + 1 + strlen(v) + 1;
856 if (space_needed > space_left)
863 dp->aj = dp->ai + strlen(k) + 1;
864 fp->ac = dp->aj + strlen(v) + 1;
866 strncpy(fp->av + dp->ai, k, space_left);
867 strncpy(fp->av + dp->aj, v, space_left - strlen(k) - 1);
870 static int read_dict_entries = 0;
872 static void make_body(struct s_file *fp,
874 char v[][MAXSTR], int c, int l0)
876 int i, mi = 0, bi = incb(fp);
887 struct s_body *bp = fp->bv + bi;
894 for (i = 0; i < c; i++)
896 if (strcmp(k[i], "targetname") == 0)
899 else if (strcmp(k[i], "target") == 0)
900 make_ref(v[i], &bp->pi);
902 else if (strcmp(k[i], "material") == 0)
903 mi = read_mtrl(fp, v[i]);
905 else if (strcmp(k[i], "model") == 0)
906 read_obj(fp, v[i], mi);
908 else if (strcmp(k[i], "origin") == 0)
909 sscanf(v[i], "%f %f %f", &x, &y, &z);
911 else if (strcmp(k[i], "classname") == 0 &&
912 strcmp(v[i], "func_rotating") == 0)
913 bp->fl |= P_ROTATING;
915 else if (read_dict_entries && strcmp(k[i], "classname") != 0)
916 make_dict(fp, k[i], v[i]);
920 bp->lc = fp->lc - l0;
922 bp->gc = fp->gc - g0;
924 for (i = 0; i < bp->gc; i++)
925 fp->iv[inci(fp)] = g0++;
931 for (i = v0; i < fp->vc; i++)
932 v_add(fp->vv[i].p, fp->vv[i].p, p);
934 read_dict_entries = 0;
937 static void make_item(struct s_file *fp,
939 char v[][MAXSTR], int c)
941 int i, hi = inch(fp);
943 struct s_item *hp = fp->hv + hi;
952 for (i = 0; i < c; i++)
954 if (strcmp(k[i], "classname") == 0)
956 if (strcmp(v[i], "light") == 0)
958 else if (strcmp(v[i], "item_health_large") == 0)
960 else if (strcmp(v[i], "item_health_small") == 0)
964 if (strcmp(k[i], "light") == 0)
965 sscanf(v[i], "%d", &hp->n);
967 if (strcmp(k[i], "origin") == 0)
969 float x = 0.f, y = 0.f, z = 0.f;
971 sscanf(v[i], "%f %f %f", &x, &y, &z);
973 hp->p[0] = +x / SCALE;
974 hp->p[1] = +z / SCALE;
975 hp->p[2] = -y / SCALE;
980 static void make_bill(struct s_file *fp,
982 char v[][MAXSTR], int c)
984 int i, ri = incr(fp);
986 struct s_bill *rp = fp->rv + ri;
988 memset(rp, 0, sizeof (struct s_bill));
991 for (i = 0; i < c; i++)
993 if (strcmp(k[i], "width") == 0)
994 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
995 if (strcmp(k[i], "height") == 0)
996 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
998 if (strcmp(k[i], "xrot") == 0)
999 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
1000 if (strcmp(k[i], "yrot") == 0)
1001 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
1002 if (strcmp(k[i], "zrot") == 0)
1003 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
1005 if (strcmp(k[i], "time") == 0)
1006 sscanf(v[i], "%f", &rp->t);
1007 if (strcmp(k[i], "dist") == 0)
1008 sscanf(v[i], "%f", &rp->d);
1009 if (strcmp(k[i], "flag") == 0)
1010 sscanf(v[i], "%d", &rp->fl);
1012 if (strcmp(k[i], "image") == 0)
1014 rp->mi = read_mtrl(fp, v[i]);
1015 fp->mv[rp->mi].fl |= M_CLAMPED;
1018 if (strcmp(k[i], "origin") == 0)
1020 float x = 0.f, y = 0.f, z = 0.f;
1022 sscanf(v[i], "%f %f %f", &x, &y, &z);
1024 rp->p[0] = +x / SCALE;
1025 rp->p[1] = +z / SCALE;
1026 rp->p[2] = -y / SCALE;
1030 if (rp->fl & B_ADDITIVE)
1031 fp->mv[rp->mi].fl |= M_ADDITIVE;
1034 static void make_goal(struct s_file *fp,
1036 char v[][MAXSTR], int c)
1038 int i, zi = incz(fp);
1040 struct s_goal *zp = fp->zv + zi;
1047 for (i = 0; i < c; i++)
1049 if (strcmp(k[i], "radius") == 0)
1050 sscanf(v[i], "%f", &zp->r);
1052 if (strcmp(k[i], "origin") == 0)
1054 float x = 0.f, y = 0.f, z = 0.f;
1056 sscanf(v[i], "%f %f %f", &x, &y, &z);
1058 zp->p[0] = +(x) / SCALE;
1059 zp->p[1] = +(z - 24) / SCALE;
1060 zp->p[2] = -(y) / SCALE;
1065 static void make_view(struct s_file *fp,
1067 char v[][MAXSTR], int c)
1069 int i, wi = incw(fp);
1071 struct s_view *wp = fp->wv + wi;
1080 for (i = 0; i < c; i++)
1082 if (strcmp(k[i], "target") == 0)
1083 make_ref(v[i], targ_wi + wi);
1085 if (strcmp(k[i], "origin") == 0)
1087 float x = 0.f, y = 0.f, z = 0.f;
1089 sscanf(v[i], "%f %f %f", &x, &y, &z);
1091 wp->p[0] = +x / SCALE;
1092 wp->p[1] = +z / SCALE;
1093 wp->p[2] = -y / SCALE;
1098 static void make_jump(struct s_file *fp,
1100 char v[][MAXSTR], int c)
1102 int i, ji = incj(fp);
1104 struct s_jump *jp = fp->jv + ji;
1114 for (i = 0; i < c; i++)
1116 if (strcmp(k[i], "radius") == 0)
1117 sscanf(v[i], "%f", &jp->r);
1119 if (strcmp(k[i], "target") == 0)
1120 make_ref(v[i], targ_ji + ji);
1122 if (strcmp(k[i], "origin") == 0)
1124 float x = 0.f, y = 0.f, z = 0.f;
1126 sscanf(v[i], "%f %f %f", &x, &y, &z);
1128 jp->p[0] = +x / SCALE;
1129 jp->p[1] = +z / SCALE;
1130 jp->p[2] = -y / SCALE;
1135 static void make_swch(struct s_file *fp,
1137 char v[][MAXSTR], int c)
1139 int i, xi = incx(fp);
1141 struct s_swch *xp = fp->xv + xi;
1154 for (i = 0; i < c; i++)
1156 if (strcmp(k[i], "radius") == 0)
1157 sscanf(v[i], "%f", &xp->r);
1159 if (strcmp(k[i], "target") == 0)
1160 make_ref(v[i], &xp->pi);
1162 if (strcmp(k[i], "timer") == 0)
1164 sscanf(v[i], "%f", &xp->t0);
1168 if (strcmp(k[i], "state") == 0)
1171 xp->f0 = atoi(v[i]);
1174 if (strcmp(k[i], "invisible") == 0)
1177 if (strcmp(k[i], "origin") == 0)
1179 float x = 0.f, y = 0.f, z = 0.f;
1181 sscanf(v[i], "%f %f %f", &x, &y, &z);
1183 xp->p[0] = +x / SCALE;
1184 xp->p[1] = +z / SCALE;
1185 xp->p[2] = -y / SCALE;
1190 static void make_targ(struct s_file *fp,
1192 char v[][MAXSTR], int c)
1196 targ_p[targ_n][0] = 0.f;
1197 targ_p[targ_n][1] = 0.f;
1198 targ_p[targ_n][2] = 0.f;
1200 for (i = 0; i < c; i++)
1202 if (strcmp(k[i], "targetname") == 0)
1203 make_sym(v[i], targ_n);
1205 if (strcmp(k[i], "origin") == 0)
1207 float x = 0.f, y = 0.f, z = 0.f;
1209 sscanf(v[i], "%f %f %f", &x, &y, &z);
1211 targ_p[targ_n][0] = +x / SCALE;
1212 targ_p[targ_n][1] = +z / SCALE;
1213 targ_p[targ_n][2] = -y / SCALE;
1220 static void make_ball(struct s_file *fp,
1222 char v[][MAXSTR], int c)
1224 int i, ui = incu(fp);
1226 struct s_ball *up = fp->uv + ui;
1233 for (i = 0; i < c; i++)
1235 if (strcmp(k[i], "radius") == 0)
1236 sscanf(v[i], "%f", &up->r);
1238 if (strcmp(k[i], "origin") == 0)
1240 float x = 0.f, y = 0.f, z = 0.f;
1242 sscanf(v[i], "%f %f %f", &x, &y, &z);
1244 up->p[0] = +(x) / SCALE;
1245 up->p[1] = +(z - 24) / SCALE;
1246 up->p[2] = -(y) / SCALE;
1250 up->p[1] += up->r + SMALL;
1253 /*---------------------------------------------------------------------------*/
1255 static void read_ent(struct s_file *fp, fs_file fin)
1257 char k[MAXKEY][MAXSTR];
1258 char v[MAXKEY][MAXSTR];
1259 int t, i = 0, c = 0;
1263 while ((t = map_token(fin, -1, k[c], v[c])))
1267 if (strcmp(k[c], "classname") == 0)
1271 if (t == T_BEG) read_lump(fp, fin);
1272 if (t == T_END) break;
1275 if (!strcmp(v[i], "light")) make_item(fp, k, v, c);
1276 if (!strcmp(v[i], "item_health_large")) make_item(fp, k, v, c);
1277 if (!strcmp(v[i], "item_health_small")) make_item(fp, k, v, c);
1278 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1279 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1280 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1281 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1282 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1283 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1284 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1285 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1286 if (!strcmp(v[i], "worldspawn"))
1288 read_dict_entries = 1;
1289 make_body(fp, k, v, c, l0);
1291 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1292 if (!strcmp(v[i], "func_rotating")) make_body(fp, k, v, c, l0);
1293 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1296 static void read_map(struct s_file *fp, fs_file fin)
1302 while ((t = map_token(fin, -1, k, v)))
1307 /*---------------------------------------------------------------------------*/
1309 /* Test the location of a point with respect to a side plane. */
1311 static int fore_side(const float p[3], const struct s_side *sp)
1313 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1316 static int on_side(const float p[3], const struct s_side *sp)
1318 float d = v_dot(p, sp->n) - sp->d;
1320 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1323 /*---------------------------------------------------------------------------*/
1325 * Confirm that the addition of a vert would not result in degenerate
1329 static int ok_vert(const struct s_file *fp,
1330 const struct s_lump *lp, const float p[3])
1335 for (i = 0; i < lp->vc; i++)
1337 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1341 if (v_len(r) < SMALL)
1347 /*---------------------------------------------------------------------------*/
1350 * The following functions take the set of planes defining a lump and
1351 * find the verts, edges, and geoms that describe its boundaries. To
1352 * do this, they first find the verts, and then search these verts for
1353 * valid edges and geoms. It may be more efficient to compute edges
1354 * and geoms directly by clipping down infinite line segments and
1355 * planes, but this would be more complex and prone to numerical
1360 * Given 3 side planes, compute the point of intersection, if any.
1361 * Confirm that this point falls within the current lump, and that it
1362 * is unique. Add it as a vert of the solid.
1364 static void clip_vert(struct s_file *fp,
1365 struct s_lump *lp, int si, int sj, int sk)
1367 float M[16], X[16], I[16];
1371 d[0] = fp->sv[si].d;
1372 d[1] = fp->sv[sj].d;
1373 d[2] = fp->sv[sk].d;
1375 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1382 for (i = 0; i < lp->sc; i++)
1384 int si = fp->iv[lp->s0 + i];
1386 if (fore_side(p, fp->sv + si))
1390 if (ok_vert(fp, lp, p))
1392 v_cpy(fp->vv[fp->vc].p, p);
1394 fp->iv[fp->ic] = fp->vc;
1403 * Given two side planes, find an edge along their intersection by
1404 * finding a pair of vertices that fall on both planes. Add it to the
1407 static void clip_edge(struct s_file *fp,
1408 struct s_lump *lp, int si, int sj)
1412 for (i = 1; i < lp->vc; i++)
1414 int vi = fp->iv[lp->v0 + i];
1416 if (!on_side(fp->vv[vi].p, fp->sv + si) ||
1417 !on_side(fp->vv[vi].p, fp->sv + sj))
1420 for (j = 0; j < i; j++)
1422 int vj = fp->iv[lp->v0 + j];
1424 if (on_side(fp->vv[vj].p, fp->sv + si) &&
1425 on_side(fp->vv[vj].p, fp->sv + sj))
1427 fp->ev[fp->ec].vi = vi;
1428 fp->ev[fp->ec].vj = vj;
1430 fp->iv[fp->ic] = fp->ec;
1441 * Find all verts that lie on the given side of the lump. Sort these
1442 * verts to have a counter-clockwise winding about the plane normal.
1443 * Add the resulting convex polygon to the lump.
1445 static void clip_face(struct s_file *fp,
1446 struct s_lump *lp, int si)
1448 int m[256], d, i, j, n = 0;
1454 struct s_side *sp = fp->sv + si;
1458 for (i = 0; i < lp->vc; i++)
1459 if (on_side(fp->vv[fp->iv[lp->v0 + i]].p, sp))
1464 for (i = 1; i < n; i++)
1465 for (j = i + 1; j < n; j++)
1467 float *p0 = fp->vv[fp->iv[lp->v0 + m[0]]].p;
1468 float *p1 = fp->vv[fp->iv[lp->v0 + m[i]]].p;
1469 float *p2 = fp->vv[fp->iv[lp->v0 + m[j]]].p;
1475 if (v_dot(w, sp->n) < 0.0f)
1485 fp->iv[inci(fp)] = n;
1488 for (i = 0; i < n; i++)
1490 fp->iv[inci(fp)] = m[i];
1496 * Create geoms to tessellate the given convex polygon.
1498 static void clip_geom(struct s_file *fp,
1499 struct s_lump *lp, int si, int fi)
1506 for (i = 0; i < n; i++)
1508 int vi = fp->iv[lp->v0 + fp->iv[fi + i]];
1512 v_add(v, fp->vv[vi].p, plane_p[si]);
1514 fp->tv[t[i]].u[0] = v_dot(v, plane_u[si]);
1515 fp->tv[t[i]].u[1] = v_dot(v, plane_v[si]);
1518 for (i = 0; i < n - 2; i++)
1520 fp->gv[fp->gc].mi = plane_m[si];
1522 fp->gv[fp->gc].ti = t[0];
1523 fp->gv[fp->gc].tj = t[i + 1];
1524 fp->gv[fp->gc].tk = t[i + 2];
1526 fp->gv[fp->gc].si = si;
1527 fp->gv[fp->gc].sj = si;
1528 fp->gv[fp->gc].sk = si;
1530 fp->gv[fp->gc].vi = fp->iv[lp->v0 + fp->iv[fi]];
1531 fp->gv[fp->gc].vj = fp->iv[lp->v0 + fp->iv[fi + i + 1]];
1532 fp->gv[fp->gc].vk = fp->iv[lp->v0 + fp->iv[fi + i + 2]];
1534 fp->iv[fp->ic] = fp->gc;
1542 * Iterate the sides of the lump, attempting to generate a new vert for
1543 * each trio of planes, a new edge for each pair of planes, and a new
1544 * set of geom for each visible plane.
1546 static void clip_lump(struct s_file *fp, struct s_lump *lp)
1553 for (i = 2; i < lp->sc; i++)
1554 for (j = 1; j < i; j++)
1555 for (k = 0; k < j; k++)
1559 fp->iv[lp->s0 + k]);
1564 for (i = 1; i < lp->sc; i++)
1565 for (j = 0; j < i; j++)
1568 fp->iv[lp->s0 + j]);
1573 for (i = 0; i < lp->sc; i++)
1574 clip_face(fp, lp, fp->iv[lp->s0 + i]);
1581 for (i = 0; i < lp->sc; i++)
1583 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0.0f)
1584 clip_geom(fp, lp, fp->iv[lp->s0 + i], fi);
1586 fi += fp->iv[fi] + 1;
1589 for (i = 0; i < lp->sc; i++)
1590 if (plane_f[fp->iv[lp->s0 + i]])
1594 static void clip_file(struct s_file *fp)
1598 for (i = 0; i < fp->lc; i++)
1599 clip_lump(fp, fp->lv + i);
1602 /*---------------------------------------------------------------------------*/
1605 * For each body element type, determine if element 'p' is equivalent
1606 * to element 'q'. This is more than a simple memory compare. It
1607 * effectively snaps mtrls and verts together, and may reverse the
1608 * winding of an edge or a geom. This is done in order to maximize
1609 * the number of elements that can be eliminated.
1612 static int comp_mtrl(const struct s_mtrl *mp, const struct s_mtrl *mq)
1614 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1615 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1616 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1617 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1619 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1620 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1621 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1622 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1624 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1625 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1626 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1627 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1629 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1630 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1631 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1632 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1634 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1636 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1641 static int comp_vert(const struct s_vert *vp, const struct s_vert *vq)
1643 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1644 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1645 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1650 static int comp_edge(const struct s_edge *ep, const struct s_edge *eq)
1652 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1653 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1658 static int comp_side(const struct s_side *sp, const struct s_side *sq)
1660 if (fabs(sp->d - sq->d) > SMALL) return 0;
1661 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1666 static int comp_texc(const struct s_texc *tp, const struct s_texc *tq)
1668 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1669 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1674 static int comp_geom(const struct s_geom *gp, const struct s_geom *gq)
1676 if (gp->mi != gq->mi) return 0;
1678 if (gp->ti != gq->ti) return 0;
1679 if (gp->si != gq->si) return 0;
1680 if (gp->vi != gq->vi) return 0;
1682 if (gp->tj != gq->tj) return 0;
1683 if (gp->sj != gq->sj) return 0;
1684 if (gp->vj != gq->vj) return 0;
1686 if (gp->tk != gq->tk) return 0;
1687 if (gp->sk != gq->sk) return 0;
1688 if (gp->vk != gq->vk) return 0;
1693 /*---------------------------------------------------------------------------*/
1696 * For each file element type, replace all references to element 'i'
1697 * with a reference to element 'j'. These are used when optimizing
1698 * and sorting the file.
1701 static void swap_mtrl(struct s_file *fp, int mi, int mj)
1705 for (i = 0; i < fp->gc; i++)
1706 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1707 for (i = 0; i < fp->rc; i++)
1708 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1711 static int vert_swaps[MAXV];
1713 static void apply_vert_swaps(struct s_file *fp)
1717 for (i = 0; i < fp->ec; i++)
1719 fp->ev[i].vi = vert_swaps[fp->ev[i].vi];
1720 fp->ev[i].vj = vert_swaps[fp->ev[i].vj];
1723 for (i = 0; i < fp->gc; i++)
1725 fp->gv[i].vi = vert_swaps[fp->gv[i].vi];
1726 fp->gv[i].vj = vert_swaps[fp->gv[i].vj];
1727 fp->gv[i].vk = vert_swaps[fp->gv[i].vk];
1730 for (i = 0; i < fp->lc; i++)
1731 for (j = 0; j < fp->lv[i].vc; j++)
1732 fp->iv[fp->lv[i].v0 + j] = vert_swaps[fp->iv[fp->lv[i].v0 + j]];
1735 static void swap_vert(struct s_file *fp, int vi, int vj)
1739 for (i = 0; i < fp->ec; i++)
1741 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1742 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1745 for (i = 0; i < fp->gc; i++)
1747 if (fp->gv[i].vi == vi) fp->gv[i].vi = vj;
1748 if (fp->gv[i].vj == vi) fp->gv[i].vj = vj;
1749 if (fp->gv[i].vk == vi) fp->gv[i].vk = vj;
1752 for (i = 0; i < fp->lc; i++)
1753 for (j = 0; j < fp->lv[i].vc; j++)
1754 if (fp->iv[fp->lv[i].v0 + j] == vi)
1755 fp->iv[fp->lv[i].v0 + j] = vj;
1758 static int edge_swaps[MAXE];
1760 static void apply_edge_swaps(struct s_file *fp)
1764 for (i = 0; i < fp->lc; i++)
1765 for (j = 0; j < fp->lv[i].ec; j++)
1766 fp->iv[fp->lv[i].e0 + j] = edge_swaps[fp->iv[fp->lv[i].e0 + j]];
1769 static int side_swaps[MAXS];
1771 static void apply_side_swaps(struct s_file *fp)
1775 for (i = 0; i < fp->gc; i++)
1777 fp->gv[i].si = side_swaps[fp->gv[i].si];
1778 fp->gv[i].sj = side_swaps[fp->gv[i].sj];
1779 fp->gv[i].sk = side_swaps[fp->gv[i].sk];
1781 for (i = 0; i < fp->nc; i++)
1782 fp->nv[i].si = side_swaps[fp->nv[i].si];
1784 for (i = 0; i < fp->lc; i++)
1785 for (j = 0; j < fp->lv[i].sc; j++)
1786 fp->iv[fp->lv[i].s0 + j] = side_swaps[fp->iv[fp->lv[i].s0 + j]];
1789 static int texc_swaps[MAXT];
1791 static void apply_texc_swaps(struct s_file *fp)
1795 for (i = 0; i < fp->gc; i++)
1797 fp->gv[i].ti = texc_swaps[fp->gv[i].ti];
1798 fp->gv[i].tj = texc_swaps[fp->gv[i].tj];
1799 fp->gv[i].tk = texc_swaps[fp->gv[i].tk];
1803 static int geom_swaps[MAXG];
1805 static void apply_geom_swaps(struct s_file *fp)
1809 for (i = 0; i < fp->lc; i++)
1810 for (j = 0; j < fp->lv[i].gc; j++)
1811 fp->iv[fp->lv[i].g0 + j] = geom_swaps[fp->iv[fp->lv[i].g0 + j]];
1813 for (i = 0; i < fp->bc; i++)
1814 for (j = 0; j < fp->bv[i].gc; j++)
1815 fp->iv[fp->bv[i].g0 + j] = geom_swaps[fp->iv[fp->bv[i].g0 + j]];
1818 /*---------------------------------------------------------------------------*/
1820 static void uniq_mtrl(struct s_file *fp)
1824 for (i = 0; i < fp->mc; i++)
1826 for (j = 0; j < k; j++)
1827 if (comp_mtrl(fp->mv + i, fp->mv + j))
1829 swap_mtrl(fp, i, j);
1837 fp->mv[k] = fp->mv[i];
1838 swap_mtrl(fp, i, k);
1847 static void uniq_vert(struct s_file *fp)
1851 for (i = 0; i < fp->vc; i++)
1853 for (j = 0; j < k; j++)
1854 if (comp_vert(fp->vv + i, fp->vv + j))
1862 fp->vv[k] = fp->vv[i];
1867 apply_vert_swaps(fp);
1872 static void uniq_edge(struct s_file *fp)
1876 for (i = 0; i < fp->ec; i++)
1878 for (j = 0; j < k; j++)
1879 if (comp_edge(fp->ev + i, fp->ev + j))
1887 fp->ev[k] = fp->ev[i];
1892 apply_edge_swaps(fp);
1897 static int geomlist[MAXV];
1898 static int nextgeom[MAXG];
1900 static void uniq_geom(struct s_file *fp)
1904 for (i = 0; i < MAXV; i++)
1907 for (i = 0; i < fp->gc; i++)
1909 int key = fp->gv[i].vj;
1911 for (j = geomlist[key]; j != -1; j = nextgeom[j])
1912 if (comp_geom(fp->gv + i, fp->gv + j))
1915 fp->gv[k] = fp->gv[i];
1917 nextgeom[k] = geomlist[key];
1927 apply_geom_swaps(fp);
1932 static void uniq_texc(struct s_file *fp)
1936 for (i = 0; i < fp->tc; i++)
1938 for (j = 0; j < k; j++)
1939 if (comp_texc(fp->tv + i, fp->tv + j))
1947 fp->tv[k] = fp->tv[i];
1952 apply_texc_swaps(fp);
1957 static void uniq_side(struct s_file *fp)
1961 for (i = 0; i < fp->sc; i++)
1963 for (j = 0; j < k; j++)
1964 if (comp_side(fp->sv + i, fp->sv + j))
1972 fp->sv[k] = fp->sv[i];
1977 apply_side_swaps(fp);
1982 static void uniq_file(struct s_file *fp)
1984 /* Debug mode skips optimization, producing oversized output files. */
1986 if (debug_output == 0)
1997 /*---------------------------------------------------------------------------*/
2007 static int comp_trip(const void *p, const void *q)
2009 const struct s_trip *tp = (const struct s_trip *) p;
2010 const struct s_trip *tq = (const struct s_trip *) q;
2012 if (tp->vi < tq->vi) return -1;
2013 if (tp->vi > tq->vi) return +1;
2014 if (tp->mi < tq->mi) return -1;
2015 if (tp->mi > tq->mi) return +1;
2020 static void smth_file(struct s_file *fp)
2022 struct s_trip temp, *T;
2024 if (debug_output == 0)
2026 if ((T = (struct s_trip *) malloc(fp->gc * 3 * sizeof (struct s_trip))))
2028 int gi, i, j, k, l, c = 0;
2030 /* Create a list of all non-faceted vertex triplets. */
2032 for (gi = 0; gi < fp->gc; ++gi)
2034 struct s_geom *gp = fp->gv + gi;
2055 /* Sort all triplets by vertex index and material. */
2057 qsort(T, c, sizeof (struct s_trip), comp_trip);
2059 /* For each set of triplets sharing vertex index and material... */
2061 for (i = 0; i < c; i = l)
2065 float N[3], angle = fp->mv[T[i].mi].angle;
2066 const float *Ni = fp->sv[T[i].si].n;
2068 /* Sort the set by side similarity to the first. */
2070 for (j = i + 1; j < c && (T[j].vi == T[i].vi &&
2071 T[j].mi == T[i].mi); ++j)
2073 for (k = j + 1; k < c && (T[k].vi == T[i].vi &&
2074 T[k].mi == T[i].mi); ++k)
2076 const float *Nj = fp->sv[T[j].si].n;
2077 const float *Nk = fp->sv[T[k].si].n;
2079 if (T[j].si != T[k].si && v_dot(Nk, Ni) > v_dot(Nj, Ni))
2088 /* Accumulate all similar side normals. */
2094 for (l = i + 1; l < c && (T[l].vi == T[i].vi &&
2095 T[l].mi == T[i].mi); ++l)
2096 if (T[l].si != T[i].si)
2098 const float *Nl = fp->sv[T[l].si].n;
2100 if (V_DEG(facosf(v_dot(Ni, Nl))) > angle)
2110 /* If at least two normals have been accumulated... */
2114 /* Store the accumulated normal as a new side. */
2118 v_nrm(fp->sv[ss].n, N);
2119 fp->sv[ss].d = 0.0f;
2121 /* Assign the new normal to the merged triplets. */
2123 for (j = i; j < l; ++j)
2128 /* Assign the remapped normals to the original geoms. */
2130 for (i = 0; i < c; ++i)
2132 struct s_geom *gp = fp->gv + T[i].gi;
2134 if (gp->vi == T[i].vi) gp->si = T[i].si;
2135 if (gp->vj == T[i].vi) gp->sj = T[i].si;
2136 if (gp->vk == T[i].vi) gp->sk = T[i].si;
2147 /*---------------------------------------------------------------------------*/
2149 static void sort_file(struct s_file *fp)
2153 /* Sort billboards by material within distance. */
2155 for (i = 0; i < fp->rc; i++)
2156 for (j = i + 1; j < fp->rc; j++)
2157 if ((fp->rv[j].d > fp->rv[i].d) ||
2158 (fp->rv[j].d == fp->rv[i].d &&
2159 fp->rv[j].mi > fp->rv[i].mi))
2164 fp->rv[i] = fp->rv[j];
2168 /* Ensure the first vertex is the lowest. */
2170 for (i = 0; i < fp->vc; i++)
2171 if (fp->vv[0].p[1] > fp->vv[i].p[1])
2176 fp->vv[0] = fp->vv[i];
2179 swap_vert(fp, 0, -1);
2180 swap_vert(fp, i, 0);
2181 swap_vert(fp, -1, i);
2185 /*---------------------------------------------------------------------------*/
2187 static int test_lump_side(const struct s_file *fp,
2188 const struct s_lump *lp,
2189 const struct s_side *sp,
2203 /* Check if the bounding sphere of the lump is completely on one side. */
2205 d = v_dot(bsphere, sp->n) - sp->d;
2207 if (fabs(d) > bsphere[3])
2208 return d > 0 ? 1 : -1;
2210 /* If the given side is part of the given lump, then the lump is behind. */
2212 for (si = 0; si < lp->sc; si++)
2213 if (fp->sv + fp->iv[lp->s0 + si] == sp)
2216 /* Check if each lump vertex is in front of, behind, on the side. */
2218 for (vi = 0; vi < lp->vc; vi++)
2220 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
2226 /* If no verts are behind, the lump is in front, and vice versa. */
2228 if (f > 0 && b == 0) return +1;
2229 if (b > 0 && f == 0) return -1;
2231 /* Else, the lump crosses the side. */
2236 static int node_node(struct s_file *fp, int l0, int lc, float bsphere[][4])
2240 /* Base case. Dump all given lumps into a leaf node. */
2242 fp->nv[fp->nc].si = -1;
2243 fp->nv[fp->nc].ni = -1;
2244 fp->nv[fp->nc].nj = -1;
2245 fp->nv[fp->nc].l0 = l0;
2246 fp->nv[fp->nc].lc = lc;
2256 int li = 0, lic = 0;
2257 int lj = 0, ljc = 0;
2258 int lk = 0, lkc = 0;
2261 /* Find the side that most evenly splits the given lumps. */
2263 for (si = 0; si < fp->sc; si++)
2269 for (li = 0; li < lc; li++)
2270 if ((k = test_lump_side(fp,
2280 if ((d < sjd) || (d == sjd && o < sjo))
2288 /* Flag each lump with its position WRT the side. */
2290 for (li = 0; li < lc; li++)
2293 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2297 switch (test_lump_side(fp,
2303 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10;
2307 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2311 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40;
2316 /* Sort all lumps in the range by their flag values. */
2318 for (li = 1; li < lc; li++)
2319 for (lj = 0; lj < li; lj++)
2320 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
2326 for (i = 0; i < 4; i++)
2328 f = bsphere[l0 + li][i];
2329 bsphere[l0 + li][i] = bsphere[l0 + lj][i];
2330 bsphere[l0 + lj][i] = f;
2333 l = fp->lv[l0 + li];
2334 fp->lv[l0 + li] = fp->lv[l0 + lj];
2335 fp->lv[l0 + lj] = l;
2338 /* Establish the in-front, on, and behind lump ranges. */
2344 for (i = lc - 1; i >= 0; i--)
2345 switch (fp->lv[l0 + i].fl & 0xf0)
2347 case 0x10: li = l0 + i; lic++; break;
2348 case 0x20: lj = l0 + i; ljc++; break;
2349 case 0x40: lk = l0 + i; lkc++; break;
2352 /* Add the lumps on the side to the node. */
2357 fp->nv[i].ni = node_node(fp, li, lic, bsphere);
2359 fp->nv[i].nj = node_node(fp, lk, lkc, bsphere);
2368 * Compute a bounding sphere for a lump (not optimal)
2370 static void lump_bounding_sphere(struct s_file *fp,
2381 bbox[0] = bbox[3] = fp->vv[fp->iv[lp->v0]].p[0];
2382 bbox[1] = bbox[4] = fp->vv[fp->iv[lp->v0]].p[1];
2383 bbox[2] = bbox[5] = fp->vv[fp->iv[lp->v0]].p[2];
2385 for (i = 1; i < lp->vc; i++)
2387 struct s_vert *vp = fp->vv + fp->iv[lp->v0 + i];
2390 for (j = 0; j < 3; j++)
2391 if (vp->p[j] < bbox[j])
2394 for (j = 0; j < 3; j++)
2395 if (vp->p[j] > bbox[j + 3])
2396 bbox[j + 3] = vp->p[j];
2401 for (i = 0; i < 3; i++)
2403 bsphere[i] = (bbox[i] + bbox[i + 3]) / 2;
2404 r += (bsphere[i] - bbox[i]) * (bsphere[i] - bbox[i]);
2407 bsphere[3] = fsqrtf(r);
2410 static void node_file(struct s_file *fp)
2412 float bsphere[MAXL][4];
2415 /* Compute a bounding sphere for each lump. */
2417 for (i = 0; i < fp->lc; i++)
2418 lump_bounding_sphere(fp, fp->lv + i, bsphere[i]);
2420 /* Sort the lumps of each body into BSP nodes. */
2422 for (i = 0; i < fp->bc; i++)
2423 fp->bv[i].ni = node_node(fp, fp->bv[i].l0, fp->bv[i].lc, bsphere);
2426 /*---------------------------------------------------------------------------*/
2428 static void dump_file(struct s_file *p, const char *name)
2435 /* Count the number of solid lumps. */
2437 for (i = 0; i < p->lc; i++)
2438 if ((p->lv[i].fl & 1) == 0)
2441 /* Count the number of visible geoms. */
2443 m = p->rc + (p->zc + p->jc + p->xc) * 32;
2445 for (i = 0; i < p->hc; i++)
2446 if (p->hv[i].t == ITEM_COIN)
2451 for (i = 0; i < p->bc; i++)
2453 for (j = 0; j < p->bv[i].lc; j++)
2454 m += p->lv[p->bv[i].l0 + j].gc;
2458 /* Count the total value of all coins. */
2460 for (i = 0; i < p->hc; i++)
2461 if (p->hv[i].t == ITEM_COIN)
2464 printf("%s (%d/%d/$%d)\n"
2465 " mtrl vert edge side texc"
2466 " geom lump path node body\n"
2467 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2468 " item goal view jump swch"
2469 " bill ball char dict indx\n"
2470 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2472 p->mc, p->vc, p->ec, p->sc, p->tc,
2473 p->gc, p->lc, p->pc, p->nc, p->bc,
2474 p->hc, p->zc, p->wc, p->jc, p->xc,
2475 p->rc, p->uc, p->ac, p->dc, p->ic);
2478 int main(int argc, char *argv[])
2480 char src[MAXSTR] = "";
2481 char dst[MAXSTR] = "";
2485 if (!fs_init(argv[0]))
2487 fprintf(stderr, "Failure to initialize virtual file system: %s\n",
2494 input_file = argv[1];
2496 if (argc > 3 && strcmp(argv[3], "--debug") == 0)
2499 strncpy(src, argv[1], MAXSTR - 1);
2500 strncpy(dst, argv[1], MAXSTR - 1);
2502 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2503 strcpy(dst + strlen(dst) - 4, ".sol");
2505 strcat(dst, ".sol");
2507 fs_add_path (dir_name(src));
2508 fs_set_write_dir(dir_name(dst));
2510 if ((fin = fs_open(base_name(src, NULL), "r")))
2512 if (!fs_add_path_with_archives(argv[2]))
2514 fprintf(stderr, "Failure to establish data directory\n");
2534 sol_stor(&f, base_name(dst, NULL));
2541 else fprintf(stderr, "Usage: %s <map> <data> [--debug]\n", argv[0]);