The Quake 2 tools as originally released under the GPL license.

This commit is contained in:
Travis Bradshaw
2012-01-31 15:22:13 -06:00
commit 707e849167
132 changed files with 52073 additions and 0 deletions

1330
bsp/qbsp3/brushbsp.c Normal file

File diff suppressed because it is too large Load Diff

635
bsp/qbsp3/csg.c Normal file
View File

@@ -0,0 +1,635 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
tag all brushes with original contents
brushes may contain multiple contents
there will be no brush overlap after csg phase
each side has a count of the other sides it splits
the best split will be the one that minimizes the total split counts
of all remaining sides
precalc side on plane table
evaluate split side
{
cost = 0
for all sides
for all sides
get
if side splits side and splitside is on same child
cost++;
}
*/
void SplitBrush2 (bspbrush_t *brush, int planenum,
bspbrush_t **front, bspbrush_t **back)
{
SplitBrush (brush, planenum, front, back);
#if 0
if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
(*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1
if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
(*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1
#endif
}
/*
===============
SubtractBrush
Returns a list of brushes that remain after B is subtracted from A.
May by empty if A is contained inside B.
The originals are undisturbed.
===============
*/
bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
{ // a - b = out (list)
int i;
bspbrush_t *front, *back;
bspbrush_t *out, *in;
in = a;
out = NULL;
for (i=0 ; i<b->numsides && in ; i++)
{
SplitBrush2 (in, b->sides[i].planenum, &front, &back);
if (in != a)
FreeBrush (in);
if (front)
{ // add to list
front->next = out;
out = front;
}
in = back;
}
if (in)
FreeBrush (in);
else
{ // didn't really intersect
FreeBrushList (out);
return a;
}
return out;
}
/*
===============
IntersectBrush
Returns a single brush made up by the intersection of the
two provided brushes, or NULL if they are disjoint.
The originals are undisturbed.
===============
*/
bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
{
int i;
bspbrush_t *front, *back;
bspbrush_t *in;
in = a;
for (i=0 ; i<b->numsides && in ; i++)
{
SplitBrush2 (in, b->sides[i].planenum, &front, &back);
if (in != a)
FreeBrush (in);
if (front)
FreeBrush (front);
in = back;
}
if (in == a)
return NULL;
in->next = NULL;
return in;
}
/*
===============
BrushesDisjoint
Returns true if the two brushes definately do not intersect.
There will be false negatives for some non-axial combinations.
===============
*/
qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
{
int i, j;
// check bounding boxes
for (i=0 ; i<3 ; i++)
if (a->mins[i] >= b->maxs[i]
|| a->maxs[i] <= b->mins[i])
return true; // bounding boxes don't overlap
// check for opposing planes
for (i=0 ; i<a->numsides ; i++)
{
for (j=0 ; j<b->numsides ; j++)
{
if (a->sides[i].planenum ==
(b->sides[j].planenum^1) )
return true; // opposite planes, so not touching
}
}
return false; // might intersect
}
/*
===============
IntersectionContents
Returns a content word for the intersection of two brushes.
Some combinations will generate a combination (water + clip),
but most will be the stronger of the two contents.
===============
*/
int IntersectionContents (int c1, int c2)
{
int out;
out = c1 | c2;
if (out & CONTENTS_SOLID)
out = CONTENTS_SOLID;
return out;
}
int minplanenums[3];
int maxplanenums[3];
/*
===============
ClipBrushToBox
Any planes shared with the box edge will be set to no texinfo
===============
*/
bspbrush_t *ClipBrushToBox (bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs)
{
int i, j;
bspbrush_t *front, *back;
int p;
for (j=0 ; j<2 ; j++)
{
if (brush->maxs[j] > clipmaxs[j])
{
SplitBrush (brush, maxplanenums[j], &front, &back);
if (front)
FreeBrush (front);
brush = back;
if (!brush)
return NULL;
}
if (brush->mins[j] < clipmins[j])
{
SplitBrush (brush, minplanenums[j], &front, &back);
if (back)
FreeBrush (back);
brush = front;
if (!brush)
return NULL;
}
}
// remove any colinear faces
for (i=0 ; i<brush->numsides ; i++)
{
p = brush->sides[i].planenum & ~1;
if (p == maxplanenums[0] || p == maxplanenums[1]
|| p == minplanenums[0] || p == minplanenums[1])
{
brush->sides[i].texinfo = TEXINFO_NODE;
brush->sides[i].visible = false;
}
}
return brush;
}
/*
===============
MakeBspBrushList
===============
*/
bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
vec3_t clipmins, vec3_t clipmaxs)
{
mapbrush_t *mb;
bspbrush_t *brushlist, *newbrush;
int i, j;
int c_faces;
int c_brushes;
int numsides;
int vis;
vec3_t normal;
float dist;
for (i=0 ; i<2 ; i++)
{
VectorClear (normal);
normal[i] = 1;
dist = clipmaxs[i];
maxplanenums[i] = FindFloatPlane (normal, dist);
dist = clipmins[i];
minplanenums[i] = FindFloatPlane (normal, dist);
}
brushlist = NULL;
c_faces = 0;
c_brushes = 0;
for (i=startbrush ; i<endbrush ; i++)
{
mb = &mapbrushes[i];
numsides = mb->numsides;
if (!numsides)
continue;
// make sure the brush has at least one face showing
vis = 0;
for (j=0 ; j<numsides ; j++)
if (mb->original_sides[j].visible && mb->original_sides[j].winding)
vis++;
#if 0
if (!vis)
continue; // no faces at all
#endif
// if the brush is outside the clip area, skip it
for (j=0 ; j<3 ; j++)
if (mb->mins[j] >= clipmaxs[j]
|| mb->maxs[j] <= clipmins[j])
break;
if (j != 3)
continue;
//
// make a copy of the brush
//
newbrush = AllocBrush (mb->numsides);
newbrush->original = mb;
newbrush->numsides = mb->numsides;
memcpy (newbrush->sides, mb->original_sides, numsides*sizeof(side_t));
for (j=0 ; j<numsides ; j++)
{
if (newbrush->sides[j].winding)
newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
if (newbrush->sides[j].surf & SURF_HINT)
newbrush->sides[j].visible = true; // hints are always visible
}
VectorCopy (mb->mins, newbrush->mins);
VectorCopy (mb->maxs, newbrush->maxs);
//
// carve off anything outside the clip box
//
newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
if (!newbrush)
continue;
c_faces += vis;
c_brushes++;
newbrush->next = brushlist;
brushlist = newbrush;
}
return brushlist;
}
/*
===============
AddBspBrushListToTail
===============
*/
bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
{
bspbrush_t *walk, *next;
for (walk=list ; walk ; walk=next)
{ // add to end of list
next = walk->next;
walk->next = NULL;
tail->next = walk;
tail = walk;
}
return tail;
}
/*
===========
CullList
Builds a new list that doesn't hold the given brush
===========
*/
bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
{
bspbrush_t *newlist;
bspbrush_t *next;
newlist = NULL;
for ( ; list ; list = next)
{
next = list->next;
if (list == skip1)
{
FreeBrush (list);
continue;
}
list->next = newlist;
newlist = list;
}
return newlist;
}
/*
==================
WriteBrushMap
==================
*/
void WriteBrushMap (char *name, bspbrush_t *list)
{
FILE *f;
side_t *s;
int i;
winding_t *w;
printf ("writing %s\n", name);
f = fopen (name, "wb");
if (!f)
Error ("Can't write %s\b", name);
fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
for ( ; list ; list=list->next )
{
fprintf (f, "{\n");
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
{
w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
FreeWinding (w);
}
fprintf (f, "}\n");
}
fprintf (f, "}\n");
fclose (f);
}
/*
==================
BrushGE
Returns true if b1 is allowed to bite b2
==================
*/
qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
{
// detail brushes never bite structural brushes
if ( (b1->original->contents & CONTENTS_DETAIL)
&& !(b2->original->contents & CONTENTS_DETAIL) )
return false;
if (b1->original->contents & CONTENTS_SOLID)
return true;
return false;
}
/*
=================
ChopBrushes
Carves any intersecting solid brushes into the minimum number
of non-intersecting brushes.
=================
*/
bspbrush_t *ChopBrushes (bspbrush_t *head)
{
bspbrush_t *b1, *b2, *next;
bspbrush_t *tail;
bspbrush_t *keep;
bspbrush_t *sub, *sub2;
int c1, c2;
qprintf ("---- ChopBrushes ----\n");
qprintf ("original brushes: %i\n", CountBrushList (head));
#if 0
if (startbrush == 0)
WriteBrushList ("before.gl", head, false);
#endif
keep = NULL;
newlist:
// find tail
if (!head)
return NULL;
for (tail=head ; tail->next ; tail=tail->next)
;
for (b1=head ; b1 ; b1=next)
{
next = b1->next;
for (b2=b1->next ; b2 ; b2 = b2->next)
{
if (BrushesDisjoint (b1, b2))
continue;
sub = NULL;
sub2 = NULL;
c1 = 999999;
c2 = 999999;
if ( BrushGE (b2, b1) )
{
sub = SubtractBrush (b1, b2);
if (sub == b1)
continue; // didn't really intersect
if (!sub)
{ // b1 is swallowed by b2
head = CullList (b1, b1);
goto newlist;
}
c1 = CountBrushList (sub);
}
if ( BrushGE (b1, b2) )
{
sub2 = SubtractBrush (b2, b1);
if (sub2 == b2)
continue; // didn't really intersect
if (!sub2)
{ // b2 is swallowed by b1
FreeBrushList (sub);
head = CullList (b1, b2);
goto newlist;
}
c2 = CountBrushList (sub2);
}
if (!sub && !sub2)
continue; // neither one can bite
// only accept if it didn't fragment
// (commening this out allows full fragmentation)
if (c1 > 1 && c2 > 1)
{
if (sub2)
FreeBrushList (sub2);
if (sub)
FreeBrushList (sub);
continue;
}
if (c1 < c2)
{
if (sub2)
FreeBrushList (sub2);
tail = AddBrushListToTail (sub, tail);
head = CullList (b1, b1);
goto newlist;
}
else
{
if (sub)
FreeBrushList (sub);
tail = AddBrushListToTail (sub2, tail);
head = CullList (b1, b2);
goto newlist;
}
}
if (!b2)
{ // b1 is no longer intersecting anything, so keep it
b1->next = keep;
keep = b1;
}
}
qprintf ("output brushes: %i\n", CountBrushList (keep));
#if 0
{
WriteBrushList ("after.gl", keep, false);
WriteBrushMap ("after.map", keep);
}
#endif
return keep;
}
/*
=================
InitialBrushList
=================
*/
bspbrush_t *InitialBrushList (bspbrush_t *list)
{
bspbrush_t *b;
bspbrush_t *out, *newb;
int i;
// only return brushes that have visible faces
out = NULL;
for (b=list ; b ; b=b->next)
{
#if 0
for (i=0 ; i<b->numsides ; i++)
if (b->sides[i].visible)
break;
if (i == b->numsides)
continue;
#endif
newb = CopyBrush (b);
newb->next = out;
out = newb;
// clear visible, so it must be set by MarkVisibleFaces_r
// to be used in the optimized list
for (i=0 ; i<b->numsides ; i++)
{
newb->sides[i].original = &b->sides[i];
// newb->sides[i].visible = true;
b->sides[i].visible = false;
}
}
return out;
}
/*
=================
OptimizedBrushList
=================
*/
bspbrush_t *OptimizedBrushList (bspbrush_t *list)
{
bspbrush_t *b;
bspbrush_t *out, *newb;
int i;
// only return brushes that have visible faces
out = NULL;
for (b=list ; b ; b=b->next)
{
for (i=0 ; i<b->numsides ; i++)
if (b->sides[i].visible)
break;
if (i == b->numsides)
continue;
newb = CopyBrush (b);
newb->next = out;
out = newb;
}
// WriteBrushList ("vis.gl", out, true);
return out;
}

1076
bsp/qbsp3/faces.c Normal file

File diff suppressed because it is too large Load Diff

232
bsp/qbsp3/gldraw.c Normal file
View File

@@ -0,0 +1,232 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include "qbsp.h"
// can't use the glvertex3fv functions, because the vec3_t fields
// could be either floats or doubles, depending on DOUBLEVEC_T
qboolean drawflag;
vec3_t draw_mins, draw_maxs;
#define WIN_SIZE 512
void InitWindow (void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE);
auxInitWindow ("qcsg");
}
void Draw_ClearWindow (void)
{
static int init;
int w, h, g;
vec_t mx, my;
if (!drawflag)
return;
if (!init)
{
init = true;
InitWindow ();
}
glClearColor (1,0.8,0.8,0);
glClear (GL_COLOR_BUFFER_BIT);
w = (draw_maxs[0] - draw_mins[0]);
h = (draw_maxs[1] - draw_mins[1]);
mx = draw_mins[0] + w/2;
my = draw_mins[1] + h/2;
g = w > h ? w : h;
glLoadIdentity ();
gluPerspective (90, 1, 2, 16384);
gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
glColor3f (0,0,0);
// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
glDisable (GL_DEPTH_TEST);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if 0
glColor4f (1,0,0,0.5);
glBegin (GL_POLYGON);
glVertex3f (0, 500, 0);
glVertex3f (0, 900, 0);
glVertex3f (0, 900, 100);
glVertex3f (0, 500, 100);
glEnd ();
#endif
glFlush ();
}
void Draw_SetRed (void)
{
if (!drawflag)
return;
glColor3f (1,0,0);
}
void Draw_SetGrey (void)
{
if (!drawflag)
return;
glColor3f (0.5,0.5,0.5);
}
void Draw_SetBlack (void)
{
if (!drawflag)
return;
glColor3f (0,0,0);
}
void DrawWinding (winding_t *w)
{
int i;
if (!drawflag)
return;
glColor4f (0,0,0,0.5);
glBegin (GL_LINE_LOOP);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glColor4f (0,1,0,0.3);
glBegin (GL_POLYGON);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glFlush ();
}
void DrawAuxWinding (winding_t *w)
{
int i;
if (!drawflag)
return;
glColor4f (0,0,0,0.5);
glBegin (GL_LINE_LOOP);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glColor4f (1,0,0,0.3);
glBegin (GL_POLYGON);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glFlush ();
}
//============================================================
#define GLSERV_PORT 25001
qboolean wins_init;
int draw_socket;
void GLS_BeginScene (void)
{
WSADATA winsockdata;
WORD wVersionRequested;
struct sockaddr_in address;
int r;
if (!wins_init)
{
wins_init = true;
wVersionRequested = MAKEWORD(1, 1);
r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
if (r)
Error ("Winsock initialization failed.");
}
// connect a socket to the server
draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (draw_socket == -1)
Error ("draw_socket failed");
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_port = GLSERV_PORT;
r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
if (r == -1)
{
closesocket (draw_socket);
draw_socket = 0;
}
}
void GLS_Winding (winding_t *w, int code)
{
byte buf[1024];
int i, j;
if (!draw_socket)
return;
((int *)buf)[0] = w->numpoints;
((int *)buf)[1] = code;
for (i=0 ; i<w->numpoints ; i++)
for (j=0 ; j<3 ; j++)
((float *)buf)[2+i*3+j] = w->p[i][j];
send (draw_socket, buf, w->numpoints*12+8, 0);
}
void GLS_EndScene (void)
{
closesocket (draw_socket);
draw_socket = 0;
}

