PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/quakeforge/trunk/tools/bsp2img/bsp2img.c

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