/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "PostEffects.h" #include void CParamBool::SetParam(float fParam, bool bForceValue) { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; CParamBoolThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID]; pThreadSafeData->bParam = (fParam) ? true : false; pThreadSafeData->bSetThisFrame = true; } float CParamBool::GetParam() { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; return static_cast(m_threadSafeData[threadID].bParam); } void CParamBool::SyncMainWithRender() { CParamBoolThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID]; const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false; CParamBoolThreadSafeData* pProcessData = NULL; if (bIsMultiThreaded) { // If value is set on render thread, then this should override main thread value pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID]; if (pProcessData->bSetThisFrame) { pFillData->bParam = pProcessData->bParam; } } // Reset set value pFillData->bSetThisFrame = false; if (bIsMultiThreaded) { // Copy fill data into process data memcpy(pProcessData, pFillData, sizeof(CParamBoolThreadSafeData)); } } void CParamInt::SetParam(float fParam, bool bForceValue) { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; CParamIntThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID]; pThreadSafeData->nParam = static_cast(fParam); pThreadSafeData->bSetThisFrame = true; } float CParamInt::GetParam() { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; return static_cast(m_threadSafeData[threadID].nParam); } void CParamInt::SyncMainWithRender() { CParamIntThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID]; const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false; CParamIntThreadSafeData* pProcessData = NULL; if (bIsMultiThreaded) { // If value is set on render thread, then this should override main thread value pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID]; if (pProcessData->bSetThisFrame) { pFillData->nParam = pProcessData->nParam; } } // Reset set value pFillData->bSetThisFrame = false; if (bIsMultiThreaded) { // Copy fill data into process data memcpy(pProcessData, pFillData, sizeof(CParamIntThreadSafeData)); } } void CParamFloat::SetParam(float fParam, bool bForceValue) { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; CParamFloatThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID]; pThreadSafeData->fParam = fParam; pThreadSafeData->bSetThisFrame = true; } float CParamFloat::GetParam() { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; return m_threadSafeData[threadID].fParam; } void CParamFloat::SyncMainWithRender() { // The Effect params can be set/get from both threads, accumulate and sync data here CParamFloatThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID]; const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false; CParamFloatThreadSafeData* pProcessData = NULL; if (bIsMultiThreaded) { // If value is set on render thread, then this should override main thread value pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID]; if (pProcessData->bSetThisFrame) { pFillData->fParam = pProcessData->fParam; } } // Reset set value pFillData->bSetThisFrame = false; if (bIsMultiThreaded) { // Copy fill data into process data memcpy(pProcessData, pFillData, sizeof(CParamFloatThreadSafeData)); } } void CParamVec4::SetParamVec4(const Vec4& vParam, bool bForceValue) { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; CParamVec4ThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID]; pThreadSafeData->vParam = vParam; pThreadSafeData->bSetThisFrame = true; } Vec4 CParamVec4::GetParamVec4() { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; return m_threadSafeData[threadID].vParam; } void CParamVec4::SyncMainWithRender() { // The Effect params can be set/get from both threads, accumulate and sync data here CParamVec4ThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID]; const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false; CParamVec4ThreadSafeData* pProcessData = NULL; if (bIsMultiThreaded) { // If value is set on render thread, then this should override main thread value pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID]; if (pProcessData->bSetThisFrame) { pFillData->vParam = pProcessData->vParam; } } // Reset set value pFillData->bSetThisFrame = false; if (bIsMultiThreaded) { // Copy fill data into process data memcpy(pProcessData, pFillData, sizeof(CParamVec4ThreadSafeData)); } } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// #if !defined(_RELEASE) static void SetPostEffectParamF(IConsoleCmdArgs* pArgs) { if (pArgs->GetArgCount() < 3) { return; } bool bForceValue = false; if (pArgs->GetArgCount() > 3) { const int iForceValue = (int)atoi(pArgs->GetArg(3)); if (iForceValue != 0) { bForceValue = true; } } const char* szPostEffectParamName = pArgs->GetArg(1); const float fValue = (float)atof(pArgs->GetArg(2)); I3DEngine* pEngine = gEnv->p3DEngine; pEngine->SetPostEffectParam(szPostEffectParamName, fValue, bForceValue); } static void GetPostEffectParamF(IConsoleCmdArgs* pArgs) { if (pArgs->GetArgCount() < 2) { return; } const char* szPostEffectParamName = pArgs->GetArg(1); I3DEngine* pEngine = gEnv->p3DEngine; float fValue = 0.0f; pEngine->GetPostEffectParam(szPostEffectParamName, fValue); CryLogAlways("\nPost effect param value: %f", fValue); } #endif // !defined(_RELEASE) //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// int CPostEffectsMgr::Init() { m_bPostReset = false; ClearCache(); // Initialize the CRC table // This is the official polynomial used by CRC-32 // in PKZip, WinZip and Ethernet. unsigned int ulPolynomial = 0x04c11db7; // 256 values representing ASCII character codes. for (int i = 0; i <= 0xFF; i++) { m_nCRC32Table[i] = CRC32Reflect(i, 8) << 24; for (int j = 0; j < 8; j++) { m_nCRC32Table[i] = (m_nCRC32Table[i] << 1) ^ (m_nCRC32Table[i] & (1 << 31) ? ulPolynomial : 0); } m_nCRC32Table[i] = CRC32Reflect(m_nCRC32Table[i], 32); } //i // Register default parameters AddParamFloat("Global_Brightness", m_pBrightness, 1.0f); // brightness AddParamFloat("Global_Contrast", m_pContrast, 1.0f); // contrast AddParamFloat("Global_Saturation", m_pSaturation, 1.0f); // saturation AddParamFloat("Global_ColorC", m_pColorC, 0.0f); // cyan amount AddParamFloat("Global_ColorY", m_pColorY, 0.0f); // yellow amount AddParamFloat("Global_ColorM", m_pColorM, 0.0f); // magenta amount AddParamFloat("Global_ColorK", m_pColorK, 0.0f); // darkness amount AddParamFloat("Global_ColorHue", m_pColorHue, 0.0f); // image hue rotation // User parameters AddParamFloat("Global_User_Brightness", m_pUserBrightness, 1.0f); // brightness AddParamFloat("Global_User_Contrast", m_pUserContrast, 1.0f); // contrast AddParamFloat("Global_User_Saturation", m_pUserSaturation, 1.0f); // saturation AddParamFloat("Global_User_ColorC", m_pUserColorC, 0.0f); // cyan amount AddParamFloat("Global_User_ColorY", m_pUserColorY, 0.0f); // yellow amount AddParamFloat("Global_User_ColorM", m_pUserColorM, 0.0f); // magenta amount AddParamFloat("Global_User_ColorK", m_pUserColorK, 0.0f); // darkness amount AddParamFloat("Global_User_ColorHue", m_pUserColorHue, 0.0f); // image hue rotation AddParamFloat("Global_User_HDRBloom", m_UserHDRBloom, 0.f); // bloom amount // Register all post processes AddEffect(CSceneSnow); AddEffect(CSceneRain); AddEffect(CSunShafts); AddEffect(CDepthOfField); AddEffect(CMotionBlur); AddEffect(CUnderwaterGodRays); AddEffect(CVolumetricScattering); AddEffect(CRainDrops); AddEffect(CWaterDroplets); AddEffect(CWaterFlow); AddEffect(CScreenFrost); AddEffect(CAlienInterference); AddEffect(CFlashBang); AddEffect(CFilterSharpening); AddEffect(CFilterBlurring); AddEffect(CColorGrading); AddEffect(CHudSilhouettes); AddEffect(CImageGhosting); AddEffect(CWaterRipples); AddEffect(CWaterVolume); AddEffect(CPostAA); AddEffect(CFilterKillCamera); AddEffect(CUberGamePostProcess); AddEffect(CSoftAlphaTest); AddEffect(CScreenBlood); AddEffect(CPost3DRenderer); AddEffect(CGhostVision); AddEffect(ScreenFader); // Sort all post effects by ID std::sort(m_pEffects.begin(), m_pEffects.end(), SortEffectsByID); // Initialize all post process techniques std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectInitialize()); m_bCreated = false; // Initialize parameters StringEffectMapItor pItor = m_pNameIdMapGen.begin(), pEnd = m_pNameIdMapGen.end(); for (; pItor != pEnd; ++pItor) { m_pNameIdMap.insert(KeyEffectMapItor::value_type(GetCRC(pItor->first.c_str()), pItor->second)); } m_pNameIdMapGen.clear(); #if !defined(_RELEASE) REGISTER_COMMAND("r_setposteffectparamf", SetPostEffectParamF, VF_CHEAT, "Sets post effect param (float)n" "Usage: r_setposteffectparamf [posteffectparamname, value, forceValue(OPTIONAL)]\n" "Example: r_setposteffectparamf HUD3D_FOV 35.0 (Doesn't force value)\n" "Example: r_setposteffectparamf HUD3D_FOV 35.0 1 (Forces value)\n"); REGISTER_COMMAND("r_getposteffectparamf", GetPostEffectParamF, VF_CHEAT, "Outputs post effect param value (float) to log" "Usage: r_setposteffectparamf [posteffectparamname]\n" "Example: r_getposteffectparamf HUD3D_FOV\n"); #endif // TESTING static int r_3MonHack; static float r_3MonHackHUDFOVX; static float r_3MonHackHUDFOVY; static float r_3MonHackLeftCGFOffsetX; static float r_3MonHackRightCGFOffsetX; REGISTER_CVAR(r_3MonHack, 0, VF_CHEAT | VF_CHEAT_NOCHECK, "Enables 3 monitor hack hud in center"); REGISTER_CVAR(r_3MonHackHUDFOVX, 28, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - X FOV"); REGISTER_CVAR(r_3MonHackHUDFOVY, 60, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - Y FOV"); REGISTER_CVAR(r_3MonHackLeftCGFOffsetX, 0.93f, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - Adds position offset in X direction to all left CGF planes"); REGISTER_CVAR(r_3MonHackRightCGFOffsetX, -0.93f, VF_CHEAT | VF_CHEAT_NOCHECK, "3 monitor hack hud in center - Adds position offset in X direction to all right CGF planes"); return 1; } void CPostEffectsMgr::CreateResources() { // Initialize all post process techniques if (!m_bCreated) { std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectCreateResources()); } m_bCreated = true; } void CPostEffectsMgr::ReleaseResources() { if (m_bCreated) { std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_release()); } #ifndef _RELEASE ClearDebugInfo(); #endif m_bCreated = false; } void CPostEffectsMgr::Release() { // Free all resources ClearCache(); std::for_each(m_pNameIdMap.begin(), m_pNameIdMap.end(), SContainerKeyEffectParamDelete()); m_pNameIdMap.clear(); std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_release()); std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_delete()); m_pEffects.clear(); m_bCreated = false; #if !defined(_RELEASE) IConsole* pConsole = gEnv->pConsole; if (pConsole) { pConsole->RemoveCommand("r_setposteffectparamf"); pConsole->RemoveCommand("r_getposteffectparamf"); } #endif } void CPostEffectsMgr::Reset(bool bOnSpecChange) { ClearCache(); m_pBrightness->ResetParam(1.0f); m_pContrast->ResetParam(1.0f); m_pSaturation->ResetParam(1.0f); m_pColorC->ResetParam(0.0f); m_pColorY->ResetParam(0.0f); m_pColorM->ResetParam(0.0f); m_pColorK->ResetParam(0.0f); m_pColorHue->ResetParam(0.0f); m_pUserBrightness->ResetParam(1.0f); m_pUserContrast->ResetParam(1.0f); m_pUserSaturation->ResetParam(1.0f); m_pUserColorC->ResetParam(0.0f); m_pUserColorY->ResetParam(0.0f); m_pUserColorM->ResetParam(0.0f); m_pUserColorK->ResetParam(0.0f); m_pUserColorHue->ResetParam(0.0f); if (bOnSpecChange) { std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectResetOnSpecChange()); } else { std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectReset()); } } int32 CPostEffectsMgr::GetEffectID(const char* pEffectName) { int32 effectID = ePFX_Invalid; CPostEffectsMgr* pPostMgr = PostEffectMgr(); for (CPostEffectItor pItor = pPostMgr->GetEffects().begin(), pItorEnd = pPostMgr->GetEffects().end(); pItor != pItorEnd; ++pItor) { CPostEffect* pCurrEffect = (*pItor); if (strcmp(pEffectName, pCurrEffect->GetName()) == 0) { effectID = pCurrEffect->GetID(); break; } } return effectID; } void CPostEffectsMgr::OnLostDevice() { std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectOnLostDevice()); } void CPostEffectsMgr::OnBeginFrame() { std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectOnBeginFrame()); } void CPostEffectsMgr::SyncMainWithRender() { KeyEffectMapItor iter; for (iter = m_pNameIdMap.begin(); iter != m_pNameIdMap.end(); iter++) { iter->second->SyncMainWithRender(); } } // Used only when creating the crc table uint32 CPostEffectsMgr::CRC32Reflect(uint32 ref, char ch) { uint32 value = 0; // Swap bit 0 for bit 7 // bit 1 for bit 6, etc. for (int i = 1; i < (ch + 1); i++) { if (ref & 1) { value |= 1 << (ch - i); } ref >>= 1; } return value; } uint32 CPostEffectsMgr::GetCRC(const char* pszName) { if (!pszName) { assert(false && "CPostEffectsMgr::GetCRC() invalid string passed"); return 0; } // Once the lookup table has been filled in by the constructor, // this function creates all CRCs using only the lookup table. // Be sure to use unsigned variables, because negative values introduce high bits where zero bits are required. const char* szPtr = pszName; uint32 ulCRC = 0xffffffff; // Start out with all bits set high. unsigned char c; while (*szPtr) { c = *szPtr++; if (c >= 'a' && c <= 'z') { c -= 32; //convert to uppercase } ulCRC = (ulCRC >> 8) ^ m_nCRC32Table[(ulCRC & 0xFF) ^ c]; } // Exclusive OR the result with the beginning value...avoids the % operator return ulCRC ^ 0xffffffff; } CEffectParam* CPostEffectsMgr::GetByName(const char* pszParam) { assert(pszParam || "mfGetByName: null FX name"); uint32 nCurrKey = GetCRC(pszParam); int nThreadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; // cache per-thread ParamCache* pCache = &m_pParamCache[nThreadID]; // Check cache first if (nCurrKey == pCache->m_nKey && pCache->m_pParam) { if (CRenderer::CV_r_PostProcess == 3) { m_pEffectParamsUpdated.insert(StringEffectMapItor::value_type(pszParam, pCache->m_pParam)); } return pCache->m_pParam; } KeyEffectMapItor pItor = m_pNameIdMap.find(nCurrKey); if (pItor != m_pNameIdMap.end()) { pCache->m_pParam = pItor->second; pCache->m_nKey = nCurrKey; if (CRenderer::CV_r_PostProcess == 3) { m_pEffectParamsUpdated.insert(StringEffectMapItor::value_type(pszParam, pCache->m_pParam)); } return pCache->m_pParam; } return 0; } float CPostEffectsMgr::GetByNameF(const char* pszParam) { CEffectParam* pParam = GetByName(pszParam); if (pParam) { return pParam->GetParam(); } return 0.0f; } Vec4 CPostEffectsMgr::GetByNameVec4(const char* pszParam) { CEffectParam* pParam = GetByName(pszParam); if (pParam) { return pParam->GetParamVec4(); } return Vec4(0, 0, 0, 0); } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// int CPostEffectsMgr::SortEffectsByID(const CPostEffect* p1, const CPostEffect* p2) { return (p1->GetID() < p2->GetID()); } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// int CParamTexture::Create(const char* pszFileName) { if (!pszFileName || pszFileName[0] == '\0') { return 0; } const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; CParamTextureThreadSafeData* pThreadSafeData = &m_threadSafeData[threadID]; if (pThreadSafeData->pTexParam) { // check if texture is same if (!azstricmp(pThreadSafeData->pTexParam->GetName(), pszFileName)) { return 0; } // release texture if required SAFE_RELEASE(pThreadSafeData->pTexParam); } pThreadSafeData->pTexParam = CTexture::ForName(pszFileName, FT_DONT_STREAM, eTF_Unknown); pThreadSafeData->bSetThisFrame = true; assert(pThreadSafeData->pTexParam || "CParamTexture.Create: texture not found!"); return 1; } const char* CParamTexture::GetParamString() const { const int threadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0; const CTexture* texture = m_threadSafeData[threadID].pTexParam; return texture ? texture->GetName() : ""; } void CParamTexture::Release() { CParamTextureThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID]; const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false; if (bIsMultiThreaded) { CParamTextureThreadSafeData* pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID]; if (pProcessData->pTexParam != pFillData->pTexParam) { SAFE_RELEASE(pProcessData->pTexParam); pProcessData->bSetThisFrame = true; } } SAFE_RELEASE(pFillData->pTexParam); pFillData->bSetThisFrame = true; } void CParamTexture::SyncMainWithRender() { CParamTextureThreadSafeData* pFillData = &m_threadSafeData[gRenDev->m_RP.m_nFillThreadID]; const bool bIsMultiThreaded = (gRenDev->m_pRT) ? (gRenDev->m_pRT->IsMultithreaded()) : false; CParamTextureThreadSafeData* pProcessData = NULL; if (bIsMultiThreaded) { // If value is set on render thread, then this should override main thread value pProcessData = &m_threadSafeData[gRenDev->m_RP.m_nProcessThreadID]; if (pProcessData->bSetThisFrame) { if (pFillData->bSetThisFrame) { // If the main thread also set a texture on the same frame (highly unlikely), then release this texture // 1st before overriding it with the texture set from the render thread SAFE_RELEASE(pFillData->pTexParam); } pFillData->pTexParam = pProcessData->pTexParam; } } // Reset set value pFillData->bSetThisFrame = false; if (bIsMultiThreaded) { // Copy fill data into process data memcpy(pProcessData, pFillData, sizeof(CParamTextureThreadSafeData)); } }