149
bsp/qbsp3/glfile.c Normal file
View File

@@ -0,0 +1,149 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int c_glfaces;
int PortalVisibleSides (portal_t *p)
{
int fcon, bcon;
if (!p->onnode)
return 0; // outside
fcon = p->nodes[0]->contents;
bcon = p->nodes[1]->contents;
// same contents never create a face
if (fcon == bcon)
return 0;
// FIXME: is this correct now?
if (!fcon)
return 1;
if (!bcon)
return 2;
return 0;
}
void OutputWinding (winding_t *w, FILE *glview)
{
static int level = 128;
vec_t light;
int i;
fprintf (glview, "%i\n", w->numpoints);
level+=28;
light = (level&255)/255.0;
for (i=0 ; i<w->numpoints ; i++)
{
fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
w->p[i][0],
w->p[i][1],
w->p[i][2],
light,
light,
light);
}
fprintf (glview, "\n");
}
/*
=============
OutputPortal
=============
*/
void OutputPortal (portal_t *p, FILE *glview)
{
winding_t *w;
int sides;
sides = PortalVisibleSides (p);
if (!sides)
return;
c_glfaces++;
w = p->winding;
if (sides == 2) // back side
w = ReverseWinding (w);
OutputWinding (w, glview);
if (sides == 2)
FreeWinding(w);
}
/*
=============
WriteGLView_r
=============
*/
void WriteGLView_r (node_t *node, FILE *glview)
{
portal_t *p, *nextp;
if (node->planenum != PLANENUM_LEAF)
{
WriteGLView_r (node->children[0], glview);
WriteGLView_r (node->children[1], glview);
return;
}
// write all the portals
for (p=node->portals ; p ; p=nextp)
{
if (p->nodes[0] == node)
{
OutputPortal (p, glview);
nextp = p->next[0];
}
else
nextp = p->next[1];
}
}
/*
=============
WriteGLView
=============
*/
void WriteGLView (tree_t *tree, char *source)
{
char name[1024];
FILE *glview;
c_glfaces = 0;
sprintf (name, "%s%s.gl",outbase, source);
printf ("Writing %s\n", name);
glview = fopen (name, "w");
if (!glview)
Error ("Couldn't open %s", name);
WriteGLView_r (tree->headnode, glview);
fclose (glview);
printf ("%5i c_glfaces\n", c_glfaces);
}

