Implemented area cooker, added the ability to resave Prime 1 MREAs from the World Editor

This commit is contained in:
parax0 2016-01-31 19:29:10 -07:00
parent 7a28db3d40
commit e430cbfb73
19 changed files with 529 additions and 153 deletions

View File

@ -94,7 +94,6 @@ HEADERS += \
Resource/Cooker/CWorldCooker.h \
Resource/Factory/CAnimSetLoader.h \
Resource/Factory/CAreaLoader.h \
Resource/Factory/CBlockMgrIn.h \
Resource/Factory/CCollisionLoader.h \
Resource/Factory/CFontLoader.h \
Resource/Factory/CMaterialLoader.h \
@ -185,7 +184,9 @@ HEADERS += \
Resource/CResourceInfo.h \
Resource/CPoiToWorld.h \
Resource/Factory/CPoiToWorldLoader.h \
Resource/Cooker/CPoiToWorldCooker.h
Resource/Cooker/CPoiToWorldCooker.h \
Resource/Factory/CSectionMgrIn.h \
Resource/Cooker/CScriptCooker.h
# Source Files
SOURCES += \
@ -202,7 +203,6 @@ SOURCES += \
Resource/Cooker/CWorldCooker.cpp \
Resource/Factory/CAnimSetLoader.cpp \
Resource/Factory/CAreaLoader.cpp \
Resource/Factory/CBlockMgr.cpp \
Resource/Factory/CCollisionLoader.cpp \
Resource/Factory/CFontLoader.cpp \
Resource/Factory/CMaterialLoader.cpp \
@ -274,4 +274,5 @@ SOURCES += \
Scene/CSceneIterator.cpp \
Resource/CPoiToWorld.cpp \
Resource/Factory/CPoiToWorldLoader.cpp \
Resource/Cooker/CPoiToWorldCooker.cpp
Resource/Cooker/CPoiToWorldCooker.cpp \
Resource/Cooker/CScriptCooker.cpp

View File

