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;
893 for (i = 0; i < c; i++)
895 if (strcmp(k[i], "targetname") == 0)
898 else if (strcmp(k[i], "target") == 0)
899 make_ref(v[i], &bp->pi);
901 else if (strcmp(k[i], "material") == 0)
902 mi = read_mtrl(fp, v[i]);
904 else if (strcmp(k[i], "model") == 0)
905 read_obj(fp, v[i], mi);
907 else if (strcmp(k[i], "origin") == 0)
908 sscanf(v[i], "%f %f %f", &x, &y, &z);
910 else if (read_dict_entries && strcmp(k[i], "classname") != 0)
911 make_dict(fp, k[i], v[i]);
915 bp->lc = fp->lc - l0;
917 bp->gc = fp->gc - g0;
919 for (i = 0; i < bp->gc; i++)
920 fp->iv[inci(fp)] = g0++;
926 for (i = v0; i < fp->vc; i++)
927 v_add(fp->vv[i].p, fp->vv[i].p, p);
929 read_dict_entries = 0;
932 static void make_item(struct s_file *fp,
934 char v[][MAXSTR], int c)
936 int i, hi = inch(fp);
938 struct s_item *hp = fp->hv + hi;
947 for (i = 0; i < c; i++)
949 if (strcmp(k[i], "classname") == 0)
951 if (strcmp(v[i], "light") == 0)
953 else if (strcmp(v[i], "item_health_large") == 0)
955 else if (strcmp(v[i], "item_health_small") == 0)
959 if (strcmp(k[i], "light") == 0)
960 sscanf(v[i], "%d", &hp->n);
962 if (strcmp(k[i], "origin") == 0)
964 float x = 0.f, y = 0.f, z = 0.f;
966 sscanf(v[i], "%f %f %f", &x, &y, &z);
968 hp->p[0] = +x / SCALE;
969 hp->p[1] = +z / SCALE;
970 hp->p[2] = -y / SCALE;
975 static void make_bill(struct s_file *fp,
977 char v[][MAXSTR], int c)
979 int i, ri = incr(fp);
981 struct s_bill *rp = fp->rv + ri;
983 memset(rp, 0, sizeof (struct s_bill));
986 for (i = 0; i < c; i++)
988 if (strcmp(k[i], "width") == 0)
989 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
990 if (strcmp(k[i], "height") == 0)
991 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
993 if (strcmp(k[i], "xrot") == 0)
994 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
995 if (strcmp(k[i], "yrot") == 0)
996 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
997 if (strcmp(k[i], "zrot") == 0)
998 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
1000 if (strcmp(k[i], "time") == 0)
1001 sscanf(v[i], "%f", &rp->t);
1002 if (strcmp(k[i], "dist") == 0)
1003 sscanf(v[i], "%f", &rp->d);
1004 if (strcmp(k[i], "flag") == 0)
1005 sscanf(v[i], "%d", &rp->fl);
1007 if (strcmp(k[i], "image") == 0)
1009 rp->mi = read_mtrl(fp, v[i]);
1010 fp->mv[rp->mi].fl |= M_CLAMPED;
1013 if (strcmp(k[i], "origin") == 0)
1015 float x = 0.f, y = 0.f, z = 0.f;
1017 sscanf(v[i], "%f %f %f", &x, &y, &z);
1019 rp->p[0] = +x / SCALE;
1020 rp->p[1] = +z / SCALE;
1021 rp->p[2] = -y / SCALE;
1025 if (rp->fl & B_ADDITIVE)
1026 fp->mv[rp->mi].fl |= M_ADDITIVE;
1029 static void make_goal(struct s_file *fp,
1031 char v[][MAXSTR], int c)
1033 int i, zi = incz(fp);
1035 struct s_goal *zp = fp->zv + zi;
1042 for (i = 0; i < c; i++)
1044 if (strcmp(k[i], "radius") == 0)
1045 sscanf(v[i], "%f", &zp->r);
1047 if (strcmp(k[i], "origin") == 0)
1049 float x = 0.f, y = 0.f, z = 0.f;
1051 sscanf(v[i], "%f %f %f", &x, &y, &z);
1053 zp->p[0] = +(x) / SCALE;
1054 zp->p[1] = +(z - 24) / SCALE;
1055 zp->p[2] = -(y) / SCALE;
1060 static void make_view(struct s_file *fp,
1062 char v[][MAXSTR], int c)
1064 int i, wi = incw(fp);
1066 struct s_view *wp = fp->wv + wi;
1075 for (i = 0; i < c; i++)
1077 if (strcmp(k[i], "target") == 0)
1078 make_ref(v[i], targ_wi + wi);
1080 if (strcmp(k[i], "origin") == 0)
1082 float x = 0.f, y = 0.f, z = 0.f;
1084 sscanf(v[i], "%f %f %f", &x, &y, &z);
1086 wp->p[0] = +x / SCALE;
1087 wp->p[1] = +z / SCALE;
1088 wp->p[2] = -y / SCALE;
1093 static void make_jump(struct s_file *fp,
1095 char v[][MAXSTR], int c)
1097 int i, ji = incj(fp);
1099 struct s_jump *jp = fp->jv + ji;
1109 for (i = 0; i < c; i++)
1111 if (strcmp(k[i], "radius") == 0)
1112 sscanf(v[i], "%f", &jp->r);
1114 if (strcmp(k[i], "target") == 0)
1115 make_ref(v[i], targ_ji + ji);
1117 if (strcmp(k[i], "origin") == 0)
1119 float x = 0.f, y = 0.f, z = 0.f;
1121 sscanf(v[i], "%f %f %f", &x, &y, &z);
1123 jp->p[0] = +x / SCALE;
1124 jp->p[1] = +z / SCALE;
1125 jp->p[2] = -y / SCALE;
1130 static void make_swch(struct s_file *fp,
1132 char v[][MAXSTR], int c)
1134 int i, xi = incx(fp);
1136 struct s_swch *xp = fp->xv + xi;
1149 for (i = 0; i < c; i++)
1151 if (strcmp(k[i], "radius") == 0)
1152 sscanf(v[i], "%f", &xp->r);
1154 if (strcmp(k[i], "target") == 0)
1155 make_ref(v[i], &xp->pi);
1157 if (strcmp(k[i], "timer") == 0)
1159 sscanf(v[i], "%f", &xp->t0);
1163 if (strcmp(k[i], "state") == 0)
1166 xp->f0 = atoi(v[i]);
1169 if (strcmp(k[i], "invisible") == 0)
1172 if (strcmp(k[i], "origin") == 0)
1174 float x = 0.f, y = 0.f, z = 0.f;
1176 sscanf(v[i], "%f %f %f", &x, &y, &z);
1178 xp->p[0] = +x / SCALE;
1179 xp->p[1] = +z / SCALE;
1180 xp->p[2] = -y / SCALE;
1185 static void make_targ(struct s_file *fp,
1187 char v[][MAXSTR], int c)
1191 targ_p[targ_n][0] = 0.f;
1192 targ_p[targ_n][1] = 0.f;
1193 targ_p[targ_n][2] = 0.f;
1195 for (i = 0; i < c; i++)
1197 if (strcmp(k[i], "targetname") == 0)
1198 make_sym(v[i], targ_n);
1200 if (strcmp(k[i], "origin") == 0)
1202 float x = 0.f, y = 0.f, z = 0.f;
1204 sscanf(v[i], "%f %f %f", &x, &y, &z);
1206 targ_p[targ_n][0] = +x / SCALE;
1207 targ_p[targ_n][1] = +z / SCALE;
1208 targ_p[targ_n][2] = -y / SCALE;
1215 static void make_ball(struct s_file *fp,
1217 char v[][MAXSTR], int c)
1219 int i, ui = incu(fp);
1221 struct s_ball *up = fp->uv + ui;
1228 for (i = 0; i < c; i++)
1230 if (strcmp(k[i], "radius") == 0)
1231 sscanf(v[i], "%f", &up->r);
1233 if (strcmp(k[i], "origin") == 0)
1235 float x = 0.f, y = 0.f, z = 0.f;
1237 sscanf(v[i], "%f %f %f", &x, &y, &z);
1239 up->p[0] = +(x) / SCALE;
1240 up->p[1] = +(z - 24) / SCALE;
1241 up->p[2] = -(y) / SCALE;
1245 up->p[1] += up->r + SMALL;
1248 /*---------------------------------------------------------------------------*/
1250 static void read_ent(struct s_file *fp, fs_file fin)
1252 char k[MAXKEY][MAXSTR];
1253 char v[MAXKEY][MAXSTR];
1254 int t, i = 0, c = 0;
1258 while ((t = map_token(fin, -1, k[c], v[c])))
1262 if (strcmp(k[c], "classname") == 0)
1266 if (t == T_BEG) read_lump(fp, fin);
1267 if (t == T_END) break;
1270 if (!strcmp(v[i], "light")) make_item(fp, k, v, c);
1271 if (!strcmp(v[i], "item_health_large")) make_item(fp, k, v, c);
1272 if (!strcmp(v[i], "item_health_small")) make_item(fp, k, v, c);
1273 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1274 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1275 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1276 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1277 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1278 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1279 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1280 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1281 if (!strcmp(v[i], "worldspawn"))
1283 read_dict_entries = 1;
1284 make_body(fp, k, v, c, l0);
1286 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1287 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1290 static void read_map(struct s_file *fp, fs_file fin)
1296 while ((t = map_token(fin, -1, k, v)))
1301 /*---------------------------------------------------------------------------*/
1303 /* Test the location of a point with respect to a side plane. */
1305 static int fore_side(const float p[3], const struct s_side *sp)
1307 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1310 static int on_side(const float p[3], const struct s_side *sp)
1312 float d = v_dot(p, sp->n) - sp->d;
1314 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1317 /*---------------------------------------------------------------------------*/
1319 * Confirm that the addition of a vert would not result in degenerate
1323 static int ok_vert(const struct s_file *fp,
1324 const struct s_lump *lp, const float p[3])
1329 for (i = 0; i < lp->vc; i++)
1331 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1335 if (v_len(r) < SMALL)
1341 /*---------------------------------------------------------------------------*/
1344 * The following functions take the set of planes defining a lump and
1345 * find the verts, edges, and geoms that describe its boundaries. To
1346 * do this, they first find the verts, and then search these verts for
1347 * valid edges and geoms. It may be more efficient to compute edges
1348 * and geoms directly by clipping down infinite line segments and
1349 * planes, but this would be more complex and prone to numerical
1354 * Given 3 side planes, compute the point of intersection, if any.
1355 * Confirm that this point falls within the current lump, and that it
1356 * is unique. Add it as a vert of the solid.
1358 static void clip_vert(struct s_file *fp,
1359 struct s_lump *lp, int si, int sj, int sk)
1361 float M[16], X[16], I[16];
1365 d[0] = fp->sv[si].d;
1366 d[1] = fp->sv[sj].d;
1367 d[2] = fp->sv[sk].d;
1369 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1376 for (i = 0; i < lp->sc; i++)
1378 int si = fp->iv[lp->s0 + i];
1380 if (fore_side(p, fp->sv + si))
1384 if (ok_vert(fp, lp, p))
1386 v_cpy(fp->vv[fp->vc].p, p);
1388 fp->iv[fp->ic] = fp->vc;
1397 * Given two side planes, find an edge along their intersection by
1398 * finding a pair of vertices that fall on both planes. Add it to the
1401 static void clip_edge(struct s_file *fp,
1402 struct s_lump *lp, int si, int sj)
1406 for (i = 1; i < lp->vc; i++)
1408 int vi = fp->iv[lp->v0 + i];
1410 if (!on_side(fp->vv[vi].p, fp->sv + si) ||
1411 !on_side(fp->vv[vi].p, fp->sv + sj))
1414 for (j = 0; j < i; j++)
1416 int vj = fp->iv[lp->v0 + j];
1418 if (on_side(fp->vv[vj].p, fp->sv + si) &&
1419 on_side(fp->vv[vj].p, fp->sv + sj))
1421 fp->ev[fp->ec].vi = vi;
1422 fp->ev[fp->ec].vj = vj;
1424 fp->iv[fp->ic] = fp->ec;
1435 * Find all verts that lie on the given side of the lump. Sort these
1436 * verts to have a counter-clockwise winding about the plane normal.
1437 * Create geoms to tessellate the resulting convex polygon.
1439 static void clip_geom(struct s_file *fp,
1440 struct s_lump *lp, int si)
1442 int m[256], t[256], d, i, j, n = 0;
1447 struct s_side *sp = fp->sv + si;
1451 for (i = 0; i < lp->vc; i++)
1453 int vi = fp->iv[lp->v0 + i];
1455 if (on_side(fp->vv[vi].p, sp))
1460 v_add(v, fp->vv[vi].p, plane_p[si]);
1462 fp->tv[t[n]].u[0] = v_dot(v, plane_u[si]);
1463 fp->tv[t[n]].u[1] = v_dot(v, plane_v[si]);
1471 for (i = 1; i < n; i++)
1472 for (j = i + 1; j < n; j++)
1474 v_sub(u, fp->vv[m[i]].p, fp->vv[m[0]].p);
1475 v_sub(v, fp->vv[m[j]].p, fp->vv[m[0]].p);
1478 if (v_dot(w, sp->n) < 0.f)
1492 for (i = 0; i < n - 2; i++)
1494 fp->gv[fp->gc].mi = plane_m[si];
1496 fp->gv[fp->gc].ti = t[0];
1497 fp->gv[fp->gc].tj = t[i + 1];
1498 fp->gv[fp->gc].tk = t[i + 2];
1500 fp->gv[fp->gc].si = si;
1501 fp->gv[fp->gc].sj = si;
1502 fp->gv[fp->gc].sk = si;
1504 fp->gv[fp->gc].vi = m[0];
1505 fp->gv[fp->gc].vj = m[i + 1];
1506 fp->gv[fp->gc].vk = m[i + 2];
1508 fp->iv[fp->ic] = fp->gc;
1516 * Iterate the sides of the lump, attempting to generate a new vert for
1517 * each trio of planes, a new edge for each pair of planes, and a new
1518 * set of geom for each visible plane.
1520 static void clip_lump(struct s_file *fp, struct s_lump *lp)
1527 for (i = 2; i < lp->sc; i++)
1528 for (j = 1; j < i; j++)
1529 for (k = 0; k < j; k++)
1533 fp->iv[lp->s0 + k]);
1538 for (i = 1; i < lp->sc; i++)
1539 for (j = 0; j < i; j++)
1542 fp->iv[lp->s0 + j]);
1547 for (i = 0; i < lp->sc; i++)
1548 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0.0f)
1550 fp->iv[lp->s0 + i]);
1552 for (i = 0; i < lp->sc; i++)
1553 if (plane_f[fp->iv[lp->s0 + i]])
1557 static void clip_file(struct s_file *fp)
1561 for (i = 0; i < fp->lc; i++)
1562 clip_lump(fp, fp->lv + i);
1565 /*---------------------------------------------------------------------------*/
1568 * For each body element type, determine if element 'p' is equivalent
1569 * to element 'q'. This is more than a simple memory compare. It
1570 * effectively snaps mtrls and verts together, and may reverse the
1571 * winding of an edge or a geom. This is done in order to maximize
1572 * the number of elements that can be eliminated.
1575 static int comp_mtrl(const struct s_mtrl *mp, const struct s_mtrl *mq)
1577 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1578 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1579 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1580 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1582 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1583 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1584 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1585 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1587 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1588 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1589 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1590 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1592 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1593 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1594 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1595 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1597 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1599 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1604 static int comp_vert(const struct s_vert *vp, const struct s_vert *vq)
1606 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1607 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1608 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1613 static int comp_edge(const struct s_edge *ep, const struct s_edge *eq)
1615 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1616 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1621 static int comp_side(const struct s_side *sp, const struct s_side *sq)
1623 if (fabs(sp->d - sq->d) > SMALL) return 0;
1624 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1629 static int comp_texc(const struct s_texc *tp, const struct s_texc *tq)
1631 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1632 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1637 static int comp_geom(const struct s_geom *gp, const struct s_geom *gq)
1639 if (gp->mi != gq->mi) return 0;
1641 if (gp->ti != gq->ti) return 0;
1642 if (gp->si != gq->si) return 0;
1643 if (gp->vi != gq->vi) return 0;
1645 if (gp->tj != gq->tj) return 0;
1646 if (gp->sj != gq->sj) return 0;
1647 if (gp->vj != gq->vj) return 0;
1649 if (gp->tk != gq->tk) return 0;
1650 if (gp->sk != gq->sk) return 0;
1651 if (gp->vk != gq->vk) return 0;
1656 /*---------------------------------------------------------------------------*/
1659 * For each file element type, replace all references to element 'i'
1660 * with a reference to element 'j'. These are used when optimizing
1661 * and sorting the file.
1664 static void swap_mtrl(struct s_file *fp, int mi, int mj)
1668 for (i = 0; i < fp->gc; i++)
1669 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1670 for (i = 0; i < fp->rc; i++)
1671 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1674 static int vert_swaps[MAXV];
1676 static void apply_vert_swaps(struct s_file *fp)
1680 for (i = 0; i < fp->ec; i++)
1682 fp->ev[i].vi = vert_swaps[fp->ev[i].vi];
1683 fp->ev[i].vj = vert_swaps[fp->ev[i].vj];
1686 for (i = 0; i < fp->gc; i++)
1688 fp->gv[i].vi = vert_swaps[fp->gv[i].vi];
1689 fp->gv[i].vj = vert_swaps[fp->gv[i].vj];
1690 fp->gv[i].vk = vert_swaps[fp->gv[i].vk];
1693 for (i = 0; i < fp->lc; i++)
1694 for (j = 0; j < fp->lv[i].vc; j++)
1695 fp->iv[fp->lv[i].v0 + j] = vert_swaps[fp->iv[fp->lv[i].v0 + j]];
1698 static void swap_vert(struct s_file *fp, int vi, int vj)
1702 for (i = 0; i < fp->ec; i++)
1704 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1705 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1708 for (i = 0; i < fp->gc; i++)
1710 if (fp->gv[i].vi == vi) fp->gv[i].vi = vj;
1711 if (fp->gv[i].vj == vi) fp->gv[i].vj = vj;
1712 if (fp->gv[i].vk == vi) fp->gv[i].vk = vj;
1715 for (i = 0; i < fp->lc; i++)
1716 for (j = 0; j < fp->lv[i].vc; j++)
1717 if (fp->iv[fp->lv[i].v0 + j] == vi)
1718 fp->iv[fp->lv[i].v0 + j] = vj;
1721 static int edge_swaps[MAXE];
1723 static void apply_edge_swaps(struct s_file *fp)
1727 for (i = 0; i < fp->lc; i++)
1728 for (j = 0; j < fp->lv[i].ec; j++)
1729 fp->iv[fp->lv[i].e0 + j] = edge_swaps[fp->iv[fp->lv[i].e0 + j]];
1732 static int side_swaps[MAXS];
1734 static void apply_side_swaps(struct s_file *fp)
1738 for (i = 0; i < fp->gc; i++)
1740 fp->gv[i].si = side_swaps[fp->gv[i].si];
1741 fp->gv[i].sj = side_swaps[fp->gv[i].sj];
1742 fp->gv[i].sk = side_swaps[fp->gv[i].sk];
1744 for (i = 0; i < fp->nc; i++)
1745 fp->nv[i].si = side_swaps[fp->nv[i].si];
1747 for (i = 0; i < fp->lc; i++)
1748 for (j = 0; j < fp->lv[i].sc; j++)
1749 fp->iv[fp->lv[i].s0 + j] = side_swaps[fp->iv[fp->lv[i].s0 + j]];
1752 static int texc_swaps[MAXT];
1754 static void apply_texc_swaps(struct s_file *fp)
1758 for (i = 0; i < fp->gc; i++)
1760 fp->gv[i].ti = texc_swaps[fp->gv[i].ti];
1761 fp->gv[i].tj = texc_swaps[fp->gv[i].tj];
1762 fp->gv[i].tk = texc_swaps[fp->gv[i].tk];
1766 static int geom_swaps[MAXG];
1768 static void apply_geom_swaps(struct s_file *fp)
1772 for (i = 0; i < fp->lc; i++)
1773 for (j = 0; j < fp->lv[i].gc; j++)
1774 fp->iv[fp->lv[i].g0 + j] = geom_swaps[fp->iv[fp->lv[i].g0 + j]];
1776 for (i = 0; i < fp->bc; i++)
1777 for (j = 0; j < fp->bv[i].gc; j++)
1778 fp->iv[fp->bv[i].g0 + j] = geom_swaps[fp->iv[fp->bv[i].g0 + j]];
1781 /*---------------------------------------------------------------------------*/
1783 static void uniq_mtrl(struct s_file *fp)
1787 for (i = 0; i < fp->mc; i++)
1789 for (j = 0; j < k; j++)
1790 if (comp_mtrl(fp->mv + i, fp->mv + j))
1792 swap_mtrl(fp, i, j);
1800 fp->mv[k] = fp->mv[i];
1801 swap_mtrl(fp, i, k);
1810 static void uniq_vert(struct s_file *fp)
1814 for (i = 0; i < fp->vc; i++)
1816 for (j = 0; j < k; j++)
1817 if (comp_vert(fp->vv + i, fp->vv + j))
1825 fp->vv[k] = fp->vv[i];
1830 apply_vert_swaps(fp);
1835 static void uniq_edge(struct s_file *fp)
1839 for (i = 0; i < fp->ec; i++)
1841 for (j = 0; j < k; j++)
1842 if (comp_edge(fp->ev + i, fp->ev + j))
1850 fp->ev[k] = fp->ev[i];
1855 apply_edge_swaps(fp);
1860 static int geomlist[MAXV];
1861 static int nextgeom[MAXG];
1863 static void uniq_geom(struct s_file *fp)
1867 for (i = 0; i < MAXV; i++)
1870 for (i = 0; i < fp->gc; i++)
1872 int key = fp->gv[i].vj;
1874 for (j = geomlist[key]; j != -1; j = nextgeom[j])
1875 if (comp_geom(fp->gv + i, fp->gv + j))
1878 fp->gv[k] = fp->gv[i];
1880 nextgeom[k] = geomlist[key];
1890 apply_geom_swaps(fp);
1895 static void uniq_texc(struct s_file *fp)
1899 for (i = 0; i < fp->tc; i++)
1901 for (j = 0; j < k; j++)
1902 if (comp_texc(fp->tv + i, fp->tv + j))
1910 fp->tv[k] = fp->tv[i];
1915 apply_texc_swaps(fp);
1920 static void uniq_side(struct s_file *fp)
1924 for (i = 0; i < fp->sc; i++)
1926 for (j = 0; j < k; j++)
1927 if (comp_side(fp->sv + i, fp->sv + j))
1935 fp->sv[k] = fp->sv[i];
1940 apply_side_swaps(fp);
1945 static void uniq_file(struct s_file *fp)
1947 /* Debug mode skips optimization, producing oversized output files. */
1949 if (debug_output == 0)
1960 /*---------------------------------------------------------------------------*/
1970 static int comp_trip(const void *p, const void *q)
1972 const struct s_trip *tp = (const struct s_trip *) p;
1973 const struct s_trip *tq = (const struct s_trip *) q;
1975 if (tp->vi < tq->vi) return -1;
1976 if (tp->vi > tq->vi) return +1;
1977 if (tp->mi < tq->mi) return -1;
1978 if (tp->mi > tq->mi) return +1;
1983 static void smth_file(struct s_file *fp)
1985 struct s_trip temp, *T;
1987 if (debug_output == 0)
1989 if ((T = (struct s_trip *) malloc(fp->gc * 3 * sizeof (struct s_trip))))
1991 int gi, i, j, k, l, c = 0;
1993 /* Create a list of all non-faceted vertex triplets. */
1995 for (gi = 0; gi < fp->gc; ++gi)
1997 struct s_geom *gp = fp->gv + gi;
2018 /* Sort all triplets by vertex index and material. */
2020 qsort(T, c, sizeof (struct s_trip), comp_trip);
2022 /* For each set of triplets sharing vertex index and material... */
2024 for (i = 0; i < c; i = l)
2028 float N[3], angle = fp->mv[T[i].mi].angle;
2029 const float *Ni = fp->sv[T[i].si].n;
2031 /* Sort the set by side similarity to the first. */
2033 for (j = i + 1; j < c && (T[j].vi == T[i].vi &&
2034 T[j].mi == T[i].mi); ++j)
2036 for (k = j + 1; k < c && (T[k].vi == T[i].vi &&
2037 T[k].mi == T[i].mi); ++k)
2039 const float *Nj = fp->sv[T[j].si].n;
2040 const float *Nk = fp->sv[T[k].si].n;
2042 if (T[j].si != T[k].si && v_dot(Nk, Ni) > v_dot(Nj, Ni))
2051 /* Accumulate all similar side normals. */
2057 for (l = i + 1; l < c && (T[l].vi == T[i].vi &&
2058 T[l].mi == T[i].mi); ++l)
2059 if (T[l].si != T[i].si)
2061 const float *Nl = fp->sv[T[l].si].n;
2063 if (V_DEG(facosf(v_dot(Ni, Nl))) > angle)
2073 /* If at least two normals have been accumulated... */
2077 /* Store the accumulated normal as a new side. */
2081 v_nrm(fp->sv[ss].n, N);
2082 fp->sv[ss].d = 0.0f;
2084 /* Assign the new normal to the merged triplets. */
2086 for (j = i; j < l; ++j)
2091 /* Assign the remapped normals to the original geoms. */
2093 for (i = 0; i < c; ++i)
2095 struct s_geom *gp = fp->gv + T[i].gi;
2097 if (gp->vi == T[i].vi) gp->si = T[i].si;
2098 if (gp->vj == T[i].vi) gp->sj = T[i].si;
2099 if (gp->vk == T[i].vi) gp->sk = T[i].si;
2110 /*---------------------------------------------------------------------------*/
2112 static void sort_file(struct s_file *fp)
2116 /* Sort billboards by material within distance. */
2118 for (i = 0; i < fp->rc; i++)
2119 for (j = i + 1; j < fp->rc; j++)
2120 if ((fp->rv[j].d > fp->rv[i].d) ||
2121 (fp->rv[j].d == fp->rv[i].d &&
2122 fp->rv[j].mi > fp->rv[i].mi))
2127 fp->rv[i] = fp->rv[j];
2131 /* Ensure the first vertex is the lowest. */
2133 for (i = 0; i < fp->vc; i++)
2134 if (fp->vv[0].p[1] > fp->vv[i].p[1])
2139 fp->vv[0] = fp->vv[i];
2142 swap_vert(fp, 0, -1);
2143 swap_vert(fp, i, 0);
2144 swap_vert(fp, -1, i);
2148 /*---------------------------------------------------------------------------*/
2150 static int test_lump_side(const struct s_file *fp,
2151 const struct s_lump *lp,
2152 const struct s_side *sp,
2166 /* Check if the bounding sphere of the lump is completely on one side. */
2168 d = v_dot(bsphere, sp->n) - sp->d;
2170 if (fabs(d) > bsphere[3])
2171 return d > 0 ? 1 : -1;
2173 /* If the given side is part of the given lump, then the lump is behind. */
2175 for (si = 0; si < lp->sc; si++)
2176 if (fp->sv + fp->iv[lp->s0 + si] == sp)
2179 /* Check if each lump vertex is in front of, behind, on the side. */
2181 for (vi = 0; vi < lp->vc; vi++)
2183 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
2189 /* If no verts are behind, the lump is in front, and vice versa. */
2191 if (f > 0 && b == 0) return +1;
2192 if (b > 0 && f == 0) return -1;
2194 /* Else, the lump crosses the side. */
2199 static int node_node(struct s_file *fp, int l0, int lc, float bsphere[][4])
2203 /* Base case. Dump all given lumps into a leaf node. */
2205 fp->nv[fp->nc].si = -1;
2206 fp->nv[fp->nc].ni = -1;
2207 fp->nv[fp->nc].nj = -1;
2208 fp->nv[fp->nc].l0 = l0;
2209 fp->nv[fp->nc].lc = lc;
2219 int li = 0, lic = 0;
2220 int lj = 0, ljc = 0;
2221 int lk = 0, lkc = 0;
2224 /* Find the side that most evenly splits the given lumps. */
2226 for (si = 0; si < fp->sc; si++)
2232 for (li = 0; li < lc; li++)
2233 if ((k = test_lump_side(fp,
2243 if ((d < sjd) || (d == sjd && o < sjo))
2251 /* Flag each lump with its position WRT the side. */
2253 for (li = 0; li < lc; li++)
2256 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2260 switch (test_lump_side(fp,
2266 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10;
2270 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2274 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40;
2279 /* Sort all lumps in the range by their flag values. */
2281 for (li = 1; li < lc; li++)
2282 for (lj = 0; lj < li; lj++)
2283 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
2289 for (i = 0; i < 4; i++)
2291 f = bsphere[l0 + li][i];
2292 bsphere[l0 + li][i] = bsphere[l0 + lj][i];
2293 bsphere[l0 + lj][i] = f;
2296 l = fp->lv[l0 + li];
2297 fp->lv[l0 + li] = fp->lv[l0 + lj];
2298 fp->lv[l0 + lj] = l;
2301 /* Establish the in-front, on, and behind lump ranges. */
2307 for (i = lc - 1; i >= 0; i--)
2308 switch (fp->lv[l0 + i].fl & 0xf0)
2310 case 0x10: li = l0 + i; lic++; break;
2311 case 0x20: lj = l0 + i; ljc++; break;
2312 case 0x40: lk = l0 + i; lkc++; break;
2315 /* Add the lumps on the side to the node. */
2320 fp->nv[i].ni = node_node(fp, li, lic, bsphere);
2322 fp->nv[i].nj = node_node(fp, lk, lkc, bsphere);
2331 * Compute a bounding sphere for a lump (not optimal)
2333 static void lump_bounding_sphere(struct s_file *fp,
2344 bbox[0] = bbox[3] = fp->vv[fp->iv[lp->v0]].p[0];
2345 bbox[1] = bbox[4] = fp->vv[fp->iv[lp->v0]].p[1];
2346 bbox[2] = bbox[5] = fp->vv[fp->iv[lp->v0]].p[2];
2348 for (i = 1; i < lp->vc; i++)
2350 struct s_vert *vp = fp->vv + fp->iv[lp->v0 + i];
2353 for (j = 0; j < 3; j++)
2354 if (vp->p[j] < bbox[j])
2357 for (j = 0; j < 3; j++)
2358 if (vp->p[j] > bbox[j + 3])
2359 bbox[j + 3] = vp->p[j];
2364 for (i = 0; i < 3; i++)
2366 bsphere[i] = (bbox[i] + bbox[i + 3]) / 2;
2367 r += (bsphere[i] - bbox[i]) * (bsphere[i] - bbox[i]);
2370 bsphere[3] = fsqrtf(r);
2373 static void node_file(struct s_file *fp)
2375 float bsphere[MAXL][4];
2378 /* Compute a bounding sphere for each lump. */
2380 for (i = 0; i < fp->lc; i++)
2381 lump_bounding_sphere(fp, fp->lv + i, bsphere[i]);
2383 /* Sort the lumps of each body into BSP nodes. */
2385 for (i = 0; i < fp->bc; i++)
2386 fp->bv[i].ni = node_node(fp, fp->bv[i].l0, fp->bv[i].lc, bsphere);
2389 /*---------------------------------------------------------------------------*/
2391 static void dump_file(struct s_file *p, const char *name)
2393 /* FIXME: Count visible geoms.
2395 * I'm afraid items break this (not sure though) so leaving it out.
2405 int m = p->rc + p->cc * 128 + (p->zc * p->jc + p->xc) * 32;
2408 /* Count the number of solid lumps. */
2410 for (i = 0; i < p->lc; i++)
2411 if ((p->lv[i].fl & 1) == 0)
2415 /* Count the number of visible geoms. */
2417 for (i = 0; i < p->bc; i++)
2419 for (j = 0; j < p->bv[i].lc; j++)
2420 m += p->lv[p->bv[i].l0 + j].gc;
2425 /* Count the total value of all coins. */
2427 for (i = 0; i < p->hc; i++)
2428 if (p->hv[i].t == ITEM_COIN)
2432 printf("%s (%d/%d/$%d)\n"
2434 printf("%s (%d/$%d)\n"
2435 " mtrl vert edge side texc"
2436 " geom lump path node body\n"
2437 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2438 " item goal view jump swch"
2439 " bill ball char dict indx\n"
2440 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2445 p->mc, p->vc, p->ec, p->sc, p->tc,
2446 p->gc, p->lc, p->pc, p->nc, p->bc,
2447 p->hc, p->zc, p->wc, p->jc, p->xc,
2448 p->rc, p->uc, p->ac, p->dc, p->ic);
2451 int main(int argc, char *argv[])
2453 char src[MAXSTR] = "";
2454 char dst[MAXSTR] = "";
2458 if (!fs_init(argv[0]))
2460 fprintf(stderr, "Failure to initialize virtual file system: %s\n",
2467 input_file = argv[1];
2469 if (argc > 3 && strcmp(argv[3], "--debug") == 0)
2472 strncpy(src, argv[1], MAXSTR - 1);
2473 strncpy(dst, argv[1], MAXSTR - 1);
2475 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2476 strcpy(dst + strlen(dst) - 4, ".sol");
2478 strcat(dst, ".sol");
2480 fs_add_path (dir_name(src));
2481 fs_set_write_dir(dir_name(dst));
2483 if ((fin = fs_open(base_name(src, NULL), "r")))
2485 if (!fs_add_path_with_archives(argv[2]))
2487 fprintf(stderr, "Failure to establish data directory\n");
2507 sol_stor(&f, base_name(dst, NULL));
2514 else fprintf(stderr, "Usage: %s <map> <data> [--debug]\n", argv[0]);