100
bsp/qbsp3/leakfile.c Normal file
View File

@@ -0,0 +1,100 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
==============================================================================
LEAF FILE GENERATION
Save out name.line for qe3 to read
==============================================================================
*/
/*
=============
LeakFile
Finds the shortest possible chain of portals
that leads from the outside leaf to a specifically
occupied leaf
=============
*/
void LeakFile (tree_t *tree)
{
vec3_t mid;
FILE *linefile;
char filename[1024];
node_t *node;
int count;
if (!tree->outside_node.occupied)
return;
qprintf ("--- LeakFile ---\n");
//
// write the points to the file
//
sprintf (filename, "%s.lin", source);
linefile = fopen (filename, "w");
if (!linefile)
Error ("Couldn't open %s\n", filename);
count = 0;
node = &tree->outside_node;
while (node->occupied > 1)
{
int next;
portal_t *p, *nextportal;
node_t *nextnode;
int s;
// find the best portal exit
next = node->occupied;
for (p=node->portals ; p ; p = p->next[!s])
{
s = (p->nodes[0] == node);
if (p->nodes[s]->occupied
&& p->nodes[s]->occupied < next)
{
nextportal = p;
nextnode = p->nodes[s];
next = nextnode->occupied;
}
}
node = nextnode;
WindingCenter (nextportal->winding, mid);
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
count++;
}
// add the occupant center
GetVectorForKey (node->occupant, "origin", mid);
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
qprintf ("%5i point linefile\n", count+1);
fclose (linefile);
}

98
bsp/qbsp3/makefile Normal file
View File

@@ -0,0 +1,98 @@
CFLAGS = -c
LDFLAGS =
ODIR = baddir
EXEBASE = qbsp3
EXE = $(ODIR)/qbsp3
all: $(EXE)
_next:
make "CFLAGS = -c -g -I../../common -DDOUBLEVEC_T" "ODIR = next"
_irix:
make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3" "ODIR = irix"
_irixdebug:
make "CFLAGS = -c -O2 -g -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -g" "ODIR = irix"
_irixinst:
make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3" "ODIR = irix"
cp irix/$(EXEBASE) /limbo/quake2/bin_irix
_irixclean:
rm -f irix/*.o irix/$(EXEBASE)
_osf:
make "CFLAGS = -c -O4 -I../../common -threads -DDOUBLEVEC_T" "LDFLAGS = -threads" "ODIR = osf"
clean:
rm -f irix/*.o irix/$(EXEBASE)
install:
cp irix/$(EXEBASE) /limbo/quake2/bin_irix
FILES = $(ODIR)/brushbsp.o $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/faces.o $(ODIR)/nodraw.o $(ODIR)/glfile.o $(ODIR)/leakfile.o $(ODIR)/map.o $(ODIR)/mathlib.o $(ODIR)/polylib.o $(ODIR)/portals.o $(ODIR)/prtfile.o $(ODIR)/qbsp3.o $(ODIR)/scriplib.o $(ODIR)/textures.o $(ODIR)/threads.o $(ODIR)/tree.o $(ODIR)/writebsp.o $(ODIR)/csg.o
$(EXE) : $(FILES)
cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
$(ODIR)/brushbsp.o : brushbsp.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/faces.o : faces.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/nodraw.o : nodraw.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/glfile.o : glfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/leakfile.o : leakfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/map.o : map.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/portals.o : portals.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/prtfile.o : prtfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/qbsp3.o : qbsp3.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/tree.o : tree.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/textures.o : textures.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/writebsp.o : writebsp.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/csg.o : csg.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/cmdlib.o : ../../common/cmdlib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/mathlib.o : ../../common/mathlib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/polylib.o : ../../common/polylib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/scriplib.o : ../../common/scriplib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/threads.o : ../../common/threads.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/bspfile.o : ../../common/bspfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i

1017
bsp/qbsp3/map.c Normal file

File diff suppressed because it is too large Load Diff

47
bsp/qbsp3/nodraw.c Normal file
View File

@@ -0,0 +1,47 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
vec3_t draw_mins, draw_maxs;
qboolean drawflag;
void Draw_ClearWindow (void)
{
}
//============================================================
#define GLSERV_PORT 25001
void GLS_BeginScene (void)
{
}
void GLS_Winding (winding_t *w, int code)
{
}
void GLS_EndScene (void)
{
}

1111
bsp/qbsp3/portals.c Normal file

File diff suppressed because it is too large Load Diff

287
bsp/qbsp3/prtfile.c Normal file
View File

@@ -0,0 +1,287 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
==============================================================================
PORTAL FILE GENERATION
Save out name.prt for qvis to read
==============================================================================
*/
#define PORTALFILE "PRT1"
FILE *pf;
int num_visclusters; // clusters the player can be in
int num_visportals;
void WriteFloat (FILE *f, vec_t v)
{
if ( fabs(v - Q_rint(v)) < 0.001 )
fprintf (f,"%i ",(int)Q_rint(v));
else
fprintf (f,"%f ",v);
}
/*
=================
WritePortalFile_r
=================
*/
void WritePortalFile_r (node_t *node)
{
int i, s;
portal_t *p;
winding_t *w;
vec3_t normal;
vec_t dist;
// decision node
if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
{
WritePortalFile_r (node->children[0]);
WritePortalFile_r (node->children[1]);
return;
}
if (node->contents & CONTENTS_SOLID)
return;
for (p = node->portals ; p ; p=p->next[s])
{
w = p->winding;
s = (p->nodes[1] == node);
if (w && p->nodes[0] == node)
{
if (!Portal_VisFlood (p))
continue;
// write out to the file
// sometimes planes get turned around when they are very near
// the changeover point between different axis. interpret the
// plane the same way vis will, and flip the side orders if needed
// FIXME: is this still relevent?
WindingPlane (w, normal, &dist);
if ( DotProduct (p->plane.normal, normal) < 0.99 )
{ // backwards...
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
}
else
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
for (i=0 ; i<w->numpoints ; i++)
{
fprintf (pf,"(");
WriteFloat (pf, w->p[i][0]);
WriteFloat (pf, w->p[i][1]);
WriteFloat (pf, w->p[i][2]);
fprintf (pf,") ");
}
fprintf (pf,"\n");
}
}
}
/*
================
FillLeafNumbers_r
All of the leafs under node will have the same cluster
================
*/
void FillLeafNumbers_r (node_t *node, int num)
{
if (node->planenum == PLANENUM_LEAF)
{
if (node->contents & CONTENTS_SOLID)
node->cluster = -1;
else
node->cluster = num;
return;
}
node->cluster = num;
FillLeafNumbers_r (node->children[0], num);
FillLeafNumbers_r (node->children[1], num);
}
/*
================
NumberLeafs_r
================
*/
void NumberLeafs_r (node_t *node)
{
portal_t *p;
if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
{ // decision node
node->cluster = -99;
NumberLeafs_r (node->children[0]);
NumberLeafs_r (node->children[1]);
return;
}
// either a leaf or a detail cluster
if ( node->contents & CONTENTS_SOLID )
{ // solid block, viewpoint never inside
node->cluster = -1;
return;
}
FillLeafNumbers_r (node, num_visclusters);
num_visclusters++;
// count the portals
for (p = node->portals ; p ; )
{
if (p->nodes[0] == node) // only write out from first leaf
{
if (Portal_VisFlood (p))
num_visportals++;
p = p->next[0];
}
else
p = p->next[1];
}
}
/*
================
CreateVisPortals_r
================
*/
void CreateVisPortals_r (node_t *node)
{
// stop as soon as we get to a detail_seperator, which
// means that everything below is in a single cluster
if (node->planenum == PLANENUM_LEAF || node->detail_seperator )
return;
MakeNodePortal (node);
SplitNodePortals (node);
CreateVisPortals_r (node->children[0]);
CreateVisPortals_r (node->children[1]);
}
/*
================
FinishVisPortals_r
================
*/
void FinishVisPortals2_r (node_t *node)
{
if (node->planenum == PLANENUM_LEAF)
return;
MakeNodePortal (node);
SplitNodePortals (node);
FinishVisPortals2_r (node->children[0]);
FinishVisPortals2_r (node->children[1]);
}
void FinishVisPortals_r (node_t *node)
{
if (node->planenum == PLANENUM_LEAF)
return;
if (node->detail_seperator)
{
FinishVisPortals2_r (node);
return;
}
FinishVisPortals_r (node->children[0]);
FinishVisPortals_r (node->children[1]);
}
int clusterleaf;
void SaveClusters_r (node_t *node)
{
if (node->planenum == PLANENUM_LEAF)
{
dleafs[clusterleaf++].cluster = node->cluster;
return;
}
SaveClusters_r (node->children[0]);
SaveClusters_r (node->children[1]);
}
/*
================
WritePortalFile
================
*/
void WritePortalFile (tree_t *tree)
{
char filename[1024];
node_t *headnode;
qprintf ("--- WritePortalFile ---\n");
headnode = tree->headnode;
num_visclusters = 0;
num_visportals = 0;
FreeTreePortals_r (headnode);
MakeHeadnodePortals (tree);
CreateVisPortals_r (headnode);
// set the cluster field in every leaf and count the total number of portals
NumberLeafs_r (headnode);
// write the file
sprintf (filename, "%s.prt", source);
printf ("writing %s\n", filename);
pf = fopen (filename, "w");
if (!pf)
Error ("Error opening %s", filename);
fprintf (pf, "%s\n", PORTALFILE);
fprintf (pf, "%i\n", num_visclusters);
fprintf (pf, "%i\n", num_visportals);
qprintf ("%5i visclusters\n", num_visclusters);
qprintf ("%5i visportals\n", num_visportals);
WritePortalFile_r (headnode);
fclose (pf);
// we need to store the clusters out now because ordering
// issues made us do this after writebsp...
clusterleaf = 1;
SaveClusters_r (headnode);
}