@ -70,6 +70,25 @@ CAnimationParameters::CAnimationParameters(IInputStream& SCLY, EGame Game)
}
}
void CAnimationParameters::Write(IOutputStream& rSCLY)
{
if (mGame <= eEchoes)
{
if (mpCharSet)
{
rSCLY.WriteLong(AnimSet()->ResID().ToLong());
rSCLY.WriteLong(mNodeIndex);
rSCLY.WriteLong(mUnknown1);
}
else
{
rSCLY.WriteLong(0xFFFFFFFF);
rSCLY.WriteLong(0);
rSCLY.WriteLong(0xFFFFFFFF);
}
}
}
CModel* CAnimationParameters::GetCurrentModel(s32 NodeIndex /*= -1*/)
{
if (!mpCharSet) return nullptr;

View File

@ -20,6 +20,8 @@ class CAnimationParameters
public:
CAnimationParameters();
CAnimationParameters(IInputStream& SCLY, EGame Game);
void Write(IOutputStream& rSCLY);
CModel* GetCurrentModel(s32 NodeIndex = -1);
TString GetCurrentCharacterName(s32 NodeIndex = -1);

View File

@ -20,6 +20,7 @@ class CGameArea : public CResource
{
DECLARE_RESOURCE_TYPE(eArea)
friend class CAreaLoader;
friend class CAreaCooker;
EGame mVersion;
u32 mVertexCount;
@ -28,9 +29,11 @@ class CGameArea : public CResource
CTransform4f mTransform;
CAABox mAABox;
// Section data buffers; this is used to avoid having to regenerate the entire contents of the file on cook
std::vector<std::vector<u8>> mSectionDataBuffers;
// Geometry
CMaterialSet *mMaterialSet;
std::vector<CModel*> mTerrainModels; // TerrainModels is the original version of each model; this is used by the editor (bounding box checks, material editing, etc)
std::vector<CModel*> mTerrainModels; // TerrainModels is the original version of each model; this is currently mainly used in the POI map editor
std::vector<CStaticModel*> mStaticTerrainModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
// Script
std::vector<CScriptLayer*> mScriptLayers;

View File

@ -1,9 +1,135 @@
#include "CAreaCooker.h"
#include "CScriptCooker.h"
#include <Core/Log.h>
CAreaCooker::CAreaCooker()
{
}
void CAreaCooker::DetermineSectionNumbers()
{
mGeometrySecNum = 0;
// Determine how many sections are taken up by geometry...
u32 GeometrySections = 1; // Starting at 1 to account for materials
// Each world mesh has (7 + surface count) sections
// header, verts, normals, colors, float UVs, short UVs, surface offsets + surfaces
for (u32 iMesh = 0; iMesh < mpArea->mTerrainModels.size(); iMesh++)
{
CModel *pModel = mpArea->mTerrainModels[iMesh];
GeometrySections += (7 + pModel->GetSurfaceCount());
}
// Set section numbers
mArotSecNum = mGeometrySecNum + GeometrySections;
mSclySecNum = mArotSecNum + 1;
mCollisionSecNum = mSclySecNum + 1;
mUnknownSecNum = mCollisionSecNum + 1;
mLightsSecNum = mUnknownSecNum + 1;
mVisiSecNum = mLightsSecNum + 1;
mPathSecNum = mVisiSecNum + 1;
}
void CAreaCooker::WritePrimeHeader(IOutputStream& rOut)
{
rOut.WriteLong(0xDEADBEEF);
rOut.WriteLong(GetMREAVersion(mVersion));
mpArea->mTransform.Write(rOut);
rOut.WriteLong(mpArea->mTerrainModels.size());
rOut.WriteLong(mpArea->mSectionDataBuffers.size());
rOut.WriteLong(mGeometrySecNum);
rOut.WriteLong(mSclySecNum);
rOut.WriteLong(mCollisionSecNum);
rOut.WriteLong(mUnknownSecNum);
rOut.WriteLong(mLightsSecNum);
rOut.WriteLong(mVisiSecNum);
rOut.WriteLong(mPathSecNum);
rOut.WriteLong(mArotSecNum);
mSectionSizesOffset = rOut.Tell();
for (u32 iSec = 0; iSec < mpArea->mSectionDataBuffers.size(); iSec++)
rOut.WriteLong(0);
rOut.WriteToBoundary(32, 0);
mSectionMgr.SetSectionCount(mpArea->mSectionDataBuffers.size());
mSectionMgr.Init(rOut);
}
void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut)
{
u32 NumLayers = mpArea->mScriptLayers.size();
rOut.WriteString("SCLY", 4);
rOut.WriteLong(0x1); // Unknown value, but it's always 1
rOut.WriteLong(NumLayers);
u32 LayerSizesStart = rOut.Tell();
for (u32 iLyr = 0; iLyr < NumLayers; iLyr++)
rOut.WriteLong(0);
std::vector<u32> LayerSizes(NumLayers);
for (u32 iLyr = 0; iLyr < mpArea->mScriptLayers.size(); iLyr++)
{
u32 LayerStart = rOut.Tell();
CScriptCooker::WriteLayer(mpArea->Version(), mpArea->mScriptLayers[iLyr], rOut);
LayerSizes[iLyr] = rOut.Tell() - LayerStart;
}
u32 LayersEnd = rOut.Tell();
rOut.Seek(LayerSizesStart, SEEK_SET);
for (u32 iLyr = 0; iLyr < NumLayers; iLyr++)
rOut.WriteLong(LayerSizes[iLyr]);
rOut.Seek(LayersEnd, SEEK_SET);
rOut.WriteToBoundary(32, 0);
}
// ************ STATIC ************
void CAreaCooker::WriteCookedArea(CGameArea *pArea, IOutputStream& rOut)
{
CAreaCooker Cooker;
Cooker.mpArea = pArea;
Cooker.mVersion = pArea->Version();
if (Cooker.mVersion > ePrime)
{
Log::Error("Area cooking is not supported for games other than Metroid Prime");
return;
}
// Write header
Cooker.DetermineSectionNumbers();
Cooker.WritePrimeHeader(rOut);
// Write pre-SCLY data sections
for (u32 iSec = 0; iSec < Cooker.mSclySecNum; iSec++)
{
rOut.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size());
Cooker.mSectionMgr.AddSize(rOut);
}
// Write SCLY
Cooker.WritePrimeSCLY(rOut);
Cooker.mSectionMgr.AddSize(rOut);
// Write post-SCLY data sections
for (u32 iSec = Cooker.mSclySecNum + 1; iSec < pArea->mSectionDataBuffers.size(); iSec++)
{
rOut.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size());
Cooker.mSectionMgr.AddSize(rOut);
}
// Write section sizes
u32 AreaEnd = rOut.Tell();
rOut.Seek(Cooker.mSectionSizesOffset, SEEK_SET);
Cooker.mSectionMgr.WriteSizes(rOut);
rOut.Seek(AreaEnd, SEEK_SET);
}
u32 CAreaCooker::GetMREAVersion(EGame version)
{
switch (version)

View File

@ -1,13 +1,35 @@
#ifndef CAREACOOKER_H
#define CAREACOOKER_H
#include "CSectionMgrOut.h"
#include "Core/Resource/CGameArea.h"
#include "Core/Resource/EGame.h"
#include <Common/types.h>
#include <FileIO/FileIO.h>
class CAreaCooker
{
TResPtr<CGameArea> mpArea;
EGame mVersion;
CSectionMgrOut mSectionMgr;
u32 mSectionSizesOffset;
u32 mGeometrySecNum;
u32 mSclySecNum;
u32 mCollisionSecNum;
u32 mUnknownSecNum;
u32 mLightsSecNum;
u32 mVisiSecNum;
u32 mPathSecNum;
u32 mArotSecNum;
CAreaCooker();
void DetermineSectionNumbers();
void WritePrimeHeader(IOutputStream& rOut);
void WritePrimeSCLY(IOutputStream& rOut);
public:
static void WriteCookedArea(CGameArea *pArea, IOutputStream& rOut);
static u32 GetMREAVersion(EGame version);
};

View File

@ -0,0 +1,170 @@
#include "CScriptCooker.h"
void CScriptCooker::WriteProperty(IProperty *pProp)
{
switch (pProp->Type())
{
case eBoolProperty:
{
TBoolProperty *pBoolCast = static_cast<TBoolProperty*>(pProp);
mpSCLY->WriteByte(pBoolCast->Get() ? 1 : 0);
break;
}
case eByteProperty:
{
TByteProperty *pByteCast = static_cast<TByteProperty*>(pProp);
mpSCLY->WriteByte(pByteCast->Get());
break;
}
case eShortProperty:
{
TShortProperty *pShortCast = static_cast<TShortProperty*>(pProp);
mpSCLY->WriteShort(pShortCast->Get());
break;
}
case eLongProperty:
{
TLongProperty *pLongCast = static_cast<TLongProperty*>(pProp);
mpSCLY->WriteLong(pLongCast->Get());
break;
}
case eEnumProperty:
{
TEnumProperty *pEnumCast = static_cast<TEnumProperty*>(pProp);
CEnumTemplate *pEnumTemp = static_cast<CEnumTemplate*>(pEnumCast->Template());
u32 ID = pEnumTemp->EnumeratorID(pEnumCast->Get());
mpSCLY->WriteLong(ID);
break;
}
case eBitfieldProperty:
{
TBitfieldProperty *pBitfieldCast = static_cast<TBitfieldProperty*>(pProp);
mpSCLY->WriteLong(pBitfieldCast->Get());
break;
}
case eFloatProperty:
{
TFloatProperty *pFloatCast = static_cast<TFloatProperty*>(pProp);
mpSCLY->WriteFloat(pFloatCast->Get());
break;
}
case eStringProperty:
{
TStringProperty *pStringCast = static_cast<TStringProperty*>(pProp);
mpSCLY->WriteString(pStringCast->Get().ToStdString());
break;
}
case eVector3Property:
{
TVector3Property *pVectorCast = static_cast<TVector3Property*>(pProp);
pVectorCast->Get().Write(*mpSCLY);
break;
}
case eColorProperty:
{
TColorProperty *pColorCast = static_cast<TColorProperty*>(pProp);
pColorCast->Get().Write(*mpSCLY, false);
break;
}
case eFileProperty:
{
TFileProperty *pFileCast = static_cast<TFileProperty*>(pProp);
if (mVersion <= eEchoes)
mpSCLY->WriteLong(pFileCast->Get().ID().ToLong());
else
mpSCLY->WriteLongLong(pFileCast->Get().ID().ToLongLong());
break;
}
case eCharacterProperty:
{
TCharacterProperty *pCharCast = static_cast<TCharacterProperty*>(pProp);
pCharCast->Get().Write(*mpSCLY);
break;
}
case eStructProperty:
case eArrayProperty:
{
CPropertyStruct *pStruct = static_cast<CPropertyStruct*>(pProp);
CStructTemplate *pTemp = static_cast<CStructTemplate*>(pStruct->Template());
if (!pTemp->IsSingleProperty() || pProp->Type() == eArrayProperty)
mpSCLY->WriteLong(pStruct->Count());
for (u32 iProp = 0; iProp < pStruct->Count(); iProp++)
WriteProperty(pStruct->PropertyByIndex(iProp));
break;
}
}
}
void CScriptCooker::WriteLayerMP1(CScriptLayer *pLayer)
{
u32 LayerStart = mpSCLY->Tell();
mpSCLY->WriteByte(0); // Unknown value
mpSCLY->WriteLong(pLayer->GetNumObjects());
for (u32 iInst = 0; iInst < pLayer->GetNumObjects(); iInst++)
{
CScriptObject *pInstance = pLayer->ObjectByIndex(iInst);
WriteInstanceMP1(pInstance);
}
u32 LayerSize = mpSCLY->Tell() - LayerStart;
u32 NumPadBytes = 32 - (LayerSize % 32);
if (NumPadBytes == 32) NumPadBytes = 0;
for (u32 iPad = 0; iPad < NumPadBytes; iPad++)
mpSCLY->WriteByte(0);
}
void CScriptCooker::WriteInstanceMP1(CScriptObject *pInstance)
{
mpSCLY->WriteByte((u8) pInstance->ObjectTypeID());
u32 SizeOffset = mpSCLY->Tell();
mpSCLY->WriteLong(0);
u32 InstanceStart = mpSCLY->Tell();
mpSCLY->WriteLong(pInstance->InstanceID());
mpSCLY->WriteLong(pInstance->NumOutLinks());
for (u32 iLink = 0; iLink < pInstance->NumOutLinks(); iLink++)
{
const SLink& rkLink = pInstance->OutLink(iLink);
mpSCLY->WriteLong(rkLink.State);
mpSCLY->WriteLong(rkLink.Message);
mpSCLY->WriteLong(rkLink.ObjectID);
}
WriteProperty(pInstance->Properties());
u32 InstanceEnd = mpSCLY->Tell();
u32 InstanceSize = InstanceEnd - InstanceStart;
mpSCLY->Seek(SizeOffset, SEEK_SET);
mpSCLY->WriteLong(InstanceSize);
mpSCLY->Seek(InstanceEnd, SEEK_SET);
}
// ************ STATIC ************
void CScriptCooker::WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut)
{
CScriptCooker Cooker;
Cooker.mpSCLY = &rOut;
Cooker.mVersion = Game;
Cooker.WriteLayerMP1(pLayer);
}

