quakeforge/tools/qfbsp/source/writebsp.c

527 lines
11 KiB
C
Raw Normal View History

2002-09-19 18:51:19 +00:00
/*
Copyright (C) 1996-1997 Id Software, Inc.
2002-09-19 18:51:19 +00:00
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.
2002-09-19 18:51:19 +00:00
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.
2002-09-19 18:51:19 +00:00
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
2002-09-19 18:51:19 +00:00
See file, 'COPYING', for details.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
2003-08-11 17:11:44 +00:00
#include <errno.h>
#include "QF/dstring.h"
#include "QF/qendian.h"
#include "QF/sys.h"
2002-09-20 21:48:34 +00:00
#include "QF/va.h"
#include "QF/wad.h"
#include "tools/qfbsp/include/brush.h"
#include "tools/qfbsp/include/bsp5.h"
#include "tools/qfbsp/include/options.h"
#include "tools/qfbsp/include/writebsp.h"
/** \addtogroup qfbsp_writebsp
*/
//@{
2002-09-19 17:14:23 +00:00
int headclipnode;
int firstface;
2002-09-19 17:14:23 +00:00
int
2010-09-02 04:01:45 +00:00
FindFinalPlane (const dplane_t *p)
{
2010-09-02 04:01:45 +00:00
const dplane_t *dplane;
2002-09-19 18:51:19 +00:00
int i;
2002-09-19 17:14:23 +00:00
for (i = 0, dplane = bsp->planes; i < bsp->numplanes; i++, dplane++) {
if (p->type != dplane->type)
continue;
if (p->dist != dplane->dist)
continue;
2010-08-31 01:56:15 +00:00
if (!VectorCompare (p->normal, dplane->normal))
continue;
return i;
}
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// new plane
if (bsp->numplanes == MAX_MAP_PLANES)
Sys_Error ("numplanes == MAX_MAP_PLANES");
BSP_AddPlane (bsp, p);
2002-09-19 17:14:23 +00:00
return bsp->numplanes - 1;
}
2002-09-19 17:14:23 +00:00
int planemapping[MAX_MAP_PLANES];
2010-09-01 03:06:37 +00:00
/** Recursively write the nodes' planes to the bsp file.
If the plane has already been written, do not write it again. Set the
node's output plane number.
\param node The current node of which to write the plane.
*/
static void
2002-09-19 18:51:19 +00:00
WriteNodePlanes_r (node_t *node)
{
dplane_t dplane;
2002-09-19 18:51:19 +00:00
plane_t *plane;
if (node->planenum == -1)
return;
2002-09-19 17:14:23 +00:00
if (planemapping[node->planenum] == -1) { // a new plane
planemapping[node->planenum] = bsp->numplanes;
2002-09-19 17:14:23 +00:00
plane = &planes[node->planenum];
2010-08-31 01:56:15 +00:00
VectorCopy (plane->normal, dplane.normal);
dplane.dist = plane->dist;
dplane.type = plane->type;
BSP_AddPlane (bsp, &dplane);
}
node->outputplanenum = planemapping[node->planenum];
2002-09-19 17:14:23 +00:00
WriteNodePlanes_r (node->children[0]);
WriteNodePlanes_r (node->children[1]);
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteNodePlanes (node_t *nodes)
{
2002-09-19 17:14:23 +00:00
memset (planemapping, -1, sizeof (planemapping));
WriteNodePlanes_r (nodes);
}
2010-09-01 03:06:37 +00:00
/** Recursively write clip nodes to the bsp file.
\note The node will be freed.
\param node The node to be written to the bsp file.
*/
static int
2002-09-19 18:51:19 +00:00
WriteClipNodes_r (node_t *node)
{
dclipnode_t cn;
2002-09-19 18:51:19 +00:00
int num, c, i;
2002-09-19 17:14:23 +00:00
2010-08-27 13:12:13 +00:00
// FIXME: free more stuff?
2002-09-19 17:14:23 +00:00
if (node->planenum == -1) {
num = node->contents;
free (node);
return num;
}
2002-09-23 16:27:17 +00:00
// emit a clipnode
c = bsp->numclipnodes;
2003-02-05 05:06:14 +00:00
BSP_AddClipnode (bsp, &cn);
cn.planenum = node->outputplanenum;
2002-09-19 17:14:23 +00:00
for (i = 0; i < 2; i++)
cn.children[i] = WriteClipNodes_r (node->children[i]);
2003-02-05 05:06:14 +00:00
bsp->clipnodes[c] = cn;
2002-09-19 17:14:23 +00:00
free (node);
return c;
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteClipNodes (node_t *nodes)
{
headclipnode = bsp->numclipnodes;
WriteClipNodes_r (nodes);
}
2010-09-01 03:06:37 +00:00
/** Write a leaf node to the bsp file.
\param node The leaf node to be written to the bsp file.
*/
static void
2010-09-02 04:01:45 +00:00
WriteLeaf (const node_t *node)
{
dleaf_t leaf_p;
2002-09-19 18:51:19 +00:00
face_t **fp, *f;
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// emit a leaf
leaf_p.contents = node->contents;
2002-09-23 16:27:17 +00:00
// write bounding box info
VectorCopy (node->mins, leaf_p.mins);
VectorCopy (node->maxs, leaf_p.maxs);
2002-09-19 17:14:23 +00:00
leaf_p.visofs = -1; // no vis info yet
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// write the marksurfaces
leaf_p.firstmarksurface = bsp->nummarksurfaces;
2002-09-19 17:14:23 +00:00
for (fp = node->markfaces; *fp; fp++) {
// emit a marksurface
if (bsp->nummarksurfaces == MAX_MAP_MARKSURFACES)
Sys_Error ("nummarksurfaces == MAX_MAP_MARKSURFACES");
f = *fp;
if (f->texturenum < 0)
continue;
2002-09-19 17:14:23 +00:00
do {
BSP_AddMarkSurface (bsp, f->outputnumber);
2002-09-19 17:14:23 +00:00
f = f->original; // grab tjunction split faces
} while (f);
}
2002-09-19 17:14:23 +00:00
leaf_p.nummarksurfaces = bsp->nummarksurfaces - leaf_p.firstmarksurface;
memset (leaf_p.ambient_level, 0, sizeof (leaf_p.ambient_level));
BSP_AddLeaf (bsp, &leaf_p);
}
2010-09-01 03:06:37 +00:00
/** Recursively write the draw nodes of the map bsp.
\param node The current node to be written.
*/
static void
2010-09-02 04:01:45 +00:00
WriteDrawNodes_r (const node_t *node)
{
static dnode_t dummy;
2002-09-19 17:14:23 +00:00
dnode_t *n;
int i;
int nodenum = bsp->numnodes;
2010-08-27 13:12:13 +00:00
// emit a node
if (bsp->numnodes == MAX_MAP_NODES)
Sys_Error ("numnodes == MAX_MAP_NODES");
BSP_AddNode (bsp, &dummy);
n = &bsp->nodes[nodenum];
VectorCopy (node->mins, n->mins);
VectorCopy (node->maxs, n->maxs);
n->planenum = node->outputplanenum;
n->firstface = node->firstface;
n->numfaces = node->numfaces;
2002-09-23 16:27:17 +00:00
// recursively output the other nodes
2002-09-19 17:14:23 +00:00
for (i = 0; i < 2; i++) {
n = &bsp->nodes[nodenum];
2002-09-19 17:14:23 +00:00
if (node->children[i]->planenum == -1) {
if (node->children[i]->contents == CONTENTS_SOLID)
n->children[i] = -1;
2002-09-19 17:14:23 +00:00
else {
n->children[i] = -(bsp->numleafs + 1);
WriteLeaf (node->children[i]);
}
2002-09-19 17:14:23 +00:00
} else {
n->children[i] = bsp->numnodes;
WriteDrawNodes_r (node->children[i]);
}
}
}
2002-09-19 17:14:23 +00:00
void
2010-09-02 04:01:45 +00:00
WriteDrawNodes (const node_t *headnode)
{
dmodel_t bm;
2002-09-19 18:51:19 +00:00
int start, i;
#if 0
if (headnode->contents < 0)
Sys_Error ("FinishBSPModel: empty model");
#endif
2002-09-23 16:27:17 +00:00
// emit a model
bm.headnode[0] = bsp->numnodes;
for (i = 1; i < MAX_MAP_HULLS; i++)
bm.headnode[i] = 0;
bm.firstface = firstface;
bm.numfaces = bsp->numfaces - firstface;
firstface = bsp->numfaces;
2002-09-19 17:14:23 +00:00
start = bsp->numleafs;
2002-09-19 17:14:23 +00:00
if (headnode->contents < 0)
WriteLeaf (headnode);
else
WriteDrawNodes_r (headnode);
bm.visleafs = bsp->numleafs - start;
2002-09-19 17:14:23 +00:00
for (i = 0; i < 3; i++) {
bm.mins[i] = headnode->mins[i] + SIDESPACE + 1; // remove the padding
bm.maxs[i] = headnode->maxs[i] - SIDESPACE - 1;
}
VectorZero (bm.origin);
BSP_AddModel (bsp, &bm);
2002-09-23 16:27:17 +00:00
// FIXME: are all the children decendant of padded nodes?
}
2002-09-19 17:14:23 +00:00
void
BumpModel (int hullnum)
{
static dmodel_t bm;
2002-09-23 16:27:17 +00:00
// emit a model
bm.headnode[hullnum] = headclipnode;
BSP_AddModel (bsp, &bm);
}
2002-09-19 20:58:29 +00:00
typedef struct wadlist_s {
struct wadlist_s *next;
const char *path;
wad_t *wad;
2002-09-19 20:58:29 +00:00
} wadlist_t;
wadlist_t *wadlist;
2010-09-01 03:06:37 +00:00
/** Load a texture wad file.
\param path The path to the wad file.
*/
static int
2010-09-02 04:01:45 +00:00
TEX_InitFromWad (const char *path)
{
2002-09-19 20:58:29 +00:00
wadlist_t *wl;
wad_t *wad;
2002-09-19 17:14:23 +00:00
wad = wad_open (path);
2002-09-20 21:48:34 +00:00
#ifdef HAVE_ZLIB
if (!wad)
wad = wad_open (path = va (0, "%s.gz", path));
2002-09-20 21:48:34 +00:00
#endif
if (!wad)
2002-09-20 21:48:34 +00:00
return -1;
printf ("wadfile: %s\n", path);
2002-09-19 20:58:29 +00:00
wl = calloc (1, sizeof (wadlist_t));
wl->path = strdup (path);
2002-09-19 20:58:29 +00:00
wl->wad = wad;
2002-09-19 20:58:29 +00:00
wl->next = wadlist;
wadlist = wl;
2002-09-20 21:48:34 +00:00
return 0;
}
2010-09-01 03:06:37 +00:00
/** Read a lump's data.
Searches all loaded wad files for the lump.
\param name The name of the lump for which to search.
\param dest The destination buffer to which the lump's data will be
written.
\return The size of the lump's data or 0 if the lump was not
found.
*/
static int
2010-09-02 04:01:45 +00:00
LoadLump (const char *name, dstring_t *dest)
{
int r;
int ofs = dest->size;
2002-09-19 20:58:29 +00:00
wadlist_t *wl;
lumpinfo_t *lump;
QFile *texfile;
2002-09-19 17:14:23 +00:00
2002-09-19 20:58:29 +00:00
for (wl = wadlist; wl; wl = wl->next) {
if ((lump = wad_find_lump (wl->wad, name))) {
dest->size += lump->disksize;
dstring_adjust (dest);
texfile = Qopen (wl->path, "rbz");
if (!texfile) {
printf ("couldn't open %s\n", wl->path);
continue;
}
r = Qseek (texfile, lump->filepos, SEEK_SET);
if (r == -1) {
printf ("seek error: %s:%s %d\n", wl->path,
lump->name, lump->filepos);
Qclose (texfile);
continue;
2002-09-19 20:58:29 +00:00
}
r = Qread (texfile, dest->str + ofs, lump->disksize);
Qclose (texfile);
if (r != lump->disksize) {
printf ("seek error: %s:%s %d\n", wl->path,
lump->name, lump->disksize);
continue;
}
return lump->disksize;
}
}
2002-09-19 17:14:23 +00:00
printf ("WARNING: texture %s not found\n", name);
return 0;
}
2010-09-01 03:06:37 +00:00
/** Search loaded miptex for animated textures and load their animations.
*/
static void
2002-09-19 17:14:23 +00:00
AddAnimatingTextures (void)
{
int base, i, j;
char name[32];
2002-09-19 20:58:29 +00:00
wadlist_t *wl;
base = nummiptexnames;
2002-09-19 17:14:23 +00:00
name[sizeof (name) - 1] = 0;
2002-09-19 17:14:23 +00:00
for (i = 0; i < base; i++) {
if (miptexnames[i][0] != '+')
continue;
strncpy (name, miptexnames[i], sizeof (name) - 1);
2002-09-19 17:14:23 +00:00
for (j = 0; j < 20; j++) {
if (j < 10)
2002-09-19 17:14:23 +00:00
name[1] = '0' + j;
else
2002-09-19 17:14:23 +00:00
name[1] = 'A' + j - 10; // alternate animation
2002-09-19 17:14:23 +00:00
// see if this name exists in the wadfile
2002-09-19 20:58:29 +00:00
for (wl = wadlist; wl; wl = wl->next) {
if (wad_find_lump (wl->wad, name)) {
FindMiptex (name); // add to the miptex list
break;
}
2002-09-19 20:58:29 +00:00
}
}
}
2002-09-19 17:14:23 +00:00
if (nummiptexnames - base)
printf ("added %i texture frames\n", nummiptexnames - base);
}
2010-09-01 03:06:37 +00:00
/** Write the miptex data to the bsp file.
The miptex data is read from the wad files specified by the \c wad or
\c _wad key of the world entity. The files are a semi-colon separated list
(this is a QuakeForge extension). The \c wadpath is used to search for the
files.
*/
static void
2002-09-19 17:14:23 +00:00
WriteMiptex (void)
{
dstring_t *data;
2002-09-20 21:48:34 +00:00
char *wad_list, *wad, *w;
char *path_list, *path, *p;
dstring_t *fullpath;
2002-09-19 18:51:19 +00:00
dmiptexlump_t *l;
2002-09-20 21:48:34 +00:00
int i, len, res = -1;
2004-03-21 18:53:41 +00:00
wad_list = (char *) ValueForKey (&entities[0], "_wad");
2002-09-20 21:48:34 +00:00
if (!wad_list || !wad_list[0]) {
2004-03-21 18:53:41 +00:00
wad_list = (char *) ValueForKey (&entities[0], "wad");
2002-09-20 21:48:34 +00:00
if (!wad_list || !wad_list[0]) {
printf ("WARNING: no wadfile specified\n");
bsp->texdatasize = 0;
return;
}
}
2002-09-20 21:48:34 +00:00
fullpath = dstring_new ();
wad = wad_list = strdup (wad_list);
w = strchr (wad, ';');
if (w)
*w++ = 0;
while (1) {
path = path_list = strdup (options.wadpath);
p = strchr (path, ';');
if (p)
*p++ = 0;
while (1) {
dsprintf (fullpath, "%s%s%s", path, path[0] ? "/" : "", wad);
res = TEX_InitFromWad (fullpath->str);
if (!res)
break;
path = p;
if (!path || !*path)
break;
p = strchr (path, ';');
if (p)
*p++ = 0;
}
free (path_list);
if (res == -1)
Sys_Error ("couldn't open %s[.gz]", wad);
2002-09-20 21:48:34 +00:00
wad = w;
if (!wad || !*wad)
break;
w = strchr (wad, ';');
if (w)
*w++ = 0;
}
2002-09-20 21:48:34 +00:00
free (wad_list);
dstring_delete (fullpath);
2002-09-19 17:14:23 +00:00
AddAnimatingTextures ();
data = dstring_new ();
data->size = sizeof (dmiptexlump_t);
dstring_adjust (data);
l = (dmiptexlump_t *) data->str;
l->nummiptex = nummiptexnames;
data->size = (char *) &l->dataofs[nummiptexnames] - data->str;
dstring_adjust (data);
for (i = 0; i < nummiptexnames; i++) {
l = (dmiptexlump_t *) data->str;
l->dataofs[i] = data->size;
len = LoadLump (miptexnames[i], data);
l = (dmiptexlump_t *) data->str;
if (!len)
2002-09-19 17:14:23 +00:00
l->dataofs[i] = -1; // didn't find the texture
}
BSP_AddTextures (bsp, (byte *) data->str, data->size);
}
2002-09-19 17:14:23 +00:00
void
BeginBSPFile (void)
{
static dedge_t edge;
static dleaf_t leaf;
2002-09-23 16:27:17 +00:00
// edge 0 is not used, because 0 can't be negated
BSP_AddEdge (bsp, &edge);
2002-09-23 16:27:17 +00:00
// leaf 0 is common solid with no faces
leaf.contents = CONTENTS_SOLID;
leaf.visofs = -1;
BSP_AddLeaf (bsp, &leaf);
2002-09-19 17:14:23 +00:00
firstface = 0;
}
2002-09-19 17:14:23 +00:00
void
FinishBSPFile (void)
{
QFile *f;
2002-09-19 17:14:23 +00:00
printf ("--- FinishBSPFile ---\n");
2002-09-19 17:14:23 +00:00
WriteMiptex ();
2002-09-19 18:51:19 +00:00
// XXX PrintBSPFileSizes ();
printf ("WriteBSPFile: %s\n", options.bspfile);
2002-09-20 21:48:34 +00:00
f = Qopen (options.bspfile, "wb");
2003-08-11 17:11:44 +00:00
if (!f)
Sys_Error ("couldn't open %s. %s", options.bspfile, strerror(errno));
WriteBSPFile (bsp, f);
Qclose (f);
}
//@}