Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Client/core/DXHook/CDirect3DEvents9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "StdInc.h"
#include <d3dx9shader.h>
#include <game/CRenderWare.h>
#include <game/CFx.h>
#define DECLARE_PROFILER_SECTION_CDirect3DEvents9
#include "profiler/SharedUtil.Profiler.h"
#include "CProxyDirect3DVertexBuffer.h"
Expand Down Expand Up @@ -510,6 +511,9 @@ void CDirect3DEvents9::OnInvalidate(IDirect3DDevice9* pDevice)
{
WriteDebugEvent("CDirect3DEvents9::OnInvalidate");

if (auto game = g_pCore->GetGame())
game->GetFx()->ClearCustomShadows(true);

const HRESULT hrCooperativeLevel = pDevice->TestCooperativeLevel();
const bool bDeviceOperational = (hrCooperativeLevel == D3D_OK);
const bool bDeviceTemporarilyLost = (hrCooperativeLevel == D3DERR_DEVICELOST || hrCooperativeLevel == D3DERR_DEVICENOTRESET);
Expand Down
108 changes: 108 additions & 0 deletions Client/game_sa/CFxSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
*****************************************************************************/

#include "StdInc.h"
#include <game/RenderWare.h>
#include <game/RenderWareD3D.h>
#include "gamesa_renderware.h"
#include "CVector2D.h"
#include "CFxSA.h"
#include "CEntitySA.h"

Expand Down Expand Up @@ -359,3 +363,107 @@ void CFxSA::AddParticle(FxParticleSystems::Enum eFxParticle, const CVector& vecP
fxParticleSystem, &vecPosition, &newDirection, 0, &fxPrt, -1.0f, fBrightness, 0, 0);
}
}

namespace
{
constexpr unsigned int MAX_SCRIPT_SHADOWS = 48;
RwTexture* customShadowTextures[MAX_SCRIPT_SHADOWS] = {};

void DestroyCustomShadowTexture(RwTexture* texture)
{
if (!texture)
return;
auto native = reinterpret_cast<RwD3D9Raster*>(&texture->raster->renderResource);
if (native->texture)
native->texture->Release();
native->texture = nullptr;
RwTextureDestroy(texture);
}

unsigned short& StoredShadowCount()
{
return *reinterpret_cast<unsigned short*>(VAR_FXSystem_StoreShadows);
}
}

CFxSA::~CFxSA()
{
ClearCustomShadows(true);
}

void CFxSA::ClearCustomShadows(bool force)
{
// GTA consumes the queue before the pre-FX callback. Keep textures alive if
// rendering was skipped; device invalidation instead discards the queue.
if (force)
StoredShadowCount() = 0;
else if (StoredShadowCount() != 0)
return;

for (auto& texture : customShadowTextures)
{
DestroyCustomShadowTexture(texture);
texture = nullptr;
}
}

bool CFxSA::IsShadowsLimitReached()
{
return StoredShadowCount() >= MAX_SCRIPT_SHADOWS;
}

