You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
374 lines
8.9 KiB
374 lines
8.9 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
|
|
#include "vbsp.h" |
|
#include "collisionutils.h" |
|
/* |
|
============================================================================== |
|
|
|
PORTAL FILE GENERATION |
|
|
|
Save out name.prt for qvis to read |
|
============================================================================== |
|
*/ |
|
|
|
|
|
#define PORTALFILE "PRT1" |
|
|
|
struct cluster_portals_t |
|
{ |
|
CUtlVector<portal_t *> portals; |
|
}; |
|
|
|
int num_visclusters; // clusters the player can be in |
|
int num_visportals; |
|
|
|
int g_SkyCluster = -1; |
|
|
|
void WriteFloat (FILE *f, vec_t v) |
|
{ |
|
if ( fabs(v - RoundInt(v)) < 0.001 ) |
|
fprintf (f,"%i ",(int)RoundInt(v)); |
|
else |
|
fprintf (f,"%f ",v); |
|
} |
|
|
|
|
|
/* |
|
================= |
|
WritePortalFile_r |
|
================= |
|
*/ |
|
void WritePortalFile(FILE *pFile, const CUtlVector<cluster_portals_t> &list) |
|
{ |
|
portal_t *p; |
|
winding_t *w; |
|
Vector normal; |
|
vec_t dist; |
|
|
|
for ( int clusterIndex = 0; clusterIndex < list.Count(); clusterIndex++ ) |
|
{ |
|
for ( int j = 0; j < list[clusterIndex].portals.Count(); j++ ) |
|
{ |
|
p = list[clusterIndex].portals[j]; |
|
w = p->winding; |
|
// write out to the file |
|
|
|
// sometimes planes get turned around when they are very near |
|
// the changeover point between different axis. interpret the |
|
// plane the same way vis will, and flip the side orders if needed |
|
// FIXME: is this still relevent? |
|
WindingPlane (w, normal, &dist); |
|
if ( DotProduct (p->plane.normal, normal) < 0.99 ) |
|
{ // backwards... |
|
fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster); |
|
} |
|
else |
|
{ |
|
fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster); |
|
} |
|
|
|
for (int i=0 ; i<w->numpoints ; i++) |
|
{ |
|
fprintf (pFile,"("); |
|
WriteFloat (pFile, w->p[i][0]); |
|
WriteFloat (pFile, w->p[i][1]); |
|
WriteFloat (pFile, w->p[i][2]); |
|
fprintf (pFile,") "); |
|
} |
|
fprintf (pFile,"\n"); |
|
} |
|
} |
|
} |
|
|
|
struct viscluster_t |
|
{ |
|
bspbrush_t *pBrushes; |
|
int clusterIndex; |
|
int leafCount; |
|
int leafStart; |
|
}; |
|
|
|
static CUtlVector<viscluster_t> g_VisClusters; |
|
|
|
// add to the list of brushes the merge leaves into single vis clusters |
|
void AddVisCluster( entity_t *pFuncVisCluster ) |
|
{ |
|
viscluster_t tmp; |
|
Vector clipMins, clipMaxs; |
|
clipMins[0] = clipMins[1] = clipMins[2] = MIN_COORD_INTEGER; |
|
clipMaxs[0] = clipMaxs[1] = clipMaxs[2] = MAX_COORD_INTEGER; |
|
|
|
// build the map brushes out into the minimum non-overlapping set of brushes |
|
bspbrush_t *pBSPBrush = MakeBspBrushList( pFuncVisCluster->firstbrush, pFuncVisCluster->firstbrush + pFuncVisCluster->numbrushes, |
|
clipMins, clipMaxs, NO_DETAIL); |
|
tmp.pBrushes = ChopBrushes( pBSPBrush ); |
|
|
|
// store the entry in the list |
|
tmp.clusterIndex = -1; |
|
tmp.leafCount = 0; |
|
tmp.leafStart = 0; |
|
|
|
#if DEBUG_VISUALIZE_CLUSTERS |
|
int debug = atoi(ValueForKey(pFuncVisCluster,"debug")); |
|
if ( debug ) |
|
{ |
|
Msg("Debug vis cluster %d\n", g_VisClusters.Count() ); |
|
} |
|
#endif |
|
|
|
g_VisClusters.AddToTail( tmp ); |
|
|
|
// clear out this entity so it won't get written to the bsp |
|
pFuncVisCluster->epairs = NULL; |
|
pFuncVisCluster->numbrushes = 0; |
|
} |
|
|
|
// returns the total overlapping volume of intersection between the node and the brush list |
|
float VolumeOfIntersection( bspbrush_t *pBrushList, node_t *pNode ) |
|
{ |
|
float volume = 0.0f; |
|
for ( bspbrush_t *pBrush = pBrushList; pBrush; pBrush = pBrush->next ) |
|
{ |
|
if ( IsBoxIntersectingBox( pNode->mins, pNode->maxs, pBrush->mins, pBrush->maxs ) ) |
|
{ |
|
bspbrush_t *pIntersect = IntersectBrush( pNode->volume, pBrush ); |
|
if ( pIntersect ) |
|
{ |
|
volume += BrushVolume( pIntersect ); |
|
FreeBrush( pIntersect ); |
|
} |
|
} |
|
} |
|
|
|
return volume; |
|
} |
|
|
|
// Search for a forced cluster that this node is within |
|
// NOTE: Returns the first one found, these won't merge themselves together |
|
int GetVisCluster( node_t *pNode ) |
|
{ |
|
float maxVolume = BrushVolume(pNode->volume) * 0.10f; // needs to cover at least 10% of the volume to overlap |
|
int maxIndex = -1; |
|
// UNDONE: This could get slow |
|
for ( int i = g_VisClusters.Count(); --i >= 0; ) |
|
{ |
|
float volume = VolumeOfIntersection( g_VisClusters[i].pBrushes, pNode ); |
|
if ( volume > maxVolume ) |
|
{ |
|
volume = maxVolume; |
|
maxIndex = i; |
|
} |
|
} |
|
return maxIndex; |
|
} |
|
/* |
|
================ |
|
NumberLeafs_r |
|
================ |
|
*/ |
|
void BuildVisLeafList_r (node_t *node, CUtlVector<node_t *> &leaves) |
|
{ |
|
if (node->planenum != PLANENUM_LEAF) |
|
{ // decision node |
|
node->cluster = -99; |
|
BuildVisLeafList_r (node->children[0], leaves); |
|
BuildVisLeafList_r (node->children[1], leaves); |
|
return; |
|
} |
|
|
|
if ( node->contents & CONTENTS_SOLID ) |
|
{ // solid block, viewpoint never inside |
|
node->cluster = -1; |
|
return; |
|
} |
|
leaves.AddToTail(node); |
|
} |
|
|
|
// Give each leaf in the list of empty leaves a vis cluster number |
|
// some are within func_viscluster volumes and will be merged together |
|
// every other leaf gets its own unique number |
|
void NumberLeafs( const CUtlVector<node_t *> &leaves ) |
|
{ |
|
for ( int i = 0; i < leaves.Count(); i++ ) |
|
{ |
|
node_t *node = leaves[i]; |
|
int visCluster = GetVisCluster( node ); |
|
if ( visCluster >= 0 ) |
|
{ |
|
if ( g_VisClusters[visCluster].clusterIndex < 0 ) |
|
{ |
|
g_VisClusters[visCluster].clusterIndex = num_visclusters; |
|
num_visclusters++; |
|
} |
|
g_VisClusters[visCluster].leafCount++; |
|
node->cluster = g_VisClusters[visCluster].clusterIndex; |
|
} |
|
else |
|
{ |
|
if ( !g_bSkyVis && Is3DSkyboxArea( node->area ) ) |
|
{ |
|
if ( g_SkyCluster < 0 ) |
|
{ |
|
// allocate a cluster for the sky |
|
g_SkyCluster = num_visclusters; |
|
num_visclusters++; |
|
} |
|
node->cluster = g_SkyCluster; |
|
} |
|
else |
|
{ |
|
node->cluster = num_visclusters; |
|
num_visclusters++; |
|
} |
|
} |
|
} |
|
|
|
#if DEBUG_VISUALIZE_CLUSTERS |
|
for ( int i = 0; i < g_VisClusters.Count(); i++ ) |
|
{ |
|
char name[256]; |
|
sprintf(name, "u:\\main\\game\\ep2\\maps\\vis_%02d.gl", i ); |
|
FileHandle_t fp = g_pFileSystem->Open( name, "w" ); |
|
Msg("Writing %s\n", name ); |
|
for ( bspbrush_t *pBrush = g_VisClusters[i].pBrushes; pBrush; pBrush = pBrush->next ) |
|
{ |
|
for (int i = 0; i < pBrush->numsides; i++ ) |
|
OutputWindingColor( pBrush->sides[i].winding, fp, 0, 255, 0 ); |
|
} |
|
for ( int j = 0; j < leaves.Count(); j++ ) |
|
{ |
|
if ( leaves[j]->cluster == g_VisClusters[i].clusterIndex ) |
|
{ |
|
bspbrush_t *pBrush = leaves[j]->volume; |
|
for (int k = 0; k < pBrush->numsides; k++ ) |
|
OutputWindingColor( pBrush->sides[k].winding, fp, 64 + (j&31), 64, 64 - (j&31) ); |
|
} |
|
} |
|
g_pFileSystem->Close(fp); |
|
} |
|
#endif |
|
} |
|
|
|
// build a list of all vis portals that connect clusters |
|
int BuildPortalList( CUtlVector<cluster_portals_t> &portalList, const CUtlVector<node_t *> &leaves ) |
|
{ |
|
int portalCount = 0; |
|
for ( int i = 0; i < leaves.Count(); i++ ) |
|
{ |
|
node_t *node = leaves[i]; |
|
// count the portals |
|
for (portal_t *p = node->portals ; p ; ) |
|
{ |
|
if (p->nodes[0] == node) // only write out from first leaf |
|
{ |
|
if ( p->nodes[0]->cluster != p->nodes[1]->cluster ) |
|
{ |
|
if (Portal_VisFlood (p)) |
|
{ |
|
portalCount++; |
|
portalList[node->cluster].portals.AddToTail(p); |
|
} |
|
} |
|
p = p->next[0]; |
|
} |
|
else |
|
p = p->next[1]; |
|
} |
|
} |
|
return portalCount; |
|
} |
|
|
|
|
|
/* |
|
================ |
|
CreateVisPortals_r |
|
================ |
|
*/ |
|
void CreateVisPortals_r (node_t *node) |
|
{ |
|
// stop as soon as we get to a leaf |
|
if (node->planenum == PLANENUM_LEAF ) |
|
return; |
|
|
|
MakeNodePortal (node); |
|
SplitNodePortals (node); |
|
|
|
CreateVisPortals_r (node->children[0]); |
|
CreateVisPortals_r (node->children[1]); |
|
} |
|
|
|
int clusterleaf; |
|
void SaveClusters_r (node_t *node) |
|
{ |
|
if (node->planenum == PLANENUM_LEAF) |
|
{ |
|
dleafs[clusterleaf++].cluster = node->cluster; |
|
return; |
|
} |
|
SaveClusters_r (node->children[0]); |
|
SaveClusters_r (node->children[1]); |
|
} |
|
|
|
/* |
|
================ |
|
WritePortalFile |
|
================ |
|
*/ |
|
void WritePortalFile (tree_t *tree) |
|
{ |
|
char filename[1024]; |
|
node_t *headnode; |
|
int start = Plat_FloatTime(); |
|
|
|
qprintf ("--- WritePortalFile ---\n"); |
|
|
|
sprintf (filename, "%s.prt", source); |
|
Msg ("writing %s...", filename); |
|
|
|
headnode = tree->headnode; |
|
|
|
FreeTreePortals_r (headnode); |
|
MakeHeadnodePortals (tree); |
|
|
|
CreateVisPortals_r (headnode); |
|
|
|
// set the cluster field in every leaf and count the total number of portals |
|
num_visclusters = 0; |
|
Msg("Building visibility clusters...\n"); |
|
CUtlVector<node_t *> leaves; |
|
BuildVisLeafList_r( headnode, leaves ); |
|
|
|
NumberLeafs (leaves); |
|
CUtlVector<cluster_portals_t> portalList; |
|
portalList.SetCount( num_visclusters ); |
|
num_visportals = BuildPortalList( portalList, leaves ); |
|
// write the file |
|
FILE *pf = fopen (filename, "w"); |
|
if (!pf) |
|
Error ("Error opening %s", filename); |
|
|
|
fprintf (pf, "%s\n", PORTALFILE); |
|
fprintf (pf, "%i\n", num_visclusters); |
|
fprintf (pf, "%i\n", num_visportals); |
|
|
|
qprintf ("%5i visclusters\n", num_visclusters); |
|
qprintf ("%5i visportals\n", num_visportals); |
|
|
|
WritePortalFile(pf, portalList); |
|
|
|
fclose (pf); |
|
|
|
// we need to store the clusters out now because ordering |
|
// issues made us do this after writebsp... |
|
clusterleaf = 1; |
|
SaveClusters_r (headnode); |
|
|
|
Msg("done (%d)\n", (int)(Plat_FloatTime() - start) ); |
|
} |
|
|
|
|