Added support for tracking event character indices in the dependency tree

This commit is contained in:
parax0 2016-10-21 18:16:57 -06:00
parent 10c87779b3
commit a18655da00
16 changed files with 279 additions and 99 deletions

View File

@ -208,7 +208,9 @@ HEADERS += \
Resource/Factory/CAudioGroupLoader.h \
Resource/CAudioLookupTable.h \
Resource/CStringList.h \
CAudioManager.h
CAudioManager.h \
Resource/CAnimEventData.h \
Resource/Factory/CAnimEventLoader.h
# Source Files
SOURCES += \
@ -306,4 +308,5 @@ SOURCES += \
Resource/Factory/CUnsupportedParticleLoader.cpp \
GameProject/DependencyListBuilders.cpp \
Resource/Factory/CAudioGroupLoader.cpp \
CAudioManager.cpp
CAudioManager.cpp \
Resource/Factory/CAnimEventLoader.cpp

View File

@ -42,6 +42,14 @@ void CDependencyTree::AddDependency(CResource *pRes, bool AvoidDuplicates /*= tr
AddDependency(pRes->ID(), AvoidDuplicates);
}
void CDependencyTree::AddEventDependency(const CAssetID& rkID, u32 CharIndex)
{
// Note: No duplicate check because there might be multiple events using the same asset ID with different character indices
if (!rkID.IsValid()) return;
CAnimEventDependency *pDepend = new CAnimEventDependency(rkID, CharIndex);
mChildren.push_back(pDepend);
}
void CDependencyTree::AddDependency(const CAssetID& rkID, bool AvoidDuplicates /*= true*/)
{
if (!rkID.IsValid() || (AvoidDuplicates && HasDependency(rkID))) return;
@ -172,6 +180,18 @@ void CScriptInstanceDependency::ParseStructDependencies(CScriptInstanceDependenc
}
}
// ************ CAnimEventDependency ************
EDependencyNodeType CAnimEventDependency::Type() const
{
return eDNT_AnimEvent;
}
void CAnimEventDependency::Serialize(IArchive& rArc)
{
CResourceDependency::Serialize(rArc);
rArc << SERIAL("CharacterIndex", mCharIndex);
}
// ************ CAnimSetDependencyTree ************
EDependencyNodeType CAnimSetDependencyTree::Type() const
{

View File

@ -19,6 +19,7 @@ enum EDependencyNodeType
eDNT_ScriptInstance = FOURCC_CONSTEXPR('S', 'C', 'I', 'N'),
eDNT_ScriptProperty = FOURCC_CONSTEXPR('S', 'C', 'P', 'R'),
eDNT_CharacterProperty = FOURCC_CONSTEXPR('C', 'R', 'P', 'R'),
eDNT_AnimEvent = FOURCC_CONSTEXPR('E', 'V', 'N', 'T'),
eDNT_AnimSet = FOURCC_CONSTEXPR('A', 'N', 'C', 'S'),
eDNT_Area = FOURCC_CONSTEXPR('A', 'R', 'E', 'A'),
};
@ -55,6 +56,7 @@ public:
void AddDependency(const CAssetID& rkID, bool AvoidDuplicates = true);
void AddDependency(CResource *pRes, bool AvoidDuplicates = true);
void AddEventDependency(const CAssetID& rkID, u32 CharIndex);
// Accessors
inline void SetID(const CAssetID& rkID) { mRootID = rkID; }
@ -145,6 +147,24 @@ protected:
static void ParseStructDependencies(CScriptInstanceDependency *pTree, CPropertyStruct *pStruct);
};
// Node representing an animation event.
class CAnimEventDependency : public CResourceDependency
{
protected:
u32 mCharIndex;
public:
CAnimEventDependency() : CResourceDependency() {}
CAnimEventDependency(const CAssetID& rkID, u32 CharIndex)
: CResourceDependency(rkID), mCharIndex(CharIndex) {}
virtual EDependencyNodeType Type() const;
virtual void Serialize(IArchive& rArc);
// Accessors
inline u32 CharIndex() const { return mCharIndex; }
};
// Node representing an animset resource; allows for lookup of dependencies of a particular character in the set.
class CAnimSetDependencyTree : public CDependencyTree
{
@ -200,9 +220,10 @@ public:
case eDNT_ScriptInstance: return new CScriptInstanceDependency;
case eDNT_ScriptProperty: return new CPropertyDependency;
case eDNT_CharacterProperty: return new CCharPropertyDependency;
case eDNT_AnimEvent: return new CAnimEventDependency;
case eDNT_AnimSet: return new CAnimSetDependencyTree;
case eDNT_Area: return new CAreaDependencyTree;
default: return nullptr;
default: ASSERT(false); return nullptr;
}
}
};