bool CFxSA::AddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2, SColor color,
eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings, IDirect3DBaseTexture9* customTexture)
{
if (IsShadowsLimitReached() || shadowTextureType < eShadowTextureType::CAR || shadowTextureType >= eShadowTextureType::COUNT)
return false;

const auto index = StoredShadowCount();
RwTexture* texture = reinterpret_cast<RwTexture**>(TEXTURE_FXSystem_Shadow)[static_cast<unsigned int>(shadowTextureType)];
if (customTexture)
{
if (customTexture->GetType() != D3DRTYPE_TEXTURE)
return false;
D3DSURFACE_DESC desc;
auto d3dTexture = static_cast<IDirect3DTexture9*>(customTexture);
if (FAILED(d3dTexture->GetLevelDesc(0, &desc)))
return false;

// A real RW raster without pixel allocation. The queue holds its own
// COM reference, so destroying the Lua element cannot invalidate it.
RwRaster* raster = RwRasterCreate(desc.Width, desc.Height, 32, 0x0500 | 0x04 | 0x80); // 8888 | TEXTURE | DONTALLOCATE
if (!raster)
return false;
texture = RwTextureCreate(raster);
if (!texture)
{
reinterpret_cast<int(__cdecl*)(RwRaster*)>(0x7FB020)(raster); // RwRasterDestroy
return false;
}
raster->width = desc.Width;
raster->height = desc.Height;
raster->depth = 32;
raster->format = 0x05; // 8888
auto native = reinterpret_cast<RwD3D9Raster*>(&raster->renderResource);
d3dTexture->AddRef();
native->texture = d3dTexture;
native->alpha = true;
native->format = desc.Format;
if (desc.Format >= D3DFMT_DXT1 && desc.Format <= D3DFMT_DXT5)
native->textureFlags |= 0x10;
texture->flags = 0x3302; // linear filtering, clamp U/V

// A reused queue index can only refer to an already consumed frame.
DestroyCustomShadowTexture(customShadowTextures[index]);
customShadowTextures[index] = texture;
}
if (!texture || !texture->raster)
return false;

using StoreShadow = void(__cdecl*)(unsigned char, RwTexture*, const CVector*, float, float, float, float, short, unsigned char, unsigned char,
unsigned char, float, bool, float, void*, bool);
reinterpret_cast<StoreShadow>(FUNC_FXSystem_StoreShadows)(static_cast<unsigned char>(shadowType), texture, &vecPosition, vecOffset1.fX, vecOffset1.fY,
vecOffset2.fX, vecOffset2.fY, color.A, color.R, color.G, color.B, fZDistance, bDrawOnWater, 1.0f,
nullptr, bDrawOnBuildings);
return StoredShadowCount() > index;
}
8 changes: 8 additions & 0 deletions Client/game_sa/CFxSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class FxSystem_c;
#define FUNC_CFx_TriggerBulletSplash 0x4a10e0
#define FUNC_CFx_TriggerFootSplash 0x4a1150
#define FUNC_FXSystem_c_AddParticle 0x4AA440
#define FUNC_FXSystem_StoreShadows 0x707390
#define VAR_FXSystem_StoreShadows 0xC403DC
#define TEXTURE_FXSystem_Shadow 0xC403E0