View File

@ -0,0 +1,23 @@
#ifndef CSCRIPTCOOKER_H
#define CSCRIPTCOOKER_H
#include "Core/Resource/EGame.h"
#include "Core/Resource/Script/CScriptLayer.h"
#include "Core/Resource/Script/CScriptObject.h"
#include <FileIO/FileIO.h>
class CScriptCooker
{
IOutputStream *mpSCLY;
EGame mVersion;
CScriptCooker() {}
void WriteProperty(IProperty *pProp);
void WriteLayerMP1(CScriptLayer *pLayer);
void WriteInstanceMP1(CScriptObject *pInstance);
public:
static void WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut);
};
#endif // CSCRIPTCOOKER_H

View File

@ -59,26 +59,27 @@ void CAreaLoader::ReadHeaderPrime()
mPathBlockNum = mpMREA->ReadLong();
mOctreeBlockNum = mpMREA->ReadLong();
mBlockMgr = new CBlockMgrIn(mNumBlocks, mpMREA);
mpSectionMgr = new CSectionMgrIn(mNumBlocks, mpMREA);
mpMREA->SeekToBoundary(32);
mBlockMgr->Init();
mpSectionMgr->Init();
LoadSectionDataBuffers();
}
void CAreaLoader::ReadGeometryPrime()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)");
mBlockMgr->ToBlock(mGeometryBlockNum);
mpSectionMgr->ToSection(mGeometryBlockNum);
// Materials
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
mBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
// Geometry
std::vector<CModel*> FileModels;
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
{
CModel *pModel = CModelLoader::LoadWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, mVersion);
CModel *pModel = CModelLoader::LoadWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mMaterialSet, mVersion);
FileModels.push_back(pModel);
if (mVersion <= ePrime)
@ -95,8 +96,8 @@ void CAreaLoader::ReadGeometryPrime()
pModel->GetSurface(iSurf)->MeshID = mpMREA->ReadShort();
}
mBlockMgr->ToNextBlock();
mBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
mpSectionMgr->ToNextSection();
}
}
@ -116,7 +117,7 @@ void CAreaLoader::ReadGeometryPrime()
void CAreaLoader::ReadSCLYPrime()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)");
mBlockMgr->ToBlock(mScriptLayerBlockNum);
mpSectionMgr->ToSection(mScriptLayerBlockNum);
CFourCC SCLY(*mpMREA);
if (SCLY != "SCLY")
@ -154,7 +155,7 @@ void CAreaLoader::ReadSCLYPrime()
void CAreaLoader::ReadLightsPrime()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)");
mBlockMgr->ToBlock(mLightsBlockNum);
mpSectionMgr->ToSection(mLightsBlockNum);
u32 babedead = mpMREA->ReadLong();
if (babedead != 0xbabedead) return;
@ -258,7 +259,7 @@ void CAreaLoader::ReadHeaderEchoes()
if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong());
mpMREA->SeekToBoundary(32);
mBlockMgr = new CBlockMgrIn(numBlocks, mpMREA);
mpSectionMgr = new CSectionMgrIn(numBlocks, mpMREA);
mpMREA->SeekToBoundary(32);
if (mVersion == eEchoes)
@ -267,13 +268,14 @@ void CAreaLoader::ReadHeaderEchoes()
Decompress();
}
mBlockMgr->Init();
mpSectionMgr->Init();
LoadSectionDataBuffers();
}
void CAreaLoader::ReadSCLYEchoes()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)");
mBlockMgr->ToBlock(mScriptLayerBlockNum);
mpSectionMgr->ToSection(mScriptLayerBlockNum);
// SCLY
for (u32 l = 0; l < mNumLayers; l++)
@ -283,11 +285,11 @@ void CAreaLoader::ReadSCLYEchoes()
if (pLayer)
mpArea->mScriptLayers.push_back(pLayer);
mBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
}
// SCGN
mBlockMgr->ToBlock(mScriptGeneratorBlockNum);
mpSectionMgr->ToSection(mScriptGeneratorBlockNum);
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
if (pLayer)
@ -309,7 +311,7 @@ void CAreaLoader::ReadHeaderCorruption()
u32 SectionNumberCount = mpMREA->ReadLong();
mpMREA->SeekToBoundary(32);
mBlockMgr = new CBlockMgrIn(NumSections, mpMREA);
mpSectionMgr = new CSectionMgrIn(NumSections, mpMREA);
mpMREA->SeekToBoundary(32);
ReadCompressedBlocks();
@ -336,17 +338,18 @@ void CAreaLoader::ReadHeaderCorruption()
mpMREA->SeekToBoundary(32);
Decompress();
mBlockMgr->Init();
mpSectionMgr->Init();
LoadSectionDataBuffers();
}
void CAreaLoader::ReadGeometryCorruption()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)");
mBlockMgr->ToBlock(mGeometryBlockNum);
mpSectionMgr->ToSection(mGeometryBlockNum);
// Materials
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
mBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
// Geometry
std::vector<CModel*> FileModels;
@ -355,14 +358,14 @@ void CAreaLoader::ReadGeometryCorruption()
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
{
CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
FileModels.push_back(pWorldModel);
CurWOBJSection += 4;
CurGPUSection = mBlockMgr->CurrentBlock();
CurGPUSection = mpSectionMgr->CurrentSection();
// Load surface mesh IDs
mBlockMgr->ToBlock(CurWOBJSection - 2);
mpSectionMgr->ToSection(CurWOBJSection - 2);
u16 NumSurfaces = mpMREA->ReadShort();
for (u32 iSurf = 0; iSurf < NumSurfaces; iSurf++)
@ -384,7 +387,7 @@ void CAreaLoader::ReadGeometryCorruption()
void CAreaLoader::ReadLightsCorruption()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)");
mBlockMgr->ToBlock(mLightsBlockNum);
mpSectionMgr->ToSection(mLightsBlockNum);
u32 babedead = mpMREA->ReadLong();
if (babedead != 0xbabedead) return;
@ -522,21 +525,35 @@ void CAreaLoader::Decompress()
TString Source = mpMREA->GetSourceString();
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::eBigEndian);
mpMREA->SetSourceString(Source.ToStdString());
mBlockMgr->SetInputStream(mpMREA);
mpSectionMgr->SetInputStream(mpMREA);
mHasDecompressedBuffer = true;
}
void CAreaLoader::LoadSectionDataBuffers()
{
mpArea->mSectionDataBuffers.resize(mpSectionMgr->NumSections());
mpSectionMgr->ToSection(0);
for (u32 iSec = 0; iSec < mpSectionMgr->NumSections(); iSec++)
{
u32 Size = mpSectionMgr->CurrentSectionSize();
mpArea->mSectionDataBuffers[iSec].resize(Size);
mpMREA->ReadBytes(mpArea->mSectionDataBuffers[iSec].data(), mpArea->mSectionDataBuffers[iSec].size());
mpSectionMgr->ToNextSection();
}
}
void CAreaLoader::ReadCollision()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
mBlockMgr->ToBlock(mCollisionBlockNum);
mpSectionMgr->ToSection(mCollisionBlockNum);
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
}
void CAreaLoader::ReadEGMC()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading EGMC");
mBlockMgr->ToBlock(mEGMCBlockNum);
mpSectionMgr->ToSection(mEGMCBlockNum);
CUniqueID EGMC(*mpMREA, (mVersion <= eEchoes ? e32Bit : e64Bit));
mpArea->mpPoiToWorldMap = gResCache.GetResource(EGMC, "EGMC");
}
@ -665,7 +682,7 @@ CGameArea* CAreaLoader::LoadMREA(IInputStream& MREA)
}
// Cleanup
delete Loader.mBlockMgr;
delete Loader.mpSectionMgr;
return Loader.mpArea;
}