View File

@ -108,7 +108,11 @@ void CResourceStore::LoadResourceDatabase()
mpDatabaseRoot = new CVirtualDirectory();
CXMLReader Reader(Path);
if (!mpProj) mGame = Reader.Game();
if (mpProj)
ASSERT(mpProj->Game() == Reader.Game());
mGame = Reader.Game();
SerializeResourceDatabase(Reader);
LoadCacheFile();
}

View File

@ -200,10 +200,7 @@ void CPackageDependencyListBuilder::AddDependency(CResourceEntry *pCurEntry, con
mAreaUsedAssets.clear();
mCurrentAreaHasDuplicates = false;
if (!mEnableDuplicates)
mCurrentAreaHasDuplicates = false;
else
if (mEnableDuplicates)
{
for (u32 iArea = 0; iArea < mpWorld->NumAreas(); iArea++)
{
@ -216,10 +213,18 @@ void CPackageDependencyListBuilder::AddDependency(CResourceEntry *pCurEntry, con
}
}
// Animset - keep track of the current animset ID
else if (ResType == eAnimSet)
mCurrentAnimSetID = rkID;
// Evaluate dependencies of this entry
CDependencyTree *pTree = pEntry->Dependencies();
EvaluateDependencyNode(pEntry, pTree, rOut);
rOut.push_back(rkID);
// Revert current animset ID
if (ResType == eAnimSet)
mCurrentAnimSetID = CAssetID::InvalidID(mGame);
}
void CPackageDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurEntry, IDependencyNode *pNode, std::list<CAssetID>& rOut)
@ -262,6 +267,15 @@ void CPackageDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurE
AddDependency(pCurEntry, pDep->ID(), rOut);
}
else if (Type == eDNT_AnimEvent)
{
CAnimEventDependency *pDep = static_cast<CAnimEventDependency*>(pNode);
u32 CharIndex = pDep->CharIndex();
if (CharIndex == -1 || mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, CharIndex))
AddDependency(pCurEntry, pDep->ID(), rOut);
}
else
{
if (Type == eDNT_ScriptInstance)
@ -372,6 +386,8 @@ void CAreaDependencyListBuilder::AddDependency(const CAssetID& rkID, std::list<C
// For animsets, only add used character indices
if (ResType == eAnimSet && mGame <= eEchoes)
{
mCurrentAnimSetID = rkID;
// Add base dependencies first, then character-specific ones
CAnimSetDependencyTree *pTree = static_cast<CAnimSetDependencyTree*>(pEntry->Dependencies());
u32 BaseEndIdx = (pTree->NumCharacters() > 0 ? pTree->CharacterOffset(0) : pTree->NumChildren());
@ -379,8 +395,20 @@ void CAreaDependencyListBuilder::AddDependency(const CAssetID& rkID, std::list<C
for (u32 iDep = 0; iDep < BaseEndIdx; iDep++)
{
CResourceDependency *pDep = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
ASSERT(pDep->Type() == eDNT_ResourceDependency);
EDependencyNodeType Type = pDep->Type();
ASSERT(Type == eDNT_ResourceDependency || Type == eDNT_AnimEvent);
if (Type == eDNT_ResourceDependency)
AddDependency(pDep->ID(), rOut, pAudioGroupsOut);
else
{
CAnimEventDependency *pEvent = static_cast<CAnimEventDependency*>(pDep);
u32 CharIdx = pEvent->CharIndex();
if (CharIdx == -1 || mCharacterUsageMap.IsCharacterUsed(rkID, CharIdx))
AddDependency(pDep->ID(), rOut, pAudioGroupsOut);
}
}
for (u32 iChar = 0; iChar < pTree->NumCharacters(); iChar++)
@ -400,6 +428,24 @@ void CAreaDependencyListBuilder::AddDependency(const CAssetID& rkID, std::list<C
AddDependency(pDep->ID(), rOut, pAudioGroupsOut);
}
}
mCurrentAnimSetID = CAssetID::InvalidID(mGame);
}
// For EVNT, only add events for used character indices
else if (ResType == eAnimEventData)
{
CDependencyTree *pTree = pEntry->Dependencies();
for (u32 iDep = 0; iDep < pTree->NumChildren(); iDep++)
{
CAnimEventDependency *pDep = static_cast<CAnimEventDependency*>(pTree->ChildByIndex(iDep));
ASSERT(pDep->Type() == eDNT_AnimEvent);
u32 CharIdx = pDep->CharIndex();
if (CharIdx == -1 || mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, CharIdx))
AddDependency(pDep->ID(), rOut, pAudioGroupsOut);
}
}
// For other resource types (except SCAN and DGRP), evaluate all sub-dependencies

View File

@ -34,6 +34,7 @@ class CPackageDependencyListBuilder
CPackage *mpPackage;
EGame mGame;
TResPtr<CWorld> mpWorld;
CAssetID mCurrentAnimSetID;
CCharacterUsageMap mCharacterUsageMap;
std::set<CAssetID> mPackageUsedAssets;
std::set<CAssetID> mAreaUsedAssets;
@ -59,6 +60,7 @@ class CAreaDependencyListBuilder
{
CResourceEntry *mpAreaEntry;
EGame mGame;
CAssetID mCurrentAnimSetID;
CCharacterUsageMap mCharacterUsageMap;
std::set<CAssetID> mBaseUsedAssets;
std::set<CAssetID> mLayerUsedAssets;

View File

@ -0,0 +1,43 @@
#ifndef CANIMEVENTDATA
#define CANIMEVENTDATA
#include "CResource.h"
class CAnimEventData : public CResource
{
struct SEvent
{
u32 mCharacterIndex;
CAssetID mAssetRef;
};
std::vector<SEvent> mEvents;
public:
CAnimEventData(CResourceEntry *pEntry = 0)
: CResource(pEntry)
{
}
CDependencyTree* BuildDependencyTree() const
{
CDependencyTree *pTree = new CDependencyTree(ID());
for (u32 iEvt = 0; iEvt < mEvents.size(); iEvt++)
{
const SEvent& rkEvent = mEvents[iEvt];
pTree->AddEventDependency(rkEvent.mAssetRef, rkEvent.mCharacterIndex);
}
return pTree;
}
inline u32 NumEvents() const { return mEvents.size(); }
inline u32 EventCharacterIndex(u32 EventIdx) const { return mEvents[EventIdx].mCharacterIndex; }
inline CAssetID EventAssetRef(u32 EventIdx) const { return mEvents[EventIdx].mAssetRef; }
inline void AddEvent(u32 CharIdx, CAssetID AssetID) { mEvents.push_back( SEvent { CharIdx, AssetID } ); }
};
#endif // CANIMEVENTDATA

View File

@ -3,6 +3,7 @@
#include "TResPtr.h"
#include "CAnimation.h"
#include "CAnimEventData.h"
#include "CDependencyGroup.h"
#include "CResource.h"
#include "CSkeleton.h"
@ -12,7 +13,6 @@
#include <vector>
// will expand later! this is where animation support will come in
struct SSetCharacter
{
TString Name;
@ -42,7 +42,7 @@ class CAnimSet : public CResource
TResPtr<CAnimation> pAnim;
};
std::vector<SAnimation> mAnims;
std::vector<CDependencyGroup*> mEventDependencies;
std::vector<CAnimEventData*> mEventDependencies;
public:
CAnimSet(CResourceEntry *pEntry = 0) : CResource(pEntry) {}
@ -85,12 +85,13 @@ public:
for (u32 iEvnt = 0; iEvnt < mEventDependencies.size(); iEvnt++)
{
CDependencyGroup *pGroup = mEventDependencies[iEvnt];
CAnimEventData *pData = mEventDependencies[iEvnt];
for (u32 iDep = 0; iDep < pGroup->NumDependencies(); iDep++)
for (u32 iEvt = 0; iEvt < pData->NumEvents(); iEvt++)
{
CAssetID ID = pGroup->DependencyByIndex(iDep);
pTree->AddDependency(ID);
CAssetID ID = pData->EventAssetRef(iEvt);
u32 CharIdx = pData->EventCharacterIndex(iEvt);
pTree->AddEventDependency(ID, CharIdx);
BaseUsedSet.insert(ID);
}
}

View File

@ -9,6 +9,7 @@ CWorld::CWorld(CResourceEntry *pEntry /*= 0*/)
, mpSaveWorld(nullptr)
, mpDefaultSkybox(nullptr)
, mpMapWorld(nullptr)
, mTempleKeyWorldIndex(0)
{
}

View File

@ -157,7 +157,6 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
// Audio Groups
if (Game <= ePrime)
{
#if 0
// Debug: make sure our generated list matches the original, no missing or extra audio groups
std::set<CAssetID> OriginalGroups;
@ -181,9 +180,7 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
Log::Error("Extra audio group: " + pEntry->Name().ToUTF8());
}
}
#endif
#if 0
// Create sorted list of audio groups (sort by group ID)
std::vector<CAudioGroup*> SortedAudioGroups;
@ -201,15 +198,14 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
// Write sorted audio group list to file
rMLVL.WriteLong(SortedAudioGroups.size());
for (u32 iGrp = 0; iGrp < pWorld->mAudioGrps.size(); iGrp++)
for (u32 iGrp = 0; iGrp < SortedAudioGroups.size(); iGrp++)
{
CAudioGroup *pGroup = SortedAudioGroups[iGroup];
CAudioGroup *pGroup = SortedAudioGroups[iGrp];
rMLVL.WriteLong(pGroup->GroupID());
pGroup->ID().Write(rMLVL);
}
#endif
#if 1
#if 0
rMLVL.WriteLong(pWorld->mAudioGrps.size());
for (u32 iGrp = 0; iGrp < pWorld->mAudioGrps.size(); iGrp++)

View File

@ -0,0 +1,94 @@
#include "CAnimEventLoader.h"
#include "Core/CAudioManager.h"
#include "Core/GameProject/CGameProject.h"
void CAnimEventLoader::LoadEvents(IInputStream& rEVNT, bool IsEchoes)
{
u32 Version = rEVNT.ReadLong();
ASSERT(Version == 1 || Version == 2);
// Loop Events
u32 NumLoopEvents = rEVNT.ReadLong();
for (u32 iLoop = 0; iLoop < NumLoopEvents; iLoop++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x1C, SEEK_CUR);
}
// User Events
u32 NumUserEvents = rEVNT.ReadLong();
for (u32 iUser = 0; iUser < NumUserEvents; iUser++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x1F, SEEK_CUR);
rEVNT.ReadString();
}
// Effect Events
u32 NumEffectEvents = rEVNT.ReadLong();
for (u32 iFX = 0; iFX < NumEffectEvents; iFX++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x13, SEEK_CUR);
u32 CharIndex = rEVNT.ReadLong();
rEVNT.Seek(0xC, SEEK_CUR);
CAssetID ParticleID = rEVNT.ReadLong();
mpEventData->AddEvent(CharIndex, ParticleID);
if (IsEchoes)
rEVNT.Seek(0x4, SEEK_CUR);
else
rEVNT.ReadString();
rEVNT.Seek(0x8, SEEK_CUR);
}
// Sound Events
if (Version == 2)
{
u32 NumSoundEvents = rEVNT.ReadLong();
for (u32 iSound = 0; iSound < NumSoundEvents; iSound++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x13, SEEK_CUR);
u32 CharIndex = rEVNT.ReadLong();
rEVNT.Seek(0x4, SEEK_CUR);
u32 SoundID = rEVNT.ReadLong() & 0xFFFF;
rEVNT.Seek(0x8, SEEK_CUR);
if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR);
if (SoundID != 0xFFFF)
{
SSoundInfo SoundInfo = CGameProject::ActiveProject()->AudioManager()->GetSoundInfo(SoundID);
if (SoundInfo.pAudioGroup)
mpEventData->AddEvent(CharIndex, SoundInfo.pAudioGroup->ID());
}
}
}
}
// ************ STATIC ************
CAnimEventData* CAnimEventLoader::LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry)
{
CAnimEventLoader Loader;
Loader.mpEventData = new CAnimEventData(pEntry);
Loader.LoadEvents(rEVNT, false);
return Loader.mpEventData;
}
CAnimEventData* CAnimEventLoader::LoadAnimSetEvents(IInputStream& rANCS)
{
CAnimEventLoader Loader;
Loader.mpEventData = new CAnimEventData();
Loader.LoadEvents(rANCS, true);
return Loader.mpEventData;
}