class CFxSAInterface
{
Expand Down Expand Up @@ -61,6 +64,11 @@ class CFxSA : public CFx
public:
CFxSA(CFxSAInterface* pInterface) { m_pInterface = pInterface; }

~CFxSA();
void ClearCustomShadows(bool force = false) override;
bool AddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2, SColor color,
eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings, IDirect3DBaseTexture9* customTexture = nullptr);
static bool IsShadowsLimitReached();
void AddBlood(CVector& vecPosition, CVector& vecDirection, int iCount, float fBrightness);
void AddWood(CVector& vecPosition, CVector& vecDirection, int iCount, float fBrightness);
void AddSparks(CVector& vecPosition, CVector& vecDirection, float fForce, int iCount, CVector vecAcrossLine, unsigned char ucBlurIf0, float fSpread,
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3664,6 +3664,9 @@ void CClientGame::StaticPostWorldProcessPedsAfterPreRenderHandler()

void CClientGame::StaticPreFxRenderHandler()
{
// Clear custom shadows, so that we don't have any left over from the previous frame.
g_pGame->GetFx()->ClearCustomShadows();

// RenderFadingInEntities is done at this point, so alpha entity list callbacks
// no longer reference CModelRenderer's queue elements.
g_pClientGame->GetModelRenderer()->NotifyFrameEnd();
Expand Down
8 changes: 8 additions & 0 deletions Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8135,6 +8135,14 @@ bool CStaticFunctionDefinitions::FxCreateParticle(FxParticleSystems::Enum eFxPar
return true;
}

bool CStaticFunctionDefinitions::FxAddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1,
const CVector2D& vecOffset2, SColor color, eShadowType shadowType, float fZDistance, bool bDrawOnWater,
bool bDrawOnBuildings, IDirect3DBaseTexture9* customTexture)
{
return g_pGame->GetFx()->AddShadow(shadowTextureType, vecPosition, vecOffset1, vecOffset2, color, shadowType, fZDistance, bDrawOnWater, bDrawOnBuildings,
customTexture);
}

CClientEffect* CStaticFunctionDefinitions::CreateEffect(CResource& Resource, const SString& strFxName, const CVector& vecPosition, bool bSoundEnable)
{
CClientEffect* pFx = m_pManager->GetEffectManager()->Create(strFxName, vecPosition, INVALID_ELEMENT_ID, bSoundEnable);
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,9 @@ class CStaticFunctionDefinitions
static bool FxAddFootSplash(CVector& vecPosition);
static bool FxCreateParticle(FxParticleSystems::Enum eFxParticle, CVector& vecPosition, CVector& vecDirection, float fR, float fG, float fB, float fA,
bool bRandomizeColors, std::uint32_t iCount, float fBrightness, float fSize, bool bRandomizeSizes, float fLife);
static bool FxAddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2,
SColor color, eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings,
IDirect3DBaseTexture9* customTexture = nullptr);
static CClientEffect* CreateEffect(CResource& Resource, const SString& strFxName, const CVector& vecPosition, bool bSoundEnable);

// Sound funcs
Expand Down
21 changes: 21 additions & 0 deletions Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,27 @@ ADD_ENUM(_D3DFORMAT::D3DFMT_G32R32F, "g32r32f")
ADD_ENUM(_D3DFORMAT::D3DFMT_A32B32G32R32F, "a32b32g32r32f")
IMPLEMENT_ENUM_CLASS_END("surface-format")

IMPLEMENT_ENUM_CLASS_BEGIN(eShadowTextureType)
ADD_ENUM(eShadowTextureType::CAR, "car")
ADD_ENUM(eShadowTextureType::PED, "ped")
ADD_ENUM(eShadowTextureType::HELI, "heli")
ADD_ENUM(eShadowTextureType::BIKE, "bike")
ADD_ENUM(eShadowTextureType::RCBARON, "rcbaron")
ADD_ENUM(eShadowTextureType::EXPLOSION, "explosion")
ADD_ENUM(eShadowTextureType::HEADLIGHT1, "headlight1")
ADD_ENUM(eShadowTextureType::HEADLIGHT2, "headlight2")
ADD_ENUM(eShadowTextureType::BLOOD, "blood")
ADD_ENUM(eShadowTextureType::HANDMAN, "handman")
ADD_ENUM(eShadowTextureType::WINCRACK, "wincrack")
ADD_ENUM(eShadowTextureType::LAMP, "lamp")
IMPLEMENT_ENUM_CLASS_END("shadow-texture-type")

IMPLEMENT_ENUM_CLASS_BEGIN(eShadowType)
ADD_ENUM(eShadowType::DEFAULT, "default")
ADD_ENUM(eShadowType::ADDITIVE, "additive")
ADD_ENUM(eShadowType::INVCOLOR, "invcolor")
IMPLEMENT_ENUM_CLASS_END("shadow-type")

IMPLEMENT_ENUM_CLASS_BEGIN(eRenderStage)
ADD_ENUM(eRenderStage::PRE_FX, "prefx")
ADD_ENUM(eRenderStage::POST_FX, "postfx")
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <game/CRenderWare.h>
#include <game/CHud.h>
#include <game/CStreaming.h>
#include <game/CFx.h>
#include <type_traits>

#include "enums/WeaponProperty.h"
Expand Down Expand Up @@ -93,6 +94,8 @@ DECLARE_ENUM_CLASS(eModelIdeFlag);
DECLARE_ENUM_CLASS(_D3DFORMAT);
DECLARE_ENUM_CLASS(eRenderStage);
DECLARE_ENUM(FxParticleSystems::Enum);
DECLARE_ENUM_CLASS(eShadowTextureType);
DECLARE_ENUM_CLASS(eShadowType);
DECLARE_ENUM(ePools);
DECLARE_ENUM(WorldProperty::Enum);
DECLARE_ENUM_CLASS(eModelLoadState);
Expand Down
36 changes: 36 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ void CLuaEffectDefs::LoadFunctions()
{"setEffectDensity", SetEffectDensity},
{"getEffectDensity", GetEffectDensity},
{"fxCreateParticle", ArgumentParser<FxCreateParticle>},
{"fxAddShadow", ArgumentParser<FxAddShadow>},
};

// Add functions
Expand Down Expand Up @@ -62,6 +63,7 @@ void CLuaEffectDefs::AddClass(lua_State* luaVM)
lua_classfunction(luaVM, "addWaterSplash", "fxAddWaterSplash");
lua_classfunction(luaVM, "addWood", "fxAddWood");
lua_classfunction(luaVM, "createParticle", "fxCreateParticle");
lua_classfunction(luaVM, "createShadow", "fxAddShadow");

lua_classfunction(luaVM, "setDensity", "setEffectDensity");
lua_classfunction(luaVM, "setSpeed", "setEffectSpeed");
Expand Down Expand Up @@ -651,3 +653,37 @@ bool CLuaEffectDefs::FxCreateParticle(FxParticleSystems::Enum eParticleSystem, C
bRandomizeColors.value_or(false), iCount.value_or(1), fBrightness.value_or(1.0f), fSize.value_or(0.3f),
bRandomizeSizes.value_or(false), fLife.value_or(1.0f));
}

