From 27a386d1ce51406074924667fb068655715b0d46 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Mon, 29 Aug 2022 10:00:40 +0300 Subject: [PATCH 1/4] Add ParticleMan --- CMakeLists.txt | 1 + cl_dll/CMakeLists.txt | 8 ++ cl_dll/cdll_int.cpp | 57 ++++++++ cl_dll/entity.cpp | 13 ++ cl_dll/hud_msg.cpp | 9 ++ cl_dll/input.cpp | 9 ++ cl_dll/tri.cpp | 10 ++ public/interface.cpp | 270 ++++++++++++++++++++++++++++++++++++ public/interface.h | 149 ++++++++++++++++++++ public/particleman.h | 101 ++++++++++++++ public/pman_particlemem.h | 197 ++++++++++++++++++++++++++ public/pman_triangleffect.h | 209 ++++++++++++++++++++++++++++ 12 files changed, 1033 insertions(+) create mode 100644 public/interface.cpp create mode 100644 public/interface.h create mode 100644 public/particleman.h create mode 100644 public/pman_particlemem.h create mode 100644 public/pman_triangleffect.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 15f8ba2dbf..6aca38fb0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ option(USE_VGUI "Enable VGUI1." OFF) option(USE_NOVGUI_MOTD "Prefer non-VGUI MOTD when USE_VGUI is enabled" OFF) option(USE_NOVGUI_SCOREBOARD "Prefer non-VGUI Scoreboard when USE_VGUI is enabled" OFF) option(USE_VOICEMGR "Enable VOICE MANAGER." OFF) +option(USE_PARTICLEMAN "Enable ParticleMan." OFF) option(BUILD_CLIENT "Build client dll" ON) option(BUILD_SERVER "Build server dll" ON) option(LTO "Enable interprocedural optimization" OFF) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index f6ecbb9f85..bc017c0f1e 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -62,6 +62,10 @@ if (USE_VGUI) endif() endif() +if (USE_PARTICLEMAN) + add_definitions(-DUSE_PARTICLEMAN) +endif() + set (CLDLL_SOURCES ../dlls/crossbow.cpp ../dlls/crowbar.cpp @@ -160,6 +164,10 @@ else() scoreboard.cpp) endif() +if (USE_PARTICLEMAN) + list(APPEND CLDLL_SOURCES ../public/interface.cpp) +endif() + include_directories (. hl/ ../dlls ../dlls/wpn_shared ../common ../engine ../pm_shared ../game_shared ../public) if (USE_VGUI) diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index 55516b840e..100e1ff301 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -28,6 +28,17 @@ #include "vgui_TeamFortressViewport.h" #endif +#if USE_PARTICLEMAN +#include "interface.h" +#include "particleman.h" + +CSysModule *g_hParticleManModule = NULL; +IParticleMan *g_pParticleMan = NULL; + +void CL_LoadParticleMan( void ); +void CL_UnloadParticleMan( void ); +#endif + #if GOLDSOURCE_SUPPORT && (XASH_WIN32 || XASH_LINUX || XASH_APPLE) && XASH_X86 #define USE_FAKE_VGUI !USE_VGUI #if USE_FAKE_VGUI @@ -171,6 +182,10 @@ int DLLEXPORT Initialize( cl_enginefunc_t *pEnginefuncs, int iVersion ) EV_HookEvents(); +#if USE_PARTICLEMAN + CL_LoadParticleMan(); +#endif + return 1; } @@ -388,6 +403,48 @@ void DLLEXPORT HUD_DirectorMessage( int iSize, void *pbuf ) gHUD.m_Spectator.DirectorMessage( iSize, pbuf ); } +#if USE_PARTICLEMAN +void CL_UnloadParticleMan( void ) +{ + Sys_UnloadModule( g_hParticleManModule ); + + g_pParticleMan = NULL; + g_hParticleManModule = NULL; +} + +void CL_LoadParticleMan( void ) +{ + char szPDir[512]; + + if ( gEngfuncs.COM_ExpandFilename( PARTICLEMAN_DLLNAME, szPDir, sizeof( szPDir ) ) == FALSE ) + { + g_pParticleMan = NULL; + g_hParticleManModule = NULL; + return; + } + + g_hParticleManModule = Sys_LoadModule( szPDir ); + CreateInterfaceFn particleManFactory = Sys_GetFactory( g_hParticleManModule ); + + if ( particleManFactory == NULL ) + { + g_pParticleMan = NULL; + g_hParticleManModule = NULL; + return; + } + + g_pParticleMan = (IParticleMan *)particleManFactory( PARTICLEMAN_INTERFACE, NULL); + + if ( g_pParticleMan ) + { + g_pParticleMan->SetUp( &gEngfuncs ); + + // Add custom particle classes here BEFORE calling anything else or you will die. + g_pParticleMan->AddCustomParticleClassSize ( sizeof ( CBaseParticle ) ); + } +} +#endif + void DLLEXPORT HUD_MobilityInterface( mobile_engfuncs_t *gpMobileEngfuncs ) { if( gpMobileEngfuncs->version != MOBILITY_API_VERSION ) diff --git a/cl_dll/entity.cpp b/cl_dll/entity.cpp index 367054661c..1c311e9bbd 100644 --- a/cl_dll/entity.cpp +++ b/cl_dll/entity.cpp @@ -20,6 +20,10 @@ #include "pmtrace.h" #include "pm_shared.h" +#if USE_PARTICLEMAN +#include "particleman.h" +#endif + void Game_AddObjects( void ); extern vec3_t v_origin; @@ -591,6 +595,15 @@ void DLLEXPORT HUD_TempEntUpdate ( TEMPENTITY *pTemp, *pnext, *pprev; float /*freq,*/ gravity, gravitySlow, life, fastFreq; +#if USE_PARTICLEMAN + Vector vAngles; + + gEngfuncs.GetViewAngles( (float*)vAngles ); + + if ( g_pParticleMan ) + g_pParticleMan->SetVariables( cl_gravity, vAngles ); +#endif + // Nothing to simulate if( !*ppTempEntActive ) return; diff --git a/cl_dll/hud_msg.cpp b/cl_dll/hud_msg.cpp index 3c3b6f5288..c660e04638 100644 --- a/cl_dll/hud_msg.cpp +++ b/cl_dll/hud_msg.cpp @@ -21,6 +21,10 @@ #include "parsemsg.h" #include "r_efx.h" +#if USE_PARTICLEMAN +#include "particleman.h" +#endif + #define MAX_CLIENTS 32 extern BEAM *pBeam; @@ -77,6 +81,11 @@ void CHud::MsgFunc_InitHUD( const char *pszName, int iSize, void *pbuf ) pList = pList->pNext; } +#if USE_PARTICLEMAN + if ( g_pParticleMan ) + g_pParticleMan->ResetParticles(); +#endif + //Probably not a good place to put this. pBeam = pBeam2 = NULL; pFlare = NULL; // Vit_amiN: clear egon's beam flare diff --git a/cl_dll/input.cpp b/cl_dll/input.cpp index d11abf5685..b4bf367999 100644 --- a/cl_dll/input.cpp +++ b/cl_dll/input.cpp @@ -1164,7 +1164,16 @@ void ShutdownInput( void ) KB_Shutdown(); } +#if USE_PARTICLEMAN +#include "interface.h" +void CL_UnloadParticleMan(); +#endif + void DLLEXPORT HUD_Shutdown( void ) { ShutdownInput(); + +#if USE_PARTICLEMAN + CL_UnloadParticleMan(); +#endif } diff --git a/cl_dll/tri.cpp b/cl_dll/tri.cpp index 0088daf350..fab0e97273 100644 --- a/cl_dll/tri.cpp +++ b/cl_dll/tri.cpp @@ -23,6 +23,11 @@ extern "C" void DLLEXPORT HUD_DrawTransparentTriangles( void ); } +#if USE_PARTICLEMAN +#include "com_model.h" +#include "particleman.h" +#endif + //#define TEST_IT 1 #if TEST_IT @@ -113,4 +118,9 @@ void DLLEXPORT HUD_DrawTransparentTriangles( void ) #if TEST_IT // Draw_Triangles(); #endif + +#if USE_PARTICLEMAN + if ( g_pParticleMan ) + g_pParticleMan->Update(); +#endif } diff --git a/public/interface.cpp b/public/interface.cpp new file mode 100644 index 0000000000..32ca96adde --- /dev/null +++ b/public/interface.cpp @@ -0,0 +1,270 @@ +#include +#include +#include +#include "interface.h" + +#if !defined ( _WIN32 ) +// Linux doesn't have this function so this emulates its functionality +// +// +void *GetModuleHandle(const char *name) +{ + void *handle; + + + if( name == NULL ) + { + // hmm, how can this be handled under linux.... + // is it even needed? + return NULL; + } + + if( (handle=dlopen(name, RTLD_NOW))==NULL) + { + //printf("Error:%s\n",dlerror()); + // couldn't open this file + return NULL; + } + + // read "man dlopen" for details + // in short dlopen() inc a ref count + // so dec the ref count by performing the close + dlclose(handle); + return handle; +} +#endif + +// ------------------------------------------------------------------------------------ // +// InterfaceReg. +// ------------------------------------------------------------------------------------ // +InterfaceReg *InterfaceReg::s_pInterfaceRegs = NULL; + + +InterfaceReg::InterfaceReg( InstantiateInterfaceFn fn, const char *pName ) : + m_pName(pName) +{ + m_CreateFn = fn; + m_pNext = s_pInterfaceRegs; + s_pInterfaceRegs = this; +} + + + +// ------------------------------------------------------------------------------------ // +// CreateInterface. +// ------------------------------------------------------------------------------------ // +EXPORT_FUNCTION IBaseInterface *CreateInterface( const char *pName, int *pReturnCode ) +{ + InterfaceReg *pCur; + + for(pCur=InterfaceReg::s_pInterfaceRegs; pCur; pCur=pCur->m_pNext) + { + if(strcmp(pCur->m_pName, pName) == 0) + { + if ( pReturnCode ) + { + *pReturnCode = IFACE_OK; + } + return pCur->m_CreateFn(); + } + } + + if ( pReturnCode ) + { + *pReturnCode = IFACE_FAILED; + } + return NULL; +} + +#ifdef LINUX +static IBaseInterface *CreateInterfaceLocal( const char *pName, int *pReturnCode ) +{ + InterfaceReg *pCur; + + for(pCur=InterfaceReg::s_pInterfaceRegs; pCur; pCur=pCur->m_pNext) + { + if(strcmp(pCur->m_pName, pName) == 0) + { + if ( pReturnCode ) + { + *pReturnCode = IFACE_OK; + } + return pCur->m_CreateFn(); + } + } + + if ( pReturnCode ) + { + *pReturnCode = IFACE_FAILED; + } + return NULL; +} +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a function, given a module +// Input : pModuleName - module name +// *pName - proc name +//----------------------------------------------------------------------------- +//static hlds_run wants to use this function +static void *Sys_GetProcAddress( const char *pModuleName, const char *pName ) +{ + return GetProcAddress( GetModuleHandle(pModuleName), pName ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a function, given a module +// Input : pModuleName - module name +// *pName - proc name +//----------------------------------------------------------------------------- +// hlds_run wants to use this function +void *Sys_GetProcAddress( void *pModuleHandle, const char *pName ) +{ +#if defined ( _WIN32 ) + return GetProcAddress( (HINSTANCE)pModuleHandle, pName ); +#else + return GetProcAddress( pModuleHandle, pName ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Loads a DLL/component from disk and returns a handle to it +// Input : *pModuleName - filename of the component +// Output : opaque handle to the module (hides system dependency) +//----------------------------------------------------------------------------- +CSysModule *Sys_LoadModule( const char *pModuleName ) +{ +#if defined ( _WIN32 ) + HMODULE hDLL = LoadLibrary( pModuleName ); +#else + HMODULE hDLL = NULL; + char szAbsoluteModuleName[1024]; + szAbsoluteModuleName[0] = 0; + if ( pModuleName[0] != '/' ) + { + char szCwd[1024]; + char szAbsoluteModuleName[1024]; + + getcwd( szCwd, sizeof( szCwd ) ); + if ( szCwd[ strlen( szCwd ) - 1 ] == '/' ) + szCwd[ strlen( szCwd ) - 1 ] = 0; + + _snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/%s", szCwd, pModuleName ); + + hDLL = dlopen( szAbsoluteModuleName, RTLD_NOW ); + } + else + { + _snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s", pModuleName ); + hDLL = dlopen( pModuleName, RTLD_NOW ); + } +#endif + + if( !hDLL ) + { + char str[512]; +#if defined ( _WIN32 ) + _snprintf( str, sizeof(str), "%s.dll", pModuleName ); + hDLL = LoadLibrary( str ); +#elif defined(OSX) + printf("Error:%s\n",dlerror()); + _snprintf( str, sizeof(str), "%s.dylib", szAbsoluteModuleName ); + hDLL = dlopen(str, RTLD_NOW); +#else + printf("Error:%s\n",dlerror()); + _snprintf( str, sizeof(str), "%s.so", szAbsoluteModuleName ); + hDLL = dlopen(str, RTLD_NOW); +#endif + } + + return reinterpret_cast(hDLL); +} + +//----------------------------------------------------------------------------- +// Purpose: Unloads a DLL/component from +// Input : *pModuleName - filename of the component +// Output : opaque handle to the module (hides system dependency) +//----------------------------------------------------------------------------- +void Sys_UnloadModule( CSysModule *pModule ) +{ + if ( !pModule ) + return; + + HMODULE hDLL = reinterpret_cast(pModule); +#if defined ( _WIN32 ) + FreeLibrary( hDLL ); +#else + dlclose((void *)hDLL); +#endif + +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a function, given a module +// Input : module - windows HMODULE from Sys_LoadModule() +// *pName - proc name +// Output : factory for this module +//----------------------------------------------------------------------------- +CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ) +{ + if ( !pModule ) + return NULL; + + HMODULE hDLL = reinterpret_cast(pModule); +#if defined ( _WIN32 ) + return reinterpret_cast(GetProcAddress( hDLL, CREATEINTERFACE_PROCNAME )); +#else +// Linux gives this error: +//../public/interface.cpp: In function `IBaseInterface *(*Sys_GetFactory +//(CSysModule *)) (const char *, int *)': +//../public/interface.cpp:154: ISO C++ forbids casting between +//pointer-to-function and pointer-to-object +// +// so lets get around it :) + return (CreateInterfaceFn)(GetProcAddress( hDLL, CREATEINTERFACE_PROCNAME )); +#endif +} + + + +//----------------------------------------------------------------------------- +// Purpose: returns the instance of this module +// Output : interface_instance_t +//----------------------------------------------------------------------------- +CreateInterfaceFn Sys_GetFactoryThis( void ) +{ +#ifdef LINUX + return CreateInterfaceLocal; +#else + return CreateInterface; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: returns the instance of the named module +// Input : *pModuleName - name of the module +// Output : interface_instance_t - instance of that module +//----------------------------------------------------------------------------- +CreateInterfaceFn Sys_GetFactory( const char *pModuleName ) +{ +#if defined ( _WIN32 ) + return static_cast( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) ); +#else +// Linux gives this error: +//../public/interface.cpp: In function `IBaseInterface *(*Sys_GetFactory +//(const char *)) (const char *, int *)': +//../public/interface.cpp:186: invalid static_cast from type `void *' to +//type `IBaseInterface *(*) (const char *, int *)' +// +// so lets use the old style cast. + return (CreateInterfaceFn)( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) ); +#endif +} + + + diff --git a/public/interface.h b/public/interface.h new file mode 100644 index 0000000000..0f4a5ad987 --- /dev/null +++ b/public/interface.h @@ -0,0 +1,149 @@ + +// This header defines the interface convention used in the valve engine. +// To make an interface and expose it: +// 1. Derive from IBaseInterface. +// 2. The interface must be ALL pure virtuals, and have no data members. +// 3. Define a name for it. +// 4. In its implementation file, use EXPOSE_INTERFACE or EXPOSE_SINGLE_INTERFACE. + +// Versioning +// There are two versioning cases that are handled by this: +// 1. You add functions to the end of an interface, so it is binary compatible with the previous interface. In this case, +// you need two EXPOSE_INTERFACEs: one to expose your class as the old interface and one to expose it as the new interface. +// 2. You update an interface so it's not compatible anymore (but you still want to be able to expose the old interface +// for legacy code). In this case, you need to make a new version name for your new interface, and make a wrapper interface and +// expose it for the old interface. + +//#if _MSC_VER >= 1300 // VC7 +//#include "tier1/interface.h" +//#else + +#ifndef INTERFACE_H +#define INTERFACE_H + +#if !defined ( _WIN32 ) + +#include // dlopen,dlclose, et al +#include + +#define HMODULE void * +#define GetProcAddress dlsym + +#define _snprintf snprintf + +#endif + +void *Sys_GetProcAddress( void *pModuleHandle, const char *pName ); + +// All interfaces derive from this. +class IBaseInterface +{ +public: + + virtual ~IBaseInterface() {} +}; + + +#define CREATEINTERFACE_PROCNAME "CreateInterface" +typedef IBaseInterface* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); + + +typedef IBaseInterface* (*InstantiateInterfaceFn)(); + + +// Used internally to register classes. +class InterfaceReg +{ +public: + InterfaceReg(InstantiateInterfaceFn fn, const char *pName); + +public: + + InstantiateInterfaceFn m_CreateFn; + const char *m_pName; + + InterfaceReg *m_pNext; // For the global list. + static InterfaceReg *s_pInterfaceRegs; +}; + + +// Use this to expose an interface that can have multiple instances. +// e.g.: +// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) +// This will expose a class called CInterfaceImp that implements IInterface (a pure class) +// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) +// +// In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") +// so that each component can use these names/vtables to communicate +// +// A single class can support multiple interfaces through multiple inheritance +// +// Use this if you want to write the factory function. +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ + static InterfaceReg __g_Create##className##_reg(functionName, versionName); + +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + static IBaseInterface* __Create##className##_interface() {return (interfaceName *)new className;}\ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); + +// Use this to expose a singleton interface with a global variable you've created. +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + static IBaseInterface* __Create##className##interfaceName##_interface() {return (IBaseInterface *)&globalVarName;}\ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); + +// Use this to expose a singleton interface. This creates the global variable for you automatically. +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + static className __g_##className##_singleton;\ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) + + +#ifdef WIN32 + #define EXPORT_FUNCTION __declspec(dllexport) +#else + #define EXPORT_FUNCTION __attribute__ ((visibility("default"))) +#endif + + +// This function is automatically exported and allows you to access any interfaces exposed with the above macros. +// if pReturnCode is set, it will return one of the following values +// extend this for other error conditions/code +enum +{ + IFACE_OK = 0, + IFACE_FAILED +}; + + +extern "C" +{ + EXPORT_FUNCTION IBaseInterface* CreateInterface(const char *pName, int *pReturnCode); +}; + + +extern CreateInterfaceFn Sys_GetFactoryThis( void ); + + +//----------------------------------------------------------------------------- +// UNDONE: This is obsolete, use the module load/unload/get instead!!! +//----------------------------------------------------------------------------- +extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName ); + + +// load/unload components +class CSysModule; + +//----------------------------------------------------------------------------- +// Load & Unload should be called in exactly one place for each module +// The factory for that module should be passed on to dependent components for +// proper versioning. +//----------------------------------------------------------------------------- +extern CSysModule *Sys_LoadModule( const char *pModuleName ); +extern void Sys_UnloadModule( CSysModule *pModule ); + +extern CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ); + + +#endif +//#endif // MSVC 6.0 + + diff --git a/public/particleman.h b/public/particleman.h new file mode 100644 index 0000000000..0f9b70242b --- /dev/null +++ b/public/particleman.h @@ -0,0 +1,101 @@ +#ifndef PARTICLEMAN_H +#define PARTICLEMAN_H + +#include "interface.h" +#include "pman_triangleffect.h" + +#define PARTICLEMAN_INTERFACE "create_particleman" + +#ifdef _WIN32 +#define PARTICLEMAN_DLLNAME "cl_dlls/particleman.dll" +#elif defined(OSX) +#define PARTICLEMAN_DLLNAME "cl_dlls/particleman.dylib" +#elif defined(LINUX) +#define PARTICLEMAN_DLLNAME "cl_dlls/particleman.so" +#else +#error +#endif + +class CBaseParticle; + +class IParticleMan : public IBaseInterface +{ + +protected: + virtual ~IParticleMan() {} + +public: + + virtual void SetUp( cl_enginefunc_t *pEnginefuncs ) = 0; + virtual void Update ( void ) = 0; + virtual void SetVariables ( float flGravity, Vector vViewAngles ) = 0; + virtual void ResetParticles ( void ) = 0; + virtual void ApplyForce ( Vector vOrigin, Vector vDirection, float flRadius, float flStrength, float flDuration ) = 0; + virtual void AddCustomParticleClassSize ( unsigned long lSize ) = 0; + + //Use this if you want to create a new particle without any overloaded functions, Think, Touch, etc. + //Just call this function, set the particle's behavior and let it rip. + virtual CBaseParticle *CreateParticle( Vector org, Vector normal, model_s * sprite, float size, float brightness, const char *classname ) = 0; + + //Use this to take a block from the mempool for custom particles ( used in new ). + virtual char *RequestNewMemBlock( int iSize ) = 0; + + //These ones are used along a custom Create for new particles you want to override their behavior. + //You can call these whenever you want, but they are mainly used by CBaseParticle. + virtual void CoreInitializeSprite ( CCoreTriangleEffect *pParticle, Vector org, Vector normal, model_s *sprite, float size, float brightness ) = 0; //Only use this for TrianglePlanes + virtual void CoreThink( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreDraw( CCoreTriangleEffect *pParticle ) = 0; + virtual void CoreAnimate( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreAnimateAndDie( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreExpand ( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreContract ( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreFade ( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreSpin ( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreCalculateVelocity( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreCheckCollision( CCoreTriangleEffect *pParticle, float time ) = 0; + virtual void CoreTouch ( CCoreTriangleEffect *pParticle, Vector pos, Vector normal, int index ) = 0; + virtual void CoreDie ( CCoreTriangleEffect *pParticle ) = 0; + virtual void CoreForce ( CCoreTriangleEffect *pParticle ) = 0; + virtual bool CoreCheckVisibility ( CCoreTriangleEffect *pParticle ) = 0; + virtual void SetRender( int iRender ) = 0; +}; + +extern IParticleMan *g_pParticleMan; + +class CBaseParticle : public CCoreTriangleEffect +{ +public: + virtual void Think( float time ){ g_pParticleMan->CoreThink( this, time ); } + virtual void Draw( void ) { g_pParticleMan->CoreDraw( this ); } + virtual void Animate( float time ) { g_pParticleMan->CoreAnimate( this, time ); } + virtual void AnimateAndDie( float time ) { g_pParticleMan->CoreAnimateAndDie( this, time ); } + virtual void Expand( float time ) { g_pParticleMan->CoreExpand( this, time ); } + virtual void Contract( float time ) { g_pParticleMan->CoreContract( this, time ); } + virtual void Fade( float time ) { g_pParticleMan->CoreFade( this, time ); } + virtual void Spin( float time ) { g_pParticleMan->CoreSpin( this, time ); } + virtual void CalculateVelocity( float time ) { g_pParticleMan->CoreCalculateVelocity( this, time ); } + virtual void CheckCollision( float time ) { g_pParticleMan->CoreCheckCollision( this, time ); } + virtual void Touch(Vector pos, Vector normal, int index) { g_pParticleMan->CoreTouch( this, pos, normal, index ); } + virtual void Die ( void ) { g_pParticleMan->CoreDie( this ); } + virtual void Force ( void ) { g_pParticleMan->CoreForce( this ); } + virtual bool CheckVisibility ( void ) { return g_pParticleMan->CoreCheckVisibility( this ); } + + virtual void InitializeSprite( Vector org, Vector normal, model_s *sprite, float size, float brightness ) + { + g_pParticleMan->CoreInitializeSprite ( this, org, normal, sprite, size, brightness ); + } + + void * operator new( size_t size ) //this asks for a new block of memory from the MiniMem class + { + return( g_pParticleMan->RequestNewMemBlock( size ) ); + } +#ifdef POSIX + void * operator new( size_t size, const std::nothrow_t&) throw() //this asks for a new block of memory from the MiniMem class + { + return( g_pParticleMan->RequestNewMemBlock( size ) ); + } +#endif +}; + + +#endif //PARTICLEMAN_H diff --git a/public/pman_particlemem.h b/public/pman_particlemem.h new file mode 100644 index 0000000000..6ccb971280 --- /dev/null +++ b/public/pman_particlemem.h @@ -0,0 +1,197 @@ +#ifndef PARTICLEMEM_H__ +#define PARTICLEMEM_H__ + +#ifdef _WIN32 +#pragma once +#endif + +#include + +class CCoreTriangleEffect; + +#define TRIANGLE_FPS 30 + +typedef struct visibleparticles_s +{ + CCoreTriangleEffect *pVisibleParticle; +} visibleparticles_t; + +//--------------------------------------------------------------------------- +// Memory block record. +class MemoryBlock +{ +private: + char *m_pData; + //bool m_bBlockIsInUse; +public: + MemoryBlock(long lBlockSize) + : next(NULL), prev(NULL) + //m_bBlockIsInUse(false) // Initialize block to 'free' state. + { + // Allocate memory here. + m_pData = new char[lBlockSize]; + } + + virtual ~MemoryBlock() + { + // Free memory. + delete[] m_pData; + } + + inline char *Memory(void) { return m_pData; } + + MemoryBlock * next; + MemoryBlock * prev; +}; + +class MemList +{ +public: + MemList() : m_pHead(NULL) {} + + ~MemList() { Reset(); } + + void Push(MemoryBlock * newItem) + { + if(!m_pHead) + { + m_pHead = newItem; + newItem->next = NULL; + newItem->prev = NULL; + return; + } + + MemoryBlock * temp = m_pHead; + m_pHead = newItem; + m_pHead->next = temp; + m_pHead->prev = NULL; + + temp->prev = m_pHead; + } + + + MemoryBlock * Front( void ) + { + return(m_pHead); + } + + MemoryBlock * Pop( void ) + { + if(!m_pHead) + return(NULL); + + MemoryBlock * temp = m_pHead; + + m_pHead = m_pHead->next; + + if(m_pHead) + m_pHead->prev = NULL; + + temp->next = NULL; + temp->prev = NULL; + + return(temp); + } + + void Delete( MemoryBlock * pItem) + { + + if(m_pHead == pItem) + { + MemoryBlock * temp = m_pHead; + + m_pHead = m_pHead->next; + if(m_pHead) + m_pHead->prev = NULL; + + temp->next = NULL; + temp->prev = NULL; + return; + } + + MemoryBlock * prev = pItem->prev; + MemoryBlock * next = pItem->next; + + if(prev) + prev->next = next; + + if(next) + next->prev = prev; + + pItem->next = NULL; + pItem->prev = NULL; + } + + void Reset( void ) + { + while(m_pHead) + Delete(m_pHead); + } + +private: + MemoryBlock * m_pHead; +}; + + +// Some helpful typedefs. +typedef std::vector VectorOfMemoryBlocks; +typedef VectorOfMemoryBlocks::iterator MemoryBlockIterator; + +// Mini memory manager - singleton. +class CMiniMem +{ +private: + // Main memory pool. Array is fine, but vectors are + // easier. :) + static VectorOfMemoryBlocks m_vecMemoryPool; + // Size of memory blocks in pool. + static long m_lMemoryBlockSize; + static long m_lMaxBlocks; + static long m_lMemoryPoolSize; + static CMiniMem *_instance; + + int m_iTotalParticles; + int m_iParticlesDrawn; + +protected: + // private constructor and destructor. + CMiniMem(long lMemoryPoolSize, long lMaxBlockSize); + virtual ~CMiniMem(); + + // ------------ Memory pool manager calls. + // Find a free block and mark it as "in use". Return NULL + // if no free blocks found. + char *AllocateFreeBlock(void); +public: + + // Return a pointer to usable block of memory. + char *newBlock(void); + + // Mark a target memory item as no longer "in use". + void deleteBlock(MemoryBlock *p); + + // Return the remaining capacity of the memory pool as a percent. + long PercentUsed(void); + + void ProcessAll( void ); //Processes all + + void Reset( void ); //clears memory, setting all particles to not used. + + static int ApplyForce( Vector vOrigin, Vector vDirection, float flRadius, float flStrength ); + + static CMiniMem *Instance(void); + static long MaxBlockSize(void); + + bool CheckSize( int iSize ); + + int GetTotalParticles( void ) { return m_iTotalParticles; } + int GetDrawnParticles( void ) { return m_iParticlesDrawn; } + void IncreaseParticlesDrawn( void ){ m_iParticlesDrawn++; } + + void Shutdown( void ); + + visibleparticles_t *m_pVisibleParticles; +}; + + +#endif//PARTICLEMEM_H__ \ No newline at end of file diff --git a/public/pman_triangleffect.h b/public/pman_triangleffect.h new file mode 100644 index 0000000000..6af4c2b3d3 --- /dev/null +++ b/public/pman_triangleffect.h @@ -0,0 +1,209 @@ +#ifndef TRIANGLEEFFECT_H__ +#define TRIANGLEEFFECT_H__ + +#ifdef _WIN32 +#pragma once +#endif + +#define TRI_COLLIDEWORLD 0x00000020 +#define TRI_COLLIDEALL 0x00001000 // will collide with world and slideboxes +#define TRI_COLLIDEKILL 0x00004000 // tent is removed upon collision with anything +#define TRI_SPIRAL 0x00008000 +#define TRI_ANIMATEDIE 0x00016000 //animate once and then die +#define TRI_WATERTRACE 0x00032000 + + +#define CULL_FRUSTUM_POINT ( 1 << 0 ) +#define CULL_FRUSTUM_SPHERE ( 1 << 1 ) +#define CULL_FRUSTUM_PLANE ( 1 << 2 ) +#define CULL_PVS ( 1 << 3 ) + +#define LIGHT_NONE ( 1 << 4 ) +#define LIGHT_COLOR ( 1 << 5 ) +#define LIGHT_INTENSITY ( 1 << 6 ) + +#define RENDER_FACEPLAYER ( 1 << 7 ) // m_vAngles == Player view angles +#define RENDER_FACEPLAYER_ROTATEZ ( 1 << 8 ) //Just like above but m_vAngles.z is untouched so the sprite can rotate. + + +#include "pman_particlemem.h" + +//pure virtual baseclass +class CCoreTriangleEffect +{ +private: + int m_iRenderFlags; + float m_flNextPVSCheck; + bool m_bInPVS; + + int m_iCollisionFlags; + float m_flPlayerDistance; //Used for sorting the particles, DO NOT TOUCH. + +public: + + void * operator new(size_t size) + { + // Requested size should match size of class. + if ( size != sizeof( CCoreTriangleEffect ) ) +#ifdef _WIN32 + throw "Error in requested size of new particle class instance."; +#else + return NULL; +#endif + + return((CCoreTriangleEffect *) CMiniMem::Instance()->newBlock()); + + }//this asks for a new block of memory from the MiniMen class + + virtual void Think( float time ) = 0; + virtual bool CheckVisibility ( void ) = 0; + virtual void Draw( void ) = 0; + virtual void Animate( float time ) = 0; + virtual void AnimateAndDie( float time ) = 0; + virtual void Expand( float time ) = 0; + virtual void Contract( float time ) = 0; + virtual void Fade( float time ) = 0; + virtual void Spin( float time ) = 0; + virtual void CalculateVelocity( float time ) = 0; + virtual void CheckCollision( float time ) = 0; + virtual void Touch(Vector pos, Vector normal, int index) = 0; + virtual void Die ( void ) = 0; + virtual void InitializeSprite( Vector org, Vector normal, model_s * sprite, float size, float brightness ) = 0; + virtual void Force ( void ) = 0; + + float m_flSize; //scale of object + float m_flScaleSpeed; //speed at which object expands + float m_flContractSpeed; //speed at which object expands + + float m_flStretchX; + float m_flStretchY; + + float m_flBrightness; //transparency of object + float m_flFadeSpeed; //speed at which object fades + + float m_flTimeCreated; //time object was instanced + float m_flDieTime; //time to remove an object + + float m_flGravity; //how effected by gravity is this object + float m_flAfterDampGrav; + float m_flDampingVelocity; + float m_flDampingTime; + + int m_iFramerate; + int m_iNumFrames; + int m_iFrame; + int m_iRendermode; + + Vector m_vOrigin; //object's position + Vector m_vAngles; //normal angles of object + + Vector m_vAVelocity; + + Vector m_vVelocity; + + Vector m_vLowLeft; + Vector m_vLowRight; + Vector m_vTopLeft; + + Vector m_vColor; + float m_flMass; + + model_s * m_pTexture; + + float m_flBounceFactor; + + char m_szClassname[32]; + + bool m_bInWater; + bool m_bAffectedByForce; + + int m_iAfterDampFlags; + + void SetLightFlag ( int iFlag ) + { + m_iRenderFlags &= ~( LIGHT_NONE | LIGHT_INTENSITY | LIGHT_COLOR ); + m_iRenderFlags |= iFlag; + } + + void SetCullFlag( int iFlag ) + { + m_iRenderFlags &= ~( CULL_PVS | CULL_FRUSTUM_POINT | CULL_FRUSTUM_PLANE | CULL_FRUSTUM_SPHERE ); + m_iRenderFlags |= iFlag; + } + + int GetRenderFlags( void ) + { + return m_iRenderFlags; + } + + bool GetParticlePVS ( void ) + { + return m_bInPVS; + } + + void SetParticlePVS ( bool bPVSStat ) + { + m_bInPVS = bPVSStat; + } + + float GetNextPVSCheck( void ) + { + return m_flNextPVSCheck; + } + + void SetNextPVSCheck( float flTime ) + { + m_flNextPVSCheck = flTime; + } + + void SetCollisionFlags ( int iFlag ) + { + m_iCollisionFlags |= iFlag; + } + + void ClearCollisionFlags ( int iFlag ) + { + m_iCollisionFlags &= ~iFlag; + } + + int GetCollisionFlags ( void ) + { + return m_iCollisionFlags; + } + + void SetRenderFlag( int iFlag ) + { + m_iRenderFlags |= iFlag; + } + + float GetPlayerDistance ( void ) { return m_flPlayerDistance; } + void SetPlayerDistance ( float flDistance ) { m_flPlayerDistance = flDistance; } + +protected: + float m_flOriginalSize; + Vector m_vOriginalAngles; + float m_flOriginalBrightness; + Vector m_vPrevOrigin; + + float m_flNextCollisionTime; + +protected: + static bool CheckSize(int size) + { + // This check will help prevent a class frome being defined later, + // that is larger than the max size MemoryPool is expecting, + // from being successfully allocated. + if (size > (unsigned long) CMiniMem::Instance()->MaxBlockSize()) + { +#ifdef _WIN32 + throw "New particle class is larger than memory pool max size, update lMaxParticleClassSize() function."; +#endif + return(false); + } + + return(true); + } +}; + + +#endif//TRIANGLEEFFECT_H__ From 6dc205a4e514c2826050f533dad67ba08a5f8383 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Wed, 13 Mar 2024 19:06:24 +0300 Subject: [PATCH 2/4] Add test_particles command --- cl_dll/cdll_int.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index 100e1ff301..f960932e6f 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -31,6 +31,7 @@ #if USE_PARTICLEMAN #include "interface.h" #include "particleman.h" +#include "com_model.h" CSysModule *g_hParticleManModule = NULL; IParticleMan *g_pParticleMan = NULL; @@ -404,6 +405,49 @@ void DLLEXPORT HUD_DirectorMessage( int iSize, void *pbuf ) } #if USE_PARTICLEMAN +void TestParticlesCmd() +{ + static model_t* texture = 0; + + if ( g_pParticleMan ) + { + const float clTime = gEngfuncs.GetClientTime(); + + if (texture == 0) + { + texture = (model_t*)gEngfuncs.GetSpritePointer(SPR_Load("sprites/steam1.spr")); + } + + if (!texture) + return; + + cl_entity_t* player = gEngfuncs.GetLocalPlayer(); + Vector origin = player->origin; + Vector forward; + AngleVectors(player->angles, forward, NULL, NULL); + + for (int i = 0; i < 10; ++i) + { + Vector shift = forward * 64.0f + forward * 8.0f * i + Vector( 0.0f, 0.0f, i * 8.0f ); + + CBaseParticle *particle = g_pParticleMan->CreateParticle(origin + shift, Vector(0.0f, 0.0f, 0.0f), texture, 32.0f, 255.0f, "particle"); + + particle->SetLightFlag(LIGHT_NONE); + particle->SetCullFlag(CULL_PVS); + particle->SetRenderFlag(RENDER_FACEPLAYER); + particle->SetCollisionFlags(TRI_COLLIDEWORLD); + particle->m_iRendermode = kRenderTransAlpha; + particle->m_vColor = Vector(255, 255, 255); + particle->m_iFramerate = 10; + particle->m_iNumFrames = texture->numframes; + particle->m_flGravity = 0.01f; + particle->m_vVelocity = shift.Normalize() * 2; + + particle->m_flDieTime = clTime + 5 + i; + } + } +} + void CL_UnloadParticleMan( void ) { Sys_UnloadModule( g_hParticleManModule ); @@ -441,6 +485,8 @@ void CL_LoadParticleMan( void ) // Add custom particle classes here BEFORE calling anything else or you will die. g_pParticleMan->AddCustomParticleClassSize ( sizeof ( CBaseParticle ) ); + + gEngfuncs.pfnAddCommand("test_particles", &TestParticlesCmd); } } #endif From 4d7f3564a8075c4584aac92ad6f4c989c9917d53 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Wed, 13 Mar 2024 19:07:57 +0300 Subject: [PATCH 3/4] Reset particles in HUD_VidInit, so they don't stay after the changelevel --- cl_dll/cdll_int.cpp | 8 ++++++++ cl_dll/hud_msg.cpp | 5 ----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index f960932e6f..659f12e4f9 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -285,6 +285,14 @@ int DLLEXPORT HUD_VidInit( void ) #elif USE_VGUI VGui_Startup(); #endif + +#if USE_PARTICLEMAN + if (g_pParticleMan) + { + g_pParticleMan->ResetParticles(); + } +#endif + return 1; } diff --git a/cl_dll/hud_msg.cpp b/cl_dll/hud_msg.cpp index c660e04638..daa8f05944 100644 --- a/cl_dll/hud_msg.cpp +++ b/cl_dll/hud_msg.cpp @@ -81,11 +81,6 @@ void CHud::MsgFunc_InitHUD( const char *pszName, int iSize, void *pbuf ) pList = pList->pNext; } -#if USE_PARTICLEMAN - if ( g_pParticleMan ) - g_pParticleMan->ResetParticles(); -#endif - //Probably not a good place to put this. pBeam = pBeam2 = NULL; pFlare = NULL; // Vit_amiN: clear egon's beam flare From dfe1b701434bac8662444ab15b64ccaaab28ef2d Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Wed, 13 Mar 2024 19:10:01 +0300 Subject: [PATCH 4/4] Add check for getcwd result in Sys_LoadModule --- public/interface.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/interface.cpp b/public/interface.cpp index 32ca96adde..6caf5ca55c 100644 --- a/public/interface.cpp +++ b/public/interface.cpp @@ -150,7 +150,9 @@ CSysModule *Sys_LoadModule( const char *pModuleName ) char szCwd[1024]; char szAbsoluteModuleName[1024]; - getcwd( szCwd, sizeof( szCwd ) ); + const char* cwdResult = getcwd( szCwd, sizeof( szCwd ) ); + if (!cwdResult) + return NULL; if ( szCwd[ strlen( szCwd ) - 1 ] == '/' ) szCwd[ strlen( szCwd ) - 1 ] = 0;