vhlt/hlcsg/qcsg.cpp
2016-09-21 00:07:53 +03:00

3077 lines
84 KiB
C++

//#pragma warning(disable: 4018) // '<' : signed/unsigned mismatch
/*
CONSTRUCTIVE SOLID GEOMETRY -aka- C S G
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]
*/
#include "csg.h"
#ifdef SYSTEM_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h> //--vluzacn
#endif
/*
NOTES
- check map size for +/- 4k limit at load time
- allow for multiple wad.cfg configurations per compile
*/
static FILE* out[NUM_HULLS]; // pointer to each of the hull out files (.p0, .p1, ect.)
#ifdef HLCSG_VIEWSURFACE
static FILE* out_view[NUM_HULLS];
#endif
#ifdef ZHLT_DETAILBRUSH
static FILE* out_detailbrush[NUM_HULLS];
#endif
static int c_tiny;
static int c_tiny_clip;
static int c_outfaces;
static int c_csgfaces;
BoundingBox world_bounds;
#ifndef HLCSG_WADCFG_NEW
#ifdef HLCSG_WADCFG
char wadconfigname[MAX_WAD_CFG_NAME];
#endif
#endif
vec_t g_tiny_threshold = DEFAULT_TINY_THRESHOLD;
bool g_noclip = DEFAULT_NOCLIP; // no clipping hull "-noclip"
bool g_onlyents = DEFAULT_ONLYENTS; // onlyents mode "-onlyents"
bool g_wadtextures = DEFAULT_WADTEXTURES; // "-nowadtextures"
bool g_chart = DEFAULT_CHART; // show chart "-chart"
bool g_skyclip = DEFAULT_SKYCLIP; // no sky clipping "-noskyclip"
bool g_estimate = DEFAULT_ESTIMATE; // progress estimates "-estimate"
bool g_info = DEFAULT_INFO; // "-info" ?
const char* g_hullfile = NULL; // external hullfile "-hullfie sdfsd"
#ifdef HLCSG_WADCFG_NEW
const char* g_wadcfgfile = NULL;
const char* g_wadconfigname = NULL;
#endif
#ifdef ZHLT_NULLTEX // AJM
bool g_bUseNullTex = DEFAULT_NULLTEX; // "-nonulltex"
#endif
#ifdef HLCSG_PRECISIONCLIP // KGP
cliptype g_cliptype = DEFAULT_CLIPTYPE; // "-cliptype <value>"
#endif
#ifdef HLCSG_NULLIFY_INVISIBLE
const char* g_nullfile = NULL;
#endif
#ifdef HLCSG_CLIPECONOMY // AJM
bool g_bClipNazi = DEFAULT_CLIPNAZI; // "-noclipeconomy"
#endif
#ifdef HLCSG_AUTOWAD // AJM
bool g_bWadAutoDetect = DEFAULT_WADAUTODETECT; // "-wadautodetect"
#endif
#ifdef ZHLT_DETAIL // AJM
bool g_bDetailBrushes = DEFAULT_DETAIL; // "-detail"
#endif
#ifdef ZHLT_PROGRESSFILE // AJM
char* g_progressfile = DEFAULT_PROGRESSFILE; // "-progressfile path"
#endif
#ifdef HLCSG_SCALESIZE
vec_t g_scalesize = DEFAULT_SCALESIZE;
#endif
#ifdef HLCSG_KEEPLOG
bool g_resetlog = DEFAULT_RESETLOG;
#endif
#ifdef HLCSG_OPTIMIZELIGHTENTITY
bool g_nolightopt = DEFAULT_NOLIGHTOPT;
#endif
#ifdef HLCSG_GAMETEXTMESSAGE_UTF8
bool g_noutf8 = DEFAULT_NOUTF8;
#endif
#ifdef HLCSG_NULLIFYAAATRIGGER
bool g_nullifytrigger = DEFAULT_NULLIFYTRIGGER;
#endif
#ifdef HLCSG_VIEWSURFACE
bool g_viewsurface = false;
#endif
#ifdef ZHLT_INFO_COMPILE_PARAMETERS
// =====================================================================================
// GetParamsFromEnt
// parses entity keyvalues for setting information
// =====================================================================================
void GetParamsFromEnt(entity_t* mapent)
{
int iTmp;
char szTmp[256];
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");
}
// texdata(string) : "Texture Data Memory" : "4096"
iTmp = IntForKey(mapent, "texdata") * 1024;
if (iTmp > g_max_map_miptex)
{
g_max_map_miptex = iTmp;
}
sprintf_s(szTmp, "%i", g_max_map_miptex);
Log("%30s [ %-9s ]\n", "Texture Data Memory", szTmp);
// hullfile(string) : "Custom Hullfile"
if (ValueForKey(mapent, "hullfile"))
{
g_hullfile = ValueForKey(mapent, "hullfile");
Log("%30s [ %-9s ]\n", "Custom Hullfile", g_hullfile);
}
#ifdef HLCSG_AUTOWAD
// wadautodetect(choices) : "Wad Auto Detect" : 0 = [ 0 : "Off" 1 : "On" ]
if (!strcmp(ValueForKey(mapent, "wadautodetect"), "1"))
{
g_bWadAutoDetect = true;
}
else
{
g_bWadAutoDetect = false;
}
Log("%30s [ %-9s ]\n", "Wad Auto Detect", g_bWadAutoDetect ? "on" : "off");
#endif
#ifndef HLCSG_WADCFG_NEW
#ifdef HLCSG_WADCFG
// wadconfig(string) : "Custom Wad Configuration" : ""
if (strlen(ValueForKey(mapent, "wadconfig")) > 0)
{
safe_strncpy(wadconfigname, ValueForKey(mapent, "wadconfig"), MAX_WAD_CFG_NAME);
Log("%30s [ %-9s ]\n", "Custom Wad Configuration", wadconfigname);
}
#endif
#endif
#ifdef HLCSG_WADCFG_NEW
// wadconfig(string) : "Custom Wad Configuration" : ""
if (*ValueForKey(mapent, "wadconfig"))
{
g_wadconfigname = _strdup (ValueForKey(mapent, "wadconfig"));
Log("%30s [ %-9s ]\n", "Custom Wad Configuration Name", g_wadconfigname);
}
// wadcfgfile(string) : "Custom Wad Configuration File" : ""
if (*ValueForKey(mapent, "wadcfgfile"))
{
g_wadcfgfile = _strdup (ValueForKey(mapent, "wadcfgfile"));
Log("%30s [ %-9s ]\n", "Custom Wad Configuration File", g_wadcfgfile);
}
#endif
#ifdef HLCSG_CLIPECONOMY
// noclipeconomy(choices) : "Strip Uneeded Clipnodes?" : 1 = [ 1 : "Yes" 0 : "No" ]
iTmp = IntForKey(mapent, "noclipeconomy");
if (iTmp == 1)
{
g_bClipNazi = true;
}
else if (iTmp == 0)
{
g_bClipNazi = false;
}
Log("%30s [ %-9s ]\n", "Clipnode Economy Mode", g_bClipNazi ? "on" : "off");
#endif
/*
hlcsg(choices) : "HLCSG" : 1 =
[
1 : "Normal"
2 : "Onlyents"
0 : "Off"
]
*/
iTmp = IntForKey(mapent, "hlcsg");
g_onlyents = false;
if (iTmp == 2)
{
g_onlyents = true;
}
else if (iTmp == 0)
{
Fatal(assume_TOOL_CANCEL,
"%s was set to \"Off\" (0) in info_compile_parameters entity, execution cancelled", g_Program);
CheckFatal();
}
Log("%30s [ %-9s ]\n", "Onlyents", g_onlyents ? "on" : "off");
/*
nocliphull(choices) : "Generate clipping hulls" : 0 =
[
0 : "Yes"
1 : "No"
]
*/
iTmp = IntForKey(mapent, "nocliphull");
if (iTmp == 1)
{
g_noclip = true;
}
else
{
g_noclip = false;
}
Log("%30s [ %-9s ]\n", "Clipping Hull Generation", g_noclip ? "off" : "on");
#ifdef HLCSG_PRECISIONCLIP
// cliptype(choices) : "Clip Hull Type" : 4 = [ 0 : "Smallest" 1 : "Normalized" 2: "Simple" 3 : "Precise" 4 : "Legacy" ]
iTmp = IntForKey(mapent, "cliptype");
switch(iTmp)
{
case 0:
g_cliptype = clip_smallest;
break;
case 1:
g_cliptype = clip_normalized;
break;
case 2:
g_cliptype = clip_simple;
break;
case 3:
g_cliptype = clip_precise;
break;
default:
g_cliptype = clip_legacy;
break;
}
Log("%30s [ %-9s ]\n", "Clip Hull Type", GetClipTypeString(g_cliptype));
#endif
/*
noskyclip(choices) : "No Sky Clip" : 0 =
[
1 : "On"
0 : "Off"
]
*/
iTmp = IntForKey(mapent, "noskyclip");
if (iTmp == 1)
{
g_skyclip = false;
}
else
{
g_skyclip = true;
}
Log("%30s [ %-9s ]\n", "Sky brush clip generation", g_skyclip ? "on" : "off");
///////////////
Log("\n");
}
#endif
#ifndef HLCSG_CUSTOMHULL
#ifdef HLCSG_PRECISIONCLIP
// =====================================================================================
// FixBevelTextures
// =====================================================================================
void FixBevelTextures()
{
for(int counter = 0; counter < g_numtexinfo; counter++)
{
if(g_texinfo[counter].flags & TEX_BEVEL)
{ g_texinfo[counter].flags &= ~TEX_BEVEL; }
}
}
#endif
#endif
// =====================================================================================
// NewFaceFromFace
// Duplicates the non point information of a face, used by SplitFace
// =====================================================================================
bface_t* NewFaceFromFace(const bface_t* const in)
{
bface_t* newf;
newf = (bface_t*)Alloc(sizeof(bface_t));
newf->contents = in->contents;
newf->texinfo = in->texinfo;
newf->planenum = in->planenum;
newf->plane = in->plane;
#ifdef HLCSG_EMPTYBRUSH
newf->backcontents = in->backcontents;
#endif
return newf;
}
// =====================================================================================
// FreeFace
// =====================================================================================
void FreeFace(bface_t* f)
{
delete f->w;
Free(f);
}
#ifndef HLCSG_NOFAKESPLITS
// =====================================================================================
// ClipFace
// Clips a faces by a plane, returning the fragment on the backside and adding any
// fragment to the outside.
// Faces exactly on the plane will stay inside unless overdrawn by later brush.
// Frontside is the side of the plane that holds the outside list.
// Precedence is necesary to handle overlapping coplanar faces.
#define SPLIT_EPSILON 0.3
// =====================================================================================
static bface_t* ClipFace(bface_t* f, bface_t** outside, const int splitplane, const bool precedence)
{
bface_t* front; // clip face
Winding* fw; // forward wind
Winding* bw; // back wind
plane_t* split; // plane to clip on
// handle exact plane matches special
if (f->planenum == (splitplane ^ 1))
return f; // opposite side, so put on inside list
if (f->planenum == splitplane) // coplanar
{
// this fragment will go to the inside, because
// the earlier one was clipped to the outside
if (precedence)
return f;
f->next = *outside;
*outside = f;
return NULL;
}
split = &g_mapplanes[splitplane];
f->w->Clip(split->normal, split->dist, &fw, &bw);
if (!fw)
{
delete bw;
return f;
}
else if (!bw)
{
delete fw;
f->next = *outside;
*outside = f;
return NULL;
}
else
{
delete f->w;
front = NewFaceFromFace(f);
front->w = fw;
fw->getBounds(front->bounds);
front->next = *outside;
*outside = front;
f->w = bw;
bw->getBounds(f->bounds);
return f;
}
}
#endif
// =====================================================================================
// WriteFace
// =====================================================================================
void WriteFace(const int hull, const bface_t* const f
#ifdef ZHLT_DETAILBRUSH
, int detaillevel
#endif
)
{
unsigned int i;
Winding* w;
ThreadLock();
if (!hull)
c_csgfaces++;
// .p0 format
w = f->w;
// plane summary
#ifdef ZHLT_DETAILBRUSH
fprintf (out[hull], "%i %i %i %i %u\n", detaillevel, f->planenum, f->texinfo, f->contents, w->m_NumPoints);
#else
fprintf(out[hull], "%i %i %i %u\n", f->planenum, f->texinfo, f->contents, w->m_NumPoints);
#endif
// for each of the points on the face
for (i = 0; i < w->m_NumPoints; i++)
{
// write the co-ords
#ifdef HLCSG_PRICISION_FIX
fprintf(out[hull], "%5.8f %5.8f %5.8f\n", w->m_Points[i][0], w->m_Points[i][1], w->m_Points[i][2]);
#else
fprintf(out[hull], "%5.2f %5.2f %5.2f\n", w->m_Points[i][0], w->m_Points[i][1], w->m_Points[i][2]);
#endif
}
// put in an extra line break
fprintf(out[hull], "\n");
#ifdef HLCSG_VIEWSURFACE
if (g_viewsurface)
{
static bool side = false;
side = !side;
if (side)
{
vec3_t center, center2;
w->getCenter (center);
VectorAdd (center, f->plane->normal, center2);
fprintf (out_view[hull], "%5.2f %5.2f %5.2f\n", center2[0], center2[1], center2[2]);
for (i = 0; i < w->m_NumPoints; i++)
{
vec_t *p1, *p2;
p1 = w->m_Points[i];
p2 = w->m_Points[(i+1)%w->m_NumPoints];
fprintf (out_view[hull], "%5.2f %5.2f %5.2f\n", center[0], center[1], center[2]);
fprintf (out_view[hull], "%5.2f %5.2f %5.2f\n", p1[0], p1[1], p1[2]);
fprintf (out_view[hull], "%5.2f %5.2f %5.2f\n", p2[0], p2[1], p2[2]);
}
fprintf (out_view[hull], "%5.2f %5.2f %5.2f\n", center[0], center[1], center[2]);
fprintf (out_view[hull], "%5.2f %5.2f %5.2f\n", center2[0], center2[1], center2[2]);
}
}
#endif
ThreadUnlock();
}
#ifdef ZHLT_DETAILBRUSH
void WriteDetailBrush (int hull, const bface_t *faces)
{
ThreadLock ();
fprintf (out_detailbrush[hull], "0\n");
for (const bface_t *f = faces; f; f = f->next)
{
Winding *w = f->w;
fprintf (out_detailbrush[hull], "%i %u\n", f->planenum, w->m_NumPoints);
for (int i = 0; i < w->m_NumPoints; i++)
{
fprintf (out_detailbrush[hull], "%5.8f %5.8f %5.8f\n", w->m_Points[i][0], w->m_Points[i][1], w->m_Points[i][2]);
}
}
fprintf (out_detailbrush[hull], "-1 -1\n");
ThreadUnlock ();
}
#endif
// =====================================================================================
// SaveOutside
// The faces remaining on the outside list are final polygons. Write them to the
// output file.
// Passable contents (water, lava, etc) will generate a mirrored copy of the face
// to be seen from the inside.
// =====================================================================================
static void SaveOutside(const brush_t* const b, const int hull, bface_t* outside, const int mirrorcontents)
{
bface_t* f;
bface_t* f2;
bface_t* next;
int i;
vec3_t temp;
for (f = outside; f; f = next)
{
next = f->next;
#ifdef HLCSG_EMPTYBRUSH
int frontcontents, backcontents;
int texinfo = f->texinfo;
const char *texname = GetTextureByNumber_CSG (texinfo);
frontcontents = f->contents;
if (mirrorcontents == CONTENTS_TOEMPTY)
{
backcontents = f->backcontents;
}
else
{
backcontents = mirrorcontents;
}
if (frontcontents == CONTENTS_TOEMPTY)
{
frontcontents = CONTENTS_EMPTY;
}
if (backcontents == CONTENTS_TOEMPTY)
{
backcontents = CONTENTS_EMPTY;
}
bool frontnull, backnull;
frontnull = false;
backnull = false;
if (mirrorcontents == CONTENTS_TOEMPTY)
{
if (strncasecmp (texname, "SKIP", 4) && strncasecmp (texname, "HINT", 4)
#ifdef HLCSG_HLBSP_SOLIDHINT
&& strncasecmp (texname, "SOLIDHINT", 9)
#endif
)
// SKIP and HINT are special textures for hlbsp
{
backnull = true;
}
}
#ifdef HLCSG_HLBSP_SOLIDHINT
if (!strncasecmp (texname, "SOLIDHINT", 9))
{
if (frontcontents != backcontents)
{
frontnull = backnull = true; // not discardable, so remove "SOLIDHINT" texture name and behave like NULL
}
}
#endif
#ifdef HLCSG_WATERBACKFACE_FIX
if (b->entitynum != 0 && !strncasecmp (texname, "!", 1))
{
backnull = true; // strip water face on one side
}
#endif
#endif
#ifdef HLCSG_EMPTYBRUSH
f->contents = frontcontents;
f->texinfo = frontnull? -1: texinfo;
#endif
if (f->w->getArea() < g_tiny_threshold)
{
c_tiny++;
Verbose("Entity %i, Brush %i: tiny fragment\n",
#ifdef HLCSG_COUNT_NEW
b->originalentitynum, b->originalbrushnum
#else
b->entitynum, b->brushnum
#endif
);
continue;
}
// count unique faces
if (!hull)
{
for (f2 = b->hulls[hull].faces; f2; f2 = f2->next)
{
if (f2->planenum == f->planenum)
{
if (!f2->used)
{
f2->used = true;
c_outfaces++;
}
break;
}
}
}
#ifdef HLCSG_WARNBADTEXINFO
// check the texture alignment of this face
if (!hull)
{
int texinfo = f->texinfo;
const char *texname = GetTextureByNumber_CSG (texinfo);
texinfo_t *tex = &g_texinfo[texinfo];
if (texinfo != -1 // nullified textures (NULL, BEVEL, aaatrigger, etc.)
&& !(tex->flags & TEX_SPECIAL) // sky
&& strncasecmp (texname, "SKIP", 4) && strncasecmp (texname, "HINT", 4) // HINT and SKIP will be nullified only after hlbsp
#ifdef HLCSG_HLBSP_SOLIDHINT
&& strncasecmp (texname, "SOLIDHINT", 9)
#endif
)
{
// check for "Malformed face (%d) normal"
vec3_t texnormal;
CrossProduct (tex->vecs[1], tex->vecs[0], texnormal);
VectorNormalize (texnormal);
if (fabs (DotProduct (texnormal, f->plane->normal)) <= NORMAL_EPSILON)
{
Warning ("Entity %i, Brush %i: Malformed texture alignment (texture %s): Texture axis perpendicular to face.",
#ifdef HLCSG_COUNT_NEW
b->originalentitynum, b->originalbrushnum,
#else
b->entitynum, b->brushnum,
#endif
texname
);
}
// check for "Bad surface extents"
bool bad;
int i;
int j;
vec_t val;
bad = false;
for (i = 0; i < f->w->m_NumPoints; i++)
{
for (j = 0; j < 2; j++)
{
val = DotProduct (f->w->m_Points[i], tex->vecs[j]) + tex->vecs[j][3];
if (val < -99999 || val > 999999)
{
bad = true;
}
}
}
if (bad)
{
Warning ("Entity %i, Brush %i: Malformed texture alignment (texture %s): Bad surface extents.",
#ifdef HLCSG_COUNT_NEW
b->originalentitynum, b->originalbrushnum,
#else
b->entitynum, b->brushnum,
#endif
texname
);
}
}
}
#endif
WriteFace(hull, f
#ifdef ZHLT_DETAILBRUSH
,
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? b->clipnodedetaillevel: b->detaillevel)
#else
b->detaillevel
#endif
#endif
);
// if (mirrorcontents != CONTENTS_SOLID)
{
f->planenum ^= 1;
f->plane = &g_mapplanes[f->planenum];
#ifdef HLCSG_EMPTYBRUSH
f->contents = backcontents;
f->texinfo = backnull? -1: texinfo;
#else
f->contents = mirrorcontents;
#endif
// swap point orders
for (i = 0; i < f->w->m_NumPoints / 2; i++) // add points backwards
{
VectorCopy(f->w->m_Points[i], temp);
VectorCopy(f->w->m_Points[f->w->m_NumPoints - 1 - i], f->w->m_Points[i]);
VectorCopy(temp, f->w->m_Points[f->w->m_NumPoints - 1 - i]);
}
WriteFace(hull, f
#ifdef ZHLT_DETAILBRUSH
,
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? b->clipnodedetaillevel: b->detaillevel)
#else
b->detaillevel
#endif
#endif
);
}
FreeFace(f);
}
}
// =====================================================================================
// CopyFace
// =====================================================================================
bface_t* CopyFace(const bface_t* const f)
{
bface_t* n;
n = NewFaceFromFace(f);
n->w = f->w->Copy();
n->bounds = f->bounds;
return n;
}
// =====================================================================================
// CopyFaceList
// =====================================================================================
bface_t* CopyFaceList(bface_t* f)
{
bface_t* head;
bface_t* n;
if (f)
{
head = CopyFace(f);
n = head;
f = f->next;
while (f)
{
n->next = CopyFace(f);
n = n->next;
f = f->next;
}
return head;
}
else
{
return NULL;
}
}
// =====================================================================================
// FreeFaceList
// =====================================================================================
void FreeFaceList(bface_t* f)
{
if (f)
{
if (f->next)
{
FreeFaceList(f->next);
}
FreeFace(f);
}
}
// =====================================================================================
// CopyFacesToOutside
// Make a copy of all the faces of the brush, so they can be chewed up by other
// brushes.
// All of the faces start on the outside list.
// As other brushes take bites out of the faces, the fragments are moved to the
// inside list, so they can be freed when they are determined to be completely
// enclosed in solid.
// =====================================================================================
static bface_t* CopyFacesToOutside(brushhull_t* bh)
{
bface_t* f;
bface_t* newf;
bface_t* outside;
outside = NULL;
for (f = bh->faces; f; f = f->next)
{
newf = CopyFace(f);
newf->w->getBounds(newf->bounds);
newf->next = outside;
outside = newf;
}
return outside;
}
// =====================================================================================
// CSGBrush
// =====================================================================================
#ifdef ZHLT_DETAILBRUSH
extern const char *ContentsToString (const contents_t type);
#endif
static void CSGBrush(int brushnum)
{
int hull;
brush_t* b1;
brush_t* b2;
brushhull_t* bh1;
brushhull_t* bh2;
int bn;
bool overwrite;
bface_t* f;
bface_t* f2;
bface_t* next;
#ifndef HLCSG_NOFAKESPLITS
bface_t* fcopy;
#endif
bface_t* outside;
#ifndef HLCSG_NOFAKESPLITS
bface_t* oldoutside;
#endif
entity_t* e;
vec_t area;
// get entity and brush info from the given brushnum that we can work with
b1 = &g_mapbrushes[brushnum];
e = &g_entities[b1->entitynum];
// for each of the hulls
for (hull = 0; hull < NUM_HULLS; hull++)
{
bh1 = &b1->hulls[hull];
#ifdef ZHLT_DETAILBRUSH
if (bh1->faces &&
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? b1->clipnodedetaillevel: b1->detaillevel)
#else
b1->detaillevel
#endif
)
{
switch (b1->contents)
{
case CONTENTS_ORIGIN:
#ifdef HLCSG_HLBSP_CUSTOMBOUNDINGBOX
case CONTENTS_BOUNDINGBOX:
#endif
case CONTENTS_HINT:
#ifdef HLCSG_EMPTYBRUSH
case CONTENTS_TOEMPTY:
#endif
break;
default:
Error ("Entity %i, Brush %i: %s brushes not allowed in detail\n",
#ifdef HLCSG_COUNT_NEW
b1->originalentitynum, b1->originalbrushnum,
#else
b1->entitynum, b1->brushnum,
#endif
ContentsToString((contents_t)b1->contents));
break;
case CONTENTS_SOLID:
WriteDetailBrush (hull, bh1->faces);
break;
}
}
#endif
// set outside to a copy of the brush's faces
outside = CopyFacesToOutside(bh1);
overwrite = false;
#ifdef HLCSG_EMPTYBRUSH
if (b1->contents == CONTENTS_TOEMPTY)
{
for (f = outside; f; f = f->next)
{
f->contents = CONTENTS_TOEMPTY;
f->backcontents = CONTENTS_TOEMPTY;
}
}
#endif
// for each brush in entity e
for (bn = 0; bn < e->numbrushes; bn++)
{
// see if b2 needs to clip a chunk out of b1
#ifdef HLCSG_CSGBrush_BRUSHNUM_FIX
if (e->firstbrush + bn == brushnum)
{
continue;
}
overwrite = e->firstbrush + bn > brushnum;
#else
if (bn == brushnum)
{
overwrite = true; // later brushes now overwrite
continue;
}
#endif
b2 = &g_mapbrushes[e->firstbrush + bn];
bh2 = &b2->hulls[hull];
#ifdef HLCSG_EMPTYBRUSH
if (b2->contents == CONTENTS_TOEMPTY)
continue;
#endif
#ifdef ZHLT_DETAILBRUSH
if (
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? (b2->clipnodedetaillevel - 0 > b1->clipnodedetaillevel + 0): (b2->detaillevel - b2->chopdown > b1->detaillevel + b1->chopup))
#else
b2->detaillevel - b2->chopdown > b1->detaillevel + b1->chopup
#endif
)
continue; // you can't chop
if (b2->contents == b1->contents &&
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? (b2->clipnodedetaillevel != b1->clipnodedetaillevel): (b2->detaillevel != b1->detaillevel))
#else
b2->detaillevel != b1->detaillevel
#endif
)
{
overwrite =
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? (b2->clipnodedetaillevel < b1->clipnodedetaillevel): (b2->detaillevel < b1->detaillevel))
#else
b2->detaillevel < b1->detaillevel
#endif
;
}
#endif
#ifdef HLCSG_COPLANARPRIORITY
if (b2->contents == b1->contents
&& hull == 0 && b2->detaillevel == b1->detaillevel
&& b2->coplanarpriority != b1->coplanarpriority)
{
overwrite = b2->coplanarpriority > b1->coplanarpriority;
}
#endif
if (!bh2->faces)
continue; // brush isn't in this hull
// check brush bounding box first
// TODO: use boundingbox method instead
if (bh1->bounds.testDisjoint(bh2->bounds))
{
continue;
}
// divide faces by the planes of the b2 to find which
// fragments are inside
f = outside;
outside = NULL;
for (; f; f = next)
{
next = f->next;
// check face bounding box first
if (bh2->bounds.testDisjoint(f->bounds))
{ // this face doesn't intersect brush2's bbox
f->next = outside;
outside = f;
continue;
}
#ifdef ZHLT_DETAILBRUSH
if (
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? (b2->clipnodedetaillevel > b1->clipnodedetaillevel): (b2->detaillevel > b1->detaillevel))
#else
b2->detaillevel > b1->detaillevel
#endif
)
{
const char *texname = GetTextureByNumber_CSG (f->texinfo);
if (f->texinfo == -1 || !strncasecmp (texname, "SKIP", 4) || !strncasecmp (texname, "HINT", 4)
#ifdef HLCSG_HLBSP_SOLIDHINT
|| !strncasecmp (texname, "SOLIDHINT", 9)
#endif
)
{
// should not nullify the fragment inside detail brush
f->next = outside;
outside = f;
continue;
}
}
#endif
#ifndef HLCSG_NOFAKESPLITS
oldoutside = outside;
fcopy = CopyFace(f); // save to avoid fake splits
#endif
// throw pieces on the front sides of the planes
// into the outside list, return the remains on the inside
#ifdef HLCSG_NOFAKESPLITS
// find the fragment inside brush2
Winding *w = new Winding (*f->w);
for (f2 = bh2->faces; f2; f2 = f2->next)
{
if (f->planenum == f2->planenum)
{
if (!overwrite)
{
// face plane is outside brush2
w->m_NumPoints = 0;
break;
}
else
{
continue;
}
}
if (f->planenum == (f2->planenum ^ 1))
{
continue;
}
Winding *fw;
Winding *bw;
w->Clip (f2->plane->normal, f2->plane->dist, &fw, &bw);
if (fw)
{
delete fw;
}
if (bw)
{
delete w;
w = bw;
}
else
{
w->m_NumPoints = 0;
break;
}
}
// do real split
if (w->m_NumPoints)
{
for (f2 = bh2->faces; f2; f2 = f2->next)
{
if (f->planenum == f2->planenum || f->planenum == (f2->planenum ^ 1))
{
continue;
}
int valid = 0;
int x;
for (x = 0; x < w->m_NumPoints; x++)
{
vec_t dist = DotProduct (w->m_Points[x], f2->plane->normal) - f2->plane->dist;
if (dist >= -ON_EPSILON*4) // only estimate
{
valid++;
}
}
if (valid >= 2)
{ // this splitplane forms an edge
Winding *fw;
Winding *bw;
f->w->Clip (f2->plane->normal, f2->plane->dist, &fw, &bw);
if (fw)
{
bface_t *front = NewFaceFromFace (f);
front->w = fw;
fw->getBounds (front->bounds);
front->next = outside;
outside = front;
}
if (bw)
{
delete f->w;
f->w = bw;
bw->getBounds (f->bounds);
}
else
{
FreeFace (f);
f = NULL;
break;
}
}
}
}
else
{
f->next = outside;
outside = f;
f = NULL;
}
delete w;
#else
for (f2 = bh2->faces; f2 && f; f2 = f2->next)
{
f = ClipFace(f, &outside, f2->planenum, overwrite);
}
#endif
area = f ? f->w->getArea() : 0;
if (f && area < g_tiny_threshold)
{
Verbose("Entity %i, Brush %i: tiny penetration\n",
#ifdef HLCSG_COUNT_NEW
b1->originalentitynum, b1->originalbrushnum
#else
b1->entitynum, b1->brushnum
#endif
);
c_tiny_clip++;
FreeFace(f);
f = NULL;
}
if (f)
{
// there is one convex fragment of the original
// face left inside brush2
#ifndef HLCSG_NOFAKESPLITS
FreeFace(fcopy);
#endif
#ifdef ZHLT_DETAILBRUSH
if (
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? (b2->clipnodedetaillevel > b1->clipnodedetaillevel): (b2->detaillevel > b1->detaillevel))
#else
b2->detaillevel > b1->detaillevel
#endif
)
{ // don't chop or set contents, only nullify
f->next = outside;
outside = f;
f->texinfo = -1;
continue;
}
if (
#ifdef ZHLT_CLIPNODEDETAILLEVEL
(hull? b2->clipnodedetaillevel < b1->clipnodedetaillevel: b2->detaillevel < b1->detaillevel)
#else
b2->detaillevel < b1->detaillevel
#endif
&& b2->contents == CONTENTS_SOLID)
{ // real solid
FreeFace (f);
continue;
}
#endif
#ifdef HLCSG_EMPTYBRUSH
if (b1->contents == CONTENTS_TOEMPTY)
{
bool onfront = true, onback = true;
for (f2 = bh2->faces; f2; f2 = f2->next)
{
if (f->planenum == (f2->planenum ^ 1))
onback = false;
if (f->planenum == f2->planenum)
onfront = false;
}
if (onfront && f->contents < b2->contents)
f->contents = b2->contents;
if (onback && f->backcontents < b2->contents)
f->backcontents = b2->contents;
if (f->contents == CONTENTS_SOLID && f->backcontents == CONTENTS_SOLID
#ifdef HLCSG_HLBSP_SOLIDHINT
&& strncasecmp (GetTextureByNumber_CSG (f->texinfo), "SOLIDHINT", 9)
#endif
)
{
FreeFace (f);
}
else
{
f->next = outside;
outside = f;
}
continue;
}
#endif
if (b1->contents > b2->contents
#ifdef HLCSG_HLBSP_SOLIDHINT
|| b1->contents == b2->contents && !strncasecmp (GetTextureByNumber_CSG (f->texinfo), "SOLIDHINT", 9)
#endif
)
{ // inside a water brush
f->contents = b2->contents;
f->next = outside;
outside = f;
}
else // inside a solid brush
{
FreeFace(f); // throw it away
}
}
#ifndef HLCSG_NOFAKESPLITS
else
{ // the entire thing was on the outside, even
// though the bounding boxes intersected,
// which will never happen with axial planes
// free the fragments chopped to the outside
while (outside != oldoutside)
{
f2 = outside->next;
FreeFace(outside);
outside = f2;
}
// revert to the original face to avoid
// unneeded false cuts
fcopy->next = outside;
outside = fcopy;
}
#endif
}
}
// all of the faces left in outside are real surface faces
SaveOutside(b1, hull, outside, b1->contents);
}
}
//
// =====================================================================================
//
// =====================================================================================
// EmitPlanes
// =====================================================================================
static void EmitPlanes()
{
int i;
dplane_t* dp;
plane_t* mp;
g_numplanes = g_nummapplanes;
mp = g_mapplanes;
dp = g_dplanes;
#ifdef HLCSG_HLBSP_DOUBLEPLANE
{
char name[_MAX_PATH];
safe_snprintf (name, _MAX_PATH, "%s.pln", g_Mapname);
FILE *planeout = fopen (name, "wb");
if (!planeout)
Error("Couldn't open %s", name);
SafeWrite (planeout, g_mapplanes, g_nummapplanes * sizeof (plane_t));
fclose (planeout);
}
#endif
for (i = 0; i < g_nummapplanes; i++, mp++, dp++)
{
//if (!(mp->redundant))
//{
// Log("EmitPlanes: plane %i non redundant\n", i);
VectorCopy(mp->normal, dp->normal);
dp->dist = mp->dist;
dp->type = mp->type;
// }
//else
// {
// Log("EmitPlanes: plane %i redundant\n", i);
// }
}
}
// =====================================================================================
// SetModelNumbers
// blah
// =====================================================================================
static void SetModelNumbers()
{
int i;
int models;
char value[10];
models = 1;
for (i = 1; i < g_numentities; i++)
{
if (g_entities[i].numbrushes)
{
safe_snprintf(value, sizeof(value), "*%i", models);
models++;
SetKeyValue(&g_entities[i], "model", value);
}
}
}
#ifdef HLCSG_COPYMODELKEYVALUE
void ReuseModel ()
{
int i;
for (i = g_numentities - 1; i >= 1; i--) // so it won't affect the remaining entities in the loop when we move this entity backward
{
const char *name = ValueForKey (&g_entities[i], "zhlt_usemodel");
if (!*name)
{
continue;
}
int j;
for (j = 1; j < g_numentities; j++)
{
if (*ValueForKey (&g_entities[j], "zhlt_usemodel"))
{
continue;
}
if (!strcmp (name, ValueForKey (&g_entities[j], "targetname")))
{
break;
}
}
if (j == g_numentities)
{
if (!strcasecmp (name, "null"))
{
SetKeyValue (&g_entities[i], "model", "");
continue;
}
Error ("zhlt_usemodel: can not find target entity '%s', or that entity is also using 'zhlt_usemodel'.\n", name);
}
SetKeyValue (&g_entities[i], "model", ValueForKey (&g_entities[j], "model"));
if (j > i)
{
// move this entity backward
// to prevent precache error in case of .mdl/.spr and wrong result of EntityForModel in case of map model
entity_t tmp;
tmp = g_entities[i];
memmove (&g_entities[i], &g_entities[i + 1], ((j + 1) - (i + 1)) * sizeof (entity_t));
g_entities[j] = tmp;
}
}
}
#endif
// =====================================================================================
// SetLightStyles
// =====================================================================================
#define MAX_SWITCHED_LIGHTS 32
#define MAX_LIGHTTARGETS_NAME 64
static void SetLightStyles()
{
int stylenum;
const char* t;
entity_t* e;
int i, j;
char value[10];
char lighttargets[MAX_SWITCHED_LIGHTS][MAX_LIGHTTARGETS_NAME];
#ifdef ZHLT_TEXLIGHT
bool newtexlight = false;
#endif
// any light that is controlled (has a targetname)
// must have a unique style number generated for it
stylenum = 0;
for (i = 1; i < g_numentities; i++)
{
e = &g_entities[i];
t = ValueForKey(e, "classname");
if (strncasecmp(t, "light", 5))
{
#ifdef ZHLT_TEXLIGHT
//LRC:
// if it's not a normal light entity, allocate it a new style if necessary.
t = ValueForKey(e, "style");
switch (atoi(t))
{
case 0: // not a light, no style, generally pretty boring
continue;
case -1: // normal switchable texlight
safe_snprintf(value, sizeof(value), "%i", 32 + stylenum);
SetKeyValue(e, "style", value);
stylenum++;
continue;
case -2: // backwards switchable texlight
safe_snprintf(value, sizeof(value), "%i", -(32 + stylenum));
SetKeyValue(e, "style", value);
stylenum++;
continue;
case -3: // (HACK) a piggyback texlight: switched on and off by triggering a real light that has the same name
SetKeyValue(e, "style", "0"); // just in case the level designer didn't give it a name
newtexlight = true;
// don't 'continue', fall out
}
//LRC (ends)
#else
continue;
#endif
}
t = ValueForKey(e, "targetname");
#ifdef HLCSG_STYLEHACK
if (*ValueForKey (e, "zhlt_usestyle"))
{
t = ValueForKey(e, "zhlt_usestyle");
if (!strcasecmp (t, "null"))
{
t = "";
}
}
#endif
if (!t[0])
{
continue;
}
// find this targetname
for (j = 0; j < stylenum; j++)
{
if (!strcmp(lighttargets[j], t))
{
break;
}
}
if (j == stylenum)
{
hlassume(stylenum < MAX_SWITCHED_LIGHTS, assume_MAX_SWITCHED_LIGHTS);
safe_strncpy(lighttargets[j], t, MAX_LIGHTTARGETS_NAME);
stylenum++;
}
safe_snprintf(value, sizeof(value), "%i", 32 + j);
SetKeyValue(e, "style", value);
}
}
// =====================================================================================
// ConvertHintToEmtpy
// =====================================================================================
static void ConvertHintToEmpty()
{
int i;
// Convert HINT brushes to EMPTY after they have been carved by csg
for (i = 0; i < MAX_MAP_BRUSHES; i++)
{
if (g_mapbrushes[i].contents == CONTENTS_HINT)
{
g_mapbrushes[i].contents = CONTENTS_EMPTY;
}
}
}
// =====================================================================================
// WriteBSP
// =====================================================================================
#ifdef HLCSG_ONLYENTS_NOWADCHANGE
void LoadWadValue ()
{
char *wadvalue;
ParseFromMemory (g_dentdata, g_entdatasize);
epair_t *e;
entity_t ent0;
entity_t *mapent = &ent0;
memset (mapent, 0, sizeof (entity_t));
if (!GetToken (true))
{
wadvalue = strdup ("");
}
else
{
if (strcmp (g_token, "{"))
{
Error ("ParseEntity: { not found");
}
while (1)
{
if (!GetToken (true))
{
Error ("ParseEntity: EOF without closing brace");
}
if (!strcmp (g_token, "}"))
{
break;
}
e = ParseEpair ();
e->next = mapent->epairs;
mapent->epairs = e;
}
wadvalue = strdup (ValueForKey (mapent, "wad"));
epair_t *next;
for (e = mapent->epairs; e; e = next)
{
next = e->next;
free (e->key);
free (e->value);
free (e);
}
}
if (*wadvalue)
{
Log ("Wad files required to run the map: \"%s\"\n", wadvalue);
}
else
{
Log ("Wad files required to run the map: (None)\n");
}
SetKeyValue (&g_entities[0], "wad", wadvalue);
free (wadvalue);
}
#endif
void WriteBSP(const char* const name)
{
char path[_MAX_PATH];
#ifdef ZHLT_DEFAULTEXTENSION_FIX
safe_snprintf(path, _MAX_PATH, "%s.bsp", name);
#else
safe_strncpy(path, name, _MAX_PATH);
DefaultExtension(path, ".bsp");
#endif
SetModelNumbers();
#ifdef HLCSG_COPYMODELKEYVALUE
ReuseModel();
#endif
SetLightStyles();
if (!g_onlyents)
WriteMiptex();
#ifdef HLCSG_ONLYENTS_NOWADCHANGE
if (g_onlyents)
{
LoadWadValue ();
}
#endif
UnparseEntities();
ConvertHintToEmpty(); // this is ridiculous. --vluzacn
#ifdef HLCSG_CHART_FIX
if (g_chart)
PrintBSPFileSizes();
#endif
WriteBSPFile(path);
}
//
// =====================================================================================
//
// AJM: added in function
// =====================================================================================
// CopyGenerictoCLIP
// clips a generic brush
// =====================================================================================
/*static void CopyGenerictoCLIP(const brush_t* const b)
{
// code blatently ripped from CopySKYtoCLIP()
int i;
entity_t* mapent;
brush_t* newbrush;
mapent = &g_entities[b->entitynum];
mapent->numbrushes++;
newbrush = &g_mapbrushes[g_nummapbrushes];
#ifdef HLCSG_COUNT_NEW
newbrush->originalentitynum = b->originalentitynum;
newbrush->originalbrushnum = b->originalbrushnum;
#endif
newbrush->entitynum = b->entitynum;
newbrush->brushnum = g_nummapbrushes - mapent->firstbrush;
newbrush->firstside = g_numbrushsides;
newbrush->numsides = b->numsides;
newbrush->contents = CONTENTS_CLIP;
newbrush->noclip = 0;
for (i = 0; i < b->numsides; i++)
{
int j;
side_t* side = &g_brushsides[g_numbrushsides];
*side = g_brushsides[b->firstside + i];
safe_strncpy(side->td.name, "CLIP", sizeof(side->td.name));
for (j = 0; j < NUM_HULLS; j++)
{
newbrush->hulls[j].faces = NULL;
newbrush->hulls[j].bounds = b->hulls[j].bounds;
}
g_numbrushsides++;
hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
}
g_nummapbrushes++;
hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
}*/
#ifdef HLCSG_CLIPECONOMY
// AJM: added in
unsigned int BrushClipHullsDiscarded = 0;
unsigned int ClipNodesDiscarded = 0;
//AJM: added in function
static void MarkEntForNoclip(entity_t* ent)
{
int i;
brush_t* b;
for (i = ent->firstbrush; i < ent->firstbrush + ent->numbrushes; i++)
{
b = &g_mapbrushes[i];
b->noclip = 1;
BrushClipHullsDiscarded++;
ClipNodesDiscarded += b->numsides;
}
}
// AJM
// =====================================================================================
// CheckForNoClip
// marks the noclip flag on any brushes that dont need clipnode generation, eg. func_illusionaries
// =====================================================================================
static void CheckForNoClip()
{
int i;
entity_t* ent;
char entclassname[MAX_KEY];
int spawnflags;
#ifdef HLCSG_CUSTOMHULL
int count = 0;
#endif
if (!g_bClipNazi)
return; // NO CLIP FOR YOU!!!
for (i = 0; i < g_numentities; i++)
{
if (!g_entities[i].numbrushes)
continue; // not a model
if (!i)
continue; // dont waste our time with worldspawn
ent = &g_entities[i];
strcpy_s(entclassname, ValueForKey(ent, "classname"));
spawnflags = atoi(ValueForKey(ent, "spawnflags"));
int skin = IntForKey(ent, "skin"); //vluzacn
#ifndef HLCSG_CUSTOMHULL
// condition 0, it's marked noclip (KGP)
if(strlen(ValueForKey(ent,"zhlt_noclip")) && strcmp(ValueForKey(ent,"zhlt_noclip"),"0"))
{
MarkEntForNoclip(ent);
}
// condition 1 //modified. --vluzacn
else
#endif
if ((skin != -16) &&
(
!strcmp(entclassname, "env_bubbles")
|| !strcmp(entclassname, "func_illusionary")
|| (spawnflags & 8) &&
( /* NOTE: func_doors as far as i can tell may need clipnodes for their
player collision detection, so for now, they stay out of it. */
!strcmp(entclassname, "func_train")
|| !strcmp(entclassname, "func_door")
|| !strcmp(entclassname, "func_water")
|| !strcmp(entclassname, "func_door_rotating")
|| !strcmp(entclassname, "func_pendulum")
|| !strcmp(entclassname, "func_train")
|| !strcmp(entclassname, "func_tracktrain")
|| !strcmp(entclassname, "func_vehicle")
)
|| (skin != 0) && (!strcmp(entclassname, "func_door") || !strcmp(entclassname, "func_water"))
|| (spawnflags & 2) && (!strcmp(entclassname, "func_conveyor"))
|| (spawnflags & 1) && (!strcmp(entclassname, "func_rot_button"))
|| (spawnflags & 64) && (!strcmp(entclassname, "func_rotating"))
))
{
MarkEntForNoclip(ent);
#ifdef HLCSG_CUSTOMHULL
count++;
#endif
}
/*
// condition 6: its a func_wall, while we noclip it, we remake the clipnodes manually
else if (!strncasecmp(entclassname, "func_wall", 9))
{
for (int j = ent->firstbrush; j < ent->firstbrush + ent->numbrushes; j++)
CopyGenerictoCLIP(&g_mapbrushes[i]);
MarkEntForNoclip(ent);
}
*/
}
#ifdef HLCSG_CUSTOMHULL
Log("%i entities discarded from clipping hulls\n", count);
#else
Log("%i brushes (totalling %i sides) discarded from clipping hulls\n", BrushClipHullsDiscarded, ClipNodesDiscarded);
#endif
}
#endif
// =====================================================================================
// ProcessModels
// =====================================================================================
#ifndef HLCSG_SORTBRUSH_FIX
#define NUM_TYPECONTENTS 5 // AJM: should reflect the number of values below
int typecontents[NUM_TYPECONTENTS] = {
CONTENTS_WATER, CONTENTS_SLIME, CONTENTS_LAVA, CONTENTS_SKY, CONTENTS_HINT
};
#endif
static void ProcessModels()
{
int i, j, type;
int placed;
int first, contents;
brush_t temp;
for (i = 0; i < g_numentities; i++)
{
if (!g_entities[i].numbrushes) // only models
continue;
// sort the contents down so stone bites water, etc
first = g_entities[i].firstbrush;
#ifdef HLCSG_SORTBRUSH_KEEPORDER
brush_t *temps = (brush_t *)malloc (g_entities[i].numbrushes * sizeof (brush_t));
hlassume (temps, assume_NoMemory);
for (j = 0; j < g_entities[i].numbrushes; j++)
{
temps[j] = g_mapbrushes[first + j];
}
int placedcontents;
bool b_placedcontents = false;
for (placed = 0; placed < g_entities[i].numbrushes; )
{
bool b_contents = false;
for (j = 0; j < g_entities[i].numbrushes; j++)
{
brush_t *brush = &temps[j];
if (b_placedcontents && brush->contents <= placedcontents)
continue;
if (b_contents && brush->contents >= contents)
continue;
b_contents = true;
contents = brush->contents;
}
for (j = 0; j < g_entities[i].numbrushes; j++)
{
brush_t *brush = &temps[j];
if (brush->contents == contents)
{
g_mapbrushes[first + placed] = *brush;
placed++;
}
}
b_placedcontents = true;
placedcontents = contents;
}
free (temps);
#else
#ifdef HLCSG_SORTBRUSH_FIX
placed = 0;
while (placed < g_entities[i].numbrushes)
{
for (j = placed; j < g_entities[i].numbrushes; j++)
{
if (j == placed)
{
contents = g_mapbrushes[first + j].contents;
}
else
{
contents = qmin(g_mapbrushes[first + j].contents, contents);
}
}
for (j = placed; j < g_entities[i].numbrushes; j++)
{
if (g_mapbrushes[first + j].contents == contents)
{
temp = g_mapbrushes[first + placed];
g_mapbrushes[first + placed] = g_mapbrushes[first + j];
g_mapbrushes[first + j] = temp;
placed++;
}
}
}
#else
placed = 0;
for (type = 0; type < NUM_TYPECONTENTS; type++) // for each of the contents types
{
contents = typecontents[type];
for (j = placed + 1; j < g_entities[i].numbrushes; j++) // for each of the model's brushes
{
// if this brush is of the contents type in this for iteration
if (g_mapbrushes[first + j].contents == contents)
{
temp = g_mapbrushes[first + placed];
g_mapbrushes[first + placed] = g_mapbrushes[j];
g_mapbrushes[j] = temp;
placed++;
}
}
}
#endif
#endif
// csg them in order
if (i == 0) // if its worldspawn....
{
NamedRunThreadsOnIndividual(g_entities[i].numbrushes, g_estimate, CSGBrush);
CheckFatal();
}
else
{
for (j = 0; j < g_entities[i].numbrushes; j++)
{
CSGBrush(first + j);
}
}
// write end of model marker
for (j = 0; j < NUM_HULLS; j++)
{
#ifdef ZHLT_DETAILBRUSH
fprintf (out[j], "-1 -1 -1 -1 -1\n");
fprintf (out_detailbrush[j], "-1\n");
#else
fprintf(out[j], "-1 -1 -1 -1\n");
#endif
}
}
}
// =====================================================================================
// SetModelCenters
// =====================================================================================
static void SetModelCenters(int entitynum)
{
int i;
int last;
char string[MAXTOKEN];
entity_t* e = &g_entities[entitynum];
BoundingBox bounds;
vec3_t center;
if ((entitynum == 0) || (e->numbrushes == 0)) // skip worldspawn and point entities
return;
if (!*ValueForKey(e, "light_origin")) // skip if its not a zhlt_flags light_origin
return;
for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++)
{
if (g_mapbrushes[i].contents != CONTENTS_ORIGIN
#ifdef HLCSG_HLBSP_CUSTOMBOUNDINGBOX
&& g_mapbrushes[i].contents != CONTENTS_BOUNDINGBOX
#endif
)
{
bounds.add(g_mapbrushes[i].hulls->bounds);
}
}
VectorAdd(bounds.m_Mins, bounds.m_Maxs, center);
VectorScale(center, 0.5, center);
safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)center[0], (int)center[1], (int)center[2]);
SetKeyValue(e, "model_center", string);
}
//
// =====================================================================================
//
// =====================================================================================
// BoundWorld
// =====================================================================================
static void BoundWorld()
{
int i;
brushhull_t* h;
world_bounds.reset();
for (i = 0; i < g_nummapbrushes; i++)
{
h = &g_mapbrushes[i].hulls[0];
if (!h->faces)
{
continue;
}
world_bounds.add(h->bounds);
}
Verbose("World bounds: (%i %i %i) to (%i %i %i)\n",
(int)world_bounds.m_Mins[0], (int)world_bounds.m_Mins[1], (int)world_bounds.m_Mins[2],
(int)world_bounds.m_Maxs[0], (int)world_bounds.m_Maxs[1], (int)world_bounds.m_Maxs[2]);
}
// =====================================================================================
// Usage
// prints out usage sheet
// =====================================================================================
static void Usage()
{
Banner(); // TODO: Call banner from main CSG process?
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(" -nowadtextures : include all used textures into bsp\n");
Log(" -wadinclude file : place textures used from wad specified into bsp\n");
Log(" -noclip : don't create clipping hull\n");
#ifdef HLCSG_CLIPECONOMY // AJM
#ifdef HLCSG_CUSTOMHULL // default clip economy off
Log(" -clipeconomy : turn clipnode economy mode on\n");
#else
Log(" -noclipeconomy : turn clipnode economy mode off\n");
#endif
#endif
#ifdef HLCSG_PRECISIONCLIP // KGP
Log(" -cliptype value : set to smallest, normalized, simple, precise, or legacy (default)\n");
#endif
#ifdef HLCSG_NULLIFY_INVISIBLE // KGP
Log(" -nullfile file : specify list of entities to retexture with NULL\n");
#endif
Log(" -onlyents : do an entity update from .map to .bsp\n");
Log(" -noskyclip : disable automatic clipping of SKY brushes\n");
Log(" -tiny # : minmum brush face surface area before it is discarded\n");
Log(" -brushunion # : threshold to warn about overlapping brushes\n\n");
Log(" -hullfile file : Reads in custom collision hull dimensions\n");
#ifdef HLCSG_WADCFG_NEW
Log(" -wadcfgfile file : wad configuration file\n");
Log(" -wadconfig name : use the old wad configuration approach (select a group from wad.cfg)\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");
#ifdef HLCSG_KEEPLOG
Log(" -noresetlog : Do not delete log file\n");
#endif
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
Log(" -verbose : compile with verbose messages\n");
Log(" -noinfo : Do not show tool configuration information\n");
#ifdef ZHLT_NULLTEX // AJM
Log(" -nonulltex : Turns off null texture stripping\n");
#endif
#ifdef HLCSG_NULLIFYAAATRIGGER
Log(" -nonullifytrigger: don't remove 'aaatrigger' texture\n");
#endif
#ifdef ZHLT_DETAIL // AJM
Log(" -nodetail : dont handle detail brushes\n");
#endif
#ifdef HLCSG_OPTIMIZELIGHTENTITY
Log(" -nolightopt : don't optimize engine light entities\n");
#endif
#ifdef HLCSG_GAMETEXTMESSAGE_UTF8
Log(" -notextconvert : don't convert game_text message from Windows ANSI to UTF8 format\n");
#endif
Log(" -dev # : compile with developer message\n\n");
#ifndef HLCSG_WADCFG_NEW
#ifdef HLCSG_WADCFG // AJM
Log(" -wadconfig name : Specify a configuration to use from wad.cfg\n");
Log(" -wadcfgfile path : Manually specify a path to the wad.cfg file\n"); //JK:
#endif
#endif
#ifdef HLCSG_AUTOWAD // AJM:
Log(" -wadautodetect : Force auto-detection of wadfiles\n");
#endif
#ifdef HLCSG_SCALESIZE
Log(" -scale # : Scale the world. Use at your own risk.\n");
#endif
Log(" mapfile : The mapfile to compile\n\n");
exit(1);
}
// =====================================================================================
// DumpWadinclude
// prints out the wadinclude list
// =====================================================================================
static void DumpWadinclude()
{
Log("Wadinclude list :\n");
WadInclude_i it;
for (it = g_WadInclude.begin(); it != g_WadInclude.end(); it++)
{
Log("[%s]\n", it->c_str());
}
}
// =====================================================================================
// Settings
// prints out settings sheet
// =====================================================================================
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");
#ifdef HLCSG_KEEPLOG
Log("reset logfile [ %7s ] [ %7s ]\n", g_resetlog ? "on" : "off", DEFAULT_RESETLOG ? "on" : "off");
#endif
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);
Log("max lighting memory [ %7d ] [ %7d ]\n", g_max_map_lightdata, DEFAULT_MAX_MAP_LIGHTDATA);
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");
// HLCSG Specific Settings
Log("noclip [ %7s ] [ %7s ]\n", g_noclip ? "on" : "off", DEFAULT_NOCLIP ? "on" : "off");
#ifdef ZHLT_NULLTEX // AJM:
Log("null texture 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
#ifdef HLCSG_CLIPECONOMY // AJM
Log("clipnode economy mode [ %7s ] [ %7s ]\n", g_bClipNazi ? "on" : "off", DEFAULT_CLIPNAZI ? "on" : "off");
#endif
#ifdef HLCSG_PRECISIONCLIP // KGP
Log("clip hull type [ %7s ] [ %7s ]\n", GetClipTypeString(g_cliptype), GetClipTypeString(DEFAULT_CLIPTYPE));
#endif
Log("onlyents [ %7s ] [ %7s ]\n", g_onlyents ? "on" : "off", DEFAULT_ONLYENTS ? "on" : "off");
Log("wadtextures [ %7s ] [ %7s ]\n", g_wadtextures ? "on" : "off", DEFAULT_WADTEXTURES ? "on" : "off");
Log("skyclip [ %7s ] [ %7s ]\n", g_skyclip ? "on" : "off", DEFAULT_SKYCLIP ? "on" : "off");
Log("hullfile [ %7s ] [ %7s ]\n", g_hullfile ? g_hullfile : "None", "None");
#ifdef HLCSG_WADCFG_NEW
Log("wad configuration file[ %7s ] [ %7s ]\n", g_wadcfgfile? g_wadcfgfile: "None", "None");
Log("wad.cfg group name [ %7s ] [ %7s ]\n", g_wadconfigname? g_wadconfigname: "None", "None");
#endif
#ifdef HLCSG_NULLIFY_INVISIBLE // KGP
Log("nullfile [ %7s ] [ %7s ]\n", g_nullfile ? g_nullfile : "None", "None");
#endif
#ifdef HLCSG_NULLIFYAAATRIGGER
Log("nullify trigger [ %7s ] [ %7s ]\n", g_nullifytrigger? "on": "off", DEFAULT_NULLIFYTRIGGER? "on": "off");
#endif
// calc min surface area
{
char tiny_penetration[10];
char default_tiny_penetration[10];
safe_snprintf(tiny_penetration, sizeof(tiny_penetration), "%3.3f", g_tiny_threshold);
safe_snprintf(default_tiny_penetration, sizeof(default_tiny_penetration), "%3.3f", DEFAULT_TINY_THRESHOLD);
Log("min surface area [ %7s ] [ %7s ]\n", tiny_penetration, default_tiny_penetration);
}
// calc union threshold
{
char brush_union[10];
char default_brush_union[10];
safe_snprintf(brush_union, sizeof(brush_union), "%3.3f", g_BrushUnionThreshold);
safe_snprintf(default_brush_union, sizeof(default_brush_union), "%3.3f", DEFAULT_BRUSH_UNION_THRESHOLD);
Log("brush union threshold [ %7s ] [ %7s ]\n", brush_union, default_brush_union);
}
#ifdef HLCSG_SCALESIZE
{
char buf1[10];
char buf2[10];
if (g_scalesize > 0)
safe_snprintf(buf1, sizeof(buf1), "%3.3f", g_scalesize);
else
strcpy (buf1, "None");
if (DEFAULT_SCALESIZE > 0)
safe_snprintf(buf2, sizeof(buf2), "%3.3f", DEFAULT_SCALESIZE);
else
strcpy (buf2, "None");
Log("map scaling [ %7s ] [ %7s ]\n", buf1, buf2);
}
#endif
#ifdef HLCSG_OPTIMIZELIGHTENTITY
Log("light name optimize [ %7s ] [ %7s ]\n", !g_nolightopt? "on" : "off", !DEFAULT_NOLIGHTOPT? "on" : "off");
#endif
#ifdef HLCSG_GAMETEXTMESSAGE_UTF8
Log("convert game_text [ %7s ] [ %7s ]\n", !g_noutf8? "on" : "off", !DEFAULT_NOUTF8? "on" : "off");
#endif
Log("\n");
}
// AJM: added in
// =====================================================================================
// CSGCleanup
// =====================================================================================
void CSGCleanup()
{
//Log("CSGCleanup\n");
#ifndef HLCSG_AUTOWAD_NEW
#ifdef HLCSG_AUTOWAD
autowad_cleanup();
#endif
#endif
#ifndef HLCSG_WADCFG_NEW
#ifdef HLCSG_WADCFG
WadCfg_cleanup();
#endif
#endif
FreeWadPaths();
}
// =====================================================================================
// Main
// Oh, come on.
// =====================================================================================
int main(const int argc, char** argv)
{
int i;
char name[_MAX_PATH]; // mapanme
double start, end; // start/end time log
const char* mapname_from_arg = NULL; // mapname path from passed argvar
g_Program = "hlcsg";
#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 (argc == 1)
Usage();
// Hard coded list of -wadinclude files, used for HINT texture brushes so lazy
// mapmakers wont cause beta testers (or possibly end users) to get a wad
// error on zhlt.wad etc
g_WadInclude.push_back("zhlt.wad");
#ifndef HLCSG_WADCFG_NEW
#ifdef HLCSG_WADCFG
memset(wadconfigname, 0, sizeof(wadconfigname));//AJM
#endif
#endif
#ifdef HLCSG_HULLBRUSH
InitDefaultHulls ();
#endif
// detect argv
for (i = 1; i < argc; i++)
{
if (!strcasecmp(argv[i], "-threads"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
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
#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
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], "-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;
}
else if (!strcasecmp(argv[i], "-skyclip"))
{
g_skyclip = true;
}
else if (!strcasecmp(argv[i], "-noskyclip"))
{
g_skyclip = false;
}
else if (!strcasecmp(argv[i], "-noclip"))
{
g_noclip = true;
}
else if (!strcasecmp(argv[i], "-onlyents"))
{
g_onlyents = true;
}
#ifdef ZHLT_NULLTEX // AJM: added in -nonulltex
else if (!strcasecmp(argv[i], "-nonulltex"))
{
g_bUseNullTex = false;
}
#endif
#ifdef HLCSG_CLIPECONOMY // AJM: added in -noclipeconomy
#ifdef HLCSG_CUSTOMHULL // default clip economy off
else if (!strcasecmp(argv[i], "-clipeconomy"))
{
g_bClipNazi = true;
}
#else
else if (!strcasecmp(argv[i], "-noclipeconomy"))
{
g_bClipNazi = false;
}
#endif
#endif
#ifdef HLCSG_PRECISIONCLIP // KGP: added in -cliptype
else if (!strcasecmp(argv[i], "-cliptype"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
++i;
if(!strcasecmp(argv[i],"smallest"))
{ g_cliptype = clip_smallest; }
else if(!strcasecmp(argv[i],"normalized"))
{ g_cliptype = clip_normalized; }
else if(!strcasecmp(argv[i],"simple"))
{ g_cliptype = clip_simple; }
else if(!strcasecmp(argv[i],"precise"))
{ g_cliptype = clip_precise; }
else if(!strcasecmp(argv[i],"legacy"))
{ g_cliptype = clip_legacy; }
}
else
{
Log("Error: -cliptype: incorrect usage of parameter\n");
Usage();
}
}
#endif
#ifndef HLCSG_WADCFG_NEW
#ifdef HLCSG_WADCFG
// AJM: added in -wadconfig
else if (!strcasecmp(argv[i], "-wadconfig"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
safe_strncpy(wadconfigname, argv[++i], MAX_WAD_CFG_NAME);
if (strlen(argv[i]) > MAX_WAD_CFG_NAME)
{
Warning("wad configuration name was truncated to %i chars", MAX_WAD_CFG_NAME);
wadconfigname[MAX_WAD_CFG_NAME] = 0;
}
}
else
{
Log("Error: -wadconfig: incorrect usage of parameter\n");
Usage();
}
}
//JK: added in -wadcfgfile
else if (!strcasecmp(argv[i], "-wadcfgfile"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_wadcfgfile = argv[++i];
}
else
{
Log("Error: -wadcfgfile: incorrect usage of parameter\n");
Usage();
}
}
#endif
#endif
#ifdef HLCSG_NULLIFY_INVISIBLE
else if (!strcasecmp(argv[i], "-nullfile"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_nullfile = argv[++i];
}
else
{
Log("Error: -nullfile: expected path to null ent file following parameter\n");
Usage();
}
}
#endif
#ifdef HLCSG_AUTOWAD // AJM
else if (!strcasecmp(argv[i], "-wadautodetect"))
{
g_bWadAutoDetect = true;
}
#endif
#ifdef ZHLT_DETAIL // AJM
else if (!strcasecmp(argv[i], "-nodetail"))
{
g_bDetailBrushes = false;
}
#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], "-nowadtextures"))
{
g_wadtextures = false;
}
else if (!strcasecmp(argv[i], "-wadinclude"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_WadInclude.push_back(argv[++i]);
}
else
{
Usage();
}
}
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();
}
}
else if (!strcasecmp(argv[i], "-brushunion"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_BrushUnionThreshold = (float)atof(argv[++i]);
}
else
{
Usage();
}
}
else if (!strcasecmp(argv[i], "-tiny"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_tiny_threshold = (float)atof(argv[++i]);
}
else
{
Usage();
}
}
else if (!strcasecmp(argv[i], "-hullfile"))
{
if (i + 1 < argc) //added "1" .--vluzacn
{
g_hullfile = argv[++i];
}
else
{
Usage();
}
}
#ifdef HLCSG_WADCFG_NEW
else if (!strcasecmp (argv[i], "-wadcfgfile"))
{
if (i + 1 < argc)
{
g_wadcfgfile = argv[++i];
}
else
{
Usage ();
}
}
else if (!strcasecmp (argv[i], "-wadconfig"))
{
if (i + 1 < argc)
{
g_wadconfigname = argv[++i];
}
else
{
Usage ();
}
}
#endif
#ifdef HLCSG_SCALESIZE
else if (!strcasecmp(argv[i], "-scale"))
{
if (i + 1 < argc)
{
g_scalesize = atof(argv[++i]);
}
else
{
Usage();
}
}
#endif
#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
#ifdef HLCSG_KEEPLOG
else if (!strcasecmp (argv[i], "-noresetlog"))
{
g_resetlog = false;
}
#endif
#ifdef HLCSG_OPTIMIZELIGHTENTITY
else if (!strcasecmp (argv[i], "-nolightopt"))
{
g_nolightopt = true;
}
#endif
#ifdef HLCSG_GAMETEXTMESSAGE_UTF8
else if (!strcasecmp (argv[i], "-notextconvert"))
{
g_noutf8 = true;
}
#endif
#ifdef HLCSG_VIEWSURFACE
else if (!strcasecmp (argv[i], "-viewsurface"))
{
g_viewsurface = true;
}
#endif
#ifdef HLCSG_NULLIFYAAATRIGGER
else if (!strcasecmp (argv[i], "-nonullifytrigger"))
{
g_nullifytrigger = false;
}
#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();
}
}
// no mapfile?
if (!mapname_from_arg)
{
// what a shame.
Log("No mapfile specified\n");
Usage();
}
// handle mapname
safe_strncpy(g_Mapname, mapname_from_arg, _MAX_PATH);
FlipSlashes(g_Mapname);
StripExtension(g_Mapname);
// onlyents
if (!g_onlyents)
ResetTmpFiles();
// other stuff
ResetErrorLog();
#ifdef HLCSG_KEEPLOG
if (!g_onlyents && g_resetlog)
#endif
ResetLog();
OpenLog(g_clientid);
atexit(CloseLog);
#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
#ifdef ZHLT_64BIT_FIX
#ifdef PLATFORM_CAN_CALC_EXTENT
hlassume (CalcFaceExtents_test (), assume_first);
#endif
#endif
atexit(CSGCleanup); // AJM
dtexdata_init();
atexit(dtexdata_free);
// START CSG
// AJM: re-arranged some stuff up here so that the mapfile is loaded
// before settings are finalised and printed out, so that the info_compile_parameters
// entity can be dealt with effectively
start = I_FloatTime();
#ifdef HLCSG_HULLFILE_AUTOPATH
if (g_hullfile)
{
char temp[_MAX_PATH];
char test[_MAX_PATH];
safe_strncpy (temp, g_Mapname, _MAX_PATH);
ExtractFilePath (temp, test);
safe_strncat (test, g_hullfile, _MAX_PATH);
if (q_exists (test))
{
g_hullfile = strdup (test);
}
else
{
#ifdef SYSTEM_WIN32
GetModuleFileName (NULL, temp, _MAX_PATH);
#else
safe_strncpy (temp, argv[0], _MAX_PATH);
#endif
ExtractFilePath (temp, test);
safe_strncat (test, g_hullfile, _MAX_PATH);
if (q_exists (test))
{
g_hullfile = strdup (test);
}
}
}
if (g_nullfile)
{
char temp[_MAX_PATH];
char test[_MAX_PATH];
safe_strncpy (temp, g_Mapname, _MAX_PATH);
ExtractFilePath (temp, test);
safe_strncat (test, g_nullfile, _MAX_PATH);
if (q_exists (test))
{
g_nullfile = strdup (test);
}
else
{
#ifdef SYSTEM_WIN32
GetModuleFileName (NULL, temp, _MAX_PATH);
#else
safe_strncpy (temp, argv[0], _MAX_PATH);
#endif
ExtractFilePath (temp, test);
safe_strncat (test, g_nullfile, _MAX_PATH);
if (q_exists (test))
{
g_nullfile = strdup (test);
}
}
}
#endif
#ifdef HLCSG_WADCFG_NEW
if (g_wadcfgfile)
{
char temp[_MAX_PATH];
char test[_MAX_PATH];
safe_strncpy (temp, g_Mapname, _MAX_PATH);
ExtractFilePath (temp, test);
safe_strncat (test, g_wadcfgfile, _MAX_PATH);
if (q_exists (test))
{
g_wadcfgfile = strdup (test);
}
else
{
#ifdef SYSTEM_WIN32
GetModuleFileName (NULL, temp, _MAX_PATH);
#else
safe_strncpy (temp, argv[0], _MAX_PATH);
#endif
ExtractFilePath (temp, test);
safe_strncat (test, g_wadcfgfile, _MAX_PATH);
if (q_exists (test))
{
g_wadcfgfile = strdup (test);
}
}
}
#endif
LoadHullfile(g_hullfile); // if the user specified a hull file, load it now
#ifdef HLCSG_NULLIFY_INVISIBLE
if(g_bUseNullTex)
{ properties_initialize(g_nullfile); }
#endif
safe_strncpy(name, mapname_from_arg, _MAX_PATH); // make a copy of the nap name
#ifdef ZHLT_DEFAULTEXTENSION_FIX
FlipSlashes(name);
#endif
DefaultExtension(name, ".map"); // might be .reg
LoadMapFile(name);
ThreadSetDefault();
ThreadSetPriority(g_threadpriority);
Settings();
#ifdef HLCSG_GAMETEXTMESSAGE_UTF8
if (!g_noutf8)
{
int count = 0;
for (i = 0; i < g_numentities; i++)
{
entity_t *ent = &g_entities[i];
const char *value;
char *newvalue;
if (strcmp (ValueForKey (ent, "classname"), "game_text"))
{
continue;
}
value = ValueForKey (ent, "message");
if (*value)
{
newvalue = ANSItoUTF8 (value);
if (strcmp (newvalue, value))
{
SetKeyValue (ent, "message", newvalue);
count++;
}
free (newvalue);
}
}
if (count)
{
Log ("%d game_text messages converted from Windows ANSI(CP_ACP) to UTF-8 encoding\n", count);
}
}
#endif
#ifdef HLCSG_ONLYENTS_NOWADCHANGE
if (!g_onlyents)
{
#endif
#ifdef HLCSG_WADCFG_NEW
if (g_wadconfigname)
{
char temp[_MAX_PATH];
char test[_MAX_PATH];
#ifdef SYSTEM_WIN32
GetModuleFileName (NULL, temp, _MAX_PATH);
#else
safe_strncpy (temp, argv[0], _MAX_PATH);
#endif
ExtractFilePath (temp, test);
safe_strncat (test, "wad.cfg", _MAX_PATH);
if (g_wadcfgfile)
{
safe_strncpy (test, g_wadcfgfile, _MAX_PATH);
}
LoadWadconfig (test, g_wadconfigname);
}
else if (g_wadcfgfile)
{
if (!q_exists (g_wadcfgfile))
{
Error("Couldn't find wad configuration file '%s'\n", g_wadcfgfile);
}
LoadWadcfgfile (g_wadcfgfile);
}
else
{
Log("Using mapfile wad configuration\n");
GetUsedWads();
}
#endif
#ifndef HLCSG_WADCFG_NEW
#ifdef HLCSG_WADCFG // AJM
// figure out what to do with the texture settings
if (wadconfigname[0]) // custom wad configuations will take precedence
{
LoadWadConfigFile();
ProcessWadConfiguration();
}
else
{
Log("Using mapfile wad configuration\n");
}
if (!g_bWadConfigsLoaded) // dont try and override wad.cfg
#endif
{
GetUsedWads();
}
#endif
#ifdef HLCSG_AUTOWAD
if (g_bWadAutoDetect)
{
Log("Wadfiles not in use by the map will be excluded\n");
}
#endif
DumpWadinclude();
Log("\n");
#ifdef HLCSG_ONLYENTS_NOWADCHANGE
}
#endif
// if onlyents, just grab the entites and resave
if (g_onlyents)
{
char out[_MAX_PATH];
safe_snprintf(out, _MAX_PATH, "%s.bsp", g_Mapname);
LoadBSPFile(out);
#ifndef HLCSG_ONLYENTS_NOWADCHANGE
LoadWadincludeFile(g_Mapname);
HandleWadinclude();
#endif
// Write it all back out again.
#ifndef HLCSG_CHART_FIX
if (g_chart)
{
PrintBSPFileSizes();
}
#endif
WriteBSP(g_Mapname);
end = I_FloatTime();
LogTimeElapsed(end - start);
return 0;
}
#ifndef HLCSG_ONLYENTS_NOWADCHANGE
else
{
SaveWadincludeFile(g_Mapname);
}
#endif
#ifdef HLCSG_CLIPECONOMY // AJM
CheckForNoClip();
#endif
// createbrush
NamedRunThreadsOnIndividual(g_nummapbrushes, g_estimate, CreateBrush);
CheckFatal();
#ifdef HLCSG_PRECISIONCLIP // KGP - drop TEX_BEVEL flag
#ifndef HLCSG_CUSTOMHULL
FixBevelTextures();
#endif
#endif
// boundworld
BoundWorld();
Verbose("%5i map planes\n", g_nummapplanes);
// Set model centers
for (i = 0; i < g_numentities; i++) SetModelCenters (i); //NamedRunThreadsOnIndividual(g_numentities, g_estimate, SetModelCenters); //--vluzacn
// Calc brush unions
if ((g_BrushUnionThreshold > 0.0) && (g_BrushUnionThreshold <= 100.0))
{
NamedRunThreadsOnIndividual(g_nummapbrushes, g_estimate, CalculateBrushUnions);
}
// open hull files
for (i = 0; i < NUM_HULLS; i++)
{
char name[_MAX_PATH];
safe_snprintf(name, _MAX_PATH, "%s.p%i", g_Mapname, i);
out[i] = fopen(name, "w");
if (!out[i])
Error("Couldn't open %s", name);
#ifdef ZHLT_DETAILBRUSH
safe_snprintf(name, _MAX_PATH, "%s.b%i", g_Mapname, i);
out_detailbrush[i] = fopen(name, "w");
if (!out_detailbrush[i])
Error("Couldn't open %s", name);
#endif
#ifdef HLCSG_VIEWSURFACE
if (g_viewsurface)
{
safe_snprintf (name, _MAX_PATH, "%s_surface%i.pts", g_Mapname, i);
out_view[i] = fopen (name, "w");
if (!out[i])
Error ("Counldn't open %s", name);
}
#endif
}
#ifdef HLCSG_HLBSP_ALLOWEMPTYENTITY
{
FILE *f;
char name[_MAX_PATH];
safe_snprintf (name, _MAX_PATH, "%s.hsz", g_Mapname);
f = fopen (name, "w");
if (!f)
Error("Couldn't open %s", name);
float x1,y1,z1;
float x2,y2,z2;
for (i = 0; i < NUM_HULLS; i++)
{
x1 = g_hull_size[i][0][0];
y1 = g_hull_size[i][0][1];
z1 = g_hull_size[i][0][2];
x2 = g_hull_size[i][1][0];
y2 = g_hull_size[i][1][1];
z2 = g_hull_size[i][1][2];
fprintf (f, "%g %g %g %g %g %g\n", x1, y1, z1, x2, y2, z2);
}
fclose (f);
}
#endif
ProcessModels();
Verbose("%5i csg faces\n", c_csgfaces);
Verbose("%5i used faces\n", c_outfaces);
Verbose("%5i tiny faces\n", c_tiny);
Verbose("%5i tiny clips\n", c_tiny_clip);
// close hull files
for (i = 0; i < NUM_HULLS; i++)
{
fclose(out[i]);
#ifdef ZHLT_DETAILBRUSH
fclose (out_detailbrush[i]);
#endif
#ifdef HLCSG_VIEWSURFACE
if (g_viewsurface)
{
fclose (out_view[i]);
}
#endif
}
EmitPlanes();
#ifndef HLCSG_CHART_FIX
if (g_chart)
PrintBSPFileSizes();
#endif
WriteBSP(g_Mapname);
// AJM: debug
#if 0
Log("\n---------------------------------------\n"
"Map Plane Usage:\n"
" # normal origin dist type\n"
" ( x, y, z) ( x, y, z) ( )\n"
);
for (i = 0; i < g_nummapplanes; i++)
{
plane_t* p = &g_mapplanes[i];
Log(
"%3i (%4.0f, %4.0f, %4.0f) (%4.0f, %4.0f, %4.0f) (%5.0f) %i\n",
i,
p->normal[1], p->normal[2], p->normal[3],
p->origin[1], p->origin[2], p->origin[3],
p->dist,
p->type
);
}
Log("---------------------------------------\n\n");
#endif
// elapsed time
end = I_FloatTime();
LogTimeElapsed(end - start);
#ifdef ZHLT_PARAMFILE
}
}
#endif
return 0;
}