bool CLuaEffectDefs::FxAddShadow(std::variant<eShadowTextureType, CClientTexture*> texture, CVector vecPosition, CVector2D vecOffset1, CVector2D vecOffset2,
SColor color, eShadowType shadowType, float zDistance, bool bDrawOnWater, bool bDrawOnBuildings)
{
if (!std::isfinite(vecPosition.fX) || !std::isfinite(vecPosition.fY) || !std::isfinite(vecPosition.fZ) || !std::isfinite(vecOffset1.fX) ||
!std::isfinite(vecOffset1.fY) || !std::isfinite(vecOffset2.fX) || !std::isfinite(vecOffset2.fY))
throw std::invalid_argument("Position and offsets must be finite");
if (vecOffset1.Length() > 32)
{
throw std::invalid_argument("First offset can not be longer than 32 units");
}
else if (vecOffset2.Length() > 32) // bigger and close to limit shadows size can be partially invisible
{
throw std::invalid_argument("Second offset can not be longer than 32 units");
}
else if (!std::isfinite(zDistance) || zDistance < 0 || zDistance > 3000) // negative distance not working
{
throw std::invalid_argument("Z Distance must be between 0.0 and 3000.0");
}
IDirect3DBaseTexture9* customTexture = nullptr;
eShadowTextureType shadowTextureType = eShadowTextureType::CAR;
if (auto element = std::get_if<CClientTexture*>(&texture))
{
customTexture = (*element)->GetTextureItem()->m_pD3DTexture;
if (!customTexture)
return false;
if (customTexture->GetType() != D3DRTYPE_TEXTURE)
throw std::invalid_argument("Shadow texture must be a 2D texture");
}
else
shadowTextureType = std::get<eShadowTextureType>(texture);
return CStaticFunctionDefinitions::FxAddShadow(shadowTextureType, vecPosition, vecOffset1, vecOffset2, color, shadowType, zDistance, bDrawOnWater,
bDrawOnBuildings, customTexture);
}
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEffectDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ class CLuaEffectDefs : public CLuaDefs
static bool FxCreateParticle(FxParticleSystems::Enum eParticleSystem, CVector vecPosition, CVector vecDirection, float fR, float fG, float fB, float fA,
std::optional<bool> bRandomizeColors, std::optional<std::uint32_t> iCount, std::optional<float> fBrightness,
std::optional<float> fSize, std::optional<bool> bRandomizeSizes, std::optional<float> fLife);
static bool FxAddShadow(std::variant<eShadowTextureType, CClientTexture*> texture, CVector vecPosition, CVector2D vecOffset1, CVector2D vecOffset2,
SColor color, eShadowType shadowType, float zDistance, bool bDrawOnWater, bool bDrawOnBuildings);
};
33 changes: 33 additions & 0 deletions Client/sdk/game/CFx.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,35 @@
#pragma once
#include "enums/FxParticleSystems.h"

#include <SharedUtil.Misc.h>
class CVector2D;
struct IDirect3DBaseTexture9;
enum class eShadowTextureType
{
CAR,
PED,
HELI,
BIKE,
RCBARON,
EXPLOSION,
HEADLIGHT1,
HEADLIGHT2,
BLOOD,
HANDMAN,
WINCRACK,
LAMP,
COUNT

};

enum class eShadowType
{
NONE,
DEFAULT,
ADDITIVE,
INVCOLOR,
};

class CEntity;
class CVector;
class CVehicle;
Expand All @@ -37,4 +66,8 @@ class CFx
virtual void TriggerFootSplash(CVector& vecPosition) = 0;
virtual void AddParticle(FxParticleSystems::Enum eFxParticle, const CVector& vecPosition, const CVector& vecDirection, float fR, float fG, float fB,
float fA, bool bRandomizeColors, std::uint32_t iCount, float fBrightness, float fSize, bool bRandomizeSizes, float fLife) = 0;
virtual bool AddShadow(eShadowTextureType shadowTextureType, const CVector& vecPosition, const CVector2D& vecOffset1, const CVector2D& vecOffset2,
SharedUtil::SColor color, eShadowType shadowType, float fZDistance, bool bDrawOnWater, bool bDrawOnBuildings,
IDirect3DBaseTexture9* customTexture = nullptr) = 0;
virtual void ClearCustomShadows(bool force = false) = 0;
};
Loading