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

527 lines
16 KiB
C++

//
// CCustomShaderEffectAnimUV - class for managing UV animation for entities and their shaders;
//
// 20/06/2006 - Andrzej: - intial;
// 19/11/2007 - Andrzej: - Update(): anim time synchronization between entities and their lods added;
//
// The gta5 'testbed' level has an example of an AnimUV.
#include "shaders\CustomShaderEffectAnimUV.h"
//Rage includes
#include "cranimation/frame.h"
#include "cranimation/framedata.h"
#include "fwanimation/animmanager.h"
#include "fwanimation/directorcomponentcreature.h"
#include "fwgeovis/geovis.h"
#include "fwscene/stores/clipdictionarystore.h"
#include "fwsys/timer.h" // fwTimer()...
#include "fwdrawlist/drawlistmgr.h"
// Game headers
#include "scene/Entity.h"
#include "shaders/ShaderLib.h"
#include "streaming/defragmentation.h"
#include "Control/Replay/Replay.h"
ANIM_OPTIMISATIONS()
// Defines
#define VARNAME_GLOBAL_ANIM_UV0 ("globalAnimUV0")
#define VARNAME_GLOBAL_ANIM_UV1 ("globalAnimUV1")
#if CSE_ANIM_EDITABLEVALUES
static bool bDbgUpdateAnimUVEnabled = TRUE;
#endif
//
//
//
//
CCustomShaderEffectAnimUV::CCustomShaderEffectAnimUV(u32 size)
: CCustomShaderEffectBase(size, CSE_ANIMUV)
{
}
//
//
//
//
CCustomShaderEffectAnimUV::~CCustomShaderEffectAnimUV()
{
}
//
//
// creates extra shader variables in drawable, which will hold UV animation variables;
//
bool CCustomShaderEffectAnimUV::CreateExtraAnimationUVvariables(rmcDrawable *pDrawable)
{
Assert(pDrawable);
if(!pDrawable)
return(FALSE);
bool bVarsCreated = FALSE;
grmShaderGroup *pShaderGroup = &pDrawable->GetShaderGroup();
Assert(pShaderGroup);
if(!pShaderGroup)
return(FALSE);
const s32 shaderCount = pShaderGroup->GetCount();
Assert(shaderCount >= 1);
for(s32 j=0; j<shaderCount; j++)
{
grmShader *pShader = &pShaderGroup->GetShader(j);
Assert(pShader);
// check if shader contains UV anim variables:
grcEffectVar shaderEffectVarUV0 = pShader->LookupVar(VARNAME_GLOBAL_ANIM_UV0, FALSE);
grcEffectVar shaderEffectVarUV1 = pShader->LookupVar(VARNAME_GLOBAL_ANIM_UV1, FALSE);
if(shaderEffectVarUV0 && shaderEffectVarUV1)
{
// this is simpler version of it, we don't create ShaderVars (=save memory)
// and assume that all instances of the building will reset their uv anims otherwise
// (by applying zero anim for instance):
bVarsCreated = TRUE;
}// if(shaderEffectVarUV0 && shaderEffectVarUV1)...
}//for(s32 j=0; j<shaderCount; j++)...
return(bVarsCreated);
}
//
//
// master initialize in CBaseModelInfo::SetDrawable()
//
CCustomShaderEffectAnimUVType* CCustomShaderEffectAnimUVType::Create(rmcDrawable *pDrawable, u32 nObjectHashKey, s32 animDictFileIndex)
{
Assert(pDrawable);
if(!pDrawable)
return NULL;
if(!nObjectHashKey)
return NULL;
// Is the anim dictionary invalid?
if(animDictFileIndex < 0)
{
return NULL;
}
grmShaderGroup *pShaderGroup = &pDrawable->GetShaderGroup();
const s32 shaderCount = pShaderGroup->GetCount();
Assert(shaderCount >= 1);
CCustomShaderEffectAnimUVType *pType = rage_new CCustomShaderEffectAnimUVType;
pType->m_tabAnimUVInfo.Reserve(shaderCount);
for(s32 i=0; i<shaderCount; i++)
{
CCustomShaderEffectAnimUVType::structAnimUVinfo& animInfo = pType->m_tabAnimUVInfo.Append();
animInfo.m_pClip = NULL;
animInfo.m_shaderIdx = 0;
animInfo.m_varUV0 = grcevNONE;
animInfo.m_varUV1 = grcevNONE;
}
pType->m_wadAnimIndex = animDictFileIndex;
bool bAnimsFound = FALSE;
for(s32 j=0; j<shaderCount; j++)
{
// load animations for shaders (if they exist):
// UV anim names are of the form <objectNameString>"_uv_"<shaderIndexString>
// Normally during resourcing (in ragebuilder) the hashKey for an anim is created using :
// animHash = atStringHash(<animNameString>)
// But the hashkey for anims that contain the string "_uv_" are created using :
// animHash = atStringHash(<objectNameString>) + <shaderIndex> + 1
// ragebuilder.cpp:
// - JimmyClipDictionary::CalcKey(): code = atStringHash(rootName) + atoi(p_Found) + 1;
// - (no longer used) SaveDictionaryOfAnims(): code = atStringHash(rootName) + atoi(p_Found) + 1;
// create animation hashkey (objHash+shaderNo+1)
const u32 nAnimHashKey = nObjectHashKey+j+1;
const crClip* pClip = fwAnimManager::GetClipIfExistsByDictIndex(animDictFileIndex, nAnimHashKey);
if(pClip)
{
grmShader *pShader = &pShaderGroup->GetShader(j);
Assert(pShader);
grcEffectVar effectVarUV0 = pShader->LookupVar(VARNAME_GLOBAL_ANIM_UV0, FALSE);
grcEffectVar effectVarUV1 = pShader->LookupVar(VARNAME_GLOBAL_ANIM_UV1, FALSE);
// Andrzej's temp fix:
// this is wrong, proper animations should be returned for given shaders' indices.
if(effectVarUV0 != grcevNONE && effectVarUV1 != grcevNONE)
{
bAnimsFound = TRUE;
CCustomShaderEffectAnimUVType::structAnimUVinfo *pAnimInfo = &pType->m_tabAnimUVInfo[j];
pAnimInfo->m_pClip = pClip;
Assign(pAnimInfo->m_shaderIdx,j);
Assign(pAnimInfo->m_varUV0,effectVarUV0);
Assign(pAnimInfo->m_varUV1,effectVarUV1);
//u32 frameCount = rage::sysTimeManager::GetGlobalManager().GetFrameCount();
//extern char* CCustomShaderEffectBase_EntityName;
//Warningf("frameCount = %u : ObjectName (%s) has been assigned UV anim (animDictName %s, animDictHash %d, animName %d, animHash %d) to shader (%d)", frameCount, CCustomShaderEffectBase_EntityName, g_ClipDictionaryStore.GetName(animDictFileIndex), animDictFileIndex, pClip->GetName().c_str(), nAnimHashKey, j);
}
#if !__FINAL && !HEIGHTMAP_GENERATOR_TOOL
else
{
// this is wrong: animation is present (as taken from hashkey),
// but shader doesn't support it (probably wrong hash was generated?):
u32 frameCount = rage::sysTimeManager::GetGlobalManager().GetFrameCount();
extern char* CCustomShaderEffectBase_EntityName;
Warningf("frameCount = %u : ObjectName (%s) has been wrongly assigned UV anim (animDictName %s, animDictIndex %d, animName %s, animHash %d) to shader (%d) hashkey mismatch?", frameCount, CCustomShaderEffectBase_EntityName, g_ClipDictionaryStore.GetName(strLocalIndex(animDictFileIndex)), animDictFileIndex, pClip->GetName(), nAnimHashKey, j);
}
#endif //!__FINAL && !HEIGHTMAP_GENERATOR_TOOL
}// if(pClip)...
else
{
//u32 frameCount = rage::sysTimeManager::GetGlobalManager().GetFrameCount();
//extern char* CCustomShaderEffectBase_EntityName;
//char uvAnimName[100];
//sprintf(uvAnimName, "%d", CCustomShaderEffectBase_EntityName, "_uv_0", j);
//Warningf("frameCount = %u : Object '%s' Can't find UV animation (animDictName %s, animDictHash %d, animName %d, animHash %d) to shader (%d)", frameCount, CCustomShaderEffectBase_EntityName, g_ClipDictionaryStore.GetName(animDictFileIndex), animDictFileIndex, uvAnimName, nAnimHashKey, j);
}
}//for(s32 j=0; j<numShaders; j++)...
// The anim dictionary is streamed in as a dependency of the drawable
// Add reference so the anim dictionary is not deleted
if(animDictFileIndex > -1)
{
fwAnimManager::AddRef(strLocalIndex(animDictFileIndex));
}
// No anims found return nothing
if(!bAnimsFound)
{
delete pType;
return NULL;
}
#if __DEV
//extern char* CCustomShaderEffectBase_EntityName;
//AreClipsValid(pNewAnimUV, CCustomShaderEffectBase_EntityName);
#endif //__DEV
return pType;
}// end of Create()...
//
//
// master shutdown in CBaseModelInfo::DeleteDrawable()
//
CCustomShaderEffectAnimUVType::~CCustomShaderEffectAnimUVType()
{
if(m_wadAnimIndex > -1)
{
// remove a reference to the anim dictionary
fwAnimManager::RemoveRef(strLocalIndex(m_wadAnimIndex));
}
for (int i=0; i<m_tabAnimUVInfo.GetCount(); i++)
m_tabAnimUVInfo[i].m_pClip = 0;
#if __DEV
//AreClipsValid(this);
#endif //__DEV
}// end of Shutdown()...
//
//
// returns AnimUVFX of given entity's LOD (if one exists):
//
// static
// CCustomShaderEffectAnimUV* GetLODAnimUVFX(CEntity *pEntity)
// {
// if(!pEntity->__GetLod())
// return(NULL);
//
// CEntity *pLodEntity = (CEntity*)pEntity->__GetLod();
// Assert(pLodEntity);
//
// if(pLodEntity->GetAlphaFade()==0)
// return(NULL);
//
// CGtaEntityDrawData* lodGtaDrawable = pLodEntity->GetDrawData();
// if(!lodGtaDrawable)
// return(NULL);
//
//
// CCustomShaderEffectAnimUV *pLodAnimUVFX = static_cast<CCustomShaderEffectAnimUV*>(lodGtaDrawable->GetShaderEffect());
// return(pLodAnimUVFX);
// }
//
//
// instance update in CEntity::PreRender()
//
void CCustomShaderEffectAnimUV::Update(fwEntity*)
{
#if CSE_ANIM_EDITABLEVALUES
// debug widgets:
if(!bDbgUpdateAnimUVEnabled)
return;
#endif //CSE_ANIM_EDITABLEVALUES...
// Is the anim dictionary valid?
if (m_pType->m_wadAnimIndex > -1)
{
// synchronize with this entity LOD's UV anim (if available) (BS#156358):
const s32 count = m_pType->m_tabAnimUVInfo.GetCount();
// JW : shouldn't need to sync anims between LODs because they should all get created synced to LodTree local clock
// if(count)
// {
// CCustomShaderEffectAnimUV *pLodAnimUVFX = GetLODAnimUVFX(pEntity);
// if(pLodAnimUVFX)
// {
// const s32 maxCountLod = pLodAnimUVFX->m_tabAnimUVInfoCount;
// for(s32 i=0; i<count; i++)
// {
// const crClip *pClip = m_tabAnimUVInfo[i].m_pClip;
// if(pClip)
// {
// const float animDuration = pClip->GetDuration();
// const float animNumFrames = pClip->GetNum30Frames();
// for(s32 j=0; j<maxCountLod; j++)
// {
// const crClip *pLodClip = pLodAnimUVFX->m_tabAnimUVInfo[j].m_pClip;
// if(pLodClip)
// {
// // find matching anim (by length and num frames):
// // JA - This seems like an error prone way of matching the anim why not use the anim hash?
// if( (pLodClip->GetDuration() == animDuration)
// && (pLodClip->GetNum30Frames() == animNumFrames) )
// {
// m_tabAnimUVInfo[i].m_timeAnimStart = pLodAnimUVFX->m_tabAnimUVInfo[j].m_timeAnimStart;
// }
// }
// }//for(s32 j=0; j<maxCountLod; j++)...
// }//if(pAnim)...
// }//for(s32 i=0; i<count; i++)...
// }//if(pLodAnimUVFX)...
// }//if(count)...
crFrameDataFixedDofs<2> frameData;
frameData.AddDof(kTrackShaderSlideU, 0, kFormatTypeVector3);
frameData.AddDof(kTrackShaderSlideV, 0, kFormatTypeVector3);
crFrameFixedDofs<2> frame(frameData);
frame.SetAccelerator(fwAnimDirectorComponentCreature::GetAccelerator());
CCustomShaderEffectAnimUVType *pType = m_pType;
// update animation
for(s32 i=0; i<count; i++)
{
CCustomShaderEffectAnimUVType::structAnimUVinfo *pAnimInfo = &pType->m_tabAnimUVInfo[i];
const crClip *pClip = pAnimInfo->m_pClip;
if(pClip)
{
const s32 nAnimDurationInMs = s32(pClip->GetDuration() * 1000.0f);
const s32 nCurrTimeInMs = fwTimer::GetTimeInMilliseconds();
s32 nCurrAnimTimeMs = nCurrTimeInMs - m_uvs[i].m_timeAnimStart;
//Assertf(nCurrAnimTime >= 0, "Animation time is not in positive domain - something wrong with current gametime.");
if(nCurrAnimTimeMs < 0) // temp fix for bug #22526 & #54280
{
#if GTA_REPLAY
//On replay when we rewind or jump backwards on the timeline we need to wrap the anim times correctly.
if( CReplayMgr::IsEditModeActive() )
{
nCurrAnimTimeMs = nAnimDurationInMs + nCurrAnimTimeMs;
m_uvs[i].m_timeAnimStart = nCurrTimeInMs - nCurrAnimTimeMs;
}
else
#endif //GTA_REPLAY
{
nCurrAnimTimeMs = 0;
}
}
// How many frames have we looped by
if ( nCurrTimeInMs > (m_uvs[i].m_timeAnimStart + nAnimDurationInMs) )
{
nCurrAnimTimeMs = nCurrAnimTimeMs % nAnimDurationInMs;
m_uvs[i].m_timeAnimStart = nCurrTimeInMs - nCurrAnimTimeMs;
}
float fCurrAnimPhase = float(nCurrAnimTimeMs)/nAnimDurationInMs;
float fCurrAnim30Frame = pClip->ConvertPhaseTo30Frame(fCurrAnimPhase);
fCurrAnim30Frame = float(s32(fCurrAnim30Frame));
pClip->Composite(frame, pClip->Convert30FrameToTime(fCurrAnim30Frame));
// Save and restore timeAnimStart since it may get stomped by vector operations
unsigned temp = m_uvs[i].m_timeAnimStart;
frame.GetValue<Vec3V>(kTrackShaderSlideU, 0, (Vec3V&)(m_uvs[i].m_uv0));
frame.GetValue<Vec3V>(kTrackShaderSlideV, 0, (Vec3V&)(m_uvs[i].m_uv1));
m_uvs[i].m_timeAnimStart = temp;
// invert v1 offset
// (Greg's TODO #16006: to be done during export)
// pAnimInfo->m_uv1.z = 1.0f - pAnimInfo->m_uv1.z;
}// if(pAnim)...
} // end of for(s32 i=0; i<count; i++)...
}//m_wadAnimIndex > -1)
} // end of Update()...
//
//
// instance setting variables in CEntity/DynamicEntity::Render()
//
//void CCustomShaderEffectAnimUV::SetShaderVariables(CEntity* ASSERT_ONLY(pEntity))
void CCustomShaderEffectAnimUV::SetShaderVariables(rmcDrawable* pDrawable)
{
if(!pDrawable)
return;
if(DRAWLISTMGR->IsExecutingShadowDrawList() || grmModel::GetForceShader())
return;
#if __BANK
if(DRAWLISTMGR->IsExecutingDebugOverlayDrawList())
return;
#endif
const s32 count = m_pType->m_tabAnimUVInfo.GetCount();
Assert(count > 0);
for(s32 i=0; i<count; i++)
{
CCustomShaderEffectAnimUVType::structAnimUVinfo *pAnimInfo = &m_pType->m_tabAnimUVInfo[i];
if(pAnimInfo->m_pClip)
{
pDrawable->GetShaderGroup().GetShader(pAnimInfo->m_shaderIdx).SetVar((grcEffectVar)pAnimInfo->m_varUV0, (Vector3&)m_uvs[i].m_uv0);
pDrawable->GetShaderGroup().GetShader(pAnimInfo->m_shaderIdx).SetVar((grcEffectVar)pAnimInfo->m_varUV1, (Vector3&)m_uvs[i].m_uv1);
}
}
}// end of SetShaderVariables()...
//
//
// extra access to light's animation:
//
bool CCustomShaderEffectAnimUV::GetLightAnimUV(Vector3 *uv0, Vector3 *uv1)
{
if((!uv0) || (!uv1))
return(FALSE);
const s32 count = m_pType->m_tabAnimUVInfo.GetCount();
Assert(count > 0);
Vector3& out0 = *uv0;
Vector3& out1 = *uv1;
// find 1st anim data and return it at it is:
for(s32 i=0; i<count; i++)
{
CCustomShaderEffectAnimUVType::structAnimUVinfo *pAnimInfo = &m_pType->m_tabAnimUVInfo[i];
if(pAnimInfo->m_pClip)
{
out0.Set(m_uvs[i].m_uv0[0],m_uvs[i].m_uv0[1],m_uvs[i].m_uv0[2]);
out1.Set(m_uvs[i].m_uv1[0],m_uvs[i].m_uv1[1],m_uvs[i].m_uv1[2]);
break;
}
}
return(TRUE);
}
//
//
//
//
void CCustomShaderEffectAnimUV::AddToDrawList(u32 modelIndex, bool bExecute)
{
DLC(CCustomShaderEffectDC, (*this, modelIndex, bExecute, m_pType));
}
//
//
// instance create in CEntity::CreateDrawable()
//
CCustomShaderEffectBase* CCustomShaderEffectAnimUVType::CreateInstance(CEntity* pEntity)
{
const u32 seedVal = (((u32)(size_t) pEntity->GetRootLod()) >> 3) & 0xff; // for syncing across LODs
const s32 count = m_tabAnimUVInfo.GetCount();
u32 size = sizeof(CCustomShaderEffectAnimUV) + count * sizeof(CCustomShaderEffectAnimUV::structUVs);
CCustomShaderEffectAnimUV* pNewAnimUV = new(rage_aligned_new(16) u8[size]) CCustomShaderEffectAnimUV(size);
pNewAnimUV->m_pType = this;
for(s32 i=0; i<count; i++)
{
CCustomShaderEffectAnimUV::structUVs &uv = pNewAnimUV->m_uvs[i];
uv.m_uv0[0] = 1.0f; uv.m_uv0[1] = 0.0f; uv.m_uv0[2] = 0.0f; uv.m_timeAnimStart = seedVal;
uv.m_uv1[0] = 0.0f; uv.m_uv1[1] = 1.0f; uv.m_uv1[2] = 0.0f; uv.pad = 0;
}
#if __DEV
//AreClipsValid(pNewAnimUV);
#endif //__DEV
return(pNewAnimUV);
}// end of CreateInstance()...
//
//
//
//
#if __DEV
// Added to help track down defrag bugs
void CCustomShaderEffectAnimUV::AreClipsValid(CCustomShaderEffectAnimUV* pShader, const char *pName)
{
if (pShader)
{
Assertf(g_ClipDictionaryStore.IsValidSlot(pShader->GetClipDictIndex()), "Clip dict index is invalid");
if (pShader->GetClipDictIndex().IsValid())
{
Assertf(g_ClipDictionaryStore.IsValidSlot(pShader->GetClipDictIndex()), "Clip dict has no slot");
Assertf(g_ClipDictionaryStore.IsObjectInImage(pShader->GetClipDictIndex()), "Clip dict is not in image");
Assertf(g_ClipDictionaryStore.HasObjectLoaded(pShader->GetClipDictIndex()), "Clip dict is not loaded");
Assertf(g_ClipDictionaryStore.GetNumRefs(strLocalIndex(pShader->GetClipDictIndex())), "Clip dict has no refs");
}
const s32 count = pShader->m_pType->m_tabAnimUVInfo.GetCount();
for(s32 i=0; i<count; i++)
{
CCustomShaderEffectAnimUVType::structAnimUVinfo* pAnimInfo = &pShader->m_pType->m_tabAnimUVInfo[i];
if ( (pAnimInfo->m_pClip == NULL) || (pAnimInfo->m_pClip->GetType() == crClip::kClipTypeAnimation) )
{
}
else
{
u32 frameCount = rage::sysTimeManager::GetGlobalManager().GetFrameCount();
Errorf("Frame %u = CCustomShaderEffectAnimUV::IsClipValid : Clip is not type kClipTypeAnimation on Object '%s' : UV animation dict (%d) : shader %d\n", frameCount, pName ? pName : "unknown", pShader->GetClipDictIndex().Get(), i);
}
}
}
}
#endif //__DEV
#if CSE_ANIM_EDITABLEVALUES
//
//
//
//
bool CCustomShaderEffectAnimUV::InitWidgets(bkBank& bank)
{
bank.AddToggle("AnimUV update enabled:", &bDbgUpdateAnimUVEnabled);
return(TRUE);
}
#endif //CSE_ANIM_EDITABLEVALUES...