mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-21 18:59:12 +00:00
Mass refactoring part 1/2: establishing multiple subprojects, moving source files to their new location, adding resources/templates to version control
This commit is contained in:
364
src/Core/Resource/Cooker/CMaterialCooker.cpp
Normal file
364
src/Core/Resource/Cooker/CMaterialCooker.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#include "CMaterialCooker.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
CMaterialCooker::CMaterialCooker()
|
||||
{
|
||||
mpMat = nullptr;
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMatSetPrime(COutputStream& Out)
|
||||
{
|
||||
// Gather texture list from the materials before starting
|
||||
mTextureIDs.clear();
|
||||
u32 NumMats = mpSet->mMaterials.size();
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
{
|
||||
CMaterial *pMat = mpSet->mMaterials[iMat];
|
||||
|
||||
u32 NumPasses = pMat->PassCount();
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CTexture *pTex = pMat->Pass(iPass)->Texture();
|
||||
if (pTex)
|
||||
mTextureIDs.push_back(pTex->ResID().ToLong());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort/remove duplicates
|
||||
std::sort(mTextureIDs.begin(), mTextureIDs.end());
|
||||
mTextureIDs.erase(std::unique(mTextureIDs.begin(), mTextureIDs.end()), mTextureIDs.end());
|
||||
|
||||
// Write texture IDs
|
||||
Out.WriteLong(mTextureIDs.size());
|
||||
|
||||
for (u32 iTex = 0; iTex < mTextureIDs.size(); iTex++)
|
||||
Out.WriteLong(mTextureIDs[iTex]);
|
||||
|
||||
// Write material offset filler
|
||||
Out.WriteLong(NumMats);
|
||||
u32 MatOffsetsStart = Out.Tell();
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
Out.WriteLong(0);
|
||||
|
||||
// Write materials
|
||||
u32 MatsStart = Out.Tell();
|
||||
std::vector<u32> MatEndOffsets(NumMats);
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
{
|
||||
mpMat = mpSet->mMaterials[iMat];
|
||||
WriteMaterialPrime(Out);
|
||||
MatEndOffsets[iMat] = Out.Tell() - MatsStart;
|
||||
}
|
||||
|
||||
// Write material offsets
|
||||
u32 MatsEnd = Out.Tell();
|
||||
Out.Seek(MatOffsetsStart, SEEK_SET);
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
Out.WriteLong(MatEndOffsets[iMat]);
|
||||
|
||||
// Done!
|
||||
Out.Seek(MatsEnd, SEEK_SET);
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMatSetCorruption(COutputStream&)
|
||||
{
|
||||
// Not using parameter 1 (COutputStream& - Out)
|
||||
// todo
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMaterialPrime(COutputStream& Out)
|
||||
{
|
||||
// Gather data from the passes before we start writing
|
||||
u32 TexFlags = 0;
|
||||
u32 NumKonst = 0;
|
||||
std::vector<u32> TexIndices;
|
||||
|
||||
for (u32 iPass = 0; iPass < mpMat->mPasses.size(); iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
|
||||
if ((pPass->KColorSel() >= 0xC) || (pPass->KAlphaSel() >= 0x10))
|
||||
{
|
||||
// Determine the highest Konst index being used
|
||||
u32 KColorIndex = pPass->KColorSel() % 4;
|
||||
u32 KAlphaIndex = pPass->KAlphaSel() % 4;
|
||||
|
||||
if (KColorIndex >= NumKonst)
|
||||
NumKonst = KColorIndex + 1;
|
||||
if (KAlphaIndex >= NumKonst)
|
||||
NumKonst = KAlphaIndex + 1;
|
||||
}
|
||||
|
||||
CTexture *pPassTex = pPass->Texture();
|
||||
if (pPassTex != nullptr)
|
||||
{
|
||||
TexFlags |= (1 << iPass);
|
||||
u32 TexID = pPassTex->ResID().ToLong();
|
||||
|
||||
for (u32 iTex = 0; iTex < mTextureIDs.size(); iTex++)
|
||||
{
|
||||
if (mTextureIDs[iTex] == TexID)
|
||||
{
|
||||
TexIndices.push_back(iTex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get group index
|
||||
u32 GroupIndex;
|
||||
u64 MatHash = mpMat->HashParameters();
|
||||
bool NewHash = true;
|
||||
|
||||
for (u32 iHash = 0; iHash < mMaterialHashes.size(); iHash++)
|
||||
{
|
||||
if (mMaterialHashes[iHash] == MatHash)
|
||||
{
|
||||
GroupIndex = iHash;
|
||||
NewHash = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NewHash)
|
||||
{
|
||||
GroupIndex = mMaterialHashes.size();
|
||||
mMaterialHashes.push_back(MatHash);
|
||||
}
|
||||
|
||||
// Start writing!
|
||||
// Generate flags value
|
||||
bool HasKonst = (NumKonst > 0);
|
||||
u32 Flags;
|
||||
|
||||
if (mVersion <= ePrime)
|
||||
Flags = 0x1003;
|
||||
else
|
||||
Flags = 0x4002;
|
||||
|
||||
Flags |= (HasKonst << 3) | mpMat->Options() | (TexFlags << 16);
|
||||
|
||||
Out.WriteLong(Flags);
|
||||
|
||||
// Texture indices
|
||||
Out.WriteLong(TexIndices.size());
|
||||
for (u32 iTex = 0; iTex < TexIndices.size(); iTex++)
|
||||
Out.WriteLong(TexIndices[iTex]);
|
||||
|
||||
// Vertex description
|
||||
EVertexDescription Desc = mpMat->VtxDesc();
|
||||
|
||||
if (mVersion < eEchoes)
|
||||
Desc = (EVertexDescription) (Desc & 0x00FFFFFF);
|
||||
|
||||
Out.WriteLong(Desc);
|
||||
|
||||
// Echoes unknowns
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
Out.WriteLong(mpMat->EchoesUnknownA());
|
||||
Out.WriteLong(mpMat->EchoesUnknownB());
|
||||
}
|
||||
|
||||
// Group index
|
||||
Out.WriteLong(GroupIndex);
|
||||
|
||||
// Konst
|
||||
if (HasKonst)
|
||||
{
|
||||
Out.WriteLong(NumKonst);
|
||||
for (u32 iKonst = 0; iKonst < NumKonst; iKonst++)
|
||||
Out.WriteLong( mpMat->Konst(iKonst).AsLongRGBA() );
|
||||
}
|
||||
|
||||
// Blend Mode
|
||||
// Some modifications are done to convert the GLenum to the corresponding GX enum
|
||||
u16 BlendSrcFac = (u16) mpMat->BlendSrcFac();
|
||||
u16 BlendDstFac = (u16) mpMat->BlendDstFac();
|
||||
if (BlendSrcFac >= 0x300) BlendSrcFac -= 0x2FE;
|
||||
if (BlendDstFac >= 0x300) BlendDstFac -= 0x2FE;
|
||||
Out.WriteShort(BlendDstFac);
|
||||
Out.WriteShort(BlendSrcFac);
|
||||
|
||||
// Color Channels
|
||||
Out.WriteLong(1);
|
||||
Out.WriteLong(0x3000 | (mpMat->IsLightingEnabled() ? 1 : 0));
|
||||
|
||||
// TEV
|
||||
u32 NumPasses = mpMat->PassCount();
|
||||
Out.WriteLong(NumPasses);
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
|
||||
u32 ColorInputFlags = ((pPass->ColorInput(0)) |
|
||||
(pPass->ColorInput(1) << 5) |
|
||||
(pPass->ColorInput(2) << 10) |
|
||||
(pPass->ColorInput(3) << 15));
|
||||
u32 AlphaInputFlags = ((pPass->AlphaInput(0)) |
|
||||
(pPass->AlphaInput(1) << 5) |
|
||||
(pPass->AlphaInput(2) << 10) |
|
||||
(pPass->AlphaInput(3) << 15));
|
||||
|
||||
u32 ColorOpFlags = 0x100 | (pPass->ColorOutput() << 9);
|
||||
u32 AlphaOpFlags = 0x100 | (pPass->AlphaOutput() << 9);
|
||||
|
||||
Out.WriteLong(ColorInputFlags);
|
||||
Out.WriteLong(AlphaInputFlags);
|
||||
Out.WriteLong(ColorOpFlags);
|
||||
Out.WriteLong(AlphaOpFlags);
|
||||
Out.WriteByte(0); // Padding
|
||||
Out.WriteByte(pPass->KAlphaSel());
|
||||
Out.WriteByte(pPass->KColorSel());
|
||||
Out.WriteByte(pPass->RasSel());
|
||||
}
|
||||
|
||||
// TEV Tex/UV input selection
|
||||
u32 CurTexIdx = 0;
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
Out.WriteShort(0); // Padding
|
||||
|
||||
if (mpMat->Pass(iPass)->Texture())
|
||||
{
|
||||
Out.WriteByte((u8) CurTexIdx);
|
||||
Out.WriteByte((u8) CurTexIdx);
|
||||
CurTexIdx++;
|
||||
}
|
||||
|
||||
else
|
||||
Out.WriteShort((u16) 0xFFFF);
|
||||
}
|
||||
|
||||
// TexGen
|
||||
u32 NumTexCoords = CurTexIdx; // TexIdx is currently equal to the tex coord count
|
||||
Out.WriteLong(NumTexCoords);
|
||||
u32 CurTexMtx = 0;
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
if (pPass->Texture() == nullptr) continue;
|
||||
|
||||
u32 AnimType = pPass->AnimMode();
|
||||
u32 CoordSource = pPass->TexCoordSource();
|
||||
|
||||
u32 TexMtxIdx, PostMtxIdx;
|
||||
bool Normalize;
|
||||
|
||||
// No animation - set TexMtx and PostMtx to identity, disable normalization
|
||||
if (AnimType == eNoUVAnim)
|
||||
{
|
||||
TexMtxIdx = 30;
|
||||
PostMtxIdx = 61;
|
||||
Normalize = false;
|
||||
}
|
||||
|
||||
// Animation - set parameters as the animation mode needs them
|
||||
else
|
||||
{
|
||||
TexMtxIdx = CurTexMtx;
|
||||
|
||||
if ((AnimType < 2) || (AnimType > 5))
|
||||
{
|
||||
PostMtxIdx = CurTexMtx;
|
||||
Normalize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PostMtxIdx = 61;
|
||||
Normalize = false;
|
||||
}
|
||||
CurTexMtx += 3;
|
||||
}
|
||||
|
||||
u32 TexGenFlags = (CoordSource << 4) | (TexMtxIdx << 9) | (Normalize << 14) | (PostMtxIdx << 15);
|
||||
Out.WriteLong(TexGenFlags);
|
||||
}
|
||||
|
||||
// Animations
|
||||
u32 AnimSizeOffset = Out.Tell();
|
||||
u32 NumAnims = CurTexMtx; // CurTexMtx is currently equal to the anim count
|
||||
Out.WriteLong(0); // Anim size filler
|
||||
u32 AnimsStart = Out.Tell();
|
||||
Out.WriteLong(NumAnims);
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
u32 AnimMode = pPass->AnimMode();
|
||||
if (AnimMode == eNoUVAnim) continue;
|
||||
|
||||
Out.WriteLong(AnimMode);
|
||||
|
||||
if ((AnimMode > 1) && (AnimMode != 6))
|
||||
{
|
||||
Out.WriteFloat(pPass->AnimParam(0));
|
||||
Out.WriteFloat(pPass->AnimParam(1));
|
||||
|
||||
if ((AnimMode == 2) || (AnimMode == 4) || (AnimMode == 5))
|
||||
{
|
||||
Out.WriteFloat(pPass->AnimParam(2));
|
||||
Out.WriteFloat(pPass->AnimParam(3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 AnimsEnd = Out.Tell();
|
||||
u32 AnimsSize = AnimsEnd - AnimsStart;
|
||||
Out.Seek(AnimSizeOffset, SEEK_SET);
|
||||
Out.WriteLong(AnimsSize);
|
||||
Out.Seek(AnimsEnd, SEEK_SET);
|
||||
|
||||
// Done!
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMaterialCorruption(COutputStream&)
|
||||
{
|
||||
// Not using parameter 1 (COutputStream& - Out)
|
||||
// todo
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
void CMaterialCooker::WriteCookedMatSet(CMaterialSet *pSet, EGame Version, COutputStream &Out)
|
||||
{
|
||||
CMaterialCooker Cooker;
|
||||
Cooker.mpSet = pSet;
|
||||
Cooker.mVersion = Version;
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Cooker.WriteMatSetPrime(Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteCookedMaterial(CMaterial *pMat, EGame Version, COutputStream &Out)
|
||||
{
|
||||
CMaterialCooker Cooker;
|
||||
Cooker.mpMat = pMat;
|
||||
Cooker.mVersion = Version;
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Cooker.WriteMaterialPrime(Out);
|
||||
break;
|
||||
// TODO: Corruption/Uncooked
|
||||
}
|
||||
}
|
||||
27
src/Core/Resource/Cooker/CMaterialCooker.h
Normal file
27
src/Core/Resource/Cooker/CMaterialCooker.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef CMATERIALCOOKER_H
|
||||
#define CMATERIALCOOKER_H
|
||||
|
||||
#include "../CMaterial.h"
|
||||
#include "../CMaterialSet.h"
|
||||
#include "../EFormatVersion.h"
|
||||
|
||||
class CMaterialCooker
|
||||
{
|
||||
CMaterialSet *mpSet;
|
||||
CMaterial *mpMat;
|
||||
EGame mVersion;
|
||||
std::vector<u32> mTextureIDs;
|
||||
std::vector<u64> mMaterialHashes;
|
||||
|
||||
CMaterialCooker();
|
||||
void WriteMatSetPrime(COutputStream& Out);
|
||||
void WriteMatSetCorruption(COutputStream& Out);
|
||||
void WriteMaterialPrime(COutputStream& Out);
|
||||
void WriteMaterialCorruption(COutputStream& Out);
|
||||
|
||||
public:
|
||||
static void WriteCookedMatSet(CMaterialSet *pSet, EGame Version, COutputStream& Out);
|
||||
static void WriteCookedMaterial(CMaterial *pMat, EGame Version, COutputStream& Out);
|
||||
};
|
||||
|
||||
#endif // CMATERIALCOOKER_H
|
||||
275
src/Core/Resource/Cooker/CModelCooker.cpp
Normal file
275
src/Core/Resource/Cooker/CModelCooker.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include "CModelCooker.h"
|
||||
#include "CMaterialCooker.h"
|
||||
#include "CSectionMgrOut.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
CModelCooker::CModelCooker()
|
||||
{
|
||||
}
|
||||
|
||||
bool SortVertsByArrayPos(const CVertex& A, const CVertex& B) {
|
||||
return (A.ArrayPosition < B.ArrayPosition);
|
||||
}
|
||||
|
||||
bool CheckDuplicateVertsByArrayPos(const CVertex& A, const CVertex& B) {
|
||||
return (A.ArrayPosition == B.ArrayPosition);
|
||||
}
|
||||
|
||||
void CModelCooker::GenerateSurfaceData()
|
||||
{
|
||||
// Need to gather metadata from the model before we can start
|
||||
mNumMatSets = mpModel->mMaterialSets.size();
|
||||
mNumSurfaces = mpModel->mSurfaces.size();
|
||||
mNumVertices = mpModel->mVertexCount;
|
||||
mVertices.resize(mNumVertices);
|
||||
|
||||
// Get vertex attributes
|
||||
mVtxAttribs = eNoAttributes;
|
||||
|
||||
for (u32 iMat = 0; iMat < mpModel->GetMatCount(); iMat++)
|
||||
{
|
||||
CMaterial *pMat = mpModel->GetMaterialByIndex(0, iMat);
|
||||
mVtxAttribs |= pMat->VtxDesc();
|
||||
}
|
||||
|
||||
// Get vertices
|
||||
u32 MaxIndex = 0;
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
{
|
||||
u32 NumPrimitives = mpModel->mSurfaces[iSurf]->Primitives.size();
|
||||
|
||||
for (u32 iPrim = 0; iPrim < NumPrimitives; iPrim++)
|
||||
{
|
||||
SSurface::SPrimitive *pPrim = &mpModel->mSurfaces[iSurf]->Primitives[iPrim];
|
||||
u32 NumVerts = pPrim->Vertices.size();
|
||||
|
||||
for (u32 iVtx = 0; iVtx < NumVerts; iVtx++)
|
||||
{
|
||||
u32 VertIndex = pPrim->Vertices[iVtx].ArrayPosition;
|
||||
mVertices[VertIndex] = pPrim->Vertices[iVtx];
|
||||
|
||||
if (VertIndex > MaxIndex) MaxIndex = VertIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mVertices.resize(MaxIndex + 1);
|
||||
mNumVertices = mVertices.size();
|
||||
}
|
||||
|
||||
void CModelCooker::WriteEditorModel(COutputStream& /*Out*/)
|
||||
{
|
||||
}
|
||||
|
||||
void CModelCooker::WriteModelPrime(COutputStream& Out)
|
||||
{
|
||||
GenerateSurfaceData();
|
||||
|
||||
// Header
|
||||
Out.WriteLong(0xDEADBABE);
|
||||
Out.WriteLong(GetCMDLVersion(mVersion));
|
||||
Out.WriteLong(5);
|
||||
mpModel->mAABox.Write(Out);
|
||||
|
||||
u32 NumSections = mNumMatSets + mNumSurfaces + 6;
|
||||
Out.WriteLong(NumSections);
|
||||
Out.WriteLong(mNumMatSets);
|
||||
|
||||
u32 SectionSizesOffset = Out.Tell();
|
||||
for (u32 iSec = 0; iSec < NumSections; iSec++)
|
||||
Out.WriteLong(0);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
|
||||
std::vector<u32> SectionSizes;
|
||||
SectionSizes.reserve(NumSections);
|
||||
|
||||
CSectionMgrOut SectionMgr;
|
||||
SectionMgr.SetSectionCount(NumSections);
|
||||
SectionMgr.Init(Out);
|
||||
|
||||
// Materials
|
||||
for (u32 iSet = 0; iSet < mNumMatSets; iSet++)
|
||||
{
|
||||
CMaterialCooker::WriteCookedMatSet(mpModel->mMaterialSets[iSet], mVersion, Out);
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
}
|
||||
|
||||
// Vertices
|
||||
for (u32 iPos = 0; iPos < mNumVertices; iPos++)
|
||||
mVertices[iPos].Position.Write(Out);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Normals
|
||||
for (u32 iNrm = 0; iNrm < mNumVertices; iNrm++)
|
||||
mVertices[iNrm].Normal.Write(Out);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Colors
|
||||
for (u32 iColor = 0; iColor < mNumVertices; iColor++)
|
||||
mVertices[iColor].Color[0].Write(Out);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Float UV coordinates
|
||||
for (u32 iTexSlot = 0; iTexSlot < 8; iTexSlot++)
|
||||
{
|
||||
bool HasTexSlot = (mVtxAttribs & (eTex0 << (iTexSlot * 2))) != 0;
|
||||
if (HasTexSlot)
|
||||
{
|
||||
for (u32 iTex = 0; iTex < mNumVertices; iTex++)
|
||||
mVertices[iTex].Tex[iTexSlot].Write(Out);
|
||||
}
|
||||
}
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
SectionMgr.AddSize(Out); // Skipping short UV coordinates
|
||||
|
||||
// Surface offsets
|
||||
Out.WriteLong(mNumSurfaces);
|
||||
u32 SurfaceOffsetsStart = Out.Tell();
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
Out.WriteLong(0);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Surfaces
|
||||
u32 SurfacesStart = Out.Tell();
|
||||
std::vector<u32> SurfaceEndOffsets(mNumSurfaces);
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
{
|
||||
SSurface *pSurface = mpModel->GetSurface(iSurf);
|
||||
|
||||
pSurface->CenterPoint.Write(Out);
|
||||
Out.WriteLong(pSurface->MaterialID);
|
||||
Out.WriteShort((u16) 0x8000);
|
||||
u32 PrimTableSizeOffset = Out.Tell();
|
||||
Out.WriteShort(0);
|
||||
Out.WriteLongLong(0);
|
||||
Out.WriteLong(0);
|
||||
pSurface->ReflectionDirection.Write(Out);
|
||||
Out.WriteToBoundary(32, 0);
|
||||
|
||||
u32 PrimTableStart = Out.Tell();
|
||||
EVertexDescription MatAttribs = mpModel->GetMaterialBySurface(0, iSurf)->VtxDesc();
|
||||
|
||||
for (u32 iPrim = 0; iPrim < pSurface->Primitives.size(); iPrim++)
|
||||
{
|
||||
SSurface::SPrimitive *pPrimitive = &pSurface->Primitives[iPrim];
|
||||
Out.WriteByte((u8) pPrimitive->Type);
|
||||
Out.WriteShort((u16) pPrimitive->Vertices.size());
|
||||
|
||||
for (u32 iVert = 0; iVert < pPrimitive->Vertices.size(); iVert++)
|
||||
{
|
||||
CVertex *pVert = &pPrimitive->Vertices[iVert];
|
||||
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
for (u32 iMtxAttribs = 0; iMtxAttribs < 8; iMtxAttribs++)
|
||||
if (MatAttribs & (ePosMtx << iMtxAttribs))
|
||||
Out.WriteByte(pVert->MatrixIndices[iMtxAttribs]);
|
||||
}
|
||||
|
||||
u16 VertexIndex = (u16) pVert->ArrayPosition;
|
||||
|
||||
if (MatAttribs & ePosition)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
if (MatAttribs & eNormal)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
if (MatAttribs & eColor0)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
if (MatAttribs & eColor1)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
u16 TexOffset = 0;
|
||||
for (u32 iTex = 0; iTex < 8; iTex++)
|
||||
{
|
||||
if (MatAttribs & (eTex0 << (iTex * 2)))
|
||||
{
|
||||
Out.WriteShort(VertexIndex + TexOffset);
|
||||
TexOffset += (u16) mNumVertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
u32 PrimTableEnd = Out.Tell();
|
||||
u32 PrimTableSize = PrimTableEnd - PrimTableStart;
|
||||
Out.Seek(PrimTableSizeOffset, SEEK_SET);
|
||||
Out.WriteShort((u16) PrimTableSize);
|
||||
Out.Seek(PrimTableEnd, SEEK_SET);
|
||||
|
||||
SectionMgr.AddSize(Out);
|
||||
SurfaceEndOffsets[iSurf] = Out.Tell() - SurfacesStart;
|
||||
}
|
||||
|
||||
// Done writing the file - now we go back to fill in surface offsets + section sizes
|
||||
Out.Seek(SurfaceOffsetsStart, SEEK_SET);
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
Out.WriteLong(SurfaceEndOffsets[iSurf]);
|
||||
|
||||
Out.Seek(SectionSizesOffset, SEEK_SET);
|
||||
SectionMgr.WriteSizes(Out);
|
||||
|
||||
// Done!
|
||||
}
|
||||
|
||||
void CModelCooker::WriteCookedModel(CModel *pModel, EGame Version, COutputStream& CMDL)
|
||||
{
|
||||
CModelCooker Cooker;
|
||||
Cooker.mpModel = pModel;
|
||||
Cooker.mVersion = Version;
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Cooker.WriteModelPrime(CMDL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CModelCooker::WriteUncookedModel(CModel* /*pModel*/, COutputStream& /*EMDL*/)
|
||||
{
|
||||
}
|
||||
|
||||
u32 CModelCooker::GetCMDLVersion(EGame Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
return 0x2;
|
||||
case eEchoesDemo:
|
||||
return 0x3;
|
||||
case eEchoes:
|
||||
return 0x4;
|
||||
case eCorruptionProto:
|
||||
case eCorruption:
|
||||
return 0x5;
|
||||
case eReturns:
|
||||
return 0xA;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
30
src/Core/Resource/Cooker/CModelCooker.h
Normal file
30
src/Core/Resource/Cooker/CModelCooker.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef CMODELCOOKER_H
|
||||
#define CMODELCOOKER_H
|
||||
|
||||
#include "../model/CModel.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <FileIO/FileIO.h>
|
||||
|
||||
class CModelCooker
|
||||
{
|
||||
TResPtr<CModel> mpModel;
|
||||
EGame mVersion;
|
||||
u32 mNumMatSets;
|
||||
u32 mNumSurfaces;
|
||||
u32 mNumVertices;
|
||||
u8 mVertexFormat;
|
||||
std::vector<CVertex> mVertices;
|
||||
EVertexDescription mVtxAttribs;
|
||||
|
||||
CModelCooker();
|
||||
void GenerateSurfaceData();
|
||||
void WriteEditorModel(COutputStream& Out);
|
||||
void WriteModelPrime(COutputStream& Out);
|
||||
|
||||
public:
|
||||
static void WriteCookedModel(CModel *pModel, EGame Version, COutputStream& Out);
|
||||
static void WriteUncookedModel(CModel *pModel, COutputStream& Out);
|
||||
static u32 GetCMDLVersion(EGame Version);
|
||||
};
|
||||
|
||||
#endif // CMODELCOOKER_H
|
||||
33
src/Core/Resource/Cooker/CSectionMgrOut.cpp
Normal file
33
src/Core/Resource/Cooker/CSectionMgrOut.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "CSectionMgrOut.h"
|
||||
|
||||
CSectionMgrOut::CSectionMgrOut()
|
||||
{
|
||||
mSectionCount = 0;
|
||||
mCurSectionStart = 0;
|
||||
mCurSectionIndex = 0;
|
||||
}
|
||||
|
||||
void CSectionMgrOut::SetSectionCount(u32 Count)
|
||||
{
|
||||
mSectionCount = Count;
|
||||
mSectionSizes.resize(Count);
|
||||
}
|
||||
|
||||
void CSectionMgrOut::Init(const COutputStream& OutputStream)
|
||||
{
|
||||
mCurSectionStart = OutputStream.Tell();
|
||||
mCurSectionIndex = 0;
|
||||
}
|
||||
|
||||
void CSectionMgrOut::AddSize(COutputStream& OutputStream)
|
||||
{
|
||||
mSectionSizes[mCurSectionIndex] = OutputStream.Tell() - mCurSectionStart;
|
||||
mCurSectionIndex++;
|
||||
mCurSectionStart = OutputStream.Tell();
|
||||
}
|
||||
|
||||
void CSectionMgrOut::WriteSizes(COutputStream& OutputStream)
|
||||
{
|
||||
for (u32 iSec = 0; iSec < mSectionCount; iSec++)
|
||||
OutputStream.WriteLong(mSectionSizes[iSec]);
|
||||
}
|
||||
24
src/Core/Resource/Cooker/CSectionMgrOut.h
Normal file
24
src/Core/Resource/Cooker/CSectionMgrOut.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CBLOCKMGROUT_H
|
||||
#define CBLOCKMGROUT_H
|
||||
|
||||
#include <FileIO/COutputStream.h>
|
||||
#include <Common/types.h>
|
||||
#include <vector>
|
||||
|
||||
// Small class to manage file sections for CMDL/MREA output
|
||||
class CSectionMgrOut
|
||||
{
|
||||
u32 mSectionCount;
|
||||
u32 mCurSectionStart;
|
||||
u32 mCurSectionIndex;
|
||||
std::vector<u32> mSectionSizes;
|
||||
|
||||
public:
|
||||
CSectionMgrOut();
|
||||
void SetSectionCount(u32 Count);
|
||||
void Init(const COutputStream& OutputStream);
|
||||
void AddSize(COutputStream& OutputStream);
|
||||
void WriteSizes(COutputStream& OutputStream);
|
||||
};
|
||||
|
||||
#endif // CBLOCKMGROUT_H
|
||||
580
src/Core/Resource/Cooker/CTemplateWriter.cpp
Normal file
580
src/Core/Resource/Cooker/CTemplateWriter.cpp
Normal file
@@ -0,0 +1,580 @@
|
||||
#include "CTemplateWriter.h"
|
||||
#include "../cooker/CWorldCooker.h"
|
||||
#include <tinyxml2.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
using namespace tinyxml2;
|
||||
|
||||
CTemplateWriter::CTemplateWriter()
|
||||
{
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveAllTemplates()
|
||||
{
|
||||
// Create directory
|
||||
std::list<CMasterTemplate*> masterList = CMasterTemplate::GetMasterList();
|
||||
TString out = "../templates/";
|
||||
boost::filesystem::create_directory(out.ToStdString());
|
||||
|
||||
// Resave master templates
|
||||
for (auto it = masterList.begin(); it != masterList.end(); it++)
|
||||
SaveGameTemplates(*it, out);
|
||||
|
||||
// Resave game list
|
||||
XMLDocument gameList;
|
||||
|
||||
XMLDeclaration *pDecl = gameList.NewDeclaration();
|
||||
gameList.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = gameList.NewElement("GameList");
|
||||
pBase->SetAttribute("version", 3);
|
||||
gameList.LinkEndChild(pBase);
|
||||
|
||||
for (auto it = masterList.begin(); it != masterList.end(); it++)
|
||||
{
|
||||
CMasterTemplate *pMaster = *it;
|
||||
|
||||
XMLElement *pGame = gameList.NewElement("game");
|
||||
|
||||
XMLElement *pGameName = gameList.NewElement("name");
|
||||
pGameName->SetText(*pMaster->mGameName);
|
||||
|
||||
XMLElement *pWorldVersion = gameList.NewElement("mlvl");
|
||||
u32 versionNumber = CWorldCooker::GetMLVLVersion(pMaster->GetGame());
|
||||
pWorldVersion->SetText(*TString::HexString(versionNumber, true, true, 2));
|
||||
|
||||
XMLElement *pTempPath = gameList.NewElement("master");
|
||||
pTempPath->SetText(*pMaster->mSourceFile);
|
||||
|
||||
pGame->LinkEndChild(pGameName);
|
||||
pGame->LinkEndChild(pWorldVersion);
|
||||
pGame->LinkEndChild(pTempPath);
|
||||
pBase->LinkEndChild(pGame);
|
||||
}
|
||||
|
||||
gameList.SaveFile(*(out + "GameList.xml"));
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveGameTemplates(CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pMaster->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
boost::filesystem::create_directory(outDir.ToStdString());
|
||||
|
||||
// Resave script templates
|
||||
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
|
||||
SaveScriptTemplate(it->second, outDir);
|
||||
|
||||
// Resave master template
|
||||
XMLDocument master;
|
||||
|
||||
XMLDeclaration *pDecl = master.NewDeclaration();
|
||||
master.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = master.NewElement("MasterTemplate");
|
||||
pBase->SetAttribute("version", 3);
|
||||
master.LinkEndChild(pBase);
|
||||
|
||||
// Write property list
|
||||
if (!pMaster->mPropertyList.empty())
|
||||
{
|
||||
SavePropertyList(pMaster, outDir);
|
||||
|
||||
XMLElement *pPropList = master.NewElement("properties");
|
||||
pPropList->SetText("Properties.xml");
|
||||
pBase->LinkEndChild(pPropList);
|
||||
}
|
||||
|
||||
// Write script objects
|
||||
XMLElement *pObjects = master.NewElement("objects");
|
||||
pBase->LinkEndChild(pObjects);
|
||||
|
||||
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
|
||||
{
|
||||
TString objID;
|
||||
u32 intID = (it->second)->ObjectID();
|
||||
if (intID <= 0xFF) objID = TString::HexString(intID, true, true, 2);
|
||||
else objID = CFourCC(intID).ToString();
|
||||
|
||||
XMLElement *pObj = master.NewElement("object");
|
||||
pObj->SetAttribute("ID", *objID);
|
||||
pObj->SetAttribute("template", *(it->second)->mSourceFile);
|
||||
pObjects->LinkEndChild(pObj);
|
||||
}
|
||||
|
||||
// Write script states/messages
|
||||
std::map<u32, TString> *pMaps[2] = { &pMaster->mStates, &pMaster->mMessages };
|
||||
TString types[2] = { "state", "message" };
|
||||
|
||||
for (u32 iScr = 0; iScr < 2; iScr++)
|
||||
{
|
||||
XMLElement *pElem = master.NewElement(*(types[iScr] + "s"));
|
||||
pBase->LinkEndChild(pElem);
|
||||
|
||||
for (auto it = pMaps[iScr]->begin(); it != pMaps[iScr]->end(); it++)
|
||||
{
|
||||
TString ID;
|
||||
if (it->first <= 0xFF) ID = TString::HexString(it->first, true, true, 2);
|
||||
else ID = CFourCC(it->first).ToString();
|
||||
|
||||
XMLElement *pSubElem = master.NewElement(*types[iScr]);
|
||||
pSubElem->SetAttribute("ID", *ID);
|
||||
pSubElem->SetAttribute("name", *(it->second));
|
||||
pElem->LinkEndChild(pSubElem);
|
||||
}
|
||||
}
|
||||
|
||||
// Save file
|
||||
master.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SavePropertyList(CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
// Create XML
|
||||
XMLDocument list;
|
||||
|
||||
XMLDeclaration *pDecl = list.NewDeclaration();
|
||||
list.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = list.NewElement("Properties");
|
||||
pBase->SetAttribute("version", 3);
|
||||
list.LinkEndChild(pBase);
|
||||
|
||||
// Write properties
|
||||
for (auto it = pMaster->mPropertyList.begin(); it != pMaster->mPropertyList.end(); it++)
|
||||
{
|
||||
CPropertyTemplate *pTemp = it->second;
|
||||
|
||||
if (pTemp->Type() == eStructProperty)
|
||||
{
|
||||
CStructTemplate *pStructTemp = static_cast<CStructTemplate*>(pTemp);
|
||||
|
||||
XMLElement *pElem = list.NewElement("struct");
|
||||
pElem->SetAttribute("ID", *TString::HexString(pTemp->PropertyID(), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->Name());
|
||||
|
||||
if (!pStructTemp->mSourceFile.IsEmpty())
|
||||
{
|
||||
SaveStructTemplate(pStructTemp, pMaster, dir);
|
||||
pElem->SetAttribute("template", *pStructTemp->mSourceFile);
|
||||
}
|
||||
|
||||
pBase->LinkEndChild(pElem);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
XMLElement *pElem = list.NewElement("property");
|
||||
pElem->SetAttribute("ID", *TString::HexString(pTemp->PropertyID(), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->Name());
|
||||
pElem->SetAttribute("type", *PropEnumToPropString(pTemp->Type()));
|
||||
|
||||
if (pTemp->Type() == eFileProperty)
|
||||
{
|
||||
// Construct extension list string
|
||||
CFileTemplate *pFileProp = static_cast<CFileTemplate*>(pTemp);
|
||||
const TStringList& extensions = pFileProp->Extensions();
|
||||
|
||||
TString strList = "";
|
||||
|
||||
for (auto it = extensions.begin(); it != extensions.end();)
|
||||
{
|
||||
strList += *it;
|
||||
it++;
|
||||
if (it != extensions.end()) strList += ",";
|
||||
}
|
||||
|
||||
pElem->SetAttribute("ext", *strList);
|
||||
}
|
||||
|
||||
pBase->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
|
||||
list.SaveFile(*(dir + "Properties.xml"));
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveScriptTemplate(CScriptTemplate *pTemp, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
boost::filesystem::create_directory(*outDir);
|
||||
|
||||
// Create new document
|
||||
XMLDocument scriptXML;
|
||||
|
||||
XMLDeclaration *pDecl = scriptXML.NewDeclaration();
|
||||
scriptXML.LinkEndChild(pDecl);
|
||||
|
||||
// Base element
|
||||
XMLElement *pBase = scriptXML.NewElement("ScriptTemplate");
|
||||
pBase->SetAttribute("version", 3.0f);
|
||||
scriptXML.LinkEndChild(pBase);
|
||||
|
||||
// Write object name
|
||||
XMLElement *pName = scriptXML.NewElement("name");
|
||||
pName->SetText(*pTemp->TemplateName());
|
||||
pBase->LinkEndChild(pName);
|
||||
|
||||
// Write properties
|
||||
for (auto it = pTemp->mPropertySets.begin(); it != pTemp->mPropertySets.end(); it++)
|
||||
{
|
||||
XMLElement *pProperties = scriptXML.NewElement("properties");
|
||||
pProperties->SetAttribute("version", *it->SetName);
|
||||
SaveProperties(&scriptXML, pProperties, it->pBaseStruct, pTemp->MasterTemplate(), dir);
|
||||
pBase->LinkEndChild(pProperties);
|
||||
}
|
||||
|
||||
// Write editor properties
|
||||
XMLElement *pEditor = scriptXML.NewElement("editor");
|
||||
pBase->LinkEndChild(pEditor);
|
||||
|
||||
// Editor Properties
|
||||
XMLElement *pEditorProperties = scriptXML.NewElement("properties");
|
||||
pEditor->LinkEndChild(pEditorProperties);
|
||||
|
||||
TString propNames[6] = {
|
||||
"InstanceName", "Position", "Rotation",
|
||||
"Scale", "Active", "LightParameters"
|
||||
};
|
||||
|
||||
TIDString *pPropStrings[6] = {
|
||||
&pTemp->mNameIDString, &pTemp->mPositionIDString, &pTemp->mRotationIDString,
|
||||
&pTemp->mScaleIDString, &pTemp->mActiveIDString, &pTemp->mLightParametersIDString
|
||||
};
|
||||
|
||||
for (u32 iProp = 0; iProp < 6; iProp++)
|
||||
{
|
||||
if (!pPropStrings[iProp]->IsEmpty())
|
||||
{
|
||||
XMLElement *pProperty = scriptXML.NewElement("property");
|
||||
pProperty->SetAttribute("name", *propNames[iProp]);
|
||||
pProperty->SetAttribute("ID", **pPropStrings[iProp]);
|
||||
pEditorProperties->LinkEndChild(pProperty);
|
||||
}
|
||||
}
|
||||
|
||||
// Editor Assets
|
||||
XMLElement *pAssets = scriptXML.NewElement("assets");
|
||||
pEditor->LinkEndChild(pAssets);
|
||||
|
||||
for (auto it = pTemp->mAssets.begin(); it != pTemp->mAssets.end(); it++)
|
||||
{
|
||||
TString source = (it->AssetSource == CScriptTemplate::SEditorAsset::eFile ? "file" : "property");
|
||||
TString type;
|
||||
|
||||
switch (it->AssetType)
|
||||
{
|
||||
case CScriptTemplate::SEditorAsset::eModel: type = "model"; break;
|
||||
case CScriptTemplate::SEditorAsset::eAnimParams: type = "animparams"; break;
|
||||
case CScriptTemplate::SEditorAsset::eBillboard: type = "billboard"; break;
|
||||
case CScriptTemplate::SEditorAsset::eCollision: type = "collision"; break;
|
||||
}
|
||||
|
||||
s32 force = -1;
|
||||
if (it->AssetSource == CScriptTemplate::SEditorAsset::eAnimParams) force = it->ForceNodeIndex;
|
||||
|
||||
XMLElement *pAsset = scriptXML.NewElement(*type);
|
||||
pAsset->SetAttribute("source", *source);
|
||||
if (force >= 0) pAsset->SetAttribute("force", std::to_string(force).c_str());
|
||||
pAsset->SetText(*it->AssetLocation);
|
||||
pAssets->LinkEndChild(pAsset);
|
||||
}
|
||||
|
||||
// Preview Scale
|
||||
if (pTemp->mPreviewScale != 1.f)
|
||||
{
|
||||
XMLElement *pPreviewScale = scriptXML.NewElement("preview_scale");
|
||||
pEditor->LinkEndChild(pPreviewScale);
|
||||
pPreviewScale->SetText(pTemp->mPreviewScale);
|
||||
}
|
||||
|
||||
// Rot/Scale Type
|
||||
XMLElement *pRotType = scriptXML.NewElement("rotation_type");
|
||||
pEditor->LinkEndChild(pRotType);
|
||||
pRotType->SetText(pTemp->mRotationType == CScriptTemplate::eRotationEnabled ? "enabled" : "disabled");
|
||||
|
||||
XMLElement *pScaleType = scriptXML.NewElement("scale_type");
|
||||
pEditor->LinkEndChild(pScaleType);
|
||||
|
||||
if (pTemp->mScaleType != CScriptTemplate::eScaleVolume)
|
||||
pScaleType->SetText(pTemp->mScaleType == CScriptTemplate::eScaleEnabled ? "enabled" : "disabled");
|
||||
|
||||
else
|
||||
{
|
||||
pScaleType->SetText("volume");
|
||||
|
||||
// Volume Preview
|
||||
XMLElement *pVolume = scriptXML.NewElement("preview_volume");
|
||||
pEditor->LinkEndChild(pVolume);
|
||||
|
||||
// Enum -> String conversion lambda to avoid redundant code
|
||||
auto GetVolumeString = [](EVolumeShape shape) -> TString
|
||||
{
|
||||
switch (shape)
|
||||
{
|
||||
case eBoxShape: return "Box";
|
||||
case eAxisAlignedBoxShape: return "AxisAlignedBox";
|
||||
case eEllipsoidShape: return "Ellipsoid";
|
||||
case eCylinderShape: return "Cylinder";
|
||||
case eCylinderLargeShape: return "CylinderLarge";
|
||||
case eConditionalShape: return "Conditional";
|
||||
default: return "INVALID";
|
||||
}
|
||||
};
|
||||
|
||||
pVolume->SetAttribute("shape", *GetVolumeString(pTemp->mVolumeShape));
|
||||
|
||||
if (pTemp->mVolumeShape == eConditionalShape)
|
||||
{
|
||||
pVolume->SetAttribute("propertyID", *pTemp->mVolumeConditionIDString);
|
||||
|
||||
// Find conditional test property
|
||||
CPropertyTemplate *pProp;
|
||||
|
||||
for (auto it = pTemp->mPropertySets.begin(); it != pTemp->mPropertySets.end(); it++)
|
||||
{
|
||||
pProp = it->pBaseStruct->PropertyByIDString(pTemp->mVolumeConditionIDString);
|
||||
if (pProp) break;
|
||||
}
|
||||
|
||||
// Write conditions
|
||||
for (auto it = pTemp->mVolumeConditions.begin(); it != pTemp->mVolumeConditions.end(); it++)
|
||||
{
|
||||
// Value should be an integer, or a boolean condition?
|
||||
TString strVal;
|
||||
|
||||
if (pProp->Type() == eBoolProperty)
|
||||
strVal = (it->Value == 1 ? "true" : "false");
|
||||
else
|
||||
strVal = TString::HexString((u32) it->Value, true, true, (it->Value > 0xFF ? 8 : 2));
|
||||
|
||||
XMLElement *pCondition = scriptXML.NewElement("condition");
|
||||
pCondition->SetAttribute("value", *strVal);
|
||||
pCondition->SetAttribute("shape", *GetVolumeString(it->Shape));
|
||||
pVolume->LinkEndChild(pCondition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write to file
|
||||
scriptXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
TString name = pTemp->mSourceFile.GetFileName();
|
||||
boost::filesystem::create_directory(outDir.ToStdString());
|
||||
|
||||
// Create new document and write struct properties to it
|
||||
XMLDocument structXML;
|
||||
|
||||
XMLDeclaration *pDecl = structXML.NewDeclaration();
|
||||
structXML.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = structXML.NewElement("struct");
|
||||
pBase->SetAttribute("name", *name);
|
||||
pBase->SetAttribute("type", (pTemp->IsSingleProperty() ? "single" : "multi"));
|
||||
SaveProperties(&structXML, pBase, pTemp, pMaster, dir);
|
||||
structXML.LinkEndChild(pBase);
|
||||
|
||||
structXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveEnumTemplate(CEnumTemplate *pTemp, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
TString name = pTemp->mSourceFile.GetFileName(false);
|
||||
boost::filesystem::create_directory(*outDir);
|
||||
|
||||
// Create new document and write enumerators to it
|
||||
XMLDocument enumXML;
|
||||
|
||||
XMLDeclaration *pDecl = enumXML.NewDeclaration();
|
||||
enumXML.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = enumXML.NewElement("enum");
|
||||
pBase->SetAttribute("name", *name);
|
||||
SaveEnumerators(&enumXML, pBase, pTemp);
|
||||
enumXML.LinkEndChild(pBase);
|
||||
|
||||
enumXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveBitfieldTemplate(CBitfieldTemplate *pTemp, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
TString name = pTemp->mSourceFile.GetFileName();
|
||||
boost::filesystem::create_directory(*outDir);
|
||||
|
||||
// Create new document and write enumerators to it
|
||||
XMLDocument bitfieldXML;
|
||||
|
||||
XMLDeclaration *pDecl = bitfieldXML.NewDeclaration();
|
||||
bitfieldXML.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = bitfieldXML.NewElement("bitfield");
|
||||
pBase->SetAttribute("name", *name);
|
||||
SaveBitFlags(&bitfieldXML, pBase, pTemp);
|
||||
bitfieldXML.LinkEndChild(pBase);
|
||||
|
||||
bitfieldXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
for (u32 iProp = 0; iProp < pTemp->Count(); iProp++)
|
||||
{
|
||||
CPropertyTemplate *pProp = pTemp->PropertyByIndex(iProp);
|
||||
u32 propID = (pProp->PropertyID() == 0xFFFFFFFF ? iProp : pProp->PropertyID());
|
||||
TString strID = TString::HexString(propID, true, true, (propID > 0xFF ? 8 : 2));
|
||||
|
||||
if (pProp->Type() == eStructProperty)
|
||||
{
|
||||
CStructTemplate *pStructTemp = static_cast<CStructTemplate*>(pProp);
|
||||
bool isExternal = (!pStructTemp->mSourceFile.IsEmpty());
|
||||
|
||||
XMLElement *pElem = pDoc->NewElement("struct");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1) || pTemp->IsSingleProperty())
|
||||
{
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
}
|
||||
|
||||
if (!isExternal) {
|
||||
TString type = pStructTemp->IsSingleProperty() ? "single" : "multi";
|
||||
pElem->SetAttribute("type", *type);
|
||||
}
|
||||
|
||||
// Only save properties if this is a multi struct, or if there is no master property list
|
||||
if (!pMaster->HasPropertyList() || !pStructTemp->IsSingleProperty())
|
||||
{
|
||||
// Embed struct or save to external XML?
|
||||
if (!pStructTemp->mSourceFile.IsEmpty())
|
||||
{
|
||||
SaveStructTemplate(pStructTemp, pMaster, dir);
|
||||
pElem->SetAttribute("template", *pStructTemp->mSourceFile);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SaveProperties(pDoc, pElem, pStructTemp, pMaster, dir);
|
||||
}
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
|
||||
else if (pProp->Type() == eEnumProperty)
|
||||
{
|
||||
CEnumTemplate *pEnumTemp = static_cast<CEnumTemplate*>(pProp);
|
||||
bool isExternal = (!pEnumTemp->mSourceFile.IsEmpty());
|
||||
|
||||
XMLElement *pElem = pDoc->NewElement("enum");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1))
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
|
||||
if (isExternal)
|
||||
{
|
||||
SaveEnumTemplate(pEnumTemp, dir);
|
||||
pElem->SetAttribute("template", *pEnumTemp->mSourceFile);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SaveEnumerators(pDoc, pElem, pEnumTemp);
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
else if (pProp->Type() == eBitfieldProperty)
|
||||
{
|
||||
CBitfieldTemplate *pBitfieldTemp = static_cast<CBitfieldTemplate*>(pProp);
|
||||
bool isExternal = (!pBitfieldTemp->mSourceFile.IsEmpty());
|
||||
|
||||
XMLElement *pElem = pDoc->NewElement("bitfield");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1))
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
|
||||
if (isExternal)
|
||||
{
|
||||
SaveBitfieldTemplate(pBitfieldTemp, dir);
|
||||
pElem->SetAttribute("template", *pBitfieldTemp->mSourceFile);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SaveBitFlags(pDoc, pElem, pBitfieldTemp);
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
else
|
||||
{
|
||||
XMLElement *pElem = pDoc->NewElement("property");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1) || pTemp->IsSingleProperty())
|
||||
{
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
pElem->SetAttribute("type", *PropEnumToPropString(pProp->Type()));
|
||||
|
||||
if (pProp->Type() == eFileProperty)
|
||||
{
|
||||
// Construct extension list string
|
||||
CFileTemplate *pFileProp = static_cast<CFileTemplate*>(pProp);
|
||||
const TStringList& extensions = pFileProp->Extensions();
|
||||
|
||||
TString strList = "";
|
||||
|
||||
for (auto it = extensions.begin(); it != extensions.end();)
|
||||
{
|
||||
strList += *it;
|
||||
it++;
|
||||
if (it != extensions.end()) strList += ",";
|
||||
}
|
||||
|
||||
pElem->SetAttribute("ext", *strList);
|
||||
}
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveEnumerators(XMLDocument *pDoc, XMLElement *pParent, CEnumTemplate *pTemp)
|
||||
{
|
||||
for (u32 iEnum = 0; iEnum < pTemp->NumEnumerators(); iEnum++)
|
||||
{
|
||||
XMLElement *pElem = pDoc->NewElement("enumerator");
|
||||
pElem->SetAttribute("ID", *TString::HexString(pTemp->EnumeratorID(iEnum), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->EnumeratorName(iEnum));
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveBitFlags(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CBitfieldTemplate *pTemp)
|
||||
{
|
||||
for (u32 iFlag = 0; iFlag < pTemp->NumFlags(); iFlag++)
|
||||
{
|
||||
XMLElement *pElem = pDoc->NewElement("bitflag");
|
||||
pElem->SetAttribute("mask", *TString::HexString(pTemp->FlagMask(iFlag), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->FlagName(iFlag));
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
24
src/Core/Resource/Cooker/CTemplateWriter.h
Normal file
24
src/Core/Resource/Cooker/CTemplateWriter.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CTEMPLATEWRITER_H
|
||||
#define CTEMPLATEWRITER_H
|
||||
|
||||
#include "../script/CMasterTemplate.h"
|
||||
#include "../script/CScriptTemplate.h"
|
||||
|
||||
class CTemplateWriter
|
||||
{
|
||||
CTemplateWriter();
|
||||
|
||||
public:
|
||||
static void SaveAllTemplates();
|
||||
static void SaveGameTemplates(CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SavePropertyList(CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SaveScriptTemplate(CScriptTemplate *pTemp, const TString& dir);
|
||||
static void SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SaveEnumTemplate(CEnumTemplate *pTemp, const TString& dir);
|
||||
static void SaveBitfieldTemplate(CBitfieldTemplate *pTemp, const TString& dir);
|
||||
static void SaveProperties(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SaveEnumerators(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CEnumTemplate *pTemp);
|
||||
static void SaveBitFlags(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CBitfieldTemplate *pTemp);
|
||||
};
|
||||
|
||||
#endif // CTEMPLATEWRITER_H
|
||||
108
src/Core/Resource/Cooker/CTextureEncoder.cpp
Normal file
108
src/Core/Resource/Cooker/CTextureEncoder.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "CTextureEncoder.h"
|
||||
#include <iostream>
|
||||
|
||||
CTextureEncoder::CTextureEncoder()
|
||||
{
|
||||
mpTexture = nullptr;
|
||||
}
|
||||
|
||||
void CTextureEncoder::WriteTXTR(COutputStream& TXTR)
|
||||
{
|
||||
// Only DXT1->CMPR supported at the moment
|
||||
TXTR.WriteLong(mOutputFormat);
|
||||
TXTR.WriteShort(mpTexture->mWidth);
|
||||
TXTR.WriteShort(mpTexture->mHeight);
|
||||
TXTR.WriteLong(mpTexture->mNumMipMaps);
|
||||
|
||||
u32 MipW = mpTexture->Width() / 4;
|
||||
u32 MipH = mpTexture->Height() / 4;
|
||||
CMemoryInStream Image(mpTexture->mImgDataBuffer, mpTexture->mImgDataSize, IOUtil::LittleEndian);
|
||||
u32 MipOffset = Image.Tell();
|
||||
|
||||
for (u32 iMip = 0; iMip < mpTexture->mNumMipMaps; iMip++)
|
||||
{
|
||||
for (u32 BlockY = 0; BlockY < MipH; BlockY += 2)
|
||||
for (u32 BlockX = 0; BlockX < MipW; BlockX += 2)
|
||||
for (u32 ImgY = BlockY; ImgY < BlockY + 2; ImgY++)
|
||||
for (u32 ImgX = BlockX; ImgX < BlockX + 2; ImgX++)
|
||||
{
|
||||
u32 SrcPos = ((ImgY * MipW) + ImgX) * 8;
|
||||
Image.Seek(MipOffset + SrcPos, SEEK_SET);
|
||||
|
||||
ReadSubBlockCMPR(Image, TXTR);
|
||||
}
|
||||
|
||||
MipOffset += MipW * MipH * 8;
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
if (MipW < 2) MipW = 2;
|
||||
if (MipH < 2) MipH = 2;
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureEncoder::DetermineBestOutputFormat()
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
void CTextureEncoder::ReadSubBlockCMPR(CInputStream& Source, COutputStream& Dest)
|
||||
{
|
||||
Dest.WriteShort(Source.ReadShort());
|
||||
Dest.WriteShort(Source.ReadShort());
|
||||
|
||||
for (u32 byte = 0; byte < 4; byte++) {
|
||||
u8 b = Source.ReadByte();
|
||||
b = ((b & 0x3) << 6) | ((b & 0xC) << 2) | ((b & 0x30) >> 2) | ((b & 0xC0) >> 6);
|
||||
Dest.WriteByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
void CTextureEncoder::EncodeTXTR(COutputStream& TXTR, CTexture *pTex)
|
||||
{
|
||||
if (pTex->mTexelFormat != eDXT1)
|
||||
{
|
||||
std::cout << "\rError: Unsupported texel format for decoding\n";
|
||||
return;
|
||||
}
|
||||
|
||||
CTextureEncoder Encoder;
|
||||
Encoder.mpTexture = pTex;
|
||||
Encoder.mSourceFormat = eDXT1;
|
||||
Encoder.mOutputFormat = eGX_CMPR;
|
||||
Encoder.WriteTXTR(TXTR);
|
||||
}
|
||||
|
||||
void CTextureEncoder::EncodeTXTR(COutputStream& TXTR, CTexture *pTex, ETexelFormat /*OutputFormat*/)
|
||||
{
|
||||
// todo: support for encoding a specific format
|
||||
EncodeTXTR(TXTR, pTex);
|
||||
}
|
||||
|
||||
ETexelFormat CTextureEncoder::GetGXFormat(ETexelFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case eLuminance: return eGX_I8;
|
||||
case eLuminanceAlpha: return eGX_IA8;
|
||||
case eRGBA4: return eGX_RGB5A3;
|
||||
case eRGB565: return eGX_RGB565;
|
||||
case eRGBA8: return eGX_RGBA8;
|
||||
case eDXT1: return eGX_CMPR;
|
||||
default: return eInvalidTexelFormat;
|
||||
}
|
||||
}
|
||||
|
||||
ETexelFormat CTextureEncoder::GetFormat(ETexelFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case eGX_I4: return eLuminance;
|
||||
case eGX_I8: return eLuminance;
|
||||
case eGX_IA4: return eLuminanceAlpha;
|
||||
case eGX_IA8: return eLuminanceAlpha;
|
||||
// todo rest of these
|
||||
case eGX_CMPR: return eDXT1;
|
||||
default: return eInvalidTexelFormat;
|
||||
}
|
||||
}
|
||||
27
src/Core/Resource/Cooker/CTextureEncoder.h
Normal file
27
src/Core/Resource/Cooker/CTextureEncoder.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef CTEXTUREENCODER_H
|
||||
#define CTEXTUREENCODER_H
|
||||
|
||||
#include "../CTexture.h"
|
||||
#include <Core/TResPtr.h>
|
||||
|
||||
// Class contains basic functionality right now - only supports directly converting DXT1 to CMPR
|
||||
// More advanced functions (including actual encoding!) coming later
|
||||
class CTextureEncoder
|
||||
{
|
||||
TResPtr<CTexture> mpTexture;
|
||||
ETexelFormat mSourceFormat;
|
||||
ETexelFormat mOutputFormat;
|
||||
|
||||
CTextureEncoder();
|
||||
void WriteTXTR(COutputStream& TXTR);
|
||||
void DetermineBestOutputFormat();
|
||||
void ReadSubBlockCMPR(CInputStream& Source, COutputStream& Dest);
|
||||
|
||||
public:
|
||||
static void EncodeTXTR(COutputStream& TXTR, CTexture *pTex);
|
||||
static void EncodeTXTR(COutputStream& TXTR, CTexture *pTex, ETexelFormat OutputFormat);
|
||||
static ETexelFormat GetGXFormat(ETexelFormat Format);
|
||||
static ETexelFormat GetFormat(ETexelFormat Format);
|
||||
};
|
||||
|
||||
#endif // CTEXTUREENCODER_H
|
||||
19
src/Core/Resource/Cooker/CWorldCooker.cpp
Normal file
19
src/Core/Resource/Cooker/CWorldCooker.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "CWorldCooker.h"
|
||||
|
||||
CWorldCooker::CWorldCooker()
|
||||
{
|
||||
}
|
||||
|
||||
u32 CWorldCooker::GetMLVLVersion(EGame version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case ePrimeDemo: return 0xD;
|
||||
case ePrime: return 0x11;
|
||||
case eEchoesDemo: return 0x14;
|
||||
case eEchoes: return 0x17;
|
||||
case eCorruption: return 0x19;
|
||||
case eReturns: return 0x1B;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
14
src/Core/Resource/Cooker/CWorldCooker.h
Normal file
14
src/Core/Resource/Cooker/CWorldCooker.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef CWORLDCOOKER_H
|
||||
#define CWORLDCOOKER_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include "../EFormatVersion.h"
|
||||
|
||||
class CWorldCooker
|
||||
{
|
||||
CWorldCooker();
|
||||
public:
|
||||
static u32 GetMLVLVersion(EGame version);
|
||||
};
|
||||
|
||||
#endif // CWORLDCOOKER_H
|
||||
Reference in New Issue
Block a user