quakeforge/tools/bsp2img/bsp2img.c

991 lines
23 KiB
C
Raw Normal View History

/*
bsp2bp - converts Quake I BSP's to a bitmap (map!) of the level
Copyright (C) 1999 Matthew Wong <mkyw@iname.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2007-04-06 01:09:58 +00:00
#ifdef HAVE_CONFIG_H
2004-02-05 23:46:21 +00:00
#include "config.h"
2007-04-06 01:09:58 +00:00
#endif
#include <stdio.h>
#include <stdlib.h>
2007-04-06 01:09:58 +00:00
#ifdef HAVE_UNISTD_H
#include <unistd.h>
2007-04-06 01:09:58 +00:00
#endif
#include <errno.h>
#include <string.h>
#include <math.h>
2002-11-19 05:02:57 +00:00
#include <limits.h>
2002-11-18 22:46:53 +00:00
#include "QF/bspfile.h"
#include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/mathlib.h"
#include "QF/pcx.h"
2004-05-01 04:14:59 +00:00
#include "QF/png.h"
#include "QF/quakefs.h"
#include "QF/quakeio.h"
#include "QF/sys.h"
#include "QF/zone.h"
2004-02-05 23:46:21 +00:00
#include "compat.h"
#define PROGNAME "bsp2img"
#define V_MAJOR 0
#define V_MINOR 0
#define V_REV 12
#define Z_PAD_HACK 16
#define MAX_REF_FACES 4
#define MEMSIZE (12 * 1024 * 1024)
2002-11-18 22:46:53 +00:00
#define X point[0]
#define Y point[1]
#define Z point[2]
2004-02-05 23:46:21 +00:00
#define SUB(a,b,c) ((c).X = (a).X - (b).X, \
(c).Y = (a).Y - (b).Y, \
(c).Z = (a).Z - (b).Z)
#define DOT(a,b) ((a).X * (b).X + (a).Y * (b).Y + (a).Z * (b).Z)
#define CROSS(a,b,c) ((c).X = (a).Y * (b).Z - (a).Z * (b).Y, \
(c).Y = (a).Z * (b).X - (a).X * (b).Z, \
(c).Z = (a).X * (b).Y - (a).Y * (b).X)
/*
Thanks fly to Id for a hackable game! :)
*/
/* MW */
2002-11-18 22:14:31 +00:00
typedef struct edge_extra_t {
2021-08-02 05:02:41 +00:00
uint32_t num_face_ref;
uint32_t ref_faces[MAX_REF_FACES]; // which faces are referenced
2002-11-18 22:46:53 +00:00
dvertex_t ref_faces_normal[MAX_REF_FACES]; // normal of referenced
2002-11-18 22:14:31 +00:00
// faces
int ref_faces_area[MAX_REF_FACES]; // area of the referenced faces
2002-11-18 22:14:31 +00:00
} edge_extra_t;
typedef unsigned char eightbit;
2002-11-18 22:14:31 +00:00
typedef struct options_t {
char *bspf_name;
char *outf_name;
2004-05-01 04:14:59 +00:00
int outf_type;
2002-11-18 22:14:31 +00:00
float scaledown;
float z_pad;
2002-11-18 22:14:31 +00:00
float image_pad;
2002-11-18 22:14:31 +00:00
int z_direction;
int camera_axis; // 1 - X, 2 - Y, 3 - Z, negatives will
// come from negative side of axis
2002-11-18 22:14:31 +00:00
int edgeremove;
float flat_threshold;
int area_threshold;
int linelen_threshold;
2002-11-18 22:14:31 +00:00
int negative_image;
2002-11-18 22:14:31 +00:00
int write_raw;
int write_nocomp;
} options_t;
typedef struct {
2002-11-18 22:14:31 +00:00
eightbit *image;
long width;
long height;
} image_t;
2004-02-05 23:46:21 +00:00
struct options_t options;
static void
show_help (void)
2002-11-18 22:14:31 +00:00
{
printf ("BSP->bitmap, version %d.%d.%d\n\n", V_MAJOR, V_MINOR, V_REV);
printf ("Usage:\n");
printf (" %s [options] <bspfile> <outfile>\n\n", PROGNAME);
printf ("Options:\n");
printf (" -s<scaledown> default: 4, ie 1/4 scale\n");
printf (" -z<z_scaling> default: 0 for flat map, >0 for iso 3d, -1 for auto\n");
printf (" -p<padding> default: 16-pixel border around final image\n");
printf (" -d<direction> iso 3d direction: 7 0 1\n");
printf (" \\ | /\n");
printf (" 6--+--2\n");
printf (" / | \\\n");
printf (" 5 4 3\n");
printf (" default: 7\n");
printf (" -c<camera_axis> default: +Z (+/- X/Y/Z axis)\n");
printf (" -t<flatness> threshold of dot product for edge removal;\n");
printf (" default is 0.90\n");
printf (" -e disable extraneous edges removal\n");
printf (" -a<area> minimum area for a polygon to be drawn\n");
printf (" default is 0\n");
printf (" -l<length> minimum length for an edge to be drawn\n");
printf (" default is 0\n");
printf (" -n negative image (black on white\n");
printf (" -r write raw data, rather than bmp file\n");
return;
}
static void
2004-02-05 23:46:21 +00:00
plotpoint (image_t *image, long xco, long yco, unsigned int color)
2002-11-18 22:14:31 +00:00
{
unsigned int bigcol = 0;
2010-08-23 00:50:42 +00:00
if (image->width <= 0 || image->height <= 0
|| xco < 0 || xco >= image->width
|| yco < 0 || yco >= image->height)
2002-11-18 22:14:31 +00:00
return;
bigcol = (unsigned int) image->image[yco * image->width + xco];
2004-02-05 23:46:21 +00:00
bigcol += color;
2002-11-18 22:14:31 +00:00
bigcol = min (bigcol, 255);
2002-11-18 22:14:31 +00:00
image->image[yco * image->width + xco] = (eightbit) bigcol;
return;
}
static void
2002-11-18 22:14:31 +00:00
bresline (image_t * image, long x1, long y1, long x2, long y2,
unsigned int color)
{
long x = 0, y = 0;
long deltax = 0, deltay = 0;
long xchange = 0, ychange = 0;
long error, length, i;
x = x1;
y = y1;
deltax = x2 - x1;
deltay = y2 - y1;
if (deltax < 0) {
xchange = -1;
deltax = -deltax;
} else {
xchange = 1;
}
if (deltay < 0) {
ychange = -1;
deltay = -deltay;
} else {
ychange = 1;
}
/* Main seq */
error = 0;
i = 0;
if (deltax < deltay) {
length = deltay + 1;
while (i < length) {
y = y + ychange;
error = error + deltax;
if (error > deltay) {
x = x + xchange;
error = error - deltay;
}
i++;
plotpoint (image, x, y, color);
}
} else {
length = deltax + 1;
while (i < length) {
x = x + xchange;
error = error + deltay;
if (error > deltax) {
y = y + ychange;
error = error - deltax;
}
i++;
plotpoint (image, x, y, color);
}
}
}
static void
2002-11-18 22:14:31 +00:00
def_options (struct options_t *opt)
{
2002-11-18 22:14:31 +00:00
static struct options_t locopt;
locopt.bspf_name = NULL;
locopt.outf_name = NULL;
2004-05-01 04:14:59 +00:00
locopt.outf_type = 0;
2002-11-18 22:14:31 +00:00
locopt.scaledown = 4.0;
locopt.image_pad = 16.0;
locopt.z_pad = 0.0;
locopt.z_direction = 1;
locopt.camera_axis = 3; // default is from +Z
locopt.edgeremove = 1;
locopt.flat_threshold = 0.90;
locopt.area_threshold = 0;
locopt.linelen_threshold = 0;
locopt.negative_image = 0;
locopt.write_raw = 0;
locopt.write_nocomp = 1;
memcpy (opt, &locopt, sizeof (struct options_t));
return;
}
static void
2002-11-18 22:14:31 +00:00
get_options (struct options_t *opt, int argc, char *argv[])
{
static struct options_t locopt;
int i = 0;
char *arg;
long lnum = 0;
float fnum = 0.0;
char pm = '+', axis = 'Z';
/* Copy curr options */
memcpy (&locopt, opt, sizeof (struct options_t));
/* Go through command line */
for (i = 1; i < argc; i++) {
arg = argv[i];
if (arg[0] == '-') {
/* Okay, dash-something */
switch (arg[1]) {
case 's':
if (sscanf (&arg[2], "%ld", &lnum) == 1)
if (lnum > 0)
locopt.scaledown = (float) lnum;
break;
case 'z':
if (sscanf (&arg[2], "%ld", &lnum) == 1)
if (lnum >= -1)
locopt.z_pad = (float) lnum;
break;
case 'p':
if (sscanf (&arg[2], "%ld", &lnum) == 1)
if (lnum >= 0)
locopt.image_pad = (float) lnum;
break;
case 'd':
if (sscanf (&arg[2], "%ld", &lnum) == 1)
if (lnum >= 0 && lnum <= 7)
locopt.z_direction = (int) lnum;
break;
case 'c':
if (strlen (&arg[2]) == 2) {
pm = arg[2];
axis = arg[3];
printf ("-c%c%c\n", pm, axis);
switch (axis) {
case 'x':
case 'X':
locopt.camera_axis = 1;
break;
case 'y':
case 'Y':
locopt.camera_axis = 2;
break;
case 'z':
case 'Z':
locopt.camera_axis = 3;
break;
default:
printf ("Must specify a valid axis.\n");
show_help ();
exit (1);
break;
}
switch (pm) {
case '+':
break;
case '-':
locopt.camera_axis = -locopt.camera_axis;
break;
default:
printf ("Must specify +/-\n");
show_help ();
exit (1);
break;
}
} else {
printf ("Unknown option: -%s\n", &arg[1]);
show_help ();
exit (1);
}
break;
case 't':
if (sscanf (&arg[2], "%f", &fnum) == 1)
if (fnum >= 0.0 && fnum <= 1.0)
locopt.flat_threshold = (float) fnum;
break;
case 'e':
locopt.edgeremove = 0;
break;
case 'n':
locopt.negative_image = 1;
break;
case 'a':
if (sscanf (&arg[2], "%ld", &lnum) == 1)
if (lnum >= 0)
locopt.area_threshold = (int) lnum;
break;
case 'l':
if (sscanf (&arg[2], "%ld", &lnum) == 1)
if (lnum >= 0)
locopt.linelen_threshold = (int) lnum;
break;
case 'r':
locopt.write_raw = 1;
break;
case 'u':
locopt.write_nocomp = 1;
break;
default:
printf ("Unknown option: -%s\n", &arg[1]);
show_help ();
exit (1);
break;
} /* switch */
} else {
if (locopt.bspf_name == NULL) {
locopt.bspf_name = arg;
} else if (locopt.outf_name == NULL) {
2004-05-01 04:14:59 +00:00
const char *ext;
2002-11-18 22:14:31 +00:00
locopt.outf_name = arg;
2004-05-01 04:14:59 +00:00
ext = QFS_FileExtension (locopt.outf_name);
if (strcmp (ext, ".pcx") == 0)
locopt.outf_type = 0;
else if (strcmp (ext, ".png") == 0)
locopt.outf_type = 1;
else {
printf ("Unknown output format: %s\n", ext);
exit (1);
}
2002-11-18 22:14:31 +00:00
} else {
printf ("Unknown option: %s\n", arg);
show_help ();
exit (1);
}
} /* if */
} /* for */
memcpy (opt, &locopt, sizeof (struct options_t));
return;
}
static void
2002-11-18 22:14:31 +00:00
show_options (struct options_t *opt)
{
char dirstr[80];
printf ("Options:\n");
printf (" Scale down by: %.0f\n", opt->scaledown);
printf (" Z scale: %.0f\n", opt->z_pad);
printf (" Border: %.0f\n", opt->image_pad);
/* Zoffset calculations */
switch (opt->z_direction) {
case 0:
sprintf (dirstr, "up");
break;
case 1:
sprintf (dirstr, "up & right");
break;
case 2:
sprintf (dirstr, "right");
break;
case 3:
sprintf (dirstr, "down & right");
break;
case 4:
sprintf (dirstr, "down");
break;
case 5:
sprintf (dirstr, "down & left");
break;
case 6:
sprintf (dirstr, "left");
break;
case 7:
sprintf (dirstr, "up & left");
break;
default:
sprintf (dirstr, "unknown!");
break;
}
printf (" Z direction: %d [%s]\n", opt->z_direction, dirstr);
if (opt->z_pad == 0) {
printf (" Warning: direction option has no effect with Z scale set to 0.\n");
}
/* Camera axis */
switch (opt->camera_axis) {
case 1:
sprintf (dirstr, "+X");
break;
case -1:
sprintf (dirstr, "-X");
break;
case 2:
sprintf (dirstr, "+Y");
break;
case -2:
sprintf (dirstr, "-Y");
break;
case 3:
sprintf (dirstr, "+Z");
break;
case -3:
sprintf (dirstr, "-Z");
break;
default:
sprintf (dirstr, "unknown!");
break;
}
printf (" Camera axis: %s\n", dirstr);
printf (" Remove extraneous edges: %s\n",
(opt->edgeremove == 1) ? "yes" : "no");
printf (" Edge removal dot product theshold: %f\n", opt->flat_threshold);
printf (" Minimum polygon area threshold (approximate): %d\n",
opt->area_threshold);
printf (" Minimum line length threshold: %d\n", opt->linelen_threshold);
printf (" Creating %s image.\n",
(opt->negative_image == 1) ? "negative" : "positive");
printf ("\n");
printf (" Input (bsp) file: %s\n", opt->bspf_name);
if (opt->write_raw)
printf (" Output (raw) file: %s\n\n", opt->outf_name);
else
printf (" Output (%s bmp) file: %s\n\n",
opt->write_nocomp ? "uncompressed" : "RLE compressed",
opt->outf_name);
return;
}
2004-02-05 23:46:21 +00:00
static image_t *
create_image (long width, long height)
{
long size;
image_t *image;
image = malloc (sizeof (image_t));
image->width = width;
image->height = height;
size = sizeof (eightbit) * width * height;
if (!(image->image = malloc (size))) {
fprintf (stderr, "Error allocating image buffer %ldx%ld.\n",
width, height);
exit (2);
}
printf ("Allocated buffer %ldx%ld for image.\n", width, height);
memset (image->image, 0, size);
return image;
}
static image_t *
render_map (bsp_t *bsp)
2002-11-18 22:14:31 +00:00
{
2021-08-02 05:02:41 +00:00
uint32_t j = 0, k = 0, x = 0;
2002-11-18 22:14:31 +00:00
2004-02-05 23:46:21 +00:00
dvertex_t *vertexlist, *vert1, *vert2;
2002-11-18 22:46:53 +00:00
dedge_t *edgelist;
dface_t *facelist;
int *ledges;
2002-11-18 22:14:31 +00:00
/* edge removal stuff */
struct edge_extra_t *edge_extra = NULL;
2002-11-18 22:46:53 +00:00
dvertex_t v0, v1, vect;
2002-11-18 22:14:31 +00:00
int area = 0, usearea;
float minX = 0.0, maxX = 0.0, minY = 0.0, maxY = 0.0, minZ = 0.0;
float maxZ = 0.0, midZ = 0.0, tempf = 0.0;
long Zoffset0 = 0, Zoffset1 = 0;
long Z_Xdir = 1, Z_Ydir = -1;
image_t *image;
int drawcol;
2002-11-18 22:46:53 +00:00
vertexlist = bsp->vertexes;
edgelist = bsp->edges;
facelist = bsp->faces;
ledges = bsp->surfedges;
2002-11-18 22:14:31 +00:00
/* Precalc stuff if we're removing edges - - - - - - - - - - - */
if (options.edgeremove) {
printf ("Precalc edge removal stuff...\n");
2002-11-18 22:46:53 +00:00
edge_extra = malloc (sizeof (struct edge_extra_t) * bsp->numedges);
2002-11-18 22:14:31 +00:00
if (edge_extra == NULL) {
fprintf (stderr, "Error allocating %ld bytes for extra edge info.",
2021-08-02 05:02:41 +00:00
(long) (sizeof (struct edge_extra_t) * bsp->numedges));
2004-02-05 23:46:21 +00:00
exit (2);
2002-11-18 22:14:31 +00:00
}
/* initialize the array */
for (unsigned i = 0; i < bsp->numedges; i++) {
2002-11-18 22:14:31 +00:00
edge_extra[i].num_face_ref = 0;
for (int j = 0; j < MAX_REF_FACES; j++) {
2002-11-18 22:14:31 +00:00
edge_extra[i].ref_faces[j] = -1;
}
}
for (unsigned i = 0; i < bsp->numfaces; i++) {
2002-11-18 22:14:31 +00:00
/* calculate the normal (cross product) */
2002-11-18 22:46:53 +00:00
/* starting edge: edgelist[ledges[facelist[i].firstedge]] */
/* number of edges: facelist[i].numedges; */
2002-11-18 22:14:31 +00:00
/* quick hack - just take the first 2 edges */
2002-11-18 22:46:53 +00:00
j = facelist[i].firstedge;
2002-11-18 22:14:31 +00:00
k = j;
vect.X = 0.0;
vect.Y = 0.0;
vect.Z = 0.0;
while (vect.X == 0.0 && vect.Y == 0.0 && vect.Z == 0.0
2002-11-18 22:46:53 +00:00
&& k < (facelist[i].numedges + j)) {
2004-02-05 23:46:21 +00:00
dedge_t *e1, *e2;
2002-11-18 23:22:52 +00:00
/* If the first 2 are parallel edges, go with the next one */
2002-11-18 22:14:31 +00:00
k++;
2004-02-05 23:46:21 +00:00
e1 = &edgelist[abs (ledges[j])];
e2 = &edgelist[abs (ledges[k])];
//FIXME verify directions
2002-11-18 22:14:31 +00:00
if (ledges[j] > 0) {
SUB (vertexlist[e1->v[0]], vertexlist[e1->v[1]], v0);
SUB (vertexlist[e2->v[0]], vertexlist[e2->v[1]], v1);
2002-11-18 22:14:31 +00:00
} else {
/* negative index, therefore walk in reverse order */
SUB (vertexlist[e1->v[1]], vertexlist[e1->v[0]], v0);
SUB (vertexlist[e2->v[1]], vertexlist[e2->v[0]], v1);
2002-11-18 22:14:31 +00:00
}
/* cross product */
2004-02-05 23:46:21 +00:00
CROSS (v0, v1, vect);
2002-11-18 22:14:31 +00:00
/* Okay, it's not the REAL area, but i'm lazy, and since a lot
of mapmakers use rectangles anyways... */
2004-02-05 23:46:21 +00:00
area = sqrt (DOT (v0, v0)) * sqrt (DOT (v1, v1));
2002-11-18 22:14:31 +00:00
} /* while */
/* reduce cross product to a unit vector */
2004-02-05 23:46:21 +00:00
tempf = sqrt (DOT (vect, vect));
2002-11-18 22:14:31 +00:00
if (tempf > 0.0) {
vect.X = vect.X / tempf;
vect.Y = vect.Y / tempf;
vect.Z = vect.Z / tempf;
2004-02-05 23:46:21 +00:00
} else {
2002-11-18 22:14:31 +00:00
vect.X = 0.0;
vect.Y = 0.0;
vect.Z = 0.0;
}
/* Now go put ref in all edges... */
2002-11-18 22:46:53 +00:00
for (j = 0; j < facelist[i].numedges; j++) {
2004-02-05 23:46:21 +00:00
edge_extra_t *e;
2002-11-18 22:46:53 +00:00
k = j + facelist[i].firstedge;
2004-02-05 23:46:21 +00:00
e = &edge_extra[abs (ledges[k])];
x = e->num_face_ref;
if (e->num_face_ref < MAX_REF_FACES) {
2002-11-18 22:14:31 +00:00
x++;
2004-02-05 23:46:21 +00:00
e->num_face_ref = x;
e->ref_faces[x - 1] = i;
e->ref_faces_normal[x - 1].X = vect.X;
e->ref_faces_normal[x - 1].Y = vect.Y;
e->ref_faces_normal[x - 1].Z = vect.Z;
e->ref_faces_area[x - 1] = area;
2002-11-18 22:14:31 +00:00
}
} /* for */
}
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
printf ("Collecting min/max\n");
/* Collect min and max */
for (unsigned i = 0; i < bsp->numvertexes; i++) {
2002-11-18 22:14:31 +00:00
/* Ugly hack - flip stuff around for different camera angles */
switch (options.camera_axis) {
case -1: /* -X -- (-y <--> +y, +x into screen,
-x out of screen; -z down, +z up) */
tempf = vertexlist[i].X;
vertexlist[i].X = vertexlist[i].Y;
vertexlist[i].Y = vertexlist[i].Z;
vertexlist[i].Z = -tempf;
break;
case 1: /* +X -- (+y <--> -y; -x into screen,
+x out of screen; -z down, +z up) */
tempf = vertexlist[i].X;
vertexlist[i].X = -vertexlist[i].Y;
vertexlist[i].Y = vertexlist[i].Z;
vertexlist[i].Z = tempf;
break;
case -2: /* -Y -- (+x <--> -x; -y out of screen,
2002-11-18 22:14:31 +00:00
+z up) */
vertexlist[i].X = -vertexlist[i].X;
tempf = vertexlist[i].Z;
vertexlist[i].Z = vertexlist[i].Y;
2021-01-09 11:42:23 +00:00
vertexlist[i].Y = tempf;
2002-11-18 22:14:31 +00:00
break;
case 2: /* +Y -- (-x <--> +x; +y out of screen,
2002-11-18 22:14:31 +00:00
+z up) */
tempf = vertexlist[i].Z;
vertexlist[i].Z = -vertexlist[i].Y;
2021-01-09 11:42:23 +00:00
vertexlist[i].Y = tempf;
2002-11-18 22:14:31 +00:00
break;
case -3: /* -Z -- negate X and Z (ie. 180 rotate
2002-11-18 22:14:31 +00:00
along Y axis) */
vertexlist[i].X = -vertexlist[i].X;
vertexlist[i].Z = -vertexlist[i].Z;
break;
case 3: /* +Z -- do nothing! */
default: /* do nothing! */
break;
}
/* flip Y for proper screen cords */
vertexlist[i].Y = -vertexlist[i].Y;
/* max and min */
if (i == 0) {
minX = vertexlist[i].X;
maxX = vertexlist[i].X;
minY = vertexlist[i].Y;
maxY = vertexlist[i].Y;
minZ = vertexlist[i].Z;
maxZ = vertexlist[i].Z;
} else {
2004-02-05 23:46:21 +00:00
minX = min (vertexlist[i].X, minX);
maxX = max (vertexlist[i].X, maxX);
minY = min (vertexlist[i].Y, minY);
maxY = max (vertexlist[i].Y, maxY);
minZ = min (vertexlist[i].Z, minZ);
maxZ = max (vertexlist[i].Z, maxZ);
2002-11-18 22:14:31 +00:00
}
}
if (options.z_pad == -1)
options.z_pad = (long) (maxZ - minZ) / (options.scaledown * Z_PAD_HACK);
midZ = (maxZ + minZ) / 2.0;
printf ("\n");
printf ("Bounds: X [%8.4f .. %8.4f] %8.4f\n", minX, maxX, (maxX - minX));
printf (" Y [%8.4f .. %8.4f] %8.4f\n", minY, maxY, (maxY - minY));
printf (" Z [%8.4f .. %8.4f] %8.4f - mid: %8.4f\n", minZ, maxZ,
(maxZ - minZ), midZ);
/* image array */
2004-02-05 23:46:21 +00:00
{
long width, height;
2010-08-23 00:50:42 +00:00
width = (maxX - minX + 1) / options.scaledown;
2004-02-05 23:46:21 +00:00
width += (options.image_pad + options.z_pad) * 2;
2010-08-23 00:50:42 +00:00
height = (maxY - minY + 1) / options.scaledown;
2004-02-05 23:46:21 +00:00
height += (options.image_pad + options.z_pad) * 2;
image = create_image (width, height);
2002-11-18 22:14:31 +00:00
}
/* Zoffset calculations */
switch (options.z_direction) {
case 0:
Z_Xdir = 0; /* up */
Z_Ydir = 1;
break;
2004-02-05 23:46:21 +00:00
default: /* unknown */
case 1: /* up & right */
Z_Xdir = 1;
2002-11-18 22:14:31 +00:00
Z_Ydir = -1;
break;
2004-02-05 23:46:21 +00:00
case 2: /* right */
Z_Xdir = 1;
2002-11-18 22:14:31 +00:00
Z_Ydir = 0;
break;
2004-02-05 23:46:21 +00:00
case 3: /* down & right */
Z_Xdir = 1;
2002-11-18 22:14:31 +00:00
Z_Ydir = 1;
break;
2004-02-05 23:46:21 +00:00
case 4: /* down */
Z_Xdir = 0;
2002-11-18 22:14:31 +00:00
Z_Ydir = 1;
break;
2004-02-05 23:46:21 +00:00
case 5: /* down & left */
Z_Xdir = -1;
2002-11-18 22:14:31 +00:00
Z_Ydir = 1;
break;
2004-02-05 23:46:21 +00:00
case 6: /* left */
Z_Xdir = -1;
2002-11-18 22:14:31 +00:00
Z_Ydir = 0;
break;
2004-02-05 23:46:21 +00:00
case 7: /* up & left */
Z_Xdir = -1;
2002-11-18 22:14:31 +00:00
Z_Ydir = -1;
break;
}
/* Plot edges on image */
fprintf (stderr, "Plotting edges...");
k = 0;
drawcol = (options.edgeremove) ? 64 : 32;
for (unsigned i = 0; i < bsp->numedges; i++) {
2002-11-18 22:14:31 +00:00
/* Do a check on this line ... see if we keep this line or not */
/* run through all referenced faces */
/* ICK ... do I want to check area of all faces? */
usearea = INT_MAX;
2002-11-18 22:14:31 +00:00
if (options.edgeremove) {
if (edge_extra[i].num_face_ref > 1) {
tempf = 1.0;
/* dot products of all referenced faces */
for (j = 0; j < edge_extra[i].num_face_ref - 1; j = j + 2) {
tempf =
2004-02-05 23:46:21 +00:00
tempf * (DOT (edge_extra[i].ref_faces_normal[j],
edge_extra[i].ref_faces_normal[j + 1]));
2002-11-18 22:14:31 +00:00
/* What is the smallest area this edge references? */
if (usearea > edge_extra[i].ref_faces_area[j])
usearea = edge_extra[i].ref_faces_area[j];
if (usearea > edge_extra[i].ref_faces_area[j + 1])
usearea = edge_extra[i].ref_faces_area[j + 1];
}
} else {
tempf = 0.0;
}
} else {
tempf = 0.0;
}
2004-02-05 23:46:21 +00:00
vert1 = &vertexlist[edgelist[i].v[0]];
vert2 = &vertexlist[edgelist[i].v[1]];
SUB (*vert1, *vert2, vect);
if (fabsf (tempf) < options.flat_threshold
2004-02-05 23:46:21 +00:00
&& usearea > options.area_threshold
&& sqrt (DOT (vect, vect)) > options.linelen_threshold) {
float offs0, offs1;
Zoffset0 = (options.z_pad * (vert1->Z - midZ) / (maxZ - minZ));
Zoffset1 = (options.z_pad * (vert2->Z - midZ) / (maxZ - minZ));
offs0 = options.image_pad + options.z_pad + (Zoffset0 * Z_Xdir);
offs1 = options.image_pad + options.z_pad + (Zoffset1 * Z_Ydir);
2004-02-05 23:46:21 +00:00
bresline (image, (vert1->X - minX) / options.scaledown + offs0,
(vert1->Y - minY) / options.scaledown + offs0,
(vert2->X - minX) / options.scaledown + offs1,
(vert2->Y - minY) / options.scaledown + offs1,
2002-11-18 22:14:31 +00:00
drawcol);
} else {
k++;
}
}
printf ("%zd edges plotted", bsp->numedges);
2002-11-18 22:14:31 +00:00
if (options.edgeremove) {
2021-08-02 05:02:41 +00:00
printf (" (%u edges removed)\n", k);
2002-11-18 22:14:31 +00:00
} else {
printf ("\n");
}
/* Little gradient */
for (unsigned i = 0; i <= 255; i++) {
2002-11-18 22:14:31 +00:00
// across from top left
plotpoint (image, i, 0, 255 - i);
// down from top left
plotpoint (image, 0, i, 255 - i);
// back from top right
plotpoint (image, image->width - i - 1, 0, 255 - i);
// down from top right
plotpoint (image, image->width - 1, i, 255 - i);
// back from bottom right
plotpoint (image, image->width - i - 1, image->height - 1, 255 - i);
// up from bottom right
plotpoint (image, image->width - 1, image->height - i - 1, 255 - i);
// across from bottom left
plotpoint (image, i, image->height - 1, 255 - i);
// up from bottom left
plotpoint (image, 0, image->height - i - 1, 255 - i);
}
/* Negate image if necessary */
if (options.negative_image) {
for (int i = 0; i < image->height; i++) {
for (int j = 0; j < image->width; j++) {
2002-11-18 22:14:31 +00:00
image->image[i * image->width + j] =
255 - image->image[i * image->width + j];
}
}
}
2004-02-05 23:46:21 +00:00
if (options.edgeremove) {
free (edge_extra);
}
return image;
}
2004-05-01 04:14:59 +00:00
static void
write_png (image_t *image)
{
byte *data, *in, *out, b;
int size = image->width * image->height;
out = data = malloc (size * 3);
for (in = image->image; in - image->image < size; in++) {
b = *in;
*out++ = b;
*out++ = b;
*out++ = b;
}
QFile *file = Qopen (options.outf_name, "wb");
if (file) {
WritePNG (file, data, image->width, image->height);
Qclose (file);
}
2004-05-01 04:14:59 +00:00
}
static void
write_pcx (image_t *image)
{
pcx_t *pcx;
int pcx_len, i;
byte palette[768];
QFile *outfile;
outfile = Qopen (options.outf_name, "wb");
if (outfile == NULL) {
fprintf (stderr, "Error opening output file %s.\n", options.outf_name);
exit (1);
}
// quick and dirty greyscale palette
for (i = 0; i < 256; i++) {
palette[i * 3 + 0] = i;
palette[i * 3 + 1] = i;
palette[i * 3 + 2] = i;
}
Sys_Init ();
2004-05-01 04:14:59 +00:00
Memory_Init (Sys_Alloc (MEMSIZE), MEMSIZE);
2004-05-01 04:14:59 +00:00
pcx = EncodePCX (image->image, image->width, image->height,
image->width, palette, false, &pcx_len);
if (Qwrite (outfile, pcx, pcx_len) != pcx_len) {
fprintf (stderr, "Error writing to %s\n", options.outf_name);
exit (1);
}
Qclose (outfile);
}
2004-02-05 23:46:21 +00:00
int
main (int argc, char *argv[])
{
QFile *bspfile;
bsp_t *bsp;
image_t *image;
/* Enough args? */
if (argc < 3) {
show_help ();
return 1;
}
/* Setup options */
def_options (&options);
get_options (&options, argc, argv);
show_options (&options);
bspfile = Qopen (options.bspf_name, "rbz");
if (bspfile == NULL) {
fprintf (stderr, "Error opening bsp file %s.\n", options.bspf_name);
return 1;
}
bsp = LoadBSPFile (bspfile, Qfilesize (bspfile));
Qclose (bspfile);
image = render_map (bsp);
2010-08-23 00:50:42 +00:00
BSP_Free (bsp);
2002-11-18 22:14:31 +00:00
/* Write image */
2004-05-01 04:14:59 +00:00
switch (options.outf_type) {
case 0:
write_pcx (image);
break;
case 1:
write_png (image);
break;
}
2002-11-18 22:14:31 +00:00
printf ("File written to %s.\n", options.outf_name);
/* Close, done! */
2002-11-18 23:22:52 +00:00
2002-11-18 22:14:31 +00:00
free (image->image);
free (image);
return 0;
}