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 /*---------------------------------------------------------------------------*/
18 #pragma comment(lib, "SDL_ttf.lib")
19 #pragma comment(lib, "SDL_image.lib")
20 #pragma comment(lib, "SDL_mixer.lib")
21 #pragma comment(lib, "SDL.lib")
22 #pragma comment(lib, "SDLmain.lib")
23 #pragma comment(lib, "opengl32.lib")
26 /*---------------------------------------------------------------------------*/
29 #include <SDL_image.h>
37 #include "base_config.h"
45 * The overall design of this map converter is very stupid, but very
46 * simple. It begins by assuming that every mtrl, vert, edge, side,
47 * and texc in the map is unique. It then makes an optimizing pass
48 * that discards redundant information. The result is optimal, though
49 * the process is terribly inefficient.
52 /*---------------------------------------------------------------------------*/
54 /* Ohhhh... arbitrary! */
77 static int overflow(const char *s)
79 printf("%s overflow\n", s);
84 static int incm(struct s_file *fp)
86 return (fp->mc < MAXM) ? fp->mc++ : overflow("mtrl");
89 static int incv(struct s_file *fp)
91 return (fp->vc < MAXV) ? fp->vc++ : overflow("vert");
94 static int ince(struct s_file *fp)
96 return (fp->ec < MAXE) ? fp->ec++ : overflow("edge");
99 static int incs(struct s_file *fp)
101 return (fp->sc < MAXS) ? fp->sc++ : overflow("side");
104 static int inct(struct s_file *fp)
106 return (fp->tc < MAXT) ? fp->tc++ : overflow("texc");
109 static int incg(struct s_file *fp)
111 return (fp->gc < MAXG) ? fp->gc++ : overflow("geom");
114 static int incl(struct s_file *fp)
116 return (fp->lc < MAXL) ? fp->lc++ : overflow("lump");
119 static int incn(struct s_file *fp)
121 return (fp->nc < MAXN) ? fp->nc++ : overflow("node");
124 static int incp(struct s_file *fp)
126 return (fp->pc < MAXP) ? fp->pc++ : overflow("path");
129 static int incb(struct s_file *fp)
131 return (fp->bc < MAXB) ? fp->bc++ : overflow("body");
134 static int inch(struct s_file *fp)
136 return (fp->hc < MAXH) ? fp->hc++ : overflow("item");
139 static int incz(struct s_file *fp)
141 return (fp->zc < MAXZ) ? fp->zc++ : overflow("goal");
144 static int incj(struct s_file *fp)
146 return (fp->jc < MAXJ) ? fp->jc++ : overflow("jump");
149 static int incx(struct s_file *fp)
151 return (fp->xc < MAXX) ? fp->xc++ : overflow("swch");
154 static int incr(struct s_file *fp)
156 return (fp->rc < MAXR) ? fp->rc++ : overflow("bill");
159 static int incu(struct s_file *fp)
161 return (fp->uc < MAXU) ? fp->uc++ : overflow("ball");
164 static int incw(struct s_file *fp)
166 return (fp->wc < MAXW) ? fp->wc++ : overflow("view");
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)
196 fp->mv = (struct s_mtrl *) calloc(MAXM, sizeof (struct s_mtrl));
197 fp->vv = (struct s_vert *) calloc(MAXV, sizeof (struct s_vert));
198 fp->ev = (struct s_edge *) calloc(MAXE, sizeof (struct s_edge));
199 fp->sv = (struct s_side *) calloc(MAXS, sizeof (struct s_side));
200 fp->tv = (struct s_texc *) calloc(MAXT, sizeof (struct s_texc));
201 fp->gv = (struct s_geom *) calloc(MAXG, sizeof (struct s_geom));
202 fp->lv = (struct s_lump *) calloc(MAXL, sizeof (struct s_lump));
203 fp->nv = (struct s_node *) calloc(MAXN, sizeof (struct s_node));
204 fp->pv = (struct s_path *) calloc(MAXP, sizeof (struct s_path));
205 fp->bv = (struct s_body *) calloc(MAXB, sizeof (struct s_body));
206 fp->hv = (struct s_item *) calloc(MAXH, sizeof (struct s_item));
207 fp->zv = (struct s_goal *) calloc(MAXZ, sizeof (struct s_goal));
208 fp->jv = (struct s_jump *) calloc(MAXJ, sizeof (struct s_jump));
209 fp->xv = (struct s_swch *) calloc(MAXX, sizeof (struct s_swch));
210 fp->rv = (struct s_bill *) calloc(MAXR, sizeof (struct s_bill));
211 fp->uv = (struct s_ball *) calloc(MAXU, sizeof (struct s_ball));
212 fp->wv = (struct s_view *) calloc(MAXW, sizeof (struct s_view));
213 fp->av = (char *) calloc(MAXA, sizeof (char));
214 fp->iv = (int *) calloc(MAXI, sizeof (int));
217 /*---------------------------------------------------------------------------*/
220 * The following is a small symbol table data structure. Symbols and
221 * their integer values are collected in symv and valv. References
222 * and pointers to their unsatisfied integer values are collected in
223 * refv and pntv. The resolve procedure matches references to symbols
224 * and fills waiting ints with the proper values.
229 static char symv[MAXSYM][MAXSTR];
230 static int valv[MAXSYM];
232 static char refv[MAXSYM][MAXSTR];
233 static int *pntv[MAXSYM];
238 static void make_sym(const char *s, int v)
240 strncpy(symv[strc], s, MAXSTR - 1);
245 static void make_ref(const char *r, int *p)
247 strncpy(refv[refc], r, MAXSTR - 1);
252 static void resolve(void)
256 for (i = 0; i < refc; i++)
257 for (j = 0; j < strc; j++)
258 if (strncmp(refv[i], symv[j], MAXSTR) == 0)
260 *(pntv[i]) = valv[j];
265 /*---------------------------------------------------------------------------*/
268 * The following globals are used to cache target_positions. They are
269 * targeted by various entities and must be resolved in a second pass.
272 static float targ_p [MAXW][3];
273 static int targ_wi[MAXW];
274 static int targ_ji[MAXW];
277 static void targets(struct s_file *fp)
281 for (i = 0; i < fp->wc; i++)
282 v_cpy(fp->wv[i].q, targ_p[targ_wi[i]]);
284 for (i = 0; i < fp->jc; i++)
285 v_cpy(fp->jv[i].q, targ_p[targ_ji[i]]);
288 /*---------------------------------------------------------------------------*/
291 * The following code caches image sizes. Textures are referenced by
292 * name, but their sizes are necessary when computing texture
293 * coordinates. This code allows each file to be accessed only once
294 * regardless of the number of surfaces referring to it.
303 static struct _imagedata *imagedata = NULL;
304 static int image_n = 0;
305 static int image_alloc = 0;
307 #define IMAGE_REALLOC 32
309 static void free_imagedata()
315 for (i = 0; i < image_n; i++)
316 free(imagedata[i].s);
320 image_n = image_alloc = 0;
323 static int size_load(const char *file, int *w, int *h)
327 if ((S = IMG_Load(file)))
339 static void size_image(const char *name, int *w, int *h)
347 for (i = 0; i < image_n; i++)
348 if (strncmp(imagedata[i].s, name, MAXSTR) == 0)
359 strcpy(jpg, name); strcat(jpg, ".jpg");
360 strcpy(tga, name); strcat(tga, ".tga");
361 strcpy(png, name); strcat(png, ".png");
363 if (size_load(config_data(png), w, h) ||
364 size_load(config_data(tga), w, h) ||
365 size_load(config_data(jpg), w, h))
368 if (image_n + 1 >= image_alloc)
370 struct _imagedata *tmp =
371 (struct _imagedata *) malloc(sizeof(struct _imagedata) * (image_alloc + IMAGE_REALLOC));
374 printf("malloc error\n");
379 (void) memcpy(tmp, imagedata, sizeof(struct _imagedata) * image_alloc);
383 image_alloc += IMAGE_REALLOC;
386 imagedata[image_n].s = (char *) calloc(strlen(name) + 1, 1);
387 imagedata[image_n].w = *w;
388 imagedata[image_n].h = *h;
389 strcpy(imagedata[image_n].s, name);
395 /*---------------------------------------------------------------------------*/
397 /* Read the given material file, adding a new material to the solid. */
399 static int read_mtrl(struct s_file *fp, const char *name)
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] = mp->a[3] = 1.0f;
414 mp->d[0] = mp->d[1] = mp->d[2] = mp->d[3] = 1.0f;
415 mp->s[0] = mp->s[1] = mp->s[2] = mp->s[3] = 1.0f;
416 mp->e[0] = mp->e[1] = mp->e[2] = mp->e[3] = 1.0f;
420 if ((fin = fopen(config_data(name), "r")))
428 mp->d, mp->d + 1, mp->d + 2, mp->d + 3,
429 mp->a, mp->a + 1, mp->a + 2, mp->a + 3,
430 mp->s, mp->s + 1, mp->s + 2, mp->s + 3,
431 mp->e, mp->e + 1, mp->e + 2, mp->e + 3,
439 /*---------------------------------------------------------------------------*/
442 * All bodies with an associated path are assumed to be positioned at
443 * the beginning of that path. These bodies must be moved to the
444 * origin in order for their path transforms to behave correctly.
445 * This is how we get away with defining func_trains with no origin
449 static void move_side(struct s_side *sp, const float p[3])
451 sp->d -= v_dot(sp->n, p);
454 static void move_vert(struct s_vert *vp, const float p[3])
456 v_sub(vp->p, vp->p, p);
459 static void move_lump(struct s_file *fp,
460 struct s_lump *lp, const float p[3])
464 for (i = 0; i < lp->sc; i++)
465 move_side(fp->sv + fp->iv[lp->s0 + i], p);
466 for (i = 0; i < lp->vc; i++)
467 move_vert(fp->vv + fp->iv[lp->v0 + i], p);
470 static void move_body(struct s_file *fp,
475 for (i = 0; i < bp->lc; i++)
476 move_lump(fp, fp->lv + bp->l0 + i, fp->pv[bp->pi].p);
479 static void move_file(struct s_file *fp)
483 for (i = 0; i < fp->bc; i++)
484 if (fp->bv[i].pi >= 0)
485 move_body(fp, fp->bv + i);
488 /*---------------------------------------------------------------------------*/
491 * This is a basic OBJ loader. It is by no means fully compliant with
492 * the OBJ specification, but it works well with the output of
493 * Wings3D. All faces must be triangles and all vertices must include
494 * normals and texture coordinates. Material names are taken to be
495 * references to Neverball materials, rather than MTL definitions.
498 static void read_vt(struct s_file *fp, const char *line)
500 struct s_texc *tp = fp->tv + inct(fp);
502 sscanf(line, "%f %f", tp->u, tp->u + 1);
505 static void read_vn(struct s_file *fp, const char *line)
507 struct s_side *sp = fp->sv + incs(fp);
509 sscanf(line, "%f %f %f", sp->n, sp->n + 1, sp->n + 2);
512 static void read_v(struct s_file *fp, const char *line)
514 struct s_vert *vp = fp->vv + incv(fp);
516 sscanf(line, "%f %f %f", vp->p, vp->p + 1, vp->p + 2);
519 static void read_f(struct s_file *fp, const char *line,
520 int v0, int t0, int s0, int mi)
522 struct s_geom *gp = fp->gv + incg(fp);
527 sscanf(line, "%d%c%d%c%d %d%c%d%c%d %d%c%d%c%d",
528 &gp->vi, &c1, &gp->ti, &c2, &gp->si,
529 &gp->vj, &c1, &gp->tj, &c2, &gp->sj,
530 &gp->vk, &c1, &gp->tk, &c2, &gp->sk);
545 static void read_obj(struct s_file *fp, const char *name)
556 if ((fin = fopen(config_data(name), "r")))
558 while (fgets(line, MAXSTR, fin))
560 if (strncmp(line, "usemtl", 6) == 0)
562 sscanf(line + 6, "%s", mtrl);
563 mi = read_mtrl(fp, mtrl);
566 else if (strncmp(line, "f", 1) == 0)
568 if (fp->mv[mi].d[3] > 0)
569 read_f(fp, line + 1, v0, t0, s0, mi);
572 else if (strncmp(line, "vt", 2) == 0) read_vt(fp, line + 2);
573 else if (strncmp(line, "vn", 2) == 0) read_vn(fp, line + 2);
574 else if (strncmp(line, "v", 1) == 0) read_v (fp, line + 1);
580 /*---------------------------------------------------------------------------*/
582 static float plane_d[MAXS];
583 static float plane_n[MAXS][3];
584 static float plane_p[MAXS][3];
585 static float plane_u[MAXS][3];
586 static float plane_v[MAXS][3];
587 static int plane_f[MAXS];
588 static int plane_m[MAXS];
590 static void make_plane(int pi, int x0, int y0, int z0,
591 int x1, int y1, int z1,
592 int x2, int y2, int z2,
593 int tu, int tv, int r,
594 float su, float sv, int fl, const char *s)
596 static const float base[6][3][3] = {
597 {{ 0, 0, 1 }, { 1, 0, 0 }, { 0, -1, 0 }},
598 {{ 0, 0, -1 }, { 1, 0, 0 }, { 0, -1, 0 }},
599 {{ 1, 0, 0 }, { 0, 0, -1 }, { 0, -1, 0 }},
600 {{ -1, 0, 0 }, { 0, 0, -1 }, { 0, -1, 0 }},
601 {{ 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, 1 }},
602 {{ 0, -1, 0 }, { 1, 0, 0 }, { 0, 0, 1 }},
606 float p0[3], p1[3], p2[3];
607 float u[3], v[3], p[3];
612 size_image(s, &w, &h);
614 plane_f[pi] = fl ? L_DETAIL : 0;
616 p0[0] = +(float) x0 / SCALE;
617 p0[1] = +(float) z0 / SCALE;
618 p0[2] = -(float) y0 / SCALE;
620 p1[0] = +(float) x1 / SCALE;
621 p1[1] = +(float) z1 / SCALE;
622 p1[2] = -(float) y1 / SCALE;
624 p2[0] = +(float) x2 / SCALE;
625 p2[1] = +(float) z2 / SCALE;
626 p2[2] = -(float) y2 / SCALE;
631 v_crs(plane_n[pi], u, v);
632 v_nrm(plane_n[pi], plane_n[pi]);
634 plane_d[pi] = v_dot(plane_n[pi], p1);
636 for (i = 0; i < 6; i++)
637 if ((k = v_dot(plane_n[pi], base[i][0])) >= d)
647 m_rot(R, base[n][0], V_RAD(r));
649 v_mad(p, p, base[n][1], su * tu / SCALE);
650 v_mad(p, p, base[n][2], sv * tv / SCALE);
652 m_vxfm(plane_u[pi], R, base[n][1]);
653 m_vxfm(plane_v[pi], R, base[n][2]);
654 m_vxfm(plane_p[pi], R, p);
656 v_scl(plane_u[pi], plane_u[pi], 64.f / w);
657 v_scl(plane_v[pi], plane_v[pi], 64.f / h);
659 v_scl(plane_u[pi], plane_u[pi], 1.f / su);
660 v_scl(plane_v[pi], plane_v[pi], 1.f / sv);
663 /*---------------------------------------------------------------------------*/
672 static int map_token(FILE *fin, int pi, char key[MAXSTR], char val[MAXSTR])
676 if (fgets(buf, MAXSTR, fin))
686 /* Scan the beginning or end of a block. */
688 if (buf[0] == '{') return T_BEG;
689 if (buf[0] == '}') return T_END;
691 /* Scan a key-value pair. */
695 strcpy(key, strtok(buf, "\""));
696 (void) strtok(NULL, "\"");
697 strcpy(val, strtok(NULL, "\""));
708 "%s %d %d %d %f %f %d",
709 &c, &x0, &y0, &z0, &c,
710 &c, &x1, &y1, &z1, &c,
711 &c, &x2, &y2, &z2, &c,
712 key, &tu, &tv, &r, &su, &sv, &fl) == 22)
714 make_plane(pi, x0, y0, z0,
717 tu, tv, r, su, sv, fl, key);
721 /* If it's not recognized, it must be uninteresting. */
728 /*---------------------------------------------------------------------------*/
730 /* Parse a lump from the given file and add it to the solid. */
732 static void read_lump(struct s_file *fp, FILE *fin)
738 struct s_lump *lp = fp->lv + incl(fp);
742 while ((t = map_token(fin, fp->sc, k, v)))
746 fp->sv[fp->sc].n[0] = plane_n[fp->sc][0];
747 fp->sv[fp->sc].n[1] = plane_n[fp->sc][1];
748 fp->sv[fp->sc].n[2] = plane_n[fp->sc][2];
749 fp->sv[fp->sc].d = plane_d[fp->sc];
751 plane_m[fp->sc] = read_mtrl(fp, k);
753 fp->iv[fp->ic] = fp->sc;
763 /*---------------------------------------------------------------------------*/
765 static void make_path(struct s_file *fp,
767 char v[][MAXSTR], int c)
769 int i, pi = incp(fp);
771 struct s_path *pp = fp->pv + pi;
780 for (i = 0; i < c; i++)
782 if (strcmp(k[i], "targetname") == 0)
785 if (strcmp(k[i], "target") == 0)
786 make_ref(v[i], &pp->pi);
788 if (strcmp(k[i], "state") == 0)
791 if (strcmp(k[i], "speed") == 0)
792 sscanf(v[i], "%f", &pp->t);
794 if (strcmp(k[i], "origin") == 0)
796 int x = 0, y = 0, z = 0;
798 sscanf(v[i], "%d %d %d", &x, &y, &z);
800 pp->p[0] = +(float) x / SCALE;
801 pp->p[1] = +(float) z / SCALE;
802 pp->p[2] = -(float) y / SCALE;
807 static void make_body(struct s_file *fp,
809 char v[][MAXSTR], int c, int l0)
811 int i, bi = incb(fp);
822 struct s_body *bp = fp->bv + bi;
828 for (i = 0; i < c; i++)
830 if (strcmp(k[i], "targetname") == 0)
833 else if (strcmp(k[i], "target") == 0)
834 make_ref(v[i], &bp->pi);
836 else if (strcmp(k[i], "model") == 0)
839 else if (strcmp(k[i], "origin") == 0)
840 sscanf(v[i], "%d %d %d", &x, &y, &z);
842 else if (strcmp(k[i], "classname") != 0)
844 /* Considers other strings as metadata */
845 strcat(fp->av, k[i]);
847 strcat(fp->av, v[i]);
848 strcat(fp->av, "\n");
849 fp->ac += (int) (strlen(v[i]) + (strlen(k[i])) + 2);
854 bp->lc = fp->lc - l0;
856 bp->gc = fp->gc - g0;
858 for (i = 0; i < bp->gc; i++)
859 fp->iv[inci(fp)] = g0++;
861 p[0] = +(float) x / SCALE;
862 p[1] = +(float) z / SCALE;
863 p[2] = -(float) y / SCALE;
865 for (i = v0; i < fp->vc; i++)
866 v_add(fp->vv[i].p, fp->vv[i].p, p);
869 static void make_item(struct s_file *fp,
871 char v[][MAXSTR], int c)
873 int i, hi = inch(fp);
875 struct s_item *hp = fp->hv + hi;
884 for (i = 0; i < c; i++)
886 if (strcmp(k[i], "classname") == 0)
888 if (strcmp(v[i], "light") == 0)
890 else if (strcmp(v[i], "item_health_large") == 0)
892 else if (strcmp(v[i], "item_health_small") == 0)
896 if (strcmp(k[i], "light") == 0)
897 sscanf(v[i], "%d", &hp->n);
899 if (strcmp(k[i], "origin") == 0)
901 int x = 0, y = 0, z = 0;
903 sscanf(v[i], "%d %d %d", &x, &y, &z);
905 hp->p[0] = +(float) x / SCALE;
906 hp->p[1] = +(float) z / SCALE;
907 hp->p[2] = -(float) y / SCALE;
912 static void make_bill(struct s_file *fp,
914 char v[][MAXSTR], int c)
916 int i, ri = incr(fp);
918 struct s_bill *rp = fp->rv + ri;
920 memset(rp, 0, sizeof (struct s_bill));
923 for (i = 0; i < c; i++)
925 if (strcmp(k[i], "width") == 0)
926 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
927 if (strcmp(k[i], "height") == 0)
928 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
930 if (strcmp(k[i], "xrot") == 0)
931 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
932 if (strcmp(k[i], "yrot") == 0)
933 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
934 if (strcmp(k[i], "zrot") == 0)
935 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
937 if (strcmp(k[i], "time") == 0)
938 sscanf(v[i], "%f", &rp->t);
939 if (strcmp(k[i], "dist") == 0)
940 sscanf(v[i], "%f", &rp->d);
941 if (strcmp(k[i], "flag") == 0)
942 sscanf(v[i], "%d", &rp->fl);
944 if (strcmp(k[i], "image") == 0)
946 rp->mi = read_mtrl(fp, v[i]);
947 fp->mv[rp->mi].fl |= M_CLAMPED;
950 if (strcmp(k[i], "origin") == 0)
952 int x = 0, y = 0, z = 0;
955 sscanf(v[i], "%d %d %d", &x, &y, &z);
957 p[0] = +(float) x / SCALE;
958 p[1] = +(float) z / SCALE;
959 p[2] = -(float) y / SCALE;
962 rp->rx[0] = V_DEG(fatan2f(+p[1], rp->d));
963 rp->ry[0] = V_DEG(fatan2f(+p[0], -p[2]));
967 if (rp->fl & B_ADDITIVE)
968 fp->mv[rp->mi].fl |= M_ADDITIVE;
971 static void make_goal(struct s_file *fp,
973 char v[][MAXSTR], int c)
975 int i, zi = incz(fp);
977 struct s_goal *zp = fp->zv + zi;
986 for (i = 0; i < c; i++)
988 if (strcmp(k[i], "radius") == 0)
989 sscanf(v[i], "%f", &zp->r);
990 if (strcmp(k[i], "skip") == 0)
991 sscanf(v[i], "%d", &zp->s);
992 if (strcmp(k[i], "special") == 0)
993 sscanf(v[i], "%d", &zp->c);
995 if (strcmp(k[i], "origin") == 0)
997 int x = 0, y = 0, z = 0;
999 sscanf(v[i], "%d %d %d", &x, &y, &z);
1001 zp->p[0] = +(float) (x) / SCALE;
1002 zp->p[1] = +(float) (z - 24) / SCALE;
1003 zp->p[2] = -(float) (y) / SCALE;
1008 static void make_view(struct s_file *fp,
1010 char v[][MAXSTR], int c)
1012 int i, wi = incw(fp);
1014 struct s_view *wp = fp->wv + wi;
1023 for (i = 0; i < c; i++)
1025 if (strcmp(k[i], "target") == 0)
1026 make_ref(v[i], targ_wi + wi);
1028 if (strcmp(k[i], "origin") == 0)
1030 int x = 0, y = 0, z = 0;
1032 sscanf(v[i], "%d %d %d", &x, &y, &z);
1034 wp->p[0] = +(float) x / SCALE;
1035 wp->p[1] = +(float) z / SCALE;
1036 wp->p[2] = -(float) y / SCALE;
1041 static void make_jump(struct s_file *fp,
1043 char v[][MAXSTR], int c)
1045 int i, ji = incj(fp);
1047 struct s_jump *jp = fp->jv + ji;
1057 for (i = 0; i < c; i++)
1059 if (strcmp(k[i], "radius") == 0)
1060 sscanf(v[i], "%f", &jp->r);
1062 if (strcmp(k[i], "target") == 0)
1063 make_ref(v[i], targ_ji + ji);
1065 if (strcmp(k[i], "origin") == 0)
1067 int x = 0, y = 0, z = 0;
1069 sscanf(v[i], "%d %d %d", &x, &y, &z);
1071 jp->p[0] = +(float) x / SCALE;
1072 jp->p[1] = +(float) z / SCALE;
1073 jp->p[2] = -(float) y / SCALE;
1078 static void make_swch(struct s_file *fp,
1080 char v[][MAXSTR], int c)
1082 int i, xi = incx(fp);
1084 struct s_swch *xp = fp->xv + xi;
1097 for (i = 0; i < c; i++)
1099 if (strcmp(k[i], "radius") == 0)
1100 sscanf(v[i], "%f", &xp->r);
1102 if (strcmp(k[i], "target") == 0)
1103 make_ref(v[i], &xp->pi);
1105 if (strcmp(k[i], "timer") == 0)
1106 sscanf(v[i], "%f", &xp->t0);
1108 if (strcmp(k[i], "state") == 0)
1111 if (strcmp(k[i], "invisible") == 0)
1114 if (strcmp(k[i], "origin") == 0)
1116 int x = 0, y = 0, z = 0;
1118 sscanf(v[i], "%d %d %d", &x, &y, &z);
1120 xp->p[0] = +(float) x / SCALE;
1121 xp->p[1] = +(float) z / SCALE;
1122 xp->p[2] = -(float) y / SCALE;
1127 static void make_targ(struct s_file *fp,
1129 char v[][MAXSTR], int c)
1133 targ_p[targ_n][0] = 0.f;
1134 targ_p[targ_n][1] = 0.f;
1135 targ_p[targ_n][3] = 0.f;
1137 for (i = 0; i < c; i++)
1139 if (strcmp(k[i], "targetname") == 0)
1140 make_sym(v[i], targ_n);
1142 if (strcmp(k[i], "origin") == 0)
1144 int x = 0, y = 0, z = 0;
1146 sscanf(v[i], "%d %d %d", &x, &y, &z);
1148 targ_p[targ_n][0] = +(float) x / SCALE;
1149 targ_p[targ_n][1] = +(float) z / SCALE;
1150 targ_p[targ_n][2] = -(float) y / SCALE;
1157 static void make_ball(struct s_file *fp,
1159 char v[][MAXSTR], int c)
1161 int i, ui = incu(fp);
1163 struct s_ball *up = fp->uv + ui;
1187 for (i = 0; i < c; i++)
1189 if (strcmp(k[i], "radius") == 0)
1190 sscanf(v[i], "%f", &up->r);
1192 if (strcmp(k[i], "origin") == 0)
1194 int x = 0, y = 0, z = 0;
1196 sscanf(v[i], "%d %d %d", &x, &y, &z);
1198 up->p[0] = +(float) (x) / SCALE;
1199 up->p[1] = +(float) (z - 24) / SCALE;
1200 up->p[2] = -(float) (y) / SCALE;
1204 up->p[1] += up->r + SMALL;
1207 /*---------------------------------------------------------------------------*/
1209 static void read_ent(struct s_file *fp, FILE *fin)
1211 char k[MAXKEY][MAXSTR];
1212 char v[MAXKEY][MAXSTR];
1213 int t, i = 0, c = 0;
1217 while ((t = map_token(fin, -1, k[c], v[c])))
1221 if (strcmp(k[c], "classname") == 0)
1225 if (t == T_BEG) read_lump(fp, fin);
1226 if (t == T_END) break;
1229 if (!strcmp(v[i], "light")) make_item(fp, k, v, c);
1230 if (!strcmp(v[i], "item_health_large")) make_item(fp, k, v, c);
1231 if (!strcmp(v[i], "item_health_small")) make_item(fp, k, v, c);
1232 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1233 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1234 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1235 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1236 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1237 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1238 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1239 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1240 if (!strcmp(v[i], "worldspawn")) make_body(fp, k, v, c, l0);
1241 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1242 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1245 static void read_map(struct s_file *fp, FILE *fin)
1251 while ((t = map_token(fin, -1, k, v)))
1256 /*---------------------------------------------------------------------------*/
1258 /* Test the location of a point with respect to a side plane. */
1260 static int fore_side(const float p[3], const struct s_side *sp)
1262 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1265 static int on_side(const float p[3], const struct s_side *sp)
1267 float d = v_dot(p, sp->n) - sp->d;
1269 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1272 /*---------------------------------------------------------------------------*/
1274 * Confirm that the addition of a vert would not result in degenerate
1278 static int ok_vert(const struct s_file *fp,
1279 const struct s_lump *lp, const float p[3])
1284 for (i = 0; i < lp->vc; i++)
1286 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1290 if (v_len(r) < SMALL)
1296 /*---------------------------------------------------------------------------*/
1299 * The following functions take the set of planes defining a lump and
1300 * find the verts, edges, and geoms that describe its boundaries. To
1301 * do this, they first find the verts, and then search these verts for
1302 * valid edges and geoms. It may be more efficient to compute edges
1303 * and geoms directly by clipping down infinite line segments and
1304 * planes, but this would be more complex and prone to numerical
1309 * Given 3 side planes, compute the point of intersection, if any.
1310 * Confirm that this point falls within the current lump, and that it
1311 * is unique. Add it as a vert of the solid.
1313 static void clip_vert(struct s_file *fp,
1314 struct s_lump *lp, int si, int sj, int sk)
1316 float M[16], X[16], I[16];
1320 d[0] = fp->sv[si].d;
1321 d[1] = fp->sv[sj].d;
1322 d[2] = fp->sv[sk].d;
1324 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1331 for (i = 0; i < lp->sc; i++)
1333 int si = fp->iv[lp->s0 + i];
1335 if (fore_side(p, fp->sv + si))
1339 if (ok_vert(fp, lp, p))
1341 v_cpy(fp->vv[fp->vc].p, p);
1343 fp->iv[fp->ic] = fp->vc;
1352 * Given two side planes, find an edge along their intersection by
1353 * finding a pair of vertices that fall on both planes. Add it to the
1356 static void clip_edge(struct s_file *fp,
1357 struct s_lump *lp, int si, int sj)
1361 for (i = 1; i < lp->vc; i++)
1362 for (j = 0; j < i; j++)
1364 int vi = fp->iv[lp->v0 + i];
1365 int vj = fp->iv[lp->v0 + j];
1367 if (on_side(fp->vv[vi].p, fp->sv + si) &&
1368 on_side(fp->vv[vj].p, fp->sv + si) &&
1369 on_side(fp->vv[vi].p, fp->sv + sj) &&
1370 on_side(fp->vv[vj].p, fp->sv + sj))
1372 fp->ev[fp->ec].vi = vi;
1373 fp->ev[fp->ec].vj = vj;
1375 fp->iv[fp->ic] = fp->ec;
1385 * Find all verts that lie on the given side of the lump. Sort these
1386 * verts to have a counter-clockwise winding about the plane normal.
1387 * Create geoms to tessellate the resulting convex polygon.
1389 static void clip_geom(struct s_file *fp,
1390 struct s_lump *lp, int si)
1392 int m[256], t[256], d, i, j, n = 0;
1397 struct s_side *sp = fp->sv + si;
1401 for (i = 0; i < lp->vc; i++)
1403 int vi = fp->iv[lp->v0 + i];
1405 if (on_side(fp->vv[vi].p, sp))
1410 v_add(v, fp->vv[vi].p, plane_p[si]);
1412 fp->tv[t[n]].u[0] = v_dot(v, plane_u[si]);
1413 fp->tv[t[n]].u[1] = v_dot(v, plane_v[si]);
1421 for (i = 1; i < n; i++)
1422 for (j = i + 1; j < n; j++)
1424 v_sub(u, fp->vv[m[i]].p, fp->vv[m[0]].p);
1425 v_sub(v, fp->vv[m[j]].p, fp->vv[m[0]].p);
1428 if (v_dot(w, sp->n) < 0.f)
1442 for (i = 0; i < n - 2; i++)
1444 fp->gv[fp->gc].mi = plane_m[si];
1446 fp->gv[fp->gc].ti = t[0];
1447 fp->gv[fp->gc].tj = t[i + 1];
1448 fp->gv[fp->gc].tk = t[i + 2];
1450 fp->gv[fp->gc].si = si;
1451 fp->gv[fp->gc].sj = si;
1452 fp->gv[fp->gc].sk = si;
1454 fp->gv[fp->gc].vi = m[0];
1455 fp->gv[fp->gc].vj = m[i + 1];
1456 fp->gv[fp->gc].vk = m[i + 2];
1458 fp->iv[fp->ic] = fp->gc;
1466 * Iterate the sides of the lump, attempting to generate a new vert for
1467 * each trio of planes, a new edge for each pair of planes, and a new
1468 * set of geom for each visible plane.
1470 static void clip_lump(struct s_file *fp, struct s_lump *lp)
1477 for (i = 2; i < lp->sc; i++)
1478 for (j = 1; j < i; j++)
1479 for (k = 0; k < j; k++)
1483 fp->iv[lp->s0 + k]);
1488 for (i = 1; i < lp->sc; i++)
1489 for (j = 0; j < i; j++)
1492 fp->iv[lp->s0 + j]);
1497 for (i = 0; i < lp->sc; i++)
1498 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0)
1500 fp->iv[lp->s0 + i]);
1502 for (i = 0; i < lp->sc; i++)
1503 if (plane_f[fp->iv[lp->s0 + i]])
1507 static void clip_file(struct s_file *fp)
1511 for (i = 0; i < fp->lc; i++)
1512 clip_lump(fp, fp->lv + i);
1515 /*---------------------------------------------------------------------------*/
1518 * For each body element type, determine if element 'p' is equivalent
1519 * to element 'q'. This is more than a simple memory compare. It
1520 * effectively snaps mtrls and verts together, and may reverse the
1521 * winding of an edge or a geom. This is done in order to maximize
1522 * the number of elements that can be eliminated.
1525 static int comp_mtrl(const struct s_mtrl *mp, const struct s_mtrl *mq)
1527 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1528 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1529 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1530 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1532 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1533 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1534 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1535 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1537 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1538 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1539 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1540 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1542 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1543 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1544 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1545 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1547 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1549 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1554 static int comp_vert(const struct s_vert *vp, const struct s_vert *vq)
1556 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1557 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1558 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1563 static int comp_edge(const struct s_edge *ep, const struct s_edge *eq)
1565 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1566 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1571 static int comp_side(const struct s_side *sp, const struct s_side *sq)
1573 if (fabs(sp->d - sq->d) > SMALL) return 0;
1574 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1579 static int comp_texc(const struct s_texc *tp, const struct s_texc *tq)
1581 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1582 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1587 static int comp_geom(const struct s_geom *gp, const struct s_geom *gq)
1589 if (gp->mi != gq->mi) return 0;
1591 if (gp->ti != gq->ti) return 0;
1592 if (gp->si != gq->si) return 0;
1593 if (gp->vi != gq->vi) return 0;
1595 if (gp->tj != gq->tj) return 0;
1596 if (gp->sj != gq->sj) return 0;
1597 if (gp->vj != gq->vj) return 0;
1599 if (gp->tk != gq->tk) return 0;
1600 if (gp->sk != gq->sk) return 0;
1601 if (gp->vk != gq->vk) return 0;
1606 /*---------------------------------------------------------------------------*/
1609 * For each file element type, replace all references to element 'i'
1610 * with a reference to element 'j'. These are used when optimizing
1611 * and sorting the file.
1614 static void swap_mtrl(struct s_file *fp, int mi, int mj)
1618 for (i = 0; i < fp->gc; i++)
1619 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1620 for (i = 0; i < fp->rc; i++)
1621 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1624 static void swap_vert(struct s_file *fp, int vi, int vj)
1628 for (i = 0; i < fp->ec; i++)
1630 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1631 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1634 for (i = 0; i < fp->gc; i++)
1636 if (fp->gv[i].vi == vi) fp->gv[i].vi = vj;
1637 if (fp->gv[i].vj == vi) fp->gv[i].vj = vj;
1638 if (fp->gv[i].vk == vi) fp->gv[i].vk = vj;
1641 for (i = 0; i < fp->lc; i++)
1642 for (j = 0; j < fp->lv[i].vc; j++)
1643 if (fp->iv[fp->lv[i].v0 + j] == vi)
1644 fp->iv[fp->lv[i].v0 + j] = vj;
1647 static void swap_edge(struct s_file *fp, int ei, int ej)
1651 for (i = 0; i < fp->lc; i++)
1652 for (j = 0; j < fp->lv[i].ec; j++)
1653 if (fp->iv[fp->lv[i].e0 + j] == ei)
1654 fp->iv[fp->lv[i].e0 + j] = ej;
1657 static void swap_side(struct s_file *fp, int si, int sj)
1661 for (i = 0; i < fp->gc; i++)
1663 if (fp->gv[i].si == si) fp->gv[i].si = sj;
1664 if (fp->gv[i].sj == si) fp->gv[i].sj = sj;
1665 if (fp->gv[i].sk == si) fp->gv[i].sk = sj;
1667 for (i = 0; i < fp->nc; i++)
1668 if (fp->nv[i].si == si) fp->nv[i].si = sj;
1670 for (i = 0; i < fp->lc; i++)
1671 for (j = 0; j < fp->lv[i].sc; j++)
1672 if (fp->iv[fp->lv[i].s0 + j] == si)
1673 fp->iv[fp->lv[i].s0 + j] = sj;
1676 static void swap_texc(struct s_file *fp, int ti, int tj)
1680 for (i = 0; i < fp->gc; i++)
1682 if (fp->gv[i].ti == ti) fp->gv[i].ti = tj;
1683 if (fp->gv[i].tj == ti) fp->gv[i].tj = tj;
1684 if (fp->gv[i].tk == ti) fp->gv[i].tk = tj;
1689 static void swap_geom(struct s_file *fp, int gi, int gj)
1693 for (i = 0; i < fp->lc; i++)
1694 for (j = 0; j < fp->lv[i].gc; j++)
1695 if (fp->iv[fp->lv[i].g0 + j] == gi)
1696 fp->iv[fp->lv[i].g0 + j] = gj;
1698 for (i = 0; i < fp->bc; i++)
1699 for (j = 0; j < fp->bv[i].gc; j++)
1700 if (fp->iv[fp->bv[i].g0 + j] == gi)
1701 fp->iv[fp->bv[i].g0 + j] = gj;
1704 /*---------------------------------------------------------------------------*/
1706 static void uniq_mtrl(struct s_file *fp)
1710 for (i = 0; i < fp->mc; i++)
1712 for (j = 0; j < k; j++)
1713 if (comp_mtrl(fp->mv + i, fp->mv + j))
1715 swap_mtrl(fp, i, j);
1723 fp->mv[k] = fp->mv[i];
1724 swap_mtrl(fp, i, k);
1733 static void uniq_vert(struct s_file *fp)
1737 for (i = 0; i < fp->vc; i++)
1739 for (j = 0; j < k; j++)
1740 if (comp_vert(fp->vv + i, fp->vv + j))
1742 swap_vert(fp, i, j);
1750 fp->vv[k] = fp->vv[i];
1751 swap_vert(fp, i, k);
1760 static void uniq_edge(struct s_file *fp)
1764 for (i = 0; i < fp->ec; i++)
1766 for (j = 0; j < k; j++)
1767 if (comp_edge(fp->ev + i, fp->ev + j))
1769 swap_edge(fp, i, j);
1777 fp->ev[k] = fp->ev[i];
1778 swap_edge(fp, i, k);
1787 static void uniq_geom(struct s_file *fp)
1791 for (i = 0; i < fp->gc; i++)
1793 for (j = 0; j < k; j++)
1794 if (comp_geom(fp->gv + i, fp->gv + j))
1796 swap_geom(fp, i, j);
1804 fp->gv[k] = fp->gv[i];
1805 swap_geom(fp, i, k);
1814 static void uniq_texc(struct s_file *fp)
1818 for (i = 0; i < fp->tc; i++)
1820 for (j = 0; j < k; j++)
1821 if (comp_texc(fp->tv + i, fp->tv + j))
1823 swap_texc(fp, i, j);
1831 fp->tv[k] = fp->tv[i];
1832 swap_texc(fp, i, k);
1841 static void uniq_side(struct s_file *fp)
1845 for (i = 0; i < fp->sc; i++)
1847 for (j = 0; j < k; j++)
1848 if (comp_side(fp->sv + i, fp->sv + j))
1850 swap_side(fp, i, j);
1858 fp->sv[k] = fp->sv[i];
1859 swap_side(fp, i, k);
1868 static void uniq_file(struct s_file *fp)
1878 /*---------------------------------------------------------------------------*/
1880 static void sort_file(struct s_file *fp)
1884 /* Sort billboards farthest to nearest. */
1886 for (i = 0; i < fp->rc; i++)
1887 for (j = i + 1; j < fp->rc; j++)
1888 if (fp->rv[j].d > fp->rv[i].d)
1893 fp->rv[i] = fp->rv[j];
1897 /* Ensure the first vertex is the lowest. */
1899 for (i = 0; i < fp->vc; i++)
1900 if (fp->vv[0].p[1] > fp->vv[i].p[1])
1905 fp->vv[0] = fp->vv[i];
1908 swap_vert(fp, 0, -1);
1909 swap_vert(fp, i, 0);
1910 swap_vert(fp, -1, i);
1914 /*---------------------------------------------------------------------------*/
1916 static int test_lump_side(const struct s_file *fp,
1917 const struct s_lump *lp,
1918 const struct s_side *sp)
1926 /* If the given side is part of the given lump, then the lump is behind. */
1928 for (si = 0; si < lp->sc; si++)
1929 if (fp->sv + fp->iv[lp->s0 + si] == sp)
1932 /* Check if each lump vertex is in front of, behind, on the side. */
1934 for (vi = 0; vi < lp->vc; vi++)
1936 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
1942 /* If no verts are behind, the lump is in front, and vice versa. */
1944 if (f > 0 && b == 0) return +1;
1945 if (b > 0 && f == 0) return -1;
1947 /* Else, the lump crosses the side. */
1952 static int node_node(struct s_file *fp, int l0, int lc)
1956 /* Base case. Dump all given lumps into a leaf node. */
1958 fp->nv[fp->nc].si = -1;
1959 fp->nv[fp->nc].ni = -1;
1960 fp->nv[fp->nc].nj = -1;
1961 fp->nv[fp->nc].l0 = l0;
1962 fp->nv[fp->nc].lc = lc;
1972 int li = 0, lic = 0;
1973 int lj = 0, ljc = 0;
1974 int lk = 0, lkc = 0;
1977 /* Find the side that most evenly splits the given lumps. */
1979 for (si = 0; si < fp->sc; si++)
1985 for (li = 0; li < lc; li++)
1986 if ((k = test_lump_side(fp, fp->lv + l0 + li, fp->sv + si)))
1993 if ((d < sjd) || (d == sjd && o < sjo))
2001 /* Flag each lump with its position WRT the side. */
2003 for (li = 0; li < lc; li++)
2004 switch (test_lump_side(fp, fp->lv + l0 + li, fp->sv + sj))
2006 case +1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10; break;
2007 case 0: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20; break;
2008 case -1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40; break;
2011 /* Sort all lumps in the range by their flag values. */
2013 for (li = 1; li < lc; li++)
2014 for (lj = 0; lj < li; lj++)
2015 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
2019 l = fp->lv[l0 + li];
2020 fp->lv[l0 + li] = fp->lv[l0 + lj];
2021 fp->lv[l0 + lj] = l;
2024 /* Establish the in-front, on, and behind lump ranges. */
2030 for (i = lc - 1; i >= 0; i--)
2031 switch (fp->lv[l0 + i].fl & 0xf0)
2033 case 0x10: li = l0 + i; lic++; break;
2034 case 0x20: lj = l0 + i; ljc++; break;
2035 case 0x40: lk = l0 + i; lkc++; break;
2038 /* Add the lumps on the side to the node. */
2043 fp->nv[i].ni = node_node(fp, li, lic);
2045 fp->nv[i].nj = node_node(fp, lk, lkc);
2053 static void node_file(struct s_file *fp)
2057 /* Sort the lumps of each body into BSP nodes. */
2059 for (bi = 0; bi < fp->bc; bi++)
2060 fp->bv[bi].ni = node_node(fp, fp->bv[bi].l0, fp->bv[bi].lc);
2063 /*---------------------------------------------------------------------------*/
2065 static void dump_file(struct s_file *p, const char *name)
2067 /* FIXME: Count visible geoms.
2069 * I'm afraid items break this (not sure though) so leaving it out.
2079 int m = p->rc + p->cc * 128 + (p->zc * p->jc + p->xc) * 32;
2082 /* Count the number of solid lumps. */
2084 for (i = 0; i < p->lc; i++)
2085 if ((p->lv[i].fl & 1) == 0)
2089 /* Count the number of visible geoms. */
2091 for (i = 0; i < p->bc; i++)
2093 for (j = 0; j < p->bv[i].lc; j++)
2094 m += p->lv[p->bv[i].l0 + j].gc;
2099 /* Count the total value of all coins. */
2101 for (i = 0; i < p->hc; i++)
2102 if (p->hv[i].t == ITEM_COIN)
2106 printf("%s (%d/%d/$%d)\n"
2108 printf("%s (%d/$%d)\n"
2109 " mtrl vert edge side texc"
2110 " geom lump path node body\n"
2111 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2112 " item goal view jump swch"
2113 " bill ball char indx\n"
2114 "%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2119 p->mc, p->vc, p->ec, p->sc, p->tc,
2120 p->gc, p->lc, p->pc, p->nc, p->bc,
2121 p->hc, p->zc, p->wc, p->jc, p->xc,
2122 p->rc, p->uc, p->ac, p->ic);
2125 /* Skip the ugly SDL main substitution since we only need sdl_image. */
2130 int main(int argc, char *argv[])
2139 if (config_data_path(argv[2], NULL))
2141 strncpy(src, argv[1], MAXSTR);
2142 strncpy(dst, argv[1], MAXSTR);
2144 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2145 strcpy(dst + strlen(dst) - 4, ".sol");
2147 strcat(dst, ".sol");
2149 if ((fin = fopen(src, "r")))
2171 else fprintf(stderr, "Failure to establish data directory\n");
2173 else fprintf(stderr, "Usage: %s <map> [data]\n", argv[0]);