355
bsp/qbsp3/qbsp.h Normal file
View File

@@ -0,0 +1,355 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "cmdlib.h"
#include "mathlib.h"
#include "scriplib.h"
#include "polylib.h"
#include "threads.h"
#include "bspfile.h"
#define MAX_BRUSH_SIDES 128
#define CLIP_EPSILON 0.1
#define BOGUS_RANGE 8192
#define TEXINFO_NODE -1 // side is allready on a node
typedef struct plane_s
{
vec3_t normal;
vec_t dist;
int type;
struct plane_s *hash_chain;
} plane_t;
typedef struct
{
vec_t shift[2];
vec_t rotate;
vec_t scale[2];
char name[32];
int flags;
int value;
} brush_texture_t;
typedef struct side_s
{
int planenum;
int texinfo;
winding_t *winding;
struct side_s *original; // bspbrush_t sides will reference the mapbrush_t sides
int contents; // from miptex
int surf; // from miptex
qboolean visible; // choose visble planes first
qboolean tested; // this plane allready checked as a split
qboolean bevel; // don't ever use for bsp splitting
} side_t;
typedef struct brush_s
{
int entitynum;
int brushnum;
int contents;
vec3_t mins, maxs;
int numsides;
side_t *original_sides;
} mapbrush_t;
#define PLANENUM_LEAF -1
#define MAXEDGES 20
typedef struct face_s
{
struct face_s *next; // on node
// the chain of faces off of a node can be merged or split,
// but each face_t along the way will remain in the chain
// until the entire tree is freed
struct face_s *merged; // if set, this face isn't valid anymore
struct face_s *split[2]; // if set, this face isn't valid anymore
struct portal_s *portal;
int texinfo;
int planenum;
int contents; // faces in different contents can't merge
int outputnumber;
winding_t *w;
int numpoints;
qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex
int vertexnums[MAXEDGES];
} face_t;
typedef struct bspbrush_s
{
struct bspbrush_s *next;
vec3_t mins, maxs;
int side, testside; // side of node during construction
mapbrush_t *original;
int numsides;
side_t sides[6]; // variably sized
} bspbrush_t;
#define MAX_NODE_BRUSHES 8
typedef struct node_s
{
// both leafs and nodes
int planenum; // -1 = leaf node
struct node_s *parent;
vec3_t mins, maxs; // valid after portalization
bspbrush_t *volume; // one for each leaf/node
// nodes only
qboolean detail_seperator; // a detail brush caused the split
side_t *side; // the side that created the node
struct node_s *children[2];
face_t *faces;
// leafs only
bspbrush_t *brushlist; // fragments of all brushes in this leaf
int contents; // OR of all brush contents
int occupied; // 1 or greater can reach entity
entity_t *occupant; // for leak file testing
int cluster; // for portalfile writing
int area; // for areaportals
struct portal_s *portals; // also on nodes during construction
} node_t;
typedef struct portal_s
{
plane_t plane;
node_t *onnode; // NULL = outside box
node_t *nodes[2]; // [0] = front side of plane
struct portal_s *next[2];
winding_t *winding;
qboolean sidefound; // false if ->side hasn't been checked
side_t *side; // NULL = non-visible
face_t *face[2]; // output face in bsp file
} portal_t;
typedef struct
{
node_t *headnode;
node_t outside_node;
vec3_t mins, maxs;
} tree_t;
extern int entity_num;
extern plane_t mapplanes[MAX_MAP_PLANES];
extern int nummapplanes;
extern int nummapbrushes;
extern mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
extern vec3_t map_mins, map_maxs;
#define MAX_MAP_SIDES (MAX_MAP_BRUSHES*6)
extern int nummapbrushsides;
extern side_t brushsides[MAX_MAP_SIDES];
extern qboolean noprune;
extern qboolean nodetail;
extern qboolean fulldetail;
extern qboolean nomerge;
extern qboolean nosubdiv;
extern qboolean nowater;
extern qboolean noweld;
extern qboolean noshare;
extern qboolean notjunc;
extern vec_t microvolume;
extern char outbase[32];
extern char source[1024];
void LoadMapFile (char *filename);
int FindFloatPlane (vec3_t normal, vec_t dist);
//=============================================================================
// textures.c
typedef struct
{
char name[64];
int flags;
int value;
int contents;
char animname[64];
} textureref_t;
#define MAX_MAP_TEXTURES 1024
extern textureref_t textureref[MAX_MAP_TEXTURES];
int FindMiptex (char *name);
int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin);
//=============================================================================
void FindGCD (int *v);
mapbrush_t *Brush_LoadEntity (entity_t *ent);
int PlaneTypeForNormal (vec3_t normal);
qboolean MakeBrushPlanes (mapbrush_t *b);
int FindIntPlane (int *inormal, int *iorigin);
void CreateBrush (int brushnum);
//=============================================================================
// draw.c
extern vec3_t draw_mins, draw_maxs;
extern qboolean drawflag;
void Draw_ClearWindow (void);
void DrawWinding (winding_t *w);
void GLS_BeginScene (void);
void GLS_Winding (winding_t *w, int code);
void GLS_EndScene (void);
//=============================================================================
// csg
bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
vec3_t clipmins, vec3_t clipmaxs);
bspbrush_t *ChopBrushes (bspbrush_t *head);
bspbrush_t *InitialBrushList (bspbrush_t *list);
bspbrush_t *OptimizedBrushList (bspbrush_t *list);
void WriteBrushMap (char *name, bspbrush_t *list);
//=============================================================================
// brushbsp
void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
bspbrush_t *CopyBrush (bspbrush_t *brush);
void SplitBrush (bspbrush_t *brush, int planenum,
bspbrush_t **front, bspbrush_t **back);
tree_t *AllocTree (void);
node_t *AllocNode (void);
bspbrush_t *AllocBrush (int numsides);
int CountBrushList (bspbrush_t *brushes);
void FreeBrush (bspbrush_t *brushes);
vec_t BrushVolume (bspbrush_t *brush);
void BoundBrush (bspbrush_t *brush);
void FreeBrushList (bspbrush_t *brushes);
tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
//=============================================================================
// portals.c
int VisibleContents (int contents);
void MakeHeadnodePortals (tree_t *tree);
void MakeNodePortal (node_t *node);
void SplitNodePortals (node_t *node);
qboolean Portal_VisFlood (portal_t *p);
qboolean FloodEntities (tree_t *tree);
void FillOutside (node_t *headnode);
void FloodAreas (tree_t *tree);
void MarkVisibleSides (tree_t *tree, int start, int end);
void FreePortal (portal_t *p);
void EmitAreaPortals (node_t *headnode);
void MakeTreePortals (tree_t *tree);
//=============================================================================
// glfile.c
void OutputWinding (winding_t *w, FILE *glview);
void WriteGLView (tree_t *tree, char *source);
//=============================================================================
// leakfile.c
void LeakFile (tree_t *tree);
//=============================================================================
// prtfile.c
void WritePortalFile (tree_t *tree);
//=============================================================================
// writebsp.c
void SetModelNumbers (void);
void SetLightStyles (void);
void BeginBSPFile (void);
void WriteBSP (node_t *headnode);
void EndBSPFile (void);
void BeginModel (void);
void EndModel (void);
//=============================================================================
// faces.c
void MakeFaces (node_t *headnode);
void FixTjuncs (node_t *headnode);
int GetEdge2 (int v1, int v2, face_t *f);
face_t *AllocFace (void);
void FreeFace (face_t *f);
void MergeNodeFaces (node_t *node);
//=============================================================================
// tree.c
void FreeTree (tree_t *tree);
void FreeTree_r (node_t *node);
void PrintTree_r (node_t *node, int depth);
void FreeTreePortals_r (node_t *node);
void PruneNodes_r (node_t *node);
void PruneNodes (node_t *node);

