mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2025-09-19 12:06:07 +08:00
First version of the SOurce SDK 2013
This commit is contained in:
29
utils/common/ISQLDBReplyTarget.h
Normal file
29
utils/common/ISQLDBReplyTarget.h
Normal file
@ -0,0 +1,29 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ISQLDLREPLYTARGET_H
|
||||
#define ISQLDLREPLYTARGET_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Interface to handle results of SQL queries
|
||||
//-----------------------------------------------------------------------------
|
||||
class ISQLDBReplyTarget
|
||||
{
|
||||
public:
|
||||
// handles a response from the database
|
||||
virtual void SQLDBResponse(int cmdID, int returnState, int returnVal, void *data) = 0;
|
||||
|
||||
// called from a seperate thread; tells the reply target that a message is waiting for it
|
||||
virtual void WakeUp() = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // ISQLDLREPLYTARGET_H
|
192
utils/common/MySqlDatabase.cpp
Normal file
192
utils/common/MySqlDatabase.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "MySqlDatabase.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CMySqlDatabase::CMySqlDatabase()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Destructor
|
||||
// blocks until db process thread has stopped
|
||||
//-----------------------------------------------------------------------------
|
||||
CMySqlDatabase::~CMySqlDatabase()
|
||||
{
|
||||
// flag the thread to stop
|
||||
m_bRunThread = false;
|
||||
|
||||
// pulse the thread to make it run
|
||||
::SetEvent(m_hEvent);
|
||||
|
||||
// make sure it's done
|
||||
::EnterCriticalSection(&m_csThread);
|
||||
::LeaveCriticalSection(&m_csThread);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Thread access function
|
||||
//-----------------------------------------------------------------------------
|
||||
static DWORD WINAPI staticThreadFunc(void *param)
|
||||
{
|
||||
((CMySqlDatabase *)param)->RunThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Establishes connection to the database and sets up this object to handle db command
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CMySqlDatabase::Initialize()
|
||||
{
|
||||
// prepare critical sections
|
||||
//!! need to download SDK and replace these with InitializeCriticalSectionAndSpinCount() calls
|
||||
::InitializeCriticalSection(&m_csThread);
|
||||
::InitializeCriticalSection(&m_csInQueue);
|
||||
::InitializeCriticalSection(&m_csOutQueue);
|
||||
::InitializeCriticalSection(&m_csDBAccess);
|
||||
|
||||
// initialize wait calls
|
||||
m_hEvent = ::CreateEvent(NULL, false, true, NULL);
|
||||
|
||||
// start the DB-access thread
|
||||
m_bRunThread = true;
|
||||
|
||||
unsigned long threadID;
|
||||
::CreateThread(NULL, 0, staticThreadFunc, this, 0, &threadID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Main thread loop
|
||||
//-----------------------------------------------------------------------------
|
||||
void CMySqlDatabase::RunThread()
|
||||
{
|
||||
::EnterCriticalSection(&m_csThread);
|
||||
while (m_bRunThread)
|
||||
{
|
||||
if (m_InQueue.Count() > 0)
|
||||
{
|
||||
// get a dispatched DB request
|
||||
::EnterCriticalSection(&m_csInQueue);
|
||||
|
||||
// pop the front of the queue
|
||||
int headIndex = m_InQueue.Head();
|
||||
msg_t msg = m_InQueue[headIndex];
|
||||
m_InQueue.Remove(headIndex);
|
||||
|
||||
::LeaveCriticalSection(&m_csInQueue);
|
||||
|
||||
::EnterCriticalSection(&m_csDBAccess);
|
||||
|
||||
// run sqldb command
|
||||
msg.result = msg.cmd->RunCommand();
|
||||
|
||||
::LeaveCriticalSection(&m_csDBAccess);
|
||||
|
||||
if (msg.replyTarget)
|
||||
{
|
||||
// put the results in the outgoing queue
|
||||
::EnterCriticalSection(&m_csOutQueue);
|
||||
m_OutQueue.AddToTail(msg);
|
||||
::LeaveCriticalSection(&m_csOutQueue);
|
||||
|
||||
// wake up out queue
|
||||
msg.replyTarget->WakeUp();
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is no return data from the call, so kill the object now
|
||||
msg.cmd->deleteThis();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing in incoming queue, so wait until we get the signal
|
||||
::WaitForSingleObject(m_hEvent, INFINITE);
|
||||
}
|
||||
|
||||
// check the size of the outqueue; if it's getting too big, sleep to let the main thread catch up
|
||||
if (m_OutQueue.Count() > 50)
|
||||
{
|
||||
::Sleep(2);
|
||||
}
|
||||
}
|
||||
::LeaveCriticalSection(&m_csThread);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Adds a database command to the queue, and wakes the db thread
|
||||
//-----------------------------------------------------------------------------
|
||||
void CMySqlDatabase::AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState)
|
||||
{
|
||||
::EnterCriticalSection(&m_csInQueue);
|
||||
|
||||
// add to the queue
|
||||
msg_t msg = { cmd, replyTarget, 0, returnState };
|
||||
m_InQueue.AddToTail(msg);
|
||||
|
||||
::LeaveCriticalSection(&m_csInQueue);
|
||||
|
||||
// signal the thread to start running
|
||||
::SetEvent(m_hEvent);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Dispatches responses to SQLDB queries
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CMySqlDatabase::RunFrame()
|
||||
{
|
||||
bool doneWork = false;
|
||||
|
||||
while (m_OutQueue.Count() > 0)
|
||||
{
|
||||
::EnterCriticalSection(&m_csOutQueue);
|
||||
|
||||
// pop the first item in the queue
|
||||
int headIndex = m_OutQueue.Head();
|
||||
msg_t msg = m_OutQueue[headIndex];
|
||||
m_OutQueue.Remove(headIndex);
|
||||
|
||||
::LeaveCriticalSection(&m_csOutQueue);
|
||||
|
||||
// run result
|
||||
if (msg.replyTarget)
|
||||
{
|
||||
msg.replyTarget->SQLDBResponse(msg.cmd->GetID(), msg.returnState, msg.result, msg.cmd->GetReturnData());
|
||||
|
||||
// kill command
|
||||
// it would be a good optimization to be able to reuse these
|
||||
msg.cmd->deleteThis();
|
||||
}
|
||||
|
||||
doneWork = true;
|
||||
}
|
||||
|
||||
return doneWork;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: load info - returns the number of sql db queries waiting to be processed
|
||||
//-----------------------------------------------------------------------------
|
||||
int CMySqlDatabase::QueriesInOutQueue()
|
||||
{
|
||||
// the queue names are from the DB point of view, not the server - thus the reversal
|
||||
return m_InQueue.Count();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: number of queries finished processing, waiting to be responded to
|
||||
//-----------------------------------------------------------------------------
|
||||
int CMySqlDatabase::QueriesInFinishedQueue()
|
||||
{
|
||||
return m_OutQueue.Count();
|
||||
}
|
104
utils/common/MySqlDatabase.h
Normal file
104
utils/common/MySqlDatabase.h
Normal file
@ -0,0 +1,104 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MYSQLDATABASE_H
|
||||
#define MYSQLDATABASE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include "ISQLDBReplyTarget.h"
|
||||
#include "utlvector.h"
|
||||
#include "UtlLinkedList.h"
|
||||
|
||||
class ISQLDBCommand;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Generic MySQL accessing database
|
||||
// Provides threaded I/O queue functionality for accessing a mysql db
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMySqlDatabase
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
CMySqlDatabase();
|
||||
~CMySqlDatabase();
|
||||
|
||||
// initialization - must be called before this object can be used
|
||||
bool Initialize();
|
||||
|
||||
// Dispatches responses to SQLDB queries
|
||||
bool RunFrame();
|
||||
|
||||
// load info - returns the number of sql db queries waiting to be processed
|
||||
virtual int QueriesInOutQueue();
|
||||
|
||||
// number of queries finished processing, waiting to be responded to
|
||||
virtual int QueriesInFinishedQueue();
|
||||
|
||||
// activates the thread
|
||||
void RunThread();
|
||||
|
||||
// command queues
|
||||
void AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState = 0);
|
||||
|
||||
private:
|
||||
|
||||
// threading data
|
||||
bool m_bRunThread;
|
||||
CRITICAL_SECTION m_csThread;
|
||||
CRITICAL_SECTION m_csInQueue;
|
||||
CRITICAL_SECTION m_csOutQueue;
|
||||
CRITICAL_SECTION m_csDBAccess;
|
||||
|
||||
// wait event
|
||||
HANDLE m_hEvent;
|
||||
|
||||
struct msg_t
|
||||
{
|
||||
ISQLDBCommand *cmd;
|
||||
ISQLDBReplyTarget *replyTarget;
|
||||
int result;
|
||||
int returnState;
|
||||
};
|
||||
|
||||
// command queues
|
||||
CUtlLinkedList<msg_t, int> m_InQueue;
|
||||
CUtlLinkedList<msg_t, int> m_OutQueue;
|
||||
};
|
||||
|
||||
class Connection;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Interface to a command
|
||||
//-----------------------------------------------------------------------------
|
||||
class ISQLDBCommand
|
||||
{
|
||||
public:
|
||||
// makes the command run (blocking), returning the success code
|
||||
virtual int RunCommand() = 0;
|
||||
|
||||
// return data
|
||||
virtual void *GetReturnData() { return NULL; }
|
||||
|
||||
// returns the command ID
|
||||
virtual int GetID() { return 0; }
|
||||
|
||||
// gets information about the command for if it failed
|
||||
virtual void GetDebugInfo(char *buf, int bufSize) { buf[0] = 0; }
|
||||
|
||||
// use to delete
|
||||
virtual void deleteThis() = 0;
|
||||
|
||||
protected:
|
||||
// protected destructor, so that it has to be deleted through deleteThis()
|
||||
virtual ~ISQLDBCommand() {}
|
||||
};
|
||||
|
||||
|
||||
#endif // MYSQLDATABASE_H
|
5064
utils/common/bsplib.cpp
Normal file
5064
utils/common/bsplib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
404
utils/common/bsplib.h
Normal file
404
utils/common/bsplib.h
Normal file
@ -0,0 +1,404 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef BSPLIB_H
|
||||
#define BSPLIB_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "bspfile.h"
|
||||
#include "utlvector.h"
|
||||
#include "utlstring.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "byteswap.h"
|
||||
#ifdef ENGINE_DLL
|
||||
#include "zone.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENGINE_DLL
|
||||
typedef CUtlVector<unsigned char, CHunkMemory<unsigned char> > CDispLightmapSamplePositions;
|
||||
#else
|
||||
typedef CUtlVector<unsigned char> CDispLightmapSamplePositions;
|
||||
#endif
|
||||
|
||||
class ISpatialQuery;
|
||||
struct Ray_t;
|
||||
class Vector2D;
|
||||
struct portal_t;
|
||||
class CUtlBuffer;
|
||||
class IZip;
|
||||
|
||||
// this is only true in vrad
|
||||
extern bool g_bHDR;
|
||||
|
||||
// default width/height of luxels in world units.
|
||||
#define DEFAULT_LUXEL_SIZE ( 16.0f )
|
||||
|
||||
#define SINGLE_BRUSH_MAP (MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER)
|
||||
#define SINGLEMAP (MAX_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_LIGHTMAP_DIM_INCLUDING_BORDER)
|
||||
|
||||
struct entity_t
|
||||
{
|
||||
Vector origin;
|
||||
int firstbrush;
|
||||
int numbrushes;
|
||||
epair_t *epairs;
|
||||
|
||||
// only valid for func_areaportals
|
||||
int areaportalnum;
|
||||
int portalareas[2];
|
||||
portal_t *m_pPortalsLeadingIntoAreas[2]; // portals leading into portalareas
|
||||
};
|
||||
|
||||
extern int num_entities;
|
||||
extern entity_t entities[MAX_MAP_ENTITIES];
|
||||
|
||||
extern int nummodels;
|
||||
extern dmodel_t dmodels[MAX_MAP_MODELS];
|
||||
|
||||
extern int visdatasize;
|
||||
extern byte dvisdata[MAX_MAP_VISIBILITY];
|
||||
extern dvis_t *dvis;
|
||||
|
||||
extern CUtlVector<byte> dlightdataHDR;
|
||||
extern CUtlVector<byte> dlightdataLDR;
|
||||
extern CUtlVector<byte> *pdlightdata;
|
||||
extern CUtlVector<char> dentdata;
|
||||
|
||||
extern int numleafs;
|
||||
#if !defined( _X360 )
|
||||
extern dleaf_t dleafs[MAX_MAP_LEAFS];
|
||||
#else
|
||||
extern dleaf_t *dleafs;
|
||||
#endif
|
||||
extern CUtlVector<dleafambientlighting_t> *g_pLeafAmbientLighting;
|
||||
extern CUtlVector<dleafambientindex_t> *g_pLeafAmbientIndex;
|
||||
extern unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS];
|
||||
|
||||
extern int numplanes;
|
||||
extern dplane_t dplanes[MAX_MAP_PLANES];
|
||||
|
||||
extern int numvertexes;
|
||||
extern dvertex_t dvertexes[MAX_MAP_VERTS];
|
||||
|
||||
extern int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals.
|
||||
extern unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS];
|
||||
|
||||
extern int g_numvertnormals;
|
||||
extern Vector g_vertnormals[MAX_MAP_VERTNORMALS];
|
||||
|
||||
extern int numnodes;
|
||||
extern dnode_t dnodes[MAX_MAP_NODES];
|
||||
|
||||
extern CUtlVector<texinfo_t> texinfo;
|
||||
|
||||
extern int numtexdata;
|
||||
extern dtexdata_t dtexdata[MAX_MAP_TEXDATA];
|
||||
|
||||
// displacement map .bsp file info
|
||||
extern CUtlVector<ddispinfo_t> g_dispinfo;
|
||||
extern CUtlVector<CDispVert> g_DispVerts;
|
||||
extern CUtlVector<CDispTri> g_DispTris;
|
||||
extern CDispLightmapSamplePositions g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS
|
||||
|
||||
extern int numorigfaces;
|
||||
extern dface_t dorigfaces[MAX_MAP_FACES];
|
||||
|
||||
extern int g_numprimitives;
|
||||
extern dprimitive_t g_primitives[MAX_MAP_PRIMITIVES];
|
||||
|
||||
extern int g_numprimverts;
|
||||
extern dprimvert_t g_primverts[MAX_MAP_PRIMVERTS];
|
||||
|
||||
extern int g_numprimindices;
|
||||
extern unsigned short g_primindices[MAX_MAP_PRIMINDICES];
|
||||
|
||||
extern int numfaces;
|
||||
extern dface_t dfaces[MAX_MAP_FACES];
|
||||
|
||||
extern int numfaceids;
|
||||
extern CUtlVector<dfaceid_t> dfaceids;
|
||||
|
||||
extern int numfaces_hdr;
|
||||
extern dface_t dfaces_hdr[MAX_MAP_FACES];
|
||||
|
||||
extern int numedges;
|
||||
extern dedge_t dedges[MAX_MAP_EDGES];
|
||||
|
||||
extern int numleaffaces;
|
||||
extern unsigned short dleaffaces[MAX_MAP_LEAFFACES];
|
||||
|
||||
extern int numleafbrushes;
|
||||
extern unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES];
|
||||
|
||||
extern int numsurfedges;
|
||||
extern int dsurfedges[MAX_MAP_SURFEDGES];
|
||||
|
||||
extern int numareas;
|
||||
extern darea_t dareas[MAX_MAP_AREAS];
|
||||
|
||||
extern int numareaportals;
|
||||
extern dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
|
||||
|
||||
extern int numbrushes;
|
||||
extern dbrush_t dbrushes[MAX_MAP_BRUSHES];
|
||||
|
||||
extern int numbrushsides;
|
||||
extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
|
||||
|
||||
extern int *pNumworldlights;
|
||||
extern dworldlight_t *dworldlights;
|
||||
|
||||
extern Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS];
|
||||
extern int g_nClipPortalVerts;
|
||||
|
||||
extern dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES];
|
||||
extern int g_nCubemapSamples;
|
||||
|
||||
extern int g_nOverlayCount;
|
||||
extern doverlay_t g_Overlays[MAX_MAP_OVERLAYS];
|
||||
extern doverlayfade_t g_OverlayFades[MAX_MAP_OVERLAYS]; // Parallel array of fade info in a separate lump to avoid breaking backwards compat
|
||||
|
||||
extern int g_nWaterOverlayCount;
|
||||
extern dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS];
|
||||
|
||||
extern CUtlVector<char> g_TexDataStringData;
|
||||
extern CUtlVector<int> g_TexDataStringTable;
|
||||
|
||||
extern int numleafwaterdata;
|
||||
extern dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA];
|
||||
|
||||
extern CUtlVector<CFaceMacroTextureInfo> g_FaceMacroTextureInfos;
|
||||
|
||||
extern CUtlVector<doccluderdata_t> g_OccluderData;
|
||||
extern CUtlVector<doccluderpolydata_t> g_OccluderPolyData;
|
||||
extern CUtlVector<int> g_OccluderVertexIndices;
|
||||
|
||||
// level flags - see LVLFLAGS_xxx in bspfile.h
|
||||
extern uint32 g_LevelFlags;
|
||||
|
||||
// physics collision data
|
||||
extern byte *g_pPhysCollide;
|
||||
extern int g_PhysCollideSize;
|
||||
extern byte *g_pPhysDisp;
|
||||
extern int g_PhysDispSize;
|
||||
|
||||
// Embedded pack/pak file
|
||||
IZip *GetPakFile( void );
|
||||
IZip *GetSwapPakFile( void );
|
||||
void ClearPakFile( IZip *pak );
|
||||
void AddFileToPak( IZip *pak, const char *pRelativeName, const char *fullpath );
|
||||
void AddBufferToPak( IZip *pak, const char *pRelativeName, void *data, int length, bool bTextMode );
|
||||
bool FileExistsInPak( IZip *pak, const char *pRelativeName );
|
||||
bool ReadFileFromPak( IZip *pak, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf );
|
||||
void RemoveFileFromPak( IZip *pak, const char *pRelativeName );
|
||||
int GetNextFilename( IZip *pak, int id, char *pBuffer, int bufferSize, int &fileSize );
|
||||
void ForceAlignment( IZip *pak, bool bAlign, bool bCompatibleFormat, unsigned int alignmentSize );
|
||||
|
||||
typedef bool (*CompressFunc_t)( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer );
|
||||
typedef bool (*VTFConvertFunc_t)( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc );
|
||||
typedef bool (*VHVFixupFunc_t)( const char *pVhvFilename, const char *pModelName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Game lump memory storage
|
||||
//-----------------------------------------------------------------------------
|
||||
// NOTE: This is not optimal at all; since I expect client lumps to
|
||||
// not be accessed all that often.
|
||||
|
||||
struct GameLump_t
|
||||
{
|
||||
GameLumpId_t m_Id;
|
||||
unsigned short m_Flags;
|
||||
unsigned short m_Version;
|
||||
CUtlMemory< unsigned char > m_Memory;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Handle to a game lump
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef unsigned short GameLumpHandle_t;
|
||||
|
||||
class CGameLump
|
||||
{
|
||||
public:
|
||||
//-----------------------------------------------------------------------------
|
||||
// Convert four-CC code to a handle + back
|
||||
//-----------------------------------------------------------------------------
|
||||
GameLumpHandle_t GetGameLumpHandle( GameLumpId_t id );
|
||||
GameLumpId_t GetGameLumpId( GameLumpHandle_t handle );
|
||||
int GetGameLumpFlags( GameLumpHandle_t handle );
|
||||
int GetGameLumpVersion( GameLumpHandle_t handle );
|
||||
void ComputeGameLumpSizeAndCount( int& size, int& clumpCount );
|
||||
void ParseGameLump( dheader_t* pHeader );
|
||||
void SwapGameLump( GameLumpId_t id, int version, byte *dest, byte *src, int size );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Game lump accessor methods
|
||||
//-----------------------------------------------------------------------------
|
||||
void* GetGameLump( GameLumpHandle_t handle );
|
||||
int GameLumpSize( GameLumpHandle_t handle );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Game lump iteration methods
|
||||
//-----------------------------------------------------------------------------
|
||||
GameLumpHandle_t FirstGameLump();
|
||||
GameLumpHandle_t NextGameLump( GameLumpHandle_t handle );
|
||||
GameLumpHandle_t InvalidGameLump();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Game lump creation/destruction method
|
||||
//-----------------------------------------------------------------------------
|
||||
GameLumpHandle_t CreateGameLump( GameLumpId_t id, int size, int flags, int version );
|
||||
void DestroyGameLump( GameLumpHandle_t handle );
|
||||
void DestroyAllGameLumps();
|
||||
|
||||
private:
|
||||
CUtlLinkedList< GameLump_t, GameLumpHandle_t > m_GameLumps;
|
||||
};
|
||||
|
||||
extern CGameLump g_GameLumps;
|
||||
extern CByteswap g_Swap;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helper for the bspzip tool
|
||||
//-----------------------------------------------------------------------------
|
||||
void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String table methods
|
||||
//-----------------------------------------------------------------------------
|
||||
const char * TexDataStringTable_GetString( int stringID );
|
||||
int TexDataStringTable_AddOrFindString( const char *pString );
|
||||
|
||||
void DecompressVis (byte *in, byte *decompressed);
|
||||
int CompressVis (byte *vis, byte *dest);
|
||||
|
||||
void OpenBSPFile( const char *filename );
|
||||
void CloseBSPFile(void);
|
||||
void LoadBSPFile( const char *filename );
|
||||
void LoadBSPFile_FileSystemOnly( const char *filename );
|
||||
void LoadBSPFileTexinfo( const char *filename );
|
||||
void WriteBSPFile( const char *filename, char *pUnused = NULL );
|
||||
void PrintBSPFileSizes(void);
|
||||
void PrintBSPPackDirectory(void);
|
||||
void ReleasePakFileLumps(void);
|
||||
bool SwapBSPFile( const char *filename, const char *swapFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc );
|
||||
bool GetPakFileLump( const char *pBSPFilename, void **pPakData, int *pPakSize );
|
||||
bool SetPakFileLump( const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize );
|
||||
void WriteLumpToFile( char *filename, int lump );
|
||||
void WriteLumpToFile( char *filename, int lump, int nLumpVersion, void *pBuffer, size_t nBufLen );
|
||||
bool GetBSPDependants( const char *pBSPFilename, CUtlVector< CUtlString > *pList );
|
||||
void UnloadBSPFile();
|
||||
|
||||
void ParseEntities (void);
|
||||
void UnparseEntities (void);
|
||||
void PrintEntity (entity_t *ent);
|
||||
|
||||
void SetKeyValue (entity_t *ent, const char *key, const char *value);
|
||||
char *ValueForKey (entity_t *ent, char *key);
|
||||
// will return "" if not present
|
||||
int IntForKey (entity_t *ent, char *key);
|
||||
int IntForKeyWithDefault(entity_t *ent, char *key, int nDefault );
|
||||
vec_t FloatForKey (entity_t *ent, char *key);
|
||||
vec_t FloatForKeyWithDefault (entity_t *ent, char *key, float default_value);
|
||||
void GetVectorForKey (entity_t *ent, char *key, Vector& vec);
|
||||
void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec);
|
||||
void GetAnglesForKey (entity_t *ent, char *key, QAngle& vec);
|
||||
epair_t *ParseEpair (void);
|
||||
void StripTrailing (char *e);
|
||||
|
||||
// Build a list of the face's vertices (index into dvertexes).
|
||||
// points must be able to hold pFace->numedges indices.
|
||||
void BuildFaceCalcWindingData( dface_t *pFace, int *points );
|
||||
|
||||
// Convert a tristrip to a trilist.
|
||||
// Removes degenerates.
|
||||
// Fills in pTriListIndices and pnTriListIndices.
|
||||
// You must free pTriListIndices with delete[].
|
||||
void TriStripToTriList(
|
||||
unsigned short const *pTriStripIndices,
|
||||
int nTriStripIndices,
|
||||
unsigned short **pTriListIndices,
|
||||
int *pnTriListIndices );
|
||||
|
||||
// Calculates the lightmap coordinates at a given set of positions given the
|
||||
// lightmap basis information.
|
||||
void CalcTextureCoordsAtPoints(
|
||||
float const texelsPerWorldUnits[2][4],
|
||||
int const subtractOffset[2],
|
||||
Vector const *pPoints,
|
||||
int const nPoints,
|
||||
Vector2D *pCoords );
|
||||
|
||||
// Figure out lightmap extents on all (lit) faces.
|
||||
void UpdateAllFaceLightmapExtents();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets at an interface for the tree for enumeration of leaves in volumes.
|
||||
//-----------------------------------------------------------------------------
|
||||
ISpatialQuery* ToolBSPTree();
|
||||
|
||||
class IBSPNodeEnumerator
|
||||
{
|
||||
public:
|
||||
// call back with a node and a context
|
||||
virtual bool EnumerateNode( int node, Ray_t const& ray, float f, int context ) = 0;
|
||||
|
||||
// call back with a leaf and a context
|
||||
virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context ) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Enumerates nodes + leafs in front to back order...
|
||||
//-----------------------------------------------------------------------------
|
||||
bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helps us find all leaves associated with a particular cluster
|
||||
//-----------------------------------------------------------------------------
|
||||
struct clusterlist_t
|
||||
{
|
||||
int leafCount;
|
||||
CUtlVector<int> leafs;
|
||||
};
|
||||
|
||||
extern CUtlVector<clusterlist_t> g_ClusterLeaves;
|
||||
|
||||
// Call this to build the mapping from cluster to leaves
|
||||
void BuildClusterTable( );
|
||||
|
||||
void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength );
|
||||
|
||||
void SetHDRMode( bool bHDR );
|
||||
|
||||
// ----------------------------------------------------------------------------- //
|
||||
// Helper accessors for the various structures.
|
||||
// ----------------------------------------------------------------------------- //
|
||||
|
||||
inline ColorRGBExp32* dface_AvgLightColor( dface_t *pFace, int nLightStyleIndex )
|
||||
{
|
||||
return (ColorRGBExp32*)&(*pdlightdata)[pFace->lightofs - (nLightStyleIndex+1) * 4];
|
||||
}
|
||||
|
||||
inline const char* TexInfo_TexName( int iTexInfo )
|
||||
{
|
||||
return TexDataStringTable_GetString( dtexdata[texinfo[iTexInfo].texdata].nameStringTableID );
|
||||
}
|
||||
|
||||
|
||||
#endif // BSPLIB_H
|
1007
utils/common/cmdlib.cpp
Normal file
1007
utils/common/cmdlib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
178
utils/common/cmdlib.h
Normal file
178
utils/common/cmdlib.h
Normal file
@ -0,0 +1,178 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef CMDLIB_H
|
||||
#define CMDLIB_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// cmdlib.h
|
||||
|
||||
#include "basetypes.h"
|
||||
|
||||
// This can go away when everything is in bin.
|
||||
#if defined( CMDLIB_NODBGLIB )
|
||||
void Error( PRINTF_FORMAT_STRING char const *pMsg, ... );
|
||||
#else
|
||||
#include "tier0/dbg.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#include "filesystem.h"
|
||||
#include "filesystem_tools.h"
|
||||
#include "tier1/utlstring.h"
|
||||
|
||||
|
||||
// Tools should use this as the read path ID. It'll look into the paths specified by gameinfo.txt
|
||||
#define TOOLS_READ_PATH_ID "GAME"
|
||||
|
||||
|
||||
// Tools should use this to fprintf data to files.
|
||||
void CmdLib_FPrintf( FileHandle_t hFile, PRINTF_FORMAT_STRING const char *pFormat, ... );
|
||||
char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile );
|
||||
|
||||
|
||||
// This can be set so Msg() sends output to hook functions (like the VMPI MySQL database),
|
||||
// but doesn't actually printf the output.
|
||||
extern bool g_bSuppressPrintfOutput;
|
||||
|
||||
extern IBaseFileSystem *g_pFileSystem;
|
||||
|
||||
// These call right into the functions in filesystem_tools.h
|
||||
void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage = 0 );
|
||||
void CmdLib_TermFileSystem(); // GracefulExit calls this.
|
||||
CreateInterfaceFn CmdLib_GetFileSystemFactory();
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable : 4244) // MIPS
|
||||
#pragma warning(disable : 4136) // X86
|
||||
#pragma warning(disable : 4051) // ALPHA
|
||||
|
||||
#pragma warning(disable : 4018) // signed/unsigned mismatch
|
||||
#pragma warning(disable : 4305) // truncate from double to float
|
||||
|
||||
#pragma warning(disable : 4389) // singned/unsigned mismatch in ==
|
||||
#pragma warning(disable: 4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
|
||||
// the dec offsetof macro doesnt work very well...
|
||||
#define myoffsetof(type,identifier) offsetof( type, identifier )
|
||||
|
||||
|
||||
// set these before calling CheckParm
|
||||
extern int myargc;
|
||||
extern char **myargv;
|
||||
|
||||
int Q_filelength (FileHandle_t f);
|
||||
int FileTime (char *path);
|
||||
|
||||
void Q_mkdir( char *path );
|
||||
|
||||
char *ExpandArg (char *path); // expand relative to CWD
|
||||
char *ExpandPath (char *path); // expand relative to gamedir
|
||||
|
||||
char *ExpandPathAndArchive (char *path);
|
||||
|
||||
// Fills in pOut with "X hours, Y minutes, Z seconds". Leaves out hours or minutes if they're zero.
|
||||
void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen );
|
||||
|
||||
|
||||
|
||||
int CheckParm (char *check);
|
||||
|
||||
FileHandle_t SafeOpenWrite ( const char *filename );
|
||||
FileHandle_t SafeOpenRead ( const char *filename );
|
||||
void SafeRead( FileHandle_t f, void *buffer, int count);
|
||||
void SafeWrite( FileHandle_t f, void *buffer, int count);
|
||||
|
||||
int LoadFile ( const char *filename, void **bufferptr );
|
||||
void SaveFile ( const char *filename, void *buffer, int count );
|
||||
qboolean FileExists ( const char *filename );
|
||||
|
||||
int ParseNum (char *str);
|
||||
|
||||
// Do a printf in the specified color.
|
||||
#define CP_ERROR stderr, 1, 0, 0, 1 // default colors..
|
||||
#define CP_WARNING stderr, 1, 1, 0, 1
|
||||
#define CP_STARTUP stdout, 0, 1, 1, 1
|
||||
#define CP_NOTIFY stdout, 1, 1, 1, 1
|
||||
void ColorPrintf( FILE *pFile, bool red, bool green, bool blue, bool intensity, PRINTF_FORMAT_STRING char const *pFormat, ... );
|
||||
|
||||
// Initialize spew output.
|
||||
void InstallSpewFunction();
|
||||
|
||||
// This registers an extra callback for spew output.
|
||||
typedef void (*SpewHookFn)( const char * );
|
||||
void InstallExtraSpewHook( SpewHookFn pFn );
|
||||
|
||||
// Install allocation hooks so we error out if an allocation can't happen.
|
||||
void InstallAllocationFunctions();
|
||||
|
||||
// This shuts down mgrs that use threads gracefully. If you just call exit(), the threads can
|
||||
// get in a state where you can't tell if they are shutdown or not, and it can stall forever.
|
||||
typedef void (*CleanupFn)();
|
||||
void CmdLib_AtCleanup( CleanupFn pFn ); // register a callback when Cleanup() is called.
|
||||
void CmdLib_Cleanup();
|
||||
void CmdLib_Exit( int exitCode ); // Use this to cleanup and call exit().
|
||||
|
||||
// entrypoint if chaining spew functions
|
||||
SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg );
|
||||
unsigned short SetConsoleTextColor( int red, int green, int blue, int intensity );
|
||||
void RestoreConsoleTextColor( unsigned short color );
|
||||
|
||||
// Append all spew output to the specified file.
|
||||
void SetSpewFunctionLogFile( char const *pFilename );
|
||||
|
||||
char *COM_Parse (char *data);
|
||||
|
||||
extern char com_token[1024];
|
||||
|
||||
char *copystring(const char *s);
|
||||
|
||||
void CreatePath( char *path );
|
||||
void QCopyFile( char *from, char *to );
|
||||
void SafeCreatePath( char *path );
|
||||
|
||||
extern qboolean archive;
|
||||
extern char archivedir[1024];
|
||||
|
||||
extern qboolean verbose;
|
||||
|
||||
void qprintf( PRINTF_FORMAT_STRING const char *format, ... );
|
||||
|
||||
void ExpandWildcards (int *argc, char ***argv);
|
||||
|
||||
void CmdLib_AddBasePath( const char *pBasePath );
|
||||
bool CmdLib_HasBasePath( const char *pFileName, int &pathLength );
|
||||
int CmdLib_GetNumBasePaths( void );
|
||||
const char *CmdLib_GetBasePath( int i );
|
||||
// Like ExpandPath but expands the path for each base path like SafeOpenRead
|
||||
int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath );
|
||||
|
||||
extern bool g_bStopOnExit;
|
||||
|
||||
// for compression routines
|
||||
typedef struct
|
||||
{
|
||||
byte *data;
|
||||
int count;
|
||||
} cblock_t;
|
||||
|
||||
|
||||
#endif // CMDLIB_H
|
333
utils/common/consolewnd.cpp
Normal file
333
utils/common/consolewnd.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "consolewnd.h"
|
||||
|
||||
|
||||
#pragma warning( disable : 4311 ) // warning C4311: 'reinterpret_cast' : pointer truncation from 'CConsoleWnd *const ' to 'LONG'
|
||||
#pragma warning( disable : 4312 ) // warning C4312: 'type cast' : conversion from 'LONG' to 'CConsoleWnd *' of greater size
|
||||
|
||||
#define EDITCONTROL_BORDER_SIZE 5
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
// Functions to manage the console window.
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
|
||||
class CConsoleWnd : public IConsoleWnd
|
||||
{
|
||||
public:
|
||||
CConsoleWnd();
|
||||
~CConsoleWnd();
|
||||
|
||||
bool Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible );
|
||||
void Term();
|
||||
|
||||
virtual void Release();
|
||||
|
||||
virtual void SetVisible( bool bVisible );
|
||||
virtual bool IsVisible() const;
|
||||
|
||||
virtual void PrintToConsole( const char *pMsg );
|
||||
virtual void SetTitle( const char *pTitle );
|
||||
|
||||
virtual void SetDeleteOnClose( bool bDelete );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
int WindowProc(
|
||||
HWND hwndDlg, // handle to dialog box
|
||||
UINT uMsg, // message
|
||||
WPARAM wParam, // first message parameter
|
||||
LPARAM lParam // second message parameter
|
||||
);
|
||||
|
||||
static int CALLBACK StaticWindowProc(
|
||||
HWND hwndDlg, // handle to dialog box
|
||||
UINT uMsg, // message
|
||||
WPARAM wParam, // first message parameter
|
||||
LPARAM lParam // second message parameter
|
||||
);
|
||||
|
||||
void RepositionEditControl();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
HWND m_hWnd;
|
||||
HWND m_hEditControl;
|
||||
bool m_bVisible;
|
||||
bool m_bDeleteOnClose;
|
||||
int m_nCurrentChars;
|
||||
};
|
||||
|
||||
|
||||
CConsoleWnd::CConsoleWnd()
|
||||
{
|
||||
m_hWnd = m_hEditControl = NULL;
|
||||
m_bVisible = false;
|
||||
m_bDeleteOnClose = false;
|
||||
m_nCurrentChars = 0;
|
||||
}
|
||||
|
||||
|
||||
CConsoleWnd::~CConsoleWnd()
|
||||
{
|
||||
Term();
|
||||
}
|
||||
|
||||
bool CConsoleWnd::Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible )
|
||||
{
|
||||
// Create the window.
|
||||
m_hWnd = CreateDialog(
|
||||
(HINSTANCE)hInstance,
|
||||
MAKEINTRESOURCE( dialogResourceID ),
|
||||
NULL,
|
||||
&CConsoleWnd::StaticWindowProc );
|
||||
|
||||
if ( !m_hWnd )
|
||||
return false;
|
||||
|
||||
SetWindowLong( m_hWnd, GWL_USERDATA, reinterpret_cast< LONG >( this ) );
|
||||
if ( bVisible )
|
||||
ShowWindow( m_hWnd, SW_SHOW );
|
||||
|
||||
// Get a handle to the edit control.
|
||||
m_hEditControl = GetDlgItem( m_hWnd, editControlID );
|
||||
if ( !m_hEditControl )
|
||||
return false;
|
||||
|
||||
RepositionEditControl();
|
||||
|
||||
m_bVisible = bVisible;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CConsoleWnd::Term()
|
||||
{
|
||||
if ( m_hWnd )
|
||||
{
|
||||
DestroyWindow( m_hWnd );
|
||||
m_hWnd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CConsoleWnd::Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
void CConsoleWnd::SetVisible( bool bVisible )
|
||||
{
|
||||
ShowWindow( m_hWnd, bVisible ? SW_RESTORE : SW_HIDE );
|
||||
|
||||
if ( bVisible )
|
||||
{
|
||||
ShowWindow( m_hWnd, SW_SHOW );
|
||||
SetWindowPos( m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
|
||||
UpdateWindow( m_hWnd );
|
||||
|
||||
int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 );
|
||||
SendMessage( m_hEditControl, EM_SETSEL, nLen, nLen );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowPos( m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_NOOWNERZORDER );
|
||||
}
|
||||
|
||||
m_bVisible = bVisible;
|
||||
}
|
||||
|
||||
|
||||
bool CConsoleWnd::IsVisible() const
|
||||
{
|
||||
return m_bVisible;
|
||||
}
|
||||
|
||||
|
||||
void CConsoleWnd::PrintToConsole( const char *pMsg )
|
||||
{
|
||||
if ( m_nCurrentChars >= 16*1024 )
|
||||
{
|
||||
// Clear the edit control otherwise it'll stop outputting anything.
|
||||
m_nCurrentChars = 0;
|
||||
|
||||
int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 );
|
||||
SendMessage( m_hEditControl, EM_SETSEL, 0, nLen );
|
||||
SendMessage( m_hEditControl, EM_REPLACESEL, FALSE, (LPARAM)"" );
|
||||
}
|
||||
|
||||
FormatAndSendToEditControl( m_hEditControl, pMsg );
|
||||
m_nCurrentChars += (int)strlen( pMsg );
|
||||
}
|
||||
|
||||
|
||||
void CConsoleWnd::SetTitle( const char *pTitle )
|
||||
{
|
||||
SetWindowText( m_hWnd, pTitle );
|
||||
}
|
||||
|
||||
|
||||
int CConsoleWnd::WindowProc(
|
||||
HWND hwndDlg, // handle to dialog box
|
||||
UINT uMsg, // message
|
||||
WPARAM wParam, // first message parameter
|
||||
LPARAM lParam // second message parameter
|
||||
)
|
||||
{
|
||||
lParam = lParam; // avoid compiler warning
|
||||
|
||||
if ( hwndDlg != m_hWnd )
|
||||
return false;
|
||||
|
||||
switch ( uMsg )
|
||||
{
|
||||
case WM_SYSCOMMAND:
|
||||
{
|
||||
if ( wParam == SC_CLOSE )
|
||||
{
|
||||
if ( m_bDeleteOnClose )
|
||||
{
|
||||
Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVisible( false );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SHOWWINDOW:
|
||||
{
|
||||
m_bVisible = (wParam != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SIZE:
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
RepositionEditControl();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int CConsoleWnd::StaticWindowProc(
|
||||
HWND hwndDlg, // handle to dialog box
|
||||
UINT uMsg, // message
|
||||
WPARAM wParam, // first message parameter
|
||||
LPARAM lParam // second message parameter
|
||||
)
|
||||
{
|
||||
CConsoleWnd *pDlg = (CConsoleWnd*)GetWindowLong( hwndDlg, GWL_USERDATA );
|
||||
if ( pDlg )
|
||||
return pDlg->WindowProc( hwndDlg, uMsg, wParam, lParam );
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CConsoleWnd::RepositionEditControl()
|
||||
{
|
||||
RECT rcMain;
|
||||
GetClientRect( m_hWnd, &rcMain );
|
||||
|
||||
RECT rcNew;
|
||||
rcNew.left = rcMain.left + EDITCONTROL_BORDER_SIZE;
|
||||
rcNew.right = rcMain.right - EDITCONTROL_BORDER_SIZE;
|
||||
rcNew.top = rcMain.top + EDITCONTROL_BORDER_SIZE;
|
||||
rcNew.bottom = rcMain.bottom - EDITCONTROL_BORDER_SIZE;
|
||||
|
||||
SetWindowPos(
|
||||
m_hEditControl,
|
||||
NULL,
|
||||
rcNew.left,
|
||||
rcNew.top,
|
||||
rcNew.right - rcNew.left,
|
||||
rcNew.bottom - rcNew.top,
|
||||
SWP_NOZORDER );
|
||||
}
|
||||
|
||||
|
||||
void CConsoleWnd::SetDeleteOnClose( bool bDelete )
|
||||
{
|
||||
m_bDeleteOnClose = bDelete;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------ //
|
||||
// Module interface.
|
||||
// ------------------------------------------------------------------------------------ //
|
||||
|
||||
void SendToEditControl( HWND hEditControl, const char *pText )
|
||||
{
|
||||
int nLen = (int)SendMessage( hEditControl, EM_GETLIMITTEXT, 0, 0 );
|
||||
SendMessage( hEditControl, EM_SETSEL, nLen, nLen );
|
||||
SendMessage( hEditControl, EM_REPLACESEL, FALSE, (LPARAM)pText );
|
||||
}
|
||||
|
||||
|
||||
void FormatAndSendToEditControl( void *hWnd, const char *pText )
|
||||
{
|
||||
HWND hEditControl = (HWND)hWnd;
|
||||
|
||||
// Translate \n to \r\n.
|
||||
char outMsg[1024];
|
||||
const char *pIn = pText;
|
||||
char *pOut = outMsg;
|
||||
while ( *pIn )
|
||||
{
|
||||
if ( *pIn == '\n' )
|
||||
{
|
||||
*pOut = '\r';
|
||||
pOut++;
|
||||
}
|
||||
*pOut = *pIn;
|
||||
|
||||
++pIn;
|
||||
++pOut;
|
||||
|
||||
if ( pOut - outMsg >= 1020 )
|
||||
{
|
||||
*pOut = 0;
|
||||
SendToEditControl( hEditControl, outMsg );
|
||||
pOut = outMsg;
|
||||
}
|
||||
}
|
||||
*pOut = 0;
|
||||
SendToEditControl( hEditControl, outMsg );
|
||||
}
|
||||
|
||||
|
||||
IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible )
|
||||
{
|
||||
CConsoleWnd *pWnd = new CConsoleWnd;
|
||||
|
||||
if ( pWnd->Init( hInstance, dialogResourceID, editControlID, bVisible ) )
|
||||
{
|
||||
return pWnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
pWnd->Release();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
45
utils/common/consolewnd.h
Normal file
45
utils/common/consolewnd.h
Normal file
@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef CONSOLEWND_H
|
||||
#define CONSOLEWND_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
class IConsoleWnd
|
||||
{
|
||||
public:
|
||||
virtual void Release() = 0;
|
||||
|
||||
// Print a message to the console.
|
||||
virtual void PrintToConsole( const char *pMsg ) = 0;
|
||||
|
||||
// Set the window title.
|
||||
virtual void SetTitle( const char *pTitle ) = 0;
|
||||
|
||||
// Show and hide the console window.
|
||||
virtual void SetVisible( bool bVisible ) = 0;
|
||||
virtual bool IsVisible() const = 0;
|
||||
|
||||
// Normally, the window just hides itself when closed. You can use this to make the window
|
||||
// automatically go away when they close it.
|
||||
virtual void SetDeleteOnClose( bool bDelete ) = 0;
|
||||
};
|
||||
|
||||
|
||||
// Utility functions.
|
||||
|
||||
// This converts adds \r's where necessary and sends the text to the edit control.
|
||||
void FormatAndSendToEditControl( void *hWnd, const char *pText );
|
||||
|
||||
|
||||
IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible );
|
||||
|
||||
|
||||
#endif // CONSOLEWND_H
|
209
utils/common/filesystem_tools.cpp
Normal file
209
utils/common/filesystem_tools.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#include <io.h> // _chmod
|
||||
#elif _LINUX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include "tier1/strtools.h"
|
||||
#include "filesystem_tools.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include "KeyValues.h"
|
||||
#include "tier2/tier2.h"
|
||||
|
||||
#ifdef MPI
|
||||
#include "vmpi.h"
|
||||
#include "vmpi_tools_shared.h"
|
||||
#include "vmpi_filesystem.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include <tier0/memdbgon.h>
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------- //
|
||||
// Module interface.
|
||||
// ---------------------------------------------------------------------------------------------------- //
|
||||
|
||||
IBaseFileSystem *g_pFileSystem = NULL;
|
||||
|
||||
// These are only used for tools that need the search paths that the engine's file system provides.
|
||||
CSysModule *g_pFullFileSystemModule = NULL;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// These are the base paths that everything will be referenced relative to (textures especially)
|
||||
// All of these directories include the trailing slash
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// This is the the path of the initial source file (relative to the cwd)
|
||||
char qdir[1024];
|
||||
|
||||
// This is the base engine + mod-specific game dir (e.g. "c:\tf2\mytfmod\")
|
||||
char gamedir[1024];
|
||||
|
||||
void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath )
|
||||
{
|
||||
// Set qdir.
|
||||
if ( !pFilename )
|
||||
{
|
||||
pFilename = ".";
|
||||
}
|
||||
|
||||
Q_MakeAbsolutePath( qdir, sizeof( qdir ), pFilename, NULL );
|
||||
Q_StripFilename( qdir );
|
||||
Q_strlower( qdir );
|
||||
if ( qdir[0] != 0 )
|
||||
{
|
||||
Q_AppendSlash( qdir, sizeof( qdir ) );
|
||||
}
|
||||
|
||||
// Set gamedir.
|
||||
Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), pGameInfoPath );
|
||||
Q_AppendSlash( gamedir, sizeof( gamedir ) );
|
||||
}
|
||||
|
||||
|
||||
bool FileSystem_Init_Normal( const char *pFilename, FSInitType_t initType, bool bOnlyUseDirectoryName )
|
||||
{
|
||||
if ( initType == FS_INIT_FULL )
|
||||
{
|
||||
// First, get the name of the module
|
||||
char fileSystemDLLName[MAX_PATH];
|
||||
bool bSteam;
|
||||
if ( FileSystem_GetFileSystemDLLName( fileSystemDLLName, MAX_PATH, bSteam ) != FS_OK )
|
||||
return false;
|
||||
|
||||
// If we're under Steam we need extra setup to let us find the proper modules
|
||||
FileSystem_SetupSteamInstallPath();
|
||||
|
||||
// Next, load the module, call Connect/Init.
|
||||
CFSLoadModuleInfo loadModuleInfo;
|
||||
loadModuleInfo.m_pFileSystemDLLName = fileSystemDLLName;
|
||||
loadModuleInfo.m_pDirectoryName = pFilename;
|
||||
loadModuleInfo.m_bOnlyUseDirectoryName = bOnlyUseDirectoryName;
|
||||
loadModuleInfo.m_ConnectFactory = Sys_GetFactoryThis();
|
||||
loadModuleInfo.m_bSteam = bSteam;
|
||||
loadModuleInfo.m_bToolsMode = true;
|
||||
if ( FileSystem_LoadFileSystemModule( loadModuleInfo ) != FS_OK )
|
||||
return false;
|
||||
|
||||
// Next, mount the content
|
||||
CFSMountContentInfo mountContentInfo;
|
||||
mountContentInfo.m_pDirectoryName= loadModuleInfo.m_GameInfoPath;
|
||||
mountContentInfo.m_pFileSystem = loadModuleInfo.m_pFileSystem;
|
||||
mountContentInfo.m_bToolsMode = true;
|
||||
if ( FileSystem_MountContent( mountContentInfo ) != FS_OK )
|
||||
return false;
|
||||
|
||||
// Finally, load the search paths.
|
||||
CFSSearchPathsInit searchPathsInit;
|
||||
searchPathsInit.m_pDirectoryName = loadModuleInfo.m_GameInfoPath;
|
||||
searchPathsInit.m_pFileSystem = loadModuleInfo.m_pFileSystem;
|
||||
if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
|
||||
return false;
|
||||
|
||||
// Store the data we got from filesystem_init.
|
||||
g_pFileSystem = g_pFullFileSystem = loadModuleInfo.m_pFileSystem;
|
||||
g_pFullFileSystemModule = loadModuleInfo.m_pModule;
|
||||
|
||||
FileSystem_AddSearchPath_Platform( g_pFullFileSystem, loadModuleInfo.m_GameInfoPath );
|
||||
|
||||
FileSystem_SetupStandardDirectories( pFilename, loadModuleInfo.m_GameInfoPath );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !Sys_LoadInterface(
|
||||
"filesystem_stdio",
|
||||
FILESYSTEM_INTERFACE_VERSION,
|
||||
&g_pFullFileSystemModule,
|
||||
(void**)&g_pFullFileSystem ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( g_pFullFileSystem->Init() != INIT_OK )
|
||||
return false;
|
||||
|
||||
g_pFullFileSystem->RemoveAllSearchPaths();
|
||||
g_pFullFileSystem->AddSearchPath( "../platform", "PLATFORM" );
|
||||
g_pFullFileSystem->AddSearchPath( ".", "GAME" );
|
||||
|
||||
g_pFileSystem = g_pFullFileSystem;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FileSystem_Init( const char *pBSPFilename, int maxMemoryUsage, FSInitType_t initType, bool bOnlyUseFilename )
|
||||
{
|
||||
Assert( CommandLine()->GetCmdLine() != NULL ); // Should have called CreateCmdLine by now.
|
||||
|
||||
// If this app uses VMPI, then let VMPI intercept all filesystem calls.
|
||||
#if defined( MPI )
|
||||
if ( g_bUseMPI )
|
||||
{
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
if ( !FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename ) )
|
||||
return false;
|
||||
|
||||
g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, g_pFullFileSystem );
|
||||
SendQDirInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, NULL );
|
||||
RecvQDirInfo();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename );
|
||||
}
|
||||
|
||||
|
||||
void FileSystem_Term()
|
||||
{
|
||||
#if defined( MPI )
|
||||
if ( g_bUseMPI )
|
||||
{
|
||||
g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Term();
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( g_pFullFileSystem )
|
||||
{
|
||||
g_pFullFileSystem->Shutdown();
|
||||
g_pFullFileSystem = NULL;
|
||||
g_pFileSystem = NULL;
|
||||
}
|
||||
|
||||
if ( g_pFullFileSystemModule )
|
||||
{
|
||||
Sys_UnloadModule( g_pFullFileSystemModule );
|
||||
g_pFullFileSystemModule = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CreateInterfaceFn FileSystem_GetFactory()
|
||||
{
|
||||
#if defined( MPI )
|
||||
if ( g_bUseMPI )
|
||||
return VMPI_FileSystem_GetFactory();
|
||||
#endif
|
||||
return Sys_GetFactory( g_pFullFileSystemModule );
|
||||
}
|
59
utils/common/filesystem_tools.h
Normal file
59
utils/common/filesystem_tools.h
Normal file
@ -0,0 +1,59 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef FILESYSTEM_TOOLS_H
|
||||
#define FILESYSTEM_TOOLS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "filesystem_init.h"
|
||||
|
||||
|
||||
// This is the the path of the initial source file
|
||||
extern char qdir[1024];
|
||||
|
||||
// This is the base engine + mod-specific game dir (e.g. "d:\tf2\mytfmod\")
|
||||
extern char gamedir[1024];
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------- //
|
||||
// Filesystem initialization.
|
||||
// ---------------------------------------------------------------------------------------- //
|
||||
|
||||
enum FSInitType_t
|
||||
{
|
||||
FS_INIT_FULL, // Load gameinfo.txt, maybe use filesystem_steam, and setup search paths.
|
||||
FS_INIT_COMPATIBILITY_MODE // Load filesystem_stdio and that's it.
|
||||
};
|
||||
|
||||
//
|
||||
// Initializes qdir, and gamedir. Also initializes the VMPI filesystem if MPI is defined.
|
||||
//
|
||||
// pFilename can be NULL if you want to rely on vproject and qproject. If it's specified, FileSystem_Init
|
||||
// will go up directories from pFilename looking for gameinfo.txt (if vproject isn't specified).
|
||||
//
|
||||
// If bOnlyUseFilename is true, then it won't use any alternative methods of finding the vproject dir
|
||||
// (ie: it won't use -game or -vproject or the vproject env var or qproject).
|
||||
//
|
||||
bool FileSystem_Init( const char *pFilename, int maxMemoryUsage=0, FSInitType_t initType=FS_INIT_FULL, bool bOnlyUseFilename=false );
|
||||
void FileSystem_Term();
|
||||
|
||||
// Used to connect app-framework based console apps to the filesystem tools
|
||||
void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath );
|
||||
|
||||
CreateInterfaceFn FileSystem_GetFactory( void );
|
||||
|
||||
|
||||
extern IBaseFileSystem *g_pFileSystem;
|
||||
extern IFileSystem *g_pFullFileSystem; // NOTE: this is here when VMPI is being used, but a VMPI app can
|
||||
// ONLY use LoadModule/UnloadModule.
|
||||
|
||||
|
||||
#endif // FILESYSTEM_TOOLS_H
|
136
utils/common/map_shared.cpp
Normal file
136
utils/common/map_shared.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "map_shared.h"
|
||||
#include "bsplib.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
|
||||
CMapError g_MapError;
|
||||
int g_nMapFileVersion;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *szKey -
|
||||
// *szValue -
|
||||
// *pLoadEntity -
|
||||
// Output : ChunkFileResult_t
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
|
||||
{
|
||||
if (!stricmp(szKey, "classname"))
|
||||
{
|
||||
if (!stricmp(szValue, "func_detail"))
|
||||
{
|
||||
pLoadEntity->nBaseContents = CONTENTS_DETAIL;
|
||||
}
|
||||
else if (!stricmp(szValue, "func_ladder"))
|
||||
{
|
||||
pLoadEntity->nBaseContents = CONTENTS_LADDER;
|
||||
}
|
||||
else if (!stricmp(szValue, "func_water"))
|
||||
{
|
||||
pLoadEntity->nBaseContents = CONTENTS_WATER;
|
||||
}
|
||||
}
|
||||
else if (!stricmp(szKey, "id"))
|
||||
{
|
||||
// UNDONE: flag entity errors by ID instead of index
|
||||
//g_MapError.EntityState( atoi( szValue ) );
|
||||
// rename this field since DME code uses this name
|
||||
SetKeyValue( pLoadEntity->pEntity, "hammerid", szValue );
|
||||
return(ChunkFile_Ok);
|
||||
}
|
||||
else if( !stricmp( szKey, "mapversion" ) )
|
||||
{
|
||||
// .vmf map revision number
|
||||
g_MapRevision = atoi( szValue );
|
||||
SetKeyValue( pLoadEntity->pEntity, szKey, szValue );
|
||||
return ( ChunkFile_Ok );
|
||||
}
|
||||
|
||||
SetKeyValue( pLoadEntity->pEntity, szKey, szValue );
|
||||
|
||||
return(ChunkFile_Ok);
|
||||
}
|
||||
|
||||
|
||||
static ChunkFileResult_t 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->numbrushes = 0;
|
||||
|
||||
LoadEntity_t LoadEntity;
|
||||
LoadEntity.pEntity = mapent;
|
||||
|
||||
// No default flags/contents
|
||||
LoadEntity.nBaseFlags = 0;
|
||||
LoadEntity.nBaseContents = 0;
|
||||
|
||||
//
|
||||
// Read the entity chunk.
|
||||
//
|
||||
ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity);
|
||||
|
||||
return eResult;
|
||||
}
|
||||
|
||||
|
||||
bool LoadEntsFromMapFile( char const *pFilename )
|
||||
{
|
||||
//
|
||||
// Dummy this up for the texture handling. This can be removed when old .MAP file
|
||||
// support is removed.
|
||||
//
|
||||
g_nMapFileVersion = 400;
|
||||
|
||||
//
|
||||
// Open the file.
|
||||
//
|
||||
CChunkFile File;
|
||||
ChunkFileResult_t eResult = File.Open( pFilename, ChunkFile_Read );
|
||||
|
||||
if(eResult == ChunkFile_Ok)
|
||||
{
|
||||
num_entities = 0;
|
||||
|
||||
//
|
||||
// Set up handlers for the subchunks that we are interested in.
|
||||
//
|
||||
CChunkHandlerMap Handlers;
|
||||
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();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error("Error in LoadEntsFromMapFile (in-memory file): %s.\n", File.GetErrorText(eResult));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
91
utils/common/map_shared.h
Normal file
91
utils/common/map_shared.h
Normal file
@ -0,0 +1,91 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MAP_SHARED_H
|
||||
#define MAP_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "ChunkFile.h"
|
||||
#include "bsplib.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
|
||||
struct LoadEntity_t
|
||||
{
|
||||
entity_t *pEntity;
|
||||
int nID;
|
||||
int nBaseFlags;
|
||||
int nBaseContents;
|
||||
};
|
||||
|
||||
|
||||
class CMapError
|
||||
{
|
||||
public:
|
||||
|
||||
void BrushState( int brushID )
|
||||
{
|
||||
m_brushID = brushID;
|
||||
}
|
||||
|
||||
void BrushSide( int side )
|
||||
{
|
||||
m_sideIndex = side;
|
||||
}
|
||||
|
||||
void TextureState( const char *pTextureName )
|
||||
{
|
||||
Q_strncpy( m_textureName, pTextureName, sizeof( m_textureName ) );
|
||||
}
|
||||
|
||||
void ClearState( void )
|
||||
{
|
||||
BrushState( 0 );
|
||||
BrushSide( 0 );
|
||||
TextureState( "Not a Parse error!" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Hook the map parse errors and report brush/ent/texture state
|
||||
// Input : *pErrorString -
|
||||
//-----------------------------------------------------------------------------
|
||||
void ReportError( const char *pErrorString )
|
||||
{
|
||||
Error( "Brush %i: %s\nSide %i\nTexture: %s\n", m_brushID, pErrorString, m_sideIndex, m_textureName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Hook the map parse errors and report brush/ent/texture state without exiting.
|
||||
// Input : pWarningString -
|
||||
//-----------------------------------------------------------------------------
|
||||
void ReportWarning( const char *pWarningString )
|
||||
{
|
||||
printf( "Brush %i, Side %i: %s\n", m_brushID, m_sideIndex, pWarningString );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int m_brushID;
|
||||
int m_sideIndex;
|
||||
char m_textureName[80];
|
||||
};
|
||||
|
||||
|
||||
extern CMapError g_MapError;
|
||||
extern int g_nMapFileVersion;
|
||||
|
||||
|
||||
// Shared mapload code.
|
||||
ChunkFileResult_t LoadEntityKeyCallback( const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity );
|
||||
|
||||
// Used by VRAD incremental lighting - only load ents from the file and
|
||||
// fill in the global entities/num_entities array.
|
||||
bool LoadEntsFromMapFile( char const *pFilename );
|
||||
|
||||
#endif // MAP_SHARED_H
|
34
utils/common/movie.h
Normal file
34
utils/common/movie.h
Normal file
@ -0,0 +1,34 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef _MOVIE_H_
|
||||
#define _MOVIE_H_
|
||||
|
||||
/*
|
||||
movie.h
|
||||
|
||||
definitions and such for dumping screen shots to make a movie
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long tag;
|
||||
unsigned long size;
|
||||
} movieblockheader_t;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short width;
|
||||
short height;
|
||||
short depth;
|
||||
} movieframe_t;
|
||||
|
||||
|
||||
|
||||
#endif _MOVIE_H_
|
839
utils/common/mpi_stats.cpp
Normal file
839
utils/common/mpi_stats.cpp
Normal file
@ -0,0 +1,839 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
// Nasty headers!
|
||||
#include "MySqlDatabase.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "vmpi.h"
|
||||
#include "vmpi_dispatch.h"
|
||||
#include "mpi_stats.h"
|
||||
#include "cmdlib.h"
|
||||
#include "imysqlwrapper.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "vmpi_tools_shared.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
/*
|
||||
|
||||
-- MySQL code to create the databases, create the users, and set access privileges.
|
||||
-- You only need to ever run this once.
|
||||
|
||||
create database vrad;
|
||||
|
||||
use mysql;
|
||||
|
||||
create user vrad_worker;
|
||||
create user vmpi_browser;
|
||||
|
||||
-- This updates the "user" table, which is checked when someone tries to connect to the database.
|
||||
grant select,insert,update on vrad.* to vrad_worker;
|
||||
grant select on vrad.* to vmpi_browser;
|
||||
flush privileges;
|
||||
|
||||
/*
|
||||
|
||||
-- SQL code to (re)create the tables.
|
||||
|
||||
-- Master generates a unique job ID (in job_master_start) and sends it to workers.
|
||||
-- Each worker (and the master) make a job_worker_start, link it to the primary job ID,
|
||||
-- get their own unique ID, which represents that process in that job.
|
||||
-- All JobWorkerID fields link to the JobWorkerID field in job_worker_start.
|
||||
|
||||
-- NOTE: do a "use vrad" or "use vvis" first, depending on the DB you want to create.
|
||||
|
||||
|
||||
use vrad;
|
||||
|
||||
|
||||
drop table job_master_start;
|
||||
create table job_master_start (
|
||||
JobID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, index id( JobID, MachineName(5) ),
|
||||
BSPFilename TINYTEXT NOT NULL,
|
||||
StartTime TIMESTAMP NOT NULL,
|
||||
MachineName TEXT NOT NULL,
|
||||
RunningTimeMS INTEGER UNSIGNED NOT NULL,
|
||||
NumWorkers INTEGER UNSIGNED NOT NULL default 0
|
||||
);
|
||||
|
||||
drop table job_master_end;
|
||||
create table job_master_end (
|
||||
JobID INTEGER UNSIGNED NOT NULL, PRIMARY KEY ( JobID ),
|
||||
NumWorkersConnected SMALLINT UNSIGNED NOT NULL,
|
||||
NumWorkersDisconnected SMALLINT UNSIGNED NOT NULL,
|
||||
ErrorText TEXT NOT NULL
|
||||
);
|
||||
|
||||
drop table job_worker_start;
|
||||
create table job_worker_start (
|
||||
JobWorkerID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
index index_jobid( JobID ),
|
||||
index index_jobworkerid( JobWorkerID ),
|
||||
|
||||
JobID INTEGER UNSIGNED NOT NULL, -- links to job_master_start::JobID
|
||||
IsMaster BOOL NOT NULL, -- Set to 1 if this "worker" is the master process.
|
||||
RunningTimeMS INTEGER UNSIGNED NOT NULL default 0,
|
||||
MachineName TEXT NOT NULL,
|
||||
WorkerState SMALLINT UNSIGNED NOT NULL default 0, -- 0 = disconnected, 1 = connected
|
||||
NumWorkUnits INTEGER UNSIGNED NOT NULL default 0, -- how many work units this worker has completed
|
||||
CurrentStage TINYTEXT NOT NULL, -- which compile stage is it on
|
||||
Thread0WU INTEGER NOT NULL default 0, -- which WU thread 0 is on
|
||||
Thread1WU INTEGER NOT NULL default 0, -- which WU thread 1 is on
|
||||
Thread2WU INTEGER NOT NULL default 0, -- which WU thread 2 is on
|
||||
Thread3WU INTEGER NOT NULL default 0 -- which WU thread 3 is on
|
||||
);
|
||||
|
||||
drop table text_messages;
|
||||
create table text_messages (
|
||||
JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID, MessageIndex ),
|
||||
MessageIndex INTEGER UNSIGNED NOT NULL,
|
||||
Text TEXT NOT NULL
|
||||
);
|
||||
|
||||
drop table graph_entry;
|
||||
create table graph_entry (
|
||||
JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ),
|
||||
MSSinceJobStart INTEGER UNSIGNED NOT NULL,
|
||||
BytesSent INTEGER UNSIGNED NOT NULL,
|
||||
BytesReceived INTEGER UNSIGNED NOT NULL
|
||||
);
|
||||
|
||||
drop table events;
|
||||
create table events (
|
||||
JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ),
|
||||
Text TEXT NOT NULL
|
||||
);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Stats set by the app.
|
||||
int g_nWorkersConnected = 0;
|
||||
int g_nWorkersDisconnected = 0;
|
||||
|
||||
|
||||
DWORD g_StatsStartTime;
|
||||
|
||||
CMySqlDatabase *g_pDB = NULL;
|
||||
|
||||
IMySQL *g_pSQL = NULL;
|
||||
CSysModule *g_hMySQLDLL = NULL;
|
||||
|
||||
char g_BSPFilename[256];
|
||||
|
||||
bool g_bMaster = false;
|
||||
unsigned long g_JobPrimaryID = 0; // This represents this job, but doesn't link to a particular machine.
|
||||
unsigned long g_JobWorkerID = 0; // A unique key in the DB that represents this machine in this job.
|
||||
char g_MachineName[MAX_COMPUTERNAME_LENGTH+1] = {0};
|
||||
|
||||
unsigned long g_CurrentMessageIndex = 0;
|
||||
|
||||
|
||||
HANDLE g_hPerfThread = NULL;
|
||||
DWORD g_PerfThreadID = 0xFEFEFEFE;
|
||||
HANDLE g_hPerfThreadExitEvent = NULL;
|
||||
|
||||
// These are set by the app and they go into the database.
|
||||
extern uint64 g_ThreadWUs[4];
|
||||
|
||||
extern uint64 VMPI_GetNumWorkUnitsCompleted( int iProc );
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------- //
|
||||
// This is a helper class to build queries like the stream IO.
|
||||
// ---------------------------------------------------------------------------------------------------- //
|
||||
|
||||
class CMySQLQuery
|
||||
{
|
||||
friend class CMySQL;
|
||||
|
||||
public:
|
||||
// This is like a sprintf, but it will grow the string as necessary.
|
||||
void Format( const char *pFormat, ... );
|
||||
|
||||
int Execute( IMySQL *pDB );
|
||||
|
||||
private:
|
||||
CUtlVector<char> m_QueryText;
|
||||
};
|
||||
|
||||
|
||||
void CMySQLQuery::Format( const char *pFormat, ... )
|
||||
{
|
||||
#define QUERYTEXT_GROWSIZE 1024
|
||||
|
||||
// This keeps growing the buffer and calling _vsnprintf until the buffer is
|
||||
// large enough to hold all the data.
|
||||
m_QueryText.SetSize( QUERYTEXT_GROWSIZE );
|
||||
while ( 1 )
|
||||
{
|
||||
va_list marker;
|
||||
va_start( marker, pFormat );
|
||||
int ret = _vsnprintf( m_QueryText.Base(), m_QueryText.Count(), pFormat, marker );
|
||||
va_end( marker );
|
||||
|
||||
if ( ret < 0 )
|
||||
{
|
||||
m_QueryText.SetSize( m_QueryText.Count() + QUERYTEXT_GROWSIZE );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_QueryText[ m_QueryText.Count() - 1 ] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CMySQLQuery::Execute( IMySQL *pDB )
|
||||
{
|
||||
int ret = pDB->Execute( m_QueryText.Base() );
|
||||
m_QueryText.Purge();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------- //
|
||||
// This inserts the necessary backslashes in front of backslashes or quote characters.
|
||||
// ---------------------------------------------------------------------------------------------------- //
|
||||
|
||||
char* FormatStringForSQL( const char *pText )
|
||||
{
|
||||
// First, count the quotes in the string. We need to put a backslash in front of each one.
|
||||
int nChars = 0;
|
||||
const char *pCur = pText;
|
||||
while ( *pCur != 0 )
|
||||
{
|
||||
if ( *pCur == '\"' || *pCur == '\\' )
|
||||
++nChars;
|
||||
|
||||
++pCur;
|
||||
++nChars;
|
||||
}
|
||||
|
||||
pCur = pText;
|
||||
char *pRetVal = new char[nChars+1];
|
||||
for ( int i=0; i < nChars; )
|
||||
{
|
||||
if ( *pCur == '\"' || *pCur == '\\' )
|
||||
pRetVal[i++] = '\\';
|
||||
|
||||
pRetVal[i++] = *pCur;
|
||||
++pCur;
|
||||
}
|
||||
pRetVal[nChars] = 0;
|
||||
|
||||
return pRetVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// Commands to add data to the database.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
class CSQLDBCommandBase : public ISQLDBCommand
|
||||
{
|
||||
public:
|
||||
virtual ~CSQLDBCommandBase()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void deleteThis()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
class CSQLDBCommand_WorkerStats : public CSQLDBCommandBase
|
||||
{
|
||||
public:
|
||||
virtual int RunCommand()
|
||||
{
|
||||
int nCurConnections = VMPI_GetCurrentNumberOfConnections();
|
||||
|
||||
|
||||
// Update the NumWorkers entry.
|
||||
char query[2048];
|
||||
Q_snprintf( query, sizeof( query ), "update job_master_start set NumWorkers=%d where JobID=%lu",
|
||||
nCurConnections,
|
||||
g_JobPrimaryID );
|
||||
g_pSQL->Execute( query );
|
||||
|
||||
|
||||
// Update the job_master_worker_stats stuff.
|
||||
for ( int i=1; i < nCurConnections; i++ )
|
||||
{
|
||||
unsigned long jobWorkerID = VMPI_GetJobWorkerID( i );
|
||||
|
||||
if ( jobWorkerID != 0xFFFFFFFF )
|
||||
{
|
||||
Q_snprintf( query, sizeof( query ), "update "
|
||||
"job_worker_start set WorkerState=%d, NumWorkUnits=%d where JobWorkerID=%lu",
|
||||
VMPI_IsProcConnected( i ),
|
||||
(int) VMPI_GetNumWorkUnitsCompleted( i ),
|
||||
VMPI_GetJobWorkerID( i )
|
||||
);
|
||||
g_pSQL->Execute( query );
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
class CSQLDBCommand_JobMasterEnd : public CSQLDBCommandBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual int RunCommand()
|
||||
{
|
||||
CMySQLQuery query;
|
||||
query.Format( "insert into job_master_end values ( %lu, %d, %d, \"no errors\" )", g_JobPrimaryID, g_nWorkersConnected, g_nWorkersDisconnected );
|
||||
query.Execute( g_pSQL );
|
||||
|
||||
// Now set RunningTimeMS.
|
||||
unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime;
|
||||
query.Format( "update job_master_start set RunningTimeMS=%lu where JobID=%lu", runningTimeMS, g_JobPrimaryID );
|
||||
query.Execute( g_pSQL );
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void UpdateJobWorkerRunningTime()
|
||||
{
|
||||
unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime;
|
||||
|
||||
char curStage[256];
|
||||
VMPI_GetCurrentStage( curStage, sizeof( curStage ) );
|
||||
|
||||
CMySQLQuery query;
|
||||
query.Format( "update job_worker_start set RunningTimeMS=%lu, CurrentStage=\"%s\", "
|
||||
"Thread0WU=%d, Thread1WU=%d, Thread2WU=%d, Thread3WU=%d where JobWorkerID=%lu",
|
||||
runningTimeMS,
|
||||
curStage,
|
||||
(int) g_ThreadWUs[0],
|
||||
(int) g_ThreadWUs[1],
|
||||
(int) g_ThreadWUs[2],
|
||||
(int) g_ThreadWUs[3],
|
||||
g_JobWorkerID );
|
||||
query.Execute( g_pSQL );
|
||||
}
|
||||
|
||||
|
||||
class CSQLDBCommand_GraphEntry : public CSQLDBCommandBase
|
||||
{
|
||||
public:
|
||||
|
||||
CSQLDBCommand_GraphEntry( DWORD msTime, DWORD nBytesSent, DWORD nBytesReceived )
|
||||
{
|
||||
m_msTime = msTime;
|
||||
m_nBytesSent = nBytesSent;
|
||||
m_nBytesReceived = nBytesReceived;
|
||||
}
|
||||
|
||||
virtual int RunCommand()
|
||||
{
|
||||
CMySQLQuery query;
|
||||
query.Format( "insert into graph_entry (JobWorkerID, MSSinceJobStart, BytesSent, BytesReceived) "
|
||||
"values ( %lu, %lu, %lu, %lu )",
|
||||
g_JobWorkerID,
|
||||
m_msTime,
|
||||
m_nBytesSent,
|
||||
m_nBytesReceived );
|
||||
|
||||
query.Execute( g_pSQL );
|
||||
|
||||
UpdateJobWorkerRunningTime();
|
||||
|
||||
++g_CurrentMessageIndex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD m_nBytesSent;
|
||||
DWORD m_nBytesReceived;
|
||||
DWORD m_msTime;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CSQLDBCommand_TextMessage : public CSQLDBCommandBase
|
||||
{
|
||||
public:
|
||||
|
||||
CSQLDBCommand_TextMessage( const char *pText )
|
||||
{
|
||||
m_pText = FormatStringForSQL( pText );
|
||||
}
|
||||
|
||||
virtual ~CSQLDBCommand_TextMessage()
|
||||
{
|
||||
delete [] m_pText;
|
||||
}
|
||||
|
||||
virtual int RunCommand()
|
||||
{
|
||||
CMySQLQuery query;
|
||||
query.Format( "insert into text_messages (JobWorkerID, MessageIndex, Text) values ( %lu, %lu, \"%s\" )", g_JobWorkerID, g_CurrentMessageIndex, m_pText );
|
||||
query.Execute( g_pSQL );
|
||||
|
||||
++g_CurrentMessageIndex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *m_pText;
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// Internal helpers.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
// This is the spew output before it has connected to the MySQL database.
|
||||
CCriticalSection g_SpewTextCS;
|
||||
CUtlVector<char> g_SpewText( 1024 );
|
||||
|
||||
|
||||
void VMPI_Stats_SpewHook( const char *pMsg )
|
||||
{
|
||||
CCriticalSectionLock csLock( &g_SpewTextCS );
|
||||
csLock.Lock();
|
||||
|
||||
// Queue the text up so we can send it to the DB right away when we connect.
|
||||
g_SpewText.AddMultipleToTail( strlen( pMsg ), pMsg );
|
||||
}
|
||||
|
||||
|
||||
void PerfThread_SendSpewText()
|
||||
{
|
||||
// Send the spew text to the database.
|
||||
CCriticalSectionLock csLock( &g_SpewTextCS );
|
||||
csLock.Lock();
|
||||
|
||||
if ( g_SpewText.Count() > 0 )
|
||||
{
|
||||
g_SpewText.AddToTail( 0 );
|
||||
|
||||
if ( g_bMPI_StatsTextOutput )
|
||||
{
|
||||
g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( g_SpewText.Base() ), NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just show one message in the vmpi_job_watch window to let them know that they need
|
||||
// to use a command line option to get the output.
|
||||
static bool bFirst = true;
|
||||
if ( bFirst )
|
||||
{
|
||||
char msg[512];
|
||||
V_snprintf( msg, sizeof( msg ), "%s not enabled", VMPI_GetParamString( mpi_Stats_TextOutput ) );
|
||||
bFirst = false;
|
||||
g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( msg ), NULL );
|
||||
}
|
||||
}
|
||||
|
||||
g_SpewText.RemoveAll();
|
||||
}
|
||||
|
||||
csLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void PerfThread_AddGraphEntry( DWORD startTicks, DWORD &lastSent, DWORD &lastReceived )
|
||||
{
|
||||
// Send the graph entry with data transmission info.
|
||||
DWORD curSent = g_nBytesSent + g_nMulticastBytesSent;
|
||||
DWORD curReceived = g_nBytesReceived + g_nMulticastBytesReceived;
|
||||
|
||||
g_pDB->AddCommandToQueue(
|
||||
new CSQLDBCommand_GraphEntry(
|
||||
GetTickCount() - startTicks,
|
||||
curSent - lastSent,
|
||||
curReceived - lastReceived ),
|
||||
NULL );
|
||||
|
||||
lastSent = curSent;
|
||||
lastReceived = curReceived;
|
||||
}
|
||||
|
||||
|
||||
// This function adds a graph_entry into the database periodically.
|
||||
DWORD WINAPI PerfThreadFn( LPVOID pParameter )
|
||||
{
|
||||
DWORD lastSent = 0;
|
||||
DWORD lastReceived = 0;
|
||||
DWORD startTicks = GetTickCount();
|
||||
|
||||
while ( WaitForSingleObject( g_hPerfThreadExitEvent, 1000 ) != WAIT_OBJECT_0 )
|
||||
{
|
||||
PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived );
|
||||
|
||||
// Send updates for text output.
|
||||
PerfThread_SendSpewText();
|
||||
|
||||
// If we're the master, update all the worker stats.
|
||||
if ( g_bMaster )
|
||||
{
|
||||
g_pDB->AddCommandToQueue(
|
||||
new CSQLDBCommand_WorkerStats,
|
||||
NULL );
|
||||
}
|
||||
}
|
||||
|
||||
// Add the remaining text and one last graph entry (which will include the current stage info).
|
||||
PerfThread_SendSpewText();
|
||||
PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived );
|
||||
|
||||
SetEvent( g_hPerfThreadExitEvent );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// VMPI_Stats interface.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
void VMPI_Stats_InstallSpewHook()
|
||||
{
|
||||
InstallExtraSpewHook( VMPI_Stats_SpewHook );
|
||||
}
|
||||
|
||||
|
||||
void UnloadMySQLWrapper()
|
||||
{
|
||||
if ( g_hMySQLDLL )
|
||||
{
|
||||
if ( g_pSQL )
|
||||
{
|
||||
g_pSQL->Release();
|
||||
g_pSQL = NULL;
|
||||
}
|
||||
|
||||
Sys_UnloadModule( g_hMySQLDLL );
|
||||
g_hMySQLDLL = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LoadMySQLWrapper(
|
||||
const char *pHostName,
|
||||
const char *pDBName,
|
||||
const char *pUserName
|
||||
)
|
||||
{
|
||||
UnloadMySQLWrapper();
|
||||
|
||||
// Load the DLL and the interface.
|
||||
if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &g_hMySQLDLL, (void**)&g_pSQL ) )
|
||||
return false;
|
||||
|
||||
// Try to init the database.
|
||||
if ( !g_pSQL->InitMySQL( pDBName, pHostName, pUserName ) )
|
||||
{
|
||||
UnloadMySQLWrapper();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool VMPI_Stats_Init_Master(
|
||||
const char *pHostName,
|
||||
const char *pDBName,
|
||||
const char *pUserName,
|
||||
const char *pBSPFilename,
|
||||
unsigned long *pDBJobID )
|
||||
{
|
||||
Assert( !g_pDB );
|
||||
|
||||
g_bMaster = true;
|
||||
|
||||
// Connect the database.
|
||||
g_pDB = new CMySqlDatabase;
|
||||
if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) )
|
||||
{
|
||||
delete g_pDB;
|
||||
g_pDB = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD size = sizeof( g_MachineName );
|
||||
GetComputerName( g_MachineName, &size );
|
||||
|
||||
// Create the job_master_start row.
|
||||
Q_FileBase( pBSPFilename, g_BSPFilename, sizeof( g_BSPFilename ) );
|
||||
|
||||
g_JobPrimaryID = 0;
|
||||
CMySQLQuery query;
|
||||
query.Format( "insert into job_master_start ( BSPFilename, StartTime, MachineName, RunningTimeMS ) values ( \"%s\", null, \"%s\", %lu )", g_BSPFilename, g_MachineName, RUNNINGTIME_MS_SENTINEL );
|
||||
query.Execute( g_pSQL );
|
||||
|
||||
g_JobPrimaryID = g_pSQL->InsertID();
|
||||
if ( g_JobPrimaryID == 0 )
|
||||
{
|
||||
delete g_pDB;
|
||||
g_pDB = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Now init the worker portion.
|
||||
*pDBJobID = g_JobPrimaryID;
|
||||
return VMPI_Stats_Init_Worker( NULL, NULL, NULL, g_JobPrimaryID );
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID )
|
||||
{
|
||||
g_StatsStartTime = GetTickCount();
|
||||
|
||||
// If pDBServerName is null, then we're the master and we just want to make the job_worker_start entry.
|
||||
if ( pHostName )
|
||||
{
|
||||
Assert( !g_pDB );
|
||||
|
||||
// Connect the database.
|
||||
g_pDB = new CMySqlDatabase;
|
||||
if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) )
|
||||
{
|
||||
delete g_pDB;
|
||||
g_pDB = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get our machine name to store in the database.
|
||||
DWORD size = sizeof( g_MachineName );
|
||||
GetComputerName( g_MachineName, &size );
|
||||
}
|
||||
|
||||
|
||||
g_JobPrimaryID = DBJobID;
|
||||
g_JobWorkerID = 0;
|
||||
|
||||
CMySQLQuery query;
|
||||
query.Format( "insert into job_worker_start ( JobID, CurrentStage, IsMaster, MachineName ) values ( %lu, \"none\", %d, \"%s\" )",
|
||||
g_JobPrimaryID, g_bMaster, g_MachineName );
|
||||
query.Execute( g_pSQL );
|
||||
|
||||
g_JobWorkerID = g_pSQL->InsertID();
|
||||
if ( g_JobWorkerID == 0 )
|
||||
{
|
||||
delete g_pDB;
|
||||
g_pDB = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now create a thread that samples perf data and stores it in the database.
|
||||
g_hPerfThreadExitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
g_hPerfThread = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
PerfThreadFn,
|
||||
NULL,
|
||||
0,
|
||||
&g_PerfThreadID );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void VMPI_Stats_Term()
|
||||
{
|
||||
if ( !g_pDB )
|
||||
return;
|
||||
|
||||
// Stop the thread.
|
||||
SetEvent( g_hPerfThreadExitEvent );
|
||||
WaitForSingleObject( g_hPerfThread, INFINITE );
|
||||
|
||||
CloseHandle( g_hPerfThreadExitEvent );
|
||||
g_hPerfThreadExitEvent = NULL;
|
||||
|
||||
CloseHandle( g_hPerfThread );
|
||||
g_hPerfThread = NULL;
|
||||
|
||||
if ( g_bMaster )
|
||||
{
|
||||
// (Write a job_master_end entry here).
|
||||
g_pDB->AddCommandToQueue( new CSQLDBCommand_JobMasterEnd, NULL );
|
||||
}
|
||||
|
||||
// Wait for up to a second for the DB to finish writing its data.
|
||||
DWORD startTime = GetTickCount();
|
||||
while ( GetTickCount() - startTime < 1000 )
|
||||
{
|
||||
if ( g_pDB->QueriesInOutQueue() == 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
delete g_pDB;
|
||||
g_pDB = NULL;
|
||||
|
||||
UnloadMySQLWrapper();
|
||||
}
|
||||
|
||||
|
||||
static bool ReadStringFromFile( FILE *fp, char *pStr, int strSize )
|
||||
{
|
||||
int i=0;
|
||||
for ( i; i < strSize-2; i++ )
|
||||
{
|
||||
if ( fread( &pStr[i], 1, 1, fp ) != 1 ||
|
||||
pStr[i] == '\n' )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pStr[i] = 0;
|
||||
return i != 0;
|
||||
}
|
||||
|
||||
|
||||
// This looks for pDBInfoFilename in the same path as pBaseExeFilename.
|
||||
// The file has 3 lines: machine name (with database), database name, username
|
||||
void GetDBInfo( const char *pDBInfoFilename, CDBInfo *pInfo )
|
||||
{
|
||||
char baseExeFilename[512];
|
||||
if ( !GetModuleFileName( GetModuleHandle( NULL ), baseExeFilename, sizeof( baseExeFilename ) ) )
|
||||
Error( "GetModuleFileName failed." );
|
||||
|
||||
// Look for the info file in the same directory as the exe.
|
||||
char dbInfoFilename[512];
|
||||
Q_strncpy( dbInfoFilename, baseExeFilename, sizeof( dbInfoFilename ) );
|
||||
Q_StripFilename( dbInfoFilename );
|
||||
|
||||
if ( dbInfoFilename[0] == 0 )
|
||||
Q_strncpy( dbInfoFilename, ".", sizeof( dbInfoFilename ) );
|
||||
|
||||
Q_strncat( dbInfoFilename, "/", sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS );
|
||||
Q_strncat( dbInfoFilename, pDBInfoFilename, sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS );
|
||||
|
||||
FILE *fp = fopen( dbInfoFilename, "rt" );
|
||||
if ( !fp )
|
||||
{
|
||||
Error( "Can't open %s for database info.\n", dbInfoFilename );
|
||||
}
|
||||
|
||||
if ( !ReadStringFromFile( fp, pInfo->m_HostName, sizeof( pInfo->m_HostName ) ) ||
|
||||
!ReadStringFromFile( fp, pInfo->m_DBName, sizeof( pInfo->m_DBName ) ) ||
|
||||
!ReadStringFromFile( fp, pInfo->m_UserName, sizeof( pInfo->m_UserName ) )
|
||||
)
|
||||
{
|
||||
Error( "%s is not a valid database info file.\n", dbInfoFilename );
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
}
|
||||
|
||||
|
||||
void RunJobWatchApp( char *pCmdLine )
|
||||
{
|
||||
STARTUPINFO si;
|
||||
memset( &si, 0, sizeof( si ) );
|
||||
si.cb = sizeof( si );
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
memset( &pi, 0, sizeof( pi ) );
|
||||
|
||||
// Working directory should be the same as our exe's directory.
|
||||
char dirName[512];
|
||||
if ( GetModuleFileName( NULL, dirName, sizeof( dirName ) ) != 0 )
|
||||
{
|
||||
char *s1 = V_strrchr( dirName, '\\' );
|
||||
char *s2 = V_strrchr( dirName, '/' );
|
||||
if ( s1 || s2 )
|
||||
{
|
||||
// Get rid of the last slash.
|
||||
s1 = max( s1, s2 );
|
||||
s1[0] = 0;
|
||||
|
||||
if ( !CreateProcess(
|
||||
NULL,
|
||||
pCmdLine,
|
||||
NULL, // security
|
||||
NULL,
|
||||
TRUE,
|
||||
0, // flags
|
||||
NULL, // environment
|
||||
dirName, // current directory
|
||||
&si,
|
||||
&pi ) )
|
||||
{
|
||||
Warning( "%s - error launching '%s'\n", VMPI_GetParamString( mpi_Job_Watch ), pCmdLine );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StatsDB_InitStatsDatabase(
|
||||
int argc,
|
||||
char **argv,
|
||||
const char *pDBInfoFilename )
|
||||
{
|
||||
// Did they disable the stats database?
|
||||
if ( !g_bMPI_Stats && !VMPI_IsParamUsed( mpi_Job_Watch ) )
|
||||
return;
|
||||
|
||||
unsigned long jobPrimaryID;
|
||||
|
||||
// Now open the DB.
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
CDBInfo dbInfo;
|
||||
GetDBInfo( pDBInfoFilename, &dbInfo );
|
||||
|
||||
if ( !VMPI_Stats_Init_Master( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, argv[argc-1], &jobPrimaryID ) )
|
||||
{
|
||||
Warning( "VMPI_Stats_Init_Master( %s, %s, %s ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName );
|
||||
|
||||
// Tell the workers not to use stats.
|
||||
dbInfo.m_HostName[0] = 0;
|
||||
}
|
||||
|
||||
char cmdLine[2048];
|
||||
Q_snprintf( cmdLine, sizeof( cmdLine ), "vmpi_job_watch -JobID %d", jobPrimaryID );
|
||||
|
||||
Msg( "\nTo watch this job, run this command line:\n%s\n\n", cmdLine );
|
||||
|
||||
if ( VMPI_IsParamUsed( mpi_Job_Watch ) )
|
||||
{
|
||||
// Convenience thing to automatically launch the job watch for this job.
|
||||
RunJobWatchApp( cmdLine );
|
||||
}
|
||||
|
||||
// Send the database info to all the workers.
|
||||
SendDBInfo( &dbInfo, jobPrimaryID );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait to get DB info so we can connect to the MySQL database.
|
||||
CDBInfo dbInfo;
|
||||
unsigned long jobPrimaryID;
|
||||
RecvDBInfo( &dbInfo, &jobPrimaryID );
|
||||
|
||||
if ( dbInfo.m_HostName[0] != 0 )
|
||||
{
|
||||
if ( !VMPI_Stats_Init_Worker( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID ) )
|
||||
Error( "VMPI_Stats_Init_Worker( %s, %s, %s, %d ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned long StatsDB_GetUniqueJobID()
|
||||
{
|
||||
return g_JobPrimaryID;
|
||||
}
|
||||
|
||||
|
||||
unsigned long VMPI_Stats_GetJobWorkerID()
|
||||
{
|
||||
return g_JobWorkerID;
|
||||
}
|
59
utils/common/mpi_stats.h
Normal file
59
utils/common/mpi_stats.h
Normal file
@ -0,0 +1,59 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MPI_STATS_H
|
||||
#define MPI_STATS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// The VMPI stats module reports a bunch of statistics to a MySQL server, and the
|
||||
// stats can be used to trace and graph a compile session.
|
||||
//
|
||||
|
||||
// Call this as soon as possible to initialize spew hooks.
|
||||
void VMPI_Stats_InstallSpewHook();
|
||||
|
||||
//
|
||||
// pDBServerName is the hostname (or dotted IP address) of the MySQL server to connect to.
|
||||
// pBSPFilename is the last argument on the command line.
|
||||
// pMachineIP is the dotted IP address of this machine.
|
||||
// jobID is an 8-byte unique identifier for this job.
|
||||
//
|
||||
bool VMPI_Stats_Init_Master( const char *pHostName, const char *pDBName, const char *pUserName, const char *pBSPFilename, unsigned long *pDBJobID );
|
||||
bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID );
|
||||
void VMPI_Stats_Term();
|
||||
|
||||
// Add a generic text event to the database.
|
||||
void VMPI_Stats_AddEventText( const char *pText );
|
||||
|
||||
class CDBInfo
|
||||
{
|
||||
public:
|
||||
char m_HostName[128];
|
||||
char m_DBName[128];
|
||||
char m_UserName[128];
|
||||
};
|
||||
|
||||
// If you're the master, this loads pDBInfoFilename, sends that info to the workers, and
|
||||
// connects to the database.
|
||||
//
|
||||
// If you're a worker, this waits for the DB info, then connects to the database.
|
||||
void StatsDB_InitStatsDatabase(
|
||||
int argc,
|
||||
char **argv,
|
||||
const char *pDBInfoFilename );
|
||||
|
||||
// The database gives back a unique ID for the job.
|
||||
unsigned long StatsDB_GetUniqueJobID();
|
||||
|
||||
// Get the worker ID (used for the JobWorkerID fields in the database).
|
||||
unsigned long VMPI_Stats_GetJobWorkerID();
|
||||
|
||||
|
||||
#endif // MPI_STATS_H
|
930
utils/common/mstristrip.cpp
Normal file
930
utils/common/mstristrip.cpp
Normal file
@ -0,0 +1,930 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//-----------------------------------------------------------------------------
|
||||
// FILE: TRISTRIP.CPP
|
||||
//
|
||||
// Desc: Xbox tristripper
|
||||
//
|
||||
// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// identifier was truncated to '255' characters in the debug information
|
||||
#pragma warning(disable: 4786)
|
||||
// conversion from 'double' to 'float'
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4530)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include <assert.h>
|
||||
#ifdef _DEBUG
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
#include "mstristrip.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
//=========================================================================
|
||||
// structs
|
||||
//=========================================================================
|
||||
typedef vector<WORD> STRIPVERTS;
|
||||
typedef list<STRIPVERTS *> STRIPLIST;
|
||||
typedef WORD (*TRIANGLELIST)[3];
|
||||
|
||||
struct TRIANGLEINFO
|
||||
{
|
||||
int neighbortri[3];
|
||||
int neighboredge[3];
|
||||
};
|
||||
|
||||
// return true if strip starts clockwise
|
||||
inline bool FIsStripCW(const STRIPVERTS &stripvertices)
|
||||
{
|
||||
// last index should have cw/ccw bool
|
||||
return !!stripvertices[stripvertices.size() - 1];
|
||||
}
|
||||
|
||||
// return length of strip
|
||||
inline int StripLen(const STRIPVERTS &stripvertices)
|
||||
{
|
||||
return (int)stripvertices.size() - 1;
|
||||
}
|
||||
|
||||
// free all stripverts and clear the striplist
|
||||
inline void FreeStripListVerts(STRIPLIST *pstriplist)
|
||||
{
|
||||
STRIPLIST::iterator istriplist = pstriplist->begin();
|
||||
while(istriplist != pstriplist->end())
|
||||
{
|
||||
STRIPVERTS *pstripverts = *istriplist;
|
||||
delete pstripverts;
|
||||
pstriplist->erase(istriplist++);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// main stripper class
|
||||
//=========================================================================
|
||||
class CStripper
|
||||
{
|
||||
public:
|
||||
// ctors/dtors
|
||||
CStripper(int numtris, TRIANGLELIST ptriangles);
|
||||
~CStripper();
|
||||
|
||||
// initialize tri info
|
||||
void InitTriangleInfo(int tri, int vert);
|
||||
|
||||
// get maximum length strip from tri/vert
|
||||
int CreateStrip(int tri, int vert, int maxlen, int *pswaps,
|
||||
bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts);
|
||||
|
||||
// stripify entire mesh
|
||||
void BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead);
|
||||
|
||||
// blast strip indices to ppstripindices
|
||||
int CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices);
|
||||
int CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices);
|
||||
|
||||
inline int GetNeighborCount(int tri)
|
||||
{
|
||||
int count = 0;
|
||||
for(int vert = 0; vert < 3; vert++)
|
||||
{
|
||||
int neighbortri = m_ptriinfo[tri].neighbortri[vert];
|
||||
count += (neighbortri != -1) && !m_pused[neighbortri];
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// from callee
|
||||
int m_numtris; // # tris
|
||||
TRIANGLELIST m_ptriangles; // trilist
|
||||
|
||||
TRIANGLEINFO *m_ptriinfo; // tri edge, neighbor info
|
||||
int *m_pused; // tri used flag
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// vertex cache class
|
||||
//=========================================================================
|
||||
class CVertCache
|
||||
{
|
||||
public:
|
||||
CVertCache()
|
||||
{ Reset(); }
|
||||
~CVertCache()
|
||||
{};
|
||||
|
||||
// reset cache
|
||||
void Reset()
|
||||
{
|
||||
m_iCachePtr = 0;
|
||||
m_cachehits = 0;
|
||||
memset(m_rgCache, 0xff, sizeof(m_rgCache));
|
||||
}
|
||||
|
||||
// add vertindex to cache
|
||||
bool Add(int strip, int vertindex);
|
||||
|
||||
int NumCacheHits() const
|
||||
{ return m_cachehits; }
|
||||
|
||||
// enum { CACHE_SIZE = 10 };
|
||||
enum { CACHE_SIZE = 18 };
|
||||
|
||||
private:
|
||||
int m_cachehits; // current # of cache hits
|
||||
WORD m_rgCache[CACHE_SIZE]; // vertex cache
|
||||
int m_rgCacheStrip[CACHE_SIZE]; // strip # which added vert
|
||||
int m_iCachePtr; // fifo ptr
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// Get maximum length of strip starting at tri/vert
|
||||
//=========================================================================
|
||||
int CStripper::CreateStrip(int tri, int vert, int maxlen, int *pswaps,
|
||||
bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts)
|
||||
{
|
||||
*pswaps = 0;
|
||||
|
||||
// this guy has already been used?
|
||||
if(m_pused[tri])
|
||||
return 0;
|
||||
|
||||
// mark tri as used
|
||||
m_pused[tri] = 1;
|
||||
|
||||
int swaps = 0;
|
||||
|
||||
// add first tri info
|
||||
pstriptris[0] = tri;
|
||||
pstriptris[1] = tri;
|
||||
pstriptris[2] = tri;
|
||||
|
||||
if(fstartcw)
|
||||
{
|
||||
pstripverts[0] = (vert) % 3;
|
||||
pstripverts[1] = (vert + 1) % 3;
|
||||
pstripverts[2] = (vert + 2) % 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
pstripverts[0] = (vert + 1) % 3;
|
||||
pstripverts[1] = (vert + 0) % 3;
|
||||
pstripverts[2] = (vert + 2) % 3;
|
||||
}
|
||||
fstartcw = !fstartcw;
|
||||
|
||||
// get next tri information
|
||||
int edge = (fstartcw ? vert + 2 : vert + 1) % 3;
|
||||
int nexttri = m_ptriinfo[tri].neighbortri[edge];
|
||||
int nextvert = m_ptriinfo[tri].neighboredge[edge];
|
||||
|
||||
// start building the strip until we run out of room or indices
|
||||
int stripcount;
|
||||
for( stripcount = 3; stripcount < maxlen; stripcount++)
|
||||
{
|
||||
// dead end?
|
||||
if(nexttri == -1 || m_pused[nexttri])
|
||||
break;
|
||||
|
||||
// move to next tri
|
||||
tri = nexttri;
|
||||
vert = nextvert;
|
||||
|
||||
// toggle orientation
|
||||
fstartcw = !fstartcw;
|
||||
|
||||
// find the next natural edge
|
||||
int edge = (fstartcw ? vert + 2 : vert + 1) % 3;
|
||||
nexttri = m_ptriinfo[tri].neighbortri[edge];
|
||||
nextvert = m_ptriinfo[tri].neighboredge[edge];
|
||||
|
||||
bool fswap = false;
|
||||
if(nexttri == -1 || m_pused[nexttri])
|
||||
{
|
||||
// if the next tri is a dead end - try swapping orientation
|
||||
fswap = true;
|
||||
}
|
||||
else if(flookahead)
|
||||
{
|
||||
// try a swap and see who our new neighbor would be
|
||||
int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3;
|
||||
int nexttriswap = m_ptriinfo[tri].neighbortri[edgeswap];
|
||||
int nextvertswap = m_ptriinfo[tri].neighboredge[edgeswap];
|
||||
|
||||
if(nexttriswap != -1 && !m_pused[nexttriswap])
|
||||
{
|
||||
assert(nexttri != -1);
|
||||
|
||||
// if the swap neighbor has a lower count, change directions
|
||||
if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri))
|
||||
{
|
||||
fswap = true;
|
||||
}
|
||||
else if(GetNeighborCount(nexttriswap) == GetNeighborCount(nexttri))
|
||||
{
|
||||
// if they have the same number of neighbors - check their neighbors
|
||||
edgeswap = (fstartcw ? nextvertswap + 2 : nextvertswap + 1) % 3;
|
||||
nexttriswap = m_ptriinfo[nexttriswap].neighbortri[edgeswap];
|
||||
|
||||
int edge1 = (fstartcw ? nextvert + 1 : nextvert + 2) % 3;
|
||||
int nexttri1 = m_ptriinfo[nexttri].neighbortri[edge1];
|
||||
|
||||
if(nexttri1 == -1 || m_pused[nexttri1])
|
||||
{
|
||||
// natural winding order leads us to a dead end so turn
|
||||
fswap = true;
|
||||
}
|
||||
else if(nexttriswap != -1 && !m_pused[nexttriswap])
|
||||
{
|
||||
// check neighbor counts on both directions and swap if it's better
|
||||
if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri1))
|
||||
fswap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(fswap)
|
||||
{
|
||||
// we've been told to change directions so make sure we actually can
|
||||
// and then add the swap vertex
|
||||
int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3;
|
||||
nexttri = m_ptriinfo[tri].neighbortri[edgeswap];
|
||||
nextvert = m_ptriinfo[tri].neighboredge[edgeswap];
|
||||
|
||||
if(nexttri != -1 && !m_pused[nexttri])
|
||||
{
|
||||
pstriptris[stripcount] = pstriptris[stripcount - 2];
|
||||
pstripverts[stripcount] = pstripverts[stripcount - 2];
|
||||
stripcount++;
|
||||
swaps++;
|
||||
fstartcw = !fstartcw;
|
||||
}
|
||||
}
|
||||
|
||||
// record index information
|
||||
pstriptris[stripcount] = tri;
|
||||
pstripverts[stripcount] = (vert + 2) % 3;
|
||||
|
||||
// mark triangle as used
|
||||
m_pused[tri] = 1;
|
||||
}
|
||||
|
||||
// clear the used flags
|
||||
for(int j = 2; j < stripcount; j++)
|
||||
m_pused[pstriptris[j]] = 0;
|
||||
|
||||
// return swap count and striplen
|
||||
*pswaps = swaps;
|
||||
return stripcount;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Given a striplist and current cache state, pick the best next strip
|
||||
//=========================================================================
|
||||
STRIPLIST::iterator FindBestCachedStrip(STRIPLIST *pstriplist,
|
||||
const CVertCache &vertcachestate)
|
||||
{
|
||||
if(pstriplist->empty())
|
||||
return pstriplist->end();
|
||||
|
||||
bool fFlipStrip = false;
|
||||
int maxcachehits = -1;
|
||||
STRIPLIST::iterator istriplistbest = pstriplist->begin();
|
||||
|
||||
int striplen = StripLen(**istriplistbest);
|
||||
bool fstartcw = FIsStripCW(**istriplistbest);
|
||||
|
||||
// go through all the other strips looking for the best caching
|
||||
for(STRIPLIST::iterator istriplist = pstriplist->begin();
|
||||
istriplist != pstriplist->end();
|
||||
++istriplist)
|
||||
{
|
||||
bool fFlip = false;
|
||||
const STRIPVERTS &stripverts = **istriplist;
|
||||
int striplennew = StripLen(stripverts);
|
||||
|
||||
// check cache if this strip is the same type as us (ie: cw/odd)
|
||||
if((FIsStripCW(stripverts) == fstartcw) &&
|
||||
((striplen & 0x1) == (striplennew & 0x1)))
|
||||
{
|
||||
// copy current state of cache
|
||||
CVertCache vertcachenew = vertcachestate;
|
||||
|
||||
// figure out what this guy would do to our cache
|
||||
for(int ivert = 0; ivert < striplennew; ivert++)
|
||||
vertcachenew.Add(2, stripverts[ivert]);
|
||||
|
||||
// even length strip - see if better cache hits reversed
|
||||
if(!(striplennew & 0x1))
|
||||
{
|
||||
CVertCache vertcacheflipped = vertcachestate;
|
||||
|
||||
for(int ivert = StripLen(stripverts) - 1; ivert >= 0; ivert--)
|
||||
vertcacheflipped.Add(2, stripverts[ivert]);
|
||||
|
||||
if(vertcacheflipped.NumCacheHits() > vertcachenew.NumCacheHits())
|
||||
{
|
||||
vertcachenew = vertcacheflipped;
|
||||
fFlip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// record the best number of cache hits to date
|
||||
int numcachehits = vertcachenew.NumCacheHits() - vertcachestate.NumCacheHits();
|
||||
if(numcachehits > maxcachehits)
|
||||
{
|
||||
maxcachehits = numcachehits;
|
||||
istriplistbest = istriplist;
|
||||
fFlipStrip = fFlip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(fFlipStrip)
|
||||
{
|
||||
STRIPVERTS &stripverts = **istriplistbest;
|
||||
STRIPVERTS::iterator vend = stripverts.end();
|
||||
|
||||
reverse(stripverts.begin(), --vend);
|
||||
}
|
||||
|
||||
// make sure we keep the list in order and always pull off
|
||||
// the first dude.
|
||||
if(istriplistbest != pstriplist->begin())
|
||||
swap(*istriplistbest, *pstriplist->begin());
|
||||
|
||||
return pstriplist->begin();
|
||||
}
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Don't merge the strips - just blast em into the stripbuffer one by one
|
||||
// (useful for debugging)
|
||||
//=========================================================================
|
||||
int CStripper::CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices)
|
||||
{
|
||||
// allow room for each of the strips size plus the final 0
|
||||
int indexcount = (int)pstriplist->size() + 1;
|
||||
|
||||
// we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format
|
||||
STRIPLIST::iterator istriplist;
|
||||
for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist)
|
||||
{
|
||||
// add striplength plus potential degenerate to swap ccw --> cw
|
||||
indexcount += StripLen(**istriplist) + 1;
|
||||
}
|
||||
|
||||
// alloc the space for all this stuff
|
||||
WORD *pstripindices = new WORD [indexcount];
|
||||
assert(pstripindices);
|
||||
|
||||
CVertCache vertcache;
|
||||
int numstripindices = 0;
|
||||
|
||||
for(istriplist = pstriplist->begin();
|
||||
!pstriplist->empty();
|
||||
istriplist = FindBestCachedStrip(pstriplist, vertcache))
|
||||
{
|
||||
const STRIPVERTS &stripverts = **istriplist;
|
||||
|
||||
if(!FIsStripCW(stripverts))
|
||||
{
|
||||
// add an extra index if it's ccw
|
||||
pstripindices[numstripindices++] = StripLen(stripverts) + 1;
|
||||
pstripindices[numstripindices++] = stripverts[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the strip length
|
||||
pstripindices[numstripindices++] = StripLen(stripverts);
|
||||
}
|
||||
|
||||
// add all the strip indices
|
||||
for(int i = 0; i < StripLen(stripverts); i++)
|
||||
{
|
||||
pstripindices[numstripindices++] = stripverts[i];
|
||||
vertcache.Add(1, stripverts[i]);
|
||||
}
|
||||
|
||||
// free this guy and pop him off the list
|
||||
delete &stripverts;
|
||||
pstriplist->pop_front();
|
||||
}
|
||||
|
||||
// add terminating zero
|
||||
pstripindices[numstripindices++] = 0;
|
||||
*ppstripindices = pstripindices;
|
||||
|
||||
return numstripindices;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Merge striplist into one big uberlist with (hopefully) optimal caching
|
||||
//=========================================================================
|
||||
int CStripper::CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices)
|
||||
{
|
||||
// allow room for one strip length plus a possible 3 extra indices per
|
||||
// concatenated strip list plus the final 0
|
||||
int indexcount = ((int)pstriplist->size() * 3) + 2;
|
||||
|
||||
// we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format
|
||||
STRIPLIST::iterator istriplist;
|
||||
for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist)
|
||||
{
|
||||
indexcount += StripLen(**istriplist);
|
||||
}
|
||||
|
||||
// alloc the space for all this stuff
|
||||
WORD *pstripindices = new WORD [indexcount];
|
||||
assert(pstripindices);
|
||||
|
||||
CVertCache vertcache;
|
||||
int numstripindices = 0;
|
||||
|
||||
// add first strip
|
||||
istriplist = pstriplist->begin();
|
||||
const STRIPVERTS &stripverts = **istriplist;
|
||||
|
||||
// first strip should be cw
|
||||
assert(FIsStripCW(stripverts));
|
||||
|
||||
for(int ivert = 0; ivert < StripLen(stripverts); ivert++)
|
||||
{
|
||||
pstripindices[numstripindices++] = stripverts[ivert];
|
||||
vertcache.Add(1, stripverts[ivert]);
|
||||
}
|
||||
|
||||
// kill first dude
|
||||
delete &stripverts;
|
||||
pstriplist->erase(istriplist);
|
||||
|
||||
// add all the others
|
||||
while(pstriplist->size())
|
||||
{
|
||||
istriplist = FindBestCachedStrip(pstriplist, vertcache);
|
||||
STRIPVERTS &stripverts = **istriplist;
|
||||
short lastvert = pstripindices[numstripindices - 1];
|
||||
short firstvert = stripverts[0];
|
||||
|
||||
if(firstvert != lastvert)
|
||||
{
|
||||
// add degenerate from last strip
|
||||
pstripindices[numstripindices++] = lastvert;
|
||||
|
||||
// add degenerate from our strip
|
||||
pstripindices[numstripindices++] = firstvert;
|
||||
}
|
||||
|
||||
// if we're not orientated correctly, we need to add a degenerate
|
||||
if(FIsStripCW(stripverts) != !(numstripindices & 0x1))
|
||||
{
|
||||
// This shouldn't happen - we're currently trying very hard
|
||||
// to keep everything oriented correctly.
|
||||
assert(false);
|
||||
pstripindices[numstripindices++] = firstvert;
|
||||
}
|
||||
|
||||
// add these verts
|
||||
for(int ivert = 0; ivert < StripLen(stripverts); ivert++)
|
||||
{
|
||||
pstripindices[numstripindices++] = stripverts[ivert];
|
||||
vertcache.Add(1, stripverts[ivert]);
|
||||
}
|
||||
|
||||
// free these guys
|
||||
delete &stripverts;
|
||||
pstriplist->erase(istriplist);
|
||||
}
|
||||
|
||||
*ppstripindices = pstripindices;
|
||||
return numstripindices;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Build a (hopefully) optimal set of strips from a trilist
|
||||
//=========================================================================
|
||||
void CStripper::BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead)
|
||||
{
|
||||
// temp indices storage
|
||||
const int ctmpverts = 1024;
|
||||
int pstripverts[ctmpverts + 1];
|
||||
int pstriptris[ctmpverts + 1];
|
||||
|
||||
assert(maxlen <= ctmpverts);
|
||||
|
||||
// clear all the used flags for the tris
|
||||
memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);
|
||||
|
||||
bool fstartcw = true;
|
||||
for(;;)
|
||||
{
|
||||
int besttri = 0;
|
||||
int bestvert = 0;
|
||||
float bestratio = 2.0f;
|
||||
int bestneighborcount = INT_MAX;
|
||||
|
||||
int tri;
|
||||
for( tri = 0; tri < m_numtris; tri++)
|
||||
{
|
||||
// if used the continue
|
||||
if(m_pused[tri])
|
||||
continue;
|
||||
|
||||
// get the neighbor count
|
||||
int curneightborcount = GetNeighborCount(tri);
|
||||
assert(curneightborcount >= 0 && curneightborcount <= 3);
|
||||
|
||||
// push all the singletons to the very end
|
||||
if(!curneightborcount)
|
||||
curneightborcount = 4;
|
||||
|
||||
// if this guy has more neighbors than the current best - bail
|
||||
if(curneightborcount > bestneighborcount)
|
||||
continue;
|
||||
|
||||
// try starting the strip with each of this tris verts
|
||||
for(int vert = 0; vert < 3; vert++)
|
||||
{
|
||||
int swaps;
|
||||
int len = CreateStrip(tri, vert, maxlen, &swaps, flookahead,
|
||||
fstartcw, pstriptris, pstripverts);
|
||||
assert(len);
|
||||
|
||||
float ratio = (len == 3) ? 1.0f : (float)swaps / len;
|
||||
|
||||
// check if this ratio is better than what we've already got for
|
||||
// this neighborcount
|
||||
if((curneightborcount < bestneighborcount) ||
|
||||
((curneightborcount == bestneighborcount) && (ratio < bestratio)))
|
||||
{
|
||||
bestneighborcount = curneightborcount;
|
||||
|
||||
besttri = tri;
|
||||
bestvert = vert;
|
||||
bestratio = ratio;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// no strips found?
|
||||
if(bestneighborcount == INT_MAX)
|
||||
break;
|
||||
|
||||
// recreate this strip
|
||||
int swaps;
|
||||
int len = CreateStrip(besttri, bestvert, maxlen,
|
||||
&swaps, flookahead, fstartcw, pstriptris, pstripverts);
|
||||
assert(len);
|
||||
|
||||
// mark the tris on the best strip as used
|
||||
for(tri = 0; tri < len; tri++)
|
||||
m_pused[pstriptris[tri]] = 1;
|
||||
|
||||
// create a new STRIPVERTS and stuff in the indices
|
||||
STRIPVERTS *pstripvertices = new STRIPVERTS(len + 1);
|
||||
assert(pstripvertices);
|
||||
|
||||
// store orientation in first entry
|
||||
for(tri = 0; tri < len; tri++)
|
||||
(*pstripvertices)[tri] = m_ptriangles[pstriptris[tri]][pstripverts[tri]];
|
||||
(*pstripvertices)[len] = fstartcw;
|
||||
|
||||
// store the STRIPVERTS
|
||||
pstriplist->push_back(pstripvertices);
|
||||
|
||||
// if strip was odd - swap orientation
|
||||
if((len & 0x1))
|
||||
fstartcw = !fstartcw;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// make sure all tris are used
|
||||
for(int t = 0; t < m_numtris; t++)
|
||||
assert(m_pused[t]);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Guesstimate on the total index count for this list of strips
|
||||
//=========================================================================
|
||||
int EstimateStripCost(STRIPLIST *pstriplist)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(STRIPLIST::iterator istriplist = pstriplist->begin();
|
||||
istriplist != pstriplist->end();
|
||||
++istriplist)
|
||||
{
|
||||
// add count of indices
|
||||
count += StripLen(**istriplist);
|
||||
}
|
||||
|
||||
// assume 2 indices per strip to tack all these guys together
|
||||
return count + ((int)pstriplist->size() - 1) * 2;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Initialize triangle information (edges, #neighbors, etc.)
|
||||
//=========================================================================
|
||||
void CStripper::InitTriangleInfo(int tri, int vert)
|
||||
{
|
||||
WORD *ptriverts = &m_ptriangles[tri + 1][0];
|
||||
int vert1 = m_ptriangles[tri][(vert + 1) % 3];
|
||||
int vert2 = m_ptriangles[tri][vert];
|
||||
|
||||
for(int itri = tri + 1; itri < m_numtris; itri++, ptriverts += 3)
|
||||
{
|
||||
if(m_pused[itri] != 0x7)
|
||||
{
|
||||
for(int ivert = 0; ivert < 3; ivert++)
|
||||
{
|
||||
if((ptriverts[ivert] == vert1) &&
|
||||
(ptriverts[(ivert + 1) % 3] == vert2))
|
||||
{
|
||||
// add the triangle info
|
||||
m_ptriinfo[tri].neighbortri[vert] = itri;
|
||||
m_ptriinfo[tri].neighboredge[vert] = ivert;
|
||||
m_pused[tri] |= (1 << vert);
|
||||
|
||||
m_ptriinfo[itri].neighbortri[ivert] = tri;
|
||||
m_ptriinfo[itri].neighboredge[ivert] = vert;
|
||||
m_pused[itri] |= (1 << ivert);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CStripper ctor
|
||||
//=========================================================================
|
||||
CStripper::CStripper(int numtris, TRIANGLELIST ptriangles)
|
||||
{
|
||||
// store trilist info
|
||||
m_numtris = numtris;
|
||||
m_ptriangles = ptriangles;
|
||||
|
||||
m_pused = new int[numtris];
|
||||
assert(m_pused);
|
||||
m_ptriinfo = new TRIANGLEINFO[numtris];
|
||||
assert(m_ptriinfo);
|
||||
|
||||
// init triinfo
|
||||
int itri;
|
||||
for( itri = 0; itri < numtris; itri++)
|
||||
{
|
||||
m_ptriinfo[itri].neighbortri[0] = -1;
|
||||
m_ptriinfo[itri].neighbortri[1] = -1;
|
||||
m_ptriinfo[itri].neighbortri[2] = -1;
|
||||
}
|
||||
|
||||
// clear the used flag
|
||||
memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);
|
||||
|
||||
// go through all the triangles and find edges, neighbor counts
|
||||
for(itri = 0; itri < numtris; itri++)
|
||||
{
|
||||
for(int ivert = 0; ivert < 3; ivert++)
|
||||
{
|
||||
if(!(m_pused[itri] & (1 << ivert)))
|
||||
InitTriangleInfo(itri, ivert);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the used flags from InitTriangleInfo
|
||||
memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// CStripper dtor
|
||||
//=========================================================================
|
||||
CStripper::~CStripper()
|
||||
{
|
||||
// free stuff
|
||||
delete [] m_pused;
|
||||
m_pused = NULL;
|
||||
|
||||
delete [] m_ptriinfo;
|
||||
m_ptriinfo = NULL;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Add an index to the cache - returns true if it was added, false otherwise
|
||||
//=========================================================================
|
||||
bool CVertCache::Add(int strip, int vertindex)
|
||||
{
|
||||
// find index in cache
|
||||
for(int iCache = 0; iCache < CACHE_SIZE; iCache++)
|
||||
{
|
||||
if(vertindex == m_rgCache[iCache])
|
||||
{
|
||||
// if it's in the cache and it's from a different strip
|
||||
// change the strip to the new one and count the cache hit
|
||||
if(strip != m_rgCacheStrip[iCache])
|
||||
{
|
||||
m_cachehits++;
|
||||
m_rgCacheStrip[iCache] = strip;
|
||||
return true;
|
||||
}
|
||||
|
||||
// we added this item to the cache earlier - carry on
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// not in cache, add vert and strip
|
||||
m_rgCache[m_iCachePtr] = vertindex;
|
||||
m_rgCacheStrip[m_iCachePtr] = strip;
|
||||
m_iCachePtr = (m_iCachePtr + 1) % CACHE_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
//=========================================================================
|
||||
// Turn on c runtime leak checking, etc.
|
||||
//=========================================================================
|
||||
void EnableLeakChecking()
|
||||
{
|
||||
int flCrtDbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
||||
|
||||
flCrtDbgFlags &=
|
||||
~(_CRTDBG_LEAK_CHECK_DF |
|
||||
_CRTDBG_CHECK_ALWAYS_DF |
|
||||
_CRTDBG_DELAY_FREE_MEM_DF);
|
||||
|
||||
// always check for memory leaks
|
||||
flCrtDbgFlags |= _CRTDBG_LEAK_CHECK_DF;
|
||||
|
||||
// others you may / may not want to set
|
||||
flCrtDbgFlags |= _CRTDBG_CHECK_ALWAYS_DF;
|
||||
flCrtDbgFlags |= _CRTDBG_DELAY_FREE_MEM_DF;
|
||||
|
||||
_CrtSetDbgFlag(flCrtDbgFlags);
|
||||
|
||||
// all types of reports go via OutputDebugString
|
||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
|
||||
|
||||
// big errors and asserts get their own assert window
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
|
||||
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW);
|
||||
|
||||
// _CrtSetBreakAlloc(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Main Stripify routine
|
||||
//=========================================================================
|
||||
int Stripify(int numtris, WORD *ptriangles, int *pnumindices, WORD **ppstripindices)
|
||||
{
|
||||
if(!numtris || !ptriangles)
|
||||
return 0;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// EnableLeakChecking();
|
||||
#endif
|
||||
|
||||
CStripper stripper(numtris, (TRIANGLELIST)ptriangles);
|
||||
|
||||
// map of various args to try stripifying mesh with
|
||||
struct ARGMAP
|
||||
{
|
||||
int maxlen; // maximum length of strips
|
||||
bool flookahead; // use sgi greedy lookahead (or not)
|
||||
} rgargmap[] =
|
||||
{
|
||||
{ 1024, true },
|
||||
{ 1024, false },
|
||||
};
|
||||
static const int cargmaps = sizeof(rgargmap) / sizeof(rgargmap[0]);
|
||||
STRIPLIST striplistbest;
|
||||
int bestlistcost = 0;
|
||||
|
||||
for(int imap = 0; imap < cargmaps; imap++)
|
||||
{
|
||||
STRIPLIST striplist;
|
||||
|
||||
// build the strip with the various args
|
||||
stripper.BuildStrips(&striplist, rgargmap[imap].maxlen,
|
||||
rgargmap[imap].flookahead);
|
||||
|
||||
// guesstimate the list cost and store it if it's good
|
||||
int listcost = EstimateStripCost(&striplist);
|
||||
if(!bestlistcost || (listcost < bestlistcost))
|
||||
{
|
||||
// free the old best list
|
||||
FreeStripListVerts(&striplistbest);
|
||||
|
||||
// store the new best list
|
||||
striplistbest = striplist;
|
||||
bestlistcost = listcost;
|
||||
assert(bestlistcost > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeStripListVerts(&striplist);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NEVER
|
||||
// Return the strips in [size1 i1 i2 i3][size2 i4 i5 i6]...[0] format
|
||||
// Very useful for debugging...
|
||||
return stripper.CreateManyStrips(&striplistbest, ppstripindices);
|
||||
#endif // NEVER
|
||||
|
||||
// return one big long strip
|
||||
int numindices = stripper.CreateLongStrip(&striplistbest, ppstripindices);
|
||||
|
||||
if(pnumindices)
|
||||
*pnumindices = numindices;
|
||||
return numindices;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Class used to vertices for locality of access.
|
||||
//=========================================================================
|
||||
struct SortEntry
|
||||
{
|
||||
public:
|
||||
int iFirstUsed;
|
||||
int iOrigIndex;
|
||||
|
||||
bool operator<(const SortEntry& rhs)
|
||||
{
|
||||
return iFirstUsed < rhs.iFirstUsed;
|
||||
}
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
// Reorder the vertices
|
||||
//=========================================================================
|
||||
void ComputeVertexPermutation(int numstripindices, WORD* pstripindices,
|
||||
int* pnumverts, WORD** ppvertexpermutation)
|
||||
{
|
||||
// Sort verts to maximize locality.
|
||||
SortEntry* pSortTable = new SortEntry[*pnumverts];
|
||||
|
||||
// Fill in original index.
|
||||
int i;
|
||||
for( i = 0; i < *pnumverts; i++)
|
||||
{
|
||||
pSortTable[i].iOrigIndex = i;
|
||||
pSortTable[i].iFirstUsed = -1;
|
||||
}
|
||||
|
||||
// Fill in first used flag.
|
||||
for(i = 0; i < numstripindices; i++)
|
||||
{
|
||||
int index = pstripindices[i];
|
||||
|
||||
if(pSortTable[index].iFirstUsed == -1)
|
||||
pSortTable[index].iFirstUsed = i;
|
||||
}
|
||||
|
||||
// Sort the table.
|
||||
sort(pSortTable, pSortTable + *pnumverts);
|
||||
|
||||
// Copy re-mapped to orignal vertex permutaion into output array.
|
||||
*ppvertexpermutation = new WORD[*pnumverts];
|
||||
|
||||
for(i = 0; i < *pnumverts; i++)
|
||||
{
|
||||
(*ppvertexpermutation)[i] = pSortTable[i].iOrigIndex;
|
||||
}
|
||||
|
||||
// Build original to re-mapped permutation.
|
||||
WORD* pInversePermutation = new WORD[numstripindices];
|
||||
|
||||
for(i = 0; i < *pnumverts; i++)
|
||||
{
|
||||
pInversePermutation[pSortTable[i].iOrigIndex] = i;
|
||||
}
|
||||
|
||||
// We need to remap indices as well.
|
||||
for(i = 0; i < numstripindices; i++)
|
||||
{
|
||||
pstripindices[i] = pInversePermutation[pstripindices[i]];
|
||||
}
|
||||
|
||||
delete[] pSortTable;
|
||||
delete[] pInversePermutation;
|
||||
}
|
||||
|
43
utils/common/mstristrip.h
Normal file
43
utils/common/mstristrip.h
Normal file
@ -0,0 +1,43 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//-----------------------------------------------------------------------------
|
||||
// FILE: TRISTRIP.H
|
||||
//
|
||||
// Desc: tristrip header file
|
||||
//
|
||||
// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef unsigned short WORD;
|
||||
|
||||
//
|
||||
// Main Stripify routine. Returns number of strip indices contained
|
||||
// in ppstripindices. Caller must delete [] ppstripindices.
|
||||
//
|
||||
int Stripify(
|
||||
int numtris, // Number of triangles
|
||||
WORD *ptriangles, // triangle indices pointer
|
||||
int *pnumindices, // number of indices in ppstripindices (out)
|
||||
WORD **ppstripindices // triangle strip indices
|
||||
);
|
||||
|
||||
//
|
||||
// Re-arrange vertices so that they occur in the order that they are first
|
||||
// used. This function doesn't actually move vertex data around, it returns
|
||||
// an array that specifies where in the new vertex array each old vertex
|
||||
// should go. It also re-maps the strip indices to use the new vertex
|
||||
// locations. Caller must delete [] pVertexPermutation.
|
||||
//
|
||||
void ComputeVertexPermutation
|
||||
(
|
||||
int numstripindices, // Number of strip indices
|
||||
WORD *pstripindices, // Strip indices
|
||||
int *pnumverts, // Number of verts (in and out)
|
||||
WORD **ppvertexpermutation // Map from orignal index to remapped index
|
||||
);
|
||||
|
63
utils/common/pacifier.cpp
Normal file
63
utils/common/pacifier.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <stdio.h>
|
||||
#include "basetypes.h"
|
||||
#include "pacifier.h"
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
|
||||
static int g_LastPacifierDrawn = -1;
|
||||
static bool g_bPacifierSuppressed = false;
|
||||
|
||||
#define clamp(a,b,c) ( (a) > (c) ? (c) : ( (a) < (b) ? (b) : (a) ) )
|
||||
|
||||
void StartPacifier( char const *pPrefix )
|
||||
{
|
||||
Msg( "%s", pPrefix );
|
||||
g_LastPacifierDrawn = -1;
|
||||
UpdatePacifier( 0.001f );
|
||||
}
|
||||
|
||||
void UpdatePacifier( float flPercent )
|
||||
{
|
||||
int iCur = (int)(flPercent * 40.0f);
|
||||
iCur = clamp( iCur, g_LastPacifierDrawn, 40 );
|
||||
|
||||
if( iCur != g_LastPacifierDrawn && !g_bPacifierSuppressed )
|
||||
{
|
||||
for( int i=g_LastPacifierDrawn+1; i <= iCur; i++ )
|
||||
{
|
||||
if ( !( i % 4 ) )
|
||||
{
|
||||
Msg("%d", i/4);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( i != 40 )
|
||||
{
|
||||
Msg(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_LastPacifierDrawn = iCur;
|
||||
}
|
||||
}
|
||||
|
||||
void EndPacifier( bool bCarriageReturn )
|
||||
{
|
||||
UpdatePacifier(1);
|
||||
|
||||
if( bCarriageReturn && !g_bPacifierSuppressed )
|
||||
Msg("\n");
|
||||
}
|
||||
|
||||
void SuppressPacifier( bool bSuppress )
|
||||
{
|
||||
g_bPacifierSuppressed = bSuppress;
|
||||
}
|
23
utils/common/pacifier.h
Normal file
23
utils/common/pacifier.h
Normal file
@ -0,0 +1,23 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PACIFIER_H
|
||||
#define PACIFIER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// Use these to display a pacifier like:
|
||||
// ProcessBlock_Thread: 0...1...2...3...4...5...6...7...8...9... (0)
|
||||
void StartPacifier( char const *pPrefix ); // Prints the prefix and resets the pacifier
|
||||
void UpdatePacifier( float flPercent ); // percent value between 0 and 1.
|
||||
void EndPacifier( bool bCarriageReturn = true ); // Completes pacifier as if 100% was done
|
||||
void SuppressPacifier( bool bSuppress = true ); // Suppresses pacifier updates if another thread might still be firing them
|
||||
|
||||
|
||||
#endif // PACIFIER_H
|
31
utils/common/physdll.cpp
Normal file
31
utils/common/physdll.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include <stdio.h>
|
||||
#include "physdll.h"
|
||||
#include "filesystem_tools.h"
|
||||
|
||||
static CSysModule *pPhysicsModule = NULL;
|
||||
CreateInterfaceFn GetPhysicsFactory( void )
|
||||
{
|
||||
if ( !pPhysicsModule )
|
||||
{
|
||||
pPhysicsModule = g_pFullFileSystem->LoadModule( "VPHYSICS.DLL" );
|
||||
if ( !pPhysicsModule )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Sys_GetFactory( pPhysicsModule );
|
||||
}
|
||||
|
||||
void PhysicsDLLPath( const char *pPathname )
|
||||
{
|
||||
if ( !pPhysicsModule )
|
||||
{
|
||||
pPhysicsModule = g_pFullFileSystem->LoadModule( pPathname );
|
||||
}
|
||||
}
|
30
utils/common/physdll.h
Normal file
30
utils/common/physdll.h
Normal file
@ -0,0 +1,30 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PHYSDLL_H
|
||||
#define PHYSDLL_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "vphysics_interface.h"
|
||||
class IPhysics;
|
||||
class IPhysicsCollision;
|
||||
|
||||
extern CreateInterfaceFn GetPhysicsFactory( void );
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// tools need to force the path
|
||||
void PhysicsDLLPath( const char *pPathname );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // PHYSDLL_H
|
915
utils/common/polylib.cpp
Normal file
915
utils/common/polylib.cpp
Normal file
@ -0,0 +1,915 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "polylib.h"
|
||||
#include "worldsize.h"
|
||||
#include "threads.h"
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
// doesn't seem to need to be here? -- in threads.h
|
||||
//extern int numthreads;
|
||||
|
||||
// counters are only bumped when running single threaded,
|
||||
// because they are an awefull coherence problem
|
||||
int c_active_windings;
|
||||
int c_peak_windings;
|
||||
int c_winding_allocs;
|
||||
int c_winding_points;
|
||||
|
||||
void pw(winding_t *w)
|
||||
{
|
||||
int i;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
|
||||
}
|
||||
|
||||
winding_t *winding_pool[MAX_POINTS_ON_WINDING+4];
|
||||
|
||||
/*
|
||||
=============
|
||||
AllocWinding
|
||||
=============
|
||||
*/
|
||||
winding_t *AllocWinding (int points)
|
||||
{
|
||||
winding_t *w;
|
||||
|
||||
if (numthreads == 1)
|
||||
{
|
||||
c_winding_allocs++;
|
||||
c_winding_points += points;
|
||||
c_active_windings++;
|
||||
if (c_active_windings > c_peak_windings)
|
||||
c_peak_windings = c_active_windings;
|
||||
}
|
||||
ThreadLock();
|
||||
if (winding_pool[points])
|
||||
{
|
||||
w = winding_pool[points];
|
||||
winding_pool[points] = w->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
w = (winding_t *)malloc(sizeof(*w));
|
||||
w->p = (Vector *)calloc( points, sizeof(Vector) );
|
||||
}
|
||||
ThreadUnlock();
|
||||
w->numpoints = 0; // None are occupied yet even though allocated.
|
||||
w->maxpoints = points;
|
||||
w->next = NULL;
|
||||
return w;
|
||||
}
|
||||
|
||||
void FreeWinding (winding_t *w)
|
||||
{
|
||||
if (w->numpoints == 0xdeaddead)
|
||||
Error ("FreeWinding: freed a freed winding");
|
||||
|
||||
ThreadLock();
|
||||
w->numpoints = 0xdeaddead; // flag as freed
|
||||
w->next = winding_pool[w->maxpoints];
|
||||
winding_pool[w->maxpoints] = w;
|
||||
ThreadUnlock();
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
RemoveColinearPoints
|
||||
============
|
||||
*/
|
||||
int c_removed;
|
||||
|
||||
void RemoveColinearPoints (winding_t *w)
|
||||
{
|
||||
int i, j, k;
|
||||
Vector v1, v2;
|
||||
int nump;
|
||||
Vector p[MAX_POINTS_ON_WINDING];
|
||||
|
||||
nump = 0;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
j = (i+1)%w->numpoints;
|
||||
k = (i+w->numpoints-1)%w->numpoints;
|
||||
VectorSubtract (w->p[j], w->p[i], v1);
|
||||
VectorSubtract (w->p[i], w->p[k], v2);
|
||||
VectorNormalize(v1);
|
||||
VectorNormalize(v2);
|
||||
if (DotProduct(v1, v2) < 0.999)
|
||||
{
|
||||
VectorCopy (w->p[i], p[nump]);
|
||||
nump++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nump == w->numpoints)
|
||||
return;
|
||||
|
||||
if (numthreads == 1)
|
||||
c_removed += w->numpoints - nump;
|
||||
w->numpoints = nump;
|
||||
memcpy (w->p, p, nump*sizeof(p[0]));
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WindingPlane
|
||||
============
|
||||
*/
|
||||
void WindingPlane (winding_t *w, Vector &normal, vec_t *dist)
|
||||
{
|
||||
Vector v1, v2;
|
||||
|
||||
VectorSubtract (w->p[1], w->p[0], v1);
|
||||
|
||||
// HACKHACK: Avoid potentially collinear verts
|
||||
if ( w->numpoints > 3 )
|
||||
{
|
||||
VectorSubtract (w->p[3], w->p[0], v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract (w->p[2], w->p[0], v2);
|
||||
}
|
||||
CrossProduct (v2, v1, normal);
|
||||
VectorNormalize (normal);
|
||||
*dist = DotProduct (w->p[0], normal);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
WindingArea
|
||||
=============
|
||||
*/
|
||||
vec_t WindingArea(winding_t *w)
|
||||
{
|
||||
int i;
|
||||
Vector d1, d2, cross;
|
||||
vec_t total;
|
||||
|
||||
total = 0;
|
||||
for (i=2 ; i<w->numpoints ; i++)
|
||||
{
|
||||
VectorSubtract (w->p[i-1], w->p[0], d1);
|
||||
VectorSubtract (w->p[i], w->p[0], d2);
|
||||
CrossProduct (d1, d2, cross);
|
||||
total += VectorLength ( cross );
|
||||
}
|
||||
return total * 0.5;
|
||||
}
|
||||
|
||||
void WindingBounds (winding_t *w, Vector &mins, Vector &maxs)
|
||||
{
|
||||
vec_t v;
|
||||
int i,j;
|
||||
|
||||
mins[0] = mins[1] = mins[2] = 99999;
|
||||
maxs[0] = maxs[1] = maxs[2] = -99999;
|
||||
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
v = w->p[i][j];
|
||||
if (v < mins[j])
|
||||
mins[j] = v;
|
||||
if (v > maxs[j])
|
||||
maxs[j] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
WindingCenter
|
||||
=============
|
||||
*/
|
||||
void WindingCenter (winding_t *w, Vector ¢er)
|
||||
{
|
||||
int i;
|
||||
float scale;
|
||||
|
||||
VectorCopy (vec3_origin, center);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
VectorAdd (w->p[i], center, center);
|
||||
|
||||
scale = 1.0/w->numpoints;
|
||||
VectorScale (center, scale, center);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
WindingCenter
|
||||
=============
|
||||
*/
|
||||
vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er )
|
||||
{
|
||||
int i;
|
||||
Vector d1, d2, cross;
|
||||
vec_t total;
|
||||
|
||||
VectorCopy (vec3_origin, center);
|
||||
if ( !w )
|
||||
return 0.0f;
|
||||
|
||||
total = 0;
|
||||
for (i=2 ; i<w->numpoints ; i++)
|
||||
{
|
||||
VectorSubtract (w->p[i-1], w->p[0], d1);
|
||||
VectorSubtract (w->p[i], w->p[0], d2);
|
||||
CrossProduct (d1, d2, cross);
|
||||
float area = VectorLength ( cross );
|
||||
total += area;
|
||||
|
||||
// center of triangle, weighed by area
|
||||
VectorMA( center, area / 3.0, w->p[i-1], center );
|
||||
VectorMA( center, area / 3.0, w->p[i], center );
|
||||
VectorMA( center, area / 3.0, w->p[0], center );
|
||||
}
|
||||
if (total)
|
||||
{
|
||||
VectorScale( center, 1.0 / total, center );
|
||||
}
|
||||
return total * 0.5;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
BaseWindingForPlane
|
||||
=================
|
||||
*/
|
||||
winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist)
|
||||
{
|
||||
int i, x;
|
||||
vec_t max, v;
|
||||
Vector org, vright, vup;
|
||||
winding_t *w;
|
||||
|
||||
// find the major axis
|
||||
|
||||
max = -1;
|
||||
x = -1;
|
||||
for (i=0 ; i<3; i++)
|
||||
{
|
||||
v = fabs(normal[i]);
|
||||
if (v > max)
|
||||
{
|
||||
x = i;
|
||||
max = v;
|
||||
}
|
||||
}
|
||||
if (x==-1)
|
||||
Error ("BaseWindingForPlane: no axis found");
|
||||
|
||||
VectorCopy (vec3_origin, vup);
|
||||
switch (x)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
vup[2] = 1;
|
||||
break;
|
||||
case 2:
|
||||
vup[0] = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
v = DotProduct (vup, normal);
|
||||
VectorMA (vup, -v, normal, vup);
|
||||
VectorNormalize (vup);
|
||||
|
||||
VectorScale (normal, dist, org);
|
||||
|
||||
CrossProduct (vup, normal, vright);
|
||||
|
||||
VectorScale (vup, (MAX_COORD_INTEGER*4), vup);
|
||||
VectorScale (vright, (MAX_COORD_INTEGER*4), vright);
|
||||
|
||||
// project a really big axis aligned box onto the plane
|
||||
w = AllocWinding (4);
|
||||
|
||||
VectorSubtract (org, vright, w->p[0]);
|
||||
VectorAdd (w->p[0], vup, w->p[0]);
|
||||
|
||||
VectorAdd (org, vright, w->p[1]);
|
||||
VectorAdd (w->p[1], vup, w->p[1]);
|
||||
|
||||
VectorAdd (org, vright, w->p[2]);
|
||||
VectorSubtract (w->p[2], vup, w->p[2]);
|
||||
|
||||
VectorSubtract (org, vright, w->p[3]);
|
||||
VectorSubtract (w->p[3], vup, w->p[3]);
|
||||
|
||||
w->numpoints = 4;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CopyWinding
|
||||
==================
|
||||
*/
|
||||
winding_t *CopyWinding (winding_t *w)
|
||||
{
|
||||
int size;
|
||||
winding_t *c;
|
||||
|
||||
c = AllocWinding (w->numpoints);
|
||||
c->numpoints = w->numpoints;
|
||||
size = w->numpoints*sizeof(w->p[0]);
|
||||
memcpy (c->p, w->p, size);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
ReverseWinding
|
||||
==================
|
||||
*/
|
||||
winding_t *ReverseWinding (winding_t *w)
|
||||
{
|
||||
int i;
|
||||
winding_t *c;
|
||||
|
||||
c = AllocWinding (w->numpoints);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
|
||||
}
|
||||
c->numpoints = w->numpoints;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
// BUGBUG: Hunt this down - it's causing CSG errors
|
||||
#pragma optimize("g", off)
|
||||
/*
|
||||
=============
|
||||
ClipWindingEpsilon
|
||||
=============
|
||||
*/
|
||||
|
||||
void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist,
|
||||
vec_t epsilon, winding_t **front, winding_t **back)
|
||||
{
|
||||
vec_t dists[MAX_POINTS_ON_WINDING+4];
|
||||
int sides[MAX_POINTS_ON_WINDING+4];
|
||||
int counts[3];
|
||||
vec_t dot;
|
||||
int i, j;
|
||||
Vector mid = vec3_origin;
|
||||
winding_t *f, *b;
|
||||
int maxpts;
|
||||
|
||||
counts[0] = counts[1] = counts[2] = 0;
|
||||
|
||||
// determine sides for each point
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
dot = DotProduct (in->p[i], normal);
|
||||
dot -= dist;
|
||||
dists[i] = dot;
|
||||
if (dot > epsilon)
|
||||
sides[i] = SIDE_FRONT;
|
||||
else if (dot < -epsilon)
|
||||
sides[i] = SIDE_BACK;
|
||||
else
|
||||
{
|
||||
sides[i] = SIDE_ON;
|
||||
}
|
||||
counts[sides[i]]++;
|
||||
}
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
|
||||
*front = *back = NULL;
|
||||
|
||||
if (!counts[0])
|
||||
{
|
||||
*back = CopyWinding (in);
|
||||
return;
|
||||
}
|
||||
if (!counts[1])
|
||||
{
|
||||
*front = CopyWinding (in);
|
||||
return;
|
||||
}
|
||||
|
||||
maxpts = in->numpoints+4; // cant use counts[0]+2 because
|
||||
// of fp grouping errors
|
||||
|
||||
*front = f = AllocWinding (maxpts);
|
||||
*back = b = AllocWinding (maxpts);
|
||||
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
Vector& p1 = in->p[i];
|
||||
|
||||
if (sides[i] == SIDE_ON)
|
||||
{
|
||||
VectorCopy (p1, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
VectorCopy (p1, b->p[b->numpoints]);
|
||||
b->numpoints++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sides[i] == SIDE_FRONT)
|
||||
{
|
||||
VectorCopy (p1, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
}
|
||||
if (sides[i] == SIDE_BACK)
|
||||
{
|
||||
VectorCopy (p1, b->p[b->numpoints]);
|
||||
b->numpoints++;
|
||||
}
|
||||
|
||||
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
||||
continue;
|
||||
|
||||
// generate a split point
|
||||
Vector& p2 = in->p[(i+1)%in->numpoints];
|
||||
|
||||
dot = dists[i] / (dists[i]-dists[i+1]);
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{ // avoid round off error when possible
|
||||
if (normal[j] == 1)
|
||||
mid[j] = dist;
|
||||
else if (normal[j] == -1)
|
||||
mid[j] = -dist;
|
||||
else
|
||||
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
|
||||
}
|
||||
|
||||
VectorCopy (mid, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
VectorCopy (mid, b->p[b->numpoints]);
|
||||
b->numpoints++;
|
||||
}
|
||||
|
||||
if (f->numpoints > maxpts || b->numpoints > maxpts)
|
||||
Error ("ClipWinding: points exceeded estimate");
|
||||
if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
|
||||
Error ("ClipWinding: MAX_POINTS_ON_WINDING");
|
||||
}
|
||||
#pragma optimize("", on)
|
||||
|
||||
|
||||
// NOTE: This is identical to ClipWindingEpsilon, but it does a pre/post translation to improve precision
|
||||
void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset )
|
||||
{
|
||||
TranslateWinding( in, offset );
|
||||
ClipWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back );
|
||||
TranslateWinding( in, -offset );
|
||||
if ( front && *front )
|
||||
{
|
||||
TranslateWinding( *front, -offset );
|
||||
}
|
||||
if ( back && *back )
|
||||
{
|
||||
TranslateWinding( *back, -offset );
|
||||
}
|
||||
}
|
||||
|
||||
void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset)
|
||||
{
|
||||
TranslateWinding( in, offset );
|
||||
ClassifyWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back, on );
|
||||
TranslateWinding( in, -offset );
|
||||
if ( front && *front )
|
||||
{
|
||||
TranslateWinding( *front, -offset );
|
||||
}
|
||||
if ( back && *back )
|
||||
{
|
||||
TranslateWinding( *back, -offset );
|
||||
}
|
||||
if ( on && *on )
|
||||
{
|
||||
TranslateWinding( *on, -offset );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
ClassifyWindingEpsilon
|
||||
=============
|
||||
*/
|
||||
// This version returns the winding as "on" if all verts lie in the plane
|
||||
void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist,
|
||||
vec_t epsilon, winding_t **front, winding_t **back, winding_t **on)
|
||||
{
|
||||
vec_t dists[MAX_POINTS_ON_WINDING+4];
|
||||
int sides[MAX_POINTS_ON_WINDING+4];
|
||||
int counts[3];
|
||||
vec_t dot;
|
||||
int i, j;
|
||||
Vector mid = vec3_origin;
|
||||
winding_t *f, *b;
|
||||
int maxpts;
|
||||
|
||||
counts[0] = counts[1] = counts[2] = 0;
|
||||
|
||||
// determine sides for each point
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
dot = DotProduct (in->p[i], normal);
|
||||
dot -= dist;
|
||||
dists[i] = dot;
|
||||
if (dot > epsilon)
|
||||
sides[i] = SIDE_FRONT;
|
||||
else if (dot < -epsilon)
|
||||
sides[i] = SIDE_BACK;
|
||||
else
|
||||
{
|
||||
sides[i] = SIDE_ON;
|
||||
}
|
||||
counts[sides[i]]++;
|
||||
}
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
|
||||
*front = *back = *on = NULL;
|
||||
|
||||
if ( !counts[0] && !counts[1] )
|
||||
{
|
||||
*on = CopyWinding(in);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!counts[0])
|
||||
{
|
||||
*back = CopyWinding(in);
|
||||
return;
|
||||
}
|
||||
if (!counts[1])
|
||||
{
|
||||
*front = CopyWinding(in);
|
||||
return;
|
||||
}
|
||||
|
||||
maxpts = in->numpoints+4; // cant use counts[0]+2 because
|
||||
// of fp grouping errors
|
||||
|
||||
*front = f = AllocWinding (maxpts);
|
||||
*back = b = AllocWinding (maxpts);
|
||||
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
Vector& p1 = in->p[i];
|
||||
|
||||
if (sides[i] == SIDE_ON)
|
||||
{
|
||||
VectorCopy (p1, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
VectorCopy (p1, b->p[b->numpoints]);
|
||||
b->numpoints++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sides[i] == SIDE_FRONT)
|
||||
{
|
||||
VectorCopy (p1, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
}
|
||||
if (sides[i] == SIDE_BACK)
|
||||
{
|
||||
VectorCopy (p1, b->p[b->numpoints]);
|
||||
b->numpoints++;
|
||||
}
|
||||
|
||||
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
||||
continue;
|
||||
|
||||
// generate a split point
|
||||
Vector& p2 = in->p[(i+1)%in->numpoints];
|
||||
|
||||
dot = dists[i] / (dists[i]-dists[i+1]);
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{ // avoid round off error when possible
|
||||
if (normal[j] == 1)
|
||||
mid[j] = dist;
|
||||
else if (normal[j] == -1)
|
||||
mid[j] = -dist;
|
||||
else
|
||||
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
|
||||
}
|
||||
|
||||
VectorCopy (mid, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
VectorCopy (mid, b->p[b->numpoints]);
|
||||
b->numpoints++;
|
||||
}
|
||||
|
||||
if (f->numpoints > maxpts || b->numpoints > maxpts)
|
||||
Error ("ClipWinding: points exceeded estimate");
|
||||
if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
|
||||
Error ("ClipWinding: MAX_POINTS_ON_WINDING");
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
ChopWindingInPlace
|
||||
=============
|
||||
*/
|
||||
void ChopWindingInPlace (winding_t **inout, const Vector &normal, vec_t dist, vec_t epsilon)
|
||||
{
|
||||
winding_t *in;
|
||||
vec_t dists[MAX_POINTS_ON_WINDING+4];
|
||||
int sides[MAX_POINTS_ON_WINDING+4];
|
||||
int counts[3];
|
||||
vec_t dot;
|
||||
int i, j;
|
||||
Vector mid = vec3_origin;
|
||||
winding_t *f;
|
||||
int maxpts;
|
||||
|
||||
in = *inout;
|
||||
counts[0] = counts[1] = counts[2] = 0;
|
||||
// determine sides for each point
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
dot = DotProduct (in->p[i], normal);
|
||||
dot -= dist;
|
||||
dists[i] = dot;
|
||||
if (dot > epsilon)
|
||||
{
|
||||
sides[i] = SIDE_FRONT;
|
||||
}
|
||||
else if (dot < -epsilon)
|
||||
{
|
||||
sides[i] = SIDE_BACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
sides[i] = SIDE_ON;
|
||||
}
|
||||
counts[sides[i]]++;
|
||||
}
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
|
||||
if (!counts[0])
|
||||
{
|
||||
FreeWinding (in);
|
||||
*inout = NULL;
|
||||
return;
|
||||
}
|
||||
if (!counts[1])
|
||||
return; // inout stays the same
|
||||
|
||||
maxpts = in->numpoints+4; // cant use counts[0]+2 because
|
||||
// of fp grouping errors
|
||||
|
||||
f = AllocWinding (maxpts);
|
||||
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
Vector& p1 = in->p[i];
|
||||
|
||||
if (sides[i] == SIDE_ON)
|
||||
{
|
||||
VectorCopy (p1, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sides[i] == SIDE_FRONT)
|
||||
{
|
||||
VectorCopy (p1, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
}
|
||||
|
||||
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
||||
continue;
|
||||
|
||||
// generate a split point
|
||||
Vector& p2 = in->p[(i+1)%in->numpoints];
|
||||
|
||||
dot = dists[i] / (dists[i]-dists[i+1]);
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{ // avoid round off error when possible
|
||||
if (normal[j] == 1)
|
||||
mid[j] = dist;
|
||||
else if (normal[j] == -1)
|
||||
mid[j] = -dist;
|
||||
else
|
||||
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
|
||||
}
|
||||
|
||||
VectorCopy (mid, f->p[f->numpoints]);
|
||||
f->numpoints++;
|
||||
}
|
||||
|
||||
if (f->numpoints > maxpts)
|
||||
Error ("ClipWinding: points exceeded estimate");
|
||||
if (f->numpoints > MAX_POINTS_ON_WINDING)
|
||||
Error ("ClipWinding: MAX_POINTS_ON_WINDING");
|
||||
|
||||
FreeWinding (in);
|
||||
*inout = f;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
ChopWinding
|
||||
|
||||
Returns the fragment of in that is on the front side
|
||||
of the cliping plane. The original is freed.
|
||||
=================
|
||||
*/
|
||||
winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist)
|
||||
{
|
||||
winding_t *f, *b;
|
||||
|
||||
ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
|
||||
FreeWinding (in);
|
||||
if (b)
|
||||
FreeWinding (b);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CheckWinding
|
||||
|
||||
=================
|
||||
*/
|
||||
void CheckWinding (winding_t *w)
|
||||
{
|
||||
int i, j;
|
||||
vec_t d, edgedist;
|
||||
Vector dir, edgenormal, facenormal;
|
||||
vec_t area;
|
||||
vec_t facedist;
|
||||
|
||||
if (w->numpoints < 3)
|
||||
Error ("CheckWinding: %i points",w->numpoints);
|
||||
|
||||
area = WindingArea(w);
|
||||
if (area < 1)
|
||||
Error ("CheckWinding: %f area", area);
|
||||
|
||||
WindingPlane (w, facenormal, &facedist);
|
||||
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
Vector& p1 = w->p[i];
|
||||
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
if (p1[j] > MAX_COORD_INTEGER || p1[j] < MIN_COORD_INTEGER)
|
||||
Error ("CheckFace: out of range: %f",p1[j]);
|
||||
}
|
||||
|
||||
j = i+1 == w->numpoints ? 0 : i+1;
|
||||
|
||||
// check the point is on the face plane
|
||||
d = DotProduct (p1, facenormal) - facedist;
|
||||
if (d < -ON_EPSILON || d > ON_EPSILON)
|
||||
Error ("CheckWinding: point off plane");
|
||||
|
||||
// check the edge isnt degenerate
|
||||
Vector& p2 = w->p[j];
|
||||
VectorSubtract (p2, p1, dir);
|
||||
|
||||
if (VectorLength (dir) < ON_EPSILON)
|
||||
Error ("CheckWinding: degenerate edge");
|
||||
|
||||
CrossProduct (facenormal, dir, edgenormal);
|
||||
VectorNormalize (edgenormal);
|
||||
edgedist = DotProduct (p1, edgenormal);
|
||||
edgedist += ON_EPSILON;
|
||||
|
||||
// all other points must be on front side
|
||||
for (j=0 ; j<w->numpoints ; j++)
|
||||
{
|
||||
if (j == i)
|
||||
continue;
|
||||
d = DotProduct (w->p[j], edgenormal);
|
||||
if (d > edgedist)
|
||||
Error ("CheckWinding: non-convex");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
WindingOnPlaneSide
|
||||
============
|
||||
*/
|
||||
int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist)
|
||||
{
|
||||
qboolean front, back;
|
||||
int i;
|
||||
vec_t d;
|
||||
|
||||
front = false;
|
||||
back = false;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
d = DotProduct (w->p[i], normal) - dist;
|
||||
if (d < -ON_EPSILON)
|
||||
{
|
||||
if (front)
|
||||
return SIDE_CROSS;
|
||||
back = true;
|
||||
continue;
|
||||
}
|
||||
if (d > ON_EPSILON)
|
||||
{
|
||||
if (back)
|
||||
return SIDE_CROSS;
|
||||
front = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (back)
|
||||
return SIDE_BACK;
|
||||
if (front)
|
||||
return SIDE_FRONT;
|
||||
return SIDE_ON;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: 2d point inside of winding test (assumes the point resides in the
|
||||
// winding plane)
|
||||
//-----------------------------------------------------------------------------
|
||||
bool PointInWinding( const Vector &pt, winding_t *pWinding )
|
||||
{
|
||||
if( !pWinding )
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
//
|
||||
// NOTE: this will be a quicker way to calculate this, however I don't
|
||||
// know the trick off hand (post dot product tests??)
|
||||
// TODO: look in graphics gems!!!! (cab)
|
||||
//
|
||||
|
||||
Vector edge1, edge2;
|
||||
for( int ndxPt = 0; ndxPt < pWinding->numpoints; ndxPt++ )
|
||||
{
|
||||
edge1 = pWinding->p[ndxPt] - pt;
|
||||
edge2 = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pt;
|
||||
|
||||
VectorNormalize( edge1 );
|
||||
VectorNormalize( edge2 );
|
||||
|
||||
if( edge2.Dot( edge1 ) < 0.0f )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
#else
|
||||
Vector edge, toPt, cross, testCross;
|
||||
|
||||
//
|
||||
// get the first normal to test
|
||||
//
|
||||
toPt = pt - pWinding->p[0];
|
||||
edge = pWinding->p[1] - pWinding->p[0];
|
||||
testCross = edge.Cross( toPt );
|
||||
VectorNormalize( testCross );
|
||||
|
||||
for( int ndxPt = 1; ndxPt < pWinding->numpoints; ndxPt++ )
|
||||
{
|
||||
toPt = pt - pWinding->p[ndxPt];
|
||||
edge = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pWinding->p[ndxPt];
|
||||
cross = edge.Cross( toPt );
|
||||
VectorNormalize( cross );
|
||||
|
||||
if( cross.Dot( testCross ) < 0.0f )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void TranslateWinding( winding_t *pWinding, const Vector &offset )
|
||||
{
|
||||
for ( int i = 0; i < pWinding->numpoints; i++ )
|
||||
{
|
||||
pWinding->p[i] += offset;
|
||||
}
|
||||
}
|
78
utils/common/polylib.h
Normal file
78
utils/common/polylib.h
Normal file
@ -0,0 +1,78 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef POLYLIB_H
|
||||
#define POLYLIB_H
|
||||
#pragma once
|
||||
|
||||
#ifndef MATHLIB_H
|
||||
#include "mathlib/mathlib.h"
|
||||
#endif
|
||||
|
||||
struct winding_t
|
||||
{
|
||||
int numpoints;
|
||||
Vector *p; // variable sized
|
||||
int maxpoints;
|
||||
winding_t *next;
|
||||
};
|
||||
|
||||
#define MAX_POINTS_ON_WINDING 64
|
||||
|
||||
// you can define on_epsilon in the makefile as tighter
|
||||
// point on plane side epsilon
|
||||
// todo: need a world-space epsilon, a lightmap-space epsilon, and a texture space epsilon
|
||||
// or at least convert from a world-space epsilon to lightmap and texture space epsilons
|
||||
#ifndef ON_EPSILON
|
||||
#define ON_EPSILON 0.1
|
||||
#endif
|
||||
|
||||
|
||||
winding_t *AllocWinding (int points);
|
||||
vec_t WindingArea (winding_t *w);
|
||||
void WindingCenter (winding_t *w, Vector ¢er);
|
||||
vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er );
|
||||
void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist,
|
||||
vec_t epsilon, winding_t **front, winding_t **back);
|
||||
|
||||
// translates everything by offset, then does the clip, then translates back (to keep precision)
|
||||
void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset );
|
||||
|
||||
void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist,
|
||||
vec_t epsilon, winding_t **front, winding_t **back, winding_t **on);
|
||||
void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist,
|
||||
vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset);
|
||||
|
||||
winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist);
|
||||
winding_t *CopyWinding (winding_t *w);
|
||||
winding_t *ReverseWinding (winding_t *w);
|
||||
winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist);
|
||||
void CheckWinding (winding_t *w);
|
||||
void WindingPlane (winding_t *w, Vector &normal, vec_t *dist);
|
||||
void RemoveColinearPoints (winding_t *w);
|
||||
int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist);
|
||||
void FreeWinding (winding_t *w);
|
||||
void WindingBounds (winding_t *w, Vector &mins, Vector &maxs);
|
||||
|
||||
void ChopWindingInPlace (winding_t **w, const Vector &normal, vec_t dist, vec_t epsilon);
|
||||
// frees the original if clipped
|
||||
|
||||
bool PointInWinding( Vector const &pt, winding_t *pWinding );
|
||||
|
||||
// translates a winding by offset
|
||||
void TranslateWinding( winding_t *pWinding, const Vector &offset );
|
||||
|
||||
void pw(winding_t *w);
|
||||
|
||||
|
||||
#endif // POLYLIB_H
|
42
utils/common/qfiles.h
Normal file
42
utils/common/qfiles.h
Normal file
@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef QFILES_H
|
||||
#define QFILES_H
|
||||
#pragma once
|
||||
|
||||
|
||||
//
|
||||
// qfiles.h: quake file formats
|
||||
// This file must be identical in the quake and utils directories
|
||||
//
|
||||
|
||||
#include "basetypes.h"
|
||||
#include "commonmacros.h"
|
||||
#include "worldsize.h"
|
||||
#include "bspfile.h"
|
||||
|
||||
#define MAX_OSPATH 260
|
||||
#define MAX_QPATH 64
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
The .pak files are just a linear collapse of a directory tree
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
#define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P')
|
||||
|
||||
#endif // QFILES_H
|
103
utils/common/scratchpad_helpers.cpp
Normal file
103
utils/common/scratchpad_helpers.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "scratchpad_helpers.h"
|
||||
#include "bspfile.h"
|
||||
#include "bsplib.h"
|
||||
|
||||
|
||||
void ScratchPad_DrawWinding(
|
||||
IScratchPad3D *pPad,
|
||||
int nPoints,
|
||||
Vector *pPoints,
|
||||
Vector vColor,
|
||||
Vector vOffset )
|
||||
{
|
||||
for ( int i=0; i < nPoints; i++ )
|
||||
{
|
||||
pPad->DrawLine( CSPVert( pPoints[i]+vOffset, vColor ), CSPVert( pPoints[(i+1)%nPoints]+vOffset, vColor ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber, const CSPColor &faceColor, const Vector &vOffset )
|
||||
{
|
||||
// Draw the face's outline, then put text for its face index on it too.
|
||||
CUtlVector<Vector> points;
|
||||
for ( int iEdge = 0; iEdge < f->numedges; iEdge++ )
|
||||
{
|
||||
int v;
|
||||
int se = dsurfedges[f->firstedge + iEdge];
|
||||
if ( se < 0 )
|
||||
v = dedges[-se].v[1];
|
||||
else
|
||||
v = dedges[se].v[0];
|
||||
|
||||
dvertex_t *dv = &dvertexes[v];
|
||||
points.AddToTail( dv->point );
|
||||
}
|
||||
|
||||
// Draw the outline.
|
||||
Vector vCenter( 0, 0, 0 );
|
||||
for ( int iEdge=0; iEdge < points.Count(); iEdge++ )
|
||||
{
|
||||
pPad->DrawLine( CSPVert( points[iEdge]+vOffset, faceColor ), CSPVert( points[(iEdge+1)%points.Count()]+vOffset, faceColor ) );
|
||||
vCenter += points[iEdge];
|
||||
}
|
||||
vCenter /= points.Count();
|
||||
vCenter += vOffset;
|
||||
|
||||
// Draw the text.
|
||||
if ( iFaceNumber != -1 )
|
||||
{
|
||||
char str[64];
|
||||
Q_snprintf( str, sizeof( str ), "%d", iFaceNumber );
|
||||
|
||||
CTextParams params;
|
||||
|
||||
params.m_bCentered = true;
|
||||
params.m_bOutline = true;
|
||||
params.m_flLetterWidth = 2;
|
||||
params.m_vColor.Init( 1, 0, 0 );
|
||||
|
||||
VectorAngles( dplanes[f->planenum].normal, params.m_vAngles );
|
||||
params.m_bTwoSided = true;
|
||||
|
||||
params.m_vPos = vCenter;
|
||||
|
||||
pPad->DrawText( str, params );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor )
|
||||
{
|
||||
bool bAutoFlush = pPad->GetAutoFlush();
|
||||
pPad->SetAutoFlush( false );
|
||||
|
||||
for ( int i=0; i < numleafs; i++ )
|
||||
{
|
||||
dleaf_t *l = &dleafs[i];
|
||||
if ( l->contents & CONTENTS_DETAIL )
|
||||
continue;
|
||||
|
||||
for ( int z=0; z < l->numleaffaces; z++ )
|
||||
{
|
||||
int iFace = dleaffaces[l->firstleafface+z];
|
||||
dface_t *f = &dfaces[iFace];
|
||||
ScratchPad_DrawFace( pPad, f, bDrawFaceNumbers ? i : -1 );
|
||||
}
|
||||
}
|
||||
|
||||
pPad->SetAutoFlush( bAutoFlush );
|
||||
}
|
||||
|
||||
|
||||
void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor )
|
||||
{
|
||||
IScratchPad3D *pPad = ScratchPad3D_Create();
|
||||
ScratchPad_DrawWorld( pPad, bDrawFaceNumbers );
|
||||
}
|
25
utils/common/scratchpad_helpers.h
Normal file
25
utils/common/scratchpad_helpers.h
Normal file
@ -0,0 +1,25 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef SCRATCHPAD_HELPERS_H
|
||||
#define SCRATCHPAD_HELPERS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "iscratchpad3d.h"
|
||||
#include "bspfile.h"
|
||||
|
||||
|
||||
void ScratchPad_DrawWinding( IScratchPad3D *pPad, int nPoints, Vector *pPoints, Vector vColor, Vector vOffset = Vector(0,0,0) );
|
||||
|
||||
void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber = -1, const CSPColor &faceColor=CSPColor(1,1,1,1), const Vector &vOffset=Vector(0,0,0) );
|
||||
void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) );
|
||||
void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) );
|
||||
|
||||
|
||||
#endif // SCRATCHPAD_HELPERS_H
|
1349
utils/common/scriplib.cpp
Normal file
1349
utils/common/scriplib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
96
utils/common/scriplib.h
Normal file
96
utils/common/scriplib.h
Normal file
@ -0,0 +1,96 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef SCRIPLIB_H
|
||||
#define SCRIPLIB_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
enum ScriptPathMode_t
|
||||
{
|
||||
SCRIPT_USE_ABSOLUTE_PATH,
|
||||
SCRIPT_USE_RELATIVE_PATH
|
||||
};
|
||||
|
||||
|
||||
// scriplib.h
|
||||
|
||||
#define MAXTOKEN 1024
|
||||
|
||||
extern char token[MAXTOKEN];
|
||||
extern char *scriptbuffer,*script_p,*scriptend_p;
|
||||
extern int grabbed;
|
||||
extern int scriptline;
|
||||
extern qboolean endofscript;
|
||||
|
||||
|
||||
// If pathMode is SCRIPT_USE_ABSOLUTE_PATH, then it uses ExpandPath() on the filename before
|
||||
// trying to open it. Otherwise, it passes the filename straight into the filesystem
|
||||
// (so you can leave it as a relative path).
|
||||
void LoadScriptFile (char *filename, ScriptPathMode_t pathMode=SCRIPT_USE_ABSOLUTE_PATH);
|
||||
void ParseFromMemory (char *buffer, int size);
|
||||
|
||||
qboolean GetToken (qboolean crossline);
|
||||
qboolean GetExprToken (qboolean crossline);
|
||||
void UnGetToken (void);
|
||||
qboolean TokenAvailable (void);
|
||||
qboolean GetTokenizerStatus( char **pFilename, int *pLine );
|
||||
bool SetCheckSingleCharTokens( bool bCheck );
|
||||
|
||||
// SCRIPT_LOADED_CALLBACK:
|
||||
// Is called after the contents of a file is loaded.
|
||||
// pFilenameLoaded is the path of a file that got loaded.
|
||||
// pIncludedFromFileName is the name of the parent file or NULL if loaded because of "LoadScriptFile" toplevel call.
|
||||
// nIncludeLineNumber is the number of the line in the parent file with $include statement or 0 in case of "LoadScriptFile"
|
||||
typedef void ( * SCRIPT_LOADED_CALLBACK )( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber );
|
||||
|
||||
// SetScriptLoadedCallback:
|
||||
// Sets the new callback for script loading.
|
||||
// Returns the previous callback function.
|
||||
SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback );
|
||||
|
||||
#include "tier1/utlstring.h"
|
||||
#include "tier1/utlvector.h"
|
||||
|
||||
CUtlString SetSingleCharTokenList( const char *pszSingleCharTokenList );
|
||||
|
||||
class CUtlBuffer;
|
||||
|
||||
enum DiskWriteMode_t
|
||||
{
|
||||
WRITE_TO_DISK_NEVER,
|
||||
WRITE_TO_DISK_ALWAYS,
|
||||
WRITE_TO_DISK_UPDATE, // file must exist
|
||||
};
|
||||
|
||||
struct fileList_t
|
||||
{
|
||||
CUtlString fileName;
|
||||
time_t timeWrite;
|
||||
};
|
||||
|
||||
class IScriptLib
|
||||
{
|
||||
public:
|
||||
virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false ) = 0;
|
||||
virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ) = 0;
|
||||
virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList ) = 0;
|
||||
virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ) = 0;
|
||||
virtual void DeleteTemporaryFiles( const char *pFileMask ) = 0;
|
||||
virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB ) = 0;
|
||||
virtual bool DoesFileExist( const char *pFilename ) = 0;
|
||||
};
|
||||
|
||||
extern IScriptLib *scriptlib;
|
||||
|
||||
|
||||
#endif // SCRIPLIB_H
|
257
utils/common/threads.cpp
Normal file
257
utils/common/threads.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#define USED
|
||||
|
||||
#include <windows.h>
|
||||
#include "cmdlib.h"
|
||||
#define NO_THREAD_NAMES
|
||||
#include "threads.h"
|
||||
#include "pacifier.h"
|
||||
|
||||
#define MAX_THREADS 16
|
||||
|
||||
|
||||
class CRunThreadsData
|
||||
{
|
||||
public:
|
||||
int m_iThread;
|
||||
void *m_pUserData;
|
||||
RunThreadsFn m_Fn;
|
||||
};
|
||||
|
||||
CRunThreadsData g_RunThreadsData[MAX_THREADS];
|
||||
|
||||
|
||||
int dispatch;
|
||||
int workcount;
|
||||
qboolean pacifier;
|
||||
|
||||
qboolean threaded;
|
||||
bool g_bLowPriorityThreads = false;
|
||||
|
||||
HANDLE g_ThreadHandles[MAX_THREADS];
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
GetThreadWork
|
||||
|
||||
=============
|
||||
*/
|
||||
int GetThreadWork (void)
|
||||
{
|
||||
int r;
|
||||
|
||||
ThreadLock ();
|
||||
|
||||
if (dispatch == workcount)
|
||||
{
|
||||
ThreadUnlock ();
|
||||
return -1;
|
||||
}
|
||||
|
||||
UpdatePacifier( (float)dispatch / workcount );
|
||||
|
||||
r = dispatch;
|
||||
dispatch++;
|
||||
ThreadUnlock ();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
ThreadWorkerFn workfunction;
|
||||
|
||||
void ThreadWorkerFunction( int iThread, void *pUserData )
|
||||
{
|
||||
int work;
|
||||
|
||||
while (1)
|
||||
{
|
||||
work = GetThreadWork ();
|
||||
if (work == -1)
|
||||
break;
|
||||
|
||||
workfunction( iThread, work );
|
||||
}
|
||||
}
|
||||
|
||||
void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, ThreadWorkerFn func)
|
||||
{
|
||||
if (numthreads == -1)
|
||||
ThreadSetDefault ();
|
||||
|
||||
workfunction = func;
|
||||
RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================================================================
|
||||
|
||||
WIN32
|
||||
|
||||
===================================================================
|
||||
*/
|
||||
|
||||
int numthreads = -1;
|
||||
CRITICAL_SECTION crit;
|
||||
static int enter;
|
||||
|
||||
|
||||
class CCritInit
|
||||
{
|
||||
public:
|
||||
CCritInit()
|
||||
{
|
||||
InitializeCriticalSection (&crit);
|
||||
}
|
||||
} g_CritInit;
|
||||
|
||||
|
||||
|
||||
void SetLowPriority()
|
||||
{
|
||||
SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );
|
||||
}
|
||||
|
||||
|
||||
void ThreadSetDefault (void)
|
||||
{
|
||||
SYSTEM_INFO info;
|
||||
|
||||
if (numthreads == -1) // not set manually
|
||||
{
|
||||
GetSystemInfo (&info);
|
||||
numthreads = info.dwNumberOfProcessors;
|
||||
if (numthreads < 1 || numthreads > 32)
|
||||
numthreads = 1;
|
||||
}
|
||||
|
||||
Msg ("%i threads\n", numthreads);
|
||||
}
|
||||
|
||||
|
||||
void ThreadLock (void)
|
||||
{
|
||||
if (!threaded)
|
||||
return;
|
||||
EnterCriticalSection (&crit);
|
||||
if (enter)
|
||||
Error ("Recursive ThreadLock\n");
|
||||
enter = 1;
|
||||
}
|
||||
|
||||
void ThreadUnlock (void)
|
||||
{
|
||||
if (!threaded)
|
||||
return;
|
||||
if (!enter)
|
||||
Error ("ThreadUnlock without lock\n");
|
||||
enter = 0;
|
||||
LeaveCriticalSection (&crit);
|
||||
}
|
||||
|
||||
|
||||
// This runs in the thread and dispatches a RunThreadsFn call.
|
||||
DWORD WINAPI InternalRunThreadsFn( LPVOID pParameter )
|
||||
{
|
||||
CRunThreadsData *pData = (CRunThreadsData*)pParameter;
|
||||
pData->m_Fn( pData->m_iThread, pData->m_pUserData );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority )
|
||||
{
|
||||
Assert( numthreads > 0 );
|
||||
threaded = true;
|
||||
|
||||
if ( numthreads > MAX_TOOL_THREADS )
|
||||
numthreads = MAX_TOOL_THREADS;
|
||||
|
||||
for ( int i=0; i < numthreads ;i++ )
|
||||
{
|
||||
g_RunThreadsData[i].m_iThread = i;
|
||||
g_RunThreadsData[i].m_pUserData = pUserData;
|
||||
g_RunThreadsData[i].m_Fn = fn;
|
||||
|
||||
DWORD dwDummy;
|
||||
g_ThreadHandles[i] = CreateThread(
|
||||
NULL, // LPSECURITY_ATTRIBUTES lpsa,
|
||||
0, // DWORD cbStack,
|
||||
InternalRunThreadsFn, // LPTHREAD_START_ROUTINE lpStartAddr,
|
||||
&g_RunThreadsData[i], // LPVOID lpvThreadParm,
|
||||
0, // DWORD fdwCreate,
|
||||
&dwDummy );
|
||||
|
||||
if ( ePriority == k_eRunThreadsPriority_UseGlobalState )
|
||||
{
|
||||
if( g_bLowPriorityThreads )
|
||||
SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_LOWEST );
|
||||
}
|
||||
else if ( ePriority == k_eRunThreadsPriority_Idle )
|
||||
{
|
||||
SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_IDLE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RunThreads_End()
|
||||
{
|
||||
WaitForMultipleObjects( numthreads, g_ThreadHandles, TRUE, INFINITE );
|
||||
for ( int i=0; i < numthreads; i++ )
|
||||
CloseHandle( g_ThreadHandles[i] );
|
||||
|
||||
threaded = false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RunThreadsOn
|
||||
=============
|
||||
*/
|
||||
void RunThreadsOn( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData )
|
||||
{
|
||||
int start, end;
|
||||
|
||||
start = Plat_FloatTime();
|
||||
dispatch = 0;
|
||||
workcount = workcnt;
|
||||
StartPacifier("");
|
||||
pacifier = showpacifier;
|
||||
|
||||
#ifdef _PROFILE
|
||||
threaded = false;
|
||||
(*func)( 0 );
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
RunThreads_Start( fn, pUserData );
|
||||
RunThreads_End();
|
||||
|
||||
|
||||
end = Plat_FloatTime();
|
||||
if (pacifier)
|
||||
{
|
||||
EndPacifier(false);
|
||||
printf (" (%i)\n", end-start);
|
||||
}
|
||||
}
|
||||
|
||||
|
65
utils/common/threads.h
Normal file
65
utils/common/threads.h
Normal file
@ -0,0 +1,65 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef THREADS_H
|
||||
#define THREADS_H
|
||||
#pragma once
|
||||
|
||||
|
||||
// Arrays that are indexed by thread should always be MAX_TOOL_THREADS+1
|
||||
// large so THREADINDEX_MAIN can be used from the main thread.
|
||||
#define MAX_TOOL_THREADS 16
|
||||
#define THREADINDEX_MAIN (MAX_TOOL_THREADS)
|
||||
|
||||
|
||||
extern int numthreads;
|
||||
|
||||
// If set to true, then all the threads that are created are low priority.
|
||||
extern bool g_bLowPriorityThreads;
|
||||
|
||||
typedef void (*ThreadWorkerFn)( int iThread, int iWorkItem );
|
||||
typedef void (*RunThreadsFn)( int iThread, void *pUserData );
|
||||
|
||||
|
||||
enum ERunThreadsPriority
|
||||
{
|
||||
k_eRunThreadsPriority_UseGlobalState=0, // Default.. uses g_bLowPriorityThreads to decide what to set the priority to.
|
||||
k_eRunThreadsPriority_Normal, // Doesn't touch thread priorities.
|
||||
k_eRunThreadsPriority_Idle // Sets threads to idle priority.
|
||||
};
|
||||
|
||||
|
||||
// Put the process into an idle priority class so it doesn't hog the UI.
|
||||
void SetLowPriority();
|
||||
|
||||
void ThreadSetDefault (void);
|
||||
int GetThreadWork (void);
|
||||
|
||||
void RunThreadsOnIndividual ( int workcnt, qboolean showpacifier, ThreadWorkerFn fn );
|
||||
|
||||
void RunThreadsOn ( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData=NULL );
|
||||
|
||||
// This version doesn't track work items - it just runs your function and waits for it to finish.
|
||||
void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority=k_eRunThreadsPriority_UseGlobalState );
|
||||
void RunThreads_End();
|
||||
|
||||
void ThreadLock (void);
|
||||
void ThreadUnlock (void);
|
||||
|
||||
|
||||
#ifndef NO_THREAD_NAMES
|
||||
#define RunThreadsOn(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOn(n,p,f); }
|
||||
#define RunThreadsOnIndividual(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOnIndividual(n,p,f); }
|
||||
#endif
|
||||
|
||||
#endif // THREADS_H
|
61
utils/common/tools_minidump.cpp
Normal file
61
utils/common/tools_minidump.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include "tier0/minidump.h"
|
||||
#include "tools_minidump.h"
|
||||
|
||||
static bool g_bToolsWriteFullMinidumps = false;
|
||||
static ToolsExceptionHandler g_pCustomExceptionHandler = NULL;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------- //
|
||||
// Internal helpers.
|
||||
// --------------------------------------------------------------------------------- //
|
||||
|
||||
static LONG __stdcall ToolsExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
|
||||
{
|
||||
// Non VMPI workers write a minidump and show a crash dialog like normal.
|
||||
int iType = MiniDumpNormal;
|
||||
if ( g_bToolsWriteFullMinidumps )
|
||||
iType = MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory;
|
||||
|
||||
WriteMiniDumpUsingExceptionInfo( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo, (MINIDUMP_TYPE)iType );
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
||||
static LONG __stdcall ToolsExceptionFilter_Custom( struct _EXCEPTION_POINTERS *ExceptionInfo )
|
||||
{
|
||||
// Run their custom handler.
|
||||
g_pCustomExceptionHandler( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo );
|
||||
return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway)
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------- //
|
||||
// Interface functions.
|
||||
// --------------------------------------------------------------------------------- //
|
||||
|
||||
void EnableFullMinidumps( bool bFull )
|
||||
{
|
||||
g_bToolsWriteFullMinidumps = bFull;
|
||||
}
|
||||
|
||||
|
||||
void SetupDefaultToolsMinidumpHandler()
|
||||
{
|
||||
SetUnhandledExceptionFilter( ToolsExceptionFilter );
|
||||
}
|
||||
|
||||
|
||||
void SetupToolsMinidumpHandler( ToolsExceptionHandler fn )
|
||||
{
|
||||
g_pCustomExceptionHandler = fn;
|
||||
SetUnhandledExceptionFilter( ToolsExceptionFilter_Custom );
|
||||
}
|
35
utils/common/tools_minidump.h
Normal file
35
utils/common/tools_minidump.h
Normal file
@ -0,0 +1,35 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TOOLS_MINIDUMP_H
|
||||
#define TOOLS_MINIDUMP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Defaults to false. If true, it'll write larger minidump files with the contents
|
||||
// of global variables and following pointers from where the crash occurred.
|
||||
void EnableFullMinidumps( bool bFull );
|
||||
|
||||
|
||||
// This handler catches any crash, writes a minidump, and runs the default system
|
||||
// crash handler (which usually shows a dialog).
|
||||
void SetupDefaultToolsMinidumpHandler();
|
||||
|
||||
|
||||
// (Used by VMPI) - you specify your own crash handler.
|
||||
// Arguments passed to ToolsExceptionHandler
|
||||
// exceptionCode - exception code
|
||||
// pvExceptionInfo - on Win32 platform points to "struct _EXCEPTION_POINTERS"
|
||||
// otherwise NULL
|
||||
//
|
||||
typedef void (*ToolsExceptionHandler)( unsigned long exceptionCode, void *pvExceptionInfo );
|
||||
void SetupToolsMinidumpHandler( ToolsExceptionHandler fn );
|
||||
|
||||
|
||||
#endif // MINIDUMP_H
|
184
utils/common/utilmatlib.cpp
Normal file
184
utils/common/utilmatlib.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
// C callable material system interface for the utils.
|
||||
|
||||
#include "materialsystem/imaterialsystem.h"
|
||||
#include "materialsystem/imaterial.h"
|
||||
#include "materialsystem/imaterialvar.h"
|
||||
#include <cmdlib.h>
|
||||
#include "utilmatlib.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include <windows.h>
|
||||
#include "filesystem.h"
|
||||
#include "materialsystem/materialsystem_config.h"
|
||||
#include "mathlib/Mathlib.h"
|
||||
|
||||
void LoadMaterialSystemInterface( CreateInterfaceFn fileSystemFactory )
|
||||
{
|
||||
if( g_pMaterialSystem )
|
||||
return;
|
||||
|
||||
// materialsystem.dll should be in the path, it's in bin along with vbsp.
|
||||
const char *pDllName = "materialsystem.dll";
|
||||
CSysModule *materialSystemDLLHInst;
|
||||
materialSystemDLLHInst = g_pFullFileSystem->LoadModule( pDllName );
|
||||
if( !materialSystemDLLHInst )
|
||||
{
|
||||
Error( "Can't load MaterialSystem.dll\n" );
|
||||
}
|
||||
|
||||
CreateInterfaceFn clientFactory = Sys_GetFactory( materialSystemDLLHInst );
|
||||
if ( clientFactory )
|
||||
{
|
||||
g_pMaterialSystem = (IMaterialSystem *)clientFactory( MATERIAL_SYSTEM_INTERFACE_VERSION, NULL );
|
||||
if ( !g_pMaterialSystem )
|
||||
{
|
||||
Error( "Could not get the material system interface from materialsystem.dll (" __FILE__ ")" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Could not find factory interface in library MaterialSystem.dll" );
|
||||
}
|
||||
|
||||
if (!g_pMaterialSystem->Init( "shaderapiempty.dll", 0, fileSystemFactory ))
|
||||
{
|
||||
Error( "Could not start the empty shader (shaderapiempty.dll)!" );
|
||||
}
|
||||
}
|
||||
|
||||
void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory )
|
||||
{
|
||||
LoadMaterialSystemInterface( fileSystemFactory );
|
||||
MaterialSystem_Config_t config;
|
||||
g_pMaterialSystem->OverrideConfig( config, false );
|
||||
}
|
||||
|
||||
void ShutdownMaterialSystem( )
|
||||
{
|
||||
if ( g_pMaterialSystem )
|
||||
{
|
||||
g_pMaterialSystem->Shutdown();
|
||||
g_pMaterialSystem = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain )
|
||||
{
|
||||
IMaterial *pMat = g_pMaterialSystem->FindMaterial( materialName, TEXTURE_GROUP_OTHER, bComplain );
|
||||
MaterialSystemMaterial_t matHandle = pMat;
|
||||
|
||||
if ( pFound )
|
||||
{
|
||||
*pFound = true;
|
||||
if ( IsErrorMaterial( pMat ) )
|
||||
*pFound = false;
|
||||
}
|
||||
|
||||
return matHandle;
|
||||
}
|
||||
|
||||
void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height )
|
||||
{
|
||||
PreviewImageRetVal_t retVal;
|
||||
ImageFormat dummyImageFormat;
|
||||
IMaterial *material = ( IMaterial * )materialHandle;
|
||||
bool translucent;
|
||||
retVal = material->GetPreviewImageProperties( width, height, &dummyImageFormat, &translucent );
|
||||
if (retVal != MATERIAL_PREVIEW_IMAGE_OK )
|
||||
{
|
||||
#if 0
|
||||
if (retVal == MATERIAL_PREVIEW_IMAGE_BAD )
|
||||
{
|
||||
Error( "problem getting preview image for %s",
|
||||
g_pMaterialSystem->GetMaterialName( materialInfo[matID].materialHandle ) );
|
||||
}
|
||||
#else
|
||||
*width = 128;
|
||||
*height = 128;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect )
|
||||
{
|
||||
IMaterial *material = ( IMaterial * )materialHandle;
|
||||
const IMaterialVar *reflectivityVar;
|
||||
|
||||
bool found;
|
||||
reflectivityVar = material->FindVar( "$reflectivity", &found, false );
|
||||
if( !found )
|
||||
{
|
||||
Vector tmp;
|
||||
material->GetReflectivity( tmp );
|
||||
VectorCopy( tmp.Base(), reflectivityVect );
|
||||
}
|
||||
else
|
||||
{
|
||||
reflectivityVar->GetVecValue( reflectivityVect, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID )
|
||||
{
|
||||
IMaterial *material = ( IMaterial * )materialHandle;
|
||||
switch( propID )
|
||||
{
|
||||
case UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS:
|
||||
return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
|
||||
|
||||
case UTILMATLIB_NEEDS_LIGHTMAP:
|
||||
return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP );
|
||||
|
||||
default:
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID )
|
||||
{
|
||||
IMaterial *material = ( IMaterial * )materialHandle;
|
||||
switch( propID )
|
||||
{
|
||||
case UTILMATLIB_OPACITY:
|
||||
if (material->IsTranslucent())
|
||||
return UTILMATLIB_TRANSLUCENT;
|
||||
if (material->IsAlphaTested())
|
||||
return UTILMATLIB_ALPHATEST;
|
||||
return UTILMATLIB_OPAQUE;
|
||||
|
||||
default:
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName )
|
||||
{
|
||||
IMaterial *material = ( IMaterial * )materialHandle;
|
||||
IMaterialVar *var;
|
||||
bool found;
|
||||
var = material->FindVar( propertyName, &found, false );
|
||||
if( found )
|
||||
{
|
||||
return var->GetStringValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle )
|
||||
{
|
||||
IMaterial *material = ( IMaterial * )materialHandle;
|
||||
return material->GetShaderName();
|
||||
}
|
41
utils/common/utilmatlib.h
Normal file
41
utils/common/utilmatlib.h
Normal file
@ -0,0 +1,41 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef UTILMATLIB_H
|
||||
#define UTILMATLIB_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#define MATERIAL_NOT_FOUND NULL
|
||||
|
||||
class IMaterialSystem;
|
||||
extern IMaterialSystem *g_pMaterialSystem;
|
||||
|
||||
typedef void *MaterialSystemMaterial_t;
|
||||
|
||||
#define UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS 0
|
||||
#define UTILMATLIB_NEEDS_LIGHTMAP 1
|
||||
#define UTILMATLIB_OPACITY 2
|
||||
|
||||
enum { UTILMATLIB_ALPHATEST = 0, UTILMATLIB_OPAQUE, UTILMATLIB_TRANSLUCENT };
|
||||
|
||||
void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory );
|
||||
void ShutdownMaterialSystem( );
|
||||
MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain = true );
|
||||
void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height );
|
||||
int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID );
|
||||
int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID );
|
||||
const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName );
|
||||
void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect );
|
||||
const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle );
|
||||
|
||||
|
||||
#endif // UTILMATLIB_H
|
374
utils/common/vmpi_tools_shared.cpp
Normal file
374
utils/common/vmpi_tools_shared.cpp
Normal file
@ -0,0 +1,374 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include "vmpi.h"
|
||||
#include "cmdlib.h"
|
||||
#include "vmpi_tools_shared.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "mpi_stats.h"
|
||||
#include "iphelpers.h"
|
||||
#include "tier0/minidump.h"
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- //
|
||||
// Globals.
|
||||
// ----------------------------------------------------------------------------- //
|
||||
|
||||
static bool g_bReceivedDirectoryInfo = false; // Have we gotten the qdir info yet?
|
||||
|
||||
static bool g_bReceivedDBInfo = false;
|
||||
static CDBInfo g_DBInfo;
|
||||
static unsigned long g_JobPrimaryID;
|
||||
|
||||
static int g_nDisconnects = 0; // Tracks how many remote processes have disconnected ungracefully.
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- //
|
||||
// Shared dispatch code.
|
||||
// ----------------------------------------------------------------------------- //
|
||||
|
||||
bool SharedDispatch( MessageBuffer *pBuf, int iSource, int iPacketID )
|
||||
{
|
||||
char *pInPos = &pBuf->data[2];
|
||||
|
||||
switch ( pBuf->data[1] )
|
||||
{
|
||||
case VMPI_SUBPACKETID_DIRECTORIES:
|
||||
{
|
||||
Q_strncpy( gamedir, pInPos, sizeof( gamedir ) );
|
||||
pInPos += strlen( pInPos ) + 1;
|
||||
|
||||
Q_strncpy( qdir, pInPos, sizeof( qdir ) );
|
||||
|
||||
g_bReceivedDirectoryInfo = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
case VMPI_SUBPACKETID_DBINFO:
|
||||
{
|
||||
g_DBInfo = *((CDBInfo*)pInPos);
|
||||
pInPos += sizeof( CDBInfo );
|
||||
g_JobPrimaryID = *((unsigned long*)pInPos);
|
||||
|
||||
g_bReceivedDBInfo = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
case VMPI_SUBPACKETID_CRASH:
|
||||
{
|
||||
char const chCrashInfoType = *pInPos;
|
||||
pInPos += 2;
|
||||
switch ( chCrashInfoType )
|
||||
{
|
||||
case 't':
|
||||
Warning( "\nWorker '%s' dead: %s\n", VMPI_GetMachineName( iSource ), pInPos );
|
||||
break;
|
||||
case 'f':
|
||||
{
|
||||
int iFileSize = * reinterpret_cast< int const * >( pInPos );
|
||||
pInPos += sizeof( iFileSize );
|
||||
|
||||
// Temp folder
|
||||
char const *szFolder = NULL;
|
||||
if ( !szFolder ) szFolder = getenv( "TEMP" );
|
||||
if ( !szFolder ) szFolder = getenv( "TMP" );
|
||||
if ( !szFolder ) szFolder = "c:";
|
||||
|
||||
// Base module name
|
||||
char chModuleName[_MAX_PATH], *pModuleName = chModuleName;
|
||||
::GetModuleFileName( NULL, chModuleName, sizeof( chModuleName ) / sizeof( chModuleName[0] ) );
|
||||
|
||||
if ( char *pch = strrchr( chModuleName, '.' ) )
|
||||
*pch = 0;
|
||||
if ( char *pch = strrchr( chModuleName, '\\' ) )
|
||||
*pch = 0, pModuleName = pch + 1;
|
||||
|
||||
// Current time
|
||||
time_t currTime = ::time( NULL );
|
||||
struct tm * pTime = ::localtime( &currTime );
|
||||
|
||||
// Number of minidumps this run
|
||||
static int s_numMiniDumps = 0;
|
||||
++ s_numMiniDumps;
|
||||
|
||||
// Prepare the filename
|
||||
char chSaveFileName[ 2 * _MAX_PATH ] = { 0 };
|
||||
sprintf( chSaveFileName, "%s\\vmpi_%s_on_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp",
|
||||
szFolder,
|
||||
pModuleName,
|
||||
VMPI_GetMachineName( iSource ),
|
||||
pTime->tm_year + 1900, /* Year less 2000 */
|
||||
pTime->tm_mon + 1, /* month (0 - 11 : 0 = January) */
|
||||
pTime->tm_mday, /* day of month (1 - 31) */
|
||||
pTime->tm_hour, /* hour (0 - 23) */
|
||||
pTime->tm_min, /* minutes (0 - 59) */
|
||||
pTime->tm_sec, /* seconds (0 - 59) */
|
||||
s_numMiniDumps
|
||||
);
|
||||
|
||||
if ( FILE *fDump = fopen( chSaveFileName, "wb" ) )
|
||||
{
|
||||
fwrite( pInPos, 1, iFileSize, fDump );
|
||||
fclose( fDump );
|
||||
|
||||
Warning( "\nSaved worker crash minidump '%s', size %d byte(s).\n",
|
||||
chSaveFileName, iFileSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "\nReceived worker crash minidump size %d byte(s), failed to save.\n", iFileSize );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CDispatchReg g_SharedDispatchReg( VMPI_SHARED_PACKET_ID, SharedDispatch );
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- //
|
||||
// Module interfaces.
|
||||
// ----------------------------------------------------------------------------- //
|
||||
|
||||
void SendQDirInfo()
|
||||
{
|
||||
char cPacketID[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DIRECTORIES };
|
||||
|
||||
MessageBuffer mb;
|
||||
mb.write( cPacketID, 2 );
|
||||
mb.write( gamedir, strlen( gamedir ) + 1 );
|
||||
mb.write( qdir, strlen( qdir ) + 1 );
|
||||
|
||||
VMPI_SendData( mb.data, mb.getLen(), VMPI_PERSISTENT );
|
||||
}
|
||||
|
||||
|
||||
void RecvQDirInfo()
|
||||
{
|
||||
while ( !g_bReceivedDirectoryInfo )
|
||||
VMPI_DispatchNextMessage();
|
||||
}
|
||||
|
||||
|
||||
void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID )
|
||||
{
|
||||
char cPacketInfo[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DBINFO };
|
||||
const void *pChunks[] = { cPacketInfo, pInfo, &jobPrimaryID };
|
||||
int chunkLengths[] = { 2, sizeof( CDBInfo ), sizeof( jobPrimaryID ) };
|
||||
|
||||
VMPI_SendChunks( pChunks, chunkLengths, ARRAYSIZE( pChunks ), VMPI_PERSISTENT );
|
||||
}
|
||||
|
||||
|
||||
void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID )
|
||||
{
|
||||
while ( !g_bReceivedDBInfo )
|
||||
VMPI_DispatchNextMessage();
|
||||
|
||||
*pInfo = g_DBInfo;
|
||||
*pJobPrimaryID = g_JobPrimaryID;
|
||||
}
|
||||
|
||||
// If the file is successfully opened, read and sent returns the size of the file in bytes
|
||||
// otherwise returns 0 and nothing is sent
|
||||
int VMPI_SendFileChunk( const void *pvChunkPrefix, int lenPrefix, tchar const *ptchFileName )
|
||||
{
|
||||
HANDLE hFile = NULL;
|
||||
HANDLE hMapping = NULL;
|
||||
void const *pvMappedData = NULL;
|
||||
int iResult = 0;
|
||||
|
||||
hFile = ::CreateFile( ptchFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
if ( !hFile || ( hFile == INVALID_HANDLE_VALUE ) )
|
||||
goto done;
|
||||
|
||||
hMapping = ::CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
|
||||
if ( !hMapping || ( hMapping == INVALID_HANDLE_VALUE ) )
|
||||
goto done;
|
||||
|
||||
pvMappedData = ::MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
|
||||
if ( !pvMappedData )
|
||||
goto done;
|
||||
|
||||
int iMappedFileSize = ::GetFileSize( hFile, NULL );
|
||||
if ( INVALID_FILE_SIZE == iMappedFileSize )
|
||||
goto done;
|
||||
|
||||
// Send the data over VMPI
|
||||
if ( VMPI_Send3Chunks(
|
||||
pvChunkPrefix, lenPrefix,
|
||||
&iMappedFileSize, sizeof( iMappedFileSize ),
|
||||
pvMappedData, iMappedFileSize,
|
||||
VMPI_MASTER_ID ) )
|
||||
iResult = iMappedFileSize;
|
||||
|
||||
// Fall-through for cleanup code to execute
|
||||
done:
|
||||
if ( pvMappedData )
|
||||
::UnmapViewOfFile( pvMappedData );
|
||||
|
||||
if ( hMapping && ( hMapping != INVALID_HANDLE_VALUE ) )
|
||||
::CloseHandle( hMapping );
|
||||
|
||||
if ( hFile && ( hFile != INVALID_HANDLE_VALUE ) )
|
||||
::CloseHandle( hFile );
|
||||
|
||||
return iResult;
|
||||
}
|
||||
|
||||
void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert )
|
||||
{
|
||||
static LONG crashHandlerCount = 0;
|
||||
if ( InterlockedIncrement( &crashHandlerCount ) == 1 )
|
||||
{
|
||||
Msg( "\nFAILURE: '%s' (assert: %d)\n", pMessage, bAssert );
|
||||
|
||||
// Send a message to the master.
|
||||
char crashMsg[4] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_CRASH, 't', ':' };
|
||||
|
||||
VMPI_Send2Chunks(
|
||||
crashMsg,
|
||||
sizeof( crashMsg ),
|
||||
pMessage,
|
||||
strlen( pMessage ) + 1,
|
||||
VMPI_MASTER_ID );
|
||||
|
||||
// Now attempt to create a minidump with the given exception information
|
||||
if ( pvExceptionInfo )
|
||||
{
|
||||
struct _EXCEPTION_POINTERS *pvExPointers = ( struct _EXCEPTION_POINTERS * ) pvExceptionInfo;
|
||||
tchar tchMinidumpFileName[_MAX_PATH] = { 0 };
|
||||
bool bSucceededWritingMinidump = WriteMiniDumpUsingExceptionInfo(
|
||||
pvExPointers->ExceptionRecord->ExceptionCode,
|
||||
pvExPointers,
|
||||
( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData ),
|
||||
// ( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory ),
|
||||
// ( MINIDUMP_TYPE )( MiniDumpNormal ),
|
||||
tchMinidumpFileName );
|
||||
if ( bSucceededWritingMinidump )
|
||||
{
|
||||
crashMsg[2] = 'f';
|
||||
VMPI_SendFileChunk( crashMsg, sizeof( crashMsg ), tchMinidumpFileName );
|
||||
::DeleteFile( tchMinidumpFileName );
|
||||
}
|
||||
}
|
||||
|
||||
// Let the messages go out.
|
||||
Sleep( 500 );
|
||||
}
|
||||
|
||||
InterlockedDecrement( &crashHandlerCount );
|
||||
}
|
||||
|
||||
|
||||
// This is called if we crash inside our crash handler. It just terminates the process immediately.
|
||||
LONG __stdcall VMPI_SecondExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
|
||||
{
|
||||
TerminateProcess( GetCurrentProcess(), 2 );
|
||||
return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway)
|
||||
}
|
||||
|
||||
|
||||
void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo )
|
||||
{
|
||||
// This is called if we crash inside our crash handler. It just terminates the process immediately.
|
||||
SetUnhandledExceptionFilter( VMPI_SecondExceptionFilter );
|
||||
|
||||
//DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
|
||||
|
||||
#define ERR_RECORD( name ) { name, #name }
|
||||
struct
|
||||
{
|
||||
int code;
|
||||
char *pReason;
|
||||
} errors[] =
|
||||
{
|
||||
ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
|
||||
ERR_RECORD( EXCEPTION_ARRAY_BOUNDS_EXCEEDED ),
|
||||
ERR_RECORD( EXCEPTION_BREAKPOINT ),
|
||||
ERR_RECORD( EXCEPTION_DATATYPE_MISALIGNMENT ),
|
||||
ERR_RECORD( EXCEPTION_FLT_DENORMAL_OPERAND ),
|
||||
ERR_RECORD( EXCEPTION_FLT_DIVIDE_BY_ZERO ),
|
||||
ERR_RECORD( EXCEPTION_FLT_INEXACT_RESULT ),
|
||||
ERR_RECORD( EXCEPTION_FLT_INVALID_OPERATION ),
|
||||
ERR_RECORD( EXCEPTION_FLT_OVERFLOW ),
|
||||
ERR_RECORD( EXCEPTION_FLT_STACK_CHECK ),
|
||||
ERR_RECORD( EXCEPTION_FLT_UNDERFLOW ),
|
||||
ERR_RECORD( EXCEPTION_ILLEGAL_INSTRUCTION ),
|
||||
ERR_RECORD( EXCEPTION_IN_PAGE_ERROR ),
|
||||
ERR_RECORD( EXCEPTION_INT_DIVIDE_BY_ZERO ),
|
||||
ERR_RECORD( EXCEPTION_INT_OVERFLOW ),
|
||||
ERR_RECORD( EXCEPTION_INVALID_DISPOSITION ),
|
||||
ERR_RECORD( EXCEPTION_NONCONTINUABLE_EXCEPTION ),
|
||||
ERR_RECORD( EXCEPTION_PRIV_INSTRUCTION ),
|
||||
ERR_RECORD( EXCEPTION_SINGLE_STEP ),
|
||||
ERR_RECORD( EXCEPTION_STACK_OVERFLOW ),
|
||||
ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
|
||||
};
|
||||
|
||||
int nErrors = sizeof( errors ) / sizeof( errors[0] );
|
||||
int i=0;
|
||||
char *pchReason = NULL;
|
||||
char chUnknownBuffer[32];
|
||||
for ( i; ( i < nErrors ) && !pchReason; i++ )
|
||||
{
|
||||
if ( errors[i].code == uCode )
|
||||
pchReason = errors[i].pReason;
|
||||
}
|
||||
|
||||
if ( i == nErrors )
|
||||
{
|
||||
sprintf( chUnknownBuffer, "Error code 0x%08X", uCode );
|
||||
pchReason = chUnknownBuffer;
|
||||
}
|
||||
|
||||
VMPI_HandleCrash( pchReason, pvExceptionInfo, true );
|
||||
|
||||
TerminateProcess( GetCurrentProcess(), 1 );
|
||||
}
|
||||
|
||||
|
||||
void HandleMPIDisconnect( int procID, const char *pReason )
|
||||
{
|
||||
int nLiveWorkers = VMPI_GetCurrentNumberOfConnections() - g_nDisconnects - 1;
|
||||
|
||||
// We ran into the size limit before and it wasn't readily apparent that the size limit had
|
||||
// been breached, so make sure to show errors about invalid packet sizes..
|
||||
bool bOldSuppress = g_bSuppressPrintfOutput;
|
||||
g_bSuppressPrintfOutput = ( Q_stristr( pReason, "invalid packet size" ) == 0 );
|
||||
|
||||
Warning( "\n\n--- WARNING: lost connection to '%s' (%s).\n", VMPI_GetMachineName( procID ), pReason );
|
||||
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
Warning( "%d workers remain.\n\n", nLiveWorkers );
|
||||
|
||||
++g_nDisconnects;
|
||||
/*
|
||||
if ( VMPI_GetCurrentNumberOfConnections() - g_nDisconnects <= 1 )
|
||||
{
|
||||
Error( "All machines disconnected!" );
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
VMPI_HandleAutoRestart();
|
||||
Error( "Worker quitting." );
|
||||
}
|
||||
|
||||
g_bSuppressPrintfOutput = bOldSuppress;
|
||||
}
|
||||
|
||||
|
45
utils/common/vmpi_tools_shared.h
Normal file
45
utils/common/vmpi_tools_shared.h
Normal file
@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_TOOLS_SHARED_H
|
||||
#define VMPI_TOOLS_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// Packet IDs.
|
||||
#define VMPI_SUBPACKETID_DIRECTORIES 0 // qdir directories.
|
||||
#define VMPI_SUBPACKETID_DBINFO 1 // MySQL database info.
|
||||
#define VMPI_SUBPACKETID_CRASH 3 // A worker saying it crashed.
|
||||
#define VMPI_SUBPACKETID_MULTICAST_ADDR 4 // Filesystem multicast address.
|
||||
|
||||
|
||||
class CDBInfo;
|
||||
class CIPAddr;
|
||||
|
||||
|
||||
// Send/receive the qdir info.
|
||||
void SendQDirInfo();
|
||||
void RecvQDirInfo();
|
||||
|
||||
void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID );
|
||||
void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID );
|
||||
|
||||
void SendMulticastIP( const CIPAddr *pAddr );
|
||||
void RecvMulticastIP( CIPAddr *pAddr );
|
||||
|
||||
void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert );
|
||||
|
||||
// Call this from an exception handler (set by SetUnhandledExceptionHandler).
|
||||
// uCode = ExceptionInfo->ExceptionRecord->ExceptionCode.
|
||||
// pvExceptionInfo = ExceptionInfo
|
||||
void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo );
|
||||
|
||||
void HandleMPIDisconnect( int procID, const char *pReason );
|
||||
|
||||
|
||||
#endif // VMPI_TOOLS_SHARED_H
|
334
utils/common/wadlib.c
Normal file
334
utils/common/wadlib.c
Normal file
@ -0,0 +1,334 @@
|
||||
//========= Copyright <20> 1996-2005, Valve LLC, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// wad2lib.c
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
//#include <sys/file.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef NeXT
|
||||
#include <libc.h>
|
||||
#endif
|
||||
#include "cmdlib.h"
|
||||
#include "wadlib.h"
|
||||
#include "commonmacros.h"
|
||||
|
||||
/*
|
||||
============================================================================
|
||||
|
||||
WAD READING
|
||||
|
||||
============================================================================
|
||||
*/
|
||||
|
||||
|
||||
lumpinfo_t *lumpinfo; // location of each lump on disk
|
||||
int numlumps;
|
||||
|
||||
wadinfo_t header;
|
||||
FILE *wadhandle;
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
W_OpenWad
|
||||
====================
|
||||
*/
|
||||
void W_OpenWad (char *filename)
|
||||
{
|
||||
lumpinfo_t *lump_p;
|
||||
unsigned i;
|
||||
int length;
|
||||
|
||||
//
|
||||
// open the file and add to directory
|
||||
//
|
||||
wadhandle = SafeOpenRead (filename);
|
||||
SafeRead (wadhandle, &header, sizeof(header));
|
||||
|
||||
if (!STRING_MATCHES_ID(header.identification,WAD_ID))
|
||||
Error ("Wad file %s doesn't have %s identifier\n",filename, WAD_IDNAME);
|
||||
|
||||
header.numlumps = LittleLong(header.numlumps);
|
||||
header.infotableofs = LittleLong(header.infotableofs);
|
||||
|
||||
numlumps = header.numlumps;
|
||||
|
||||
length = numlumps*sizeof(lumpinfo_t);
|
||||
lumpinfo = malloc (length);
|
||||
lump_p = lumpinfo;
|
||||
|
||||
fseek (wadhandle, header.infotableofs, SEEK_SET);
|
||||
SafeRead (wadhandle, lumpinfo, length);
|
||||
|
||||
//
|
||||
// Fill in lumpinfo
|
||||
//
|
||||
|
||||
for (i=0 ; i<numlumps ; i++,lump_p++)
|
||||
{
|
||||
lump_p->filepos = LittleLong(lump_p->filepos);
|
||||
lump_p->size = LittleLong(lump_p->size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CleanupName (char *in, char *out)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<sizeof( ((lumpinfo_t *)0)->name ) ; i++ )
|
||||
{
|
||||
if (!in[i])
|
||||
break;
|
||||
|
||||
out[i] = toupper(in[i]);
|
||||
}
|
||||
|
||||
for ( ; i<sizeof( ((lumpinfo_t *)0)->name ); i++ )
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
W_CheckNumForName
|
||||
|
||||
Returns -1 if name not found
|
||||
====================
|
||||
*/
|
||||
int W_CheckNumForName (char *name)
|
||||
{
|
||||
char cleanname[TEXTURE_NAME_LENGTH];
|
||||
int v1,v2, v3, v4;
|
||||
int i;
|
||||
lumpinfo_t *lump_p;
|
||||
|
||||
CleanupName (name, cleanname);
|
||||
|
||||
// make the name into four integers for easy compares
|
||||
|
||||
v1 = *(int *)cleanname;
|
||||
v2 = *(int *)&cleanname[4];
|
||||
v3 = *(int *)&cleanname[8];
|
||||
v4 = *(int *)&cleanname[12];
|
||||
|
||||
// find it
|
||||
|
||||
lump_p = lumpinfo;
|
||||
for (i=0 ; i<numlumps ; i++, lump_p++)
|
||||
{
|
||||
if ( *(int *)lump_p->name == v1
|
||||
&& *(int *)&lump_p->name[4] == v2
|
||||
&& *(int *)&lump_p->name[8] == v3
|
||||
&& *(int *)&lump_p->name[12] == v4
|
||||
&& !strcmp( lump_p->name, cleanname ) )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
W_GetNumForName
|
||||
|
||||
Calls W_CheckNumForName, but bombs out if not found
|
||||
====================
|
||||
*/
|
||||
int W_GetNumForName (char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = W_CheckNumForName (name);
|
||||
if (i != -1)
|
||||
return i;
|
||||
|
||||
Error ("W_GetNumForName: %s not found!",name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
W_LumpLength
|
||||
|
||||
Returns the buffer size needed to load the given lump
|
||||
====================
|
||||
*/
|
||||
int W_LumpLength (int lump)
|
||||
{
|
||||
if (lump >= numlumps)
|
||||
Error ("W_LumpLength: %i >= numlumps",lump);
|
||||
return lumpinfo[lump].size;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
W_ReadLumpNum
|
||||
|
||||
Loads the lump into the given buffer, which must be >= W_LumpLength()
|
||||
====================
|
||||
*/
|
||||
void W_ReadLumpNum (int lump, void *dest)
|
||||
{
|
||||
lumpinfo_t *l;
|
||||
|
||||
if (lump >= numlumps)
|
||||
Error ("W_ReadLump: %i >= numlumps",lump);
|
||||
l = lumpinfo+lump;
|
||||
|
||||
fseek (wadhandle, l->filepos, SEEK_SET);
|
||||
SafeRead (wadhandle, dest, l->size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
W_LoadLumpNum
|
||||
====================
|
||||
*/
|
||||
void *W_LoadLumpNum (int lump)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
if ((unsigned)lump >= numlumps)
|
||||
Error ("W_CacheLumpNum: %i >= numlumps",lump);
|
||||
|
||||
buf = malloc (W_LumpLength (lump));
|
||||
W_ReadLumpNum (lump, buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
W_LoadLumpName
|
||||
====================
|
||||
*/
|
||||
void *W_LoadLumpName (char *name)
|
||||
{
|
||||
return W_LoadLumpNum (W_GetNumForName(name));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
WAD CREATION
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
FILE *outwad;
|
||||
|
||||
lumpinfo_t outinfo[4096];
|
||||
int outlumps;
|
||||
|
||||
short (*wadshort) (short l);
|
||||
int (*wadlong) (int l);
|
||||
|
||||
/*
|
||||
===============
|
||||
NewWad
|
||||
===============
|
||||
*/
|
||||
|
||||
void NewWad (char *pathname, qboolean bigendien)
|
||||
{
|
||||
outwad = SafeOpenWrite (pathname);
|
||||
fseek (outwad, sizeof(wadinfo_t), SEEK_SET);
|
||||
memset (outinfo, 0, sizeof(outinfo));
|
||||
|
||||
if (bigendien)
|
||||
{
|
||||
wadshort = BigShort;
|
||||
wadlong = BigLong;
|
||||
}
|
||||
else
|
||||
{
|
||||
wadshort = LittleShort;
|
||||
wadlong = LittleLong;
|
||||
}
|
||||
|
||||
outlumps = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
AddLump
|
||||
===============
|
||||
*/
|
||||
|
||||
void AddLump (char *name, void *buffer, int length, int type, int compress)
|
||||
{
|
||||
lumpinfo_t *info;
|
||||
int ofs;
|
||||
|
||||
info = &outinfo[outlumps];
|
||||
outlumps++;
|
||||
|
||||
memset (info,0,sizeof(info));
|
||||
|
||||
strcpy (info->name, name);
|
||||
Q_strupr (info->name);
|
||||
|
||||
ofs = ftell(outwad);
|
||||
info->filepos = wadlong(ofs);
|
||||
info->size = info->disksize = wadlong(length);
|
||||
info->type = type;
|
||||
info->compression = compress;
|
||||
|
||||
// FIXME: do compression
|
||||
|
||||
SafeWrite (outwad, buffer, length);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
WriteWad
|
||||
===============
|
||||
*/
|
||||
|
||||
void WriteWad (int wad3)
|
||||
{
|
||||
wadinfo_t header;
|
||||
int ofs;
|
||||
|
||||
// write the lumpingo
|
||||
ofs = ftell(outwad);
|
||||
|
||||
SafeWrite (outwad, outinfo, outlumps*sizeof(lumpinfo_t) );
|
||||
|
||||
// write the header
|
||||
|
||||
// a program will be able to tell the ednieness of a wad by the id
|
||||
ID_TO_STRING( WAD_ID, header.identification );
|
||||
|
||||
header.numlumps = wadlong(outlumps);
|
||||
header.infotableofs = wadlong(ofs);
|
||||
|
||||
fseek (outwad, 0, SEEK_SET);
|
||||
SafeWrite (outwad, &header, sizeof(header));
|
||||
fclose (outwad);
|
||||
}
|
||||
|
||||
|
46
utils/common/wadlib.h
Normal file
46
utils/common/wadlib.h
Normal file
@ -0,0 +1,46 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
// wadlib.h
|
||||
|
||||
//
|
||||
// wad reading
|
||||
//
|
||||
|
||||
#define CMP_NONE 0
|
||||
#define CMP_LZSS 1
|
||||
|
||||
#define TYP_NONE 0
|
||||
#define TYP_LABEL 1
|
||||
#define TYP_LUMPY 64 // 64 + grab command number
|
||||
|
||||
#ifndef WADTYPES_H
|
||||
#include "wadtypes.h"
|
||||
#endif
|
||||
|
||||
extern lumpinfo_t *lumpinfo; // location of each lump on disk
|
||||
extern int numlumps;
|
||||
extern wadinfo_t header;
|
||||
|
||||
void W_OpenWad (char *filename);
|
||||
int W_CheckNumForName (char *name);
|
||||
int W_GetNumForName (char *name);
|
||||
int W_LumpLength (int lump);
|
||||
void W_ReadLumpNum (int lump, void *dest);
|
||||
void *W_LoadLumpNum (int lump);
|
||||
void *W_LoadLumpName (char *name);
|
||||
|
||||
void CleanupName (char *in, char *out);
|
||||
|
||||
//
|
||||
// wad creation
|
||||
//
|
||||
void NewWad (char *pathname, qboolean bigendien);
|
||||
void AddLump (char *name, void *buffer, int length, int type, int compress);
|
||||
void WriteWad (int wad3);
|
||||
|
Reference in New Issue
Block a user