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. --- sp/src/utils/vbsp/map.cpp | 6608 ++++++++++++++++++++++----------------------- 1 file changed, 3304 insertions(+), 3304 deletions(-) (limited to 'sp/src/utils/vbsp/map.cpp') diff --git a/sp/src/utils/vbsp/map.cpp b/sp/src/utils/vbsp/map.cpp index 34219bd4..a5456c32 100644 --- a/sp/src/utils/vbsp/map.cpp +++ b/sp/src/utils/vbsp/map.cpp @@ -1,3304 +1,3304 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "vbsp.h" -#include "map_shared.h" -#include "disp_vbsp.h" -#include "tier1/strtools.h" -#include "builddisp.h" -#include "tier0/icommandline.h" -#include "KeyValues.h" -#include "materialsub.h" -#include "fgdlib/fgdlib.h" -#include "manifest.h" - -#ifdef VSVMFIO -#include "VmfImport.h" -#endif // VSVMFIO - - -// undefine to make plane finding use linear sort -#define USE_HASHING - -#define RENDER_NORMAL_EPSILON 0.00001 -#define RENDER_DIST_EPSILON 0.01f - -#define BRUSH_CLIP_EPSILON 0.01f // this should probably be the same - // as clip epsilon, but it is 0.1f and I - // currently don't know how that number was - // come to (cab) - this is 0.01 of an inch - // for clipping brush solids -struct LoadSide_t -{ - mapbrush_t *pBrush; - side_t *pSide; - int nSideIndex; - int nBaseFlags; - int nBaseContents; - Vector planepts[3]; - brush_texture_t td; -}; - - -extern qboolean onlyents; - - -CUtlVector< CMapFile * > g_Maps; -CMapFile *g_MainMap = NULL; -CMapFile *g_LoadingMap = NULL; - -char CMapFile::m_InstancePath[ MAX_PATH ] = ""; -int CMapFile::m_InstanceCount = 0; -int CMapFile::c_areaportals = 0; - -void CMapFile::Init( void ) -{ - entity_num = 0; - num_entities = 0; - - nummapplanes = 0; - memset( mapplanes, 0, sizeof( mapplanes ) ); - - nummapbrushes = 0; - memset( mapbrushes, 0, sizeof( mapbrushes ) ); - - nummapbrushsides = 0; - memset( brushsides, 0, sizeof( brushsides ) ); - - memset( side_brushtextures, 0, sizeof( side_brushtextures ) ); - - memset( planehash, 0, sizeof( planehash ) ); - - m_ConnectionPairs = NULL; - - m_StartMapOverlays = g_aMapOverlays.Count(); - m_StartMapWaterOverlays = g_aMapWaterOverlays.Count(); - - c_boxbevels = 0; - c_edgebevels = 0; - c_clipbrushes = 0; - g_ClipTexinfo = -1; -} - - -// All the brush sides referenced by info_no_dynamic_shadow entities. -CUtlVector g_NoDynamicShadowSides; - - -void TestExpandBrushes (void); - -ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo ); -ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); - -#ifdef VSVMFIO -ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); -ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); -#endif // VSVMFIO - -ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam); -ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity); - -ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity); -ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity); - -ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity); -ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush); - -ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo); -ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo); - - - -/* -============================================================================= - -PLANE FINDING - -============================================================================= -*/ - - -/* -================= -PlaneTypeForNormal -================= -*/ -int PlaneTypeForNormal (Vector& normal) -{ - vec_t ax, ay, az; - -// NOTE: should these have an epsilon around 1.0? - if (normal[0] == 1.0 || normal[0] == -1.0) - return PLANE_X; - if (normal[1] == 1.0 || normal[1] == -1.0) - return PLANE_Y; - if (normal[2] == 1.0 || normal[2] == -1.0) - return PLANE_Z; - - ax = fabs(normal[0]); - ay = fabs(normal[1]); - az = fabs(normal[2]); - - if (ax >= ay && ax >= az) - return PLANE_ANYX; - if (ay >= ax && ay >= az) - return PLANE_ANYY; - return PLANE_ANYZ; -} - -/* -================ -PlaneEqual -================ -*/ -qboolean PlaneEqual (plane_t *p, Vector& normal, vec_t dist, float normalEpsilon, float distEpsilon) -{ -#if 1 - if ( - fabs(p->normal[0] - normal[0]) < normalEpsilon - && fabs(p->normal[1] - normal[1]) < normalEpsilon - && fabs(p->normal[2] - normal[2]) < normalEpsilon - && fabs(p->dist - dist) < distEpsilon ) - return true; -#else - if (p->normal[0] == normal[0] - && p->normal[1] == normal[1] - && p->normal[2] == normal[2] - && p->dist == dist) - return true; -#endif - return false; -} - -/* -================ -AddPlaneToHash -================ -*/ -void CMapFile::AddPlaneToHash (plane_t *p) -{ - int hash; - - hash = (int)fabs(p->dist) / 8; - hash &= (PLANE_HASHES-1); - - p->hash_chain = planehash[hash]; - planehash[hash] = p; -} - -/* -================ -CreateNewFloatPlane -================ -*/ -int CMapFile::CreateNewFloatPlane (Vector& normal, vec_t dist) -{ - plane_t *p, temp; - - if (VectorLength(normal) < 0.5) - g_MapError.ReportError ("FloatPlane: bad normal"); - // create a new plane - if (nummapplanes+2 > MAX_MAP_PLANES) - g_MapError.ReportError ("MAX_MAP_PLANES"); - - p = &mapplanes[nummapplanes]; - VectorCopy (normal, p->normal); - p->dist = dist; - p->type = (p+1)->type = PlaneTypeForNormal (p->normal); - - VectorSubtract (vec3_origin, normal, (p+1)->normal); - (p+1)->dist = -dist; - - nummapplanes += 2; - - // allways put axial planes facing positive first - if (p->type < 3) - { - if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) - { - // flip order - temp = *p; - *p = *(p+1); - *(p+1) = temp; - - AddPlaneToHash (p); - AddPlaneToHash (p+1); - return nummapplanes - 1; - } - } - - AddPlaneToHash (p); - AddPlaneToHash (p+1); - return nummapplanes - 2; -} - - -/* -============== -SnapVector -============== -*/ -bool SnapVector (Vector& normal) -{ - int i; - - for (i=0 ; i<3 ; i++) - { - if ( fabs(normal[i] - 1) < RENDER_NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = 1; - return true; - } - - if ( fabs(normal[i] - -1) < RENDER_NORMAL_EPSILON ) - { - VectorClear (normal); - normal[i] = -1; - return true; - } - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial. -// Rounds dist to integer if it is within an epsilon of integer. -// Input : normal - Plane normal vector (assumed to be unit length). -// dist - Plane constant. -//----------------------------------------------------------------------------- -void SnapPlane(Vector &normal, vec_t &dist) -{ - SnapVector(normal); - - if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON) - { - dist = RoundInt(dist); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial. -// Recalculates dist if the normal was snapped. Rounds dist to integer -// if it is within an epsilon of integer. -// Input : normal - Plane normal vector (assumed to be unit length). -// dist - Plane constant. -// p0, p1, p2 - Three points on the plane. -//----------------------------------------------------------------------------- -void SnapPlane(Vector &normal, vec_t &dist, const Vector &p0, const Vector &p1, const Vector &p2) -{ - if (SnapVector(normal)) - { - // - // Calculate a new plane constant using the snapped normal. Use the - // centroid of the three plane points to minimize error. This is like - // rotating the plane around the centroid. - // - Vector p3 = (p0 + p1 + p2) / 3.0f; - dist = normal.Dot(p3); - if ( g_snapAxialPlanes ) - { - dist = RoundInt(dist); - } - } - - if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON) - { - dist = RoundInt(dist); - } -} - - -/* -============= -FindFloatPlane - -============= -*/ -#ifndef USE_HASHING -int CMapFile::FindFloatPlane (Vector& normal, vec_t dist) -{ - int i; - plane_t *p; - - SnapPlane(normal, dist); - for (i=0, p=mapplanes ; ihash_chain) - { - if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON)) - return p-mapplanes; - } - } - - return CreateNewFloatPlane (normal, dist); -} -#endif - - -//----------------------------------------------------------------------------- -// Purpose: Builds a plane normal and distance from three points on the plane. -// If the normal is nearly axial, it will be snapped to be axial. Looks -// up the plane in the unique planes. -// Input : p0, p1, p2 - Three points on the plane. -// Output : Returns the index of the plane in the planes list. -//----------------------------------------------------------------------------- -int CMapFile::PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2) -{ - Vector t1, t2, normal; - vec_t dist; - - VectorSubtract (p0, p1, t1); - VectorSubtract (p2, p1, t2); - CrossProduct (t1, t2, normal); - VectorNormalize (normal); - - dist = DotProduct (p0, normal); - - SnapPlane(normal, dist, p0, p1, p2); - - return FindFloatPlane (normal, dist); -} - - -/* -=========== -BrushContents -=========== -*/ -int BrushContents (mapbrush_t *b) -{ - int contents; - int unionContents = 0; - side_t *s; - int i; - - s = &b->original_sides[0]; - contents = s->contents; - unionContents = contents; - for (i=1 ; inumsides ; i++, s++) - { - s = &b->original_sides[i]; - - unionContents |= s->contents; -#if 0 - if (s->contents != contents) - { - Msg("Brush %i: mixed face contents\n", b->id); - break; - } -#endif - } - - // NOTE: we're making slime translucent so that it doesn't block lighting on things floating on its surface - int transparentContents = unionContents & (CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_WATER|CONTENTS_SLIME); - if ( transparentContents ) - { - contents |= transparentContents | CONTENTS_TRANSLUCENT; - contents &= ~CONTENTS_SOLID; - } - - return contents; -} - - -//============================================================================ - -bool IsAreaPortal( char const *pClassName ) -{ - // If the class name starts with "func_areaportal", then it's considered an area portal. - char const *pBaseName = "func_areaportal"; - char const *pCur = pBaseName; - while( *pCur && *pClassName ) - { - if( *pCur != *pClassName ) - break; - - ++pCur; - ++pClassName; - } - - return *pCur == 0; -} - - -/* -================= -AddBrushBevels - -Adds any additional planes necessary to allow the brush to be expanded -against axial bounding boxes -================= -*/ -void CMapFile::AddBrushBevels (mapbrush_t *b) -{ - int axis, dir; - int i, j, k, l, order; - side_t sidetemp; - brush_texture_t tdtemp; - side_t *s, *s2; - Vector normal; - float dist; - winding_t *w, *w2; - Vector vec, vec2; - float d; - - // - // add the axial planes - // - order = 0; - for (axis=0 ; axis <3 ; axis++) - { - for (dir=-1 ; dir <= 1 ; dir+=2, order++) - { - // see if the plane is allready present - for (i=0, s=b->original_sides ; inumsides ; i++,s++) - { - if (mapplanes[s->planenum].normal[axis] == dir) - break; - } - - if (i == b->numsides) - { // add a new side - if (nummapbrushsides == MAX_MAP_BRUSHSIDES) - g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); - nummapbrushsides++; - b->numsides++; - VectorClear (normal); - normal[axis] = dir; - if (dir == 1) - dist = b->maxs[axis]; - else - dist = -b->mins[axis]; - s->planenum = FindFloatPlane (normal, dist); - s->texinfo = b->original_sides[0].texinfo; - s->contents = b->original_sides[0].contents; - s->bevel = true; - c_boxbevels++; - } - - // if the plane is not in it canonical order, swap it - if (i != order) - { - sidetemp = b->original_sides[order]; - b->original_sides[order] = b->original_sides[i]; - b->original_sides[i] = sidetemp; - - j = b->original_sides - brushsides; - tdtemp = side_brushtextures[j+order]; - side_brushtextures[j+order] = side_brushtextures[j+i]; - side_brushtextures[j+i] = tdtemp; - } - } - } - - // - // add the edge bevels - // - if (b->numsides == 6) - return; // pure axial - - // test the non-axial plane edges - for (i=6 ; inumsides ; i++) - { - s = b->original_sides + i; - w = s->winding; - if (!w) - continue; - for (j=0 ; jnumpoints ; j++) - { - k = (j+1)%w->numpoints; - VectorSubtract (w->p[j], w->p[k], vec); - if (VectorNormalize (vec) < 0.5) - continue; - SnapVector (vec); - for (k=0 ; k<3 ; k++) - if ( vec[k] == -1 || vec[k] == 1) - break; // axial - if (k != 3) - continue; // only test non-axial edges - - // try the six possible slanted axials from this edge - for (axis=0 ; axis <3 ; axis++) - { - for (dir=-1 ; dir <= 1 ; dir+=2) - { - // construct a plane - VectorClear (vec2); - vec2[axis] = dir; - CrossProduct (vec, vec2, normal); - if (VectorNormalize (normal) < 0.5) - continue; - dist = DotProduct (w->p[j], normal); - - // if all the points on all the sides are - // behind this plane, it is a proper edge bevel - for (k=0 ; knumsides ; k++) - { - // if this plane has allready been used, skip it - // NOTE: Use a larger tolerance for collision planes than for rendering planes - if ( PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist, 0.01f, 0.01f ) ) - break; - - w2 = b->original_sides[k].winding; - if (!w2) - continue; - for (l=0 ; lnumpoints ; l++) - { - d = DotProduct (w2->p[l], normal) - dist; - if (d > 0.1) - break; // point in front - } - if (l != w2->numpoints) - break; - } - - if (k != b->numsides) - continue; // wasn't part of the outer hull - // add this plane - if (nummapbrushsides == MAX_MAP_BRUSHSIDES) - g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); - nummapbrushsides++; - s2 = &b->original_sides[b->numsides]; - s2->planenum = FindFloatPlane (normal, dist); - s2->texinfo = b->original_sides[0].texinfo; - s2->contents = b->original_sides[0].contents; - s2->bevel = true; - c_edgebevels++; - b->numsides++; - } - } - } - } -} - -/* -================ -MakeBrushWindings - -makes basewindigs for sides and mins / maxs for the brush -================ -*/ -qboolean CMapFile::MakeBrushWindings (mapbrush_t *ob) -{ - int i, j; - winding_t *w; - side_t *side; - plane_t *plane; - - ClearBounds (ob->mins, ob->maxs); - - for (i=0 ; inumsides ; i++) - { - plane = &mapplanes[ob->original_sides[i].planenum]; - w = BaseWindingForPlane (plane->normal, plane->dist); - for (j=0 ; jnumsides && w; j++) - { - if (i == j) - continue; - if (ob->original_sides[j].bevel) - continue; - plane = &mapplanes[ob->original_sides[j].planenum^1]; -// ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); - // adding an epsilon here, due to precision issues creating complex - // displacement surfaces (cab) - ChopWindingInPlace( &w, plane->normal, plane->dist, BRUSH_CLIP_EPSILON ); - } - - side = &ob->original_sides[i]; - side->winding = w; - if (w) - { - side->visible = true; - for (j=0 ; jnumpoints ; j++) - AddPointToBounds (w->p[j], ob->mins, ob->maxs); - } - } - - for (i=0 ; i<3 ; i++) - { - if (ob->mins[i] < MIN_COORD_INTEGER || ob->maxs[i] > MAX_COORD_INTEGER) - Msg("Brush %i: bounds out of range\n", ob->id); - if (ob->mins[i] > MAX_COORD_INTEGER || ob->maxs[i] < MIN_COORD_INTEGER) - Msg("Brush %i: no visible sides on brush\n", ob->id); - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Takes all of the brushes from the current entity and adds them to the -// world's brush list. Used by func_detail and func_areaportal. -// THIS ROUTINE MAY ONLY BE USED DURING ENTITY LOADING. -// Input : mapent - Entity whose brushes are to be moved to the world. -//----------------------------------------------------------------------------- -void CMapFile::MoveBrushesToWorld( entity_t *mapent ) -{ - int newbrushes; - int worldbrushes; - mapbrush_t *temp; - int i; - - // this is pretty gross, because the brushes are expected to be - // in linear order for each entity - - newbrushes = mapent->numbrushes; - worldbrushes = entities[0].numbrushes; - - temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t)); - memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t)); - -#if 0 // let them keep their original brush numbers - for (i=0 ; inumbrushes = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Takes all of the brushes from the current entity and adds them to the -// world's brush list. Used by func_detail and func_areaportal. -// Input : mapent - Entity whose brushes are to be moved to the world. -//----------------------------------------------------------------------------- -void CMapFile::MoveBrushesToWorldGeneral( entity_t *mapent ) -{ - int newbrushes; - int worldbrushes; - mapbrush_t *temp; - int i; - - for( i = 0; i < nummapdispinfo; i++ ) - { - if ( mapdispinfo[ i ].entitynum == ( mapent - entities ) ) - { - mapdispinfo[ i ].entitynum = 0; - } - } - - // this is pretty gross, because the brushes are expected to be - // in linear order for each entity - newbrushes = mapent->numbrushes; - worldbrushes = entities[0].numbrushes; - - temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t)); - memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t)); - -#if 0 // let them keep their original brush numbers - for (i=0 ; ifirstbrush - worldbrushes) ); - - - // wwwxxxmmyyy - - // copy the new brushes down - memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes); - - // fix up indexes - entities[0].numbrushes += newbrushes; - for (i=1 ; ifirstbrush ) // if we use <=, then we'll remap the passed in ent, which we don't want to - { - entities[ i ].firstbrush += newbrushes; - } - } - free (temp); - - mapent->numbrushes = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Iterates the sides of brush and removed CONTENTS_DETAIL from each side -// Input : *brush - -//----------------------------------------------------------------------------- -void RemoveContentsDetailFromBrush( mapbrush_t *brush ) -{ - // Only valid on non-world brushes - Assert( brush->entitynum != 0 ); - - side_t *s; - int i; - - s = &brush->original_sides[0]; - for ( i=0 ; inumsides ; i++, s++ ) - { - if ( s->contents & CONTENTS_DETAIL ) - { - s->contents &= ~CONTENTS_DETAIL; - } - } - -} - -//----------------------------------------------------------------------------- -// Purpose: Iterates all brushes in an entity and removes CONTENTS_DETAIL from all brushes -// Input : *mapent - -//----------------------------------------------------------------------------- -void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent ) -{ - int i; - for ( i = 0; i < mapent->numbrushes; i++ ) - { - int brushnum = mapent->firstbrush + i; - - mapbrush_t *brush = &mapbrushes[ brushnum ]; - RemoveContentsDetailFromBrush( brush ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pFile - -// *pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) -{ - return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pMapDispInfo)); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : szKey - -// szValue - -// pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) -{ - if (!strnicmp(szKey, "row", 3)) - { - char szBuf[MAX_KEYVALUE_LEN]; - strcpy(szBuf, szValue); - - int nCols = (1 << pMapDispInfo->power) + 1; - int nRow = atoi(&szKey[3]); - - char *pszNext = strtok(szBuf, " "); - int nIndex = nRow * nCols; - - while (pszNext != NULL) - { - pMapDispInfo->dispDists[nIndex] = (float)atof(pszNext); - pszNext = strtok(NULL, " "); - nIndex++; - } - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: load in the displacement info "chunk" from the .map file into the -// vbsp map displacement info data structure -// Output : return the index of the map displacement info -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo ) -{ - // - // check to see if we exceeded the maximum displacement info list size - // - if (nummapdispinfo > MAX_MAP_DISPINFO) - { - g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO" ); - } - - // get a pointer to the next available displacement info slot - mapdispinfo_t *pMapDispInfo = &mapdispinfo[nummapdispinfo]; - nummapdispinfo++; - - // - // Set up handlers for the subchunks that we are interested in. - // - CChunkHandlerMap Handlers; - Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, pMapDispInfo); - Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, pMapDispInfo); - Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, pMapDispInfo); - Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, pMapDispInfo); - Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, pMapDispInfo); - -#ifdef VSVMFIO - Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, pMapDispInfo); -#endif // VSVMFIO - - // - // Read the displacement chunk. - // - pFile->PushHandlers(&Handlers); - ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispInfoKeyCallback, pMapDispInfo); - pFile->PopHandlers(); - - if (eResult == ChunkFile_Ok) - { - // return a pointer to the displacement info - *ppMapDispInfo = pMapDispInfo; - } - - return(eResult); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *mapent - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) -{ - if (!stricmp(szKey, "power")) - { - CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->power); - } -#ifdef VSVMFIO - else if (!stricmp(szKey, "elevation")) - { - CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->m_elevation); - } -#endif // VSVMFIO - else if (!stricmp(szKey, "uaxis")) - { - CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->uAxis); - } - else if (!stricmp(szKey, "vaxis")) - { - CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->vAxis); - } - else if( !stricmp( szKey, "startposition" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pMapDispInfo->startPosition ); - } - else if( !stricmp( szKey, "flags" ) ) - { - CChunkFile::ReadKeyValueInt( szValue, pMapDispInfo->flags ); - } -#if 0 // old data - else if (!stricmp( szKey, "alpha" ) ) - { - CChunkFile::ReadKeyValueVector4( szValue, pMapDispInfo->alphaValues ); - } -#endif - else if (!stricmp(szKey, "mintess")) - { - CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->minTess); - } - else if (!stricmp(szKey, "smooth")) - { - CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->smoothingAngle); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pFile - -// *pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) -{ - return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pMapDispInfo)); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) -{ - if (!strnicmp(szKey, "row", 3)) - { - char szBuf[MAX_KEYVALUE_LEN]; - strcpy(szBuf, szValue); - - int nCols = (1 << pMapDispInfo->power) + 1; - int nRow = atoi(&szKey[3]); - - char *pszNext0 = strtok(szBuf, " "); - char *pszNext1 = strtok(NULL, " "); - char *pszNext2 = strtok(NULL, " "); - - int nIndex = nRow * nCols; - - while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL)) - { - pMapDispInfo->vectorDisps[nIndex][0] = (float)atof(pszNext0); - pMapDispInfo->vectorDisps[nIndex][1] = (float)atof(pszNext1); - pMapDispInfo->vectorDisps[nIndex][2] = (float)atof(pszNext2); - - pszNext0 = strtok(NULL, " "); - pszNext1 = strtok(NULL, " "); - pszNext2 = strtok(NULL, " "); - - nIndex++; - } - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) -{ - return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pMapDispInfo)); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) -{ - if (!strnicmp(szKey, "row", 3)) - { - char szBuf[MAX_KEYVALUE_LEN]; - strcpy(szBuf, szValue); - - int nCols = (1 << pMapDispInfo->power) + 1; - int nRow = atoi(&szKey[3]); - - char *pszNext0 = strtok(szBuf, " "); - char *pszNext1 = strtok(NULL, " "); - char *pszNext2 = strtok(NULL, " "); - - int nIndex = nRow * nCols; - - while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL)) - { - pMapDispInfo->vectorOffsets[nIndex][0] = (float)atof(pszNext0); - pMapDispInfo->vectorOffsets[nIndex][1] = (float)atof(pszNext1); - pMapDispInfo->vectorOffsets[nIndex][2] = (float)atof(pszNext2); - - pszNext0 = strtok(NULL, " "); - pszNext1 = strtok(NULL, " "); - pszNext2 = strtok(NULL, " "); - - nIndex++; - } - } - - return(ChunkFile_Ok); -} - - -#ifdef VSVMFIO -ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) -{ - return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pMapDispInfo)); -} - - -ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) -{ - if (!strnicmp(szKey, "row", 3)) - { - char szBuf[MAX_KEYVALUE_LEN]; - strcpy(szBuf, szValue); - - int nCols = (1 << pMapDispInfo->power) + 1; - int nRow = atoi(&szKey[3]); - - char *pszNext0 = strtok(szBuf, " "); - char *pszNext1 = strtok(NULL, " "); - char *pszNext2 = strtok(NULL, " "); - - int nIndex = nRow * nCols; - - while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL)) - { - pMapDispInfo->m_offsetNormals[nIndex][0] = (float)atof(pszNext0); - pMapDispInfo->m_offsetNormals[nIndex][1] = (float)atof(pszNext1); - pMapDispInfo->m_offsetNormals[nIndex][2] = (float)atof(pszNext2); - - pszNext0 = strtok(NULL, " "); - pszNext1 = strtok(NULL, " "); - pszNext2 = strtok(NULL, " "); - - nIndex++; - } - } - - return(ChunkFile_Ok); -} -#endif // VSVMFIO - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) -{ - return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pMapDispInfo)); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *szKey - -// *szValue - -// *pDisp - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) -{ - if (!strnicmp(szKey, "row", 3)) - { - char szBuf[MAX_KEYVALUE_LEN]; - strcpy(szBuf, szValue); - - int nCols = (1 << pMapDispInfo->power) + 1; - int nRow = atoi(&szKey[3]); - - char *pszNext0 = strtok(szBuf, " "); - - int nIndex = nRow * nCols; - - while (pszNext0 != NULL) - { - pMapDispInfo->alphaValues[nIndex] = (float)atof(pszNext0); - pszNext0 = strtok(NULL, " "); - nIndex++; - } - } - - return(ChunkFile_Ok); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) -{ - return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pMapDispInfo)); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) -{ - if ( !strnicmp( szKey, "row", 3 ) ) - { - char szBuf[MAX_KEYVALUE_LEN]; - strcpy( szBuf, szValue ); - - int nCols = ( 1 << pMapDispInfo->power ); - int nRow = atoi( &szKey[3] ); - - char *pszNext = strtok( szBuf, " " ); - - int nIndex = nRow * nCols; - int iTri = nIndex * 2; - - while ( pszNext != NULL ) - { - // Collapse the tags here! - unsigned short nTriTags = ( unsigned short )atoi( pszNext ); - - // Walkable - bool bWalkable = ( ( nTriTags & COREDISPTRI_TAG_WALKABLE ) != 0 ); - if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) != 0 ) ) - { - bWalkable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_VAL ) != 0 ); - } - - // Buildable - bool bBuildable = ( ( nTriTags & COREDISPTRI_TAG_BUILDABLE ) != 0 ); - if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) != 0 ) ) - { - bBuildable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ) != 0 ); - } - - nTriTags = 0; - if ( bWalkable ) - { - nTriTags |= DISPTRI_TAG_WALKABLE; - } - - if ( bBuildable ) - { - nTriTags |= DISPTRI_TAG_BUILDABLE; - } - - pMapDispInfo->triTags[iTri] = nTriTags; - pszNext = strtok( NULL, " " ); - iTri++; - } - } - - return( ChunkFile_Ok ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : brushSideID - -// Output : int -//----------------------------------------------------------------------------- -int CMapFile::SideIDToIndex( int brushSideID ) -{ - int i; - for ( i = 0; i < nummapbrushsides; i++ ) - { - if ( brushsides[i].id == brushSideID ) - { - return i; - } - } - Assert( 0 ); - return -1; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *mapent - -// *key - -//----------------------------------------------------------------------------- -void ConvertSideList( entity_t *mapent, char *key ) -{ - char *pszSideList = ValueForKey( mapent, key ); - - if (pszSideList) - { - char *pszTmpList = ( char* )_alloca( strlen( pszSideList ) + 1 ); - strcpy( pszTmpList, pszSideList ); - - bool bFirst = true; - char szNewValue[1024]; - szNewValue[0] = '\0'; - - const char *pszScan = strtok( pszTmpList, " " ); - if ( !pszScan ) - return; - do - { - int nSideID; - - if ( sscanf( pszScan, "%d", &nSideID ) == 1 ) - { - int nIndex = g_LoadingMap->SideIDToIndex(nSideID); - if (nIndex != -1) - { - if (!bFirst) - { - strcat( szNewValue, " " ); - } - else - { - bFirst = false; - } - - char szIndex[15]; - itoa( nIndex, szIndex, 10 ); - strcat( szNewValue, szIndex ); - } - } - } while ( ( pszScan = strtok( NULL, " " ) ) ); - - SetKeyValue( mapent, key, szNewValue ); - } -} - - -// Add all the sides referenced by info_no_dynamic_shadows entities to g_NoDynamicShadowSides. -ChunkFileResult_t HandleNoDynamicShadowsEnt( entity_t *pMapEnt ) -{ - // Get the list of the sides. - char *pSideList = ValueForKey( pMapEnt, "sides" ); - - // Parse the side list. - char *pScan = strtok( pSideList, " " ); - if( pScan ) - { - do - { - int brushSideID; - if( sscanf( pScan, "%d", &brushSideID ) == 1 ) - { - if ( g_NoDynamicShadowSides.Find( brushSideID ) == -1 ) - g_NoDynamicShadowSides.AddToTail( brushSideID ); - } - } while( ( pScan = strtok( NULL, " " ) ) ); - } - - // Clear out this entity. - pMapEnt->epairs = NULL; - return ( ChunkFile_Ok ); -} - - -static ChunkFileResult_t LoadOverlayDataTransitionKeyCallback( const char *szKey, const char *szValue, mapoverlay_t *pOverlay ) -{ - if ( !stricmp( szKey, "material" ) ) - { - // Get the material name. - const char *pMaterialName = szValue; - if( g_ReplaceMaterials ) - { - pMaterialName = ReplaceMaterialName( szValue ); - } - - Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN ); - if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN ) - { - Error( "Overlay Material Name (%s) > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN ); - return ChunkFile_Fail; - } - strcpy( pOverlay->szMaterialName, pMaterialName ); - } - else if ( !stricmp( szKey, "StartU") ) - { - CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[0] ); - } - else if ( !stricmp( szKey, "EndU" ) ) - { - CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[1] ); - } - else if ( !stricmp( szKey, "StartV" ) ) - { - CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[0] ); - } - else if ( !stricmp( szKey, "EndV" ) ) - { - CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[1] ); - } - else if ( !stricmp( szKey, "BasisOrigin" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecOrigin ); - } - else if ( !stricmp( szKey, "BasisU" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[0] ); - } - else if ( !stricmp( szKey, "BasisV" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[1] ); - } - else if ( !stricmp( szKey, "BasisNormal" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[2] ); - } - else if ( !stricmp( szKey, "uv0" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[0] ); - } - else if ( !stricmp( szKey, "uv1" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[1] ); - } - else if ( !stricmp( szKey, "uv2" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[2] ); - } - else if ( !stricmp( szKey, "uv3" ) ) - { - CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[3] ); - } - else if ( !stricmp( szKey, "sides" ) ) - { - const char *pSideList = szValue; - char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 ); - strcpy( pTmpList, pSideList ); - const char *pScan = strtok( pTmpList, " " ); - if ( !pScan ) - return ChunkFile_Fail; - - pOverlay->aSideList.Purge(); - pOverlay->aFaceList.Purge(); - - do - { - int nSideId; - if ( sscanf( pScan, "%d", &nSideId ) == 1 ) - { - pOverlay->aSideList.AddToTail( nSideId ); - } - } while ( ( pScan = strtok( NULL, " " ) ) ); - } - - return ChunkFile_Ok; -} - -static ChunkFileResult_t LoadOverlayDataTransitionCallback( CChunkFile *pFile, int nParam ) -{ - int iOverlay = g_aMapWaterOverlays.AddToTail(); - mapoverlay_t *pOverlay = &g_aMapWaterOverlays[iOverlay]; - if ( !pOverlay ) - return ChunkFile_Fail; - - pOverlay->nId = ( MAX_MAP_OVERLAYS + 1 ) + g_aMapWaterOverlays.Count() - 1; - pOverlay->m_nRenderOrder = 0; - - ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadOverlayDataTransitionKeyCallback, pOverlay ); - return eResult; -} - -static ChunkFileResult_t LoadOverlayTransitionCallback( CChunkFile *pFile, int nParam ) -{ - CChunkHandlerMap Handlers; - Handlers.AddHandler( "overlaydata", ( ChunkHandler_t )LoadOverlayDataTransitionCallback, 0 ); - pFile->PushHandlers( &Handlers ); - - ChunkFileResult_t eResult = pFile->ReadChunk( NULL, NULL ); - - pFile->PopHandlers(); - - return eResult; -} - -//----------------------------------------------------------------------------- -// Purpose: Iterates all brushes in a ladder entity, generates its mins and maxs. -// These are stored in the object, since the brushes are going to go away. -// Input : *mapent - -//----------------------------------------------------------------------------- -void CMapFile::AddLadderKeys( entity_t *mapent ) -{ - Vector mins, maxs; - ClearBounds( mins, maxs ); - - int i; - for ( i = 0; i < mapent->numbrushes; i++ ) - { - int brushnum = mapent->firstbrush + i; - mapbrush_t *brush = &mapbrushes[ brushnum ]; - - AddPointToBounds( brush->mins, mins, maxs ); - AddPointToBounds( brush->maxs, mins, maxs ); - } - - char buf[16]; - - Q_snprintf( buf, sizeof(buf), "%2.2f", mins.x ); - SetKeyValue( mapent, "mins.x", buf ); - - Q_snprintf( buf, sizeof(buf), "%2.2f", mins.y ); - SetKeyValue( mapent, "mins.y", buf ); - - Q_snprintf( buf, sizeof(buf), "%2.2f", mins.z ); - SetKeyValue( mapent, "mins.z", buf ); - - Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.x ); - SetKeyValue( mapent, "maxs.x", buf ); - - Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.y ); - SetKeyValue( mapent, "maxs.y", buf ); - - Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.z ); - SetKeyValue( mapent, "maxs.z", buf ); -} - -ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam) -{ - return g_LoadingMap->LoadEntityCallback( pFile, nParam ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pFile - -// ulParam - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) -{ - if (num_entities == MAX_MAP_ENTITIES) - { - // Exits. - g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); - } - - entity_t *mapent = &entities[num_entities]; - num_entities++; - memset(mapent, 0, sizeof(*mapent)); - mapent->firstbrush = nummapbrushes; - mapent->numbrushes = 0; - //mapent->portalareas[0] = -1; - //mapent->portalareas[1] = -1; - - LoadEntity_t LoadEntity; - LoadEntity.pEntity = mapent; - - // No default flags/contents - LoadEntity.nBaseFlags = 0; - LoadEntity.nBaseContents = 0; - - // - // Set up handlers for the subchunks that we are interested in. - // - CChunkHandlerMap Handlers; - Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity); - Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, &LoadEntity); - Handlers.AddHandler( "overlaytransition", ( ChunkHandler_t )LoadOverlayTransitionCallback, 0 ); - - // - // Read the entity chunk. - // - pFile->PushHandlers(&Handlers); - ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity); - pFile->PopHandlers(); - - if (eResult == ChunkFile_Ok) - { - GetVectorForKey (mapent, "origin", mapent->origin); - - const char *pMinDXLevelStr = ValueForKey( mapent, "mindxlevel" ); - const char *pMaxDXLevelStr = ValueForKey( mapent, "maxdxlevel" ); - if( *pMinDXLevelStr != '\0' || *pMaxDXLevelStr != '\0' ) - { - int min = 0; - int max = 0; - if( *pMinDXLevelStr ) - { - min = atoi( pMinDXLevelStr ); - } - if( *pMaxDXLevelStr ) - { - max = atoi( pMaxDXLevelStr ); - } - - // Set min and max to default values. - if( min == 0 ) - { - min = g_nDXLevel; - } - if( max == 0 ) - { - max = g_nDXLevel; - } - if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < min || g_nDXLevel > max ) ) - { - mapent->numbrushes = 0; - mapent->epairs = NULL; - return(ChunkFile_Ok); - } - } - - // offset all of the planes and texinfo - if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) - { - for (int i=0 ; inumbrushes ; i++) - { - mapbrush_t *b = &mapbrushes[mapent->firstbrush + i]; - for (int j=0 ; jnumsides ; j++) - { - side_t *s = &b->original_sides[j]; - vec_t newdist = mapplanes[s->planenum].dist - DotProduct (mapplanes[s->planenum].normal, mapent->origin); - s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist); - if ( !onlyents ) - { - s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], &side_brushtextures[s-brushsides], mapent->origin); - } - } - MakeBrushWindings (b); - } - } - - // - // func_detail brushes are moved into the world entity. The CONTENTS_DETAIL flag was set by the loader. - // - const char *pClassName = ValueForKey( mapent, "classname" ); - - if ( !strcmp( "func_detail", pClassName ) ) - { - MoveBrushesToWorld (mapent); - mapent->numbrushes = 0; - - // clear out this entity - mapent->epairs = NULL; - return(ChunkFile_Ok); - } - - // these get added to a list for processing the portal file - // but aren't necessary to emit to the BSP - if ( !strcmp( "func_viscluster", pClassName ) ) - { - AddVisCluster(mapent); - return(ChunkFile_Ok); - } - - // - // func_ladder brushes are moved into the world entity. We convert the func_ladder to an info_ladder - // that holds the ladder's mins and maxs, and leave the entity. This helps the bots figure out ladders. - // - if ( !strcmp( "func_ladder", pClassName ) ) - { - AddLadderKeys( mapent ); - - MoveBrushesToWorld (mapent); - - // Convert to info_ladder entity - SetKeyValue( mapent, "classname", "info_ladder" ); - - return(ChunkFile_Ok); - } - - if( !strcmp( "env_cubemap", pClassName ) ) - { - if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) ) - { - const char *pSideListStr = ValueForKey( mapent, "sides" ); - int size; - size = IntForKey( mapent, "cubemapsize" ); - Cubemap_InsertSample( mapent->origin, size ); - Cubemap_SaveBrushSides( pSideListStr ); - } - // clear out this entity - mapent->epairs = NULL; - return(ChunkFile_Ok); - } - - if ( !strcmp( "test_sidelist", pClassName ) ) - { - ConvertSideList(mapent, "sides"); - return ChunkFile_Ok; - } - - if ( !strcmp( "info_overlay", pClassName ) ) - { - int iAccessorID = Overlay_GetFromEntity( mapent ); - - if ( iAccessorID < 0 ) - { - // Clear out this entity. - mapent->epairs = NULL; - } - else - { - // Convert to info_overlay_accessor entity - SetKeyValue( mapent, "classname", "info_overlay_accessor" ); - - // Remember the id for accessing the overlay - char buf[16]; - Q_snprintf( buf, sizeof(buf), "%i", iAccessorID ); - SetKeyValue( mapent, "OverlayID", buf ); - } - - return ( ChunkFile_Ok ); - } - - if ( !strcmp( "info_overlay_transition", pClassName ) ) - { - // Clear out this entity. - mapent->epairs = NULL; - return ( ChunkFile_Ok ); - } - - if ( Q_stricmp( pClassName, "info_no_dynamic_shadow" ) == 0 ) - { - return HandleNoDynamicShadowsEnt( mapent ); - } - - if ( Q_stricmp( pClassName, "func_instance_parms" ) == 0 ) - { - // Clear out this entity. - mapent->epairs = NULL; - return ( ChunkFile_Ok ); - } - - // areaportal entities move their brushes, but don't eliminate - // the entity - if( IsAreaPortal( pClassName ) ) - { - char str[128]; - - if (mapent->numbrushes != 1) - { - Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1); - } - - mapbrush_t *b = &mapbrushes[nummapbrushes-1]; - b->contents = CONTENTS_AREAPORTAL; - c_areaportals++; - mapent->areaportalnum = c_areaportals; - - // set the portal number as "portalnumber" - sprintf (str, "%i", c_areaportals); - SetKeyValue (mapent, "portalnumber", str); - - MoveBrushesToWorld (mapent); - return(ChunkFile_Ok); - } - -#ifdef VSVMFIO - if ( !Q_stricmp( pClassName, "light" ) ) - { - CVmfImport::GetVmfImporter()->ImportLightCallback( - ValueForKey( mapent, "hammerid" ), - ValueForKey( mapent, "origin" ), - ValueForKey( mapent, "_light" ), - ValueForKey( mapent, "_lightHDR" ), - ValueForKey( mapent, "_lightscaleHDR" ), - ValueForKey( mapent, "_quadratic_attn" ) ); - } - - if ( !Q_stricmp( pClassName, "light_spot" ) ) - { - CVmfImport::GetVmfImporter()->ImportLightSpotCallback( - ValueForKey( mapent, "hammerid" ), - ValueForKey( mapent, "origin" ), - ValueForKey( mapent, "angles" ), - ValueForKey( mapent, "pitch" ), - ValueForKey( mapent, "_light" ), - ValueForKey( mapent, "_lightHDR" ), - ValueForKey( mapent, "_lightscaleHDR" ), - ValueForKey( mapent, "_quadratic_attn" ), - ValueForKey( mapent, "_inner_cone" ), - ValueForKey( mapent, "_cone" ), - ValueForKey( mapent, "_exponent" ) ); - } - - if ( !Q_stricmp( pClassName, "light_dynamic" ) ) - { - CVmfImport::GetVmfImporter()->ImportLightDynamicCallback( - ValueForKey( mapent, "hammerid" ), - ValueForKey( mapent, "origin" ), - ValueForKey( mapent, "angles" ), - ValueForKey( mapent, "pitch" ), - ValueForKey( mapent, "_light" ), - ValueForKey( mapent, "_quadratic_attn" ), - ValueForKey( mapent, "_inner_cone" ), - ValueForKey( mapent, "_cone" ), - ValueForKey( mapent, "brightness" ), - ValueForKey( mapent, "distance" ), - ValueForKey( mapent, "spotlight_radius" ) ); - } - - if ( !Q_stricmp( pClassName, "light_environment" ) ) - { - CVmfImport::GetVmfImporter()->ImportLightEnvironmentCallback( - ValueForKey( mapent, "hammerid" ), - ValueForKey( mapent, "origin" ), - ValueForKey( mapent, "angles" ), - ValueForKey( mapent, "pitch" ), - ValueForKey( mapent, "_light" ), - ValueForKey( mapent, "_lightHDR" ), - ValueForKey( mapent, "_lightscaleHDR" ), - ValueForKey( mapent, "_ambient" ), - ValueForKey( mapent, "_ambientHDR" ), - ValueForKey( mapent, "_AmbientScaleHDR" ), - ValueForKey( mapent, "SunSpreadAngle" ) ); - } - - const char *pModel = ValueForKey( mapent, "model" ); - if ( pModel && Q_strlen( pModel ) ) - { - CVmfImport::GetVmfImporter()->ImportModelCallback( - pModel, - ValueForKey( mapent, "hammerid" ), - ValueForKey( mapent, "angles" ), - ValueForKey( mapent, "origin" ), - MDagPath() ); - } -#endif // VSVMFIO - - // If it's not in the world at this point, unmark CONTENTS_DETAIL from all sides... - if ( mapent != &entities[ 0 ] ) - { - RemoveContentsDetailFromEntity( mapent ); - } - - return(ChunkFile_Ok); - } - - return(eResult); -} - - -entity_t* EntityByName( char const *pTestName ) -{ - if( !pTestName ) - return 0; - - for( int i=0; i < g_MainMap->num_entities; i++ ) - { - entity_t *e = &g_MainMap->entities[i]; - - const char *pName = ValueForKey( e, "targetname" ); - if( stricmp( pName, pTestName ) == 0 ) - return e; - } - - return 0; -} - - -void CMapFile::ForceFuncAreaPortalWindowContents() -{ - // Now go through all areaportal entities and force CONTENTS_WINDOW - // on the brushes of the bmodels they point at. - char *targets[] = {"target", "BackgroundBModel"}; - int nTargets = sizeof(targets) / sizeof(targets[0]); - - for( int i=0; i < num_entities; i++ ) - { - entity_t *e = &entities[i]; - - const char *pClassName = ValueForKey( e, "classname" ); - - // Don't do this on "normal" func_areaportal entities. Those are tied to doors - // and should be opaque when closed. But areaportal windows (and any other - // distance-based areaportals) should be windows because they are normally open/transparent - if( !IsAreaPortal( pClassName ) || !Q_stricmp( pClassName, "func_areaportal" ) ) - continue; - -// const char *pTestEntName = ValueForKey( e, "targetname" ); - - for( int iTarget=0; iTarget < nTargets; iTarget++ ) - { - char const *pEntName = ValueForKey( e, targets[iTarget] ); - if( !pEntName[0] ) - continue; - - entity_t *pBrushEnt = EntityByName( pEntName ); - if( !pBrushEnt ) - continue; - - for( int iBrush=0; iBrush < pBrushEnt->numbrushes; iBrush++ ) - { - mapbrushes[pBrushEnt->firstbrush + iBrush].contents &= ~CONTENTS_SOLID; - mapbrushes[pBrushEnt->firstbrush + iBrush].contents |= CONTENTS_TRANSLUCENT | CONTENTS_WINDOW; - } - } - } -} - - -// ============ Instancing ============ - -// #define MERGE_INSTANCE_DEBUG_INFO 1 - -#define INSTANCE_VARIABLE_KEY "replace" - -static GameData GD; - -//----------------------------------------------------------------------------- -// Purpose: this function will read in a standard key / value file -// Input : pFilename - the absolute name of the file to read -// Output : returns the KeyValues of the file, NULL if the file could not be read. -//----------------------------------------------------------------------------- -static KeyValues *ReadKeyValuesFile( const char *pFilename ) -{ - // Read in the gameinfo.txt file and null-terminate it. - FILE *fp = fopen( pFilename, "rb" ); - if ( !fp ) - return NULL; - CUtlVector buf; - fseek( fp, 0, SEEK_END ); - buf.SetSize( ftell( fp ) + 1 ); - fseek( fp, 0, SEEK_SET ); - fread( buf.Base(), 1, buf.Count()-1, fp ); - fclose( fp ); - buf[buf.Count()-1] = 0; - - KeyValues *kv = new KeyValues( "" ); - if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) ) - { - kv->deleteThis(); - return NULL; - } - - return kv; -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will set a secondary lookup path for instances. -// Input : pszInstancePath - the secondary lookup path -//----------------------------------------------------------------------------- -void CMapFile::SetInstancePath( const char *pszInstancePath ) -{ - strcpy( m_InstancePath, pszInstancePath ); - V_strlower( m_InstancePath ); - V_FixSlashes( m_InstancePath ); -} - - -//----------------------------------------------------------------------------- -// Purpose: This function will attempt to find a full path given the base and relative names. -// Input : pszBaseFileName - the base file that referenced this instance -// pszInstanceFileName - the relative file name of this instance -// Output : Returns true if it was able to locate the file -// pszOutFileName - the full path to the file name if located -//----------------------------------------------------------------------------- -bool CMapFile::DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName ) -{ - char szInstanceFileNameFixed[ MAX_PATH ]; - const char *pszMapPath = "\\maps\\"; - - strcpy( szInstanceFileNameFixed, pszInstanceFileName ); - V_SetExtension( szInstanceFileNameFixed, ".vmf", sizeof( szInstanceFileNameFixed ) ); - V_FixSlashes( szInstanceFileNameFixed ); - - // first, try to find a relative location based upon the Base file name - strcpy( pszOutFileName, pszBaseFileName ); - V_StripFilename( pszOutFileName ); - - strcat( pszOutFileName, "\\" ); - strcat( pszOutFileName, szInstanceFileNameFixed ); - - if ( g_pFullFileSystem->FileExists( pszOutFileName ) ) - { - return true; - } - - // second, try to find the master 'maps' directory and make it relative from that - strcpy( pszOutFileName, pszBaseFileName ); - V_StripFilename( pszOutFileName ); - V_RemoveDotSlashes( pszOutFileName ); - V_FixDoubleSlashes( pszOutFileName ); - V_strlower( pszOutFileName ); - strcat( pszOutFileName, "\\" ); - - char *pos = strstr( pszOutFileName, pszMapPath ); - if ( pos ) - { - pos += strlen( pszMapPath ); - *pos = 0; - strcat( pszOutFileName, szInstanceFileNameFixed ); - - if ( g_pFullFileSystem->FileExists( pszOutFileName ) ) - { - return true; - } - } - - if ( m_InstancePath[ 0 ] != 0 ) - { - sprintf( szInstanceFileNameFixed, "%s%s", m_InstancePath, pszInstanceFileName ); - - if ( g_pFullFileSystem->FileExists( szInstanceFileNameFixed, "GAME" ) ) - { - char FullPath[ MAX_PATH ]; - g_pFullFileSystem->RelativePathToFullPath( szInstanceFileNameFixed, "GAME", FullPath, sizeof( FullPath ) ); - strcpy( pszOutFileName, FullPath ); - - return true; - } - } - - pszOutFileName[ 0 ] = 0; - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will check the main map for any func_instances. It will -// also attempt to load in the gamedata file for instancing remapping help. -// Input : none -// Output : none -//----------------------------------------------------------------------------- -void CMapFile::CheckForInstances( const char *pszFileName ) -{ - if ( this != g_MainMap ) - { // all sub-instances will be appended to the main map master list as they are read in - // so the main loop below will naturally get to the appended ones. - return; - } - - char GameInfoPath[ MAX_PATH ]; - - g_pFullFileSystem->RelativePathToFullPath( "gameinfo.txt", "MOD", GameInfoPath, sizeof( GameInfoPath ) ); - KeyValues *GameInfoKV = ReadKeyValuesFile( GameInfoPath ); - if ( !GameInfoKV ) - { - Msg( "Could not locate gameinfo.txt for Instance Remapping at %s\n", GameInfoPath ); - return; - } - - const char *InstancePath = GameInfoKV->GetString( "InstancePath", NULL ); - if ( InstancePath ) - { - CMapFile::SetInstancePath( InstancePath ); - } - - const char *GameDataFile = GameInfoKV->GetString( "GameData", NULL ); - if ( !GameDataFile ) - { - Msg( "Could not locate 'GameData' key in %s\n", GameInfoPath ); - return; - } - - char FDGPath[ MAX_PATH ]; - if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) ) - { - if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "", FDGPath, sizeof( FDGPath ) ) ) - { - Msg( "Could not locate GameData file %s\n", GameDataFile ); - } - } - - GD.Load( FDGPath ); - - // this list will grow as instances are merged onto it. sub-instances are merged and - // automatically done in this processing. - for ( int i = 0; i < num_entities; i++ ) - { - char *pEntity = ValueForKey( &entities[ i ], "classname" ); - if ( !strcmp( pEntity, "func_instance" ) ) - { - char *pInstanceFile = ValueForKey( &entities[ i ], "file" ); - if ( pInstanceFile[ 0 ] ) - { - char InstancePath[ MAX_PATH ]; - bool bLoaded = false; - - if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) ) - { - if ( LoadMapFile( InstancePath ) ) - { - MergeInstance( &entities[ i ], g_LoadingMap ); - delete g_LoadingMap; - bLoaded = true; - } - } - - if ( bLoaded == false ) - { - Color red( 255, 0, 0, 255 ); - - ColorSpewMessage( SPEW_ERROR, &red, "Could not open instance file %s\n", pInstanceFile ); - } - } - - entities[ i ].numbrushes = 0; - entities[ i ].epairs = NULL; - } - } - - g_LoadingMap = this; -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will do all of the necessary work to merge the instance -// into the main map. -// Input : pInstanceEntity - the entity of the func_instance -// Instance - the map file of the instance -// Output : none -//----------------------------------------------------------------------------- -void CMapFile::MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance ) -{ - matrix3x4_t mat; - QAngle angles; - Vector OriginOffset = pInstanceEntity->origin; - - m_InstanceCount++; - - GetAnglesForKey( pInstanceEntity, "angles", angles ); - AngleMatrix( angles, OriginOffset, mat ); - -#ifdef MERGE_INSTANCE_DEBUG_INFO - Msg( "Instance Remapping: O:( %g, %g, %g ) A:( %g, %g, %g )\n", OriginOffset.x, OriginOffset.y, OriginOffset.z, angles.x, angles.y, angles.z ); -#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO - MergePlanes( pInstanceEntity, Instance, OriginOffset, angles, mat ); - MergeBrushes( pInstanceEntity, Instance, OriginOffset, angles, mat ); - MergeBrushSides( pInstanceEntity, Instance, OriginOffset, angles, mat ); - MergeEntities( pInstanceEntity, Instance, OriginOffset, angles, mat ); - MergeOverlays( pInstanceEntity, Instance, OriginOffset, angles, mat ); -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will merge in the map planes from the instance into -// the main map. -// Input : pInstanceEntity - the entity of the func_instance -// Instance - the map file of the instance -// InstanceOrigin - the translation of the instance -// InstanceAngle - the rotation of the instance -// InstanceMatrix - the translation / rotation matrix of the instance -// Output : none -//----------------------------------------------------------------------------- -void CMapFile::MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) -{ - // Each pair of planes needs to be added to the main map - for ( int i = 0; i < Instance->nummapplanes; i += 2 ) - { - FindFloatPlane( Instance->mapplanes[i].normal, Instance->mapplanes[i].dist ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will merge in the map brushes from the instance into -// the main map. -// Input : pInstanceEntity - the entity of the func_instance -// Instance - the map file of the instance -// InstanceOrigin - the translation of the instance -// InstanceAngle - the rotation of the instance -// InstanceMatrix - the translation / rotation matrix of the instance -// Output : none -//----------------------------------------------------------------------------- -void CMapFile::MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) -{ - int max_brush_id = 0; - - for( int i = 0; i < nummapbrushes; i++ ) - { - if ( mapbrushes[ i ].id > max_brush_id ) - { - max_brush_id = mapbrushes[ i ].id; - } - } - - for( int i = 0; i < Instance->nummapbrushes; i++ ) - { - mapbrushes[ nummapbrushes + i ] = Instance->mapbrushes[ i ]; - - mapbrush_t *brush = &mapbrushes[ nummapbrushes + i ]; - brush->entitynum += num_entities; - brush->brushnum += nummapbrushes; - - if ( i < Instance->entities[ 0 ].numbrushes || ( brush->contents & CONTENTS_LADDER ) != 0 ) - { // world spawn brushes as well as ladders we physically move - Vector minsIn = brush->mins; - Vector maxsIn = brush->maxs; - - TransformAABB( InstanceMatrix, minsIn, maxsIn, brush->mins, brush->maxs ); - } - else - { - } - brush->id += max_brush_id; - - int index = brush->original_sides - Instance->brushsides; - brush->original_sides = &brushsides[ nummapbrushsides + index ]; - } - - nummapbrushes += Instance->nummapbrushes; -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will merge in the map sides from the instance into -// the main map. -// Input : pInstanceEntity - the entity of the func_instance -// Instance - the map file of the instance -// InstanceOrigin - the translation of the instance -// InstanceAngle - the rotation of the instance -// InstanceMatrix - the translation / rotation matrix of the instance -// Output : none -//----------------------------------------------------------------------------- -void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) -{ - int max_side_id = 0; - - for( int i = 0; i < nummapbrushsides; i++ ) - { - if ( brushsides[ i ].id > max_side_id ) - { - max_side_id = brushsides[ i ].id; - } - } - - for( int i = 0; i < Instance->nummapbrushsides; i++ ) - { - brushsides[ nummapbrushsides + i ] = Instance->brushsides[ i ]; - - side_t *side = &brushsides[ nummapbrushsides + i ]; - // The planes got merged & remapped. So you need to search for the output plane index on each side - // NOTE: You could optimize this by saving off an index map in MergePlanes - side->planenum = FindFloatPlane( Instance->mapplanes[side->planenum].normal, Instance->mapplanes[side->planenum].dist ); - side->id += max_side_id; - - // this could be pre-processed into a list for quicker checking - bool bNeedsTranslation = ( side->pMapDisp && side->pMapDisp->entitynum == 0 ); - if ( !bNeedsTranslation ) - { // check for sides that are part of the world spawn - those need translating - for( int j = 0; j < Instance->entities[ 0 ].numbrushes; j++ ) - { - int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides; - - if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) ) - { - bNeedsTranslation = true; - break; - } - } - } - if ( !bNeedsTranslation ) - { // sides for ladders are outside of the world spawn, but also need translating - for( int j = Instance->entities[ 0 ].numbrushes; j < Instance->nummapbrushes; j++ ) - { - int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides; - - if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) && ( Instance->mapbrushes[ j ].contents & CONTENTS_LADDER ) != 0 ) - { - bNeedsTranslation = true; - break; - } - } - } - if ( bNeedsTranslation ) - { // we only want to do the adjustment on world spawn brushes, not entity brushes - if ( side->winding ) - { - for( int point = 0; point < side->winding->numpoints; point++ ) - { - Vector inPoint = side->winding->p[ point ]; - VectorTransform( inPoint, InstanceMatrix, side->winding->p[ point ] ); - } - } - - int planenum = side->planenum; - cplane_t inPlane, outPlane; - inPlane.normal = mapplanes[ planenum ].normal; - inPlane.dist = mapplanes[ planenum ].dist; - - MatrixTransformPlane( InstanceMatrix, inPlane, outPlane ); - planenum = FindFloatPlane( outPlane.normal, outPlane.dist ); - side->planenum = planenum; - - brush_texture_t bt = Instance->side_brushtextures[ i ]; - - VectorRotate( Instance->side_brushtextures[ i ].UAxis, InstanceMatrix, bt.UAxis ); - VectorRotate( Instance->side_brushtextures[ i ].VAxis, InstanceMatrix, bt.VAxis ); - bt.shift[ 0 ] -= InstanceOrigin.Dot( bt.UAxis ) / bt.textureWorldUnitsPerTexel[ 0 ]; - bt.shift[ 1 ] -= InstanceOrigin.Dot( bt.VAxis ) / bt.textureWorldUnitsPerTexel[ 1 ]; - - if ( !onlyents ) - { - side->texinfo = TexinfoForBrushTexture ( &mapplanes[ side->planenum ], &bt, vec3_origin ); - } - } - - if ( side->pMapDisp ) - { - mapdispinfo_t *disp = side->pMapDisp; - - disp->brushSideID = side->id; - Vector inPoint = disp->startPosition; - VectorTransform( inPoint, InstanceMatrix, disp->startPosition ); - - disp->face.originalface = side; - disp->face.texinfo = side->texinfo; - disp->face.planenum = side->planenum; - disp->entitynum += num_entities; - - for( int point = 0; point < disp->face.w->numpoints; point++ ) - { - Vector inPoint = disp->face.w->p[ point ]; - VectorTransform( inPoint, InstanceMatrix, disp->face.w->p[ point ] ); - } - - } - } - - nummapbrushsides += Instance->nummapbrushsides; -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will look for replace parameters in the function instance -// to see if there is anything in the epair that should be replaced. -// Input : pPair - the epair with the value -// pInstanceEntity - the func_instance that may ahve replace keywords -// Output : pPair - the value field may be updated -//----------------------------------------------------------------------------- -void CMapFile::ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity ) -{ - char Value[ MAX_KEYVALUE_LEN ], NewValue[ MAX_KEYVALUE_LEN ]; - bool Overwritten = false; - - strcpy( NewValue, pPair->value ); - for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next ) - { - if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 ) - { - char InstanceVariable[ MAX_KEYVALUE_LEN ]; - - strcpy( InstanceVariable, epInstance->value ); - - char *ValuePos = strchr( InstanceVariable, ' ' ); - if ( !ValuePos ) - { - continue; - } - *ValuePos = 0; - ValuePos++; - - strcpy( Value, NewValue ); - if ( !V_StrSubst( Value, InstanceVariable, ValuePos, NewValue, sizeof( NewValue ), false ) ) - { - Overwritten = true; - break; - } - } - } - - if ( !Overwritten && strcmp( pPair->value, NewValue ) != 0 ) - { - free( pPair->value ); - pPair->value = copystring( NewValue ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will merge in the entities from the instance into -// the main map. -// Input : pInstanceEntity - the entity of the func_instance -// Instance - the map file of the instance -// InstanceOrigin - the translation of the instance -// InstanceAngle - the rotation of the instance -// InstanceMatrix - the translation / rotation matrix of the instance -// Output : none -//----------------------------------------------------------------------------- -void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) -{ - int max_entity_id = 0; - char temp[ 2048 ]; - char NameFixup[ 128 ]; - entity_t *WorldspawnEnt = NULL; - GameData::TNameFixup FixupStyle; - - char *pTargetName = ValueForKey( pInstanceEntity, "targetname" ); - char *pName = ValueForKey( pInstanceEntity, "name" ); - if ( pTargetName[ 0 ] ) - { - sprintf( NameFixup, "%s", pTargetName ); - } - else if ( pName[ 0 ] ) - { - sprintf( NameFixup, "%s", pName ); - } - else - { - sprintf( NameFixup, "InstanceAuto%d", m_InstanceCount ); - } - - for( int i = 0; i < num_entities; i++ ) - { - char *pID = ValueForKey( &entities[ i ], "hammerid" ); - if ( pID[ 0 ] ) - { - int value = atoi( pID ); - if ( value > max_entity_id ) - { - max_entity_id = value; - } - } - } - - FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) ); - - for( int i = 0; i < Instance->num_entities; i++ ) - { - entities[ num_entities + i ] = Instance->entities[ i ]; - - entity_t *entity = &entities[ num_entities + i ]; - entity->firstbrush += ( nummapbrushes - Instance->nummapbrushes ); - - char *pID = ValueForKey( entity, "hammerid" ); - if ( pID[ 0 ] ) - { - int value = atoi( pID ); - value += max_entity_id; - sprintf( temp, "%d", value ); - - SetKeyValue( entity, "hammerid", temp ); - } - - char *pEntity = ValueForKey( entity, "classname" ); - if ( strcmpi( pEntity, "worldspawn" ) == 0 ) - { - WorldspawnEnt = entity; - } - else - { - Vector inOrigin = entity->origin; - VectorTransform( inOrigin, InstanceMatrix, entity->origin ); - - // search for variables coming from the func_instance to replace inside of the instance - // this is done before entity fixup, so fixup may occur on the replaced value. Not sure if this is a desired order of operation yet. - for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next ) - { - ReplaceInstancePair( ep, pInstanceEntity ); - } - -#ifdef MERGE_INSTANCE_DEBUG_INFO - Msg( "Remapping class %s\n", pEntity ); -#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO - GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle ); - if ( EntClass ) - { - for( int i = 0; i < EntClass->GetVariableCount(); i++ ) - { - GDinputvariable *EntVar = EntClass->GetVariableAt( i ); - char *pValue = ValueForKey( entity, ( char * )EntVar->GetName() ); - if ( GD.RemapKeyValue( EntVar->GetName(), pValue, temp, FixupStyle ) ) - { -#ifdef MERGE_INSTANCE_DEBUG_INFO - Msg( " %d. Remapped %s: from %s to %s\n", i, EntVar->GetName(), pValue, temp ); -#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO - SetKeyValue( entity, EntVar->GetName(), temp ); - } - else - { -#ifdef MERGE_INSTANCE_DEBUG_INFO - Msg( " %d. Ignored %s: %s\n", i, EntVar->GetName(), pValue ); -#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO - } - } - } - - if ( strcmpi( pEntity, "func_simpleladder" ) == 0 ) - { // hate having to do this, but the key values are so screwed up - AddLadderKeys( entity ); -/* Vector vInNormal, vOutNormal; - - vInNormal.x = FloatForKey( entity, "normal.x" ); - vInNormal.y = FloatForKey( entity, "normal.y" ); - vInNormal.z = FloatForKey( entity, "normal.z" ); - VectorRotate( vInNormal, InstanceMatrix, vOutNormal ); - - Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.x ); - SetKeyValue( entity, "normal.x", temp ); - - Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.y ); - SetKeyValue( entity, "normal.y", temp ); - - Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z ); - SetKeyValue( entity, "normal.z", temp );*/ - } - } - -#ifdef MERGE_INSTANCE_DEBUG_INFO - Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i ); - Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush ); - Msg( " KV Pairs:\n" ); - for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next ) - { - Msg( " %s %s\n", ep->key, ep->value ); - } -#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO - } - - // search for variables coming from the func_instance to replace inside of the instance - // this is done before connection fix up, so fix up may occur on the replaced value. Not sure if this is a desired order of operation yet. - for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next ) - { - ReplaceInstancePair( Connection->m_Pair, pInstanceEntity ); - } - - for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next ) - { - char *newValue, *oldValue; - char origValue[ 4096 ]; - int extraLen = 0; - - oldValue = Connection->m_Pair->value; - strcpy( origValue, oldValue ); - char *pos = strchr( origValue, ',' ); - if ( pos ) - { // null terminate the first field - *pos = NULL; - extraLen = strlen( pos + 1) + 1; // for the comma we just null'd - } - - if ( GD.RemapNameField( origValue, temp, FixupStyle ) ) - { - newValue = new char [ strlen( temp ) + extraLen + 1 ]; - strcpy( newValue, temp ); - if ( pos ) - { - strcat( newValue, "," ); - strcat( newValue, pos + 1 ); - } - - Connection->m_Pair->value = newValue; - delete oldValue; - } - } - - num_entities += Instance->num_entities; - - MoveBrushesToWorldGeneral( WorldspawnEnt ); - WorldspawnEnt->numbrushes = 0; - WorldspawnEnt->epairs = NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: this function will translate overlays from the instance into -// the main map. -// Input : InstanceEntityNum - the entity number of the func_instance -// Instance - the map file of the instance -// InstanceOrigin - the translation of the instance -// InstanceAngle - the rotation of the instance -// InstanceMatrix - the translation / rotation matrix of the instance -// Output : none -//----------------------------------------------------------------------------- -void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) -{ - for( int i = Instance->m_StartMapOverlays; i < g_aMapOverlays.Count(); i++ ) - { - Overlay_Translate( &g_aMapOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix ); - } - for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ ) - { - Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Loads a VMF or MAP file. If the file has a .MAP extension, the MAP -// loader is used, otherwise the file is assumed to be in VMF format. -// Input : pszFileName - Full path of the map file to load. -//----------------------------------------------------------------------------- -bool LoadMapFile( const char *pszFileName ) -{ - bool bLoadingManifest = false; - CManifest *pMainManifest = NULL; - ChunkFileResult_t eResult; - - // - // Dummy this up for the texture handling. This can be removed when old .MAP file - // support is removed. - // - g_nMapFileVersion = 400; - - const char *pszExtension =V_GetFileExtension( pszFileName ); - if ( pszExtension && strcmpi( pszExtension, "vmm" ) == 0 ) - { - pMainManifest = new CManifest(); - if ( pMainManifest->LoadVMFManifest( pszFileName ) ) - { - eResult = ChunkFile_Ok; - pszFileName = pMainManifest->GetInstancePath(); - } - else - { - eResult = ChunkFile_Fail; - } - bLoadingManifest = true; - } - else - { - // - // Open the file. - // - CChunkFile File; - eResult = File.Open(pszFileName, ChunkFile_Read); - - // - // Read the file. - // - if (eResult == ChunkFile_Ok) - { - int index = g_Maps.AddToTail( new CMapFile() ); - g_LoadingMap = g_Maps[ index ]; - if ( g_MainMap == NULL ) - { - g_MainMap = g_LoadingMap; - } - - if ( g_MainMap == g_LoadingMap || verbose ) - { - Msg( "Loading %s\n", pszFileName ); - } - - - // reset the displacement info count - // nummapdispinfo = 0; - - // - // Set up handlers for the subchunks that we are interested in. - // - CChunkHandlerMap Handlers; - Handlers.AddHandler("world", (ChunkHandler_t)LoadEntityCallback, 0); - Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0); - - File.PushHandlers(&Handlers); - - // - // Read the sub-chunks. We ignore keys in the root of the file. - // - while (eResult == ChunkFile_Ok) - { - eResult = File.ReadChunk(); - } - - File.PopHandlers(); - } - else - { - Error("Error opening %s: %s.\n", pszFileName, File.GetErrorText(eResult)); - } - } - - if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF)) - { - // Update the overlay/side list(s). - Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays ); - OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays ); - - g_LoadingMap->CheckForInstances( pszFileName ); - - if ( pMainManifest ) - { - pMainManifest->CordonWorld(); - } - - ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs); - for (int i=0 ; ientities[0].numbrushes ; i++) - { - // HLTOOLS: Raise map limits - if (g_LoadingMap->mapbrushes[i].mins[0] > MAX_COORD_INTEGER) - { - continue; // no valid points - } - - AddPointToBounds (g_LoadingMap->mapbrushes[i].mins, g_LoadingMap->map_mins, g_LoadingMap->map_maxs); - AddPointToBounds (g_LoadingMap->mapbrushes[i].maxs, g_LoadingMap->map_mins, g_LoadingMap->map_maxs); - } - - qprintf ("%5i brushes\n", g_LoadingMap->nummapbrushes); - qprintf ("%5i clipbrushes\n", g_LoadingMap->c_clipbrushes); - qprintf ("%5i total sides\n", g_LoadingMap->nummapbrushsides); - qprintf ("%5i boxbevels\n", g_LoadingMap->c_boxbevels); - qprintf ("%5i edgebevels\n", g_LoadingMap->c_edgebevels); - qprintf ("%5i entities\n", g_LoadingMap->num_entities); - qprintf ("%5i planes\n", g_LoadingMap->nummapplanes); - qprintf ("%5i areaportals\n", g_LoadingMap->c_areaportals); - qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", g_LoadingMap->map_mins[0],g_LoadingMap->map_mins[1],g_LoadingMap->map_mins[2], - g_LoadingMap->map_maxs[0],g_LoadingMap->map_maxs[1],g_LoadingMap->map_maxs[2]); - - //TestExpandBrushes(); - - // Clear the error reporting - g_MapError.ClearState(); - } - - if ( g_MainMap == g_LoadingMap ) - { - num_entities = g_MainMap->num_entities; - memcpy( entities, g_MainMap->entities, sizeof( g_MainMap->entities ) ); - } - g_LoadingMap->ForceFuncAreaPortalWindowContents(); - - return ( ( eResult == ChunkFile_Ok ) || ( eResult == ChunkFile_EOF ) ); -} - -ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo) -{ - return g_LoadingMap->LoadSideCallback( pFile, pSideInfo ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pFile - -// pParent - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo) -{ - if (nummapbrushsides == MAX_MAP_BRUSHSIDES) - { - g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); - } - - pSideInfo->pSide = &brushsides[nummapbrushsides]; - - side_t *side = pSideInfo->pSide; - mapbrush_t *b = pSideInfo->pBrush; - g_MapError.BrushSide( pSideInfo->nSideIndex++ ); - - // initialize the displacement info - pSideInfo->pSide->pMapDisp = NULL; - - // - // Set up handlers for the subchunks that we are interested in. - // - CChunkHandlerMap Handlers; - Handlers.AddHandler( "dispinfo", ( ChunkHandler_t )LoadDispInfoCallback, &side->pMapDisp ); - - // - // Read the side chunk. - // - pFile->PushHandlers(&Handlers); - ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSideKeyCallback, pSideInfo); - pFile->PopHandlers(); - - if (eResult == ChunkFile_Ok) - { - side->contents |= pSideInfo->nBaseContents; - side->surf |= pSideInfo->nBaseFlags; - pSideInfo->td.flags |= pSideInfo->nBaseFlags; - - if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - { - side->contents |= CONTENTS_DETAIL; - } - - if (fulldetail ) - { - side->contents &= ~CONTENTS_DETAIL; - } - - if (!(side->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) ) - { - side->contents |= CONTENTS_SOLID; - } - - // hints and skips are never detail, and have no content - if (side->surf & (SURF_HINT|SURF_SKIP) ) - { - side->contents = 0; - } - - // - // find the plane number - // - int planenum = PlaneFromPoints(pSideInfo->planepts[0], pSideInfo->planepts[1], pSideInfo->planepts[2]); - if (planenum != -1) - { - // - // See if the plane has been used already. - // - int k; - for ( k = 0; k < b->numsides; k++) - { - side_t *s2 = b->original_sides + k; - if (s2->planenum == planenum) - { - g_MapError.ReportWarning("duplicate plane"); - break; - } - if ( s2->planenum == (planenum^1) ) - { - g_MapError.ReportWarning("mirrored plane"); - break; - } - } - - // - // If the plane hasn't been used already, keep this side. - // - if (k == b->numsides) - { - side = b->original_sides + b->numsides; - side->planenum = planenum; - if ( !onlyents ) - { - side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &pSideInfo->td, vec3_origin); - } - - // save the td off in case there is an origin brush and we - // have to recalculate the texinfo - if (nummapbrushsides == MAX_MAP_BRUSHSIDES) - g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); - side_brushtextures[nummapbrushsides] = pSideInfo->td; - nummapbrushsides++; - b->numsides++; - -#ifdef VSVMFIO - // Tell Maya We Have Another Side - if ( CVmfImport::GetVmfImporter() ) - { - CVmfImport::GetVmfImporter()->AddSideCallback( - b, side, pSideInfo->td, - pSideInfo->planepts[ 0 ], pSideInfo->planepts[ 1 ], pSideInfo->planepts[ 2 ] ); - } -#endif // VSVMFIO - - } - } - else - { - g_MapError.ReportWarning("plane with no normal"); - } - } - - return(eResult); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : szKey - -// szValue - -// pSideInfo - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo) -{ - if (!stricmp(szKey, "plane")) - { - int nRead = sscanf(szValue, "(%f %f %f) (%f %f %f) (%f %f %f)", - &pSideInfo->planepts[0][0], &pSideInfo->planepts[0][1], &pSideInfo->planepts[0][2], - &pSideInfo->planepts[1][0], &pSideInfo->planepts[1][1], &pSideInfo->planepts[1][2], - &pSideInfo->planepts[2][0], &pSideInfo->planepts[2][1], &pSideInfo->planepts[2][2]); - - if (nRead != 9) - { - g_MapError.ReportError("parsing plane definition"); - } - } - else if (!stricmp(szKey, "material")) - { - // Get the material name. - if( g_ReplaceMaterials ) - { - szValue = ReplaceMaterialName( szValue ); - } - - strcpy(pSideInfo->td.name, szValue); - g_MapError.TextureState(szValue); - - // Find default flags and values for this material. - int mt = FindMiptex(pSideInfo->td.name); - pSideInfo->td.flags = textureref[mt].flags; - pSideInfo->td.lightmapWorldUnitsPerLuxel = textureref[mt].lightmapWorldUnitsPerLuxel; - - pSideInfo->pSide->contents = textureref[mt].contents; - pSideInfo->pSide->surf = pSideInfo->td.flags; - } - else if (!stricmp(szKey, "uaxis")) - { - int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.UAxis[0], &pSideInfo->td.UAxis[1], &pSideInfo->td.UAxis[2], &pSideInfo->td.shift[0], &pSideInfo->td.textureWorldUnitsPerTexel[0]); - if (nRead != 5) - { - g_MapError.ReportError("parsing U axis definition"); - } - } - else if (!stricmp(szKey, "vaxis")) - { - int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.VAxis[0], &pSideInfo->td.VAxis[1], &pSideInfo->td.VAxis[2], &pSideInfo->td.shift[1], &pSideInfo->td.textureWorldUnitsPerTexel[1]); - if (nRead != 5) - { - g_MapError.ReportError("parsing V axis definition"); - } - } - else if (!stricmp(szKey, "lightmapscale")) - { - pSideInfo->td.lightmapWorldUnitsPerLuxel = atoi(szValue); - if (pSideInfo->td.lightmapWorldUnitsPerLuxel == 0.0f) - { - g_MapError.ReportWarning("luxel size of 0"); - pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize; - } - pSideInfo->td.lightmapWorldUnitsPerLuxel *= g_luxelScale; - if (pSideInfo->td.lightmapWorldUnitsPerLuxel < g_minLuxelScale) - { - pSideInfo->td.lightmapWorldUnitsPerLuxel = g_minLuxelScale; - } - } - else if (!stricmp(szKey, "contents")) - { - pSideInfo->pSide->contents |= atoi(szValue); - } - else if (!stricmp(szKey, "flags")) - { - pSideInfo->td.flags |= atoi(szValue); - pSideInfo->pSide->surf = pSideInfo->td.flags; - } - else if (!stricmp(szKey, "id")) - { - pSideInfo->pSide->id = atoi( szValue ); - } - else if (!stricmp(szKey, "smoothing_groups")) - { - pSideInfo->pSide->smoothingGroups = atoi( szValue ); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: Reads the connections chunk of the entity. -// Input : pFile - Chunk file to load from. -// pLoadEntity - Structure to receive loaded entity information. -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity) -{ - return(pFile->ReadChunk((KeyHandler_t)LoadConnectionsKeyCallback, pLoadEntity)); -} - - -//----------------------------------------------------------------------------- -// Purpose: Parses a key/value pair from the entity connections chunk. -// Input : szKey - Key indicating the name of the entity output. -// szValue - Comma delimited fields in the following format: -// ,,,, -// pLoadEntity - Structure to receive loaded entity information. -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity) -{ - return g_LoadingMap->LoadConnectionsKeyCallback( szKey, szValue, pLoadEntity ); -} - -ChunkFileResult_t CMapFile::LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity) -{ - // - // Create new input and fill it out. - // - epair_t *pOutput = new epair_t; - - pOutput->key = new char [strlen(szKey) + 1]; - pOutput->value = new char [strlen(szValue) + 1]; - - strcpy(pOutput->key, szKey); - strcpy(pOutput->value, szValue); - - m_ConnectionPairs = new CConnectionPairs( pOutput, m_ConnectionPairs ); - - // - // Append it to the end of epairs list. - // - pOutput->next = NULL; - - if (!pLoadEntity->pEntity->epairs) - { - pLoadEntity->pEntity->epairs = pOutput; - } - else - { - epair_t *ep; - for ( ep = pLoadEntity->pEntity->epairs; ep->next != NULL; ep = ep->next ) - { - } - ep->next = pOutput; - } - - return(ChunkFile_Ok); -} - - -ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity) -{ - return g_LoadingMap->LoadSolidCallback( pFile, pLoadEntity ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pFile - -// pParent - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity) -{ - if (nummapbrushes == MAX_MAP_BRUSHES) - { - g_MapError.ReportError ("nummapbrushes == MAX_MAP_BRUSHES"); - } - - mapbrush_t *b = &mapbrushes[nummapbrushes]; - b->original_sides = &brushsides[nummapbrushsides]; - b->entitynum = num_entities-1; - b->brushnum = nummapbrushes - pLoadEntity->pEntity->firstbrush; - - LoadSide_t SideInfo; - SideInfo.pBrush = b; - SideInfo.nSideIndex = 0; - SideInfo.nBaseContents = pLoadEntity->nBaseContents; - SideInfo.nBaseFlags = pLoadEntity->nBaseFlags; - - // - // Set up handlers for the subchunks that we are interested in. - // - CChunkHandlerMap Handlers; - Handlers.AddHandler("side", (ChunkHandler_t)::LoadSideCallback, &SideInfo); - - // - // Read the solid chunk. - // - pFile->PushHandlers(&Handlers); - ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSolidKeyCallback, b); - pFile->PopHandlers(); - - if (eResult == ChunkFile_Ok) - { - // get the content for the entire brush - b->contents = BrushContents (b); - - // allow detail brushes to be removed - if (nodetail && (b->contents & CONTENTS_DETAIL) && !HasDispInfo( b ) ) - { - b->numsides = 0; - return(ChunkFile_Ok); - } - - // allow water brushes to be removed - if (nowater && (b->contents & MASK_WATER) ) - { - b->numsides = 0; - return(ChunkFile_Ok); - } - - // create windings for sides and bounds for brush - MakeBrushWindings (b); - - // - // brushes that will not be visible at all will never be - // used as bsp splitters - // - // only do this on the world entity - // - if ( b->entitynum == 0 ) - { - if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) - { - if ( g_ClipTexinfo < 0 ) - { - g_ClipTexinfo = b->original_sides[0].texinfo; - } - c_clipbrushes++; - for (int i=0 ; inumsides ; i++) - { - b->original_sides[i].texinfo = TEXINFO_NODE; - } - } - } - - // - // origin brushes are removed, but they set - // the rotation origin for the rest of the brushes - // in the entity. After the entire entity is parsed, - // the planenums and texinfos will be adjusted for - // the origin brush - // - if (b->contents & CONTENTS_ORIGIN) - { - char string[32]; - Vector origin; - - if (num_entities == 1) - { - Error("Brush %i: origin brushes not allowed in world", b->id); - } - - VectorAdd (b->mins, b->maxs, origin); - VectorScale (origin, 0.5, origin); - - sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); - SetKeyValue (&entities[b->entitynum], "origin", string); - - VectorCopy (origin, entities[b->entitynum].origin); - - // don't keep this brush - b->numsides = 0; - - return(ChunkFile_Ok); - } - -#ifdef VSVMFIO - if ( CVmfImport::GetVmfImporter() ) - { - CVmfImport::GetVmfImporter()->MapBrushToMayaCallback( b ); - } -#endif // VSVMFIO - - // - // find a map brushes with displacement surfaces and remove them from the "world" - // - if( HasDispInfo( b ) ) - { - // add the base face data to the displacement surface - DispGetFaceInfo( b ); - - // don't keep this brush - b->numsides = 0; - - return( ChunkFile_Ok ); - } - - AddBrushBevels (b); - - nummapbrushes++; - pLoadEntity->pEntity->numbrushes++; - } - else - { - return eResult; - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pFile - -// parent - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush) -{ - if (!stricmp(szKey, "id")) - { - pLoadBrush->id = atoi(szValue); - g_MapError.BrushState(pLoadBrush->id); - } - - return ChunkFile_Ok; -} - - -/* -================ -TestExpandBrushes - -Expands all the brush planes and saves a new map out -================ -*/ -void CMapFile::TestExpandBrushes (void) -{ - FILE *f; - side_t *s; - int i, j, bn; - winding_t *w; - char *name = "expanded.map"; - mapbrush_t *brush; - vec_t dist; - - Msg ("writing %s\n", name); - f = fopen (name, "wb"); - if (!f) - Error ("Can't write %s\b", name); - - fprintf (f, "{\n\"classname\" \"worldspawn\"\n"); - fprintf( f, "\"mapversion\" \"220\"\n\"sounds\" \"1\"\n\"MaxRange\" \"4096\"\n\"mapversion\" \"220\"\n\"wad\" \"vert.wad;dev.wad;generic.wad;spire.wad;urb.wad;cit.wad;water.wad\"\n" ); - - - for (bn=0 ; bnnumsides ; i++) - { - s = brush->original_sides + i; - dist = mapplanes[s->planenum].dist; - for (j=0 ; j<3 ; j++) - dist += fabs( 16 * mapplanes[s->planenum].normal[j] ); - - w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist); - - fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]); - fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]); - fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]); - - fprintf (f, "%s [ 0 0 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n", - TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) ); - - FreeWinding (w); - } - fprintf (f, "}\n"); - } - fprintf (f, "}\n"); - - fclose (f); - - Error ("can't proceed after expanding brushes"); -} - - -//----------------------------------------------------------------------------- -// Purpose: load in the displacement info "chunk" from the .map file into the -// vbsp map displacement info data structure -// Output: return the pointer to the displacement map -//----------------------------------------------------------------------------- -mapdispinfo_t *ParseDispInfoChunk( void ) -{ - int i, j; - int vertCount; - mapdispinfo_t *pMapDispInfo; - - // - // check to see if we exceeded the maximum displacement info list size - // - if( nummapdispinfo > MAX_MAP_DISPINFO ) - g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO"); - - // get a pointer to the next available displacement info slot - pMapDispInfo = &mapdispinfo[nummapdispinfo]; - nummapdispinfo++; - - // - // get the chunk opener - "{" - // - GetToken( false ); - if( strcmp( token, "{" ) ) - g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - {" ); - - // - // - // get the displacement info attribs - // - // - - // power - GetToken( true ); - pMapDispInfo->power = atoi( token ); - - // u and v mapping axes - for( i = 0; i < 2; i++ ) - { - GetToken( false ); - if( strcmp( token, "[" ) ) - g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - [" ); - - for( j = 0; j < 3; j++ ) - { - GetToken( false ); - - if( i == 0 ) - { - pMapDispInfo->uAxis[j] = atof( token ); - } - else - { - pMapDispInfo->vAxis[j] = atof( token ); - } - } - - GetToken( false ); - if( strcmp( token, "]" ) ) - g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - ]" ); - } - - // max displacement value - if( g_nMapFileVersion < 350 ) - { - GetToken( false ); - pMapDispInfo->maxDispDist = atof( token ); - } - - // minimum tesselation value - GetToken( false ); - pMapDispInfo->minTess = atoi( token ); - - // light smoothing angle - GetToken( false ); - pMapDispInfo->smoothingAngle = atof( token ); - - // - // get the displacement info displacement normals - // - GetToken( true ); - pMapDispInfo->vectorDisps[0][0] = atof( token ); - GetToken( false ); - pMapDispInfo->vectorDisps[0][1] = atof( token ); - GetToken( false ); - pMapDispInfo->vectorDisps[0][2] = atof( token ); - - vertCount = ( ( ( 1 << pMapDispInfo->power ) + 1 ) * ( ( 1 << pMapDispInfo->power ) + 1 ) ); - for( i = 1; i < vertCount; i++ ) - { - GetToken( false ); - pMapDispInfo->vectorDisps[i][0] = atof( token ); - GetToken( false ); - pMapDispInfo->vectorDisps[i][1] = atof( token ); - GetToken( false ); - pMapDispInfo->vectorDisps[i][2] = atof( token ); - } - - // - // get the displacement info displacement values - // - GetToken( true ); - pMapDispInfo->dispDists[0] = atof( token ); - - for( i = 1; i < vertCount; i++ ) - { - GetToken( false ); - pMapDispInfo->dispDists[i] = atof( token ); - } - - // - // get the chunk closer - "}" - // - GetToken( true ); - if( strcmp( token, "}" ) ) - g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - }" ); - - // return the index of the displacement info slot - return pMapDispInfo; -} - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "vbsp.h" +#include "map_shared.h" +#include "disp_vbsp.h" +#include "tier1/strtools.h" +#include "builddisp.h" +#include "tier0/icommandline.h" +#include "KeyValues.h" +#include "materialsub.h" +#include "fgdlib/fgdlib.h" +#include "manifest.h" + +#ifdef VSVMFIO +#include "VmfImport.h" +#endif // VSVMFIO + + +// undefine to make plane finding use linear sort +#define USE_HASHING + +#define RENDER_NORMAL_EPSILON 0.00001 +#define RENDER_DIST_EPSILON 0.01f + +#define BRUSH_CLIP_EPSILON 0.01f // this should probably be the same + // as clip epsilon, but it is 0.1f and I + // currently don't know how that number was + // come to (cab) - this is 0.01 of an inch + // for clipping brush solids +struct LoadSide_t +{ + mapbrush_t *pBrush; + side_t *pSide; + int nSideIndex; + int nBaseFlags; + int nBaseContents; + Vector planepts[3]; + brush_texture_t td; +}; + + +extern qboolean onlyents; + + +CUtlVector< CMapFile * > g_Maps; +CMapFile *g_MainMap = NULL; +CMapFile *g_LoadingMap = NULL; + +char CMapFile::m_InstancePath[ MAX_PATH ] = ""; +int CMapFile::m_InstanceCount = 0; +int CMapFile::c_areaportals = 0; + +void CMapFile::Init( void ) +{ + entity_num = 0; + num_entities = 0; + + nummapplanes = 0; + memset( mapplanes, 0, sizeof( mapplanes ) ); + + nummapbrushes = 0; + memset( mapbrushes, 0, sizeof( mapbrushes ) ); + + nummapbrushsides = 0; + memset( brushsides, 0, sizeof( brushsides ) ); + + memset( side_brushtextures, 0, sizeof( side_brushtextures ) ); + + memset( planehash, 0, sizeof( planehash ) ); + + m_ConnectionPairs = NULL; + + m_StartMapOverlays = g_aMapOverlays.Count(); + m_StartMapWaterOverlays = g_aMapWaterOverlays.Count(); + + c_boxbevels = 0; + c_edgebevels = 0; + c_clipbrushes = 0; + g_ClipTexinfo = -1; +} + + +// All the brush sides referenced by info_no_dynamic_shadow entities. +CUtlVector g_NoDynamicShadowSides; + + +void TestExpandBrushes (void); + +ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo ); +ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); + +#ifdef VSVMFIO +ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo); +ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo); +#endif // VSVMFIO + +ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam); +ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity); + +ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity); +ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity); + +ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity); +ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush); + +ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo); +ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo); + + + +/* +============================================================================= + +PLANE FINDING + +============================================================================= +*/ + + +/* +================= +PlaneTypeForNormal +================= +*/ +int PlaneTypeForNormal (Vector& normal) +{ + vec_t ax, ay, az; + +// NOTE: should these have an epsilon around 1.0? + if (normal[0] == 1.0 || normal[0] == -1.0) + return PLANE_X; + if (normal[1] == 1.0 || normal[1] == -1.0) + return PLANE_Y; + if (normal[2] == 1.0 || normal[2] == -1.0) + return PLANE_Z; + + ax = fabs(normal[0]); + ay = fabs(normal[1]); + az = fabs(normal[2]); + + if (ax >= ay && ax >= az) + return PLANE_ANYX; + if (ay >= ax && ay >= az) + return PLANE_ANYY; + return PLANE_ANYZ; +} + +/* +================ +PlaneEqual +================ +*/ +qboolean PlaneEqual (plane_t *p, Vector& normal, vec_t dist, float normalEpsilon, float distEpsilon) +{ +#if 1 + if ( + fabs(p->normal[0] - normal[0]) < normalEpsilon + && fabs(p->normal[1] - normal[1]) < normalEpsilon + && fabs(p->normal[2] - normal[2]) < normalEpsilon + && fabs(p->dist - dist) < distEpsilon ) + return true; +#else + if (p->normal[0] == normal[0] + && p->normal[1] == normal[1] + && p->normal[2] == normal[2] + && p->dist == dist) + return true; +#endif + return false; +} + +/* +================ +AddPlaneToHash +================ +*/ +void CMapFile::AddPlaneToHash (plane_t *p) +{ + int hash; + + hash = (int)fabs(p->dist) / 8; + hash &= (PLANE_HASHES-1); + + p->hash_chain = planehash[hash]; + planehash[hash] = p; +} + +/* +================ +CreateNewFloatPlane +================ +*/ +int CMapFile::CreateNewFloatPlane (Vector& normal, vec_t dist) +{ + plane_t *p, temp; + + if (VectorLength(normal) < 0.5) + g_MapError.ReportError ("FloatPlane: bad normal"); + // create a new plane + if (nummapplanes+2 > MAX_MAP_PLANES) + g_MapError.ReportError ("MAX_MAP_PLANES"); + + p = &mapplanes[nummapplanes]; + VectorCopy (normal, p->normal); + p->dist = dist; + p->type = (p+1)->type = PlaneTypeForNormal (p->normal); + + VectorSubtract (vec3_origin, normal, (p+1)->normal); + (p+1)->dist = -dist; + + nummapplanes += 2; + + // allways put axial planes facing positive first + if (p->type < 3) + { + if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) + { + // flip order + temp = *p; + *p = *(p+1); + *(p+1) = temp; + + AddPlaneToHash (p); + AddPlaneToHash (p+1); + return nummapplanes - 1; + } + } + + AddPlaneToHash (p); + AddPlaneToHash (p+1); + return nummapplanes - 2; +} + + +/* +============== +SnapVector +============== +*/ +bool SnapVector (Vector& normal) +{ + int i; + + for (i=0 ; i<3 ; i++) + { + if ( fabs(normal[i] - 1) < RENDER_NORMAL_EPSILON ) + { + VectorClear (normal); + normal[i] = 1; + return true; + } + + if ( fabs(normal[i] - -1) < RENDER_NORMAL_EPSILON ) + { + VectorClear (normal); + normal[i] = -1; + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial. +// Rounds dist to integer if it is within an epsilon of integer. +// Input : normal - Plane normal vector (assumed to be unit length). +// dist - Plane constant. +//----------------------------------------------------------------------------- +void SnapPlane(Vector &normal, vec_t &dist) +{ + SnapVector(normal); + + if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON) + { + dist = RoundInt(dist); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial. +// Recalculates dist if the normal was snapped. Rounds dist to integer +// if it is within an epsilon of integer. +// Input : normal - Plane normal vector (assumed to be unit length). +// dist - Plane constant. +// p0, p1, p2 - Three points on the plane. +//----------------------------------------------------------------------------- +void SnapPlane(Vector &normal, vec_t &dist, const Vector &p0, const Vector &p1, const Vector &p2) +{ + if (SnapVector(normal)) + { + // + // Calculate a new plane constant using the snapped normal. Use the + // centroid of the three plane points to minimize error. This is like + // rotating the plane around the centroid. + // + Vector p3 = (p0 + p1 + p2) / 3.0f; + dist = normal.Dot(p3); + if ( g_snapAxialPlanes ) + { + dist = RoundInt(dist); + } + } + + if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON) + { + dist = RoundInt(dist); + } +} + + +/* +============= +FindFloatPlane + +============= +*/ +#ifndef USE_HASHING +int CMapFile::FindFloatPlane (Vector& normal, vec_t dist) +{ + int i; + plane_t *p; + + SnapPlane(normal, dist); + for (i=0, p=mapplanes ; ihash_chain) + { + if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON)) + return p-mapplanes; + } + } + + return CreateNewFloatPlane (normal, dist); +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: Builds a plane normal and distance from three points on the plane. +// If the normal is nearly axial, it will be snapped to be axial. Looks +// up the plane in the unique planes. +// Input : p0, p1, p2 - Three points on the plane. +// Output : Returns the index of the plane in the planes list. +//----------------------------------------------------------------------------- +int CMapFile::PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2) +{ + Vector t1, t2, normal; + vec_t dist; + + VectorSubtract (p0, p1, t1); + VectorSubtract (p2, p1, t2); + CrossProduct (t1, t2, normal); + VectorNormalize (normal); + + dist = DotProduct (p0, normal); + + SnapPlane(normal, dist, p0, p1, p2); + + return FindFloatPlane (normal, dist); +} + + +/* +=========== +BrushContents +=========== +*/ +int BrushContents (mapbrush_t *b) +{ + int contents; + int unionContents = 0; + side_t *s; + int i; + + s = &b->original_sides[0]; + contents = s->contents; + unionContents = contents; + for (i=1 ; inumsides ; i++, s++) + { + s = &b->original_sides[i]; + + unionContents |= s->contents; +#if 0 + if (s->contents != contents) + { + Msg("Brush %i: mixed face contents\n", b->id); + break; + } +#endif + } + + // NOTE: we're making slime translucent so that it doesn't block lighting on things floating on its surface + int transparentContents = unionContents & (CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_WATER|CONTENTS_SLIME); + if ( transparentContents ) + { + contents |= transparentContents | CONTENTS_TRANSLUCENT; + contents &= ~CONTENTS_SOLID; + } + + return contents; +} + + +//============================================================================ + +bool IsAreaPortal( char const *pClassName ) +{ + // If the class name starts with "func_areaportal", then it's considered an area portal. + char const *pBaseName = "func_areaportal"; + char const *pCur = pBaseName; + while( *pCur && *pClassName ) + { + if( *pCur != *pClassName ) + break; + + ++pCur; + ++pClassName; + } + + return *pCur == 0; +} + + +/* +================= +AddBrushBevels + +Adds any additional planes necessary to allow the brush to be expanded +against axial bounding boxes +================= +*/ +void CMapFile::AddBrushBevels (mapbrush_t *b) +{ + int axis, dir; + int i, j, k, l, order; + side_t sidetemp; + brush_texture_t tdtemp; + side_t *s, *s2; + Vector normal; + float dist; + winding_t *w, *w2; + Vector vec, vec2; + float d; + + // + // add the axial planes + // + order = 0; + for (axis=0 ; axis <3 ; axis++) + { + for (dir=-1 ; dir <= 1 ; dir+=2, order++) + { + // see if the plane is allready present + for (i=0, s=b->original_sides ; inumsides ; i++,s++) + { + if (mapplanes[s->planenum].normal[axis] == dir) + break; + } + + if (i == b->numsides) + { // add a new side + if (nummapbrushsides == MAX_MAP_BRUSHSIDES) + g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); + nummapbrushsides++; + b->numsides++; + VectorClear (normal); + normal[axis] = dir; + if (dir == 1) + dist = b->maxs[axis]; + else + dist = -b->mins[axis]; + s->planenum = FindFloatPlane (normal, dist); + s->texinfo = b->original_sides[0].texinfo; + s->contents = b->original_sides[0].contents; + s->bevel = true; + c_boxbevels++; + } + + // if the plane is not in it canonical order, swap it + if (i != order) + { + sidetemp = b->original_sides[order]; + b->original_sides[order] = b->original_sides[i]; + b->original_sides[i] = sidetemp; + + j = b->original_sides - brushsides; + tdtemp = side_brushtextures[j+order]; + side_brushtextures[j+order] = side_brushtextures[j+i]; + side_brushtextures[j+i] = tdtemp; + } + } + } + + // + // add the edge bevels + // + if (b->numsides == 6) + return; // pure axial + + // test the non-axial plane edges + for (i=6 ; inumsides ; i++) + { + s = b->original_sides + i; + w = s->winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; j++) + { + k = (j+1)%w->numpoints; + VectorSubtract (w->p[j], w->p[k], vec); + if (VectorNormalize (vec) < 0.5) + continue; + SnapVector (vec); + for (k=0 ; k<3 ; k++) + if ( vec[k] == -1 || vec[k] == 1) + break; // axial + if (k != 3) + continue; // only test non-axial edges + + // try the six possible slanted axials from this edge + for (axis=0 ; axis <3 ; axis++) + { + for (dir=-1 ; dir <= 1 ; dir+=2) + { + // construct a plane + VectorClear (vec2); + vec2[axis] = dir; + CrossProduct (vec, vec2, normal); + if (VectorNormalize (normal) < 0.5) + continue; + dist = DotProduct (w->p[j], normal); + + // if all the points on all the sides are + // behind this plane, it is a proper edge bevel + for (k=0 ; knumsides ; k++) + { + // if this plane has allready been used, skip it + // NOTE: Use a larger tolerance for collision planes than for rendering planes + if ( PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist, 0.01f, 0.01f ) ) + break; + + w2 = b->original_sides[k].winding; + if (!w2) + continue; + for (l=0 ; lnumpoints ; l++) + { + d = DotProduct (w2->p[l], normal) - dist; + if (d > 0.1) + break; // point in front + } + if (l != w2->numpoints) + break; + } + + if (k != b->numsides) + continue; // wasn't part of the outer hull + // add this plane + if (nummapbrushsides == MAX_MAP_BRUSHSIDES) + g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); + nummapbrushsides++; + s2 = &b->original_sides[b->numsides]; + s2->planenum = FindFloatPlane (normal, dist); + s2->texinfo = b->original_sides[0].texinfo; + s2->contents = b->original_sides[0].contents; + s2->bevel = true; + c_edgebevels++; + b->numsides++; + } + } + } + } +} + +/* +================ +MakeBrushWindings + +makes basewindigs for sides and mins / maxs for the brush +================ +*/ +qboolean CMapFile::MakeBrushWindings (mapbrush_t *ob) +{ + int i, j; + winding_t *w; + side_t *side; + plane_t *plane; + + ClearBounds (ob->mins, ob->maxs); + + for (i=0 ; inumsides ; i++) + { + plane = &mapplanes[ob->original_sides[i].planenum]; + w = BaseWindingForPlane (plane->normal, plane->dist); + for (j=0 ; jnumsides && w; j++) + { + if (i == j) + continue; + if (ob->original_sides[j].bevel) + continue; + plane = &mapplanes[ob->original_sides[j].planenum^1]; +// ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); + // adding an epsilon here, due to precision issues creating complex + // displacement surfaces (cab) + ChopWindingInPlace( &w, plane->normal, plane->dist, BRUSH_CLIP_EPSILON ); + } + + side = &ob->original_sides[i]; + side->winding = w; + if (w) + { + side->visible = true; + for (j=0 ; jnumpoints ; j++) + AddPointToBounds (w->p[j], ob->mins, ob->maxs); + } + } + + for (i=0 ; i<3 ; i++) + { + if (ob->mins[i] < MIN_COORD_INTEGER || ob->maxs[i] > MAX_COORD_INTEGER) + Msg("Brush %i: bounds out of range\n", ob->id); + if (ob->mins[i] > MAX_COORD_INTEGER || ob->maxs[i] < MIN_COORD_INTEGER) + Msg("Brush %i: no visible sides on brush\n", ob->id); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Takes all of the brushes from the current entity and adds them to the +// world's brush list. Used by func_detail and func_areaportal. +// THIS ROUTINE MAY ONLY BE USED DURING ENTITY LOADING. +// Input : mapent - Entity whose brushes are to be moved to the world. +//----------------------------------------------------------------------------- +void CMapFile::MoveBrushesToWorld( entity_t *mapent ) +{ + int newbrushes; + int worldbrushes; + mapbrush_t *temp; + int i; + + // this is pretty gross, because the brushes are expected to be + // in linear order for each entity + + newbrushes = mapent->numbrushes; + worldbrushes = entities[0].numbrushes; + + temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t)); + memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t)); + +#if 0 // let them keep their original brush numbers + for (i=0 ; inumbrushes = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Takes all of the brushes from the current entity and adds them to the +// world's brush list. Used by func_detail and func_areaportal. +// Input : mapent - Entity whose brushes are to be moved to the world. +//----------------------------------------------------------------------------- +void CMapFile::MoveBrushesToWorldGeneral( entity_t *mapent ) +{ + int newbrushes; + int worldbrushes; + mapbrush_t *temp; + int i; + + for( i = 0; i < nummapdispinfo; i++ ) + { + if ( mapdispinfo[ i ].entitynum == ( mapent - entities ) ) + { + mapdispinfo[ i ].entitynum = 0; + } + } + + // this is pretty gross, because the brushes are expected to be + // in linear order for each entity + newbrushes = mapent->numbrushes; + worldbrushes = entities[0].numbrushes; + + temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t)); + memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t)); + +#if 0 // let them keep their original brush numbers + for (i=0 ; ifirstbrush - worldbrushes) ); + + + // wwwxxxmmyyy + + // copy the new brushes down + memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes); + + // fix up indexes + entities[0].numbrushes += newbrushes; + for (i=1 ; ifirstbrush ) // if we use <=, then we'll remap the passed in ent, which we don't want to + { + entities[ i ].firstbrush += newbrushes; + } + } + free (temp); + + mapent->numbrushes = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Iterates the sides of brush and removed CONTENTS_DETAIL from each side +// Input : *brush - +//----------------------------------------------------------------------------- +void RemoveContentsDetailFromBrush( mapbrush_t *brush ) +{ + // Only valid on non-world brushes + Assert( brush->entitynum != 0 ); + + side_t *s; + int i; + + s = &brush->original_sides[0]; + for ( i=0 ; inumsides ; i++, s++ ) + { + if ( s->contents & CONTENTS_DETAIL ) + { + s->contents &= ~CONTENTS_DETAIL; + } + } + +} + +//----------------------------------------------------------------------------- +// Purpose: Iterates all brushes in an entity and removes CONTENTS_DETAIL from all brushes +// Input : *mapent - +//----------------------------------------------------------------------------- +void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent ) +{ + int i; + for ( i = 0; i < mapent->numbrushes; i++ ) + { + int brushnum = mapent->firstbrush + i; + + mapbrush_t *brush = &mapbrushes[ brushnum ]; + RemoveContentsDetailFromBrush( brush ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pMapDispInfo)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : szKey - +// szValue - +// pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) +{ + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pMapDispInfo->power) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext = strtok(szBuf, " "); + int nIndex = nRow * nCols; + + while (pszNext != NULL) + { + pMapDispInfo->dispDists[nIndex] = (float)atof(pszNext); + pszNext = strtok(NULL, " "); + nIndex++; + } + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: load in the displacement info "chunk" from the .map file into the +// vbsp map displacement info data structure +// Output : return the index of the map displacement info +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo ) +{ + // + // check to see if we exceeded the maximum displacement info list size + // + if (nummapdispinfo > MAX_MAP_DISPINFO) + { + g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO" ); + } + + // get a pointer to the next available displacement info slot + mapdispinfo_t *pMapDispInfo = &mapdispinfo[nummapdispinfo]; + nummapdispinfo++; + + // + // Set up handlers for the subchunks that we are interested in. + // + CChunkHandlerMap Handlers; + Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, pMapDispInfo); + Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, pMapDispInfo); + Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, pMapDispInfo); + Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, pMapDispInfo); + Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, pMapDispInfo); + +#ifdef VSVMFIO + Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, pMapDispInfo); +#endif // VSVMFIO + + // + // Read the displacement chunk. + // + pFile->PushHandlers(&Handlers); + ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispInfoKeyCallback, pMapDispInfo); + pFile->PopHandlers(); + + if (eResult == ChunkFile_Ok) + { + // return a pointer to the displacement info + *ppMapDispInfo = pMapDispInfo; + } + + return(eResult); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *mapent - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) +{ + if (!stricmp(szKey, "power")) + { + CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->power); + } +#ifdef VSVMFIO + else if (!stricmp(szKey, "elevation")) + { + CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->m_elevation); + } +#endif // VSVMFIO + else if (!stricmp(szKey, "uaxis")) + { + CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->uAxis); + } + else if (!stricmp(szKey, "vaxis")) + { + CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->vAxis); + } + else if( !stricmp( szKey, "startposition" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pMapDispInfo->startPosition ); + } + else if( !stricmp( szKey, "flags" ) ) + { + CChunkFile::ReadKeyValueInt( szValue, pMapDispInfo->flags ); + } +#if 0 // old data + else if (!stricmp( szKey, "alpha" ) ) + { + CChunkFile::ReadKeyValueVector4( szValue, pMapDispInfo->alphaValues ); + } +#endif + else if (!stricmp(szKey, "mintess")) + { + CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->minTess); + } + else if (!stricmp(szKey, "smooth")) + { + CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->smoothingAngle); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pMapDispInfo)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) +{ + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pMapDispInfo->power) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext0 = strtok(szBuf, " "); + char *pszNext1 = strtok(NULL, " "); + char *pszNext2 = strtok(NULL, " "); + + int nIndex = nRow * nCols; + + while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL)) + { + pMapDispInfo->vectorDisps[nIndex][0] = (float)atof(pszNext0); + pMapDispInfo->vectorDisps[nIndex][1] = (float)atof(pszNext1); + pMapDispInfo->vectorDisps[nIndex][2] = (float)atof(pszNext2); + + pszNext0 = strtok(NULL, " "); + pszNext1 = strtok(NULL, " "); + pszNext2 = strtok(NULL, " "); + + nIndex++; + } + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pMapDispInfo)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) +{ + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pMapDispInfo->power) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext0 = strtok(szBuf, " "); + char *pszNext1 = strtok(NULL, " "); + char *pszNext2 = strtok(NULL, " "); + + int nIndex = nRow * nCols; + + while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL)) + { + pMapDispInfo->vectorOffsets[nIndex][0] = (float)atof(pszNext0); + pMapDispInfo->vectorOffsets[nIndex][1] = (float)atof(pszNext1); + pMapDispInfo->vectorOffsets[nIndex][2] = (float)atof(pszNext2); + + pszNext0 = strtok(NULL, " "); + pszNext1 = strtok(NULL, " "); + pszNext2 = strtok(NULL, " "); + + nIndex++; + } + } + + return(ChunkFile_Ok); +} + + +#ifdef VSVMFIO +ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pMapDispInfo)); +} + + +ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) +{ + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pMapDispInfo->power) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext0 = strtok(szBuf, " "); + char *pszNext1 = strtok(NULL, " "); + char *pszNext2 = strtok(NULL, " "); + + int nIndex = nRow * nCols; + + while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL)) + { + pMapDispInfo->m_offsetNormals[nIndex][0] = (float)atof(pszNext0); + pMapDispInfo->m_offsetNormals[nIndex][1] = (float)atof(pszNext1); + pMapDispInfo->m_offsetNormals[nIndex][2] = (float)atof(pszNext2); + + pszNext0 = strtok(NULL, " "); + pszNext1 = strtok(NULL, " "); + pszNext2 = strtok(NULL, " "); + + nIndex++; + } + } + + return(ChunkFile_Ok); +} +#endif // VSVMFIO + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pMapDispInfo)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szKey - +// *szValue - +// *pDisp - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) +{ + if (!strnicmp(szKey, "row", 3)) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy(szBuf, szValue); + + int nCols = (1 << pMapDispInfo->power) + 1; + int nRow = atoi(&szKey[3]); + + char *pszNext0 = strtok(szBuf, " "); + + int nIndex = nRow * nCols; + + while (pszNext0 != NULL) + { + pMapDispInfo->alphaValues[nIndex] = (float)atof(pszNext0); + pszNext0 = strtok(NULL, " "); + nIndex++; + } + } + + return(ChunkFile_Ok); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pMapDispInfo)); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo) +{ + if ( !strnicmp( szKey, "row", 3 ) ) + { + char szBuf[MAX_KEYVALUE_LEN]; + strcpy( szBuf, szValue ); + + int nCols = ( 1 << pMapDispInfo->power ); + int nRow = atoi( &szKey[3] ); + + char *pszNext = strtok( szBuf, " " ); + + int nIndex = nRow * nCols; + int iTri = nIndex * 2; + + while ( pszNext != NULL ) + { + // Collapse the tags here! + unsigned short nTriTags = ( unsigned short )atoi( pszNext ); + + // Walkable + bool bWalkable = ( ( nTriTags & COREDISPTRI_TAG_WALKABLE ) != 0 ); + if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) != 0 ) ) + { + bWalkable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_VAL ) != 0 ); + } + + // Buildable + bool bBuildable = ( ( nTriTags & COREDISPTRI_TAG_BUILDABLE ) != 0 ); + if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) != 0 ) ) + { + bBuildable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ) != 0 ); + } + + nTriTags = 0; + if ( bWalkable ) + { + nTriTags |= DISPTRI_TAG_WALKABLE; + } + + if ( bBuildable ) + { + nTriTags |= DISPTRI_TAG_BUILDABLE; + } + + pMapDispInfo->triTags[iTri] = nTriTags; + pszNext = strtok( NULL, " " ); + iTri++; + } + } + + return( ChunkFile_Ok ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : brushSideID - +// Output : int +//----------------------------------------------------------------------------- +int CMapFile::SideIDToIndex( int brushSideID ) +{ + int i; + for ( i = 0; i < nummapbrushsides; i++ ) + { + if ( brushsides[i].id == brushSideID ) + { + return i; + } + } + Assert( 0 ); + return -1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *mapent - +// *key - +//----------------------------------------------------------------------------- +void ConvertSideList( entity_t *mapent, char *key ) +{ + char *pszSideList = ValueForKey( mapent, key ); + + if (pszSideList) + { + char *pszTmpList = ( char* )_alloca( strlen( pszSideList ) + 1 ); + strcpy( pszTmpList, pszSideList ); + + bool bFirst = true; + char szNewValue[1024]; + szNewValue[0] = '\0'; + + const char *pszScan = strtok( pszTmpList, " " ); + if ( !pszScan ) + return; + do + { + int nSideID; + + if ( sscanf( pszScan, "%d", &nSideID ) == 1 ) + { + int nIndex = g_LoadingMap->SideIDToIndex(nSideID); + if (nIndex != -1) + { + if (!bFirst) + { + strcat( szNewValue, " " ); + } + else + { + bFirst = false; + } + + char szIndex[15]; + itoa( nIndex, szIndex, 10 ); + strcat( szNewValue, szIndex ); + } + } + } while ( ( pszScan = strtok( NULL, " " ) ) ); + + SetKeyValue( mapent, key, szNewValue ); + } +} + + +// Add all the sides referenced by info_no_dynamic_shadows entities to g_NoDynamicShadowSides. +ChunkFileResult_t HandleNoDynamicShadowsEnt( entity_t *pMapEnt ) +{ + // Get the list of the sides. + char *pSideList = ValueForKey( pMapEnt, "sides" ); + + // Parse the side list. + char *pScan = strtok( pSideList, " " ); + if( pScan ) + { + do + { + int brushSideID; + if( sscanf( pScan, "%d", &brushSideID ) == 1 ) + { + if ( g_NoDynamicShadowSides.Find( brushSideID ) == -1 ) + g_NoDynamicShadowSides.AddToTail( brushSideID ); + } + } while( ( pScan = strtok( NULL, " " ) ) ); + } + + // Clear out this entity. + pMapEnt->epairs = NULL; + return ( ChunkFile_Ok ); +} + + +static ChunkFileResult_t LoadOverlayDataTransitionKeyCallback( const char *szKey, const char *szValue, mapoverlay_t *pOverlay ) +{ + if ( !stricmp( szKey, "material" ) ) + { + // Get the material name. + const char *pMaterialName = szValue; + if( g_ReplaceMaterials ) + { + pMaterialName = ReplaceMaterialName( szValue ); + } + + Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN ); + if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN ) + { + Error( "Overlay Material Name (%s) > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN ); + return ChunkFile_Fail; + } + strcpy( pOverlay->szMaterialName, pMaterialName ); + } + else if ( !stricmp( szKey, "StartU") ) + { + CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[0] ); + } + else if ( !stricmp( szKey, "EndU" ) ) + { + CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[1] ); + } + else if ( !stricmp( szKey, "StartV" ) ) + { + CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[0] ); + } + else if ( !stricmp( szKey, "EndV" ) ) + { + CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[1] ); + } + else if ( !stricmp( szKey, "BasisOrigin" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecOrigin ); + } + else if ( !stricmp( szKey, "BasisU" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[0] ); + } + else if ( !stricmp( szKey, "BasisV" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[1] ); + } + else if ( !stricmp( szKey, "BasisNormal" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[2] ); + } + else if ( !stricmp( szKey, "uv0" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[0] ); + } + else if ( !stricmp( szKey, "uv1" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[1] ); + } + else if ( !stricmp( szKey, "uv2" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[2] ); + } + else if ( !stricmp( szKey, "uv3" ) ) + { + CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[3] ); + } + else if ( !stricmp( szKey, "sides" ) ) + { + const char *pSideList = szValue; + char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 ); + strcpy( pTmpList, pSideList ); + const char *pScan = strtok( pTmpList, " " ); + if ( !pScan ) + return ChunkFile_Fail; + + pOverlay->aSideList.Purge(); + pOverlay->aFaceList.Purge(); + + do + { + int nSideId; + if ( sscanf( pScan, "%d", &nSideId ) == 1 ) + { + pOverlay->aSideList.AddToTail( nSideId ); + } + } while ( ( pScan = strtok( NULL, " " ) ) ); + } + + return ChunkFile_Ok; +} + +static ChunkFileResult_t LoadOverlayDataTransitionCallback( CChunkFile *pFile, int nParam ) +{ + int iOverlay = g_aMapWaterOverlays.AddToTail(); + mapoverlay_t *pOverlay = &g_aMapWaterOverlays[iOverlay]; + if ( !pOverlay ) + return ChunkFile_Fail; + + pOverlay->nId = ( MAX_MAP_OVERLAYS + 1 ) + g_aMapWaterOverlays.Count() - 1; + pOverlay->m_nRenderOrder = 0; + + ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadOverlayDataTransitionKeyCallback, pOverlay ); + return eResult; +} + +static ChunkFileResult_t LoadOverlayTransitionCallback( CChunkFile *pFile, int nParam ) +{ + CChunkHandlerMap Handlers; + Handlers.AddHandler( "overlaydata", ( ChunkHandler_t )LoadOverlayDataTransitionCallback, 0 ); + pFile->PushHandlers( &Handlers ); + + ChunkFileResult_t eResult = pFile->ReadChunk( NULL, NULL ); + + pFile->PopHandlers(); + + return eResult; +} + +//----------------------------------------------------------------------------- +// Purpose: Iterates all brushes in a ladder entity, generates its mins and maxs. +// These are stored in the object, since the brushes are going to go away. +// Input : *mapent - +//----------------------------------------------------------------------------- +void CMapFile::AddLadderKeys( entity_t *mapent ) +{ + Vector mins, maxs; + ClearBounds( mins, maxs ); + + int i; + for ( i = 0; i < mapent->numbrushes; i++ ) + { + int brushnum = mapent->firstbrush + i; + mapbrush_t *brush = &mapbrushes[ brushnum ]; + + AddPointToBounds( brush->mins, mins, maxs ); + AddPointToBounds( brush->maxs, mins, maxs ); + } + + char buf[16]; + + Q_snprintf( buf, sizeof(buf), "%2.2f", mins.x ); + SetKeyValue( mapent, "mins.x", buf ); + + Q_snprintf( buf, sizeof(buf), "%2.2f", mins.y ); + SetKeyValue( mapent, "mins.y", buf ); + + Q_snprintf( buf, sizeof(buf), "%2.2f", mins.z ); + SetKeyValue( mapent, "mins.z", buf ); + + Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.x ); + SetKeyValue( mapent, "maxs.x", buf ); + + Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.y ); + SetKeyValue( mapent, "maxs.y", buf ); + + Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.z ); + SetKeyValue( mapent, "maxs.z", buf ); +} + +ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam) +{ + return g_LoadingMap->LoadEntityCallback( pFile, nParam ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFile - +// ulParam - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) +{ + if (num_entities == MAX_MAP_ENTITIES) + { + // Exits. + g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES"); + } + + entity_t *mapent = &entities[num_entities]; + num_entities++; + memset(mapent, 0, sizeof(*mapent)); + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + //mapent->portalareas[0] = -1; + //mapent->portalareas[1] = -1; + + LoadEntity_t LoadEntity; + LoadEntity.pEntity = mapent; + + // No default flags/contents + LoadEntity.nBaseFlags = 0; + LoadEntity.nBaseContents = 0; + + // + // Set up handlers for the subchunks that we are interested in. + // + CChunkHandlerMap Handlers; + Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity); + Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, &LoadEntity); + Handlers.AddHandler( "overlaytransition", ( ChunkHandler_t )LoadOverlayTransitionCallback, 0 ); + + // + // Read the entity chunk. + // + pFile->PushHandlers(&Handlers); + ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity); + pFile->PopHandlers(); + + if (eResult == ChunkFile_Ok) + { + GetVectorForKey (mapent, "origin", mapent->origin); + + const char *pMinDXLevelStr = ValueForKey( mapent, "mindxlevel" ); + const char *pMaxDXLevelStr = ValueForKey( mapent, "maxdxlevel" ); + if( *pMinDXLevelStr != '\0' || *pMaxDXLevelStr != '\0' ) + { + int min = 0; + int max = 0; + if( *pMinDXLevelStr ) + { + min = atoi( pMinDXLevelStr ); + } + if( *pMaxDXLevelStr ) + { + max = atoi( pMaxDXLevelStr ); + } + + // Set min and max to default values. + if( min == 0 ) + { + min = g_nDXLevel; + } + if( max == 0 ) + { + max = g_nDXLevel; + } + if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < min || g_nDXLevel > max ) ) + { + mapent->numbrushes = 0; + mapent->epairs = NULL; + return(ChunkFile_Ok); + } + } + + // offset all of the planes and texinfo + if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) + { + for (int i=0 ; inumbrushes ; i++) + { + mapbrush_t *b = &mapbrushes[mapent->firstbrush + i]; + for (int j=0 ; jnumsides ; j++) + { + side_t *s = &b->original_sides[j]; + vec_t newdist = mapplanes[s->planenum].dist - DotProduct (mapplanes[s->planenum].normal, mapent->origin); + s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist); + if ( !onlyents ) + { + s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], &side_brushtextures[s-brushsides], mapent->origin); + } + } + MakeBrushWindings (b); + } + } + + // + // func_detail brushes are moved into the world entity. The CONTENTS_DETAIL flag was set by the loader. + // + const char *pClassName = ValueForKey( mapent, "classname" ); + + if ( !strcmp( "func_detail", pClassName ) ) + { + MoveBrushesToWorld (mapent); + mapent->numbrushes = 0; + + // clear out this entity + mapent->epairs = NULL; + return(ChunkFile_Ok); + } + + // these get added to a list for processing the portal file + // but aren't necessary to emit to the BSP + if ( !strcmp( "func_viscluster", pClassName ) ) + { + AddVisCluster(mapent); + return(ChunkFile_Ok); + } + + // + // func_ladder brushes are moved into the world entity. We convert the func_ladder to an info_ladder + // that holds the ladder's mins and maxs, and leave the entity. This helps the bots figure out ladders. + // + if ( !strcmp( "func_ladder", pClassName ) ) + { + AddLadderKeys( mapent ); + + MoveBrushesToWorld (mapent); + + // Convert to info_ladder entity + SetKeyValue( mapent, "classname", "info_ladder" ); + + return(ChunkFile_Ok); + } + + if( !strcmp( "env_cubemap", pClassName ) ) + { + if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) ) + { + const char *pSideListStr = ValueForKey( mapent, "sides" ); + int size; + size = IntForKey( mapent, "cubemapsize" ); + Cubemap_InsertSample( mapent->origin, size ); + Cubemap_SaveBrushSides( pSideListStr ); + } + // clear out this entity + mapent->epairs = NULL; + return(ChunkFile_Ok); + } + + if ( !strcmp( "test_sidelist", pClassName ) ) + { + ConvertSideList(mapent, "sides"); + return ChunkFile_Ok; + } + + if ( !strcmp( "info_overlay", pClassName ) ) + { + int iAccessorID = Overlay_GetFromEntity( mapent ); + + if ( iAccessorID < 0 ) + { + // Clear out this entity. + mapent->epairs = NULL; + } + else + { + // Convert to info_overlay_accessor entity + SetKeyValue( mapent, "classname", "info_overlay_accessor" ); + + // Remember the id for accessing the overlay + char buf[16]; + Q_snprintf( buf, sizeof(buf), "%i", iAccessorID ); + SetKeyValue( mapent, "OverlayID", buf ); + } + + return ( ChunkFile_Ok ); + } + + if ( !strcmp( "info_overlay_transition", pClassName ) ) + { + // Clear out this entity. + mapent->epairs = NULL; + return ( ChunkFile_Ok ); + } + + if ( Q_stricmp( pClassName, "info_no_dynamic_shadow" ) == 0 ) + { + return HandleNoDynamicShadowsEnt( mapent ); + } + + if ( Q_stricmp( pClassName, "func_instance_parms" ) == 0 ) + { + // Clear out this entity. + mapent->epairs = NULL; + return ( ChunkFile_Ok ); + } + + // areaportal entities move their brushes, but don't eliminate + // the entity + if( IsAreaPortal( pClassName ) ) + { + char str[128]; + + if (mapent->numbrushes != 1) + { + Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1); + } + + mapbrush_t *b = &mapbrushes[nummapbrushes-1]; + b->contents = CONTENTS_AREAPORTAL; + c_areaportals++; + mapent->areaportalnum = c_areaportals; + + // set the portal number as "portalnumber" + sprintf (str, "%i", c_areaportals); + SetKeyValue (mapent, "portalnumber", str); + + MoveBrushesToWorld (mapent); + return(ChunkFile_Ok); + } + +#ifdef VSVMFIO + if ( !Q_stricmp( pClassName, "light" ) ) + { + CVmfImport::GetVmfImporter()->ImportLightCallback( + ValueForKey( mapent, "hammerid" ), + ValueForKey( mapent, "origin" ), + ValueForKey( mapent, "_light" ), + ValueForKey( mapent, "_lightHDR" ), + ValueForKey( mapent, "_lightscaleHDR" ), + ValueForKey( mapent, "_quadratic_attn" ) ); + } + + if ( !Q_stricmp( pClassName, "light_spot" ) ) + { + CVmfImport::GetVmfImporter()->ImportLightSpotCallback( + ValueForKey( mapent, "hammerid" ), + ValueForKey( mapent, "origin" ), + ValueForKey( mapent, "angles" ), + ValueForKey( mapent, "pitch" ), + ValueForKey( mapent, "_light" ), + ValueForKey( mapent, "_lightHDR" ), + ValueForKey( mapent, "_lightscaleHDR" ), + ValueForKey( mapent, "_quadratic_attn" ), + ValueForKey( mapent, "_inner_cone" ), + ValueForKey( mapent, "_cone" ), + ValueForKey( mapent, "_exponent" ) ); + } + + if ( !Q_stricmp( pClassName, "light_dynamic" ) ) + { + CVmfImport::GetVmfImporter()->ImportLightDynamicCallback( + ValueForKey( mapent, "hammerid" ), + ValueForKey( mapent, "origin" ), + ValueForKey( mapent, "angles" ), + ValueForKey( mapent, "pitch" ), + ValueForKey( mapent, "_light" ), + ValueForKey( mapent, "_quadratic_attn" ), + ValueForKey( mapent, "_inner_cone" ), + ValueForKey( mapent, "_cone" ), + ValueForKey( mapent, "brightness" ), + ValueForKey( mapent, "distance" ), + ValueForKey( mapent, "spotlight_radius" ) ); + } + + if ( !Q_stricmp( pClassName, "light_environment" ) ) + { + CVmfImport::GetVmfImporter()->ImportLightEnvironmentCallback( + ValueForKey( mapent, "hammerid" ), + ValueForKey( mapent, "origin" ), + ValueForKey( mapent, "angles" ), + ValueForKey( mapent, "pitch" ), + ValueForKey( mapent, "_light" ), + ValueForKey( mapent, "_lightHDR" ), + ValueForKey( mapent, "_lightscaleHDR" ), + ValueForKey( mapent, "_ambient" ), + ValueForKey( mapent, "_ambientHDR" ), + ValueForKey( mapent, "_AmbientScaleHDR" ), + ValueForKey( mapent, "SunSpreadAngle" ) ); + } + + const char *pModel = ValueForKey( mapent, "model" ); + if ( pModel && Q_strlen( pModel ) ) + { + CVmfImport::GetVmfImporter()->ImportModelCallback( + pModel, + ValueForKey( mapent, "hammerid" ), + ValueForKey( mapent, "angles" ), + ValueForKey( mapent, "origin" ), + MDagPath() ); + } +#endif // VSVMFIO + + // If it's not in the world at this point, unmark CONTENTS_DETAIL from all sides... + if ( mapent != &entities[ 0 ] ) + { + RemoveContentsDetailFromEntity( mapent ); + } + + return(ChunkFile_Ok); + } + + return(eResult); +} + + +entity_t* EntityByName( char const *pTestName ) +{ + if( !pTestName ) + return 0; + + for( int i=0; i < g_MainMap->num_entities; i++ ) + { + entity_t *e = &g_MainMap->entities[i]; + + const char *pName = ValueForKey( e, "targetname" ); + if( stricmp( pName, pTestName ) == 0 ) + return e; + } + + return 0; +} + + +void CMapFile::ForceFuncAreaPortalWindowContents() +{ + // Now go through all areaportal entities and force CONTENTS_WINDOW + // on the brushes of the bmodels they point at. + char *targets[] = {"target", "BackgroundBModel"}; + int nTargets = sizeof(targets) / sizeof(targets[0]); + + for( int i=0; i < num_entities; i++ ) + { + entity_t *e = &entities[i]; + + const char *pClassName = ValueForKey( e, "classname" ); + + // Don't do this on "normal" func_areaportal entities. Those are tied to doors + // and should be opaque when closed. But areaportal windows (and any other + // distance-based areaportals) should be windows because they are normally open/transparent + if( !IsAreaPortal( pClassName ) || !Q_stricmp( pClassName, "func_areaportal" ) ) + continue; + +// const char *pTestEntName = ValueForKey( e, "targetname" ); + + for( int iTarget=0; iTarget < nTargets; iTarget++ ) + { + char const *pEntName = ValueForKey( e, targets[iTarget] ); + if( !pEntName[0] ) + continue; + + entity_t *pBrushEnt = EntityByName( pEntName ); + if( !pBrushEnt ) + continue; + + for( int iBrush=0; iBrush < pBrushEnt->numbrushes; iBrush++ ) + { + mapbrushes[pBrushEnt->firstbrush + iBrush].contents &= ~CONTENTS_SOLID; + mapbrushes[pBrushEnt->firstbrush + iBrush].contents |= CONTENTS_TRANSLUCENT | CONTENTS_WINDOW; + } + } + } +} + + +// ============ Instancing ============ + +// #define MERGE_INSTANCE_DEBUG_INFO 1 + +#define INSTANCE_VARIABLE_KEY "replace" + +static GameData GD; + +//----------------------------------------------------------------------------- +// Purpose: this function will read in a standard key / value file +// Input : pFilename - the absolute name of the file to read +// Output : returns the KeyValues of the file, NULL if the file could not be read. +//----------------------------------------------------------------------------- +static KeyValues *ReadKeyValuesFile( const char *pFilename ) +{ + // Read in the gameinfo.txt file and null-terminate it. + FILE *fp = fopen( pFilename, "rb" ); + if ( !fp ) + return NULL; + CUtlVector buf; + fseek( fp, 0, SEEK_END ); + buf.SetSize( ftell( fp ) + 1 ); + fseek( fp, 0, SEEK_SET ); + fread( buf.Base(), 1, buf.Count()-1, fp ); + fclose( fp ); + buf[buf.Count()-1] = 0; + + KeyValues *kv = new KeyValues( "" ); + if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) ) + { + kv->deleteThis(); + return NULL; + } + + return kv; +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will set a secondary lookup path for instances. +// Input : pszInstancePath - the secondary lookup path +//----------------------------------------------------------------------------- +void CMapFile::SetInstancePath( const char *pszInstancePath ) +{ + strcpy( m_InstancePath, pszInstancePath ); + V_strlower( m_InstancePath ); + V_FixSlashes( m_InstancePath ); +} + + +//----------------------------------------------------------------------------- +// Purpose: This function will attempt to find a full path given the base and relative names. +// Input : pszBaseFileName - the base file that referenced this instance +// pszInstanceFileName - the relative file name of this instance +// Output : Returns true if it was able to locate the file +// pszOutFileName - the full path to the file name if located +//----------------------------------------------------------------------------- +bool CMapFile::DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName ) +{ + char szInstanceFileNameFixed[ MAX_PATH ]; + const char *pszMapPath = "\\maps\\"; + + strcpy( szInstanceFileNameFixed, pszInstanceFileName ); + V_SetExtension( szInstanceFileNameFixed, ".vmf", sizeof( szInstanceFileNameFixed ) ); + V_FixSlashes( szInstanceFileNameFixed ); + + // first, try to find a relative location based upon the Base file name + strcpy( pszOutFileName, pszBaseFileName ); + V_StripFilename( pszOutFileName ); + + strcat( pszOutFileName, "\\" ); + strcat( pszOutFileName, szInstanceFileNameFixed ); + + if ( g_pFullFileSystem->FileExists( pszOutFileName ) ) + { + return true; + } + + // second, try to find the master 'maps' directory and make it relative from that + strcpy( pszOutFileName, pszBaseFileName ); + V_StripFilename( pszOutFileName ); + V_RemoveDotSlashes( pszOutFileName ); + V_FixDoubleSlashes( pszOutFileName ); + V_strlower( pszOutFileName ); + strcat( pszOutFileName, "\\" ); + + char *pos = strstr( pszOutFileName, pszMapPath ); + if ( pos ) + { + pos += strlen( pszMapPath ); + *pos = 0; + strcat( pszOutFileName, szInstanceFileNameFixed ); + + if ( g_pFullFileSystem->FileExists( pszOutFileName ) ) + { + return true; + } + } + + if ( m_InstancePath[ 0 ] != 0 ) + { + sprintf( szInstanceFileNameFixed, "%s%s", m_InstancePath, pszInstanceFileName ); + + if ( g_pFullFileSystem->FileExists( szInstanceFileNameFixed, "GAME" ) ) + { + char FullPath[ MAX_PATH ]; + g_pFullFileSystem->RelativePathToFullPath( szInstanceFileNameFixed, "GAME", FullPath, sizeof( FullPath ) ); + strcpy( pszOutFileName, FullPath ); + + return true; + } + } + + pszOutFileName[ 0 ] = 0; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will check the main map for any func_instances. It will +// also attempt to load in the gamedata file for instancing remapping help. +// Input : none +// Output : none +//----------------------------------------------------------------------------- +void CMapFile::CheckForInstances( const char *pszFileName ) +{ + if ( this != g_MainMap ) + { // all sub-instances will be appended to the main map master list as they are read in + // so the main loop below will naturally get to the appended ones. + return; + } + + char GameInfoPath[ MAX_PATH ]; + + g_pFullFileSystem->RelativePathToFullPath( "gameinfo.txt", "MOD", GameInfoPath, sizeof( GameInfoPath ) ); + KeyValues *GameInfoKV = ReadKeyValuesFile( GameInfoPath ); + if ( !GameInfoKV ) + { + Msg( "Could not locate gameinfo.txt for Instance Remapping at %s\n", GameInfoPath ); + return; + } + + const char *InstancePath = GameInfoKV->GetString( "InstancePath", NULL ); + if ( InstancePath ) + { + CMapFile::SetInstancePath( InstancePath ); + } + + const char *GameDataFile = GameInfoKV->GetString( "GameData", NULL ); + if ( !GameDataFile ) + { + Msg( "Could not locate 'GameData' key in %s\n", GameInfoPath ); + return; + } + + char FDGPath[ MAX_PATH ]; + if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) ) + { + if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "", FDGPath, sizeof( FDGPath ) ) ) + { + Msg( "Could not locate GameData file %s\n", GameDataFile ); + } + } + + GD.Load( FDGPath ); + + // this list will grow as instances are merged onto it. sub-instances are merged and + // automatically done in this processing. + for ( int i = 0; i < num_entities; i++ ) + { + char *pEntity = ValueForKey( &entities[ i ], "classname" ); + if ( !strcmp( pEntity, "func_instance" ) ) + { + char *pInstanceFile = ValueForKey( &entities[ i ], "file" ); + if ( pInstanceFile[ 0 ] ) + { + char InstancePath[ MAX_PATH ]; + bool bLoaded = false; + + if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) ) + { + if ( LoadMapFile( InstancePath ) ) + { + MergeInstance( &entities[ i ], g_LoadingMap ); + delete g_LoadingMap; + bLoaded = true; + } + } + + if ( bLoaded == false ) + { + Color red( 255, 0, 0, 255 ); + + ColorSpewMessage( SPEW_ERROR, &red, "Could not open instance file %s\n", pInstanceFile ); + } + } + + entities[ i ].numbrushes = 0; + entities[ i ].epairs = NULL; + } + } + + g_LoadingMap = this; +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will do all of the necessary work to merge the instance +// into the main map. +// Input : pInstanceEntity - the entity of the func_instance +// Instance - the map file of the instance +// Output : none +//----------------------------------------------------------------------------- +void CMapFile::MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance ) +{ + matrix3x4_t mat; + QAngle angles; + Vector OriginOffset = pInstanceEntity->origin; + + m_InstanceCount++; + + GetAnglesForKey( pInstanceEntity, "angles", angles ); + AngleMatrix( angles, OriginOffset, mat ); + +#ifdef MERGE_INSTANCE_DEBUG_INFO + Msg( "Instance Remapping: O:( %g, %g, %g ) A:( %g, %g, %g )\n", OriginOffset.x, OriginOffset.y, OriginOffset.z, angles.x, angles.y, angles.z ); +#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO + MergePlanes( pInstanceEntity, Instance, OriginOffset, angles, mat ); + MergeBrushes( pInstanceEntity, Instance, OriginOffset, angles, mat ); + MergeBrushSides( pInstanceEntity, Instance, OriginOffset, angles, mat ); + MergeEntities( pInstanceEntity, Instance, OriginOffset, angles, mat ); + MergeOverlays( pInstanceEntity, Instance, OriginOffset, angles, mat ); +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will merge in the map planes from the instance into +// the main map. +// Input : pInstanceEntity - the entity of the func_instance +// Instance - the map file of the instance +// InstanceOrigin - the translation of the instance +// InstanceAngle - the rotation of the instance +// InstanceMatrix - the translation / rotation matrix of the instance +// Output : none +//----------------------------------------------------------------------------- +void CMapFile::MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) +{ + // Each pair of planes needs to be added to the main map + for ( int i = 0; i < Instance->nummapplanes; i += 2 ) + { + FindFloatPlane( Instance->mapplanes[i].normal, Instance->mapplanes[i].dist ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will merge in the map brushes from the instance into +// the main map. +// Input : pInstanceEntity - the entity of the func_instance +// Instance - the map file of the instance +// InstanceOrigin - the translation of the instance +// InstanceAngle - the rotation of the instance +// InstanceMatrix - the translation / rotation matrix of the instance +// Output : none +//----------------------------------------------------------------------------- +void CMapFile::MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) +{ + int max_brush_id = 0; + + for( int i = 0; i < nummapbrushes; i++ ) + { + if ( mapbrushes[ i ].id > max_brush_id ) + { + max_brush_id = mapbrushes[ i ].id; + } + } + + for( int i = 0; i < Instance->nummapbrushes; i++ ) + { + mapbrushes[ nummapbrushes + i ] = Instance->mapbrushes[ i ]; + + mapbrush_t *brush = &mapbrushes[ nummapbrushes + i ]; + brush->entitynum += num_entities; + brush->brushnum += nummapbrushes; + + if ( i < Instance->entities[ 0 ].numbrushes || ( brush->contents & CONTENTS_LADDER ) != 0 ) + { // world spawn brushes as well as ladders we physically move + Vector minsIn = brush->mins; + Vector maxsIn = brush->maxs; + + TransformAABB( InstanceMatrix, minsIn, maxsIn, brush->mins, brush->maxs ); + } + else + { + } + brush->id += max_brush_id; + + int index = brush->original_sides - Instance->brushsides; + brush->original_sides = &brushsides[ nummapbrushsides + index ]; + } + + nummapbrushes += Instance->nummapbrushes; +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will merge in the map sides from the instance into +// the main map. +// Input : pInstanceEntity - the entity of the func_instance +// Instance - the map file of the instance +// InstanceOrigin - the translation of the instance +// InstanceAngle - the rotation of the instance +// InstanceMatrix - the translation / rotation matrix of the instance +// Output : none +//----------------------------------------------------------------------------- +void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) +{ + int max_side_id = 0; + + for( int i = 0; i < nummapbrushsides; i++ ) + { + if ( brushsides[ i ].id > max_side_id ) + { + max_side_id = brushsides[ i ].id; + } + } + + for( int i = 0; i < Instance->nummapbrushsides; i++ ) + { + brushsides[ nummapbrushsides + i ] = Instance->brushsides[ i ]; + + side_t *side = &brushsides[ nummapbrushsides + i ]; + // The planes got merged & remapped. So you need to search for the output plane index on each side + // NOTE: You could optimize this by saving off an index map in MergePlanes + side->planenum = FindFloatPlane( Instance->mapplanes[side->planenum].normal, Instance->mapplanes[side->planenum].dist ); + side->id += max_side_id; + + // this could be pre-processed into a list for quicker checking + bool bNeedsTranslation = ( side->pMapDisp && side->pMapDisp->entitynum == 0 ); + if ( !bNeedsTranslation ) + { // check for sides that are part of the world spawn - those need translating + for( int j = 0; j < Instance->entities[ 0 ].numbrushes; j++ ) + { + int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides; + + if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) ) + { + bNeedsTranslation = true; + break; + } + } + } + if ( !bNeedsTranslation ) + { // sides for ladders are outside of the world spawn, but also need translating + for( int j = Instance->entities[ 0 ].numbrushes; j < Instance->nummapbrushes; j++ ) + { + int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides; + + if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) && ( Instance->mapbrushes[ j ].contents & CONTENTS_LADDER ) != 0 ) + { + bNeedsTranslation = true; + break; + } + } + } + if ( bNeedsTranslation ) + { // we only want to do the adjustment on world spawn brushes, not entity brushes + if ( side->winding ) + { + for( int point = 0; point < side->winding->numpoints; point++ ) + { + Vector inPoint = side->winding->p[ point ]; + VectorTransform( inPoint, InstanceMatrix, side->winding->p[ point ] ); + } + } + + int planenum = side->planenum; + cplane_t inPlane, outPlane; + inPlane.normal = mapplanes[ planenum ].normal; + inPlane.dist = mapplanes[ planenum ].dist; + + MatrixTransformPlane( InstanceMatrix, inPlane, outPlane ); + planenum = FindFloatPlane( outPlane.normal, outPlane.dist ); + side->planenum = planenum; + + brush_texture_t bt = Instance->side_brushtextures[ i ]; + + VectorRotate( Instance->side_brushtextures[ i ].UAxis, InstanceMatrix, bt.UAxis ); + VectorRotate( Instance->side_brushtextures[ i ].VAxis, InstanceMatrix, bt.VAxis ); + bt.shift[ 0 ] -= InstanceOrigin.Dot( bt.UAxis ) / bt.textureWorldUnitsPerTexel[ 0 ]; + bt.shift[ 1 ] -= InstanceOrigin.Dot( bt.VAxis ) / bt.textureWorldUnitsPerTexel[ 1 ]; + + if ( !onlyents ) + { + side->texinfo = TexinfoForBrushTexture ( &mapplanes[ side->planenum ], &bt, vec3_origin ); + } + } + + if ( side->pMapDisp ) + { + mapdispinfo_t *disp = side->pMapDisp; + + disp->brushSideID = side->id; + Vector inPoint = disp->startPosition; + VectorTransform( inPoint, InstanceMatrix, disp->startPosition ); + + disp->face.originalface = side; + disp->face.texinfo = side->texinfo; + disp->face.planenum = side->planenum; + disp->entitynum += num_entities; + + for( int point = 0; point < disp->face.w->numpoints; point++ ) + { + Vector inPoint = disp->face.w->p[ point ]; + VectorTransform( inPoint, InstanceMatrix, disp->face.w->p[ point ] ); + } + + } + } + + nummapbrushsides += Instance->nummapbrushsides; +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will look for replace parameters in the function instance +// to see if there is anything in the epair that should be replaced. +// Input : pPair - the epair with the value +// pInstanceEntity - the func_instance that may ahve replace keywords +// Output : pPair - the value field may be updated +//----------------------------------------------------------------------------- +void CMapFile::ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity ) +{ + char Value[ MAX_KEYVALUE_LEN ], NewValue[ MAX_KEYVALUE_LEN ]; + bool Overwritten = false; + + strcpy( NewValue, pPair->value ); + for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next ) + { + if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 ) + { + char InstanceVariable[ MAX_KEYVALUE_LEN ]; + + strcpy( InstanceVariable, epInstance->value ); + + char *ValuePos = strchr( InstanceVariable, ' ' ); + if ( !ValuePos ) + { + continue; + } + *ValuePos = 0; + ValuePos++; + + strcpy( Value, NewValue ); + if ( !V_StrSubst( Value, InstanceVariable, ValuePos, NewValue, sizeof( NewValue ), false ) ) + { + Overwritten = true; + break; + } + } + } + + if ( !Overwritten && strcmp( pPair->value, NewValue ) != 0 ) + { + free( pPair->value ); + pPair->value = copystring( NewValue ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will merge in the entities from the instance into +// the main map. +// Input : pInstanceEntity - the entity of the func_instance +// Instance - the map file of the instance +// InstanceOrigin - the translation of the instance +// InstanceAngle - the rotation of the instance +// InstanceMatrix - the translation / rotation matrix of the instance +// Output : none +//----------------------------------------------------------------------------- +void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) +{ + int max_entity_id = 0; + char temp[ 2048 ]; + char NameFixup[ 128 ]; + entity_t *WorldspawnEnt = NULL; + GameData::TNameFixup FixupStyle; + + char *pTargetName = ValueForKey( pInstanceEntity, "targetname" ); + char *pName = ValueForKey( pInstanceEntity, "name" ); + if ( pTargetName[ 0 ] ) + { + sprintf( NameFixup, "%s", pTargetName ); + } + else if ( pName[ 0 ] ) + { + sprintf( NameFixup, "%s", pName ); + } + else + { + sprintf( NameFixup, "InstanceAuto%d", m_InstanceCount ); + } + + for( int i = 0; i < num_entities; i++ ) + { + char *pID = ValueForKey( &entities[ i ], "hammerid" ); + if ( pID[ 0 ] ) + { + int value = atoi( pID ); + if ( value > max_entity_id ) + { + max_entity_id = value; + } + } + } + + FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) ); + + for( int i = 0; i < Instance->num_entities; i++ ) + { + entities[ num_entities + i ] = Instance->entities[ i ]; + + entity_t *entity = &entities[ num_entities + i ]; + entity->firstbrush += ( nummapbrushes - Instance->nummapbrushes ); + + char *pID = ValueForKey( entity, "hammerid" ); + if ( pID[ 0 ] ) + { + int value = atoi( pID ); + value += max_entity_id; + sprintf( temp, "%d", value ); + + SetKeyValue( entity, "hammerid", temp ); + } + + char *pEntity = ValueForKey( entity, "classname" ); + if ( strcmpi( pEntity, "worldspawn" ) == 0 ) + { + WorldspawnEnt = entity; + } + else + { + Vector inOrigin = entity->origin; + VectorTransform( inOrigin, InstanceMatrix, entity->origin ); + + // search for variables coming from the func_instance to replace inside of the instance + // this is done before entity fixup, so fixup may occur on the replaced value. Not sure if this is a desired order of operation yet. + for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next ) + { + ReplaceInstancePair( ep, pInstanceEntity ); + } + +#ifdef MERGE_INSTANCE_DEBUG_INFO + Msg( "Remapping class %s\n", pEntity ); +#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO + GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle ); + if ( EntClass ) + { + for( int i = 0; i < EntClass->GetVariableCount(); i++ ) + { + GDinputvariable *EntVar = EntClass->GetVariableAt( i ); + char *pValue = ValueForKey( entity, ( char * )EntVar->GetName() ); + if ( GD.RemapKeyValue( EntVar->GetName(), pValue, temp, FixupStyle ) ) + { +#ifdef MERGE_INSTANCE_DEBUG_INFO + Msg( " %d. Remapped %s: from %s to %s\n", i, EntVar->GetName(), pValue, temp ); +#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO + SetKeyValue( entity, EntVar->GetName(), temp ); + } + else + { +#ifdef MERGE_INSTANCE_DEBUG_INFO + Msg( " %d. Ignored %s: %s\n", i, EntVar->GetName(), pValue ); +#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO + } + } + } + + if ( strcmpi( pEntity, "func_simpleladder" ) == 0 ) + { // hate having to do this, but the key values are so screwed up + AddLadderKeys( entity ); +/* Vector vInNormal, vOutNormal; + + vInNormal.x = FloatForKey( entity, "normal.x" ); + vInNormal.y = FloatForKey( entity, "normal.y" ); + vInNormal.z = FloatForKey( entity, "normal.z" ); + VectorRotate( vInNormal, InstanceMatrix, vOutNormal ); + + Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.x ); + SetKeyValue( entity, "normal.x", temp ); + + Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.y ); + SetKeyValue( entity, "normal.y", temp ); + + Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z ); + SetKeyValue( entity, "normal.z", temp );*/ + } + } + +#ifdef MERGE_INSTANCE_DEBUG_INFO + Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i ); + Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush ); + Msg( " KV Pairs:\n" ); + for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next ) + { + Msg( " %s %s\n", ep->key, ep->value ); + } +#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO + } + + // search for variables coming from the func_instance to replace inside of the instance + // this is done before connection fix up, so fix up may occur on the replaced value. Not sure if this is a desired order of operation yet. + for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next ) + { + ReplaceInstancePair( Connection->m_Pair, pInstanceEntity ); + } + + for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next ) + { + char *newValue, *oldValue; + char origValue[ 4096 ]; + int extraLen = 0; + + oldValue = Connection->m_Pair->value; + strcpy( origValue, oldValue ); + char *pos = strchr( origValue, ',' ); + if ( pos ) + { // null terminate the first field + *pos = NULL; + extraLen = strlen( pos + 1) + 1; // for the comma we just null'd + } + + if ( GD.RemapNameField( origValue, temp, FixupStyle ) ) + { + newValue = new char [ strlen( temp ) + extraLen + 1 ]; + strcpy( newValue, temp ); + if ( pos ) + { + strcat( newValue, "," ); + strcat( newValue, pos + 1 ); + } + + Connection->m_Pair->value = newValue; + delete oldValue; + } + } + + num_entities += Instance->num_entities; + + MoveBrushesToWorldGeneral( WorldspawnEnt ); + WorldspawnEnt->numbrushes = 0; + WorldspawnEnt->epairs = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: this function will translate overlays from the instance into +// the main map. +// Input : InstanceEntityNum - the entity number of the func_instance +// Instance - the map file of the instance +// InstanceOrigin - the translation of the instance +// InstanceAngle - the rotation of the instance +// InstanceMatrix - the translation / rotation matrix of the instance +// Output : none +//----------------------------------------------------------------------------- +void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix ) +{ + for( int i = Instance->m_StartMapOverlays; i < g_aMapOverlays.Count(); i++ ) + { + Overlay_Translate( &g_aMapOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix ); + } + for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ ) + { + Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Loads a VMF or MAP file. If the file has a .MAP extension, the MAP +// loader is used, otherwise the file is assumed to be in VMF format. +// Input : pszFileName - Full path of the map file to load. +//----------------------------------------------------------------------------- +bool LoadMapFile( const char *pszFileName ) +{ + bool bLoadingManifest = false; + CManifest *pMainManifest = NULL; + ChunkFileResult_t eResult; + + // + // Dummy this up for the texture handling. This can be removed when old .MAP file + // support is removed. + // + g_nMapFileVersion = 400; + + const char *pszExtension =V_GetFileExtension( pszFileName ); + if ( pszExtension && strcmpi( pszExtension, "vmm" ) == 0 ) + { + pMainManifest = new CManifest(); + if ( pMainManifest->LoadVMFManifest( pszFileName ) ) + { + eResult = ChunkFile_Ok; + pszFileName = pMainManifest->GetInstancePath(); + } + else + { + eResult = ChunkFile_Fail; + } + bLoadingManifest = true; + } + else + { + // + // Open the file. + // + CChunkFile File; + eResult = File.Open(pszFileName, ChunkFile_Read); + + // + // Read the file. + // + if (eResult == ChunkFile_Ok) + { + int index = g_Maps.AddToTail( new CMapFile() ); + g_LoadingMap = g_Maps[ index ]; + if ( g_MainMap == NULL ) + { + g_MainMap = g_LoadingMap; + } + + if ( g_MainMap == g_LoadingMap || verbose ) + { + Msg( "Loading %s\n", pszFileName ); + } + + + // reset the displacement info count + // nummapdispinfo = 0; + + // + // Set up handlers for the subchunks that we are interested in. + // + CChunkHandlerMap Handlers; + Handlers.AddHandler("world", (ChunkHandler_t)LoadEntityCallback, 0); + Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0); + + File.PushHandlers(&Handlers); + + // + // Read the sub-chunks. We ignore keys in the root of the file. + // + while (eResult == ChunkFile_Ok) + { + eResult = File.ReadChunk(); + } + + File.PopHandlers(); + } + else + { + Error("Error opening %s: %s.\n", pszFileName, File.GetErrorText(eResult)); + } + } + + if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF)) + { + // Update the overlay/side list(s). + Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays ); + OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays ); + + g_LoadingMap->CheckForInstances( pszFileName ); + + if ( pMainManifest ) + { + pMainManifest->CordonWorld(); + } + + ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs); + for (int i=0 ; ientities[0].numbrushes ; i++) + { + // HLTOOLS: Raise map limits + if (g_LoadingMap->mapbrushes[i].mins[0] > MAX_COORD_INTEGER) + { + continue; // no valid points + } + + AddPointToBounds (g_LoadingMap->mapbrushes[i].mins, g_LoadingMap->map_mins, g_LoadingMap->map_maxs); + AddPointToBounds (g_LoadingMap->mapbrushes[i].maxs, g_LoadingMap->map_mins, g_LoadingMap->map_maxs); + } + + qprintf ("%5i brushes\n", g_LoadingMap->nummapbrushes); + qprintf ("%5i clipbrushes\n", g_LoadingMap->c_clipbrushes); + qprintf ("%5i total sides\n", g_LoadingMap->nummapbrushsides); + qprintf ("%5i boxbevels\n", g_LoadingMap->c_boxbevels); + qprintf ("%5i edgebevels\n", g_LoadingMap->c_edgebevels); + qprintf ("%5i entities\n", g_LoadingMap->num_entities); + qprintf ("%5i planes\n", g_LoadingMap->nummapplanes); + qprintf ("%5i areaportals\n", g_LoadingMap->c_areaportals); + qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", g_LoadingMap->map_mins[0],g_LoadingMap->map_mins[1],g_LoadingMap->map_mins[2], + g_LoadingMap->map_maxs[0],g_LoadingMap->map_maxs[1],g_LoadingMap->map_maxs[2]); + + //TestExpandBrushes(); + + // Clear the error reporting + g_MapError.ClearState(); + } + + if ( g_MainMap == g_LoadingMap ) + { + num_entities = g_MainMap->num_entities; + memcpy( entities, g_MainMap->entities, sizeof( g_MainMap->entities ) ); + } + g_LoadingMap->ForceFuncAreaPortalWindowContents(); + + return ( ( eResult == ChunkFile_Ok ) || ( eResult == ChunkFile_EOF ) ); +} + +ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo) +{ + return g_LoadingMap->LoadSideCallback( pFile, pSideInfo ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pFile - +// pParent - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo) +{ + if (nummapbrushsides == MAX_MAP_BRUSHSIDES) + { + g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); + } + + pSideInfo->pSide = &brushsides[nummapbrushsides]; + + side_t *side = pSideInfo->pSide; + mapbrush_t *b = pSideInfo->pBrush; + g_MapError.BrushSide( pSideInfo->nSideIndex++ ); + + // initialize the displacement info + pSideInfo->pSide->pMapDisp = NULL; + + // + // Set up handlers for the subchunks that we are interested in. + // + CChunkHandlerMap Handlers; + Handlers.AddHandler( "dispinfo", ( ChunkHandler_t )LoadDispInfoCallback, &side->pMapDisp ); + + // + // Read the side chunk. + // + pFile->PushHandlers(&Handlers); + ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSideKeyCallback, pSideInfo); + pFile->PopHandlers(); + + if (eResult == ChunkFile_Ok) + { + side->contents |= pSideInfo->nBaseContents; + side->surf |= pSideInfo->nBaseFlags; + pSideInfo->td.flags |= pSideInfo->nBaseFlags; + + if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) + { + side->contents |= CONTENTS_DETAIL; + } + + if (fulldetail ) + { + side->contents &= ~CONTENTS_DETAIL; + } + + if (!(side->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) ) + { + side->contents |= CONTENTS_SOLID; + } + + // hints and skips are never detail, and have no content + if (side->surf & (SURF_HINT|SURF_SKIP) ) + { + side->contents = 0; + } + + // + // find the plane number + // + int planenum = PlaneFromPoints(pSideInfo->planepts[0], pSideInfo->planepts[1], pSideInfo->planepts[2]); + if (planenum != -1) + { + // + // See if the plane has been used already. + // + int k; + for ( k = 0; k < b->numsides; k++) + { + side_t *s2 = b->original_sides + k; + if (s2->planenum == planenum) + { + g_MapError.ReportWarning("duplicate plane"); + break; + } + if ( s2->planenum == (planenum^1) ) + { + g_MapError.ReportWarning("mirrored plane"); + break; + } + } + + // + // If the plane hasn't been used already, keep this side. + // + if (k == b->numsides) + { + side = b->original_sides + b->numsides; + side->planenum = planenum; + if ( !onlyents ) + { + side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &pSideInfo->td, vec3_origin); + } + + // save the td off in case there is an origin brush and we + // have to recalculate the texinfo + if (nummapbrushsides == MAX_MAP_BRUSHSIDES) + g_MapError.ReportError ("MAX_MAP_BRUSHSIDES"); + side_brushtextures[nummapbrushsides] = pSideInfo->td; + nummapbrushsides++; + b->numsides++; + +#ifdef VSVMFIO + // Tell Maya We Have Another Side + if ( CVmfImport::GetVmfImporter() ) + { + CVmfImport::GetVmfImporter()->AddSideCallback( + b, side, pSideInfo->td, + pSideInfo->planepts[ 0 ], pSideInfo->planepts[ 1 ], pSideInfo->planepts[ 2 ] ); + } +#endif // VSVMFIO + + } + } + else + { + g_MapError.ReportWarning("plane with no normal"); + } + } + + return(eResult); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : szKey - +// szValue - +// pSideInfo - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo) +{ + if (!stricmp(szKey, "plane")) + { + int nRead = sscanf(szValue, "(%f %f %f) (%f %f %f) (%f %f %f)", + &pSideInfo->planepts[0][0], &pSideInfo->planepts[0][1], &pSideInfo->planepts[0][2], + &pSideInfo->planepts[1][0], &pSideInfo->planepts[1][1], &pSideInfo->planepts[1][2], + &pSideInfo->planepts[2][0], &pSideInfo->planepts[2][1], &pSideInfo->planepts[2][2]); + + if (nRead != 9) + { + g_MapError.ReportError("parsing plane definition"); + } + } + else if (!stricmp(szKey, "material")) + { + // Get the material name. + if( g_ReplaceMaterials ) + { + szValue = ReplaceMaterialName( szValue ); + } + + strcpy(pSideInfo->td.name, szValue); + g_MapError.TextureState(szValue); + + // Find default flags and values for this material. + int mt = FindMiptex(pSideInfo->td.name); + pSideInfo->td.flags = textureref[mt].flags; + pSideInfo->td.lightmapWorldUnitsPerLuxel = textureref[mt].lightmapWorldUnitsPerLuxel; + + pSideInfo->pSide->contents = textureref[mt].contents; + pSideInfo->pSide->surf = pSideInfo->td.flags; + } + else if (!stricmp(szKey, "uaxis")) + { + int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.UAxis[0], &pSideInfo->td.UAxis[1], &pSideInfo->td.UAxis[2], &pSideInfo->td.shift[0], &pSideInfo->td.textureWorldUnitsPerTexel[0]); + if (nRead != 5) + { + g_MapError.ReportError("parsing U axis definition"); + } + } + else if (!stricmp(szKey, "vaxis")) + { + int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.VAxis[0], &pSideInfo->td.VAxis[1], &pSideInfo->td.VAxis[2], &pSideInfo->td.shift[1], &pSideInfo->td.textureWorldUnitsPerTexel[1]); + if (nRead != 5) + { + g_MapError.ReportError("parsing V axis definition"); + } + } + else if (!stricmp(szKey, "lightmapscale")) + { + pSideInfo->td.lightmapWorldUnitsPerLuxel = atoi(szValue); + if (pSideInfo->td.lightmapWorldUnitsPerLuxel == 0.0f) + { + g_MapError.ReportWarning("luxel size of 0"); + pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize; + } + pSideInfo->td.lightmapWorldUnitsPerLuxel *= g_luxelScale; + if (pSideInfo->td.lightmapWorldUnitsPerLuxel < g_minLuxelScale) + { + pSideInfo->td.lightmapWorldUnitsPerLuxel = g_minLuxelScale; + } + } + else if (!stricmp(szKey, "contents")) + { + pSideInfo->pSide->contents |= atoi(szValue); + } + else if (!stricmp(szKey, "flags")) + { + pSideInfo->td.flags |= atoi(szValue); + pSideInfo->pSide->surf = pSideInfo->td.flags; + } + else if (!stricmp(szKey, "id")) + { + pSideInfo->pSide->id = atoi( szValue ); + } + else if (!stricmp(szKey, "smoothing_groups")) + { + pSideInfo->pSide->smoothingGroups = atoi( szValue ); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: Reads the connections chunk of the entity. +// Input : pFile - Chunk file to load from. +// pLoadEntity - Structure to receive loaded entity information. +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity) +{ + return(pFile->ReadChunk((KeyHandler_t)LoadConnectionsKeyCallback, pLoadEntity)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Parses a key/value pair from the entity connections chunk. +// Input : szKey - Key indicating the name of the entity output. +// szValue - Comma delimited fields in the following format: +// ,,,, +// pLoadEntity - Structure to receive loaded entity information. +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity) +{ + return g_LoadingMap->LoadConnectionsKeyCallback( szKey, szValue, pLoadEntity ); +} + +ChunkFileResult_t CMapFile::LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity) +{ + // + // Create new input and fill it out. + // + epair_t *pOutput = new epair_t; + + pOutput->key = new char [strlen(szKey) + 1]; + pOutput->value = new char [strlen(szValue) + 1]; + + strcpy(pOutput->key, szKey); + strcpy(pOutput->value, szValue); + + m_ConnectionPairs = new CConnectionPairs( pOutput, m_ConnectionPairs ); + + // + // Append it to the end of epairs list. + // + pOutput->next = NULL; + + if (!pLoadEntity->pEntity->epairs) + { + pLoadEntity->pEntity->epairs = pOutput; + } + else + { + epair_t *ep; + for ( ep = pLoadEntity->pEntity->epairs; ep->next != NULL; ep = ep->next ) + { + } + ep->next = pOutput; + } + + return(ChunkFile_Ok); +} + + +ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity) +{ + return g_LoadingMap->LoadSolidCallback( pFile, pLoadEntity ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pFile - +// pParent - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity) +{ + if (nummapbrushes == MAX_MAP_BRUSHES) + { + g_MapError.ReportError ("nummapbrushes == MAX_MAP_BRUSHES"); + } + + mapbrush_t *b = &mapbrushes[nummapbrushes]; + b->original_sides = &brushsides[nummapbrushsides]; + b->entitynum = num_entities-1; + b->brushnum = nummapbrushes - pLoadEntity->pEntity->firstbrush; + + LoadSide_t SideInfo; + SideInfo.pBrush = b; + SideInfo.nSideIndex = 0; + SideInfo.nBaseContents = pLoadEntity->nBaseContents; + SideInfo.nBaseFlags = pLoadEntity->nBaseFlags; + + // + // Set up handlers for the subchunks that we are interested in. + // + CChunkHandlerMap Handlers; + Handlers.AddHandler("side", (ChunkHandler_t)::LoadSideCallback, &SideInfo); + + // + // Read the solid chunk. + // + pFile->PushHandlers(&Handlers); + ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSolidKeyCallback, b); + pFile->PopHandlers(); + + if (eResult == ChunkFile_Ok) + { + // get the content for the entire brush + b->contents = BrushContents (b); + + // allow detail brushes to be removed + if (nodetail && (b->contents & CONTENTS_DETAIL) && !HasDispInfo( b ) ) + { + b->numsides = 0; + return(ChunkFile_Ok); + } + + // allow water brushes to be removed + if (nowater && (b->contents & MASK_WATER) ) + { + b->numsides = 0; + return(ChunkFile_Ok); + } + + // create windings for sides and bounds for brush + MakeBrushWindings (b); + + // + // brushes that will not be visible at all will never be + // used as bsp splitters + // + // only do this on the world entity + // + if ( b->entitynum == 0 ) + { + if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) + { + if ( g_ClipTexinfo < 0 ) + { + g_ClipTexinfo = b->original_sides[0].texinfo; + } + c_clipbrushes++; + for (int i=0 ; inumsides ; i++) + { + b->original_sides[i].texinfo = TEXINFO_NODE; + } + } + } + + // + // origin brushes are removed, but they set + // the rotation origin for the rest of the brushes + // in the entity. After the entire entity is parsed, + // the planenums and texinfos will be adjusted for + // the origin brush + // + if (b->contents & CONTENTS_ORIGIN) + { + char string[32]; + Vector origin; + + if (num_entities == 1) + { + Error("Brush %i: origin brushes not allowed in world", b->id); + } + + VectorAdd (b->mins, b->maxs, origin); + VectorScale (origin, 0.5, origin); + + sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (&entities[b->entitynum], "origin", string); + + VectorCopy (origin, entities[b->entitynum].origin); + + // don't keep this brush + b->numsides = 0; + + return(ChunkFile_Ok); + } + +#ifdef VSVMFIO + if ( CVmfImport::GetVmfImporter() ) + { + CVmfImport::GetVmfImporter()->MapBrushToMayaCallback( b ); + } +#endif // VSVMFIO + + // + // find a map brushes with displacement surfaces and remove them from the "world" + // + if( HasDispInfo( b ) ) + { + // add the base face data to the displacement surface + DispGetFaceInfo( b ); + + // don't keep this brush + b->numsides = 0; + + return( ChunkFile_Ok ); + } + + AddBrushBevels (b); + + nummapbrushes++; + pLoadEntity->pEntity->numbrushes++; + } + else + { + return eResult; + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pFile - +// parent - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush) +{ + if (!stricmp(szKey, "id")) + { + pLoadBrush->id = atoi(szValue); + g_MapError.BrushState(pLoadBrush->id); + } + + return ChunkFile_Ok; +} + + +/* +================ +TestExpandBrushes + +Expands all the brush planes and saves a new map out +================ +*/ +void CMapFile::TestExpandBrushes (void) +{ + FILE *f; + side_t *s; + int i, j, bn; + winding_t *w; + char *name = "expanded.map"; + mapbrush_t *brush; + vec_t dist; + + Msg ("writing %s\n", name); + f = fopen (name, "wb"); + if (!f) + Error ("Can't write %s\b", name); + + fprintf (f, "{\n\"classname\" \"worldspawn\"\n"); + fprintf( f, "\"mapversion\" \"220\"\n\"sounds\" \"1\"\n\"MaxRange\" \"4096\"\n\"mapversion\" \"220\"\n\"wad\" \"vert.wad;dev.wad;generic.wad;spire.wad;urb.wad;cit.wad;water.wad\"\n" ); + + + for (bn=0 ; bnnumsides ; i++) + { + s = brush->original_sides + i; + dist = mapplanes[s->planenum].dist; + for (j=0 ; j<3 ; j++) + dist += fabs( 16 * mapplanes[s->planenum].normal[j] ); + + w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist); + + fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]); + fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]); + fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]); + + fprintf (f, "%s [ 0 0 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n", + TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) ); + + FreeWinding (w); + } + fprintf (f, "}\n"); + } + fprintf (f, "}\n"); + + fclose (f); + + Error ("can't proceed after expanding brushes"); +} + + +//----------------------------------------------------------------------------- +// Purpose: load in the displacement info "chunk" from the .map file into the +// vbsp map displacement info data structure +// Output: return the pointer to the displacement map +//----------------------------------------------------------------------------- +mapdispinfo_t *ParseDispInfoChunk( void ) +{ + int i, j; + int vertCount; + mapdispinfo_t *pMapDispInfo; + + // + // check to see if we exceeded the maximum displacement info list size + // + if( nummapdispinfo > MAX_MAP_DISPINFO ) + g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO"); + + // get a pointer to the next available displacement info slot + pMapDispInfo = &mapdispinfo[nummapdispinfo]; + nummapdispinfo++; + + // + // get the chunk opener - "{" + // + GetToken( false ); + if( strcmp( token, "{" ) ) + g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - {" ); + + // + // + // get the displacement info attribs + // + // + + // power + GetToken( true ); + pMapDispInfo->power = atoi( token ); + + // u and v mapping axes + for( i = 0; i < 2; i++ ) + { + GetToken( false ); + if( strcmp( token, "[" ) ) + g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - [" ); + + for( j = 0; j < 3; j++ ) + { + GetToken( false ); + + if( i == 0 ) + { + pMapDispInfo->uAxis[j] = atof( token ); + } + else + { + pMapDispInfo->vAxis[j] = atof( token ); + } + } + + GetToken( false ); + if( strcmp( token, "]" ) ) + g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - ]" ); + } + + // max displacement value + if( g_nMapFileVersion < 350 ) + { + GetToken( false ); + pMapDispInfo->maxDispDist = atof( token ); + } + + // minimum tesselation value + GetToken( false ); + pMapDispInfo->minTess = atoi( token ); + + // light smoothing angle + GetToken( false ); + pMapDispInfo->smoothingAngle = atof( token ); + + // + // get the displacement info displacement normals + // + GetToken( true ); + pMapDispInfo->vectorDisps[0][0] = atof( token ); + GetToken( false ); + pMapDispInfo->vectorDisps[0][1] = atof( token ); + GetToken( false ); + pMapDispInfo->vectorDisps[0][2] = atof( token ); + + vertCount = ( ( ( 1 << pMapDispInfo->power ) + 1 ) * ( ( 1 << pMapDispInfo->power ) + 1 ) ); + for( i = 1; i < vertCount; i++ ) + { + GetToken( false ); + pMapDispInfo->vectorDisps[i][0] = atof( token ); + GetToken( false ); + pMapDispInfo->vectorDisps[i][1] = atof( token ); + GetToken( false ); + pMapDispInfo->vectorDisps[i][2] = atof( token ); + } + + // + // get the displacement info displacement values + // + GetToken( true ); + pMapDispInfo->dispDists[0] = atof( token ); + + for( i = 1; i < vertCount; i++ ) + { + GetToken( false ); + pMapDispInfo->dispDists[i] = atof( token ); + } + + // + // get the chunk closer - "}" + // + GetToken( true ); + if( strcmp( token, "}" ) ) + g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - }" ); + + // return the index of the displacement info slot + return pMapDispInfo; +} + + -- cgit v1.2.3