Added dependency tree system, methods for generating dependency trees from resources, and saving/loading dependency trees to the project cache folder
This commit is contained in:
parent
c1405bfac1
commit
9341c11ac8
|
@ -32,6 +32,10 @@ public:
|
|||
inline bool HasFlag(FlagEnum Flag) const { return ((mValue & Flag) != 0); }
|
||||
inline bool HasAnyFlags(TFlags Flags) const { return ((mValue & Flags) != 0); }
|
||||
inline bool HasAllFlags(TFlags Flags) const { return ((mValue & Flags) == Flags); }
|
||||
inline void SetFlag(FlagEnum Flag) { mValue |= Flag; }
|
||||
inline void SetFlag(TFlags Flags) { mValue |= Flags; }
|
||||
inline void ClearFlag(FlagEnum Flag) { mValue &= ~Flag; }
|
||||
inline void ClearFlag(TFlags Flags) { mValue &= ~Flags; }
|
||||
};
|
||||
#define DECLARE_FLAGS(Enum, FlagTypeName) typedef TFlags<Enum> FlagTypeName;
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ public:
|
|||
inline void Insert(u32 Pos, CharType Chr)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (Size() <= Pos)
|
||||
if (Size() < Pos)
|
||||
throw std::out_of_range("Invalid position passed to TBasicString::Insert()");
|
||||
#endif
|
||||
mInternalString.insert(Pos, 1, Chr);
|
||||
|
@ -221,7 +221,7 @@ public:
|
|||
inline void Insert(u32 Pos, const CharType* pkStr)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (Size() <= Pos)
|
||||
if (Size() < Pos)
|
||||
throw std::out_of_range("Invalid position passed to TBasicString::Insert()");
|
||||
#endif
|
||||
mInternalString.insert(Pos, pkStr);
|
||||
|
@ -846,7 +846,10 @@ public:
|
|||
|
||||
static TBasicString<CharType> FromFloat(float Value, int MinDecimals = 1)
|
||||
{
|
||||
_TString Out = std::to_string(Value);
|
||||
std::basic_stringstream<CharType> sstream;
|
||||
sstream << Value;
|
||||
_TString Out = sstream.str();
|
||||
|
||||
int NumZeroes = Out.Size() - (Out.IndexOf(LITERAL(".")) + 1);
|
||||
|
||||
while (Out.Back() == CHAR_LITERAL('0') && NumZeroes > MinDecimals)
|
||||
|
|
|
@ -196,7 +196,10 @@ HEADERS += \
|
|||
GameProject/CResourceStore.h \
|
||||
GameProject/CVirtualDirectory.h \
|
||||
GameProject/CResourceEntry.h \
|
||||
GameProject/CResourceIterator.h
|
||||
GameProject/CResourceIterator.h \
|
||||
Resource/CDependencyGroup.h \
|
||||
Resource/Factory/CDependencyGroupLoader.h \
|
||||
GameProject/CDependencyTree.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
@ -287,4 +290,6 @@ SOURCES += \
|
|||
GameProject/CResourceStore.cpp \
|
||||
GameProject/CVirtualDirectory.cpp \
|
||||
GameProject/CResourceEntry.cpp \
|
||||
GameProject/CPackage.cpp
|
||||
GameProject/CPackage.cpp \
|
||||
Resource/Factory/CDependencyGroupLoader.cpp \
|
||||
GameProject/CDependencyTree.cpp
|
||||
|
|
|
@ -0,0 +1,320 @@
|
|||
#include "CDependencyTree.h"
|
||||
#include "Core/Resource/Script/CScriptLayer.h"
|
||||
#include "Core/Resource/Script/CScriptObject.h"
|
||||
|
||||
// ************ CResourceDependency ************
|
||||
EDependencyNodeType CResourceDependency::Type() const
|
||||
{
|
||||
return eDNT_ResourceDependency;
|
||||
}
|
||||
|
||||
void CResourceDependency::Read(IInputStream& rFile, EUIDLength IDLength)
|
||||
{
|
||||
mID = CUniqueID(rFile, IDLength);
|
||||
}
|
||||
|
||||
void CResourceDependency::Write(IOutputStream& rFile, EUIDLength IDLength) const
|
||||
{
|
||||
if (IDLength == e32Bit)
|
||||
rFile.WriteLong(mID.ToLong());
|
||||
else
|
||||
rFile.WriteLongLong(mID.ToLongLong());
|
||||
}
|
||||
|
||||
// ************ CAnimSetDependency ************
|
||||
EDependencyNodeType CAnimSetDependency::Type() const
|
||||
{
|
||||
return eDNT_AnimSet;
|
||||
}
|
||||
|
||||
void CAnimSetDependency::Read(IInputStream& rFile, EUIDLength IDLength)
|
||||
{
|
||||
CResourceDependency::Read(rFile, IDLength);
|
||||
mUsedChar = rFile.ReadLong();
|
||||
}
|
||||
|
||||
void CAnimSetDependency::Write(IOutputStream& rFile, EUIDLength IDLength) const
|
||||
{
|
||||
CResourceDependency::Write(rFile, IDLength);
|
||||
rFile.WriteLong(mUsedChar);
|
||||
}
|
||||
|
||||
// Static
|
||||
CAnimSetDependency* CAnimSetDependency::BuildDependency(TCharacterProperty *pProp)
|
||||
{
|
||||
ASSERT(pProp && pProp->Type() == eCharacterProperty && pProp->Instance()->Area()->Game() <= eEchoes);
|
||||
|
||||
CAnimationParameters Params = pProp->Get();
|
||||
if (!Params.ID().IsValid()) return nullptr;
|
||||
|
||||
CAnimSetDependency *pDepend = new CAnimSetDependency;
|
||||
pDepend->SetID(Params.ID());
|
||||
pDepend->SetUsedChar(Params.CharacterIndex());
|
||||
return pDepend;
|
||||
}
|
||||
|
||||
// ************ CDependencyTree ************
|
||||
CDependencyTree::~CDependencyTree()
|
||||
{
|
||||
for (u32 iRef = 0; iRef < mReferencedResources.size(); iRef++)
|
||||
delete mReferencedResources[iRef];
|
||||
}
|
||||
|
||||
EDependencyNodeType CDependencyTree::Type() const
|
||||
{
|
||||
return eDNT_Root;
|
||||
}
|
||||
|
||||
void CDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
|
||||
{
|
||||
mID = CUniqueID(rFile, IDLength);
|
||||
|
||||
u32 NumDepends = rFile.ReadLong();
|
||||
mReferencedResources.reserve(NumDepends);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumDepends; iDep++)
|
||||
{
|
||||
CResourceDependency *pDepend = new CResourceDependency;
|
||||
pDepend->Read(rFile, IDLength);
|
||||
mReferencedResources.push_back(pDepend);
|
||||
}
|
||||
}
|
||||
|
||||
void CDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
|
||||
{
|
||||
if (IDLength == e32Bit)
|
||||
rFile.WriteLong(mID.ToLong());
|
||||
else
|
||||
rFile.WriteLongLong(mID.ToLongLong());
|
||||
|
||||
rFile.WriteLong( mReferencedResources.size() );
|
||||
|
||||
for (u32 iDep = 0; iDep < mReferencedResources.size(); iDep++)
|
||||
mReferencedResources[iDep]->Write(rFile, IDLength);
|
||||
}
|
||||
|
||||
u32 CDependencyTree::NumDependencies() const
|
||||
{
|
||||
return mReferencedResources.size();
|
||||
}
|
||||
|
||||
bool CDependencyTree::HasDependency(const CUniqueID& rkID)
|
||||
{
|
||||
for (u32 iDep = 0; iDep < mReferencedResources.size(); iDep++)
|
||||
{
|
||||
if (mReferencedResources[iDep]->ID() == rkID)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CUniqueID CDependencyTree::DependencyByIndex(u32 Index) const
|
||||
{
|
||||
ASSERT(Index >= 0 && Index < mReferencedResources.size());
|
||||
return mReferencedResources[Index]->ID();
|
||||
}
|
||||
|
||||
void CDependencyTree::AddDependency(CResource *pRes)
|
||||
{
|
||||
if (!pRes || HasDependency(pRes->ResID())) return;
|
||||
CResourceDependency *pDepend = new CResourceDependency(pRes->ResID());
|
||||
mReferencedResources.push_back(pDepend);
|
||||
}
|
||||
|
||||
void CDependencyTree::AddDependency(const CUniqueID& rkID)
|
||||
{
|
||||
if (!rkID.IsValid() || HasDependency(rkID)) return;
|
||||
CResourceDependency *pDepend = new CResourceDependency(rkID);
|
||||
mReferencedResources.push_back(pDepend);
|
||||
}
|
||||
|
||||
// ************ CAnimSetDependencyTree ************
|
||||
EDependencyNodeType CAnimSetDependencyTree::Type() const
|
||||
{
|
||||
return eDNT_AnimSet;
|
||||
}
|
||||
|
||||
void CAnimSetDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
|
||||
{
|
||||
CDependencyTree::Read(rFile, IDLength);
|
||||
u32 NumChars = rFile.ReadLong();
|
||||
mCharacterOffsets.reserve(NumChars);
|
||||
|
||||
for (u32 iChar = 0; iChar < NumChars; iChar++)
|
||||
mCharacterOffsets.push_back( rFile.ReadLong() );
|
||||
}
|
||||
|
||||
void CAnimSetDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
|
||||
{
|
||||
CDependencyTree::Write(rFile, IDLength);
|
||||
rFile.WriteLong(mCharacterOffsets.size());
|
||||
|
||||
for (u32 iChar = 0; iChar < mCharacterOffsets.size(); iChar++)
|
||||
rFile.WriteLong( mCharacterOffsets[iChar] );
|
||||
}
|
||||
|
||||
// ************ CScriptInstanceDependencyTree ************
|
||||
CScriptInstanceDependencyTree::~CScriptInstanceDependencyTree()
|
||||
{
|
||||
for (u32 iDep = 0; iDep < mDependencies.size(); iDep++)
|
||||
delete mDependencies[iDep];
|
||||
}
|
||||
|
||||
EDependencyNodeType CScriptInstanceDependencyTree::Type() const
|
||||
{
|
||||
return eDNT_ScriptInstance;
|
||||
}
|
||||
|
||||
void CScriptInstanceDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
|
||||
{
|
||||
mObjectType = rFile.ReadLong();
|
||||
u32 NumDepends = rFile.ReadLong();
|
||||
mDependencies.reserve(NumDepends);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumDepends; iDep++)
|
||||
{
|
||||
CUniqueID ID(rFile, IDLength);
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(ID);
|
||||
|
||||
if (pEntry && pEntry->ResourceType() == eAnimSet && pEntry->Game() <= eEchoes)
|
||||
{
|
||||
CAnimSetDependency *pSet = new CAnimSetDependency();
|
||||
pSet->SetID(ID);
|
||||
pSet->SetUsedChar( rFile.ReadLong() );
|
||||
mDependencies.push_back(pSet);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
CResourceDependency *pRes = new CResourceDependency(ID);
|
||||
mDependencies.push_back(pRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptInstanceDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
|
||||
{
|
||||
rFile.WriteLong(mObjectType);
|
||||
rFile.WriteLong(mDependencies.size());
|
||||
|
||||
for (u32 iDep = 0; iDep < mDependencies.size(); iDep++)
|
||||
mDependencies[iDep]->Write(rFile, IDLength);
|
||||
}
|
||||
|
||||
bool CScriptInstanceDependencyTree::HasDependency(const CUniqueID& rkID)
|
||||
{
|
||||
if (!rkID.IsValid()) return false;
|
||||
|
||||
for (u32 iDep = 0; iDep < mDependencies.size(); iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = mDependencies[iDep];
|
||||
if (pDep->ID() == rkID) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Static
|
||||
CScriptInstanceDependencyTree* CScriptInstanceDependencyTree::BuildTree(CScriptObject *pInstance)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pTree = new CScriptInstanceDependencyTree();
|
||||
pTree->mObjectType = pInstance->ObjectTypeID();
|
||||
ParseStructDependencies(pTree, pInstance->Properties());
|
||||
return pTree;
|
||||
}
|
||||
|
||||
void CScriptInstanceDependencyTree::ParseStructDependencies(CScriptInstanceDependencyTree *pTree, CPropertyStruct *pStruct)
|
||||
{
|
||||
for (u32 iProp = 0; iProp < pStruct->Count(); iProp++)
|
||||
{
|
||||
IProperty *pProp = pStruct->PropertyByIndex(iProp);
|
||||
|
||||
if (pProp->Type() == eStructProperty || pProp->Type() == eArrayProperty)
|
||||
ParseStructDependencies(pTree, static_cast<CPropertyStruct*>(pProp));
|
||||
|
||||
else if (pProp->Type() == eFileProperty)
|
||||
{
|
||||
CUniqueID ID = static_cast<TFileProperty*>(pProp)->Get().ID();
|
||||
|
||||
if (ID.IsValid() && !pTree->HasDependency(ID))
|
||||
{
|
||||
CResourceDependency *pDep = new CResourceDependency(ID);
|
||||
pTree->mDependencies.push_back(pDep);
|
||||
}
|
||||
}
|
||||
|
||||
else if (pProp->Type() == eCharacterProperty)
|
||||
{
|
||||
TCharacterProperty *pChar = static_cast<TCharacterProperty*>(pProp);
|
||||
CUniqueID ID = pChar->Get().ID();
|
||||
|
||||
if (ID.IsValid() && !pTree->HasDependency(ID))
|
||||
pTree->mDependencies.push_back( CAnimSetDependency::BuildDependency(pChar) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ CAreaDependencyTree ************
|
||||
CAreaDependencyTree::~CAreaDependencyTree()
|
||||
{
|
||||
for (u32 iInst = 0; iInst < mScriptInstances.size(); iInst++)
|
||||
delete mScriptInstances[iInst];
|
||||
}
|
||||
|
||||
EDependencyNodeType CAreaDependencyTree::Type() const
|
||||
{
|
||||
return eDNT_Area;
|
||||
}
|
||||
|
||||
void CAreaDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
|
||||
{
|
||||
// Base dependency list contains non-script dependencies (world geometry textures + PATH/PTLA/EGMC)
|
||||
CDependencyTree::Read(rFile, IDLength);
|
||||
u32 NumScriptInstances = rFile.ReadLong();
|
||||
mScriptInstances.reserve(NumScriptInstances);
|
||||
|
||||
for (u32 iInst = 0; iInst < NumScriptInstances; iInst++)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pInst = new CScriptInstanceDependencyTree;
|
||||
pInst->Read(rFile, IDLength);
|
||||
mScriptInstances.push_back(pInst);
|
||||
}
|
||||
|
||||
u32 NumLayers = rFile.ReadLong();
|
||||
mLayerOffsets.reserve(NumLayers);
|
||||
|
||||
for (u32 iLyr = 0; iLyr < NumLayers; iLyr++)
|
||||
mLayerOffsets.push_back( rFile.ReadLong() );
|
||||
}
|
||||
|
||||
void CAreaDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
|
||||
{
|
||||
CDependencyTree::Write(rFile, IDLength);
|
||||
rFile.WriteLong(mScriptInstances.size());
|
||||
|
||||
for (u32 iInst = 0; iInst < mScriptInstances.size(); iInst++)
|
||||
mScriptInstances[iInst]->Write(rFile, IDLength);
|
||||
|
||||
rFile.WriteLong(mLayerOffsets.size());
|
||||
|
||||
for (u32 iLyr = 0; iLyr < mLayerOffsets.size(); iLyr++)
|
||||
rFile.WriteLong(mLayerOffsets[iLyr]);
|
||||
}
|
||||
|
||||
void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer)
|
||||
{
|
||||
if (!pLayer) return;
|
||||
mLayerOffsets.push_back(mScriptInstances.size());
|
||||
|
||||
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pTree = CScriptInstanceDependencyTree::BuildTree( pLayer->InstanceByIndex(iInst) );
|
||||
ASSERT(pTree != nullptr);
|
||||
|
||||
if (pTree->NumDependencies() > 0)
|
||||
mScriptInstances.push_back(pTree);
|
||||
else
|
||||
delete pTree;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
#ifndef CDEPENDENCYTREE
|
||||
#define CDEPENDENCYTREE
|
||||
|
||||
#include "CResourceEntry.h"
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <Common/AssertMacro.h>
|
||||
#include <Common/CUniqueID.h>
|
||||
|
||||
class CScriptLayer;
|
||||
class CScriptObject;
|
||||
class CPropertyStruct;
|
||||
class TCharacterProperty;
|
||||
|
||||
// Group of node classes forming a tree of cached resource dependencies.
|
||||
enum EDependencyNodeType
|
||||
{
|
||||
eDNT_Root,
|
||||
eDNT_AnimSet,
|
||||
eDNT_ScriptInstance,
|
||||
eDNT_Area,
|
||||
eDNT_ResourceDependency,
|
||||
eDNT_AnimSetDependency
|
||||
};
|
||||
|
||||
// Base class providing an interface for reading/writing to cache file and determining type.
|
||||
class IDependencyNode
|
||||
{
|
||||
public:
|
||||
virtual ~IDependencyNode() {}
|
||||
virtual EDependencyNodeType Type() const = 0;
|
||||
virtual void Read(IInputStream& rFile, EUIDLength IDLength) = 0;
|
||||
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const = 0;
|
||||
};
|
||||
|
||||
// Node representing a single resource dependency.
|
||||
class CResourceDependency : public IDependencyNode
|
||||
{
|
||||
CUniqueID mID;
|
||||
|
||||
public:
|
||||
CResourceDependency() {}
|
||||
CResourceDependency(const CUniqueID& rkID) : mID(rkID) {}
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
|
||||
|
||||
// Accessors
|
||||
inline CUniqueID ID() const { return mID; }
|
||||
inline void SetID(const CUniqueID& rkID) { mID = rkID; }
|
||||
};
|
||||
|
||||
// Node representing a single animset dependency contained in a script object. Indicates which character is being used.
|
||||
class CAnimSetDependency : public CResourceDependency
|
||||
{
|
||||
protected:
|
||||
u32 mUsedChar;
|
||||
|
||||
public:
|
||||
CAnimSetDependency() : CResourceDependency(), mUsedChar(-1) {}
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
|
||||
|
||||
// Accessors
|
||||
inline u32 UsedChar() const { return mUsedChar; }
|
||||
inline void SetUsedChar(u32 CharIdx) { mUsedChar = CharIdx; }
|
||||
|
||||
// Static
|
||||
static CAnimSetDependency* BuildDependency(TCharacterProperty *pProp);
|
||||
};
|
||||
|
||||
// Tree root node, representing a resource.
|
||||
class CDependencyTree : public IDependencyNode
|
||||
{
|
||||
protected:
|
||||
CUniqueID mID;
|
||||
std::vector<CResourceDependency*> mReferencedResources;
|
||||
|
||||
public:
|
||||
CDependencyTree(const CUniqueID& rkID) : mID(rkID) {}
|
||||
~CDependencyTree();
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
|
||||
|
||||
u32 NumDependencies() const;
|
||||
bool HasDependency(const CUniqueID& rkID);
|
||||
CUniqueID DependencyByIndex(u32 Index) const;
|
||||
void AddDependency(const CUniqueID& rkID);
|
||||
void AddDependency(CResource *pRes);
|
||||
|
||||
// Accessors
|
||||
inline void SetID(const CUniqueID& rkID) { mID = rkID; }
|
||||
inline CUniqueID ID() const { return mID; }
|
||||
|
||||
};
|
||||
|
||||
// Node representing an animset resource; allows for lookup of dependencies of a particular character in the set.
|
||||
class CAnimSetDependencyTree : public CDependencyTree
|
||||
{
|
||||
protected:
|
||||
std::vector<u32> mCharacterOffsets;
|
||||
|
||||
public:
|
||||
CAnimSetDependencyTree(const CUniqueID& rkID) : CDependencyTree(rkID) {}
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
|
||||
};
|
||||
|
||||
// Node representing a script object. Indicates the type of object.
|
||||
class CScriptInstanceDependencyTree : public IDependencyNode
|
||||
{
|
||||
protected:
|
||||
u32 mObjectType;
|
||||
std::vector<CResourceDependency*> mDependencies;
|
||||
|
||||
public:
|
||||
~CScriptInstanceDependencyTree();
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
|
||||
bool HasDependency(const CUniqueID& rkID);
|
||||
|
||||
// Accessors
|
||||
u32 NumDependencies() const { return mDependencies.size(); }
|
||||
|
||||
// Static
|
||||
static CScriptInstanceDependencyTree* BuildTree(CScriptObject *pInstance);
|
||||
static void ParseStructDependencies(CScriptInstanceDependencyTree *pTree, CPropertyStruct *pStruct);
|
||||
};
|
||||
|
||||
// Node representing an area. Tracks dependencies on a per-instance basis and can separate dependencies of different script layers.
|
||||
class CAreaDependencyTree : public CDependencyTree
|
||||
{
|
||||
protected:
|
||||
std::vector<CScriptInstanceDependencyTree*> mScriptInstances;
|
||||
std::vector<u32> mLayerOffsets;
|
||||
|
||||
public:
|
||||
CAreaDependencyTree(const CUniqueID& rkID) : CDependencyTree(rkID) {}
|
||||
~CAreaDependencyTree();
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
|
||||
|
||||
void AddScriptLayer(CScriptLayer *pLayer);
|
||||
};
|
||||
|
||||
#endif // CDEPENDENCYTREE
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "CGameExporter.h"
|
||||
#include "Core/GameProject/CResourceIterator.h"
|
||||
#include "Core/GameProject/CResourceStore.h"
|
||||
#include "Core/Resource/CWorld.h"
|
||||
#include "Core/Resource/Script/CMasterTemplate.h"
|
||||
|
@ -14,6 +15,7 @@
|
|||
#define SAVE_PACKAGE_DEFINITIONS 1
|
||||
#define EXPORT_WORLDS 1
|
||||
#define EXPORT_COOKED 1
|
||||
#define EXPORT_CACHE 1
|
||||
|
||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
||||
: mStore(this)
|
||||
|
@ -476,7 +478,6 @@ void CGameExporter::ExportWorlds()
|
|||
|
||||
void CGameExporter::ExportCookedResources()
|
||||
{
|
||||
#if EXPORT_COOKED
|
||||
{
|
||||
SCOPED_TIMER(ExportCookedResources);
|
||||
FileUtil::CreateDirectory(mCookedDir);
|
||||
|
@ -487,7 +488,6 @@ void CGameExporter::ExportCookedResources()
|
|||
ExportResource(rRes);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
{
|
||||
SCOPED_TIMER(SaveResourceDatabase);
|
||||
#if EXPORT_COOKED
|
||||
|
@ -495,6 +495,20 @@ void CGameExporter::ExportCookedResources()
|
|||
#endif
|
||||
mpProject->Save();
|
||||
}
|
||||
#if EXPORT_CACHE
|
||||
{
|
||||
SCOPED_TIMER(SaveCacheData);
|
||||
|
||||
for (CResourceIterator It(&mStore); It; ++It)
|
||||
{
|
||||
if (!It->IsTransient())
|
||||
{
|
||||
It->UpdateDependencies();
|
||||
It->SaveCacheData();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CGameExporter::ExportResource(SResourceInstance& rRes)
|
||||
|
@ -520,6 +534,7 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
|||
// Register resource and write to file
|
||||
CResourceEntry *pEntry = mStore.RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), OutDir, OutName);
|
||||
|
||||
#if EXPORT_COOKED
|
||||
// Cooked (todo: save raw)
|
||||
TWideString OutPath = pEntry->CookedAssetPath();
|
||||
FileUtil::CreateDirectory(OutPath.GetFileDirectory());
|
||||
|
@ -530,5 +545,8 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
|||
|
||||
rRes.Exported = true;
|
||||
ASSERT(pEntry->HasCookedVersion());
|
||||
#else
|
||||
(void) pEntry; // Prevent "unused local variable" compiler warning
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ bool CGameProject::Load(const TWideString& rkPath)
|
|||
|
||||
// All loaded!
|
||||
mProjectRoot = rkPath.GetFileDirectory();
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ public:
|
|||
, mProjectName("Unnamed Project")
|
||||
, mProjectRoot(rkProjRootDir)
|
||||
, mResourceDBPath(L"ResourceDB.rdb")
|
||||
{}
|
||||
{
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
}
|
||||
|
||||
~CGameProject();
|
||||
|
||||
|
@ -51,6 +53,7 @@ public:
|
|||
inline TWideString ProjectRoot() const { return mProjectRoot; }
|
||||
inline TWideString ResourceDBPath(bool Relative) const { return Relative ? mResourceDBPath : mProjectRoot + mResourceDBPath; }
|
||||
inline TWideString DiscDir(bool Relative) const { return Relative ? L"Disc\\" : mProjectRoot + L"Disc\\"; }
|
||||
inline TWideString CacheDir(bool Relative) const { return Relative ? L"Cache\\" : mProjectRoot + L"Cache\\"; }
|
||||
inline TWideString ContentDir(bool Relative) const { return Relative ? L"Content\\" : mProjectRoot + L"Content\\"; }
|
||||
inline TWideString CookedDir(bool Relative) const { return Relative ? L"Cooked\\" : mProjectRoot + L"Cooked\\"; }
|
||||
inline TWideString PackagesDir(bool Relative) const { return Relative ? L"Packages\\" : mProjectRoot + L"Packages\\"; }
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "CGameProject.h"
|
||||
#include "CResourceStore.h"
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <Common/FileUtil.h>
|
||||
#include <Common/TString.h>
|
||||
|
||||
|
@ -11,6 +12,7 @@
|
|||
#include "Core/Resource/Factory/CAnimSetLoader.h"
|
||||
#include "Core/Resource/Factory/CAreaLoader.h"
|
||||
#include "Core/Resource/Factory/CCollisionLoader.h"
|
||||
#include "Core/Resource/Factory/CDependencyGroupLoader.h"
|
||||
#include "Core/Resource/Factory/CFontLoader.h"
|
||||
#include "Core/Resource/Factory/CMaterialLoader.h"
|
||||
#include "Core/Resource/Factory/CModelLoader.h"
|
||||
|
@ -26,19 +28,20 @@
|
|||
CResourceEntry::CResourceEntry(CResourceStore *pStore, const CUniqueID& rkID,
|
||||
const TWideString& rkDir, const TWideString& rkFilename,
|
||||
EResType Type, bool Transient /*= false*/)
|
||||
: mpStore(pStore)
|
||||
, mpResource(nullptr)
|
||||
: mpResource(nullptr)
|
||||
, mpStore(pStore)
|
||||
, mpDependencies(nullptr)
|
||||
, mID(rkID)
|
||||
, mName(rkFilename)
|
||||
, mType(Type)
|
||||
, mNeedsRecook(false)
|
||||
, mTransient(Transient)
|
||||
, mCachedSize(-1)
|
||||
, mCachedUppercaseName(rkFilename.ToUpper())
|
||||
{
|
||||
if (Transient) mFlags |= eREF_Transient;
|
||||
|
||||
mpDirectory = mpStore->GetVirtualDirectory(rkDir, Transient, true);
|
||||
if (mpDirectory) mpDirectory->AddChild(L"", this);
|
||||
mGame = ((mTransient || !mpStore->ActiveProject()) ? eUnknownVersion : mpStore->ActiveProject()->Game());
|
||||
mGame = ((Transient || !mpStore->ActiveProject()) ? eUnknownVersion : mpStore->ActiveProject()->Game());
|
||||
}
|
||||
|
||||
CResourceEntry::~CResourceEntry()
|
||||
|
@ -46,6 +49,104 @@ CResourceEntry::~CResourceEntry()
|
|||
if (mpResource) delete mpResource;
|
||||
}
|
||||
|
||||
bool CResourceEntry::LoadCacheData()
|
||||
{
|
||||
ASSERT(!IsTransient());
|
||||
|
||||
TWideString Path = CacheDataPath(false);
|
||||
CFileInStream File(Path.ToUTF8().ToStdString(), IOUtil::eLittleEndian);
|
||||
|
||||
if (!File.IsValid())
|
||||
{
|
||||
Log::Error("Unable to load cache data " + Path.ToUTF8() + "; couldn't open file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Header
|
||||
TString Magic = File.ReadString(4);
|
||||
ASSERT(Magic == "CACH");
|
||||
File.Seek(0x4, SEEK_CUR); // Skip Version
|
||||
mFlags = File.ReadLong() & eREF_SavedFlags;
|
||||
|
||||
// Dependency Tree
|
||||
u32 DepsTreeSize = File.ReadLong();
|
||||
|
||||
if (mpDependencies)
|
||||
{
|
||||
delete mpDependencies;
|
||||
mpDependencies = nullptr;
|
||||
}
|
||||
|
||||
if (DepsTreeSize > 0)
|
||||
{
|
||||
mpDependencies = new CDependencyTree(mID);
|
||||
mpDependencies->Read(File, Game() <= eEchoes ? e32Bit : e64Bit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CResourceEntry::SaveCacheData()
|
||||
{
|
||||
ASSERT(!IsTransient());
|
||||
|
||||
TWideString Path = CacheDataPath(false);
|
||||
TWideString Dir = Path.GetFileDirectory();
|
||||
FileUtil::CreateDirectory(Dir);
|
||||
CFileOutStream File(Path.ToUTF8().ToStdString(), IOUtil::eLittleEndian);
|
||||
|
||||
if (!File.IsValid())
|
||||
{
|
||||
Log::Error("Unable to save cache data " + TString(Path.GetFileName()) + "; couldn't open file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Header
|
||||
File.WriteString("CACH", 4);
|
||||
File.WriteLong(0); // Reserved Space (Version)
|
||||
File.WriteLong(mFlags & eREF_SavedFlags);
|
||||
|
||||
// Dependency Tree
|
||||
if (!mpDependencies) UpdateDependencies();
|
||||
|
||||
u32 DepsSizeOffset = File.Tell();
|
||||
File.WriteLong(0);
|
||||
|
||||
u32 DepsStart = File.Tell();
|
||||
if (mpDependencies) mpDependencies->Write(File, Game() <= eEchoes ? e32Bit : e64Bit);
|
||||
u32 DepsSize = File.Tell() - DepsStart;
|
||||
File.Seek(DepsSizeOffset, SEEK_SET);
|
||||
File.WriteLong(DepsSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CResourceEntry::UpdateDependencies()
|
||||
{
|
||||
if (mpDependencies)
|
||||
{
|
||||
delete mpDependencies;
|
||||
mpDependencies = nullptr;
|
||||
}
|
||||
|
||||
if (!mpResource)
|
||||
Load();
|
||||
|
||||
if (!mpResource)
|
||||
{
|
||||
Log::Error("Unable to update cached dependencies; failed to load resource");
|
||||
return;
|
||||
}
|
||||
|
||||
mpDependencies = mpResource->BuildDependencyTree();
|
||||
gpResourceStore->DestroyUnreferencedResources();
|
||||
}
|
||||
|
||||
TWideString CResourceEntry::CacheDataPath(bool Relative) const
|
||||
{
|
||||
return mpStore->ActiveProject()->CacheDir(Relative) + mID.ToString().ToUTF16() + L".rcd";
|
||||
}
|
||||
|
||||
bool CResourceEntry::HasRawVersion() const
|
||||
{
|
||||
return FileUtil::Exists(RawAssetPath());
|
||||
|
@ -61,7 +162,7 @@ TString CResourceEntry::RawAssetPath(bool Relative) const
|
|||
TWideString Ext = GetResourceRawExtension(mType, mGame).ToUTF16();
|
||||
TWideString Path = mpDirectory ? mpDirectory->FullPath() : L"";
|
||||
TWideString Name = mName + L"." + Ext;
|
||||
return ((mTransient || Relative) ? Path + Name : mpStore->ActiveProject()->ContentDir(false) + Path + Name);
|
||||
return ((IsTransient() || Relative) ? Path + Name : mpStore->ActiveProject()->ContentDir(false) + Path + Name);
|
||||
}
|
||||
|
||||
TString CResourceEntry::CookedAssetPath(bool Relative) const
|
||||
|
@ -69,7 +170,7 @@ TString CResourceEntry::CookedAssetPath(bool Relative) const
|
|||
TWideString Ext = GetResourceCookedExtension(mType, mGame).ToUTF16();
|
||||
TWideString Path = mpDirectory ? mpDirectory->FullPath() : L"";
|
||||
TWideString Name = mName + L"." + Ext;
|
||||
return ((mTransient || Relative) ? Path + Name : mpStore->ActiveProject()->CookedDir(false) + Path + Name);
|
||||
return ((IsTransient() || Relative) ? Path + Name : mpStore->ActiveProject()->CookedDir(false) + Path + Name);
|
||||
}
|
||||
|
||||
bool CResourceEntry::IsInDirectory(CVirtualDirectory *pDir) const
|
||||
|
@ -101,11 +202,11 @@ u64 CResourceEntry::Size() const
|
|||
bool CResourceEntry::NeedsRecook() const
|
||||
{
|
||||
// Assets that do not have a raw version can't be recooked since they will always just be saved cooked to begin with.
|
||||
// We will recook any asset where the raw version has been updated but not recooked yet. mNeedsRecook can also be
|
||||
// We will recook any asset where the raw version has been updated but not recooked yet. eREF_NeedsRecook can also be
|
||||
// toggled to arbitrarily flag any asset for recook.
|
||||
if (!HasRawVersion()) return false;
|
||||
if (!HasCookedVersion()) return true;
|
||||
if (mNeedsRecook) return true;
|
||||
if (mFlags.HasFlag(eREF_NeedsRecook)) return true;
|
||||
return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath()));
|
||||
}
|
||||
|
||||
|
@ -152,6 +253,7 @@ CResource* CResourceEntry::Load(IInputStream& rInput)
|
|||
case eAnimation: mpResource = CAnimationLoader::LoadANIM(rInput, this); break;
|
||||
case eAnimSet: mpResource = CAnimSetLoader::LoadANCSOrCHAR(rInput, this); break;
|
||||
case eArea: mpResource = CAreaLoader::LoadMREA(rInput, this); break;
|
||||
case eDependencyGroup: mpResource = CDependencyGroupLoader::LoadDGRP(rInput, this);break;
|
||||
case eDynamicCollision: mpResource = CCollisionLoader::LoadDCLN(rInput, this); break;
|
||||
case eFont: mpResource = CFontLoader::LoadFONT(rInput, this); break;
|
||||
case eModel: mpResource = CModelLoader::LoadCMDL(rInput, this); break;
|
||||
|
@ -185,7 +287,7 @@ void CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
|
|||
|
||||
// Set new directory and name
|
||||
bool HasDirectory = mpDirectory != nullptr;
|
||||
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, mTransient, true);
|
||||
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, IsTransient(), true);
|
||||
|
||||
if (pNewDir != mpDirectory)
|
||||
{
|
||||
|
@ -216,9 +318,9 @@ void CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
|
|||
|
||||
void CResourceEntry::AddToProject(const TWideString& rkDir, const TWideString& rkName)
|
||||
{
|
||||
if (mTransient)
|
||||
if (mFlags.HasFlag(eREF_Transient))
|
||||
{
|
||||
mTransient = false;
|
||||
mFlags.ClearFlag(eREF_Transient);
|
||||
Move(rkDir, rkName);
|
||||
}
|
||||
|
||||
|
@ -230,10 +332,10 @@ void CResourceEntry::AddToProject(const TWideString& rkDir, const TWideString& r
|
|||
|
||||
void CResourceEntry::RemoveFromProject()
|
||||
{
|
||||
if (!mTransient)
|
||||
if (!mFlags.HasFlag(eREF_Transient))
|
||||
{
|
||||
TString Dir = CookedAssetPath().GetFileDirectory();
|
||||
mTransient = true;
|
||||
mFlags.SetFlag(eREF_Transient);
|
||||
Move(Dir, mName);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,31 +4,49 @@
|
|||
#include "CVirtualDirectory.h"
|
||||
#include "Core/Resource/EResType.h"
|
||||
#include <Common/CUniqueID.h>
|
||||
#include <Common/Flags.h>
|
||||
#include <Common/types.h>
|
||||
|
||||
class CResource;
|
||||
class CResourceStore;
|
||||
class CDependencyTree;
|
||||
|
||||
enum EResEntryFlag
|
||||
{
|
||||
eREF_NeedsRecook = 0x1,
|
||||
eREF_Transient = 0x2,
|
||||
eREF_HasThumbnail = 0x4,
|
||||
// Flags that save to the cache file
|
||||
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail
|
||||
};
|
||||
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
||||
|
||||
class CResourceEntry
|
||||
{
|
||||
CResourceStore *mpStore;
|
||||
CResource *mpResource;
|
||||
CResourceStore *mpStore;
|
||||
CDependencyTree *mpDependencies;
|
||||
CUniqueID mID;
|
||||
EResType mType;
|
||||
EGame mGame;
|
||||
CVirtualDirectory *mpDirectory;
|
||||
TWideString mName;
|
||||
bool mNeedsRecook;
|
||||
bool mTransient;
|
||||
FResEntryFlags mFlags;
|
||||
|
||||
mutable u64 mCachedSize;
|
||||
mutable TWideString mCachedUppercaseName; // This is used to speed up case-insensitive sorting.
|
||||
mutable TWideString mCachedUppercaseName; // This is used to speed up case-insensitive sorting and filtering.
|
||||
|
||||
public:
|
||||
CResourceEntry(CResourceStore *pStore, const CUniqueID& rkID,
|
||||
const TWideString& rkDir, const TWideString& rkFilename,
|
||||
EResType Type, bool Transient = false);
|
||||
~CResourceEntry();
|
||||
|
||||
bool LoadCacheData();
|
||||
bool SaveCacheData();
|
||||
void UpdateDependencies();
|
||||
TWideString CacheDataPath(bool Relative = false) const;
|
||||
|
||||
bool HasRawVersion() const;
|
||||
bool HasCookedVersion() const;
|
||||
TString RawAssetPath(bool Relative = false) const;
|
||||
|
@ -45,17 +63,18 @@ public:
|
|||
void RemoveFromProject();
|
||||
|
||||
// Accessors
|
||||
void SetDirty() { mNeedsRecook = true; }
|
||||
void SetDirty() { mFlags.SetFlag(eREF_NeedsRecook); }
|
||||
|
||||
inline bool IsLoaded() const { return mpResource != nullptr; }
|
||||
inline CResource* Resource() const { return mpResource; }
|
||||
inline CUniqueID ID() const { return mID; }
|
||||
inline EGame Game() const { return mGame; }
|
||||
inline CVirtualDirectory* Directory() const { return mpDirectory; }
|
||||
inline TWideString Name() const { return mName; }
|
||||
inline TWideString UppercaseName() const { return mCachedUppercaseName; }
|
||||
inline EResType ResourceType() const { return mType; }
|
||||
inline bool IsTransient() const { return mTransient; }
|
||||
inline bool IsLoaded() const { return mpResource != nullptr; }
|
||||
inline CResource* Resource() const { return mpResource; }
|
||||
inline CDependencyTree* Dependencies() const { return mpDependencies; }
|
||||
inline CUniqueID ID() const { return mID; }
|
||||
inline EGame Game() const { return mGame; }
|
||||
inline CVirtualDirectory* Directory() const { return mpDirectory; }
|
||||
inline TWideString Name() const { return mName; }
|
||||
inline const TWideString& UppercaseName() const { return mCachedUppercaseName; }
|
||||
inline EResType ResourceType() const { return mType; }
|
||||
inline bool IsTransient() const { return mFlags.HasFlag(eREF_Transient); }
|
||||
|
||||
protected:
|
||||
CResource* InternalLoad(IInputStream& rInput);
|
||||
|
|
|
@ -11,7 +11,7 @@ class CResourceIterator
|
|||
CResourceEntry *mpCurEntry;
|
||||
|
||||
public:
|
||||
CResourceIterator(CResourceStore *pStore)
|
||||
CResourceIterator(CResourceStore *pStore = gpResourceStore)
|
||||
: mpStore(pStore)
|
||||
, mpCurEntry(nullptr)
|
||||
{
|
||||
|
|
|
@ -69,6 +69,14 @@ void CResourceStore::LoadResourceDatabase(const TString& rkPath)
|
|||
pRes = pRes->NextSiblingElement("Resource");
|
||||
}
|
||||
}
|
||||
|
||||
// All resources registered - load cache data
|
||||
for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++)
|
||||
{
|
||||
CResourceEntry *pEntry = It->second;
|
||||
if (!pEntry->IsTransient())
|
||||
pEntry->LoadCacheData();
|
||||
}
|
||||
}
|
||||
|
||||
void CResourceStore::SaveResourceDatabase(const TString& rkPath) const
|
||||
|
@ -108,10 +116,6 @@ void CResourceStore::SaveResourceDatabase(const TString& rkPath) const
|
|||
XMLElement *pName = Doc.NewElement("FileName");
|
||||
pName->SetText(*pEntry->Name().ToUTF8());
|
||||
pRes->LinkEndChild(pName);
|
||||
|
||||
XMLElement *pRecook = Doc.NewElement("NeedsRecook");
|
||||
pRecook->SetText(pEntry->NeedsRecook() ? "true" : "false");
|
||||
pRes->LinkEndChild(pRecook);
|
||||
}
|
||||
|
||||
Doc.SaveFile(*rkPath);
|
||||
|
@ -231,10 +235,7 @@ CResourceEntry* CResourceStore::RegisterTransientResource(EResType Type, const C
|
|||
{
|
||||
CResourceEntry *pEntry = FindEntry(rkID);
|
||||
|
||||
if (pEntry)
|
||||
Log::Error("Attempted to register transient resource that already exists: " + rkID.ToString() + " / Dir: " + rkDir.ToUTF8() + " / Name: " + rkFileName.ToUTF8());
|
||||
|
||||
else
|
||||
if (!pEntry)
|
||||
{
|
||||
pEntry = new CResourceEntry(this, rkID, rkDir, rkFileName, Type, true);
|
||||
mResourceEntries[rkID] = pEntry;
|
||||
|
|
|
@ -65,6 +65,8 @@ public:
|
|||
// Accessors
|
||||
inline CGameProject* ActiveProject() const { return mpProj; }
|
||||
inline CVirtualDirectory* RootDirectory() const { return mpProjectRoot; }
|
||||
inline u32 NumTotalResources() const { return mResourceEntries.size(); }
|
||||
inline u32 NumLoadedResources() const { return mLoadedResources.size(); }
|
||||
};
|
||||
|
||||
extern CResourceStore *gpResourceStore;
|
||||
|
|
|
@ -10,7 +10,7 @@ CGameArea::CGameArea(CResourceEntry *pEntry /*= 0*/)
|
|||
, mTerrainMerged(false)
|
||||
, mOriginalWorldMeshCount(0)
|
||||
, mUsesCompression(false)
|
||||
, mMaterialSet(nullptr)
|
||||
, mpMaterialSet(nullptr)
|
||||
, mpGeneratorLayer(nullptr)
|
||||
, mpCollision(nullptr)
|
||||
{
|
||||
|
@ -31,6 +31,32 @@ CGameArea::~CGameArea()
|
|||
delete mLightLayers[iLyr][iLight];
|
||||
}
|
||||
|
||||
CDependencyTree* CGameArea::BuildDependencyTree() const
|
||||
{
|
||||
// Base dependencies
|
||||
CAreaDependencyTree *pTree = new CAreaDependencyTree(ResID());
|
||||
|
||||
for (u32 iMat = 0; iMat < mpMaterialSet->NumMaterials(); iMat++)
|
||||
{
|
||||
CMaterial *pMat = mpMaterialSet->MaterialByIndex(iMat);
|
||||
pTree->AddDependency(pMat->IndTexture());
|
||||
|
||||
for (u32 iPass = 0; iPass < pMat->PassCount(); iPass++)
|
||||
pTree->AddDependency(pMat->Pass(iPass)->Texture());
|
||||
}
|
||||
|
||||
pTree->AddDependency(mpPoiToWorldMap);
|
||||
Log::Warning("CGameArea::FindDependencies not handling PATH/PTLA");
|
||||
|
||||
// Layer dependencies
|
||||
for (u32 iLayer = 0; iLayer < mScriptLayers.size(); iLayer++)
|
||||
pTree->AddScriptLayer(mScriptLayers[iLayer]);
|
||||
|
||||
pTree->AddScriptLayer(mpGeneratorLayer);
|
||||
|
||||
return pTree;
|
||||
}
|
||||
|
||||
void CGameArea::AddWorldModel(CModel *pModel)
|
||||
{
|
||||
mWorldModels.push_back(pModel);
|
||||
|
@ -52,7 +78,7 @@ void CGameArea::MergeTerrain()
|
|||
for (u32 iSurf = 0; iSurf < SubmeshCount; iSurf++)
|
||||
{
|
||||
SSurface *pSurf = pMdl->GetSurface(iSurf);
|
||||
CMaterial *pMat = mMaterialSet->MaterialByIndex(pSurf->MaterialID);
|
||||
CMaterial *pMat = mpMaterialSet->MaterialByIndex(pSurf->MaterialID);
|
||||
|
||||
bool NewMat = true;
|
||||
for (std::vector<CStaticModel*>::iterator it = mStaticWorldModels.begin(); it != mStaticWorldModels.end(); it++)
|
||||
|
@ -93,7 +119,7 @@ void CGameArea::ClearTerrain()
|
|||
delete mStaticWorldModels[iStatic];
|
||||
mStaticWorldModels.clear();
|
||||
|
||||
if (mMaterialSet) delete mMaterialSet;
|
||||
if (mpMaterialSet) delete mpMaterialSet;
|
||||
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
|
|
|
@ -45,7 +45,7 @@ class CGameArea : public CResource
|
|||
std::vector<SSectionNumber> mSectionNumbers;
|
||||
|
||||
// Geometry
|
||||
CMaterialSet *mMaterialSet;
|
||||
CMaterialSet *mpMaterialSet;
|
||||
std::vector<CModel*> mWorldModels; // TerrainModels is the original version of each model; this is currently mainly used in the POI map editor
|
||||
std::vector<CStaticModel*> mStaticWorldModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
|
||||
// Script
|
||||
|
@ -62,6 +62,7 @@ class CGameArea : public CResource
|
|||
public:
|
||||
CGameArea(CResourceEntry *pEntry = 0);
|
||||
~CGameArea();
|
||||
CDependencyTree* BuildDependencyTree() const;
|
||||
|
||||
void AddWorldModel(CModel *pModel);
|
||||
void MergeTerrain();
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
// Accessors
|
||||
inline EGame Version() const { return mGame; }
|
||||
inline CUniqueID ID() const { return mCharacter.ID(); }
|
||||
inline CAnimSet* AnimSet() const { return (CAnimSet*) mCharacter.Load(); }
|
||||
inline u32 CharacterIndex() const { return mCharIndex; }
|
||||
inline u32 AnimIndex() const { return mAnimIndex; }
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef CDEPENDENCYGROUP
|
||||
#define CDEPENDENCYGROUP
|
||||
|
||||
#include "CResource.h"
|
||||
|
||||
class CDependencyGroup : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eDependencyGroup)
|
||||
std::set<CUniqueID> mDependencies;
|
||||
|
||||
public:
|
||||
CDependencyGroup(CResourceEntry *pEntry = 0) : CResource(pEntry) {}
|
||||
inline void AddDependency(const CUniqueID& rkID) { mDependencies.insert(rkID); }
|
||||
inline void AddDependency(CResource *pRes) { if (pRes) mDependencies.insert(pRes->ResID()); }
|
||||
inline void RemoveDependency(const CUniqueID& rkID) { mDependencies.erase(rkID); }
|
||||
inline void Clear() { mDependencies.clear(); }
|
||||
inline bool HasDependency(const CUniqueID& rkID) const { return mDependencies.find(rkID) != mDependencies.end(); }
|
||||
inline u32 NumDependencies() const { return mDependencies.size(); }
|
||||
inline CUniqueID DependencyByIndex(u32 Index) const { return *std::next(mDependencies.begin(), Index); }
|
||||
|
||||
CDependencyTree* BuildDependencyTree() const
|
||||
{
|
||||
CDependencyTree *pTree = new CDependencyTree(ResID());
|
||||
|
||||
for (auto DepIt = mDependencies.begin(); DepIt != mDependencies.end(); DepIt++)
|
||||
pTree->AddDependency(*DepIt);
|
||||
|
||||
return pTree;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CDEPENDENCYGROUP
|
||||
|
|
@ -22,6 +22,13 @@ inline float PtsToFloat(s32 Pt)
|
|||
return 0.00208333f * Pt;
|
||||
}
|
||||
|
||||
CDependencyTree* CFont::BuildDependencyTree() const
|
||||
{
|
||||
CDependencyTree *pOut = new CDependencyTree(ResID());
|
||||
pOut->AddDependency(mpFontTexture);
|
||||
return pOut;
|
||||
}
|
||||
|
||||
CVector2f CFont::RenderString(const TString& rkString, CRenderer* /*pRenderer*/, float /*AspectRatio*/,
|
||||
CVector2f /*Position*/, CColor FillColor, CColor StrokeColor, u32 FontSize)
|
||||
{
|
||||
|
|
|
@ -60,6 +60,7 @@ class CFont : public CResource
|
|||
public:
|
||||
CFont(CResourceEntry *pEntry = 0);
|
||||
~CFont();
|
||||
CDependencyTree* BuildDependencyTree() const;
|
||||
CVector2f RenderString(const TString& rkString, CRenderer *pRenderer, float AspectRatio,
|
||||
CVector2f Position = CVector2f(0,0),
|
||||
CColor FillColor = CColor::skWhite, CColor StrokeColor = CColor::skBlack,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define CRESOURCE_H
|
||||
|
||||
#include "EResType.h"
|
||||
#include "Core/GameProject/CDependencyTree.h"
|
||||
#include "Core/GameProject/CResourceEntry.h"
|
||||
#include "Core/GameProject/CResourceStore.h"
|
||||
#include <Common/CFourCC.h>
|
||||
|
@ -39,7 +40,8 @@ public:
|
|||
}
|
||||
|
||||
virtual ~CResource() {}
|
||||
|
||||
virtual CDependencyTree* BuildDependencyTree() const { return new CDependencyTree(ResID()); }
|
||||
|
||||
inline CResourceEntry* Entry() const { return mpEntry; }
|
||||
inline TString Source() const { return mpEntry ? mpEntry->CookedAssetPath(true).GetFileName() : ""; }
|
||||
inline TString FullSource() const { return mpEntry ? mpEntry->CookedAssetPath(true) : ""; }
|
||||
|
|
|
@ -40,6 +40,17 @@ public:
|
|||
, mCategory(eNone)
|
||||
{}
|
||||
|
||||
CDependencyTree* BuildDependencyTree() const
|
||||
{
|
||||
if (Game() >= eEchoesDemo)
|
||||
Log::Warning("CScan::BuildDependencyTree not handling Echoes/Corruption dependencies");
|
||||
|
||||
CDependencyTree *pTree = new CDependencyTree(ResID());
|
||||
pTree->AddDependency(mpFrame);
|
||||
pTree->AddDependency(mpStringTable);
|
||||
return pTree;
|
||||
}
|
||||
|
||||
EGame Version() const { return mVersion; }
|
||||
CStringTable* ScanText() const { return mpStringTable; }
|
||||
bool IsImportant() const { return mIsImportant; }
|
||||
|
|
|
@ -17,6 +17,28 @@ CWorld::~CWorld()
|
|||
{
|
||||
}
|
||||
|
||||
CDependencyTree* CWorld::BuildDependencyTree() const
|
||||
{
|
||||
CDependencyTree *pTree = new CDependencyTree(ResID());
|
||||
|
||||
for (u32 iArea = 0; iArea < mAreas.size(); iArea++)
|
||||
{
|
||||
pTree->AddDependency(mAreas[iArea].FileID);
|
||||
pTree->AddDependency(mAreas[iArea].pAreaName);
|
||||
}
|
||||
|
||||
pTree->AddDependency(mpWorldName);
|
||||
pTree->AddDependency(mpDarkWorldName);
|
||||
pTree->AddDependency(mpSaveWorld);
|
||||
pTree->AddDependency(mpDefaultSkybox);
|
||||
pTree->AddDependency(mpMapWorld);
|
||||
|
||||
if (Game() <= ePrime)
|
||||
Log::Warning("CWorld::BuildDependencyTree not handling audio groups");
|
||||
|
||||
return pTree;
|
||||
}
|
||||
|
||||
void CWorld::SetAreaLayerInfo(CGameArea *pArea)
|
||||
{
|
||||
// The AreaIndex parameter is a placeholder until an improved world loader is implemented.
|
||||
|
|
|
@ -84,6 +84,7 @@ public:
|
|||
CWorld(CResourceEntry *pEntry = 0);
|
||||
~CWorld();
|
||||
|
||||
CDependencyTree* BuildDependencyTree() const;
|
||||
void SetAreaLayerInfo(CGameArea *pArea);
|
||||
|
||||
// Accessors
|
||||
|
|
|
@ -73,7 +73,7 @@ void CAreaLoader::ReadGeometryPrime()
|
|||
mpSectionMgr->ToSection(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mpArea->mpMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mpSectionMgr->ToNextSection();
|
||||
|
||||
// Geometry
|
||||
|
@ -81,7 +81,7 @@ void CAreaLoader::ReadGeometryPrime()
|
|||
|
||||
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
|
||||
{
|
||||
CModel *pModel = CModelLoader::LoadWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mMaterialSet, mVersion);
|
||||
CModel *pModel = CModelLoader::LoadWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mpMaterialSet, mVersion);
|
||||
FileModels.push_back(pModel);
|
||||
|
||||
if (mVersion <= ePrime)
|
||||
|
@ -394,7 +394,7 @@ void CAreaLoader::ReadGeometryCorruption()
|
|||
mpSectionMgr->ToSection(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mpArea->mpMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mpSectionMgr->ToNextSection();
|
||||
|
||||
// Geometry
|
||||
|
@ -404,7 +404,7 @@ void CAreaLoader::ReadGeometryCorruption()
|
|||
|
||||
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
|
||||
{
|
||||
CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
|
||||
CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mpMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
|
||||
FileModels.push_back(pWorldModel);
|
||||
|
||||
CurWOBJSection += 4;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#include "CDependencyGroupLoader.h"
|
||||
#include <Common/AssertMacro.h>
|
||||
|
||||
EGame CDependencyGroupLoader::VersionTest(IInputStream& rDGRP, u32 DepCount)
|
||||
{
|
||||
// Only difference between versions is asset ID length. Just check for EOF with 32-bit ID length.
|
||||
u32 Start = rDGRP.Tell();
|
||||
rDGRP.Seek(DepCount * 4, SEEK_CUR);
|
||||
u32 Remaining = rDGRP.Size() - Start;
|
||||
|
||||
EGame Game = ePrimeDemo;
|
||||
|
||||
if (Remaining < 32)
|
||||
{
|
||||
for (u32 iRem = 0; iRem < Remaining; iRem++)
|
||||
{
|
||||
if (rDGRP.ReadByte() != 0xFF)
|
||||
{
|
||||
Game = eCorruptionProto;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rDGRP.Seek(Start, SEEK_SET);
|
||||
return Game;
|
||||
}
|
||||
|
||||
CDependencyGroup* CDependencyGroupLoader::LoadDGRP(IInputStream& rDGRP, CResourceEntry *pEntry)
|
||||
{
|
||||
if (!rDGRP.IsValid()) return nullptr;
|
||||
|
||||
u32 NumDependencies = rDGRP.ReadLong();
|
||||
EGame Game = VersionTest(rDGRP, NumDependencies);
|
||||
EUIDLength IDLength = (Game < eCorruptionProto ? e32Bit : e64Bit);
|
||||
|
||||
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
|
||||
pGroup->SetGame(Game);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumDependencies; iDep++)
|
||||
{
|
||||
rDGRP.Seek(0x4, SEEK_CUR); // Skip dependency type
|
||||
CUniqueID AssetID(rDGRP, IDLength);
|
||||
pGroup->AddDependency(AssetID);
|
||||
}
|
||||
|
||||
return pGroup;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef CDEPENDENCYGROUPLOADER_H
|
||||
#define CDEPENDENCYGROUPLOADER_H
|
||||
|
||||
#include "Core/Resource/CDependencyGroup.h"
|
||||
#include "Core/Resource/EGame.h"
|
||||
|
||||
class CDependencyGroupLoader
|
||||
{
|
||||
CDependencyGroupLoader() {}
|
||||
static EGame VersionTest(IInputStream& rDGRP, u32 DepCount);
|
||||
|
||||
public:
|
||||
static CDependencyGroup* LoadDGRP(IInputStream& rDGRP, CResourceEntry *pEntry);
|
||||
};
|
||||
|
||||
#endif // CDEPENDENCYGROUPLOADER_H
|
|
@ -65,7 +65,7 @@ void CMaterialLoader::ReadPrimeMatSet()
|
|||
{
|
||||
mpSet->mMaterials[iMat] = ReadPrimeMaterial();
|
||||
mpSet->mMaterials[iMat]->mVersion = mVersion;
|
||||
mpSet->mMaterials[iMat]->mName = TString("Material #") + std::to_string(iMat + 1);
|
||||
mpSet->mMaterials[iMat]->mName = TString("Material #") + TString::FromInt32(iMat + 1, 0, 10);
|
||||
mpFile->Seek(MatsStart + Offsets[iMat], SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ void CMaterialLoader::ReadCorruptionMatSet()
|
|||
u32 Next = mpFile->Tell() + Size;
|
||||
mpSet->mMaterials[iMat] = ReadCorruptionMaterial();
|
||||
mpSet->mMaterials[iMat]->mVersion = mVersion;
|
||||
mpSet->mMaterials[iMat]->mName = TString("Material #") + std::to_string(iMat + 1);
|
||||
mpSet->mMaterials[iMat]->mName = TString("Material #") + TString::FromInt32(iMat + 1, 0, 10);
|
||||
mpFile->Seek(Next, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,31 @@ CModel::~CModel()
|
|||
delete mMaterialSets[iMat];
|
||||
}
|
||||
|
||||
|
||||
CDependencyTree* CModel::BuildDependencyTree() const
|
||||
{
|
||||
CDependencyTree *pTree = new CDependencyTree(ResID());
|
||||
|
||||
for (u32 iSet = 0; iSet < mMaterialSets.size(); iSet++)
|
||||
{
|
||||
CMaterialSet *pSet = mMaterialSets[iSet];
|
||||
|
||||
for (u32 iMat = 0; iMat < pSet->NumMaterials(); iMat++)
|
||||
{
|
||||
CMaterial *pMat = pSet->MaterialByIndex(iMat);
|
||||
pTree->AddDependency(pMat->IndTexture());
|
||||
|
||||
for (u32 iPass = 0; iPass < pMat->PassCount(); iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = pMat->Pass(iPass);
|
||||
pTree->AddDependency(pPass->Texture());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pTree;
|
||||
}
|
||||
|
||||
void CModel::BufferGL()
|
||||
{
|
||||
if (!mBuffered)
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
CModel(CMaterialSet *pSet, bool OwnsMatSet);
|
||||
~CModel();
|
||||
|
||||
CDependencyTree* BuildDependencyTree() const;
|
||||
void BufferGL();
|
||||
void GenerateMaterialShaders();
|
||||
void ClearGLBuffer();
|
||||
|
|
|
@ -63,6 +63,7 @@ bool CScriptObject::IsEditorProperty(IProperty *pProp)
|
|||
(pProp == mpRotation) ||
|
||||
(pProp == mpScale) ||
|
||||
(pProp == mpActive) ||
|
||||
(pProp == mpLightParameters) ||
|
||||
(pProp->Parent() == mpLightParameters)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue