mirror of
https://github.com/nzp-team/vhlt.git
synced 2024-11-10 06:31:38 +00:00
2318 lines
62 KiB
C++
2318 lines
62 KiB
C++
/*
|
|
|
|
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;
|
|
}
|