537
bsp/qbsp3/qbsp3.c Normal file
View File

@@ -0,0 +1,537 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
extern float subdivide_size;
char source[1024];
char name[1024];
vec_t microvolume = 1.0;
qboolean noprune;
qboolean glview;
qboolean nodetail;
qboolean fulldetail;
qboolean onlyents;
qboolean nomerge;
qboolean nowater;
qboolean nofill;
qboolean nocsg;
qboolean noweld;
qboolean noshare;
qboolean nosubdiv;
qboolean notjunc;
qboolean noopt;
qboolean leaktest;
qboolean verboseentities;
char outbase[32];
int block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7;
int entity_num;
node_t *block_nodes[10][10];
/*
============
BlockTree
============
*/
node_t *BlockTree (int xl, int yl, int xh, int yh)
{
node_t *node;
vec3_t normal;
float dist;
int mid;
if (xl == xh && yl == yh)
{
node = block_nodes[xl+5][yl+5];
if (!node)
{ // return an empty leaf
node = AllocNode ();
node->planenum = PLANENUM_LEAF;
node->contents = 0; //CONTENTS_SOLID;
return node;
}
return node;
}
// create a seperator along the largest axis
node = AllocNode ();
if (xh - xl > yh - yl)
{ // split x axis
mid = xl + (xh-xl)/2 + 1;
normal[0] = 1;
normal[1] = 0;
normal[2] = 0;
dist = mid*1024;
node->planenum = FindFloatPlane (normal, dist);
node->children[0] = BlockTree ( mid, yl, xh, yh);
node->children[1] = BlockTree ( xl, yl, mid-1, yh);
}
else
{
mid = yl + (yh-yl)/2 + 1;
normal[0] = 0;
normal[1] = 1;
normal[2] = 0;
dist = mid*1024;
node->planenum = FindFloatPlane (normal, dist);
node->children[0] = BlockTree ( xl, mid, xh, yh);
node->children[1] = BlockTree ( xl, yl, xh, mid-1);
}
return node;
}
/*
============
ProcessBlock_Thread
============
*/
int brush_start, brush_end;
void ProcessBlock_Thread (int blocknum)
{
int xblock, yblock;
vec3_t mins, maxs;
bspbrush_t *brushes;
tree_t *tree;
node_t *node;
yblock = block_yl + blocknum / (block_xh-block_xl+1);
xblock = block_xl + blocknum % (block_xh-block_xl+1);
qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
mins[0] = xblock*1024;
mins[1] = yblock*1024;
mins[2] = -4096;
maxs[0] = (xblock+1)*1024;
maxs[1] = (yblock+1)*1024;
maxs[2] = 4096;
// the makelist and chopbrushes could be cached between the passes...
brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs);
if (!brushes)
{
node = AllocNode ();
node->planenum = PLANENUM_LEAF;
node->contents = CONTENTS_SOLID;
block_nodes[xblock+5][yblock+5] = node;
return;
}
if (!nocsg)
brushes = ChopBrushes (brushes);
tree = BrushBSP (brushes, mins, maxs);
block_nodes[xblock+5][yblock+5] = tree->headnode;
}
/*
============
ProcessWorldModel
============
*/
void ProcessWorldModel (void)
{
entity_t *e;
tree_t *tree;
qboolean leaked;
qboolean optimize;
e = &entities[entity_num];
brush_start = e->firstbrush;
brush_end = brush_start + e->numbrushes;
leaked = false;
//
// perform per-block operations
//
if (block_xh * 1024 > map_maxs[0])
block_xh = floor(map_maxs[0]/1024.0);
if ( (block_xl+1) * 1024 < map_mins[0])
block_xl = floor(map_mins[0]/1024.0);
if (block_yh * 1024 > map_maxs[1])
block_yh = floor(map_maxs[1]/1024.0);
if ( (block_yl+1) * 1024 < map_mins[1])
block_yl = floor(map_mins[1]/1024.0);
if (block_xl <-4)
block_xl = -4;
if (block_yl <-4)
block_yl = -4;
if (block_xh > 3)
block_xh = 3;
if (block_yh > 3)
block_yh = 3;
for (optimize = false ; optimize <= true ; optimize++)
{
qprintf ("--------------------------------------------\n");
RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
!verbose, ProcessBlock_Thread);
//
// build the division tree
// oversizing the blocks guarantees that all the boundaries
// will also get nodes.
//
qprintf ("--------------------------------------------\n");
tree = AllocTree ();
tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
tree->mins[0] = (block_xl)*1024;
tree->mins[1] = (block_yl)*1024;
tree->mins[2] = map_mins[2] - 8;
tree->maxs[0] = (block_xh+1)*1024;
tree->maxs[1] = (block_yh+1)*1024;
tree->maxs[2] = map_maxs[2] + 8;
//
// perform the global operations
//
MakeTreePortals (tree);
if (FloodEntities (tree))
FillOutside (tree->headnode);
else
{
printf ("**** leaked ****\n");
leaked = true;
LeakFile (tree);
if (leaktest)
{
printf ("--- MAP LEAKED ---\n");
exit (0);
}
}
MarkVisibleSides (tree, brush_start, brush_end);
if (noopt || leaked)
break;
if (!optimize)
{
FreeTree (tree);
}
}
FloodAreas (tree);
if (glview)
WriteGLView (tree, source);
MakeFaces (tree->headnode);
FixTjuncs (tree->headnode);
if (!noprune)
PruneNodes (tree->headnode);
WriteBSP (tree->headnode);
if (!leaked)
WritePortalFile (tree);
FreeTree (tree);
}
/*
============
ProcessSubModel
============
*/
void ProcessSubModel (void)
{
entity_t *e;
int start, end;
tree_t *tree;
bspbrush_t *list;
vec3_t mins, maxs;
e = &entities[entity_num];
start = e->firstbrush;
end = start + e->numbrushes;
mins[0] = mins[1] = mins[2] = -4096;
maxs[0] = maxs[1] = maxs[2] = 4096;
list = MakeBspBrushList (start, end, mins, maxs);
if (!nocsg)
list = ChopBrushes (list);
tree = BrushBSP (list, mins, maxs);
MakeTreePortals (tree);
MarkVisibleSides (tree, start, end);
MakeFaces (tree->headnode);
FixTjuncs (tree->headnode);
WriteBSP (tree->headnode);
FreeTree (tree);
}
/*
============
ProcessModels
============
*/
void ProcessModels (void)
{
BeginBSPFile ();
for (entity_num=0 ; entity_num< num_entities ; entity_num++)
{
if (!entities[entity_num].numbrushes)
continue;
qprintf ("############### model %i ###############\n", nummodels);
BeginModel ();
if (entity_num == 0)
ProcessWorldModel ();
else
ProcessSubModel ();
EndModel ();
if (!verboseentities)
verbose = false; // don't bother printing submodels
}
EndBSPFile ();
}
/*
============
main
============
*/
int main (int argc, char **argv)
{
int i;
double start, end;
char path[1024];
printf ("---- qbsp3 ----\n");
for (i=1 ; i<argc ; i++)
{
if (!strcmp(argv[i],"-threads"))
{
numthreads = atoi (argv[i+1]);
i++;
}
else if (!strcmp(argv[i],"-glview"))
{
glview = true;
}
else if (!strcmp(argv[i], "-v"))
{
printf ("verbose = true\n");
verbose = true;
}
else if (!strcmp(argv[i], "-draw"))
{
printf ("drawflag = true\n");
drawflag = true;
}
else if (!strcmp(argv[i], "-noweld"))
{
printf ("noweld = true\n");
noweld = true;
}
else if (!strcmp(argv[i], "-nocsg"))
{
printf ("nocsg = true\n");
nocsg = true;
}
else if (!strcmp(argv[i], "-noshare"))
{
printf ("noshare = true\n");
noshare = true;
}
else if (!strcmp(argv[i], "-notjunc"))
{
printf ("notjunc = true\n");
notjunc = true;
}
else if (!strcmp(argv[i], "-nowater"))
{
printf ("nowater = true\n");
nowater = true;
}
else if (!strcmp(argv[i], "-noopt"))
{
printf ("noopt = true\n");
noopt = true;
}
else if (!strcmp(argv[i], "-noprune"))
{
printf ("noprune = true\n");
noprune = true;
}
else if (!strcmp(argv[i], "-nofill"))
{
printf ("nofill = true\n");
nofill = true;
}
else if (!strcmp(argv[i], "-nomerge"))
{
printf ("nomerge = true\n");
nomerge = true;
}
else if (!strcmp(argv[i], "-nosubdiv"))
{
printf ("nosubdiv = true\n");
nosubdiv = true;
}
else if (!strcmp(argv[i], "-nodetail"))
{
printf ("nodetail = true\n");
nodetail = true;
}
else if (!strcmp(argv[i], "-fulldetail"))
{
printf ("fulldetail = true\n");
fulldetail = true;
}
else if (!strcmp(argv[i], "-onlyents"))
{
printf ("onlyents = true\n");
onlyents = true;
}
else if (!strcmp(argv[i], "-micro"))
{
microvolume = atof(argv[i+1]);
printf ("microvolume = %f\n", microvolume);
i++;
}
else if (!strcmp(argv[i], "-leaktest"))
{
printf ("leaktest = true\n");
leaktest = true;
}
else if (!strcmp(argv[i], "-verboseentities"))
{
printf ("verboseentities = true\n");
verboseentities = true;
}
else if (!strcmp(argv[i], "-chop"))
{
subdivide_size = atof(argv[i+1]);
printf ("subdivide_size = %f\n", subdivide_size);
i++;
}
else if (!strcmp(argv[i], "-block"))
{
block_xl = block_xh = atoi(argv[i+1]);
block_yl = block_yh = atoi(argv[i+2]);
printf ("block: %i,%i\n", block_xl, block_yl);
i+=2;
}
else if (!strcmp(argv[i], "-blocks"))
{
block_xl = atoi(argv[i+1]);
block_yl = atoi(argv[i+2]);
block_xh = atoi(argv[i+3]);
block_yh = atoi(argv[i+4]);
printf ("blocks: %i,%i to %i,%i\n",
block_xl, block_yl, block_xh, block_yh);
i+=4;
}
else if (!strcmp (argv[i],"-tmpout"))
{
strcpy (outbase, "/tmp");
}
else if (argv[i][0] == '-')
Error ("Unknown option \"%s\"", argv[i]);
else
break;
}
if (i != argc - 1)
Error ("usage: qbsp3 [options] mapfile");
start = I_FloatTime ();
ThreadSetDefault ();
numthreads = 1; // multiple threads aren't helping...
SetQdirFromPath (argv[i]);
strcpy (source, ExpandArg (argv[i]));
StripExtension (source);
// delete portal and line files
sprintf (path, "%s.prt", source);
remove (path);
sprintf (path, "%s.lin", source);
remove (path);
strcpy (name, ExpandArg (argv[i]));
DefaultExtension (name, ".map"); // might be .reg
//
// if onlyents, just grab the entites and resave
//
if (onlyents)
{
char out[1024];
sprintf (out, "%s.bsp", source);
LoadBSPFile (out);
num_entities = 0;
LoadMapFile (name);
SetModelNumbers ();
SetLightStyles ();
UnparseEntities ();
WriteBSPFile (out);
}
else
{
//
// start from scratch
//
LoadMapFile (name);
SetModelNumbers ();
SetLightStyles ();
ProcessModels ();
}
end = I_FloatTime ();
printf ("%5.0f seconds elapsed\n", end-start);
return 0;
}

