You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
425 lines
20 KiB
425 lines
20 KiB
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
|
//
|
|
// Class & functions to parse and represent a VMF file.
|
|
//
|
|
//===============================================================================
|
|
|
|
#ifndef SIMPLEMAPFILE_H
|
|
#define SIMPLEMAPFILE_H
|
|
|
|
#if defined( COMPILER_MSVC )
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "chunkfile.h"
|
|
#include "stringpool.h"
|
|
|
|
#include "vbspmathutil.h"
|
|
|
|
class CSimpleMapFile;
|
|
class GameData;
|
|
|
|
DECLARE_LOGGING_CHANNEL( LOG_VBSP2 );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Flags that modify a brush volume or brush side surface
|
|
//-----------------------------------------------------------------------------
|
|
typedef int MapBrushContentsFlags_t;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Flags that modify a brush side surface
|
|
//-----------------------------------------------------------------------------
|
|
typedef short MapBrushSideSurfaceFlags_t;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A single entity from a VMF file.
|
|
//-----------------------------------------------------------------------------
|
|
struct MapEntity_t
|
|
{
|
|
// World-space location of the object.
|
|
Vector m_vOrigin;
|
|
|
|
// Index & count of brushes contained within this entity. These are stored in the map's brush array.
|
|
// Brushes in an entity have contiguous indices in the range: [m_nFirstBrushIndex, m_nFirstBrushIndex + m_nNumBrushes).
|
|
int m_nFirstBrushIndex;
|
|
int m_nNumBrushes;
|
|
|
|
// Index & count of key-value pairs (entity properties) contained within this entity. These are stored in the map's key-value pair array.
|
|
// Key-value pairs in an entity have contiguous indices in the range: [m_nFirstKVPairIndex, m_nFirstKVPairIndex + m_nNumKVPairs).
|
|
int m_nFirstKVPairIndex;
|
|
int m_nNumKVPairs;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A property of an entity from a VMF file.
|
|
//-----------------------------------------------------------------------------
|
|
struct MapEntityKeyValuePair_t
|
|
{
|
|
// These point into the string pool owned by the map object, so no memory management is necessary.
|
|
const char *m_pKey;
|
|
const char *m_pValue;
|
|
|
|
// True if this key value pair came from the "connections" section of an entity,
|
|
// false if it came from an ordinary entity key.
|
|
bool m_bIsConnection;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A CSG brush (convex solid) from a VMF file.
|
|
//-----------------------------------------------------------------------------
|
|
struct MapBrush_t
|
|
{
|
|
// Contents of the brush, based on the properties of its sides.
|
|
MapBrushContentsFlags_t m_ContentsFlags;
|
|
|
|
// Index & count of brush sides. These are stored in the map's brush sides array.
|
|
// Brush sides have contiguous indices in the range: [m_nFirstSideIndex, m_nFirstSideIndex + m_nNumSides).
|
|
int m_nFirstSideIndex;
|
|
int m_nNumSides;
|
|
|
|
// AABB of the brush
|
|
Vector m_vMinBounds, m_vMaxBounds;
|
|
};
|
|
|
|
static const int INVALID_DISPLACEMENT_INDEX = -1;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A single side of a CSG brush from a VMF file.
|
|
//-----------------------------------------------------------------------------
|
|
struct MapBrushSide_t
|
|
{
|
|
// Contents of the brush side, based on the material properties.
|
|
MapBrushContentsFlags_t m_ContentsFlags;
|
|
// Surface flags of the brush side, based on the material properties.
|
|
MapBrushSideSurfaceFlags_t m_SurfaceFlags;
|
|
// The index of the plane coincident with this brush side. The index is in the map's plane hash.
|
|
int m_nPlaneIndex;
|
|
// The index of the texture info struct for this brush side, describing the material applied to this side.
|
|
// The index is into the map's texture info array.
|
|
int m_nTextureInfoIndex;
|
|
// The polygonal face of this brush side.
|
|
Polygon_t m_Polygon;
|
|
// The index of the displacement associated with this side, or INVALID_DISPLACEMENT_INDEX if
|
|
// this side is not a displacement surface.
|
|
int m_nDisplacementIndex;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A displacement surface, specified on a particular CSG brush side.
|
|
//-----------------------------------------------------------------------------
|
|
class CMapDisplacement
|
|
{
|
|
public:
|
|
CMapDisplacement();
|
|
|
|
bool AreVerticesAllocated() const { return m_Vertices.Count() > 0 && m_nPower > 0; }
|
|
|
|
// NOTE: If new fields are added to this class, you must update MoveFrom()!
|
|
|
|
// Power-of-2 number of subdivisions.
|
|
// MIN_MAP_DISP_POWER <= m_nPower <= MAX_MAP_DISP_POWER
|
|
int m_nPower;
|
|
// Map brush side corresponding to this displacement surface. The brush side is "orphaned"
|
|
// in that no brushes will point to it (since the original brush is removed from the world).
|
|
int m_nOriginalBrushSide;
|
|
// SURF_* flags, defined in builddisp.h
|
|
int m_nFlags;
|
|
// Contents flags taken from the original brush.
|
|
// The value is stored here, since the original brush gets removed from the world.
|
|
MapBrushContentsFlags_t m_ContentsFlags;
|
|
Vector m_vStartPosition;
|
|
|
|
struct Vertex_t
|
|
{
|
|
float m_flAlpha;
|
|
float m_flDistance;
|
|
Vector m_vNormal;
|
|
Vector m_vOffset;
|
|
Vector4D m_vMultiBlend;
|
|
Vector m_vMultiBlendColors[MAX_MULTIBLEND_CHANNELS];
|
|
};
|
|
|
|
CUtlVector< Vertex_t > m_Vertices;
|
|
|
|
// DISPTRI_* flags, defined in bspfile.h (one per triangle).
|
|
CUtlVector< unsigned short > m_TriangleTags;
|
|
|
|
// Copies values and takes ownership of owned data from the other displacement object
|
|
// (this is a destructive, but efficient, operation used to migrate a displacement
|
|
// from one map object to another).
|
|
// NOTE: If you add new fields to CMapDisplacement, you must update this function!
|
|
void MoveFrom( CMapDisplacement *pOther );
|
|
|
|
private:
|
|
// Disallow value semantics
|
|
CMapDisplacement( const CMapDisplacement &other );
|
|
CMapDisplacement &operator=( const CMapDisplacement &other );
|
|
};
|
|
|
|
static const int MAX_TEXTURE_NAME_LENGTH = 128;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Texture properties for a brush side, as loaded from a VMF file.
|
|
//-----------------------------------------------------------------------------
|
|
struct MapBrushTexture_t
|
|
{
|
|
Vector m_vUAxis, m_vVAxis;
|
|
float m_flShift[2];
|
|
float m_flTextureWorldUnitsPerTexel[2];
|
|
float m_flLightmapWorldUnitsPerLuxel;
|
|
char m_MaterialName[MAX_TEXTURE_NAME_LENGTH];
|
|
MapBrushSideSurfaceFlags_t m_SurfaceFlags;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sentinel texture info index for a side of a solid BSP node.
|
|
//-----------------------------------------------------------------------------
|
|
static const int NODE_TEXTURE_INFO_INDEX = -1;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Planar mapping of texture data to a particular surface.
|
|
//-----------------------------------------------------------------------------
|
|
struct MapTextureInfo_t
|
|
{
|
|
// The U and V vectors (w-component contains the offset) that define
|
|
// a homogeneous planar mapping from 3D (world) space to 2D (texture) space.
|
|
float m_flTextureVectors[2][4];
|
|
float m_flLightmapVectors[2][4];
|
|
// Index into the map's texture data array of the structure which describes
|
|
// the material being mapped to this surface.
|
|
int m_nTextureDataIndex;
|
|
MapBrushSideSurfaceFlags_t m_SurfaceFlags;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Basic material information for a surface.
|
|
//-----------------------------------------------------------------------------
|
|
struct MapTextureData_t
|
|
{
|
|
Vector m_vReflectivity;
|
|
// Width and height of the base texture referenced by the material
|
|
int m_nWidth, m_nHeight;
|
|
// Name of the material (e.g. DEV/GRAYGRID) being mapped
|
|
char m_MaterialName[MAX_TEXTURE_NAME_LENGTH];
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Finds a key-value pair with the specified key in a given array of
|
|
// key-value pairs, or NULL if none is found
|
|
//-----------------------------------------------------------------------------
|
|
const MapEntityKeyValuePair_t *FindPair( const char *pKeyName, const MapEntityKeyValuePair_t *pPairs, int nNumPairs );
|
|
MapEntityKeyValuePair_t *FindPair( const char *pKeyName, MapEntityKeyValuePair_t *pPairs, int nNumPairs );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the value associated with the given pair or "" if the pair
|
|
// or value string is NULL (safe to chain with FindPair)
|
|
//-----------------------------------------------------------------------------
|
|
const char *GetPairValue( const MapEntityKeyValuePair_t *pPair );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A class which represents a VMF (map) file in memory
|
|
// or value string is NULL (safe to chain with FindPair)
|
|
//-----------------------------------------------------------------------------
|
|
class CSimpleMapFile
|
|
{
|
|
public:
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the minimum and maximum bound of all world brushes in this map file
|
|
//-----------------------------------------------------------------------------
|
|
Vector GetMinBounds() const { return m_vMinBounds; }
|
|
Vector GetMaxBounds() const { return m_vMaxBounds; }
|
|
|
|
int GetMapRevision() const { return m_nMapRevision; }
|
|
|
|
const MapEntity_t *GetEntities() const { return &m_Entities[0]; }
|
|
MapEntity_t *GetEntities() { return &m_Entities[0]; }
|
|
int GetEntityCount() const { return m_Entities.Count(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the world entity, which is the 0th entity in the file
|
|
//-----------------------------------------------------------------------------
|
|
const MapEntity_t *GetWorldEntity() const { return GetEntities(); }
|
|
MapEntity_t *GetWorldEntity() { return GetEntities(); }
|
|
|
|
const MapBrush_t *GetBrushes() const { return &m_Brushes[0]; }
|
|
MapBrush_t *GetBrushes() { return &m_Brushes[0]; }
|
|
int GetBrushCount() const { return m_Brushes.Count(); }
|
|
|
|
const MapBrushSide_t *GetBrushSides() const { return &m_BrushSides[0]; }
|
|
MapBrushSide_t *GetBrushSides() { return &m_BrushSides[0]; }
|
|
int GetBrushSideCount() const { return m_BrushSides.Count(); }
|
|
|
|
const CMapDisplacement *GetDisplacements() const { return &m_Displacements[0]; }
|
|
CMapDisplacement *GetDisplacements() { return &m_Displacements[0]; }
|
|
int GetDisplacementCount() const { return m_Displacements.Count(); }
|
|
|
|
const MapTextureInfo_t *GetTextureInfos() const { return &m_TextureInfos[0]; }
|
|
MapTextureInfo_t *GetTextureInfos() { return &m_TextureInfos[0]; }
|
|
int GetTextureInfoCount() const { return m_TextureInfos.Count(); }
|
|
|
|
const MapTextureData_t *GetTextureData() const { return &m_TextureData[0]; }
|
|
MapTextureData_t *GetTextureData() { return &m_TextureData[0]; }
|
|
int GetTextureDataCount() const { return m_TextureData.Count(); }
|
|
|
|
const MapEntityKeyValuePair_t *GetKeyValuePairs() const { return &m_KeyValuePairs[0]; }
|
|
MapEntityKeyValuePair_t *GetKeyValuePairs() { return &m_KeyValuePairs[0]; }
|
|
int GetKeyValuePairCount() const { return m_KeyValuePairs.Count(); }
|
|
|
|
const CPlaneHash *GetPlaneHash() const { return &m_PlaneHash; }
|
|
|
|
const char *GetFilename() const { return m_pFilename; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Adds a func_instance entity to the map file.
|
|
//-----------------------------------------------------------------------------
|
|
void AddFuncInstance( const char *pFilename, QAngle angles, Vector vOrigin, MapEntityKeyValuePair_t *pExtraKeyValues = NULL, int nExtraKeyValues = 0 );
|
|
|
|
int FindEntity( const char *pClassName, const char *pKeyName, const char *pValue, int nStartingIndex = 0 );
|
|
void RemoveEntity( int nEntityIndex );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Flags passed to ResolveInstances() to control behavior.
|
|
//-----------------------------------------------------------------------------
|
|
enum ResolveInstanceFlags_t
|
|
{
|
|
NO_FLAGS = 0x0,
|
|
CONVERT_STRUCTURAL_TO_DETAIL = 0x1, // convert all solids in world entities to func_detail
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Callback invoked after loading each instance.
|
|
// The callback is given the originally provided context value, the loaded
|
|
// instance map, and the key-value pairs from the parent map's
|
|
// func_instance entity chunk.
|
|
//-----------------------------------------------------------------------------
|
|
typedef void ( *PostLoadInstanceHandler_t )( void *pContext, CSimpleMapFile *pInstanceMapFile, MapEntityKeyValuePair_t *pFuncInstanceKeyValuePairs, int nNumKeyValuePairs );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Recursively loads all contained func_instance entities and merges
|
|
// them with the current map in breadth-first recursive order.
|
|
//-----------------------------------------------------------------------------
|
|
bool ResolveInstances( ResolveInstanceFlags_t instanceFlags = NO_FLAGS, PostLoadInstanceHandler_t pPostLoadInstanceHandler = NULL, void *pHandlerContext = NULL );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Loads a VMF (map) file from disk and constructs a new CSimpleMapFile object.
|
|
// The caller must delete the object when done.
|
|
//-----------------------------------------------------------------------------
|
|
static void LoadFromFile( IFileSystem *pFileSystem, const char *pVMFFilename, CSimpleMapFile **ppNewMapFile, ResolveInstanceFlags_t instanceFlags = NO_FLAGS );
|
|
|
|
private:
|
|
CSimpleMapFile( IFileSystem *pFileSystem, const char *pVMFFilename );
|
|
|
|
MapEntity_t *AllocateNewEntity();
|
|
MapBrush_t *AllocateNewBrush();
|
|
MapBrushSide_t *AllocateNewBrushSide();
|
|
CMapDisplacement *AllocateNewDisplacement();
|
|
MapBrushTexture_t *AllocateNewBrushTexture();
|
|
MapTextureData_t *AllocateNewTextureData();
|
|
MapEntityKeyValuePair_t *AllocateNewKeyValuePair();
|
|
|
|
// Some entity types are simply ignored
|
|
void DeallocateLastEntity() { m_Entities.RemoveMultipleFromTail( 1 ); }
|
|
|
|
// Some brushes are loaded and then discarded (e.g. brushes with displacement)
|
|
void DeallocateLastBrush() { m_Brushes.RemoveMultipleFromTail( 1 ); }
|
|
|
|
void ReportParseError( const char *pErrorString );
|
|
|
|
void MakeBrushPolygons( MapBrush_t *pBrush );
|
|
|
|
void MoveEntityBrushesToWorld( int nEntityIndex );
|
|
|
|
int GetTextureInfoForBrushTexture( MapBrushTexture_t *pBrushTexture, const Vector &vRelativeOrigin );
|
|
int FindOrCreateTextureData( const char *pTextureName );
|
|
int FindOrCreateTextureInfo( const MapTextureInfo_t &textureInfo );
|
|
MapBrushContentsFlags_t ComputeBrushContents( MapBrush_t *b );
|
|
|
|
// Functions used to merge instance data (recursively) with the primary map
|
|
void MergeInstance( int nEntityIndex, CSimpleMapFile *pInstanceMap, GameData *pGameData );
|
|
void PreLoadInstances( GameData *pGD );
|
|
void MergeBrushes( int nEntityIndex, const CSimpleMapFile *pInstanceMap, const Vector &vOrigin, const QAngle &orientation, const matrix3x4_t &transform );
|
|
void MergeBrushSides( int nEntityIndex, CSimpleMapFile *pInstanceMap, const Vector &vOrigin, const QAngle &orientation, const matrix3x4_t &transform );
|
|
void MergeEntities( int nEntityIndex, const CSimpleMapFile *pInstanceMap, const Vector &vOrigin, const QAngle &orientation, const matrix3x4_t &transform, GameData *pGameData );
|
|
void FixupInstanceKeyValuePair( MapEntityKeyValuePair_t *pNewKeyValuePair, const char *pOriginalValue, const MapEntityKeyValuePair_t *pInstancePairs, int nNumPairs );
|
|
|
|
// The following static functions are passed as callbacks to the chunk file reader
|
|
// when certain chunks and key blocks are encountered in the VMF file.
|
|
static ChunkFileResult_t EntityChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t SolidChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t SideChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DispInfoChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t ConnectionsChunkHandler( CChunkFile *pFile, void *pData );
|
|
|
|
static ChunkFileResult_t EntityKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t SolidKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t SideKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t DisplacementKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
|
|
// These are shim functions which simply call into their corresponding displacement key handlers
|
|
static ChunkFileResult_t DisplacementNormalsChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementDistancesChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementOffsetsChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementAlphasChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementTriangleTagsChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementMultiBlendChunkHandler( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementMultiBlendColor0( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementMultiBlendColor1( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementMultiBlendColor2( CChunkFile *pFile, void *pData );
|
|
static ChunkFileResult_t DisplacementMultiBlendColor3( CChunkFile *pFile, void *pData );
|
|
|
|
// These read the actual key data associated with each displacement attribute
|
|
static ChunkFileResult_t DisplacementNormalsKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t DisplacementDistancesKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t DisplacementOffsetsKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t DisplacementAlphasKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t DisplacementTriangleTagsKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t DisplacementMultiBlendKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
static ChunkFileResult_t DisplacementMultiBlendColorKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
|
|
static ChunkFileResult_t ConnectionsKeyHandler( const char *pKey, const char *pValue, void *pData );
|
|
|
|
struct MaterialInfo_t
|
|
{
|
|
char m_Name[MAX_TEXTURE_NAME_LENGTH];
|
|
MapBrushContentsFlags_t m_ContentsFlags;
|
|
MapBrushSideSurfaceFlags_t m_SurfaceFlags;
|
|
};
|
|
MaterialInfo_t* FindMaterialInfo( const char *pName );
|
|
|
|
IFileSystem *m_pFileSystem;
|
|
|
|
// This is needed so instances can be resolved. If we're loading map files from memory buffers
|
|
// or other non-file sources, we'll need an "include interface" (like D3DX/HLSL's include resolution system).
|
|
const char *m_pFilename;
|
|
|
|
CPlaneHash m_PlaneHash;
|
|
CUtlVector< MapEntity_t > m_Entities;
|
|
CUtlVector< MapBrush_t > m_Brushes;
|
|
CUtlVector< CMapDisplacement > m_Displacements;
|
|
// These two arrays (m_BrushSides & m_BrushTextures) should always be the same size as there is a 1:1 correspondence.
|
|
CUtlVector< MapBrushSide_t > m_BrushSides;
|
|
CUtlVector< MapBrushTexture_t > m_BrushTextures;
|
|
CUtlVector< MapTextureInfo_t > m_TextureInfos;
|
|
CUtlVector< MapTextureData_t > m_TextureData;
|
|
|
|
// Number of instances loaded (recursively).
|
|
// This value is only used on the root-level map. It is used to generate unique names for instanced entities.
|
|
int m_nInstanceCount;
|
|
|
|
// This value is only used on instance maps to control loading and processing.
|
|
ResolveInstanceFlags_t m_InstanceFlags;
|
|
|
|
// Used to store entity property data
|
|
CUtlVector< MapEntityKeyValuePair_t > m_KeyValuePairs;
|
|
CStringPool m_KeyValueStringPool;
|
|
|
|
Vector m_vMinBounds, m_vMaxBounds;
|
|
|
|
int m_nMapRevision;
|
|
int m_nHighestID;
|
|
|
|
// Used to cache results of FindMaterialInfo, which gets information about materials applied to brush sides.
|
|
CUtlVector< MaterialInfo_t > m_MaterialInfos;
|
|
};
|
|
|
|
#endif // SIMPLEMAPFILE_H
|