View File

@ -1,7 +1,7 @@
#ifndef CAREALOADER_H
#define CAREALOADER_H
#include "CBlockMgrIn.h"
#include "CSectionMgrIn.h"
#include "Core/Resource/Script/SConnection.h"
#include "Core/Resource/CGameArea.h"
#include "Core/Resource/EGame.h"
@ -16,7 +16,7 @@ class CAreaLoader
// Area data
TResPtr<CGameArea> mpArea;
IInputStream *mpMREA;
CBlockMgrIn *mBlockMgr;
CSectionMgrIn *mpSectionMgr;
EGame mVersion;
u32 mNumMeshes;
u32 mNumLayers;
@ -74,6 +74,7 @@ class CAreaLoader
// Common
void ReadCompressedBlocks();
void Decompress();
void LoadSectionDataBuffers();
void ReadCollision();
void ReadEGMC();
void SetUpObjects();

View File

@ -1,58 +0,0 @@
#include "CBlockMgrIn.h"
CBlockMgrIn::CBlockMgrIn(unsigned long count, IInputStream* src)
{
mpInputStream = src;
mBlockCount = count;
mBlockSizes.resize(count);
for (unsigned long b = 0; b < count; b++)
mBlockSizes[b] = src->ReadLong();
}
void CBlockMgrIn::Init()
{
// Initialize the block manager; this marks the start of the first block
mCurBlock = 0;
mCurBlockStart = mpInputStream->Tell();
mBlocksStart = mpInputStream->Tell();
}
void CBlockMgrIn::ToBlock(unsigned long block)
{
unsigned long offset = mBlocksStart;
for (unsigned long b = 0; b < block; b++)
offset += mBlockSizes[b];
mpInputStream->Seek(offset, SEEK_SET);
mCurBlock = block;
mCurBlockStart = mpInputStream->Tell();
}
void CBlockMgrIn::ToNextBlock()
{
mpInputStream->Seek(mCurBlockStart + mBlockSizes[mCurBlock], SEEK_SET);
mCurBlock++;
mCurBlockStart = mpInputStream->Tell();
}
long CBlockMgrIn::NextOffset()
{
return mCurBlockStart + mBlockSizes[mCurBlock];
}
long CBlockMgrIn::CurrentBlock()
{
return mCurBlock;
}
long CBlockMgrIn::CurrentBlockSize()
{
return mBlockSizes[mCurBlock];
}
void CBlockMgrIn::SetInputStream(IInputStream *in)
{
mpInputStream = in;
}

