From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/utils/vbsp/writebsp.cpp | 3104 ++++++++++++++++++++-------------------- 1 file changed, 1552 insertions(+), 1552 deletions(-) (limited to 'mp/src/utils/vbsp/writebsp.cpp') diff --git a/mp/src/utils/vbsp/writebsp.cpp b/mp/src/utils/vbsp/writebsp.cpp index c776bede..60d05980 100644 --- a/mp/src/utils/vbsp/writebsp.cpp +++ b/mp/src/utils/vbsp/writebsp.cpp @@ -1,1552 +1,1552 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#include "vbsp.h" -#include "disp_vbsp.h" -#include "utlvector.h" -#include "faces.h" -#include "builddisp.h" -#include "tier1/strtools.h" -#include "utilmatlib.h" -#include "utldict.h" -#include "map.h" - -int c_nofaces; -int c_facenodes; - -// NOTE: This is a global used to link faces back to the tree node/portals they came from -// it's used when filling water volumes -node_t *dfacenodes[MAX_MAP_FACES]; - - -/* -========================================================= - -ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES - -========================================================= -*/ - -void EmitFaceVertexes (face_t **list, face_t *f); -void AssignOccluderAreas(); - -/* -============ -EmitPlanes - -There is no oportunity to discard planes, because all of the original -brushes will be saved in the map. -============ -*/ -void EmitPlanes (void) -{ - int i; - dplane_t *dp; - plane_t *mp; - int planetranslate[MAX_MAP_PLANES]; - - mp = g_MainMap->mapplanes; - for (i=0 ; inummapplanes ; i++, mp++) - { - dp = &dplanes[numplanes]; - planetranslate[i] = numplanes; - VectorCopy ( mp->normal, dp->normal); - dp->dist = mp->dist; - dp->type = mp->type; - numplanes++; - } -} - - -//======================================================== - -void EmitMarkFace (dleaf_t *leaf_p, face_t *f) -{ - int i; - int facenum; - - while (f->merged) - f = f->merged; - - if (f->split[0]) - { - EmitMarkFace (leaf_p, f->split[0]); - EmitMarkFace (leaf_p, f->split[1]); - return; - } - - facenum = f->outputnumber; - if (facenum == -1) - return; // degenerate face - - if (facenum < 0 || facenum >= numfaces) - Error ("Bad leafface"); - for (i=leaf_p->firstleafface ; i= MAX_MAP_LEAFFACES) - Error ("Too many detail brush faces, max = %d\n", MAX_MAP_LEAFFACES); - - dleaffaces[numleaffaces] = facenum; - numleaffaces++; - } - -} - - -/* -================== -EmitLeaf -================== -*/ -void EmitLeaf (node_t *node) -{ - dleaf_t *leaf_p; - portal_t *p; - int s; - face_t *f; - bspbrush_t *b; - int i; - int brushnum; - leafface_t *pList; - - // emit a leaf - if (numleafs >= MAX_MAP_LEAFS) - Error ("Too many BSP leaves, max = %d", MAX_MAP_LEAFS); - - node->diskId = numleafs; - leaf_p = &dleafs[numleafs]; - numleafs++; - - if( nummodels == 0 ) - { - leaf_p->cluster = node->cluster; - } - else - { - // Submodels don't have clusters. If this isn't set to -1 here, then there - // will be multiple leaves (albeit from different models) that reference - // the same cluster and parts of the code like ivp.cpp's ConvertWaterModelToPhysCollide - // won't work. - leaf_p->cluster = -1; - } - - leaf_p->contents = node->contents; - leaf_p->area = node->area; - - // By default, assume the leaf can see the skybox. - // VRAD will do the actual computation to see if it really can see the skybox - leaf_p->flags = LEAF_FLAGS_SKY; - - // - // write bounding box info - // - VECTOR_COPY (node->mins, leaf_p->mins); - VECTOR_COPY (node->maxs, leaf_p->maxs); - - // - // write the leafbrushes - // - leaf_p->firstleafbrush = numleafbrushes; - for (b=node->brushlist ; b ; b=b->next) - { - if (numleafbrushes >= MAX_MAP_LEAFBRUSHES) - Error ("Too many brushes in one leaf, max = %d", MAX_MAP_LEAFBRUSHES); - - brushnum = b->original - g_MainMap->mapbrushes; - for (i=leaf_p->firstleafbrush ; inumleafbrushes = numleafbrushes - leaf_p->firstleafbrush; - - // - // write the leaffaces - // - if (leaf_p->contents & CONTENTS_SOLID) - return; // no leaffaces in solids - - leaf_p->firstleafface = numleaffaces; - - for (p = node->portals ; p ; p = p->next[s]) - { - s = (p->nodes[1] == node); - f = p->face[s]; - if (!f) - continue; // not a visible portal - - EmitMarkFace (leaf_p, f); - } - - // emit the detail faces - for ( pList = node->leaffacelist; pList; pList = pList->pNext ) - { - EmitMarkFace( leaf_p, pList->pFace ); - } - - - leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface; -} - -// per face plane - original face "side" list -side_t *pOrigFaceSideList[MAX_MAP_PLANES]; - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CreateOrigFace( face_t *f ) -{ - int i, j; - dface_t *of; - side_t *side; - int vIndices[128]; - int eIndex[2]; - winding_t *pWinding; - - // not a real face! - if( !f->w ) - return -1; - - // get the original face -- the "side" - side = f->originalface; - - // get the original face winding - if( !side->winding ) - { - return -1; - } - - // - // get the next original face - // - if( numorigfaces >= MAX_MAP_FACES ) - Error( "Too many faces in map, max = %d", MAX_MAP_FACES ); - of = &dorigfaces[numorigfaces]; - numorigfaces++; - - // set original face to -1 -- it is an origianl face! - of->origFace = -1; - - // - // add side to plane list - // - side->next = pOrigFaceSideList[f->planenum]; - pOrigFaceSideList[f->planenum] = side; - side->origIndex = numorigfaces - 1; - - pWinding = CopyWinding( side->winding ); - - // - // plane info - // - of->planenum = side->planenum; - if ( side->contents & CONTENTS_DETAIL ) - of->onNode = 0; - else - of->onNode = 1; - of->side = side->planenum & 1; - - // - // edge info - // - of->firstedge = numsurfedges; - of->numedges = side->winding->numpoints; - - // - // material info - // - of->texinfo = side->texinfo; - of->dispinfo = f->dispinfo; - - // - // save the vertices - // - for( i = 0; i < pWinding->numpoints; i++ ) - { - // - // compare vertices - // - vIndices[i] = GetVertexnum( pWinding->p[i] ); - } - - // - // save off points -- as edges - // - for( i = 0; i < pWinding->numpoints; i++ ) - { - // - // look for matching edges first - // - eIndex[0] = vIndices[i]; - eIndex[1] = vIndices[(i+1)%pWinding->numpoints]; - - for( j = firstmodeledge; j < numedges; j++ ) - { - if( ( eIndex[0] == dedges[j].v[1] ) && - ( eIndex[1] == dedges[j].v[0] ) && - ( edgefaces[j][0]->contents == f->contents ) ) - { - // check for multiple backward edges!! -- shouldn't have - if( edgefaces[j][1] ) - continue; - - // set back edge - edgefaces[j][1] = f; - - // - // get next surface edge - // - if( numsurfedges >= MAX_MAP_SURFEDGES ) - Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" ); - dsurfedges[numsurfedges] = -j; - numsurfedges++; - break; - } - } - - if( j == numedges ) - { - // - // get next edge - // - AddEdge( eIndex[0], eIndex[1], f ); - - // - // get next surface edge - // - if( numsurfedges >= MAX_MAP_SURFEDGES ) - Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" ); - dsurfedges[numsurfedges] = ( numedges - 1 ); - numsurfedges++; - } - } - - // return the index - return ( numorigfaces - 1 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: search for a face within the origface list and return the index if -// found -// Input: f - the face to compare -// Output: the index of the face it found, -1 if not found -//----------------------------------------------------------------------------- -int FindOrigFace( face_t *f ) -{ - int i; - static int bClear = 0; - side_t *pSide; - - // - // initially clear the face side lists (per face plane) - // - if( !bClear ) - { - for( i = 0; i < MAX_MAP_PLANES; i++ ) - { - pOrigFaceSideList[i] = NULL; - } - bClear = 1; - } - - // - // compare the sides - // - for( pSide = pOrigFaceSideList[f->planenum]; pSide; pSide = pSide->next ) - { - if( pSide == f->originalface ) - return pSide->origIndex; - } - - // original face not found in list - return -1; -} - - -//----------------------------------------------------------------------------- -// Purpose: to find an the original face within the list of original faces, if -// a match is not found then create a new origFace -- either way pass -// back the index of the origface in the list -// Input: f - face containing the original face information -// Output: the index of the origface in the origface list -//----------------------------------------------------------------------------- -int FindOrCreateOrigFace( face_t *f ) -{ - int index; - - // check for an original face - if( !f->originalface ) - return -1; - - // - // find or create a orig face and return the index - // - index = FindOrigFace( f ); - - if( index == -1 ) - return CreateOrigFace( f ); - else if( index == -2 ) - return -1; - - return index; -} - -/* -================== -EmitFace -================== -*/ -void EmitFace( face_t *f, qboolean onNode ) -{ - dface_t *df; - int i; - int e; - -// void SubdivideFaceBySubdivSize( face_t *f ); // garymcthack -// SubdivideFaceBySubdivSize( f ); - - // set initial output number - f->outputnumber = -1; - - // degenerated - if( f->numpoints < 3 ) - return; - - // not a final face - if( f->merged || f->split[0] || f->split[1] ) - return; - - // don't emit NODRAW faces for runtime - if ( texinfo[f->texinfo].flags & SURF_NODRAW ) - { - // keep NODRAW terrain surfaces though - if ( f->dispinfo == -1 ) - return; - Warning("NODRAW on terrain surface!\n"); - } - - // save output number so leaffaces can use - f->outputnumber = numfaces; - - // - // get the next available .bsp face slot - // - if (numfaces >= MAX_MAP_FACES) - Error( "Too many faces in map, max = %d", MAX_MAP_FACES ); - df = &dfaces[numfaces]; - - // Save the correlation between dfaces and faces -- since dfaces doesnt have worldcraft face id - dfaceids.AddToTail(); - dfaceids[numfaces].hammerfaceid = f->originalface->id; - - numfaces++; - - // - // plane info - planenum is used by qlight, but not quake - // - df->planenum = f->planenum; - df->onNode = onNode; - df->side = f->planenum & 1; - - // - // material info - // - df->texinfo = f->texinfo; - df->dispinfo = f->dispinfo; - df->smoothingGroups = f->smoothingGroups; - - // save the original "side"/face data - df->origFace = FindOrCreateOrigFace( f ); - df->surfaceFogVolumeID = -1; - dfacenodes[numfaces-1] = f->fogVolumeLeaf; - if ( f->fogVolumeLeaf ) - { - Assert( f->fogVolumeLeaf->planenum == PLANENUM_LEAF ); - } - - // - // edge info - // - df->firstedge = numsurfedges; - df->numedges = f->numpoints; - - // UNDONE: Nodraw faces have no winding - revisit to see if this is necessary - if ( f->w ) - { - df->area = WindingArea( f->w ); - } - else - { - df->area = 0; - } - - df->firstPrimID = f->firstPrimID; - df->SetNumPrims( f->numPrims ); - df->SetDynamicShadowsEnabled( f->originalface->m_bDynamicShadowsEnabled ); - - // - // save off points -- as edges - // - for( i = 0; i < f->numpoints; i++ ) - { - //e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f); - e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f); - - if (numsurfedges >= MAX_MAP_SURFEDGES) - Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" ); - dsurfedges[numsurfedges] = e; - numsurfedges++; - } - - // Create overlay face lists. - side_t *pSide = f->originalface; - if ( pSide ) - { - int nOverlayCount = pSide->aOverlayIds.Count(); - if ( nOverlayCount > 0 ) - { - Overlay_AddFaceToLists( ( numfaces - 1 ), pSide ); - } - - nOverlayCount = pSide->aWaterOverlayIds.Count(); - if ( nOverlayCount > 0 ) - { - OverlayTransition_AddFaceToLists( ( numfaces - 1 ), pSide ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Emit all of the faces stored at the leaves (faces from detail brushes) -//----------------------------------------------------------------------------- -void EmitLeafFaces( face_t *pLeafFaceList ) -{ - face_t *f = pLeafFaceList; - while ( f ) - { - EmitFace( f, false ); - f = f->next; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Free the list of faces stored at the leaves -//----------------------------------------------------------------------------- -void FreeLeafFaces( face_t *pLeafFaceList ) -{ - int count = 0; - face_t *f, *next; - - f = pLeafFaceList; - - while ( f ) - { - next = f->next; - FreeFace( f ); - f = next; - count++; - } -} - -/* -============ -EmitDrawingNode_r -============ -*/ -int EmitDrawNode_r (node_t *node) -{ - dnode_t *n; - face_t *f; - int i; - - if (node->planenum == PLANENUM_LEAF) - { - EmitLeaf (node); - return -numleafs; - } - - // emit a node - if (numnodes == MAX_MAP_NODES) - Error ("MAX_MAP_NODES"); - node->diskId = numnodes; - - n = &dnodes[numnodes]; - numnodes++; - - VECTOR_COPY (node->mins, n->mins); - VECTOR_COPY (node->maxs, n->maxs); - - if (node->planenum & 1) - Error ("WriteDrawNodes_r: odd planenum"); - n->planenum = node->planenum; - n->firstface = numfaces; - n->area = node->area; - - if (!node->faces) - c_nofaces++; - else - c_facenodes++; - - for (f=node->faces ; f ; f=f->next) - EmitFace (f, true); - - n->numfaces = numfaces - n->firstface; - - - // - // recursively output the other nodes - // - for (i=0 ; i<2 ; i++) - { - if (node->children[i]->planenum == PLANENUM_LEAF) - { - n->children[i] = -(numleafs + 1); - EmitLeaf (node->children[i]); - } - else - { - n->children[i] = numnodes; - EmitDrawNode_r (node->children[i]); - } - } - - return n - dnodes; -} - - -//========================================================= - -// This will generate a scratchpad file with the level's geometry in it and the noshadow faces drawn red. -// #define SCRATCHPAD_NO_SHADOW_FACES -#if defined( SCRATCHPAD_NO_SHADOW_FACES ) - #include "scratchpad_helpers.h" - IScratchPad3D *g_pPad; -#endif - - -void MarkNoShadowFaces() -{ -#if defined( SCRATCHPAD_NO_SHADOW_FACES ) - g_pPad = ScratchPad3D_Create(); - ScratchPad_DrawWorld( g_pPad, false, CSPColor(1,1,1,0.3) ); - - for ( int iFace=0; iFace < numfaces; iFace++ ) - { - dface_t *pFace = &dfaces[iFace]; - - if ( !pFace->AreDynamicShadowsEnabled() ) - { - ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(1,0,0) ); - ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(-1,0,0) ); - ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(0,1,0) ); - } - } - g_pPad->Release(); -#endif -} - -struct texinfomap_t -{ - int refCount; - int outputIndex; -}; -struct texdatamap_t -{ - int refCount; - int outputIndex; -}; - -// Find the best used texinfo to remap this brush side -int FindMatchingBrushSideTexinfo( int sideIndex, const texinfomap_t *pMap ) -{ - dbrushside_t &side = dbrushsides[sideIndex]; - // find one with the same flags & surfaceprops (even if the texture name is different) - int sideTexFlags = texinfo[side.texinfo].flags; - int sideTexData = texinfo[side.texinfo].texdata; - int sideSurfaceProp = g_SurfaceProperties[sideTexData]; - for ( int j = 0; j < texinfo.Count(); j++ ) - { - if ( pMap[j].refCount > 0 && - texinfo[j].flags == sideTexFlags && - g_SurfaceProperties[texinfo[j].texdata] == sideSurfaceProp ) - { - // found one - return j; - } - } - - // can't find a better match - return side.texinfo; -} - -// Remove all unused texinfos and rebuild array -void ComapctTexinfoArray( texinfomap_t *pMap ) -{ - CUtlVector old; - old.CopyArray( texinfo.Base(), texinfo.Count() ); - texinfo.RemoveAll(); - int firstSky = -1; - int first2DSky = -1; - for ( int i = 0; i < old.Count(); i++ ) - { - if ( !pMap[i].refCount ) - { - pMap[i].outputIndex = -1; - continue; - } - // only add one sky texinfo + one 2D sky texinfo - if ( old[i].flags & SURF_SKY2D ) - { - if ( first2DSky < 0 ) - { - first2DSky = texinfo.AddToTail( old[i] ); - } - pMap[i].outputIndex = first2DSky; - continue; - } - if ( old[i].flags & SURF_SKY ) - { - if ( firstSky < 0 ) - { - firstSky = texinfo.AddToTail( old[i] ); - } - pMap[i].outputIndex = firstSky; - continue; - } - pMap[i].outputIndex = texinfo.AddToTail( old[i] ); - } -} - -void CompactTexdataArray( texdatamap_t *pMap ) -{ - CUtlVector oldStringData; - oldStringData.CopyArray( g_TexDataStringData.Base(), g_TexDataStringData.Count() ); - g_TexDataStringData.RemoveAll(); - CUtlVector oldStringTable; - oldStringTable.CopyArray( g_TexDataStringTable.Base(), g_TexDataStringTable.Count() ); - g_TexDataStringTable.RemoveAll(); - CUtlVector oldTexData; - oldTexData.CopyArray( dtexdata, numtexdata ); - // clear current table and rebuild - numtexdata = 0; - for ( int i = 0; i < oldTexData.Count(); i++ ) - { - // unreferenced, note in map and skip - if ( !pMap[i].refCount ) - { - pMap[i].outputIndex = -1; - continue; - } - pMap[i].outputIndex = numtexdata; - - // get old string and re-add to table - const char *pString = &oldStringData[oldStringTable[oldTexData[i].nameStringTableID]]; - int nameIndex = TexDataStringTable_AddOrFindString( pString ); - // copy old texdata and fixup with new name in compacted table - dtexdata[numtexdata] = oldTexData[i]; - dtexdata[numtexdata].nameStringTableID = nameIndex; - numtexdata++; - } -} - -void CompactTexinfos() -{ - Msg("Compacting texture/material tables...\n"); - texinfomap_t *texinfoMap = new texinfomap_t[texinfo.Count()]; - texdatamap_t *texdataMap = new texdatamap_t[numtexdata]; - memset( texinfoMap, 0, sizeof(texinfoMap[0])*texinfo.Count() ); - memset( texdataMap, 0, sizeof(texdataMap[0])*numtexdata ); - int i; - // get texinfos referenced by faces - for ( i = 0; i < numfaces; i++ ) - { - texinfoMap[dfaces[i].texinfo].refCount++; - } - // get texinfos referenced by brush sides - for ( i = 0; i < numbrushsides; i++ ) - { - // not referenced by any visible geometry - Assert( dbrushsides[i].texinfo >= 0 ); - if ( !texinfoMap[dbrushsides[i].texinfo].refCount ) - { - dbrushsides[i].texinfo = FindMatchingBrushSideTexinfo( i, texinfoMap ); - // didn't find anything suitable, go ahead and reference it - if ( !texinfoMap[dbrushsides[i].texinfo].refCount ) - { - texinfoMap[dbrushsides[i].texinfo].refCount++; - } - } - } - // get texinfos referenced by overlays - for ( i = 0; i < g_nOverlayCount; i++ ) - { - texinfoMap[g_Overlays[i].nTexInfo].refCount++; - } - for ( i = 0; i < numleafwaterdata; i++ ) - { - if ( dleafwaterdata[i].surfaceTexInfoID >= 0 ) - { - texinfoMap[dleafwaterdata[i].surfaceTexInfoID].refCount++; - } - } - for ( i = 0; i < *pNumworldlights; i++ ) - { - if ( dworldlights[i].texinfo >= 0 ) - { - texinfoMap[dworldlights[i].texinfo].refCount++; - } - } - for ( i = 0; i < g_nWaterOverlayCount; i++ ) - { - if ( g_WaterOverlays[i].nTexInfo >= 0 ) - { - texinfoMap[g_WaterOverlays[i].nTexInfo].refCount++; - } - } - // reference all used texdatas - for ( i = 0; i < texinfo.Count(); i++ ) - { - if ( texinfoMap[i].refCount > 0 ) - { - texdataMap[texinfo[i].texdata].refCount++; - } - } - - int oldCount = texinfo.Count(); - int oldTexdataCount = numtexdata; - int oldTexdataString = g_TexDataStringData.Count(); - ComapctTexinfoArray( texinfoMap ); - CompactTexdataArray( texdataMap ); - for ( i = 0; i < texinfo.Count(); i++ ) - { - int mapIndex = texdataMap[texinfo[i].texdata].outputIndex; - Assert( mapIndex >= 0 ); - texinfo[i].texdata = mapIndex; - //const char *pName = TexDataStringTable_GetString( dtexdata[texinfo[i].texdata].nameStringTableID ); - } - // remap texinfos on faces - for ( i = 0; i < numfaces; i++ ) - { - Assert( texinfoMap[dfaces[i].texinfo].outputIndex >= 0 ); - dfaces[i].texinfo = texinfoMap[dfaces[i].texinfo].outputIndex; - } - // remap texinfos on brushsides - for ( i = 0; i < numbrushsides; i++ ) - { - Assert( texinfoMap[dbrushsides[i].texinfo].outputIndex >= 0 ); - dbrushsides[i].texinfo = texinfoMap[dbrushsides[i].texinfo].outputIndex; - } - // remap texinfos on overlays - for ( i = 0; i < g_nOverlayCount; i++ ) - { - g_Overlays[i].nTexInfo = texinfoMap[g_Overlays[i].nTexInfo].outputIndex; - } - // remap leaf water data - for ( i = 0; i < numleafwaterdata; i++ ) - { - if ( dleafwaterdata[i].surfaceTexInfoID >= 0 ) - { - dleafwaterdata[i].surfaceTexInfoID = texinfoMap[dleafwaterdata[i].surfaceTexInfoID].outputIndex; - } - } - // remap world lights - for ( i = 0; i < *pNumworldlights; i++ ) - { - if ( dworldlights[i].texinfo >= 0 ) - { - dworldlights[i].texinfo = texinfoMap[dworldlights[i].texinfo].outputIndex; - } - } - // remap water overlays - for ( i = 0; i < g_nWaterOverlayCount; i++ ) - { - if ( g_WaterOverlays[i].nTexInfo >= 0 ) - { - g_WaterOverlays[i].nTexInfo = texinfoMap[g_WaterOverlays[i].nTexInfo].outputIndex; - } - } - - Msg("Reduced %d texinfos to %d\n", oldCount, texinfo.Count() ); - Msg("Reduced %d texdatas to %d (%d bytes to %d)\n", oldTexdataCount, numtexdata, oldTexdataString, g_TexDataStringData.Count() ); - - delete[] texinfoMap; - delete[] texdataMap; -} - -/* -============ -WriteBSP -============ -*/ -void WriteBSP (node_t *headnode, face_t *pLeafFaceList ) -{ - int i; - int oldfaces; - int oldorigfaces; - - c_nofaces = 0; - c_facenodes = 0; - - qprintf ("--- WriteBSP ---\n"); - - oldfaces = numfaces; - oldorigfaces = numorigfaces; - - GetEdge2_InitOptimizedList(); - EmitLeafFaces( pLeafFaceList ); - dmodels[nummodels].headnode = EmitDrawNode_r (headnode); - - // Only emit area portals for the main world. - if( nummodels == 0 ) - { - EmitAreaPortals (headnode); - } - - // - // add all displacement faces for the particular model - // - for( i = 0; i < nummapdispinfo; i++ ) - { - int entityIndex = GetDispInfoEntityNum( &mapdispinfo[i] ); - if( entityIndex == entity_num ) - { - EmitFaceVertexes( NULL, &mapdispinfo[i].face ); - EmitFace( &mapdispinfo[i].face, FALSE ); - } - } - - EmitWaterVolumesForBSP( &dmodels[nummodels], headnode ); - qprintf ("%5i nodes with faces\n", c_facenodes); - qprintf ("%5i nodes without faces\n", c_nofaces); - qprintf ("%5i faces\n", numfaces-oldfaces); - qprintf( "%5i original faces\n", numorigfaces-oldorigfaces ); -} - - - -//=========================================================== - -/* -============ -SetModelNumbers -============ -*/ -void SetModelNumbers (void) -{ - int i; - int models; - char value[10]; - - models = 1; - for (i=1 ; inummapbrushes; - - for (bnum=0 ; bnumnummapbrushes ; bnum++) - { - b = &g_MainMap->mapbrushes[bnum]; - db = &dbrushes[bnum]; - - db->contents = b->contents; - db->firstside = numbrushsides; - db->numsides = b->numsides; - for (j=0 ; jnumsides ; j++) - { - if (numbrushsides == MAX_MAP_BRUSHSIDES) - Error ("MAX_MAP_BRUSHSIDES"); - cp = &dbrushsides[numbrushsides]; - numbrushsides++; - cp->planenum = b->original_sides[j].planenum; - cp->texinfo = b->original_sides[j].texinfo; - if ( cp->texinfo == -1 ) - { - cp->texinfo = g_MainMap->g_ClipTexinfo; - } - cp->bevel = b->original_sides[j].bevel; - } - - // add any axis planes not contained in the brush to bevel off corners - for (x=0 ; x<3 ; x++) - for (s=-1 ; s<=1 ; s+=2) - { - // add the plane - VectorCopy (vec3_origin, normal); - normal[x] = s; - if (s == -1) - dist = -b->mins[x]; - else - dist = b->maxs[x]; - planenum = g_MainMap->FindFloatPlane (normal, dist); - for (i=0 ; inumsides ; i++) - if (b->original_sides[i].planenum == planenum) - break; - if (i == b->numsides) - { - if (numbrushsides >= MAX_MAP_BRUSHSIDES) - Error ("MAX_MAP_BRUSHSIDES"); - - dbrushsides[numbrushsides].planenum = planenum; - dbrushsides[numbrushsides].texinfo = - dbrushsides[numbrushsides-1].texinfo; - numbrushsides++; - db->numsides++; - } - } - } -} - - - -/* -================== -BeginBSPFile -================== -*/ -void BeginBSPFile (void) -{ - // these values may actually be initialized - // if the file existed when loaded, so clear them explicitly - nummodels = 0; - numfaces = 0; - numnodes = 0; - numbrushsides = 0; - numvertexes = 0; - numleaffaces = 0; - numleafbrushes = 0; - numsurfedges = 0; - - // edge 0 is not used, because 0 can't be negated - numedges = 1; - - // leave vertex 0 as an error - numvertexes = 1; - - // leave leaf 0 as an error - numleafs = 1; - dleafs[0].contents = CONTENTS_SOLID; - - // BUGBUG: This doesn't work! -#if 0 - // make a default empty leaf for the tracing code - memset( &dleafs[1], 0, sizeof(dleafs[1]) ); - dleafs[1].contents = CONTENTS_EMPTY; -#endif -} - -// We can't calculate this properly until vvis (since we need vis to do this), so we set -// to zero everywhere by default. -static void ClearDistToClosestWater( void ) -{ - int i; - for( i = 0; i < numleafs; i++ ) - { - g_LeafMinDistToWater[i] = 0; - } -} - - -void DiscoverMacroTextures() -{ - CUtlDict tempDict; - - g_FaceMacroTextureInfos.SetSize( numfaces ); - for ( int iFace=0; iFace < numfaces; iFace++ ) - { - texinfo_t *pTexInfo = &texinfo[dfaces[iFace].texinfo]; - if ( pTexInfo->texdata < 0 ) - continue; - - dtexdata_t *pTexData = &dtexdata[pTexInfo->texdata]; - const char *pMaterialName = &g_TexDataStringData[ g_TexDataStringTable[pTexData->nameStringTableID] ]; - - MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false ); - - const char *pMacroTextureName = GetMaterialVar( hMaterial, "$macro_texture" ); - if ( pMacroTextureName ) - { - if ( tempDict.Find( pMacroTextureName ) == tempDict.InvalidIndex() ) - { - Msg( "-- DiscoverMacroTextures: %s\n", pMacroTextureName ); - tempDict.Insert( pMacroTextureName, 0 ); - } - - int stringID = TexDataStringTable_AddOrFindString( pMacroTextureName ); - g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = (unsigned short)stringID; - } - else - { - g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = 0xFFFF; - } - } -} - - -// Make sure that we have a water lod control entity if we have water in the map. -void EnsurePresenceOfWaterLODControlEntity( void ) -{ - extern bool g_bHasWater; - if( !g_bHasWater ) - { - // Don't bother if there isn't any water in the map. - return; - } - for( int i=0; i < num_entities; i++ ) - { - entity_t *e = &entities[i]; - - const char *pClassName = ValueForKey( e, "classname" ); - if( !Q_stricmp( pClassName, "water_lod_control" ) ) - { - // Found one!!!! - return; - } - } - - // None found, add one. - Warning( "Water found with no water_lod_control entity, creating a default one.\n" ); - - entity_t *mapent = &entities[num_entities]; - num_entities++; - memset(mapent, 0, sizeof(*mapent)); - mapent->firstbrush = g_MainMap->nummapbrushes; - mapent->numbrushes = 0; - - SetKeyValue( mapent, "classname", "water_lod_control" ); - SetKeyValue( mapent, "cheapwaterstartdistance", "1000" ); - SetKeyValue( mapent, "cheapwaterenddistance", "2000" ); -} - - -/* -============ -EndBSPFile -============ -*/ -void EndBSPFile (void) -{ - // Mark noshadow faces. - MarkNoShadowFaces(); - - EmitBrushes (); - EmitPlanes (); - - // stick flat normals at the verts - SaveVertexNormals(); - - // Figure out lightmap extents for all faces. - UpdateAllFaceLightmapExtents(); - - // Generate geometry and lightmap alpha for displacements. - EmitDispLMAlphaAndNeighbors(); - - // Emit overlay data. - Overlay_EmitOverlayFaces(); - OverlayTransition_EmitOverlayFaces(); - - // phys collision needs dispinfo to operate (needs to generate phys collision for displacement surfs) - EmitPhysCollision(); - - // We can't calculate this properly until vvis (since we need vis to do this), so we set - // to zero everywhere by default. - ClearDistToClosestWater(); - - // Emit static props found in the .vmf file - EmitStaticProps(); - - // Place detail props found in .vmf and based on material properties - EmitDetailObjects(); - - // Compute bounds after creating disp info because we need to reference it - ComputeBoundsNoSkybox(); - - // Make sure that we have a water lod control eneity if we have water in the map. - EnsurePresenceOfWaterLODControlEntity(); - - // Doing this here because stuff about may filter out entities - UnparseEntities (); - - // remove unused texinfos - CompactTexinfos(); - - // Figure out which faces want macro textures. - DiscoverMacroTextures(); - - char targetPath[1024]; - GetPlatformMapPath( source, targetPath, g_nDXLevel, 1024 ); - Msg ("Writing %s\n", targetPath); - WriteBSPFile (targetPath); -} - - -/* -================== -BeginModel -================== -*/ -int firstmodleaf; -void BeginModel (void) -{ - dmodel_t *mod; - int start, end; - mapbrush_t *b; - int j; - entity_t *e; - Vector mins, maxs; - - if (nummodels == MAX_MAP_MODELS) - Error ("Too many brush models in map, max = %d", MAX_MAP_MODELS); - mod = &dmodels[nummodels]; - - mod->firstface = numfaces; - - firstmodleaf = numleafs; - firstmodeledge = numedges; - firstmodelface = numfaces; - - // - // bound the brushes - // - e = &entities[entity_num]; - - start = e->firstbrush; - end = start + e->numbrushes; - ClearBounds (mins, maxs); - - for (j=start ; jmapbrushes[j]; - if (!b->numsides) - continue; // not a real brush (origin brush) - AddPointToBounds (b->mins, mins, maxs); - AddPointToBounds (b->maxs, mins, maxs); - } - - VectorCopy (mins, mod->mins); - VectorCopy (maxs, mod->maxs); -} - - -/* -================== -EndModel -================== -*/ -void EndModel (void) -{ - dmodel_t *mod; - - mod = &dmodels[nummodels]; - - mod->numfaces = numfaces - mod->firstface; - - nummodels++; -} - - - -//----------------------------------------------------------------------------- -// figure out which leaf a point is in -//----------------------------------------------------------------------------- -static int PointLeafnum_r (const Vector& p, int num) -{ - float d; - while (num >= 0) - { - dnode_t* node = dnodes + num; - dplane_t* plane = dplanes + node->planenum; - - if (plane->type < 3) - d = p[plane->type] - plane->dist; - else - d = DotProduct (plane->normal, p) - plane->dist; - if (d < 0) - num = node->children[1]; - else - num = node->children[0]; - } - - return -1 - num; -} - -int PointLeafnum ( dmodel_t* pModel, const Vector& p ) -{ - return PointLeafnum_r (p, pModel->headnode); -} - - -//----------------------------------------------------------------------------- -// Adds a noew to the bounding box -//----------------------------------------------------------------------------- -static void AddNodeToBounds(int node, CUtlVector& skipAreas, Vector& mins, Vector& maxs) -{ - // not a leaf - if (node >= 0) - { - AddNodeToBounds( dnodes[node].children[0], skipAreas, mins, maxs ); - AddNodeToBounds( dnodes[node].children[1], skipAreas, mins, maxs ); - } - else - { - int leaf = - 1 - node; - - // Don't bother with solid leaves - if (dleafs[leaf].contents & CONTENTS_SOLID) - return; - - // Skip 3D skybox - int i; - for ( i = skipAreas.Count(); --i >= 0; ) - { - if (dleafs[leaf].area == skipAreas[i]) - return; - } - - unsigned int firstface = dleafs[leaf].firstleafface; - for ( i = 0; i < dleafs[leaf].numleaffaces; ++i ) - { - unsigned int face = dleaffaces[ firstface + i ]; - - // Skip skyboxes + nodraw - texinfo_t& tex = texinfo[dfaces[face].texinfo]; - if (tex.flags & (SURF_SKY | SURF_NODRAW)) - continue; - - unsigned int firstedge = dfaces[face].firstedge; - Assert( firstedge >= 0 ); - - for (int j = 0; j < dfaces[face].numedges; ++j) - { - Assert( firstedge+j < numsurfedges ); - int edge = abs(dsurfedges[firstedge+j]); - dedge_t* pEdge = &dedges[edge]; - Assert( pEdge->v[0] >= 0 ); - Assert( pEdge->v[1] >= 0 ); - AddPointToBounds (dvertexes[pEdge->v[0]].point, mins, maxs); - AddPointToBounds (dvertexes[pEdge->v[1]].point, mins, maxs); - } - } - } -} - - -//----------------------------------------------------------------------------- -// Check to see if a displacement lives in any leaves that are not -// in the 3d skybox -//----------------------------------------------------------------------------- -bool IsBoxInsideWorld( int node, CUtlVector &skipAreas, const Vector &vecMins, const Vector &vecMaxs ) -{ - while( 1 ) - { - // leaf - if (node < 0) - { - // get the leaf - int leaf = - 1 - node; - - // Don't bother with solid leaves - if (dleafs[leaf].contents & CONTENTS_SOLID) - return false; - - // Skip 3D skybox - int i; - for ( i = skipAreas.Count(); --i >= 0; ) - { - if ( dleafs[leaf].area == skipAreas[i] ) - return false; - } - - return true; - } - - // - // get displacement bounding box position relative to the node plane - // - dnode_t *pNode = &dnodes[ node ]; - dplane_t *pPlane = &dplanes[ pNode->planenum ]; - - int sideResult = BrushBspBoxOnPlaneSide( vecMins, vecMaxs, pPlane ); - - // front side - if( sideResult == 1 ) - { - node = pNode->children[0]; - } - // back side - else if( sideResult == 2 ) - { - node = pNode->children[1]; - } - //split - else - { - if ( IsBoxInsideWorld( pNode->children[0], skipAreas, vecMins, vecMaxs ) ) - return true; - - node = pNode->children[1]; - } - } -} - - -//----------------------------------------------------------------------------- -// Adds the displacement surfaces in the world to the bounds -//----------------------------------------------------------------------------- -void AddDispsToBounds( int nHeadNode, CUtlVector& skipAreas, Vector &vecMins, Vector &vecMaxs ) -{ - Vector vecDispMins, vecDispMaxs; - - // first determine how many displacement surfaces there will be per leaf - int i; - for ( i = 0; i < g_dispinfo.Count(); ++i ) - { - ComputeDispInfoBounds( i, vecDispMins, vecDispMaxs ); - if ( IsBoxInsideWorld( nHeadNode, skipAreas, vecDispMins, vecDispMaxs ) ) - { - AddPointToBounds( vecDispMins, vecMins, vecMaxs ); - AddPointToBounds( vecDispMaxs, vecMins, vecMaxs ); - } - } -} - - -//----------------------------------------------------------------------------- -// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues -//----------------------------------------------------------------------------- -void ComputeBoundsNoSkybox( ) -{ - // Iterate over all world leaves, skip those which are part of skybox - Vector mins, maxs; - ClearBounds (mins, maxs); - AddNodeToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs ); - AddDispsToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs ); - - // Add the bounds to the worldspawn data - for (int i = 0; i < num_entities; ++i) - { - char* pEntity = ValueForKey(&entities[i], "classname"); - if (!strcmp(pEntity, "worldspawn")) - { - char string[32]; - sprintf (string, "%i %i %i", (int)mins[0], (int)mins[1], (int)mins[2]); - SetKeyValue (&entities[i], "world_mins", string); - sprintf (string, "%i %i %i", (int)maxs[0], (int)maxs[1], (int)maxs[2]); - SetKeyValue (&entities[i], "world_maxs", string); - break; - } - } -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "vbsp.h" +#include "disp_vbsp.h" +#include "utlvector.h" +#include "faces.h" +#include "builddisp.h" +#include "tier1/strtools.h" +#include "utilmatlib.h" +#include "utldict.h" +#include "map.h" + +int c_nofaces; +int c_facenodes; + +// NOTE: This is a global used to link faces back to the tree node/portals they came from +// it's used when filling water volumes +node_t *dfacenodes[MAX_MAP_FACES]; + + +/* +========================================================= + +ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES + +========================================================= +*/ + +void EmitFaceVertexes (face_t **list, face_t *f); +void AssignOccluderAreas(); + +/* +============ +EmitPlanes + +There is no oportunity to discard planes, because all of the original +brushes will be saved in the map. +============ +*/ +void EmitPlanes (void) +{ + int i; + dplane_t *dp; + plane_t *mp; + int planetranslate[MAX_MAP_PLANES]; + + mp = g_MainMap->mapplanes; + for (i=0 ; inummapplanes ; i++, mp++) + { + dp = &dplanes[numplanes]; + planetranslate[i] = numplanes; + VectorCopy ( mp->normal, dp->normal); + dp->dist = mp->dist; + dp->type = mp->type; + numplanes++; + } +} + + +//======================================================== + +void EmitMarkFace (dleaf_t *leaf_p, face_t *f) +{ + int i; + int facenum; + + while (f->merged) + f = f->merged; + + if (f->split[0]) + { + EmitMarkFace (leaf_p, f->split[0]); + EmitMarkFace (leaf_p, f->split[1]); + return; + } + + facenum = f->outputnumber; + if (facenum == -1) + return; // degenerate face + + if (facenum < 0 || facenum >= numfaces) + Error ("Bad leafface"); + for (i=leaf_p->firstleafface ; i= MAX_MAP_LEAFFACES) + Error ("Too many detail brush faces, max = %d\n", MAX_MAP_LEAFFACES); + + dleaffaces[numleaffaces] = facenum; + numleaffaces++; + } + +} + + +/* +================== +EmitLeaf +================== +*/ +void EmitLeaf (node_t *node) +{ + dleaf_t *leaf_p; + portal_t *p; + int s; + face_t *f; + bspbrush_t *b; + int i; + int brushnum; + leafface_t *pList; + + // emit a leaf + if (numleafs >= MAX_MAP_LEAFS) + Error ("Too many BSP leaves, max = %d", MAX_MAP_LEAFS); + + node->diskId = numleafs; + leaf_p = &dleafs[numleafs]; + numleafs++; + + if( nummodels == 0 ) + { + leaf_p->cluster = node->cluster; + } + else + { + // Submodels don't have clusters. If this isn't set to -1 here, then there + // will be multiple leaves (albeit from different models) that reference + // the same cluster and parts of the code like ivp.cpp's ConvertWaterModelToPhysCollide + // won't work. + leaf_p->cluster = -1; + } + + leaf_p->contents = node->contents; + leaf_p->area = node->area; + + // By default, assume the leaf can see the skybox. + // VRAD will do the actual computation to see if it really can see the skybox + leaf_p->flags = LEAF_FLAGS_SKY; + + // + // write bounding box info + // + VECTOR_COPY (node->mins, leaf_p->mins); + VECTOR_COPY (node->maxs, leaf_p->maxs); + + // + // write the leafbrushes + // + leaf_p->firstleafbrush = numleafbrushes; + for (b=node->brushlist ; b ; b=b->next) + { + if (numleafbrushes >= MAX_MAP_LEAFBRUSHES) + Error ("Too many brushes in one leaf, max = %d", MAX_MAP_LEAFBRUSHES); + + brushnum = b->original - g_MainMap->mapbrushes; + for (i=leaf_p->firstleafbrush ; inumleafbrushes = numleafbrushes - leaf_p->firstleafbrush; + + // + // write the leaffaces + // + if (leaf_p->contents & CONTENTS_SOLID) + return; // no leaffaces in solids + + leaf_p->firstleafface = numleaffaces; + + for (p = node->portals ; p ; p = p->next[s]) + { + s = (p->nodes[1] == node); + f = p->face[s]; + if (!f) + continue; // not a visible portal + + EmitMarkFace (leaf_p, f); + } + + // emit the detail faces + for ( pList = node->leaffacelist; pList; pList = pList->pNext ) + { + EmitMarkFace( leaf_p, pList->pFace ); + } + + + leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface; +} + +// per face plane - original face "side" list +side_t *pOrigFaceSideList[MAX_MAP_PLANES]; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CreateOrigFace( face_t *f ) +{ + int i, j; + dface_t *of; + side_t *side; + int vIndices[128]; + int eIndex[2]; + winding_t *pWinding; + + // not a real face! + if( !f->w ) + return -1; + + // get the original face -- the "side" + side = f->originalface; + + // get the original face winding + if( !side->winding ) + { + return -1; + } + + // + // get the next original face + // + if( numorigfaces >= MAX_MAP_FACES ) + Error( "Too many faces in map, max = %d", MAX_MAP_FACES ); + of = &dorigfaces[numorigfaces]; + numorigfaces++; + + // set original face to -1 -- it is an origianl face! + of->origFace = -1; + + // + // add side to plane list + // + side->next = pOrigFaceSideList[f->planenum]; + pOrigFaceSideList[f->planenum] = side; + side->origIndex = numorigfaces - 1; + + pWinding = CopyWinding( side->winding ); + + // + // plane info + // + of->planenum = side->planenum; + if ( side->contents & CONTENTS_DETAIL ) + of->onNode = 0; + else + of->onNode = 1; + of->side = side->planenum & 1; + + // + // edge info + // + of->firstedge = numsurfedges; + of->numedges = side->winding->numpoints; + + // + // material info + // + of->texinfo = side->texinfo; + of->dispinfo = f->dispinfo; + + // + // save the vertices + // + for( i = 0; i < pWinding->numpoints; i++ ) + { + // + // compare vertices + // + vIndices[i] = GetVertexnum( pWinding->p[i] ); + } + + // + // save off points -- as edges + // + for( i = 0; i < pWinding->numpoints; i++ ) + { + // + // look for matching edges first + // + eIndex[0] = vIndices[i]; + eIndex[1] = vIndices[(i+1)%pWinding->numpoints]; + + for( j = firstmodeledge; j < numedges; j++ ) + { + if( ( eIndex[0] == dedges[j].v[1] ) && + ( eIndex[1] == dedges[j].v[0] ) && + ( edgefaces[j][0]->contents == f->contents ) ) + { + // check for multiple backward edges!! -- shouldn't have + if( edgefaces[j][1] ) + continue; + + // set back edge + edgefaces[j][1] = f; + + // + // get next surface edge + // + if( numsurfedges >= MAX_MAP_SURFEDGES ) + Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" ); + dsurfedges[numsurfedges] = -j; + numsurfedges++; + break; + } + } + + if( j == numedges ) + { + // + // get next edge + // + AddEdge( eIndex[0], eIndex[1], f ); + + // + // get next surface edge + // + if( numsurfedges >= MAX_MAP_SURFEDGES ) + Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" ); + dsurfedges[numsurfedges] = ( numedges - 1 ); + numsurfedges++; + } + } + + // return the index + return ( numorigfaces - 1 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: search for a face within the origface list and return the index if +// found +// Input: f - the face to compare +// Output: the index of the face it found, -1 if not found +//----------------------------------------------------------------------------- +int FindOrigFace( face_t *f ) +{ + int i; + static int bClear = 0; + side_t *pSide; + + // + // initially clear the face side lists (per face plane) + // + if( !bClear ) + { + for( i = 0; i < MAX_MAP_PLANES; i++ ) + { + pOrigFaceSideList[i] = NULL; + } + bClear = 1; + } + + // + // compare the sides + // + for( pSide = pOrigFaceSideList[f->planenum]; pSide; pSide = pSide->next ) + { + if( pSide == f->originalface ) + return pSide->origIndex; + } + + // original face not found in list + return -1; +} + + +//----------------------------------------------------------------------------- +// Purpose: to find an the original face within the list of original faces, if +// a match is not found then create a new origFace -- either way pass +// back the index of the origface in the list +// Input: f - face containing the original face information +// Output: the index of the origface in the origface list +//----------------------------------------------------------------------------- +int FindOrCreateOrigFace( face_t *f ) +{ + int index; + + // check for an original face + if( !f->originalface ) + return -1; + + // + // find or create a orig face and return the index + // + index = FindOrigFace( f ); + + if( index == -1 ) + return CreateOrigFace( f ); + else if( index == -2 ) + return -1; + + return index; +} + +/* +================== +EmitFace +================== +*/ +void EmitFace( face_t *f, qboolean onNode ) +{ + dface_t *df; + int i; + int e; + +// void SubdivideFaceBySubdivSize( face_t *f ); // garymcthack +// SubdivideFaceBySubdivSize( f ); + + // set initial output number + f->outputnumber = -1; + + // degenerated + if( f->numpoints < 3 ) + return; + + // not a final face + if( f->merged || f->split[0] || f->split[1] ) + return; + + // don't emit NODRAW faces for runtime + if ( texinfo[f->texinfo].flags & SURF_NODRAW ) + { + // keep NODRAW terrain surfaces though + if ( f->dispinfo == -1 ) + return; + Warning("NODRAW on terrain surface!\n"); + } + + // save output number so leaffaces can use + f->outputnumber = numfaces; + + // + // get the next available .bsp face slot + // + if (numfaces >= MAX_MAP_FACES) + Error( "Too many faces in map, max = %d", MAX_MAP_FACES ); + df = &dfaces[numfaces]; + + // Save the correlation between dfaces and faces -- since dfaces doesnt have worldcraft face id + dfaceids.AddToTail(); + dfaceids[numfaces].hammerfaceid = f->originalface->id; + + numfaces++; + + // + // plane info - planenum is used by qlight, but not quake + // + df->planenum = f->planenum; + df->onNode = onNode; + df->side = f->planenum & 1; + + // + // material info + // + df->texinfo = f->texinfo; + df->dispinfo = f->dispinfo; + df->smoothingGroups = f->smoothingGroups; + + // save the original "side"/face data + df->origFace = FindOrCreateOrigFace( f ); + df->surfaceFogVolumeID = -1; + dfacenodes[numfaces-1] = f->fogVolumeLeaf; + if ( f->fogVolumeLeaf ) + { + Assert( f->fogVolumeLeaf->planenum == PLANENUM_LEAF ); + } + + // + // edge info + // + df->firstedge = numsurfedges; + df->numedges = f->numpoints; + + // UNDONE: Nodraw faces have no winding - revisit to see if this is necessary + if ( f->w ) + { + df->area = WindingArea( f->w ); + } + else + { + df->area = 0; + } + + df->firstPrimID = f->firstPrimID; + df->SetNumPrims( f->numPrims ); + df->SetDynamicShadowsEnabled( f->originalface->m_bDynamicShadowsEnabled ); + + // + // save off points -- as edges + // + for( i = 0; i < f->numpoints; i++ ) + { + //e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f); + e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f); + + if (numsurfedges >= MAX_MAP_SURFEDGES) + Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" ); + dsurfedges[numsurfedges] = e; + numsurfedges++; + } + + // Create overlay face lists. + side_t *pSide = f->originalface; + if ( pSide ) + { + int nOverlayCount = pSide->aOverlayIds.Count(); + if ( nOverlayCount > 0 ) + { + Overlay_AddFaceToLists( ( numfaces - 1 ), pSide ); + } + + nOverlayCount = pSide->aWaterOverlayIds.Count(); + if ( nOverlayCount > 0 ) + { + OverlayTransition_AddFaceToLists( ( numfaces - 1 ), pSide ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Emit all of the faces stored at the leaves (faces from detail brushes) +//----------------------------------------------------------------------------- +void EmitLeafFaces( face_t *pLeafFaceList ) +{ + face_t *f = pLeafFaceList; + while ( f ) + { + EmitFace( f, false ); + f = f->next; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Free the list of faces stored at the leaves +//----------------------------------------------------------------------------- +void FreeLeafFaces( face_t *pLeafFaceList ) +{ + int count = 0; + face_t *f, *next; + + f = pLeafFaceList; + + while ( f ) + { + next = f->next; + FreeFace( f ); + f = next; + count++; + } +} + +/* +============ +EmitDrawingNode_r +============ +*/ +int EmitDrawNode_r (node_t *node) +{ + dnode_t *n; + face_t *f; + int i; + + if (node->planenum == PLANENUM_LEAF) + { + EmitLeaf (node); + return -numleafs; + } + + // emit a node + if (numnodes == MAX_MAP_NODES) + Error ("MAX_MAP_NODES"); + node->diskId = numnodes; + + n = &dnodes[numnodes]; + numnodes++; + + VECTOR_COPY (node->mins, n->mins); + VECTOR_COPY (node->maxs, n->maxs); + + if (node->planenum & 1) + Error ("WriteDrawNodes_r: odd planenum"); + n->planenum = node->planenum; + n->firstface = numfaces; + n->area = node->area; + + if (!node->faces) + c_nofaces++; + else + c_facenodes++; + + for (f=node->faces ; f ; f=f->next) + EmitFace (f, true); + + n->numfaces = numfaces - n->firstface; + + + // + // recursively output the other nodes + // + for (i=0 ; i<2 ; i++) + { + if (node->children[i]->planenum == PLANENUM_LEAF) + { + n->children[i] = -(numleafs + 1); + EmitLeaf (node->children[i]); + } + else + { + n->children[i] = numnodes; + EmitDrawNode_r (node->children[i]); + } + } + + return n - dnodes; +} + + +//========================================================= + +// This will generate a scratchpad file with the level's geometry in it and the noshadow faces drawn red. +// #define SCRATCHPAD_NO_SHADOW_FACES +#if defined( SCRATCHPAD_NO_SHADOW_FACES ) + #include "scratchpad_helpers.h" + IScratchPad3D *g_pPad; +#endif + + +void MarkNoShadowFaces() +{ +#if defined( SCRATCHPAD_NO_SHADOW_FACES ) + g_pPad = ScratchPad3D_Create(); + ScratchPad_DrawWorld( g_pPad, false, CSPColor(1,1,1,0.3) ); + + for ( int iFace=0; iFace < numfaces; iFace++ ) + { + dface_t *pFace = &dfaces[iFace]; + + if ( !pFace->AreDynamicShadowsEnabled() ) + { + ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(1,0,0) ); + ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(-1,0,0) ); + ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(0,1,0) ); + } + } + g_pPad->Release(); +#endif +} + +struct texinfomap_t +{ + int refCount; + int outputIndex; +}; +struct texdatamap_t +{ + int refCount; + int outputIndex; +}; + +// Find the best used texinfo to remap this brush side +int FindMatchingBrushSideTexinfo( int sideIndex, const texinfomap_t *pMap ) +{ + dbrushside_t &side = dbrushsides[sideIndex]; + // find one with the same flags & surfaceprops (even if the texture name is different) + int sideTexFlags = texinfo[side.texinfo].flags; + int sideTexData = texinfo[side.texinfo].texdata; + int sideSurfaceProp = g_SurfaceProperties[sideTexData]; + for ( int j = 0; j < texinfo.Count(); j++ ) + { + if ( pMap[j].refCount > 0 && + texinfo[j].flags == sideTexFlags && + g_SurfaceProperties[texinfo[j].texdata] == sideSurfaceProp ) + { + // found one + return j; + } + } + + // can't find a better match + return side.texinfo; +} + +// Remove all unused texinfos and rebuild array +void ComapctTexinfoArray( texinfomap_t *pMap ) +{ + CUtlVector old; + old.CopyArray( texinfo.Base(), texinfo.Count() ); + texinfo.RemoveAll(); + int firstSky = -1; + int first2DSky = -1; + for ( int i = 0; i < old.Count(); i++ ) + { + if ( !pMap[i].refCount ) + { + pMap[i].outputIndex = -1; + continue; + } + // only add one sky texinfo + one 2D sky texinfo + if ( old[i].flags & SURF_SKY2D ) + { + if ( first2DSky < 0 ) + { + first2DSky = texinfo.AddToTail( old[i] ); + } + pMap[i].outputIndex = first2DSky; + continue; + } + if ( old[i].flags & SURF_SKY ) + { + if ( firstSky < 0 ) + { + firstSky = texinfo.AddToTail( old[i] ); + } + pMap[i].outputIndex = firstSky; + continue; + } + pMap[i].outputIndex = texinfo.AddToTail( old[i] ); + } +} + +void CompactTexdataArray( texdatamap_t *pMap ) +{ + CUtlVector oldStringData; + oldStringData.CopyArray( g_TexDataStringData.Base(), g_TexDataStringData.Count() ); + g_TexDataStringData.RemoveAll(); + CUtlVector oldStringTable; + oldStringTable.CopyArray( g_TexDataStringTable.Base(), g_TexDataStringTable.Count() ); + g_TexDataStringTable.RemoveAll(); + CUtlVector oldTexData; + oldTexData.CopyArray( dtexdata, numtexdata ); + // clear current table and rebuild + numtexdata = 0; + for ( int i = 0; i < oldTexData.Count(); i++ ) + { + // unreferenced, note in map and skip + if ( !pMap[i].refCount ) + { + pMap[i].outputIndex = -1; + continue; + } + pMap[i].outputIndex = numtexdata; + + // get old string and re-add to table + const char *pString = &oldStringData[oldStringTable[oldTexData[i].nameStringTableID]]; + int nameIndex = TexDataStringTable_AddOrFindString( pString ); + // copy old texdata and fixup with new name in compacted table + dtexdata[numtexdata] = oldTexData[i]; + dtexdata[numtexdata].nameStringTableID = nameIndex; + numtexdata++; + } +} + +void CompactTexinfos() +{ + Msg("Compacting texture/material tables...\n"); + texinfomap_t *texinfoMap = new texinfomap_t[texinfo.Count()]; + texdatamap_t *texdataMap = new texdatamap_t[numtexdata]; + memset( texinfoMap, 0, sizeof(texinfoMap[0])*texinfo.Count() ); + memset( texdataMap, 0, sizeof(texdataMap[0])*numtexdata ); + int i; + // get texinfos referenced by faces + for ( i = 0; i < numfaces; i++ ) + { + texinfoMap[dfaces[i].texinfo].refCount++; + } + // get texinfos referenced by brush sides + for ( i = 0; i < numbrushsides; i++ ) + { + // not referenced by any visible geometry + Assert( dbrushsides[i].texinfo >= 0 ); + if ( !texinfoMap[dbrushsides[i].texinfo].refCount ) + { + dbrushsides[i].texinfo = FindMatchingBrushSideTexinfo( i, texinfoMap ); + // didn't find anything suitable, go ahead and reference it + if ( !texinfoMap[dbrushsides[i].texinfo].refCount ) + { + texinfoMap[dbrushsides[i].texinfo].refCount++; + } + } + } + // get texinfos referenced by overlays + for ( i = 0; i < g_nOverlayCount; i++ ) + { + texinfoMap[g_Overlays[i].nTexInfo].refCount++; + } + for ( i = 0; i < numleafwaterdata; i++ ) + { + if ( dleafwaterdata[i].surfaceTexInfoID >= 0 ) + { + texinfoMap[dleafwaterdata[i].surfaceTexInfoID].refCount++; + } + } + for ( i = 0; i < *pNumworldlights; i++ ) + { + if ( dworldlights[i].texinfo >= 0 ) + { + texinfoMap[dworldlights[i].texinfo].refCount++; + } + } + for ( i = 0; i < g_nWaterOverlayCount; i++ ) + { + if ( g_WaterOverlays[i].nTexInfo >= 0 ) + { + texinfoMap[g_WaterOverlays[i].nTexInfo].refCount++; + } + } + // reference all used texdatas + for ( i = 0; i < texinfo.Count(); i++ ) + { + if ( texinfoMap[i].refCount > 0 ) + { + texdataMap[texinfo[i].texdata].refCount++; + } + } + + int oldCount = texinfo.Count(); + int oldTexdataCount = numtexdata; + int oldTexdataString = g_TexDataStringData.Count(); + ComapctTexinfoArray( texinfoMap ); + CompactTexdataArray( texdataMap ); + for ( i = 0; i < texinfo.Count(); i++ ) + { + int mapIndex = texdataMap[texinfo[i].texdata].outputIndex; + Assert( mapIndex >= 0 ); + texinfo[i].texdata = mapIndex; + //const char *pName = TexDataStringTable_GetString( dtexdata[texinfo[i].texdata].nameStringTableID ); + } + // remap texinfos on faces + for ( i = 0; i < numfaces; i++ ) + { + Assert( texinfoMap[dfaces[i].texinfo].outputIndex >= 0 ); + dfaces[i].texinfo = texinfoMap[dfaces[i].texinfo].outputIndex; + } + // remap texinfos on brushsides + for ( i = 0; i < numbrushsides; i++ ) + { + Assert( texinfoMap[dbrushsides[i].texinfo].outputIndex >= 0 ); + dbrushsides[i].texinfo = texinfoMap[dbrushsides[i].texinfo].outputIndex; + } + // remap texinfos on overlays + for ( i = 0; i < g_nOverlayCount; i++ ) + { + g_Overlays[i].nTexInfo = texinfoMap[g_Overlays[i].nTexInfo].outputIndex; + } + // remap leaf water data + for ( i = 0; i < numleafwaterdata; i++ ) + { + if ( dleafwaterdata[i].surfaceTexInfoID >= 0 ) + { + dleafwaterdata[i].surfaceTexInfoID = texinfoMap[dleafwaterdata[i].surfaceTexInfoID].outputIndex; + } + } + // remap world lights + for ( i = 0; i < *pNumworldlights; i++ ) + { + if ( dworldlights[i].texinfo >= 0 ) + { + dworldlights[i].texinfo = texinfoMap[dworldlights[i].texinfo].outputIndex; + } + } + // remap water overlays + for ( i = 0; i < g_nWaterOverlayCount; i++ ) + { + if ( g_WaterOverlays[i].nTexInfo >= 0 ) + { + g_WaterOverlays[i].nTexInfo = texinfoMap[g_WaterOverlays[i].nTexInfo].outputIndex; + } + } + + Msg("Reduced %d texinfos to %d\n", oldCount, texinfo.Count() ); + Msg("Reduced %d texdatas to %d (%d bytes to %d)\n", oldTexdataCount, numtexdata, oldTexdataString, g_TexDataStringData.Count() ); + + delete[] texinfoMap; + delete[] texdataMap; +} + +/* +============ +WriteBSP +============ +*/ +void WriteBSP (node_t *headnode, face_t *pLeafFaceList ) +{ + int i; + int oldfaces; + int oldorigfaces; + + c_nofaces = 0; + c_facenodes = 0; + + qprintf ("--- WriteBSP ---\n"); + + oldfaces = numfaces; + oldorigfaces = numorigfaces; + + GetEdge2_InitOptimizedList(); + EmitLeafFaces( pLeafFaceList ); + dmodels[nummodels].headnode = EmitDrawNode_r (headnode); + + // Only emit area portals for the main world. + if( nummodels == 0 ) + { + EmitAreaPortals (headnode); + } + + // + // add all displacement faces for the particular model + // + for( i = 0; i < nummapdispinfo; i++ ) + { + int entityIndex = GetDispInfoEntityNum( &mapdispinfo[i] ); + if( entityIndex == entity_num ) + { + EmitFaceVertexes( NULL, &mapdispinfo[i].face ); + EmitFace( &mapdispinfo[i].face, FALSE ); + } + } + + EmitWaterVolumesForBSP( &dmodels[nummodels], headnode ); + qprintf ("%5i nodes with faces\n", c_facenodes); + qprintf ("%5i nodes without faces\n", c_nofaces); + qprintf ("%5i faces\n", numfaces-oldfaces); + qprintf( "%5i original faces\n", numorigfaces-oldorigfaces ); +} + + + +//=========================================================== + +/* +============ +SetModelNumbers +============ +*/ +void SetModelNumbers (void) +{ + int i; + int models; + char value[10]; + + models = 1; + for (i=1 ; inummapbrushes; + + for (bnum=0 ; bnumnummapbrushes ; bnum++) + { + b = &g_MainMap->mapbrushes[bnum]; + db = &dbrushes[bnum]; + + db->contents = b->contents; + db->firstside = numbrushsides; + db->numsides = b->numsides; + for (j=0 ; jnumsides ; j++) + { + if (numbrushsides == MAX_MAP_BRUSHSIDES) + Error ("MAX_MAP_BRUSHSIDES"); + cp = &dbrushsides[numbrushsides]; + numbrushsides++; + cp->planenum = b->original_sides[j].planenum; + cp->texinfo = b->original_sides[j].texinfo; + if ( cp->texinfo == -1 ) + { + cp->texinfo = g_MainMap->g_ClipTexinfo; + } + cp->bevel = b->original_sides[j].bevel; + } + + // add any axis planes not contained in the brush to bevel off corners + for (x=0 ; x<3 ; x++) + for (s=-1 ; s<=1 ; s+=2) + { + // add the plane + VectorCopy (vec3_origin, normal); + normal[x] = s; + if (s == -1) + dist = -b->mins[x]; + else + dist = b->maxs[x]; + planenum = g_MainMap->FindFloatPlane (normal, dist); + for (i=0 ; inumsides ; i++) + if (b->original_sides[i].planenum == planenum) + break; + if (i == b->numsides) + { + if (numbrushsides >= MAX_MAP_BRUSHSIDES) + Error ("MAX_MAP_BRUSHSIDES"); + + dbrushsides[numbrushsides].planenum = planenum; + dbrushsides[numbrushsides].texinfo = + dbrushsides[numbrushsides-1].texinfo; + numbrushsides++; + db->numsides++; + } + } + } +} + + + +/* +================== +BeginBSPFile +================== +*/ +void BeginBSPFile (void) +{ + // these values may actually be initialized + // if the file existed when loaded, so clear them explicitly + nummodels = 0; + numfaces = 0; + numnodes = 0; + numbrushsides = 0; + numvertexes = 0; + numleaffaces = 0; + numleafbrushes = 0; + numsurfedges = 0; + + // edge 0 is not used, because 0 can't be negated + numedges = 1; + + // leave vertex 0 as an error + numvertexes = 1; + + // leave leaf 0 as an error + numleafs = 1; + dleafs[0].contents = CONTENTS_SOLID; + + // BUGBUG: This doesn't work! +#if 0 + // make a default empty leaf for the tracing code + memset( &dleafs[1], 0, sizeof(dleafs[1]) ); + dleafs[1].contents = CONTENTS_EMPTY; +#endif +} + +// We can't calculate this properly until vvis (since we need vis to do this), so we set +// to zero everywhere by default. +static void ClearDistToClosestWater( void ) +{ + int i; + for( i = 0; i < numleafs; i++ ) + { + g_LeafMinDistToWater[i] = 0; + } +} + + +void DiscoverMacroTextures() +{ + CUtlDict tempDict; + + g_FaceMacroTextureInfos.SetSize( numfaces ); + for ( int iFace=0; iFace < numfaces; iFace++ ) + { + texinfo_t *pTexInfo = &texinfo[dfaces[iFace].texinfo]; + if ( pTexInfo->texdata < 0 ) + continue; + + dtexdata_t *pTexData = &dtexdata[pTexInfo->texdata]; + const char *pMaterialName = &g_TexDataStringData[ g_TexDataStringTable[pTexData->nameStringTableID] ]; + + MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false ); + + const char *pMacroTextureName = GetMaterialVar( hMaterial, "$macro_texture" ); + if ( pMacroTextureName ) + { + if ( tempDict.Find( pMacroTextureName ) == tempDict.InvalidIndex() ) + { + Msg( "-- DiscoverMacroTextures: %s\n", pMacroTextureName ); + tempDict.Insert( pMacroTextureName, 0 ); + } + + int stringID = TexDataStringTable_AddOrFindString( pMacroTextureName ); + g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = (unsigned short)stringID; + } + else + { + g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = 0xFFFF; + } + } +} + + +// Make sure that we have a water lod control entity if we have water in the map. +void EnsurePresenceOfWaterLODControlEntity( void ) +{ + extern bool g_bHasWater; + if( !g_bHasWater ) + { + // Don't bother if there isn't any water in the map. + return; + } + for( int i=0; i < num_entities; i++ ) + { + entity_t *e = &entities[i]; + + const char *pClassName = ValueForKey( e, "classname" ); + if( !Q_stricmp( pClassName, "water_lod_control" ) ) + { + // Found one!!!! + return; + } + } + + // None found, add one. + Warning( "Water found with no water_lod_control entity, creating a default one.\n" ); + + entity_t *mapent = &entities[num_entities]; + num_entities++; + memset(mapent, 0, sizeof(*mapent)); + mapent->firstbrush = g_MainMap->nummapbrushes; + mapent->numbrushes = 0; + + SetKeyValue( mapent, "classname", "water_lod_control" ); + SetKeyValue( mapent, "cheapwaterstartdistance", "1000" ); + SetKeyValue( mapent, "cheapwaterenddistance", "2000" ); +} + + +/* +============ +EndBSPFile +============ +*/ +void EndBSPFile (void) +{ + // Mark noshadow faces. + MarkNoShadowFaces(); + + EmitBrushes (); + EmitPlanes (); + + // stick flat normals at the verts + SaveVertexNormals(); + + // Figure out lightmap extents for all faces. + UpdateAllFaceLightmapExtents(); + + // Generate geometry and lightmap alpha for displacements. + EmitDispLMAlphaAndNeighbors(); + + // Emit overlay data. + Overlay_EmitOverlayFaces(); + OverlayTransition_EmitOverlayFaces(); + + // phys collision needs dispinfo to operate (needs to generate phys collision for displacement surfs) + EmitPhysCollision(); + + // We can't calculate this properly until vvis (since we need vis to do this), so we set + // to zero everywhere by default. + ClearDistToClosestWater(); + + // Emit static props found in the .vmf file + EmitStaticProps(); + + // Place detail props found in .vmf and based on material properties + EmitDetailObjects(); + + // Compute bounds after creating disp info because we need to reference it + ComputeBoundsNoSkybox(); + + // Make sure that we have a water lod control eneity if we have water in the map. + EnsurePresenceOfWaterLODControlEntity(); + + // Doing this here because stuff about may filter out entities + UnparseEntities (); + + // remove unused texinfos + CompactTexinfos(); + + // Figure out which faces want macro textures. + DiscoverMacroTextures(); + + char targetPath[1024]; + GetPlatformMapPath( source, targetPath, g_nDXLevel, 1024 ); + Msg ("Writing %s\n", targetPath); + WriteBSPFile (targetPath); +} + + +/* +================== +BeginModel +================== +*/ +int firstmodleaf; +void BeginModel (void) +{ + dmodel_t *mod; + int start, end; + mapbrush_t *b; + int j; + entity_t *e; + Vector mins, maxs; + + if (nummodels == MAX_MAP_MODELS) + Error ("Too many brush models in map, max = %d", MAX_MAP_MODELS); + mod = &dmodels[nummodels]; + + mod->firstface = numfaces; + + firstmodleaf = numleafs; + firstmodeledge = numedges; + firstmodelface = numfaces; + + // + // bound the brushes + // + e = &entities[entity_num]; + + start = e->firstbrush; + end = start + e->numbrushes; + ClearBounds (mins, maxs); + + for (j=start ; jmapbrushes[j]; + if (!b->numsides) + continue; // not a real brush (origin brush) + AddPointToBounds (b->mins, mins, maxs); + AddPointToBounds (b->maxs, mins, maxs); + } + + VectorCopy (mins, mod->mins); + VectorCopy (maxs, mod->maxs); +} + + +/* +================== +EndModel +================== +*/ +void EndModel (void) +{ + dmodel_t *mod; + + mod = &dmodels[nummodels]; + + mod->numfaces = numfaces - mod->firstface; + + nummodels++; +} + + + +//----------------------------------------------------------------------------- +// figure out which leaf a point is in +//----------------------------------------------------------------------------- +static int PointLeafnum_r (const Vector& p, int num) +{ + float d; + while (num >= 0) + { + dnode_t* node = dnodes + num; + dplane_t* plane = dplanes + node->planenum; + + if (plane->type < 3) + d = p[plane->type] - plane->dist; + else + d = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return -1 - num; +} + +int PointLeafnum ( dmodel_t* pModel, const Vector& p ) +{ + return PointLeafnum_r (p, pModel->headnode); +} + + +//----------------------------------------------------------------------------- +// Adds a noew to the bounding box +//----------------------------------------------------------------------------- +static void AddNodeToBounds(int node, CUtlVector& skipAreas, Vector& mins, Vector& maxs) +{ + // not a leaf + if (node >= 0) + { + AddNodeToBounds( dnodes[node].children[0], skipAreas, mins, maxs ); + AddNodeToBounds( dnodes[node].children[1], skipAreas, mins, maxs ); + } + else + { + int leaf = - 1 - node; + + // Don't bother with solid leaves + if (dleafs[leaf].contents & CONTENTS_SOLID) + return; + + // Skip 3D skybox + int i; + for ( i = skipAreas.Count(); --i >= 0; ) + { + if (dleafs[leaf].area == skipAreas[i]) + return; + } + + unsigned int firstface = dleafs[leaf].firstleafface; + for ( i = 0; i < dleafs[leaf].numleaffaces; ++i ) + { + unsigned int face = dleaffaces[ firstface + i ]; + + // Skip skyboxes + nodraw + texinfo_t& tex = texinfo[dfaces[face].texinfo]; + if (tex.flags & (SURF_SKY | SURF_NODRAW)) + continue; + + unsigned int firstedge = dfaces[face].firstedge; + Assert( firstedge >= 0 ); + + for (int j = 0; j < dfaces[face].numedges; ++j) + { + Assert( firstedge+j < numsurfedges ); + int edge = abs(dsurfedges[firstedge+j]); + dedge_t* pEdge = &dedges[edge]; + Assert( pEdge->v[0] >= 0 ); + Assert( pEdge->v[1] >= 0 ); + AddPointToBounds (dvertexes[pEdge->v[0]].point, mins, maxs); + AddPointToBounds (dvertexes[pEdge->v[1]].point, mins, maxs); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Check to see if a displacement lives in any leaves that are not +// in the 3d skybox +//----------------------------------------------------------------------------- +bool IsBoxInsideWorld( int node, CUtlVector &skipAreas, const Vector &vecMins, const Vector &vecMaxs ) +{ + while( 1 ) + { + // leaf + if (node < 0) + { + // get the leaf + int leaf = - 1 - node; + + // Don't bother with solid leaves + if (dleafs[leaf].contents & CONTENTS_SOLID) + return false; + + // Skip 3D skybox + int i; + for ( i = skipAreas.Count(); --i >= 0; ) + { + if ( dleafs[leaf].area == skipAreas[i] ) + return false; + } + + return true; + } + + // + // get displacement bounding box position relative to the node plane + // + dnode_t *pNode = &dnodes[ node ]; + dplane_t *pPlane = &dplanes[ pNode->planenum ]; + + int sideResult = BrushBspBoxOnPlaneSide( vecMins, vecMaxs, pPlane ); + + // front side + if( sideResult == 1 ) + { + node = pNode->children[0]; + } + // back side + else if( sideResult == 2 ) + { + node = pNode->children[1]; + } + //split + else + { + if ( IsBoxInsideWorld( pNode->children[0], skipAreas, vecMins, vecMaxs ) ) + return true; + + node = pNode->children[1]; + } + } +} + + +//----------------------------------------------------------------------------- +// Adds the displacement surfaces in the world to the bounds +//----------------------------------------------------------------------------- +void AddDispsToBounds( int nHeadNode, CUtlVector& skipAreas, Vector &vecMins, Vector &vecMaxs ) +{ + Vector vecDispMins, vecDispMaxs; + + // first determine how many displacement surfaces there will be per leaf + int i; + for ( i = 0; i < g_dispinfo.Count(); ++i ) + { + ComputeDispInfoBounds( i, vecDispMins, vecDispMaxs ); + if ( IsBoxInsideWorld( nHeadNode, skipAreas, vecDispMins, vecDispMaxs ) ) + { + AddPointToBounds( vecDispMins, vecMins, vecMaxs ); + AddPointToBounds( vecDispMaxs, vecMins, vecMaxs ); + } + } +} + + +//----------------------------------------------------------------------------- +// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues +//----------------------------------------------------------------------------- +void ComputeBoundsNoSkybox( ) +{ + // Iterate over all world leaves, skip those which are part of skybox + Vector mins, maxs; + ClearBounds (mins, maxs); + AddNodeToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs ); + AddDispsToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs ); + + // Add the bounds to the worldspawn data + for (int i = 0; i < num_entities; ++i) + { + char* pEntity = ValueForKey(&entities[i], "classname"); + if (!strcmp(pEntity, "worldspawn")) + { + char string[32]; + sprintf (string, "%i %i %i", (int)mins[0], (int)mins[1], (int)mins[2]); + SetKeyValue (&entities[i], "world_mins", string); + sprintf (string, "%i %i %i", (int)maxs[0], (int)maxs[1], (int)maxs[2]); + SetKeyValue (&entities[i], "world_maxs", string); + break; + } + } +} + + -- cgit v1.2.3