221
bsp/qbsp3/textures.c Normal file
View File

@@ -0,0 +1,221 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int nummiptex;
textureref_t textureref[MAX_MAP_TEXTURES];
//==========================================================================
int FindMiptex (char *name)
{
int i;
char path[1024];
miptex_t *mt;
for (i=0 ; i<nummiptex ; i++)
if (!strcmp (name, textureref[i].name))
{
return i;
}
if (nummiptex == MAX_MAP_TEXTURES)
Error ("MAX_MAP_TEXTURES");
strcpy (textureref[i].name, name);
// load the miptex to get the flags and values
sprintf (path, "%stextures/%s.wal", gamedir, name);
if (TryLoadFile (path, (void **)&mt) != -1)
{
textureref[i].value = LittleLong (mt->value);
textureref[i].flags = LittleLong (mt->flags);
textureref[i].contents = LittleLong (mt->contents);
strcpy (textureref[i].animname, mt->animname);
free (mt);
}
nummiptex++;
if (textureref[i].animname[0])
FindMiptex (textureref[i].animname);
return i;
}
/*
==================
textureAxisFromPlane
==================
*/
vec3_t baseaxis[18] =
{
{0,0,1}, {1,0,0}, {0,-1,0}, // floor
{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
{0,-1,0}, {1,0,0}, {0,0,-1} // north wall
};
void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
{
int bestaxis;
vec_t dot,best;
int i;
best = 0;
bestaxis = 0;
for (i=0 ; i<6 ; i++)
{
dot = DotProduct (pln->normal, baseaxis[i*3]);
if (dot > best)
{
best = dot;
bestaxis = i;
}
}
VectorCopy (baseaxis[bestaxis*3+1], xv);
VectorCopy (baseaxis[bestaxis*3+2], yv);
}
int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin)
{
vec3_t vecs[2];
int sv, tv;
vec_t ang, sinv, cosv;
vec_t ns, nt;
texinfo_t tx, *tc;
int i, j, k;
float shift[2];
brush_texture_t anim;
int mt;
if (!bt->name[0])
return 0;
memset (&tx, 0, sizeof(tx));
strcpy (tx.texture, bt->name);
TextureAxisFromPlane(plane, vecs[0], vecs[1]);
shift[0] = DotProduct (origin, vecs[0]);
shift[1] = DotProduct (origin, vecs[1]);
if (!bt->scale[0])
bt->scale[0] = 1;
if (!bt->scale[1])
bt->scale[1] = 1;
// rotate axis
if (bt->rotate == 0)
{ sinv = 0 ; cosv = 1; }
else if (bt->rotate == 90)
{ sinv = 1 ; cosv = 0; }
else if (bt->rotate == 180)
{ sinv = 0 ; cosv = -1; }
else if (bt->rotate == 270)
{ sinv = -1 ; cosv = 0; }
else
{
ang = bt->rotate / 180 * Q_PI;
sinv = sin(ang);
cosv = cos(ang);
}
if (vecs[0][0])
sv = 0;
else if (vecs[0][1])
sv = 1;
else
sv = 2;
if (vecs[1][0])
tv = 0;
else if (vecs[1][1])
tv = 1;
else
tv = 2;
for (i=0 ; i<2 ; i++)
{
ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
vecs[i][sv] = ns;
vecs[i][tv] = nt;
}
for (i=0 ; i<2 ; i++)
for (j=0 ; j<3 ; j++)
tx.vecs[i][j] = vecs[i][j] / bt->scale[i];
tx.vecs[0][3] = bt->shift[0] + shift[0];
tx.vecs[1][3] = bt->shift[1] + shift[1];
tx.flags = bt->flags;
tx.value = bt->value;
//
// find the texinfo
//
tc = texinfo;
for (i=0 ; i<numtexinfo ; i++, tc++)
{
if (tc->flags != tx.flags)
continue;
if (tc->value != tx.value)
continue;
for (j=0 ; j<2 ; j++)
{
if (strcmp (tc->texture, tx.texture))
goto skip;
for (k=0 ; k<4 ; k++)
{
if (tc->vecs[j][k] != tx.vecs[j][k])
goto skip;
}
}
return i;
skip:;
}
*tc = tx;
numtexinfo++;
// load the next animation
mt = FindMiptex (bt->name);
if (textureref[mt].animname[0])
{
anim = *bt;
strcpy (anim.name, textureref[mt].animname);
tc->nexttexinfo = TexinfoForBrushTexture (plane, &anim, origin);
}
else
tc->nexttexinfo = -1;
return i;
}