View File

@ -1,28 +0,0 @@
#ifndef CBLOCKMGRIN_H
#define CBLOCKMGRIN_H
#include <FileIO/IInputStream.h>
#include <vector>
// The purpose of this class is to keep track of data block navigation - required to read CMDL and MREA files correctly
class CBlockMgrIn
{
IInputStream *mpInputStream;
unsigned long mBlockCount;
std::vector<unsigned long> mBlockSizes;
unsigned long mCurBlock;
unsigned long mCurBlockStart;
unsigned long mBlocksStart;
public:
CBlockMgrIn(unsigned long count, IInputStream* src);
void Init();
void ToBlock(unsigned long block);
void ToNextBlock();
long NextOffset();
long CurrentBlock();
long CurrentBlockSize();
void SetInputStream(IInputStream *in);
};
#endif // CBLOCKMGRIN_H

View File

@ -18,7 +18,7 @@ void CModelLoader::LoadWorldMeshHeader(IInputStream &Model)
// I don't really have any need for most of this data, so
Model.Seek(0x34, SEEK_CUR);
mAABox = CAABox(Model);
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
}
void CModelLoader::LoadAttribArrays(IInputStream& Model)
@ -26,7 +26,7 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
// Positions
if (mFlags & eShortPositions) // Shorts (DKCR only)
{
mPositions.resize(mpBlockMgr->CurrentBlockSize() / 0x6);
mPositions.resize(mpSectionMgr->CurrentSectionSize() / 0x6);
float Divisor = 8192.f; // Might be incorrect! Needs verification via size comparison.
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
@ -40,18 +40,18 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
else // Floats
{
mPositions.resize(mpBlockMgr->CurrentBlockSize() / 0xC);
mPositions.resize(mpSectionMgr->CurrentSectionSize() / 0xC);
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
mPositions[iVtx] = CVector3f(Model);
}
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
// Normals
if (mFlags & eShortNormals) // Shorts
{
mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0x6);
mNormals.resize(mpSectionMgr->CurrentSectionSize() / 0x6);
float Divisor = (mVersion < eReturns) ? 32768.f : 16384.f;
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
@ -64,35 +64,35 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
}
else // Floats
{
mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0xC);
mNormals.resize(mpSectionMgr->CurrentSectionSize() / 0xC);
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
mNormals[iVtx] = CVector3f(Model);
}
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
// Colors
mColors.resize(mpBlockMgr->CurrentBlockSize() / 4);
mColors.resize(mpSectionMgr->CurrentSectionSize() / 4);
for (u32 iVtx = 0; iVtx < mColors.size(); iVtx++)
mColors[iVtx] = CColor(Model);
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
// Float UVs
mTex0.resize(mpBlockMgr->CurrentBlockSize() / 0x8);
mTex0.resize(mpSectionMgr->CurrentSectionSize() / 0x8);
for (u32 iVtx = 0; iVtx < mTex0.size(); iVtx++)
mTex0[iVtx] = CVector2f(Model);
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
// Short UVs
if (mFlags & eHasTex1)
{
mTex1.resize(mpBlockMgr->CurrentBlockSize() / 0x4);
mTex1.resize(mpSectionMgr->CurrentSectionSize() / 0x4);
float Divisor = (mVersion < eReturns) ? 32768.f : 8192.f;
for (u32 iVtx = 0; iVtx < mTex1.size(); iVtx++)
@ -102,7 +102,7 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
mTex1[iVtx] = CVector2f(x, y);
}
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
}
}
@ -114,7 +114,7 @@ void CModelLoader::LoadSurfaceOffsets(IInputStream& Model)
for (u32 iSurf = 0; iSurf < mSurfaceCount; iSurf++)
mSurfaceOffsets[iSurf] = Model.ReadLong();
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
}
SSurface* CModelLoader::LoadSurface(IInputStream& Model)
@ -132,7 +132,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& Model)
// Primitive table
u8 Flag = Model.ReadByte();
u32 NextSurface = mpBlockMgr->NextOffset();
u32 NextSurface = mpSectionMgr->NextOffset();
while ((Flag != 0) && ((u32) Model.Tell() < NextSurface))
{
@ -225,7 +225,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& Model)
Flag = Model.ReadByte();
} // Primitive table end
mpBlockMgr->ToNextBlock();
mpSectionMgr->ToNextSection();
return pSurf;
}
@ -451,9 +451,9 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL)
CModel *pModel = new CModel();
Loader.mpModel = pModel;
Loader.mpBlockMgr = new CBlockMgrIn(BlockCount, &CMDL);
Loader.mpSectionMgr = new CSectionMgrIn(BlockCount, &CMDL);
CMDL.SeekToBoundary(32);
Loader.mpBlockMgr->Init();
Loader.mpSectionMgr->Init();
// Materials
Loader.mMaterials.resize(MatSetCount);
@ -462,12 +462,12 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL)
Loader.mMaterials[iMat] = CMaterialLoader::LoadMaterialSet(CMDL, Loader.mVersion);
if (Loader.mVersion < eCorruptionProto)
Loader.mpBlockMgr->ToNextBlock();
Loader.mpSectionMgr->ToNextSection();
}
pModel->mMaterialSets = Loader.mMaterials;
pModel->mHasOwnMaterials = true;
if (Loader.mVersion >= eCorruptionProto) Loader.mpBlockMgr->ToNextBlock();
if (Loader.mVersion >= eCorruptionProto) Loader.mpSectionMgr->ToNextSection();
// Mesh
Loader.LoadAttribArrays(CMDL);
@ -486,14 +486,14 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL)
pModel->mHasOwnSurfaces = true;
// Cleanup
delete Loader.mpBlockMgr;
delete Loader.mpSectionMgr;
return pModel;
}
CModel* CModelLoader::LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version)
CModel* CModelLoader::LoadWorldModel(IInputStream& MREA, CSectionMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version)
{
CModelLoader Loader;
Loader.mpBlockMgr = &BlockMgr;
Loader.mpSectionMgr = &BlockMgr;
Loader.mVersion = Version;
Loader.mFlags = eShortNormals;
if (Version != eCorruptionProto) Loader.mFlags |= eHasTex1;
@ -523,10 +523,10 @@ CModel* CModelLoader::LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr,
return pModel;
}
CModel* CModelLoader::LoadCorruptionWorldModel(IInputStream &MREA, CBlockMgrIn &BlockMgr, CMaterialSet &MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version)
CModel* CModelLoader::LoadCorruptionWorldModel(IInputStream &MREA, CSectionMgrIn &BlockMgr, CMaterialSet &MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version)
{
CModelLoader Loader;
Loader.mpBlockMgr = &BlockMgr;
Loader.mpSectionMgr = &BlockMgr;
Loader.mVersion = Version;
Loader.mFlags = eShortNormals;
Loader.mMaterials.resize(1);
@ -534,10 +534,10 @@ CModel* CModelLoader::LoadCorruptionWorldModel(IInputStream &MREA, CBlockMgrIn &
if (Version == eReturns) Loader.mFlags |= eHasTex1;
// Corruption/DKCR MREAs split the mesh header and surface offsets away from the actual geometry data so I need two section numbers to read it
BlockMgr.ToBlock(HeaderSecNum);
BlockMgr.ToSection(HeaderSecNum);
Loader.LoadWorldMeshHeader(MREA);
Loader.LoadSurfaceOffsets(MREA);
BlockMgr.ToBlock(GPUSecNum);
BlockMgr.ToSection(GPUSecNum);
Loader.LoadAttribArrays(MREA);
CModel *pModel = new CModel();

View File

@ -1,7 +1,7 @@
#ifndef CMODELLOADER_H
#define CMODELLOADER_H
#include "CBlockMgrIn.h"
#include "CSectionMgrIn.h"
#include "Core/Resource/Model/CBasicModel.h"
#include "Core/Resource/Model/CModel.h"
#include "Core/Resource/CResCache.h"
@ -27,7 +27,7 @@ public:
private:
TResPtr<CModel> mpModel;
std::vector<CMaterialSet*> mMaterials;
CBlockMgrIn *mpBlockMgr;
CSectionMgrIn *mpSectionMgr;
CAABox mAABox;
EGame mVersion;
@ -57,8 +57,8 @@ private:
public:
static CModel* LoadCMDL(IInputStream& CMDL);
static CModel* LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version);
static CModel* LoadCorruptionWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version);
static CModel* LoadWorldModel(IInputStream& MREA, CSectionMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version);
static CModel* LoadCorruptionWorldModel(IInputStream& MREA, CSectionMgrIn& BlockMgr, CMaterialSet& MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version);
static void BuildWorldMeshes(const std::vector<CModel*>& rkIn, std::vector<CModel*>& rOut, bool DeleteInputModels);
static CModel* ImportAssimpNode(const aiNode *pNode, const aiScene *pScene, CMaterialSet& matSet);
static EGame GetFormatVersion(u32 Version);

