//========= Copyright © 1996-2005, 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 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 &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 ; inumpoints ; 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 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 &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 &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 &portalList, const CUtlVector &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 leaves; BuildVisLeafList_r( headnode, leaves ); NumberLeafs (leaves); CUtlVector 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) ); }