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 /*---------------------------------------------------------------------------*/
23 #include "solid_base.h"
26 #include "base_image.h"
27 #include "base_config.h"
37 * The overall design of this map converter is very stupid, but very
38 * simple. It begins by assuming that every mtrl, vert, edge, side,
39 * and texc in the map is unique. It then makes an optimizing pass
40 * that discards redundant information. The result is optimal, though
41 * the process is terribly inefficient.
44 /*---------------------------------------------------------------------------*/
46 static const char *input_file;
47 static int debug_output = 0;
48 static int csv_output = 0;
54 /*---------------------------------------------------------------------------*/
56 /* Ohhhh... arbitrary! */
80 static int overflow(const char *s)
82 printf("%s overflow\n", s);
87 static int incm(struct s_base *fp)
89 return (fp->mc < MAXM) ? fp->mc++ : overflow("mtrl");
92 static int incv(struct s_base *fp)
94 return (fp->vc < MAXV) ? fp->vc++ : overflow("vert");
97 static int ince(struct s_base *fp)
99 return (fp->ec < MAXE) ? fp->ec++ : overflow("edge");
102 static int incs(struct s_base *fp)
104 return (fp->sc < MAXS) ? fp->sc++ : overflow("side");
107 static int inct(struct s_base *fp)
109 return (fp->tc < MAXT) ? fp->tc++ : overflow("texc");
112 static int inco(struct s_base *fp)
114 return (fp->oc < MAXO) ? fp->oc++ : overflow("offs");
117 static int incg(struct s_base *fp)
119 return (fp->gc < MAXG) ? fp->gc++ : overflow("geom");
122 static int incl(struct s_base *fp)
124 return (fp->lc < MAXL) ? fp->lc++ : overflow("lump");
127 static int incn(struct s_base *fp)
129 return (fp->nc < MAXN) ? fp->nc++ : overflow("node");
132 static int incp(struct s_base *fp)
134 return (fp->pc < MAXP) ? fp->pc++ : overflow("path");
137 static int incb(struct s_base *fp)
139 return (fp->bc < MAXB) ? fp->bc++ : overflow("body");
142 static int inch(struct s_base *fp)
144 return (fp->hc < MAXH) ? fp->hc++ : overflow("item");
147 static int incz(struct s_base *fp)
149 return (fp->zc < MAXZ) ? fp->zc++ : overflow("goal");
152 static int incj(struct s_base *fp)
154 return (fp->jc < MAXJ) ? fp->jc++ : overflow("jump");
157 static int incx(struct s_base *fp)
159 return (fp->xc < MAXX) ? fp->xc++ : overflow("swch");
162 static int incr(struct s_base *fp)
164 return (fp->rc < MAXR) ? fp->rc++ : overflow("bill");
167 static int incu(struct s_base *fp)
169 return (fp->uc < MAXU) ? fp->uc++ : overflow("ball");
172 static int incw(struct s_base *fp)
174 return (fp->wc < MAXW) ? fp->wc++ : overflow("view");
177 static int incd(struct s_base *fp)
179 return (fp->dc < MAXD) ? fp->dc++ : overflow("dict");
182 static int inci(struct s_base *fp)
184 return (fp->ic < MAXI) ? fp->ic++ : overflow("indx");
187 static void init_file(struct s_base *fp)
211 fp->mv = (struct b_mtrl *) calloc(MAXM, sizeof (*fp->mv));
212 fp->vv = (struct b_vert *) calloc(MAXV, sizeof (*fp->vv));
213 fp->ev = (struct b_edge *) calloc(MAXE, sizeof (*fp->ev));
214 fp->sv = (struct b_side *) calloc(MAXS, sizeof (*fp->sv));
215 fp->tv = (struct b_texc *) calloc(MAXT, sizeof (*fp->tv));
216 fp->ov = (struct b_offs *) calloc(MAXO, sizeof (*fp->ov));
217 fp->gv = (struct b_geom *) calloc(MAXG, sizeof (*fp->gv));
218 fp->lv = (struct b_lump *) calloc(MAXL, sizeof (*fp->lv));
219 fp->nv = (struct b_node *) calloc(MAXN, sizeof (*fp->nv));
220 fp->pv = (struct b_path *) calloc(MAXP, sizeof (*fp->pv));
221 fp->bv = (struct b_body *) calloc(MAXB, sizeof (*fp->bv));
222 fp->hv = (struct b_item *) calloc(MAXH, sizeof (*fp->hv));
223 fp->zv = (struct b_goal *) calloc(MAXZ, sizeof (*fp->zv));
224 fp->jv = (struct b_jump *) calloc(MAXJ, sizeof (*fp->jv));
225 fp->xv = (struct b_swch *) calloc(MAXX, sizeof (*fp->xv));
226 fp->rv = (struct b_bill *) calloc(MAXR, sizeof (*fp->rv));
227 fp->uv = (struct b_ball *) calloc(MAXU, sizeof (*fp->uv));
228 fp->wv = (struct b_view *) calloc(MAXW, sizeof (*fp->wv));
229 fp->dv = (struct b_dict *) calloc(MAXD, sizeof (*fp->dv));
230 fp->av = (char *) calloc(MAXA, sizeof (*fp->av));
231 fp->iv = (int *) calloc(MAXI, sizeof (*fp->iv));
234 /*---------------------------------------------------------------------------*/
237 * The following is a small symbol table data structure. Symbols and
238 * their integer values are collected in symv and valv. References
239 * and pointers to their unsatisfied integer values are collected in
240 * refv and pntv. The resolve procedure matches references to symbols
241 * and fills waiting ints with the proper values.
246 static char symv[MAXSYM][MAXSTR];
247 static int valv[MAXSYM];
249 static char refv[MAXSYM][MAXSTR];
250 static int *pntv[MAXSYM];
255 static void make_sym(const char *s, int v)
257 strncpy(symv[strc], s, MAXSTR - 1);
262 static void make_ref(const char *r, int *p)
264 strncpy(refv[refc], r, MAXSTR - 1);
269 static void resolve(void)
273 for (i = 0; i < refc; i++)
274 for (j = 0; j < strc; j++)
275 if (strncmp(refv[i], symv[j], MAXSTR) == 0)
277 *(pntv[i]) = valv[j];
282 /*---------------------------------------------------------------------------*/
285 * The following globals are used to cache target_positions. They are
286 * targeted by various entities and must be resolved in a second pass.
289 static float targ_p [MAXW][3];
290 static int targ_wi[MAXW];
291 static int targ_ji[MAXW];
294 static void targets(struct s_base *fp)
298 for (i = 0; i < fp->wc; i++)
299 v_cpy(fp->wv[i].q, targ_p[targ_wi[i]]);
301 for (i = 0; i < fp->jc; i++)
302 v_cpy(fp->jv[i].q, targ_p[targ_ji[i]]);
305 /*---------------------------------------------------------------------------*/
308 * The following code caches image sizes. Textures are referenced by
309 * name, but their sizes are necessary when computing texture
310 * coordinates. This code allows each file to be accessed only once
311 * regardless of the number of surfaces referring to it.
320 static struct _imagedata *imagedata = NULL;
321 static int image_n = 0;
322 static int image_alloc = 0;
324 #define IMAGE_REALLOC 32
326 static void free_imagedata()
332 for (i = 0; i < image_n; i++)
333 free(imagedata[i].s);
337 image_n = image_alloc = 0;
340 static int size_load(const char *file, int *w, int *h)
344 if ((p = image_load(file, w, h, NULL)))
352 static void size_image(const char *name, int *w, int *h)
359 for (i = 0; i < image_n; i++)
360 if (strncmp(imagedata[i].s, name, MAXSTR) == 0)
371 strcpy(jpg, name); strcat(jpg, ".jpg");
372 strcpy(png, name); strcat(png, ".png");
374 if (size_load(png, w, h) ||
375 size_load(jpg, w, h))
378 if (image_n + 1 >= image_alloc)
380 struct _imagedata *tmp =
381 (struct _imagedata *) malloc(sizeof(struct _imagedata) * (image_alloc + IMAGE_REALLOC));
384 printf("malloc error\n");
389 (void) memcpy(tmp, imagedata, sizeof(struct _imagedata) * image_alloc);
393 image_alloc += IMAGE_REALLOC;
396 imagedata[image_n].s = (char *) calloc(strlen(name) + 1, 1);
397 imagedata[image_n].w = *w;
398 imagedata[image_n].h = *h;
399 strcpy(imagedata[image_n].s, name);
405 /*---------------------------------------------------------------------------*/
407 /* Read the given material file, adding a new material to the solid. */
409 #define scan_vec4(f, s, v) \
410 if (fs_gets((s), sizeof (s), (f))) \
411 sscanf((s), "%f %f %f %f", (v), (v) + 1, (v) + 2, (v) + 3)
413 static int read_mtrl(struct s_base *fp, const char *name)
415 static char line[MAXSTR];
416 static char word[MAXSTR];
422 for (mi = 0; mi < fp->mc; mi++)
423 if (strncmp(name, fp->mv[mi].f, MAXSTR) == 0)
426 mp = fp->mv + incm(fp);
428 strncpy(mp->f, name, PATHMAX - 1);
430 mp->a[0] = mp->a[1] = mp->a[2] = 0.2f;
431 mp->d[0] = mp->d[1] = mp->d[2] = 0.8f;
432 mp->s[0] = mp->s[1] = mp->s[2] = 0.0f;
433 mp->e[0] = mp->e[1] = mp->e[2] = 0.0f;
434 mp->a[3] = mp->d[3] = mp->s[3] = mp->e[3] = 1.0f;
439 if ((fin = fs_open(name, "r")))
441 scan_vec4(fin, line, mp->d);
442 scan_vec4(fin, line, mp->a);
443 scan_vec4(fin, line, mp->s);
444 scan_vec4(fin, line, mp->e);
446 if (fs_gets(line, sizeof (line), fin))
447 mp->h[0] = strtod(line, NULL);
449 if (fs_gets(line, sizeof (line), fin))
455 while (sscanf(p, "%s%n", word, &n) > 0)
457 if (strcmp(word, "additive") == 0) f |= M_ADDITIVE;
458 else if (strcmp(word, "clamp-s") == 0) f |= M_CLAMP_S;
459 else if (strcmp(word, "clamp-t") == 0) f |= M_CLAMP_T;
460 else if (strcmp(word, "decal") == 0) f |= M_DECAL;
461 else if (strcmp(word, "environment") == 0) f |= M_ENVIRONMENT;
462 else if (strcmp(word, "reflective") == 0) f |= M_REFLECTIVE;
463 else if (strcmp(word, "shadowed") == 0) f |= M_SHADOWED;
464 else if (strcmp(word, "transparent") == 0) f |= M_TRANSPARENT;
465 else if (strcmp(word, "two-sided") == 0) f |= M_TWO_SIDED;
472 if (fs_gets(line, sizeof (line), fin))
473 mp->angle = strtod(line, NULL);
478 fprintf(stderr, "%s: unknown material \"%s\"\n", input_file, name);
485 /*---------------------------------------------------------------------------*/
488 * All bodies with an associated path are assumed to be positioned at
489 * the beginning of that path. These bodies must be moved to the
490 * origin in order for their path transforms to behave correctly.
491 * This is how we get away with defining func_trains with no origin
495 static void move_side(struct b_side *sp, const float p[3])
497 sp->d -= v_dot(sp->n, p);
500 static void move_vert(struct b_vert *vp, const float p[3])
502 v_sub(vp->p, vp->p, p);
505 static void move_lump(struct s_base *fp,
506 struct b_lump *lp, const float p[3])
510 for (i = 0; i < lp->sc; i++)
511 move_side(fp->sv + fp->iv[lp->s0 + i], p);
512 for (i = 0; i < lp->vc; i++)
513 move_vert(fp->vv + fp->iv[lp->v0 + i], p);
516 static void move_body(struct s_base *fp,
521 /* Move the lumps. */
523 for (i = 0; i < bp->lc; i++)
524 move_lump(fp, fp->lv + bp->l0 + i, fp->pv[bp->pi].p);
526 /* Create an array to mark any verts referenced by moved geoms. */
528 if (bp->gc > 0 && (b = (int *) calloc(fp->vc, sizeof (int))))
530 /* Mark the verts. */
532 for (i = 0; i < bp->gc; i++)
534 const struct b_geom *gp = fp->gv + fp->iv[bp->g0 + i];
536 b[fp->ov[gp->oi].vi] = 1;
537 b[fp->ov[gp->oj].vi] = 1;
538 b[fp->ov[gp->ok].vi] = 1;
541 /* Apply the motion to the marked vertices. */
543 for (i = 0; i < fp->vc; ++i)
545 move_vert(fp->vv + i, fp->pv[bp->pi].p);
551 static void move_file(struct s_base *fp)
555 for (i = 0; i < fp->bc; i++)
556 if (fp->bv[i].pi >= 0)
557 move_body(fp, fp->bv + i);
560 /*---------------------------------------------------------------------------*/
563 * This is a basic OBJ loader. It is by no means fully compliant with
564 * the OBJ specification, but it works well with the output of
565 * Wings3D. All faces must be triangles and all vertices must include
566 * normals and texture coordinates. Material names are taken to be
567 * references to Neverball materials, rather than MTL definitions.
570 static void read_vt(struct s_base *fp, const char *line)
572 struct b_texc *tp = fp->tv + inct(fp);
574 sscanf(line, "%f %f", tp->u, tp->u + 1);
577 static void read_vn(struct s_base *fp, const char *line)
579 struct b_side *sp = fp->sv + incs(fp);
581 sscanf(line, "%f %f %f", sp->n, sp->n + 1, sp->n + 2);
584 static void read_v(struct s_base *fp, const char *line)
586 struct b_vert *vp = fp->vv + incv(fp);
588 sscanf(line, "%f %f %f", vp->p, vp->p + 1, vp->p + 2);
591 static void read_f(struct s_base *fp, const char *line,
592 int v0, int t0, int s0, int mi)
594 struct b_geom *gp = fp->gv + incg(fp);
596 struct b_offs *op = fp->ov + (gp->oi = inco(fp));
597 struct b_offs *oq = fp->ov + (gp->oj = inco(fp));
598 struct b_offs *or = fp->ov + (gp->ok = inco(fp));
603 sscanf(line, "%d%c%d%c%d %d%c%d%c%d %d%c%d%c%d",
604 &op->vi, &c1, &op->ti, &c2, &op->si,
605 &oq->vi, &c1, &oq->ti, &c2, &oq->si,
606 &or->vi, &c1, &or->ti, &c2, &or->si);
621 static void read_obj(struct s_base *fp, const char *name, int mi)
631 if ((fin = fs_open(name, "r")))
633 while (fs_gets(line, MAXSTR, fin))
635 if (strncmp(line, "usemtl", 6) == 0)
637 sscanf(line + 6, "%s", mtrl);
638 mi = read_mtrl(fp, mtrl);
641 else if (strncmp(line, "f", 1) == 0)
643 if (fp->mv[mi].d[3] > 0.0f)
644 read_f(fp, line + 1, v0, t0, s0, mi);
647 else if (strncmp(line, "vt", 2) == 0) read_vt(fp, line + 2);
648 else if (strncmp(line, "vn", 2) == 0) read_vn(fp, line + 2);
649 else if (strncmp(line, "v", 1) == 0) read_v (fp, line + 1);
655 /*---------------------------------------------------------------------------*/
657 static float plane_d[MAXS];
658 static float plane_n[MAXS][3];
659 static float plane_p[MAXS][3];
660 static float plane_u[MAXS][3];
661 static float plane_v[MAXS][3];
662 static int plane_f[MAXS];
663 static int plane_m[MAXS];
665 static void make_plane(int pi, float x0, float y0, float z0,
666 float x1, float y1, float z1,
667 float x2, float y2, float z2,
668 float tu, float tv, float r,
669 float su, float sv, int fl, const char *s)
671 static const float base[6][3][3] = {
672 {{ 0, 0, 1 }, { 1, 0, 0 }, { 0, 1, 0 }},
673 {{ 0, 0, -1 }, { 1, 0, 0 }, { 0, 1, 0 }},
674 {{ 1, 0, 0 }, { 0, 0, -1 }, { 0, 1, 0 }},
675 {{ -1, 0, 0 }, { 0, 0, -1 }, { 0, 1, 0 }},
676 {{ 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, -1 }},
677 {{ 0, -1, 0 }, { 1, 0, 0 }, { 0, 0, -1 }},
681 float p0[3], p1[3], p2[3];
682 float u[3], v[3], p[3];
687 size_image(s, &w, &h);
689 plane_f[pi] = fl ? L_DETAIL : 0;
706 v_crs(plane_n[pi], u, v);
707 v_nrm(plane_n[pi], plane_n[pi]);
709 plane_d[pi] = v_dot(plane_n[pi], p1);
711 for (i = 0; i < 6; i++)
712 if ((k = v_dot(plane_n[pi], base[i][0])) >= d)
722 /* Always rotate around the positive axis */
724 m_rot(R, base[n - (n % 2)][0], V_RAD(r));
726 v_mad(p, p, base[n][1], +su * tu / SCALE);
727 v_mad(p, p, base[n][2], -sv * tv / SCALE);
729 m_vxfm(plane_u[pi], R, base[n][1]);
730 m_vxfm(plane_v[pi], R, base[n][2]);
731 m_vxfm(plane_p[pi], R, p);
733 v_scl(plane_u[pi], plane_u[pi], 64.f / w);
734 v_scl(plane_v[pi], plane_v[pi], 64.f / h);
736 v_scl(plane_u[pi], plane_u[pi], 1.f / su);
737 v_scl(plane_v[pi], plane_v[pi], 1.f / sv);
740 /*---------------------------------------------------------------------------*/
749 static int map_token(fs_file fin, int pi, char key[MAXSTR], char val[MAXSTR])
753 if (fs_gets(buf, MAXSTR, fin))
763 /* Scan the beginning or end of a block. */
765 if (buf[0] == '{') return T_BEG;
766 if (buf[0] == '}') return T_END;
768 /* Scan a key-value pair. */
772 strcpy(key, strtok(buf, "\""));
773 (void) strtok(NULL, "\"");
774 strcpy(val, strtok(NULL, "\""));
785 "%s %f %f %f %f %f %d",
786 &c, &x0, &y0, &z0, &c,
787 &c, &x1, &y1, &z1, &c,
788 &c, &x2, &y2, &z2, &c,
789 key, &tu, &tv, &r, &su, &sv, &fl) == 22)
791 make_plane(pi, x0, y0, z0,
794 tu, tv, r, su, sv, fl, key);
798 /* If it's not recognized, it must be uninteresting. */
805 /*---------------------------------------------------------------------------*/
807 /* Parse a lump from the given file and add it to the solid. */
809 static void read_lump(struct s_base *fp, fs_file fin)
815 struct b_lump *lp = fp->lv + incl(fp);
819 while ((t = map_token(fin, fp->sc, k, v)))
823 fp->sv[fp->sc].n[0] = plane_n[fp->sc][0];
824 fp->sv[fp->sc].n[1] = plane_n[fp->sc][1];
825 fp->sv[fp->sc].n[2] = plane_n[fp->sc][2];
826 fp->sv[fp->sc].d = plane_d[fp->sc];
828 plane_m[fp->sc] = read_mtrl(fp, k);
830 fp->iv[fp->ic] = fp->sc;
840 /*---------------------------------------------------------------------------*/
842 static void make_path(struct s_base *fp,
844 char v[][MAXSTR], int c)
846 int i, pi = incp(fp);
848 struct b_path *pp = fp->pv + pi;
858 for (i = 0; i < c; i++)
860 if (strcmp(k[i], "targetname") == 0)
863 if (strcmp(k[i], "target") == 0)
864 make_ref(v[i], &pp->pi);
866 if (strcmp(k[i], "state") == 0)
869 if (strcmp(k[i], "speed") == 0)
870 sscanf(v[i], "%f", &pp->t);
872 if (strcmp(k[i], "smooth") == 0)
875 if (strcmp(k[i], "origin") == 0)
877 float x = 0.f, y = 0.f, z = 0.f;
879 sscanf(v[i], "%f %f %f", &x, &y, &z);
881 pp->p[0] = +x / SCALE;
882 pp->p[1] = +z / SCALE;
883 pp->p[2] = -y / SCALE;
887 * Radiant sets "angle" for yaw-only rotations, "angles"
888 * otherwise. Angles takes priority, so check for angle
892 if (strcmp(k[i], "angle") == 0)
894 static const float Y[3] = { 0.0f, 1.0f, 0.0f };
900 sscanf(v[i], "%f", &y);
901 q_by_axisangle(pp->e, Y, V_RAD(+y));
902 pp->fl |= P_ORIENTED;
905 if (strcmp(k[i], "angles") == 0)
907 static const float X[3] = { 1.0f, 0.0f, 0.0f };
908 static const float Y[3] = { 0.0f, 1.0f, 0.0f };
909 static const float Z[3] = { 0.0f, 0.0f, 1.0f };
911 float x = 0.0f, y = 0.0f, z = 0.0f;
914 /* Pitch, yaw and roll. */
916 sscanf(v[i], "%f %f %f", &x, &y, &z);
918 q_by_axisangle(pp->e, Y, V_RAD(+y));
920 q_by_axisangle(d, Z, V_RAD(-x));
924 q_by_axisangle(d, X, V_RAD(+z));
928 pp->fl |= P_ORIENTED;
933 static void make_dict(struct s_base *fp,
937 int space_left, space_needed, di = incd(fp);
939 struct b_dict *dp = fp->dv + di;
941 space_left = MAXA - fp->ac;
942 space_needed = strlen(k) + 1 + strlen(v) + 1;
944 if (space_needed > space_left)
951 dp->aj = dp->ai + strlen(k) + 1;
952 fp->ac = dp->aj + strlen(v) + 1;
954 strncpy(fp->av + dp->ai, k, space_left);
955 strncpy(fp->av + dp->aj, v, space_left - strlen(k) - 1);
958 static int read_dict_entries = 0;
960 static void make_body(struct s_base *fp,
962 char v[][MAXSTR], int c, int l0)
964 int i, mi = 0, bi = incb(fp);
975 struct b_body *bp = fp->bv + bi;
980 for (i = 0; i < c; i++)
982 if (strcmp(k[i], "targetname") == 0)
985 else if (strcmp(k[i], "target") == 0)
986 make_ref(v[i], &bp->pi);
988 else if (strcmp(k[i], "material") == 0)
989 mi = read_mtrl(fp, v[i]);
991 else if (strcmp(k[i], "model") == 0)
992 read_obj(fp, v[i], mi);
994 else if (strcmp(k[i], "origin") == 0)
995 sscanf(v[i], "%f %f %f", &x, &y, &z);
997 else if (read_dict_entries && strcmp(k[i], "classname") != 0)
998 make_dict(fp, k[i], v[i]);
1002 bp->lc = fp->lc - l0;
1004 bp->gc = fp->gc - g0;
1006 for (i = 0; i < bp->gc; i++)
1007 fp->iv[inci(fp)] = g0++;
1013 for (i = v0; i < fp->vc; i++)
1014 v_add(fp->vv[i].p, fp->vv[i].p, p);
1016 read_dict_entries = 0;
1019 static void make_item(struct s_base *fp,
1021 char v[][MAXSTR], int c)
1023 int i, hi = inch(fp);
1025 struct b_item *hp = fp->hv + hi;
1034 for (i = 0; i < c; i++)
1036 if (strcmp(k[i], "classname") == 0)
1038 if (strcmp(v[i], "light") == 0)
1040 else if (strcmp(v[i], "item_health_large") == 0)
1042 else if (strcmp(v[i], "item_health_small") == 0)
1043 hp->t = ITEM_SHRINK;
1046 if (strcmp(k[i], "light") == 0)
1047 sscanf(v[i], "%d", &hp->n);
1049 if (strcmp(k[i], "origin") == 0)
1051 float x = 0.f, y = 0.f, z = 0.f;
1053 sscanf(v[i], "%f %f %f", &x, &y, &z);
1055 hp->p[0] = +x / SCALE;
1056 hp->p[1] = +z / SCALE;
1057 hp->p[2] = -y / SCALE;
1062 static void make_bill(struct s_base *fp,
1064 char v[][MAXSTR], int c)
1066 int i, ri = incr(fp);
1068 struct b_bill *rp = fp->rv + ri;
1070 memset(rp, 0, sizeof (struct b_bill));
1073 for (i = 0; i < c; i++)
1075 if (strcmp(k[i], "width") == 0)
1076 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
1077 if (strcmp(k[i], "height") == 0)
1078 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
1080 if (strcmp(k[i], "xrot") == 0)
1081 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
1082 if (strcmp(k[i], "yrot") == 0)
1083 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
1084 if (strcmp(k[i], "zrot") == 0)
1085 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
1087 if (strcmp(k[i], "time") == 0)
1088 sscanf(v[i], "%f", &rp->t);
1089 if (strcmp(k[i], "dist") == 0)
1090 sscanf(v[i], "%f", &rp->d);
1091 if (strcmp(k[i], "flag") == 0)
1092 sscanf(v[i], "%d", &rp->fl);
1094 if (strcmp(k[i], "image") == 0)
1096 rp->mi = read_mtrl(fp, v[i]);
1097 fp->mv[rp->mi].fl |= M_CLAMP_S | M_CLAMP_T;
1100 if (strcmp(k[i], "origin") == 0)
1102 float x = 0.f, y = 0.f, z = 0.f;
1104 sscanf(v[i], "%f %f %f", &x, &y, &z);
1106 rp->p[0] = +x / SCALE;
1107 rp->p[1] = +z / SCALE;
1108 rp->p[2] = -y / SCALE;
1112 if (rp->fl & B_ADDITIVE)
1113 fp->mv[rp->mi].fl |= M_ADDITIVE;
1116 static void make_goal(struct s_base *fp,
1118 char v[][MAXSTR], int c)
1120 int i, zi = incz(fp);
1122 struct b_goal *zp = fp->zv + zi;
1129 for (i = 0; i < c; i++)
1131 if (strcmp(k[i], "radius") == 0)
1132 sscanf(v[i], "%f", &zp->r);
1134 if (strcmp(k[i], "origin") == 0)
1136 float x = 0.f, y = 0.f, z = 0.f;
1138 sscanf(v[i], "%f %f %f", &x, &y, &z);
1140 zp->p[0] = +(x) / SCALE;
1141 zp->p[1] = +(z - 24) / SCALE;
1142 zp->p[2] = -(y) / SCALE;
1147 static void make_view(struct s_base *fp,
1149 char v[][MAXSTR], int c)
1151 int i, wi = incw(fp);
1153 struct b_view *wp = fp->wv + wi;
1162 for (i = 0; i < c; i++)
1164 if (strcmp(k[i], "target") == 0)
1165 make_ref(v[i], targ_wi + wi);
1167 if (strcmp(k[i], "origin") == 0)
1169 float x = 0.f, y = 0.f, z = 0.f;
1171 sscanf(v[i], "%f %f %f", &x, &y, &z);
1173 wp->p[0] = +x / SCALE;
1174 wp->p[1] = +z / SCALE;
1175 wp->p[2] = -y / SCALE;
1180 static void make_jump(struct s_base *fp,
1182 char v[][MAXSTR], int c)
1184 int i, ji = incj(fp);
1186 struct b_jump *jp = fp->jv + ji;
1196 for (i = 0; i < c; i++)
1198 if (strcmp(k[i], "radius") == 0)
1199 sscanf(v[i], "%f", &jp->r);
1201 if (strcmp(k[i], "target") == 0)
1202 make_ref(v[i], targ_ji + ji);
1204 if (strcmp(k[i], "origin") == 0)
1206 float x = 0.f, y = 0.f, z = 0.f;
1208 sscanf(v[i], "%f %f %f", &x, &y, &z);
1210 jp->p[0] = +x / SCALE;
1211 jp->p[1] = +z / SCALE;
1212 jp->p[2] = -y / SCALE;
1217 static void make_swch(struct s_base *fp,
1219 char v[][MAXSTR], int c)
1221 int i, xi = incx(fp);
1223 struct b_swch *xp = fp->xv + xi;
1234 for (i = 0; i < c; i++)
1236 if (strcmp(k[i], "radius") == 0)
1237 sscanf(v[i], "%f", &xp->r);
1239 if (strcmp(k[i], "target") == 0)
1240 make_ref(v[i], &xp->pi);
1242 if (strcmp(k[i], "timer") == 0)
1243 sscanf(v[i], "%f", &xp->t);
1245 if (strcmp(k[i], "state") == 0)
1248 if (strcmp(k[i], "invisible") == 0)
1251 if (strcmp(k[i], "origin") == 0)
1253 float x = 0.f, y = 0.f, z = 0.f;
1255 sscanf(v[i], "%f %f %f", &x, &y, &z);
1257 xp->p[0] = +x / SCALE;
1258 xp->p[1] = +z / SCALE;
1259 xp->p[2] = -y / SCALE;
1264 static void make_targ(struct s_base *fp,
1266 char v[][MAXSTR], int c)
1270 targ_p[targ_n][0] = 0.f;
1271 targ_p[targ_n][1] = 0.f;
1272 targ_p[targ_n][2] = 0.f;
1274 for (i = 0; i < c; i++)
1276 if (strcmp(k[i], "targetname") == 0)
1277 make_sym(v[i], targ_n);
1279 if (strcmp(k[i], "origin") == 0)
1281 float x = 0.f, y = 0.f, z = 0.f;
1283 sscanf(v[i], "%f %f %f", &x, &y, &z);
1285 targ_p[targ_n][0] = +x / SCALE;
1286 targ_p[targ_n][1] = +z / SCALE;
1287 targ_p[targ_n][2] = -y / SCALE;
1294 static void make_ball(struct s_base *fp,
1296 char v[][MAXSTR], int c)
1298 int i, ui = incu(fp);
1300 struct b_ball *up = fp->uv + ui;
1307 for (i = 0; i < c; i++)
1309 if (strcmp(k[i], "radius") == 0)
1310 sscanf(v[i], "%f", &up->r);
1312 if (strcmp(k[i], "origin") == 0)
1314 float x = 0.f, y = 0.f, z = 0.f;
1316 sscanf(v[i], "%f %f %f", &x, &y, &z);
1318 up->p[0] = +(x) / SCALE;
1319 up->p[1] = +(z - 24) / SCALE;
1320 up->p[2] = -(y) / SCALE;
1324 up->p[1] += up->r + SMALL;
1327 /*---------------------------------------------------------------------------*/
1329 static void read_ent(struct s_base *fp, fs_file fin)
1331 char k[MAXKEY][MAXSTR];
1332 char v[MAXKEY][MAXSTR];
1333 int t, i = 0, c = 0;
1337 while ((t = map_token(fin, -1, k[c], v[c])))
1341 if (strcmp(k[c], "classname") == 0)
1345 if (t == T_BEG) read_lump(fp, fin);
1346 if (t == T_END) break;
1349 if (!strcmp(v[i], "light")) make_item(fp, k, v, c);
1350 if (!strcmp(v[i], "item_health_large")) make_item(fp, k, v, c);
1351 if (!strcmp(v[i], "item_health_small")) make_item(fp, k, v, c);
1352 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1353 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1354 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1355 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1356 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1357 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1358 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1359 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1360 if (!strcmp(v[i], "worldspawn"))
1362 read_dict_entries = 1;
1363 make_body(fp, k, v, c, l0);
1365 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1366 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1369 static void read_map(struct s_base *fp, fs_file fin)
1375 while ((t = map_token(fin, -1, k, v)))
1380 /*---------------------------------------------------------------------------*/
1382 /* Test the location of a point with respect to a side plane. */
1384 static int fore_side(const float p[3], const struct b_side *sp)
1386 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1389 static int on_side(const float p[3], const struct b_side *sp)
1391 float d = v_dot(p, sp->n) - sp->d;
1393 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1396 /*---------------------------------------------------------------------------*/
1398 * Confirm that the addition of a vert would not result in degenerate
1402 static int ok_vert(const struct s_base *fp,
1403 const struct b_lump *lp, const float p[3])
1408 for (i = 0; i < lp->vc; i++)
1410 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1414 if (v_len(r) < SMALL)
1420 /*---------------------------------------------------------------------------*/
1423 * The following functions take the set of planes defining a lump and
1424 * find the verts, edges, and geoms that describe its boundaries. To
1425 * do this, they first find the verts, and then search these verts for
1426 * valid edges and geoms. It may be more efficient to compute edges
1427 * and geoms directly by clipping down infinite line segments and
1428 * planes, but this would be more complex and prone to numerical
1433 * Given 3 side planes, compute the point of intersection, if any.
1434 * Confirm that this point falls within the current lump, and that it
1435 * is unique. Add it as a vert of the solid.
1437 static void clip_vert(struct s_base *fp,
1438 struct b_lump *lp, int si, int sj, int sk)
1440 float M[16], X[16], I[16];
1444 d[0] = fp->sv[si].d;
1445 d[1] = fp->sv[sj].d;
1446 d[2] = fp->sv[sk].d;
1448 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1455 for (i = 0; i < lp->sc; i++)
1457 int si = fp->iv[lp->s0 + i];
1459 if (fore_side(p, fp->sv + si))
1463 if (ok_vert(fp, lp, p))
1465 v_cpy(fp->vv[fp->vc].p, p);
1467 fp->iv[fp->ic] = fp->vc;
1476 * Given two side planes, find an edge along their intersection by
1477 * finding a pair of vertices that fall on both planes. Add it to the
1480 static void clip_edge(struct s_base *fp,
1481 struct b_lump *lp, int si, int sj)
1485 for (i = 1; i < lp->vc; i++)
1487 int vi = fp->iv[lp->v0 + i];
1489 if (!on_side(fp->vv[vi].p, fp->sv + si) ||
1490 !on_side(fp->vv[vi].p, fp->sv + sj))
1493 for (j = 0; j < i; j++)
1495 int vj = fp->iv[lp->v0 + j];
1497 if (on_side(fp->vv[vj].p, fp->sv + si) &&
1498 on_side(fp->vv[vj].p, fp->sv + sj))
1500 fp->ev[fp->ec].vi = vi;
1501 fp->ev[fp->ec].vj = vj;
1503 fp->iv[fp->ic] = fp->ec;
1514 * Find all verts that lie on the given side of the lump. Sort these
1515 * verts to have a counter-clockwise winding about the plane normal.
1516 * Create geoms to tessellate the resulting convex polygon.
1518 static void clip_geom(struct s_base *fp,
1519 struct b_lump *lp, int si)
1521 int m[256], t[256], d, i, j, n = 0;
1526 struct b_side *sp = fp->sv + si;
1530 for (i = 0; i < lp->vc; i++)
1532 int vi = fp->iv[lp->v0 + i];
1534 if (on_side(fp->vv[vi].p, sp))
1539 v_add(v, fp->vv[vi].p, plane_p[si]);
1541 fp->tv[t[n]].u[0] = v_dot(v, plane_u[si]);
1542 fp->tv[t[n]].u[1] = v_dot(v, plane_v[si]);
1550 for (i = 1; i < n; i++)
1551 for (j = i + 1; j < n; j++)
1553 v_sub(u, fp->vv[m[i]].p, fp->vv[m[0]].p);
1554 v_sub(v, fp->vv[m[j]].p, fp->vv[m[0]].p);
1557 if (v_dot(w, sp->n) < 0.f)
1571 for (i = 0; i < n - 2; i++)
1573 const int gi = incg(fp);
1575 struct b_geom *gp = fp->gv + gi;
1577 struct b_offs *op = fp->ov + (gp->oi = inco(fp));
1578 struct b_offs *oq = fp->ov + (gp->oj = inco(fp));
1579 struct b_offs *or = fp->ov + (gp->ok = inco(fp));
1581 gp->mi = plane_m[si];
1595 fp->iv[fp->ic] = gi;
1602 * Iterate the sides of the lump, attempting to generate a new vert for
1603 * each trio of planes, a new edge for each pair of planes, and a new
1604 * set of geom for each visible plane.
1606 static void clip_lump(struct s_base *fp, struct b_lump *lp)
1613 for (i = 2; i < lp->sc; i++)
1614 for (j = 1; j < i; j++)
1615 for (k = 0; k < j; k++)
1619 fp->iv[lp->s0 + k]);
1624 for (i = 1; i < lp->sc; i++)
1625 for (j = 0; j < i; j++)
1628 fp->iv[lp->s0 + j]);
1633 for (i = 0; i < lp->sc; i++)
1634 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0.0f)
1636 fp->iv[lp->s0 + i]);
1638 for (i = 0; i < lp->sc; i++)
1639 if (plane_f[fp->iv[lp->s0 + i]])
1643 static void clip_file(struct s_base *fp)
1647 for (i = 0; i < fp->lc; i++)
1648 clip_lump(fp, fp->lv + i);
1651 /*---------------------------------------------------------------------------*/
1654 * For each body element type, determine if element 'p' is equivalent
1655 * to element 'q'. This is more than a simple memory compare. It
1656 * effectively snaps mtrls and verts together, and may reverse the
1657 * winding of an edge or a geom. This is done in order to maximize
1658 * the number of elements that can be eliminated.
1661 static int comp_mtrl(const struct b_mtrl *mp, const struct b_mtrl *mq)
1663 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1664 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1665 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1666 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1668 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1669 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1670 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1671 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1673 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1674 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1675 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1676 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1678 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1679 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1680 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1681 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1683 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1685 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1690 static int comp_vert(const struct b_vert *vp, const struct b_vert *vq)
1692 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1693 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1694 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1699 static int comp_edge(const struct b_edge *ep, const struct b_edge *eq)
1701 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1702 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1707 static int comp_side(const struct b_side *sp, const struct b_side *sq)
1709 if (fabs(sp->d - sq->d) > SMALL) return 0;
1710 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1715 static int comp_texc(const struct b_texc *tp, const struct b_texc *tq)
1717 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1718 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1723 static int comp_offs(const struct b_offs *op, const struct b_offs *oq)
1725 if (op->ti != oq->ti) return 0;
1726 if (op->si != oq->si) return 0;
1727 if (op->vi != oq->vi) return 0;
1732 static int comp_geom(const struct b_geom *gp, const struct b_geom *gq)
1734 if (gp->mi != gq->mi) return 0;
1735 if (gp->oi != gq->oi) return 0;
1736 if (gp->oj != gq->oj) return 0;
1737 if (gp->ok != gq->ok) return 0;
1742 /*---------------------------------------------------------------------------*/
1744 static int mtrl_swaps[MAXM];
1745 static int vert_swaps[MAXV];
1746 static int edge_swaps[MAXE];
1747 static int side_swaps[MAXS];
1748 static int texc_swaps[MAXT];
1749 static int offs_swaps[MAXO];
1750 static int geom_swaps[MAXG];
1753 * For each file element type, replace all references to element 'i'
1754 * with a reference to element 'j'. These are used when optimizing
1755 * and sorting the file.
1758 static void swap_mtrl(struct s_base *fp, int mi, int mj)
1762 for (i = 0; i < fp->gc; i++)
1763 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1764 for (i = 0; i < fp->rc; i++)
1765 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1768 static void swap_vert(struct s_base *fp, int vi, int vj)
1772 for (i = 0; i < fp->ec; i++)
1774 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1775 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1778 for (i = 0; i < fp->oc; i++)
1779 if (fp->ov[i].vi == vi) fp->ov[i].vi = vj;
1781 for (i = 0; i < fp->lc; i++)
1782 for (j = 0; j < fp->lv[i].vc; j++)
1783 if (fp->iv[fp->lv[i].v0 + j] == vi)
1784 fp->iv[fp->lv[i].v0 + j] = vj;
1787 static void apply_mtrl_swaps(struct s_base *fp)
1791 for (i = 0; i < fp->gc; i++)
1792 fp->gv[i].mi = mtrl_swaps[fp->gv[i].mi];
1793 for (i = 0; i < fp->rc; i++)
1794 fp->rv[i].mi = mtrl_swaps[fp->rv[i].mi];
1798 static void apply_vert_swaps(struct s_base *fp)
1802 for (i = 0; i < fp->ec; i++)
1804 fp->ev[i].vi = vert_swaps[fp->ev[i].vi];
1805 fp->ev[i].vj = vert_swaps[fp->ev[i].vj];
1808 for (i = 0; i < fp->oc; i++)
1809 fp->ov[i].vi = vert_swaps[fp->ov[i].vi];
1811 for (i = 0; i < fp->lc; i++)
1812 for (j = 0; j < fp->lv[i].vc; j++)
1813 fp->iv[fp->lv[i].v0 + j] = vert_swaps[fp->iv[fp->lv[i].v0 + j]];
1816 static void apply_edge_swaps(struct s_base *fp)
1820 for (i = 0; i < fp->lc; i++)
1821 for (j = 0; j < fp->lv[i].ec; j++)
1822 fp->iv[fp->lv[i].e0 + j] = edge_swaps[fp->iv[fp->lv[i].e0 + j]];
1825 static void apply_side_swaps(struct s_base *fp)
1829 for (i = 0; i < fp->oc; i++)
1830 fp->ov[i].si = side_swaps[fp->ov[i].si];
1831 for (i = 0; i < fp->nc; i++)
1832 fp->nv[i].si = side_swaps[fp->nv[i].si];
1834 for (i = 0; i < fp->lc; i++)
1835 for (j = 0; j < fp->lv[i].sc; j++)
1836 fp->iv[fp->lv[i].s0 + j] = side_swaps[fp->iv[fp->lv[i].s0 + j]];
1839 static void apply_texc_swaps(struct s_base *fp)
1843 for (i = 0; i < fp->oc; i++)
1844 fp->ov[i].ti = texc_swaps[fp->ov[i].ti];
1847 static void apply_offs_swaps(struct s_base *fp)
1851 for (i = 0; i < fp->gc; i++)
1853 fp->gv[i].oi = offs_swaps[fp->gv[i].oi];
1854 fp->gv[i].oj = offs_swaps[fp->gv[i].oj];
1855 fp->gv[i].ok = offs_swaps[fp->gv[i].ok];
1859 static void apply_geom_swaps(struct s_base *fp)
1863 for (i = 0; i < fp->lc; i++)
1864 for (j = 0; j < fp->lv[i].gc; j++)
1865 fp->iv[fp->lv[i].g0 + j] = geom_swaps[fp->iv[fp->lv[i].g0 + j]];
1867 for (i = 0; i < fp->bc; i++)
1868 for (j = 0; j < fp->bv[i].gc; j++)
1869 fp->iv[fp->bv[i].g0 + j] = geom_swaps[fp->iv[fp->bv[i].g0 + j]];
1872 /*---------------------------------------------------------------------------*/
1874 static void uniq_mtrl(struct s_base *fp)
1878 for (i = 0; i < fp->mc; i++)
1880 for (j = 0; j < k; j++)
1881 if (comp_mtrl(fp->mv + i, fp->mv + j))
1889 fp->mv[k] = fp->mv[i];
1894 apply_mtrl_swaps(fp);
1899 static void uniq_vert(struct s_base *fp)
1903 for (i = 0; i < fp->vc; i++)
1905 for (j = 0; j < k; j++)
1906 if (comp_vert(fp->vv + i, fp->vv + j))
1914 fp->vv[k] = fp->vv[i];
1919 apply_vert_swaps(fp);
1924 static void uniq_edge(struct s_base *fp)
1928 for (i = 0; i < fp->ec; i++)
1930 for (j = 0; j < k; j++)
1931 if (comp_edge(fp->ev + i, fp->ev + j))
1939 fp->ev[k] = fp->ev[i];
1944 apply_edge_swaps(fp);
1949 static void uniq_offs(struct s_base *fp)
1953 for (i = 0; i < fp->oc; i++)
1955 for (j = 0; j < k; j++)
1956 if (comp_offs(fp->ov + i, fp->ov + j))
1964 fp->ov[k] = fp->ov[i];
1969 apply_offs_swaps(fp);
1974 static void uniq_geom(struct s_base *fp)
1978 for (i = 0; i < fp->gc; i++)
1980 for (j = 0; j < k; j++)
1981 if (comp_geom(fp->gv + i, fp->gv + j))
1989 fp->gv[k] = fp->gv[i];
1994 apply_geom_swaps(fp);
1999 static void uniq_texc(struct s_base *fp)
2003 for (i = 0; i < fp->tc; i++)
2005 for (j = 0; j < k; j++)
2006 if (comp_texc(fp->tv + i, fp->tv + j))
2014 fp->tv[k] = fp->tv[i];
2019 apply_texc_swaps(fp);
2024 static void uniq_side(struct s_base *fp)
2028 for (i = 0; i < fp->sc; i++)
2030 for (j = 0; j < k; j++)
2031 if (comp_side(fp->sv + i, fp->sv + j))
2039 fp->sv[k] = fp->sv[i];
2044 apply_side_swaps(fp);
2049 static void uniq_file(struct s_base *fp)
2051 /* Debug mode skips optimization, producing oversized output files. */
2053 if (debug_output == 0)
2065 /*---------------------------------------------------------------------------*/
2075 static int comp_trip(const void *p, const void *q)
2077 const struct b_trip *tp = (const struct b_trip *) p;
2078 const struct b_trip *tq = (const struct b_trip *) q;
2080 if (tp->vi < tq->vi) return -1;
2081 if (tp->vi > tq->vi) return +1;
2082 if (tp->mi < tq->mi) return -1;
2083 if (tp->mi > tq->mi) return +1;
2088 static void smth_file(struct s_base *fp)
2090 struct b_trip temp, *T;
2092 if (debug_output == 0)
2094 if ((T = (struct b_trip *) malloc(fp->gc * 3 * sizeof (struct b_trip))))
2096 int gi, i, j, k, l, c = 0;
2098 /* Create a list of all non-faceted vertex triplets. */
2100 for (gi = 0; gi < fp->gc; ++gi)
2102 struct b_geom *gp = fp->gv + gi;
2104 T[c].vi = fp->ov[gp->oi].vi;
2105 T[c].si = fp->ov[gp->oi].si;
2110 T[c].vi = fp->ov[gp->oj].vi;
2111 T[c].si = fp->ov[gp->oj].si;
2116 T[c].vi = fp->ov[gp->ok].vi;
2117 T[c].si = fp->ov[gp->ok].si;
2123 /* Sort all triplets by vertex index and material. */
2125 qsort(T, c, sizeof (struct b_trip), comp_trip);
2127 /* For each set of triplets sharing vertex index and material... */
2129 for (i = 0; i < c; i = l)
2133 float N[3], angle = fp->mv[T[i].mi].angle;
2134 const float *Ni = fp->sv[T[i].si].n;
2136 /* Sort the set by side similarity to the first. */
2138 for (j = i + 1; j < c && (T[j].vi == T[i].vi &&
2139 T[j].mi == T[i].mi); ++j)
2141 for (k = j + 1; k < c && (T[k].vi == T[i].vi &&
2142 T[k].mi == T[i].mi); ++k)
2144 const float *Nj = fp->sv[T[j].si].n;
2145 const float *Nk = fp->sv[T[k].si].n;
2147 if (T[j].si != T[k].si && v_dot(Nk, Ni) > v_dot(Nj, Ni))
2156 /* Accumulate all similar side normals. */
2162 for (l = i + 1; l < c && (T[l].vi == T[i].vi &&
2163 T[l].mi == T[i].mi); ++l)
2164 if (T[l].si != T[i].si)
2166 const float *Nl = fp->sv[T[l].si].n;
2168 if (V_DEG(facosf(v_dot(Ni, Nl))) > angle)
2178 /* If at least two normals have been accumulated... */
2182 /* Store the accumulated normal as a new side. */
2186 v_nrm(fp->sv[ss].n, N);
2187 fp->sv[ss].d = 0.0f;
2189 /* Assign the new normal to the merged triplets. */
2191 for (j = i; j < l; ++j)
2196 /* Assign the remapped normals to the original geoms. */
2198 for (i = 0; i < c; ++i)
2200 struct b_geom *gp = fp->gv + T[i].gi;
2201 struct b_offs *op = fp->ov + gp->oi;
2202 struct b_offs *oq = fp->ov + gp->oj;
2203 struct b_offs *or = fp->ov + gp->ok;
2205 if (op->vi == T[i].vi) op->si = T[i].si;
2206 if (oq->vi == T[i].vi) oq->si = T[i].si;
2207 if (or->vi == T[i].vi) or->si = T[i].si;
2218 /*---------------------------------------------------------------------------*/
2220 static void sort_file(struct s_base *fp)
2224 /* Sort materials by type to minimize state changes. */
2226 for (i = 0; i < fp->mc; i++)
2227 for (j = i + 1; j < fp->mc; j++)
2228 if (fp->mv[i].fl > fp->mv[j].fl)
2233 fp->mv[i] = fp->mv[j];
2236 swap_mtrl(fp, i, -1);
2237 swap_mtrl(fp, j, i);
2238 swap_mtrl(fp, -1, j);
2241 /* Sort billboards by material within distance. */
2243 for (i = 0; i < fp->rc; i++)
2244 for (j = i + 1; j < fp->rc; j++)
2245 if ((fp->rv[j].d > fp->rv[i].d) ||
2246 (fp->rv[j].d == fp->rv[i].d &&
2247 fp->rv[j].mi > fp->rv[i].mi))
2252 fp->rv[i] = fp->rv[j];
2256 /* Ensure the first vertex is the lowest. */
2258 for (i = 0; i < fp->vc; i++)
2259 if (fp->vv[0].p[1] > fp->vv[i].p[1])
2264 fp->vv[0] = fp->vv[i];
2267 swap_vert(fp, 0, -1);
2268 swap_vert(fp, i, 0);
2269 swap_vert(fp, -1, i);
2273 /*---------------------------------------------------------------------------*/
2275 static int test_lump_side(const struct s_base *fp,
2276 const struct b_lump *lp,
2277 const struct b_side *sp,
2291 /* Check if the bounding sphere of the lump is completely on one side. */
2293 d = v_dot(bsphere, sp->n) - sp->d;
2295 if (fabs(d) > bsphere[3])
2296 return d > 0 ? 1 : -1;
2298 /* If the given side is part of the given lump, then the lump is behind. */
2300 for (si = 0; si < lp->sc; si++)
2301 if (fp->sv + fp->iv[lp->s0 + si] == sp)
2304 /* Check if each lump vertex is in front of, behind, on the side. */
2306 for (vi = 0; vi < lp->vc; vi++)
2308 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
2314 /* If no verts are behind, the lump is in front, and vice versa. */
2316 if (f > 0 && b == 0) return +1;
2317 if (b > 0 && f == 0) return -1;
2319 /* Else, the lump crosses the side. */
2324 static int node_node(struct s_base *fp, int l0, int lc, float bsphere[][4])
2328 /* Base case. Dump all given lumps into a leaf node. */
2330 fp->nv[fp->nc].si = -1;
2331 fp->nv[fp->nc].ni = -1;
2332 fp->nv[fp->nc].nj = -1;
2333 fp->nv[fp->nc].l0 = l0;
2334 fp->nv[fp->nc].lc = lc;
2344 int li = 0, lic = 0;
2345 int lj = 0, ljc = 0;
2346 int lk = 0, lkc = 0;
2349 /* Find the side that most evenly splits the given lumps. */
2351 for (si = 0; si < fp->sc; si++)
2357 for (li = 0; li < lc; li++)
2358 if ((k = test_lump_side(fp,
2368 if ((d < sjd) || (d == sjd && o < sjo))
2376 /* Flag each lump with its position WRT the side. */
2378 for (li = 0; li < lc; li++)
2381 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2385 switch (test_lump_side(fp,
2391 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10;
2395 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20;
2399 fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40;
2404 /* Sort all lumps in the range by their flag values. */
2406 for (li = 1; li < lc; li++)
2407 for (lj = 0; lj < li; lj++)
2408 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
2414 for (i = 0; i < 4; i++)
2416 f = bsphere[l0 + li][i];
2417 bsphere[l0 + li][i] = bsphere[l0 + lj][i];
2418 bsphere[l0 + lj][i] = f;
2421 l = fp->lv[l0 + li];
2422 fp->lv[l0 + li] = fp->lv[l0 + lj];
2423 fp->lv[l0 + lj] = l;
2426 /* Establish the in-front, on, and behind lump ranges. */
2432 for (i = lc - 1; i >= 0; i--)
2433 switch (fp->lv[l0 + i].fl & 0xf0)
2435 case 0x10: li = l0 + i; lic++; break;
2436 case 0x20: lj = l0 + i; ljc++; break;
2437 case 0x40: lk = l0 + i; lkc++; break;
2440 /* Add the lumps on the side to the node. */
2445 fp->nv[i].ni = node_node(fp, li, lic, bsphere);
2447 fp->nv[i].nj = node_node(fp, lk, lkc, bsphere);
2456 * Compute a bounding sphere for a lump (not optimal)
2458 static void lump_bounding_sphere(struct s_base *fp,
2469 bbox[0] = bbox[3] = fp->vv[fp->iv[lp->v0]].p[0];
2470 bbox[1] = bbox[4] = fp->vv[fp->iv[lp->v0]].p[1];
2471 bbox[2] = bbox[5] = fp->vv[fp->iv[lp->v0]].p[2];
2473 for (i = 1; i < lp->vc; i++)
2475 struct b_vert *vp = fp->vv + fp->iv[lp->v0 + i];
2478 for (j = 0; j < 3; j++)
2479 if (vp->p[j] < bbox[j])
2482 for (j = 0; j < 3; j++)
2483 if (vp->p[j] > bbox[j + 3])
2484 bbox[j + 3] = vp->p[j];
2489 for (i = 0; i < 3; i++)
2491 bsphere[i] = (bbox[i] + bbox[i + 3]) / 2;
2492 r += (bsphere[i] - bbox[i]) * (bsphere[i] - bbox[i]);
2495 bsphere[3] = fsqrtf(r);
2498 static void node_file(struct s_base *fp)
2500 float bsphere[MAXL][4];
2503 /* Compute a bounding sphere for each lump. */
2505 for (i = 0; i < fp->lc; i++)
2506 lump_bounding_sphere(fp, fp->lv + i, bsphere[i]);
2508 /* Sort the lumps of each body into BSP nodes. */
2510 for (i = 0; i < fp->bc; i++)
2511 fp->bv[i].ni = node_node(fp, fp->bv[i].l0, fp->bv[i].lc, bsphere);
2514 /*---------------------------------------------------------------------------*/
2516 static void dump_file(struct s_base *p, const char *name, double t)
2522 /* Count the number of solid lumps. */
2524 for (i = 0; i < p->lc; i++)
2525 if ((p->lv[i].fl & 1) == 0)
2528 /* Count the total value of all coins. */
2530 for (i = 0; i < p->hc; i++)
2531 if (p->hv[i].t == ITEM_COIN)
2535 printf("%s,%d,%d,%.3f"
2536 "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
2537 "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
2539 p->mc, p->vc, p->ec, p->sc, p->tc,
2540 p->oc, p->gc, p->lc, p->pc, p->nc, p->bc,
2541 p->hc, p->zc, p->wc, p->jc, p->xc,
2542 p->rc, p->uc, p->ac, p->dc, p->ic);
2544 printf("%s (%d/$%d) %.3f\n"
2545 " mtrl vert edge side texc"
2546 " offs geom lump path node body\n"
2547 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2548 " item goal view jump swch"
2549 " bill ball char dict indx\n"
2550 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2552 p->mc, p->vc, p->ec, p->sc, p->tc,
2553 p->oc, p->gc, p->lc, p->pc, p->nc, p->bc,
2554 p->hc, p->zc, p->wc, p->jc, p->xc,
2555 p->rc, p->uc, p->ac, p->dc, p->ic);
2558 int main(int argc, char *argv[])
2560 char src[MAXSTR] = "";
2561 char dst[MAXSTR] = "";
2565 if (!fs_init(argv[0]))
2567 fprintf(stderr, "Failure to initialize virtual file system: %s\n",
2572 verbose = !!getenv("MAPC_VERBOSE");
2578 input_file = argv[1];
2580 for (argi = 3; argi < argc; ++argi)
2582 if (strcmp(argv[argi], "--debug") == 0) debug_output = 1;
2583 if (strcmp(argv[argi], "--csv") == 0) csv_output = 1;
2586 strncpy(src, argv[1], MAXSTR - 1);
2587 strncpy(dst, argv[1], MAXSTR - 1);
2589 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2590 strcpy(dst + strlen(dst) - 4, ".sol");
2592 strcat(dst, ".sol");
2594 fs_add_path (dir_name(src));
2595 fs_set_write_dir(dir_name(dst));
2597 if ((fin = fs_open(base_name(src), "r")))
2599 if (!fs_add_path_with_archives(argv[2]))
2601 fprintf(stderr, "Failure to establish data directory\n");
2607 gettimeofday(&t0, 0);
2622 sol_stor_base(&f, base_name(dst));
2624 gettimeofday(&t1, 0);
2626 dump_file(&f, dst, (t1.tv_sec - t0.tv_sec) +
2627 (t1.tv_usec - t0.tv_usec) / 1000000.0);
2634 else fprintf(stderr, "Usage: %s <map> <data> [--debug] [--csv]\n", argv[0]);