View File

@ -0,0 +1,19 @@
#ifndef CANIMEVENTLOADER_H
#define CANIMEVENTLOADER_H
#include "Core/Resource/CAnimEventData.h"
#include "Core/Resource/TResPtr.h"
class CAnimEventLoader
{
TResPtr<CAnimEventData> mpEventData;
CAnimEventLoader() {}
void LoadEvents(IInputStream& rEVNT, bool IsEchoes);
public:
static CAnimEventData* LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry);
static CAnimEventData* LoadAnimSetEvents(IInputStream& rANCS);
};
#endif // CANIMEVENTLOADER_H

View File

@ -1,5 +1,5 @@
#include "CAnimSetLoader.h"
#include "CUnsupportedFormatLoader.h"
#include "CAnimEventLoader.h"
#include "Core/GameProject/CResourceStore.h"
#include <Common/Log.h>
@ -381,8 +381,8 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry)
for (u32 iEvnt = 0; iEvnt < EventDataCount; iEvnt++)
{
CDependencyGroup *pGrp = CUnsupportedFormatLoader::LoadEVNT(rANCS, nullptr, true);
Loader.pSet->mEventDependencies.push_back(pGrp);
CAnimEventData *pData = CAnimEventLoader::LoadAnimSetEvents(rANCS);
Loader.pSet->mEventDependencies.push_back(pData);
}
}