219
bsp/qbsp3/tree.c Normal file
View File

@@ -0,0 +1,219 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
extern int c_nodes;
void RemovePortalFromNode (portal_t *portal, node_t *l);
node_t *NodeForPoint (node_t *node, vec3_t origin)
{
plane_t *plane;
vec_t d;
while (node->planenum != PLANENUM_LEAF)
{
plane = &mapplanes[node->planenum];
d = DotProduct (origin, plane->normal) - plane->dist;
if (d >= 0)
node = node->children[0];
else
node = node->children[1];
}
return node;
}
/*
=============
FreeTreePortals_r
=============
*/
void FreeTreePortals_r (node_t *node)
{
portal_t *p, *nextp;
int s;
// free children
if (node->planenum != PLANENUM_LEAF)
{
FreeTreePortals_r (node->children[0]);
FreeTreePortals_r (node->children[1]);
}
// free portals
for (p=node->portals ; p ; p=nextp)
{
s = (p->nodes[1] == node);
nextp = p->next[s];
RemovePortalFromNode (p, p->nodes[!s]);
FreePortal (p);
}
node->portals = NULL;
}
/*
=============
FreeTree_r
=============
*/
void FreeTree_r (node_t *node)
{
face_t *f, *nextf;
// free children
if (node->planenum != PLANENUM_LEAF)
{
FreeTree_r (node->children[0]);
FreeTree_r (node->children[1]);
}
// free bspbrushes
FreeBrushList (node->brushlist);
// free faces
for (f=node->faces ; f ; f=nextf)
{
nextf = f->next;
FreeFace (f);
}
// free the node
if (node->volume)
FreeBrush (node->volume);
if (numthreads == 1)
c_nodes--;
free (node);
}
/*
=============
FreeTree
=============
*/
void FreeTree (tree_t *tree)
{
FreeTreePortals_r (tree->headnode);
FreeTree_r (tree->headnode);
free (tree);
}
//===============================================================
void PrintTree_r (node_t *node, int depth)
{
int i;
plane_t *plane;
bspbrush_t *bb;
for (i=0 ; i<depth ; i++)
printf (" ");
if (node->planenum == PLANENUM_LEAF)
{
if (!node->brushlist)
printf ("NULL\n");
else
{
for (bb=node->brushlist ; bb ; bb=bb->next)
printf ("%i ", bb->original->brushnum);
printf ("\n");
}
return;
}
plane = &mapplanes[node->planenum];
printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
plane->normal[0], plane->normal[1], plane->normal[2],
plane->dist);
PrintTree_r (node->children[0], depth+1);
PrintTree_r (node->children[1], depth+1);
}
/*
=========================================================
NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
=========================================================
*/
int c_pruned;
/*
============
PruneNodes_r
============
*/
void PruneNodes_r (node_t *node)
{
bspbrush_t *b, *next;
if (node->planenum == PLANENUM_LEAF)
return;
PruneNodes_r (node->children[0]);
PruneNodes_r (node->children[1]);
if ( (node->children[0]->contents & CONTENTS_SOLID)
&& (node->children[1]->contents & CONTENTS_SOLID) )
{
if (node->faces)
Error ("node->faces seperating CONTENTS_SOLID");
if (node->children[0]->faces || node->children[1]->faces)
Error ("!node->faces with children");
// FIXME: free stuff
node->planenum = PLANENUM_LEAF;
node->contents = CONTENTS_SOLID;
node->detail_seperator = false;
if (node->brushlist)
Error ("PruneNodes: node->brushlist");
// combine brush lists
node->brushlist = node->children[1]->brushlist;
for (b=node->children[0]->brushlist ; b ; b=next)
{
next = b->next;
b->next = node->brushlist;
node->brushlist = b;
}
c_pruned++;
}
}
void PruneNodes (node_t *node)
{
qprintf ("--- PruneNodes ---\n");
c_pruned = 0;
PruneNodes_r (node);
qprintf ("%5i pruned nodes\n", c_pruned);
}
//===========================================================

590
bsp/qbsp3/writebsp.c Normal file
View File

