5389 lines
183 KiB
C++
5389 lines
183 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE: ParticleFx.cpp
|
|
// BY : Mark Nicholson
|
|
// FOR : Rockstar North
|
|
// ON : 31 Oct 2005
|
|
// WHAT: Implementation of the Rage Particle effects
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// INCLUDES
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "Vfx/Particles/PtFxManager.h"
|
|
|
|
// Rage headers
|
|
#include "audiosoundtypes/sound.h"
|
|
#include "bank/bkmgr.h"
|
|
#include "grcore/allocscope.h"
|
|
#include "grcore/gfxcontext.h"
|
|
#include "grcore/im.h"
|
|
#include "grcore/texturegcm.h"
|
|
#include "grprofile/timebars.h"
|
|
#include "rmptfx/ptxdrawlist.h"
|
|
#include "rmptfx/ptxInterface.h"
|
|
#include "rmptfx/ptxconfig.h"
|
|
#include "rmptfx/ptxmanager.h"
|
|
#include "rmptfx/ptxu_acceleration.h"
|
|
#include "rmptfx/ptxu_dampening.h"
|
|
#include "script/thread.h"
|
|
#include "system/param.h"
|
|
#include "system/memops.h"
|
|
#include "system/memory.h"
|
|
#include "vectormath/classes.h"
|
|
|
|
// Framework headers
|
|
#include "fwscene/world/worldmgr.h"
|
|
#include "fwscene/search/Search.h"
|
|
#include "vfx/channel.h"
|
|
#include "vfx/ptfx/ptfxasset.h"
|
|
#include "vfx/ptfx/ptfxattach.h"
|
|
#include "vfx/ptfx/ptfxconfig.h"
|
|
#include "vfx/ptfx/ptfxdebug.h"
|
|
#include "vfx/ptfx/ptfxreg.h"
|
|
#include "vfx/ptfx/ptfxscript.h"
|
|
#include "vfx/ptfx/ptfxwrapper.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_decal.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_decalpool.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_decaltrail.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_fire.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_fogvolume.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_light.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_liquid.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_river.h"
|
|
#include "vfx/ptfx/behaviours/ptxu_zcull.h"
|
|
#include "vfx/vfxutil.h"
|
|
#include "vfx/vfxwidget.h"
|
|
|
|
// Game headers
|
|
#include "Audio/ambience/ambientaudioentity.h"
|
|
#include "Camera/CamInterface.h"
|
|
#include "Camera/Cinematic/CinematicDirector.h"
|
|
#include "camera/base/BaseCamera.h"
|
|
#include "camera/gameplay/aim/FirstPersonShooterCamera.h"
|
|
#include "Camera/Gameplay/GameplayDirector.h"
|
|
#include "Camera/Viewports/ViewportManager.h"
|
|
#include "Control/replay/replay.h"
|
|
#include "Control/replay/effects/ParticleMiscFxPacket.h"
|
|
#include "Core/Game.h"
|
|
#include "Cutscene/CutSceneManagerNew.h"
|
|
#include "Game/Wind.h"
|
|
#include "ModelInfo/VehicleModelInfoFlags.h"
|
|
#include "Network/NetworkInterface.h"
|
|
#include "Peds/Ped.h"
|
|
#include "Physics/Physics.h"
|
|
#include "Renderer/Deferred/DeferredLighting.h"
|
|
#include "Renderer/DrawLists/drawListMgr.h"
|
|
#include "Renderer/Lights/AsyncLightOcclusionMgr.h"
|
|
#include "Renderer/Lights/Lights.h"
|
|
#include "Renderer/Mirrors.h"
|
|
#include "Renderer/Occlusion.h"
|
|
#include "Renderer/PostProcessFX.h"
|
|
#include "Renderer/RenderPhases/RenderPhaseReflection.h"
|
|
#include "Renderer/rendertargets.h"
|
|
#include "Renderer/RenderThread.h"
|
|
#include "Renderer/RenderPhases/RenderPhaseCascadeShadows.h"
|
|
#include "renderer/RenderPhases/RenderPhaseMirrorReflection.h"
|
|
#include "Renderer/River.h"
|
|
#include "weapons/projectiles/Projectile.h"
|
|
#if __PPU
|
|
#include "Renderer/Shadows/Shadows.h"
|
|
#endif
|
|
#include "Renderer/Util/ShaderUtil.h"
|
|
#include "Scene/DataFileMgr.h"
|
|
#include "Scene/World/GameWorld.h"
|
|
#include "Scene/World/GameWorldHeightMap.h"
|
|
#include "Script/Script.h"
|
|
#include "Script/Handlers/GameScriptResources.h"
|
|
#include "Shaders/ShaderLib.h"
|
|
#include "Scene/portals/Portal.h"
|
|
#include "Streaming/Streaming.h"
|
|
#include "System/ControlMgr.h"
|
|
#if RSG_PC
|
|
#include "System/SettingsManager.h"
|
|
#endif
|
|
#include "System/ThreadPriorities.h"
|
|
#include "TimeCycle/TimeCycle.h"
|
|
#include "TimeCycle/TimeCycleConfig.h"
|
|
#include "VehicleAI/Task/TaskVehicleAnimation.h"
|
|
#include "Vfx/Decals/DecalManager.h"
|
|
#include "Vfx/Metadata/PtFxAssetInfo.h"
|
|
#include "Vfx/Misc/Coronas.h"
|
|
#include "Vfx/Misc/Fire.h"
|
|
#include "Vfx/Misc/FogVolume.h"
|
|
#include "Vfx/Particles/PtFxDebug.h"
|
|
#include "Vfx/Particles/PtFxEntity.h"
|
|
#include "Vfx/Systems/VfxGlass.h"
|
|
#include "Vfx/Systems/VfxLens.h"
|
|
#include "Vfx/Systems/VfxMaterial.h"
|
|
#include "Vfx/Systems/VfxTrail.h"
|
|
#include "Vfx/Systems/VfxWater.h"
|
|
#include "Vfx/Systems/VfxWeapon.h"
|
|
#include "Vfx/VfxHelper.h"
|
|
#include "Vfx/VfxSettings.h"
|
|
#include "Vfx/Sky/Sky.h"
|
|
#include "Weapons/Weapon.h"
|
|
#include "control/replay/ReplaySettings.h"
|
|
|
|
#if __D3D11
|
|
#include "grcore/rendertarget_d3d11.h"
|
|
#endif
|
|
#if RSG_ORBIS
|
|
#include "grcore/rendertarget_gnm.h"
|
|
#include "grcore/texturefactory_gnm.h"
|
|
#endif
|
|
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
#include "grcore/gputimer.h"
|
|
grcGpuTimer g_ptfxGPUTimerMedRes;
|
|
grcGpuTimer g_ptfxGPUTimerLowRes;
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// OPTIMISATIONS - TURN ON IN OPTIMISATIONS.H
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
VFX_PTFX_OPTIMISATIONS()
|
|
RMPTFX_OPTIMISATIONS()
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DEFINES
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if __D3D11 || RSG_ORBIS
|
|
#define DEFAULT_TESSELLATION_FACTOR (5.0f)
|
|
#define DEFAULT_SHADOW_INTENSITY (0.5f)
|
|
#endif
|
|
|
|
#define PTFX_ENABLE_SHADOW_CULL_STATE_UPDATE (0)
|
|
|
|
#define PTFX_WATER_REFRACTION_ENABLED (0)
|
|
|
|
#define PTXSTR_GAME_PROGVAR_POST_PROCESS_FLIPDEPTH_NEARPLANEFADE_PARAM "postProcess_FlipDepth_NearPlaneFade_Params"
|
|
#define PTXSTR_GAME_PROGVAR_WATER_FOG_PARAM "waterfogPtfxParams"
|
|
#define PTXSTR_GAME_PROGVAR_WATER_FOG_COLOR_BUFFER "waterColorTexture"
|
|
#define PTXSTR_GAME_PROGVAR_SHADEDMAP "ShadedPtFxMap"
|
|
#define PTXSTR_GAME_PROGVAR_SHADEDPARAMS "gShadedPtFxParams"
|
|
#define PTXSTR_GAME_PROGVAR_SHADEDPARAMS2 "gShadedPtFxParams2"
|
|
#define PTXSTR_GAME_PROGVAR_FOG_RAY_MAP "FogRayMap"
|
|
#define PTXSTR_GAME_PROGVAR_ACTIVE_SHADOW_CASCADES "activeShadowCascades"
|
|
#define PTXSTR_GAME_PROGVAR_NUM_ACTIVE_SHADOW_CASCADES "numActiveShadowCascades"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ENUMERATIONS
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
enum PtFxDataVolumeTypes
|
|
{
|
|
PTFX_DATAVOLUMETYPE_FIRE_CONTAINED = 0,
|
|
PTFX_DATAVOLUMETYPE_FIRE,
|
|
PTFX_DATAVOLUMETYPE_WATER,
|
|
PTFX_DATAVOLUMETYPE_SAND,
|
|
PTFX_DATAVOLUMETYPE_MUD,
|
|
PTFX_DATAVOLUMETYPE_BLOOD,
|
|
PTFX_DATAVOLUMETYPE_DUST,
|
|
PTFX_DATAVOLUMETYPE_SMOKE,
|
|
PTFX_DATAVOLUMETYPE_BLAZE,
|
|
PTFX_DATAVOLUMETYPE_SOAK_SLOW,
|
|
PTFX_DATAVOLUMETYPE_SOAK_MEDIUM,
|
|
PTFX_DATAVOLUMETYPE_SOAK_FAST,
|
|
PTFX_DATAVOLUMETYPE_FIRE_FLAMETHROWER,
|
|
};
|
|
|
|
|
|
enum ptfxBucketRenderStage
|
|
{
|
|
PTFX_BUCKET_RENDER_STAGE_NORMAL = 0,
|
|
PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION,
|
|
PTFX_BUCKET_RENDER_STAGE_REFRACTION,
|
|
PTFX_BUCKET_RENDER_STAGE_SHADOWS,
|
|
PTFX_BUCKET_RENDER_STAGE_SIMPLE,
|
|
PTFX_BUCKET_RENDER_STAGE_TOTAL
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EXTERNALLY DECLARED VARIABLES
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if __BANK
|
|
extern bool gLastGenMode;
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// GLOBAL VARIABLES
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
CPtFxManager g_ptFxManager;
|
|
|
|
CPtFxCallbacks g_ptFxCallbacks;
|
|
|
|
// params
|
|
#if __DEV
|
|
XPARAM(ptfxloadall);
|
|
PARAM(ptfxloadmost, "loads most ptfx assets - skips any rayfire and script assets");
|
|
#endif
|
|
#if !__FINAL
|
|
XPARAM(ptfxassetoverride);
|
|
#endif
|
|
// We can't use timebars all the time because there are some frames where the ptx update never gets called (e.g. when paused) and not
|
|
// calling the timebar functions for every thread every frame throws off the absolute time display
|
|
PARAM(ptfxtimebar, "display the ptfx timebar along with all the others");
|
|
|
|
// settings
|
|
dev_float PTFX_SOAK_SLOW_INC_LEVEL_LOW = 0.0f;
|
|
dev_float PTFX_SOAK_SLOW_INC_LEVEL_HIGH = 0.06f;
|
|
|
|
dev_float PTFX_SOAK_MEDIUM_INC_LEVEL_LOW = 0.0f;
|
|
dev_float PTFX_SOAK_MEDIUM_INC_LEVEL_HIGH = 0.3f;
|
|
|
|
dev_float PTFX_SOAK_FAST_INC_LEVEL_LOW = 0.0f;
|
|
dev_float PTFX_SOAK_FAST_INC_LEVEL_HIGH = 1.5f;
|
|
|
|
__THREAD ptfxBucketRenderStage g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_NORMAL;
|
|
__THREAD u8 g_PtfxWaterSurfaceRenderState = PTFX_WATERSURFACE_RENDER_BOTH;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CODE
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CLASS CPtfxShadowDrawToAllCascades
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
class CPtfxShadowDrawToAllCascades : public ptxDrawInterface::ptxMultipleDrawInterface
|
|
{
|
|
public:
|
|
CPtfxShadowDrawToAllCascades(s32 noOfCascades, grcViewport *pViewports, Vec4V *pWindows, bool bUseInstancedShadow)
|
|
{
|
|
m_noOfCascades = noOfCascades;
|
|
m_pViewports = pViewports;
|
|
m_pWindows = pWindows;
|
|
m_bUseInstancedShadow = bUseInstancedShadow;
|
|
m_uCulled = 0;
|
|
}
|
|
~CPtfxShadowDrawToAllCascades()
|
|
{
|
|
}
|
|
u32 NoOfDraws()
|
|
{
|
|
return (u32)m_noOfCascades;
|
|
}
|
|
void PreDraw(u32 i)
|
|
{
|
|
const Vec4V temp = FloatToIntRaw<0>(m_pWindows[i]);
|
|
const int x = temp.GetXi();
|
|
const int y = temp.GetYi();
|
|
const int w = temp.GetZi();
|
|
const int h = temp.GetWi();
|
|
|
|
grcViewport::SetCurrent((const grcViewport *)&m_pViewports[i],false);
|
|
grcViewport::SetCurrentWorldIdentity();
|
|
grcViewport::GetCurrent()->ResetWindow();
|
|
grcViewport::GetCurrent()->SetWindow(x, y, w, h);
|
|
|
|
#if RSG_DURANGO || RSG_ORBIS
|
|
GRCDEVICE.SetProgramResources();
|
|
#endif
|
|
|
|
#if CASCADE_SHADOW_TEXARRAY
|
|
grcTextureFactory::GetInstance().SetArrayView(i);
|
|
#endif //CASCADE_SHADOW_TEXARRAY
|
|
}
|
|
void PreDrawInstanced()
|
|
{
|
|
//do nothing
|
|
}
|
|
void PostDraw(u32)
|
|
{
|
|
g_ptFxManager.SetParticleShadowsRenderedFlag(true);
|
|
}
|
|
void PostDrawInstanced()
|
|
{
|
|
g_ptFxManager.SetParticleShadowsRenderedFlag(true);
|
|
}
|
|
|
|
|
|
grcViewport *GetViewport(int i) { return (grcViewport *)&m_pViewports[i]; }
|
|
bool IsUsingInstanced() { return m_bUseInstancedShadow; }
|
|
void SetCulledState(bool Culled, u32 i)
|
|
{
|
|
Culled ? m_uCulled |= (1<<i) : m_uCulled &= ~(1<<i);
|
|
}
|
|
bool GetCulledState(int i) { return ((1 << i) & m_uCulled) ? true : false; }
|
|
bool ShouldBeRendered(ptxEffectInst* pEffectInst)
|
|
{
|
|
#if PTFX_ENABLE_SHADOW_CULL_STATE_UPDATE
|
|
return (RMPTFX_EDITOR_ONLY(RMPTFXMGR.GetInterface().IsConnected() || ) (pEffectInst && pEffectInst->GetEffectRule() && !pEffectInst->GetEffectRule()->GetHasNoShadows() && !pEffectInst->GetDataDBMT(RMPTFXTHREADER.GetDrawBufferId())->GetCustomFlag(PTFX_DRAW_SHADOW_CULLED)));
|
|
#else
|
|
return (!pEffectInst->GetDataDBMT(RMPTFXTHREADER.GetDrawBufferId())->GetCustomFlag(PTFX_DRAW_SHADOW_CULLED));
|
|
#endif
|
|
}
|
|
|
|
void SetNumActiveCascades(u8 numActiveCascades) { m_numActiveCascades = numActiveCascades;}
|
|
u32 GetNumActiveCascades() { return m_numActiveCascades;}
|
|
public:
|
|
s32 m_noOfCascades;
|
|
grcViewport *m_pViewports;
|
|
Vec4V *m_pWindows;
|
|
bool m_bUseInstancedShadow;
|
|
u8 m_uCulled;
|
|
u8 m_numActiveCascades;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CLASS CSetLightingConstants
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
bool CSetLightingConstants::ms_defShadingConstantsCached = false;
|
|
grcEffectVar CSetLightingConstants::ms_shaderVarIdDepthTexture = grcevNONE;
|
|
grcEffectVar CSetLightingConstants::ms_shaderVarIdFogRayTexture = grcevNONE;
|
|
grcEffectVar CSetLightingConstants::ms_shaderVarIdOOScreenSize = grcevNONE;
|
|
grcEffectVar CSetLightingConstants::ms_shaderVarIdProjParams = grcevNONE;
|
|
grcEffectVar CSetLightingConstants::ms_shaderVarIdShearParams0 = grcevNONE;
|
|
grcEffectVar CSetLightingConstants::ms_shaderVarIdShearParams1 = grcevNONE;
|
|
grcEffectVar CSetLightingConstants::ms_shaderVarIdShearParams2 = grcevNONE;
|
|
grcEffectTechnique CSetLightingConstants::ms_projSpriteTechniqueId = grcetNONE;
|
|
|
|
bool CSetLightingConstants::PreDraw(const ptxEffectInst* pEffectInst, const ptxEmitterInst* pEmitterInst, Vec3V_In vMin_In, Vec3V_In vMax_In, u32 shaderHashName, grmShader* pShader) const
|
|
{
|
|
if (pEmitterInst != NULL)
|
|
{
|
|
const ptxParticleRule* pEffectRule = pEmitterInst->GetParticleRule();
|
|
if (pEffectRule != NULL)
|
|
{
|
|
// get the technique id for this effect, we only want to set these shader constants
|
|
// for a specific set of techniques
|
|
grcEffectTechnique curTechnique = pEffectRule->GetShaderInst().GetShaderTemplateTechnique();
|
|
SetDeferredShadingGlobals(pShader, curTechnique);
|
|
}
|
|
}
|
|
|
|
if( g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SHADOWS )
|
|
return true;
|
|
|
|
Vec3V vMin = vMin_In;
|
|
Vec3V vMax = vMax_In;
|
|
|
|
if (m_unlitShader != shaderHashName)
|
|
{
|
|
spdAABB box(vMin, vMax);
|
|
|
|
// we need to force light shader constants to be pushed, otherwise these will contain
|
|
// whatever values were set last (which might be invalid if there are no active lights).
|
|
Lights::UseLightsInArea(box, pEffectInst->GetInInterior(), true, false, true);
|
|
CShaderLib::SetGlobalInInterior(pEffectInst->GetInInterior());
|
|
|
|
// set ambient globals
|
|
Lights::m_lightingGroup.SetAmbientLightingGlobals();
|
|
|
|
if (pEffectInst != NULL)
|
|
{
|
|
float natAmbScale, artAmbScale;
|
|
pEffectInst->GetAmbientScales(natAmbScale, artAmbScale);
|
|
CShaderLib::SetGlobalNaturalAmbientScale(natAmbScale);
|
|
CShaderLib::SetGlobalArtificialAmbientScale(artAmbScale);
|
|
}
|
|
|
|
}
|
|
|
|
if (ms_defShadingConstantsCached == false)
|
|
{
|
|
CacheDeferredShadingGlobals(pShader);
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
void CSetLightingConstants::CacheDeferredShadingGlobals(grmShader* pShader)
|
|
{
|
|
bool bOk;
|
|
|
|
if (ms_projSpriteTechniqueId == grcetNONE)
|
|
{
|
|
ms_projSpriteTechniqueId = pShader->LookupTechnique("RGBA_proj", false);
|
|
}
|
|
|
|
ms_shaderVarIdDepthTexture = pShader->LookupVar("gbufferTextureDepth", false);
|
|
bOk = ms_shaderVarIdDepthTexture != grcevNONE;
|
|
ms_shaderVarIdFogRayTexture = pShader->LookupVar("FogRayMap", false);
|
|
bOk = ms_shaderVarIdFogRayTexture != grcevNONE;
|
|
ms_shaderVarIdOOScreenSize = pShader->LookupVar("deferredLightScreenSize", false);
|
|
bOk = bOk && ms_shaderVarIdOOScreenSize != grcevNONE;
|
|
ms_shaderVarIdProjParams = pShader->LookupVar("deferredProjectionParams", false);
|
|
bOk = bOk && ms_shaderVarIdProjParams != grcevNONE;
|
|
ms_shaderVarIdShearParams0 = pShader->LookupVar("deferredPerspectiveShearParams0", false);
|
|
bOk = bOk && ms_shaderVarIdShearParams0 != grcevNONE;
|
|
ms_shaderVarIdShearParams1 = pShader->LookupVar("deferredPerspectiveShearParams1", false);
|
|
bOk = bOk && ms_shaderVarIdShearParams1 != grcevNONE;
|
|
ms_shaderVarIdShearParams2 = pShader->LookupVar("deferredPerspectiveShearParams2", false);
|
|
bOk = bOk && ms_shaderVarIdShearParams2 != grcevNONE;
|
|
|
|
ms_defShadingConstantsCached = bOk;
|
|
}
|
|
|
|
void CSetLightingConstants::SetDeferredShadingGlobals(grmShader* pShader, grcEffectTechnique techniqueId)
|
|
{
|
|
// if constant ids are not cached or the current technique is not a projection one
|
|
// we cannot set the constants
|
|
if (ms_defShadingConstantsCached == false || techniqueId != ms_projSpriteTechniqueId )
|
|
{
|
|
return;
|
|
}
|
|
|
|
pShader->SetVar(ms_shaderVarIdDepthTexture,
|
|
#if __PPU
|
|
CRenderTargets::GetDepthBufferAsColor() );
|
|
#else
|
|
# if DEVICE_MSAA
|
|
GRCDEVICE.GetMSAA() ? CRenderTargets::GetDepthResolved() :
|
|
# endif
|
|
CRenderTargets::GetDepthBuffer() );
|
|
#endif // platforms
|
|
|
|
// calc and set the one over screen size shader var
|
|
Vec4V vOOScreenSize(0.0f, 0.0f, 1.0f/GRCDEVICE.GetWidth(), 1.0f/GRCDEVICE.GetHeight());
|
|
pShader->SetVar(ms_shaderVarIdOOScreenSize, RCC_VECTOR4(vOOScreenSize));
|
|
|
|
Vec4V projParams;
|
|
Vec3V shearProj0, shearProj1, shearProj2;
|
|
DeferredLighting::GetProjectionShaderParams(projParams, shearProj0, shearProj1, shearProj2);
|
|
|
|
pShader->SetVar(ms_shaderVarIdProjParams, projParams);
|
|
pShader->SetVar(ms_shaderVarIdShearParams0, shearProj0);
|
|
pShader->SetVar(ms_shaderVarIdShearParams1, shearProj1);
|
|
pShader->SetVar(ms_shaderVarIdShearParams2, shearProj2);
|
|
|
|
pShader->SetVar(ms_shaderVarIdFogRayTexture, PostFX::GetFogRayRT());
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CLASS CPtFxCallbacks
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxCallbacks::GetMatrixFromBoneIndex(Mat34V_InOut boneMtx, const void* pEntity, int boneIndex, bool useAltSkeleton)
|
|
{
|
|
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
|
|
CVfxHelper::GetMatrixFromBoneIndex(boneMtx, pActualEntity, boneIndex, useAltSkeleton);
|
|
}
|
|
|
|
void CPtFxCallbacks::UpdateFxInst(ptxEffectInst* pFxInst)
|
|
{
|
|
// test if this effect has any additional data attached to it that needs processed (e.g. fires)
|
|
// rmptfx is storing enough data to create a capsule
|
|
if (pFxInst->GetIsPlaying() && pFxInst->GetUseDataVolume())
|
|
{
|
|
g_ptFxManager.UpdateDataCapsule(pFxInst);
|
|
}
|
|
|
|
// cache ambient scalars and interior location
|
|
if (pFxInst->GetIsFinished() == false)
|
|
{
|
|
Vec3V vPos = pFxInst->GetCurrPos();
|
|
Vec3V prevPos = pFxInst->GetPrevPos();
|
|
const bool bHasPtfxMoved = (IsEqualAll(vPos, prevPos) == 0);
|
|
|
|
fwEntity* pAttachEntity = ptfxAttach::GetAttachEntity(pFxInst);
|
|
CEntity* pEntity = static_cast<CEntity*>(pAttachEntity);
|
|
const bool bFirstTimeUpdate = (false == pFxInst->GetAmbientScalesSetup());
|
|
|
|
if (g_ptFxManager.GetForceVehicleInteriorFlag())
|
|
{
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_IS_VEHICLE_INTERIOR);
|
|
}
|
|
else
|
|
{
|
|
pFxInst->GetDataDB()->ClearCustomFlag(PTFX_IS_VEHICLE_INTERIOR);
|
|
if(pEntity)
|
|
{
|
|
//Let's check if the effect inst is attached to the vehicle
|
|
if(pEntity->GetIsTypeVehicle())
|
|
{
|
|
//If so, check if is the current vehicle that the camera is in
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pEntity);
|
|
if(pVehicle->IsCamInsideVehicleInterior() && pFxInst->GetFlag(PTFX_EFFECTINST_FORCE_VEHICLE_INTERIOR))
|
|
{
|
|
//Let's set the flag when the current vehicle has the camera in it and the effect inst has the force vehicle interior flag set
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_IS_VEHICLE_INTERIOR);
|
|
}
|
|
}
|
|
else if(CVfxHelper::IsEntityInCurrentVehicle(pEntity))
|
|
{
|
|
//Check if the effect inst is attached to weapon/ped inside the vehicle
|
|
//we wont get into this scope if entity is a vehicle due to previous condition
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_IS_VEHICLE_INTERIOR);
|
|
}
|
|
else if (pFxInst->GetFlag(PTFX_EFFECTINST_FORCE_VEHICLE_INTERIOR))
|
|
{
|
|
// check for effect inst indirectly attached to current vehicle or weapon/ped inside it
|
|
CPed* pPed = const_cast<CPed*>(camInterface::GetGameplayDirector().GetFollowPed());
|
|
if (pPed && CVfxHelper::IsEntityInCurrentVehicle(pPed) )
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_IS_VEHICLE_INTERIOR);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Always update the first time for every effect instance or if the ptfx has moved
|
|
if(bHasPtfxMoved || bFirstTimeUpdate)
|
|
{
|
|
const bool bShortLiveEffect = pFxInst->GetEffectRule()->GetIsShortLived();
|
|
bool bGetAmbientScalesFromAttachedEntity = false;
|
|
bool bGetInteriorFromAttachedEntity = false;
|
|
|
|
fwInteriorLocation interiorLocation;
|
|
|
|
// If we've got a valid cached result for interior test, use it
|
|
if(pFxInst->GetIsInteriorLocationCached())
|
|
{
|
|
u32 packedInteriorLocation = pFxInst->GetInteriorLocation();
|
|
interiorLocation.SetAsUint32(packedInteriorLocation);
|
|
}
|
|
else if(pEntity)
|
|
{
|
|
//If the ptfx is attached to the entity then we set the ambient scales and interior from the entity
|
|
//This does not cost anything
|
|
|
|
//Uncomment the third part if we want particles not use artist set
|
|
// ambient scales for certain entities
|
|
if(pEntity->GetUseAmbientScale() /* && pEntity->GetUseDynamicAmbientScale()*/)
|
|
{
|
|
bGetAmbientScalesFromAttachedEntity = true;
|
|
pFxInst->SetAmbientScales(CShaderLib::DivideBy255(pEntity->GetNaturalAmbientScale()), CShaderLib::DivideBy255(pEntity->GetArtificialAmbientScale()));
|
|
pFxInst->SetAmbientScalesSetup(true);
|
|
}
|
|
interiorLocation = pEntity->GetInteriorLocation();
|
|
pFxInst->SetInInterior(interiorLocation.IsValid());
|
|
pFxInst->SetInteriorLocation(interiorLocation.GetAsUint32());
|
|
bGetInteriorFromAttachedEntity = true;
|
|
}
|
|
|
|
//Proceed if its not a short lived effect or if its the first update or if it has moved
|
|
if(bFirstTimeUpdate || bHasPtfxMoved || !bShortLiveEffect)
|
|
{
|
|
if(!(bGetInteriorFromAttachedEntity || pFxInst->GetIsInteriorLocationCached()) )
|
|
{
|
|
interiorLocation = CVfxHelper::GetCamInteriorLocation();
|
|
pFxInst->SetInInterior(interiorLocation.IsValid());
|
|
pFxInst->SetInteriorLocation(interiorLocation.GetAsUint32());
|
|
}
|
|
//Set ambient scales if not set already by the attached effect
|
|
if(!bGetAmbientScalesFromAttachedEntity)
|
|
{
|
|
Vec2V vAmbientScale = g_timeCycle.CalcAmbientScale(vPos, interiorLocation);
|
|
pFxInst->SetAmbientScales(vAmbientScale.GetXf(), vAmbientScale.GetYf());
|
|
pFxInst->SetAmbientScalesSetup(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPtFxCallbacks::RemoveFxInst(ptxEffectInst* pFxInst)
|
|
{
|
|
|
|
//Clearing these custom flags to avoid asserts
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_REFRACT_RULE);
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_WATER_ABOVE_RULE);
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_WATER_BELOW_RULE);
|
|
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
// can this be moved to CPtFxSortedEntity?
|
|
if (pFxInst->GetFlag(PTFX_RESERVED_SORTED))
|
|
{
|
|
// finish the effect - it's still reserved so won't deactivate until a subsequent update loop
|
|
pFxInst->Finish(false);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
void CPtFxCallbacks::ShutdownFxInst(ptxEffectInst* pFxInst)
|
|
{
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
if (pFxInst->GetFlag(PTFX_RESERVED_SORTED))
|
|
{
|
|
g_ptFxManager.GetSortedInterface().Remove(pFxInst);
|
|
}
|
|
#endif
|
|
//Clearing these custom flags to avoid asserts
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_REFRACT_RULE);
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_WATER_ABOVE_RULE);
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_WATER_BELOW_RULE);
|
|
}
|
|
|
|
void CPtFxCallbacks::RemoveScriptResource(int ptfxScriptId)
|
|
{
|
|
// check if the script effect exists - it may have been removed at the start of this chain of events
|
|
if (ptfxScript::DoesExist(ptfxScriptId))
|
|
{
|
|
ptfxScript::SetRemoveWhenDestroyed(ptfxScriptId, false);
|
|
CTheScripts::GetScriptHandlerMgr().RemoveScriptResource(CGameScriptResource::SCRIPT_RESOURCE_PTFX, ptfxScriptId);
|
|
}
|
|
}
|
|
|
|
Vec3V_Out CPtFxCallbacks::GetGameCamPos()
|
|
{
|
|
return CVfxHelper::GetGameCamPos();
|
|
}
|
|
|
|
float CPtFxCallbacks::GetGameCamWaterZ()
|
|
{
|
|
return CVfxHelper::GetGameCamWaterZ();
|
|
}
|
|
|
|
float CPtFxCallbacks::GetWaterZ(Vec3V_In vPos, ptxEffectInst* pEffectInst)
|
|
{
|
|
fwEntity* pEntity = ptfxAttach::GetAttachEntity(pEffectInst);
|
|
float waterZ;
|
|
CVfxHelper::GetWaterZ(vPos, waterZ, pEntity);
|
|
return waterZ;
|
|
}
|
|
|
|
void CPtFxCallbacks::GetRiverVel(Vec3V_In vPos, Vec3V_InOut vVel)
|
|
{
|
|
// get the river flow at this position
|
|
Vector2 riverFlow(0.0f, 0.0f);
|
|
River::GetRiverFlowFromPosition(vPos, riverFlow);
|
|
vVel = Vec3V(riverFlow.x, riverFlow.y, 0.0f);
|
|
}
|
|
|
|
float CPtFxCallbacks::GetGravitationalAcceleration()
|
|
{
|
|
return CPhysics::GetGravitationalAcceleration();
|
|
}
|
|
|
|
float CPtFxCallbacks::GetAirResistance()
|
|
{
|
|
return g_vfxSettings.GetPtFxAirResistance();
|
|
}
|
|
|
|
void CPtFxCallbacks::AddLight(ptfxLightInfo& lightInfo)
|
|
{
|
|
g_ptFxManager.StoreLight(lightInfo);
|
|
}
|
|
|
|
void CPtFxCallbacks::AddFire(ptfxFireInfo& fireInfo)
|
|
{
|
|
g_ptFxManager.StoreFire(fireInfo);
|
|
}
|
|
|
|
void CPtFxCallbacks::AddDecal(ptfxDecalInfo& decalInfo)
|
|
{
|
|
g_ptFxManager.StoreDecal(decalInfo);
|
|
}
|
|
|
|
void CPtFxCallbacks::AddDecalPool(ptfxDecalPoolInfo& decalPoolInfo)
|
|
{
|
|
g_ptFxManager.StoreDecalPool(decalPoolInfo);
|
|
}
|
|
|
|
void CPtFxCallbacks::AddDecalTrail(ptfxDecalTrailInfo& decalTrailInfo)
|
|
{
|
|
g_ptFxManager.StoreDecalTrail(decalTrailInfo);
|
|
}
|
|
|
|
void CPtFxCallbacks::AddLiquid(ptfxLiquidInfo& liquidInfo)
|
|
{
|
|
g_ptFxManager.StoreLiquid(liquidInfo);
|
|
}
|
|
|
|
void CPtFxCallbacks::AddFogVolume(ptfxFogVolumeInfo& fogVolumeInfo)
|
|
{
|
|
g_ptFxManager.StoreFogVolume(fogVolumeInfo);
|
|
}
|
|
|
|
// render behaviors
|
|
void CPtFxCallbacks::DrawFogVolume(ptfxFogVolumeInfo& fogVolumeInfo)
|
|
{
|
|
g_ptFxManager.DrawFogVolume(fogVolumeInfo);
|
|
}
|
|
|
|
bool CPtFxCallbacks::CanGetTriggeredInst()
|
|
{
|
|
#if GTA_REPLAY
|
|
if (CReplayMgr::IsEditModeActive() && !CReplayMgr::IsPreCachingScene())
|
|
{
|
|
if (CReplayMgr::GetCurrentPlayBackState().IsSet(REPLAY_STATE_PAUSE) ||
|
|
CReplayMgr::GetCurrentPlayBackState().IsSet(REPLAY_DIRECTION_BACK))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif // GTA_REPLAY
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CPtFxCallbacks::CanGetRegisteredInst(bool bypassReplayPauseCheck)
|
|
{
|
|
#if GTA_REPLAY
|
|
if (CReplayMgr::IsEditModeActive() && !CReplayMgr::IsPreCachingScene())
|
|
{
|
|
if ((CReplayMgr::GetCurrentPlayBackState().IsSet(REPLAY_STATE_PAUSE) && !bypassReplayPauseCheck) ||
|
|
CReplayMgr::GetCurrentPlayBackState().IsSet(REPLAY_DIRECTION_BACK))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif // GTA_REPLAY
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CLASS CPtFxShaderVarUtils
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const char* CPtFxShaderVarUtils::sm_progVarNameTable[PTXPROGVAR_GAME_COUNT] =
|
|
{
|
|
PTXSTR_GAME_PROGVAR_POST_PROCESS_FLIPDEPTH_NEARPLANEFADE_PARAM,
|
|
PTXSTR_GAME_PROGVAR_WATER_FOG_PARAM,
|
|
PTXSTR_GAME_PROGVAR_WATER_FOG_COLOR_BUFFER,
|
|
PTXSTR_GAME_PROGVAR_SHADEDMAP,
|
|
PTXSTR_GAME_PROGVAR_SHADEDPARAMS,
|
|
PTXSTR_GAME_PROGVAR_SHADEDPARAMS2,
|
|
PTXSTR_GAME_PROGVAR_FOG_RAY_MAP,
|
|
PTXSTR_GAME_PROGVAR_ACTIVE_SHADOW_CASCADES,
|
|
PTXSTR_GAME_PROGVAR_NUM_ACTIVE_SHADOW_CASCADES,
|
|
};
|
|
|
|
const char* CPtFxShaderVarUtils::GetProgrammedVarName(ptxShaderProgVarType type)
|
|
{
|
|
ptxAssertf((type>=PTXPROGVAR_GAME_FIRST && type<PTXPROGVAR_COUNT), "programmed shader var out of range");
|
|
return sm_progVarNameTable[type - PTXPROGVAR_GAME_FIRST];
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CLASS CPtfxShaderCallbacks
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
void CPtfxShaderCallbacks::InitProgVars(ptxShaderTemplate* pPtxShaderTemplate)
|
|
{
|
|
if(pPtxShaderTemplate == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i=PTXPROGVAR_GAME_FIRST; i<PTXPROGVAR_COUNT; i++)
|
|
{
|
|
ptxShaderProgVarType shaderVarType = static_cast<ptxShaderProgVarType>(i);
|
|
grcEffectVar shaderVarHandle = pPtxShaderTemplate->GetGrmShader()->LookupVar(CPtFxShaderVarUtils::GetProgrammedVarName(shaderVarType), false);
|
|
pPtxShaderTemplate->InitProgVar(shaderVarType, shaderVarHandle);
|
|
}
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SetShaderVariables
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtfxShaderCallbacks::SetShaderVars(ptxShaderTemplate* pPtxShaderTemplate)
|
|
{
|
|
if(pPtxShaderTemplate == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Use underwater post process only when camera is above water and we're rendering underwater ptfx.
|
|
bool bUseUnderwaterPostProcess = g_ptFxManager.IsRenderingIntoWaterRefraction() && !Water::IsCameraUnderwater() BANK_ONLY(&& !g_ptFxManager.GetGameDebugInterface().GetDisableWaterRefractionPostProcess());
|
|
|
|
//use water refraction gamma correction when rendering into the refraction target.
|
|
bool bRefractionGammaCorrection = g_ptFxManager.IsRenderingIntoWaterRefraction() BANK_ONLY(&& !g_ptFxManager.GetGameDebugInterface().GetDisableWaterRefractionPostProcess());
|
|
//Let's flip the depth lookup when rendering particles in Mirror reflections renderphase
|
|
const bool bFlipDepth = (g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION);
|
|
const bool bApplyNearPlaneFade = (g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION);
|
|
const Vec4V postProcess_FlipdDepth_NearPlaneFadeParams = Vec4V(
|
|
bUseUnderwaterPostProcess?1.0f:0.0f,
|
|
bRefractionGammaCorrection?1.0f:0.0f,
|
|
bFlipDepth?1.0f:0.0f,
|
|
bApplyNearPlaneFade?1.0f:0.0f);
|
|
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_POST_PROCESS_FLIPDEPTH_NEARPLANEFADE_PARAM, postProcess_FlipdDepth_NearPlaneFadeParams);
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_WATER_FOG_PARAM, CVfxHelper::GetWaterFogParams());
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_WATER_FOG_COLOR_BUFFER, CVfxHelper::GetWaterFogTexture());
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_FOG_MAP_PARAM, PostFX::FogRayRT);
|
|
#if USE_SHADED_PTFX_MAP
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_SHADEDMAP, g_ptFxManager.GetPtfxShadedMap());
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_SHADEDPARAMS, g_ptFxManager.GetPtfxShadedParams());
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_SHADEDPARAMS2, g_ptFxManager.GetPtfxShadedParams2());
|
|
#endif
|
|
|
|
ptxDrawInterface::ptxMultipleDrawInterface* pMultiDrawInterface = g_ptFxManager.GetMultiDrawInterface();
|
|
Vec4V activeShadowCascades = Vec4V(0.0f,0.0f,0.0f,0.0f);
|
|
float numActiveShadowCascades = 0;
|
|
if(pMultiDrawInterface)
|
|
{
|
|
u32 currentIndex = 0;
|
|
for(u32 i = 0; i < pMultiDrawInterface->NoOfDraws(); i++)
|
|
{
|
|
if( pMultiDrawInterface->GetCulledState(i) == false)
|
|
{
|
|
Assert(currentIndex < 4);
|
|
activeShadowCascades[currentIndex++] = (float)i;
|
|
}
|
|
}
|
|
numActiveShadowCascades = (float)pMultiDrawInterface->GetNumActiveCascades();
|
|
Assert(numActiveShadowCascades == currentIndex);
|
|
}
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_ACTIVE_SHADOW_CASCADES, activeShadowCascades);
|
|
pPtxShaderTemplate->SetShaderVar(PTXPROGVAR_NUM_ACTIVE_SHADOW_CASCADES, Vec4V(numActiveShadowCascades, 1.0f/numActiveShadowCascades, 0.0f, 0.0f));
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CLASS PtFx
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if RMPTFX_DEBUG_PRINT_SHADER_NAMES_AND_PASSES
|
|
|
|
#define STRINGIFY(x) #x
|
|
#define ON 1
|
|
#define OFF 0
|
|
|
|
|
|
// C version of macros in ptfx_sprite,fx.
|
|
#define DEF_Technique(diffuseMode, bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract) \
|
|
{ "ptfx_sprite", STRINGIFY(diffuseMode), { bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract }, 0 },
|
|
|
|
#define DEF_TechniqueVariant(diffuseMode, variant, bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract) \
|
|
{ "ptfx_sprite", STRINGIFY(diffuseMode##_##variant), { bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract }, 0 },
|
|
|
|
#define DEF_TechniqueVariant_Tessellated(diffuseMode, variant, bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract) \
|
|
{ "ptfx_sprite", STRINGIFY(diffuseMode##_##variant), { bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract }, 0 },
|
|
|
|
#define DEF_TechniqueVariantVS(diffuseMode, variant, vs_variant, bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract) \
|
|
{ "ptfx_sprite", STRINGIFY(diffuseMode##_##variant), { bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract }, 0 },
|
|
|
|
#define DEF_TechniqueVariantVSPS(diffuseMode, variant, vs_variant, ps_variant, bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract) \
|
|
{ "ptfx_sprite", STRINGIFY(diffuseMode##_##variant), { bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract }, 0 },
|
|
|
|
#define DEF_TechniqueNameVSPS(name, vs, ps, bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract) \
|
|
{ "ptfx_sprite", STRINGIFY(name), { bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasubtract }, 0 },
|
|
|
|
// C version of macros in ptfx_trail.fx
|
|
#define DEF_ptfxTrailTechnique(tech, vs, ps, bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasub) \
|
|
{ "ptfx_trail", STRINGIFY(tech), { bm_default, bm_normal, bm_add, bm_subtract, bm_compalpha, bm_compalphasub }, 0 },
|
|
|
|
ptxShaderTemplate::RMPTFX_DEBUG_PRINT_SHADER_NAMES_AND_PASSES_FILTER g_DebugPrintFilter[] =
|
|
{
|
|
// sprite
|
|
#include "shader_source/VFX/ptfx_sprite.fxh"
|
|
// trail
|
|
#include "shader_source/VFX/ptfx_trail.fxh"
|
|
{ (char *)NULL, (char *)NULL, 0, 0 },
|
|
};
|
|
|
|
#endif //RMPTFX_DEBUG_PRINT_SHADER_NAMES_AND_PASSES
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Init
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::Init(unsigned initMode)
|
|
{
|
|
if(initMode == INIT_CORE)
|
|
{
|
|
// depth/stencil blocks
|
|
grcDepthStencilStateDesc depthStencilDesc;
|
|
grcDepthStencilStateDesc mirrorReflection_simple_DepthStencilDesc;
|
|
depthStencilDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
|
|
|
|
depthStencilDesc.StencilEnable = true;
|
|
depthStencilDesc.StencilReadMask = DEFERRED_MATERIAL_INTERIOR_VEH;
|
|
depthStencilDesc.StencilWriteMask = 0xf0;
|
|
depthStencilDesc.BackFace.StencilFunc = grcRSV::CMP_NOTEQUAL;
|
|
depthStencilDesc.BackFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
|
|
depthStencilDesc.BackFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
|
|
depthStencilDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_KEEP;
|
|
depthStencilDesc.FrontFace.StencilFunc = grcRSV::CMP_NOTEQUAL;
|
|
depthStencilDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
|
|
depthStencilDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
|
|
depthStencilDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_KEEP;
|
|
|
|
//not applying fixup for mirror reflection and simple render phase
|
|
//Once the fixup is applied to those render phase we need to apply the fixup here also
|
|
mirrorReflection_simple_DepthStencilDesc.DepthFunc = grcRSV::CMP_LESSEQUAL;
|
|
mirrorReflection_simple_DepthStencilDesc.StencilEnable = false;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Stencil On
|
|
// write off, test off
|
|
depthStencilDesc.DepthWriteMask = 0;
|
|
depthStencilDesc.DepthEnable = 0;
|
|
m_VehicleInteriorAlphaDepthStencilStates[0][0] = grcStateBlock::CreateDepthStencilState(depthStencilDesc);
|
|
|
|
// write off, test on
|
|
depthStencilDesc.DepthWriteMask = 0;
|
|
depthStencilDesc.DepthEnable = 1;
|
|
m_VehicleInteriorAlphaDepthStencilStates[0][1] = grcStateBlock::CreateDepthStencilState(depthStencilDesc);
|
|
|
|
// write on, test off
|
|
depthStencilDesc.DepthWriteMask = 1;
|
|
depthStencilDesc.DepthEnable = 0;
|
|
m_VehicleInteriorAlphaDepthStencilStates[1][0] = grcStateBlock::CreateDepthStencilState(depthStencilDesc);
|
|
|
|
// write on, test on
|
|
depthStencilDesc.DepthWriteMask = 1;
|
|
depthStencilDesc.DepthEnable = 1;
|
|
m_VehicleInteriorAlphaDepthStencilStates[1][1] = grcStateBlock::CreateDepthStencilState(depthStencilDesc);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Mirror depth-stencil
|
|
// Stencil Off
|
|
// write off, test off
|
|
mirrorReflection_simple_DepthStencilDesc.DepthWriteMask = 0;
|
|
mirrorReflection_simple_DepthStencilDesc.DepthEnable = 0;
|
|
m_MirrorReflection_Simple_DepthStencilState[0][0] = grcStateBlock::CreateDepthStencilState(mirrorReflection_simple_DepthStencilDesc);
|
|
|
|
// write off, test on
|
|
mirrorReflection_simple_DepthStencilDesc.DepthWriteMask = 0;
|
|
mirrorReflection_simple_DepthStencilDesc.DepthEnable = 1;
|
|
m_MirrorReflection_Simple_DepthStencilState[0][1] = grcStateBlock::CreateDepthStencilState(mirrorReflection_simple_DepthStencilDesc);
|
|
|
|
// write on, test off
|
|
mirrorReflection_simple_DepthStencilDesc.DepthWriteMask = 1;
|
|
mirrorReflection_simple_DepthStencilDesc.DepthEnable = 0;
|
|
m_MirrorReflection_Simple_DepthStencilState[1][0] = grcStateBlock::CreateDepthStencilState(mirrorReflection_simple_DepthStencilDesc);
|
|
|
|
// write on, test on
|
|
mirrorReflection_simple_DepthStencilDesc.DepthWriteMask = 1;
|
|
mirrorReflection_simple_DepthStencilDesc.DepthEnable = 1;
|
|
m_MirrorReflection_Simple_DepthStencilState[1][1] = grcStateBlock::CreateDepthStencilState(mirrorReflection_simple_DepthStencilDesc);
|
|
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS || PTFX_APPLY_DOF_TO_PARTICLES
|
|
grcBlendStateDesc blendStateDesc;
|
|
|
|
blendStateDesc.BlendRTDesc[0].BlendEnable = 1;
|
|
blendStateDesc.BlendRTDesc[0].SrcBlendAlpha = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].DestBlendAlpha = grcRSV::BLEND_INVSRCALPHA;
|
|
blendStateDesc.BlendRTDesc[0].BlendOpAlpha = grcRSV::BLENDOP_ADD;
|
|
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS
|
|
blendStateDesc.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].DestBlend = grcRSV::BLEND_INVSRCALPHA;
|
|
blendStateDesc.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_ADD;
|
|
|
|
//Shadow blend state has the the alpha blend state in both color and alpha
|
|
m_ShadowBlendState = grcStateBlock::CreateBlendState(blendStateDesc);
|
|
#endif
|
|
|
|
#if PTFX_APPLY_DOF_TO_PARTICLES
|
|
if (CPtFxManager::UseParticleDOF())
|
|
{
|
|
blendStateDesc.BlendRTDesc[0].DestBlend = grcRSV::BLEND_INVSRCALPHA;
|
|
blendStateDesc.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_SRCALPHA;
|
|
blendStateDesc.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_ADD;
|
|
|
|
PostFX::SetupBlendForDOF(blendStateDesc, false);
|
|
|
|
m_ptfxDofBlendNormal = grcStateBlock::CreateBlendState(blendStateDesc);
|
|
|
|
blendStateDesc.BlendRTDesc[0].DestBlend = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_ADD;
|
|
|
|
m_ptfxDofBlendAdd = grcStateBlock::CreateBlendState(blendStateDesc);
|
|
|
|
blendStateDesc.BlendRTDesc[0].DestBlend = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_SUBTRACT;
|
|
|
|
m_ptfxDofBlendSubtract = grcStateBlock::CreateBlendState(blendStateDesc);
|
|
|
|
blendStateDesc.BlendRTDesc[0].DestBlend = grcRSV::BLEND_INVSRCALPHA;
|
|
blendStateDesc.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_ADD;
|
|
|
|
m_ptfxDofBlendCompositeAlpha = grcStateBlock::CreateBlendState(blendStateDesc);
|
|
|
|
|
|
blendStateDesc.BlendRTDesc[0].DestBlend = grcRSV::BLEND_INVSRCALPHA;
|
|
blendStateDesc.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_ONE;
|
|
blendStateDesc.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_REVSUBTRACT;
|
|
|
|
m_ptfxDofBlendCompositeAlphaSubtract = grcStateBlock::CreateBlendState(blendStateDesc);
|
|
}
|
|
#endif // PTFX_APPLY_DOF_TO_PARTICLES
|
|
#endif // RMPTFX_USE_PARTICLE_SHADOWS || PTFX_APPLY_DOF_TO_PARTICLES
|
|
|
|
ptfxManager::Init(PTFX_MAX_PARTICLES, PTFX_MAX_INSTS, PTFX_MAX_TRAILS, PTFX_MAX_REGISTERED, PTFX_MAX_SCRIPTED, PTFX_MAX_HISTORIES, "common:/data/effects/ptxclipregions.dat");
|
|
|
|
InitRMPTFX();
|
|
InitShaderOverrides();
|
|
InitThread();
|
|
|
|
// init the sub systems
|
|
#if __BANK
|
|
m_gameDebug.Init();
|
|
#endif
|
|
|
|
g_pPtfxCallbacks = &g_ptFxCallbacks;
|
|
m_ptfxRenderFlags[0] = m_ptfxRenderFlags[1] = 0;
|
|
m_ptfxCurrRenderFlags = 0;
|
|
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
m_ptFxSorted.Init();
|
|
#endif
|
|
m_ptFxCollision.Init();
|
|
|
|
// init script data
|
|
m_forceVehicleInteriorFlag = false;
|
|
m_forceVehicleInteriorFlagScriptThreadId = THREAD_INVALID;
|
|
|
|
// init buffered data
|
|
m_numLightInfos = 0;
|
|
m_numFireInfos = 0;
|
|
m_numDecalInfos = 0;
|
|
m_numDecalPoolInfos = 0;
|
|
m_numDecalTrailInfos = 0;
|
|
m_numLiquidInfos = 0;
|
|
|
|
m_numGlassImpactInfos = 0;
|
|
m_numGlassSmashInfos = 0;
|
|
m_numGlassShatterInfos = 0;
|
|
|
|
m_blockFxListRemoval = false;
|
|
m_currShadowViewports.Reserve(CASCADE_SHADOWS_COUNT);
|
|
|
|
sysMemSet(m_isRenderingRefractionPtfxList, 0, sizeof(m_isRenderingRefractionPtfxList));
|
|
sysMemSet(m_isRenderingIntoWaterRefraction, 0, sizeof(m_isRenderingIntoWaterRefraction));
|
|
|
|
m_IsRefractPtfxPresent[0] = false;
|
|
m_IsRefractPtfxPresent[1] = false;
|
|
#if FPS_MODE_SUPPORTED
|
|
m_isFirstPersonCamActive[0] = false;
|
|
m_isFirstPersonCamActive[1] = false;
|
|
#endif
|
|
#if __XENON
|
|
m_useDownrezzedPtfx = false;
|
|
#endif
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
m_disableDownsamplingThisFrame = false;
|
|
#endif
|
|
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS
|
|
//m_GlobalShadowIntensityValue is already set from visual settings when we come here
|
|
RMPTFXMGR.SetGlobalShadowIntensity(m_GlobalShadowIntensityValue);
|
|
//Change m_globalParticleShadowBias and m_globalParticleShadowSlopeBias in ptfxDebug.cpp too.
|
|
m_GlobalShadowSlopeBias = CSM_PARTICLE_SHADOWS_DEPTH_SLOPE_BIAS;
|
|
m_GlobalShadowBiasRange = CSM_PARTICLE_SHADOWS_DEPTH_BIAS_RANGE;
|
|
m_GlobalshadowBiasRangeFalloff = CSM_PARTICLE_SHADOWS_DEPTH_BIAS_RANGE_FALLOFF;
|
|
m_bAnyParticleShadowsRendered = false;
|
|
#endif
|
|
#if PTFX_APPLY_DOF_TO_PARTICLES
|
|
if (CPtFxManager::UseParticleDOF())
|
|
m_globalParticleDofAlphaScale = PTFX_PARTICLE_DOF_ALPHA_SCALE;
|
|
#endif //PTFX_APPLY_DOF_TO_PARTICLES
|
|
|
|
sysMemSet(m_pMulitDrawInterface, 0, sizeof(m_pMulitDrawInterface));
|
|
|
|
#if GTA_REPLAY
|
|
m_ReplayScriptPtfxAssetID = -1;
|
|
m_RecordReplayScriptRequestAsset = true;
|
|
#endif
|
|
}
|
|
else if(initMode == INIT_SESSION)
|
|
{
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
m_ptFxSorted.InitLevel();
|
|
#endif
|
|
// init the sub systems
|
|
ptfxManager::InitLevel();
|
|
|
|
// load in the level assets
|
|
InitGlobalAssets();
|
|
}
|
|
|
|
#if RMPTFX_DEBUG_PRINT_SHADER_NAMES_AND_PASSES
|
|
ptxShaderTemplate::SetDebugPrintFilter(g_DebugPrintFilter);
|
|
#endif
|
|
|
|
BeginFrame();
|
|
}
|
|
|
|
void CPtFxManager::InitShaderOverrides()
|
|
{
|
|
|
|
|
|
//Initialize the underwater technique overrides
|
|
ptxShaderTemplate* spriteShader = ptxShaderTemplateList::LoadShader("ptfx_sprite");
|
|
m_ptxSpriteWaterRefractionShaderOverride.Init(spriteShader->GetGrmShader());
|
|
m_ptxSpriteWaterRefractionShaderOverride.DisableAllTechniques();
|
|
grcEffectTechnique ptfx_sprite_rgba_lit_soft_underwater_Technique = spriteShader->GetGrmShader()->LookupTechnique("RGBA_lit_soft_waterrefract", true);
|
|
grcEffectTechnique ptfx_sprite_rgba_lit_soft_Technique = spriteShader->GetGrmShader()->LookupTechnique("RGBA_lit_soft", true);
|
|
|
|
grcEffectTechnique ptfx_sprite_rgba_soft_underwater_Technique = spriteShader->GetGrmShader()->LookupTechnique("RGBA_soft_waterrefract", true);
|
|
grcEffectTechnique ptfx_sprite_rgba_soft_Technique = spriteShader->GetGrmShader()->LookupTechnique("RGBA_soft", true);
|
|
|
|
grcEffectTechnique ptfx_sprite_rgba_proj_underwater_Technique = spriteShader->GetGrmShader()->LookupTechnique("RGBA_proj_waterrefract", true);
|
|
grcEffectTechnique ptfx_sprite_rgba_proj_Technique = spriteShader->GetGrmShader()->LookupTechnique("RGBA_proj", true);
|
|
|
|
m_ptxSpriteWaterRefractionShaderOverride.OverrideTechniqueWith(ptfx_sprite_rgba_lit_soft_Technique,ptfx_sprite_rgba_lit_soft_underwater_Technique);
|
|
m_ptxSpriteWaterRefractionShaderOverride.OverrideTechniqueWith(ptfx_sprite_rgba_soft_Technique,ptfx_sprite_rgba_soft_underwater_Technique);
|
|
m_ptxSpriteWaterRefractionShaderOverride.OverrideTechniqueWith(ptfx_sprite_rgba_proj_Technique,ptfx_sprite_rgba_proj_underwater_Technique);
|
|
m_ptxSpriteWaterRefractionShaderOverride.EnableTechnique(ptfx_sprite_rgba_lit_soft_underwater_Technique);
|
|
m_ptxSpriteWaterRefractionShaderOverride.EnableTechnique(ptfx_sprite_rgba_soft_underwater_Technique);
|
|
m_ptxSpriteWaterRefractionShaderOverride.EnableTechnique(ptfx_sprite_rgba_proj_underwater_Technique);
|
|
|
|
|
|
ptxShaderTemplate* trailShader = ptxShaderTemplateList::LoadShader("ptfx_trail");
|
|
m_ptxTrailWaterRefractionShaderOverride.Init(trailShader->GetGrmShader());
|
|
m_ptxTrailWaterRefractionShaderOverride.DisableAllTechniques();
|
|
|
|
grcEffectTechnique ptfx_trail_rgba_lit_soft_underwater_Technique = trailShader->GetGrmShader()->LookupTechnique("RGBA_lit_soft_waterrefract", true);
|
|
grcEffectTechnique ptfx_trail_rgba_lit_soft_Technique = trailShader->GetGrmShader()->LookupTechnique("RGBA_lit_soft", true);
|
|
|
|
grcEffectTechnique ptfx_trail_rgba_soft_underwater_Technique = trailShader->GetGrmShader()->LookupTechnique("RGBA_soft_waterrefract", true);
|
|
grcEffectTechnique ptfx_trail_rgba_soft_Technique = trailShader->GetGrmShader()->LookupTechnique("RGBA_soft", true);
|
|
|
|
m_ptxTrailWaterRefractionShaderOverride.OverrideTechniqueWith(ptfx_trail_rgba_lit_soft_Technique,ptfx_trail_rgba_lit_soft_underwater_Technique);
|
|
m_ptxTrailWaterRefractionShaderOverride.OverrideTechniqueWith(ptfx_trail_rgba_soft_Technique,ptfx_trail_rgba_soft_underwater_Technique);
|
|
m_ptxTrailWaterRefractionShaderOverride.EnableTechnique(ptfx_trail_rgba_lit_soft_underwater_Technique);
|
|
m_ptxTrailWaterRefractionShaderOverride.EnableTechnique(ptfx_trail_rgba_soft_underwater_Technique);
|
|
|
|
#if RMPTFX_BANK
|
|
ptxShaderTemplateOverride::InitDebug();
|
|
#endif
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitSessionSync
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::InitSessionSync()
|
|
{
|
|
ptfxAssertf(m_streamingRequests.HaveAllLoaded(), "not all particle assets have streamed in");
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Shutdown
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::Shutdown(unsigned shutdownMode)
|
|
{
|
|
if(shutdownMode == SHUTDOWN_CORE)
|
|
{
|
|
m_ptFxCollision.Shutdown();
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
m_ptFxSorted.Shutdown();
|
|
#endif
|
|
|
|
g_pPtfxCallbacks = NULL;
|
|
|
|
ShutdownThread();
|
|
ShutdownRMPTFX();
|
|
|
|
ptfxManager::Shutdown();
|
|
}
|
|
else if(shutdownMode == SHUTDOWN_SESSION)
|
|
{
|
|
// unload the level assets
|
|
ShutdownGlobalAssets();
|
|
|
|
ptfxManager::ShutdownLevel();
|
|
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
m_ptFxSorted.ShutdownLevel();
|
|
#endif
|
|
|
|
// reset rmptfx
|
|
RMPTFXMGR.ResetAllImmediately();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RemoveScript
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RemoveScript(scrThreadId scriptThreadId)
|
|
{
|
|
if (scriptThreadId == m_forceVehicleInteriorFlagScriptThreadId)
|
|
{
|
|
m_forceVehicleInteriorFlag = false;
|
|
m_forceVehicleInteriorFlagScriptThreadId = THREAD_INVALID;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if RSG_PC
|
|
void CPtFxManager::DeviceLost ()
|
|
{
|
|
if (g_ptxManager::IsInstantiated())
|
|
{
|
|
RMPTFXMGR.SetDepthBuffer(NULL);
|
|
RMPTFXMGR.SetFrameBuffer(NULL);
|
|
RMPTFXMGR.SetFrameBufferSampler(NULL);
|
|
}
|
|
}
|
|
void CPtFxManager::DeviceReset ()
|
|
{
|
|
if (g_ptxManager::IsInstantiated())
|
|
{
|
|
g_ptFxManager.InitRMPTFXTexture();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitGlobalAssets
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::InitGlobalAssets ()
|
|
{
|
|
ptfxManager::GetAssetStore().PrepForXmlLoading();
|
|
|
|
// load in the global core asset
|
|
strLocalIndex slot = ptfxManager::GetAssetStore().FindSlotFromHashKey(atHashValue("core"));
|
|
if (ptfxVerifyf(slot.IsValid(), "cannot find particle asset with this name in any loaded rpf (core)"))
|
|
{
|
|
m_streamingRequests.PushRequest(slot.Get(), ptfxManager::GetAssetStore().GetStreamingModuleId(), STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE);
|
|
}
|
|
|
|
|
|
// load all other assets in now if required
|
|
#if __DEV
|
|
if (PARAM_ptfxloadall.Get())
|
|
{
|
|
// make sure to finish the load of core before loading everything else, because everything else may share resources with core
|
|
CStreaming::LoadAllRequestedObjects();
|
|
ptfxAssertf(m_streamingRequests.HaveAllLoaded(), "the 'core' particle asset has not streamed in");
|
|
|
|
for (int i=0; i<ptfxManager::GetAssetStore().GetSize(); i++)
|
|
{
|
|
if (ptfxManager::GetAssetStore().IsValidSlot(strLocalIndex(i)))
|
|
{
|
|
m_streamingRequests.PushRequest(i, ptfxManager::GetAssetStore().GetStreamingModuleId(), STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE);
|
|
}
|
|
}
|
|
}
|
|
else if (PARAM_ptfxloadmost.Get())
|
|
{
|
|
// make sure to finish the load of core before loading everything else, because everything else may share resources with core
|
|
CStreaming::LoadAllRequestedObjects();
|
|
ptfxAssertf(m_streamingRequests.HaveAllLoaded(), "the 'core' particle asset has not streamed in");
|
|
|
|
for (int i=0; i<ptfxManager::GetAssetStore().GetSize(); i++)
|
|
{
|
|
if (ptfxManager::GetAssetStore().IsValidSlot(strLocalIndex(i)))
|
|
{
|
|
ptxFxListDef* pFxListDef = ptfxManager::GetAssetStore().GetSlot(strLocalIndex(i));
|
|
const char* pFxListName = pFxListDef->m_name.GetCStr();
|
|
bool isCutsceneFxList = pFxListName[0]=='c' && pFxListName[1]=='u' && pFxListName[2]=='t';
|
|
bool isScriptFxList = pFxListName[0]=='s' && pFxListName[1]=='c' && pFxListName[2]=='r';
|
|
bool isRayfireFxList = pFxListName[0]=='d' && pFxListName[1]=='e' && pFxListName[2]=='s';
|
|
if (isCutsceneFxList==false && isScriptFxList==false && isRayfireFxList==false)
|
|
{
|
|
m_streamingRequests.PushRequest(i, ptfxManager::GetAssetStore().GetStreamingModuleId(), STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// load in the episodic assets
|
|
// const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetFirstFile(CDataFileMgr::RMPTFX_FILE);
|
|
// while (DATAFILEMGR.IsValid(pData))
|
|
// {
|
|
// // load the episode effects
|
|
// slot = ptfxManager::GetAssetStore().FindSlotFromHashKey(hashValue(pData->m_filename));
|
|
// if (ptfxVerifyf(slot.IsValid(), ""))
|
|
// {
|
|
// m_streamingRequests.PushRequest(slot, ptfxManager::GetAssetStore().GetStreamingModuleId());
|
|
// }
|
|
//
|
|
// pData = DATAFILEMGR.GetNextFile(pData);
|
|
// }
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ShutdownGlobalAssets
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ShutdownGlobalAssets ()
|
|
{
|
|
for (int i=0; i<m_streamingRequests.GetRequestCount(); i++)
|
|
{
|
|
strRequest& strReq = m_streamingRequests.GetRequest(i);
|
|
{
|
|
ptfxManager::GetAssetStore().ClearRequiredFlag(strReq.GetRequestId().Get(), STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE);
|
|
}
|
|
}
|
|
|
|
// release the streamed global assets (this should put their ref count to 1)
|
|
m_streamingRequests.ReleaseAll();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitThread
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::InitThread ()
|
|
{
|
|
// init the update thread
|
|
m_startUpdateSema = sysIpcCreateSema(0);
|
|
m_finishUpdateSema = sysIpcCreateSema(0);
|
|
m_nextUpdateDelta = 0.0f;
|
|
|
|
m_gpuTimerSumMedRes = 0.0f;
|
|
m_gpuTimerSumLowRes = 0.0f;
|
|
m_updateDeltaSum = 0.0f;
|
|
m_gpuTimeMedRes[0] = m_gpuTimeMedRes[1] = 0.0f;
|
|
m_gpuTimeLowRes[0] = m_gpuTimeLowRes[1] = 0.0f;
|
|
m_numGPUFrames = 0;
|
|
m_ptfxRenderFlags[0] = m_ptfxRenderFlags[1] = 0;
|
|
m_ptfxCurrRenderFlags = 0;
|
|
|
|
m_startedUpdate = false;
|
|
m_updateThreadId = sysIpcCreateThread(UpdateThread, this, ((RSG_ORBIS ? 128 : 56) * 1024), PRIO_PTFX_THREAD, "RMPTFX Update", 1, "RmptfxUpdateThread");
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ShutdownThread
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ShutdownThread ()
|
|
{
|
|
// shutdown the update thread
|
|
if (m_startedUpdate)
|
|
{
|
|
sysIpcWaitSema(m_finishUpdateSema);
|
|
}
|
|
|
|
m_nextUpdateDelta = -999.0f;
|
|
|
|
sysIpcSignalSema(m_startUpdateSema);
|
|
sysIpcWaitSema(m_finishUpdateSema);
|
|
|
|
// clean up handles
|
|
sysIpcWaitThreadExit(m_updateThreadId);
|
|
|
|
sysIpcDeleteSema(m_finishUpdateSema);
|
|
sysIpcDeleteSema(m_startUpdateSema);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitRMPTFX
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::InitRMPTFX ()
|
|
{
|
|
RMPTFXMGR.SetShaderInitProgVarsFunctor(MakeFunctor(CPtfxShaderCallbacks::InitProgVars));
|
|
RMPTFXMGR.SetShaderSetProgVarsFunctor(MakeFunctor(CPtfxShaderCallbacks::SetShaderVars));
|
|
|
|
// add game specific text to the interface
|
|
#if RMPTFX_EDITOR
|
|
RMPTFXMGR.AddEffectRuleGameFlagName("Sort Within GTA Scene");
|
|
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Fire (Contained)");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Fire");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Water");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("<NOTUSED> Sand");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("<NOTUSED> Mud");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("<NOTUSED> Blood");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("<NOTUSED> Dust");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("<NOTUSED> Smoke");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Blaze");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Soak - Slow");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Soak - Medium");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Soak - Fast");
|
|
RMPTFXMGR.AddEffectRuleDataVolumeTypeName("Fire (Flame Thrower)");
|
|
#endif
|
|
|
|
// create the draw lists
|
|
// for (s32 i=0; i<NUM_PTFX_DRAWLISTS; i++)
|
|
// {
|
|
// char drawListName[32];
|
|
// sprintf(drawListName, "drawList%d", i);
|
|
// m_pDrawList[i] = RMPTFXMGR.CreateDrawList(drawListName);
|
|
// }
|
|
m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW] = RMPTFXMGR.CreateDrawList("Low Quality");
|
|
m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM] = RMPTFXMGR.CreateDrawList("Medium Quality");
|
|
m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH] = RMPTFXMGR.CreateDrawList("High Quality");
|
|
|
|
// init the game specific behaviours
|
|
RMPTFX_REGISTER_BEHAVIOUR(ptxu_Decal);
|
|
RMPTFX_REGISTER_BEHAVIOUR(ptxu_DecalPool);
|
|
//RMPTFX_REGISTER_BEHAVIOUR(ptxu_DecalTrail);
|
|
//RMPTFX_REGISTER_BEHAVIOUR(ptxu_Fire);
|
|
RMPTFX_REGISTER_BEHAVIOUR(ptxu_FogVolume);
|
|
RMPTFX_REGISTER_BEHAVIOUR(ptxu_Light);
|
|
RMPTFX_REGISTER_BEHAVIOUR(ptxu_Liquid);
|
|
RMPTFX_REGISTER_BEHAVIOUR(ptxu_River);
|
|
RMPTFX_REGISTER_BEHAVIOUR(ptxu_ZCull);
|
|
|
|
// init the callbacks
|
|
RMPTFXMGR.SetEffectInstStartFunctor(MakeFunctor(EffectInstStartFunctor));
|
|
RMPTFXMGR.SetEffectInstStopFunctor(MakeFunctor(EffectInstStopFunctor));
|
|
RMPTFXMGR.SetEffectInstDeactivateFunctor(MakeFunctor(EffectInstDeactivateFunctor));
|
|
//RMPTFXMGR.SetEffectInstSpawnFunctor(MakeFunctorRet(EffectInstSpawnFunctor));
|
|
RMPTFXMGR.SetEffectInstDistToCamFunctor(MakeFunctorRet(EffectInstDistToCamFunctor));
|
|
RMPTFXMGR.SetEffectInstOcclusionFunctor(MakeFunctorRet(EffectInstOcclusionFunctor));
|
|
RMPTFXMGR.SetEffectInstPostUpdateFunctor(MakeFunctor(EffectInstPostUpdateFunctor));
|
|
RMPTFXMGR.SetEffectInstUpdateCullingFunctor(MakeFunctor(EffectInstUpdateCullingFunctor));
|
|
RMPTFXMGR.SetEffectInstUpdateFinalizeFunctor(MakeFunctor(EffectInstUpdateFinalizeFunctor));
|
|
RMPTFXMGR.SetEffectInstShouldBeRenderedFunctor(MakeFunctorRet(EffectInstShouldBeRenderedFunctor));
|
|
RMPTFXMGR.SetEffectInstInitFunctor(MakeFunctor(EffectInstInitFunctor));
|
|
RMPTFXMGR.SetEffectInstUpdateSetupFunctor(MakeFunctor(EffectInstUpdateSetupFunctor));
|
|
RMPTFXMGR.SetParticleRuleInitFunctor(MakeFunctor(ParticleRuleInitFunctor));
|
|
RMPTFXMGR.SetParticleRuleShouldBeRenderedFunctor(MakeFunctorRet(ParticleRuleShouldBeRenderedFunctor));
|
|
RMPTFXMGR.SetOverrideDepthStencilStateFunctor(MakeFunctorRet(OverrideDepthStencilStateFunctor));
|
|
RMPTFXMGR.SetOverrideBlendStateFunctor(MakeFunctorRet(OverrideBlendStateFunctor));
|
|
|
|
// set up the name of the shader var that controls model colour
|
|
RMPTFXMGR.SetForcedColourShaderVarName("Colorize");
|
|
|
|
// read and write access to the depth buffers for the soft particles
|
|
#if __PPU
|
|
RMPTFXMGR.SetPatchedDepthBuffer(CRenderTargets::GetDepthBufferAsColor());
|
|
#endif // __PPU
|
|
|
|
#if DEVICE_MSAA
|
|
RMPTFXMGR.SetDepthBuffer(CRenderTargets::GetDepthResolved());
|
|
#else
|
|
RMPTFXMGR.SetDepthBuffer(CRenderTargets::GetDepthBuffer());
|
|
#endif
|
|
|
|
grcRenderTarget *const backBuffer = CRenderTargets::GetBackBuffer(false);
|
|
grcRenderTarget *const backSampler =
|
|
#if __D3D11 || DEVICE_MSAA
|
|
__D3D11 || GRCDEVICE.GetMSAA()? CRenderTargets::GetBackBufferCopy(false) :
|
|
#endif
|
|
backBuffer;
|
|
|
|
RMPTFXMGR.SetFrameBuffer(backBuffer );
|
|
RMPTFXMGR.SetFrameBufferSampler(backSampler );
|
|
|
|
// Set the hook for the lighting constants
|
|
// NOTE: let RMPTFX manager the memory
|
|
RMPTFXMGR.SetRenderSetup( rage_new CSetLightingConstants("ptfx_model") );
|
|
|
|
// init the downsample buffers
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
InitDownsampling();
|
|
#endif
|
|
|
|
#if USE_SHADED_PTFX_MAP
|
|
InitShadowedPtFxBuffer();
|
|
|
|
m_CloudTexture = CShaderLib::LookupTexture("cloudnoise");
|
|
Assert(m_CloudTexture);
|
|
if(m_CloudTexture)
|
|
m_CloudTexture->AddRef();
|
|
|
|
// grab some shaders and shader variables we'll need to properly render our shaded particle buffer
|
|
m_shadedParticleBufferShader = grmShaderFactory::GetInstance().Create();
|
|
m_shadedParticleBufferShader->Load("ptfx_sprite");
|
|
m_shadedParticleBufferShaderTechnique = m_shadedParticleBufferShader->LookupTechnique("shadedparticles_buffergen");
|
|
m_shadedParticleBufferShaderShadowMapId = m_shadedParticleBufferShader->LookupVar("gCSMShadowTextureSamp");
|
|
m_shadedParticleBufferShaderParamsId = m_shadedParticleBufferShader->LookupVar("gShadedPtFxParams");
|
|
m_shadedParticleBufferShaderParams2Id = m_shadedParticleBufferShader->LookupVar("gShadedPtFxParams2");
|
|
|
|
m_shadedParticleBufferShaderCloudSampler = m_shadedParticleBufferShader->LookupVar("CloudShadowMap");
|
|
m_shadedParticleBufferShaderSmoothStepSampler = m_shadedParticleBufferShader->LookupVar("SmoothStepMap");
|
|
#endif //USE_SHADED_PTFX_MAP
|
|
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS
|
|
m_GlobalShadowBiasVar = grcEffect::LookupGlobalVar("gGlobalParticleShadowBias");
|
|
#endif
|
|
|
|
// settings
|
|
RMPTFXMGR.SetHighQualityEvo(0.0f);
|
|
|
|
#if PTFX_APPLY_DOF_TO_PARTICLES
|
|
if (CPtFxManager::UseParticleDOF())
|
|
{
|
|
m_GlobalParticleDofAlphaScaleVar = grcEffect::LookupGlobalVar("gGlobalParticleDofAlphaScale");
|
|
|
|
m_particleDepthMapResolved = NULL;
|
|
m_particleDepthMap = NULL;
|
|
m_particleAlphaMapResolved = NULL;
|
|
m_particleAlphaMap = NULL;
|
|
|
|
InitRMPTFXTexture();
|
|
}
|
|
#endif //PTFX_APPLY_DOF_TO_PARTICLES
|
|
|
|
#if PTFX_SHADOWS_INSTANCING_GEOMSHADER
|
|
RMPTFXMGR.SetUsingGeometryShader(true);
|
|
#endif
|
|
|
|
// setup the keyframe prop thread ID for the main thread
|
|
RMPTFXMGR.SetupPtxKeyFramePropThreadId();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ShutdownRMPTFX
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ShutdownRMPTFX ()
|
|
{
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
ShutdownDownsampling();
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitRMPTFXTexture
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::InitRMPTFXTexture ()
|
|
{
|
|
#if PTFX_APPLY_DOF_TO_PARTICLES
|
|
if (CPtFxManager::UseParticleDOF())
|
|
{
|
|
grcTextureFactory::CreateParams params;
|
|
const grcRenderTargetType type = grcrtPermanent;
|
|
const u8 heap = 0;
|
|
|
|
params.Multisample = GRCDEVICE.GetMSAA();
|
|
# if DEVICE_EQAA
|
|
params.Multisample.DisableCoverage();
|
|
params.ForceFragmentMask = false;
|
|
params.SupersampleFrequency = params.Multisample.m_uFragments;
|
|
# else // DEVICE_EQAA
|
|
u32 multisampleQuality = GRCDEVICE.GetMSAAQuality();
|
|
params.MultisampleQuality = (u8)multisampleQuality;
|
|
# endif // DEVICE_EQAA
|
|
# if RSG_ORBIS
|
|
// these have to be together on PS4
|
|
params.ForceFragmentMask = params.EnableFastClear = true;
|
|
# endif // RSG_ORBIS
|
|
|
|
const unsigned width = VideoResManager::GetSceneWidth();
|
|
const unsigned height = VideoResManager::GetSceneHeight();
|
|
const int ptfxDepthbpp = 16;
|
|
const grcTextureFormat ptfxDepthFormat = grctfR16F;
|
|
|
|
const int ptfxAlphabpp = 8;
|
|
const grcTextureFormat ptfxAlphaFormat = grctfL8;
|
|
|
|
//Create as an MSAA render target as its bound as a secondary target alongside the MSAA backbuffer. May be able to get around this on PS4 and Xbox?
|
|
//Particle Depth map is a 16bit buffer thats placed in ESRAM. It's 16bit because we need precision for applying Depth of field. We will be storing linear depth in
|
|
//this buffer
|
|
params.Format = ptfxDepthFormat;
|
|
#if RSG_DURANGO
|
|
params.ESRAMPhase = grcTextureFactory::TextureCreateParams::ESRPHASE_DRAWSCENE_2
|
|
| grcTextureFactory::TextureCreateParams::ESRPHASE_DRAWSCENE_3
|
|
| grcTextureFactory::TextureCreateParams::ESRPHASE_DRAWSCENE_4
|
|
| grcTextureFactory::TextureCreateParams::ESRPHASE_DRAWSCENE_5;
|
|
#endif
|
|
m_particleDepthMap = CRenderTargets::CreateRenderTarget(RTMEMPOOL_NONE, "PARTICLE_DEPTH_MAP", type, width, height, ptfxDepthbpp, ¶ms, heap WIN32PC_ONLY(, m_particleDepthMap));
|
|
#if RSG_DURANGO
|
|
params.ESRAMPhase = 0;
|
|
#endif
|
|
|
|
//This is an 8 bit alpha target used for DOF thats not part of ESRAM
|
|
params.Format = ptfxAlphaFormat;
|
|
m_particleAlphaMap = CRenderTargets::CreateRenderTarget(RTMEMPOOL_NONE, "PARTICLE_ALPHA_MAP", type, width, height, ptfxAlphabpp, ¶ms, heap WIN32PC_ONLY(, m_particleAlphaMap));
|
|
|
|
# if DEVICE_MSAA
|
|
if ((params.Multisample && RSG_PC) || params.Multisample > 1)
|
|
{
|
|
params.Multisample = grcDevice::MSAA_None;
|
|
ORBIS_ONLY(params.EnableFastClear = false;)
|
|
#if RSG_PC
|
|
if (m_particleDepthMapResolved == m_particleDepthMap) {m_particleDepthMapResolved = NULL;}
|
|
if (m_particleAlphaMapResolved == m_particleAlphaMap) {m_particleAlphaMapResolved = NULL;}
|
|
#endif
|
|
//Making sure the resolved targets are same as the MSAA targets
|
|
params.Format = ptfxDepthFormat;
|
|
m_particleDepthMapResolved = CRenderTargets::CreateRenderTarget(RTMEMPOOL_NONE, "PARTICLE_DEPTH_MAP_RESOLVED", type, width, height, ptfxDepthbpp, ¶ms, heap WIN32PC_ONLY(, m_particleDepthMapResolved));
|
|
|
|
params.Format = ptfxAlphaFormat;
|
|
m_particleAlphaMapResolved = CRenderTargets::CreateRenderTarget(RTMEMPOOL_NONE, "PARTICLE_ALPHA_MAP_RESOLVED", type, width, height, ptfxAlphabpp, ¶ms, heap WIN32PC_ONLY(, m_particleAlphaMapResolved));
|
|
}else
|
|
{
|
|
#if RSG_PC
|
|
if (m_particleDepthMapResolved != m_particleDepthMap && m_particleDepthMapResolved) {delete m_particleDepthMapResolved; m_particleDepthMapResolved = NULL;}
|
|
if (m_particleAlphaMapResolved != m_particleAlphaMap && m_particleAlphaMapResolved) {delete m_particleAlphaMapResolved; m_particleAlphaMapResolved = NULL;}
|
|
#endif
|
|
m_particleDepthMapResolved = m_particleDepthMap;
|
|
m_particleAlphaMapResolved = m_particleAlphaMap;
|
|
}
|
|
# endif //DEVICE_MSAA
|
|
}
|
|
else
|
|
{
|
|
if (m_particleDepthMapResolved != m_particleDepthMap && m_particleDepthMapResolved) {delete m_particleDepthMapResolved;}
|
|
m_particleDepthMapResolved = NULL;
|
|
if (m_particleAlphaMapResolved != m_particleAlphaMap && m_particleAlphaMapResolved) {delete m_particleAlphaMapResolved;}
|
|
m_particleAlphaMapResolved = NULL;
|
|
if (m_particleDepthMap) {delete m_particleDepthMap; m_particleDepthMap = NULL;}
|
|
if (m_particleAlphaMap) {delete m_particleAlphaMap; m_particleAlphaMap = NULL;}
|
|
}
|
|
#endif //PTFX_APPLY_DOF_TO_PARTICLES
|
|
}
|
|
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitDownsampling
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void CPtFxManager::InitDownsampling ()
|
|
{
|
|
// create downsampling Buffers
|
|
const int width = VideoResManager::GetSceneWidth()/2;
|
|
const int height = VideoResManager::GetSceneHeight()/2;
|
|
// color
|
|
grcTextureFactory::CreateParams params;
|
|
params.Multisample = 0;
|
|
params.HasParent = true;
|
|
params.Parent = NULL;
|
|
params.UseFloat = true;
|
|
#if __PPU
|
|
params.InTiledMemory = true;
|
|
params.IsSwizzled = false;
|
|
#endif
|
|
params.UseHierZ = false;
|
|
params.IsSRGB = false;
|
|
|
|
#if __PS3
|
|
const int bpp = 64;
|
|
params.Format = grctfA16B16G16R16F;
|
|
u8 rtPoolHeap = 5;
|
|
RTMemPoolID memPoolID = PSN_RTMEMPOOL_P5120_TILED_LOCAL_COMPMODE_DISABLED_2;
|
|
#else
|
|
const int bpp = 32;
|
|
params.Format = grctfA8R8G8B8;
|
|
RTMemPoolID memPoolID = XENON_RTMEMPOOL_GBUFFER23;
|
|
u8 rtPoolHeap = kPtfxDownsampleHeap;
|
|
|
|
params.Parent = CRenderTargets::GetDepthBufferQuarter();
|
|
#endif
|
|
|
|
m_pLowResPtfxBuffer = CRenderTargets::CreateRenderTarget(memPoolID, "PTX Downsample Color", grcrtPermanent, width, height, bpp, ¶ms, rtPoolHeap);
|
|
if(memPoolID != RTMEMPOOL_NONE)
|
|
{
|
|
m_pLowResPtfxBuffer->AllocateMemoryFromPool();
|
|
}
|
|
|
|
// Always allocate that one after!
|
|
// We need that surface to be at the end of the memory pool so the blurred water reflection
|
|
// doesn't screw us over!
|
|
|
|
//Create conditional query for downsampling ptfx
|
|
m_PtfxDownsampleQuery = GRCDEVICE.CreateConditionalQuery();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ShutdownDownsampling
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ShutdownDownsampling ()
|
|
{
|
|
GRCDEVICE.DestroyConditionalQuery(m_PtfxDownsampleQuery);
|
|
}
|
|
|
|
#endif //USE_DOWNSAMPLED_PARTICLES
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Update
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::Update (float deltaTime)
|
|
{
|
|
#if __BANK
|
|
if (m_gameDebug.GetOverrideDeltaTime())
|
|
{
|
|
deltaTime = m_gameDebug.GetOverrideDeltaTimeValue();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
float minDeltaTime = 0.0f;
|
|
float maxDeltaTime = fwTimer::GetMaximumFrameTime();
|
|
|
|
REPLAY_ONLY(if (!CReplayMgr::IsReplayInControlOfWorld()))
|
|
{
|
|
ptfxAssertf(deltaTime<=maxDeltaTime, "particle update delta time (%.2f) is greater than the maximum allowed (%.2f) - this will be clamped", deltaTime, maxDeltaTime);
|
|
ptfxAssertf(deltaTime>=minDeltaTime, "particle update delta time (%.2f) is less than the minimum allowed (%.2f) - this will be clamped", deltaTime, minDeltaTime);
|
|
}
|
|
|
|
deltaTime = Clamp(deltaTime, minDeltaTime, maxDeltaTime);
|
|
}
|
|
|
|
#if GTA_REPLAY
|
|
//On a replay we typically miss any calls to RequestPtFxAsset so we cache the slot id of the asset
|
|
//and add a replay packet to load the assets at the beginning of each clip.
|
|
if(CReplayMgr::IsRecording() && m_ReplayScriptPtfxAssetID != -1)
|
|
{
|
|
if( m_RecordReplayScriptRequestAsset )
|
|
{
|
|
CReplayMgr::RecordFx<CPacketRequestPtfxAsset>(
|
|
CPacketRequestPtfxAsset(m_ReplayScriptPtfxAssetID));
|
|
m_RecordReplayScriptRequestAsset = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_RecordReplayScriptRequestAsset = true;
|
|
}
|
|
#endif //GTA_REPLAY
|
|
|
|
|
|
m_updateDeltaSum += deltaTime;
|
|
UpdateDynamicQuality();
|
|
|
|
#if __BANK
|
|
m_gameDebug.Update();
|
|
|
|
if (ptfxDebug::GetDisablePtFx())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( m_gameDebug.GetExplosionPerformanceTest() )
|
|
{
|
|
ProcessExplosionPerformanceTest();
|
|
}
|
|
#endif
|
|
|
|
if (gVpMan.GetGameViewport())
|
|
{
|
|
m_currViewport = gVpMan.GetGameViewport()->GetGrcViewport();
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
const camBaseCamera* pCamera = camInterface::GetDominantRenderedCamera();
|
|
m_isFirstPersonCamActive[gRenderThreadInterface.GetUpdateBuffer()] = pCamera && pCamera->GetIsClassId(camFirstPersonShooterCamera::GetStaticClassId());
|
|
#endif
|
|
|
|
CRenderPhaseCascadeShadowsInterface::GetCascadeViewports(m_currShadowViewports);
|
|
// check all of the global assets have loaded
|
|
ptfxAssertf(m_streamingRequests.HaveAllLoaded(), "the global particle assets are not all loaded");
|
|
|
|
// set the memory bucket
|
|
USE_MEMBUCKET(MEMBUCKET_FX);
|
|
|
|
#if RMPTFX_EDITOR
|
|
// make sure rmptfx knows about our main game camera matrix - for starting debug effects
|
|
RMPTFXMGR.SetDebugEffectCameraMtx(&RCC_MAT34V(camInterface::GetMat()));
|
|
#endif
|
|
|
|
Assertf(ptfxManager::GetAssetStore().IsSafeToRemoveFxLists(), "Hey someone left the safeToRemoveFxLists flag cleared for too long!");
|
|
ptfxManager::GetAssetStore().SetIsSafeToRemoveFxLists(false);
|
|
|
|
// set some flags
|
|
CVfxHelper::SetUsingCinematicCam(camInterface::GetCinematicDirector().IsRendering());
|
|
|
|
ptfxManager::Update(deltaTime, RCC_MAT34V(camInterface::GetMat()));
|
|
|
|
CPed::ClearPedsThatCanGetWetThisFrame();
|
|
|
|
m_ptFxCollision.Update();
|
|
|
|
// check everything is fine
|
|
ptfxAssertf(!m_startedUpdate, "update thread is already started");
|
|
m_startedUpdate = true;
|
|
|
|
// set the next update delta
|
|
m_nextUpdateDelta = deltaTime;
|
|
|
|
// signal that the update can start
|
|
sysIpcSignalSema(m_startUpdateSema);
|
|
}
|
|
|
|
#if __BANK
|
|
u32 sExplosionTestState = 0;
|
|
u32 sExplosionTestStartTime = 0;
|
|
void CPtFxManager::ProcessExplosionPerformanceTest()
|
|
{
|
|
static float offsetForward = 7.0f;
|
|
static float offsetRight = 2.0f;
|
|
|
|
//Pause the explosions after this time.
|
|
static float pauseTime = 0.75;
|
|
static eExplosionTag explosionType = EXP_TAG_CAR;
|
|
|
|
CPed * pPlayerPed = FindPlayerPed();
|
|
if (pPlayerPed)
|
|
{
|
|
switch(sExplosionTestState)
|
|
{
|
|
case 0:
|
|
{
|
|
Vec3V playerPos = pPlayerPed->GetTransform().GetPosition();
|
|
Vec3V playerForward = pPlayerPed->GetTransform().GetForward();
|
|
Vec3V playerRight = pPlayerPed->GetTransform().GetRight();
|
|
|
|
Vec3V newPosA = playerPos + (playerForward * ScalarV(offsetForward));
|
|
Vec3V newPosB = newPosA + (playerRight * ScalarV(offsetRight));
|
|
Vec3V newPosC = newPosA - (playerRight * ScalarV(offsetRight));
|
|
Vec3V newPosD = newPosA + (playerForward * ScalarV(offsetForward));
|
|
Vec3V newPosE = newPosD + (playerRight * ScalarV(2*offsetRight));
|
|
Vec3V newPosF = newPosD - (playerRight * ScalarV(2*offsetRight));
|
|
Vec3V newPosG = newPosD + (playerForward * ScalarV(offsetForward));
|
|
Vec3V newPosH = newPosG + (playerRight * ScalarV(2*offsetRight));
|
|
Vec3V newPosI = newPosG - (playerRight * ScalarV(2*offsetRight));
|
|
Vec3V newPosJ = newPosG + (playerRight * ScalarV(4*offsetRight));
|
|
Vec3V newPosK = newPosG - (playerRight * ScalarV(4*offsetRight));
|
|
|
|
const u32 numPositions = 10;
|
|
Vec3V positions[numPositions] = {newPosB, newPosC, newPosD, newPosE, newPosF, newPosG, newPosH, newPosI, newPosJ, newPosK};
|
|
for( u32 i = 0; i < numPositions; i++)
|
|
{
|
|
CExplosionManager::CExplosionArgs explosionArgs(explosionType, VEC3V_TO_VECTOR3(positions[i]));
|
|
explosionArgs.m_fCamShake = 0.0;
|
|
CExplosionManager::AddExplosion(explosionArgs, NULL, true);
|
|
}
|
|
|
|
sExplosionTestStartTime = fwTimer::GetTimeInMilliseconds();
|
|
sExplosionTestState++;
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
if( fwTimer::GetTimeInMilliseconds() - sExplosionTestStartTime > pauseTime * 1000)
|
|
{
|
|
fwTimer::StartUserPause();
|
|
m_gameDebug.ResetExplosionPerformanceTest();
|
|
sExplosionTestState = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// UpdateDynamicQuality
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::UpdateDynamicQuality (void)
|
|
{
|
|
// Note: right now this only works on PS3 because the 360 has a visual pop when you switch between the two.
|
|
|
|
m_numGPUFrames++;
|
|
m_gpuTimerSumMedRes += m_gpuTimeMedRes[gRenderThreadInterface.GetUpdateBuffer()];
|
|
m_gpuTimerSumLowRes += m_gpuTimeLowRes[gRenderThreadInterface.GetUpdateBuffer()];
|
|
|
|
// the render flags may change this frame, but since this value is double buffered between the render
|
|
// thread and the update thread, we need to make sure we are keeping them in sync
|
|
m_ptfxRenderFlags[gRenderThreadInterface.GetUpdateBuffer()] = m_ptfxCurrRenderFlags;
|
|
|
|
#if __BANK
|
|
if (m_gameDebug.GetRenderAllDownsampled())
|
|
{
|
|
m_ptfxRenderFlags[gRenderThreadInterface.GetUpdateBuffer()] = PTFX_RF_MEDRES_DOWNSAMPLE | PTFX_RF_LORES_DOWNSAMPLE;
|
|
m_ptfxCurrRenderFlags = PTFX_RF_MEDRES_DOWNSAMPLE | PTFX_RF_LORES_DOWNSAMPLE;
|
|
|
|
m_numGPUFrames = 0;
|
|
m_gpuTimerSumMedRes = 0.0f;
|
|
m_gpuTimerSumLowRes = 0.0f;
|
|
m_updateDeltaSum = 0.0f;
|
|
return;
|
|
}
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
else if (!m_gameDebug.GetDynamicQualitySystemEnabled())
|
|
#endif
|
|
{
|
|
m_ptfxRenderFlags[gRenderThreadInterface.GetUpdateBuffer()] = 0;
|
|
m_ptfxCurrRenderFlags = 0;
|
|
|
|
m_numGPUFrames = 0;
|
|
m_gpuTimerSumMedRes = 0.0f;
|
|
m_gpuTimerSumLowRes = 0.0f;
|
|
m_updateDeltaSum = 0.0f;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
|
|
const float qualityUpdateTime = BANK_SWITCH(m_gameDebug.GetDQUpdateFrequency(), PTFX_DYNAMIC_QUALITY_UPDATE_TIME); // seconds
|
|
const float totalRenderTimeThreshold = BANK_SWITCH(m_gameDebug.GetDQTotalRenderTimeThreshold(), PTFX_DYNAMIC_QUALITY_TOTAL_TIME_THRESHOLD); // ms
|
|
const float lowResRenderTimeThreshold = BANK_SWITCH(m_gameDebug.GetDQLowResRenderTimeThreshold(), PTFX_DYNAMIC_QUALITY_LOWRES_TIME_THRESHOLD); // ms
|
|
const float endDownsamplingThreshold = BANK_SWITCH(m_gameDebug.GetDQEndDownsampleThreshold(), PTFX_DYNAMIC_QUALITY_END_LOWRES_TIME_THRESHOLD); // ms
|
|
|
|
// check every few seconds to see if we need to modify the quality of particles
|
|
if (m_updateDeltaSum > qualityUpdateTime)
|
|
{
|
|
float averagePtfxRenderTimeMedRes = m_gpuTimerSumMedRes / (float)m_numGPUFrames;
|
|
float averagePtfxRenderTimeLowRes = m_gpuTimerSumLowRes / (float)m_numGPUFrames;
|
|
u8 renderFlags = m_ptfxRenderFlags[gRenderThreadInterface.GetUpdateBuffer()];
|
|
|
|
if (averagePtfxRenderTimeMedRes + averagePtfxRenderTimeLowRes > totalRenderTimeThreshold)
|
|
{
|
|
// if we are actually render some low res particles, and they are individually consuming more than 2.5ms, the first
|
|
// thing we want to try is just downsampling the low res particles.
|
|
if (averagePtfxRenderTimeLowRes > lowResRenderTimeThreshold &&
|
|
!(renderFlags & PTFX_RF_LORES_DOWNSAMPLE))
|
|
{
|
|
renderFlags = PTFX_RF_LORES_DOWNSAMPLE;
|
|
}
|
|
else
|
|
{
|
|
// either there aren't enough low-res to really matter, or they are already downsampled. Either way,
|
|
// it's time to downsample the hi-res particles as well.
|
|
renderFlags = PTFX_RF_LORES_DOWNSAMPLE | PTFX_RF_MEDRES_DOWNSAMPLE;
|
|
}
|
|
}
|
|
else if (averagePtfxRenderTimeMedRes + averagePtfxRenderTimeLowRes < endDownsamplingThreshold)
|
|
{
|
|
// if particles have reached the point where they are less than specified amount in render time cost, stop downsampling.
|
|
renderFlags = 0;
|
|
}
|
|
|
|
m_ptfxRenderFlags[gRenderThreadInterface.GetUpdateBuffer()] = renderFlags;
|
|
m_ptfxCurrRenderFlags = renderFlags;
|
|
|
|
m_numGPUFrames = 0;
|
|
m_gpuTimerSumMedRes = 0.0f;
|
|
m_gpuTimerSumLowRes = 0.0f;
|
|
m_updateDeltaSum = 0.0f;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreLight
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreLight (ptfxLightInfo& lightInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeLightCS);
|
|
if (m_numLightInfos<PTFX_MAX_STORED_LIGHTS)
|
|
{
|
|
m_lightInfos[m_numLightInfos++] = lightInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many lights from particle effects this frame");
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredLights
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredLights ()
|
|
{
|
|
for (int i=0; i<m_numLightInfos; i++)
|
|
{
|
|
if(m_lightInfos[i].pEffectInst == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Vec3V vLightPos = m_lightInfos[i].vPos;
|
|
Vec3V vLightDir(V_Z_AXIS_WZERO);
|
|
Vec3V vLightTan(V_Y_AXIS_WZERO);
|
|
Vec3V vLightCol(m_lightInfos[i].lightCol.GetRedf(), m_lightInfos[i].lightCol.GetGreenf(), m_lightInfos[i].lightCol.GetBluef());
|
|
float lightIntensity = m_lightInfos[i].lightIntensity;
|
|
float lightFalloff = m_lightInfos[i].lightFalloff;
|
|
float lightRange = m_lightInfos[i].lightRange;
|
|
float lightAngle = m_lightInfos[i].lightAngle;
|
|
|
|
Vec3V vCoronaCol(m_lightInfos[i].coronaCol.GetRedf(), m_lightInfos[i].coronaCol.GetGreenf(), m_lightInfos[i].coronaCol.GetBluef());
|
|
float coronaIntensity = m_lightInfos[i].coronaIntensity;
|
|
float coronaSize = m_lightInfos[i].coronaSize;
|
|
float coronaFlare = m_lightInfos[i].coronaFlare;
|
|
float coronaZBias = m_lightInfos[i].coronaZBias;
|
|
|
|
#if __BANK
|
|
if (gLastGenMode)
|
|
{
|
|
lightIntensity *= 0.2f;
|
|
}
|
|
#endif
|
|
|
|
if (lightRange>0.0f)
|
|
{
|
|
u32 lightFlags = LIGHTFLAG_FX;
|
|
if (m_lightInfos[i].castsShadows)
|
|
{
|
|
lightFlags |= LIGHTFLAG_CAST_SHADOWS | LIGHTFLAG_CAST_DYNAMIC_GEOM_SHADOWS | LIGHTFLAG_CAST_STATIC_GEOM_SHADOWS;
|
|
}
|
|
|
|
// set up omni light
|
|
CLightSource* pLightSource = CAsyncLightOcclusionMgr::AddLight();
|
|
if (pLightSource)
|
|
{
|
|
pLightSource->Reset();
|
|
pLightSource->SetCommon(LIGHT_TYPE_POINT, lightFlags, RCC_VECTOR3(vLightPos), RCC_VECTOR3(vLightCol), lightIntensity, LIGHT_ALWAYS_ON);
|
|
|
|
// override for spotlights
|
|
if (m_lightInfos[i].lightType!=0)
|
|
{
|
|
pLightSource->SetType(LIGHT_TYPE_SPOT);
|
|
|
|
// calc direction and tangent
|
|
Mat34V vEffectMtxWld = m_lightInfos[i].pEffectInst->GetMatrix();
|
|
Mat34V vEmitterMtxWld = m_lightInfos[i].pEmitterInst->GetCreationDomainMtxWld(vEffectMtxWld);
|
|
|
|
if (m_lightInfos[i].lightType==1) // spotlight aligned to effect's x axis
|
|
{
|
|
vLightDir = vEffectMtxWld.GetCol0();
|
|
vLightTan = vEffectMtxWld.GetCol2();
|
|
}
|
|
else if (m_lightInfos[i].lightType==2) // spotlight aligned to effect's y axis
|
|
{
|
|
vLightDir = vEffectMtxWld.GetCol1();
|
|
vLightTan = vEffectMtxWld.GetCol0();
|
|
}
|
|
else if (m_lightInfos[i].lightType==3) // spotlight aligned to effect's z axis
|
|
{
|
|
vLightDir = vEffectMtxWld.GetCol2();
|
|
vLightTan = vEffectMtxWld.GetCol1();
|
|
}
|
|
else if (m_lightInfos[i].lightType==4) // spotlight aligned to emitter's x axis
|
|
{
|
|
vLightDir = vEmitterMtxWld.GetCol0();
|
|
vLightTan = vEmitterMtxWld.GetCol2();
|
|
}
|
|
else if (m_lightInfos[i].lightType==5) // spotlight aligned to emitter's y axis
|
|
{
|
|
vLightDir = vEmitterMtxWld.GetCol1();
|
|
vLightTan = vEmitterMtxWld.GetCol0();
|
|
}
|
|
else if (m_lightInfos[i].lightType==6) // spotlight aligned to emitter's z axis
|
|
{
|
|
vLightDir = vEmitterMtxWld.GetCol2();
|
|
vLightTan = vEmitterMtxWld.GetCol1();
|
|
}
|
|
}
|
|
|
|
pLightSource->SetDirTangent(RCC_VECTOR3(vLightDir), RCC_VECTOR3(vLightTan));
|
|
pLightSource->SetRadius(lightRange);
|
|
pLightSource->SetFalloffExponent(lightFalloff);
|
|
|
|
if (pLightSource->GetType()==LIGHT_TYPE_SPOT)
|
|
pLightSource->SetSpotlight(0.0f, lightAngle);
|
|
|
|
if (m_lightInfos[i].castsShadows)
|
|
{
|
|
pLightSource->SetShadowTrackingId(fwIdKeyGenerator::Get(m_lightInfos[i].pPointItem, 0));
|
|
}
|
|
|
|
fwEntity* pAttachEntityFW = ptfxAttach::GetAttachEntity(m_lightInfos[i].pEffectInst);
|
|
if (pAttachEntityFW)
|
|
{
|
|
CEntity* pAttachEntity = static_cast<CEntity*>(pAttachEntityFW);
|
|
fwInteriorLocation interiorLocation = pAttachEntity->GetInteriorLocation();
|
|
pLightSource->SetInInterior(interiorLocation);
|
|
}
|
|
else
|
|
{
|
|
pLightSource->SetInInterior(CVfxHelper::GetCamInteriorLocation());
|
|
}
|
|
|
|
// NOTE: we don't want to call Lights::AddSceneLight directly - the AsyncLightOcclusionMgr will handle that for us
|
|
}
|
|
|
|
}
|
|
|
|
if (coronaSize>0.0f)
|
|
{
|
|
bool useDir = coronaFlare>0.0f;
|
|
Vec3V vDir = Vec3V(V_ZERO);
|
|
if (useDir)
|
|
{
|
|
Vec3V_ConstRef vCamForward = RCC_VEC3V(camInterface::GetGameplayDirector().GetFrame().GetWorldMatrix().b);
|
|
Vec3V_ConstRef vCamUp = RCC_VEC3V(camInterface::GetGameplayDirector().GetFrame().GetWorldMatrix().c);
|
|
vDir = Lerp(ScalarVFromF32(coronaFlare), vCamUp, -vCamForward);
|
|
}
|
|
|
|
u8 coronaFlags = 0;
|
|
|
|
if (m_lightInfos[i].coronaOnlyInReflection || m_lightInfos[i].coronasAdded)
|
|
{
|
|
coronaFlags |= CORONA_ONLY_IN_REFLECTION;
|
|
}
|
|
|
|
if (m_lightInfos[i].coronaNotInReflection)
|
|
{
|
|
coronaFlags |= CORONA_DONT_REFLECT;
|
|
}
|
|
|
|
coronaFlags |= (useDir) ? CORONA_HAS_DIRECTION : 0;
|
|
|
|
g_coronas.Register(vLightPos,
|
|
coronaSize,
|
|
vCoronaCol,
|
|
coronaIntensity,
|
|
coronaZBias,
|
|
vDir,
|
|
1.0f,
|
|
CORONAS_DEF_DIR_LIGHT_CONE_ANGLE_INNER,
|
|
CORONAS_DEF_DIR_LIGHT_CONE_ANGLE_OUTER,
|
|
coronaFlags);
|
|
|
|
m_lightInfos[i].coronasAdded = false;
|
|
}
|
|
}
|
|
|
|
if (!fwTimer::IsGamePaused())
|
|
{
|
|
m_numLightInfos = 0;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreFogVolume
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreFogVolume (ptfxFogVolumeInfo& fogVolumeInfo)
|
|
{
|
|
|
|
FogVolume_t fogVolumeParams;
|
|
fogVolumeParams.pos = fogVolumeInfo.vPos;
|
|
fogVolumeParams.scale = fogVolumeInfo.vScale;
|
|
fogVolumeParams.rotation = fogVolumeInfo.vRotation;
|
|
fogVolumeParams.range = fogVolumeInfo.range;
|
|
fogVolumeParams.density = fogVolumeInfo.density;
|
|
fogVolumeParams.fallOff = fogVolumeInfo.falloff;
|
|
fogVolumeParams.hdrMult = fogVolumeInfo.hdrMult;
|
|
fogVolumeParams.col = fogVolumeInfo.col;
|
|
fogVolumeParams.lightingType = (FogVolumeLighting_e)fogVolumeInfo.lightingType;
|
|
fogVolumeParams.useGroundFogColour = fogVolumeInfo.useGroundFogColour;
|
|
fogVolumeParams.interiorHash = CVfxHelper::GetHashFromInteriorLocation(fogVolumeInfo.interiorLocation);
|
|
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeFogVolumeCS);
|
|
g_fogVolumeMgr.Register(fogVolumeParams);
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DrawFogVolume
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::DrawFogVolume (ptfxFogVolumeInfo& fogVolumeInfo)
|
|
{
|
|
//Disable fog volume renders during mirror reflection and see through renderphases
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION || g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SIMPLE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FogVolume_t fogVolumeParams;
|
|
fogVolumeParams.pos = fogVolumeInfo.vPos;
|
|
fogVolumeParams.scale = fogVolumeInfo.vScale;
|
|
fogVolumeParams.rotation = fogVolumeInfo.vRotation;
|
|
fogVolumeParams.range = fogVolumeInfo.range;
|
|
fogVolumeParams.density = fogVolumeInfo.density;
|
|
fogVolumeParams.fallOff = fogVolumeInfo.falloff;
|
|
fogVolumeParams.hdrMult = fogVolumeInfo.hdrMult;
|
|
fogVolumeParams.col = fogVolumeInfo.col;
|
|
fogVolumeParams.lightingType = (FogVolumeLighting_e)fogVolumeInfo.lightingType;
|
|
fogVolumeParams.useGroundFogColour = fogVolumeInfo.useGroundFogColour;
|
|
fogVolumeParams.interiorHash = CVfxHelper::GetHashFromInteriorLocation(fogVolumeInfo.interiorLocation);
|
|
BANK_ONLY(fogVolumeParams.debugRender = true;)
|
|
g_fogVolumeMgr.RenderImmediate(fogVolumeParams);
|
|
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredLights
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessLateStoredLights ()
|
|
{
|
|
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
|
|
|
|
for (int i=0; i<m_numLightInfos; i++)
|
|
{
|
|
Vec3V vLightPos = m_lightInfos[i].vPos;
|
|
Vec3V vCoronaCol(m_lightInfos[i].coronaCol.GetRedf(), m_lightInfos[i].coronaCol.GetGreenf(), m_lightInfos[i].coronaCol.GetBluef());
|
|
float coronaIntensity = m_lightInfos[i].coronaIntensity;
|
|
float coronaSize = m_lightInfos[i].coronaSize;
|
|
float coronaFlare = m_lightInfos[i].coronaFlare;
|
|
float coronaZBias = m_lightInfos[i].coronaZBias;
|
|
|
|
if (coronaSize>0.0f)
|
|
{
|
|
bool useDir = coronaFlare>0.0f;
|
|
Vec3V vDir = Vec3V(V_ZERO);
|
|
if (useDir)
|
|
{
|
|
Vec3V_ConstRef vCamForward = RCC_VEC3V(camInterface::GetGameplayDirector().GetFrame().GetWorldMatrix().b);
|
|
Vec3V_ConstRef vCamUp = RCC_VEC3V(camInterface::GetGameplayDirector().GetFrame().GetWorldMatrix().c);
|
|
vDir = Lerp(ScalarVFromF32(coronaFlare), vCamUp, -vCamForward);
|
|
}
|
|
|
|
bool added = false;
|
|
if( !m_lightInfos[i].coronaOnlyInReflection )
|
|
{
|
|
u8 coronaFlags = 0;
|
|
|
|
if (m_lightInfos[i].coronaNotInReflection)
|
|
{
|
|
coronaFlags |= CORONA_DONT_REFLECT;
|
|
}
|
|
|
|
coronaFlags |= (useDir) ? CORONA_HAS_DIRECTION : 0;
|
|
|
|
added = g_coronas.LateRegister(vLightPos,
|
|
coronaSize,
|
|
vCoronaCol,
|
|
coronaIntensity,
|
|
coronaZBias,
|
|
vDir,
|
|
1.0f,
|
|
CORONAS_DEF_DIR_LIGHT_CONE_ANGLE_INNER,
|
|
CORONAS_DEF_DIR_LIGHT_CONE_ANGLE_OUTER,
|
|
coronaFlags);
|
|
}
|
|
m_lightInfos[i].coronasAdded = added;
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreFire
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreFire (ptfxFireInfo& fireInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeFireCS);
|
|
if (m_numFireInfos<PTFX_MAX_STORED_FIRES)
|
|
{
|
|
m_fireInfos[m_numFireInfos++] = fireInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many fires from particle effects this frame");
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredFires
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredFires ()
|
|
{
|
|
for (int i=0; i<m_numFireInfos; i++)
|
|
{
|
|
g_fireMan.StartMapFire(
|
|
m_fireInfos[i].vPos,
|
|
m_fireInfos[i].vNormal,
|
|
m_fireInfos[i].mtlId,
|
|
NULL,
|
|
m_fireInfos[i].burnTime,
|
|
m_fireInfos[i].burnStrength,
|
|
m_fireInfos[i].peakTime,
|
|
m_fireInfos[i].fuelLevel,
|
|
m_fireInfos[i].fuelBurnRate,
|
|
m_fireInfos[i].numGenerations,
|
|
Vec3V(V_ZERO),
|
|
BANK_ONLY(Vec3V(V_ZERO),)
|
|
false);
|
|
}
|
|
|
|
m_numFireInfos = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreDecal
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreDecal (ptfxDecalInfo& decalInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeDecalCS);
|
|
if (m_numDecalInfos<PTFX_MAX_STORED_DECALS)
|
|
{
|
|
m_decalInfos[m_numDecalInfos++] = decalInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many decals from particle effects this frame");
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredDecals
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredDecals ()
|
|
{
|
|
for (int i=0; i<m_numDecalInfos; i++)
|
|
{
|
|
if(m_decalInfos[i].pEffectInst == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// get random side vector
|
|
Vec3V vSide;
|
|
if (m_decalInfos[i].isDirectional)
|
|
{
|
|
Vec3V vAlign = m_decalInfos[i].vPos - m_decalInfos[i].pEffectInst->GetCurrPos();
|
|
vAlign = Normalize(vAlign);
|
|
if (CVfxHelper::GetRandomTangentAlign(vSide, -m_decalInfos[i].vDir, vAlign)==false)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CVfxHelper::GetRandomTangent(vSide, -m_decalInfos[i].vDir)==false)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
s32 decalRenderSettingIndex;
|
|
s32 decalRenderSettingCount;
|
|
g_decalMan.FindRenderSettingInfo(m_decalInfos[i].decalId, decalRenderSettingIndex, decalRenderSettingCount);
|
|
|
|
Color32 col(m_decalInfos[i].colR, m_decalInfos[i].colG, m_decalInfos[i].colB, m_decalInfos[i].colA);
|
|
|
|
float width = m_decalInfos[i].width;
|
|
float height = m_decalInfos[i].height;
|
|
if (m_decalInfos[i].distanceScale!=0.0f)
|
|
{
|
|
float dist = Dist(m_decalInfos[i].vPos, m_decalInfos[i].pEffectInst->GetCurrPos()).Getf();
|
|
float whMult = 1.0f + (dist*m_decalInfos[i].distanceScale);
|
|
whMult = MAX(0.0f, whMult);
|
|
width *= whMult;
|
|
height *= whMult;
|
|
}
|
|
|
|
DecalType_e decalType = DECALTYPE_GENERIC_SIMPLE_COLN;
|
|
if (m_decalInfos[i].useComplexColn)
|
|
{
|
|
decalType = DECALTYPE_GENERIC_COMPLEX_COLN;
|
|
}
|
|
|
|
g_decalMan.AddGeneric(decalType, decalRenderSettingIndex, decalRenderSettingCount, m_decalInfos[i].vPos, m_decalInfos[i].vDir, vSide, width, height, m_decalInfos[i].projectionDepth, m_decalInfos[i].totalLife, m_decalInfos[i].fadeInTime, m_decalInfos[i].uvMultStart, m_decalInfos[i].uvMultEnd, m_decalInfos[i].uvMultTime, col, m_decalInfos[i].flipU, m_decalInfos[i].flipV, m_decalInfos[i].duplicateRejectDist, false, false REPLAY_ONLY(, 0.0f));
|
|
}
|
|
|
|
m_numDecalInfos = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreDecalPool
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreDecalPool (ptfxDecalPoolInfo& decalPoolInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeDecalPoolCS);
|
|
if (m_numDecalPoolInfos<PTFX_MAX_STORED_DECAL_POOLS)
|
|
{
|
|
m_decalPoolInfos[m_numDecalPoolInfos++] = decalPoolInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many decal pools from particle effects this frame");
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredDecalPools
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredDecalPools ()
|
|
{
|
|
for (int i=0; i<m_numDecalPoolInfos; i++)
|
|
{
|
|
VfxLiquidType_e liquidType = VFXLIQUID_TYPE_NONE;
|
|
s32 decalRenderSettingIndex = -1;
|
|
s32 decalRenderSettingCount = 0;
|
|
if (m_decalPoolInfos[i].liquidType>=0)
|
|
{
|
|
liquidType = (VfxLiquidType_e)m_decalPoolInfos[i].liquidType;
|
|
}
|
|
else
|
|
{
|
|
g_decalMan.FindRenderSettingInfo(m_decalPoolInfos[i].decalRenderSettingId, decalRenderSettingIndex, decalRenderSettingCount);
|
|
}
|
|
|
|
Color32 col(m_decalPoolInfos[i].colR, m_decalPoolInfos[i].colG, m_decalPoolInfos[i].colB, m_decalPoolInfos[i].colA);
|
|
g_decalMan.AddPool(liquidType, decalRenderSettingIndex, decalRenderSettingCount, m_decalPoolInfos[i].vPos, m_decalPoolInfos[i].vNormal, m_decalPoolInfos[i].startSize, m_decalPoolInfos[i].endSize, m_decalPoolInfos[i].growthRate, m_decalPoolInfos[i].mtlId, col, false);
|
|
}
|
|
|
|
m_numDecalPoolInfos = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreDecalTrail
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreDecalTrail (ptfxDecalTrailInfo& decalTrailInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeDecalTrailCS);
|
|
if (m_numDecalTrailInfos<PTFX_MAX_STORED_DECAL_TRAILS)
|
|
{
|
|
// check we haven't stored a decal trail this frame already for this effect
|
|
for (int i=0; i<m_numDecalTrailInfos; i++)
|
|
{
|
|
if (m_decalTrailInfos[i].pEffectInst == decalTrailInfo.pEffectInst)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_decalTrailInfos[m_numDecalTrailInfos++] = decalTrailInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many decal trails from particle effects this frame");
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredDecalTrails
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredDecalTrails ()
|
|
{
|
|
for (int i=0; i<m_numDecalTrailInfos; i++)
|
|
{
|
|
if(m_decalTrailInfos[i].pEffectInst == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
VfxTrailType_e trailType = VFXTRAIL_TYPE_GENERIC;
|
|
s32 decalRenderSettingIndex = -1;
|
|
s32 decalRenderSettingCount = 0;
|
|
u8 colR = m_decalTrailInfos[i].colR;
|
|
u8 colG = m_decalTrailInfos[i].colG;
|
|
u8 colB = m_decalTrailInfos[i].colB;
|
|
u8 colA = m_decalTrailInfos[i].colA;
|
|
if (m_decalTrailInfos[i].liquidType>=0)
|
|
{
|
|
VfxLiquidType_e liquidType = (VfxLiquidType_e)m_liquidInfos[i].liquidType;
|
|
VfxLiquidInfo_s& liquidInfo = g_vfxLiquid.GetLiquidInfo(liquidType);
|
|
|
|
trailType = (VfxTrailType_e)(VFXTRAIL_TYPE_LIQUID+liquidType);
|
|
|
|
// multiply the liquid colour by the particle colour
|
|
colR = static_cast<u8>(liquidInfo.decalColR * (m_decalTrailInfos[i].colR/255.0f));
|
|
colG = static_cast<u8>(liquidInfo.decalColG * (m_decalTrailInfos[i].colG/255.0f));
|
|
colB = static_cast<u8>(liquidInfo.decalColB * (m_decalTrailInfos[i].colB/255.0f));
|
|
colA = static_cast<u8>(liquidInfo.decalColA * (m_decalTrailInfos[i].colA/255.0f));
|
|
}
|
|
else
|
|
{
|
|
g_decalMan.FindRenderSettingInfo(m_decalTrailInfos[i].decalRenderSettingId, decalRenderSettingIndex, decalRenderSettingCount);
|
|
}
|
|
|
|
VfxTrailInfo_t vfxTrailInfo;
|
|
vfxTrailInfo.id = fwIdKeyGenerator::Get(m_decalTrailInfos[i].pEffectInst, 0);
|
|
vfxTrailInfo.type = trailType;
|
|
vfxTrailInfo.regdEnt = NULL;
|
|
vfxTrailInfo.componentId = 0;
|
|
vfxTrailInfo.vWorldPos = m_decalTrailInfos[i].vPos;
|
|
vfxTrailInfo.vNormal = m_decalTrailInfos[i].vNormal;
|
|
vfxTrailInfo.vDir = Vec3V(V_ZERO);
|
|
vfxTrailInfo.vForwardCheck = Vec3V(V_ZERO);
|
|
vfxTrailInfo.decalRenderSettingIndex = decalRenderSettingIndex;
|
|
vfxTrailInfo.decalRenderSettingCount = decalRenderSettingCount;
|
|
vfxTrailInfo.decalLife = -1.0f;
|
|
vfxTrailInfo.width = m_decalTrailInfos[i].widthMin;
|
|
vfxTrailInfo.col = Color32(colR, colG, colB, colA);
|
|
vfxTrailInfo.pVfxMaterialInfo = NULL;
|
|
vfxTrailInfo.mtlId = m_decalTrailInfos[i].mtlId;
|
|
vfxTrailInfo.liquidPoolStartSize = 0.0f;
|
|
vfxTrailInfo.liquidPoolEndSize = 0.0f;
|
|
vfxTrailInfo.liquidPoolGrowthRate = 0.0f;
|
|
vfxTrailInfo.createLiquidPools = false;
|
|
vfxTrailInfo.dontApplyDecal = false;
|
|
|
|
g_vfxTrail.RegisterPoint(vfxTrailInfo);
|
|
}
|
|
|
|
m_numDecalTrailInfos = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreLiquid
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreLiquid (ptfxLiquidInfo& liquidInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeLiquidCS);
|
|
if (m_numLiquidInfos<PTFX_MAX_STORED_LIQUIDS)
|
|
{
|
|
// check we haven't stored a liquid this frame already for this effect
|
|
for (int i=0; i<m_numLiquidInfos; i++)
|
|
{
|
|
if (m_liquidInfos[i].pEffectInst == liquidInfo.pEffectInst)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_liquidInfos[m_numLiquidInfos++] = liquidInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many liquids from particle effects this frame");
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredLiquids
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredLiquids ()
|
|
{
|
|
for (int i=0; i<m_numLiquidInfos; i++)
|
|
{
|
|
if(m_liquidInfos[i].pEffectInst == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
VfxLiquidType_e liquidType = (VfxLiquidType_e)m_liquidInfos[i].liquidType;
|
|
VfxLiquidInfo_s& liquidInfo = g_vfxLiquid.GetLiquidInfo(liquidType);
|
|
|
|
// check for blowing up vehicles when petrol leaking from them is too close to a fire
|
|
if (liquidType==VFXLIQUID_TYPE_PETROL)
|
|
{
|
|
fwEntity* pAttachEntity = ptfxAttach::GetAttachEntity(m_liquidInfos[i].pEffectInst);
|
|
if (pAttachEntity)
|
|
{
|
|
CEntity* pEntity = static_cast<CEntity*>(pAttachEntity);
|
|
if (pEntity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pEntity);
|
|
if (pVehicle && !pVehicle->IsOnFire())
|
|
{
|
|
// check if there is a fire nearby
|
|
float nearestDistSqr = 3.0f;
|
|
if (g_fireMan.FindNearestFire(m_liquidInfos[i].vPos, nearestDistSqr))
|
|
{
|
|
// this is - start a vehicle fire at the effect location
|
|
const float fBurnTime = fwRandom::GetRandomNumberInRange(20.0f, 35.0f);
|
|
const float fBurnStrength = fwRandom::GetRandomNumberInRange(0.75f, 1.0f);
|
|
const float fPeakTime = fwRandom::GetRandomNumberInRange(0.5f, 1.0f);
|
|
|
|
Vec3V vPosLcl = CVfxHelper::GetLocalEntityPos(*pVehicle, m_liquidInfos[i].pEffectInst->GetCurrPos());
|
|
Vec3V vNormLcl = CVfxHelper::GetLocalEntityDir(*pVehicle, Vec3V(V_Z_AXIS_WZERO));
|
|
|
|
g_fireMan.StartVehicleFire(pVehicle, -1, vPosLcl, vNormLcl, NULL, fBurnTime, fBurnStrength, fPeakTime, FIRE_DEFAULT_NUM_GENERATIONS, Vec3V(V_ZERO), BANK_ONLY(Vec3V(V_ZERO),) false);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// multiply the liquid colour by the particle colour
|
|
u8 colR = static_cast<u8>(liquidInfo.decalColR * (m_liquidInfos[i].colR/255.0f));
|
|
u8 colG = static_cast<u8>(liquidInfo.decalColG * (m_liquidInfos[i].colG/255.0f));
|
|
u8 colB = static_cast<u8>(liquidInfo.decalColB * (m_liquidInfos[i].colB/255.0f));
|
|
u8 colA = static_cast<u8>(liquidInfo.decalColA * (m_liquidInfos[i].colA/255.0f));
|
|
|
|
VfxTrailInfo_t vfxTrailInfo;
|
|
vfxTrailInfo.id = fwIdKeyGenerator::Get(m_liquidInfos[i].pEffectInst, 0);
|
|
vfxTrailInfo.type = (VfxTrailType_e)(VFXTRAIL_TYPE_LIQUID+liquidType);;
|
|
vfxTrailInfo.regdEnt = NULL;
|
|
vfxTrailInfo.componentId = 0;
|
|
vfxTrailInfo.vWorldPos = m_liquidInfos[i].vPos;
|
|
vfxTrailInfo.vNormal = m_liquidInfos[i].vNormal;
|
|
vfxTrailInfo.vDir = Vec3V(V_ZERO);
|
|
vfxTrailInfo.vForwardCheck = Vec3V(V_ZERO);
|
|
vfxTrailInfo.decalRenderSettingIndex = -1;
|
|
vfxTrailInfo.decalRenderSettingCount = -1;
|
|
vfxTrailInfo.decalLife = -1.0f;
|
|
vfxTrailInfo.width = m_liquidInfos[i].trailWidthMin;
|
|
vfxTrailInfo.col = Color32(colR, colG, colB, colA);
|
|
vfxTrailInfo.pVfxMaterialInfo = NULL;
|
|
vfxTrailInfo.mtlId = m_liquidInfos[i].mtlId;
|
|
vfxTrailInfo.liquidPoolStartSize = m_liquidInfos[i].poolStartSize;
|
|
vfxTrailInfo.liquidPoolEndSize = m_liquidInfos[i].poolEndSize;
|
|
vfxTrailInfo.liquidPoolGrowthRate = m_liquidInfos[i].poolGrowthRate;
|
|
vfxTrailInfo.createLiquidPools = true;
|
|
vfxTrailInfo.dontApplyDecal = false;
|
|
|
|
g_vfxTrail.RegisterPoint(vfxTrailInfo);
|
|
}
|
|
|
|
m_numLiquidInfos = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreGlassImpact
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreGlassImpact (ptfxGlassImpactInfo& glassImpactInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeGlassImpactCS);
|
|
if (ptfxVerifyf(IsFiniteAll(glassImpactInfo.vPos), "glass impact position is invalid"))
|
|
{
|
|
if (m_numGlassImpactInfos<PTFX_MAX_STORED_GLASS_IMPACTS)
|
|
{
|
|
m_glassImpactInfos[m_numGlassImpactInfos++] = glassImpactInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many glass impact particle effects this frame");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredGlassImpacts
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredGlassImpacts ()
|
|
{
|
|
for (int i=0; i<m_numGlassImpactInfos; i++)
|
|
{
|
|
if (m_glassImpactInfos[i].RegdEnt)
|
|
{
|
|
fwInteriorLocation interiorLocation;
|
|
interiorLocation.MakeInvalid();
|
|
g_vfxWeapon.TriggerPtFxBulletImpact(m_glassImpactInfos[i].pFxWeaponInfo, m_glassImpactInfos[i].RegdEnt, m_glassImpactInfos[i].vPos, m_glassImpactInfos[i].vNormal, -m_glassImpactInfos[i].vNormal, m_glassImpactInfos[i].isExitFx, NULL, false, interiorLocation, false);
|
|
}
|
|
}
|
|
|
|
m_numGlassImpactInfos = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreGlassSmash
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreGlassSmash (ptfxGlassSmashInfo& glassSmashInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeGlassSmashCS);
|
|
if (ptfxVerifyf(IsFiniteAll(glassSmashInfo.vPosHit), "glass smash hit position is invalid") &&
|
|
ptfxVerifyf(IsFiniteAll(glassSmashInfo.vPosCenter), "glass smash centre position is invalid"))
|
|
{
|
|
if (m_numGlassSmashInfos<PTFX_MAX_STORED_GLASS_SMASHES)
|
|
{
|
|
m_glassSmashInfos[m_numGlassSmashInfos++] = glassSmashInfo;
|
|
}
|
|
else
|
|
{
|
|
ptfxWarningf("trying to add too many glass smash particle effects this frame");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredGlassSmashes
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredGlassSmashes ()
|
|
{
|
|
for (int i=0; i<m_numGlassSmashInfos; i++)
|
|
{
|
|
if (m_glassSmashInfos[i].regdPhy)
|
|
{
|
|
g_vfxGlass.TriggerPtFxGlassSmash(m_glassSmashInfos[i].regdPhy, m_glassSmashInfos[i].vPosHit, m_glassSmashInfos[i].vNormal, m_glassSmashInfos[i].force);
|
|
|
|
if (m_glassSmashInfos[i].isWindscreen)
|
|
{
|
|
g_vfxGlass.TriggerPtFxVehicleWindscreenSmash(m_glassSmashInfos[i].regdPhy, m_glassSmashInfos[i].vPosCenter, m_glassSmashInfos[i].vNormal, m_glassSmashInfos[i].force);
|
|
}
|
|
else
|
|
{
|
|
g_vfxGlass.TriggerPtFxVehicleWindowSmash(m_glassSmashInfos[i].regdPhy, m_glassSmashInfos[i].vPosCenter, m_glassSmashInfos[i].vNormal, m_glassSmashInfos[i].force);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_numGlassSmashInfos = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StoreGlassShatter
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StoreGlassShatter (ptfxGlassShatterInfo& glassShatterInfo)
|
|
{
|
|
//Multiple particle update tasks could call this at the same time, so adding critical section
|
|
SYS_CS_SYNC(m_storeGlassShatterCS);
|
|
if (ptfxVerifyf(IsFiniteAll(glassShatterInfo.vPos), "glass shatter position is invalid"))
|
|
{
|
|
if (m_numGlassShatterInfos<PTFX_MAX_STORED_GLASS_SHATTERS)
|
|
{
|
|
// check if there isn't one already stored too close
|
|
for (int i=0; i<m_numGlassShatterInfos; i++)
|
|
{
|
|
Vec3V vDiff = glassShatterInfo.vPos - m_glassShatterInfos[i].vPos;
|
|
if (MagSquared(vDiff).Getf()<=VFXHISTORY_GLASS_HIT_GROUND_DIST_SQR)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_glassShatterInfos[m_numGlassShatterInfos++] = glassShatterInfo;
|
|
}
|
|
// else
|
|
// {
|
|
// ptfxWarningf("trying to add too many glass shatter particle effects this frame");
|
|
// }
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredGlassShatters
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredGlassShatters ()
|
|
{
|
|
for (int i=0; i<m_numGlassShatterInfos; i++)
|
|
{
|
|
g_vfxGlass.TriggerPtFxGlassHitGround(m_glassShatterInfos[i].vPos);
|
|
}
|
|
|
|
m_numGlassShatterInfos = 0;
|
|
}
|
|
|
|
#if USE_SHADED_PTFX_MAP
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// GetPtfxShadedParams
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
Vec4V_Out CPtFxManager::GetPtfxShadedParams ()
|
|
{
|
|
|
|
ScalarV vMapZ = CVfxHelper::GetGameCamPos().GetZ();
|
|
const ScalarV ptFxShadedRange = (BANK_SWITCH(g_ptFxManager.m_gameDebug.GetShadowedPtFxRange(), PTFX_SHADOWED_RANGE_DEFAULT));
|
|
CPed *playerPed = FindPlayerPed();
|
|
if (playerPed)
|
|
{
|
|
Vec3V playerPos = playerPed->GetTransform().GetPosition();
|
|
|
|
// we only care about the XY plane for this calculation, height doesn't matter. Just make sure they are the same
|
|
playerPos.SetZ(vMapZ);
|
|
ScalarV camDist = Sqrt(CVfxHelper::GetDistSqrToCameraV(playerPos));
|
|
|
|
// if the camera is close enough to the player, use the players position. This should always
|
|
// be the case unless we are in a cutscene or something like that where the camera could be
|
|
// far away form the actual player.
|
|
if (IsLessThanAll(camDist, ptFxShadedRange * ScalarV(V_HALF)))
|
|
{
|
|
vMapZ = playerPed->GetTransform().GetPosition().GetZ();
|
|
}
|
|
}
|
|
|
|
//BankFloat ptFxShadedRange = BANK_SWITCH(g_ptFxManager.m_gameDebug.GetShadowedPtFxRange(), PTFX_SHADOWED_RANGE_DEFAULT);
|
|
//BankFloat ptFxShadedLoHeight = BANK_SWITCH(g_ptFxManager.m_gameDebug.GetShadowedPtFxLoHeight(), PTFX_SHADOWED_LOW_HEIGHT_DEFAULT);
|
|
//BankFloat ptFxShadedHiHeight = BANK_SWITCH(g_ptFxManager.m_gameDebug.GetShadowedPtFxHiHeight(), PTFX_SHADOWED_HIGH_HEIGHT_DEFAULT);
|
|
|
|
//Vector4 shaderParams(ptFxShadedRange, fMapZ + ptFxShadedLoHeight, fMapZ + ptFxShadedHiHeight, ptFxShadedRange * 0.8f);
|
|
|
|
Vec4V ptfxShadedParams = Vec4V(PTFX_SHADOWED_RANGE_DEFAULT, PTFX_SHADOWED_LOW_HEIGHT_DEFAULT, PTFX_SHADOWED_HIGH_HEIGHT_DEFAULT, PTFX_SHADOWED_RANGE_DEFAULT * ScalarVConstant<FLOAT_TO_INT(0.8f)>());
|
|
|
|
#if __BANK
|
|
ptfxShadedParams = Vec4V(
|
|
g_ptFxManager.m_gameDebug.GetShadowedPtFxRange(),
|
|
g_ptFxManager.m_gameDebug.GetShadowedPtFxLoHeight(),
|
|
g_ptFxManager.m_gameDebug.GetShadowedPtFxHiHeight(),
|
|
g_ptFxManager.m_gameDebug.GetShadowedPtFxRange() * ScalarVConstant<FLOAT_TO_INT(0.8f)>()
|
|
);
|
|
#endif
|
|
ptfxShadedParams += Vec4V(ScalarV(V_ZERO), Vec2V(vMapZ), ScalarV(V_ZERO));
|
|
return ptfxShadedParams;
|
|
}
|
|
#endif //USE_SHADED_PTFX_MAP
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ResolvePtfxMaps
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#if DEVICE_MSAA
|
|
void CPtFxManager::ResolvePtfxMaps()
|
|
{
|
|
static_cast<grcRenderTargetMSAA*>(m_particleDepthMap)->Resolve(m_particleDepthMapResolved);
|
|
static_cast<grcRenderTargetMSAA*>(m_particleAlphaMap)->Resolve(m_particleAlphaMapResolved);
|
|
}
|
|
#endif //DEVICE_MSAA
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// TryToSoakPed
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::TryToSoakPed (CPed* pPed, ptxDataVolume& dataVolume)
|
|
{
|
|
if (pPed)
|
|
{
|
|
// check if the ped is sheltered from the water
|
|
bool isSheltered = false;
|
|
if (pPed->GetIsInVehicle())
|
|
{
|
|
isSheltered = true;
|
|
|
|
CVehicle* pVehicle = pPed->GetMyVehicle();
|
|
if (pVehicle)
|
|
{
|
|
// go through the vehicle windows to see if any are down or smashed
|
|
for (int i=VEH_FIRST_WINDOW; i<=VEH_LAST_WINDOW; i++)
|
|
{
|
|
eHierarchyId hierarchyId = (eHierarchyId)i;
|
|
|
|
if (pVehicle->IsWindowDown(hierarchyId))
|
|
{
|
|
isSheltered = false;
|
|
break;
|
|
}
|
|
|
|
s32 iWindowComponent = pVehicle->GetFragmentComponentIndex(hierarchyId);
|
|
if (iWindowComponent>-1)
|
|
{
|
|
if (pVehicle->IsHiddenFlagSet(iWindowComponent))
|
|
{
|
|
isSheltered = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if the vehicle has a soft top roof that is down
|
|
if (isSheltered)
|
|
{
|
|
if (pPed->GetAttachState()==ATTACH_STATE_PED_IN_CAR)
|
|
{
|
|
VehicleType vehType = pVehicle->GetVehicleType();
|
|
if (vehType==VEHICLE_TYPE_CAR)
|
|
{
|
|
bool hasRoof = !pVehicle->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_NO_ROOF);
|
|
if (hasRoof)
|
|
{
|
|
if (pVehicle->DoesVehicleHaveAConvertibleRoofAnimation())
|
|
{
|
|
if (pVehicle->GetConvertibleRoofState()!=CTaskVehicleConvertibleRoof::STATE_RAISED)
|
|
{
|
|
isSheltered = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (vehType==VEHICLE_TYPE_BIKE || vehType==VEHICLE_TYPE_BICYCLE || vehType==VEHICLE_TYPE_BOAT)
|
|
{
|
|
isSheltered = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isSheltered==false)
|
|
{
|
|
Vec3V_ConstRef vPedPos = pPed->GetTransform().GetMatrix().GetCol3();
|
|
Vec3V_ConstRef vPedForward = pPed->GetTransform().GetMatrix().GetCol1();
|
|
float strength = dataVolume.GetPointStrength(vPedPos, vPedForward, false, true, false, false);
|
|
if (strength>0.0f)
|
|
{
|
|
float incLevel = 0.0f;
|
|
|
|
u8 dataVolumeType = dataVolume.GetType();
|
|
if (dataVolumeType==PTFX_DATAVOLUMETYPE_SOAK_SLOW)
|
|
{
|
|
incLevel = PTFX_SOAK_SLOW_INC_LEVEL_LOW + strength*(PTFX_SOAK_SLOW_INC_LEVEL_HIGH-PTFX_SOAK_SLOW_INC_LEVEL_LOW);
|
|
}
|
|
else if (dataVolumeType==PTFX_DATAVOLUMETYPE_SOAK_MEDIUM)
|
|
{
|
|
incLevel = PTFX_SOAK_MEDIUM_INC_LEVEL_LOW + strength*(PTFX_SOAK_MEDIUM_INC_LEVEL_HIGH-PTFX_SOAK_MEDIUM_INC_LEVEL_LOW);
|
|
}
|
|
else if (dataVolumeType==PTFX_DATAVOLUMETYPE_SOAK_FAST)
|
|
{
|
|
incLevel = PTFX_SOAK_FAST_INC_LEVEL_LOW + strength*(PTFX_SOAK_FAST_INC_LEVEL_HIGH-PTFX_SOAK_FAST_INC_LEVEL_LOW);
|
|
}
|
|
|
|
pPed->IncWetClothing(incLevel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ShouldSceneSort
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
bool CPtFxManager::ShouldSceneSort (ptxEffectInst* BANK_ONLY(pFxInst))
|
|
{
|
|
// scene sorting
|
|
bool sortMe = true;
|
|
|
|
#if __BANK
|
|
if (g_ptFxManager.GetGameDebugInterface().GetOnlyFlaggedSortedEffects())
|
|
{
|
|
sortMe = pFxInst->GetEffectRule()->GetGameFlag(0);
|
|
}
|
|
else if (g_ptFxManager.GetGameDebugInterface().GetDisableSortedEffects())
|
|
{
|
|
sortMe = false;
|
|
}
|
|
else if (g_ptFxManager.GetGameDebugInterface().GetForceSortedEffects())
|
|
{
|
|
sortMe = true;
|
|
}
|
|
#endif
|
|
|
|
return sortMe;
|
|
}
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// UpdateDataCapsule
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::UpdateDataCapsule (ptxEffectInst* pFxInst)
|
|
{
|
|
static atHashValue flareTrailHashValue = atHashValue("proj_heist_flare_trail");
|
|
if (atHashValue(pFxInst->GetEffectRule()->GetName())==flareTrailHashValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pFxInst->GetIgnoreDataCapsuleTests())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ptxDataVolume dataVolume;
|
|
pFxInst->GetDataVolume(&dataVolume);
|
|
|
|
if (dataVolume.GetRadius()>0.0f)
|
|
{
|
|
u8 dataVolumeType = dataVolume.GetType();
|
|
|
|
// fire
|
|
if (dataVolumeType==PTFX_DATAVOLUMETYPE_FIRE_CONTAINED || dataVolumeType==PTFX_DATAVOLUMETYPE_FIRE || dataVolumeType==PTFX_DATAVOLUMETYPE_FIRE_FLAMETHROWER)
|
|
{
|
|
CPed* pProjectilePedOwner = NULL;
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
ptfxAttachInfo* pAttachInfo = ptfxAttach::Find(pFxInst);
|
|
if (pAttachInfo)
|
|
{
|
|
fwEntity* pfwEntity = pAttachInfo->m_pAttachEntity.Get();
|
|
if (pfwEntity)
|
|
{
|
|
if (static_cast<CEntity*>(pfwEntity)->GetIsTypeObject())
|
|
{
|
|
CEntity* pAttachEntity = static_cast<CEntity*>(pfwEntity);
|
|
if (pAttachEntity)
|
|
{
|
|
//Check if the projectile is valid.
|
|
const CProjectile* pProjectile = static_cast<const CObject *>(pAttachEntity)->GetAsProjectile();
|
|
if(pProjectile)
|
|
{
|
|
//Set the projectile owner.
|
|
CEntity* pOwner = pProjectile->GetOwner();
|
|
if (pOwner && pOwner->GetIsTypePed())
|
|
{
|
|
pProjectilePedOwner = static_cast<CPed*>(pOwner);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// register a fire here
|
|
CEntity* pAttachEntity = static_cast<CEntity*>(ptfxAttach::GetAttachEntity(pFxInst));
|
|
if (pAttachEntity && pAttachEntity->GetIsTypeObject())
|
|
{
|
|
Vec3V vPosLcl = CVfxHelper::GetLocalEntityPos(*pAttachEntity, dataVolume.GetPosWldStart());
|
|
g_fireMan.RegisterPtFxFire(pAttachEntity, vPosLcl, pFxInst, 0, dataVolume.GetRadius(), dataVolumeType==PTFX_DATAVOLUMETYPE_FIRE_CONTAINED, dataVolumeType==PTFX_DATAVOLUMETYPE_FIRE_FLAMETHROWER, pProjectilePedOwner);
|
|
}
|
|
else
|
|
{
|
|
CPed* inflictor = nullptr;
|
|
if(pAttachEntity && pAttachEntity->GetIsTypeVehicle())
|
|
{
|
|
inflictor = SafeCast(CVehicle, pAttachEntity)->GetDriver();
|
|
}
|
|
g_fireMan.RegisterPtFxFire(NULL, dataVolume.GetPosWldStart(), pFxInst, 0, dataVolume.GetRadius(), dataVolumeType==PTFX_DATAVOLUMETYPE_FIRE_CONTAINED, dataVolumeType==PTFX_DATAVOLUMETYPE_FIRE_FLAMETHROWER, inflictor);
|
|
}
|
|
}
|
|
|
|
// water extinguishes fires
|
|
if (dataVolumeType==PTFX_DATAVOLUMETYPE_WATER)
|
|
{
|
|
// extinguish any fires
|
|
g_fireMan.ExtinguishAreaSlowly(dataVolume);
|
|
}
|
|
|
|
// water soaks peds
|
|
if (dataVolumeType==PTFX_DATAVOLUMETYPE_SOAK_SLOW ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_SOAK_MEDIUM ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_SOAK_FAST)
|
|
{
|
|
const bool TRY_TO_SOAK_ALL_PEDS = true;
|
|
if (TRY_TO_SOAK_ALL_PEDS)
|
|
{
|
|
// try to soak all peds
|
|
CPed::Pool* pPedPool = CPed::GetPool();
|
|
CPed* pPed;
|
|
s32 i = (s32)pPedPool->GetSize();
|
|
while (i--)
|
|
{
|
|
pPed = pPedPool->GetSlot(i);
|
|
TryToSoakPed(pPed, dataVolume);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// try to soak the player ped
|
|
CPed* pPed = CGameWorld::FindLocalPlayer();
|
|
CPed* pLocalPlayerPed = pPed;
|
|
TryToSoakPed(pPed, dataVolume);
|
|
|
|
// try to soak peds flagged as able to get wet
|
|
for (s32 i=0; i<CPed::GetNumPedsThatCanGetWetThisFrame(); i++)
|
|
{
|
|
pPed = CPed::GetPedThatCanGetWetThisFrame(i);
|
|
TryToSoakPed(pPed, dataVolume);
|
|
}
|
|
|
|
// try to soak multiplayer player peds
|
|
unsigned numNetworkPlayers = netInterface::GetNumPhysicalPlayers();
|
|
const netPlayer* const* ppNetworkPlayers = netInterface::GetAllPhysicalPlayers();
|
|
for (unsigned index=0; index<numNetworkPlayers; index++)
|
|
{
|
|
const CNetGamePlayer* pNetworkPlayer = SafeCast(const CNetGamePlayer, ppNetworkPlayers[index]);
|
|
pPed = pNetworkPlayer->GetPlayerPed();
|
|
if (pPed && pPed!=pLocalPlayerPed)
|
|
{
|
|
TryToSoakPed(pPed, dataVolume);
|
|
}
|
|
}
|
|
}
|
|
|
|
// extinguish any fires
|
|
g_fireMan.ExtinguishAreaSlowly(dataVolume);
|
|
}
|
|
|
|
// lens effects
|
|
if (dataVolumeType==PTFX_DATAVOLUMETYPE_WATER ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_SAND ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_MUD ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_BLOOD ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_DUST ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_SMOKE ||
|
|
dataVolumeType==PTFX_DATAVOLUMETYPE_BLAZE)
|
|
{
|
|
// check if the camera is inside the data volume
|
|
if (!CVfxHelper::GetCamInVehicleFlag())
|
|
{
|
|
Vec3V_ConstRef vCamPos = RCC_VEC3V(camInterface::GetGameplayDirector().GetFrame().GetPosition());
|
|
Vec3V_ConstRef vCamForward = RCC_VEC3V(camInterface::GetGameplayDirector().GetFrame().GetWorldMatrix().b);
|
|
float strength = dataVolume.GetPointStrength(vCamPos, vCamForward, true, true, true, true);
|
|
if (pFxInst->HasEvoFromHash(ATSTRINGHASH("smoke", 0xB19506B0)))
|
|
{
|
|
// if the parent effect has a smoke evo then use this instead (for interior smoke effects)
|
|
strength = pFxInst->GetEvoValueFromHash(ATSTRINGHASH("smoke", 0xB19506B0));
|
|
}
|
|
|
|
if (strength>0.0f)
|
|
{
|
|
if (dataVolumeType==PTFX_DATAVOLUMETYPE_WATER)
|
|
{
|
|
g_vfxLens.Register(VFXLENSTYPE_WATER, strength, 0.0f, 0.0f, 1.0f);
|
|
}
|
|
// else if (dataVolumeType==PTFX_DATAVOLUMETYPE_SAND)
|
|
// {
|
|
// g_vfxLens.Register(VFXLENSTYPE_SAND, strength, 0.0f, 0.0f, 1.0f);
|
|
// }
|
|
// else if (dataVolumeType==PTFX_DATAVOLUMETYPE_MUD)
|
|
// {
|
|
// g_vfxLens.Register(VFXLENSTYPE_MUD, strength, 0.0f, 0.0f, 1.0f);
|
|
// }
|
|
// else if (dataVolumeType==PTFX_DATAVOLUMETYPE_BLOOD)
|
|
// {
|
|
// g_vfxLens.Register(VFXLENSTYPE_BLOOD, strength, 0.0f, 0.0f, 1.0f);
|
|
// }
|
|
// else if (dataVolumeType==PTFX_DATAVOLUMETYPE_DUST)
|
|
// {
|
|
// g_vfxLens.Register(VFXLENSTYPE_DUST, strength, 0.0f, 0.0f, 1.0f);
|
|
// }
|
|
// else if (dataVolumeType==PTFX_DATAVOLUMETYPE_SMOKE)
|
|
// {
|
|
// g_vfxLens.Register(VFXLENSTYPE_SMOKE, strength, 0.0f, 0.0f, 1.0f);
|
|
// }
|
|
else if (dataVolumeType==PTFX_DATAVOLUMETYPE_BLAZE)
|
|
{
|
|
g_vfxLens.Register(VFXLENSTYPE_BLAZE, strength, 0.0f, 0.0f, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ProcessStoredData
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ProcessStoredData ()
|
|
{
|
|
// process the stored requests from the update thread now that it is finished
|
|
ProcessStoredLights();
|
|
|
|
if (!fwTimer::IsGamePaused())
|
|
{
|
|
ProcessStoredFires();
|
|
ProcessStoredDecals();
|
|
ProcessStoredDecalPools();
|
|
ProcessStoredDecalTrails();
|
|
ProcessStoredLiquids();
|
|
|
|
ProcessStoredGlassImpacts();
|
|
ProcessStoredGlassSmashes();
|
|
ProcessStoredGlassShatters();
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// BeginFrame
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::BeginFrame ()
|
|
{
|
|
if (g_ptxManager::IsInstantiated())
|
|
{
|
|
{ PF_AUTO_PUSH_TIMEBAR("WaitForUpdateToFinish");
|
|
WaitForUpdateToFinish();
|
|
}
|
|
|
|
#if __BANK
|
|
if (ptxDebug::sm_drawFxlistReferences)
|
|
{
|
|
ptfxManager::GetAssetStore().DebugDrawFxListReferences();
|
|
RMPTFXMGR.DebugDrawFxListReferences();
|
|
}
|
|
#endif
|
|
|
|
#if RMPTFX_THOROUGH_ERROR_CHECKING
|
|
RMPTFXMGR.VerifyLists();
|
|
#endif
|
|
{ PF_AUTO_PUSH_TIMEBAR("RMPTFXMGR.BeginFrame");
|
|
RMPTFXMGR.BeginFrame();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// WaitForUpdateToFinish
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::WaitForUpdateToFinish()
|
|
{
|
|
PF_FUNC(TimerExt_WaitForUpdateToFinish);
|
|
// wait for the update thread to finish - if started
|
|
if (m_startedUpdate)
|
|
{
|
|
sysIpcWaitSema(m_finishUpdateSema);
|
|
}
|
|
m_startedUpdate = false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// WaitForUpdateToFinish
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::WaitForCallbackEffectsToFinish()
|
|
{
|
|
// wait for the first part of the update thread to finish - if started
|
|
if (m_startedUpdate)
|
|
{
|
|
RMPTFXMGR.WaitForCallbackEffects();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StartLocalEffectsUpdate
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StartLocalEffectsUpdate()
|
|
{
|
|
if (m_startedUpdate)
|
|
{
|
|
RMPTFXMGR.StartLocalEffectsUpdate();
|
|
}
|
|
ptfxManager::GetAssetStore().SetIsSafeToRemoveFxLists(true);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// UpdateThread
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::UpdateThread (void* _that)
|
|
{
|
|
USE_MEMBUCKET(MEMBUCKET_FX);
|
|
|
|
if (PARAM_ptfxtimebar.Get())
|
|
{
|
|
PF_INIT_TIMEBARS("RMPTFX Update", 50, 3000);
|
|
}
|
|
|
|
CPtFxManager* that = (CPtFxManager*)_that;
|
|
|
|
sysThreadType::AddCurrentThreadType(SYS_THREAD_PARTICLES);
|
|
|
|
//
|
|
while (that->m_nextUpdateDelta != -999.0f)
|
|
{
|
|
// wait until we get a signal to start the update
|
|
sysIpcWaitSema(that->m_startUpdateSema);
|
|
|
|
if (PARAM_ptfxtimebar.Get())
|
|
{
|
|
PF_FRAMEINIT_TIMEBARS(gDrawListMgr->GetUpdateThreadFrameNumber());
|
|
PF_PUSH_TIMEBAR("ptfx update");
|
|
}
|
|
|
|
that->PreUpdateThread();
|
|
ptfxManager::UpdateThread(that->m_nextUpdateDelta, &(that->m_currViewport));
|
|
|
|
if (PARAM_ptfxtimebar.Get())
|
|
{
|
|
PF_POP_TIMEBAR();
|
|
}
|
|
|
|
// signal that the update is finished
|
|
sysIpcSignalSema(that->m_finishUpdateSema);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// PreUpdateThread
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void CPtFxManager::PreUpdateThread ()
|
|
{
|
|
//Reset the game side ptfx states for the frame
|
|
#if !__SPU
|
|
m_IsRefractPtfxPresent[RMPTFXTHREADER.GetUpdateBufferId()] = false;
|
|
#endif
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// UpdateDepthBuffer
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::UpdateDepthBuffer (grcRenderTarget* depthBuffer)
|
|
{
|
|
RMPTFXMGR.SetDepthBuffer(depthBuffer);
|
|
}
|
|
|
|
#if RSG_PC
|
|
void CPtFxManager::SetDepthWriteEnabled(bool depthWriteEnabled)
|
|
{
|
|
RMPTFXMGR.SetDepthWriteEnabled(depthWriteEnabled);
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitShadowedPtFxBuffer
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if USE_SHADED_PTFX_MAP
|
|
void CPtFxManager::InitShadowedPtFxBuffer ()
|
|
{
|
|
const int width = CASCADE_SHADOWS_RES_VS_X;
|
|
const int height = CASCADE_SHADOWS_RES_VS_Y;
|
|
|
|
// color
|
|
grcTextureFactory::CreateParams params;
|
|
params.Multisample = grcDevice::MSAA_None;
|
|
params.HasParent = false;
|
|
params.Parent = NULL;
|
|
params.UseFloat = false;
|
|
#if __PPU
|
|
params.InTiledMemory = true;
|
|
params.IsSwizzled = false;
|
|
#endif
|
|
params.UseHierZ = false;
|
|
params.IsSRGB = false;
|
|
|
|
RTMemPoolID memPoolID = RTMEMPOOL_NONE;
|
|
u8 rtPoolHeap = 0;
|
|
|
|
#if __PS3
|
|
memPoolID = PSN_RTMEMPOOL_P2560_TILED_LOCAL_COMPMODE_DISABLED;
|
|
rtPoolHeap = 13;
|
|
#elif __XENON
|
|
memPoolID = XENON_RTMEMPOOL_GENERAL1;
|
|
rtPoolHeap = kDepthQuarter;
|
|
#endif
|
|
|
|
const int bpp = 16;
|
|
params.Format = grctfG8R8;
|
|
|
|
#if RSG_PC
|
|
if (CSettingsManager::GetInstance().GetSettings().m_graphics.m_ShadowQuality == (CSettings::eSettingsLevel) (0))
|
|
{
|
|
m_shadedParticleBuffer = CRenderTargets::CreateRenderTarget(memPoolID, "Shadowed PTX Buffer", grcrtPermanent, 1, 1, bpp, ¶ms, rtPoolHeap);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
m_shadedParticleBuffer = CRenderTargets::CreateRenderTarget(memPoolID, "Shadowed PTX Buffer", grcrtPermanent, width, height, bpp, ¶ms, rtPoolHeap);
|
|
}
|
|
|
|
if(memPoolID != RTMEMPOOL_NONE)
|
|
{
|
|
m_shadedParticleBuffer->AllocateMemoryFromPool();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// UpdateShadowedPtFxBuffer
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void CPtFxManager::UpdateShadowedPtFxBuffer()
|
|
{
|
|
#if RSG_PC
|
|
if (CSettingsManager::GetInstance().GetSettings().m_graphics.m_ShadowQuality == (CSettings::eSettingsLevel) (0))
|
|
return;
|
|
#endif
|
|
|
|
#if __BANK
|
|
if (g_ptFxManager.m_gameDebug.GetDisableShadowedPtFx())
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
|
|
|
|
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
|
|
|
|
const float ptFxBlurKernelSize = BANK_SWITCH(g_ptFxManager.m_gameDebug.GetShadowedPtFxBlurKernelSize(), PTFX_SHADOWED_BLUR_KERNEL_SIZE_DEFAULT);
|
|
|
|
BankFloat ptFxShadedRange = (BANK_SWITCH(g_ptFxManager.m_gameDebug.GetShadowedPtFxRange(), PTFX_SHADOWED_RANGE_DEFAULT)).Getf();
|
|
|
|
// render the actual buffer
|
|
const grcTexture* shadowMap = PS3_SWITCH(CRenderPhaseCascadeShadowsInterface::GetShadowMapPatched(), CRenderPhaseCascadeShadowsInterface::GetShadowMap());
|
|
|
|
if (g_ptFxManager.m_shadedParticleBuffer && shadowMap && shadowMap->GetBitsPerPixel() == 32)
|
|
{
|
|
CRenderTargets::UnlockSceneRenderTargets();
|
|
{
|
|
CRenderPhaseCascadeShadowsInterface::SetSharedShaderVars();
|
|
|
|
grcTextureFactory::GetInstance().LockRenderTarget(0, g_ptFxManager.m_shadedParticleBuffer, NULL);
|
|
|
|
const CLightSource &dirLight = Lights::GetRenderDirectionalLight();
|
|
const bool bNoDirLight = ((dirLight.GetIntensity() == 0.0f) || !dirLight.IsCastShadows());
|
|
const tcKeyframeUncompressed& currKeyframe = g_timeCycle.GetCurrRenderKeyframe();
|
|
const float cloudShadowAmount = 1.0f - currKeyframe.GetVar(TCVAR_CLOUD_SHADOW_OPACITY); // "fade to" value based on clouds
|
|
static dev_float timeScale = 2.0f;
|
|
|
|
g_ptFxManager.SetPtfxShadedParams2(Vec4V((float)g_timeCycle.GetCurrCycleTime()*timeScale, cloudShadowAmount, 0.0f, 0.0f));
|
|
g_ptFxManager.m_shadedParticleBufferShader->SetVar(g_ptFxManager.m_shadedParticleBufferShaderParamsId, g_ptFxManager.GetPtfxShadedParams());
|
|
g_ptFxManager.m_shadedParticleBufferShader->SetVar(g_ptFxManager.m_shadedParticleBufferShaderParams2Id, g_ptFxManager.GetPtfxShadedParams2());
|
|
g_ptFxManager.m_shadedParticleBufferShader->SetVar(g_ptFxManager.m_shadedParticleBufferShaderShadowMapId, shadowMap);
|
|
|
|
g_ptFxManager.m_shadedParticleBufferShader->SetVar(g_ptFxManager.m_shadedParticleBufferShaderCloudSampler, CShaderLib::LookupTexture("cloudnoise"));
|
|
g_ptFxManager.m_shadedParticleBufferShader->SetVar(g_ptFxManager.m_shadedParticleBufferShaderSmoothStepSampler, CRenderPhaseCascadeShadowsInterface::GetSmoothStepTexture());
|
|
|
|
// if directional light is disabled, we actually are just going to clear this buffer to fully shadowed and then move on
|
|
if (bNoDirLight)
|
|
{
|
|
GRCDEVICE.Clear(true, Color32(0.0f, 0.0f, 0.0f, 0.0f), false, 1.0f, 0);
|
|
grcTextureFactory::GetInstance().UnlockRenderTarget(0);
|
|
}
|
|
else
|
|
{
|
|
grcStateBlock::SetStates(grcStateBlock::RS_NoBackfaceCull, grcStateBlock::DSS_IgnoreDepth, grcStateBlock::BS_Default);
|
|
|
|
if (AssertVerify(g_ptFxManager.m_shadedParticleBufferShader->BeginDraw(grmShader::RMC_DRAW, false, g_ptFxManager.m_shadedParticleBufferShaderTechnique)))
|
|
{
|
|
g_ptFxManager.m_shadedParticleBufferShader->Bind(0);
|
|
{
|
|
const Color32 color = Color32(0);
|
|
|
|
const float x0 = -1.0f;
|
|
const float y0 = +1.0f;
|
|
const float x1 = +1.0f;
|
|
const float y1 = -1.0f;
|
|
const float z = 0.0f;
|
|
|
|
grcBegin(drawTriStrip, 4);
|
|
grcVertex(x0, y0, z, 0.0f, 0.0f, -1.0f, color, -ptFxShadedRange, -ptFxShadedRange);
|
|
grcVertex(x0, y1, z, 0.0f, 0.0f, -1.0f, color, -ptFxShadedRange, ptFxShadedRange);
|
|
grcVertex(x1, y0, z, 0.0f, 0.0f, -1.0f, color, ptFxShadedRange, -ptFxShadedRange);
|
|
grcVertex(x1, y1, z, 0.0f, 0.0f, -1.0f, color, ptFxShadedRange, ptFxShadedRange);
|
|
grcEnd();
|
|
}
|
|
g_ptFxManager.m_shadedParticleBufferShader->UnBind();
|
|
g_ptFxManager.m_shadedParticleBufferShader->EndDraw();
|
|
}
|
|
|
|
grcResolveFlags resolveFlags;
|
|
|
|
resolveFlags.BlurResult = true;
|
|
resolveFlags.BlurKernelSize = ptFxBlurKernelSize;
|
|
grcTextureFactory::GetInstance().UnlockRenderTarget(0,&resolveFlags);
|
|
}
|
|
}
|
|
CRenderTargets::LockSceneRenderTargets();
|
|
}
|
|
}
|
|
#endif //USE_SHADED_PTFX_MAP
|
|
|
|
#if PTFX_APPLY_DOF_TO_PARTICLES
|
|
void CPtFxManager::ClearPtfxDepthAlphaMap()
|
|
{
|
|
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
|
|
const Color32 color(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
#if RSG_ORBIS
|
|
grcRenderTargetMSAA *const targetDepth = static_cast<grcRenderTargetMSAA*>(g_ptFxManager.GetPtfxDepthMap());
|
|
grcRenderTargetMSAA *const targetAlpha = static_cast<grcRenderTargetMSAA*>(g_ptFxManager.GetPtfxAlphaMap());
|
|
// no need to lock the targets, we only clear Cmask
|
|
if (GRCDEVICE.ClearCmask(1, targetDepth->GetColorTarget(), color) &&
|
|
GRCDEVICE.ClearCmask(2, targetAlpha->GetColorTarget(), color))
|
|
{
|
|
targetDepth->PushCmaskDirty();
|
|
targetAlpha->PushCmaskDirty();
|
|
}else
|
|
#endif //RSG_ORBIS
|
|
{
|
|
const grcRenderTarget* rendertargets[grcmrtColorCount] = {
|
|
g_ptFxManager.GetPtfxDepthMap(),
|
|
g_ptFxManager.GetPtfxAlphaMap(),
|
|
NULL,
|
|
NULL
|
|
};
|
|
grcTextureFactory::GetInstance().LockMRT(rendertargets, NULL);
|
|
GRCDEVICE.Clear(true, color, false, 0, false, 0);
|
|
grcTextureFactory::GetInstance().UnlockMRT();
|
|
}
|
|
}
|
|
|
|
void CPtFxManager::LockPtfxDepthAlphaMap(bool useReadOnlyDepth)
|
|
{
|
|
const grcRenderTarget* rendertargets[grcmrtColorCount] = {
|
|
CRenderTargets::GetBackBuffer(),
|
|
g_ptFxManager.GetPtfxDepthMap(),
|
|
g_ptFxManager.GetPtfxAlphaMap(),
|
|
NULL
|
|
};
|
|
grcRenderTarget *depthRT = CRenderTargets::GetDepthBuffer();
|
|
#if __D3D11
|
|
if(useReadOnlyDepth)
|
|
{
|
|
depthRT = (GRCDEVICE.IsReadOnlyDepthAllowed()) ? CRenderTargets::GetDepthBuffer_ReadOnly() : CRenderTargets::GetDepthBufferCopy();
|
|
}
|
|
#endif
|
|
|
|
grcTextureFactory::GetInstance().LockMRT(rendertargets, depthRT);
|
|
}
|
|
|
|
void CPtFxManager::UnLockPtfxDepthAlphaMap()
|
|
{
|
|
grcTextureFactory::GetInstance().UnlockMRT();
|
|
}
|
|
|
|
void CPtFxManager::FinalizePtfxDepthAlphaMap()
|
|
{
|
|
#if RSG_ORBIS
|
|
// forced finalize Cmask clear
|
|
GRCDEVICE.FinishRendering(
|
|
static_cast<const grcRenderTargetGNM*>(g_ptFxManager.GetPtfxAlphaMap())->GetColorTarget(),
|
|
true);
|
|
// this will do nothing, since s_CmaskDirty has been set/reset several times by now since Clear()
|
|
//static_cast<grcTextureFactoryGNM&>( grcTextureFactory::GetInstance() ).FinishRendering();
|
|
#endif //RSG_ORBIS
|
|
|
|
#if DEVICE_MSAA
|
|
if ((GRCDEVICE.GetMSAA() && RSG_PC) || GRCDEVICE.GetMSAA()>1)
|
|
{
|
|
g_ptFxManager.ResolvePtfxMaps();
|
|
}
|
|
#endif //DEVICE_MSAA
|
|
}
|
|
#endif //PTFX_APPLY_DOF_TO_PARTICLES
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Render
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::Render (grcRenderTarget* pFrameBuffer, grcRenderTarget* pFrameSampler, bool bRenderIntoRefraction)
|
|
{
|
|
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
|
|
|
|
bool bIsCameraUnderwater = Water::IsCameraUnderwater();
|
|
|
|
#if __BANK
|
|
if (ptfxDebug::GetDisablePtFx() || m_gameDebug.GetDisablePtFxRender())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(true);
|
|
}
|
|
#endif
|
|
|
|
if(DRAWLISTMGR->IsExecutingMirrorReflectionDrawList())
|
|
{
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION;
|
|
}
|
|
// Make sure we undo all viewport changes made below
|
|
GRC_VIEWPORT_AUTO_PUSH_POP();
|
|
|
|
SetGlobalConstants();
|
|
|
|
RMPTFXMGR.SetFrameBuffer(pFrameBuffer );
|
|
RMPTFXMGR.SetFrameBufferSampler(pFrameSampler );
|
|
|
|
// set gIsInterior constant
|
|
// CPortal::GetInteriorFxLevel() return value range is [0, 1], where:
|
|
// 0 - fully in interior
|
|
// 1 - fully outdoors
|
|
// we don't want to consider in-between values (gIsInterior is a bool)
|
|
const float cInteriorThreshold = 0.05f;
|
|
bool bIsInterior = (CPortal::GetInteriorFxLevel() < cInteriorThreshold);
|
|
CShaderLib::SetGlobalInInterior(bIsInterior);
|
|
|
|
// Setup directional globals explicitly (don't involve clouds here, it's done in ptfxshaderparams2
|
|
Lights::SetupDirectionalGlobals(1.0f);
|
|
|
|
CRenderPhaseCascadeShadowsInterface::SetDeferredLightingShaderVars();
|
|
CRenderPhaseCascadeShadowsInterface::SetSharedShaderVars();
|
|
CRenderPhaseReflection::SetReflectionMap();
|
|
|
|
// Setup fog values...
|
|
g_timeCycle.SetShaderData();
|
|
|
|
CShaderLib::SetGlobalEmissiveScale(g_timeCycle.GetCurrRenderKeyframe().GetVar(TCVAR_PTFX_EMISSIVE_INTENSITY_MULT),true);
|
|
|
|
Lights::BeginLightweightTechniques();
|
|
grcLightState::SetEnabled(true);
|
|
|
|
#if RMPTFX_BANK
|
|
// set shader overrides
|
|
if (ptxDebug::sm_bEnableShaderDebugOverrideAlpha)
|
|
{
|
|
ptxShaderTemplateOverride::BindDebugTechniqueOverride(PTXSHADERDEBUG_ALPHA);
|
|
}
|
|
RMPTFXMGR.SetQualityState(PTX_QUALITY_HIRES);
|
|
#endif
|
|
|
|
//If this is set then we're rendering the ptfx directly into the water refraction target.
|
|
//if not, then its the usual ptfx render
|
|
if(bRenderIntoRefraction)
|
|
{
|
|
RenderPtfxIntoWaterRefraction();
|
|
}
|
|
else
|
|
{
|
|
//if camera is underwater
|
|
RenderPtfxQualityLists(bIsCameraUnderwater);
|
|
RenderRefractionBucket();
|
|
}
|
|
|
|
#if RMPTFX_BANK
|
|
// set shader overrides
|
|
if (ptxDebug::sm_bEnableShaderDebugOverrideAlpha)
|
|
{
|
|
ptxShaderTemplateOverride::UnbindDebugTechniqueOverride();
|
|
}
|
|
#endif
|
|
|
|
Lights::EndLightweightTechniques();
|
|
|
|
CShaderLib::SetGlobalEmissiveScale(1.0f);
|
|
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_NORMAL;
|
|
#if __BANK
|
|
if (m_gameDebug.GetRenderDebugCollisionPolys())
|
|
{
|
|
m_ptFxCollision.RenderDebug();
|
|
}
|
|
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(false);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RenderRefractionBucket
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RenderRefractionBucket()
|
|
{
|
|
|
|
if(m_IsRefractPtfxPresent[RMPTFXTHREADER.GetDrawBufferId()] BANK_ONLY( && !m_gameDebug.GetDisableRefractionBucket()))
|
|
{
|
|
if(g_PtfxBuckerRenderStage != PTFX_BUCKET_RENDER_STAGE_SHADOWS)
|
|
{
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_REFRACTION;
|
|
}
|
|
// We need to copy the backbuffer or refraction particles are drawn incorrectly over normal particles, if we're PC or using MSAA
|
|
// we can use the backbuffer itself if we're on consoles and not using MSAA
|
|
const bool bUseCopy = (GRCDEVICE.GetMSAA()>1) || RSG_PC;
|
|
RMPTFXMGR.SetFrameBufferSampler(bUseCopy?CRenderTargets::GetBackBufferCopy(true):CRenderTargets::GetBackBuffer());
|
|
|
|
PF_PUSH_MARKER("Refraction Ptfx Draw List");
|
|
const unsigned rti = g_RenderThreadIndex;
|
|
m_isRenderingRefractionPtfxList[rti] = true;
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
m_isRenderingRefractionPtfxList[rti] = false;
|
|
PF_POP_MARKER();
|
|
|
|
if(g_PtfxBuckerRenderStage != PTFX_BUCKET_RENDER_STAGE_SHADOWS)
|
|
{
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_NORMAL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RenderPtfxIntoWaterRefraction
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RenderPtfxIntoWaterRefraction ()
|
|
{
|
|
|
|
// draw the underwater ptfx list
|
|
PF_PUSH_MARKER("Water Refraction Ptfx");
|
|
bool bIsCameraUnderwater = Water::IsCameraUnderwater();
|
|
if(PTFX_WATER_REFRACTION_ENABLED BANK_ONLY(&& !m_gameDebug.GetDisableWaterRefractionBucket()))
|
|
{
|
|
ptxShaderTemplate* pointShader = ptxShaderTemplateList::GetShader("ptfx_sprite");
|
|
ptxShaderTemplate* trailShader = ptxShaderTemplateList::GetShader("ptfx_trail");
|
|
|
|
const unsigned rti = g_RenderThreadIndex;
|
|
|
|
g_PtfxWaterSurfaceRenderState = (u8)(bIsCameraUnderwater?PTFX_WATERSURFACE_RENDER_ABOVE_WATER:PTFX_WATERSURFACE_RENDER_BELOW_WATER);
|
|
m_isRenderingIntoWaterRefraction[rti] = true;
|
|
pointShader->SetShaderOverride(&m_ptxSpriteWaterRefractionShaderOverride);
|
|
trailShader->SetShaderOverride(&m_ptxTrailWaterRefractionShaderOverride);
|
|
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
|
|
pointShader->SetShaderOverride(NULL);
|
|
trailShader->SetShaderOverride(NULL);
|
|
m_isRenderingIntoWaterRefraction[rti] = false;
|
|
g_PtfxWaterSurfaceRenderState = PTFX_WATERSURFACE_RENDER_BOTH;
|
|
}
|
|
PF_POP_MARKER();
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RenderPtfxQualityLists
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RenderPtfxQualityLists(bool bIsCameraUnderwater)
|
|
{
|
|
u8 renderFlags = m_ptfxRenderFlags[gRenderThreadInterface.GetRenderBuffer()];
|
|
|
|
//Render underwater ptfx along with normal ptfx if camera is underwater as it does
|
|
//not need underwater fog processing
|
|
|
|
g_PtfxWaterSurfaceRenderState = (u8)(bIsCameraUnderwater?PTFX_WATERSURFACE_RENDER_BELOW_WATER:PTFX_WATERSURFACE_RENDER_ABOVE_WATER);
|
|
|
|
// draw the particle lists
|
|
// high quality bucket
|
|
PF_PUSH_MARKER("High Res Ptfx Draw List");
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableHighBucket() &&
|
|
!m_gameDebug.GetRenderAllDownsampled()) // we only ever downsample hi-res if RAG toggle is forcing us to
|
|
#endif
|
|
{
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
}
|
|
PF_POP_MARKER();
|
|
|
|
// draw the particle lists
|
|
// medium quality bucket
|
|
PF_PUSH_MARKER("Medium Res Ptfx Draw List");
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableMediumBucket())
|
|
#endif
|
|
{
|
|
if (!(renderFlags & PTFX_RF_MEDRES_DOWNSAMPLE))
|
|
{
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
g_ptfxGPUTimerMedRes.Start();
|
|
#endif
|
|
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
m_gpuTimeMedRes[gRenderThreadInterface.GetRenderBuffer()] = g_ptfxGPUTimerMedRes.Stop();
|
|
#endif
|
|
}
|
|
}
|
|
PF_POP_MARKER();
|
|
|
|
PF_PUSH_MARKER("Low Res Ptfx Draw List");
|
|
// setup the downsampling
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
const bool shouldDownsample = ShouldDownsample();
|
|
if (shouldDownsample)
|
|
{
|
|
#if RMPTFX_BANK
|
|
RMPTFXMGR.SetQualityState(PTX_QUALITY_LOWRES);
|
|
#endif
|
|
InitDownsampleRender(CRenderTargets::GetBackBuffer());
|
|
}
|
|
#endif
|
|
|
|
// high quality bucket (debug downsampled)
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableHighBucket() &&
|
|
m_gameDebug.GetRenderAllDownsampled()) // we only ever downsample hi-res if RAG toggle is forcing us to
|
|
#endif
|
|
{
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
}
|
|
|
|
// medium quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableMediumBucket())
|
|
#endif
|
|
{
|
|
if (renderFlags & PTFX_RF_MEDRES_DOWNSAMPLE)
|
|
{
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
g_ptfxGPUTimerMedRes.Start();
|
|
#endif
|
|
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
m_gpuTimeMedRes[gRenderThreadInterface.GetRenderBuffer()] = g_ptfxGPUTimerMedRes.Stop();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// low quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableLowBucket())
|
|
#endif
|
|
{
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
g_ptfxGPUTimerLowRes.Start();
|
|
#endif
|
|
|
|
// this works assuming the downsampled target is 8bit
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
|
|
#if PTFX_DYNAMIC_QUALITY
|
|
m_gpuTimeLowRes[gRenderThreadInterface.GetRenderBuffer()] = g_ptfxGPUTimerLowRes.Stop();
|
|
#endif
|
|
}
|
|
|
|
// reset the downsampling
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
if (shouldDownsample)
|
|
{
|
|
ShutdownDownsampleRender();
|
|
}
|
|
#endif
|
|
|
|
g_PtfxWaterSurfaceRenderState = PTFX_WATERSURFACE_RENDER_BOTH;
|
|
|
|
PF_POP_MARKER();
|
|
|
|
#if RMPTFX_BANK
|
|
RMPTFXMGR.SetQualityState(PTX_QUALITY_HIRES);
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RenderSimple
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RenderSimple (grcRenderTarget* pBackBuffer, grcRenderTarget* pDepthBuffer)
|
|
{
|
|
|
|
PF_PUSH_MARKER("Ptfx Simple Render");
|
|
#if __BANK
|
|
if (ptfxDebug::GetDisablePtFx() || m_gameDebug.GetDisablePtFxRender())
|
|
{
|
|
return;
|
|
}
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(true);
|
|
}
|
|
#endif
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_SIMPLE;
|
|
#if __PPU
|
|
RMPTFXMGR.SetPatchedDepthBuffer(CRenderTargets::GetDepthBufferAsColor());
|
|
#endif
|
|
|
|
RMPTFXMGR.SetDepthBuffer(pDepthBuffer);
|
|
|
|
RMPTFXMGR.SetFrameBuffer(pBackBuffer);
|
|
RMPTFXMGR.SetFrameBufferSampler(pBackBuffer);
|
|
|
|
// setup fog values
|
|
grcLightState::SetEnabled(true);
|
|
|
|
// draw the particle lists
|
|
// high quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableHighBucket())
|
|
#endif
|
|
{
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
}
|
|
|
|
// medium quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableMediumBucket())
|
|
#endif
|
|
{
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
}
|
|
|
|
// low quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableLowBucket())
|
|
#endif
|
|
{
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
}
|
|
|
|
#if __BANK
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(false);
|
|
}
|
|
#endif
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_NORMAL;
|
|
|
|
PF_POP_MARKER();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RenderManual
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RenderManual (ptxEffectInst* pFxInst BANK_ONLY(, bool debugRender))
|
|
{
|
|
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
|
|
|
|
if(DRAWLISTMGR->IsExecutingMirrorReflectionDrawList())
|
|
{
|
|
if(CRenderPhaseMirrorReflectionInterface::GetIsMirrorUsingWaterReflectionTechnique_Render())
|
|
{
|
|
//Sometimes water reflections end up using mirror refletion renderphase
|
|
//Disabling water refletions render in this case.
|
|
return;
|
|
}
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION;
|
|
}
|
|
|
|
#if RSG_BANK && (RSG_ORBIS || RSG_DURANGO)
|
|
(void)debugRender;
|
|
#endif
|
|
|
|
#if __BANK
|
|
if (ptfxDebug::GetDisablePtFx() || m_gameDebug.GetDisablePtFxRender())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(true);
|
|
}
|
|
#endif
|
|
|
|
#if RMPTFX_BANK
|
|
// set shader overrides
|
|
if (ptxDebug::sm_bEnableShaderDebugOverrideAlpha)
|
|
{
|
|
ptxShaderTemplateOverride::BindDebugTechniqueOverride(PTXSHADERDEBUG_ALPHA);
|
|
}
|
|
#endif
|
|
|
|
#if RSG_PC
|
|
// On PC we need the readonly depth bound here, as the driver imposes same read/write target validation.
|
|
// For consoles we will be sampling from the same buffer that we write into. It's alright because particles don't write depth
|
|
// and it's valid on consoles
|
|
|
|
// We render these particles in two passes. In the normal pass we need to use MSAA depth buffer if applicable.
|
|
// In the debug pass we need to set a non-msaa depth so base this on what's currently at the top of the stack.
|
|
|
|
bool bUnlockDepthRO = true;
|
|
#if __BANK
|
|
if (!(debugRender && GRCDEVICE.GetMSAA()))
|
|
#endif
|
|
{
|
|
grcViewport* pViewport = grcViewport::GetCurrent();
|
|
Assert(pViewport);
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION && CRenderPhaseMirrorReflectionInterface::GetIsMirrorUsingWaterSurface_Render())
|
|
{
|
|
CRenderTargets::LockReadOnlyDepth(false, true, CMirrors::GetMirrorWaterDepthTarget(), CMirrors::GetMirrorWaterDepthTarget_Copy(), CMirrors::GetMirrorWaterDepthTarget_ReadOnly());
|
|
}
|
|
else
|
|
if (g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION)
|
|
{
|
|
CRenderTargets::LockReadOnlyDepth(false, true, CMirrors::GetMirrorDepthTarget(), CMirrors::GetMirrorDepthTarget_Copy(), CMirrors::GetMirrorDepthTarget_ReadOnly());
|
|
}
|
|
else
|
|
{
|
|
if(RMPTFXMGR.GetDepthWriteEnabled())
|
|
bUnlockDepthRO = false;
|
|
else
|
|
CRenderTargets::LockReadOnlyDepth(false, true, CRenderTargets::GetDepthBuffer(), CRenderTargets::GetDepthBufferCopy(), CRenderTargets::GetDepthBuffer_ReadOnly());
|
|
|
|
}
|
|
|
|
//resetting the window here as the LockReadOnlyDepth call resets the window to the default full screen. But mirrors can possible setup their own window size.
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION && pViewport)
|
|
{
|
|
GRCDEVICE.SetWindow(pViewport->GetWindow());
|
|
}
|
|
}
|
|
#endif // RSG_PC
|
|
|
|
// we flush-and-invalidate on orbis to avoid read-write hazard between alpha objects writing depth and particles doing depth blending
|
|
ORBIS_ONLY( gfxc.triggerEvent(sce::Gnm::kEventTypeDbCacheFlushAndInvalidate) );
|
|
|
|
bool bIsCameraUnderwater = Water::IsCameraUnderwater();
|
|
g_PtfxWaterSurfaceRenderState = (u8)(bIsCameraUnderwater?PTFX_WATERSURFACE_RENDER_BELOW_WATER:PTFX_WATERSURFACE_RENDER_ABOVE_WATER);
|
|
|
|
RMPTFXMGR.DrawManual(pFxInst PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
|
|
g_PtfxWaterSurfaceRenderState = PTFX_WATERSURFACE_RENDER_BOTH;
|
|
|
|
#if RSG_PC
|
|
#if __BANK
|
|
if (!(debugRender && GRCDEVICE.GetMSAA()))
|
|
#endif // __BANK
|
|
{
|
|
if(bUnlockDepthRO)
|
|
CRenderTargets::UnlockReadOnlyDepth();
|
|
|
|
grcViewport* pViewport = grcViewport::GetCurrent();
|
|
Assert(pViewport);
|
|
|
|
//resetting the window here as the UnlockReadOnlyDepth call resets the window to the default full screen. But mirrors can possible setup their own window size.
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION && pViewport)
|
|
{
|
|
GRCDEVICE.SetWindow(pViewport->GetWindow());
|
|
}
|
|
}
|
|
#endif //RSG_PC
|
|
|
|
#if RMPTFX_BANK
|
|
// set shader overrides
|
|
if (ptxDebug::sm_bEnableShaderDebugOverrideAlpha)
|
|
{
|
|
ptxShaderTemplateOverride::UnbindDebugTechniqueOverride();
|
|
}
|
|
#endif
|
|
|
|
#if __BANK
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(false);
|
|
}
|
|
#endif
|
|
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_NORMAL;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SetGlobalConstants
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void CPtFxManager::SetGlobalConstants()
|
|
{
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS
|
|
float globalshadowSlopeBias = m_GlobalShadowSlopeBias;
|
|
float globalshadowBiasRange = m_GlobalShadowBiasRange;
|
|
float globalshadowBiasRangeFalloff = m_GlobalshadowBiasRangeFalloff;
|
|
|
|
#if RMPTFX_BANK
|
|
if(m_gameDebug.IsParticleShadowIntensityOverridden())
|
|
{
|
|
RMPTFXMGR.SetGlobalShadowIntensity(m_gameDebug.GetGlobalParticleShadowIntensity());
|
|
}
|
|
|
|
globalshadowSlopeBias = m_gameDebug.GetGlobalParticleShadowSlopeBias();
|
|
globalshadowBiasRange = m_gameDebug.GetGlobalParticleShadowBiasRange();
|
|
globalshadowBiasRangeFalloff = m_gameDebug.GetGlobalParticleShadowBiasRangeFalloff();
|
|
#endif
|
|
|
|
float globalshadowBiasRangeDivision = 1.0f / (globalshadowBiasRange + globalshadowBiasRangeFalloff);
|
|
Vec4V shadowBiasVec(globalshadowSlopeBias, globalshadowBiasRange, globalshadowBiasRangeFalloff, globalshadowBiasRangeDivision);
|
|
grcEffect::SetGlobalVar( m_GlobalShadowBiasVar, shadowBiasVec);
|
|
#endif
|
|
|
|
#if PTFX_APPLY_DOF_TO_PARTICLES
|
|
if (CPtFxManager::UseParticleDOF())
|
|
{
|
|
float ParticleDofAlphaScale = m_globalParticleDofAlphaScale;
|
|
#if __BANK
|
|
ParticleDofAlphaScale = m_gameDebug.GetGlobalParticleDofAlphaScale();
|
|
#endif
|
|
grcEffect::SetGlobalVar( m_GlobalParticleDofAlphaScaleVar, ParticleDofAlphaScale);
|
|
}
|
|
#endif //PTFX_APPLY_DOF_TO_PARTICLES
|
|
}
|
|
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RenderShadows
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RenderShadows(s32 UNUSED_PARAM(cascadeIndex))
|
|
{
|
|
#if RSG_PC
|
|
if (CSettingsManager::GetInstance().GetSettings().m_graphics.m_ShadowQuality == (CSettings::eSettingsLevel) (0))
|
|
return;
|
|
#endif
|
|
|
|
#if __BANK
|
|
if (ptfxDebug::GetDisablePtFx() || m_gameDebug.GetDisablePtFxRender())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(true);
|
|
}
|
|
#endif
|
|
|
|
SetGlobalConstants();
|
|
vfxAssertf(DRAWLISTMGR->IsExecutingCascadeShadowDrawList(), "Should be called from cascade shadows only");
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_SHADOWS;
|
|
|
|
ptxShaderTemplate* spriteShader = ptxShaderTemplateList::GetShader("ptfx_sprite");
|
|
ptxShaderTemplate* trailShader = ptxShaderTemplateList::GetShader("ptfx_trail");
|
|
|
|
spriteShader->SetRenderPass( ptxShaderTemplate::PTX_SHADOW_PASS );
|
|
trailShader->SetRenderPass( ptxShaderTemplate::PTX_SHADOW_PASS );
|
|
|
|
RMPTFXMGR.SetShadowPass(true);
|
|
RMPTFXMGR.SetShadowSourcePos(g_sky.GetSunPosition());
|
|
|
|
grcRenderTarget* prevDepthBuffer = RMPTFXMGR.GetDepthBuffer();
|
|
RMPTFXMGR.SetDepthBuffer( CRenderPhaseCascadeShadowsInterface::GetShadowMap() );
|
|
|
|
// set gIsInterior constant
|
|
// CPortal::GetInteriorFxLevel() return value range is [0, 1], where:
|
|
// 0 - fully in interior
|
|
// 1 - fully outdoors
|
|
// we don't want to consider in-between values (gIsInterior is a bool)
|
|
const float cInteriorThreshold = 0.05f;
|
|
bool bIsInterior = (CPortal::GetInteriorFxLevel() < cInteriorThreshold);
|
|
CShaderLib::SetGlobalInInterior(bIsInterior);
|
|
|
|
// Setup fog values...
|
|
g_timeCycle.SetShaderData();
|
|
|
|
CShaderLib::SetGlobalEmissiveScale(g_timeCycle.GetCurrRenderKeyframe().GetVar(TCVAR_PTFX_EMISSIVE_INTENSITY_MULT),true);
|
|
|
|
bool restoreLightState = grcLightState::IsEnabled();
|
|
grcLightState::SetEnabled(false);
|
|
|
|
u8 renderFlags = m_ptfxRenderFlags[gRenderThreadInterface.GetRenderBuffer()];
|
|
|
|
// draw the particle lists
|
|
// high quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableHighBucket())
|
|
#endif
|
|
{
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
}
|
|
|
|
// medium quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableMediumBucket())
|
|
#endif
|
|
{
|
|
if (renderFlags & PTFX_RF_MEDRES_DOWNSAMPLE)
|
|
{
|
|
#if __PS3
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#elif __XENON
|
|
PostFX::SetLDR8bitHDR8bit();
|
|
#else
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#endif
|
|
}
|
|
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
}
|
|
|
|
// low quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableLowBucket())
|
|
#endif
|
|
{
|
|
#if __PS3
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#elif __XENON
|
|
PostFX::SetLDR8bitHDR8bit();
|
|
#else
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#endif
|
|
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW] PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL) RMPTFX_BANK_ONLY(, false));
|
|
}
|
|
|
|
PIXBegin(0, "ManualParticleShadows");
|
|
|
|
// pfff.... sorted particle fx are specialz, so we need to store and restore those.
|
|
grcBlendStateHandle BS_Backup = grcStateBlock::BS_Active;
|
|
grcDepthStencilStateHandle DSS_Backup = grcStateBlock::DSS_Active;
|
|
grcRasterizerStateHandle RS_Backup = grcStateBlock::RS_Active;
|
|
u8 ActiveStencilRef_Backup = grcStateBlock::ActiveStencilRef;
|
|
u32 ActiveBlendFactors_Backup = grcStateBlock::ActiveBlendFactors;
|
|
u64 ActiveSampleMask_Backup = grcStateBlock::ActiveSampleMask;
|
|
|
|
u8 drawBufferId = RMPTFXTHREADER.GetDrawBufferId();
|
|
for( u32 i = 0; i < NUM_PTFX_DRAWLISTS; i++)
|
|
{
|
|
ptxList* pList = &m_pDrawList[i]->m_effectInstList;
|
|
ptxEffectInstItem* pCurrEffectInstItem = (ptxEffectInstItem*)pList->GetHeadMT(drawBufferId);
|
|
|
|
while (pCurrEffectInstItem)
|
|
{
|
|
ptxEffectInst* pEffectInst = pCurrEffectInstItem->GetDataSB();
|
|
|
|
if( pEffectInst->GetFlag(PTXEFFECTFLAG_MANUAL_DRAW))
|
|
{
|
|
if( pEffectInst->GetIsFinished() )
|
|
{
|
|
pCurrEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNextMT(drawBufferId);
|
|
continue;
|
|
}
|
|
RMPTFXMGR.DrawManual(pEffectInst PTXDRAW_MULTIPLE_DRAWS_PER_BATCH_ONLY_PARAM(NULL));
|
|
}
|
|
pCurrEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNextMT(drawBufferId);
|
|
}
|
|
}
|
|
|
|
grcStateBlock::SetDepthStencilState(DSS_Backup,ActiveStencilRef_Backup);
|
|
grcStateBlock::SetRasterizerState(RS_Backup);
|
|
grcStateBlock::SetBlendState(BS_Backup,ActiveBlendFactors_Backup,ActiveSampleMask_Backup);
|
|
|
|
CShaderLib::SetGlobalEmissiveScale(1.0f);
|
|
PIXEnd();
|
|
|
|
grcLightState::SetEnabled(restoreLightState);
|
|
|
|
RMPTFXMGR.SetDepthBuffer( prevDepthBuffer );
|
|
RMPTFXMGR.SetShadowPass(false);
|
|
|
|
spriteShader->SetRenderPass( ptxShaderTemplate::PTX_DEFAULT_PASS );
|
|
trailShader->SetRenderPass( ptxShaderTemplate::PTX_DEFAULT_PASS );
|
|
|
|
#if __BANK
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(false);
|
|
}
|
|
#endif
|
|
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_NORMAL;
|
|
}
|
|
|
|
#if PTXDRAW_MULTIPLE_DRAWS_PER_BATCH
|
|
|
|
void CPtFxManager::RenderShadowsAllCascades(s32 noOfCascades, grcViewport *pViewports, Vec4V *pWindows, bool bUseInstancedShadow)
|
|
{
|
|
#if RSG_PC
|
|
if (CSettingsManager::GetInstance().GetSettings().m_graphics.m_ShadowQuality == (CSettings::eSettingsLevel) (0))
|
|
return;
|
|
#endif
|
|
|
|
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
|
|
|
|
#if __BANK
|
|
if (ptfxDebug::GetDisablePtFx() || m_gameDebug.GetDisablePtFxRender())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(true);
|
|
}
|
|
#endif
|
|
|
|
const unsigned rti = g_RenderThreadIndex;
|
|
SetGlobalConstants();
|
|
|
|
vfxAssertf(DRAWLISTMGR->IsExecutingCascadeShadowDrawList(), "Should be called from cascade shadows only");
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_SHADOWS;
|
|
Assertf(CRenderPhaseCascadeShadowsInterface::GetParticleShadowAccessFlagThreadID() == -1, "Some other thread has already accessed the Particle shadow render flag. Expect incorrect results. Access Flag Thread ID = %d, Current Flag Thread ID = %u", CRenderPhaseCascadeShadowsInterface::GetParticleShadowAccessFlagThreadID(), rti);
|
|
ASSERT_ONLY(CRenderPhaseCascadeShadowsInterface::SetParticleShadowSetFlagThreadID((int)rti));
|
|
SetParticleShadowsRenderedFlag(false);
|
|
|
|
CPtfxShadowDrawToAllCascades toAllCascades(noOfCascades, pViewports, pWindows, bUseInstancedShadow);
|
|
m_pMulitDrawInterface[rti] = &toAllCascades;
|
|
|
|
ptxShaderTemplate* spriteShader = ptxShaderTemplateList::GetShader("ptfx_sprite");
|
|
Assert(spriteShader != NULL);
|
|
ptxShaderTemplate* trailShader = ptxShaderTemplateList::GetShader("ptfx_trail");
|
|
Assert(trailShader != NULL);
|
|
|
|
#if GS_INSTANCED_SHADOWS
|
|
bool bUseGeomShader = false;
|
|
#if PTFX_SHADOWS_INSTANCING_GEOMSHADER
|
|
bUseGeomShader = bUseInstancedShadow;
|
|
#endif
|
|
spriteShader->SetRenderPass( bUseGeomShader ? ptxShaderTemplate::PTX_INSTSHADOW_PASS : ptxShaderTemplate::PTX_SHADOW_PASS );
|
|
trailShader->SetRenderPass( bUseGeomShader ? ptxShaderTemplate::PTX_INSTSHADOW_PASS : ptxShaderTemplate::PTX_SHADOW_PASS );
|
|
|
|
// note that instanced drawmode tech set from SetInstancedDrawMode won't affect particle sprite/trail
|
|
// since it has its own shader Begin mechanic. This affects ptfx_model.
|
|
if (bUseInstancedShadow)
|
|
{
|
|
grcViewport::SetInstVPWindow(CASCADE_SHADOWS_COUNT);
|
|
grmShader::SetInstancedDrawMode(true);
|
|
grcViewport::SetInstancing(true);
|
|
}
|
|
#else
|
|
spriteShader->SetRenderPass( ptxShaderTemplate::PTX_SHADOW_PASS );
|
|
trailShader->SetRenderPass( ptxShaderTemplate::PTX_SHADOW_PASS );
|
|
#endif
|
|
|
|
RMPTFXMGR.SetShadowPass(true);
|
|
RMPTFXMGR.SetShadowSourcePos(g_sky.GetSunPosition());
|
|
|
|
grcRenderTarget* prevDepthBuffer = RMPTFXMGR.GetDepthBuffer();
|
|
RMPTFXMGR.SetDepthBuffer( CRenderPhaseCascadeShadowsInterface::GetShadowMap() );
|
|
|
|
// set gIsInterior constant
|
|
// CPortal::GetInteriorFxLevel() return value range is [0, 1], where:
|
|
// 0 - fully in interior
|
|
// 1 - fully outdoors
|
|
// we don't want to consider in-between values (gIsInterior is a bool)
|
|
const float cInteriorThreshold = 0.05f;
|
|
bool bIsInterior = (CPortal::GetInteriorFxLevel() < cInteriorThreshold);
|
|
CShaderLib::SetGlobalInInterior(bIsInterior);
|
|
|
|
// Setup fog values...
|
|
g_timeCycle.SetShaderData();
|
|
|
|
CShaderLib::SetGlobalEmissiveScale(g_timeCycle.GetCurrRenderKeyframe().GetVar(TCVAR_PTFX_EMISSIVE_INTENSITY_MULT),true);
|
|
|
|
bool restoreLightState = grcLightState::IsEnabled();
|
|
grcLightState::SetEnabled(false);
|
|
|
|
u8 renderFlags = m_ptfxRenderFlags[gRenderThreadInterface.GetRenderBuffer()];
|
|
|
|
// draw the particle lists
|
|
// high quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableHighBucket())
|
|
#endif
|
|
{
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH], &toAllCascades RMPTFX_BANK_ONLY(, false));
|
|
}
|
|
|
|
// medium quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableMediumBucket())
|
|
#endif
|
|
{
|
|
if (renderFlags & PTFX_RF_MEDRES_DOWNSAMPLE)
|
|
{
|
|
#if __PS3
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#elif __XENON
|
|
PostFX::SetLDR8bitHDR8bit();
|
|
#else
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#endif
|
|
}
|
|
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_MEDIUM], &toAllCascades RMPTFX_BANK_ONLY(, false));
|
|
}
|
|
|
|
// low quality bucket
|
|
#if __BANK
|
|
if (!m_gameDebug.GetDisableLowBucket())
|
|
#endif
|
|
{
|
|
// this works assuming the downsampled target is 8bit
|
|
#if __PS3
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#elif __XENON
|
|
PostFX::SetLDR8bitHDR8bit();
|
|
#else
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#endif
|
|
RMPTFXMGR.Draw(m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW], &toAllCascades RMPTFX_BANK_ONLY(, false));
|
|
}
|
|
|
|
PIXBegin(0, "ManualParticleShadows");
|
|
|
|
// pfff.... sorted particle fx are specialz, so we need to store and restore those.
|
|
grcBlendStateHandle BS_Backup = grcStateBlock::BS_Active;
|
|
grcDepthStencilStateHandle DSS_Backup = grcStateBlock::DSS_Active;
|
|
grcRasterizerStateHandle RS_Backup = grcStateBlock::RS_Active;
|
|
u8 ActiveStencilRef_Backup = grcStateBlock::ActiveStencilRef;
|
|
u32 ActiveBlendFactors_Backup = grcStateBlock::ActiveBlendFactors;
|
|
u64 ActiveSampleMask_Backup = grcStateBlock::ActiveSampleMask;
|
|
|
|
u8 drawBufferId = RMPTFXTHREADER.GetDrawBufferId();
|
|
for( u32 i = 0; i < NUM_PTFX_DRAWLISTS; i++)
|
|
{
|
|
ptxList* pList = &m_pDrawList[i]->m_effectInstList;
|
|
ptxEffectInstItem* pCurrEffectInstItem = (ptxEffectInstItem*)pList->GetHeadMT(drawBufferId);
|
|
|
|
while (pCurrEffectInstItem)
|
|
{
|
|
ptxEffectInst* pEffectInst = pCurrEffectInstItem->GetDataSB();
|
|
|
|
if( pEffectInst->GetFlag(PTXEFFECTFLAG_MANUAL_DRAW))
|
|
{
|
|
if( pEffectInst->GetIsFinished() )
|
|
{
|
|
pCurrEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNextMT(drawBufferId);
|
|
continue;
|
|
}
|
|
RMPTFXMGR.DrawManual(pEffectInst, &toAllCascades);
|
|
}
|
|
pCurrEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNextMT(drawBufferId);
|
|
}
|
|
}
|
|
|
|
grcStateBlock::SetDepthStencilState(DSS_Backup,ActiveStencilRef_Backup);
|
|
grcStateBlock::SetRasterizerState(RS_Backup);
|
|
grcStateBlock::SetBlendState(BS_Backup,ActiveBlendFactors_Backup,ActiveSampleMask_Backup);
|
|
|
|
CShaderLib::SetGlobalEmissiveScale(1.0f);
|
|
PIXEnd();
|
|
|
|
grcLightState::SetEnabled(restoreLightState);
|
|
|
|
RMPTFXMGR.SetDepthBuffer( prevDepthBuffer );
|
|
RMPTFXMGR.SetShadowPass(false);
|
|
|
|
#if GS_INSTANCED_SHADOWS
|
|
if (bUseInstancedShadow)
|
|
{
|
|
grmShader::SetInstancedDrawMode(false);
|
|
grcViewport::SetInstancing(false);
|
|
}
|
|
#endif
|
|
|
|
spriteShader->SetRenderPass( ptxShaderTemplate::PTX_DEFAULT_PASS );
|
|
trailShader->SetRenderPass( ptxShaderTemplate::PTX_DEFAULT_PASS );
|
|
|
|
#if __BANK
|
|
if(m_gameDebug.GetDrawWireframe())
|
|
{
|
|
grcStateBlock::SetWireframeOverride(false);
|
|
}
|
|
#endif
|
|
g_PtfxBuckerRenderStage = PTFX_BUCKET_RENDER_STAGE_NORMAL;
|
|
|
|
m_pMulitDrawInterface[rti] = NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
void CPtFxManager::ResetParticleShadowsRenderedFlag()
|
|
{
|
|
g_ptFxManager.SetParticleShadowsRenderedFlag(false);
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitWidgets
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if __BANK
|
|
void CPtFxManager::InitWidgets ()
|
|
{
|
|
ptfxManager::InitWidgets();
|
|
m_gameDebug.InitWidgets();
|
|
}
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ShouldDownsample
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
bool CPtFxManager::ShouldDownsample()
|
|
{
|
|
#if __BANK
|
|
if (m_gameDebug.GetDisableDownsampling())
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (m_disableDownsamplingThisFrame)
|
|
{
|
|
m_disableDownsamplingThisFrame = false;
|
|
return false;
|
|
}
|
|
|
|
u8 renderFlags = m_ptfxRenderFlags[gRenderThreadInterface.GetRenderBuffer()];
|
|
|
|
if (renderFlags & PTFX_RF_MEDRES_DOWNSAMPLE &&
|
|
m_pDrawList[PTFX_DRAWLIST_QUALITY_HIGH]->m_isAnyActivePoints[RMPTFXTHREADER.GetDrawBufferId()])
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (renderFlags & PTFX_RF_LORES_DOWNSAMPLE &&
|
|
m_pDrawList[PTFX_DRAWLIST_QUALITY_LOW]->m_isAnyActivePoints[RMPTFXTHREADER.GetDrawBufferId()])
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// InitDownsampleRender
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
void CPtFxManager::InitDownsampleRender (grcRenderTarget* pColourSampler)
|
|
{
|
|
CRenderTargets::UnlockSceneRenderTargets();
|
|
|
|
#if __PS3
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#elif __XENON
|
|
PostFX::SetLDR8bitHDR8bit();
|
|
#else
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#endif
|
|
|
|
grcRenderTarget* smallColorRenderTarget = m_pLowResPtfxBuffer;
|
|
#if __XENON
|
|
m_useDownrezzedPtfx = true;
|
|
#endif
|
|
grcRenderTarget* smallDepthRenderTarget = CRenderTargets::GetDepthBufferQuarter();
|
|
m_pPrevFrameBuffer = RMPTFXMGR.GetFrameBuffer();
|
|
m_pPrevFrameBufferSampler = RMPTFXMGR.GetFrameBufferSampler();
|
|
m_pPrevDepthBuffer = RMPTFXMGR.GetDepthBuffer();
|
|
#if __PPU
|
|
m_pPrevPatchedDepthBuffer = RMPTFXMGR.GetPatchedDepthBuffer();
|
|
#endif // __PPU
|
|
RMPTFXMGR.SetFrameBuffer(smallColorRenderTarget);
|
|
RMPTFXMGR.SetFrameBufferSampler(pColourSampler);
|
|
RMPTFXMGR.SetDepthBuffer(smallDepthRenderTarget);
|
|
#if __PPU
|
|
RMPTFXMGR.SetPatchedDepthBuffer(CRenderTargets::GetDepthBufferQuarterAsColor());
|
|
#endif // __PPU
|
|
|
|
|
|
ptxShaderTemplateList::SetGlobalVars();
|
|
|
|
CShaderLib::SetGlobalScreenSize(Vector2((float)smallColorRenderTarget->GetWidth(), (float)smallColorRenderTarget->GetHeight()));
|
|
|
|
CRenderTargets::DownsampleDepth();
|
|
|
|
grcTextureFactory::GetInstance().LockRenderTarget(0, smallColorRenderTarget, smallDepthRenderTarget);
|
|
GRCDEVICE.Clear(true, Color32(0.0f, 0.0f, 0.0f, 0.0f), false, 1.0f, 0);
|
|
|
|
GRCDEVICE.BeginConditionalQuery(m_PtfxDownsampleQuery);
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ShutdownDownsampleRender
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if USE_DOWNSAMPLED_PARTICLES
|
|
void CPtFxManager::ShutdownDownsampleRender ()
|
|
{
|
|
#if __XENON
|
|
m_useDownrezzedPtfx = false;
|
|
#endif
|
|
GRCDEVICE.EndConditionalQuery(m_PtfxDownsampleQuery);
|
|
grcTextureFactory::GetInstance().UnlockRenderTarget(0);
|
|
|
|
GRCDEVICE.BeginConditionalRender(m_PtfxDownsampleQuery);
|
|
|
|
#if __XENON
|
|
PostFX::SetLDR8bitHDR10bit();
|
|
#elif __PS3
|
|
PostFX::SetLDR8bitHDR16bit();
|
|
#else
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#endif
|
|
|
|
UpsampleHelper::UpsampleParams upsampleParams;
|
|
upsampleParams.blendType = grcCompositeRenderTargetHelper::COMPOSITE_RT_BLEND_COMPOSITE_ALPHA;
|
|
upsampleParams.highResUpsampleDestination = m_pPrevFrameBuffer;
|
|
upsampleParams.highResDepthTarget = m_pPrevDepthBuffer;
|
|
upsampleParams.lowResUpsampleSource = m_pLowResPtfxBuffer;
|
|
upsampleParams.lowResDepthTarget = CRenderTargets::GetDepthBufferQuarter();
|
|
upsampleParams.useSeparateEdgeDetectionPass = false;
|
|
#if __PS3
|
|
upsampleParams.highResPatchedDepthTarget = m_pPrevPatchedDepthBuffer;
|
|
upsampleParams.lowResPatchedDepthTarget = CRenderTargets::GetDepthBufferQuarterAsColor();
|
|
#else
|
|
grcResolveFlags resolveFlags;
|
|
resolveFlags.NeedResolve = false;
|
|
upsampleParams.resolveFlags = &resolveFlags;
|
|
upsampleParams.maskUpsampleByStencilAsColor = false;
|
|
#endif
|
|
upsampleParams.lockTarget = false;
|
|
upsampleParams.unlockTarget = false;
|
|
|
|
CRenderTargets::LockSceneRenderTargets();
|
|
UpsampleHelper::PerformUpsample(upsampleParams);
|
|
|
|
CShaderLib::UpdateGlobalGameScreenSize();
|
|
|
|
RMPTFXMGR.SetFrameBuffer(m_pPrevFrameBuffer);
|
|
RMPTFXMGR.SetFrameBufferSampler(m_pPrevFrameBufferSampler);
|
|
RMPTFXMGR.SetDepthBuffer(m_pPrevDepthBuffer);
|
|
#if __PPU
|
|
RMPTFXMGR.SetPatchedDepthBuffer(m_pPrevPatchedDepthBuffer);
|
|
#endif // __PPU
|
|
|
|
GRCDEVICE.EndConditionalRender(m_PtfxDownsampleQuery);
|
|
|
|
#if __XENON
|
|
PostFX::SetLDR10bitHDR10bit();
|
|
#elif __PS3
|
|
PostFX::SetLDR8bitHDR16bit();
|
|
#else
|
|
PostFX::SetLDR16bitHDR16bit();
|
|
#endif
|
|
}
|
|
#endif // USE_DOWNSAMPLED_PARTICLES
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RelocateEntityPtFx
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RelocateEntityPtFx (CEntity* pEntity)
|
|
{
|
|
u32 drawListCount = RMPTFXMGR.GetNumDrawLists();
|
|
for(u32 i=0;i<drawListCount;i++)
|
|
{
|
|
ptxEffectInstItem* pCurrEffectInstItem = (ptxEffectInstItem*)RMPTFXMGR.GetDrawList(i)->m_effectInstList.GetHead();
|
|
ptxEffectInstItem* pNextEffectInstItem = pCurrEffectInstItem;
|
|
while (pCurrEffectInstItem)
|
|
{
|
|
pNextEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNext();
|
|
ptxEffectInst* pFxInst = pCurrEffectInstItem->GetDataSB();
|
|
|
|
if (ptfxAttach::GetIsAttachedTo(pFxInst, pEntity))
|
|
{
|
|
//pFxInst->Relocate();
|
|
pFxInst->SetNeedsRelocated();
|
|
}
|
|
|
|
pCurrEffectInstItem = pNextEffectInstItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RemovePtFxFromEntity
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RemovePtFxFromEntity (CEntity* pEntity, bool isRespawning)
|
|
{
|
|
u32 drawListCount = RMPTFXMGR.GetNumDrawLists();
|
|
for (u32 i=0;i<drawListCount;i++)
|
|
{
|
|
ptxEffectInstItem* pCurrEffectInstItem = (ptxEffectInstItem*)RMPTFXMGR.GetDrawList(i)->m_effectInstList.GetHead();
|
|
ptxEffectInstItem* pNextEffectInstItem = pCurrEffectInstItem;
|
|
while (pCurrEffectInstItem)
|
|
{
|
|
pNextEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNext();
|
|
ptxEffectInst* pFxInst = pCurrEffectInstItem->GetDataSB();
|
|
|
|
// some effects don't want to be removed when respawning (e.g. angel/devil glows)
|
|
bool dontRemovePtFx = false;
|
|
if (isRespawning && pFxInst->GetEffectRule())
|
|
{
|
|
atHashWithStringNotFinal hashName(pFxInst->GetEffectRule()->GetName());
|
|
if (hashName==ATSTRINGHASH("scr_adversary_ped_glow", 0xF277B208) ||
|
|
hashName==ATSTRINGHASH("scr_adversary_ped_light_bad", 0x511BCEF0) ||
|
|
hashName==ATSTRINGHASH("scr_adversary_ped_light_good", 0xB3AF8FC))
|
|
{
|
|
dontRemovePtFx = true;
|
|
}
|
|
}
|
|
|
|
if (!dontRemovePtFx)
|
|
{
|
|
if (ptfxAttach::GetIsAttachedTo(pFxInst, pEntity))
|
|
{
|
|
ptfxManager::RemovePtFx(pFxInst);
|
|
}
|
|
}
|
|
|
|
pCurrEffectInstItem = pNextEffectInstItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// StopPtFxOnEntity
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::StopPtFxOnEntity (CEntity* pEntity)
|
|
{
|
|
u32 drawListCount = RMPTFXMGR.GetNumDrawLists();
|
|
for (u32 i=0;i<drawListCount;i++)
|
|
{
|
|
ptxEffectInstItem* pCurrEffectInstItem = (ptxEffectInstItem*)RMPTFXMGR.GetDrawList(i)->m_effectInstList.GetHead();
|
|
ptxEffectInstItem* pNextEffectInstItem = pCurrEffectInstItem;
|
|
while (pCurrEffectInstItem)
|
|
{
|
|
pNextEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNext();
|
|
ptxEffectInst* pFxInst = pCurrEffectInstItem->GetDataSB();
|
|
|
|
if (ptfxAttach::GetIsAttachedTo(pFxInst, pEntity))
|
|
{
|
|
ptfxWrapper::StopEffectInst(pFxInst);
|
|
}
|
|
|
|
pCurrEffectInstItem = pNextEffectInstItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RemovePtFxInRange
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::RemovePtFxInRange (Vec3V_In vPos, float range)
|
|
{
|
|
u32 drawListCount = RMPTFXMGR.GetNumDrawLists();
|
|
for (u32 i=0;i<drawListCount;i++)
|
|
{
|
|
ptxEffectInstItem* pCurrEffectInstItem = (ptxEffectInstItem*)RMPTFXMGR.GetDrawList(i)->m_effectInstList.GetHead();
|
|
ptxEffectInstItem* pNextEffectInstItem = pCurrEffectInstItem;
|
|
while (pCurrEffectInstItem)
|
|
{
|
|
pNextEffectInstItem = (ptxEffectInstItem*)pCurrEffectInstItem->GetNext();
|
|
ptxEffectInst* pFxInst = pCurrEffectInstItem->GetDataSB();
|
|
|
|
Vec3V vFxPos = pFxInst->GetCurrPos();
|
|
float distSqr = MagSquared(vFxPos-vPos).Getf();
|
|
if (distSqr<range*range)
|
|
{
|
|
if (!pFxInst->GetFlag(PTFX_EFFECTINST_DONT_REMOVE))
|
|
{
|
|
ptfxManager::RemovePtFx(pFxInst);
|
|
}
|
|
}
|
|
|
|
pCurrEffectInstItem = pNextEffectInstItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SetForceVehicleInteriorFlag
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::SetForceVehicleInteriorFlag(bool bSet, scrThreadId scriptThreadId)
|
|
{
|
|
if (vfxVerifyf(m_forceVehicleInteriorFlagScriptThreadId==THREAD_INVALID || m_forceVehicleInteriorFlagScriptThreadId==scriptThreadId, "trying to override force vehicle interior flag when this is already in use by another script"))
|
|
{
|
|
m_forceVehicleInteriorFlag = bSet;
|
|
m_forceVehicleInteriorFlagScriptThreadId = scriptThreadId;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstStartFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstStartFunctor (ptxEffectInst* pFxInst)
|
|
{
|
|
ptfxAssertf(pFxInst, "called with NULL effect instance");
|
|
|
|
// scene sorting
|
|
#if PTFX_ALLOW_INSTANCE_SORTING
|
|
if (ShouldSceneSort(pFxInst))
|
|
{
|
|
if (pFxInst->GetFlag(PTFX_RESERVED_SORTED))
|
|
{
|
|
// this effect has already been started and is now being restarted - ignore it
|
|
ptfxAssertf(pFxInst->GetFlag(PTXEFFECTFLAG_MANUAL_DRAW), "CPtFxSortedManager::SortedOnStartFunctor - already started effect instance doesn't have its manual draw flag set");
|
|
ptfxAssertf(pFxInst->GetFlag(PTXEFFECTFLAG_KEEP_RESERVED), "CPtFxSortedManager::SortedOnStartFunctor - already started effect instance doesn't have its reserved flag set");
|
|
return;
|
|
}
|
|
|
|
CPtFxSortedManager::Add(pFxInst);
|
|
}
|
|
#endif
|
|
|
|
// audio
|
|
g_AmbientAudioEntity.StartSpawnEffect(pFxInst);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstStopFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstStopFunctor (ptxEffectInst* pFxInst)
|
|
{
|
|
ptfxAssertf(pFxInst, "called with NULL effect instance");
|
|
|
|
// audio
|
|
g_AmbientAudioEntity.StopSpawnEffect(pFxInst);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstDeactivateFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstDeactivateFunctor (ptxEffectInst* pFxInst)
|
|
{
|
|
ptfxAssertf(pFxInst, "called with NULL effect instance");
|
|
|
|
// audio
|
|
g_AmbientAudioEntity.RecycleSpawnEffect(pFxInst);
|
|
|
|
for (int i=0; i < g_ptFxManager.m_numLightInfos; ++i)
|
|
{
|
|
if(g_ptFxManager.m_lightInfos[i].pEffectInst == pFxInst)
|
|
{
|
|
g_ptFxManager.m_lightInfos[i].pEffectInst = NULL;
|
|
}
|
|
}
|
|
|
|
for (int i=0; i < g_ptFxManager.m_numDecalInfos; ++i)
|
|
{
|
|
if(g_ptFxManager.m_decalInfos[i].pEffectInst == pFxInst)
|
|
{
|
|
g_ptFxManager.m_decalInfos[i].pEffectInst = NULL;
|
|
}
|
|
}
|
|
|
|
for (int i=0; i < g_ptFxManager.m_numDecalTrailInfos; ++i)
|
|
{
|
|
if(g_ptFxManager.m_decalTrailInfos[i].pEffectInst == pFxInst)
|
|
{
|
|
g_ptFxManager.m_decalTrailInfos[i].pEffectInst = NULL;
|
|
}
|
|
}
|
|
|
|
for (int i=0; i < g_ptFxManager.m_numLiquidInfos; ++i)
|
|
{
|
|
if(g_ptFxManager.m_liquidInfos[i].pEffectInst == pFxInst)
|
|
{
|
|
g_ptFxManager.m_liquidInfos[i].pEffectInst = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstSpawnFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// bool CPtFxManager::EffectInstSpawnFunctor (ptxEffectInst* pFxInstParent, ptxEffectInst* pFxInstChild)
|
|
// {
|
|
// ptfxAssertf(pFxInstParent, "called with NULL parent effect instance");
|
|
// ptfxAssertf(pFxInstChild, "called with NULL child effect instance");
|
|
//
|
|
// // scene sorting
|
|
// #if PTFX_ALLOW_INSTANCE_SORTING
|
|
// if (ShouldSceneSort(pFxInstParent))
|
|
// {
|
|
// CPtFxSortedManager::Add(pFxInstChild);
|
|
// }
|
|
// #endif
|
|
//
|
|
// return true;
|
|
// }
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstDistToCamFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
float CPtFxManager::EffectInstDistToCamFunctor (ptxEffectInst* pFxInst)
|
|
{
|
|
Vec3V_ConstRef vFxPos = pFxInst->GetCurrPos();
|
|
if (ptfxVerifyf(IsFiniteAll(vFxPos), "effect inst position is not valid (%s)", pFxInst->GetEffectRule()->GetName()))
|
|
{
|
|
return rage::Sqrtf(CVfxHelper::GetDistSqrToCamera(vFxPos));
|
|
}
|
|
else
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstOcclusionFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CPtFxManager::EffectInstOcclusionFunctor (Vec4V* pvCullSphere)
|
|
{
|
|
if (CVfxHelper::GetCamInteriorLocation().IsValid())
|
|
{
|
|
// we're in an interior (they don't support occlusion yet) - we're not culled
|
|
return false;
|
|
}
|
|
|
|
spdAABB cullBox;
|
|
cullBox.SetAsSphereV4(*pvCullSphere);
|
|
|
|
// test the cull box against the occlusion system
|
|
if(COcclusion::IsBoxVisibleFast(cullBox, g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SHADOWS))
|
|
{
|
|
// it's visible - we're not culled
|
|
return false;
|
|
}
|
|
|
|
// we're culled
|
|
return true;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstPostUpdateFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstPostUpdateFunctor (ptxEffectInst* pFxInst)
|
|
{
|
|
ptfxAssertf(pFxInst, "called with NULL effect instance");
|
|
|
|
// collision
|
|
ptxEffectRule* pEffectRule = pFxInst->GetEffectRule();
|
|
if (pEffectRule)
|
|
{
|
|
// check if collision is enabled on this effect
|
|
if (pEffectRule->GetColnType()>0 && pEffectRule->GetColnRange()>0.0f)
|
|
{
|
|
// check if the effect out of range for performing collision (using the lod evo min dist as the collision range)
|
|
bool outOfRange = false;
|
|
if (pFxInst->GetLodEvoIdx()>=0 && pEffectRule->GetLodEvoDistMax()>0.0f)
|
|
{
|
|
float evoDistMin = pEffectRule->GetLodEvoDistMin();
|
|
float distSqrToCamera = pFxInst->GetDataDB()->GetDistSqrToCamera();
|
|
if (distSqrToCamera>evoDistMin*evoDistMin)
|
|
{
|
|
outOfRange = true;
|
|
}
|
|
}
|
|
|
|
if (outOfRange)
|
|
{
|
|
// effect is out of range - clear any existing collision
|
|
pFxInst->SetCollisionSet(NULL);
|
|
}
|
|
else
|
|
{
|
|
// effect is in range - process collisions
|
|
g_ptFxManager.GetColnInterface().Register(pFxInst);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ParticleRuleInitFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::ParticleRuleInitFunctor(ptxParticleRule* pPtxParticleRule)
|
|
{
|
|
u8 ptxRuleFlags = PTFX_WATERSURFACE_RENDER_BOTH;
|
|
if(pPtxParticleRule != NULL)
|
|
{
|
|
ptxu_ZCull* pPtxZCullBehavior = static_cast<ptxu_ZCull*>(pPtxParticleRule->GetBehaviour("ptxu_zcull"));
|
|
if(pPtxZCullBehavior != NULL)
|
|
{
|
|
int cullMode = pPtxZCullBehavior->GetMode();
|
|
int referenceSpace = pPtxZCullBehavior->GetReferenceSpace();
|
|
|
|
if(referenceSpace == PTXU_ZCULL_REFERENCESPACE_WATER_Z_CAMERA || referenceSpace == PTXU_ZCULL_REFERENCESPACE_WATER_Z_POINT)
|
|
{
|
|
if(cullMode == PTXU_ZCULL_MODE_CULL_UNDER)
|
|
{
|
|
ptxRuleFlags = PTFX_WATERSURFACE_RENDER_ABOVE_WATER;
|
|
}
|
|
else if(cullMode == PTXU_ZCULL_MODE_CULL_OVER)
|
|
{
|
|
ptxRuleFlags = PTFX_WATERSURFACE_RENDER_BELOW_WATER;
|
|
}
|
|
}
|
|
}
|
|
pPtxParticleRule->SetFlags(ptxRuleFlags);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ParticleRuleShouldBeRenderedFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CPtFxManager::ParticleRuleShouldBeRenderedFunctor(ptxParticleRule* pPtxParticleRule)
|
|
{
|
|
#if __BANK
|
|
if (g_ptFxManager.GetGameDebugInterface().GetAlwaysPassShouldBeRenderedRuleChecks())
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
if(pPtxParticleRule == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Disabling screen space particle fx in mirror reflections and shadows
|
|
if(pPtxParticleRule->GetIsScreenSpace() && (g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION || g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SHADOWS))
|
|
{
|
|
return false;
|
|
}
|
|
const bool bIsRefractionShader = pPtxParticleRule->GetShaderInst().GetIsRefract();
|
|
u8 ptxFlags = pPtxParticleRule->GetAllFlags();
|
|
//check if we need to skip drawing this particle based on water surface state
|
|
bool validWaterSurfaceRenderMode = ((g_PtfxWaterSurfaceRenderState & ptxFlags) != 0);
|
|
// Skip non-refract shaders during refract pass, skip refract shaders during non-refract pass AND if it has valid water surface renderstate
|
|
return (g_ptFxManager.m_isRenderingRefractionPtfxList[g_RenderThreadIndex] == bIsRefractionShader && validWaterSurfaceRenderMode);
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstShouldBeRenderedFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CPtFxManager::EffectInstShouldBeRenderedFunctor(ptxEffectInst* pFxInst, bool bSkipManualDraw)
|
|
{
|
|
#if __BANK
|
|
if (g_ptFxManager.GetGameDebugInterface().GetAlwaysPassShouldBeRenderedRuleChecks())
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
vfxAssertf(CSystem::IsThisThreadId(SYS_THREAD_RENDER), "Should be called from Render Thread");
|
|
|
|
if(pFxInst == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ptxEffectInstDB* pDataDB = pFxInst->GetDataDBMT(RMPTFXTHREADER.GetDrawBufferId());
|
|
const unsigned rti = g_RenderThreadIndex;
|
|
|
|
ptxAssertf(!(pFxInst->GetFlag(PTFX_EFFECTINST_RENDER_ONLY_FIRST_PERSON_VIEW) && pFxInst->GetFlag(PTFX_EFFECTINST_RENDER_ONLY_NON_FIRST_PERSON_VIEW)), "Effect inst has both first person and non first person flags set");
|
|
|
|
//Let's first check if the particle attached to something in the vehicle interior
|
|
//only to be used when executing the draw scene draw list
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_NORMAL || g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SIMPLE)
|
|
{
|
|
#if FPS_MODE_SUPPORTED
|
|
//Skip NON first person tagged fx in normal mode if we're in first person view
|
|
if(g_ptFxManager.m_isFirstPersonCamActive[gRenderThreadInterface.GetRenderBuffer()] && pFxInst->GetFlag(PTFX_EFFECTINST_RENDER_ONLY_NON_FIRST_PERSON_VIEW))
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
fwInteriorLocation interiorLocation;
|
|
interiorLocation.SetAsUint32(pFxInst->GetInteriorLocation());
|
|
bool bIsVehicleAlphaInterior = pDataDB->GetCustomFlag(PTFX_IS_VEHICLE_INTERIOR);
|
|
bool bAlphaInteriorCheck = CRenderPhaseDrawSceneInterface::RenderAlphaInteriorCheck(interiorLocation, bIsVehicleAlphaInterior);
|
|
//This part is for Late Interior Alpha Support. If we're in an interior, it will render the outside particles
|
|
//first and then render the interior particles
|
|
if(!bAlphaInteriorCheck)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
else if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION || g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SHADOWS)
|
|
{
|
|
//Skip first person tagged fx in mirror reflection and shadows when first person view is active
|
|
#if FPS_MODE_SUPPORTED
|
|
if(g_ptFxManager.m_isFirstPersonCamActive[gRenderThreadInterface.GetRenderBuffer()] && pFxInst->GetFlag(PTFX_EFFECTINST_RENDER_ONLY_FIRST_PERSON_VIEW))
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
//Skip the effect instance if we're rendering refraction and the effect inst does not contains refraction particles
|
|
if(g_ptFxManager.m_isRenderingRefractionPtfxList[rti] && !pFxInst->GetFlag(PTFX_EFFECTINST_CONTAINS_REFRACT_RULE))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
u8 effectInstGameCustomFlags = pDataDB->GetCustomFlags();
|
|
|
|
//Render Manual also calls this function so make sure to use the appropriate flag to have the extra check against skipping manual draw
|
|
if((!bSkipManualDraw || pFxInst->GetFlag(PTXEFFECTFLAG_MANUAL_DRAW)==false) ||
|
|
(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SIMPLE) ||
|
|
(g_ptFxManager.m_isRenderingIntoWaterRefraction[rti]) ||
|
|
(g_ptFxManager.m_isRenderingRefractionPtfxList[rti] && pFxInst->GetFlag(PTFX_EFFECTINST_CONTAINS_REFRACT_RULE) ))
|
|
{
|
|
if((g_PtfxWaterSurfaceRenderState & effectInstGameCustomFlags) != 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstInitFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstInitFunctor(ptxEffectInst* pFxInst)
|
|
{
|
|
if(pFxInst == NULL)
|
|
{
|
|
return;
|
|
}
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_REFRACT_RULE);
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_WATER_ABOVE_RULE);
|
|
pFxInst->ClearFlag(PTFX_EFFECTINST_CONTAINS_WATER_BELOW_RULE);
|
|
|
|
// override data if there is an effect rule
|
|
if (pFxInst->GetEffectRule())
|
|
{
|
|
for (int i=0; i<pFxInst->GetNumEvents(); i++)
|
|
{
|
|
if (pFxInst->GetEffectRule()->GetTimeline().GetEvent(i)->GetType()==PTXEVENT_TYPE_EMITTER)
|
|
{
|
|
#if !__SPU
|
|
ptxParticleRule* pParticleRule = ((ptxEventEmitter*)pFxInst->GetEffectRule()->GetTimeline().GetEvent(i))->GetParticleRule();
|
|
if(pParticleRule)
|
|
{
|
|
if(pParticleRule->GetShaderInst().GetIsRefract())
|
|
{
|
|
pFxInst->SetFlag(PTFX_EFFECTINST_CONTAINS_REFRACT_RULE);
|
|
}
|
|
if(pParticleRule->GetFlags(PTFX_WATERSURFACE_RENDER_ABOVE_WATER))
|
|
{
|
|
pFxInst->SetFlag(PTFX_EFFECTINST_CONTAINS_WATER_ABOVE_RULE);
|
|
}
|
|
if(pParticleRule->GetFlags(PTFX_WATERSURFACE_RENDER_BELOW_WATER))
|
|
{
|
|
pFxInst->SetFlag(PTFX_EFFECTINST_CONTAINS_WATER_BELOW_RULE);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstUpdateSetupFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstUpdateSetupFunctor (ptxEffectInst* pFxInst)
|
|
{
|
|
if(pFxInst == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
pFxInst->GetDataDB()->ClearCustomFlag(PTFX_WATERSURFACE_RENDER_BOTH);
|
|
u32 effectInstFlags = pFxInst->GetFlags();
|
|
//if effect inst contains particles that renders in both, then check the distance
|
|
//from water
|
|
if((effectInstFlags & PTFX_EFFECTINST_CONTAINS_WATER_BOTH_RULE) == PTFX_EFFECTINST_CONTAINS_WATER_BOTH_RULE)
|
|
{
|
|
//Setting the both flags here and keeping the underwater checks commented out because
|
|
//the underwater test looks up the game waterheight map which contains only water bodies that
|
|
//reflect. But for shallow water, it's not exactly reflective (it does cube map but not water reflection)
|
|
//so the check would fail in cases where its closer to a higher water body. Disabling this optimization increases
|
|
//the cost but it's very negligible. Once the IsUnderwater test checkes with all the water bodies then we'll re-enable it.
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_WATERSURFACE_RENDER_BOTH);
|
|
/*
|
|
const ScalarV thresholdBelowWater = ScalarV(V_NEGFIVE);
|
|
const ScalarV thresholdAboveWater = ScalarV(V_FIVE);
|
|
if(CVfxHelper::IsUnderwater(pFxInst->GetCurrPos(), false, thresholdAboveWater))
|
|
{
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_WATERSURFACE_RENDER_BELOW_WATER);
|
|
}
|
|
|
|
if(!CVfxHelper::IsUnderwater(pFxInst->GetCurrPos(), false, thresholdBelowWater))
|
|
{
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_WATERSURFACE_RENDER_ABOVE_WATER);
|
|
}
|
|
*/
|
|
|
|
}
|
|
else
|
|
{
|
|
//Effects can have both above water and below water fx.
|
|
if(effectInstFlags & PTFX_EFFECTINST_CONTAINS_WATER_ABOVE_RULE)
|
|
{
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_WATERSURFACE_RENDER_ABOVE_WATER);
|
|
}
|
|
|
|
if(effectInstFlags & PTFX_EFFECTINST_CONTAINS_WATER_BELOW_RULE)
|
|
{
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_WATERSURFACE_RENDER_BELOW_WATER);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstUpdateFinalizeFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstUpdateCullingFunctor (ptxEffectInst* pFxInst)
|
|
{
|
|
// initialise the cull flags
|
|
bool isEmitCulled = false;
|
|
bool isUpdateCulled = false;
|
|
bool isDrawCulled = false;
|
|
bool isShadowDrawCulled = false;
|
|
|
|
// calc the cull state
|
|
ptxEffectCullState cullState = pFxInst->CalcCullState(&(g_ptFxManager.m_currViewport), true);
|
|
//setting the cull earlier in case anything other calls back to pFxInst needs it and its not updated later in this function
|
|
pFxInst->SetCullState(cullState);
|
|
ptxEffectCullState shadowCullState = cullState;
|
|
ptxEffectRule* pEffectRule = pFxInst->GetEffectRule();
|
|
|
|
//If effect contains shadows lets do the test with the shadow viewports also
|
|
//TODO: THis is disabled right now. We should implement the projected shadow OBB visibility test and make shadow cull state visible
|
|
//only when the shadow would be visible in the main viewport.
|
|
#if PTFX_ENABLE_SHADOW_CULL_STATE_UPDATE
|
|
#if RMPTFX_BANK
|
|
if (ptxDebug::sm_disableShadowCullStateCalcs==false)
|
|
#endif // RMPTFX_BANK
|
|
{
|
|
if((RMPTFX_EDITOR_ONLY(RMPTFXMGR.GetInterface().IsConnected() || ) (pEffectRule && !pEffectRule->GetHasNoShadows())))
|
|
{
|
|
for(int i=0; i<g_ptFxManager.m_currShadowViewports.GetCount(); i++)
|
|
{
|
|
shadowCullState = Min( pFxInst->CalcCullState(&(g_ptFxManager.m_currShadowViewports[i]), false),shadowCullState);
|
|
}
|
|
}
|
|
}
|
|
#endif //PTFX_ENABLE_SHADOW_CULL_STATE_UPDATE
|
|
|
|
// calc the lod alpha multiplier (relies on the cull state being calculated first)
|
|
pFxInst->UpdateLODAlphaMultipler();
|
|
|
|
// set the cull flags
|
|
if (cullState!=CULLSTATE_NOT_CULLED)
|
|
{
|
|
// we are either viewport or distance culled
|
|
PF_INCREMENT(CounterEffectInstsCulled);
|
|
|
|
// check if the cull state requires the effect to be killed
|
|
if ((cullState==CULLSTATE_VIEWPORT_CULLED || cullState==CULLSTATE_OCCLUSION_CULLED) && (shadowCullState==CULLSTATE_VIEWPORT_CULLED || shadowCullState==CULLSTATE_OCCLUSION_CULLED) && pEffectRule->GetViewportCullingMode()==1)
|
|
{
|
|
// this will get into here multiple times until the effect is deactivated
|
|
// initially the game may still have the inst reserved and it may take a few frames until it is no longer reserved
|
|
pFxInst->Finish(false);
|
|
}
|
|
|
|
//We test for shadow cull so if the effect is out of the viewport, and its shadow is visible, we would get pops.
|
|
//They still follow same distance cull rule
|
|
|
|
// check if the cull state requires us to stop emitting, updating or drawing
|
|
isEmitCulled = (cullState==CULLSTATE_DISTANCE_CULLED && shadowCullState==CULLSTATE_DISTANCE_CULLED && pEffectRule->GetEmitWhenDistanceCulled()==false) ||
|
|
((cullState==CULLSTATE_VIEWPORT_CULLED || cullState==CULLSTATE_OCCLUSION_CULLED) && (shadowCullState==CULLSTATE_VIEWPORT_CULLED || shadowCullState==CULLSTATE_OCCLUSION_CULLED) && pEffectRule->GetEmitWhenViewportCulled()==false);
|
|
|
|
isUpdateCulled = (cullState==CULLSTATE_DISTANCE_CULLED && shadowCullState==CULLSTATE_DISTANCE_CULLED && pEffectRule->GetUpdateWhenDistanceCulled()==false) ||
|
|
((cullState==CULLSTATE_VIEWPORT_CULLED || cullState==CULLSTATE_OCCLUSION_CULLED) && (shadowCullState==CULLSTATE_VIEWPORT_CULLED || shadowCullState==CULLSTATE_OCCLUSION_CULLED) && pEffectRule->GetUpdateWhenViewportCulled()==false);
|
|
|
|
isDrawCulled = (cullState==CULLSTATE_DISTANCE_CULLED && pEffectRule->GetRenderWhenDistanceCulled()==false) ||
|
|
((cullState==CULLSTATE_VIEWPORT_CULLED || cullState==CULLSTATE_OCCLUSION_CULLED) && pEffectRule->GetRenderWhenViewportCulled()==false);
|
|
|
|
//TODO: move this out of this if condition block because we will have cases where cullstate is not culled but shadow is culled. Performance improvement.
|
|
isShadowDrawCulled = (shadowCullState==CULLSTATE_DISTANCE_CULLED && pEffectRule->GetRenderWhenDistanceCulled()==false) ||
|
|
((shadowCullState==CULLSTATE_VIEWPORT_CULLED || shadowCullState==CULLSTATE_OCCLUSION_CULLED) && pEffectRule->GetRenderWhenViewportCulled()==false);
|
|
|
|
}
|
|
|
|
//Set the states back
|
|
pFxInst->SetIsEmitCulled(isEmitCulled);
|
|
pFxInst->SetIsUpdateCulled(isUpdateCulled);
|
|
pFxInst->GetDataDB()->SetIsDrawCulled(isDrawCulled);
|
|
if(isShadowDrawCulled)
|
|
{
|
|
pFxInst->GetDataDB()->SetCustomFlag(PTFX_DRAW_SHADOW_CULLED);
|
|
}
|
|
else
|
|
{
|
|
pFxInst->GetDataDB()->ClearCustomFlag(PTFX_DRAW_SHADOW_CULLED);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EffectInstUpdateFinalizeFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::EffectInstUpdateFinalizeFunctor(ptxEffectInst* pFxInst)
|
|
{
|
|
|
|
if(pFxInst->GetFlag(PTFX_EFFECTINST_CONTAINS_REFRACT_RULE))
|
|
{
|
|
g_ptFxManager.m_IsRefractPtfxPresent[RMPTFXTHREADER.GetUpdateBufferId()] = true;
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// OverrideDepthStencilStateFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
grcDepthStencilStateHandle CPtFxManager::OverrideDepthStencilStateFunctor(bool bWriteDepth, bool bTestDepth, ptxProjectionMode projMode)
|
|
{
|
|
//Adding check for IsRenderLateVehicleAlpha to disable stencil tests
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SHADOWS || projMode == PTXPROJMODE_NON_WATER || projMode == PTXPROJMODE_NUM || CRenderPhaseDrawSceneInterface::IsRenderLateVehicleAlpha())
|
|
{
|
|
return grcStateBlock::DSS_Invalid;
|
|
}
|
|
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION || g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SIMPLE)
|
|
{
|
|
return g_ptFxManager.m_MirrorReflection_Simple_DepthStencilState[bWriteDepth][bTestDepth];
|
|
}
|
|
|
|
// if we are rendering late interior alpha, then we mark the stencil. Else return invalid state
|
|
return (CRenderPhaseDrawSceneInterface::GetLateVehicleInterAlphaState_Render()?g_ptFxManager.m_VehicleInteriorAlphaDepthStencilStates[bWriteDepth][bTestDepth]:grcStateBlock::DSS_Invalid);
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// OverrideBlendStateFunctor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
grcBlendStateHandle CPtFxManager::OverrideBlendStateFunctor(int blendMode)
|
|
{
|
|
//both color and alpha has the alpha blend state set for shadows. Else use default
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SHADOWS)
|
|
{
|
|
return g_ptFxManager.m_ShadowBlendState;
|
|
}
|
|
#endif // RMPTFX_USE_PARTICLE_SHADOWS
|
|
|
|
// Refraction particles dont really need depth of field
|
|
if(g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_REFRACTION || g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_MIRROR_REFLECTION || g_PtfxBuckerRenderStage == PTFX_BUCKET_RENDER_STAGE_SIMPLE)
|
|
{
|
|
return grcStateBlock::BS_Invalid;
|
|
}
|
|
|
|
#if PTFX_APPLY_DOF_TO_PARTICLES
|
|
if (CPtFxManager::UseParticleDOF())
|
|
{
|
|
switch (blendMode)
|
|
{
|
|
case (grcbsNormal):
|
|
return g_ptFxManager.m_ptfxDofBlendNormal;
|
|
|
|
case (grcbsAdd):
|
|
return g_ptFxManager.m_ptfxDofBlendAdd;
|
|
|
|
case (grcbsSubtract):
|
|
return g_ptFxManager.m_ptfxDofBlendSubtract;
|
|
|
|
case (grcbsCompositeAlpha):
|
|
return g_ptFxManager.m_ptfxDofBlendCompositeAlpha;
|
|
|
|
case (grcbsCompositeAlphaSubtract):
|
|
return g_ptFxManager.m_ptfxDofBlendCompositeAlphaSubtract;
|
|
|
|
default:
|
|
return g_ptFxManager.m_ptfxDofBlendNormal;
|
|
}
|
|
}
|
|
else
|
|
return grcStateBlock::BS_Invalid;
|
|
#else // PTFX_APPLY_DOF_TO_PARTICLES
|
|
return grcStateBlock::BS_Invalid;
|
|
#endif // PTFX_APPLY_DOF_TO_PARTICLES
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// UpdateVisualDataSettings
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CPtFxManager::UpdateVisualDataSettings()
|
|
{
|
|
if (g_visualSettings.GetIsLoaded())
|
|
{
|
|
#if RMPTFX_USE_PARTICLE_SHADOWS
|
|
//This function is called before CPtfxManager::Init()
|
|
m_GlobalShadowIntensityValue = g_visualSettings.Get("particles.shadowintensity", DEFAULT_SHADOW_INTENSITY);
|
|
#endif
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|