View File

@ -2,6 +2,7 @@
#define CRESOURCEFACTORY
#include "CAnimationLoader.h"
#include "CAnimEventLoader.h"
#include "CAnimSetLoader.h"
#include "CAreaLoader.h"
#include "CAudioGroupLoader.h"
@ -34,6 +35,7 @@ public:
switch (pEntry->ResourceType())
{
case eAnimation: return new CAnimation(pEntry);
case eAnimEventData: return new CAnimEventData(pEntry);
case eAnimSet: return new CAnimSet(pEntry);
case eArea: return new CGameArea(pEntry);
case eAudioGroup: return new CAudioGroup(pEntry);
@ -63,7 +65,7 @@ public:
switch (pEntry->ResourceType())
{
case eAnimation: pRes = CAnimationLoader::LoadANIM(rInput, pEntry); break;
case eAnimEventData: pRes = CUnsupportedFormatLoader::LoadEVNT(rInput, pEntry); break;
case eAnimEventData: pRes = CAnimEventLoader::LoadEVNT(rInput, pEntry); break;
case eAnimSet: pRes = CAnimSetLoader::LoadANCSOrCHAR(rInput, pEntry); break;
case eArea: pRes = CAreaLoader::LoadMREA(rInput, pEntry); break;
case eAudioGroup: pRes = CAudioGroupLoader::LoadAGSC(rInput, pEntry); break;

View File

@ -13,77 +13,6 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadCSNG(IInputStream& rCSNG, CResou
return pGroup;
}
CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry, bool IsEchoes /*= false*/)
{
u32 Version = rEVNT.ReadLong();
ASSERT(Version == 1 || Version == 2);
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
// Loop Events
u32 NumLoopEvents = rEVNT.ReadLong();
for (u32 iLoop = 0; iLoop < NumLoopEvents; iLoop++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x1C, SEEK_CUR);
}
// User Events
u32 NumUserEvents = rEVNT.ReadLong();
for (u32 iUser = 0; iUser < NumUserEvents; iUser++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x1F, SEEK_CUR);
rEVNT.ReadString();
}
// Effect Events
u32 NumEffectEvents = rEVNT.ReadLong();
for (u32 iFX = 0; iFX < NumEffectEvents; iFX++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x23, SEEK_CUR);
pGroup->AddDependency(rEVNT.ReadLong());
if (IsEchoes)
rEVNT.Seek(0x4, SEEK_CUR);
else
rEVNT.ReadString();
rEVNT.Seek(0x8, SEEK_CUR);
}
// Sound Events
if (Version == 2)
{
u32 NumSoundEvents = rEVNT.ReadLong();
for (u32 iSound = 0; iSound < NumSoundEvents; iSound++)
{
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x1B, SEEK_CUR);
u32 SoundID = rEVNT.ReadLong() & 0xFFFF;
rEVNT.Seek(0x8, SEEK_CUR);
if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR);
if (SoundID != 0xFFFF)
{
SSoundInfo SoundInfo = CGameProject::ActiveProject()->AudioManager()->GetSoundInfo(SoundID);
pGroup->AddDependency(SoundInfo.pAudioGroup);
}
}
}
return pGroup;
}
CDependencyGroup* CUnsupportedFormatLoader::LoadFRME(IInputStream& rFRME, CResourceEntry *pEntry)
{
u32 Version = rFRME.ReadLong();

View File

@ -12,7 +12,6 @@ class CUnsupportedFormatLoader
public:
static CDependencyGroup* LoadCSNG(IInputStream& rCSNG, CResourceEntry *pEntry);
static CDependencyGroup* LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry, bool IsEchoes = false);
static CDependencyGroup* LoadFRME(IInputStream& rFRME, CResourceEntry *pEntry);
static CDependencyGroup* LoadFSM2(IInputStream& rFSM2, CResourceEntry *pEntry);
static CDependencyGroup* LoadHINT(IInputStream& rHINT, CResourceEntry *pEntry);