@@ -0,0 +1,590 @@
/*
===========================================================================
Copyright (C) 1997-2006 Id Software, Inc.
This file is part of Quake 2 Tools source code.
Quake 2 Tools source code 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.
Quake 2 Tools source code 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 Quake 2 Tools source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int c_nofaces;
int c_facenodes;
/*
=========================================================
ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
=========================================================
*/
int planeused[MAX_MAP_PLANES];
/*
============
EmitPlanes
There is no oportunity to discard planes, because all of the original
brushes will be saved in the map.
============
*/
void EmitPlanes (void)
{
int i;
dplane_t *dp;
plane_t *mp;
int planetranslate[MAX_MAP_PLANES];
mp = mapplanes;
for (i=0 ; i<nummapplanes ; i++, mp++)
{
dp = &dplanes[numplanes];
planetranslate[i] = numplanes;
VectorCopy ( mp->normal, dp->normal);
dp->dist = mp->dist;
dp->type = mp->type;
numplanes++;
}
}
//========================================================
void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
{
int i;
int facenum;
while (f->merged)
f = f->merged;
if (f->split[0])
{
EmitMarkFace (leaf_p, f->split[0]);
EmitMarkFace (leaf_p, f->split[1]);
return;
}
facenum = f->outputnumber;
if (facenum == -1)
return; // degenerate face
if (facenum < 0 || facenum >= numfaces)
Error ("Bad leafface");
for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
if (dleaffaces[i] == facenum)
break; // merged out face
if (i == numleaffaces)
{
if (numleaffaces >= MAX_MAP_LEAFFACES)
Error ("MAX_MAP_LEAFFACES");
dleaffaces[numleaffaces] = facenum;
numleaffaces++;
}
}
/*
==================
EmitLeaf
==================
*/
void EmitLeaf (node_t *node)
{
dleaf_t *leaf_p;
portal_t *p;
int s;
face_t *f;
bspbrush_t *b;
int i;
int brushnum;
// emit a leaf
if (numleafs >= MAX_MAP_LEAFS)
Error ("MAX_MAP_LEAFS");
leaf_p = &dleafs[numleafs];
numleafs++;
leaf_p->contents = node->contents;
leaf_p->cluster = node->cluster;
leaf_p->area = node->area;
//
// write bounding box info
//
VectorCopy (node->mins, leaf_p->mins);
VectorCopy (node->maxs, leaf_p->maxs);
//
// write the leafbrushes
//
leaf_p->firstleafbrush = numleafbrushes;
for (b=node->brushlist ; b ; b=b->next)
{
if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
Error ("MAX_MAP_LEAFBRUSHES");
brushnum = b->original - mapbrushes;
for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
if (dleafbrushes[i] == brushnum)
break;
if (i == numleafbrushes)
{
dleafbrushes[numleafbrushes] = brushnum;
numleafbrushes++;
}
}
leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
//
// write the leaffaces
//
if (leaf_p->contents & CONTENTS_SOLID)
return; // no leaffaces in solids
leaf_p->firstleafface = numleaffaces;
for (p = node->portals ; p ; p = p->next[s])
{
s = (p->nodes[1] == node);
f = p->face[s];
if (!f)
continue; // not a visible portal
EmitMarkFace (leaf_p, f);
}
leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
}
/*
==================
EmitFace
==================
*/
void EmitFace (face_t *f)
{
dface_t *df;
int i;
int e;
f->outputnumber = -1;
if (f->numpoints < 3)
{
return; // degenerated
}
if (f->merged || f->split[0] || f->split[1])
{
return; // not a final face
}
// save output number so leaffaces can use
f->outputnumber = numfaces;
if (numfaces >= MAX_MAP_FACES)
Error ("numfaces == MAX_MAP_FACES");
df = &dfaces[numfaces];
numfaces++;
// planenum is used by qlight, but not quake
df->planenum = f->planenum & (~1);
df->side = f->planenum & 1;
df->firstedge = numsurfedges;
df->numedges = f->numpoints;
df->texinfo = f->texinfo;
for (i=0 ; i<f->numpoints ; i++)
{
// e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
if (numsurfedges >= MAX_MAP_SURFEDGES)
Error ("numsurfedges == MAX_MAP_SURFEDGES");
dsurfedges[numsurfedges] = e;
numsurfedges++;
}
}
/*
============
EmitDrawingNode_r
============
*/
int EmitDrawNode_r (node_t *node)
{
dnode_t *n;
face_t *f;
int i;
if (node->planenum == PLANENUM_LEAF)
{
EmitLeaf (node);
return -numleafs;
}
// emit a node
if (numnodes == MAX_MAP_NODES)
Error ("MAX_MAP_NODES");
n = &dnodes[numnodes];
numnodes++;
VectorCopy (node->mins, n->mins);
VectorCopy (node->maxs, n->maxs);
planeused[node->planenum]++;
planeused[node->planenum^1]++;
if (node->planenum & 1)
Error ("WriteDrawNodes_r: odd planenum");
n->planenum = node->planenum;
n->firstface = numfaces;
if (!node->faces)
c_nofaces++;
else
c_facenodes++;
for (f=node->faces ; f ; f=f->next)
EmitFace (f);
n->numfaces = numfaces - n->firstface;
//
// recursively output the other nodes
//
for (i=0 ; i<2 ; i++)
{
if (node->children[i]->planenum == PLANENUM_LEAF)
{
n->children[i] = -(numleafs + 1);
EmitLeaf (node->children[i]);
}
else
{
n->children[i] = numnodes;
EmitDrawNode_r (node->children[i]);
}
}
return n - dnodes;
}
//=========================================================
/*
============
WriteBSP
============
*/
void WriteBSP (node_t *headnode)
{
int oldfaces;
c_nofaces = 0;
c_facenodes = 0;
qprintf ("--- WriteBSP ---\n");
oldfaces = numfaces;
dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
EmitAreaPortals (headnode);
qprintf ("%5i nodes with faces\n", c_facenodes);
qprintf ("%5i nodes without faces\n", c_nofaces);
qprintf ("%5i faces\n", numfaces-oldfaces);
}
//===========================================================
/*
============
SetModelNumbers
============
*/
void SetModelNumbers (void)
{
int i;
int models;
char value[10];
models = 1;
for (i=1 ; i<num_entities ; i++)
{
if (entities[i].numbrushes)
{
sprintf (value, "*%i", models);
models++;
SetKeyValue (&entities[i], "model", value);
}
}
}
/*
============
SetLightStyles
============
*/
#define MAX_SWITCHED_LIGHTS 32
void SetLightStyles (void)
{
int stylenum;
char *t;
entity_t *e;
int i, j;
char value[10];
char lighttargets[MAX_SWITCHED_LIGHTS][64];
// any light that is controlled (has a targetname)
// must have a unique style number generated for it
stylenum = 0;
for (i=1 ; i<num_entities ; i++)
{
e = &entities[i];
t = ValueForKey (e, "classname");
if (Q_strncasecmp (t, "light", 5))
continue;
t = ValueForKey (e, "targetname");
if (!t[0])
continue;
// find this targetname
for (j=0 ; j<stylenum ; j++)
if (!strcmp (lighttargets[j], t))
break;
if (j == stylenum)
{
if (stylenum == MAX_SWITCHED_LIGHTS)
Error ("stylenum == MAX_SWITCHED_LIGHTS");
strcpy (lighttargets[j], t);
stylenum++;
}
sprintf (value, "%i", 32 + j);
SetKeyValue (e, "style", value);
}
}
//===========================================================
/*
============
EmitBrushes
============
*/
void EmitBrushes (void)
{
int i, j, bnum, s, x;
dbrush_t *db;
mapbrush_t *b;
dbrushside_t *cp;
vec3_t normal;
vec_t dist;
int planenum;
numbrushsides = 0;
numbrushes = nummapbrushes;
for (bnum=0 ; bnum<nummapbrushes ; bnum++)
{
b = &mapbrushes[bnum];
db = &dbrushes[bnum];
db->contents = b->contents;
db->firstside = numbrushsides;
db->numsides = b->numsides;
for (j=0 ; j<b->numsides ; j++)
{
if (numbrushsides == MAX_MAP_BRUSHSIDES)
Error ("MAX_MAP_BRUSHSIDES");
cp = &dbrushsides[numbrushsides];
numbrushsides++;
cp->planenum = b->original_sides[j].planenum;
cp->texinfo = b->original_sides[j].texinfo;
}
// add any axis planes not contained in the brush to bevel off corners
for (x=0 ; x<3 ; x++)
for (s=-1 ; s<=1 ; s+=2)
{
// add the plane
VectorCopy (vec3_origin, normal);
normal[x] = s;
if (s == -1)
dist = -b->mins[x];
else
dist = b->maxs[x];
planenum = FindFloatPlane (normal, dist);
for (i=0 ; i<b->numsides ; i++)
if (b->original_sides[i].planenum == planenum)
break;
if (i == b->numsides)
{
if (numbrushsides >= MAX_MAP_BRUSHSIDES)
Error ("MAX_MAP_BRUSHSIDES");
dbrushsides[numbrushsides].planenum = planenum;
dbrushsides[numbrushsides].texinfo =
dbrushsides[numbrushsides-1].texinfo;
numbrushsides++;
db->numsides++;
}
}
}
}
//===========================================================
/*
==================
BeginBSPFile
==================
*/
void BeginBSPFile (void)
{
// these values may actually be initialized
// if the file existed when loaded, so clear them explicitly
nummodels = 0;
numfaces = 0;
numnodes = 0;
numbrushsides = 0;
numvertexes = 0;
numleaffaces = 0;
numleafbrushes = 0;
numsurfedges = 0;
// edge 0 is not used, because 0 can't be negated
numedges = 1;
// leave vertex 0 as an error
numvertexes = 1;
// leave leaf 0 as an error
numleafs = 1;
dleafs[0].contents = CONTENTS_SOLID;
}
/*
============
EndBSPFile
============
*/
void EndBSPFile (void)
{
char path[1024];
int len;
byte *buf;
EmitBrushes ();
EmitPlanes ();
UnparseEntities ();
// load the pop
#if 0
sprintf (path, "%s/pics/pop.lmp", gamedir);
len = LoadFile (path, &buf);
memcpy (dpop, buf, sizeof(dpop));
free (buf);
#endif
// write the map
sprintf (path, "%s.bsp", source);
printf ("Writing %s\n", path);
WriteBSPFile (path);
}
/*
==================
BeginModel
==================
*/
int firstmodleaf;
extern int firstmodeledge;
extern int firstmodelface;
void BeginModel (void)
{
dmodel_t *mod;
int start, end;
mapbrush_t *b;
int j;
entity_t *e;
vec3_t mins, maxs;
if (nummodels == MAX_MAP_MODELS)
Error ("MAX_MAP_MODELS");
mod = &dmodels[nummodels];
mod->firstface = numfaces;
firstmodleaf = numleafs;
firstmodeledge = numedges;
firstmodelface = numfaces;
//
// bound the brushes
//
e = &entities[entity_num];
start = e->firstbrush;
end = start + e->numbrushes;
ClearBounds (mins, maxs);
for (j=start ; j<end ; j++)
{
b = &mapbrushes[j];
if (!b->numsides)
continue; // not a real brush (origin brush)
AddPointToBounds (b->mins, mins, maxs);
AddPointToBounds (b->maxs, mins, maxs);
}
VectorCopy (mins, mod->mins);
VectorCopy (maxs, mod->maxs);
}
/*
==================
EndModel
==================
*/
void EndModel (void)
{
dmodel_t *mod;
mod = &dmodels[nummodels];
mod->numfaces = numfaces - mod->firstface;
nummodels++;
}