PageRenderTime 37ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/quakeforge/branches/release_0_5_5/tools/bsp2img/bsp2img.c

#
C | 948 lines | 720 code | 149 blank | 79 comment | 116 complexity | fd459d92ee6a454cf35543eb519742c8 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-3.0, AGPL-1.0, Unlicense
  1. /*
  2. bsp2bp - converts Quake I BSP's to a bitmap (map!) of the level
  3. Copyright (C) 1999 Matthew Wong <mkyw@iname.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. */
  16. #include "config.h"
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <unistd.h>
  20. #include <errno.h>
  21. #include <string.h>
  22. #include <math.h>
  23. #include <limits.h>
  24. #include "QF/bspfile.h"
  25. #include "QF/cmd.h"
  26. #include "QF/cvar.h"
  27. #include "QF/pcx.h"
  28. #include "QF/quakeio.h"
  29. #include "QF/sys.h"
  30. #include "QF/zone.h"
  31. #include "compat.h"
  32. #define PROGNAME "bsp2img"
  33. #define V_MAJOR 0
  34. #define V_MINOR 0
  35. #define V_REV 12
  36. #define Z_PAD_HACK 16
  37. #define MAX_REF_FACES 4
  38. #define MEMSIZE (12 * 1024 * 1024)
  39. #define X point[0]
  40. #define Y point[1]
  41. #define Z point[2]
  42. #define SUB(a,b,c) ((c).X = (a).X - (b).X, \
  43. (c).Y = (a).Y - (b).Y, \
  44. (c).Z = (a).Z - (b).Z)
  45. #define DOT(a,b) ((a).X * (b).X + (a).Y * (b).Y + (a).Z * (b).Z)
  46. #define CROSS(a,b,c) ((c).X = (a).Y * (b).Z - (a).Z * (b).Y, \
  47. (c).Y = (a).Z * (b).X - (a).X * (b).Z, \
  48. (c).Z = (a).X * (b).Y - (a).Y * (b).X)
  49. /*
  50. Thanks fly to Id for a hackable game! :)
  51. */
  52. /* MW */
  53. typedef struct edge_extra_t {
  54. long num_face_ref;
  55. long ref_faces[MAX_REF_FACES]; // which faces are referenced
  56. dvertex_t ref_faces_normal[MAX_REF_FACES]; // normal of referenced
  57. // faces
  58. int ref_faces_area[MAX_REF_FACES]; // area of the referenced faces
  59. } edge_extra_t;
  60. typedef unsigned char eightbit;
  61. typedef struct options_t {
  62. char *bspf_name;
  63. char *outf_name;
  64. float scaledown;
  65. float z_pad;
  66. float image_pad;
  67. int z_direction;
  68. int camera_axis; // 1 - X, 2 - Y, 3 - Z, negatives will
  69. // come from negative side of axis
  70. int edgeremove;
  71. float flat_threshold;
  72. int area_threshold;
  73. int linelen_threshold;
  74. int negative_image;
  75. int write_raw;
  76. int write_nocomp;
  77. } options_t;
  78. typedef struct {
  79. eightbit *image;
  80. long width;
  81. long height;
  82. } image_t;
  83. struct options_t options;
  84. static void
  85. show_help (void)
  86. {
  87. printf ("BSP->bitmap, version %d.%d.%d\n\n", V_MAJOR, V_MINOR, V_REV);
  88. printf ("Usage:\n");
  89. printf (" %s [options] <bspfile> <outfile>\n\n", PROGNAME);
  90. printf ("Options:\n");
  91. printf (" -s<scaledown> default: 4, ie 1/4 scale\n");
  92. printf (" -z<z_scaling> default: 0 for flat map, >0 for iso 3d, -1 for auto\n");
  93. printf (" -p<padding> default: 16-pixel border around final image\n");
  94. printf (" -d<direction> iso 3d direction: 7 0 1\n");
  95. printf (" \\ | /\n");
  96. printf (" 6--+--2\n");
  97. printf (" / | \\\n");
  98. printf (" 5 4 3\n");
  99. printf (" default: 7\n");
  100. printf (" -c<camera_axis> default: +Z (+/- X/Y/Z axis)\n");
  101. printf (" -t<flatness> threshold of dot product for edge removal;\n");
  102. printf (" default is 0.90\n");
  103. printf (" -e disable extraneous edges removal\n");
  104. printf (" -a<area> minimum area for a polygon to be drawn\n");
  105. printf (" default is 0\n");
  106. printf (" -l<length> minimum length for an edge to be drawn\n");
  107. printf (" default is 0\n");
  108. printf (" -n negative image (black on white\n");
  109. printf (" -r write raw data, rather than bmp file\n");
  110. return;
  111. }
  112. static void
  113. plotpoint (image_t *image, long xco, long yco, unsigned int color)
  114. {
  115. unsigned int bigcol = 0;
  116. if (xco < 0 || xco > image->width || yco < 0 || yco > image->height)
  117. return;
  118. bigcol = (unsigned int) image->image[yco * image->width + xco];
  119. bigcol += color;
  120. bigcol = bound (0, bigcol, 255);
  121. image->image[yco * image->width + xco] = (eightbit) bigcol;
  122. return;
  123. }
  124. static void
  125. bresline (image_t * image, long x1, long y1, long x2, long y2,
  126. unsigned int color)
  127. {
  128. long x = 0, y = 0;
  129. long deltax = 0, deltay = 0;
  130. long xchange = 0, ychange = 0;
  131. long error, length, i;
  132. x = x1;
  133. y = y1;
  134. deltax = x2 - x1;
  135. deltay = y2 - y1;
  136. if (deltax < 0) {
  137. xchange = -1;
  138. deltax = -deltax;
  139. } else {
  140. xchange = 1;
  141. }
  142. if (deltay < 0) {
  143. ychange = -1;
  144. deltay = -deltay;
  145. } else {
  146. ychange = 1;
  147. }
  148. /* Main seq */
  149. error = 0;
  150. i = 0;
  151. if (deltax < deltay) {
  152. length = deltay + 1;
  153. while (i < length) {
  154. y = y + ychange;
  155. error = error + deltax;
  156. if (error > deltay) {
  157. x = x + xchange;
  158. error = error - deltay;
  159. }
  160. i++;
  161. plotpoint (image, x, y, color);
  162. }
  163. } else {
  164. length = deltax + 1;
  165. while (i < length) {
  166. x = x + xchange;
  167. error = error + deltay;
  168. if (error > deltax) {
  169. y = y + ychange;
  170. error = error - deltax;
  171. }
  172. i++;
  173. plotpoint (image, x, y, color);
  174. }
  175. }
  176. }
  177. static void
  178. def_options (struct options_t *opt)
  179. {
  180. static struct options_t locopt;
  181. locopt.bspf_name = NULL;
  182. locopt.outf_name = NULL;
  183. locopt.scaledown = 4.0;
  184. locopt.image_pad = 16.0;
  185. locopt.z_pad = 0.0;
  186. locopt.z_direction = 1;
  187. locopt.camera_axis = 3; // default is from +Z
  188. locopt.edgeremove = 1;
  189. locopt.flat_threshold = 0.90;
  190. locopt.area_threshold = 0;
  191. locopt.linelen_threshold = 0;
  192. locopt.negative_image = 0;
  193. locopt.write_raw = 0;
  194. locopt.write_nocomp = 1;
  195. memcpy (opt, &locopt, sizeof (struct options_t));
  196. return;
  197. }
  198. static void
  199. get_options (struct options_t *opt, int argc, char *argv[])
  200. {
  201. static struct options_t locopt;
  202. int i = 0;
  203. char *arg;
  204. long lnum = 0;
  205. float fnum = 0.0;
  206. char pm = '+', axis = 'Z';
  207. /* Copy curr options */
  208. memcpy (&locopt, opt, sizeof (struct options_t));
  209. /* Go through command line */
  210. for (i = 1; i < argc; i++) {
  211. arg = argv[i];
  212. if (arg[0] == '-') {
  213. /* Okay, dash-something */
  214. switch (arg[1]) {
  215. case 's':
  216. if (sscanf (&arg[2], "%ld", &lnum) == 1)
  217. if (lnum > 0)
  218. locopt.scaledown = (float) lnum;
  219. break;
  220. case 'z':
  221. if (sscanf (&arg[2], "%ld", &lnum) == 1)
  222. if (lnum >= -1)
  223. locopt.z_pad = (float) lnum;
  224. break;
  225. case 'p':
  226. if (sscanf (&arg[2], "%ld", &lnum) == 1)
  227. if (lnum >= 0)
  228. locopt.image_pad = (float) lnum;
  229. break;
  230. case 'd':
  231. if (sscanf (&arg[2], "%ld", &lnum) == 1)
  232. if (lnum >= 0 && lnum <= 7)
  233. locopt.z_direction = (int) lnum;
  234. break;
  235. case 'c':
  236. if (strlen (&arg[2]) == 2) {
  237. pm = arg[2];
  238. axis = arg[3];
  239. printf ("-c%c%c\n", pm, axis);
  240. switch (axis) {
  241. case 'x':
  242. case 'X':
  243. locopt.camera_axis = 1;
  244. break;
  245. case 'y':
  246. case 'Y':
  247. locopt.camera_axis = 2;
  248. break;
  249. case 'z':
  250. case 'Z':
  251. locopt.camera_axis = 3;
  252. break;
  253. default:
  254. printf ("Must specify a valid axis.\n");
  255. show_help ();
  256. exit (1);
  257. break;
  258. }
  259. switch (pm) {
  260. case '+':
  261. break;
  262. case '-':
  263. locopt.camera_axis = -locopt.camera_axis;
  264. break;
  265. default:
  266. printf ("Must specify +/-\n");
  267. show_help ();
  268. exit (1);
  269. break;
  270. }
  271. } else {
  272. printf ("Unknown option: -%s\n", &arg[1]);
  273. show_help ();
  274. exit (1);
  275. }
  276. break;
  277. case 't':
  278. if (sscanf (&arg[2], "%f", &fnum) == 1)
  279. if (fnum >= 0.0 && fnum <= 1.0)
  280. locopt.flat_threshold = (float) fnum;
  281. break;
  282. case 'e':
  283. locopt.edgeremove = 0;
  284. break;
  285. case 'n':
  286. locopt.negative_image = 1;
  287. break;
  288. case 'a':
  289. if (sscanf (&arg[2], "%ld", &lnum) == 1)
  290. if (lnum >= 0)
  291. locopt.area_threshold = (int) lnum;
  292. break;
  293. case 'l':
  294. if (sscanf (&arg[2], "%ld", &lnum) == 1)
  295. if (lnum >= 0)
  296. locopt.linelen_threshold = (int) lnum;
  297. break;
  298. case 'r':
  299. locopt.write_raw = 1;
  300. break;
  301. case 'u':
  302. locopt.write_nocomp = 1;
  303. break;
  304. default:
  305. printf ("Unknown option: -%s\n", &arg[1]);
  306. show_help ();
  307. exit (1);
  308. break;
  309. } /* switch */
  310. } else {
  311. if (locopt.bspf_name == NULL) {
  312. locopt.bspf_name = arg;
  313. } else if (locopt.outf_name == NULL) {
  314. locopt.outf_name = arg;
  315. } else {
  316. printf ("Unknown option: %s\n", arg);
  317. show_help ();
  318. exit (1);
  319. }
  320. } /* if */
  321. } /* for */
  322. memcpy (opt, &locopt, sizeof (struct options_t));
  323. return;
  324. }
  325. static void
  326. show_options (struct options_t *opt)
  327. {
  328. char dirstr[80];
  329. printf ("Options:\n");
  330. printf (" Scale down by: %.0f\n", opt->scaledown);
  331. printf (" Z scale: %.0f\n", opt->z_pad);
  332. printf (" Border: %.0f\n", opt->image_pad);
  333. /* Zoffset calculations */
  334. switch (opt->z_direction) {
  335. case 0:
  336. sprintf (dirstr, "up");
  337. break;
  338. case 1:
  339. sprintf (dirstr, "up & right");
  340. break;
  341. case 2:
  342. sprintf (dirstr, "right");
  343. break;
  344. case 3:
  345. sprintf (dirstr, "down & right");
  346. break;
  347. case 4:
  348. sprintf (dirstr, "down");
  349. break;
  350. case 5:
  351. sprintf (dirstr, "down & left");
  352. break;
  353. case 6:
  354. sprintf (dirstr, "left");
  355. break;
  356. case 7:
  357. sprintf (dirstr, "up & left");
  358. break;
  359. default:
  360. sprintf (dirstr, "unknown!");
  361. break;
  362. }
  363. printf (" Z direction: %d [%s]\n", opt->z_direction, dirstr);
  364. if (opt->z_pad == 0) {
  365. printf (" Warning: direction option has no effect with Z scale set to 0.\n");
  366. }
  367. /* Camera axis */
  368. switch (opt->camera_axis) {
  369. case 1:
  370. sprintf (dirstr, "+X");
  371. break;
  372. case -1:
  373. sprintf (dirstr, "-X");
  374. break;
  375. case 2:
  376. sprintf (dirstr, "+Y");
  377. break;
  378. case -2:
  379. sprintf (dirstr, "-Y");
  380. break;
  381. case 3:
  382. sprintf (dirstr, "+Z");
  383. break;
  384. case -3:
  385. sprintf (dirstr, "-Z");
  386. break;
  387. default:
  388. sprintf (dirstr, "unknown!");
  389. break;
  390. }
  391. printf (" Camera axis: %s\n", dirstr);
  392. printf (" Remove extraneous edges: %s\n",
  393. (opt->edgeremove == 1) ? "yes" : "no");
  394. printf (" Edge removal dot product theshold: %f\n", opt->flat_threshold);
  395. printf (" Minimum polygon area threshold (approximate): %d\n",
  396. opt->area_threshold);
  397. printf (" Minimum line length threshold: %d\n", opt->linelen_threshold);
  398. printf (" Creating %s image.\n",
  399. (opt->negative_image == 1) ? "negative" : "positive");
  400. printf ("\n");
  401. printf (" Input (bsp) file: %s\n", opt->bspf_name);
  402. if (opt->write_raw)
  403. printf (" Output (raw) file: %s\n\n", opt->outf_name);
  404. else
  405. printf (" Output (%s bmp) file: %s\n\n",
  406. opt->write_nocomp ? "uncompressed" : "RLE compressed",
  407. opt->outf_name);
  408. return;
  409. }
  410. static image_t *
  411. create_image (long width, long height)
  412. {
  413. long size;
  414. image_t *image;
  415. image = malloc (sizeof (image_t));
  416. image->width = width;
  417. image->height = height;
  418. size = sizeof (eightbit) * width * height;
  419. if (!(image->image = malloc (size))) {
  420. fprintf (stderr, "Error allocating image buffer %ldx%ld.\n",
  421. width, height);
  422. exit (2);
  423. }
  424. printf ("Allocated buffer %ldx%ld for image.\n", width, height);
  425. memset (image->image, 0, size);
  426. return image;
  427. }
  428. static image_t *
  429. render_map (bsp_t *bsp)
  430. {
  431. long i = 0, j = 0, k = 0, x = 0;
  432. dvertex_t *vertexlist, *vert1, *vert2;
  433. dedge_t *edgelist;
  434. dface_t *facelist;
  435. int *ledges;
  436. /* edge removal stuff */
  437. struct edge_extra_t *edge_extra = NULL;
  438. dvertex_t v0, v1, vect;
  439. int area = 0, usearea;
  440. float minX = 0.0, maxX = 0.0, minY = 0.0, maxY = 0.0, minZ = 0.0;
  441. float maxZ = 0.0, midZ = 0.0, tempf = 0.0;
  442. long Zoffset0 = 0, Zoffset1 = 0;
  443. long Z_Xdir = 1, Z_Ydir = -1;
  444. image_t *image;
  445. int drawcol;
  446. vertexlist = bsp->vertexes;
  447. edgelist = bsp->edges;
  448. facelist = bsp->faces;
  449. ledges = bsp->surfedges;
  450. /* Precalc stuff if we're removing edges - - - - - - - - - - - */
  451. if (options.edgeremove) {
  452. printf ("Precalc edge removal stuff...\n");
  453. edge_extra = malloc (sizeof (struct edge_extra_t) * bsp->numedges);
  454. if (edge_extra == NULL) {
  455. fprintf (stderr, "Error allocating %ld bytes for extra edge info.",
  456. (long) sizeof (struct edge_extra_t) * bsp->numedges);
  457. exit (2);
  458. }
  459. /* initialize the array */
  460. for (i = 0; i < bsp->numedges; i++) {
  461. edge_extra[i].num_face_ref = 0;
  462. for (j = 0; j < MAX_REF_FACES; j++) {
  463. edge_extra[i].ref_faces[j] = -1;
  464. }
  465. }
  466. for (i = 0; i < bsp->numfaces; i++) {
  467. /* calculate the normal (cross product) */
  468. /* starting edge: edgelist[ledges[facelist[i].firstedge]] */
  469. /* number of edges: facelist[i].numedges; */
  470. /* quick hack - just take the first 2 edges */
  471. j = facelist[i].firstedge;
  472. k = j;
  473. vect.X = 0.0;
  474. vect.Y = 0.0;
  475. vect.Z = 0.0;
  476. while (vect.X == 0.0 && vect.Y == 0.0 && vect.Z == 0.0
  477. && k < (facelist[i].numedges + j)) {
  478. dedge_t *e1, *e2;
  479. /* If the first 2 are parallel edges, go with the next one */
  480. k++;
  481. e1 = &edgelist[abs (ledges[j])];
  482. e2 = &edgelist[abs (ledges[k])];
  483. if (ledges[j] > 0) {
  484. v0.X = vertexlist[e1->v[0]].X - vertexlist[e1->v[1]].X;
  485. v0.Y = vertexlist[e1->v[0]].Y - vertexlist[e1->v[1]].Y;
  486. v0.Z = vertexlist[e1->v[0]].Z - vertexlist[e1->v[1]].Z;
  487. v1.X = vertexlist[e2->v[0]].X - vertexlist[e2->v[1]].X;
  488. v1.Y = vertexlist[e2->v[0]].Y - vertexlist[e2->v[1]].Y;
  489. v1.Z = vertexlist[e2->v[0]].Z - vertexlist[e2->v[1]].Z;
  490. } else {
  491. /* negative index, therefore walk in reverse order */
  492. v0.X = vertexlist[e1->v[1]].X - vertexlist[e1->v[0]].X;
  493. v0.Y = vertexlist[e1->v[1]].Y - vertexlist[e1->v[0]].Y;
  494. v0.Z = vertexlist[e1->v[1]].Z - vertexlist[e1->v[0]].Z;
  495. v1.X = vertexlist[e2->v[1]].X - vertexlist[e2->v[0]].X;
  496. v1.Y = vertexlist[e2->v[1]].Y - vertexlist[e2->v[0]].Y;
  497. v1.Z = vertexlist[e2->v[1]].Z - vertexlist[e2->v[0]].Z;
  498. }
  499. /* cross product */
  500. CROSS (v0, v1, vect);
  501. /* Okay, it's not the REAL area, but i'm lazy, and since a lot
  502. of mapmakers use rectangles anyways... */
  503. area = sqrt (DOT (v0, v0)) * sqrt (DOT (v1, v1));
  504. } /* while */
  505. /* reduce cross product to a unit vector */
  506. tempf = sqrt (DOT (vect, vect));
  507. if (tempf > 0.0) {
  508. vect.X = vect.X / tempf;
  509. vect.Y = vect.Y / tempf;
  510. vect.Z = vect.Z / tempf;
  511. } else {
  512. vect.X = 0.0;
  513. vect.Y = 0.0;
  514. vect.Z = 0.0;
  515. }
  516. /* Now go put ref in all edges... */
  517. for (j = 0; j < facelist[i].numedges; j++) {
  518. edge_extra_t *e;
  519. k = j + facelist[i].firstedge;
  520. e = &edge_extra[abs (ledges[k])];
  521. x = e->num_face_ref;
  522. if (e->num_face_ref < MAX_REF_FACES) {
  523. x++;
  524. e->num_face_ref = x;
  525. e->ref_faces[x - 1] = i;
  526. e->ref_faces_normal[x - 1].X = vect.X;
  527. e->ref_faces_normal[x - 1].Y = vect.Y;
  528. e->ref_faces_normal[x - 1].Z = vect.Z;
  529. e->ref_faces_area[x - 1] = area;
  530. }
  531. } /* for */
  532. }
  533. }
  534. /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  535. printf ("Collecting min/max\n");
  536. /* Collect min and max */
  537. for (i = 0; i < bsp->numvertexes; i++) {
  538. /* Ugly hack - flip stuff around for different camera angles */
  539. switch (options.camera_axis) {
  540. case -1: /* -X -- (-y <--> +y, +x into screen,
  541. -x out of screen; -z down, +z up) */
  542. tempf = vertexlist[i].X;
  543. vertexlist[i].X = vertexlist[i].Y;
  544. vertexlist[i].Y = vertexlist[i].Z;
  545. vertexlist[i].Z = -tempf;
  546. break;
  547. case 1: /* +X -- (+y <--> -y; -x into screen,
  548. +x out of screen; -z down, +z up) */
  549. tempf = vertexlist[i].X;
  550. vertexlist[i].X = -vertexlist[i].Y;
  551. vertexlist[i].Y = vertexlist[i].Z;
  552. vertexlist[i].Z = tempf;
  553. break;
  554. case -2: /* -Y -- (+x <--> -x; -y out of screen,
  555. +z up) */
  556. vertexlist[i].X = -vertexlist[i].X;
  557. tempf = vertexlist[i].Z;
  558. vertexlist[i].Z = vertexlist[i].Y;
  559. vertexlist[i].Y = tempf;;
  560. break;
  561. case 2: /* +Y -- (-x <--> +x; +y out of screen,
  562. +z up) */
  563. tempf = vertexlist[i].Z;
  564. vertexlist[i].Z = -vertexlist[i].Y;
  565. vertexlist[i].Y = tempf;;
  566. break;
  567. case -3: /* -Z -- negate X and Z (ie. 180 rotate
  568. along Y axis) */
  569. vertexlist[i].X = -vertexlist[i].X;
  570. vertexlist[i].Z = -vertexlist[i].Z;
  571. break;
  572. case 3: /* +Z -- do nothing! */
  573. default: /* do nothing! */
  574. break;
  575. }
  576. /* flip Y for proper screen cords */
  577. vertexlist[i].Y = -vertexlist[i].Y;
  578. /* max and min */
  579. if (i == 0) {
  580. minX = vertexlist[i].X;
  581. maxX = vertexlist[i].X;
  582. minY = vertexlist[i].Y;
  583. maxY = vertexlist[i].Y;
  584. minZ = vertexlist[i].Z;
  585. maxZ = vertexlist[i].Z;
  586. } else {
  587. minX = min (vertexlist[i].X, minX);
  588. maxX = max (vertexlist[i].X, maxX);
  589. minY = min (vertexlist[i].Y, minY);
  590. maxY = max (vertexlist[i].Y, maxY);
  591. minZ = min (vertexlist[i].Z, minZ);
  592. maxZ = max (vertexlist[i].Z, maxZ);
  593. }
  594. }
  595. if (options.z_pad == -1)
  596. options.z_pad = (long) (maxZ - minZ) / (options.scaledown * Z_PAD_HACK);
  597. midZ = (maxZ + minZ) / 2.0;
  598. printf ("\n");
  599. printf ("Bounds: X [%8.4f .. %8.4f] %8.4f\n", minX, maxX, (maxX - minX));
  600. printf (" Y [%8.4f .. %8.4f] %8.4f\n", minY, maxY, (maxY - minY));
  601. printf (" Z [%8.4f .. %8.4f] %8.4f - mid: %8.4f\n", minZ, maxZ,
  602. (maxZ - minZ), midZ);
  603. /* image array */
  604. {
  605. long width, height;
  606. width = (maxX - minX) / options.scaledown;
  607. width += (options.image_pad + options.z_pad) * 2;
  608. height = (maxY - minY) / options.scaledown;
  609. height += (options.image_pad + options.z_pad) * 2;
  610. image = create_image (width, height);
  611. }
  612. /* Zoffset calculations */
  613. switch (options.z_direction) {
  614. case 0:
  615. Z_Xdir = 0; /* up */
  616. Z_Ydir = 1;
  617. break;
  618. default: /* unknown */
  619. case 1: /* up & right */
  620. Z_Xdir = 1;
  621. Z_Ydir = -1;
  622. break;
  623. case 2: /* right */
  624. Z_Xdir = 1;
  625. Z_Ydir = 0;
  626. break;
  627. case 3: /* down & right */
  628. Z_Xdir = 1;
  629. Z_Ydir = 1;
  630. break;
  631. case 4: /* down */
  632. Z_Xdir = 0;
  633. Z_Ydir = 1;
  634. break;
  635. case 5: /* down & left */
  636. Z_Xdir = -1;
  637. Z_Ydir = 1;
  638. break;
  639. case 6: /* left */
  640. Z_Xdir = -1;
  641. Z_Ydir = 0;
  642. break;
  643. case 7: /* up & left */
  644. Z_Xdir = -1;
  645. Z_Ydir = -1;
  646. break;
  647. }
  648. /* Plot edges on image */
  649. fprintf (stderr, "Plotting edges...");
  650. k = 0;
  651. drawcol = (options.edgeremove) ? 64 : 32;
  652. for (i = 0; i < bsp->numedges; i++) {
  653. /* Do a check on this line ... see if we keep this line or not */
  654. /* run through all referenced faces */
  655. /* ICK ... do I want to check area of all faces? */
  656. usearea = INT_MAX;
  657. if (options.edgeremove) {
  658. if (edge_extra[i].num_face_ref > 1) {
  659. tempf = 1.0;
  660. /* dot products of all referenced faces */
  661. for (j = 0; j < edge_extra[i].num_face_ref - 1; j = j + 2) {
  662. tempf =
  663. tempf * (DOT (edge_extra[i].ref_faces_normal[j],
  664. edge_extra[i].ref_faces_normal[j + 1]));
  665. /* What is the smallest area this edge references? */
  666. if (usearea > edge_extra[i].ref_faces_area[j])
  667. usearea = edge_extra[i].ref_faces_area[j];
  668. if (usearea > edge_extra[i].ref_faces_area[j + 1])
  669. usearea = edge_extra[i].ref_faces_area[j + 1];
  670. }
  671. } else {
  672. tempf = 0.0;
  673. }
  674. } else {
  675. tempf = 0.0;
  676. }
  677. vert1 = &vertexlist[edgelist[i].v[0]];
  678. vert2 = &vertexlist[edgelist[i].v[1]];
  679. SUB (*vert1, *vert2, vect);
  680. if (abs (tempf) < options.flat_threshold
  681. && usearea > options.area_threshold
  682. && sqrt (DOT (vect, vect)) > options.linelen_threshold) {
  683. float offs0, offs1;
  684. Zoffset0 = (options.z_pad * (vert1->Z - midZ) / (maxZ - minZ));
  685. Zoffset1 = (options.z_pad * (vert2->Z - midZ) / (maxZ - minZ));
  686. offs0 = options.image_pad + options.z_pad + (Zoffset0 * Z_Xdir);
  687. offs1 = options.image_pad + options.z_pad + (Zoffset1 * Z_Xdir);
  688. bresline (image, (vert1->X - minX) / options.scaledown + offs0,
  689. (vert1->Y - minY) / options.scaledown + offs0,
  690. (vert2->X - minX) / options.scaledown + offs1,
  691. (vert2->Y - minY) / options.scaledown + offs1,
  692. drawcol);
  693. } else {
  694. k++;
  695. }
  696. }
  697. printf ("%d edges plotted", bsp->numedges);
  698. if (options.edgeremove) {
  699. printf (" (%ld edges removed)\n", k);
  700. } else {
  701. printf ("\n");
  702. }
  703. /* Little gradient */
  704. for (i = 0; i <= 255; i++) {
  705. // across from top left
  706. plotpoint (image, i, 0, 255 - i);
  707. // down from top left
  708. plotpoint (image, 0, i, 255 - i);
  709. // back from top right
  710. plotpoint (image, image->width - i - 1, 0, 255 - i);
  711. // down from top right
  712. plotpoint (image, image->width - 1, i, 255 - i);
  713. // back from bottom right
  714. plotpoint (image, image->width - i - 1, image->height - 1, 255 - i);
  715. // up from bottom right
  716. plotpoint (image, image->width - 1, image->height - i - 1, 255 - i);
  717. // across from bottom left
  718. plotpoint (image, i, image->height - 1, 255 - i);
  719. // up from bottom left
  720. plotpoint (image, 0, image->height - i - 1, 255 - i);
  721. }
  722. /* Negate image if necessary */
  723. if (options.negative_image) {
  724. for (i = 0; i < image->height; i++) {
  725. for (j = 0; j < image->width; j++) {
  726. image->image[i * image->width + j] =
  727. 255 - image->image[i * image->width + j];
  728. }
  729. }
  730. }
  731. if (options.edgeremove) {
  732. free (edge_extra);
  733. }
  734. return image;
  735. }
  736. int
  737. main (int argc, char *argv[])
  738. {
  739. QFile *bspfile;
  740. QFile *outfile;
  741. bsp_t *bsp;
  742. image_t *image;
  743. /* Enough args? */
  744. if (argc < 3) {
  745. show_help ();
  746. return 1;
  747. }
  748. /* Setup options */
  749. def_options (&options);
  750. get_options (&options, argc, argv);
  751. show_options (&options);
  752. bspfile = Qopen (options.bspf_name, "rbz");
  753. if (bspfile == NULL) {
  754. fprintf (stderr, "Error opening bsp file %s.\n", options.bspf_name);
  755. return 1;
  756. }
  757. bsp = LoadBSPFile (bspfile, Qfilesize (bspfile));
  758. Qclose (bspfile);
  759. image = render_map (bsp);
  760. /* Write image */
  761. outfile = Qopen (options.outf_name, "wb");
  762. if (outfile == NULL) {
  763. fprintf (stderr, "Error opening output file %s.\n", options.outf_name);
  764. return 1;
  765. } else {
  766. pcx_t *pcx;
  767. int pcx_len, i;
  768. byte palette[768];
  769. // quick and dirty greyscale palette
  770. for (i = 0; i < 256; i++) {
  771. palette[i * 3 + 0] = i;
  772. palette[i * 3 + 1] = i;
  773. palette[i * 3 + 2] = i;
  774. }
  775. Cvar_Init_Hash ();
  776. Cmd_Init_Hash ();
  777. Cvar_Init ();
  778. Sys_Init_Cvars ();
  779. Cmd_Init ();
  780. Memory_Init (malloc (MEMSIZE), MEMSIZE);
  781. pcx = EncodePCX (image->image, image->width, image->height,
  782. image->width, palette, false, &pcx_len);
  783. if (Qwrite (outfile, pcx, pcx_len) != pcx_len) {
  784. fprintf (stderr, "Error writing to %s\n", options.outf_name);
  785. return 1;
  786. }
  787. }
  788. printf ("File written to %s.\n", options.outf_name);
  789. Qclose (outfile);
  790. /* Close, done! */
  791. free (image->image);
  792. free (image);
  793. return 0;
  794. }