View File

@ -0,0 +1,60 @@
#ifndef CSECTIONMGRIN_H
#define CSECTIONMGRIN_H
#include <FileIO/IInputStream.h>
#include <Common/types.h>
#include <vector>
// The purpose of this class is to keep track of data block navigation - required to read CMDL and MREA files correctly
class CSectionMgrIn
{
IInputStream *mpInputStream;
std::vector<u32> mSectionSizes;
u32 mCurSec;
u32 mCurSecStart;
u32 mSecsStart;
public:
CSectionMgrIn(u32 Count, IInputStream* pSrc)
: mpInputStream(pSrc)
{
mSectionSizes.resize(Count);
for (u32 iSec = 0; iSec < Count; iSec++)
mSectionSizes[iSec] = pSrc->ReadLong();
}
inline void Init()
{
// Initializes the block manager and marks the start of the first block
mCurSec = 0;
mCurSecStart = mpInputStream->Tell();
mSecsStart = mCurSecStart;
}
void ToSection(u32 SecNum)
{
u32 Offset = mSecsStart;
for (u32 iSec = 0; iSec < SecNum; iSec++)
Offset += mSectionSizes[iSec];
mpInputStream->Seek(Offset, SEEK_SET);
mCurSec = SecNum;
mCurSecStart = mpInputStream->Tell();
}
inline void ToNextSection()
{
mCurSecStart += mSectionSizes[mCurSec];
mpInputStream->Seek(mCurSecStart, SEEK_SET);
mCurSec++;
}
inline u32 NextOffset() { return mCurSecStart + mSectionSizes[mCurSec]; }
inline u32 CurrentSection() { return mCurSec; }
inline u32 CurrentSectionSize() { return mSectionSizes[mCurSec]; }
inline u32 NumSections() { return mSectionSizes.size(); }
inline void SetInputStream(IInputStream *pIn) { mpInputStream = pIn; }
};
#endif // CSECTIONMGRIN_H

