Files
GTASource/game/renderer/PostProcessFXHelper.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

4607 lines
131 KiB
C++

#include "PostProcessFXHelper.h"
#include "atl/string.h"
#include "camera/viewports/ViewportManager.h"
#include "game/Clock.h"
#include "math/math.h"
#include "Network/NetworkInterface.h"
#include "file/default_paths.h"
#include "frontend/HudTools.h"
#include "frontend/NewHud.h"
#include "fwsys/timer.h"
#include "parser/manager.h"
#include "PostProcessFXHelper_parser.h"
#include "peds/ped.h"
#include "peds/PlayerArcadeInformation.h"
#include "renderer/PostProcessFX.h"
#include "renderer/rendertargets.h"
#include "renderer/ScreenshotManager.h"
#include "SaveLoad/savegame_photo_buffer.h"
#include "SaveLoad/savegame_photo_manager.h"
#include "scene/world/GameWorld.h"
#include "timecycle/timecycle.h"
#include "fwsys/fileexts.h"
#include "grcore/debugdraw.h"
#include "grcore/quads.h"
#include "system/param.h"
#include "frontend/VideoEditor/ui/TextTemplate.h"
#include "Text/TextConversion.h"
#include "Text/TextFormat.h"
PARAM(animpostfx_debug, "animpostfx_debug");
PARAM(animpostfx_save_path, "animpostfx_save_path");
PARAM(animpostfx_enable_metadata_preview, "When enabled, the AnimPostFX Manager will try to load from the export file instead of the platform data file.");
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
static inline int RemapForBlockCompare(int user)
{
switch(user)
{
//case kDefault:
case AnimPostFXManager::kPedKill:
case AnimPostFXManager::kCameraFlash:
case AnimPostFXManager::kSelectionWheel:
case AnimPostFXManager::kLongPlayerSwitch:
case AnimPostFXManager::kShortPlayerSwitch:
case AnimPostFXManager::kKERSBoost:
case AnimPostFXManager::kFirstPersonProp:
return AnimPostFXManager::kDefault;
break;
default:
return user;
//case kScript,
//case kSpecialAbility,
}
}
#endif // __BANK
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::Set(atHashString modifierName, eAnimatedPostFXMode animMode, u32 inDelayDuration, u32 inDuration, u32 holdDuration, u32 outDuration)
{
m_ModifierName = modifierName;
m_InDuration = inDuration;
m_StartDelayDuration = inDelayDuration;
m_HoldDuration = holdDuration;
m_OutDuration = outDuration;
m_AnimMode = animMode;
m_StartTime = 0U;
m_FadeLevel = 0.0f;
m_State = POSTFX_IDLE;
m_Scale = 1.0f;
m_Loop = false;
m_LoopMode = POSTFX_LOOP_NONE;
}
void AnimatedPostFX::Init()
{
m_State = POSTFX_IDLE;
m_FadeLevel = 0.0f;
m_StartTime = 0U;
m_StartDelayDuration = 0U;
m_InDuration = 0U;
m_HoldDuration = 0U;
m_OutDuration = 0U;
m_ModifierName = 0U;
m_Disabled = false;
m_AnimMode = POSTFX_IN_HOLD_OUT;
m_Loop = false;
m_LoopMode = POSTFX_LOOP_NONE;
m_Scale = 1.0f;
m_CurrentTime = 0U;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::Reset()
{
m_State = POSTFX_IDLE;
m_FadeLevel = 0.0f;
m_StartTime = 0U;
m_Scale = 1.0f;
m_CurrentTime = 0U;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::Start()
{
m_State = POSTFX_IDLE;
u32 currentDuration = (m_StartDelayDuration+m_InDuration+m_HoldDuration+m_OutDuration);
if (m_ModifierName.IsNull() || currentDuration == 0U)
{
m_Scale = 1.0f;
return;
}
m_StartTime = (m_CanBePaused BANK_ONLY(|| ANIMPOSTFXMGR.AllowPausing())) ? fwTimer::GetTimeInMilliseconds_NonScaledClipped() : fwTimer::GetTimeInMilliseconds_NonPausedNonScaledClipped();
m_FadeLevel = 0.0f;
m_CurrentTime = 0U;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::Start(u32 duration)
{
if (duration == 0U)
{
Start();
return;
}
if (m_ModifierName.IsNull())
{
m_Scale = 1.0f;
return;
}
float currentDuration = (float)(m_StartDelayDuration+m_InDuration+m_HoldDuration+m_OutDuration)+0.01f;
m_Scale = (float)duration/currentDuration;
Start();
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::Update()
{
// do nothing if we're not active
if (m_StartTime == 0U || m_Disabled)
{
return;
}
switch (m_AnimMode)
{
case POSTFX_IN_HOLD_OUT:
case POSTFX_EASE_IN_HOLD_EASE_OUT:
Update_InHoldOut();
break;
case POSTFX_EASE_IN:
case POSTFX_EASE_OUT:
Update_EaseInOrOut();
break;
default:
{
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::Update_InHoldOut()
{
float fInterp = 0.0f;
const bool bEaseIn = NeedsEaseIn();
const bool bEaseOut = NeedsEaseOut();
u32 c_uCurrentTime = (m_CanBePaused BANK_ONLY(|| ANIMPOSTFXMGR.AllowPausing())) ? fwTimer::GetTimeInMilliseconds_NonScaledClipped() : fwTimer::GetTimeInMilliseconds_NonPausedNonScaledClipped();
// force effect to loop on its hold state
const bool bLoopDuringHoldState = (m_State == POSTFX_ON_HOLD && m_Loop && m_LoopMode == POSTFX_LOOP_HOLD_ONLY);
if (bLoopDuringHoldState)
{
m_StartTime = c_uCurrentTime;
}
m_CurrentTime = c_uCurrentTime-m_StartTime;
const u32 _inDuration = (u32)((float)(m_InDuration)*m_Scale);
const u32 _startDelayDuration = (u32)((float)(m_StartDelayDuration)*m_Scale);
const u32 _holdRuration = (u32)((float)(m_HoldDuration)*m_Scale);
const u32 _outDuration = (u32)((float)(m_OutDuration)*m_Scale);
const u32 delayInDuration = m_StartTime + _startDelayDuration;
const u32 goingInDuration = delayInDuration + _inDuration;
const u32 onHoldDuration = goingInDuration + _holdRuration;
const u32 goingOutDuration = onHoldDuration + _outDuration;
if (bLoopDuringHoldState)
{
c_uCurrentTime += (_startDelayDuration+_inDuration);
}
if (c_uCurrentTime < delayInDuration)
{
m_State = POSTFX_DELAY_IN;
}
else if (c_uCurrentTime < goingInDuration)
{
// Ramping up
fInterp = (float)(c_uCurrentTime - delayInDuration) / (float)_inDuration;
fInterp = bEaseIn ? rage::SlowIn(fInterp) : fInterp;
m_State = POSTFX_GOING_IN;
}
else if (c_uCurrentTime < onHoldDuration)
{
fInterp = 1.0f;
m_State = POSTFX_ON_HOLD;
}
else if (c_uCurrentTime < goingOutDuration)
{
fInterp = (float)(_outDuration - (c_uCurrentTime - onHoldDuration)) / (float)_outDuration;
fInterp = bEaseOut ? rage::SlowOut(fInterp) : fInterp;
m_State = POSTFX_GOING_OUT;
}
else
{
if (m_Loop && m_LoopMode == POSTFX_LOOP_ALL)
{
m_StartTime = (m_CanBePaused BANK_ONLY(|| ANIMPOSTFXMGR.AllowPausing())) ? fwTimer::GetTimeInMilliseconds_NonScaledClipped() : fwTimer::GetTimeInMilliseconds_NonPausedNonScaledClipped();
}
else
{
// Done, so disable the effect.
m_StartTime = 0U;
m_FadeLevel = 0.0f;
m_State = POSTFX_IDLE;
}
}
m_FadeLevel = Saturate(fInterp);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::Update_EaseInOrOut()
{
const bool bEaseIn = NeedsEaseIn();
float fInterp = 1.0f;
if (m_State != POSTFX_ON_HOLD)
{
const bool bEaseIn = NeedsEaseIn();
const u32 _startDelayDuration = (u32)((float)(m_StartDelayDuration)*m_Scale);
const u32 _inDuration = (u32)((float)(m_InDuration)*m_Scale);
const u32 _outDuration = (u32)((float)(m_OutDuration)*m_Scale);
const u32 c_uCurrentTime = (m_CanBePaused BANK_ONLY(|| ANIMPOSTFXMGR.AllowPausing())) ? fwTimer::GetTimeInMilliseconds_NonScaledClipped() : fwTimer::GetTimeInMilliseconds_NonPausedNonScaledClipped();
m_CurrentTime = c_uCurrentTime-m_StartTime;
const u32 delayInDuration = m_StartTime + _startDelayDuration;
const u32 goingInDuration = delayInDuration + (bEaseIn ? _inDuration : _outDuration);
if (c_uCurrentTime < delayInDuration)
{
fInterp = 0.0f;
m_State = POSTFX_DELAY_IN;
}
else if (c_uCurrentTime < goingInDuration)
{
// Ramping up
fInterp = (float)(c_uCurrentTime - delayInDuration) / (float)(bEaseIn ? _inDuration : _outDuration);
fInterp = bEaseIn ? rage::SlowIn(fInterp) : rage::SlowOut(fInterp);
m_State = POSTFX_GOING_IN;
}
else
{
fInterp = 1.0f;
if (m_Loop && m_LoopMode == POSTFX_LOOP_ALL)
{
m_StartTime = (m_CanBePaused BANK_ONLY(|| ANIMPOSTFXMGR.AllowPausing())) ? fwTimer::GetTimeInMilliseconds_NonScaledClipped() : fwTimer::GetTimeInMilliseconds_NonPausedNonScaledClipped();
}
else
{
m_State = POSTFX_ON_HOLD;
}
}
}
// effect on hold after fading out, reset automatically
else if (bEaseIn == false)
{
if (m_Loop && m_LoopMode == POSTFX_LOOP_ALL)
{
m_StartTime = (m_CanBePaused BANK_ONLY(|| ANIMPOSTFXMGR.AllowPausing())) ? fwTimer::GetTimeInMilliseconds_NonScaledClipped() : fwTimer::GetTimeInMilliseconds_NonPausedNonScaledClipped();
}
else
{
// Done, so disable the effect.
m_StartTime = 0U;
m_FadeLevel = 0.0f;
m_State = POSTFX_IDLE;
}
}
m_FadeLevel = Saturate(bEaseIn ? fInterp : 1.0f - fInterp);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFX::ApplyModifiers(tcKeyframeUncompressed& keyframe, float globalFadeLevel) const
{
if (IsDisabled() || m_ModifierName.IsNull())
{
return;
}
const float fadeLevel = GetFadeLevel()*globalFadeLevel;
atHashString modName = m_ModifierName;
const tcModifier* pModifier = g_timeCycle.FindModifier(modName, "AnimatedPostFX");
if (pModifier)
{
#if __BANK
if (AnimPostFXManager::GetInstance().DebugDisplay())
{
grcDebugDraw::AddDebugOutput(Color_white, "[ANIMPOSTFX] Applying %s - %f", modName.GetCStr(), fadeLevel);
}
if (AnimPostFXManager::GetInstance().DebugOutput())
{
Displayf("[ANIMPOSTFX] Applying %s - %f", modName.GetCStr(), fadeLevel);
}
#endif // __BANK
pModifier->Apply(keyframe, fadeLevel);
}
}
//////////////////////////////////////////////////////////////////////////
//
void LayerBlendModifier::Init()
{
m_LayerA.Null();
m_LayerB.Null();
m_Disabled = true;
m_pLayerA = NULL;
m_pLayerB = NULL;
m_FrequencyNoise = 0.0f;
m_AmplitudeNoise = 0.0f;
m_Frequency = 0.0f;
m_Bias = 0.5f;
m_Level = 0.0f;
}
//////////////////////////////////////////////////////////////////////////
//
void LayerBlendModifier::Reset()
{
Init();
}
//////////////////////////////////////////////////////////////////////////
//
void LayerBlendModifier::ApplyModifiers(tcKeyframeUncompressed& keyframe, float globalFadeLevel) const
{
const tcModifier* pModA = g_timeCycle.FindModifier(m_LayerA, "AnimatedPostFX");
const tcModifier* pModB = g_timeCycle.FindModifier(m_LayerB, "AnimatedPostFX");
if (pModA == NULL || pModB == NULL)
{
return;
}
#if __BANK
if (AnimPostFXManager::GetInstance().DebugDisplay())
{
grcDebugDraw::AddDebugOutput(Color_white, "[ANIMPOSTFX] Blending A %s - %f", m_LayerA.GetCStr(), m_Level*m_pLayerA->GetFadeLevel()*globalFadeLevel);
grcDebugDraw::AddDebugOutput(Color_white, "[ANIMPOSTFX] Blending B %s - %f", m_LayerB.GetCStr(), (1.0f-m_Level)*m_pLayerB->GetFadeLevel()*globalFadeLevel);
}
if (AnimPostFXManager::GetInstance().DebugOutput())
{
Displayf("[ANIMPOSTFX] Blending A %s - %f", m_LayerA.GetCStr(), m_Level*m_pLayerA->GetFadeLevel()*globalFadeLevel);
Displayf("[ANIMPOSTFX] Blending B %s - %f", m_LayerB.GetCStr(), (1.0f-m_Level)*m_pLayerB->GetFadeLevel()*globalFadeLevel);
}
#endif // __BANK
tcModifier blendModA;
blendModA.Init(*pModA);
blendModA.Apply(keyframe, m_Level*m_pLayerA->GetFadeLevel()*globalFadeLevel);
tcModifier blendModB;
blendModB.Init(*pModB);
blendModB.Apply(keyframe, (1.0f-m_Level)*m_pLayerB->GetFadeLevel()*globalFadeLevel);
}
//////////////////////////////////////////////////////////////////////////
//
void LayerBlendModifier::Set(const AnimatedPostFX* pLayerA, const AnimatedPostFX* pLayerB)
{
m_LayerA = pLayerA->GetModifierName();
m_LayerB = pLayerB->GetModifierName();
m_pLayerA = pLayerA;
m_pLayerB = pLayerB;
}
//////////////////////////////////////////////////////////////////////////
//
void LayerBlendModifier::Update()
{
if (fwTimer::IsGamePaused())
return;
const float timeSecs = static_cast<float>(fwTimer::GetSystemTimeInMilliseconds())*0.001f;
const float noise = static_cast<float>(rand())/static_cast<float>(RAND_MAX);
const float ampNoise = Lerp(m_AmplitudeNoise, 1.0f, noise);
float base = Sinf(timeSecs*m_Frequency + noise*m_FrequencyNoise)*ampNoise;
base = Saturate(base*0.5f + m_Bias);
m_Level = base;
}
//////////////////////////////////////////////////////////////////////////
//
const s32 AnimPostFXEventPollingManager::MAX_EVENTS = 128;
u32 AnimPostFXEventPollingManager::EVENT_POLLING_TIMEOUT = 20000U;
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventPollingManager::Init()
{
m_events.Init(MAX_EVENTS);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventPollingManager::Shutdown()
{
m_events.Shutdown();
m_managedEventGroups.Reset();
}
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXEventPollingManager::AddListener(const atHashString fxName)
{
const RegisteredAnimPostFXStack* pStack = ANIMPOSTFXMGR.Get(fxName);
// Check the effect exists at all...
if (pStack == NULL)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] AddListener: effect \"%s\" doesn't exist", fxName.TryGetCStr());
#endif
return false;
}
// Check it's been set up with a valid event group...
s32 groupId = pStack->GetGroupId();
if (groupId == -1)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] AddListener: effect \"%s\" hasn't been setup with a valid group id (-1)", fxName.TryGetCStr());
#endif
return false;
}
// Also make sure we can store a new group...
if (m_managedEventGroups.Access(groupId) == NULL && m_managedEventGroups.GetNumUsed() >= AnimPostFXEventManager::GetMaxListeners())
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] AddListener: effect \"%s\" cannot add more listeners (%d registered)", fxName.TryGetCStr(), m_managedEventGroups.GetNumUsed());
#endif
return false;
}
// Add the listener...
bool bOk = AddListener(groupId);
if (bOk)
{
s32* pGroupRefCount = m_managedEventGroups.Access(groupId);
// Is it already managed? Add a ref...
if (pGroupRefCount)
{
*pGroupRefCount = *pGroupRefCount+1;
}
else // Add the group to our managed groups table
{
m_managedEventGroups.Insert(groupId, 1);
}
}
#if __BANK
const s32* pRefCount = m_managedEventGroups.Access(groupId);
const s32 refCount = pRefCount ? *pRefCount : -1;
Displayf("[ANIMPOSTFX][POLLING] AddListener: effect \"%s\" (%d, numRefs: %d) registration %s", fxName.TryGetCStr(), groupId, refCount, bOk ? "SUCCEEDED" : "FAILED");
#endif
return bOk;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventPollingManager::RemoveListener(const atHashString fxName)
{
const RegisteredAnimPostFXStack* pStack = ANIMPOSTFXMGR.Get(fxName);
// Check the effect exists at all...
if (pStack == NULL)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] RemoveListener: effect \"%s\" doesn't exist", fxName.TryGetCStr());
#endif
return;
}
// Check it's been set up with a valid event group...
s32 groupId = pStack->GetGroupId();
if (groupId == -1)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] RemoveListener: effect \"%s\" hasn't been setup with a valid group id (-1)", fxName.TryGetCStr());
#endif
return;
}
s32* pGroupRefCount = m_managedEventGroups.Access(groupId);
bool bShouldRemoveListener = false;
// The group should be in our table... remove a ref
if (Verifyf(pGroupRefCount, "[ANIMPOSTFX][POLLING] RemoveListener: effect \"%s\" belongs to an unmanaged group (%d)", fxName.TryGetCStr(), groupId))
{
*pGroupRefCount = *pGroupRefCount-1;
// No more effects from the same group being referenced?
if (*pGroupRefCount <= 0)
{
m_managedEventGroups.Delete(groupId);
bShouldRemoveListener = true;
}
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] RemoveListener: effect \"%s\" (group %d has %d refs)", fxName.TryGetCStr(), groupId, *pGroupRefCount);
#endif
}
if (bShouldRemoveListener)
{
// We're done with all effects belonging to groupId, so completely remove the listener
RemoveListener(groupId);
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] RemoveListener: effect \"%s\" AND group %d REMOVED", fxName.TryGetCStr(), groupId);
#endif
}
}
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXEventPollingManager::AddListener(s32 groupId)
{
bool bOk = ANIMPOSTFXMGR.RegisterListener(MakeFunctor(*this, &AnimPostFXEventPollingManager::OnEventTriggered), groupId);
return bOk;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventPollingManager::RemoveListener(s32 groupId)
{
ANIMPOSTFXMGR.RemoveListener(MakeFunctor(*this, &AnimPostFXEventPollingManager::OnEventTriggered), groupId);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventPollingManager::OnEventTriggered(AnimPostFXEvent* pEvent)
{
if (m_events.GetNumFree() == 0)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] OnEventTriggered \"%s\", event queue is FULL", pEvent->fxName.TryGetCStr());
#endif
return;
}
AnimPostFXPolledEvent incomingEvent;
incomingEvent.srcEvent = *pEvent;
incomingEvent.timeStamp = fwTimer::GetTimeInMilliseconds();
incomingEvent.bConsumed = false;
m_events.Insert(incomingEvent);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventPollingManager::Update()
{
u32 currentTime = fwTimer::GetTimeInMilliseconds();
// Remove consumed and old events
PolledEventNode* pNode = m_events.GetFirst()->GetNext();
while(pNode != m_events.GetLast())
{
AnimPostFXPolledEvent& curEntry = (pNode->item);
PolledEventNode* pLastNode = pNode;
pNode = pNode->GetNext();
u32 timeElapsed = (currentTime-curEntry.timeStamp);
bool bExpired = (timeElapsed > EVENT_POLLING_TIMEOUT);
if (curEntry.bConsumed || bExpired)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] Update \"%s\", removing event - %s", curEntry.srcEvent.fxName.TryGetCStr(), bExpired ? "EXPIRED" : "CONSUMED");
#endif
// Forget about it
m_events.Remove(pLastNode);
}
}
}
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXEventPollingManager::HasEventTriggered(atHashString fxName, AnimPostFXEventType eventType, bool bPeekOnly, bool& bIsRegistered)
{
// Assume it hasn't triggered...
bool bHasTriggered = false;
// Assume the group id for the effect is actually registered (checks incoming...)
bIsRegistered = true;
// Check the effect exists at all...
const RegisteredAnimPostFXStack* pStack = ANIMPOSTFXMGR.Get(fxName);
if (pStack == NULL)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] HasEventTriggered: effect \"%s\" doesn't exist", fxName.TryGetCStr());
#endif
bIsRegistered = false;
return false;
}
// Check it's been set up with a valid event group...
s32 groupId = pStack->GetGroupId();
if (groupId == -1)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] HasEventTriggered: effect \"%s\" hasn't been setup with a valid group id (-1)", fxName.TryGetCStr());
#endif
bIsRegistered = false;
return false;
}
// Check we're actually listening to this type of event...
if (m_managedEventGroups.Access(groupId) == NULL)
{
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] HasEventTriggered: effect \"%s\" with group id (%d) is NOT registered", fxName.TryGetCStr(), groupId);
#endif
bIsRegistered = false;
return false;
}
// See if the event is here
PolledEventNode* pNode = m_events.GetFirst()->GetNext();
while(pNode != m_events.GetLast())
{
AnimPostFXPolledEvent& curEntry = (pNode->item);
pNode = pNode->GetNext();
const bool bNameMatches = (fxName == curEntry.srcEvent.fxName);
const bool bNotConsumedYet = (curEntry.bConsumed == false);
const bool bEventTypeMatches = (curEntry.srcEvent.eventType == eventType);
// If name and event type match and the event hasn't been previously consume, report it as triggered
if (bNameMatches && bEventTypeMatches && bNotConsumedYet)
{
bHasTriggered = true;
curEntry.bConsumed = !bPeekOnly;
#if __BANK
Displayf("[ANIMPOSTFX][POLLING] HasEventTriggered \"%s\" TRIGGERED (groupdId: %d, consumed: %d, eventType: %d)",
curEntry.srcEvent.fxName.TryGetCStr(), groupId, curEntry.bConsumed, curEntry.srcEvent.eventType);
#endif
break;
}
}
return bHasTriggered;
}
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
char AnimPostFXEventPollingManager::ms_registeredFxName[128] = {0};
void AnimPostFXEventPollingManager::AddWidgets(rage::bkBank& bank)
{
bank.PushGroup("Polled Events Test");
bank.AddText("FX Name To Test Events:", &ms_registeredFxName[0], 127);
bank.AddButton("REGISTER FX STACK", datCallback(MFA(AnimPostFXEventPollingManager::OnRegisterEvent), (datBase*)this));
bank.AddButton("UNREGISTER FX STACK", datCallback(MFA(AnimPostFXEventPollingManager::OnUnregisterEvent), (datBase*)this));
bank.PopGroup();
}
#endif // __BANK
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void AnimPostFXEventPollingManager::OnRegisterEvent()
{
atHashString fxName = &ms_registeredFxName[0];
AddListener(fxName);
}
#endif // __BANK
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void AnimPostFXEventPollingManager::OnUnregisterEvent()
{
atHashString fxName = &ms_registeredFxName[0];
RemoveListener(fxName);
}
#endif // __BANK
//////////////////////////////////////////////////////////////////////////
//
const s32 AnimPostFXEventManager::MAX_LISTENERS = 32;
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventManager::Init()
{
m_listenerList.Init(MAX_LISTENERS);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventManager::Shutdown()
{
m_listenerList.Shutdown();
}
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXEventManager::RegisterListener(AnimPostFXEventCB pCBFunc, s32 groupId)
{
if (m_listenerList.GetNumFree() == 0 || Exists(pCBFunc, groupId))
{
return false;
}
AnimPostFXListener newListener;
newListener.eventCallback = pCBFunc;
newListener.groupId = groupId;
m_listenerList.Insert(newListener);
return true;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventManager::RemoveListener(AnimPostFXEventCB pCBFunc, s32 groupId)
{
ListenerNode* pNode = m_listenerList.GetFirst()->GetNext();
while(pNode != m_listenerList.GetLast())
{
AnimPostFXListener& curEntry = (pNode->item);
ListenerNode* pLastNode = pNode;
pNode = pNode->GetNext();
if (curEntry.eventCallback == pCBFunc && curEntry.groupId == groupId)
{
// Forget about it
m_listenerList.Remove(pLastNode);
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXEventManager::OnEventTriggered(const RegisteredAnimPostFXStack* pStack, AnimPostFXEventType eventType)
{
// Don't bother with NULL fx stacks or invalid event types
if (pStack == NULL || eventType == ANIMPOSTFX_EVENT_INVALID)
{
return;
}
// Or if the group id is invalid
s32 groupId = pStack->GetGroupId();
if (groupId == -1)
{
return;
}
// Broadcast the event
const ListenerNode* pCurrentNode = m_listenerList.GetFirst()->GetNext();
while(pCurrentNode != m_listenerList.GetLast())
{
const AnimPostFXListener& currentEntry = (pCurrentNode->item);
pCurrentNode = pCurrentNode->GetNext();
if (currentEntry.groupId == groupId)
{
// Cache event data
AnimPostFXEvent currentEvent;
currentEvent.groupId = pStack->GetGroupId();
currentEvent.fxName = pStack->GetName();
currentEvent.eventType = eventType;
currentEvent.userTag = pStack->GetEventUserTag();
// Pass on the event data to the listener
if (Verifyf(currentEntry.eventCallback != 0, "AnimPostFXEventManager::OnEventTriggered: NULL callback"))
{
currentEntry.eventCallback(&currentEvent);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
bool AnimPostFXEventManager::Exists(AnimPostFXEventCB pCBFunc, s32 groupId) const
{
const ListenerNode* pCurrentNode = m_listenerList.GetFirst()->GetNext();
while(pCurrentNode != m_listenerList.GetLast())
{
const AnimPostFXListener& currentEntry = (pCurrentNode->item);
pCurrentNode = pCurrentNode->GetNext();
if (currentEntry.eventCallback == pCBFunc && currentEntry.groupId == groupId)
{
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::Add(atHashString modifierName, eAnimatedPostFXMode animMode, u32 inDelayDuration, u32 inDuration, u32 holdDuration, u32 outDuration)
{
if (m_Layers.GetCount() >= kNumEffectLayers)
{
return;
}
AnimatedPostFX effect;
effect.Set(modifierName, animMode, inDelayDuration, inDuration, holdDuration, outDuration);
m_Layers.Push(effect);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::Set(u32 layer, atHashString modifierName, eAnimatedPostFXMode animMode, u32 inDelayDuration, u32 inDuration, u32 holdDuration, u32 outDuration)
{
if (m_Layers.GetCount() <= layer)
{
return;
}
m_Layers[layer].Set(modifierName, animMode, inDelayDuration, inDuration, holdDuration, outDuration);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::SetLoopEnabled(bool bEnabled)
{
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
m_Layers[i].SetLoopEnabled(bEnabled);
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::Start()
{
if (m_UserFadeOverrideEnabled == false)
{
m_UserFadeOverrideLevel = 1.0f;
}
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
m_Layers[i].Start();
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::Start(u32 duration)
{
if (m_UserFadeOverrideEnabled == false)
{
m_UserFadeOverrideLevel = 1.0f;
}
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
m_Layers[i].Start(duration);
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::Update()
{
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
m_Layers[i].Update();
}
if (m_LayerBlend.IsEnabled())
{
m_LayerBlend.Update();
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::Reset()
{
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
m_Layers[i].Reset();
}
}
//////////////////////////////////////////////////////////////////////////
//
u32 AnimatedPostFXStack::GetDuration() const
{
u32 duration = 0U;
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
duration = Max(duration, m_Layers[i].GetDuration());
}
return duration;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::ApplyModifiers(tcKeyframeUncompressed& keyframe) const
{
if (m_LayerBlend.IsEnabled())
{
m_LayerBlend.ApplyModifiers(keyframe, m_UserFadeOverrideLevel);
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
bool bSkipLayer = (m_Layers[i].GetModifierName() == m_LayerBlend.m_LayerA || m_Layers[i].GetModifierName() == m_LayerBlend.m_LayerB);
if (!bSkipLayer)
{
m_Layers[i].ApplyModifiers(keyframe, m_UserFadeOverrideLevel);
}
}
return;
}
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
m_Layers[i].ApplyModifiers(keyframe, m_UserFadeOverrideLevel);
}
}
//////////////////////////////////////////////////////////////////////////
//
bool AnimatedPostFXStack::IsIdle() const
{
bool bIsIdle = true;
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
bIsIdle = bIsIdle && (m_Layers[i].IsIdle());
}
return bIsIdle;
}
//////////////////////////////////////////////////////////////////////////
//
float AnimatedPostFXStack::GetCurrentTime() const
{
float currentTime = 0.0f;
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
currentTime = rage::Max(currentTime, m_Layers[i].GetFadeLevel());
}
return currentTime;
}
//////////////////////////////////////////////////////////////////////////
//
u32 AnimatedPostFXStack::GetCurrentTimeMS() const
{
u32 currentTime = 0U;
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
currentTime = rage::Max(currentTime, m_Layers[i].GetCurrentTime());
}
return currentTime;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::SetUserFadeOverride(bool bEnable)
{
m_UserFadeOverrideEnabled = bEnable;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::SetUserFadeLevel(float level)
{
m_UserFadeOverrideLevel = Saturate(level);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::PostLoadCallback()
{
// set up layer blending modifier
if (m_LayerBlend.IsEnabled())
{
// disable it until we verify it's been correctly setup
m_LayerBlend.Disable();
const AnimatedPostFX* pLayerA = NULL;
const AnimatedPostFX* pLayerB = NULL;
const atHashString layerA = m_LayerBlend.m_LayerA;
const atHashString layerB = m_LayerBlend.m_LayerB;
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
if (m_Layers[i].GetModifierName() == layerA)
{
pLayerA = &m_Layers[i];
}
else if (m_Layers[i].GetModifierName() == layerB)
{
pLayerB = &m_Layers[i];
}
}
// if it's all good, set up and toggle the blending mod on
if (pLayerA != NULL && pLayerB != NULL && pLayerA != pLayerB)
{
m_LayerBlend.Enable();
m_LayerBlend.Set(pLayerA, pLayerB);
}
Assertf(pLayerA != NULL && pLayerB != NULL, "FX stack has bad data: layer blending modifier is enabled, but layers could not be found");
Assertf(pLayerA != pLayerB, "FX stack has bad data: layer blending modifier is enabled, but layer A and B are the same");
}
}
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void AnimatedPostFXStack::AddWidgets(rage::bkBank& bank )
{
for (u32 i = 0; i < m_Layers.GetCount(); i++)
{
bank.AddToggle("Disable Layer", &(m_Layers[i].m_Disabled));
PARSER.AddWidgets(bank, &(m_Layers[i]));
bank.AddSeparator();
}
}
#endif
//////////////////////////////////////////////////////////////////////////
//
void RegisteredAnimPostFXStack::Init()
{
m_Name = 0U;
m_Priority = 0;
m_StartRequested = false;
m_StopRequested = false;
m_OverriddenDuration = 0U;
m_FXStack.m_Layers.Reset();
m_FrameEventTriggered = false;
}
//////////////////////////////////////////////////////////////////////////
//
void RegisteredAnimPostFXStack::Start()
{
m_StartRequested = true;
m_OverriddenDuration = 0U;
m_FrameEventTriggered = false;
}
//////////////////////////////////////////////////////////////////////////
//
void RegisteredAnimPostFXStack::Start(u32 duration)
{
m_StartRequested = true;
m_OverriddenDuration = duration;
m_FrameEventTriggered = false;
}
//////////////////////////////////////////////////////////////////////////
//
void RegisteredAnimPostFXStack::CancelStartRequest()
{
m_StartRequested = false;
}
//////////////////////////////////////////////////////////////////////////
//
void RegisteredAnimPostFXStack::Reset()
{
m_FXStack.Reset();
m_OverriddenDuration = 0U;
m_FrameEventTriggered = false;
m_FrameDelay = 0U;
}
//////////////////////////////////////////////////////////////////////////
//
void RegisteredAnimPostFXStack::Update()
{
m_FXStack.Update();
}
void RegisteredAnimPostFXStack::FrameDelayUpdate()
{
#if __BANK
if (AnimPostFXManager::GetInstance().DebugDisplay())
{
Displayf("[ANIMPOSTFX] FrameDelayUpdate for %s - frameDelay %d", m_Name.TryGetCStr(), m_FrameDelay);
}
if (AnimPostFXManager::GetInstance().DebugOutput())
{
grcDebugDraw::AddDebugOutput(Color_white, "[ANIMPOSTFX] FrameDelayUpdate for %s - frameDelay %d", m_Name.TryGetCStr(), m_FrameDelay);
}
#endif
if( m_FrameDelay )
{
m_FrameDelay--;
if( m_FrameDelay == 0 )
{
if (GetGroupId() != -1 && DoesEventTypeMatch(ANIMPOSTFX_EVENT_ON_START))
{
#if __BANK
if (AnimPostFXManager::GetInstance().DebugOutput())
{
Displayf("[ANIMPOSTFX][EVENT] Delayed Start event triggered for \"%s\"", m_Name.TryGetCStr());
}
#endif
AnimPostFXManager::GetInstance().OnEventTriggered(this, ANIMPOSTFX_EVENT_ON_START);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
//
float RegisteredAnimPostFXStack::GetCurrentTime() const
{
float currentTime = 0.0f;
for (u32 i = 0; i < m_FXStack.m_Layers.GetCount(); i++)
{
currentTime = rage::Max(currentTime, m_FXStack.m_Layers[i].GetFadeLevel());
}
return currentTime;
}
//////////////////////////////////////////////////////////////////////////
//
void RegisteredAnimPostFXStack::ApplyModifiers(tcKeyframeUncompressed& keyframe)
{
if (m_StopRequested)
{
Reset();
m_StopRequested = false;
}
if (m_StartRequested)
{
const u32 duration = m_OverriddenDuration;
Reset();
if (duration > 0)
{
m_FXStack.Start(duration);
}
else
{
m_FXStack.Start();
}
// run an update tick so that the effect has a chance to get picked up this frame
m_FXStack.Update();
m_StartRequested = false;
}
m_FXStack.ApplyModifiers(keyframe);
}
//////////////////////////////////////////////////////////////////////////
//
bool RegisteredAnimPostFXStack::DoesEventTypeMatch(AnimPostFXEventType incomingEventType) const
{
AnimPostFXEventType eventType = GetEventType();
// Out of bounds?
if ((incomingEventType < ANIMPOSTFX_EVENT_ON_START || incomingEventType > ANIMPOSTFX_EVENT_ON_FRAME) ||
(eventType < ANIMPOSTFX_EVENT_ON_START || eventType > ANIMPOSTFX_EVENT_ON_STOP_ON_FRAME))
return false;
static const bool eventTypeTable[7][3]=
{
// [ANIMPOSTFX_EVENT_ON_START] [ANIMPOSTFX_EVENT_ON_STOP] [ANIMPOSTFX_EVENT_ON_FRAME]
{true, false, false }, // ANIMPOSTFX_EVENT_ON_START,
{false, true, false }, // ANIMPOSTFX_EVENT_ON_STOP,
{false, false, true }, // ANIMPOSTFX_EVENT_ON_FRAME,
{true, true, false }, // ANIMPOSTFX_EVENT_ON_START_ON_STOP,
{true, true, true }, // ANIMPOSTFX_EVENT_ON_START_ON_STOP_ON_FRAME,
{true, false, true }, // ANIMPOSTFX_EVENT_ON_START_ON_FRAME,
{true, true, false }, // ANIMPOSTFX_EVENT_ON_STOP_ON_FRAME,
};
return eventTypeTable[eventType][incomingEventType];
}
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void RegisteredAnimPostFXStack::ClearForEdit()
{
Init();
m_FXStack.m_Layers.SetCount(AnimatedPostFXStack::kNumEffectLayers);
for (int i = 0; i < m_FXStack.m_Layers.GetCount(); i++)
{
m_FXStack.m_Layers[i].Init();
}
}
//////////////////////////////////////////////////////////////////////////
//
bool RegisteredAnimPostFXStack::Validate()
{
bool bIsValid = true;
u32 numUnusedMods = 0;
u32 totalDuration = 0;
for (int i = 0; i < m_FXStack.GetCount(); i++)
{
// it's ok to leave some of the layers unused
if (m_FXStack.m_Layers[i].GetModifierName().IsNull())
{
numUnusedMods++;
}
// but if a mod name was assigned, it better exists
else if (Verifyf(g_timeCycle.FindModifier(m_FXStack.m_Layers[i].GetModifierName(),"RegisteredAnimPostFXStack") != NULL, "TCMod \"%s\" in layer %d doesn't exist", m_FXStack.m_Layers[i].GetModifierName().TryGetCStr(), i))
{
totalDuration += m_FXStack.m_Layers[i].GetDuration();
}
// neither of them: fail
else
{
bIsValid = false;
break;
}
}
bIsValid = bIsValid && Verifyf(numUnusedMods < AnimatedPostFXStack::kNumEffectLayers, "None of the layers was assigned any TC mod");
bIsValid = bIsValid && Verifyf(totalDuration > 0, "Total duration for effect stack is 0");
return bIsValid;
}
#endif
#if GTA_REPLAY
// Just add a new entry for any replay is interested in
AnimPostFXManagerReplayEffectLookup::AnimPostFXReplayEffectTableEntry AnimPostFXManagerReplayEffectLookup::m_PostEffectReplayGameCamRemovalLookupTable[] =
{
// Pause menu, weapon wheel, player switch etc...
{ ATSTRINGHASH("SwitchHUDMichaelIn",0x10493196), NULL, false },
{ ATSTRINGHASH("SwitchHUDMichaelOut",0x2fe80a59), NULL, false },
{ ATSTRINGHASH("SwitchOpenMichaelIn",0x848674fd), NULL, false },
{ ATSTRINGHASH("SwitchOpenMichaelMid",0x621be8f8), NULL, false },
{ ATSTRINGHASH("SwitchOpenMichaelOut",0x15dca17b), NULL, false },
{ ATSTRINGHASH("SwitchSceneMichael",0x2f8758e3), NULL, false },
{ ATSTRINGHASH("SwitchShortMichaelIn",0x10fb8eb6), NULL, false },
{ ATSTRINGHASH("SwitchShortMichaelMid",0x8a7d2d1b), NULL, false },
{ ATSTRINGHASH("SwitchHUDFranklinIn",0x07e17b1a), NULL, false },
{ ATSTRINGHASH("SwitchHUDFranklinOut",0x106d489b), NULL, false },
{ ATSTRINGHASH("SwitchOpenFranklinIn",0x776fe6f3), NULL, false },
{ ATSTRINGHASH("SwitchOpenFranklinMid",0x22d04949), NULL, false },
{ ATSTRINGHASH("SwitchOpenFranklinOut",0x1bae2d29), NULL, false },
{ ATSTRINGHASH("SwitchSceneFranklin",0x66fb30fe), NULL, false },
{ ATSTRINGHASH("SwitchShortFranklinIn",0xc2225603), NULL, false },
{ ATSTRINGHASH("SwitchShortFranklinMid",0x6ba28347), NULL, false },
{ ATSTRINGHASH("SwitchHUDTrevorIn",0xee33c206), NULL, false },
{ ATSTRINGHASH("SwitchHUDTrevorOut",0x08c59987), NULL, false },
{ ATSTRINGHASH("SwitchOpenTrevorIn",0x1a4e629d), NULL, false },
{ ATSTRINGHASH("SwitchOpenTrevorMid",0xfb80db62), NULL, false },
{ ATSTRINGHASH("SwitchOpenTrevorOut",0x87f585eb), NULL, false },
{ ATSTRINGHASH("SwitchSceneTrevor",0xee02c950), NULL, false },
{ ATSTRINGHASH("SwitchShortTrevorIn",0xd91bdd3), NULL, false },
{ ATSTRINGHASH("SwitchShortTrevorMid",0x67ae7f68), NULL, false },
{ ATSTRINGHASH("SwitchHUDIn",0xb2895e1b), NULL, false },
{ ATSTRINGHASH("SwitchHUDOut",0x9ac7662a), NULL, false },
{ ATSTRINGHASH("SwitchOpenNeutral",0xc36d571f), NULL, false },
{ ATSTRINGHASH("SwitchOpenNeutralIn",0xe5cafc5b), NULL, false },
{ ATSTRINGHASH("SwitchOpenNeutralMid",0x6cbdd92d), NULL, false },
{ ATSTRINGHASH("SwitchOpenNeutralOut",0x2977920c), NULL, false },
{ ATSTRINGHASH("SwitchShortNeutralIn",0x66fcfd3e), NULL, false },
{ ATSTRINGHASH("SwitchShortNeutralMid",0x19035b64), NULL, false },
// FIB2 sniping from chopper
{ ATSTRINGHASH("CamPushInFranklin",0xee41dacb), NULL, false },
// Player killing a ped
{ ATSTRINGHASH("PedGunKill",0x9cfb4c6e), NULL, false },
// Lektor motorbike
{ ATSTRINGHASH("LectroKERS",0x4020f9f7), NULL, false },
{ ATSTRINGHASH("LectroKERSOut",0xf53d5701), NULL, false },
// Player Death
{ ATSTRINGHASH("DeathFailNeutralIn",0x7842c770), NULL, false },
{ ATSTRINGHASH("DeathFailFranklinIn",0x1074ad81), NULL, false },
{ ATSTRINGHASH("DeathFailMichaelIn",0x19509151), NULL, false },
{ ATSTRINGHASH("DeathFailTrevorIn",0x2fb801ed), NULL, false },
{ ATSTRINGHASH("DeathFailOut",0x4ac9635a), NULL, false },
// Green Respawn
{ ATSTRINGHASH("RespawnMichael",0x1df47fe2), NULL, false },
{ ATSTRINGHASH("RespawnTrevor",0x7e421bfe), NULL, false },
{ ATSTRINGHASH("RespawnFranklin",0x6f193857), NULL, false },
// Mission Success
{ ATSTRINGHASH("SuccessNeutral",0xe59492af), NULL, false },
{ ATSTRINGHASH("SuccessFranklin",0x33460f26), NULL, false },
{ ATSTRINGHASH("SuccessMichael",0x4c589541), NULL, false },
{ ATSTRINGHASH("SuccessTrevor",0x96631bd8), NULL, false },
// Special Ability
{ ATSTRINGHASH("REDMIST",0xe4357941), NULL, false }, // Trevor
{ ATSTRINGHASH("REDMISTOut",0x8889c8b6), NULL, false },
{ ATSTRINGHASH("BulletTime",0x446dd766), NULL, false }, // Micheal
{ ATSTRINGHASH("BulletTimeOut",0x0d2427e1), NULL, false },
{ ATSTRINGHASH("DrivingFocus",0x2a135c43), NULL, false }, // Franklin
{ ATSTRINGHASH("DrivingFocusOut",0x6e5c08ad), NULL, false },
{ ATSTRINGHASH("DefaultBlinkIntro",0xe7590770), NULL, false },
{ ATSTRINGHASH("DefaultBlinkOutro",0x22d8dce3), NULL, false },
// Pause Menu
{ ATSTRINGHASH("PauseMenuFranklinIn",0x80802e11), NULL, false },
{ ATSTRINGHASH("PauseMenuFranklinOut",0x388a0486), NULL, false },
{ ATSTRINGHASH("PauseMenuMichaelIn",0xa0544755), NULL, false },
{ ATSTRINGHASH("PauseMenuMichaelOut",0x9cbeebee), NULL, false },
{ ATSTRINGHASH("PauseMenuTrevorIn",0xb3ed728), NULL, false },
{ ATSTRINGHASH("PauseMenuTrevorOut",0x24239ef8), NULL, false },
{ ATSTRINGHASH("PauseMenuIn",0x44dabe22), NULL, false },
{ ATSTRINGHASH("PauseMenuOut",0x725941f7), NULL, false },
// CnC Pause Menu
{ ATSTRINGHASH("PauseMenuCopsIn",0x786AA98A), NULL, false },
{ ATSTRINGHASH("PauseMenuCopsOut",0x6A4AFA62), NULL, false },
{ ATSTRINGHASH("PauseMenuCrooksIn",0x833600C0), NULL, false },
{ ATSTRINGHASH("PauseMenuCrooksOut",0x9B727EB3), NULL, false },
// Misc
{ ATSTRINGHASH("MP_race_crash",0xd78f4146), NULL, false }, // Filter effect from respawning in an MP air race
{ ATSTRINGHASH("FocusIn",0x99d8ef9), NULL, false }, // Tow Truck Focus
{ ATSTRINGHASH("FocusOut",0xd8664e89), NULL, false }, // Tow Truck Focus
{ ATSTRINGHASH("RaceTurbo",0x600d9d85), NULL, false }, // Turbo Boost at start of a race.
{ ATSTRINGHASH("MP_Bull_tost",0xa51fd197), NULL, false }, // Bullshark testosterone
{ ATSTRINGHASH("MP_Bull_tost_Out",0x876bff8e), NULL, false },
{ ATSTRINGHASH("MP_Killstreak",0xe0940793), NULL, false }, // Kill streak
{ ATSTRINGHASH("MP_Killstreak_Out",0xaba3efb6), NULL, false },
{ ATSTRINGHASH("MP_Loser_Streak",0x5d964052), NULL, true }, // Losing streak
{ ATSTRINGHASH("MP_Loser_Streak_Out",0xef8c38c5), NULL, false },
{ ATSTRINGHASH("MP_Powerplay",0xab722f6a), NULL, true },
{ ATSTRINGHASH("MP_Powerplay_Out",0x290be57e), NULL, false },
{ ATSTRINGHASH("MP_Celeb_Preload_Fade",0xee6b9efe), NULL, false }, // Gang attack wins
{ ATSTRINGHASH("MP_Celeb_Win",0xd4851430), NULL, false },
{ ATSTRINGHASH("MP_Celeb_Win_Out",0xfde9ac37), NULL, false },
{ ATSTRINGHASH("PeyoteIn",0x6a7b1294), NULL, true },
{ ATSTRINGHASH("PeyoteOut",0xe7e4f08c), NULL, true },
{ ATSTRINGHASH("PeyoteEndIn",0x91304b90), NULL, true },
{ ATSTRINGHASH("PeyoteEndOut",0xf878a71b), NULL, true },
{ ATSTRINGHASH("PennedIn",0x5aae758f), NULL, true },
{ ATSTRINGHASH("PennedInOut",0xd43dcab2), NULL, true },
{ ATSTRINGHASH("MinigameEndNeutral",0x507bf7b6), NULL, false },
{ ATSTRINGHASH("CrossLine", 0x6B66A555), NULL, false },
{ ATSTRINGHASH("CrossLineOut", 0xc67eb9dc), NULL, false },
{ ATSTRINGHASH("StuntFast", 0xee553f83), NULL, false },
{ ATSTRINGHASH("StuntSlow", 0x6824d91c), NULL, false },
{ ATSTRINGHASH("VolticTurbo", 0xe43bb5e5), NULL, false },
{ ATSTRINGHASH("ArenaEMP", 0xcfdef9f5), NULL, false },
{ ATSTRINGHASH("ArenaEMPOut", 0xca951551), NULL, false },
{ ATSTRINGHASH("CnC_FlashGrenade_Strong", 0xef6e233b), NULL, false },
{ ATSTRINGHASH("SwitchOpenNeutralCnC", 0x53f72a36), NULL, false },
{ ATSTRINGHASH("SwitchOpenCrookIn", 0x2767da43), NULL, false },
{ ATSTRINGHASH("SwitchOpenCrookMid", 0x7990e0dd), NULL, false },
{ ATSTRINGHASH("SwitchOpenCrookOut", 0x9bebec20), NULL, false },
{ ATSTRINGHASH("DanceIntensity01", 0x4676ff8c), NULL, true },
{ ATSTRINGHASH("DanceIntensity02", 0x30afd3fe), NULL, true },
{ ATSTRINGHASH("DanceIntensity03", 0x178d21b9), NULL, true },
};
AnimPostFXManagerReplayEffectLookup::AnimPostFXReplayEffectTableEntry AnimPostFXManagerReplayEffectLookup::m_PostEffectReplayFreeCamRemovalLookupTable[] =
{
{ ATSTRINGHASH("DrugsDrivingIn",0x8b05df27), NULL, false }
};
void AnimPostFXManagerReplayEffectLookup::Init(AnimPostFXManager *pFxManager)
{
// Clear out any pointers in the tables
Reset();
// And reset them
int nElements = sizeof(m_PostEffectReplayGameCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
for(int i=0; i<nElements; i++)
{
AnimPostFXReplayEffectTableEntry &tableEntry = m_PostEffectReplayGameCamRemovalLookupTable[i];
tableEntry.m_pStack = pFxManager->Get(tableEntry.m_Name);
}
nElements = sizeof(m_PostEffectReplayFreeCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
for(int i=0; i<nElements; i++)
{
AnimPostFXReplayEffectTableEntry &tableEntry = m_PostEffectReplayFreeCamRemovalLookupTable[i];
tableEntry.m_pStack = pFxManager->Get(tableEntry.m_Name);
}
}
void AnimPostFXManagerReplayEffectLookup::Shutdown()
{
}
void AnimPostFXManagerReplayEffectLookup::Reset()
{
int nElements = sizeof(m_PostEffectReplayGameCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
for(int i=0;i<nElements;i++)
{
m_PostEffectReplayGameCamRemovalLookupTable[i].m_pStack = NULL;
}
nElements = sizeof(m_PostEffectReplayFreeCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
for(int i=0;i<nElements;i++)
{
m_PostEffectReplayFreeCamRemovalLookupTable[i].m_pStack = NULL;
}
}
bool AnimPostFXManagerReplayEffectLookup::AreAllGameCamStacksIdle(bool forThumbnail, int* activeIndex)
{
bool bAllIdle = true;
if(activeIndex) *activeIndex = -1;
int nElements = sizeof(m_PostEffectReplayGameCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
for(int i=0;i<nElements;i++)
{
if(m_PostEffectReplayGameCamRemovalLookupTable[i].m_pStack)
{
if(m_PostEffectReplayGameCamRemovalLookupTable[i].m_pStack->IsIdle() == false)
{
if(forThumbnail && m_PostEffectReplayGameCamRemovalLookupTable[i].m_AllowForThumbnail)
{
// Ignore this filter for thumbnails (allow the thumbnail to be created.)
}
else
{
bAllIdle = false;
if(activeIndex) *activeIndex = i;
break;
}
}
}
}
return bAllIdle;
}
bool AnimPostFXManagerReplayEffectLookup::IsStackDisabledForGameCam(const RegisteredAnimPostFXStack* pStack)
{
int nElements = sizeof(m_PostEffectReplayGameCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
for(int i=0;i<nElements;i++)
{
if(m_PostEffectReplayGameCamRemovalLookupTable[i].m_pStack == pStack)
{
return true;
}
}
return false;
}
bool AnimPostFXManagerReplayEffectLookup::IsStackDisabledForFreeCam(const RegisteredAnimPostFXStack* pStack)
{
if( IsStackDisabledForGameCam(pStack) )
{
return true;
}
int nElements = sizeof(m_PostEffectReplayFreeCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
for(int i=0;i<nElements;i++)
{
if(m_PostEffectReplayFreeCamRemovalLookupTable[i].m_pStack == pStack)
{
return true;
}
}
return false;
}
#if !__FINAL
const char* AnimPostFXManagerReplayEffectLookup::GetFilterName(int index)
{
int nElements = sizeof(m_PostEffectReplayGameCamRemovalLookupTable) / sizeof(AnimPostFXReplayEffectTableEntry);
if(index < 0 || index >= nElements)
return "Not Filter";
return m_PostEffectReplayGameCamRemovalLookupTable[index].m_Name.GetCStr();
}
#endif // !__FINAL
#endif // GTA_REPLAY
//////////////////////////////////////////////////////////////////////////
//
AnimPostFXManager* AnimPostFXManager::smp_Instance = NULL;
#define ANIMPOSTFX_METADATA_FILE_LOAD_NAME "platform:/data/effects/animpostfx"
#define ANIMPOSTFX_METADATA_FILE_SAVE_NAME RS_ASSETS "/export/data/effects/animpostfx"
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
const char* AnimPostFXManager::ms_debugUserTypeStr[] = { "CODE_MISC",
"CODE_PEDKILL",
"CODE_CAMFLASH",
"CODE_SELECTIONWHEEL",
"CODE_LONGSWITCH",
"CODE_SHORTSWITCH",
"CODE_KERSBOOST",
"CODE_FPPROP",
"SCRIPT",
"CODE_SPECIAL_ABILITY"
};
#endif
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
static bool AnimPostFXIsSpecialAbility(u32 strHash)
{
const u32 specialFxHash[] = {
ATSTRINGHASH("REDMIST", 0xe4357941),
ATSTRINGHASH("REDMISTOut", 0x8889c8b6),
ATSTRINGHASH("BulletTime", 0x446dd766),
ATSTRINGHASH("BulletTimeOut", 0x0d2427e1),
ATSTRINGHASH("DrivingFocus", 0x2a135c43),
ATSTRINGHASH("DrivingFocusOut", 0x6e5c08ad),
ATSTRINGHASH("DefaultBlinkIntro", 0xe7590770),
ATSTRINGHASH("DefaultBlinkOutro", 0x22d8dce3)
};
for (u32 i = 0; i < NELEM(specialFxHash); i++)
{
if (specialFxHash[i] == strHash)
return true;
}
return false;
}
#endif
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::CancelStartRequest(atHashString effectName, eAnimPostFXUser BANK_ONLY(user))
{
RegisteredAnimPostFXStack* pStack = Get(effectName);
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] CancelStartRequest \"%s\" requested by %s", effectName.TryGetCStr(), ms_debugUserTypeStr[user]);
}
#endif
if (pStack)
{
pStack->CancelStartRequest();
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Start(atHashString effectName, u32 duration, bool bLoop, bool bUserControlsFade, bool bRunOnceOnly, int frameDelay, eAnimPostFXUser BANK_ONLY(user))
{
#if __BANK
if (m_bBlockRequestFromUser && RemapForBlockCompare(m_blockedUserType) == RemapForBlockCompare(user))
{
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] BLOCKED Start \"%s\" duration: %u, loop: %d, userFade: %d requested by %s", effectName.TryGetCStr(), duration, bLoop, bUserControlsFade, ms_debugUserTypeStr[user]);
}
return;
}
#endif
#if __BANK
if (m_bDebugOutput && AnimPostFXIsSpecialAbility(effectName.GetHash()) == false)
{
Displayf("[ANIMPOSTFX] Start \"%s\" duration: %u, loop: %d, userFade: %d requested by %s", effectName.TryGetCStr(), duration, bLoop, bUserControlsFade, ms_debugUserTypeStr[user]);
}
#endif
const bool bIsRunning = IsRunning(effectName);
// respect run-once flag
if (bRunOnceOnly && bIsRunning)
{
return;
}
RegisteredAnimPostFXStack* pStack = Get(effectName);
if( !bIsRunning && frameDelay > 0 )
{
pStack->SetLoopEnabled(bLoop);
pStack->SetUserFadeOverride(bUserControlsFade);
pStack->Start(duration);
pStack->SetFrameDelay(frameDelay);
AddToFrameDelayList(pStack);
return;
}
// if we found the stack and it's already running, don't add it again,
// just request a start
if (pStack && (bIsRunning || AddToActiveList(pStack)))
{
pStack->SetLoopEnabled(bLoop);
pStack->SetUserFadeOverride(bUserControlsFade);
pStack->Start(duration);
// Generate event if we've been assigned to a group...
if (pStack->GetGroupId() != -1 && pStack->DoesEventTypeMatch(ANIMPOSTFX_EVENT_ON_START))
{
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX][EVENT] Start event triggered for \"%s\"", effectName.TryGetCStr());
}
#endif
OnEventTriggered(pStack, ANIMPOSTFX_EVENT_ON_START);
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Stop(atHashString effectName, eAnimPostFXUser BANK_ONLY(user))
{
#if __BANK
if (m_bBlockRequestFromUser && RemapForBlockCompare(m_blockedUserType) == RemapForBlockCompare(user))
{
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] BLOCKED Stop \"%s\" requested by %s", effectName.TryGetCStr(), ms_debugUserTypeStr[user]);
}
return;
}
#endif
#if __BANK
if (m_bDebugOutput && AnimPostFXIsSpecialAbility(effectName.GetHash()) == false)
{
Displayf("[ANIMPOSTFX] Stop \"%s\" requested by %s", effectName.TryGetCStr(), ms_debugUserTypeStr[user]);
}
#endif
RegisteredAnimPostFXStack* pStack = Get(effectName);
if (pStack)
{
pStack->Stop();
// Generate event if we've been assigned to a group (no support for looped effects for now)...
if (pStack->GetGroupId() != -1 && pStack->DoesEventTypeMatch(ANIMPOSTFX_EVENT_ON_STOP))
{
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX][EVENT] Stop event triggered for \"%s\"", effectName.TryGetCStr());
}
#endif
OnEventTriggered(pStack, ANIMPOSTFX_EVENT_ON_STOP);
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::SetUserFadeLevel(atHashString effectName, float level)
{
RegisteredAnimPostFXStack* pStack = Get(effectName);
if (pStack)
{
pStack->SetUserFadeLevel(level);
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::StopAll(eAnimPostFXUser BANK_ONLY(user))
{
if (IsIdle())
{
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] StopAll requested by %s (NOTHING TO STOP)", ms_debugUserTypeStr[user]);
}
#endif
return;
}
#if __BANK
if (m_bBlockRequestFromUser && RemapForBlockCompare(m_blockedUserType) == RemapForBlockCompare(user))
{
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] BLOCKED StopAll requested by %s", ms_debugUserTypeStr[user]);
}
return;
}
#endif
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] StopAll requested by %s", ms_debugUserTypeStr[user]);
}
#endif
m_bStopAllRequested = true;
};
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXManager::IsRunning(atHashString effectName) const
{
const RegisteredAnimPostFXStack* pStack = Get(effectName);
if (pStack)
{
return (pStack->IsIdle() == false);
}
return false;
}
bool AnimPostFXManager::IsStartPending(atHashString effectName) const
{
const RegisteredAnimPostFXStack* pStack = Get(effectName);
if (pStack)
{
return pStack->IsStartPending();
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//
float AnimPostFXManager::GetCurrentTime(atHashString effectName) const
{
const RegisteredAnimPostFXStack* pStack = Get(effectName);
if (pStack)
{
return pStack->GetCurrentTime();
}
return 1.0f;
}
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXManager::AddToFrameDelayList(RegisteredAnimPostFXStack* pStack)
{
if (m_FrameDelayStacks.GetNumFree() == 0 || pStack == NULL)
{
return false;
}
m_FrameDelayStacks.InsertSorted(pStack);
return true;
}
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXManager::AddToActiveList(RegisteredAnimPostFXStack* pStack)
{
if (m_ActiveStacks.GetNumFree() == 0 || pStack == NULL)
{
return false;
}
m_ActiveStacks.InsertSorted(pStack);
return true;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Init()
{
#if __BANK
// This is here because ms_debugUserTypeStr is a private...
CompileTimeAssert(NELEM(AnimPostFXManager::ms_debugUserTypeStr) == AnimPostFXManager::kCount);
#endif // __BANK
m_RegisteredStacks.Reset();
m_ActiveStacks.Init(8);
m_FrameDelayStacks.Init(4);
m_bStopAllRequested = false;
#if __BANK
m_bBlockRequestFromUser = false;
m_blockedUserType = kDefault;
#endif // __BANK
#if __BANK
m_EditTool.Init();
m_bDebugOutput = PARAM_animpostfx_debug.Get() ? true : false;
m_bDebugDisplay = PARAM_animpostfx_debug.Get() ? true : false;
m_userTypeComboIdx = 0;
m_pUserTypeCombo = NULL;
m_bDebugAllowPausing = false;
// EditTool::OnLoadData will call "AnimPostFXManager::LoadMetaData"
m_EditTool.OnLoadData();
#else
LoadMetaData();
#endif
m_eventManager.Init();
m_polledEventManager.Init();
#if GTA_REPLAY
AnimPostFXManagerReplayEffectLookup::Init(this);
#endif //GTA_REPLAY
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Shutdown()
{
m_eventManager.Shutdown();
m_polledEventManager.Shutdown();
m_bStopAllRequested = false;
m_RegisteredStacks.Reset();
m_ActiveStacks.Shutdown();
m_FrameDelayStacks.Shutdown();
#if __BANK
m_EditTool.Shutdown();
#endif
#if GTA_REPLAY
AnimPostFXManagerReplayEffectLookup::Shutdown();
#endif //GTA_REPLAY
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Update()
{
if (m_FrameDelayStacks.GetNumUsed() != 0)
{
ActiveStackNode* pNode = m_FrameDelayStacks.GetFirst()->GetNext();
while(pNode != m_FrameDelayStacks.GetLast())
{
RegisteredAnimPostFXStack* pCurEntry = (pNode->item);
ActiveStackNode* pLastNode = pNode;
pNode = pNode->GetNext();
pCurEntry->FrameDelayUpdate();
if( pCurEntry->GetFrameDelay() == 0 )
{
if( AddToActiveList(pCurEntry) )
{
m_FrameDelayStacks.Remove(pLastNode);
}
}
}
}
if (m_ActiveStacks.GetNumUsed() == 0)
{
m_polledEventManager.Update();
return;
}
if (m_bStopAllRequested)
{
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] StopAll");
}
#endif
ActiveStackNode* pStopNode = m_ActiveStacks.GetFirst()->GetNext();
while(pStopNode != m_ActiveStacks.GetLast())
{
RegisteredAnimPostFXStack* pCurEntryToStop = (pStopNode->item);
pStopNode = pStopNode->GetNext();
pCurEntryToStop->Stop();
}
m_Crossfade.Stop();
m_bStopAllRequested = false;
}
m_Crossfade.Update();
ActiveStackNode* pNode = m_ActiveStacks.GetFirst()->GetNext();
while(pNode != m_ActiveStacks.GetLast())
{
RegisteredAnimPostFXStack* pCurEntry = (pNode->item);
ActiveStackNode* pLastNode = pNode;
pNode = pNode->GetNext();
if (pCurEntry->IsStartPending() == false && pCurEntry->IsIdle())
{
// Forget about it
m_ActiveStacks.Remove(pLastNode);
}
else
{
#if __BANK
if (m_bDebugDisplay)
{
grcDebugDraw::AddDebugOutput(Color_white, "[ANIMPOSTFX] \"%s\"", pCurEntry->m_Name.TryGetCStr());
}
#endif
pCurEntry->Update();
// Generate event if...
u32 eventTime = pCurEntry->GetEventTimeMS();
bool bShouldTriggerEvent = (pCurEntry->GetGroupId() != -1 && // group is valid
eventTime != 0U && // event time is valid
pCurEntry->HasFrameEventTriggered() == false && // we haven't triggered the event
pCurEntry->IsStopPending() == false && // stop request isn't pending
pCurEntry->IsStartPending() == false); // start request isn't pending
if (bShouldTriggerEvent && pCurEntry->GetCurrentTimeMS() >= eventTime && pCurEntry->DoesEventTypeMatch(ANIMPOSTFX_EVENT_ON_FRAME))
{
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX][EVENT] Frame event triggered for \"%s\" at time: %u (set up for time: %u)", pCurEntry->m_Name.TryGetCStr(), pCurEntry->GetCurrentTimeMS(), eventTime);
}
#endif
// Mark event as dispatched...
pCurEntry->MarkFrameEventTriggered(true);
OnEventTriggered(pCurEntry, ANIMPOSTFX_EVENT_ON_FRAME);
}
}
}
m_polledEventManager.Update();
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::StartCrossfade(atHashString effectNameIn, atHashString effectNameOut, u32 duration, eAnimPostFXUser BANK_ONLY(user))
{
#if __BANK
if (m_bBlockRequestFromUser && RemapForBlockCompare(m_blockedUserType) == RemapForBlockCompare(user))
{
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] BLOCKED Start Crossfade in: \"%s\" out: \"%s\" duration: %u requested by %s", effectNameIn.TryGetCStr(), effectNameOut.TryGetCStr(), duration, ms_debugUserTypeStr[user]);
}
return;
}
#endif
m_Crossfade.Start(effectNameIn, effectNameOut, duration);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::StopCrossfade(eAnimPostFXUser BANK_ONLY(user))
{
#if __BANK
if (m_bBlockRequestFromUser && RemapForBlockCompare(m_blockedUserType) == RemapForBlockCompare(user))
{
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] BLOCKED Stop Crossfade");
}
return;
}
#endif
#if __BANK
if (m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] Stop Crossfade");
}
#endif
m_Crossfade.Stop();
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::CrossfadeHelper::Start(atHashString effectNameIn, atHashString effectNameOut, u32 duration)
{
#if __BANK
if (ANIMPOSTFXMGR.m_bDebugOutput)
{
Displayf("[ANIMPOSTFX] Start Crossfade in: \"%s\" out: \"%s\" duration: %u", effectNameIn.TryGetCStr(), effectNameOut.TryGetCStr(), duration);
}
#endif
if (m_StartRequested || m_StopRequested || m_Active)
{
return;
}
m_InStackName = effectNameIn;
m_OutStackName = effectNameOut;
m_Duration = duration;
m_StartRequested = true;
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::CrossfadeHelper::Stop()
{
if (m_Active)
{
m_StopRequested = true;
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::CrossfadeHelper::Update()
{
if (m_StartRequested)
{
// Start the fade in stack
ANIMPOSTFXMGR.Start(m_InStackName, 0U, true, false, false, 0U, AnimPostFXManager::kDefault);
// Cache it
m_pInStack = ANIMPOSTFXMGR.Get(m_InStackName);
// Bail if it failed
if (m_pInStack == NULL)
{
Reset();
return;
}
// Setup fade level override
m_pInStack->SetUserFadeOverride(true);
m_pInStack->SetUserFadeLevel(0.0f);
// Try caching the stack to fade out
m_pOutStack = ANIMPOSTFXMGR.Get(m_OutStackName);
if (m_pOutStack)
{
m_pOutStack->SetUserFadeOverride(true);
m_pOutStack->SetUserFadeLevel(1.0f);
}
m_StartTime = fwTimer::GetSystemTimeInMilliseconds();
m_StartRequested = false;
m_Active = true;
m_State = AnimatedPostFX::POSTFX_GOING_IN;
}
else if (m_StopRequested)
{
m_StartTime = fwTimer::GetSystemTimeInMilliseconds();
m_StopRequested = false;
m_StartRequested = false;
m_Active = true;
m_State = AnimatedPostFX::POSTFX_GOING_OUT;
}
else if (m_Active)
{
UpdateTiming();
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::CrossfadeHelper::UpdateTiming()
{
u32 c_uCurrentTime = fwTimer::GetSystemTimeInMilliseconds();
u32 timeDiff = (c_uCurrentTime - m_StartTime);
float fInterp = (float)(timeDiff) / (float)m_Duration;
if (m_State == AnimatedPostFX::POSTFX_GOING_IN)
{
if (m_pInStack)
{
m_pInStack->SetUserFadeLevel(fInterp);
if (m_pOutStack)
{
m_pOutStack->SetUserFadeLevel(1.0f-fInterp);
}
}
}
else if (m_State == AnimatedPostFX::POSTFX_ON_HOLD)
{
if (m_pInStack)
{
m_pInStack->SetUserFadeLevel(1.0f);
if (m_pOutStack)
{
m_pOutStack->SetUserFadeLevel(0.0f);
}
}
}
else if (m_State == AnimatedPostFX::POSTFX_GOING_OUT)
{
if (m_pInStack)
{
m_pInStack->SetUserFadeLevel(1.0f-fInterp);
if (m_pOutStack)
{
m_pOutStack->SetUserFadeLevel(fInterp);
}
if (fInterp >= 1.0f)
{
m_pInStack->SetUserFadeOverride(false);
m_pInStack->Stop();
if (m_pOutStack)
{
m_pOutStack->SetUserFadeOverride(false);
m_pOutStack->SetUserFadeLevel(1.0f);
}
Reset();
}
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::ApplyModifiers(tcKeyframeUncompressed& keyframe REPLAY_ONLY(, eTimeCyclePhase currentPhase))
{
if (m_ActiveStacks.GetNumUsed() == 0)
{
return;
}
ActiveStackNode* pNode = m_ActiveStacks.GetFirst()->GetNext();
while(pNode != m_ActiveStacks.GetLast())
{
RegisteredAnimPostFXStack* pCurEntry = (pNode->item);
#if GTA_REPLAY
bool shouldApply = true;
if( currentPhase == TIME_CYCLE_PHASE_REPLAY_NORMAL )
{
if( AnimPostFXManagerReplayEffectLookup::IsStackDisabledForGameCam( pCurEntry ) )
{
shouldApply = false;
}
}
else if( currentPhase == TIME_CYCLE_PHASE_REPLAY_FREECAM )
{
if( AnimPostFXManagerReplayEffectLookup::IsStackDisabledForFreeCam( pCurEntry ) )
{
shouldApply = false;
}
}
if(shouldApply)
#endif //GTA_REPLAY
{
pCurEntry->ApplyModifiers(keyframe);
}
pNode = pNode->GetNext();
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Reset()
{
ActiveStackNode* pNode = m_ActiveStacks.GetFirst()->GetNext();
while(pNode != m_ActiveStacks.GetLast())
{
RegisteredAnimPostFXStack* pCurEntry = (pNode->item);
ActiveStackNode* pLastNode = pNode;
pNode = pNode->GetNext();
pCurEntry->Reset();
m_ActiveStacks.Remove(pLastNode);
}
m_Crossfade.Reset();
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::ClassInit()
{
smp_Instance = rage_new AnimPostFXManager();
Assert(smp_Instance);
if (smp_Instance)
{
ANIMPOSTFXMGR.Init();
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::ClassShutdown()
{
ANIMPOSTFXMGR.Shutdown();
delete smp_Instance;
smp_Instance = NULL;
}
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void AnimPostFXManager::AddWidgets(rage::bkBank& bank )
{
bank.PushGroup("Anim PostFX Manager", false);
bank.AddToggle("Enable Debug Output", &m_bDebugOutput);
bank.AddToggle("Enable Debug Display", &m_bDebugDisplay);
bank.AddToggle("Allow Pausing", &m_bDebugAllowPausing);
bank.AddToggle("Block Requests from User", &m_bBlockRequestFromUser);
m_pUserTypeCombo = bank.AddCombo("User Type To Block", &m_userTypeComboIdx, NELEM(ms_debugUserTypeStr), &ms_debugUserTypeStr[0], datCallback(MFA(AnimPostFXManager::OnUserTypeToBlockSelected), (datBase*)this));
m_EditTool.AddWidgets(bank);
m_polledEventManager.AddWidgets(bank);
bank.PopGroup();
}
#endif
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void AnimPostFXManager::OnUserTypeToBlockSelected()
{
m_blockedUserType = (eAnimPostFXUser)m_userTypeComboIdx;
}
#endif
//////////////////////////////////////////////////////////////////////////
//
bool AnimPostFXManager::LoadMetaData()
{
bool bOk = false;
#if RSG_BANK
if(PARAM_animpostfx_enable_metadata_preview.Get())
{
USE_DEBUG_MEMORY();
const char* pSavePath = (m_EditTool.GetSavePath()[0] != 0 ? m_EditTool.GetSavePath() : ANIMPOSTFX_METADATA_FILE_SAVE_NAME);
bOk = PARSER.LoadObject(pSavePath, "pso.meta", *this);
}
#endif
bOk = bOk || fwPsoStoreLoader::LoadDataIntoObject(ANIMPOSTFX_METADATA_FILE_LOAD_NAME, META_FILE_EXT, *this);
// if data is loaded ok, run the postload callback the parser is supposed to call
// tried to have the pso loader do this automatically by passing a flag to invoke the callback but didn't get too far
if (bOk)
{
for (int i = 0; i < m_RegisteredStacks.GetCount(); i++)
{
if (m_RegisteredStacks[i].GetStack() != NULL)
m_RegisteredStacks[i].GetStack()->PostLoadCallback();
}
}
//PARSER.SaveObject(ANIMPOSTFX_METADATA_FILE_LOAD_NAME, "meta", this);
return bOk;
}
//////////////////////////////////////////////////////////////////////////
//
const RegisteredAnimPostFXStack* AnimPostFXManager::Get(atHashString name) const
{
for (int i = 0; i < m_RegisteredStacks.GetCount(); i++)
{
if (m_RegisteredStacks[i].GetName() == name)
{
return &m_RegisteredStacks[i];
}
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
//
RegisteredAnimPostFXStack* AnimPostFXManager::Get(atHashString name)
{
for (int i = 0; i < m_RegisteredStacks.GetCount(); i++)
{
if (m_RegisteredStacks[i].GetName() == name)
{
return &m_RegisteredStacks[i];
}
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
//
#if __BANK && !__FINAL
bool AnimPostFXManager::SaveMetaData()
{
const char* pSavePath = (m_EditTool.GetSavePath() != NULL ? m_EditTool.GetSavePath() : ANIMPOSTFX_METADATA_FILE_SAVE_NAME);
bool bOk = PARSER.SaveObject(pSavePath, "pso.meta", this);
if (bOk == false)
{
Displayf("AnimPostFXManager::SaveMetaData: Failed to save data for \"%s\"", ANIMPOSTFX_METADATA_FILE_SAVE_NAME);
}
return bOk;
}
#endif
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Add(RegisteredAnimPostFXStack& fxStack)
{
m_RegisteredStacks.PushAndGrow(fxStack);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::Remove(atHashString name)
{
for (int i = 0; i < m_RegisteredStacks.GetCount(); i++)
{
if (m_RegisteredStacks[i].GetName() == name)
{
m_RegisteredStacks.Delete(i);
return;
}
}
}
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
atHashString AnimPostFXManager::EditTool::ms_newStackName = "[NEW]";
char AnimPostFXManager::EditTool::ms_testStackName[6][32];
char AnimPostFXManager::EditTool::ms_testCrossfadeName[2][32];
char AnimPostFXManager::EditTool::ms_savePath[256] = {0};
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::Init()
{
m_RegStackNames.Reset();
m_RegStackNames.Reserve(4);
m_RegStackNamesComboIdx = -1;
m_pRegStackNamesCombo = NULL;
m_editableStack.ClearForEdit();
UpdateRegStackNamesList(ANIMPOSTFXMGR.m_RegisteredStacks);
m_testStackDuration[0] = 0U;
m_testStackDuration[1] = 0U;
m_testStackDuration[2] = 0U;
m_testStackDuration[3] = 0U;
m_testStackDuration[4] = 0U;
m_testStackDuration[5] = 0U;
ms_testStackName[0][0] = 0;
ms_testStackName[1][0] = 0;
ms_testStackName[2][0] = 0;
ms_testStackName[3][0] = 0;
ms_testStackName[4][0] = 0;
ms_testStackName[5][0] = 0;
m_bTestLoopEnabled[0] = false;
m_bTestLoopEnabled[1] = false;
m_bTestLoopEnabled[2] = false;
m_bTestLoopEnabled[3] = false;
m_bTestLoopEnabled[4] = false;
m_bTestLoopEnabled[5] = false;
ms_testCrossfadeName[0][0] = 0;
ms_testCrossfadeName[1][0] = 0;
m_testCrossfadeDuration = 0U;
if (PARAM_animpostfx_save_path.Get())
{
const char* pSavePath = NULL;
PARAM_animpostfx_save_path.Get(pSavePath);
strcpy(&ms_savePath[0], pSavePath);
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::Shutdown()
{
m_RegStackNames.Reset();
m_RegStackNamesComboIdx = -1;
m_pRegStackNamesCombo = NULL;
m_editableStack.Init();
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::AddWidgets(rage::bkBank& bank )
{
bank.PushGroup("Edit FX Stacks");
m_pRegStackNamesCombo = bank.AddCombo("Select FX Stack For Edit", &m_RegStackNamesComboIdx, m_RegStackNames.GetCount(), &m_RegStackNames[0], datCallback(MFA(AnimPostFXManager::EditTool::OnFXStackNameSelected), (datBase*)this));
bank.AddSeparator();
bank.PushGroup("Current FX Stack Selected For Edit", false);
PARSER.AddWidgets(bank, &m_editableStack);
bank.AddSeparator();
bank.AddButton("Save Current FX Stack", datCallback(MFA(AnimPostFXManager::EditTool::OnSaveCurrentFXStack), (datBase*)this));
bank.AddButton("Delete Current FX Stack", datCallback(MFA(AnimPostFXManager::EditTool::OnDeleteCurrentFXStack), (datBase*)this));
bank.AddSeparator();
bank.AddButton("SAVE DATA", datCallback(MFA(AnimPostFXManager::EditTool::OnSaveData), (datBase*)this));
bank.AddButton("RELOAD DATA", datCallback(MFA(AnimPostFXManager::EditTool::OnLoadData), (datBase*)this));
bank.AddSeparator();
bank.PopGroup();
bank.PopGroup();
bank.PushGroup("Test FX Crossfade", false);
bank.AddSeparator();
bank.AddText("Fade In Effect:", &ms_testCrossfadeName[0][0], 32);
bank.AddText("Fade Out Effect:", &ms_testCrossfadeName[1][0], 32);
bank.AddSlider("Duration", &m_testCrossfadeDuration, 0U, 10000U, 1U);
bank.AddSeparator();
bank.AddButton("PLAY CROSSFADE", datCallback(MFA(AnimPostFXManager::EditTool::OnStartCrossfade), (datBase*)this));
bank.AddSeparator();
bank.AddButton("STOP CROSSFADE", datCallback(MFA(AnimPostFXManager::EditTool::OnStopCrossfade), (datBase*)this));
bank.AddSeparator();
bank.PopGroup();
bank.PushGroup("Test FX Stacks", false);
bank.AddSeparator();
bank.AddText("[0] Test FX Stack Name:", &ms_testStackName[0][0], 32);
bank.AddSlider("[0] Duration For Test FX Stack (0 for default duration)", &m_testStackDuration[0], 0U, 10000U, 1U);
bank.AddToggle("[0] Loop Effect", &m_bTestLoopEnabled[0]);
bank.AddSeparator();
bank.AddButton("PLAY TEST FX STACK 0", datCallback(MFA(AnimPostFXManager::EditTool::OnStartTestStackFX0), (datBase*)this));
bank.AddSeparator();
bank.AddButton("STOP TEST FX STACK 0", datCallback(MFA(AnimPostFXManager::EditTool::OnStopTestStackFX0), (datBase*)this));
bank.AddSeparator();
bank.AddText("[1] Test FX Stack Name:", &ms_testStackName[1][0], 32);
bank.AddSlider("[1] Duration For Test FX Stack (0 for default duration)", &m_testStackDuration[1], 0U, 10000U, 1U);
bank.AddToggle("[1] Loop Effect", &m_bTestLoopEnabled[1]);
bank.AddSeparator();
bank.AddButton("[1] PLAY TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStartTestStackFX1), (datBase*)this));
bank.AddSeparator();
bank.AddButton("[1] STOP TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStopTestStackFX1), (datBase*)this));
bank.AddSeparator();
bank.AddText("[2] Test FX Stack Name:", &ms_testStackName[2][0], 32);
bank.AddSlider("[2] Duration For Test FX Stack (0 for default duration)", &m_testStackDuration[2], 0U, 10000U, 1U);
bank.AddToggle("[2] Loop Effect", &m_bTestLoopEnabled[2]);
bank.AddSeparator();
bank.AddButton("[2] PLAY TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStartTestStackFX2), (datBase*)this));
bank.AddSeparator();
bank.AddButton("[2] STOP TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStopTestStackFX2), (datBase*)this));
bank.AddSeparator();
bank.AddText("[3] Test FX Stack Name:", &ms_testStackName[3][0], 32);
bank.AddSlider("[3] Duration For Test FX Stack (0 for default duration)", &m_testStackDuration[3], 0U, 10000U, 1U);
bank.AddToggle("[3] Loop Effect", &m_bTestLoopEnabled[3]);
bank.AddSeparator();
bank.AddButton("[3] PLAY TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStartTestStackFX3), (datBase*)this));
bank.AddSeparator();
bank.AddButton("[3] STOP TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStopTestStackFX3), (datBase*)this));
bank.AddSeparator();
bank.AddText("[4] Test FX Stack Name:", &ms_testStackName[4][0], 32);
bank.AddSlider("[4] Duration For Test FX Stack (0 for default duration)", &m_testStackDuration[4], 0U, 10000U, 1U);
bank.AddToggle("[4] Loop Effect", &m_bTestLoopEnabled[4]);
bank.AddSeparator();
bank.AddButton("[4] PLAY TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStartTestStackFX4), (datBase*)this));
bank.AddSeparator();
bank.AddButton("[4] STOP TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStopTestStackFX4), (datBase*)this));
bank.AddSeparator();
bank.AddText("[5] Test FX Stack Name:", &ms_testStackName[5][0], 32);
bank.AddSlider("[5] Duration For Test FX Stack (0 for default duration)", &m_testStackDuration[5], 0U, 10000U, 1U);
bank.AddToggle("[5] Loop Effect", &m_bTestLoopEnabled[5]);
bank.AddSeparator();
bank.AddButton("[5] PLAY TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStartTestStackFX5), (datBase*)this));
bank.AddSeparator();
bank.AddButton("[5] STOP TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStopTestStackFX5), (datBase*)this));
bank.AddSeparator();
bank.AddSeparator();
bank.AddButton("PLAY ALL TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStartTestStackFXAll), (datBase*)this));
bank.AddButton("STOP ALL TEST FX STACK", datCallback(MFA(AnimPostFXManager::EditTool::OnStopTestStackFXAll), (datBase*)this));
bank.AddSeparator();
bank.PopGroup();
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::UpdateRegStackNamesList(const atArray<RegisteredAnimPostFXStack>& stacks)
{
m_RegStackNames.Reset();
m_RegStackNames.PushAndGrow(ms_newStackName.TryGetCStr());
for (int i = 0; i < stacks.GetCount(); i++)
{
m_RegStackNames.PushAndGrow(stacks[i].GetName().TryGetCStr());
}
m_RegStackNamesComboIdx = 0;
if (m_pRegStackNamesCombo)
{
m_pRegStackNamesCombo->UpdateCombo("Select FX Stack", &m_RegStackNamesComboIdx, m_RegStackNames.GetCount(), &m_RegStackNames[0], datCallback(MFA(AnimPostFXManager::EditTool::OnFXStackNameSelected), (datBase*)this));
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnStartCrossfade()
{
if (ms_testCrossfadeName[0][0] == 0 || ms_testCrossfadeName[1][0] == 0)
{
return;
}
atHashString nameIn(&(ms_testCrossfadeName[0][0]));
atHashString nameOut(&(ms_testCrossfadeName[1][0]));
ANIMPOSTFXMGR.StartCrossfade(nameIn, nameOut, m_testCrossfadeDuration,AnimPostFXManager::kDefault);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnStopCrossfade()
{
ANIMPOSTFXMGR.StopCrossfade(AnimPostFXManager::kDefault);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnStartTestStackFX(int idx)
{
if (ms_testStackName[idx][0] == 0)
{
return;
}
atHashString name(&(ms_testStackName[idx][0]));
if (m_testStackDuration > 0)
{
ANIMPOSTFXMGR.Start(name, m_testStackDuration[idx], m_bTestLoopEnabled[idx], false, false, 0U, AnimPostFXManager::kDefault);
}
else
{
ANIMPOSTFXMGR.Start(name, 0U, m_bTestLoopEnabled[idx], false, false, 0U, AnimPostFXManager::kDefault);
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnStopTestStackFX(int idx)
{
if (ms_testStackName[idx][0] == 0)
{
return;
}
atHashString name(&(ms_testStackName[idx][0]));
ANIMPOSTFXMGR.Stop(name,AnimPostFXManager::kDefault);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnFXStackNameSelected()
{
int idx = m_RegStackNamesComboIdx;
if (idx < 0 || idx > m_RegStackNames.GetCount())
{
return;
}
// if the new entry option is selected, reset the editable preset instance
atHashString selectionName = atHashString(m_RegStackNames[idx]);
if (selectionName == ms_newStackName)
{
m_editableStack.ClearForEdit();
}
else
{
// otherwise, try finding the effect stack
const RegisteredAnimPostFXStack* pStack = ANIMPOSTFXMGR.Get(selectionName);
if (pStack != NULL)
{
m_editableStack = *pStack;
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnSaveCurrentFXStack()
{
int idx = m_RegStackNamesComboIdx;
if (idx < 0 || idx > m_RegStackNames.GetCount())
{
return;
}
// if the new entry option is selected, add the entry to the collection
atHashString selectionName = atHashString(m_RegStackNames[idx]);
if (m_editableStack.Validate())
{
if (selectionName == ms_newStackName)
{
// only add to the collection is another preset with the same
// name doesn't already exist
if (ANIMPOSTFXMGR.Get(m_editableStack.GetName()) == NULL)
{
ANIMPOSTFXMGR.Add(m_editableStack);
}
}
else
{
// otherwise, try finding the preset in the collection
// and update it
RegisteredAnimPostFXStack* pStack = ANIMPOSTFXMGR.Get(selectionName);
if (pStack != NULL)
{
*pStack = m_editableStack;
if (pStack->GetStack())
pStack->GetStack()->PostLoadCallback();
}
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnDeleteCurrentFXStack()
{
int idx = m_RegStackNamesComboIdx;
if (idx < 0 || idx > m_RegStackNames.GetCount())
{
return;
}
// if the new entry option is selected, reset the editable preset instance
atHashString selectionName = atHashString(m_RegStackNames[idx]);
if (selectionName == ms_newStackName)
{
m_editableStack.ClearForEdit();
}
else
{
// otherwise, try finding the preset in the collection
// and remove it
const RegisteredAnimPostFXStack* pStack = ANIMPOSTFXMGR.Get(selectionName);
if (pStack != NULL)
{
ANIMPOSTFXMGR.Remove(pStack->GetName());
m_editableStack.ClearForEdit();
}
}
UpdateRegStackNamesList(ANIMPOSTFXMGR.m_RegisteredStacks);
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnSaveData()
{
#if !__FINAL
ANIMPOSTFXMGR.SaveMetaData();
#endif
UpdateRegStackNamesList(ANIMPOSTFXMGR.m_RegisteredStacks);
m_editableStack.ClearForEdit();
}
//////////////////////////////////////////////////////////////////////////
//
void AnimPostFXManager::EditTool::OnLoadData()
{
if (ANIMPOSTFXMGR.LoadMetaData())
{
UpdateRegStackNamesList(ANIMPOSTFXMGR.m_RegisteredStacks);
m_editableStack.ClearForEdit();
}
}
#endif // __BANK
//////////////////////////////////////////////////////////////////////////
//
PauseMenuPostFXManager* PauseMenuPostFXManager::smp_Instance = NULL;
#define PAUSEMENU_POSTFX_METADATA_FILE_NAME "common:/data/ui/pausemenueffects"
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::ClassInit()
{
smp_Instance = rage_new PauseMenuPostFXManager();
Assert(smp_Instance);
if (smp_Instance)
{
PAUSEMENUPOSTFXMGR.Init();
}
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::ClassShutdown()
{
PAUSEMENUPOSTFXMGR.Shutdown();
delete smp_Instance;
smp_Instance = NULL;
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::Init()
{
m_FadeInCheckTreshold = 0.0f;
m_FadeOutCheckTreshold = 0.0f;
LoadMetaData();
m_State = PMPOSTFX_IDLE;
m_FadeInRequested = false;
m_FadeOutRequested = false;
// Base game effects
m_pFadeInEffects[PAUSEFX_FRANKLIN] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuFranklinIn", 0x80802e11));
m_pFadeOutEffects[PAUSEFX_FRANKLIN] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuFranklinOut",0x388a0486));
m_pFadeInEffects[PAUSEFX_MICHAEL] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuMichaelIn", 0xa0544755));
m_pFadeOutEffects[PAUSEFX_MICHAEL] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuMichaelOut", 0x9cbeebee));
m_pFadeInEffects[PAUSEFX_TREVOR] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuTrevorIn", 0x0b3ed728));
m_pFadeOutEffects[PAUSEFX_TREVOR] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuTrevorOut", 0x24239ef8));
m_pFadeInEffects[PAUSEFX_NEUTRAL] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuIn", 0x44dabe22));
m_pFadeOutEffects[PAUSEFX_NEUTRAL] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuOut", 0x725941f7));
/*
// CnC Effects
m_pFadeInEffects[PAUSEFX_CNC_COP] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuCopsIn", 0x786AA98A));
m_pFadeOutEffects[PAUSEFX_CNC_COP] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuCopsOut", 0x6A4AFA62));
m_pFadeInEffects[PAUSEFX_CNC_CROOK] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuCrooksIn", 0x833600C0));
m_pFadeOutEffects[PAUSEFX_CNC_CROOK] = ANIMPOSTFXMGR.Get(atHashString("PauseMenuCrooksOut", 0x9B727EB3));
*/
// don't crash because of a pause menu effect please...
for (int i = 0; i < PAUSEFX_COUNT; i++)
{
Assertf(m_pFadeInEffects[i] && m_pFadeOutEffects[i], "Pause menu effects failed to load");
if (m_pFadeInEffects[i] == NULL)
{
m_pFadeInEffects[i] = &m_FallbackEffect;
}
if (m_pFadeOutEffects[i] == NULL)
{
m_pFadeOutEffects[i] = &m_FallbackEffect;
}
}
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_NEUTRAL]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_NEUTRAL]->GetStack();
#if __BANK
m_EditTool.Init();
#endif
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::Shutdown()
{
#if __BANK
m_EditTool.Shutdown();
#endif
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::StartFadeIn()
{
m_FadeInRequested = true;
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::StartFadeOut()
{
PostFX::ResetAdaptedLuminance();
m_FadeOutRequested = true;
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::Update()
{
if (m_State == PMPOSTFX_IDLE)
{
return;
}
UpdateCurrentEffect();
if (m_State == PMPOSTFX_FADING_IN)
{
m_pCurFadeInFX->Update();
}
else
{
m_pCurFadeOutFX->Update();
}
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::UpdateCurrentEffect()
{
const CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
if (pPlayerPed == NULL)
{
return;
}
if(NetworkInterface::IsGameInProgress())
{
if(CNewHud::GetDisplayMode() == CNewHud::DM_ARCADE_CNC)
{
eArcadeTeam ePlayerTeam = pPlayerPed->GetPlayerInfo()->GetArcadeInformation().GetTeam();
switch (ePlayerTeam)
{
case eArcadeTeam::AT_CNC_COP:
/* {
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_CNC_COP]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_CNC_COP]->GetStack();
}
break;
*/
case eArcadeTeam::AT_CNC_CROOK:
/* {
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_CNC_CROOK]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_CNC_CROOK]->GetStack();
}
break;
*/
default:
{
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_NEUTRAL]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_NEUTRAL]->GetStack();
}
}
}
else
{
eEffectType effectType = PAUSEFX_NEUTRAL;
switch (CNewHud::GetCurrentCharacterColour())
{
case HUD_COLOUR_MICHAEL:
effectType = PAUSEFX_MICHAEL;
break;
case HUD_COLOUR_FRANKLIN:
effectType = PAUSEFX_FRANKLIN;
break;
case HUD_COLOUR_TREVOR:
effectType = PAUSEFX_TREVOR;
break;
default:
effectType = PAUSEFX_NEUTRAL;
}
m_pCurFadeInFX = m_pFadeInEffects[effectType]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[effectType]->GetStack();
}
}
else
{
ePedType pedType = pPlayerPed->GetPedType();
switch (pedType)
{
case PEDTYPE_PLAYER_0: //MICHAEL
{
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_MICHAEL]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_MICHAEL]->GetStack();
}
break;
case PEDTYPE_PLAYER_1: //FRANKLIN
{
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_FRANKLIN]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_FRANKLIN]->GetStack();
}
break;
case PEDTYPE_PLAYER_2: //TREVOR
{
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_TREVOR]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_TREVOR]->GetStack();
}
break;
default:
{
m_pCurFadeInFX = m_pFadeInEffects[PAUSEFX_NEUTRAL]->GetStack();
m_pCurFadeOutFX = m_pFadeOutEffects[PAUSEFX_NEUTRAL]->GetStack();
}
}
}
}
//////////////////////////////////////////////////////////////////////////
//
bool PauseMenuPostFXManager::IsFading() const
{
if (m_State == PMPOSTFX_IDLE)
{
return false;
}
const float fadeInLevel = m_pCurFadeInFX->GetCurrentTime();
const float fadeOutLevel = m_pCurFadeOutFX->GetCurrentTime();
if (((m_State == PMPOSTFX_FADING_IN) && fadeInLevel < m_FadeInCheckTreshold) ||
((m_State == PMPOSTFX_FADING_OUT) && fadeOutLevel < m_FadeOutCheckTreshold))
{
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::Reset()
{
m_State = PMPOSTFX_IDLE;
m_pCurFadeInFX->Reset();
m_pCurFadeOutFX->Reset();
m_FadeInRequested = false;
m_FadeOutRequested = false;
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::ApplyModifiers(tcKeyframeUncompressed& keyframe)
{
if (m_State != PMPOSTFX_IDLE)
{
int scriptModIdx = g_timeCycle.GetScriptModifierIndex();
const AnimatedPostFXStack& curFxStack = (m_State == PMPOSTFX_FADING_IN) ? *m_pCurFadeInFX : *m_pCurFadeOutFX;
#if __BANK
if (AnimPostFXManager::GetInstance().DebugDisplay())
{
grcDebugDraw::AddDebugOutput(Color_white, "[ANIMPOSTFX] Pause menu Post %d", m_State);
}
if (AnimPostFXManager::GetInstance().DebugOutput())
{
Displayf("[ANIMPOSTFX] Pause menu Post %d", m_State);
}
#endif // __BANK
// If script has pushed a TC mod that uses colour correction, skip over layers in the effect stack that do it too.
// we could have script flag those cases, but it'd require more data in the animpostfx stacks (and exposing disabling individual
// layers to script). Let's just hardcode the cases we know of for now.
u32 numLayers = curFxStack.GetCount();
const int gCCTVCamModIdx = 65;
if (scriptModIdx == gCCTVCamModIdx)
{
numLayers = numLayers < 2 ? numLayers : 2;
}
for (u32 i = 0; i < numLayers; i++)
{
curFxStack.GetLayer(i).ApplyModifiers(keyframe, curFxStack.GetUserFadeLevel());
}
}
// To prevent Pause menu Post 2 APFX being permanently on (bugstar://6532397)
if ((m_State == PMPOSTFX_FADING_OUT) && m_pCurFadeOutFX->IsIdle()) Reset();
// to prevent game getting stuck blurred out if we request both fade OUT and IN at the same time, (B* 1423857), let fading out win.
if (m_FadeOutRequested)
{
Reset();
UpdateCurrentEffect();
m_State = PMPOSTFX_FADING_OUT;
m_pCurFadeOutFX->Start();
m_FadeOutRequested = false;
}
else if (m_FadeInRequested)
{
Reset();
UpdateCurrentEffect();
m_State = PMPOSTFX_FADING_IN;
m_pCurFadeInFX->SetLoopEnabled(true);
m_pCurFadeInFX->Start();
m_FadeInRequested = false;
}
}
#if GTA_REPLAY
//////////////////////////////////////////////////////////////////////////
//
bool PauseMenuPostFXManager::AreAllPostFXAtIdle()
{
bool allIdle = true;
for(int i=0; i<PAUSEFX_COUNT; i++)
{
if( m_pFadeInEffects[i]->IsIdle() == false )
{
allIdle = false;
break;
}
if( m_pFadeOutEffects[i]->IsIdle() == false )
{
allIdle = false;
break;
}
}
return allIdle;
}
#endif //GTA_REPLAY
//////////////////////////////////////////////////////////////////////////
//
bool PauseMenuPostFXManager::LoadMetaData()
{
bool bOk = PARSER.LoadObject(PAUSEMENU_POSTFX_METADATA_FILE_NAME, "meta", *this);
return bOk;
}
//////////////////////////////////////////////////////////////////////////
//
#if __BANK && !__FINAL
bool PauseMenuPostFXManager::SaveMetaData()
{
bool bOk = PARSER.SaveObject(PAUSEMENU_POSTFX_METADATA_FILE_NAME, "meta", this);
if (bOk == false)
{
Displayf("PauseMenuPostFXManager::SaveMetaData: Failed to save data for \"%s\"", PAUSEMENU_POSTFX_METADATA_FILE_NAME);
}
return bOk;
}
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::AddWidgets(rage::bkBank& bank )
{
m_EditTool.AddWidgets(bank);
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::EditTool::Init()
{
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::EditTool::Shutdown()
{
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::EditTool::AddWidgets(rage::bkBank& bank )
{
bank.PushGroup("Pause Menu PostFX Edit");
bank.AddSeparator();
bank.AddButton("Trigger Fade In Effects", datCallback(MFA(PauseMenuPostFXManager::EditTool::OnTestFadeInEffects), (datBase*)this));
bank.AddButton("Trigger Fade Out Effects", datCallback(MFA(PauseMenuPostFXManager::EditTool::OnTestFadeOutEffects), (datBase*)this));
bank.AddSeparator();
bank.AddButton("SAVE DATA", datCallback(MFA(PauseMenuPostFXManager::EditTool::OnSaveData), (datBase*)this));
bank.AddButton("RELOAD DATA", datCallback(MFA(PauseMenuPostFXManager::EditTool::OnLoadData), (datBase*)this));
bank.AddSeparator();
bank.AddSlider("Fade In Check Bias", &(PAUSEMENUPOSTFXMGR.m_FadeInCheckTreshold), 0.0f, 1.0f, 0.001f);
bank.AddSlider("Fade Out Check Bias", &(PAUSEMENUPOSTFXMGR.m_FadeOutCheckTreshold), 0.0f, 1.0f, 0.001f);
bank.PopGroup();
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::EditTool::OnSaveData()
{
PAUSEMENUPOSTFXMGR.SaveMetaData();
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::EditTool::OnLoadData()
{
PAUSEMENUPOSTFXMGR.LoadMetaData();
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::EditTool::OnTestFadeInEffects()
{
PAUSEMENUPOSTFXMGR.StartFadeIn();
}
//////////////////////////////////////////////////////////////////////////
//
void PauseMenuPostFXManager::EditTool::OnTestFadeOutEffects()
{
PAUSEMENUPOSTFXMGR.StartFadeOut();
}
#endif // __BANK
//////////////////////////////////////////////////////////////////////////
//
grcBlendStateHandle OverlayTextRenderer::sm_textBlendState = grcStateBlock::BS_Invalid;
void OverlayTextRenderer::RenderText( char const * const text, Vector2 const& position, Vector2 const& scale, CRGBA const& color, s32 const style, Vector2 const& wrap, Vector2 const& c_maskTL, Vector2 const& c_maskBR )
{
if ( text == NULL || text[0] == 0 )
return;
#if __BANK
static const char *prevText = NULL;
if (prevText != text)
{
Displayf("Text rendered by replay (pos %0.2f,%0.2f): %s", position.x, position.y, text);
prevText = text;
}
#endif // __BANK
CText::Flush();
Vector2 textPos( position );
// setup the text layout/format
CTextLayout stringLayout;
stringLayout.SetOrientation(FONT_CENTRE);
if (style < FONT_MAX_FONT_STYLES) // ensure we swap out any unsupported fonts that are not in the global fontmap to the STANDARD font
{
stringLayout.SetStyle(style);
}
else
{
stringLayout.SetStyle(FONT_STYLE_STANDARD);
}
stringLayout.SetColor(color);
stringLayout.SetScale(scale);
stringLayout.SetWrap(wrap);
if (c_maskTL.IsNonZero() || c_maskBR.IsNonZero())
{
stringLayout.SetScissor(c_maskTL.x, c_maskTL.y, c_maskBR.x, c_maskBR.y);
}
float const c_charHeight = CTextFormat::GetCharacterHeight(stringLayout);
s32 const c_numLines = stringLayout.GetNumberOfLines(Vector2(0.5f, 0.0f), text);
float const c_textboxHeight = c_charHeight * c_numLines;
textPos.y -= (c_textboxHeight * 0.5f);
RenderText( text, textPos, stringLayout );
}
void OverlayTextRenderer::RenderText( char const * const text, Vector2 const& position, CTextLayout& textLayout )
{
if ( text == NULL || text[0] == 0 )
return;
// override Scaleform blend state, we don't want to write alpha
sfScaleformManager* pScaleformMgr = CScaleformMgr::GetMovieMgr();
sfRendererBase& scRenderer = pScaleformMgr->GetRageRenderer();
scRenderer.OverrideBlendState(true);
grcBlendStateHandle prevBlendState = grcStateBlock::BS_Active;
grcStateBlock::SetBlendState( GetTextBlendState() );
textLayout.Render( position, text );
CText::Flush();
// restore blend state
grcStateBlock::SetBlendState(prevBlendState);
scRenderer.OverrideBlendState(false);
}
void OverlayTextRenderer::InitializeTextBlendState()
{
if( sm_textBlendState != grcStateBlock::BS_Invalid )
return;
grcBlendStateDesc blendStateBlockDesc;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].BlendEnable = 1;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].DestBlend = grcRSV::BLEND_INVSRCALPHA;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].SrcBlend = grcRSV::BLEND_SRCALPHA;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].BlendOp = grcRSV::BLENDOP_ADD;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].BlendEnable = 1;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].DestBlendAlpha = grcRSV::BLEND_ONE;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].SrcBlendAlpha = grcRSV::BLEND_INVSRCALPHA;
blendStateBlockDesc.BlendRTDesc[GBUFFER_RT_0].BlendOpAlpha = grcRSV::BLENDOP_ADD;
blendStateBlockDesc.IndependentBlendEnable = 1;
sm_textBlendState = grcStateBlock::CreateBlendState(blendStateBlockDesc);
}
//////////////////////////////////////////////////////////////////////////
//
PhonePhotoEditor* PhonePhotoEditor::smp_Instance = NULL;
const atHashString s_PHONEPHOTOEDITOR_FRAME_TEXTURE_NAMES[5] = { atHashString("Frame_Corner", 0x44e529d5),
atHashString("Frame_Horizontal", 0x295245e1),
atHashString("Frame_Vertical", 0x5cb9dd7d),
atHashString("Frame_Detail_Left", 0xd590bc78),
atHashString("Frame_Detail_Right", 0x370f66fd) };
#if __BANK
char PhonePhotoEditor::sm_borderTxdName[PHOTO_TEXT_MAX_LENGTH*2] = "platform:/textures/graphics";
char PhonePhotoEditor::sm_topText[PHOTO_TEXT_MAX_LENGTH] = { 0 };
char PhonePhotoEditor::sm_bottomText[PHOTO_TEXT_MAX_LENGTH] = { 0 };
Color32 PhonePhotoEditor::sm_topTextCol = Color32(1.0f, 1.0f, 1.0f, 1.0f);
Color32 PhonePhotoEditor::sm_bottomTextCol = Color32(1.0f, 1.0f, 1.0f, 1.0f);
Vector4 PhonePhotoEditor::sm_textPos = Vector4(0.5f, 0.25f, 0.5f, 0.75f);
s32 PhonePhotoEditor::sm_topTextStyle = 4;
s32 PhonePhotoEditor::sm_bottomTextStyle = 4;
float PhonePhotoEditor::sm_topTextSize = 2.5f;
float PhonePhotoEditor::sm_bottomTextSize = 2.0f;
bool PhonePhotoEditor::sm_bToggleActive = false;
const char* PhonePhotoEditor::sm_fontTypesStr[] = { "STANDARD", "CURSIVE", "ROCKSTAR_TAG", "LEADERBOARD", "CONDENSED", "FIXED_WIDTH_NUMBERS", "CONDENSED_NOT_GAMERNAME" };
bool PhonePhotoEditor::sm_bToggleGalleryEdit = false;
bool PhonePhotoEditor::sm_bDebugRender = false;
bool PhonePhotoEditor::sm_bTestSave = false;
char PhonePhotoEditor::sm_photoToEditName[64] = "PHOTO_TEXTURE_0_MAX";
s32 PhonePhotoEditor::sm_galleryEditTxdId = -1;
Vector2 PhonePhotoEditor::sm_debugRenderPos = Vector2(0.0f, 0.0f);
Vector2 PhonePhotoEditor::sm_debugRenderSize = Vector2(640.0f, 360.0f);
#endif
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::Init()
{
m_bBorderTextureRefAdded = false;
ResetTextParameters();
Reset();
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::Shutdown()
{
Reset();
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::Disable()
{
if (m_bBorderTextureRefAdded && m_borderTextureTxd != -1)
{
gDrawListMgr->AddRefCountedModuleIndex(m_borderTextureTxd, &g_TxdStore);
}
Reset();
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::Reset()
{
m_borderTextureTxd = -1;
m_pDestRt = NULL;
m_pBorderTexture[0] = NULL;
m_pBorderTexture[1] = NULL;
m_pBorderTexture[2] = NULL;
m_pBorderTexture[3] = NULL;
m_pBorderTexture[4] = NULL;
m_topText[0] = 0;
m_bottomText[0] = 0;
m_borderTxdName.Null();
m_bActive = false;
m_bUpdateBorderTexture = false;
m_bReleaseCurrentBorderTexture = false;
m_bUseText = false;
m_bBorderTextureRefAdded = false;
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::SetText(const char* pTopText, const char* pBottomText, const Vector4& textPos, float topTextSize, float bottomTextSize, s32 topTextStyle, s32 bottomTextStyle, Color32 topTextCol, Color32 bottomTextCol)
{
if ( pTopText )
{
safecpy( m_topText, pTopText );
#if USE_TEXT_CANVAS
sEditedTextProperties &editedText = CTextTemplate::GetEditedText();
safecpy(editedText.m_text, m_topText, NELEM(editedText.m_text));
CTextTemplate::UpdateTemplate(0, editedText, "TEXT");
#endif // USE_TEXT_CANVAS
}
if ( pBottomText )
{
safecpy( m_bottomText, pBottomText );
}
m_textPos = textPos;
m_topTextSize = topTextSize;
m_bottomTextSize = bottomTextSize;
m_topTextCol = topTextCol;
m_bottomTextCol = bottomTextCol;
#if USE_CODE_TEXT
if (topTextStyle >= (s32)FONT_STYLE_STANDARD && topTextStyle < (s32)FONT_MAX_FONT_STYLES)
#endif
m_topTextStyle = topTextStyle;
#if USE_CODE_TEXT
if (bottomTextStyle >= (s32)FONT_STYLE_STANDARD && bottomTextStyle < (s32)FONT_MAX_FONT_STYLES)
#endif
m_bottomTextStyle = bottomTextStyle;
}
void PhonePhotoEditor::ResetTextParameters()
{
m_topText[0] = 0;
m_bottomText[0] = 0;
m_topTextCol = Color32(1.0f, 1.0f, 1.0f, 1.0f);
m_bottomTextCol = Color32(1.0f, 1.0f, 1.0f, 1.0f);
m_textPos = Vector4(0.5f, 0.5f, 0.5f, 0.5f);
m_topTextSize = 2.5f;
m_bottomTextSize = 2.5f;
m_topTextStyle = (s32)FONT_STYLE_CONDENSED;
m_bottomTextStyle = (s32)FONT_STYLE_CONDENSED;
}
void PhonePhotoEditor::SetBorderTexture(const char* txdName, bool bDisableCurrentFrame)
{
if (bDisableCurrentFrame)
{
m_bReleaseCurrentBorderTexture = true;
m_bUpdateBorderTexture = false;
return;
}
m_bUpdateBorderTexture = true;
m_borderTxdName = txdName;
}
void PhonePhotoEditor::ReleaseCurrentBorderTextures()
{
if (m_bBorderTextureRefAdded && m_borderTextureTxd != -1)
{
gDrawListMgr->AddRefCountedModuleIndex(m_borderTextureTxd, &g_TxdStore);
}
m_pBorderTexture[0] = NULL;
m_pBorderTexture[1] = NULL;
m_pBorderTexture[2] = NULL;
m_pBorderTexture[3] = NULL;
m_pBorderTexture[4] = NULL;
m_borderTextureTxd = -1;
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::Update()
{
UpdateGalleryEdit();
if (!m_bActive)
return;
if (m_bReleaseCurrentBorderTexture)
{
ReleaseCurrentBorderTextures();
m_bReleaseCurrentBorderTexture = false;
}
else if (m_bUpdateBorderTexture)
{
// Release any previous one
ReleaseCurrentBorderTextures();
m_borderTextureTxd = g_TxdStore.FindSlotFromHashKey(m_borderTxdName.GetHash()).Get();
#if __BANK
// If needed, force the texture to load when we're using the widget
if (sm_bToggleActive && m_borderTextureTxd != -1 && g_TxdStore.HasObjectLoaded(strLocalIndex(m_borderTextureTxd)) == false)
{
g_TxdStore.StreamingRequest(strLocalIndex(m_borderTextureTxd), STRFLAG_FORCE_LOAD);
return;
}
#endif
// Make sure the TXD is loaded...
if (Verifyf(m_borderTextureTxd != -1 && g_TxdStore.HasObjectLoaded(strLocalIndex(m_borderTextureTxd)), "PhonePhotoEditor::Update: txd \"%s\" is not loaded", m_borderTxdName.TryGetCStr()))
{
fwTxd* pTxd = g_TxdStore.Get(strLocalIndex(m_borderTextureTxd));
m_pBorderTexture[0] = pTxd->Lookup(s_PHONEPHOTOEDITOR_FRAME_TEXTURE_NAMES[0]);
m_pBorderTexture[1] = pTxd->Lookup(s_PHONEPHOTOEDITOR_FRAME_TEXTURE_NAMES[1]);
m_pBorderTexture[2] = pTxd->Lookup(s_PHONEPHOTOEDITOR_FRAME_TEXTURE_NAMES[2]);
m_pBorderTexture[3] = pTxd->Lookup(s_PHONEPHOTOEDITOR_FRAME_TEXTURE_NAMES[3]);
m_pBorderTexture[4] = pTxd->Lookup(s_PHONEPHOTOEDITOR_FRAME_TEXTURE_NAMES[4]);
// Add a ref...
if (m_pBorderTexture[0] != NULL && m_pBorderTexture[1] != NULL && m_pBorderTexture[2] != NULL)
{
g_TxdStore.AddRef(strLocalIndex(m_borderTextureTxd), REF_RENDER);
m_bBorderTextureRefAdded = true;
}
// Or reset everything is the lookup fails...
else
{
Assertf(0, "PhonePhotoEditor::Update: required textures don't exist in txd \"%s\"", m_borderTxdName.TryGetCStr());
Reset();
return;
}
// Grab an overlay texture if there's one
m_bUpdateBorderTexture = false;
}
}
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::Render()
{
if (!m_bActive || m_pDestRt == NULL || m_bUpdateBorderTexture)
return;
grcTextureFactory::GetInstance().LockRenderTarget(0, m_pDestRt, NULL);
RenderBorder();
#if USE_CODE_TEXT
RenderText( m_pDestRt->GetWidth(), m_pDestRt->GetHeight() );
#endif
#if USE_TEXT_CANVAS
RenderTextOverlay();
#endif
grcTextureFactory::GetInstance().UnlockRenderTarget(0);
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::RenderBorder(bool bAdjustFor43)
{
if (m_pBorderTexture[0] == NULL || m_pBorderTexture[1] == NULL || m_pBorderTexture[2] == NULL)
return;
#if SUPPORT_MULTI_MONITOR
if (bAdjustFor43)
{
const GridMonitor &mon = GRCDEVICE.GetMonitorConfig().getLandscapeMonitor();
grcViewport::GetCurrent()->SetWindow( mon.uLeft, mon.uTop, mon.getWidth(), mon.getHeight() );
}
#endif //SUPPORT_MULTI_MONITOR
float width = 0.25f;
float height = 0.4f;
float uoffset = 1.0f/80.0f;
float uoffsetCorner = 1.0f/72.0f;
float voffset = 1.0f/72.0f;
float absMaxX = 0.5625f;
if (bAdjustFor43 && !CHudTools::GetWideScreen())
{
absMaxX = 0.75f;
}
CSprite2d sprite;
grcStateBlock::SetBlendState(grcStateBlock::BS_Normal);
// Draw corners
sprite.SetGeneralParams(Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, 0.0f));
sprite.BeginCustomList(CSprite2d::CS_BLIT, m_pBorderTexture[0]);
// Top left
float x0 = -absMaxX;
float y0 = 1.0f;
float x1 = -absMaxX + width;
float y1 = 1.0f - height;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
0.0f, 0.0f, 1.0f-uoffsetCorner, 1.0f-voffset,
Color32(255, 255, 255 ,255));
// Top right
x0 = absMaxX - width;
y0 = 1.0f;
x1 = absMaxX;
y1 = 1.0f - height;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
1.0f-uoffsetCorner, 0.0f, 0.0f, 1.0f-voffset,
Color32(255, 255, 255 ,255));
// Bottom right
x0 = absMaxX - width;
y0 = -1.0f + height;
x1 = absMaxX;
y1 = -1.0f;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
1.0f-uoffsetCorner, 1.0f-voffset, 0.0f, 0.0f,
Color32(255, 255, 255 ,255));
// Bottom left
x0 = -absMaxX;
y0 = -1.0f + height;
x1 = -absMaxX + width;
y1 = -1.0f;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
0.0f, 1.0f-uoffsetCorner, 1.0f-voffset, 0.0f,
Color32(255, 255, 255 ,255));
sprite.EndCustomList();
// Draw horizontals
sprite.SetGeneralParams(Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, 0.0f));
sprite.BeginCustomList(CSprite2d::CS_BLIT, m_pBorderTexture[1]);
// Top
x0 = -absMaxX + width;
y0 = 1.0f;
x1 = absMaxX - width;
y1 = 1.0f - height;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
0.0f, 0.0f, 3.0f, 1.0f-voffset,
Color32(255, 255, 255 ,255));
// Bottom
x0 = -absMaxX + width;
y0 = -1.0f + height;
x1 = absMaxX - width;
y1 = -1.0f;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
0.0f, 1.0f-voffset, 3.0f, 0.0f,
Color32(255, 255, 255 ,255));
sprite.EndCustomList();
// Draw verticals
sprite.SetGeneralParams(Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, 0.0f));
sprite.BeginCustomList(CSprite2d::CS_BLIT, m_pBorderTexture[2]);
// Left
x0 = -absMaxX;
y0 = 1.0f - height;
x1 = -absMaxX + width;
y1 = -1.0f + height;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
0.0f, 0.0f, 1.0f-uoffset, 3.0f,
Color32(255, 255, 255 ,255));
// Right
x0 = absMaxX - width;
y0 = 1.0f - height;
x1 = absMaxX;
y1 = -1.0f + height;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
1.0f-uoffset, 0.0f, 0.0f, 3.0f,
Color32(255, 255, 255 ,255));
sprite.EndCustomList();
// Draw top-left detail texture if there's one
if (m_pBorderTexture[3] != NULL)
{
sprite.SetGeneralParams(Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, 0.0f));
sprite.BeginCustomList(CSprite2d::CS_BLIT, m_pBorderTexture[3]);
float uoffsetDetail = 1.0f/(float)m_pBorderTexture[3]->GetWidth();
float voffsetDetail = 1.0f/(float)m_pBorderTexture[3]->GetHeight();
float widthDetail = ((float)m_pBorderTexture[3]->GetWidth()/640.0f)*2.0f;
float heightDetail = ((float)m_pBorderTexture[3]->GetHeight()/360.0f)*2.0f;
float x0 = -absMaxX;
float y0 = 1.0f;
float x1 = -absMaxX + widthDetail;
float y1 = 1.0f - heightDetail;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
0.0f, 0.0f, 1.0f-uoffsetDetail, 1.0f-voffsetDetail,
Color32(255, 255, 255 ,255));
sprite.EndCustomList();
}
// Draw top-right detail texture if there's one
if (m_pBorderTexture[4] != NULL)
{
sprite.SetGeneralParams(Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, 0.0f));
sprite.BeginCustomList(CSprite2d::CS_BLIT, m_pBorderTexture[4]);
float uoffsetDetail = 1.0f/(float)m_pBorderTexture[4]->GetWidth();
float voffsetDetail = 1.0f/(float)m_pBorderTexture[4]->GetHeight();
float widthDetail = ((float)m_pBorderTexture[4]->GetWidth()/640.0f)*2.0f;
float heightDetail = ((float)m_pBorderTexture[4]->GetHeight()/360.0f)*2.0f;
x0 = absMaxX - widthDetail;
y0 = 1.0f;
x1 = absMaxX;
y1 = 1.0f - heightDetail;
grcDrawSingleQuadf( x0, y0,
x1, y1,
0.0f,
0.0f, 0.0f, 1.0f-uoffsetDetail, 1.0f-voffsetDetail,
Color32(255, 255, 255 ,255));
sprite.EndCustomList();
}
// Draw cropping areas
sprite.SetGeneralParams(Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, 0.0f));
sprite.BeginCustomList(CSprite2d::CS_BLIT, grcTexture::NoneBlack);
grcDrawSingleQuadf( -1.0f, 1.0f, -absMaxX+(2.0f/1280.0f), -1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, Color32(0, 0, 0 ,255));
grcDrawSingleQuadf( absMaxX-(2.0f/1280.0f), 1.0f, 1.0f, -1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, Color32(0, 0, 0 ,255));
sprite.EndCustomList();
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::RenderGalleryEdit()
{
if (m_galleryEdit.m_bEditing == false
PPU_ONLY(&& m_galleryEdit.m_requestFlags.bSave == false))
return;
if (m_galleryEdit.m_pSrcImg == NULL)
return;
PF_PUSH_MARKER("MemeEditor");
grcTextureFactory::GetInstance().LockRenderTarget(0, m_galleryEdit.m_pRt, NULL);
CSprite2d sprite;
sprite.SetGeneralParams(Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, 0.0f));
sprite.BeginCustomList(CSprite2d::CS_BLIT, m_galleryEdit.m_pSrcImg);
grcDrawSingleQuadf(-1.0f, 1.0f, 1.0, -1.0f, 0.0f, 0.0, 0.0, 1.0f, 1.0f, Color32(255, 255, 255 ,255));
sprite.EndCustomList();
#if USE_CODE_TEXT
RenderText( m_galleryEdit.m_pRt->GetWidth(), m_galleryEdit.m_pRt->GetHeight() );
#endif
#if USE_TEXT_CANVAS
RenderTextOverlay();
#endif
grcResolveFlags resolveFlags;
resolveFlags.NeedResolve = XENON_SWITCH(true, false);
grcTextureFactory::GetInstance().UnlockRenderTarget(0, &resolveFlags);
PF_POP_MARKER();
#if __PPU
if (m_galleryEdit.m_requestFlags.bSave)
SaveToJPEGBuffer();
#endif
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::RenderText( int const destWidth, int const destHeight )
{
if (m_topText[0] == 0 && m_bottomText[0] == 0)
return;
{
GRC_VIEWPORT_AUTO_PUSH_POP();
m_galleryEdit.m_viewport.SetWindow( 0, 0, destWidth, destHeight );
m_galleryEdit.m_viewport.Screen();
grcViewport::SetCurrent( &m_galleryEdit.m_viewport );
// override Scaleform blend state, we don't want to write alpha
sfScaleformManager* pScaleformMgr = CScaleformMgr::GetMovieMgr();
sfRendererBase& scRenderer = pScaleformMgr->GetRageRenderer();
scRenderer.OverrideBlendState(true);
grcBlendStateHandle prevBlendState = grcStateBlock::BS_Active;
grcStateBlock::SetBlendState( GetTextBlendState() );
bool bFlushText = false;
// TOP TEXT //////////////////////////////////////////////////////////////////////////
// setup the text layout/format
Color32 textCol = m_topTextCol;
Vector2 textPos = Vector2(m_textPos.x, m_textPos.y);
Vector2 textSize = Vector2(m_topTextSize, m_topTextSize);
const char* pStr = &(m_topText[0]);
CTextLayout stringLayout;
float charHeight;
if (pStr != NULL)
{
stringLayout.SetOrientation(FONT_CENTRE);
stringLayout.SetStyle(m_topTextStyle);
stringLayout.SetColor(textCol);
stringLayout.SetScale(&textSize);
charHeight = CTextFormat::GetCharacterHeight(stringLayout);
textPos.y -= charHeight * 0.5f;
stringLayout.Render(&textPos, pStr);
bFlushText = true;
}
// BOTTOM TEXT //////////////////////////////////////////////////////////////////////////
// setup the text layout/format
textCol = m_bottomTextCol;
textPos = Vector2(m_textPos.z, m_textPos.w);
textSize = Vector2(m_bottomTextSize, m_bottomTextSize);
pStr = &(m_bottomText[0]);
if (pStr != NULL)
{
stringLayout.SetOrientation(FONT_CENTRE);
stringLayout.SetStyle(m_bottomTextStyle);
stringLayout.SetColor(textCol);
stringLayout.SetScale(&textSize);
charHeight = CTextFormat::GetCharacterHeight(stringLayout);
textPos.y -= charHeight * 0.5f;
stringLayout.Render(&textPos, pStr);
bFlushText = true;
}
if (bFlushText)
CText::Flush();
// restore blend state
grcStateBlock::SetBlendState(prevBlendState);
scRenderer.OverrideBlendState(false);
}
}
void PhonePhotoEditor::RenderTextOverlay()
{
if (m_topText[0] == 0 && m_bottomText[0] == 0)
return;
sfRendererBase& scRenderer = CScaleformMgr::GetMovieMgr()->GetRageRenderer();
scRenderer.OverrideBlendState(true);
grcBlendStateHandle prevBlendState = grcStateBlock::BS_Normal;
grcStateBlock::SetBlendState( GetTextBlendState() );
CTextTemplate::RenderSnapmatic();
// restore blend state
grcStateBlock::SetBlendState(prevBlendState);
scRenderer.OverrideBlendState(false);
}
#if RSG_PC
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::DeviceLost()
{
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::DeviceReset()
{
if (smp_Instance)
{
PHONEPHOTOEDITOR.InitGalleryEdit(true);
}
}
#endif // RSG_PC
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::ClassInit()
{
smp_Instance = rage_new PhonePhotoEditor();
Assert(smp_Instance);
if (smp_Instance)
{
PHONEPHOTOEDITOR.Init();
PHONEPHOTOEDITOR.InitGalleryEdit(false);
}
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::InitGalleryEdit(bool bReInit)
{
if (!bReInit)
{
m_galleryEdit.Reset();
}
u32 width;
u32 height;
CHighQualityScreenshot::GetDefaultScreenshotSize( width, height );
#if __XENON
grcTextureFactory::CreateParams qparams;
qparams.Format = grctfA8R8G8B8;
qparams.HasParent = true;
qparams.Parent = CRenderTargets::GetUIBackBuffer();
m_galleryEdit.m_pRt = CRenderTargets::CreateRenderTarget(XENON_RTMEMPOOL_PERLIN_NOISE, "MemeBuffer", grcrtPermanent, width, height, 32, &qparams, kMemeEditorHeap);
m_galleryEdit.m_pRt->AllocateMemoryFromPool();
#elif __PS3
grcTextureFactory::CreateParams params;
params.HasParent = true;
params.Parent = NULL;
params.EnableCompression = false;
params.Format = grctfA8R8G8B8;
m_galleryEdit.m_pRt = CRenderTargets::CreateRenderTarget(PSN_RTMEMPOOL_P5120_TILED_LOCAL_COMPMODE_DISABLED_2, "MemeBuffer", grcrtPermanent, width, height, 32, &params, 10);
m_galleryEdit.m_pRt->AllocateMemoryFromPool();
#elif __WIN32PC || RSG_DURANGO || RSG_ORBIS
grcTextureFactory::CreateParams params;
params.Multisample = 0;
params.UseFloat = true;
#if RSG_ORBIS
params.Format = grctfA8R8G8B8;
params.ForceNoTiling = true;
#else
params.Format = grctfA8B8G8R8;
#endif
params.Lockable = true;
m_galleryEdit.m_pRt = CRenderTargets::CreateRenderTarget(RTMEMPOOL_NONE, "MemeBuffer ", grcrtPermanent, width, height, 32, &params, 0 WIN32PC_ONLY(, m_galleryEdit.m_pRt) );
#endif
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::ShutdownGalleryEdit()
{
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::ClassShutdown()
{
PHONEPHOTOEDITOR.Shutdown();
PHONEPHOTOEDITOR.ShutdownGalleryEdit();
delete smp_Instance;
smp_Instance = NULL;
}
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void PhonePhotoEditor::OnToggleActive()
{
if (sm_bToggleActive)
{
PHONEPHOTOEDITOR.Enable();
PHONEPHOTOEDITOR.SetBorderTexture(&sm_borderTxdName[0], false);
PHONEPHOTOEDITOR.SetText(&sm_topText[0], &sm_bottomText[0], sm_textPos, sm_topTextSize, sm_bottomTextSize, sm_topTextStyle, sm_bottomTextStyle, sm_topTextCol, sm_bottomTextCol);
}
else
{
PHONEPHOTOEDITOR.Disable();
}
}
#endif
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void PhonePhotoEditor::OnTextChange()
{
PHONEPHOTOEDITOR.SetText(&sm_topText[0], &sm_bottomText[0], sm_textPos, sm_topTextSize, sm_bottomTextSize, sm_topTextStyle, sm_bottomTextStyle, sm_topTextCol, sm_bottomTextCol);
}
#endif
//////////////////////////////////////////////////////////////////////////
//
#if __BANK
void PhonePhotoEditor::AddWidgets(rage::bkBank& bank )
{
AddWidgetsForGalleryEdit(bank);
bank.PushGroup("Phone Photo Editor");
bank.AddToggle("Enable", &(sm_bToggleActive), datCallback(MFA(PhonePhotoEditor::OnToggleActive), (datBase*)this));
bank.AddSeparator();
bank.AddText("Top Text:", &(sm_topText[0]), PHOTO_TEXT_MAX_LENGTH-1, datCallback(MFA(PhonePhotoEditor::OnTextChange), (datBase*)this));
bank.AddText("Bottom Text:", &(sm_bottomText[0]), PHOTO_TEXT_MAX_LENGTH-1, datCallback(MFA(PhonePhotoEditor::OnTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddColor("Top Text Color:", &(sm_topTextCol), datCallback(MFA(PhonePhotoEditor::OnTextChange), (datBase*)this));
bank.AddColor("Bottom Text Color:", &(sm_bottomTextCol), datCallback(MFA(PhonePhotoEditor::OnTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddSlider("Top Text Size:", &(sm_topTextSize), 0.01f, 10.0f, 0.01f, datCallback(MFA(PhonePhotoEditor::OnTextChange), (datBase*)this));
bank.AddSlider("Bottom Text Size:", &(sm_bottomTextSize), 0.01f, 100.0f, 0.01f, datCallback(MFA(PhonePhotoEditor::OnTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddVector("Text Position:", &(sm_textPos), 0.0f, 1.0f, 0.01f, datCallback(MFA(PhonePhotoEditor::OnTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddText("Border TXD Name:", &(sm_borderTxdName[0]), PHOTO_TEXT_MAX_LENGTH*2-1);
bank.AddSeparator();
bank.PopGroup();
}
#endif
//////////////////////////////////////////////////////////////////////////
//
bool PhonePhotoEditor::StartEditing(s32 txdId)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
// Check there are no pending requests and that we're available
if (m_galleryEdit.m_bRequestsPending != 0 || m_galleryEdit.m_bEditing != false || m_galleryEdit.m_bWaitingForUser != false)
{
Assertf(0, "PhonePhotoEditor::StartEditing: already in use (editing: %d saving: %d, releasing: %d)", m_galleryEdit.m_bEditing || m_galleryEdit.m_bEditing, m_galleryEdit.m_requestFlags.bSave || m_galleryEdit.m_bWaitingForUser, m_galleryEdit.m_requestFlags.bRelease );
return false;
}
// Assume everything's going to be alright
bool bOk = true;
// Try allocating memory for the JPEG buffer
if (Verifyf(AllocateGalleryEditJPEGBuffer(), "PhonePhotoEditor::StartEditing: failed to allocate memory for JPEG buffer"))
{
// Try setting up the source image
if (Verifyf(ProcessGalleryEditSourceImage(txdId), "PhonePhotoEditor::StartEditing: failed to add ref to source image (txd: %d)", txdId))
{
// We're now editing!
m_galleryEdit.m_bEditing = true;
}
else
{
// Release JPEG buffer
ReleaseGalleryEditJPEGBuffer();
bOk = false;
}
}
// No pending requests
m_galleryEdit.m_bRequestsPending = 0;
return bOk;
}
//////////////////////////////////////////////////////////////////////////
//
bool PhonePhotoEditor::IsEditingAvailable() const
{
return (m_galleryEdit.m_bRequestsPending == 0 && m_galleryEdit.m_bEditing == false && m_galleryEdit.m_bWaitingForUser == false);
}
//////////////////////////////////////////////////////////////////////////
//
bool PhonePhotoEditor::FinishEditing()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
// Verify something was going on
if (Verifyf(m_galleryEdit.m_bWaitingForUser || m_galleryEdit.m_bEditing || m_galleryEdit.m_requestFlags.bSave, "PhonePhotoEditor::FinishEditing: editor was not in use or there's a pending save request"))
{
// Request release
m_galleryEdit.m_requestFlags.bRelease = true;
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//
bool PhonePhotoEditor::RequestSave(OnPhotoEditorSaveCallback pCallback)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
// Verify we're already editing something AND that there are no pending requests
if (Verifyf(m_galleryEdit.m_bEditing && m_galleryEdit.m_bRequestsPending == false, "PhonePhotoEditor::RequestSave: there are pending requests"))
{
// Verify we've got a functor for the callback
if (Verifyf(pCallback != 0, "PhonePhotoEditor::RequestSave: functor is NULL"))
{
m_galleryEdit.m_pOnSaveFnc = pCallback;
m_galleryEdit.m_requestFlags.bSave = true;
m_galleryEdit.m_bEditing = false; // no more editing
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::UpdateGalleryEdit()
{
if (m_galleryEdit.m_requestFlags.bRelease)
{
ResetGalleryEditState();
m_galleryEdit.m_bRequestsPending = 0;
m_galleryEdit.m_bEditing = false;
m_galleryEdit.m_bWaitingForUser = false;
}
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::ResetGalleryEditState()
{
ReleaseGalleryEditJPEGBuffer();
ReleaseGalleryEditSourceImage();
}
//////////////////////////////////////////////////////////////////////////
//
bool PhonePhotoEditor::AllocateGalleryEditJPEGBuffer()
{
MEM_USE_USERDATA(MEMUSERDATA_SCREENSHOT);
m_galleryEdit.m_pJPEGBuffer = (u8*) strStreamingEngine::GetAllocator().Allocate(m_galleryEdit.m_JPEGBufferSize, 16, MEMTYPE_RESOURCE_VIRTUAL);
return (m_galleryEdit.m_pJPEGBuffer != NULL);
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::ReleaseGalleryEditJPEGBuffer()
{
if (m_galleryEdit.m_pJPEGBuffer)
{
MEM_USE_USERDATA(MEMUSERDATA_SCREENSHOT);
strStreamingEngine::GetAllocator().Free(m_galleryEdit.m_pJPEGBuffer);
m_galleryEdit.m_pJPEGBuffer = NULL;
m_galleryEdit.m_JPEGBufferSize = CPhotoBuffer::GetDefaultSizeOfJpegBuffer();
m_galleryEdit.m_JPEGActualSize = 0;
}
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::ReleaseGalleryEditSourceImage()
{
if (m_galleryEdit.m_bSrcImgRefAdded && m_galleryEdit.m_SrcImgTxd != -1)
{
gDrawListMgr->AddRefCountedModuleIndex(m_galleryEdit.m_SrcImgTxd, &g_TxdStore);
m_galleryEdit.m_pSrcImg = NULL;
m_galleryEdit.m_bSrcImgRefAdded = false;
m_galleryEdit.m_SrcImgTxd = -1;
}
}
//////////////////////////////////////////////////////////////////////////
//
bool PhonePhotoEditor::ProcessGalleryEditSourceImage(s32 txdId)
{
if (txdId == -1 || g_TxdStore.IsValidSlot(strLocalIndex(txdId)) == false)
{
return false;
}
fwTxd* pTxd = g_TxdStore.Get(strLocalIndex(txdId));
if (pTxd == NULL || pTxd->GetCount() == 0)
{
return false;
}
g_TxdStore.AddRef(strLocalIndex(txdId), REF_RENDER);
m_galleryEdit.m_pSrcImg = pTxd->GetEntry(0);
m_galleryEdit.m_SrcImgTxd = txdId;
m_galleryEdit.m_PendingSrcImgTxd = -1;
m_galleryEdit.m_bSrcImgRefAdded = true;
return true;
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::UpdateSafe()
{
#if __PPU
if (m_galleryEdit.m_bNeedToIssueCallback == true)
{
// Let user know about save result
if (m_galleryEdit.m_pOnSaveFnc)
{
m_galleryEdit.m_pOnSaveFnc( m_galleryEdit.m_pJPEGBuffer, m_galleryEdit.m_JPEGActualSize, !m_galleryEdit.m_bWasSaveOk );
}
m_galleryEdit.m_bNeedToIssueCallback = false;
}
#else
if (m_galleryEdit.m_requestFlags.bSave)
SaveToJPEGBuffer();
#endif
}
//////////////////////////////////////////////////////////////////////////
//
bool PhonePhotoEditor::SaveToJPEGBuffer()
{
Assertf(CPauseMenu::GetPauseRenderPhasesStatus(), "Rendering is not frozen");
if (m_galleryEdit.m_pJPEGBuffer == NULL)
return false;
#if __PPU
grcFenceHandle fence = GRCDEVICE.InsertFence();
GRCDEVICE.GPUBlockOnFence(fence);
#endif
m_galleryEdit.m_JPEGActualSize = 0U;
bool bOk = CHighQualityScreenshot::StoreScreenShot(NULL, // No need to pass a file name
m_galleryEdit.m_pRt, // Source image
true, // Save to memory buffer
m_galleryEdit.m_pJPEGBuffer, // Our memory buffer
m_galleryEdit.m_JPEGBufferSize, // Current JPEG buffer size
&m_galleryEdit.m_JPEGActualSize, // Actual JPEG buffer size
true, // Save as JPEG
CHighQualityScreenshot::GetDefaultQuality() ); // Quality
#if __PPU
m_galleryEdit.m_bNeedToIssueCallback = true;
m_galleryEdit.m_bWasSaveOk = bOk;
#else
// Let user know about save result
if (m_galleryEdit.m_pOnSaveFnc)
{
m_galleryEdit.m_pOnSaveFnc( m_galleryEdit.m_pJPEGBuffer, m_galleryEdit.m_JPEGActualSize, bOk );
}
#endif
m_galleryEdit.m_requestFlags.bSave = false;
m_galleryEdit.m_bWaitingForUser = true;
return bOk;
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::GalleryEditData::Reset()
{
m_bRequestsPending = 0;
m_bEditing = false;
m_bWaitingForUser = false;
m_pRt = NULL;
m_pSrcImg = NULL;
m_pJPEGBuffer = NULL;
m_JPEGBufferSize = CPhotoBuffer::GetDefaultSizeOfJpegBuffer();
m_JPEGActualSize = 0;
m_SrcImgTxd = -1;
m_PendingSrcImgTxd = -1;
m_bSrcImgRefAdded = false;
m_pOnSaveFnc = NULL;
m_bNeedToIssueCallback = 0;
m_bWasSaveOk = 0;
}
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::AddWidgetsForGalleryEdit(rage::bkBank& bank)
{
bank.PushGroup("Gallery Photo Edit");
bank.AddToggle("Toggle Editing", &sm_bToggleGalleryEdit, datCallback(MFA(PhonePhotoEditor::OnToggleGalleryEdit), (datBase*)this));
bank.AddToggle("Toggle Debug Render", &sm_bDebugRender);
bank.AddVector("Debug Render Position", &sm_debugRenderPos, 0.0f, 1280.0f, 1.0f);
bank.AddVector("Debug Render Size", &sm_debugRenderSize, 0.0f, 1280.0f, 0.5f);
bank.AddSeparator();
bank.AddToggle("Test Save", &sm_bTestSave, datCallback(MFA(PhonePhotoEditor::OnGalleryEditSave), (datBase*)this));
bank.AddSeparator();
bank.AddText("Photo TXD Name:", &(sm_photoToEditName[0]), 63, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextureNameChange), (datBase*)this));
bank.AddSeparator();
bank.AddText("Top Text:", &(sm_topText[0]), PHOTO_TEXT_MAX_LENGTH-1, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddText("Bottom Text:", &(sm_bottomText[0]), PHOTO_TEXT_MAX_LENGTH-1, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddCombo("Top Text Font", &sm_topTextStyle, FONT_MAX_FONT_STYLES-2, sm_fontTypesStr, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddCombo("Bottom Text Font", &sm_bottomTextStyle, FONT_MAX_FONT_STYLES-2, sm_fontTypesStr, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddColor("Top Text Color:", &(sm_topTextCol), datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddColor("Bottom Text Color:", &(sm_bottomTextCol), datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddSlider("Top Text Size:", &(sm_topTextSize), 0.01f, 10.0f, 0.01f, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddSlider("Bottom Text Size:", &(sm_bottomTextSize), 0.01f, 100.0f, 0.01f, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddSeparator();
bank.AddVector("Text Position:", &(sm_textPos), 0.0f, 1.0f, 0.01f, datCallback(MFA(PhonePhotoEditor::OnGalleryEditTextChange), (datBase*)this));
bank.AddSeparator();
bank.PopGroup();
}
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::OnToggleGalleryEdit()
{
if (sm_bToggleGalleryEdit)
{
if (sm_galleryEditTxdId == -1 && sm_photoToEditName[0] != 0)
{
atHashString hashStr(&sm_photoToEditName[0]);
sm_galleryEditTxdId = g_TxdStore.FindSlotFromHashKey(hashStr.GetHash()).Get();
}
if (sm_galleryEditTxdId == -1)
{
sm_bToggleGalleryEdit = false;
return;
}
PHONEPHOTOEDITOR.StartEditing(sm_galleryEditTxdId);
}
else if (PHONEPHOTOEDITOR.IsEditingAvailable() == false)
{
PHONEPHOTOEDITOR.FinishEditing();
sm_galleryEditTxdId = -1;
}
}
#endif
#if __BANK
static void GalleryEditOnSaveCallback(u8* pJPEGBuffer, u32 size, bool bSuccess )
{
if (pJPEGBuffer && size && bSuccess )
Warningf("[PhonePhotoEditor] Save test OK");
else
Warningf("[PhonePhotoEditor] Save test FAILED");
}
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::OnGalleryEditSave()
{
if (m_galleryEdit.m_bEditing)
{
PHONEPHOTOEDITOR.RequestSave( MakeFunctor(GalleryEditOnSaveCallback) );
}
}
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::OnGalleryEditTextureNameChange()
{
if (sm_photoToEditName[0] != 0)
{
atHashString hashStr(&sm_photoToEditName[0]);
sm_galleryEditTxdId = g_TxdStore.FindSlotFromHashKey(hashStr.GetHash()).Get();
}
}
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::OnGalleryEditTextChange()
{
PHONEPHOTOEDITOR.SetText(&sm_topText[0], &sm_bottomText[0], sm_textPos, sm_topTextSize, sm_bottomTextSize, sm_topTextStyle, sm_bottomTextStyle, sm_topTextCol, sm_bottomTextCol);
}
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void PhonePhotoEditor::RenderDebug()
{
if (sm_bDebugRender == false || m_galleryEdit.m_bEditing == false)
return;
PF_PUSH_MARKER("MemeEditorDebug");
float x = sm_debugRenderPos.x;
float y = sm_debugRenderPos.y;
float w = sm_debugRenderSize.x;
float h = sm_debugRenderSize.y;
PUSH_DEFAULT_SCREEN();
const grcTexture* pTexture = m_galleryEdit.m_pRt;
grcBindTexture(pTexture);
grcDrawSingleQuadf(x,y,x+w,y+h,0,0,0,1,1,Color32(255,255,255,255));
grcBindTexture(NULL);
POP_DEFAULT_SCREEN();
PF_POP_MARKER();
}
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
Vector3 DofTODOverrideHelper::sm_debugSamples[MAX_TIME_SAMPLES] = { Vector3(0.0f, 0.0f, -2.0f), Vector3(0.0f, 0.0f, -2.0f), Vector3(0.0f, 0.0f, -2.0f), Vector3(0.0f, 0.0f, -2.0f) };
float DofTODOverrideHelper::sm_debugOutOfRangeOverride = 0.0f;
bool DofTODOverrideHelper::sm_bToggle = false;
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void DofTODOverrideHelper::AddWidgets(rage::bkBank& bank)
{
bank.PushGroup("TOD Overrides");
bank.AddToggle("Toggle Override", &sm_bToggle, datCallback(MFA(DofTODOverrideHelper::OnToggle), (datBase*)this));
bank.AddSlider("Out of Range Override Value", &sm_debugOutOfRangeOverride, -1.0f, 1.0f, 0.01f);
bank.AddVector("[0] X: From (Hours) Y: To (Hours) Z: Override Value", &sm_debugSamples[0], -2.0f, 24.0f, 0.01f);
bank.AddVector("[1] X: From (Hours) Y: To (Hours) Z: Override Value", &sm_debugSamples[1], -2.0f, 24.0f, 0.01f);
bank.AddVector("[2] X: From (Hours) Y: To (Hours) Z: Override Value", &sm_debugSamples[2], -2.0f, 24.0f, 0.01f);
bank.AddVector("[3] X: From (Hours) Y: To (Hours) Z: Override Value", &sm_debugSamples[3], -2.0f, 24.0f, 0.01f);
bank.PopGroup();
}
#endif
#if __BANK
//////////////////////////////////////////////////////////////////////////
//
void DofTODOverrideHelper::OnToggle()
{
if (sm_bToggle)
{
Begin();
for (int i = 0; i < MAX_TIME_SAMPLES; i++)
{
if (sm_debugSamples[i].z >= -1.0f)
{
AddSample(sm_debugSamples[i].x, sm_debugSamples[i].y, sm_debugSamples[i].z);
}
}
}
else
{
End();
sm_debugSamples[0].z = -2.0f;
sm_debugSamples[1].z = -2.0f;
sm_debugSamples[2].z = -2.0f;
sm_debugSamples[3].z = -2.0f;
}
}
#endif
//////////////////////////////////////////////////////////////////////////
//
bool DofTODOverrideHelper::Begin()
{
if (Verifyf(IsActive() == false, "Already active"))
{
m_bActive = true;
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//
bool DofTODOverrideHelper::End()
{
if (Verifyf(IsActive(), "Not active"))
{
Reset();
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//
void DofTODOverrideHelper::Reset()
{
m_outOfRangeOverrideValue = 0.0f;
m_bActive = false;
m_currentValue = 0.0f;
m_samples.Reset();
}
//////////////////////////////////////////////////////////////////////////
//
void DofTODOverrideHelper::AddSample(float hourFrom, float hourTo, float overrideValue)
{
if (m_samples.GetCount() == m_samples.GetMaxCount())
{
Reset();
Assertf(0, "DofTODOverrideHelper::AddSample: cannot add more samples");
return;
}
DofTODOverrideEntry& sample = m_samples.Append();
sample.hourFrom = hourFrom;
sample.hourTo = hourTo;
sample.overrideValue = overrideValue;
}
//////////////////////////////////////////////////////////////////////////
//
void DofTODOverrideHelper::Update()
{
if (IsActive() == false)
return;
const float time = CClock::GetDayRatio()*24.0f;
// By default use the override value for any time that's not within the range of any of the samples
m_currentValue = m_outOfRangeOverrideValue;
// Find a suitable range
for (int i = 0; i < m_samples.GetCount(); i++)
{
float lowerBound = Min(m_samples[i].hourFrom, m_samples[i].hourTo);
float upperBound = Max(m_samples[i].hourFrom, m_samples[i].hourTo);
if (time >= lowerBound && time <= upperBound)
{
m_currentValue = m_samples[i].overrideValue;
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
//
float DofTODOverrideHelper::GetCurrentValue()
{
return m_currentValue;
}