vhlt/hlbsp/qbsp.cpp

2319 lines
62 KiB
C++
Raw Normal View History

2016-09-20 21:07:53 +00:00
/*
BINARY SPACE PARTITION -aka- B S P
Code based on original code from Valve Software,
Modified by Sean "Zoner" Cavanaugh (seanc@gearboxsoftware.com) with permission.
Modified by Tony "Merl" Moore (merlinis@bigpond.net.au) [AJM]
*/
#ifdef SYSTEM_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "bsp5.h"
/*
NOTES
*/
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
vec3_t g_hull_size[NUM_HULLS][2] =
{
{// 0x0x0
{0, 0, 0}, {0, 0, 0}
}
,
{// 32x32x72
{-16, -16, -36}, {16, 16, 36}
}
,
{// 64x64x64
{-32, -32, -32}, {32, 32, 32}
}
,
{// 32x32x36
{-16, -16, -18}, {16, 16, 18}
}
};
#endif
static FILE* polyfiles[NUM_HULLS];
#ifdef ZHLT_DETAILBRUSH
static FILE* brushfiles[NUM_HULLS];
#endif
int g_hullnum = 0;
static face_t* validfaces[MAX_INTERNAL_MAP_PLANES];
char g_bspfilename[_MAX_PATH];
char g_pointfilename[_MAX_PATH];
char g_linefilename[_MAX_PATH];
char g_portfilename[_MAX_PATH];
#ifdef ZHLT_64BIT_FIX
char g_extentfilename[_MAX_PATH];
#endif
// command line flags
bool g_noopt = DEFAULT_NOOPT; // don't optimize BSP on write
#ifdef HLBSP_MERGECLIPNODE
bool g_noclipnodemerge = DEFAULT_NOCLIPNODEMERGE;
#endif
bool g_nofill = DEFAULT_NOFILL; // dont fill "-nofill"
#ifdef HLBSP_FILL
bool g_noinsidefill = DEFAULT_NOINSIDEFILL;
#endif
bool g_notjunc = DEFAULT_NOTJUNC;
#ifdef HLBSP_BRINKHACK
bool g_nobrink = DEFAULT_NOBRINK;
#endif
bool g_noclip = DEFAULT_NOCLIP; // no clipping hull "-noclip"
bool g_chart = DEFAULT_CHART; // print out chart? "-chart"
bool g_estimate = DEFAULT_ESTIMATE; // estimate mode "-estimate"
bool g_info = DEFAULT_INFO;
bool g_bLeakOnly = DEFAULT_LEAKONLY; // leakonly mode "-leakonly"
bool g_bLeaked = false;
int g_subdivide_size = DEFAULT_SUBDIVIDE_SIZE;
#ifdef ZHLT_NULLTEX // AJM
bool g_bUseNullTex = DEFAULT_NULLTEX; // "-nonulltex"
#endif
#ifdef ZHLT_DETAIL // AJM
bool g_bDetailBrushes = DEFAULT_DETAIL; // "-nodetail"
#endif
#ifdef ZHLT_PROGRESSFILE // AJM
char* g_progressfile = DEFAULT_PROGRESSFILE; // "-progressfile path"
#endif
#ifdef HLBSP_REMOVEHULL2
bool g_nohull2 = false;
#endif
#ifdef HLBSP_VIEWPORTAL
bool g_viewportal = false;
#endif
#ifdef HLCSG_HLBSP_DOUBLEPLANE
dplane_t g_dplanes[MAX_INTERNAL_MAP_PLANES];
#endif
#ifdef ZHLT_INFO_COMPILE_PARAMETERS// AJM
// =====================================================================================
// GetParamsFromEnt
// this function is called from parseentity when it encounters the
// info_compile_parameters entity. each tool should have its own version of this
// to handle its own specific settings.
// =====================================================================================
void GetParamsFromEnt(entity_t* mapent)
{
int iTmp;
Log("\nCompile Settings detected from info_compile_parameters entity\n");
// verbose(choices) : "Verbose compile messages" : 0 = [ 0 : "Off" 1 : "On" ]
iTmp = IntForKey(mapent, "verbose");
if (iTmp == 1)
{
g_verbose = true;
}
else if (iTmp == 0)
{
g_verbose = false;
}
Log("%30s [ %-9s ]\n", "Compile Option", "setting");
Log("%30s [ %-9s ]\n", "Verbose Compile Messages", g_verbose ? "on" : "off");
// estimate(choices) :"Estimate Compile Times?" : 0 = [ 0: "Yes" 1: "No" ]
if (IntForKey(mapent, "estimate"))
{
g_estimate = true;
}
else
{
g_estimate = false;
}
Log("%30s [ %-9s ]\n", "Estimate Compile Times", g_estimate ? "on" : "off");
// priority(choices) : "Priority Level" : 0 = [ 0 : "Normal" 1 : "High" -1 : "Low" ]
if (!strcmp(ValueForKey(mapent, "priority"), "1"))
{
g_threadpriority = eThreadPriorityHigh;
Log("%30s [ %-9s ]\n", "Thread Priority", "high");
}
else if (!strcmp(ValueForKey(mapent, "priority"), "-1"))
{
g_threadpriority = eThreadPriorityLow;
Log("%30s [ %-9s ]\n", "Thread Priority", "low");
}
/*
hlbsp(choices) : "HLBSP" : 0 =
[
0 : "Off"
1 : "Normal"
2 : "Leakonly"
]
*/
iTmp = IntForKey(mapent, "hlbsp");
if (iTmp == 0)
{
Fatal(assume_TOOL_CANCEL,
"%s flag was not checked in info_compile_parameters entity, execution of %s cancelled", g_Program, g_Program);
CheckFatal();
}
else if (iTmp == 1)
{
g_bLeakOnly = false;
}
else if (iTmp == 2)
{
g_bLeakOnly = true;
}
Log("%30s [ %-9s ]\n", "Leakonly Mode", g_bLeakOnly ? "on" : "off");
iTmp = IntForKey(mapent, "noopt");
if(iTmp == 0)
{
g_noopt = false;
}
else
{
g_noopt = true;
}
/*
nocliphull(choices) : "Generate clipping hulls" : 0 =
[
0 : "Yes"
1 : "No"
]
*/
iTmp = IntForKey(mapent, "nocliphull");
if (iTmp == 0)
{
g_noclip = false;
}
else if (iTmp == 1)
{
g_noclip = true;
}
Log("%30s [ %-9s ]\n", "Clipping Hull Generation", g_noclip ? "off" : "on");
//////////////////
Verbose("\n");
}
#endif
// =====================================================================================
// Extract File stuff (ExtractFile | ExtractFilePath | ExtractFileBase)
//
// With VS 2005 - and the 64 bit build, i had to pull 3 classes over from
// cmdlib.cpp even with the proper includes to get rid of the lnk2001 error
//
// amckern - amckern@yahoo.com
// =====================================================================================
// Code Deleted. --vluzacn
// =====================================================================================
// NewFaceFromFace
// Duplicates the non point information of a face, used by SplitFace and MergeFace.
// =====================================================================================
face_t* NewFaceFromFace(const face_t* const in)
{
face_t* newf;
newf = AllocFace();
newf->planenum = in->planenum;
newf->texturenum = in->texturenum;
newf->original = in->original;
newf->contents = in->contents;
#ifdef HLBSP_NewFaceFromFace_FIX
newf->facestyle = in->facestyle;
#endif
#ifdef ZHLT_DETAILBRUSH
newf->detaillevel = in->detaillevel;
#endif
return newf;
}
// =====================================================================================
// SplitFaceTmp
// blah
// =====================================================================================
static void SplitFaceTmp(face_t* in, const dplane_t* const split, face_t** front, face_t** back)
{
vec_t dists[MAXEDGES + 1];
int sides[MAXEDGES + 1];
int counts[3];
vec_t dot;
int i;
int j;
face_t* newf;
face_t* new2;
vec_t* p1;
vec_t* p2;
vec3_t mid;
if (in->numpoints < 0)
{
Error("SplitFace: freed face");
}
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i = 0; i < in->numpoints; i++)
{
dot = DotProduct(in->pts[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > ON_EPSILON)
{
sides[i] = SIDE_FRONT;
}
else if (dot < -ON_EPSILON)
{
sides[i] = SIDE_BACK;
}
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
#ifdef HLBSP_SPLITFACE_FIX
if (!counts[0] && !counts[1])
{
if (in->detaillevel)
{
// put front face in front node, and back face in back node.
const dplane_t *faceplane = &g_dplanes[in->planenum];
if (DotProduct (faceplane->normal, split->normal) > NORMAL_EPSILON) // usually near 1.0 or -1.0
{
*front = in;
*back = NULL;
}
else
{
*front = NULL;
*back = in;
}
}
else
{
// not func_detail. front face and back face need to pair.
vec_t sum = 0.0;
for (i = 0; i < in->numpoints; i++)
{
dot = DotProduct(in->pts[i], split->normal);
dot -= split->dist;
sum += dot;
}
if (sum > NORMAL_EPSILON)
{
*front = in;
*back = NULL;
}
else
{
*front = NULL;
*back = in;
}
}
return;
}
#endif
if (!counts[0])
{
*front = NULL;
*back = in;
return;
}
if (!counts[1])
{
*front = in;
*back = NULL;
return;
}
*back = newf = NewFaceFromFace(in);
*front = new2 = NewFaceFromFace(in);
// distribute the points and generate splits
for (i = 0; i < in->numpoints; i++)
{
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
{
Error("SplitFace: numpoints > MAXEDGES");
}
p1 = in->pts[i];
if (sides[i] == SIDE_ON)
{
VectorCopy(p1, newf->pts[newf->numpoints]);
newf->numpoints++;
VectorCopy(p1, new2->pts[new2->numpoints]);
new2->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy(p1, new2->pts[new2->numpoints]);
new2->numpoints++;
}
else
{
VectorCopy(p1, newf->pts[newf->numpoints]);
newf->numpoints++;
}
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
{
continue;
}
// generate a split point
p2 = in->pts[(i + 1) % in->numpoints];
dot = dists[i] / (dists[i] - dists[i + 1]);
for (j = 0; j < 3; j++)
{ // avoid round off error when possible
if (split->normal[j] == 1)
{
mid[j] = split->dist;
}
else if (split->normal[j] == -1)
{
mid[j] = -split->dist;
}
else
{
mid[j] = p1[j] + dot * (p2[j] - p1[j]);
}
}
VectorCopy(mid, newf->pts[newf->numpoints]);
newf->numpoints++;
VectorCopy(mid, new2->pts[new2->numpoints]);
new2->numpoints++;
}
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
{
Error("SplitFace: numpoints > MAXEDGES");
}
#ifdef HLBSP_REMOVECOLINEARPOINTS
{
Winding *wd = new Winding (newf->numpoints);
int x;
for (x = 0; x < newf->numpoints; x++)
{
VectorCopy (newf->pts[x], wd->m_Points[x]);
}
wd->RemoveColinearPoints ();
newf->numpoints = wd->m_NumPoints;
for (x = 0; x < newf->numpoints; x++)
{
VectorCopy (wd->m_Points[x], newf->pts[x]);
}
delete wd;
if (newf->numpoints == 0)
{
*back = NULL;
}
}
{
Winding *wd = new Winding (new2->numpoints);
int x;
for (x = 0; x < new2->numpoints; x++)
{
VectorCopy (new2->pts[x], wd->m_Points[x]);
}
wd->RemoveColinearPoints ();
new2->numpoints = wd->m_NumPoints;
for (x = 0; x < new2->numpoints; x++)
{
VectorCopy (wd->m_Points[x], new2->pts[x]);
}
delete wd;
if (new2->numpoints == 0)
{
*front = NULL;
}
}
#endif
}
// =====================================================================================
// SplitFace
// blah
// =====================================================================================
void SplitFace(face_t* in, const dplane_t* const split, face_t** front, face_t** back)
{
SplitFaceTmp(in, split, front, back);
// free the original face now that is is represented by the fragments
if (*front && *back)
{
FreeFace(in);
}
}
// =====================================================================================
// AllocFace
// =====================================================================================
face_t* AllocFace()
{
face_t* f;
f = (face_t*)malloc(sizeof(face_t));
memset(f, 0, sizeof(face_t));
f->planenum = -1;
return f;
}
// =====================================================================================
// FreeFace
// =====================================================================================
void FreeFace(face_t* f)
{
free(f);
}
// =====================================================================================
// AllocSurface
// =====================================================================================
surface_t* AllocSurface()
{
surface_t* s;
s = (surface_t*)malloc(sizeof(surface_t));
memset(s, 0, sizeof(surface_t));
return s;
}
// =====================================================================================
// FreeSurface
// =====================================================================================
void FreeSurface(surface_t* s)
{
free(s);
}
// =====================================================================================
// AllocPortal
// =====================================================================================
portal_t* AllocPortal()
{
portal_t* p;
p = (portal_t*)malloc(sizeof(portal_t));
memset(p, 0, sizeof(portal_t));
return p;
}
// =====================================================================================
// FreePortal
// =====================================================================================
void FreePortal(portal_t* p) // consider: inline
{
free(p);
}
#ifdef ZHLT_DETAILBRUSH
side_t *AllocSide ()
{
side_t *s;
s = (side_t *)malloc (sizeof (side_t));
memset (s, 0, sizeof (side_t));
return s;
}
void FreeSide (side_t *s)
{
if (s->w)
{
delete s->w;
}
free (s);
return;
}
side_t *NewSideFromSide (const side_t *s)
{
side_t *news;
news = AllocSide ();
news->plane = s->plane;
news->w = new Winding (*s->w);
return news;
}
brush_t *AllocBrush ()
{
brush_t *b;
b = (brush_t *)malloc (sizeof (brush_t));
memset (b, 0, sizeof (brush_t));
return b;
}
void FreeBrush (brush_t *b)
{
if (b->sides)
{
side_t *s, *next;
for (s = b->sides; s; s = next)
{
next = s->next;
FreeSide (s);
}
}
free (b);
return;
}
brush_t *NewBrushFromBrush (const brush_t *b)
{
brush_t *newb;
newb = AllocBrush ();
side_t *s, **pnews;
for (s = b->sides, pnews = &newb->sides; s; s = s->next, pnews = &(*pnews)->next)
{
*pnews = NewSideFromSide (s);
}
return newb;
}
void ClipBrush (brush_t **b, const dplane_t *split, vec_t epsilon)
{
side_t *s, **pnext;
Winding *w;
for (pnext = &(*b)->sides, s = *pnext; s; s = *pnext)
{
if (s->w->Clip (*split, false, epsilon))
{
pnext = &s->next;
}
else
{
*pnext = s->next;
FreeSide (s);
}
}
if (!(*b)->sides)
{ // empty brush
FreeBrush (*b);
*b = NULL;
return;
}
w = new Winding (*split);
for (s = (*b)->sides; s; s = s->next)
{
if (!w->Clip (s->plane, false, epsilon))
{
break;
}
}
if (w->m_NumPoints == 0)
{
delete w;
}
else
{
s = AllocSide ();
s->plane = *split;
s->w = w;
s->next = (*b)->sides;
(*b)->sides = s;
}
return;
}
void SplitBrush (brush_t *in, const dplane_t *split, brush_t **front, brush_t **back)
// 'in' will be freed
{
in->next = NULL;
bool onfront;
bool onback;
onfront = false;
onback = false;
side_t *s;
for (s = in->sides; s; s = s->next)
{
switch (s->w->WindingOnPlaneSide (split->normal, split->dist, 2 * ON_EPSILON))
{
case SIDE_CROSS:
onfront = true;
onback = true;
break;
case SIDE_FRONT:
onfront = true;
break;
case SIDE_BACK:
onback = true;
break;
case SIDE_ON:
break;
}
if (onfront && onback)
break;
}
if (!onfront && !onback)
{
FreeBrush (in);
*front = NULL;
*back = NULL;
return;
}
if (!onfront)
{
*front = NULL;
*back = in;
return;
}
if (!onback)
{
*front = in;
*back = NULL;
return;
}
*front = in;
*back = NewBrushFromBrush (in);
dplane_t frontclip = *split;
dplane_t backclip = *split;
VectorSubtract (vec3_origin, backclip.normal, backclip.normal);
backclip.dist = -backclip.dist;
ClipBrush (front, &frontclip, NORMAL_EPSILON);
ClipBrush (back, &backclip, NORMAL_EPSILON);
return;
}
#ifdef HLBSP_DETAILBRUSH_CULL
brush_t *BrushFromBox (const vec3_t mins, const vec3_t maxs)
{
brush_t *b = AllocBrush ();
dplane_t planes[6];
for (int k = 0; k < 3; k++)
{
VectorFill (planes[k].normal, 0.0);
planes[k].normal[k] = 1.0;
planes[k].dist = mins[k];
VectorFill (planes[k+3].normal, 0.0);
planes[k+3].normal[k] = -1.0;
planes[k+3].dist = -maxs[k];
}
b->sides = AllocSide ();
b->sides->plane = planes[0];
b->sides->w = new Winding (planes[0]);
for (int k = 1; k < 6; k++)
{
ClipBrush (&b, &planes[k], NORMAL_EPSILON);
if (b == NULL)
{
break;
}
}
return b;
}
void CalcBrushBounds (const brush_t *b, vec3_t &mins, vec3_t &maxs)
{
VectorFill (mins, BOGUS_RANGE);
VectorFill (maxs, -BOGUS_RANGE);
for (side_t *s = b->sides; s; s = s->next)
{
vec3_t windingmins, windingmaxs;
s->w->getBounds (windingmins, windingmaxs);
VectorCompareMinimum (mins, windingmins, mins);
VectorCompareMaximum (maxs, windingmaxs, maxs);
}
}
#endif
#endif
// =====================================================================================
// AllocNode
// blah
// =====================================================================================
node_t* AllocNode()
{
node_t* n;
n = (node_t*)malloc(sizeof(node_t));
memset(n, 0, sizeof(node_t));
return n;
}
// =====================================================================================
// AddPointToBounds
// =====================================================================================
void AddPointToBounds(const vec3_t v, vec3_t mins, vec3_t maxs)
{
int i;
vec_t val;
for (i = 0; i < 3; i++)
{
val = v[i];
if (val < mins[i])
{
mins[i] = val;
}
if (val > maxs[i])
{
maxs[i] = val;
}
}
}
// =====================================================================================
// AddFaceToBounds
// =====================================================================================
static void AddFaceToBounds(const face_t* const f, vec3_t mins, vec3_t maxs)
{
int i;
for (i = 0; i < f->numpoints; i++)
{
AddPointToBounds(f->pts[i], mins, maxs);
}
}
// =====================================================================================
// ClearBounds
// =====================================================================================
static void ClearBounds(vec3_t mins, vec3_t maxs)
{
mins[0] = mins[1] = mins[2] = 99999;
maxs[0] = maxs[1] = maxs[2] = -99999;
}
// =====================================================================================
// SurflistFromValidFaces
// blah
// =====================================================================================
static surfchain_t* SurflistFromValidFaces()
{
surface_t* n;
int i;
face_t* f;
face_t* next;
surfchain_t* sc;
sc = (surfchain_t*)malloc(sizeof(*sc));
ClearBounds(sc->mins, sc->maxs);
sc->surfaces = NULL;
// grab planes from both sides
for (i = 0; i < g_numplanes; i += 2)
{
if (!validfaces[i] && !validfaces[i + 1])
{
continue;
}
n = AllocSurface();
n->next = sc->surfaces;
sc->surfaces = n;
ClearBounds(n->mins, n->maxs);
#ifdef ZHLT_DETAILBRUSH
n->detaillevel = -1;
#endif
n->planenum = i;
n->faces = NULL;
for (f = validfaces[i]; f; f = next)
{
next = f->next;
f->next = n->faces;
n->faces = f;
AddFaceToBounds(f, n->mins, n->maxs);
#ifdef ZHLT_DETAILBRUSH
if (n->detaillevel == -1 || f->detaillevel < n->detaillevel)
{
n->detaillevel = f->detaillevel;
}
#endif
}
for (f = validfaces[i + 1]; f; f = next)
{
next = f->next;
f->next = n->faces;
n->faces = f;
AddFaceToBounds(f, n->mins, n->maxs);
#ifdef ZHLT_DETAILBRUSH
if (n->detaillevel == -1 || f->detaillevel < n->detaillevel)
{
n->detaillevel = f->detaillevel;
}
#endif
}
AddPointToBounds(n->mins, sc->mins, sc->maxs);
AddPointToBounds(n->maxs, sc->mins, sc->maxs);
validfaces[i] = NULL;
validfaces[i + 1] = NULL;
}
// merge all possible polygons
MergeAll(sc->surfaces);
return sc;
}
#ifdef ZHLT_NULLTEX// AJM
// =====================================================================================
// CheckFaceForNull
// Returns true if the passed face is facetype null
// =====================================================================================
bool CheckFaceForNull(const face_t* const f)
{
#ifdef HLBSP_SKY_SOLID
if (f->contents == CONTENTS_SKY)
{
const char *name = GetTextureByNumber (f->texturenum);
if (strncasecmp(name, "sky", 3)) // for env_rain
return true;
}
#endif
// null faces are only of facetype face_null if we are using null texture stripping
if (g_bUseNullTex)
{
#ifdef HLCSG_HLBSP_VOIDTEXINFO
const char *name = GetTextureByNumber (f->texturenum);
if (!strncasecmp(name, "null", 4))
return true;
return false;
#else
texinfo_t* info;
miptex_t* miptex;
int ofs;
info = &g_texinfo[f->texturenum];
ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex];
miptex = (miptex_t*)(&g_dtexdata[ofs]);
if (!strcasecmp(miptex->name, "null"))
return true;
#ifdef HLCSG_CUSTOMHULL
else if (!strncasecmp(miptex->name, "null", 4))
return true;
#else
else
return false;
#endif
#endif
}
else // otherwise, under normal cases, null textured faces should be facetype face_normal
{
return false;
}
}
// =====================================================================================
//Cpt_Andrew - UTSky Check
// =====================================================================================
bool CheckFaceForEnv_Sky(const face_t* const f)
{
#ifdef HLCSG_HLBSP_VOIDTEXINFO
const char *name = GetTextureByNumber (f->texturenum);
if (!strncasecmp (name, "env_sky", 7))
return true;
return false;
#else
texinfo_t* info;
miptex_t* miptex;
int ofs;
info = &g_texinfo[f->texturenum];
ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex];
miptex = (miptex_t*)(&g_dtexdata[ofs]);
if (!strcasecmp(miptex->name, "env_sky"))
return true;
else
return false;
#endif
}
// =====================================================================================
#endif
#ifdef ZHLT_DETAIL
// =====================================================================================
// CheckFaceForDetail
// Returns true if the passed face is part of a detail brush
// =====================================================================================
bool CheckFaceForDetail(const face_t* const f)
{
if (f->contents == CONTENTS_DETAIL)
{
//Log("CheckFaceForDetail:: got a detail face");
return true;
}
return false;
}
#endif
// =====================================================================================
// CheckFaceForHint
// Returns true if the passed face is facetype hint
// =====================================================================================
bool CheckFaceForHint(const face_t* const f)
{
#ifdef HLCSG_HLBSP_VOIDTEXINFO
const char *name = GetTextureByNumber (f->texturenum);
if (!strncasecmp (name, "hint", 4))
return true;
return false;
#else
texinfo_t* info;
miptex_t* miptex;
int ofs;
info = &g_texinfo[f->texturenum];
ofs = ((dmiptexlump_t *)g_dtexdata)->dataofs[info->miptex];
miptex = (miptex_t *)(&g_dtexdata[ofs]);
if (!strcasecmp(miptex->name, "hint"))
{
return true;
}
else
{
return false;
}
#endif
}
// =====================================================================================
// CheckFaceForSkipt
// Returns true if the passed face is facetype skip
// =====================================================================================
bool CheckFaceForSkip(const face_t* const f)
{
#ifdef HLCSG_HLBSP_VOIDTEXINFO
const char *name = GetTextureByNumber (f->texturenum);
if (!strncasecmp (name, "skip", 4))
return true;
return false;
#else
texinfo_t* info;
miptex_t* miptex;
int ofs;
info = &g_texinfo[f->texturenum];
ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex];
miptex = (miptex_t*)(&g_dtexdata[ofs]);
if (!strcasecmp(miptex->name, "skip"))
{
return true;
}
else
{
return false;
}
#endif
}
#ifdef HLCSG_HLBSP_SOLIDHINT
bool CheckFaceForDiscardable (const face_t *f)
{
const char *name = GetTextureByNumber (f->texturenum);
if (!strncasecmp (name, "SOLIDHINT", 9))
return true;
return false;
}
#endif
// =====================================================================================
// SetFaceType
// =====================================================================================
static facestyle_e SetFaceType(face_t* f)
{
if (CheckFaceForHint(f))
{
f->facestyle = face_hint;
}
else if (CheckFaceForSkip(f))
{
f->facestyle = face_skip;
}
#ifdef ZHLT_NULLTEX // AJM
else if (CheckFaceForNull(f))
{
f->facestyle = face_null;
}
#endif
#ifdef HLCSG_HLBSP_SOLIDHINT
else if (CheckFaceForDiscardable (f))
{
f->facestyle = face_discardable;
}
#endif
// =====================================================================================
//Cpt_Andrew - Env_Sky Check
// =====================================================================================
//else if (CheckFaceForUTSky(f))
else if (CheckFaceForEnv_Sky(f))
{
f->facestyle = face_null;
}
// =====================================================================================
#ifdef ZHLT_DETAIL
else if (CheckFaceForDetail(f))
{
//Log("SetFaceType::detail face\n");
f->facestyle = face_detail;
}
#endif
else
{
f->facestyle = face_normal;
}
return f->facestyle;
}
// =====================================================================================
// ReadSurfs
// =====================================================================================
static surfchain_t* ReadSurfs(FILE* file)
{
int r;
#ifdef ZHLT_DETAILBRUSH
int detaillevel;
#endif
int planenum, g_texinfo, contents, numpoints;
face_t* f;
int i;
double v[3];
int line = 0;
#ifdef HLCSG_HLBSP_DOUBLEPLANE
double inaccuracy, inaccuracy_count = 0.0, inaccuracy_total = 0.0, inaccuracy_max = 0.0;
#endif
// read in the polygons
while (1)
{
#ifdef HLBSP_REMOVEHULL2
if (file == polyfiles[2] && g_nohull2)
break;
#endif
line++;
#ifdef ZHLT_DETAILBRUSH
r = fscanf(file, "%i %i %i %i %i\n", &detaillevel, &planenum, &g_texinfo, &contents, &numpoints);
#else
r = fscanf(file, "%i %i %i %i\n", &planenum, &g_texinfo, &contents, &numpoints);
#endif
if (r == 0 || r == -1)
{
return NULL;
}
if (planenum == -1) // end of model
{
#ifdef HLCSG_HLBSP_DOUBLEPLANE
Developer (DEVELOPER_LEVEL_MEGASPAM, "inaccuracy: average %.8f max %.8f\n", inaccuracy_total / inaccuracy_count, inaccuracy_max);
#endif
break;
}
#ifdef ZHLT_DETAILBRUSH
if (r != 5)
#else
if (r != 4)
#endif
{
Error("ReadSurfs (line %i): scanf failure", line);
}
if (numpoints > MAXPOINTS)
{
Error("ReadSurfs (line %i): %i > MAXPOINTS\nThis is caused by a face with too many verticies (typically found on end-caps of high-poly cylinders)\n", line, numpoints);
}
if (planenum > g_numplanes)
{
Error("ReadSurfs (line %i): %i > g_numplanes\n", line, planenum);
}
if (g_texinfo > g_numtexinfo)
{
Error("ReadSurfs (line %i): %i > g_numtexinfo", line, g_texinfo);
}
#ifdef ZHLT_DETAILBRUSH
if (detaillevel < 0)
{
Error("ReadSurfs (line %i): detaillevel %i < 0", line, detaillevel);
}
#endif
if (!strcasecmp(GetTextureByNumber(g_texinfo), "skip"))
{
Verbose("ReadSurfs (line %i): skipping a surface", line);
for (i = 0; i < numpoints; i++)
{
line++;
//Verbose("skipping line %d", line);
r = fscanf(file, "%lf %lf %lf\n", &v[0], &v[1], &v[2]);
if (r != 3)
{
Error("::ReadSurfs (face_skip), fscanf of points failed at line %i", line);
}
}
fscanf(file, "\n");
continue;
}
f = AllocFace();
#ifdef ZHLT_DETAILBRUSH
f->detaillevel = detaillevel;
#endif
f->planenum = planenum;
f->texturenum = g_texinfo;
f->contents = contents;
f->numpoints = numpoints;
f->next = validfaces[planenum];
validfaces[planenum] = f;
SetFaceType(f);
for (i = 0; i < f->numpoints; i++)
{
line++;
r = fscanf(file, "%lf %lf %lf\n", &v[0], &v[1], &v[2]);
if (r != 3)
{
Error("::ReadSurfs (face_normal), fscanf of points failed at line %i", line);
}
VectorCopy(v, f->pts[i]);
#ifdef HLCSG_HLBSP_DOUBLEPLANE
if (DEVELOPER_LEVEL_MEGASPAM <= g_developer)
{
const dplane_t *plane = &g_dplanes[f->planenum];
inaccuracy = fabs (DotProduct (f->pts[i], plane->normal) - plane->dist);
inaccuracy_count++;
inaccuracy_total += inaccuracy;
inaccuracy_max = qmax (inaccuracy, inaccuracy_max);
}
#endif
}
fscanf(file, "\n");
}
return SurflistFromValidFaces();
}
#ifdef ZHLT_DETAILBRUSH
static brush_t *ReadBrushes (FILE *file)
{
brush_t *brushes = NULL;
while (1)
{
#ifdef HLBSP_REMOVEHULL2
if (file == brushfiles[2] && g_nohull2)
break;
#endif
int r;
int brushinfo;
r = fscanf (file, "%i\n", &brushinfo);
if (r == 0 || r == -1)
{
if (brushes == NULL)
{
Error ("ReadBrushes: no more models");
}
else
{
Error ("ReadBrushes: file end");
}
}
if (brushinfo == -1)
{
break;
}
brush_t *b;
b = AllocBrush ();
b->next = brushes;
brushes = b;
side_t **psn;
psn = &b->sides;
while (1)
{
int planenum;
int numpoints;
r = fscanf (file, "%i %u\n", &planenum, &numpoints);
if (r != 2)
{
Error ("ReadBrushes: get side failed");
}
if (planenum == -1)
{
break;
}
side_t *s;
s = AllocSide ();
s->plane = g_dplanes[planenum ^ 1];
s->w = new Winding (numpoints);
int x;
for (x = 0; x < numpoints; x++)
{
double v[3];
r = fscanf (file, "%lf %lf %lf\n", &v[0], &v[1], &v[2]);
if (r != 3)
{
Error ("ReadBrushes: get point failed");
}
VectorCopy (v, s->w->m_Points[numpoints - 1 - x]);
}
s->next = NULL;
*psn = s;
psn = &s->next;
}
}
return brushes;
}
#endif
// =====================================================================================
// ProcessModel
// =====================================================================================
static bool ProcessModel()
{
surfchain_t* surfs;
#ifdef ZHLT_DETAILBRUSH
brush_t *detailbrushes;
#endif
node_t* nodes;
dmodel_t* model;
int startleafs;
surfs = ReadSurfs(polyfiles[0]);
if (!surfs)
return false; // all models are done
#ifdef ZHLT_DETAILBRUSH
detailbrushes = ReadBrushes (brushfiles[0]);
#endif
hlassume(g_nummodels < MAX_MAP_MODELS, assume_MAX_MAP_MODELS);
startleafs = g_numleafs;
int modnum = g_nummodels;
model = &g_dmodels[modnum];
g_nummodels++;
// Log("ProcessModel: %i (%i f)\n", modnum, model->numfaces);
g_hullnum = 0; //vluzacn
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
VectorFill (model->mins, 99999);
VectorFill (model->maxs, -99999);
{
if (surfs->mins[0] > surfs->maxs[0])
{
Developer (DEVELOPER_LEVEL_FLUFF, "model %d hull %d empty\n", modnum, g_hullnum);
}
else
{
vec3_t mins, maxs;
int i;
VectorSubtract (surfs->mins, g_hull_size[g_hullnum][0], mins);
VectorSubtract (surfs->maxs, g_hull_size[g_hullnum][1], maxs);
for (i = 0; i < 3; i++)
{
if (mins[i] > maxs[i])
{
vec_t tmp;
tmp = (mins[i] + maxs[i]) / 2;
mins[i] = tmp;
maxs[i] = tmp;
}
}
for (i = 0; i < 3; i++)
{
model->maxs[i] = qmax (model->maxs[i], maxs[i]);
model->mins[i] = qmin (model->mins[i], mins[i]);
}
}
}
#else
VectorCopy(surfs->mins, model->mins);
VectorCopy(surfs->maxs, model->maxs);
#endif
// SolidBSP generates a node tree
nodes = SolidBSP(surfs,
#ifdef ZHLT_DETAILBRUSH
detailbrushes,
#endif
modnum==0);
// build all the portals in the bsp tree
// some portals are solid polygons, and some are paths to other leafs
if (g_nummodels == 1 && !g_nofill) // assume non-world bmodels are simple
{
#ifdef HLBSP_FILL
if (!g_noinsidefill)
FillInside (nodes);
#endif
nodes = FillOutside(nodes, (g_bLeaked != true), 0); // make a leakfile if bad
}
FreePortals(nodes);
// fix tjunctions
tjunc(nodes);
MakeFaceEdges();
// emit the faces for the bsp file
model->headnode[0] = g_numnodes;
model->firstface = g_numfaces;
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
bool novisiblebrushes = false;
// model->headnode[0]<0 will crash HL, so must split it.
if (nodes->planenum == -1)
{
novisiblebrushes = true;
if (nodes->markfaces[0] != NULL)
hlassume(false, assume_EmptySolid);
if (g_numplanes == 0)
Error ("No valid planes.\n");
nodes->planenum = 0; // arbitrary plane
nodes->children[0] = AllocNode ();
nodes->children[0]->planenum = -1;
nodes->children[0]->contents = CONTENTS_EMPTY;
#ifdef ZHLT_DETAILBRUSH
nodes->children[0]->isdetail = false;
nodes->children[0]->isportalleaf = true;
nodes->children[0]->iscontentsdetail = false;
#endif
nodes->children[0]->faces = NULL;
nodes->children[0]->markfaces = (face_t**)calloc (1, sizeof(face_t*));
VectorFill (nodes->children[0]->mins, 0);
VectorFill (nodes->children[0]->maxs, 0);
nodes->children[1] = AllocNode ();
nodes->children[1]->planenum = -1;
nodes->children[1]->contents = CONTENTS_EMPTY;
#ifdef ZHLT_DETAILBRUSH
nodes->children[1]->isdetail = false;
nodes->children[1]->isportalleaf = true;
nodes->children[1]->iscontentsdetail = false;
#endif
nodes->children[1]->faces = NULL;
nodes->children[1]->markfaces = (face_t**)calloc (1, sizeof(face_t*));
VectorFill (nodes->children[1]->mins, 0);
VectorFill (nodes->children[1]->maxs, 0);
nodes->contents = 0;
#ifdef ZHLT_DETAILBRUSH
nodes->isdetail = false;
nodes->isportalleaf = false;
#endif
nodes->faces = NULL;
nodes->markfaces = NULL;
VectorFill (nodes->mins, 0);
VectorFill (nodes->maxs, 0);
}
#endif
WriteDrawNodes(nodes);
model->numfaces = g_numfaces - model->firstface;
model->visleafs = g_numleafs - startleafs;
if (g_noclip)
{
/*
KGP 12/31/03 - store empty content type in headnode pointers to signify
lack of clipping information in a way that doesn't crash the half-life
engine at runtime.
*/
model->headnode[1] = CONTENTS_EMPTY;
model->headnode[2] = CONTENTS_EMPTY;
model->headnode[3] = CONTENTS_EMPTY;
#if defined (HLCSG_HLBSP_CUSTOMBOUNDINGBOX) || defined (HLCSG_HLBSP_ALLOWEMPTYENTITY)
goto skipclip;
#else
return true;
#endif
}
// the clipping hulls are simpler
for (g_hullnum = 1; g_hullnum < NUM_HULLS; g_hullnum++)
{
surfs = ReadSurfs(polyfiles[g_hullnum]);
#ifdef ZHLT_DETAILBRUSH
detailbrushes = ReadBrushes (brushfiles[g_hullnum]);
#endif
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
{
int hullnum = g_hullnum;
if (surfs->mins[0] > surfs->maxs[0])
{
Developer (DEVELOPER_LEVEL_MESSAGE, "model %d hull %d empty\n", modnum, hullnum);
}
else
{
vec3_t mins, maxs;
int i;
VectorSubtract (surfs->mins, g_hull_size[hullnum][0], mins);
VectorSubtract (surfs->maxs, g_hull_size[hullnum][1], maxs);
for (i = 0; i < 3; i++)
{
if (mins[i] > maxs[i])
{
vec_t tmp;
tmp = (mins[i] + maxs[i]) / 2;
mins[i] = tmp;
maxs[i] = tmp;
}
}
for (i = 0; i < 3; i++)
{
model->maxs[i] = qmax (model->maxs[i], maxs[i]);
model->mins[i] = qmin (model->mins[i], mins[i]);
}
}
}
#endif
nodes = SolidBSP(surfs,
#ifdef ZHLT_DETAILBRUSH
detailbrushes,
#endif
modnum==0);
if (g_nummodels == 1 && !g_nofill) // assume non-world bmodels are simple
{
nodes = FillOutside(nodes, (g_bLeaked != true), g_hullnum);
}
FreePortals(nodes);
/*
KGP 12/31/03 - need to test that the head clip node isn't empty; if it is
we need to set model->headnode equal to the content type of the head, or create
a trivial single-node case where the content type is the same for both leaves
if setting the content type is invalid.
*/
if(nodes->planenum == -1) //empty!
{
model->headnode[g_hullnum] = nodes->contents;
}
else
{
#ifdef ZHLT_XASH2
model->headnode[g_hullnum] = g_numclipnodes[g_hullnum - 1];
#else
model->headnode[g_hullnum] = g_numclipnodes;
#endif
WriteClipNodes(nodes);
}
}
#if defined (HLCSG_HLBSP_CUSTOMBOUNDINGBOX) || defined (HLCSG_HLBSP_ALLOWEMPTYENTITY)
skipclip:
#endif
#ifdef HLCSG_HLBSP_CUSTOMBOUNDINGBOX
{
entity_t *ent;
ent = EntityForModel (modnum);
if (ent != &g_entities[0] && *ValueForKey (ent, "zhlt_minsmaxs"))
{
double origin[3], mins[3], maxs[3];
VectorClear (origin);
sscanf (ValueForKey (ent, "origin"), "%lf %lf %lf", &origin[0], &origin[1], &origin[2]);
if (sscanf (ValueForKey (ent, "zhlt_minsmaxs"), "%lf %lf %lf %lf %lf %lf", &mins[0], &mins[1], &mins[2], &maxs[0], &maxs[1], &maxs[2]) == 6)
{
VectorSubtract (mins, origin, model->mins);
VectorSubtract (maxs, origin, model->maxs);
}
}
}
#endif
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
Developer (DEVELOPER_LEVEL_MESSAGE, "model %d - mins=(%g,%g,%g) maxs=(%g,%g,%g)\n", modnum,
model->mins[0], model->mins[1], model->mins[2], model->maxs[0], model->maxs[1], model->maxs[2]);
if (model->mins[0] > model->maxs[0])
{
entity_t *ent = EntityForModel (g_nummodels - 1);
if (g_nummodels - 1 != 0 && ent == &g_entities[0])
{
ent = NULL;
}
Warning ("Empty solid entity: model %d (entity: classname \"%s\", origin \"%s\", targetname \"%s\")",
g_nummodels - 1,
(ent? ValueForKey (ent, "classname"): "unknown"),
(ent? ValueForKey (ent, "origin"): "unknown"),
(ent? ValueForKey (ent, "targetname"): "unknown"));
VectorClear (model->mins); // fix "backward minsmaxs" in HL
VectorClear (model->maxs);
}
else if (novisiblebrushes)
{
entity_t *ent = EntityForModel (g_nummodels - 1);
if (g_nummodels - 1 != 0 && ent == &g_entities[0])
{
ent = NULL;
}
Warning ("No visible brushes in solid entity: model %d (entity: classname \"%s\", origin \"%s\", targetname \"%s\", range (%.0f,%.0f,%.0f) - (%.0f,%.0f,%.0f))",
g_nummodels - 1,
(ent? ValueForKey (ent, "classname"): "unknown"),
(ent? ValueForKey (ent, "origin"): "unknown"),
(ent? ValueForKey (ent, "targetname"): "unknown"),
model->mins[0], model->mins[1], model->mins[2], model->maxs[0], model->maxs[1], model->maxs[2]);
}
#endif
return true;
}
// =====================================================================================
// Usage
// =====================================================================================
static void Usage()
{
Banner();
Log("\n-= %s Options =-\n\n", g_Program);
#ifdef ZHLT_CONSOLE
Log(" -console # : Set to 0 to turn off the pop-up console (default is 1)\n");
#endif
#ifdef ZHLT_LANGFILE
Log(" -lang file : localization file\n");
#endif
Log(" -leakonly : Run BSP only enough to check for LEAKs\n");
Log(" -subdivide # : Sets the face subdivide size\n");
Log(" -maxnodesize # : Sets the maximum portal node size\n\n");
Log(" -notjunc : Don't break edges on t-junctions (not for final runs)\n");
#ifdef HLBSP_BRINKHACK
Log(" -nobrink : Don't smooth brinks (not for final runs)\n");
#endif
Log(" -noclip : Don't process the clipping hull (not for final runs)\n");
Log(" -nofill : Don't fill outside (will mask LEAKs) (not for final runs)\n");
#ifdef HLBSP_FILL
Log(" -noinsidefill : Don't fill empty spaces\n");
#endif
Log(" -noopt : Don't optimize planes on BSP write (not for final runs)\n");
#ifdef HLBSP_MERGECLIPNODE
Log(" -noclipnodemerge: Don't optimize clipnodes\n");
#endif
Log(" -texdata # : Alter maximum texture memory limit (in kb)\n");
Log(" -lightdata # : Alter maximum lighting memory limit (in kb)\n");
Log(" -chart : display bsp statitics\n");
Log(" -low | -high : run program an altered priority level\n");
Log(" -nolog : don't generate the compile logfiles\n");
Log(" -threads # : manually specify the number of threads to run\n");
#ifdef SYSTEM_WIN32
Log(" -estimate : display estimated time during compile\n");
#endif
#ifdef ZHLT_PROGRESSFILE // AJM
Log(" -progressfile path : specify the path to a file for progress estimate output\n");
#endif
#ifdef SYSTEM_POSIX
Log(" -noestimate : do not display continuous compile time estimates\n");
#endif
#ifdef ZHLT_NULLTEX // AJM
Log(" -nonulltex : Don't strip NULL faces\n");
#endif
#ifdef ZHLT_DETAIL // AJM
Log(" -nodetail : don't handle detail brushes\n");
#endif
#ifdef HLBSP_REMOVEHULL2
Log(" -nohull2 : Don't generate hull 2 (the clipping hull for large monsters and pushables)\n");
#endif
#ifdef HLBSP_VIEWPORTAL
Log(" -viewportal : Show portal boundaries in 'mapname_portal.pts' file\n");
#endif
Log(" -verbose : compile with verbose messages\n");
Log(" -noinfo : Do not show tool configuration information\n");
Log(" -dev # : compile with developer message\n\n");
Log(" mapfile : The mapfile to compile\n\n");
exit(1);
}
// =====================================================================================
// Settings
// =====================================================================================
static void Settings()
{
char* tmp;
if (!g_info)
return;
Log("\nCurrent %s Settings\n", g_Program);
Log("Name | Setting | Default\n" "-------------------|-----------|-------------------------\n");
// ZHLT Common Settings
if (DEFAULT_NUMTHREADS == -1)
{
Log("threads [ %7d ] [ Varies ]\n", g_numthreads);
}
else
{
Log("threads [ %7d ] [ %7d ]\n", g_numthreads, DEFAULT_NUMTHREADS);
}
Log("verbose [ %7s ] [ %7s ]\n", g_verbose ? "on" : "off", DEFAULT_VERBOSE ? "on" : "off");
Log("log [ %7s ] [ %7s ]\n", g_log ? "on" : "off", DEFAULT_LOG ? "on" : "off");
Log("developer [ %7d ] [ %7d ]\n", g_developer, DEFAULT_DEVELOPER);
Log("chart [ %7s ] [ %7s ]\n", g_chart ? "on" : "off", DEFAULT_CHART ? "on" : "off");
Log("estimate [ %7s ] [ %7s ]\n", g_estimate ? "on" : "off", DEFAULT_ESTIMATE ? "on" : "off");
Log("max texture memory [ %7d ] [ %7d ]\n", g_max_map_miptex, DEFAULT_MAX_MAP_MIPTEX);
switch (g_threadpriority)
{
case eThreadPriorityNormal:
default:
tmp = "Normal";
break;
case eThreadPriorityLow:
tmp = "Low";
break;
case eThreadPriorityHigh:
tmp = "High";
break;
}
Log("priority [ %7s ] [ %7s ]\n", tmp, "Normal");
Log("\n");
// HLBSP Specific Settings
Log("noclip [ %7s ] [ %7s ]\n", g_noclip ? "on" : "off", DEFAULT_NOCLIP ? "on" : "off");
Log("nofill [ %7s ] [ %7s ]\n", g_nofill ? "on" : "off", DEFAULT_NOFILL ? "on" : "off");
#ifdef HLBSP_FILL
Log("noinsidefill [ %7s ] [ %7s ]\n", g_noinsidefill ? "on" : "off", DEFAULT_NOINSIDEFILL ? "on" : "off");
#endif
Log("noopt [ %7s ] [ %7s ]\n", g_noopt ? "on" : "off", DEFAULT_NOOPT ? "on" : "off");
#ifdef HLBSP_MERGECLIPNODE
Log("no clipnode merging [ %7s ] [ %7s ]\n", g_noclipnodemerge? "on": "off", DEFAULT_NOCLIPNODEMERGE? "on": "off");
#endif
#ifdef ZHLT_NULLTEX // AJM
Log("null tex. stripping [ %7s ] [ %7s ]\n", g_bUseNullTex ? "on" : "off", DEFAULT_NULLTEX ? "on" : "off" );
#endif
#ifdef ZHLT_DETAIL // AJM
Log("detail brushes [ %7s ] [ %7s ]\n", g_bDetailBrushes ? "on" : "off", DEFAULT_DETAIL ? "on" : "off" );
#endif
Log("notjunc [ %7s ] [ %7s ]\n", g_notjunc ? "on" : "off", DEFAULT_NOTJUNC ? "on" : "off");
#ifdef HLBSP_BRINKHACK
Log("nobrink [ %7s ] [ %7s ]\n", g_nobrink? "on": "off", DEFAULT_NOBRINK? "on": "off");
#endif
Log("subdivide size [ %7d ] [ %7d ] (Min %d) (Max %d)\n",
g_subdivide_size, DEFAULT_SUBDIVIDE_SIZE, MIN_SUBDIVIDE_SIZE, MAX_SUBDIVIDE_SIZE);
Log("max node size [ %7d ] [ %7d ] (Min %d) (Max %d)\n",
g_maxnode_size, DEFAULT_MAXNODE_SIZE, MIN_MAXNODE_SIZE, MAX_MAXNODE_SIZE);
#ifdef HLBSP_REMOVEHULL2
Log("remove hull 2 [ %7s ] [ %7s ]\n", g_nohull2? "on": "off", "off");
#endif
Log("\n\n");
}
// =====================================================================================
// ProcessFile
// =====================================================================================
static void ProcessFile(const char* const filename)
{
int i;
char name[_MAX_PATH];
// delete existing files
safe_snprintf(g_portfilename, _MAX_PATH, "%s.prt", filename);
unlink(g_portfilename);
safe_snprintf(g_pointfilename, _MAX_PATH, "%s.pts", filename);
unlink(g_pointfilename);
safe_snprintf(g_linefilename, _MAX_PATH, "%s.lin", filename);
unlink(g_linefilename);
#ifdef ZHLT_64BIT_FIX
safe_snprintf (g_extentfilename, _MAX_PATH, "%s.ext", filename);
unlink (g_extentfilename);
#endif
// open the hull files
for (i = 0; i < NUM_HULLS; i++)
{
//mapname.p[0-3]
sprintf(name, "%s.p%i", filename, i);
polyfiles[i] = fopen(name, "r");
if (!polyfiles[i])
Error("Can't open %s", name);
#ifdef ZHLT_DETAILBRUSH
sprintf(name, "%s.b%i", filename, i);
brushfiles[i] = fopen(name, "r");
if (!brushfiles[i])
Error("Can't open %s", name);
#endif
}
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
{
FILE *f;
char name[_MAX_PATH];
safe_snprintf (name, _MAX_PATH, "%s.hsz", filename);
f = fopen (name, "r");
if (!f)
{
Warning("Couldn't open %s", name);
}
else
{
float x1,y1,z1;
float x2,y2,z2;
for (i = 0; i < NUM_HULLS; i++)
{
int count;
count = fscanf (f, "%f %f %f %f %f %f\n", &x1, &y1, &z1, &x2, &y2, &z2);
if (count != 6)
{
Error ("Load hull size (line %i): scanf failure", i+1);
}
g_hull_size[i][0][0] = x1;
g_hull_size[i][0][1] = y1;
g_hull_size[i][0][2] = z1;
g_hull_size[i][1][0] = x2;
g_hull_size[i][1][1] = y2;
g_hull_size[i][1][2] = z2;
}
fclose (f);
}
}
#endif
// load the output of csg
safe_snprintf(g_bspfilename, _MAX_PATH, "%s.bsp", filename);
LoadBSPFile(g_bspfilename);
ParseEntities();
Settings(); // AJM: moved here due to info_compile_parameters entity
#ifdef HLCSG_HLBSP_DOUBLEPLANE
{
char name[_MAX_PATH];
safe_snprintf (name, _MAX_PATH, "%s.pln", filename);
FILE *planefile = fopen (name, "rb");
if (!planefile)
{
Warning("Couldn't open %s", name);
#undef dplane_t
#undef g_dplanes
for (i = 0; i < g_numplanes; i++)
{
plane_t *mp = &g_mapplanes[i];
dplane_t *dp = &g_dplanes[i];
VectorCopy (dp->normal, mp->normal);
mp->dist = dp->dist;
mp->type = dp->type;
}
#define dplane_t plane_t
#define g_dplanes g_mapplanes
}
else
{
if (q_filelength (planefile) != g_numplanes * sizeof (dplane_t))
{
Error ("Invalid plane data");
}
SafeRead (planefile, g_dplanes, g_numplanes * sizeof (dplane_t));
fclose (planefile);
}
}
#endif
// init the tables to be shared by all models
BeginBSPFile();
// process each model individually
while (ProcessModel())
;
// write the updated bsp file out
FinishBSPFile();
#ifdef HLBSP_DELETETEMPFILES
// Because the bsp file has been updated, these polyfiles are no longer valid.
for (i = 0; i < NUM_HULLS; i++)
{
sprintf (name, "%s.p%i", filename, i);
fclose (polyfiles[i]);
polyfiles[i] = NULL;
unlink (name);
#ifdef ZHLT_DETAILBRUSH
sprintf(name, "%s.b%i", filename, i);
fclose (brushfiles[i]);
brushfiles[i] = NULL;
unlink (name);
#endif
}
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
safe_snprintf (name, _MAX_PATH, "%s.hsz", filename);
unlink (name);
#endif
#ifdef HLCSG_HLBSP_DOUBLEPLANE
safe_snprintf (name, _MAX_PATH, "%s.pln", filename);
unlink (name);
#endif
#endif
}
// =====================================================================================
// main
// =====================================================================================
int main(const int argc, char** argv)
{
int i;
double start, end;
const char* mapname_from_arg = NULL;
g_Program = "hlbsp";
#ifdef ZHLT_PARAMFILE
int argcold = argc;
char ** argvold = argv;
{
int argc;
char ** argv;
ParseParamFile (argcold, argvold, argc, argv);
{
#endif
#ifdef ZHLT_CONSOLE
if (InitConsole (argc, argv) < 0)
Usage();
#endif
// if we dont have any command line argvars, print out usage and die
if (argc == 1)
Usage();
// check command line args
for (i = 1; i < argc; i++)
{
if (!strcasecmp(argv[i], "-threads"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
int g_numthreads = atoi(argv[++i]);
if (g_numthreads < 1)
{
Log("Expected value of at least 1 for '-threads'\n");
Usage();
}
}
else
{
Usage();
}
}
#ifdef ZHLT_CONSOLE
else if (!strcasecmp(argv[i], "-console"))
{
#ifndef SYSTEM_WIN32
Warning("The option '-console #' is only valid for Windows.");
#endif
if (i + 1 < argc)
++i;
else
Usage();
}
#endif
else if (!strcasecmp(argv[i], "-notjunc"))
{
g_notjunc = true;
}
#ifdef HLBSP_BRINKHACK
else if (!strcasecmp (argv[i], "-nobrink"))
{
g_nobrink = true;
}
#endif
else if (!strcasecmp(argv[i], "-noclip"))
{
g_noclip = true;
}
else if (!strcasecmp(argv[i], "-nofill"))
{
g_nofill = true;
}
#ifdef HLBSP_FILL
else if (!strcasecmp(argv[i], "-noinsidefill"))
{
g_noinsidefill = true;
}
#endif
#ifdef SYSTEM_WIN32
else if (!strcasecmp(argv[i], "-estimate"))
{
g_estimate = true;
}
#endif
#ifdef SYSTEM_POSIX
else if (!strcasecmp(argv[i], "-noestimate"))
{
g_estimate = false;
}
#endif
#ifdef ZHLT_NETVIS
else if (!strcasecmp(argv[i], "-client"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_clientid = atoi(argv[++i]);
}
else
{
Usage();
}
}
#endif
#ifdef ZHLT_PROGRESSFILE // AJM
else if (!strcasecmp(argv[i], "-progressfile"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_progressfile = argv[++i];
}
else
{
Log("Error: -progressfile: expected path to progress file following parameter\n");
Usage();
}
}
#endif
else if (!strcasecmp(argv[i], "-dev"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_developer = (developer_level_t)atoi(argv[++i]);
}
else
{
Usage();
}
}
else if (!strcasecmp(argv[i], "-verbose"))
{
g_verbose = true;
}
else if (!strcasecmp(argv[i], "-noinfo"))
{
g_info = false;
}
else if (!strcasecmp(argv[i], "-leakonly"))
{
g_bLeakOnly = true;
}
else if (!strcasecmp(argv[i], "-chart"))
{
g_chart = true;
}
else if (!strcasecmp(argv[i], "-low"))
{
g_threadpriority = eThreadPriorityLow;
}
else if (!strcasecmp(argv[i], "-high"))
{
g_threadpriority = eThreadPriorityHigh;
}
else if (!strcasecmp(argv[i], "-nolog"))
{
g_log = false;
}
#ifdef ZHLT_NULLTEX // AJM
else if (!strcasecmp(argv[i], "-nonulltex"))
{
g_bUseNullTex = false;
}
#endif
#ifdef ZHLT_DETAIL // AJM
else if (!strcasecmp(argv[i], "-nodetail"))
{
g_bDetailBrushes = false;
}
#endif
#ifdef HLBSP_REMOVEHULL2
else if (!strcasecmp (argv[i], "-nohull2"))
{
g_nohull2 = true;
}
#endif
else if (!strcasecmp(argv[i], "-noopt"))
{
g_noopt = true;
}
#ifdef HLBSP_MERGECLIPNODE
else if (!strcasecmp (argv[i], "-noclipnodemerge"))
{
g_noclipnodemerge = true;
}
#endif
else if (!strcasecmp(argv[i], "-subdivide"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_subdivide_size = atoi(argv[++i]);
if (g_subdivide_size > MAX_SUBDIVIDE_SIZE)
{
Warning
("Maximum value for subdivide size is %i, '-subdivide %i' ignored",
MAX_SUBDIVIDE_SIZE, g_subdivide_size);
g_subdivide_size = MAX_SUBDIVIDE_SIZE;
}
else if (g_subdivide_size < MIN_SUBDIVIDE_SIZE)
{
Warning
("Mininum value for subdivide size is %i, '-subdivide %i' ignored",
MIN_SUBDIVIDE_SIZE, g_subdivide_size);
g_subdivide_size = MIN_SUBDIVIDE_SIZE; //MAX_SUBDIVIDE_SIZE; //--vluzacn
}
}
else
{
Usage();
}
}
else if (!strcasecmp(argv[i], "-maxnodesize"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_maxnode_size = atoi(argv[++i]);
if (g_maxnode_size > MAX_MAXNODE_SIZE)
{
Warning
("Maximum value for max node size is %i, '-maxnodesize %i' ignored",
MAX_MAXNODE_SIZE, g_maxnode_size);
g_maxnode_size = MAX_MAXNODE_SIZE;
}
else if (g_maxnode_size < MIN_MAXNODE_SIZE)
{
Warning
("Mininimum value for max node size is %i, '-maxnodesize %i' ignored",
MIN_MAXNODE_SIZE, g_maxnode_size);
g_maxnode_size = MIN_MAXNODE_SIZE; //MAX_MAXNODE_SIZE; //vluzacn
}
}
else
{
Usage();
}
}
#ifdef HLBSP_VIEWPORTAL
else if (!strcasecmp (argv[i], "-viewportal"))
{
g_viewportal = true;
}
#endif
else if (!strcasecmp(argv[i], "-texdata"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
int x = atoi(argv[++i]) * 1024;
//if (x > g_max_map_miptex) //--vluzacn
{
g_max_map_miptex = x;
}
}
else
{
Usage();
}
}
else if (!strcasecmp(argv[i], "-lightdata"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
int x = atoi(argv[++i]) * 1024;
//if (x > g_max_map_lightdata) //--vluzacn
{
g_max_map_lightdata = x;
}
}
else
{
Usage();
}
}
#ifdef ZHLT_LANGFILE
else if (!strcasecmp (argv[i], "-lang"))
{
if (i + 1 < argc)
{
char tmp[_MAX_PATH];
#ifdef SYSTEM_WIN32
GetModuleFileName (NULL, tmp, _MAX_PATH);
#else
safe_strncpy (tmp, argv[0], _MAX_PATH);
#endif
LoadLangFile (argv[++i], tmp);
}
else
{
Usage();
}
}
#endif
else if (argv[i][0] == '-')
{
Log("Unknown option \"%s\"\n", argv[i]);
Usage();
}
else if (!mapname_from_arg)
{
mapname_from_arg = argv[i];
}
else
{
Log("Unknown option \"%s\"\n", argv[i]);
Usage();
}
}
#ifdef HLBSP_SUBDIVIDE_INMID
if (g_subdivide_size % TEXTURE_STEP != 0)
{
Warning ("Subdivide size must be a multiple of %d", (int)TEXTURE_STEP);
g_subdivide_size = TEXTURE_STEP * (g_subdivide_size / TEXTURE_STEP);
}
#endif
if (!mapname_from_arg)
{
Log("No mapfile specified\n");
Usage();
}
safe_strncpy(g_Mapname, mapname_from_arg, _MAX_PATH);
FlipSlashes(g_Mapname);
StripExtension(g_Mapname);
OpenLog(g_clientid);
atexit(CloseLog);
ThreadSetDefault();
ThreadSetPriority(g_threadpriority);
#ifdef ZHLT_PARAMFILE
LogStart(argcold, argvold);
{
int i;
Log("Arguments: ");
for (i = 1; i < argc; i++)
{
if (strchr(argv[i], ' '))
{
Log("\"%s\" ", argv[i]);
}
else
{
Log("%s ", argv[i]);
}
}
Log("\n");
}
#else
LogStart(argc, argv);
#endif
CheckForErrorLog();
#ifdef ZHLT_64BIT_FIX
#ifdef PLATFORM_CAN_CALC_EXTENT
hlassume (CalcFaceExtents_test (), assume_first);
#endif
#endif
dtexdata_init();
atexit(dtexdata_free);
//Settings();
// END INIT
// Load the .void files for allowable entities in the void
{
#ifndef ZHLT_DEFAULTEXTENSION_FIX
char g_source[_MAX_PATH];
#endif
char strSystemEntitiesVoidFile[_MAX_PATH];
char strMapEntitiesVoidFile[_MAX_PATH];
#ifndef ZHLT_DEFAULTEXTENSION_FIX
safe_strncpy(g_source, mapname_from_arg, _MAX_PATH);
StripExtension(g_source);
#endif
// try looking in the current directory
safe_strncpy(strSystemEntitiesVoidFile, ENTITIES_VOID, _MAX_PATH);
if (!q_exists(strSystemEntitiesVoidFile))
{
char tmp[_MAX_PATH];
// try looking in the directory we were run from
#ifdef SYSTEM_WIN32
GetModuleFileName(NULL, tmp, _MAX_PATH);
#else
safe_strncpy(tmp, argv[0], _MAX_PATH);
#endif
ExtractFilePath(tmp, strSystemEntitiesVoidFile);
safe_strncat(strSystemEntitiesVoidFile, ENTITIES_VOID, _MAX_PATH);
}
// Set the optional level specific lights filename
#ifdef ZHLT_DEFAULTEXTENSION_FIX
safe_snprintf(strMapEntitiesVoidFile, _MAX_PATH, "%s" ENTITIES_VOID_EXT, g_Mapname);
#else
safe_strncpy(strMapEntitiesVoidFile, g_source, _MAX_PATH);
DefaultExtension(strMapEntitiesVoidFile, ENTITIES_VOID_EXT);
#endif
LoadAllowableOutsideList(strSystemEntitiesVoidFile); // default entities.void
if (*strMapEntitiesVoidFile)
{
LoadAllowableOutsideList(strMapEntitiesVoidFile); // automatic mapname.void
}
}
// BEGIN BSP
start = I_FloatTime();
ProcessFile(g_Mapname);
end = I_FloatTime();
LogTimeElapsed(end - start);
// END BSP
FreeAllowableOutsideList();
#ifdef ZHLT_PARAMFILE
}
}
#endif
return 0;
}