Implemented area cooker, added the ability to resave Prime 1 MREAs from the World Editor
This commit is contained in:
parent
7a28db3d40
commit
e430cbfb73
|
@ -94,7 +94,6 @@ HEADERS += \
|
||||||
Resource/Cooker/CWorldCooker.h \
|
Resource/Cooker/CWorldCooker.h \
|
||||||
Resource/Factory/CAnimSetLoader.h \
|
Resource/Factory/CAnimSetLoader.h \
|
||||||
Resource/Factory/CAreaLoader.h \
|
Resource/Factory/CAreaLoader.h \
|
||||||
Resource/Factory/CBlockMgrIn.h \
|
|
||||||
Resource/Factory/CCollisionLoader.h \
|
Resource/Factory/CCollisionLoader.h \
|
||||||
Resource/Factory/CFontLoader.h \
|
Resource/Factory/CFontLoader.h \
|
||||||
Resource/Factory/CMaterialLoader.h \
|
Resource/Factory/CMaterialLoader.h \
|
||||||
|
@ -185,7 +184,9 @@ HEADERS += \
|
||||||
Resource/CResourceInfo.h \
|
Resource/CResourceInfo.h \
|
||||||
Resource/CPoiToWorld.h \
|
Resource/CPoiToWorld.h \
|
||||||
Resource/Factory/CPoiToWorldLoader.h \
|
Resource/Factory/CPoiToWorldLoader.h \
|
||||||
Resource/Cooker/CPoiToWorldCooker.h
|
Resource/Cooker/CPoiToWorldCooker.h \
|
||||||
|
Resource/Factory/CSectionMgrIn.h \
|
||||||
|
Resource/Cooker/CScriptCooker.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
@ -202,7 +203,6 @@ SOURCES += \
|
||||||
Resource/Cooker/CWorldCooker.cpp \
|
Resource/Cooker/CWorldCooker.cpp \
|
||||||
Resource/Factory/CAnimSetLoader.cpp \
|
Resource/Factory/CAnimSetLoader.cpp \
|
||||||
Resource/Factory/CAreaLoader.cpp \
|
Resource/Factory/CAreaLoader.cpp \
|
||||||
Resource/Factory/CBlockMgr.cpp \
|
|
||||||
Resource/Factory/CCollisionLoader.cpp \
|
Resource/Factory/CCollisionLoader.cpp \
|
||||||
Resource/Factory/CFontLoader.cpp \
|
Resource/Factory/CFontLoader.cpp \
|
||||||
Resource/Factory/CMaterialLoader.cpp \
|
Resource/Factory/CMaterialLoader.cpp \
|
||||||
|
@ -274,4 +274,5 @@ SOURCES += \
|
||||||
Scene/CSceneIterator.cpp \
|
Scene/CSceneIterator.cpp \
|
||||||
Resource/CPoiToWorld.cpp \
|
Resource/CPoiToWorld.cpp \
|
||||||
Resource/Factory/CPoiToWorldLoader.cpp \
|
Resource/Factory/CPoiToWorldLoader.cpp \
|
||||||
Resource/Cooker/CPoiToWorldCooker.cpp
|
Resource/Cooker/CPoiToWorldCooker.cpp \
|
||||||
|
Resource/Cooker/CScriptCooker.cpp
|
||||||
|
|
|
@ -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*/)
|
CModel* CAnimationParameters::GetCurrentModel(s32 NodeIndex /*= -1*/)
|
||||||
{
|
{
|
||||||
if (!mpCharSet) return nullptr;
|
if (!mpCharSet) return nullptr;
|
||||||
|
|
|
@ -20,6 +20,8 @@ class CAnimationParameters
|
||||||
public:
|
public:
|
||||||
CAnimationParameters();
|
CAnimationParameters();
|
||||||
CAnimationParameters(IInputStream& SCLY, EGame Game);
|
CAnimationParameters(IInputStream& SCLY, EGame Game);
|
||||||
|
void Write(IOutputStream& rSCLY);
|
||||||
|
|
||||||
CModel* GetCurrentModel(s32 NodeIndex = -1);
|
CModel* GetCurrentModel(s32 NodeIndex = -1);
|
||||||
TString GetCurrentCharacterName(s32 NodeIndex = -1);
|
TString GetCurrentCharacterName(s32 NodeIndex = -1);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ class CGameArea : public CResource
|
||||||
{
|
{
|
||||||
DECLARE_RESOURCE_TYPE(eArea)
|
DECLARE_RESOURCE_TYPE(eArea)
|
||||||
friend class CAreaLoader;
|
friend class CAreaLoader;
|
||||||
|
friend class CAreaCooker;
|
||||||
|
|
||||||
EGame mVersion;
|
EGame mVersion;
|
||||||
u32 mVertexCount;
|
u32 mVertexCount;
|
||||||
|
@ -28,9 +29,11 @@ class CGameArea : public CResource
|
||||||
CTransform4f mTransform;
|
CTransform4f mTransform;
|
||||||
CAABox mAABox;
|
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
|
// Geometry
|
||||||
CMaterialSet *mMaterialSet;
|
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
|
std::vector<CStaticModel*> mStaticTerrainModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
|
||||||
// Script
|
// Script
|
||||||
std::vector<CScriptLayer*> mScriptLayers;
|
std::vector<CScriptLayer*> mScriptLayers;
|
||||||
|
|
|
@ -1,9 +1,135 @@
|
||||||
#include "CAreaCooker.h"
|
#include "CAreaCooker.h"
|
||||||
|
#include "CScriptCooker.h"
|
||||||
|
#include <Core/Log.h>
|
||||||
|
|
||||||
CAreaCooker::CAreaCooker()
|
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)
|
u32 CAreaCooker::GetMREAVersion(EGame version)
|
||||||
{
|
{
|
||||||
switch (version)
|
switch (version)
|
||||||
|
|
|
@ -1,13 +1,35 @@
|
||||||
#ifndef CAREACOOKER_H
|
#ifndef CAREACOOKER_H
|
||||||
#define CAREACOOKER_H
|
#define CAREACOOKER_H
|
||||||
|
|
||||||
|
#include "CSectionMgrOut.h"
|
||||||
|
#include "Core/Resource/CGameArea.h"
|
||||||
#include "Core/Resource/EGame.h"
|
#include "Core/Resource/EGame.h"
|
||||||
#include <Common/types.h>
|
#include <FileIO/FileIO.h>
|
||||||
|
|
||||||
class CAreaCooker
|
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();
|
CAreaCooker();
|
||||||
|
void DetermineSectionNumbers();
|
||||||
|
void WritePrimeHeader(IOutputStream& rOut);
|
||||||
|
void WritePrimeSCLY(IOutputStream& rOut);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static void WriteCookedArea(CGameArea *pArea, IOutputStream& rOut);
|
||||||
static u32 GetMREAVersion(EGame version);
|
static u32 GetMREAVersion(EGame version);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -59,26 +59,27 @@ void CAreaLoader::ReadHeaderPrime()
|
||||||
mPathBlockNum = mpMREA->ReadLong();
|
mPathBlockNum = mpMREA->ReadLong();
|
||||||
mOctreeBlockNum = mpMREA->ReadLong();
|
mOctreeBlockNum = mpMREA->ReadLong();
|
||||||
|
|
||||||
mBlockMgr = new CBlockMgrIn(mNumBlocks, mpMREA);
|
mpSectionMgr = new CSectionMgrIn(mNumBlocks, mpMREA);
|
||||||
mpMREA->SeekToBoundary(32);
|
mpMREA->SeekToBoundary(32);
|
||||||
mBlockMgr->Init();
|
mpSectionMgr->Init();
|
||||||
|
LoadSectionDataBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAreaLoader::ReadGeometryPrime()
|
void CAreaLoader::ReadGeometryPrime()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)");
|
||||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
mpSectionMgr->ToSection(mGeometryBlockNum);
|
||||||
|
|
||||||
// Materials
|
// Materials
|
||||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||||
mBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
|
|
||||||
// Geometry
|
// Geometry
|
||||||
std::vector<CModel*> FileModels;
|
std::vector<CModel*> FileModels;
|
||||||
|
|
||||||
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
|
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);
|
FileModels.push_back(pModel);
|
||||||
|
|
||||||
if (mVersion <= ePrime)
|
if (mVersion <= ePrime)
|
||||||
|
@ -95,8 +96,8 @@ void CAreaLoader::ReadGeometryPrime()
|
||||||
pModel->GetSurface(iSurf)->MeshID = mpMREA->ReadShort();
|
pModel->GetSurface(iSurf)->MeshID = mpMREA->ReadShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
mBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
mBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +117,7 @@ void CAreaLoader::ReadGeometryPrime()
|
||||||
void CAreaLoader::ReadSCLYPrime()
|
void CAreaLoader::ReadSCLYPrime()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)");
|
||||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
mpSectionMgr->ToSection(mScriptLayerBlockNum);
|
||||||
|
|
||||||
CFourCC SCLY(*mpMREA);
|
CFourCC SCLY(*mpMREA);
|
||||||
if (SCLY != "SCLY")
|
if (SCLY != "SCLY")
|
||||||
|
@ -154,7 +155,7 @@ void CAreaLoader::ReadSCLYPrime()
|
||||||
void CAreaLoader::ReadLightsPrime()
|
void CAreaLoader::ReadLightsPrime()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)");
|
||||||
mBlockMgr->ToBlock(mLightsBlockNum);
|
mpSectionMgr->ToSection(mLightsBlockNum);
|
||||||
|
|
||||||
u32 babedead = mpMREA->ReadLong();
|
u32 babedead = mpMREA->ReadLong();
|
||||||
if (babedead != 0xbabedead) return;
|
if (babedead != 0xbabedead) return;
|
||||||
|
@ -258,7 +259,7 @@ void CAreaLoader::ReadHeaderEchoes()
|
||||||
if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong());
|
if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong());
|
||||||
mpMREA->SeekToBoundary(32);
|
mpMREA->SeekToBoundary(32);
|
||||||
|
|
||||||
mBlockMgr = new CBlockMgrIn(numBlocks, mpMREA);
|
mpSectionMgr = new CSectionMgrIn(numBlocks, mpMREA);
|
||||||
mpMREA->SeekToBoundary(32);
|
mpMREA->SeekToBoundary(32);
|
||||||
|
|
||||||
if (mVersion == eEchoes)
|
if (mVersion == eEchoes)
|
||||||
|
@ -267,13 +268,14 @@ void CAreaLoader::ReadHeaderEchoes()
|
||||||
Decompress();
|
Decompress();
|
||||||
}
|
}
|
||||||
|
|
||||||
mBlockMgr->Init();
|
mpSectionMgr->Init();
|
||||||
|
LoadSectionDataBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAreaLoader::ReadSCLYEchoes()
|
void CAreaLoader::ReadSCLYEchoes()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)");
|
||||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
mpSectionMgr->ToSection(mScriptLayerBlockNum);
|
||||||
|
|
||||||
// SCLY
|
// SCLY
|
||||||
for (u32 l = 0; l < mNumLayers; l++)
|
for (u32 l = 0; l < mNumLayers; l++)
|
||||||
|
@ -283,11 +285,11 @@ void CAreaLoader::ReadSCLYEchoes()
|
||||||
if (pLayer)
|
if (pLayer)
|
||||||
mpArea->mScriptLayers.push_back(pLayer);
|
mpArea->mScriptLayers.push_back(pLayer);
|
||||||
|
|
||||||
mBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCGN
|
// SCGN
|
||||||
mBlockMgr->ToBlock(mScriptGeneratorBlockNum);
|
mpSectionMgr->ToSection(mScriptGeneratorBlockNum);
|
||||||
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||||
|
|
||||||
if (pLayer)
|
if (pLayer)
|
||||||
|
@ -309,7 +311,7 @@ void CAreaLoader::ReadHeaderCorruption()
|
||||||
u32 SectionNumberCount = mpMREA->ReadLong();
|
u32 SectionNumberCount = mpMREA->ReadLong();
|
||||||
mpMREA->SeekToBoundary(32);
|
mpMREA->SeekToBoundary(32);
|
||||||
|
|
||||||
mBlockMgr = new CBlockMgrIn(NumSections, mpMREA);
|
mpSectionMgr = new CSectionMgrIn(NumSections, mpMREA);
|
||||||
mpMREA->SeekToBoundary(32);
|
mpMREA->SeekToBoundary(32);
|
||||||
|
|
||||||
ReadCompressedBlocks();
|
ReadCompressedBlocks();
|
||||||
|
@ -336,17 +338,18 @@ void CAreaLoader::ReadHeaderCorruption()
|
||||||
|
|
||||||
mpMREA->SeekToBoundary(32);
|
mpMREA->SeekToBoundary(32);
|
||||||
Decompress();
|
Decompress();
|
||||||
mBlockMgr->Init();
|
mpSectionMgr->Init();
|
||||||
|
LoadSectionDataBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAreaLoader::ReadGeometryCorruption()
|
void CAreaLoader::ReadGeometryCorruption()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)");
|
||||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
mpSectionMgr->ToSection(mGeometryBlockNum);
|
||||||
|
|
||||||
// Materials
|
// Materials
|
||||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||||
mBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
|
|
||||||
// Geometry
|
// Geometry
|
||||||
std::vector<CModel*> FileModels;
|
std::vector<CModel*> FileModels;
|
||||||
|
@ -355,14 +358,14 @@ void CAreaLoader::ReadGeometryCorruption()
|
||||||
|
|
||||||
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
|
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);
|
FileModels.push_back(pWorldModel);
|
||||||
|
|
||||||
CurWOBJSection += 4;
|
CurWOBJSection += 4;
|
||||||
CurGPUSection = mBlockMgr->CurrentBlock();
|
CurGPUSection = mpSectionMgr->CurrentSection();
|
||||||
|
|
||||||
// Load surface mesh IDs
|
// Load surface mesh IDs
|
||||||
mBlockMgr->ToBlock(CurWOBJSection - 2);
|
mpSectionMgr->ToSection(CurWOBJSection - 2);
|
||||||
u16 NumSurfaces = mpMREA->ReadShort();
|
u16 NumSurfaces = mpMREA->ReadShort();
|
||||||
|
|
||||||
for (u32 iSurf = 0; iSurf < NumSurfaces; iSurf++)
|
for (u32 iSurf = 0; iSurf < NumSurfaces; iSurf++)
|
||||||
|
@ -384,7 +387,7 @@ void CAreaLoader::ReadGeometryCorruption()
|
||||||
void CAreaLoader::ReadLightsCorruption()
|
void CAreaLoader::ReadLightsCorruption()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)");
|
||||||
mBlockMgr->ToBlock(mLightsBlockNum);
|
mpSectionMgr->ToSection(mLightsBlockNum);
|
||||||
|
|
||||||
u32 babedead = mpMREA->ReadLong();
|
u32 babedead = mpMREA->ReadLong();
|
||||||
if (babedead != 0xbabedead) return;
|
if (babedead != 0xbabedead) return;
|
||||||
|
@ -522,21 +525,35 @@ void CAreaLoader::Decompress()
|
||||||
TString Source = mpMREA->GetSourceString();
|
TString Source = mpMREA->GetSourceString();
|
||||||
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::eBigEndian);
|
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::eBigEndian);
|
||||||
mpMREA->SetSourceString(Source.ToStdString());
|
mpMREA->SetSourceString(Source.ToStdString());
|
||||||
mBlockMgr->SetInputStream(mpMREA);
|
mpSectionMgr->SetInputStream(mpMREA);
|
||||||
mHasDecompressedBuffer = true;
|
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()
|
void CAreaLoader::ReadCollision()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
|
||||||
mBlockMgr->ToBlock(mCollisionBlockNum);
|
mpSectionMgr->ToSection(mCollisionBlockNum);
|
||||||
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
|
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAreaLoader::ReadEGMC()
|
void CAreaLoader::ReadEGMC()
|
||||||
{
|
{
|
||||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading EGMC");
|
Log::FileWrite(mpMREA->GetSourceString(), "Reading EGMC");
|
||||||
mBlockMgr->ToBlock(mEGMCBlockNum);
|
mpSectionMgr->ToSection(mEGMCBlockNum);
|
||||||
CUniqueID EGMC(*mpMREA, (mVersion <= eEchoes ? e32Bit : e64Bit));
|
CUniqueID EGMC(*mpMREA, (mVersion <= eEchoes ? e32Bit : e64Bit));
|
||||||
mpArea->mpPoiToWorldMap = gResCache.GetResource(EGMC, "EGMC");
|
mpArea->mpPoiToWorldMap = gResCache.GetResource(EGMC, "EGMC");
|
||||||
}
|
}
|
||||||
|
@ -665,7 +682,7 @@ CGameArea* CAreaLoader::LoadMREA(IInputStream& MREA)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
delete Loader.mBlockMgr;
|
delete Loader.mpSectionMgr;
|
||||||
return Loader.mpArea;
|
return Loader.mpArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CAREALOADER_H
|
#ifndef CAREALOADER_H
|
||||||
#define CAREALOADER_H
|
#define CAREALOADER_H
|
||||||
|
|
||||||
#include "CBlockMgrIn.h"
|
#include "CSectionMgrIn.h"
|
||||||
#include "Core/Resource/Script/SConnection.h"
|
#include "Core/Resource/Script/SConnection.h"
|
||||||
#include "Core/Resource/CGameArea.h"
|
#include "Core/Resource/CGameArea.h"
|
||||||
#include "Core/Resource/EGame.h"
|
#include "Core/Resource/EGame.h"
|
||||||
|
@ -16,7 +16,7 @@ class CAreaLoader
|
||||||
// Area data
|
// Area data
|
||||||
TResPtr<CGameArea> mpArea;
|
TResPtr<CGameArea> mpArea;
|
||||||
IInputStream *mpMREA;
|
IInputStream *mpMREA;
|
||||||
CBlockMgrIn *mBlockMgr;
|
CSectionMgrIn *mpSectionMgr;
|
||||||
EGame mVersion;
|
EGame mVersion;
|
||||||
u32 mNumMeshes;
|
u32 mNumMeshes;
|
||||||
u32 mNumLayers;
|
u32 mNumLayers;
|
||||||
|
@ -74,6 +74,7 @@ class CAreaLoader
|
||||||
// Common
|
// Common
|
||||||
void ReadCompressedBlocks();
|
void ReadCompressedBlocks();
|
||||||
void Decompress();
|
void Decompress();
|
||||||
|
void LoadSectionDataBuffers();
|
||||||
void ReadCollision();
|
void ReadCollision();
|
||||||
void ReadEGMC();
|
void ReadEGMC();
|
||||||
void SetUpObjects();
|
void SetUpObjects();
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -18,7 +18,7 @@ void CModelLoader::LoadWorldMeshHeader(IInputStream &Model)
|
||||||
// I don't really have any need for most of this data, so
|
// I don't really have any need for most of this data, so
|
||||||
Model.Seek(0x34, SEEK_CUR);
|
Model.Seek(0x34, SEEK_CUR);
|
||||||
mAABox = CAABox(Model);
|
mAABox = CAABox(Model);
|
||||||
mpBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CModelLoader::LoadAttribArrays(IInputStream& Model)
|
void CModelLoader::LoadAttribArrays(IInputStream& Model)
|
||||||
|
@ -26,7 +26,7 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
|
||||||
// Positions
|
// Positions
|
||||||
if (mFlags & eShortPositions) // Shorts (DKCR only)
|
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.
|
float Divisor = 8192.f; // Might be incorrect! Needs verification via size comparison.
|
||||||
|
|
||||||
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
|
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
|
||||||
|
@ -40,18 +40,18 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
|
||||||
|
|
||||||
else // Floats
|
else // Floats
|
||||||
{
|
{
|
||||||
mPositions.resize(mpBlockMgr->CurrentBlockSize() / 0xC);
|
mPositions.resize(mpSectionMgr->CurrentSectionSize() / 0xC);
|
||||||
|
|
||||||
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
|
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
|
||||||
mPositions[iVtx] = CVector3f(Model);
|
mPositions[iVtx] = CVector3f(Model);
|
||||||
}
|
}
|
||||||
|
|
||||||
mpBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
|
|
||||||
// Normals
|
// Normals
|
||||||
if (mFlags & eShortNormals) // Shorts
|
if (mFlags & eShortNormals) // Shorts
|
||||||
{
|
{
|
||||||
mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0x6);
|
mNormals.resize(mpSectionMgr->CurrentSectionSize() / 0x6);
|
||||||
float Divisor = (mVersion < eReturns) ? 32768.f : 16384.f;
|
float Divisor = (mVersion < eReturns) ? 32768.f : 16384.f;
|
||||||
|
|
||||||
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
|
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
|
||||||
|
@ -64,35 +64,35 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
|
||||||
}
|
}
|
||||||
else // Floats
|
else // Floats
|
||||||
{
|
{
|
||||||
mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0xC);
|
mNormals.resize(mpSectionMgr->CurrentSectionSize() / 0xC);
|
||||||
|
|
||||||
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
|
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
|
||||||
mNormals[iVtx] = CVector3f(Model);
|
mNormals[iVtx] = CVector3f(Model);
|
||||||
}
|
}
|
||||||
|
|
||||||
mpBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
mColors.resize(mpBlockMgr->CurrentBlockSize() / 4);
|
mColors.resize(mpSectionMgr->CurrentSectionSize() / 4);
|
||||||
|
|
||||||
for (u32 iVtx = 0; iVtx < mColors.size(); iVtx++)
|
for (u32 iVtx = 0; iVtx < mColors.size(); iVtx++)
|
||||||
mColors[iVtx] = CColor(Model);
|
mColors[iVtx] = CColor(Model);
|
||||||
|
|
||||||
mpBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
|
|
||||||
|
|
||||||
// Float UVs
|
// Float UVs
|
||||||
mTex0.resize(mpBlockMgr->CurrentBlockSize() / 0x8);
|
mTex0.resize(mpSectionMgr->CurrentSectionSize() / 0x8);
|
||||||
|
|
||||||
for (u32 iVtx = 0; iVtx < mTex0.size(); iVtx++)
|
for (u32 iVtx = 0; iVtx < mTex0.size(); iVtx++)
|
||||||
mTex0[iVtx] = CVector2f(Model);
|
mTex0[iVtx] = CVector2f(Model);
|
||||||
|
|
||||||
mpBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
|
|
||||||
// Short UVs
|
// Short UVs
|
||||||
if (mFlags & eHasTex1)
|
if (mFlags & eHasTex1)
|
||||||
{
|
{
|
||||||
mTex1.resize(mpBlockMgr->CurrentBlockSize() / 0x4);
|
mTex1.resize(mpSectionMgr->CurrentSectionSize() / 0x4);
|
||||||
float Divisor = (mVersion < eReturns) ? 32768.f : 8192.f;
|
float Divisor = (mVersion < eReturns) ? 32768.f : 8192.f;
|
||||||
|
|
||||||
for (u32 iVtx = 0; iVtx < mTex1.size(); iVtx++)
|
for (u32 iVtx = 0; iVtx < mTex1.size(); iVtx++)
|
||||||
|
@ -102,7 +102,7 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model)
|
||||||
mTex1[iVtx] = CVector2f(x, y);
|
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++)
|
for (u32 iSurf = 0; iSurf < mSurfaceCount; iSurf++)
|
||||||
mSurfaceOffsets[iSurf] = Model.ReadLong();
|
mSurfaceOffsets[iSurf] = Model.ReadLong();
|
||||||
|
|
||||||
mpBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
SSurface* CModelLoader::LoadSurface(IInputStream& Model)
|
SSurface* CModelLoader::LoadSurface(IInputStream& Model)
|
||||||
|
@ -132,7 +132,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& Model)
|
||||||
|
|
||||||
// Primitive table
|
// Primitive table
|
||||||
u8 Flag = Model.ReadByte();
|
u8 Flag = Model.ReadByte();
|
||||||
u32 NextSurface = mpBlockMgr->NextOffset();
|
u32 NextSurface = mpSectionMgr->NextOffset();
|
||||||
|
|
||||||
while ((Flag != 0) && ((u32) Model.Tell() < NextSurface))
|
while ((Flag != 0) && ((u32) Model.Tell() < NextSurface))
|
||||||
{
|
{
|
||||||
|
@ -225,7 +225,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& Model)
|
||||||
Flag = Model.ReadByte();
|
Flag = Model.ReadByte();
|
||||||
} // Primitive table end
|
} // Primitive table end
|
||||||
|
|
||||||
mpBlockMgr->ToNextBlock();
|
mpSectionMgr->ToNextSection();
|
||||||
return pSurf;
|
return pSurf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,9 +451,9 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL)
|
||||||
|
|
||||||
CModel *pModel = new CModel();
|
CModel *pModel = new CModel();
|
||||||
Loader.mpModel = pModel;
|
Loader.mpModel = pModel;
|
||||||
Loader.mpBlockMgr = new CBlockMgrIn(BlockCount, &CMDL);
|
Loader.mpSectionMgr = new CSectionMgrIn(BlockCount, &CMDL);
|
||||||
CMDL.SeekToBoundary(32);
|
CMDL.SeekToBoundary(32);
|
||||||
Loader.mpBlockMgr->Init();
|
Loader.mpSectionMgr->Init();
|
||||||
|
|
||||||
// Materials
|
// Materials
|
||||||
Loader.mMaterials.resize(MatSetCount);
|
Loader.mMaterials.resize(MatSetCount);
|
||||||
|
@ -462,12 +462,12 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL)
|
||||||
Loader.mMaterials[iMat] = CMaterialLoader::LoadMaterialSet(CMDL, Loader.mVersion);
|
Loader.mMaterials[iMat] = CMaterialLoader::LoadMaterialSet(CMDL, Loader.mVersion);
|
||||||
|
|
||||||
if (Loader.mVersion < eCorruptionProto)
|
if (Loader.mVersion < eCorruptionProto)
|
||||||
Loader.mpBlockMgr->ToNextBlock();
|
Loader.mpSectionMgr->ToNextSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
pModel->mMaterialSets = Loader.mMaterials;
|
pModel->mMaterialSets = Loader.mMaterials;
|
||||||
pModel->mHasOwnMaterials = true;
|
pModel->mHasOwnMaterials = true;
|
||||||
if (Loader.mVersion >= eCorruptionProto) Loader.mpBlockMgr->ToNextBlock();
|
if (Loader.mVersion >= eCorruptionProto) Loader.mpSectionMgr->ToNextSection();
|
||||||
|
|
||||||
// Mesh
|
// Mesh
|
||||||
Loader.LoadAttribArrays(CMDL);
|
Loader.LoadAttribArrays(CMDL);
|
||||||
|
@ -486,14 +486,14 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL)
|
||||||
pModel->mHasOwnSurfaces = true;
|
pModel->mHasOwnSurfaces = true;
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
delete Loader.mpBlockMgr;
|
delete Loader.mpSectionMgr;
|
||||||
return pModel;
|
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;
|
CModelLoader Loader;
|
||||||
Loader.mpBlockMgr = &BlockMgr;
|
Loader.mpSectionMgr = &BlockMgr;
|
||||||
Loader.mVersion = Version;
|
Loader.mVersion = Version;
|
||||||
Loader.mFlags = eShortNormals;
|
Loader.mFlags = eShortNormals;
|
||||||
if (Version != eCorruptionProto) Loader.mFlags |= eHasTex1;
|
if (Version != eCorruptionProto) Loader.mFlags |= eHasTex1;
|
||||||
|
@ -523,10 +523,10 @@ CModel* CModelLoader::LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr,
|
||||||
return pModel;
|
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;
|
CModelLoader Loader;
|
||||||
Loader.mpBlockMgr = &BlockMgr;
|
Loader.mpSectionMgr = &BlockMgr;
|
||||||
Loader.mVersion = Version;
|
Loader.mVersion = Version;
|
||||||
Loader.mFlags = eShortNormals;
|
Loader.mFlags = eShortNormals;
|
||||||
Loader.mMaterials.resize(1);
|
Loader.mMaterials.resize(1);
|
||||||
|
@ -534,10 +534,10 @@ CModel* CModelLoader::LoadCorruptionWorldModel(IInputStream &MREA, CBlockMgrIn &
|
||||||
if (Version == eReturns) Loader.mFlags |= eHasTex1;
|
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
|
// 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.LoadWorldMeshHeader(MREA);
|
||||||
Loader.LoadSurfaceOffsets(MREA);
|
Loader.LoadSurfaceOffsets(MREA);
|
||||||
BlockMgr.ToBlock(GPUSecNum);
|
BlockMgr.ToSection(GPUSecNum);
|
||||||
Loader.LoadAttribArrays(MREA);
|
Loader.LoadAttribArrays(MREA);
|
||||||
|
|
||||||
CModel *pModel = new CModel();
|
CModel *pModel = new CModel();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CMODELLOADER_H
|
#ifndef CMODELLOADER_H
|
||||||
#define CMODELLOADER_H
|
#define CMODELLOADER_H
|
||||||
|
|
||||||
#include "CBlockMgrIn.h"
|
#include "CSectionMgrIn.h"
|
||||||
#include "Core/Resource/Model/CBasicModel.h"
|
#include "Core/Resource/Model/CBasicModel.h"
|
||||||
#include "Core/Resource/Model/CModel.h"
|
#include "Core/Resource/Model/CModel.h"
|
||||||
#include "Core/Resource/CResCache.h"
|
#include "Core/Resource/CResCache.h"
|
||||||
|
@ -27,7 +27,7 @@ public:
|
||||||
private:
|
private:
|
||||||
TResPtr<CModel> mpModel;
|
TResPtr<CModel> mpModel;
|
||||||
std::vector<CMaterialSet*> mMaterials;
|
std::vector<CMaterialSet*> mMaterials;
|
||||||
CBlockMgrIn *mpBlockMgr;
|
CSectionMgrIn *mpSectionMgr;
|
||||||
CAABox mAABox;
|
CAABox mAABox;
|
||||||
EGame mVersion;
|
EGame mVersion;
|
||||||
|
|
||||||
|
@ -57,8 +57,8 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static CModel* LoadCMDL(IInputStream& CMDL);
|
static CModel* LoadCMDL(IInputStream& CMDL);
|
||||||
static CModel* LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version);
|
static CModel* LoadWorldModel(IInputStream& MREA, CSectionMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version);
|
||||||
static CModel* LoadCorruptionWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, u32 HeaderSecNum, u32 GPUSecNum, 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 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 CModel* ImportAssimpNode(const aiNode *pNode, const aiScene *pScene, CMaterialSet& matSet);
|
||||||
static EGame GetFormatVersion(u32 Version);
|
static EGame GetFormatVersion(u32 Version);
|
||||||
|
|
|
@ -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
|
|
@ -11,13 +11,15 @@
|
||||||
#include "Editor/UICommon.h"
|
#include "Editor/UICommon.h"
|
||||||
|
|
||||||
#include <Core/Render/CDrawUtil.h>
|
#include <Core/Render/CDrawUtil.h>
|
||||||
|
#include <Core/Resource/Cooker/CAreaCooker.h>
|
||||||
#include <Core/Scene/CSceneIterator.h>
|
#include <Core/Scene/CSceneIterator.h>
|
||||||
#include <Core/Log.h>
|
#include <Core/Log.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <QOpenGLContext>
|
|
||||||
#include <QFontMetrics>
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QOpenGLContext>
|
||||||
|
|
||||||
CWorldEditor::CWorldEditor(QWidget *parent) :
|
CWorldEditor::CWorldEditor(QWidget *parent) :
|
||||||
INodeEditor(parent),
|
INodeEditor(parent),
|
||||||
|
@ -528,3 +530,11 @@ void CWorldEditor::on_ActionEditPoiToWorldMap_triggered()
|
||||||
mpPoiDialog->show();
|
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));
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ private slots:
|
||||||
void on_ActionSelectAll_triggered();
|
void on_ActionSelectAll_triggered();
|
||||||
void on_ActionInvertSelection_triggered();
|
void on_ActionInvertSelection_triggered();
|
||||||
void on_ActionEditPoiToWorldMap_triggered();
|
void on_ActionEditPoiToWorldMap_triggered();
|
||||||
|
void on_ActionSave_triggered();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CWORLDEDITOR_H
|
#endif // CWORLDEDITOR_H
|
||||||
|
|
|
@ -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 ************
|
// ************ MATH ************
|
||||||
void CTransform4f::Translate(CVector3f Translation)
|
void CTransform4f::Translate(CVector3f Translation)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
float m20, float m21, float m22, float m23);
|
float m20, float m21, float m22, float m23);
|
||||||
CTransform4f(CVector3f Position, CQuaternion Rotation, CVector3f Scale);
|
CTransform4f(CVector3f Position, CQuaternion Rotation, CVector3f Scale);
|
||||||
CTransform4f(CVector3f Position, CVector3f Rotation, CVector3f Scale);
|
CTransform4f(CVector3f Position, CVector3f Rotation, CVector3f Scale);
|
||||||
|
void Write(IOutputStream& rOut);
|
||||||
|
|
||||||
// Math
|
// Math
|
||||||
void Translate(CVector3f Translation);
|
void Translate(CVector3f Translation);
|
||||||
|
|
Loading…
Reference in New Issue