View File

@ -11,13 +11,15 @@
#include "Editor/UICommon.h"
#include <Core/Render/CDrawUtil.h>
#include <Core/Resource/Cooker/CAreaCooker.h>
#include <Core/Scene/CSceneIterator.h>
#include <Core/Log.h>
#include <iostream>
#include <QOpenGLContext>
#include <QFontMetrics>
#include <QComboBox>
#include <QFontMetrics>
#include <QMessageBox>
#include <QOpenGLContext>
CWorldEditor::CWorldEditor(QWidget *parent) :
INodeEditor(parent),
@ -528,3 +530,11 @@ void CWorldEditor::on_ActionEditPoiToWorldMap_triggered()
mpPoiDialog->show();
}
}
void CWorldEditor::on_ActionSave_triggered()
{
TString Out = mpArea->FullSource();
CFileOutStream MREA(Out.ToStdString(), IOUtil::eBigEndian);
CAreaCooker::WriteCookedArea(mpArea, MREA);
QMessageBox::information(this, "Success", "Successfully saved area to " + TO_QSTRING(Out));
}

View File

@ -85,6 +85,7 @@ private slots:
void on_ActionSelectAll_triggered();
void on_ActionInvertSelection_triggered();
void on_ActionEditPoiToWorldMap_triggered();
void on_ActionSave_triggered();
};
#endif // CWORLDEDITOR_H

View File

@ -54,6 +54,12 @@ CTransform4f::CTransform4f(CVector3f, CVector3f, CVector3f)
{
}
void CTransform4f::Write(IOutputStream& rOut)
{
for (int iFlt = 0; iFlt < 12; iFlt++)
rOut.WriteFloat(_m[iFlt]);
}
// ************ MATH ************
void CTransform4f::Translate(CVector3f Translation)
{

View File

@ -27,6 +27,7 @@ public:
float m20, float m21, float m22, float m23);
CTransform4f(CVector3f Position, CQuaternion Rotation, CVector3f Scale);
CTransform4f(CVector3f Position, CVector3f Rotation, CVector3f Scale);
void Write(IOutputStream& rOut);
// Math
void Translate(CVector3f Translation);