mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-20 10:25:40 +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:
31
src/Core/Resource/CAnimSet.cpp
Normal file
31
src/Core/Resource/CAnimSet.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "CAnimSet.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
CAnimSet::CAnimSet() : CResource()
|
||||
{
|
||||
}
|
||||
|
||||
CAnimSet::~CAnimSet()
|
||||
{
|
||||
}
|
||||
|
||||
u32 CAnimSet::getNodeCount()
|
||||
{
|
||||
return nodes.size();
|
||||
}
|
||||
|
||||
TString CAnimSet::getNodeName(u32 node)
|
||||
{
|
||||
if (node >= nodes.size())
|
||||
return nodes[0].name;
|
||||
else
|
||||
return nodes[node].name;
|
||||
}
|
||||
|
||||
CModel* CAnimSet::getNodeModel(u32 node)
|
||||
{
|
||||
if (node >= nodes.size())
|
||||
return nodes[0].model;
|
||||
else
|
||||
return nodes[node].model;
|
||||
}
|
||||
36
src/Core/Resource/CAnimSet.h
Normal file
36
src/Core/Resource/CAnimSet.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef CANIMSET_H
|
||||
#define CANIMSET_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <vector>
|
||||
#include "model/CModel.h"
|
||||
#include "CResource.h"
|
||||
|
||||
// will expand later! this is where animation support will come in
|
||||
class CAnimSet : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eAnimSet)
|
||||
friend class CAnimSetLoader;
|
||||
|
||||
struct SNode
|
||||
{
|
||||
TString name;
|
||||
TResPtr<CModel> model;
|
||||
u32 skinID;
|
||||
u32 skelID;
|
||||
|
||||
SNode() { model = nullptr; }
|
||||
};
|
||||
std::vector<SNode> nodes;
|
||||
|
||||
public:
|
||||
CAnimSet();
|
||||
~CAnimSet();
|
||||
|
||||
u32 getNodeCount();
|
||||
TString getNodeName(u32 node);
|
||||
CModel* getNodeModel(u32 node);
|
||||
};
|
||||
|
||||
#endif // CCHARACTERSET_H
|
||||
138
src/Core/Resource/CAnimationParameters.cpp
Normal file
138
src/Core/Resource/CAnimationParameters.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "CAnimationParameters.h"
|
||||
#include "CAnimSet.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
#include <iostream>
|
||||
|
||||
CAnimationParameters::CAnimationParameters()
|
||||
{
|
||||
mGame = ePrime;
|
||||
mpCharSet = nullptr;
|
||||
mNodeIndex = 0;
|
||||
mUnknown1 = 0;
|
||||
mUnknown2 = 0;
|
||||
mUnknown3 = 0;
|
||||
mUnknown4 = 0;
|
||||
}
|
||||
|
||||
CAnimationParameters::CAnimationParameters(CInputStream& SCLY, EGame game)
|
||||
{
|
||||
mGame = game;
|
||||
mpCharSet = nullptr;
|
||||
mNodeIndex = 0;
|
||||
mUnknown1 = 0;
|
||||
mUnknown2 = 0;
|
||||
mUnknown3 = 0;
|
||||
mUnknown4 = 0;
|
||||
|
||||
if (game <= eEchoes)
|
||||
{
|
||||
u32 animSetID = SCLY.ReadLong();
|
||||
mNodeIndex = SCLY.ReadLong();
|
||||
mUnknown1 = SCLY.ReadLong();
|
||||
|
||||
mpCharSet = gResCache.GetResource(animSetID, "ANCS");
|
||||
}
|
||||
|
||||
else if (game <= eCorruption)
|
||||
{
|
||||
u64 charID = SCLY.ReadLongLong();
|
||||
mUnknown1 = SCLY.ReadLong();
|
||||
|
||||
mpCharSet = gResCache.GetResource(charID, "CHAR");
|
||||
}
|
||||
|
||||
else if (game == eReturns)
|
||||
{
|
||||
SCLY.Seek(-6, SEEK_CUR);
|
||||
u32 offset = SCLY.Tell();
|
||||
u32 propID = SCLY.ReadLong();
|
||||
SCLY.Seek(2, SEEK_CUR);
|
||||
|
||||
mUnknown1 = (u32) SCLY.ReadByte();
|
||||
mUnknown1 &= 0xFF;
|
||||
|
||||
if (mUnknown1 == 0x60)
|
||||
{
|
||||
u64 charID = SCLY.ReadLongLong();
|
||||
mUnknown2 = SCLY.ReadLong();
|
||||
mUnknown3 = SCLY.ReadLong();
|
||||
mUnknown4 = SCLY.ReadLong();
|
||||
|
||||
mpCharSet = gResCache.GetResource(charID, "CHAR");
|
||||
}
|
||||
|
||||
else if (mUnknown1 != 0x80)
|
||||
{
|
||||
Log::FileError(SCLY.GetSourceString(), offset,
|
||||
"Unexpected AnimationParameters byte: " + TString::HexString(mUnknown1, true, true, 2) + " (property " + TString::HexString(propID, true, true, 8) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CModel* CAnimationParameters::GetCurrentModel(s32 nodeIndex)
|
||||
{
|
||||
if (!mpCharSet) return nullptr;
|
||||
if (mpCharSet->Type() != eAnimSet) return nullptr;
|
||||
if (nodeIndex == -1) nodeIndex = mNodeIndex;
|
||||
|
||||
CAnimSet *pSet = static_cast<CAnimSet*>(mpCharSet.RawPointer());
|
||||
if (pSet->getNodeCount() <= (u32) nodeIndex) return nullptr;
|
||||
return pSet->getNodeModel(nodeIndex);
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
EGame CAnimationParameters::Version()
|
||||
{
|
||||
return mGame;
|
||||
}
|
||||
|
||||
CResource* CAnimationParameters::Resource()
|
||||
{
|
||||
return mpCharSet;
|
||||
}
|
||||
|
||||
u32 CAnimationParameters::CharacterIndex()
|
||||
{
|
||||
return mNodeIndex;
|
||||
}
|
||||
|
||||
u32 CAnimationParameters::Unknown(u32 index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return mUnknown1;
|
||||
case 1: return mUnknown2;
|
||||
case 2: return mUnknown3;
|
||||
case 3: return mUnknown4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CAnimationParameters::SetResource(CResource *pRes)
|
||||
{
|
||||
if ((pRes->Type() == eAnimSet) || (pRes->Type() == eCharacter))
|
||||
{
|
||||
mpCharSet = pRes;
|
||||
mNodeIndex = 0;
|
||||
}
|
||||
else
|
||||
Log::Error("Resource with invalid type passed to CAnimationParameters: " + pRes->Source());
|
||||
}
|
||||
|
||||
void CAnimationParameters::SetNodeIndex(u32 index)
|
||||
{
|
||||
mNodeIndex = index;
|
||||
}
|
||||
|
||||
void CAnimationParameters::SetUnknown(u32 index, u32 value)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: mUnknown1 = value;
|
||||
case 1: mUnknown2 = value;
|
||||
case 2: mUnknown3 = value;
|
||||
case 3: mUnknown4 = value;
|
||||
}
|
||||
}
|
||||
37
src/Core/Resource/CAnimationParameters.h
Normal file
37
src/Core/Resource/CAnimationParameters.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef CANIMATIONPARAMETERS_H
|
||||
#define CANIMATIONPARAMETERS_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "model/CModel.h"
|
||||
#include <Core/TResPtr.h>
|
||||
#include "EFormatVersion.h"
|
||||
|
||||
class CAnimationParameters
|
||||
{
|
||||
EGame mGame;
|
||||
TResPtr<CResource> mpCharSet;
|
||||
|
||||
u32 mNodeIndex;
|
||||
u32 mUnknown1;
|
||||
u32 mUnknown2;
|
||||
u32 mUnknown3;
|
||||
u32 mUnknown4;
|
||||
|
||||
public:
|
||||
CAnimationParameters();
|
||||
CAnimationParameters(CInputStream& SCLY, EGame game);
|
||||
CModel* GetCurrentModel(s32 nodeIndex = -1);
|
||||
|
||||
// Getters
|
||||
EGame Version();
|
||||
CResource* Resource();
|
||||
u32 CharacterIndex();
|
||||
u32 Unknown(u32 index);
|
||||
|
||||
// Setters
|
||||
void SetResource(CResource *pRes);
|
||||
void SetNodeIndex(u32 index);
|
||||
void SetUnknown(u32 index, u32 value);
|
||||
};
|
||||
|
||||
#endif // CANIMATIONPARAMETERS_H
|
||||
114
src/Core/Resource/CCollisionMesh.cpp
Normal file
114
src/Core/Resource/CCollisionMesh.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "CCollisionMesh.h"
|
||||
#include <Core/CRenderer.h>
|
||||
|
||||
CCollisionMesh::CCollisionMesh()
|
||||
{
|
||||
mVBO.SetVertexDesc(ePosition);
|
||||
mVertexCount = 0;
|
||||
mLineCount = 0;
|
||||
mFaceCount = 0;
|
||||
mBuffered = false;
|
||||
mIBO.SetPrimitiveType(GL_TRIANGLES);
|
||||
}
|
||||
|
||||
CCollisionMesh::~CCollisionMesh()
|
||||
{
|
||||
if (mBuffered)
|
||||
{
|
||||
mIBO.Clear();
|
||||
mVBO.Clear();
|
||||
mBuffered = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CCollisionMesh::BufferGL()
|
||||
{
|
||||
if (mBuffered)
|
||||
{
|
||||
mIBO.Clear();
|
||||
mVBO.Clear();
|
||||
mBuffered = false;
|
||||
}
|
||||
|
||||
// Add all the verts to our VBO, first...
|
||||
mVBO.Reserve(mCollisionVertices.size());
|
||||
for (u16 v = 0; v < mCollisionVertices.size(); v++)
|
||||
mVBO.AddVertex(CVertex(mCollisionVertices[v].Pos));
|
||||
|
||||
// Then add all the relevant indices to the IBO
|
||||
mIBO.Reserve(mCollisionFaces.size() * 3);
|
||||
for (u32 v = 0; v < mCollisionFaces.size(); v++)
|
||||
{
|
||||
u16 Verts[3];
|
||||
|
||||
CCollisionFace *Face = &mCollisionFaces[v];
|
||||
CCollisionLine *LineA = GetLine(Face->Lines[0]);
|
||||
CCollisionLine *LineB = GetLine(Face->Lines[1]);
|
||||
Verts[0] = LineA->Vertices[0];
|
||||
Verts[1] = LineA->Vertices[1];
|
||||
|
||||
// We have two vertex indices; the last one is one of the ones on line B, but we're not sure which one
|
||||
if ((LineB->Vertices[0] != Verts[0]) &&
|
||||
(LineB->Vertices[0] != Verts[1]))
|
||||
Verts[2] = LineB->Vertices[0];
|
||||
else
|
||||
Verts[2] = LineB->Vertices[1];
|
||||
|
||||
// Some faces have a property that indicates they need to be inverted
|
||||
if (!Face->Properties.Invert)
|
||||
mIBO.AddIndices(&Verts[0], 3);
|
||||
else {
|
||||
mIBO.AddIndex(Verts[2]);
|
||||
mIBO.AddIndex(Verts[1]);
|
||||
mIBO.AddIndex(Verts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer, and done
|
||||
mVBO.Buffer();
|
||||
mIBO.Buffer();
|
||||
mBuffered = true;
|
||||
}
|
||||
|
||||
void CCollisionMesh::Draw()
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
mVBO.Bind();
|
||||
mIBO.Bind();
|
||||
|
||||
glDrawElements(GL_TRIANGLES, mIBO.GetSize(), GL_UNSIGNED_SHORT, (void*) 0);
|
||||
gDrawCount++;
|
||||
mIBO.Unbind();
|
||||
mVBO.Unbind();
|
||||
}
|
||||
|
||||
void CCollisionMesh::DrawWireframe()
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
mVBO.Bind();
|
||||
mIBO.Bind();
|
||||
for (u32 f = 0; f < mFaceCount; f++)
|
||||
{
|
||||
glDrawElements(GL_LINE_LOOP, 3, GL_UNSIGNED_SHORT, (void*) (f * 6));
|
||||
gDrawCount++;
|
||||
}
|
||||
mIBO.Unbind();
|
||||
mVBO.Unbind();
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionVertex* CCollisionMesh::GetVertex(u16 index)
|
||||
{
|
||||
return &mCollisionVertices[index];
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionLine* CCollisionMesh::GetLine(u16 index)
|
||||
{
|
||||
return &mCollisionLines[index];
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionFace* CCollisionMesh::GetFace(u16 index)
|
||||
{
|
||||
return &mCollisionFaces[index];
|
||||
}
|
||||
88
src/Core/Resource/CCollisionMesh.h
Normal file
88
src/Core/Resource/CCollisionMesh.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef CCOLLISIONMESH_H
|
||||
#define CCOLLISIONMESH_H
|
||||
|
||||
#include <Common/CAABox.h>
|
||||
#include <OpenGL/CVertexBuffer.h>
|
||||
#include <OpenGL/CIndexBuffer.h>
|
||||
#include "CResource.h"
|
||||
|
||||
class CCollisionMesh
|
||||
{
|
||||
friend class CCollisionLoader;
|
||||
|
||||
class CCollisionOctree
|
||||
{
|
||||
friend class CCollisionLoader;
|
||||
struct SOctreeNode {};
|
||||
|
||||
struct SLeaf : public SOctreeNode
|
||||
{
|
||||
CAABox AABox;
|
||||
std::vector<u16> FaceIndices;
|
||||
};
|
||||
|
||||
struct SBranch : public SOctreeNode
|
||||
{
|
||||
u16 Flags;
|
||||
SOctreeNode *pChildren[8];
|
||||
};
|
||||
|
||||
SOctreeNode* Root;
|
||||
};
|
||||
|
||||
struct SCollisionProperties
|
||||
{
|
||||
// todo: figure out what the other properties are
|
||||
bool Invert;
|
||||
};
|
||||
|
||||
class CCollisionVertex
|
||||
{
|
||||
public:
|
||||
SCollisionProperties Properties;
|
||||
CVector3f Pos;
|
||||
};
|
||||
|
||||
class CCollisionLine
|
||||
{
|
||||
public:
|
||||
SCollisionProperties Properties;
|
||||
u16 Vertices[2];
|
||||
};
|
||||
|
||||
class CCollisionFace
|
||||
{
|
||||
public:
|
||||
SCollisionProperties Properties;
|
||||
u16 Lines[3];
|
||||
};
|
||||
|
||||
CVertexBuffer mVBO;
|
||||
CIndexBuffer mIBO;
|
||||
u32 mVertexCount;
|
||||
u32 mLineCount;
|
||||
u32 mFaceCount;
|
||||
bool mBuffered;
|
||||
|
||||
CAABox mAABox;
|
||||
CCollisionOctree *mOctree;
|
||||
std::vector<u32> mFlags;
|
||||
std::vector<CCollisionVertex> mCollisionVertices;
|
||||
std::vector<CCollisionLine> mCollisionLines;
|
||||
std::vector<CCollisionFace> mCollisionFaces;
|
||||
bool mOctreeLoaded;
|
||||
|
||||
CCollisionVertex *GetVertex(u16 index);
|
||||
CCollisionLine *GetLine(u16 index);
|
||||
CCollisionFace *GetFace(u16 index);
|
||||
|
||||
public:
|
||||
CCollisionMesh();
|
||||
~CCollisionMesh();
|
||||
|
||||
void BufferGL();
|
||||
void Draw();
|
||||
void DrawWireframe();
|
||||
};
|
||||
|
||||
#endif // CCOLLISIONMESH_H
|
||||
38
src/Core/Resource/CCollisionMeshGroup.cpp
Normal file
38
src/Core/Resource/CCollisionMeshGroup.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "CCollisionMeshGroup.h"
|
||||
|
||||
CCollisionMeshGroup::CCollisionMeshGroup()
|
||||
{
|
||||
}
|
||||
|
||||
CCollisionMeshGroup::~CCollisionMeshGroup()
|
||||
{
|
||||
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
u32 CCollisionMeshGroup::NumMeshes()
|
||||
{
|
||||
return mMeshes.size();
|
||||
}
|
||||
|
||||
CCollisionMesh* CCollisionMeshGroup::MeshByIndex(u32 index)
|
||||
{
|
||||
return mMeshes[index];
|
||||
}
|
||||
|
||||
void CCollisionMeshGroup::AddMesh(CCollisionMesh *pMesh)
|
||||
{
|
||||
mMeshes.push_back(pMesh);
|
||||
}
|
||||
|
||||
void CCollisionMeshGroup::Draw()
|
||||
{
|
||||
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
|
||||
(*it)->Draw();
|
||||
}
|
||||
|
||||
void CCollisionMeshGroup::DrawWireframe()
|
||||
{
|
||||
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
|
||||
(*it)->DrawWireframe();
|
||||
}
|
||||
25
src/Core/Resource/CCollisionMeshGroup.h
Normal file
25
src/Core/Resource/CCollisionMeshGroup.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef CCOLLISIONMESHGROUP_H
|
||||
#define CCOLLISIONMESHGROUP_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CCollisionMesh.h"
|
||||
#include <Core/TResPtr.h>
|
||||
#include <vector>
|
||||
|
||||
class CCollisionMeshGroup : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eCollisionMeshGroup)
|
||||
std::vector<CCollisionMesh*> mMeshes;
|
||||
|
||||
public:
|
||||
CCollisionMeshGroup();
|
||||
~CCollisionMeshGroup();
|
||||
|
||||
u32 NumMeshes();
|
||||
CCollisionMesh* MeshByIndex(u32 index);
|
||||
void AddMesh(CCollisionMesh *pMesh);
|
||||
void Draw();
|
||||
void DrawWireframe();
|
||||
};
|
||||
|
||||
#endif // CCOLLISIONMESHGROUP_H
|
||||
175
src/Core/Resource/CFont.cpp
Normal file
175
src/Core/Resource/CFont.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "CFont.h"
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <Common/AnimUtil.h>
|
||||
|
||||
CDynamicVertexBuffer CFont::smGlyphVertices;
|
||||
CIndexBuffer CFont::smGlyphIndices;
|
||||
bool CFont::smBuffersInitialized = false;
|
||||
|
||||
CFont::CFont() : CResource()
|
||||
{
|
||||
}
|
||||
|
||||
CFont::~CFont()
|
||||
{
|
||||
}
|
||||
|
||||
inline float PtsToFloat(s32 pt)
|
||||
{
|
||||
// This is a bit of an arbitrary number but it works
|
||||
// 1 / (1280 / 1.333333f / 2)
|
||||
return 0.00208333f * pt;
|
||||
}
|
||||
|
||||
CVector2f CFont::RenderString(const TString& String, CRenderer* /*pRenderer*/, float /*AspectRatio*/,
|
||||
CVector2f /*Position*/, CColor FillColor, CColor StrokeColor, u32 FontSize)
|
||||
{
|
||||
// WIP
|
||||
if (!smBuffersInitialized) InitBuffers();
|
||||
|
||||
// Shader setup
|
||||
CShader *pTextShader = CDrawUtil::GetTextShader();
|
||||
pTextShader->SetCurrent();
|
||||
|
||||
GLuint ModelMtxLoc = pTextShader->GetUniformLocation("ModelMtx");
|
||||
GLuint ColorLoc = pTextShader->GetUniformLocation("FontColor");
|
||||
GLuint LayerLoc = pTextShader->GetUniformLocation("RGBALayer");
|
||||
CVector4f FillColor4f = FillColor.ToVector4f();
|
||||
CVector4f StrokeColor4f = StrokeColor.ToVector4f();
|
||||
mpFontTexture->Bind(0);
|
||||
smGlyphVertices.Bind();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
// Initialize some more stuff before we start the character loop
|
||||
CVector2f PrintHead(-1.f, 1.f);
|
||||
CTransform4f PtScale = CTransform4f::ScaleMatrix(PtsToFloat(1));
|
||||
SGlyph *pPrevGlyph = nullptr;
|
||||
|
||||
float Scale;
|
||||
if (FontSize == CFONT_DEFAULT_SIZE) Scale = 1.f;
|
||||
else Scale = (float) FontSize / (mDefaultSize != 0 ? mDefaultSize : 18);
|
||||
|
||||
for (u32 iChar = 0; iChar < String.Length(); iChar++)
|
||||
{
|
||||
// Get character, check for newline
|
||||
char Char = String[iChar];
|
||||
|
||||
if (Char == '\n')
|
||||
{
|
||||
pPrevGlyph = nullptr;
|
||||
PrintHead.x = -1;
|
||||
PrintHead.y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get glyph
|
||||
auto iGlyph = mGlyphs.find(Char);
|
||||
if (iGlyph == mGlyphs.end()) continue;
|
||||
SGlyph *pGlyph = &iGlyph->second;
|
||||
|
||||
// Apply left padding and kerning
|
||||
PrintHead.x += PtsToFloat(pGlyph->LeftPadding) * Scale;
|
||||
|
||||
if (pPrevGlyph)
|
||||
{
|
||||
if (pPrevGlyph->KerningIndex != -1)
|
||||
{
|
||||
for (u32 iKern = pPrevGlyph->KerningIndex; iKern < mKerningTable.size(); iKern++)
|
||||
{
|
||||
if (mKerningTable[iKern].CharacterA != pPrevGlyph->Character) break;
|
||||
if (mKerningTable[iKern].CharacterB == String[iChar])
|
||||
{
|
||||
PrintHead.x += PtsToFloat(mKerningTable[iKern].Adjust) * Scale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a newline if this character goes over the right edge of the screen
|
||||
if (PrintHead.x + ((PtsToFloat(pGlyph->PrintAdvance) + PtsToFloat(pGlyph->RightPadding)) * Scale) > 1)
|
||||
{
|
||||
PrintHead.x = -1;
|
||||
PrintHead.y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
|
||||
|
||||
if (Char == ' ') continue;
|
||||
}
|
||||
|
||||
float XTrans = PrintHead.x;
|
||||
float YTrans = PrintHead.y + ((PtsToFloat(pGlyph->BaseOffset * 2) - PtsToFloat(mVerticalOffset * 2)) * Scale);
|
||||
|
||||
CTransform4f GlyphTransform = PtScale;
|
||||
GlyphTransform.Scale(CVector3f((float) pGlyph->Width / 2, (float) pGlyph->Height, 1.f));
|
||||
GlyphTransform.Scale(Scale);
|
||||
GlyphTransform.Translate(CVector3f(XTrans, YTrans, 0.f));
|
||||
CMatrix4f Glyph4f = GlyphTransform.ToMatrix4f();
|
||||
|
||||
// Get glyph layer
|
||||
u8 GlyphLayer = pGlyph->RGBAChannel;
|
||||
if (mTextureFormat == 3) GlyphLayer *= 2;
|
||||
else if (mTextureFormat == 8) GlyphLayer = 3;
|
||||
|
||||
// Load shader uniforms, buffer texture
|
||||
glUniformMatrix4fv(ModelMtxLoc, 1, GL_FALSE, (GLfloat*) &Glyph4f);
|
||||
smGlyphVertices.BufferAttrib(eTex0, &pGlyph->TexCoords);
|
||||
|
||||
// Draw fill
|
||||
glUniform1i(LayerLoc, GlyphLayer);
|
||||
glUniform4fv(ColorLoc, 1, &FillColor4f.x);
|
||||
smGlyphIndices.DrawElements();
|
||||
|
||||
// Draw stroke
|
||||
if ((mTextureFormat == 1) || (mTextureFormat == 3) || (mTextureFormat == 8))
|
||||
{
|
||||
u8 StrokeLayer;
|
||||
if (mTextureFormat == 1) StrokeLayer = 1;
|
||||
else if (mTextureFormat == 3) StrokeLayer = GlyphLayer + 1;
|
||||
else if (mTextureFormat == 8) StrokeLayer = GlyphLayer - 2;
|
||||
|
||||
glUniform1i(LayerLoc, StrokeLayer);
|
||||
glUniform4fv(ColorLoc, 1, &StrokeColor4f.x);
|
||||
smGlyphIndices.DrawElements();
|
||||
}
|
||||
|
||||
// Update print head
|
||||
PrintHead.x += PtsToFloat(pGlyph->PrintAdvance) * Scale;
|
||||
PrintHead.x += PtsToFloat(pGlyph->RightPadding) * Scale;
|
||||
pPrevGlyph = pGlyph;
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
return PrintHead;
|
||||
}
|
||||
|
||||
void CFont::InitBuffers()
|
||||
{
|
||||
smGlyphVertices.SetActiveAttribs(ePosition | eTex0);
|
||||
smGlyphVertices.SetVertexCount(4);
|
||||
|
||||
CVector3f Vertices[4] = {
|
||||
CVector3f( 0.f, 0.f, 0.f),
|
||||
CVector3f( 2.f, 0.f, 0.f),
|
||||
CVector3f( 0.f, -2.f, 0.f),
|
||||
CVector3f( 2.f, -2.f, 0.f)
|
||||
};
|
||||
smGlyphVertices.BufferAttrib(ePosition, Vertices);
|
||||
|
||||
CVector2f TexCoords[4] = {
|
||||
CVector2f(0.f, 0.f),
|
||||
CVector2f(1.f, 0.f),
|
||||
CVector2f(0.f, 1.f),
|
||||
CVector2f(1.f, 1.f)
|
||||
};
|
||||
smGlyphVertices.BufferAttrib(eTex0, TexCoords);
|
||||
|
||||
smGlyphIndices.Reserve(4);
|
||||
smGlyphIndices.AddIndex(0);
|
||||
smGlyphIndices.AddIndex(2);
|
||||
smGlyphIndices.AddIndex(1);
|
||||
smGlyphIndices.AddIndex(3);
|
||||
smGlyphIndices.SetPrimitiveType(GL_TRIANGLE_STRIP);
|
||||
|
||||
smBuffersInitialized = true;
|
||||
}
|
||||
72
src/Core/Resource/CFont.h
Normal file
72
src/Core/Resource/CFont.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef CFONT_H
|
||||
#define CFONT_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CTexture.h"
|
||||
#include "model/CVertex.h"
|
||||
#include <Common/types.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <OpenGL/CDynamicVertexBuffer.h>
|
||||
#include <OpenGL/CIndexBuffer.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#define CFONT_DEFAULT_SIZE -1
|
||||
|
||||
class CRenderer;
|
||||
|
||||
class CFont : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eFont)
|
||||
friend class CFontLoader;
|
||||
static CDynamicVertexBuffer smGlyphVertices; // This is the vertex buffer used to draw glyphs. It has two attributes - Pos and Tex0. Tex0 should be updated for each glyph.
|
||||
static CIndexBuffer smGlyphIndices; // This is the index buffer used to draw glyphs. It uses a triangle strip.
|
||||
static bool smBuffersInitialized; // This bool indicates whether the vertex/index buffer have been initialized. Checked at the start of RenderString().
|
||||
|
||||
u32 mUnknown; // Value at offset 0x8. Not sure what this is. Including for experimentation purposes.
|
||||
u32 mLineHeight; // Height of each line, in points
|
||||
u32 mLineMargin; // Gap between lines, in points - this is added to the line height
|
||||
u32 mVerticalOffset; // In points. This is used to reposition glyphs after the per-glyph vertical offset is applied
|
||||
u32 mDefaultSize; // In points.
|
||||
TString mFontName; // Self-explanatory
|
||||
TResPtr<CTexture> mpFontTexture; // The texture used by this font
|
||||
u32 mTextureFormat; // Indicates which layers on the texture are for what - multiple glyph layers or fill/stroke
|
||||
|
||||
struct SGlyph
|
||||
{
|
||||
u16 Character; // The UTF-16 character that this glyph corresponds to
|
||||
CVector2f TexCoords[4]; // The format only lists the min/max X/Y values; tracking absolute coordinates in memory is faster
|
||||
s32 LeftPadding; // The amount of padding applied left of this glyph, in points
|
||||
s32 RightPadding; // The amount of padding applied right of this glyph, in points
|
||||
u32 Width; // The width of the glyph, in points
|
||||
u32 Height; // The height of the glyph, in points
|
||||
u32 PrintAdvance; // How far the print head advances horizontally after printing this glyph, in points
|
||||
u32 BaseOffset; // Vertical offset for this glyph, in points; the font-wide offset is added to this
|
||||
u32 KerningIndex; // Index into the kerning table of the first kerning pair for this glyph. -1 if no pairs.
|
||||
u8 RGBAChannel; // Fonts can store multiple glyphs in the same space on different RGBA channels. This value corresponds to R, G, B, or A.
|
||||
};
|
||||
std::unordered_map<u16, SGlyph> mGlyphs;
|
||||
|
||||
struct SKerningPair
|
||||
{
|
||||
u16 CharacterA; // Left character
|
||||
u16 CharacterB; // Right character
|
||||
s32 Adjust; // The horizontal offset to apply to CharacterB if this pair is encountered, in points
|
||||
};
|
||||
std::vector<SKerningPair> mKerningTable; // The kerning table should be laid out in alphabetical order for the indices to work properly
|
||||
|
||||
|
||||
public:
|
||||
CFont();
|
||||
~CFont();
|
||||
CResource* MakeCopy(CResCache *pCopyCache);
|
||||
CVector2f RenderString(const TString& String, CRenderer *pRenderer, float AspectRatio,
|
||||
CVector2f Position = CVector2f(0,0),
|
||||
CColor FillColor = CColor::skWhite, CColor StrokeColor = CColor::skBlack,
|
||||
u32 FontSize = CFONT_DEFAULT_SIZE);
|
||||
private:
|
||||
void InitBuffers();
|
||||
};
|
||||
|
||||
#endif // CFONT_H
|
||||
180
src/Core/Resource/CGameArea.cpp
Normal file
180
src/Core/Resource/CGameArea.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#include "CGameArea.h"
|
||||
#include "script/CScriptLayer.h"
|
||||
#include <Core/CRenderer.h>
|
||||
|
||||
CGameArea::CGameArea() : CResource()
|
||||
{
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
mTerrainMerged = false;
|
||||
mMaterialSet = nullptr;
|
||||
mpGeneratorLayer = nullptr;
|
||||
mCollision = nullptr;
|
||||
}
|
||||
|
||||
CGameArea::~CGameArea()
|
||||
{
|
||||
ClearTerrain();
|
||||
|
||||
delete mCollision;
|
||||
delete mpGeneratorLayer;
|
||||
|
||||
for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++)
|
||||
delete mScriptLayers[iSCLY];
|
||||
|
||||
for (u32 lyr = 0; lyr < mLightLayers.size(); lyr++)
|
||||
for (u32 lit = 0; lit < mLightLayers[lyr].size(); lit++)
|
||||
delete mLightLayers[lyr][lit];
|
||||
}
|
||||
|
||||
void CGameArea::AddWorldModel(CModel *mdl)
|
||||
{
|
||||
mTerrainModels.push_back(mdl);
|
||||
mVertexCount += mdl->GetVertexCount();
|
||||
mTriangleCount += mdl->GetTriangleCount();
|
||||
mAABox.ExpandBounds(mdl->AABox());
|
||||
}
|
||||
|
||||
void CGameArea::MergeTerrain()
|
||||
{
|
||||
if (mTerrainMerged) return;
|
||||
|
||||
// Nothing really complicated here - iterate through every terrain submesh, add each to a static model
|
||||
for (u32 iMdl = 0; iMdl < mTerrainModels.size(); iMdl++)
|
||||
{
|
||||
CModel *pMdl = mTerrainModels[iMdl];
|
||||
u32 SubmeshCount = pMdl->GetSurfaceCount();
|
||||
|
||||
for (u32 iSurf = 0; iSurf < SubmeshCount; iSurf++)
|
||||
{
|
||||
SSurface *pSurf = pMdl->GetSurface(iSurf);
|
||||
CMaterial *pMat = mMaterialSet->MaterialByIndex(pSurf->MaterialID);
|
||||
|
||||
bool newMat = true;
|
||||
for (std::vector<CStaticModel*>::iterator it = mStaticTerrainModels.begin(); it != mStaticTerrainModels.end(); it++)
|
||||
{
|
||||
if ((*it)->GetMaterial() == pMat)
|
||||
{
|
||||
// When we append a new submesh to an existing static model, we bump it to the back of the vector.
|
||||
// This is because mesh ordering actually matters sometimes
|
||||
// (particularly with multi-layered transparent meshes)
|
||||
// so we need to at least try to maintain it.
|
||||
// This is maybe not the most efficient way to do this, but it works.
|
||||
CStaticModel *pStatic = *it;
|
||||
pStatic->AddSurface(pSurf);
|
||||
mStaticTerrainModels.erase(it);
|
||||
mStaticTerrainModels.push_back(pStatic);
|
||||
newMat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newMat)
|
||||
{
|
||||
CStaticModel *pStatic = new CStaticModel(pMat);
|
||||
pStatic->AddSurface(pSurf);
|
||||
mStaticTerrainModels.push_back(pStatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameArea::ClearTerrain()
|
||||
{
|
||||
for (u32 t = 0; t < mTerrainModels.size(); t++)
|
||||
delete mTerrainModels[t];
|
||||
mTerrainModels.clear();
|
||||
|
||||
for (u32 s = 0; s < mStaticTerrainModels.size(); s++)
|
||||
delete mStaticTerrainModels[s];
|
||||
mStaticTerrainModels.clear();
|
||||
|
||||
if (mMaterialSet) delete mMaterialSet;
|
||||
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
mTerrainMerged = false;
|
||||
mAABox = CAABox::skInfinite;
|
||||
}
|
||||
|
||||
void CGameArea::ClearScriptLayers()
|
||||
{
|
||||
for (auto it = mScriptLayers.begin(); it != mScriptLayers.end(); it++)
|
||||
delete *it;
|
||||
mScriptLayers.clear();
|
||||
delete mpGeneratorLayer;
|
||||
mpGeneratorLayer = nullptr;
|
||||
}
|
||||
|
||||
CTransform4f CGameArea::GetTransform()
|
||||
{
|
||||
return mTransform;
|
||||
}
|
||||
|
||||
u32 CGameArea::GetTerrainModelCount()
|
||||
{
|
||||
return mTerrainModels.size();
|
||||
}
|
||||
|
||||
u32 CGameArea::GetStaticModelCount()
|
||||
{
|
||||
return mStaticTerrainModels.size();
|
||||
}
|
||||
|
||||
CModel* CGameArea::GetTerrainModel(u32 mdl)
|
||||
{
|
||||
return mTerrainModels[mdl];
|
||||
}
|
||||
|
||||
CStaticModel* CGameArea::GetStaticModel(u32 mdl)
|
||||
{
|
||||
return mStaticTerrainModels[mdl];
|
||||
}
|
||||
|
||||
CCollisionMeshGroup* CGameArea::GetCollision()
|
||||
{
|
||||
return mCollision;
|
||||
}
|
||||
|
||||
u32 CGameArea::GetScriptLayerCount()
|
||||
{
|
||||
return mScriptLayers.size();
|
||||
}
|
||||
|
||||
CScriptLayer* CGameArea::GetScriptLayer(u32 index)
|
||||
{
|
||||
return mScriptLayers[index];
|
||||
}
|
||||
|
||||
CScriptLayer* CGameArea::GetGeneratorLayer()
|
||||
{
|
||||
return mpGeneratorLayer;
|
||||
}
|
||||
|
||||
CScriptObject* CGameArea::GetInstanceByID(u32 InstanceID)
|
||||
{
|
||||
auto it = mObjectMap.find(InstanceID);
|
||||
if (it != mObjectMap.end()) return it->second;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
u32 CGameArea::GetLightLayerCount()
|
||||
{
|
||||
return mLightLayers.size();
|
||||
}
|
||||
|
||||
u32 CGameArea::GetLightCount(u32 layer)
|
||||
{
|
||||
if (mLightLayers.empty()) return 0;
|
||||
return mLightLayers[layer].size();
|
||||
}
|
||||
|
||||
CLight* CGameArea::GetLight(u32 layer, u32 light)
|
||||
{
|
||||
return mLightLayers[layer][light];
|
||||
}
|
||||
|
||||
CAABox CGameArea::AABox()
|
||||
{
|
||||
return mAABox;
|
||||
}
|
||||
68
src/Core/Resource/CGameArea.h
Normal file
68
src/Core/Resource/CGameArea.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef CGAMEAREA_H
|
||||
#define CGAMEAREA_H
|
||||
|
||||
#include "CCollisionMeshGroup.h"
|
||||
#include "CLight.h"
|
||||
#include "CMaterialSet.h"
|
||||
#include "model/CModel.h"
|
||||
#include "model/CStaticModel.h"
|
||||
#include "CResource.h"
|
||||
#include <Common/types.h>
|
||||
#include <Common/CTransform4f.h>
|
||||
#include <unordered_map>
|
||||
|
||||
class CScriptLayer;
|
||||
class CScriptObject;
|
||||
|
||||
class CGameArea : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eArea)
|
||||
friend class CAreaLoader;
|
||||
|
||||
u32 mVertexCount;
|
||||
u32 mTriangleCount;
|
||||
bool mTerrainMerged;
|
||||
CTransform4f mTransform;
|
||||
CAABox mAABox;
|
||||
|
||||
// Geometry
|
||||
CMaterialSet *mMaterialSet;
|
||||
std::vector<CModel*> mTerrainModels; // TerrainModels is the original version of each model; this is used by the editor (bounding box checks, material editing, etc)
|
||||
std::vector<CStaticModel*> mStaticTerrainModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
|
||||
// Script
|
||||
std::vector<CScriptLayer*> mScriptLayers;
|
||||
CScriptLayer *mpGeneratorLayer;
|
||||
std::unordered_map<u32, CScriptObject*> mObjectMap;
|
||||
|
||||
// Collision
|
||||
CCollisionMeshGroup *mCollision;
|
||||
// Lights
|
||||
std::vector<std::vector<CLight*>> mLightLayers;
|
||||
|
||||
public:
|
||||
CGameArea();
|
||||
~CGameArea();
|
||||
|
||||
void AddWorldModel(CModel *mdl);
|
||||
void MergeTerrain();
|
||||
void ClearTerrain();
|
||||
void ClearScriptLayers();
|
||||
|
||||
// Getters
|
||||
CTransform4f GetTransform();
|
||||
u32 GetTerrainModelCount();
|
||||
u32 GetStaticModelCount();
|
||||
CModel* GetTerrainModel(u32 mdl);
|
||||
CStaticModel* GetStaticModel(u32 mdl);
|
||||
CCollisionMeshGroup* GetCollision();
|
||||
u32 GetScriptLayerCount();
|
||||
CScriptLayer* GetScriptLayer(u32 index);
|
||||
CScriptLayer* GetGeneratorLayer();
|
||||
CScriptObject* GetInstanceByID(u32 InstanceID);
|
||||
u32 GetLightLayerCount();
|
||||
u32 GetLightCount(u32 layer);
|
||||
CLight* GetLight(u32 layer, u32 light);
|
||||
CAABox AABox();
|
||||
};
|
||||
|
||||
#endif // CGAMEAREA_H
|
||||
283
src/Core/Resource/CLight.cpp
Normal file
283
src/Core/Resource/CLight.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "CLight.h"
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
#include <Core/CGraphics.h>
|
||||
|
||||
#define CLIGHT_NO_RADIUS 0x40
|
||||
#define CLIGHT_NO_INTENSITY 0x80
|
||||
|
||||
CLight::CLight()
|
||||
{
|
||||
mPosition = skDefaultLightPos;
|
||||
mDirection = skDefaultLightDir;
|
||||
mDistAttenCoefficients = CVector3f(0.f, 1.f, 0.f);
|
||||
mAngleAttenCoefficients = CVector3f(0.f, 1.f, 0.f);
|
||||
mCachedRadius = 0.f;
|
||||
mCachedIntensity = 0.f;
|
||||
mDirtyFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
|
||||
}
|
||||
|
||||
// ************ DATA MANIPULATION ************
|
||||
|
||||
// This function is reverse engineered from the kiosk demo's code
|
||||
float CLight::CalculateRadius() const
|
||||
{
|
||||
if ((mDistAttenCoefficients.y >= FLT_EPSILON) ||
|
||||
(mDistAttenCoefficients.z >= FLT_EPSILON))
|
||||
{
|
||||
float Intensity = GetIntensity();
|
||||
|
||||
if (mDistAttenCoefficients.z > FLT_EPSILON)
|
||||
{
|
||||
if (Intensity <= FLT_EPSILON)
|
||||
return 0.f;
|
||||
|
||||
float IntensityMod = (Intensity * 5.f / 255.f * mDistAttenCoefficients.z);
|
||||
return sqrt(Intensity / IntensityMod);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (mDistAttenCoefficients.y <= FLT_EPSILON)
|
||||
return 0.f;
|
||||
|
||||
float IntensityMod = (Intensity * 5.f) / 255.f;
|
||||
if (IntensityMod < 0.2f)
|
||||
IntensityMod = 0.2f;
|
||||
|
||||
return Intensity / (IntensityMod * mDistAttenCoefficients.y);
|
||||
}
|
||||
}
|
||||
|
||||
else return 3000000000000000000000000000000000000.f;
|
||||
}
|
||||
|
||||
// This function is also reverse engineered from the kiosk demo's code
|
||||
float CLight::CalculateIntensity() const
|
||||
{
|
||||
float Multiplier = (mType == eCustom) ? mAngleAttenCoefficients.x : 1.0f;
|
||||
float ColorR = float(mColor.r) / 255.f;
|
||||
float ColorG = float(mColor.g) / 255.f;
|
||||
float ColorB = float(mColor.b) / 255.f;
|
||||
|
||||
// Get the color component with the greatest numeric value
|
||||
float Greatest = (ColorG >= ColorB) ? ColorG : ColorB;
|
||||
Greatest = (ColorR >= Greatest) ? ColorR : Greatest;
|
||||
|
||||
return Greatest * Multiplier;
|
||||
}
|
||||
|
||||
// As is this one... partly
|
||||
CVector3f CLight::CalculateSpotAngleAtten()
|
||||
{
|
||||
if (mType != eSpot) return CVector3f(1.f, 0.f, 0.f);
|
||||
|
||||
if ((mSpotCutoff < 0.f) || (mSpotCutoff > 90.f))
|
||||
return CVector3f(1.f, 0.f, 0.f);
|
||||
|
||||
float RadianCutoff = (mSpotCutoff * 3.1415927f) / 180.f;
|
||||
float RadianCosine = cosf(RadianCutoff);
|
||||
float InvCosine = 1.f - RadianCosine;
|
||||
|
||||
return CVector3f(0.f, -RadianCosine / InvCosine, 1.f / InvCosine);
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
ELightType CLight::GetType() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
u32 CLight::GetLayerIndex() const
|
||||
{
|
||||
return mLayerIndex;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetPosition() const
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetDirection() const
|
||||
{
|
||||
return mDirection;
|
||||
}
|
||||
|
||||
CColor CLight::GetColor() const
|
||||
{
|
||||
return mColor;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetDistAttenuation() const
|
||||
{
|
||||
return mDistAttenCoefficients;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetAngleAttenuation() const
|
||||
{
|
||||
return mAngleAttenCoefficients;
|
||||
}
|
||||
|
||||
float CLight::GetRadius() const
|
||||
{
|
||||
if (mDirtyFlags & CLIGHT_NO_RADIUS)
|
||||
{
|
||||
mCachedRadius = CalculateRadius();
|
||||
mDirtyFlags &= ~CLIGHT_NO_RADIUS;
|
||||
}
|
||||
|
||||
return mCachedRadius * 2;
|
||||
}
|
||||
|
||||
float CLight::GetIntensity() const
|
||||
{
|
||||
if (mDirtyFlags & CLIGHT_NO_INTENSITY)
|
||||
{
|
||||
mCachedIntensity = CalculateIntensity();
|
||||
mDirtyFlags &= ~CLIGHT_NO_INTENSITY;
|
||||
}
|
||||
|
||||
return mCachedIntensity;
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CLight::SetLayer(u32 index)
|
||||
{
|
||||
mLayerIndex = index;
|
||||
}
|
||||
|
||||
void CLight::SetPosition(const CVector3f& Position)
|
||||
{
|
||||
mPosition = Position;
|
||||
}
|
||||
|
||||
void CLight::SetDirection(const CVector3f& Direction)
|
||||
{
|
||||
mDirection = Direction;
|
||||
}
|
||||
|
||||
void CLight::SetColor(const CColor& Color)
|
||||
{
|
||||
mColor = Color;
|
||||
mDirtyFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
|
||||
}
|
||||
|
||||
void CLight::SetSpotCutoff(float Cutoff)
|
||||
{
|
||||
mSpotCutoff = Cutoff * 0.5f;
|
||||
CalculateSpotAngleAtten();
|
||||
}
|
||||
|
||||
void CLight::SetDistAtten(float DistCoefA, float DistCoefB, float DistCoefC)
|
||||
{
|
||||
mDistAttenCoefficients.x = DistCoefA;
|
||||
mDistAttenCoefficients.y = DistCoefB;
|
||||
mDistAttenCoefficients.z = DistCoefC;
|
||||
}
|
||||
|
||||
void CLight::SetAngleAtten(float AngleCoefA, float AngleCoefB, float AngleCoefC)
|
||||
{
|
||||
mAngleAttenCoefficients.x = AngleCoefA;
|
||||
mAngleAttenCoefficients.y = AngleCoefB;
|
||||
mAngleAttenCoefficients.z = AngleCoefC;
|
||||
}
|
||||
|
||||
// ************ OTHER ************
|
||||
void CLight::Load() const
|
||||
{
|
||||
u8 Index = (u8) CGraphics::sNumLights;
|
||||
if (Index >= 8) return;
|
||||
|
||||
CGraphics::SLightBlock::SGXLight *Light = &CGraphics::sLightBlock.Lights[Index];
|
||||
CVector3f PosView = CGraphics::sMVPBlock.ViewMatrix * mPosition;
|
||||
CVector3f DirView = CGraphics::sMVPBlock.ViewMatrix * mDirection;
|
||||
|
||||
switch (mType)
|
||||
{
|
||||
case eLocalAmbient:
|
||||
// LocalAmbient is already accounted for in CGraphics::sAreaAmbientColor
|
||||
return;
|
||||
case eDirectional:
|
||||
Light->Position = CVector4f(-mDirection * 1048576.f, 1.f);
|
||||
Light->Direction = CVector4f(mDirection, 0.f);
|
||||
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
|
||||
Light->DistAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
|
||||
Light->AngleAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
|
||||
break;
|
||||
case eSpot:
|
||||
Light->Position = CVector4f(mPosition, 1.f);
|
||||
Light->Direction = CVector4f(mDirection, 0.f);
|
||||
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
|
||||
Light->DistAtten = mDistAttenCoefficients;
|
||||
Light->AngleAtten = mAngleAttenCoefficients;
|
||||
break;
|
||||
case eCustom:
|
||||
Light->Position = CVector4f(mPosition, 1.f);
|
||||
Light->Direction = CVector4f(mDirection, 0.f);
|
||||
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
|
||||
Light->DistAtten = mDistAttenCoefficients;
|
||||
Light->AngleAtten = mAngleAttenCoefficients;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
CGraphics::sNumLights++;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CLight* CLight::BuildLocalAmbient(const CVector3f& Position, const CColor& Color)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eLocalAmbient;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = skDefaultLightDir;
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = 0.f;
|
||||
return Light;
|
||||
}
|
||||
|
||||
CLight* CLight::BuildDirectional(const CVector3f& Position, const CVector3f& Direction, const CColor& Color)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eDirectional;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = Direction;
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = 0.f;
|
||||
return Light;
|
||||
}
|
||||
|
||||
CLight* CLight::BuildSpot(const CVector3f& Position, const CVector3f& Direction, const CColor& Color, float Cutoff)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eSpot;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = -Direction.Normalized();
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = Cutoff * 0.5f;
|
||||
Light->mAngleAttenCoefficients = Light->CalculateSpotAngleAtten();
|
||||
return Light;
|
||||
}
|
||||
|
||||
CLight* CLight::BuildCustom(const CVector3f& Position, const CVector3f& Direction, const CColor& Color,
|
||||
float DistAttenA, float DistAttenB, float DistAttenC,
|
||||
float AngleAttenA, float AngleAttenB, float AngleAttenC)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eCustom;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = Direction;
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = 0.f;
|
||||
Light->mDistAttenCoefficients.x = DistAttenA;
|
||||
Light->mDistAttenCoefficients.y = DistAttenB;
|
||||
Light->mDistAttenCoefficients.z = DistAttenC;
|
||||
Light->mAngleAttenCoefficients.x = AngleAttenA;
|
||||
Light->mAngleAttenCoefficients.y = AngleAttenB;
|
||||
Light->mAngleAttenCoefficients.z = AngleAttenC * AngleAttenC;
|
||||
return Light;
|
||||
}
|
||||
|
||||
// ************ CONSTANTS ************
|
||||
const CVector3f CLight::skDefaultLightPos(0.f, 0.f, 0.f);
|
||||
const CVector3f CLight::skDefaultLightDir(0.f,-1.f, 0.f);
|
||||
81
src/Core/Resource/CLight.h
Normal file
81
src/Core/Resource/CLight.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef CLIGHT_H
|
||||
#define CLIGHT_H
|
||||
|
||||
#include <FileIO/CInputStream.h>
|
||||
#include <Common/CColor.h>
|
||||
#include <Common/CVector3f.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
/* CLight is currently heavily based on the lights system from Metroid Prime,
|
||||
* including code reverse engineered from the game's executable. Not yet sure
|
||||
* how much needs to be modified to properly support DKCR. */
|
||||
enum ELightType
|
||||
{
|
||||
eLocalAmbient = 0,
|
||||
eDirectional = 1,
|
||||
eSpot = 3,
|
||||
eCustom = 2
|
||||
};
|
||||
|
||||
class CLight
|
||||
{
|
||||
ELightType mType;
|
||||
u32 mLayerIndex;
|
||||
CVector3f mPosition;
|
||||
CVector3f mDirection;
|
||||
CColor mColor;
|
||||
float mSpotCutoff;
|
||||
CVector3f mDistAttenCoefficients;
|
||||
CVector3f mAngleAttenCoefficients;
|
||||
|
||||
mutable float mCachedRadius;
|
||||
mutable float mCachedIntensity;
|
||||
mutable u8 mDirtyFlags;
|
||||
|
||||
public:
|
||||
CLight();
|
||||
|
||||
private:
|
||||
// Data Manipulation
|
||||
float CalculateRadius() const;
|
||||
float CalculateIntensity() const;
|
||||
CVector3f CalculateSpotAngleAtten();
|
||||
|
||||
public:
|
||||
// Getters
|
||||
ELightType GetType() const;
|
||||
u32 GetLayerIndex() const;
|
||||
CVector3f GetPosition() const;
|
||||
CVector3f GetDirection() const;
|
||||
CColor GetColor() const;
|
||||
CVector3f GetDistAttenuation() const;
|
||||
CVector3f GetAngleAttenuation() const;
|
||||
float GetRadius() const;
|
||||
float GetIntensity() const;
|
||||
|
||||
// Setters
|
||||
void SetLayer(u32 index);
|
||||
void SetPosition(const CVector3f& Position);
|
||||
void SetDirection(const CVector3f& Direction);
|
||||
void SetColor(const CColor& Color);
|
||||
void SetSpotCutoff(float Cutoff);
|
||||
void SetDistAtten(float DistCoefA, float DistCoefB, float DistCoefC);
|
||||
void SetAngleAtten(float AngleCoefA, float AngleCoefB, float AngleCoefC);
|
||||
|
||||
// Other
|
||||
void Load() const;
|
||||
|
||||
// Static
|
||||
static CLight* BuildLocalAmbient(const CVector3f& Position, const CColor& Color);
|
||||
static CLight* BuildDirectional(const CVector3f& Position, const CVector3f& Direction, const CColor& Color);
|
||||
static CLight* BuildSpot(const CVector3f& Position, const CVector3f& Direction, const CColor& Color, float Cutoff);
|
||||
static CLight* BuildCustom(const CVector3f& Position, const CVector3f& Direction, const CColor& Color,
|
||||
float DistAttenA, float DistAttenB, float DistAttenC,
|
||||
float AngleAttenA, float AngleAttenB, float AngleAttenC);
|
||||
|
||||
// Constants
|
||||
static const CVector3f skDefaultLightPos;
|
||||
static const CVector3f skDefaultLightDir;
|
||||
};
|
||||
|
||||
#endif // CLIGHT_H
|
||||
350
src/Core/Resource/CMaterial.cpp
Normal file
350
src/Core/Resource/CMaterial.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
#include "CMaterial.h"
|
||||
#include <Common/CHashFNV1A.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <OpenGL/GLCommon.h>
|
||||
#include <OpenGL/CShaderGenerator.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <gl/glew.h>
|
||||
|
||||
u64 CMaterial::sCurrentMaterial = 0;
|
||||
CColor CMaterial::sCurrentTint = CColor::skWhite;
|
||||
|
||||
CMaterial::CMaterial()
|
||||
{
|
||||
mpShader = nullptr;
|
||||
mShaderStatus = eNoShader;
|
||||
mRecalcHash = true;
|
||||
mEnableBloom = false;
|
||||
mVersion = eUnknownVersion;
|
||||
mOptions = eNoSettings;
|
||||
mVtxDesc = eNoAttributes;
|
||||
mBlendSrcFac = GL_ONE;
|
||||
mBlendDstFac = GL_ZERO;
|
||||
mLightingEnabled = true;
|
||||
mEchoesUnknownA = 0;
|
||||
mEchoesUnknownB = 0;
|
||||
mpIndirectTexture = nullptr;
|
||||
}
|
||||
|
||||
CMaterial::CMaterial(EGame version, EVertexDescription vtxDesc)
|
||||
{
|
||||
mpShader = nullptr;
|
||||
mShaderStatus = eNoShader;
|
||||
mRecalcHash = true;
|
||||
mEnableBloom = (version == eCorruption);
|
||||
mVersion = version;
|
||||
mOptions = eDepthWrite;
|
||||
mVtxDesc = vtxDesc;
|
||||
mBlendSrcFac = GL_ONE;
|
||||
mBlendDstFac = GL_ZERO;
|
||||
mLightingEnabled = true;
|
||||
mEchoesUnknownA = 0;
|
||||
mEchoesUnknownB = 0;
|
||||
mpIndirectTexture = nullptr;
|
||||
}
|
||||
|
||||
CMaterial::~CMaterial()
|
||||
{
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
delete mPasses[iPass];
|
||||
|
||||
delete mpShader;
|
||||
}
|
||||
|
||||
CMaterial* CMaterial::Clone()
|
||||
{
|
||||
CMaterial *pOut = new CMaterial();
|
||||
pOut->mName = mName;
|
||||
pOut->mEnableBloom = mEnableBloom;
|
||||
pOut->mVersion = mVersion;
|
||||
pOut->mOptions = mOptions;
|
||||
pOut->mVtxDesc = mVtxDesc;
|
||||
for (u32 iKonst = 0; iKonst < 4; iKonst++)
|
||||
pOut->mKonstColors[iKonst] = mKonstColors[iKonst];
|
||||
pOut->mBlendSrcFac = mBlendSrcFac;
|
||||
pOut->mBlendDstFac = mBlendDstFac;
|
||||
pOut->mLightingEnabled = mLightingEnabled;
|
||||
pOut->mEchoesUnknownA = mEchoesUnknownA;
|
||||
pOut->mEchoesUnknownB = mEchoesUnknownB;
|
||||
pOut->mpIndirectTexture = mpIndirectTexture;
|
||||
|
||||
pOut->mPasses.resize(mPasses.size());
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
pOut->mPasses[iPass] = mPasses[iPass]->Clone(pOut);
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
void CMaterial::GenerateShader()
|
||||
{
|
||||
delete mpShader;
|
||||
mpShader = CShaderGenerator::GenerateShader(*this);
|
||||
|
||||
if (!mpShader->IsValidProgram()) mShaderStatus = eShaderFailed;
|
||||
else mShaderStatus = eShaderExists;
|
||||
}
|
||||
|
||||
bool CMaterial::SetCurrent(ERenderOptions Options)
|
||||
{
|
||||
// Bind textures
|
||||
const char *skpSamplers[8] = {
|
||||
"Texture0", "Texture1", "Texture2", "Texture3",
|
||||
"Texture4", "Texture5", "Texture6", "Texture7"
|
||||
};
|
||||
|
||||
// Skip material setup if the currently bound material is identical
|
||||
if (sCurrentMaterial != HashParameters())
|
||||
{
|
||||
// Shader setup
|
||||
if (mShaderStatus == eNoShader) GenerateShader();
|
||||
mpShader->SetCurrent();
|
||||
|
||||
if (mShaderStatus == eShaderFailed)
|
||||
return false;
|
||||
|
||||
// Set RGB blend equation - force to ZERO/ONE if alpha is disabled
|
||||
GLenum srcRGB, dstRGB, srcAlpha, dstAlpha;
|
||||
|
||||
if (Options & eNoAlpha) {
|
||||
srcRGB = GL_ONE;
|
||||
dstRGB = GL_ZERO;
|
||||
} else {
|
||||
srcRGB = mBlendSrcFac;
|
||||
dstRGB = mBlendDstFac;
|
||||
}
|
||||
|
||||
// Set alpha blend equation
|
||||
bool AlphaBlended = ((mBlendSrcFac == GL_SRC_ALPHA) && (mBlendDstFac == GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
if ((mEnableBloom) && (Options & eEnableBloom) && (!AlphaBlended)) {
|
||||
srcAlpha = mBlendSrcFac;
|
||||
dstAlpha = mBlendDstFac;
|
||||
} else {
|
||||
srcAlpha = GL_ZERO;
|
||||
dstAlpha = GL_ZERO;
|
||||
}
|
||||
|
||||
glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
|
||||
// Set konst inputs
|
||||
for (u32 iKonst = 0; iKonst < 4; iKonst++)
|
||||
CGraphics::sPixelBlock.Konst[iKonst] = mKonstColors[iKonst].ToVector4f();
|
||||
|
||||
// Set color channels
|
||||
// COLOR0_Amb is initialized by the node instead of by the material
|
||||
CGraphics::sVertexBlock.COLOR0_Mat = CColor::skWhite.ToVector4f();
|
||||
|
||||
// Set depth write - force on if alpha is disabled (lots of weird depth issues otherwise)
|
||||
if ((mOptions & eDepthWrite) || (Options & eNoAlpha)) glDepthMask(GL_TRUE);
|
||||
else glDepthMask(GL_FALSE);
|
||||
|
||||
// Load uniforms
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
mPasses[iPass]->SetAnimCurrent(Options, iPass);
|
||||
|
||||
sCurrentMaterial = HashParameters();
|
||||
}
|
||||
|
||||
// If the passes are otherwise the same, update UV anims that use the model matrix
|
||||
else
|
||||
{
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
{
|
||||
EUVAnimMode mode = mPasses[iPass]->AnimMode();
|
||||
|
||||
if ((mode == eInverseMV) || (mode == eInverseMVTranslated) ||
|
||||
(mode == eModelMatrix) || (mode == eSimpleMode))
|
||||
mPasses[iPass]->SetAnimCurrent(Options, iPass);
|
||||
}
|
||||
}
|
||||
|
||||
// Bind textures
|
||||
CShader *pShader = CShader::CurrentShader();
|
||||
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
{
|
||||
mPasses[iPass]->LoadTexture(iPass);
|
||||
GLint sampler = pShader->GetUniformLocation(skpSamplers[iPass]);
|
||||
glUniform1i(sampler, iPass);
|
||||
}
|
||||
|
||||
// Bind num lights
|
||||
GLuint NumLightsLoc = pShader->GetUniformLocation("NumLights");
|
||||
glUniform1i(NumLightsLoc, CGraphics::sNumLights);
|
||||
|
||||
// Update shader blocks
|
||||
CGraphics::UpdateVertexBlock();
|
||||
CGraphics::UpdatePixelBlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 CMaterial::HashParameters()
|
||||
{
|
||||
if (mRecalcHash)
|
||||
{
|
||||
CHashFNV1A Hash;
|
||||
Hash.Init64();
|
||||
|
||||
Hash.HashLong(mVersion);
|
||||
Hash.HashLong(mOptions);
|
||||
Hash.HashLong(mVtxDesc);
|
||||
Hash.HashData(mKonstColors, sizeof(CColor) * 4);
|
||||
Hash.HashLong(mBlendSrcFac);
|
||||
Hash.HashLong(mBlendDstFac);
|
||||
Hash.HashByte(mLightingEnabled);
|
||||
Hash.HashLong(mEchoesUnknownA);
|
||||
Hash.HashLong(mEchoesUnknownB);
|
||||
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
mPasses[iPass]->HashParameters(Hash);
|
||||
|
||||
mParametersHash = Hash.GetHash64();
|
||||
mRecalcHash = false;
|
||||
}
|
||||
|
||||
return mParametersHash;
|
||||
}
|
||||
|
||||
void CMaterial::Update()
|
||||
{
|
||||
mRecalcHash = true;
|
||||
mShaderStatus = eNoShader;
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
TString CMaterial::Name() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
EGame CMaterial::Version() const
|
||||
{
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
CMaterial::EMaterialOptions CMaterial::Options() const
|
||||
{
|
||||
return mOptions;
|
||||
}
|
||||
|
||||
EVertexDescription CMaterial::VtxDesc() const
|
||||
{
|
||||
return mVtxDesc;
|
||||
}
|
||||
|
||||
GLenum CMaterial::BlendSrcFac() const {
|
||||
return mBlendSrcFac;
|
||||
}
|
||||
|
||||
GLenum CMaterial::BlendDstFac() const {
|
||||
return mBlendDstFac;
|
||||
}
|
||||
|
||||
CColor CMaterial::Konst(u32 KIndex) const
|
||||
{
|
||||
if (KIndex > 3) return CColor::skTransparentBlack;
|
||||
else return mKonstColors[KIndex];
|
||||
}
|
||||
|
||||
CTexture* CMaterial::IndTexture() const
|
||||
{
|
||||
return mpIndirectTexture;
|
||||
}
|
||||
|
||||
bool CMaterial::IsLightingEnabled() const
|
||||
{
|
||||
return mLightingEnabled;
|
||||
}
|
||||
|
||||
u32 CMaterial::EchoesUnknownA() const
|
||||
{
|
||||
return mEchoesUnknownA;
|
||||
}
|
||||
|
||||
u32 CMaterial::EchoesUnknownB() const
|
||||
{
|
||||
return mEchoesUnknownB;
|
||||
}
|
||||
|
||||
u32 CMaterial::PassCount() const
|
||||
{
|
||||
return mPasses.size();
|
||||
}
|
||||
|
||||
CMaterialPass* CMaterial::Pass(u32 PassIndex) const
|
||||
{
|
||||
return mPasses[PassIndex];
|
||||
}
|
||||
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CMaterial::SetName(const TString& name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
void CMaterial::SetOptions(EMaterialOptions Options)
|
||||
{
|
||||
mOptions = Options;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetVertexDescription(EVertexDescription desc)
|
||||
{
|
||||
mVtxDesc = desc;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetBlendMode(GLenum SrcFac, GLenum DstFac)
|
||||
{
|
||||
mBlendSrcFac = SrcFac;
|
||||
mBlendDstFac = DstFac;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetKonst(CColor& Konst, u32 KIndex)
|
||||
{
|
||||
mKonstColors[KIndex] = Konst;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetIndTexture(CTexture *pTex)
|
||||
{
|
||||
mpIndirectTexture = pTex;
|
||||
}
|
||||
|
||||
void CMaterial::SetLightingEnabled(bool Enabled)
|
||||
{
|
||||
mLightingEnabled = Enabled;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetNumPasses(u32 NumPasses)
|
||||
{
|
||||
if (NumPasses < mPasses.size())
|
||||
{
|
||||
for (u32 iPass = NumPasses; iPass < mPasses.size(); iPass++)
|
||||
delete mPasses[iPass];
|
||||
}
|
||||
|
||||
u32 OldCount = mPasses.size();
|
||||
mPasses.resize(NumPasses);
|
||||
|
||||
if (NumPasses > OldCount)
|
||||
{
|
||||
for (u32 iPass = OldCount; iPass < NumPasses; iPass++)
|
||||
mPasses[iPass] = new CMaterialPass(this);
|
||||
}
|
||||
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
void CMaterial::KillCachedMaterial()
|
||||
{
|
||||
sCurrentMaterial = 0;
|
||||
}
|
||||
113
src/Core/Resource/CMaterial.h
Normal file
113
src/Core/Resource/CMaterial.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#ifndef MATERIAL_H
|
||||
#define MATERIAL_H
|
||||
|
||||
#include "CMaterialPass.h"
|
||||
#include "CTexture.h"
|
||||
#include "EFormatVersion.h"
|
||||
#include "model/EVertexDescription.h"
|
||||
#include <Common/CColor.h>
|
||||
#include <Common/EnumUtil.h>
|
||||
#include <Common/types.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <Core/ERenderOptions.h>
|
||||
#include <FileIO/CInputStream.h>
|
||||
#include <OpenGL/CShader.h>
|
||||
#include <Resource/CTexture.h>
|
||||
|
||||
class CMaterialSet;
|
||||
|
||||
class CMaterial
|
||||
{
|
||||
public:
|
||||
friend class CMaterialLoader;
|
||||
friend class CMaterialCooker;
|
||||
|
||||
// Enums
|
||||
enum EMaterialOptions
|
||||
{
|
||||
eNoSettings = 0,
|
||||
eKonst = 0x8,
|
||||
eTransparent = 0x10,
|
||||
ePunchthrough = 0x20,
|
||||
eReflection = 0x40,
|
||||
eDepthWrite = 0x80,
|
||||
eSurfaceReflection = 0x100,
|
||||
eOccluder = 0x200,
|
||||
eIndStage = 0x400,
|
||||
eLightmap = 0x800,
|
||||
eShortTexCoord = 0x2000,
|
||||
eAllSettings = 0x2FF8
|
||||
};
|
||||
|
||||
private:
|
||||
enum EShaderStatus
|
||||
{
|
||||
eNoShader, eShaderExists, eShaderFailed
|
||||
};
|
||||
|
||||
// Statics
|
||||
static u64 sCurrentMaterial; // The hash for the currently bound material
|
||||
static CColor sCurrentTint; // The tint for the currently bound material
|
||||
|
||||
// Members
|
||||
TString mName; // Name of the material
|
||||
CShader *mpShader; // This material's generated shader. Created with GenerateShader().
|
||||
EShaderStatus mShaderStatus; // A status variable so that PWE won't crash if a shader fails to compile.
|
||||
u64 mParametersHash; // A hash of all the parameters that can identify this TEV setup.
|
||||
bool mRecalcHash; // Indicates the hash needs to be recalculated. Set true when parameters are changed.
|
||||
bool mEnableBloom; // Bool that toggles bloom on or off. On by default on MP3 materials, off by default on MP1 materials.
|
||||
|
||||
EGame mVersion;
|
||||
EMaterialOptions mOptions; // See the EMaterialOptions enum above
|
||||
EVertexDescription mVtxDesc; // Descriptor of vertex attributes used by this material
|
||||
CColor mKonstColors[4]; // Konst color values for TEV
|
||||
GLenum mBlendSrcFac; // Source blend factor
|
||||
GLenum mBlendDstFac; // Dest blend factor
|
||||
bool mLightingEnabled; // Color channel control flags; indicate whether lighting is enabled
|
||||
u32 mEchoesUnknownA; // First unknown value introduced in Echoes. Included for cooking.
|
||||
u32 mEchoesUnknownB; // Second unknown value introduced in Echoes. Included for cooking.
|
||||
TResPtr<CTexture> mpIndirectTexture; // Optional texture used for the indirect stage for reflections
|
||||
|
||||
std::vector<CMaterialPass*> mPasses;
|
||||
|
||||
public:
|
||||
CMaterial();
|
||||
CMaterial(EGame version, EVertexDescription vtxDesc);
|
||||
~CMaterial();
|
||||
CMaterial* Clone();
|
||||
void GenerateShader();
|
||||
bool SetCurrent(ERenderOptions Options);
|
||||
u64 HashParameters();
|
||||
void Update();
|
||||
|
||||
// Getters
|
||||
TString Name() const;
|
||||
EGame Version() const;
|
||||
EMaterialOptions Options() const;
|
||||
EVertexDescription VtxDesc() const;
|
||||
GLenum BlendSrcFac() const;
|
||||
GLenum BlendDstFac() const;
|
||||
CColor Konst(u32 KIndex) const;
|
||||
CTexture* IndTexture() const;
|
||||
bool IsLightingEnabled() const;
|
||||
u32 EchoesUnknownA() const;
|
||||
u32 EchoesUnknownB() const;
|
||||
u32 PassCount() const;
|
||||
CMaterialPass* Pass(u32 PassIndex) const;
|
||||
|
||||
// Setters
|
||||
void SetName(const TString& name);
|
||||
void SetOptions(EMaterialOptions Options);
|
||||
void SetVertexDescription(EVertexDescription desc);
|
||||
void SetBlendMode(GLenum SrcFac, GLenum DstFac);
|
||||
void SetKonst(CColor& Konst, u32 KIndex);
|
||||
void SetIndTexture(CTexture *pTex);
|
||||
void SetLightingEnabled(bool Enabled);
|
||||
void SetNumPasses(u32 NumPasses);
|
||||
|
||||
// Static
|
||||
static void KillCachedMaterial();
|
||||
};
|
||||
DEFINE_ENUM_FLAGS(CMaterial::EMaterialOptions)
|
||||
|
||||
#endif // MATERIAL_H
|
||||
320
src/Core/Resource/CMaterialPass.cpp
Normal file
320
src/Core/Resource/CMaterialPass.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
#include "CMaterialPass.h"
|
||||
#include "CMaterial.h"
|
||||
#include <Common/AnimUtil.h>
|
||||
#include <Core/CGraphics.h>
|
||||
|
||||
CMaterialPass::CMaterialPass(CMaterial *pParent)
|
||||
{
|
||||
mPassType = "CUST";
|
||||
mSettings = eNoPassSettings;
|
||||
mpTexture = nullptr;
|
||||
mEnabled = true;
|
||||
mpParentMat = pParent;
|
||||
|
||||
mColorOutput = ePrevReg;
|
||||
mAlphaOutput = ePrevReg;
|
||||
mKColorSel = eKonstOne;
|
||||
mKAlphaSel = eKonstOne;
|
||||
mRasSel = eRasColorNull;
|
||||
mTexCoordSource = 0xFF;
|
||||
mAnimMode = eNoUVAnim;
|
||||
|
||||
for (u32 iParam = 0; iParam < 4; iParam++)
|
||||
{
|
||||
mColorInputs[iParam] = eZeroRGB;
|
||||
mAlphaInputs[iParam] = eZeroAlpha;
|
||||
mAnimParams[iParam] = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
CMaterialPass::~CMaterialPass()
|
||||
{
|
||||
}
|
||||
|
||||
CMaterialPass* CMaterialPass::Clone(CMaterial *pParent)
|
||||
{
|
||||
CMaterialPass *pOut = new CMaterialPass(pParent);
|
||||
pOut->mPassType = mPassType;
|
||||
pOut->mSettings = mSettings;
|
||||
for (u32 iIn = 0; iIn < 4; iIn++) {
|
||||
pOut->mColorInputs[iIn] = mColorInputs[iIn];
|
||||
pOut->mAlphaInputs[iIn] = mAlphaInputs[iIn];
|
||||
}
|
||||
pOut->mColorOutput = mColorOutput;
|
||||
pOut->mAlphaOutput = mAlphaOutput;
|
||||
pOut->mKColorSel = mKColorSel;
|
||||
pOut->mKAlphaSel = mKAlphaSel;
|
||||
pOut->mRasSel = mRasSel;
|
||||
pOut->mTexCoordSource = mTexCoordSource;
|
||||
pOut->mpTexture = mpTexture;
|
||||
pOut->mAnimMode = mAnimMode;
|
||||
for (u32 iParam = 0; iParam < 4; iParam++)
|
||||
pOut->mAnimParams[iParam] = mAnimParams[iParam];
|
||||
pOut->mEnabled = mEnabled;
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
void CMaterialPass::HashParameters(CHashFNV1A &Hash)
|
||||
{
|
||||
if (mEnabled)
|
||||
{
|
||||
Hash.HashLong(mPassType.ToLong());
|
||||
Hash.HashLong(mSettings);
|
||||
Hash.HashData(&mColorInputs[0], sizeof(ETevColorInput) * 4);
|
||||
Hash.HashData(&mAlphaInputs[0], sizeof(ETevAlphaInput) * 4);
|
||||
Hash.HashLong(mColorOutput);
|
||||
Hash.HashLong(mAlphaOutput);
|
||||
Hash.HashLong(mKColorSel);
|
||||
Hash.HashLong(mKAlphaSel);
|
||||
Hash.HashLong(mRasSel);
|
||||
Hash.HashLong(mTexCoordSource);
|
||||
Hash.HashLong(mAnimMode);
|
||||
Hash.HashData(mAnimParams, sizeof(float) * 4);
|
||||
Hash.HashByte(mEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void CMaterialPass::LoadTexture(u32 PassIndex)
|
||||
{
|
||||
if (mpTexture)
|
||||
mpTexture->Bind(PassIndex);
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAnimCurrent(ERenderOptions Options, u32 PassIndex)
|
||||
{
|
||||
if (mAnimMode == eNoUVAnim) return;
|
||||
|
||||
float s = AnimUtil::SecondsMod900();
|
||||
const CMatrix4f& ModelMtx = CGraphics::sMVPBlock.ModelMatrix;
|
||||
const CMatrix4f& ViewMtx = CGraphics::sMVPBlock.ViewMatrix;
|
||||
|
||||
CMatrix4f TexMtx = CMatrix4f::skIdentity;
|
||||
CMatrix4f PostMtx = CMatrix4f::skIdentity;
|
||||
|
||||
switch (mAnimMode)
|
||||
{
|
||||
|
||||
case eInverseMV: // Mode 0
|
||||
case eSimpleMode: // Mode 10 - maybe not correct?
|
||||
{
|
||||
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
mtx[0][3] = mtx[1][3] = mtx[2][3] = 0.f;
|
||||
TexMtx = CMatrix4f::FromGlmMat4(mtx);
|
||||
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
case eInverseMVTranslated: // Mode 1
|
||||
{
|
||||
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
TexMtx = CMatrix4f::FromGlmMat4(mtx);
|
||||
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
case eUVScroll: // Mode 2
|
||||
{
|
||||
if (Options & eEnableUVScroll)
|
||||
{
|
||||
TexMtx[0][3] = (s * mAnimParams[2]) + mAnimParams[0];
|
||||
TexMtx[1][3] = (s * mAnimParams[3]) + mAnimParams[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case eUVRotation: // Mode 3
|
||||
{
|
||||
if (Options & eEnableUVScroll)
|
||||
{
|
||||
float Angle = (s * mAnimParams[1]) + mAnimParams[0];
|
||||
|
||||
float ACos = cos(Angle);
|
||||
float ASin = sin(Angle);
|
||||
float TransX = (1.f - (ACos - ASin)) * 0.5f;
|
||||
float TransY = (1.f - (ASin + ACos)) * 0.5f;
|
||||
|
||||
TexMtx = CMatrix4f(ACos, -ASin, 0.0f, TransX,
|
||||
ASin, ACos, 0.0f, TransY,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case eHFilmstrip: // Mode 4
|
||||
case eVFilmstrip: // Mode 5
|
||||
{
|
||||
if (Options & eEnableUVScroll)
|
||||
{
|
||||
float Offset = mAnimParams[2] * mAnimParams[0] * (mAnimParams[3] + s);
|
||||
Offset = (float)(short)(float)(mAnimParams[1] * fmod(Offset, 1.0f)) * mAnimParams[2];
|
||||
if (mAnimMode == eHFilmstrip) TexMtx[0][3] = Offset;
|
||||
if (mAnimMode == eVFilmstrip) TexMtx[1][3] = Offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case eModelMatrix: // Mode 6
|
||||
{
|
||||
// It looks ok, but I can't tell whether it's correct...
|
||||
TexMtx = CMatrix4f::FromGlmMat4(glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, TexMtx[0][3] * 0.50000001f,
|
||||
0.0f, 0.5f, 0.0f, TexMtx[1][3] * 0.50000001f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
TexMtx[0][3] = 0.f;
|
||||
TexMtx[1][3] = 0.f;
|
||||
TexMtx[2][3] = 0.f;
|
||||
}
|
||||
|
||||
case eConvolutedModeA: // Mode 7
|
||||
{
|
||||
CMatrix4f view = CGraphics::sMVPBlock.ViewMatrix;
|
||||
|
||||
// Oh god I seriously need a CMatrix4f inverse function.
|
||||
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
mtx[0][3] = mtx[1][3] = mtx[2][3] = 0.f;
|
||||
TexMtx = CMatrix4f::FromGlmMat4(mtx);
|
||||
|
||||
float xy = (view[3][0] + view[3][1]) * 0.025f * mAnimParams[1];
|
||||
xy = (xy - (int) xy);
|
||||
|
||||
float z = view[3][2] * 0.05f * mAnimParams[1];
|
||||
z = (z - (int) z);
|
||||
|
||||
float halfA = mAnimParams[0] * 0.5f;
|
||||
|
||||
PostMtx = CMatrix4f(halfA, 0.0f, 0.0f, xy,
|
||||
0.0f, 0.0f, halfA, z,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
case eConvolutedModeB: // Mode 8 (MP3/DKCR only)
|
||||
{
|
||||
// todo
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CGraphics::sVertexBlock.TexMatrices[PassIndex] = TexMtx;
|
||||
CGraphics::sVertexBlock.PostMatrices[PassIndex] = PostMtx;
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CMaterialPass::SetType(CFourCC Type)
|
||||
{
|
||||
mPassType = Type;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetColorInputs(ETevColorInput InputA, ETevColorInput InputB, ETevColorInput InputC, ETevColorInput InputD)
|
||||
{
|
||||
mColorInputs[0] = InputA;
|
||||
mColorInputs[1] = InputB;
|
||||
mColorInputs[2] = InputC;
|
||||
mColorInputs[3] = InputD;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAlphaInputs(ETevAlphaInput InputA, ETevAlphaInput InputB, ETevAlphaInput InputC, ETevAlphaInput InputD)
|
||||
{
|
||||
mAlphaInputs[0] = InputA;
|
||||
mAlphaInputs[1] = InputB;
|
||||
mAlphaInputs[2] = InputC;
|
||||
mAlphaInputs[3] = InputD;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetColorOutput(ETevOutput OutputReg)
|
||||
{
|
||||
mColorOutput = OutputReg;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAlphaOutput(ETevOutput OutputReg)
|
||||
{
|
||||
mAlphaOutput = OutputReg;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetKColorSel(ETevKSel Sel)
|
||||
{
|
||||
mKColorSel = Sel;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetKAlphaSel(ETevKSel Sel)
|
||||
{
|
||||
// Konst RGB is invalid for alpha, so reset to One if that's the selection
|
||||
if ((Sel >= eKonst0_RGB) && (Sel <= eKonst3_RGB))
|
||||
Sel = eKonstOne;
|
||||
|
||||
mKAlphaSel = Sel;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetRasSel(ETevRasSel Sel)
|
||||
{
|
||||
mRasSel = Sel;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetTexCoordSource(u32 Source)
|
||||
{
|
||||
mTexCoordSource = Source;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetTexture(CTexture *pTex)
|
||||
{
|
||||
mpTexture = pTex;
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAnimMode(EUVAnimMode Mode)
|
||||
{
|
||||
mAnimMode = Mode;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAnimParam(u32 ParamIndex, float Value)
|
||||
{
|
||||
mAnimParams[ParamIndex] = Value;
|
||||
}
|
||||
|
||||
void CMaterialPass::SetEnabled(bool Enabled)
|
||||
{
|
||||
mEnabled = Enabled;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
TString CMaterialPass::PassTypeName(CFourCC Type)
|
||||
{
|
||||
if (Type == "CUST") return "Custom";
|
||||
if (Type == "DIFF") return "Light";
|
||||
if (Type == "RIML") return "Rim Light";
|
||||
if (Type == "BLOL") return "Bloom Light";
|
||||
// BLOD
|
||||
if (Type == "CLR ") return "Diffuse";
|
||||
if (Type == "TRAN") return "Opacity";
|
||||
if (Type == "INCA") return "Emissive";
|
||||
if (Type == "RFLV") return "Specular";
|
||||
if (Type == "RFLD") return "Reflection";
|
||||
// LRLD
|
||||
// LURD
|
||||
if (Type == "BLOI") return "Bloom Diffuse";
|
||||
if (Type == "XRAY") return "X-Ray";
|
||||
// TOON
|
||||
return Type.ToString();
|
||||
}
|
||||
128
src/Core/Resource/CMaterialPass.h
Normal file
128
src/Core/Resource/CMaterialPass.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#ifndef CMATERIALPASS_H
|
||||
#define CMATERIALPASS_H
|
||||
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/CHashFNV1A.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <Core/ERenderOptions.h>
|
||||
#include <Resource/CTexture.h>
|
||||
#include "ETevEnums.h"
|
||||
|
||||
class CMaterial;
|
||||
|
||||
class CMaterialPass
|
||||
{
|
||||
friend class CMaterialLoader;
|
||||
friend class CMaterialCooker;
|
||||
|
||||
public:
|
||||
enum EPassSettings
|
||||
{
|
||||
eNoPassSettings = 0x0,
|
||||
eEmissiveBloom = 0x4,
|
||||
eInvertOpacityMap = 0x10
|
||||
};
|
||||
|
||||
private:
|
||||
CMaterial *mpParentMat;
|
||||
CFourCC mPassType;
|
||||
EPassSettings mSettings;
|
||||
|
||||
ETevColorInput mColorInputs[4];
|
||||
ETevAlphaInput mAlphaInputs[4];
|
||||
ETevOutput mColorOutput;
|
||||
ETevOutput mAlphaOutput;
|
||||
ETevKSel mKColorSel;
|
||||
ETevKSel mKAlphaSel;
|
||||
ETevRasSel mRasSel;
|
||||
u32 mTexCoordSource; // Should maybe be an enum but worried about conflicts with EVertexDescriptionn
|
||||
TResPtr<CTexture> mpTexture;
|
||||
EUVAnimMode mAnimMode;
|
||||
float mAnimParams[4];
|
||||
bool mEnabled;
|
||||
|
||||
public:
|
||||
CMaterialPass(CMaterial *pParent);
|
||||
~CMaterialPass();
|
||||
CMaterialPass* Clone(CMaterial *pParent);
|
||||
void HashParameters(CHashFNV1A& Hash);
|
||||
void LoadTexture(u32 PassIndex);
|
||||
void SetAnimCurrent(ERenderOptions Options, u32 PassIndex);
|
||||
|
||||
// Setters
|
||||
void SetType(CFourCC Type);
|
||||
void SetColorInputs(ETevColorInput InputA, ETevColorInput InputB, ETevColorInput InputC, ETevColorInput InputD);
|
||||
void SetAlphaInputs(ETevAlphaInput InputA, ETevAlphaInput InputB, ETevAlphaInput InputC, ETevAlphaInput InputD);
|
||||
void SetColorOutput(ETevOutput OutputReg);
|
||||
void SetAlphaOutput(ETevOutput OutputReg);
|
||||
void SetKColorSel(ETevKSel Sel);
|
||||
void SetKAlphaSel(ETevKSel Sel);
|
||||
void SetRasSel(ETevRasSel Sel);
|
||||
void SetTexCoordSource(u32 Source);
|
||||
void SetTexture(CTexture *pTex);
|
||||
void SetAnimMode(EUVAnimMode Mode);
|
||||
void SetAnimParam(u32 ParamIndex, float Value);
|
||||
void SetEnabled(bool Enabled);
|
||||
|
||||
// Getters
|
||||
inline CFourCC Type() const {
|
||||
return mPassType;
|
||||
}
|
||||
|
||||
inline TString NamedType() const {
|
||||
return PassTypeName(mPassType);
|
||||
}
|
||||
|
||||
inline ETevColorInput ColorInput(u32 Input) const {
|
||||
return mColorInputs[Input];
|
||||
}
|
||||
|
||||
inline ETevAlphaInput AlphaInput(u32 Input) const {
|
||||
return mAlphaInputs[Input];
|
||||
}
|
||||
|
||||
inline ETevOutput ColorOutput() const {
|
||||
return mColorOutput;
|
||||
}
|
||||
|
||||
inline ETevOutput AlphaOutput() const {
|
||||
return mAlphaOutput;
|
||||
}
|
||||
|
||||
inline ETevKSel KColorSel() const {
|
||||
return mKColorSel;
|
||||
}
|
||||
|
||||
inline ETevKSel KAlphaSel() const {
|
||||
return mKAlphaSel;
|
||||
}
|
||||
|
||||
inline ETevRasSel RasSel() const {
|
||||
return mRasSel;
|
||||
}
|
||||
|
||||
inline u32 TexCoordSource() const {
|
||||
return mTexCoordSource;
|
||||
}
|
||||
|
||||
inline CTexture* Texture() const {
|
||||
return mpTexture;
|
||||
}
|
||||
|
||||
inline EUVAnimMode AnimMode() const {
|
||||
return mAnimMode;
|
||||
}
|
||||
|
||||
inline float AnimParam(u32 ParamIndex) const {
|
||||
return mAnimParams[ParamIndex];
|
||||
}
|
||||
|
||||
inline bool IsEnabled() const {
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
// Static
|
||||
static TString PassTypeName(CFourCC Type);
|
||||
};
|
||||
|
||||
#endif // CMATERIALPASS_H
|
||||
49
src/Core/Resource/CMaterialSet.cpp
Normal file
49
src/Core/Resource/CMaterialSet.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "CMaterialSet.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <iostream>
|
||||
|
||||
CMaterialSet::CMaterialSet()
|
||||
{
|
||||
}
|
||||
|
||||
CMaterialSet::~CMaterialSet()
|
||||
{
|
||||
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
|
||||
delete mMaterials[iMat];
|
||||
}
|
||||
|
||||
CMaterialSet* CMaterialSet::Clone()
|
||||
{
|
||||
CMaterialSet *pOut = new CMaterialSet();
|
||||
|
||||
pOut->mMaterials.resize(mMaterials.size());
|
||||
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
|
||||
pOut->mMaterials[iMat] = mMaterials[iMat]->Clone();
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
u32 CMaterialSet::NumMaterials()
|
||||
{
|
||||
return mMaterials.size();
|
||||
}
|
||||
|
||||
CMaterial* CMaterialSet::MaterialByIndex(u32 index)
|
||||
{
|
||||
if (index >= NumMaterials()) return nullptr;
|
||||
return mMaterials[index];
|
||||
}
|
||||
|
||||
CMaterial* CMaterialSet::MaterialByName(const TString& name)
|
||||
{
|
||||
for (auto it = mMaterials.begin(); it != mMaterials.end(); it++)
|
||||
if ((*it)->Name() == name) return *it;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 CMaterialSet::MaterialIndexByName(const TString& name)
|
||||
{
|
||||
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
|
||||
if (mMaterials[iMat]->Name() == name) return iMat;
|
||||
return -1;
|
||||
}
|
||||
26
src/Core/Resource/CMaterialSet.h
Normal file
26
src/Core/Resource/CMaterialSet.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CMATERIALSET_H
|
||||
#define CMATERIALSET_H
|
||||
|
||||
#include <FileIO/CInputStream.h>
|
||||
#include <Resource/CTexture.h>
|
||||
#include <Resource/CMaterial.h>
|
||||
#include "EFormatVersion.h"
|
||||
|
||||
class CMaterialSet
|
||||
{
|
||||
friend class CMaterialLoader;
|
||||
friend class CMaterialCooker;
|
||||
|
||||
std::vector<CMaterial*> mMaterials;
|
||||
|
||||
public:
|
||||
CMaterialSet();
|
||||
~CMaterialSet();
|
||||
CMaterialSet* Clone();
|
||||
u32 NumMaterials();
|
||||
CMaterial* MaterialByIndex(u32 index);
|
||||
CMaterial* MaterialByName(const TString& name);
|
||||
u32 MaterialIndexByName(const TString& name);
|
||||
};
|
||||
|
||||
#endif // CMATERIALSET_H
|
||||
174
src/Core/Resource/CPakFile.cpp
Normal file
174
src/Core/Resource/CPakFile.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include "CPakFile.h"
|
||||
#include <Common/types.h>
|
||||
#include <FileIO/CMemoryInStream.h>
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <zlib.h>
|
||||
#include <lzo/lzo1x.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
CPakFile::CPakFile()
|
||||
{
|
||||
pak = nullptr;
|
||||
}
|
||||
|
||||
CPakFile::CPakFile(CInputStream* pakfile)
|
||||
{
|
||||
pak = pakfile;
|
||||
if (!pak->IsValid()) return;
|
||||
|
||||
version = pak->ReadLong();
|
||||
pak->Seek(0x4, SEEK_CUR);
|
||||
|
||||
u32 namedResCount = pak->ReadLong();
|
||||
NamedResTable.resize(namedResCount);
|
||||
|
||||
for (u32 n = 0; n < namedResCount; n++)
|
||||
{
|
||||
SNamedResource *res = &NamedResTable[n];
|
||||
res->resType = CFourCC(*pak);
|
||||
res->resID = (u64) pak->ReadLong();
|
||||
u32 resNameLength = pak->ReadLong();
|
||||
res->resName = pak->ReadString(resNameLength);
|
||||
}
|
||||
|
||||
u32 resCount = pak->ReadLong();
|
||||
ResInfoTable.resize(resCount);
|
||||
|
||||
for (u32 r = 0; r < resCount; r++)
|
||||
{
|
||||
SResInfo *res = &ResInfoTable[r];
|
||||
res->compressed = (pak->ReadLong() != 0);
|
||||
res->resType = CFourCC(*pak);
|
||||
res->resID = (u64) pak->ReadLong();
|
||||
res->size = pak->ReadLong();
|
||||
res->offset = pak->ReadLong();
|
||||
}
|
||||
}
|
||||
|
||||
CPakFile::~CPakFile()
|
||||
{
|
||||
if (pak) delete pak;
|
||||
}
|
||||
|
||||
std::vector<SNamedResource> CPakFile::getNamedResources()
|
||||
{
|
||||
return NamedResTable;
|
||||
}
|
||||
|
||||
SResInfo CPakFile::getResourceInfo(u64 assetID, CFourCC assetType)
|
||||
{
|
||||
// TODO: figure out how the game finds assets in paks, implement similar system to speed things up
|
||||
if (ResInfoTable.empty())
|
||||
return SResInfo();
|
||||
|
||||
for (u32 r = 0; r < ResInfoTable.size(); r++)
|
||||
{
|
||||
if (((u64) (ResInfoTable[r].resID & 0xFFFFFFFF) == (u64) (assetID & 0xFFFFFFFF)) && (ResInfoTable[r].resType == assetType))
|
||||
return ResInfoTable[r];
|
||||
}
|
||||
|
||||
return SResInfo();
|
||||
}
|
||||
|
||||
std::vector<u8>* CPakFile::getResource(u64 assetID, CFourCC assetType)
|
||||
{
|
||||
SResInfo info = getResourceInfo(assetID, assetType);
|
||||
|
||||
// make sure SResInfo is valid
|
||||
if ((u64) (info.resID & 0xFFFFFFFF) != (u64) (assetID & 0xFFFFFFFF)) return nullptr;
|
||||
else return getResource(info);
|
||||
}
|
||||
|
||||
std::vector<u8>* CPakFile::getResource(SResInfo& info)
|
||||
{
|
||||
pak->Seek(info.offset, SEEK_SET);
|
||||
std::vector<u8> *res_buf = new std::vector<u8>;
|
||||
|
||||
if (info.compressed)
|
||||
{
|
||||
u32 decmp_size = pak->ReadLong();
|
||||
res_buf->resize(decmp_size);
|
||||
|
||||
std::vector<u8> cmp_buf(info.size - 4);
|
||||
pak->ReadBytes(&cmp_buf[0], info.size - 4);
|
||||
|
||||
bool dcmp = decompress(cmp_buf.data(), cmp_buf.size(), res_buf->data(), res_buf->size());
|
||||
|
||||
if (!dcmp) {
|
||||
std::cout << "Error: Unable to decompress " << info.resType.ToString() << " 0x" << std::hex << std::setw(8) << std::setfill('0') << info.resID << std::dec << "\n";
|
||||
delete res_buf;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
res_buf->resize(info.size);
|
||||
pak->ReadBytes(res_buf->data(), info.size);
|
||||
}
|
||||
|
||||
return res_buf;
|
||||
}
|
||||
|
||||
bool CPakFile::decompress(u8 *src, u32 src_len, u8 *dst, u32 dst_len)
|
||||
{
|
||||
if ((src[0] == 0x78) && (src[1] == 0xda))
|
||||
{
|
||||
// zlib
|
||||
z_stream z;
|
||||
z.zalloc = Z_NULL;
|
||||
z.zfree = Z_NULL;
|
||||
z.opaque = Z_NULL;
|
||||
z.avail_in = src_len;
|
||||
z.next_in = src;
|
||||
z.avail_out = dst_len;
|
||||
z.next_out = dst;
|
||||
|
||||
s32 ret = inflateInit(&z);
|
||||
|
||||
if (ret == Z_OK)
|
||||
{
|
||||
ret = inflate(&z, Z_NO_FLUSH);
|
||||
|
||||
if ((ret == Z_OK) || (ret == Z_STREAM_END))
|
||||
ret = inflateEnd(&z);
|
||||
}
|
||||
|
||||
if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
|
||||
std::cout << "zlib error: " << std::dec << ret << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
else return true;
|
||||
}
|
||||
|
||||
else {
|
||||
// LZO
|
||||
lzo_uint decmp;
|
||||
s32 ret;
|
||||
u8 *src_end = src + src_len;
|
||||
u8 *dst_end = dst + dst_len;
|
||||
lzo_init();
|
||||
|
||||
while ((src < src_end) && (dst < dst_end)) {
|
||||
short block_size;
|
||||
memcpy(&block_size, src, 2);
|
||||
if (IOUtil::SystemEndianness == IOUtil::LittleEndian) IOUtil::SwapBytes(block_size);
|
||||
src += 2;
|
||||
|
||||
ret = lzo1x_decompress(src, block_size, dst, &decmp, LZO1X_MEM_DECOMPRESS);
|
||||
if (ret != LZO_E_OK) break;
|
||||
src += block_size;
|
||||
dst += decmp;
|
||||
}
|
||||
|
||||
if (ret != LZO_E_OK) {
|
||||
std::cout << "LZO error: " << std::dec << ret << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
else return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
31
src/Core/Resource/CPakFile.h
Normal file
31
src/Core/Resource/CPakFile.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef CPAKFILE_H
|
||||
#define CPAKFILE_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <FileIO/CFileInStream.h>
|
||||
#include "SNamedResource.h"
|
||||
#include "SResInfo.h"
|
||||
#include <vector>
|
||||
|
||||
class CPakFile
|
||||
{
|
||||
private:
|
||||
u32 version;
|
||||
std::vector<SNamedResource> NamedResTable;
|
||||
std::vector<SResInfo> ResInfoTable;
|
||||
CInputStream* pak;
|
||||
|
||||
bool decompress(u8 *src, u32 src_len, u8 *dst, u32 dst_len);
|
||||
|
||||
public:
|
||||
CPakFile();
|
||||
CPakFile(CInputStream* pakfile);
|
||||
~CPakFile();
|
||||
|
||||
std::vector<SNamedResource> getNamedResources();
|
||||
SResInfo getResourceInfo(u64 assetID, CFourCC assetType);
|
||||
std::vector<u8>* getResource(u64 assetID, CFourCC assetType);
|
||||
std::vector<u8>* getResource(SResInfo& info);
|
||||
};
|
||||
|
||||
#endif // CPAKFILE_H
|
||||
233
src/Core/Resource/CResCache.cpp
Normal file
233
src/Core/Resource/CResCache.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "CResCache.h"
|
||||
#include "Log.h"
|
||||
#include <Common/TString.h>
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// Loaders
|
||||
#include <Resource/factory/CAreaLoader.h>
|
||||
#include <Resource/factory/CAnimSetLoader.h>
|
||||
#include <Resource/factory/CCollisionLoader.h>
|
||||
#include <Resource/factory/CFontLoader.h>
|
||||
#include <Resource/factory/CModelLoader.h>
|
||||
#include <Resource/factory/CScanLoader.h>
|
||||
#include <Resource/factory/CStringLoader.h>
|
||||
#include <Resource/factory/CTextureDecoder.h>
|
||||
#include <Resource/factory/CWorldLoader.h>
|
||||
|
||||
CResCache::CResCache()
|
||||
{
|
||||
mpPak = nullptr;
|
||||
}
|
||||
|
||||
CResCache::~CResCache()
|
||||
{
|
||||
Clean();
|
||||
}
|
||||
|
||||
void CResCache::Clean()
|
||||
{
|
||||
if (mResourceCache.empty()) return;
|
||||
Log::Write("Cleaning unused resources");
|
||||
|
||||
// I couldn't get this to work properly using reverse iterators, lol.
|
||||
// Resources get cached after their dependencies, which is why I go backwards
|
||||
// while loop is to ensure -all- unused resources are cleaned. Not sure of a better way to do it.
|
||||
int numResourcesCleaned = 1;
|
||||
|
||||
while (numResourcesCleaned)
|
||||
{
|
||||
numResourcesCleaned = 0;
|
||||
|
||||
for (auto it = mResourceCache.end(); it != mResourceCache.begin();)
|
||||
{
|
||||
it--;
|
||||
if (it->second->mRefCount <= 0)
|
||||
{
|
||||
delete it->second;
|
||||
it = mResourceCache.erase(it);
|
||||
numResourcesCleaned++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::Write(std::to_string(mResourceCache.size()) + " resources loaded");
|
||||
}
|
||||
|
||||
void CResCache::SetFolder(TString path)
|
||||
{
|
||||
path.EnsureEndsWith("/");
|
||||
mResSource.Path = path;
|
||||
mResSource.Source = SResSource::Folder;
|
||||
Log::Write("Set resource folder: " + path);
|
||||
}
|
||||
|
||||
void CResCache::SetPak(const TString& path)
|
||||
{
|
||||
CFileInStream *pakfile = new CFileInStream(path.ToStdString(), IOUtil::BigEndian);
|
||||
if (!pakfile->IsValid())
|
||||
{
|
||||
Log::Error("Couldn't load pak file: " + path);
|
||||
delete pakfile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mpPak) delete mpPak;
|
||||
mpPak = new CPakFile(pakfile);
|
||||
mResSource.Path = path;
|
||||
mResSource.Source = SResSource::PakFile;
|
||||
Log::Write("Loaded pak file: " + path);
|
||||
}
|
||||
|
||||
void CResCache::SetResSource(SResSource& ResSource)
|
||||
{
|
||||
mResSource = ResSource;
|
||||
}
|
||||
|
||||
SResSource CResCache::GetResSource()
|
||||
{
|
||||
return mResSource;
|
||||
}
|
||||
|
||||
TString CResCache::GetSourcePath()
|
||||
{
|
||||
return mResSource.Path;
|
||||
}
|
||||
|
||||
CResource* CResCache::GetResource(CUniqueID ResID, CFourCC type)
|
||||
{
|
||||
if (!ResID.IsValid()) return nullptr;
|
||||
|
||||
auto got = mResourceCache.find(ResID.ToLongLong());
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
return got->second;
|
||||
|
||||
std::vector<u8> *pBuffer = nullptr;
|
||||
TString Source;
|
||||
|
||||
// Load from pak
|
||||
if (mResSource.Source == SResSource::PakFile)
|
||||
{
|
||||
pBuffer = mpPak->getResource(ResID.ToLongLong(), type);
|
||||
Source = ResID.ToString() + "." + type.ToString();
|
||||
}
|
||||
|
||||
// Load from folder
|
||||
else
|
||||
{
|
||||
Source = mResSource.Path + ResID.ToString() + "." + type.ToString();
|
||||
CFileInStream file(Source.ToStdString(), IOUtil::BigEndian);
|
||||
if (!file.IsValid())
|
||||
{
|
||||
Log::Error("Couldn't open resource: " + ResID.ToString() + "." + type.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pBuffer = new std::vector<u8>;
|
||||
pBuffer->resize(file.Size());
|
||||
file.ReadBytes(pBuffer->data(), pBuffer->size());
|
||||
}
|
||||
if (!pBuffer) return nullptr;
|
||||
|
||||
// Load resource
|
||||
CMemoryInStream mem(pBuffer->data(), pBuffer->size(), IOUtil::BigEndian);
|
||||
mem.SetSourceString(*Source.GetFileName());
|
||||
CResource *Res = nullptr;
|
||||
bool SupportedFormat = true;
|
||||
|
||||
if (type == "CMDL") Res = CModelLoader::LoadCMDL(mem);
|
||||
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(mem);
|
||||
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(mem);
|
||||
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(mem);
|
||||
else if (type == "MREA") Res = CAreaLoader::LoadMREA(mem);
|
||||
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(mem);
|
||||
else if (type == "STRG") Res = CStringLoader::LoadSTRG(mem);
|
||||
else if (type == "FONT") Res = CFontLoader::LoadFONT(mem);
|
||||
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(mem);
|
||||
else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(mem);
|
||||
else SupportedFormat = false;
|
||||
|
||||
// Log errors
|
||||
if (!SupportedFormat)
|
||||
Log::Write("Unsupported format; unable to load " + type.ToString() + " " + ResID.ToString());
|
||||
|
||||
if (!Res) Res = new CResource(); // Default for invalid resource or unsupported format
|
||||
|
||||
// Add to cache and cleanup
|
||||
Res->mID = ResID;
|
||||
Res->mResSource = Source;
|
||||
mResourceCache[ResID.ToLongLong()] = Res;
|
||||
delete pBuffer;
|
||||
return Res;
|
||||
}
|
||||
|
||||
CResource* CResCache::GetResource(const TString& ResPath)
|
||||
{
|
||||
// Since this function takes a string argument it always loads directly from a file - no pak
|
||||
CUniqueID ResID = ResPath.Hash64();
|
||||
|
||||
auto got = mResourceCache.find(ResID.ToLongLong());
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
return got->second;
|
||||
|
||||
CFileInStream file(ResPath.ToStdString(), IOUtil::BigEndian);
|
||||
if (!file.IsValid())
|
||||
{
|
||||
Log::Error("Couldn't open resource: " + ResPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Save old ResSource to restore later
|
||||
const SResSource OldSource = mResSource;
|
||||
mResSource.Source = SResSource::Folder;
|
||||
mResSource.Path = ResPath.GetFileDirectory();
|
||||
|
||||
// Load resource
|
||||
CResource *Res = nullptr;
|
||||
CFourCC type = ResPath.GetFileExtension().ToUpper();
|
||||
bool SupportedFormat = true;
|
||||
|
||||
if (type == "CMDL") Res = CModelLoader::LoadCMDL(file);
|
||||
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(file);
|
||||
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(file);
|
||||
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(file);
|
||||
else if (type == "MREA") Res = CAreaLoader::LoadMREA(file);
|
||||
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(file);
|
||||
else if (type == "FONT") Res = CFontLoader::LoadFONT(file);
|
||||
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(file);
|
||||
else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(file);
|
||||
else SupportedFormat = false;
|
||||
|
||||
if (!Res) Res = new CResource(); // Default for unsupported formats
|
||||
|
||||
// Add to cache and cleanup
|
||||
Res->mID = *ResPath;
|
||||
Res->mResSource = ResPath;
|
||||
mResourceCache[ResID.ToLongLong()] = Res;
|
||||
mResSource = OldSource;
|
||||
return Res;
|
||||
}
|
||||
|
||||
void CResCache::CacheResource(CResource *pRes)
|
||||
{
|
||||
u64 ID = pRes->ResID().ToLongLong();
|
||||
auto got = mResourceCache.find(ID);
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
mResourceCache[ID] = pRes;
|
||||
}
|
||||
|
||||
void CResCache::DeleteResource(CUniqueID ResID)
|
||||
{
|
||||
auto got = mResourceCache.find(ResID.ToLongLong());
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
{
|
||||
delete got->second;
|
||||
mResourceCache.erase(got, got);
|
||||
}
|
||||
}
|
||||
|
||||
CResCache gResCache;
|
||||
41
src/Core/Resource/CResCache.h
Normal file
41
src/Core/Resource/CResCache.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef CRESCACHE_H
|
||||
#define CRESCACHE_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <Common/TString.h>
|
||||
#include <Resource/CPakFile.h>
|
||||
#include <Resource/CResource.h>
|
||||
#include <unordered_map>
|
||||
|
||||
struct SResSource
|
||||
{
|
||||
TString Path;
|
||||
enum {
|
||||
Folder, PakFile
|
||||
} Source;
|
||||
};
|
||||
|
||||
class CResCache
|
||||
{
|
||||
std::unordered_map<u64, CResource*> mResourceCache;
|
||||
CPakFile *mpPak;
|
||||
SResSource mResSource;
|
||||
|
||||
public:
|
||||
CResCache();
|
||||
~CResCache();
|
||||
void Clean();
|
||||
void SetFolder(TString path);
|
||||
void SetPak(const TString& path);
|
||||
void SetResSource(SResSource& ResSource);
|
||||
SResSource GetResSource();
|
||||
TString GetSourcePath();
|
||||
CResource* GetResource(CUniqueID ResID, CFourCC type);
|
||||
CResource* GetResource(const TString& ResPath);
|
||||
void CacheResource(CResource *pRes);
|
||||
void DeleteResource(CUniqueID ResID);
|
||||
};
|
||||
|
||||
extern CResCache gResCache;
|
||||
|
||||
#endif // CRESCACHE_H
|
||||
110
src/Core/Resource/CResource.cpp
Normal file
110
src/Core/Resource/CResource.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "CResource.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <iostream>
|
||||
|
||||
CResource::CResource()
|
||||
{
|
||||
mRefCount = 0;
|
||||
}
|
||||
|
||||
CResource::~CResource()
|
||||
{
|
||||
}
|
||||
|
||||
TString CResource::Source()
|
||||
{
|
||||
return mResSource.GetFileName();
|
||||
}
|
||||
|
||||
TString CResource::FullSource()
|
||||
{
|
||||
return mResSource;
|
||||
}
|
||||
|
||||
CUniqueID CResource::ResID()
|
||||
{
|
||||
return mID;
|
||||
}
|
||||
|
||||
void CResource::Lock()
|
||||
{
|
||||
mRefCount++;
|
||||
}
|
||||
|
||||
void CResource::Release()
|
||||
{
|
||||
mRefCount--;
|
||||
}
|
||||
|
||||
bool CResource::IsValidResource()
|
||||
{
|
||||
return (Type() != eResource);
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
EResType CResource::ResTypeForExtension(CFourCC Extension)
|
||||
{
|
||||
Extension = Extension.ToUpper();
|
||||
|
||||
if (Extension < "FONT")
|
||||
{
|
||||
if (Extension < "CSKR")
|
||||
{
|
||||
if (Extension == "AFSM") return eStateMachine;
|
||||
if (Extension == "AGSC") return eAudioGrp;
|
||||
if (Extension == "ANCS") return eAnimSet;
|
||||
if (Extension == "ANIM") return eAnimation;
|
||||
if (Extension == "ATBL") return eAudioTable;
|
||||
if (Extension == "CAUD") return eAudioData;
|
||||
if (Extension == "CHAR") return eAnimSet;
|
||||
if (Extension == "CINF") return eSkeleton;
|
||||
if (Extension == "CMDL") return eModel;
|
||||
if (Extension == "CRSC") return eCollisionResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Extension == "CSKR") return eSkin;
|
||||
if (Extension == "CSMP") return eAudioSample;
|
||||
if (Extension == "CSNG") return eMidi;
|
||||
if (Extension == "CTWK") return eTweak;
|
||||
if (Extension == "DCLN") return eCollisionMeshGroup;
|
||||
if (Extension == "DGRP") return eDependencyGroup;
|
||||
if (Extension == "DSP ") return eMusicTrack;
|
||||
if (Extension == "DUMB") return eDataDump;
|
||||
if (Extension == "ELSC") return eParticleElectric;
|
||||
if (Extension == "EVNT") return eAnimEventData;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Extension < "PAK ")
|
||||
{
|
||||
if (Extension == "FONT") return eFont;
|
||||
if (Extension == "FRME") return eGuiFrame;
|
||||
if (Extension == "FSM2") return eStateMachine;
|
||||
if (Extension == "HINT") return eHintSystem;
|
||||
if (Extension == "MAPA") return eMapArea;
|
||||
if (Extension == "MAPW") return eMapWorld;
|
||||
if (Extension == "MAPU") return eMapUniverse;
|
||||
if (Extension == "MLVL") return eWorld;
|
||||
if (Extension == "MREA") return eArea;
|
||||
if (Extension == "NTWK") return eTweak;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Extension == "PAK ") return ePackFile;
|
||||
if (Extension == "PART") return eParticle;
|
||||
if (Extension == "PATH") return eNavMesh;
|
||||
if (Extension == "SAVW") return eSaveWorld;
|
||||
if (Extension == "SCAN") return eScan;
|
||||
if (Extension == "STRG") return eStringTable;
|
||||
if (Extension == "STRM") return eAudioStream;
|
||||
if (Extension == "SWHC") return eParticleSwoosh;
|
||||
if (Extension == "THP ") return eVideo;
|
||||
if (Extension == "TXTR") return eTexture;
|
||||
if (Extension == "WPSC") return eProjectile;
|
||||
}
|
||||
}
|
||||
|
||||
return eInvalidResType;
|
||||
}
|
||||
49
src/Core/Resource/CResource.h
Normal file
49
src/Core/Resource/CResource.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef CRESOURCE_H
|
||||
#define CRESOURCE_H
|
||||
|
||||
#include "EResType.h"
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/CUniqueID.h>
|
||||
#include <Common/types.h>
|
||||
#include <Common/TString.h>
|
||||
|
||||
class CResCache;
|
||||
|
||||
// This macro creates functions that allow us to easily identify this resource type.
|
||||
// Must be included on every CResource subclass.
|
||||
#define DECLARE_RESOURCE_TYPE(ResTypeEnum) \
|
||||
public: \
|
||||
virtual EResType Type() const \
|
||||
{ \
|
||||
return ResTypeEnum; \
|
||||
} \
|
||||
\
|
||||
static EResType StaticType() \
|
||||
{ \
|
||||
return ResTypeEnum; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
|
||||
class CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eResource)
|
||||
friend class CResCache;
|
||||
|
||||
TString mResSource;
|
||||
CUniqueID mID;
|
||||
int mRefCount;
|
||||
|
||||
public:
|
||||
CResource();
|
||||
virtual ~CResource();
|
||||
TString Source();
|
||||
TString FullSource();
|
||||
CUniqueID ResID();
|
||||
void Lock();
|
||||
void Release();
|
||||
bool IsValidResource();
|
||||
static EResType ResTypeForExtension(CFourCC Extension);
|
||||
};
|
||||
|
||||
#endif // CRESOURCE_H
|
||||
39
src/Core/Resource/CScan.cpp
Normal file
39
src/Core/Resource/CScan.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "CScan.h"
|
||||
|
||||
CScan::CScan()
|
||||
{
|
||||
mpFrame = nullptr;
|
||||
mpStringTable = nullptr;
|
||||
mIsSlow = false;
|
||||
mIsImportant = false;
|
||||
mCategory = eNone;
|
||||
}
|
||||
|
||||
CScan::~CScan()
|
||||
{
|
||||
}
|
||||
|
||||
EGame CScan::Version()
|
||||
{
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
CStringTable* CScan::ScanText()
|
||||
{
|
||||
return mpStringTable;
|
||||
}
|
||||
|
||||
bool CScan::IsImportant()
|
||||
{
|
||||
return mIsImportant;
|
||||
}
|
||||
|
||||
bool CScan::IsSlow()
|
||||
{
|
||||
return mIsSlow;
|
||||
}
|
||||
|
||||
CScan::ELogbookCategory CScan::LogbookCategory()
|
||||
{
|
||||
return mCategory;
|
||||
}
|
||||
43
src/Core/Resource/CScan.h
Normal file
43
src/Core/Resource/CScan.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef CSCAN_H
|
||||
#define CSCAN_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CStringTable.h"
|
||||
#include "EFormatVersion.h"
|
||||
#include <Core/TResPtr.h>
|
||||
|
||||
class CScan : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eScan)
|
||||
friend class CScanLoader;
|
||||
|
||||
public:
|
||||
// This likely needs revising when MP2/MP3 support is added
|
||||
enum ELogbookCategory
|
||||
{
|
||||
eNone,
|
||||
ePirateData,
|
||||
eChozoLore,
|
||||
eCreatures,
|
||||
eResearch
|
||||
};
|
||||
|
||||
private:
|
||||
EGame mVersion;
|
||||
TResPtr<CResource> mpFrame;
|
||||
TResPtr<CStringTable> mpStringTable;
|
||||
bool mIsSlow;
|
||||
bool mIsImportant;
|
||||
ELogbookCategory mCategory;
|
||||
|
||||
public:
|
||||
CScan();
|
||||
~CScan();
|
||||
EGame Version();
|
||||
CStringTable* ScanText();
|
||||
bool IsImportant();
|
||||
bool IsSlow();
|
||||
ELogbookCategory LogbookCategory();
|
||||
};
|
||||
|
||||
#endif // CSCAN_H
|
||||
52
src/Core/Resource/CStringTable.cpp
Normal file
52
src/Core/Resource/CStringTable.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "CStringTable.h"
|
||||
|
||||
CStringTable::CStringTable() : CResource()
|
||||
{
|
||||
}
|
||||
|
||||
CStringTable::~CStringTable()
|
||||
{
|
||||
}
|
||||
|
||||
CResource* CStringTable::MakeCopy(CResCache*)
|
||||
{
|
||||
// Not using parameter 1 (CResCache* - pResCache)
|
||||
return new CStringTable(*this);
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
u32 CStringTable::GetStringCount()
|
||||
{
|
||||
return mNumStrings;
|
||||
}
|
||||
|
||||
u32 CStringTable::GetLangCount()
|
||||
{
|
||||
return mLangTables.size();
|
||||
}
|
||||
|
||||
CFourCC CStringTable::GetLangTag(u32 Index)
|
||||
{
|
||||
return mLangTables[Index].Language;
|
||||
}
|
||||
|
||||
TWideString CStringTable::GetString(CFourCC Lang, u32 StringIndex)
|
||||
{
|
||||
for (u32 iLang = 0; iLang < GetLangCount(); iLang++)
|
||||
{
|
||||
if (GetLangTag(iLang) == Lang)
|
||||
return GetString(iLang, StringIndex);
|
||||
}
|
||||
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
TWideString CStringTable::GetString(u32 LangIndex, u32 StringIndex)
|
||||
{
|
||||
return mLangTables[LangIndex].Strings[StringIndex];
|
||||
}
|
||||
|
||||
TString CStringTable::GetStringName(u32 StringIndex)
|
||||
{
|
||||
return mStringNames[StringIndex];
|
||||
}
|
||||
39
src/Core/Resource/CStringTable.h
Normal file
39
src/Core/Resource/CStringTable.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef CSTRINGTABLE_H
|
||||
#define CSTRINGTABLE_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include <Common/types.h>
|
||||
#include <Common/CFourCC.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class CStringTable : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eStringTable)
|
||||
friend class CStringLoader;
|
||||
|
||||
std::vector<TString> mStringNames;
|
||||
u32 mNumStrings;
|
||||
|
||||
struct SLangTable
|
||||
{
|
||||
CFourCC Language;
|
||||
std::vector<TWideString> Strings;
|
||||
};
|
||||
std::vector<SLangTable> mLangTables;
|
||||
|
||||
public:
|
||||
CStringTable();
|
||||
~CStringTable();
|
||||
CResource* MakeCopy(CResCache *pCopyCache);
|
||||
|
||||
// Getters
|
||||
u32 GetStringCount();
|
||||
u32 GetLangCount();
|
||||
CFourCC GetLangTag(u32 Index);
|
||||
TWideString GetString(CFourCC Lang, u32 StringIndex);
|
||||
TWideString GetString(u32 LangIndex, u32 StringIndex);
|
||||
TString GetStringName(u32 StringIndex);
|
||||
};
|
||||
|
||||
#endif // CSTRINGTABLE_H
|
||||
375
src/Core/Resource/CTexture.cpp
Normal file
375
src/Core/Resource/CTexture.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
#include "CTexture.h"
|
||||
|
||||
CTexture::CTexture() : CResource()
|
||||
{
|
||||
mTexelFormat = eRGBA8;
|
||||
mSourceTexelFormat = eRGBA8;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mNumMipMaps = 0;
|
||||
mLinearSize = 0;
|
||||
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
|
||||
CTexture::CTexture(const CTexture& Source)
|
||||
{
|
||||
mTexelFormat = Source.mTexelFormat;
|
||||
mSourceTexelFormat = Source.mSourceTexelFormat;
|
||||
mWidth = Source.mWidth;
|
||||
mHeight = Source.mHeight;
|
||||
mLinearSize = Source.mLinearSize;
|
||||
|
||||
mBufferExists = Source.mBufferExists;
|
||||
mImgDataSize = Source.mImgDataSize;
|
||||
mImgDataBuffer = new u8[mImgDataSize];
|
||||
memcpy(mImgDataBuffer, Source.mImgDataBuffer, mImgDataSize);
|
||||
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
|
||||
CTexture::CTexture(u32 Width, u32 Height)
|
||||
{
|
||||
mTexelFormat = eRGBA8;
|
||||
mSourceTexelFormat = eRGBA8;
|
||||
mWidth = (u16) Width;
|
||||
mHeight = (u16) Height;
|
||||
mNumMipMaps = 1;
|
||||
mLinearSize = Width * Height * 4;
|
||||
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
|
||||
CTexture::~CTexture()
|
||||
{
|
||||
DeleteBuffers();
|
||||
}
|
||||
|
||||
bool CTexture::BufferGL()
|
||||
{
|
||||
glGenTextures(1, &mTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||
|
||||
GLenum GLFormat, GLType;
|
||||
bool IsCompressed = false;
|
||||
|
||||
switch (mTexelFormat) {
|
||||
|
||||
case eLuminance:
|
||||
GLFormat = GL_LUMINANCE;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case eLuminanceAlpha:
|
||||
GLFormat = GL_LUMINANCE_ALPHA;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case eRGB565:
|
||||
GLFormat = GL_RGB;
|
||||
GLType = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
case eRGBA4:
|
||||
GLFormat = GL_RGBA;
|
||||
GLType = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
break;
|
||||
case eRGBA8:
|
||||
GLFormat = GL_RGBA;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case eDXT1:
|
||||
GLFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
IsCompressed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// The smallest mipmaps are probably not being loaded correctly, because mipmaps in GX textures have a minimum size depending on the format, and these don't.
|
||||
// Not sure specifically what accomodations should be made to fix that though so whatever.
|
||||
u32 MipSize = mLinearSize;
|
||||
u32 MipOffset = 0;
|
||||
u16 MipW = mWidth, MipH = mHeight;
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
GLvoid *pData = (mBufferExists) ? (mImgDataBuffer + MipOffset) : NULL;
|
||||
|
||||
if (!IsCompressed)
|
||||
glTexImage2D(GL_TEXTURE_2D, iMip, GLFormat, MipW, MipH, 0, GLFormat, GLType, pData);
|
||||
else
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, iMip, GLFormat, MipW, MipH, 0, MipSize, pData);
|
||||
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
MipOffset += MipSize;
|
||||
MipSize /= 4;
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mNumMipMaps - 1);
|
||||
|
||||
// Linear filtering on mipmaps:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
|
||||
// Anisotropic filtering:
|
||||
float MaxAnisotropy;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisotropy);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, MaxAnisotropy);
|
||||
|
||||
mGLBufferExists = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTexture::Bind(u32 GLTextureUnit)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + GLTextureUnit);
|
||||
|
||||
if (!mGLBufferExists)
|
||||
BufferGL();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||
}
|
||||
|
||||
void CTexture::Resize(u32 Width, u32 Height)
|
||||
{
|
||||
if ((mWidth != Width) || (mHeight != Height))
|
||||
{
|
||||
DeleteBuffers();
|
||||
mWidth = (u16) Width;
|
||||
mHeight = (u16) Height;
|
||||
mNumMipMaps = 1;
|
||||
}
|
||||
}
|
||||
|
||||
float CTexture::ReadTexelAlpha(const CVector2f& TexCoord)
|
||||
{
|
||||
// todo: support texel formats other than DXT1
|
||||
// DXT1 is definitely the most complicated one anyway; try reusing CTextureDecoder functions for other formats
|
||||
u32 TexelX = (u32) ((mWidth - 1) * TexCoord.x);
|
||||
u32 TexelY = (u32) ((mHeight - 1) * (1.f - fmodf(TexCoord.y, 1.f)));
|
||||
|
||||
if (mTexelFormat == eDXT1 && mBufferExists)
|
||||
{
|
||||
CMemoryInStream Buffer(mImgDataBuffer, mImgDataSize, IOUtil::SystemEndianness);
|
||||
|
||||
// 8 bytes per 4x4 16-pixel block, left-to-right top-to-bottom
|
||||
u32 BlockIdxX = TexelX / 4;
|
||||
u32 BlockIdxY = TexelY / 4;
|
||||
u32 BlocksPerRow = mWidth / 4;
|
||||
|
||||
u32 BufferPos = (8 * BlockIdxX) + (8 * BlockIdxY * BlocksPerRow);
|
||||
Buffer.Seek(BufferPos, SEEK_SET);
|
||||
|
||||
u16 PaletteA = Buffer.ReadShort();
|
||||
u16 PaletteB = Buffer.ReadShort();
|
||||
|
||||
if (PaletteA > PaletteB)
|
||||
{
|
||||
// No palette colors have alpha
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
// We only care about alpha, which is only present on palette index 3.
|
||||
// We don't need to calculate/decode the actual palette colors.
|
||||
u32 BlockCol = (TexelX & 0xF) / 4;
|
||||
u32 BlockRow = (TexelY & 0xF) / 4;
|
||||
|
||||
Buffer.Seek(BlockRow, SEEK_CUR);
|
||||
u8 Row = Buffer.ReadByte();
|
||||
u8 Shift = (u8) (6 - (BlockCol * 2));
|
||||
u8 PaletteIndex = (Row >> Shift) & 0x3;
|
||||
return (PaletteIndex == 3 ? 0.f : 1.f);
|
||||
}
|
||||
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
bool CTexture::WriteDDS(COutputStream& out)
|
||||
{
|
||||
if (!out.IsValid()) return false;
|
||||
|
||||
CopyGLBuffer();
|
||||
|
||||
out.WriteString("DDS ", 4); // "DDS " fourCC
|
||||
out.WriteLong(0x7C); // dwSize
|
||||
out.WriteLong(0x21007); // dwFlags
|
||||
out.WriteLong(mHeight); // dwHeight
|
||||
out.WriteLong(mWidth); // dwWidth
|
||||
out.WriteLong(mLinearSize); // dwPitchOrLinearSize
|
||||
out.WriteLong(0); // dwDepth
|
||||
out.WriteLong(mNumMipMaps - 1); // dwMipMapCount
|
||||
|
||||
for (u32 i = 0; i < 11; i++)
|
||||
out.WriteLong(0); // dwReserved1[11]
|
||||
|
||||
// DDS_PIXELFORMAT
|
||||
out.WriteLong(32); // DDS_PIXELFORMAT.dwSize
|
||||
|
||||
u32 pfFlags = 0, pfBpp = 0, pfRBitMask = 0, pfGBitMask = 0, pfBBitMask = 0, pfABitMask = 0;
|
||||
switch (mTexelFormat) {
|
||||
case eLuminance:
|
||||
pfFlags = 0x20000;
|
||||
pfBpp = 0x8;
|
||||
pfRBitMask = 0xFF;
|
||||
break;
|
||||
case eLuminanceAlpha:
|
||||
pfFlags = 0x20001;
|
||||
pfBpp = 0x10;
|
||||
pfRBitMask = 0x00FF;
|
||||
pfABitMask = 0xFF00;
|
||||
break;
|
||||
case eRGBA4:
|
||||
pfFlags = 0x41;
|
||||
pfBpp = 0x10;
|
||||
pfRBitMask = 0x0F00;
|
||||
pfGBitMask = 0x00F0;
|
||||
pfBBitMask = 0x000F;
|
||||
pfABitMask = 0xF000;
|
||||
break;
|
||||
case eRGB565:
|
||||
pfFlags = 0x40;
|
||||
pfBpp = 0x10;
|
||||
pfRBitMask = 0xF800;
|
||||
pfGBitMask = 0x7E0;
|
||||
pfBBitMask = 0x1F;
|
||||
break;
|
||||
case eRGBA8:
|
||||
pfFlags = 0x41;
|
||||
pfBpp = 0x20;
|
||||
pfRBitMask = 0x00FF0000;
|
||||
pfGBitMask = 0x0000FF00;
|
||||
pfBBitMask = 0x000000FF;
|
||||
pfABitMask = 0xFF000000;
|
||||
break;
|
||||
case eDXT1:
|
||||
pfFlags = 0x4;
|
||||
break;
|
||||
}
|
||||
|
||||
out.WriteLong(pfFlags); // DDS_PIXELFORMAT.dwFlags
|
||||
(mTexelFormat == eDXT1) ? out.WriteString("DXT1", 4) : out.WriteLong(0); // DDS_PIXELFORMAT.dwFourCC
|
||||
out.WriteLong(pfBpp); // DDS_PIXELFORMAT.dwRGBBitCount
|
||||
out.WriteLong(pfRBitMask); // DDS_PIXELFORMAT.dwRBitMask
|
||||
out.WriteLong(pfGBitMask); // DDS_PIXELFORMAT.dwGBitMask
|
||||
out.WriteLong(pfBBitMask); // DDS_PIXELFORMAT.dwBBitMask
|
||||
out.WriteLong(pfABitMask); // DDS_PIXELFORMAT.dwABitMask
|
||||
|
||||
out.WriteLong(0x401000); // dwCaps
|
||||
out.WriteLong(0); // dwCaps2
|
||||
out.WriteLong(0); // dwCaps3
|
||||
out.WriteLong(0); // dwCaps4
|
||||
out.WriteLong(0); // dwReserved2
|
||||
|
||||
out.WriteBytes(mImgDataBuffer, mImgDataSize); // Image data
|
||||
return true;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
u32 CTexture::FormatBPP(ETexelFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case eGX_I4: return 4;
|
||||
case eGX_I8: return 8;
|
||||
case eGX_IA4: return 8;
|
||||
case eGX_IA8: return 16;
|
||||
case eGX_C4: return 4;
|
||||
case eGX_C8: return 8;
|
||||
case eGX_RGB565: return 16;
|
||||
case eGX_RGB5A3: return 16;
|
||||
case eGX_RGBA8: return 32;
|
||||
case eGX_CMPR: return 4;
|
||||
case eLuminance: return 8;
|
||||
case eLuminanceAlpha: return 16;
|
||||
case eRGBA4: return 16;
|
||||
case eRGB565: return 16;
|
||||
case eRGBA8: return 32;
|
||||
case eDXT1: return 4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ PRIVATE ************
|
||||
void CTexture::CalcLinearSize()
|
||||
{
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
mLinearSize = (u32) (mWidth * mHeight * BytesPerPixel);
|
||||
}
|
||||
|
||||
u32 CTexture::CalcTotalSize()
|
||||
{
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
u32 MipW = mWidth, MipH = mHeight;
|
||||
u32 Size = 0;
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
Size += (u32) (MipW * MipH * BytesPerPixel);
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
}
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
void CTexture::CopyGLBuffer()
|
||||
{
|
||||
if (!mGLBufferExists) return;
|
||||
|
||||
// Clear existing buffer
|
||||
if (mBufferExists)
|
||||
{
|
||||
delete[] mImgDataBuffer;
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
}
|
||||
|
||||
// Calculate buffer size
|
||||
mImgDataSize = CalcTotalSize();
|
||||
mImgDataBuffer = new u8[mImgDataSize];
|
||||
mBufferExists = true;
|
||||
|
||||
// Get texture
|
||||
u32 MipW = mWidth, MipH = mHeight, MipOffset = 0;
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
void *pData = mImgDataBuffer + MipOffset;
|
||||
|
||||
glGetTexImage(GL_TEXTURE_2D, iMip, GL_RGBA, GL_UNSIGNED_BYTE, pData);
|
||||
|
||||
MipOffset += (u32) (MipW * MipH * BytesPerPixel);
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
}
|
||||
|
||||
mTexelFormat = eRGBA8;
|
||||
mLinearSize = mWidth * mHeight * 4;
|
||||
}
|
||||
|
||||
void CTexture::DeleteBuffers()
|
||||
{
|
||||
if (mBufferExists)
|
||||
{
|
||||
delete[] mImgDataBuffer;
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
}
|
||||
|
||||
if (mGLBufferExists)
|
||||
{
|
||||
glDeleteTextures(1, &mTextureID);
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
}
|
||||
85
src/Core/Resource/CTexture.h
Normal file
85
src/Core/Resource/CTexture.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef CTEXTURE_H
|
||||
#define CTEXTURE_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <Common/CVector2f.h>
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <gl/glew.h>
|
||||
#include "CResource.h"
|
||||
#include "ETexelFormat.h"
|
||||
|
||||
class CTexture : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eTexture)
|
||||
friend class CTextureDecoder;
|
||||
friend class CTextureEncoder;
|
||||
|
||||
ETexelFormat mTexelFormat; // Format of decoded image data
|
||||
ETexelFormat mSourceTexelFormat; // Format of input TXTR file
|
||||
u16 mWidth, mHeight; // Image dimensions
|
||||
u32 mNumMipMaps; // The number of mipmaps this texture has
|
||||
u32 mLinearSize; // The size of the top level mipmap, in bytes
|
||||
|
||||
bool mBufferExists; // Boolean that indicates whether image data buffer has valid data
|
||||
u8 *mImgDataBuffer; // Pointer to image data buffer
|
||||
u32 mImgDataSize; // Size of image data buffer
|
||||
|
||||
bool mGLBufferExists; // Boolean that indicates whether GL buffer has valid data
|
||||
GLuint mTextureID; // ID for texture GL buffer
|
||||
|
||||
public:
|
||||
CTexture();
|
||||
CTexture(const CTexture& Source);
|
||||
CTexture(u32 Width, u32 Height);
|
||||
~CTexture();
|
||||
|
||||
bool BufferGL();
|
||||
void Bind(u32 GLTextureUnit);
|
||||
void Resize(u32 Width, u32 Height);
|
||||
float ReadTexelAlpha(const CVector2f& TexCoord);
|
||||
bool WriteDDS(COutputStream& out);
|
||||
|
||||
// Getters
|
||||
ETexelFormat TexelFormat();
|
||||
ETexelFormat SourceTexelFormat();
|
||||
u32 Width();
|
||||
u32 Height();
|
||||
u32 NumMipMaps();
|
||||
GLuint TextureID();
|
||||
|
||||
// Static
|
||||
static u32 FormatBPP(ETexelFormat Format);
|
||||
|
||||
// Private
|
||||
private:
|
||||
void CalcLinearSize();
|
||||
u32 CalcTotalSize();
|
||||
void CopyGLBuffer();
|
||||
void DeleteBuffers();
|
||||
};
|
||||
|
||||
inline ETexelFormat CTexture::TexelFormat() {
|
||||
return mTexelFormat;
|
||||
}
|
||||
|
||||
inline ETexelFormat CTexture::SourceTexelFormat() {
|
||||
return mSourceTexelFormat;
|
||||
}
|
||||
|
||||
inline u32 CTexture::Width() {
|
||||
return (u32) mWidth;
|
||||
}
|
||||
|
||||
inline u32 CTexture::Height() {
|
||||
return (u32) mHeight;
|
||||
}
|
||||
|
||||
inline u32 CTexture::NumMipMaps() {
|
||||
return mNumMipMaps;
|
||||
}
|
||||
|
||||
inline GLuint CTexture::TextureID() {
|
||||
return mTextureID;
|
||||
}
|
||||
|
||||
#endif // CTEXTURE_H
|
||||
97
src/Core/Resource/CWorld.cpp
Normal file
97
src/Core/Resource/CWorld.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "CWorld.h"
|
||||
#include "script/CScriptLayer.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
CWorld::CWorld() : CResource()
|
||||
{
|
||||
mWorldVersion = eUnknownVersion;
|
||||
mpWorldName = nullptr;
|
||||
mpDarkWorldName = nullptr;
|
||||
mpSaveWorld = nullptr;
|
||||
mpDefaultSkybox = nullptr;
|
||||
mpMapWorld = nullptr;
|
||||
}
|
||||
|
||||
CWorld::~CWorld()
|
||||
{
|
||||
}
|
||||
|
||||
void CWorld::SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex)
|
||||
{
|
||||
// The AreaIndex parameter is a placeholder until an improved world loader is implemented.
|
||||
// For now it's the easiest/fastest way to do this because this function is called from
|
||||
// the start window and the start window already knows the area index.
|
||||
SArea& AreaInfo = mAreas[AreaIndex];
|
||||
|
||||
for (u32 iLyr = 0; iLyr < pArea->GetScriptLayerCount(); iLyr++)
|
||||
{
|
||||
CScriptLayer *pLayer = pArea->GetScriptLayer(iLyr);
|
||||
SArea::SLayer& LayerInfo = AreaInfo.Layers[iLyr];
|
||||
|
||||
pLayer->SetName(LayerInfo.LayerName);
|
||||
pLayer->SetActive(LayerInfo.EnabledByDefault);
|
||||
}
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
// World
|
||||
EGame CWorld::Version()
|
||||
{
|
||||
return mWorldVersion;
|
||||
}
|
||||
|
||||
CStringTable* CWorld::GetWorldName()
|
||||
{
|
||||
return mpWorldName;
|
||||
}
|
||||
|
||||
CStringTable* CWorld::GetDarkWorldName()
|
||||
{
|
||||
return mpDarkWorldName;
|
||||
}
|
||||
|
||||
CResource* CWorld::GetSaveWorld()
|
||||
{
|
||||
return mpSaveWorld;
|
||||
}
|
||||
|
||||
CModel* CWorld::GetDefaultSkybox()
|
||||
{
|
||||
return mpDefaultSkybox;
|
||||
}
|
||||
|
||||
CResource* CWorld::GetMapWorld()
|
||||
{
|
||||
return mpMapWorld;
|
||||
}
|
||||
|
||||
// Area
|
||||
u32 CWorld::GetNumAreas()
|
||||
{
|
||||
return mAreas.size();
|
||||
}
|
||||
|
||||
u64 CWorld::GetAreaResourceID(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].FileID;
|
||||
}
|
||||
|
||||
u32 CWorld::GetAreaAttachedCount(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].AttachedAreaIDs.size();
|
||||
}
|
||||
|
||||
u32 CWorld::GetAreaAttachedID(u32 AreaIndex, u32 AttachedIndex)
|
||||
{
|
||||
return (u32) mAreas[AreaIndex].AttachedAreaIDs[AttachedIndex];
|
||||
}
|
||||
|
||||
TString CWorld::GetAreaInternalName(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].InternalName;
|
||||
}
|
||||
|
||||
CStringTable* CWorld::GetAreaName(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].pAreaName;
|
||||
}
|
||||
105
src/Core/Resource/CWorld.h
Normal file
105
src/Core/Resource/CWorld.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#ifndef CWORLD_H
|
||||
#define CWORLD_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CGameArea.h"
|
||||
#include "CStringTable.h"
|
||||
#include "SDependency.h"
|
||||
#include "model/CModel.h"
|
||||
#include <Common/CTransform4f.h>
|
||||
|
||||
class CWorld : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eWorld)
|
||||
friend class CWorldLoader;
|
||||
|
||||
// Instances of CResource pointers are placeholders for unimplemented resource types (eg CMapWorld)
|
||||
EGame mWorldVersion;
|
||||
TResPtr<CStringTable> mpWorldName;
|
||||
TResPtr<CStringTable> mpDarkWorldName;
|
||||
TResPtr<CResource> mpSaveWorld;
|
||||
TResPtr<CModel> mpDefaultSkybox;
|
||||
TResPtr<CResource> mpMapWorld;
|
||||
|
||||
u32 mUnknown1;
|
||||
u32 mUnknownAreas;
|
||||
|
||||
u32 mUnknownAGSC;
|
||||
struct SAudioGrp
|
||||
{
|
||||
u32 ResID;
|
||||
u32 Unknown;
|
||||
};
|
||||
std::vector<SAudioGrp> mAudioGrps;
|
||||
|
||||
struct SMemoryRelay
|
||||
{
|
||||
u32 InstanceID;
|
||||
u32 TargetID;
|
||||
u16 Message;
|
||||
u8 Unknown;
|
||||
};
|
||||
std::vector<SMemoryRelay> mMemoryRelays;
|
||||
|
||||
struct SArea
|
||||
{
|
||||
TString InternalName;
|
||||
TResPtr<CStringTable> pAreaName;
|
||||
CTransform4f Transform;
|
||||
CAABox AetherBox;
|
||||
u64 FileID; // Loading every single area as a CResource would be a very bad idea
|
||||
u64 AreaID;
|
||||
|
||||
std::vector<u16> AttachedAreaIDs;
|
||||
std::vector<SDependency> Dependencies;
|
||||
std::vector<TString> RelFilenames;
|
||||
std::vector<u32> RelOffsets;
|
||||
u32 CommonDependenciesStart;
|
||||
|
||||
struct SDock
|
||||
{
|
||||
struct SConnectingDock
|
||||
{
|
||||
u32 AreaIndex;
|
||||
u32 DockIndex;
|
||||
};
|
||||
std::vector<SConnectingDock> ConnectingDocks;
|
||||
CVector3f DockCoordinates[4];
|
||||
};
|
||||
std::vector<SDock> Docks;
|
||||
|
||||
struct SLayer
|
||||
{
|
||||
TString LayerName;
|
||||
bool EnabledByDefault;
|
||||
u8 LayerID[16];
|
||||
u32 LayerDependenciesStart; // Offset into Dependencies vector
|
||||
};
|
||||
std::vector<SLayer> Layers;
|
||||
};
|
||||
std::vector<SArea> mAreas;
|
||||
|
||||
|
||||
public:
|
||||
CWorld();
|
||||
~CWorld();
|
||||
|
||||
void SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex);
|
||||
|
||||
// Setters
|
||||
EGame Version();
|
||||
CStringTable* GetWorldName();
|
||||
CStringTable* GetDarkWorldName();
|
||||
CResource* GetSaveWorld();
|
||||
CModel* GetDefaultSkybox();
|
||||
CResource* GetMapWorld();
|
||||
|
||||
u32 GetNumAreas();
|
||||
u64 GetAreaResourceID(u32 AreaIndex);
|
||||
u32 GetAreaAttachedCount(u32 AreaIndex);
|
||||
u32 GetAreaAttachedID(u32 AreaIndex, u32 AttachedIndex);
|
||||
TString GetAreaInternalName(u32 AreaIndex);
|
||||
CStringTable* GetAreaName(u32 AreaIndex);
|
||||
};
|
||||
|
||||
#endif // CWORLD_H
|
||||
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
|
||||
18
src/Core/Resource/EFormatVersion.h
Normal file
18
src/Core/Resource/EFormatVersion.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef EFORMATVERSION_H
|
||||
#define EFORMATVERSION_H
|
||||
|
||||
// Global version enum that can be easily shared between loaders
|
||||
enum EGame
|
||||
{
|
||||
ePrimeDemo = 0,
|
||||
ePrime = 1,
|
||||
eEchoesDemo = 2,
|
||||
eEchoes = 3,
|
||||
eCorruptionProto = 4,
|
||||
eCorruption = 5,
|
||||
eReturns = 6,
|
||||
TropicalFreeze = 7,
|
||||
eUnknownVersion = -1
|
||||
};
|
||||
|
||||
#endif // EFORMATVERSION_H
|
||||
51
src/Core/Resource/EResType.h
Normal file
51
src/Core/Resource/EResType.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef ERESTYPE
|
||||
#define ERESTYPE
|
||||
|
||||
enum EResType
|
||||
{
|
||||
eAnimation = 0,
|
||||
eAnimEventData = 1,
|
||||
eAnimSet = 2,
|
||||
eArea = 3,
|
||||
eAudioData = 4,
|
||||
eAudioGrp = 5,
|
||||
eAudioSample = 6,
|
||||
eAudioStream = 7,
|
||||
eAudioTable = 8,
|
||||
eCharacter = 9,
|
||||
eCollisionMeshGroup = 10,
|
||||
eCollisionResponse = 11,
|
||||
eDataDump = 12,
|
||||
eDecal = 13,
|
||||
eDependencyGroup = 14,
|
||||
eFont = 15,
|
||||
eGuiFrame = 16,
|
||||
eHintSystem = 17,
|
||||
eInvalidResType = 18,
|
||||
eMapArea = 19,
|
||||
eMapWorld = 20,
|
||||
eMapUniverse = 21,
|
||||
eMidi = 22,
|
||||
eModel = 23,
|
||||
eMusicTrack = 24,
|
||||
eNavMesh = 25,
|
||||
ePackFile = 26,
|
||||
eParticle = 27,
|
||||
eParticleElectric = 28,
|
||||
eParticleSwoosh = 29,
|
||||
eProjectile = 30,
|
||||
eResource = 31,
|
||||
eSaveWorld = 32,
|
||||
eScan = 33,
|
||||
eSkeleton = 34,
|
||||
eSkin = 35,
|
||||
eStateMachine = 36,
|
||||
eStringTable = 37,
|
||||
eTexture = 38,
|
||||
eTweak = 39,
|
||||
eVideo = 40,
|
||||
eWorld = 41
|
||||
};
|
||||
|
||||
#endif // ERESTYPE
|
||||
|
||||
106
src/Core/Resource/ETevEnums.h
Normal file
106
src/Core/Resource/ETevEnums.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef ETEVENUMS
|
||||
#define ETEVENUMS
|
||||
|
||||
enum ETevColorInput
|
||||
{
|
||||
ePrevRGB = 0x0,
|
||||
ePrevAAA = 0x1,
|
||||
eColor0RGB = 0x2,
|
||||
eColor0AAA = 0x3,
|
||||
eColor1RGB = 0x4,
|
||||
eColor1AAA = 0x5,
|
||||
eColor2RGB = 0x6,
|
||||
eColor2AAA = 0x7,
|
||||
eTextureRGB = 0x8,
|
||||
eTextureAAA = 0x9,
|
||||
eRasRGB = 0xA,
|
||||
eRasAAA = 0xB,
|
||||
eOneRGB = 0xC,
|
||||
eHalfRGB = 0xD,
|
||||
eKonstRGB = 0xE,
|
||||
eZeroRGB = 0xF
|
||||
};
|
||||
|
||||
enum ETevAlphaInput
|
||||
{
|
||||
ePrevAlpha = 0x0,
|
||||
eColor0Alpha = 0x1,
|
||||
eColor1Alpha = 0x2,
|
||||
eColor2Alpha = 0x3,
|
||||
eTextureAlpha = 0x4,
|
||||
eRasAlpha = 0x5,
|
||||
eKonstAlpha = 0x6,
|
||||
eZeroAlpha = 0x7
|
||||
};
|
||||
|
||||
enum ETevOutput
|
||||
{
|
||||
ePrevReg = 0x0,
|
||||
eColor0Reg = 0x1,
|
||||
eColor1Reg = 0x2,
|
||||
eColor2Reg = 0x3
|
||||
};
|
||||
|
||||
enum ETevKSel
|
||||
{
|
||||
eKonstOne = 0x0,
|
||||
eKonstSevenEighths = 0x1,
|
||||
eKonstThreeFourths = 0x2,
|
||||
eKonstFiveEighths = 0x3,
|
||||
eKonstOneHalf = 0x4,
|
||||
eKonstThreeEighths = 0x5,
|
||||
eKonstOneFourth = 0x6,
|
||||
eKonstOneEighth = 0x7,
|
||||
eKonst0_RGB = 0xC,
|
||||
eKonst1_RGB = 0xD,
|
||||
eKonst2_RGB = 0xE,
|
||||
eKonst3_RGB = 0xF,
|
||||
eKonst0_R = 0x10,
|
||||
eKonst1_R = 0x11,
|
||||
eKonst2_R = 0x12,
|
||||
eKonst3_R = 0x13,
|
||||
eKonst0_G = 0x14,
|
||||
eKonst1_G = 0x15,
|
||||
eKonst2_G = 0x16,
|
||||
eKonst3_G = 0x17,
|
||||
eKonst0_B = 0x18,
|
||||
eKonst1_B = 0x19,
|
||||
eKonst2_B = 0x1A,
|
||||
eKonst3_B = 0x1B,
|
||||
eKonst0_A = 0x1C,
|
||||
eKonst1_A = 0x1D,
|
||||
eKonst2_A = 0x1E,
|
||||
eKonst3_A = 0x1F
|
||||
};
|
||||
|
||||
enum ETevRasSel
|
||||
{
|
||||
eRasColor0 = 0x0,
|
||||
eRasColor1 = 0x1,
|
||||
eRasAlpha0 = 0x2,
|
||||
eRasAlpha1 = 0x3,
|
||||
eRasColor0A0 = 0x4,
|
||||
eRasColor1A1 = 0x5,
|
||||
eRasColorZero = 0x6,
|
||||
eRasAlphaBump = 0x7,
|
||||
eRasAlphaBumpN = 0x8,
|
||||
eRasColorNull = 0xFF
|
||||
};
|
||||
|
||||
enum EUVAnimMode
|
||||
{
|
||||
eInverseMV = 0x0,
|
||||
eInverseMVTranslated = 0x1,
|
||||
eUVScroll = 0x2,
|
||||
eUVRotation = 0x3,
|
||||
eHFilmstrip = 0x4,
|
||||
eVFilmstrip = 0x5,
|
||||
eModelMatrix = 0x6,
|
||||
eConvolutedModeA = 0x7,
|
||||
eConvolutedModeB = 0x8,
|
||||
eSimpleMode = 0xA,
|
||||
eNoUVAnim = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
#endif // ETEVENUMS
|
||||
|
||||
38
src/Core/Resource/ETexelFormat.h
Normal file
38
src/Core/Resource/ETexelFormat.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef ETEXELFORMAT
|
||||
#define ETEXELFORMAT
|
||||
|
||||
// ETexelFormat - supported internal formats for decoded textures
|
||||
enum ETexelFormat
|
||||
{
|
||||
// Supported texel formats in GX using Retro's numbering
|
||||
eGX_I4 = 0x0,
|
||||
eGX_I8 = 0x1,
|
||||
eGX_IA4 = 0x2,
|
||||
eGX_IA8 = 0x3,
|
||||
eGX_C4 = 0x4,
|
||||
eGX_C8 = 0x5,
|
||||
eGX_C14x2 = 0x6,
|
||||
eGX_RGB565 = 0x7,
|
||||
eGX_RGB5A3 = 0x8,
|
||||
eGX_RGBA8 = 0x9,
|
||||
eGX_CMPR = 0xA,
|
||||
// Supported internal texel formats for decoded textures
|
||||
eLuminance,
|
||||
eLuminanceAlpha,
|
||||
eRGBA4,
|
||||
eRGB565,
|
||||
eRGBA8,
|
||||
eDXT1,
|
||||
eInvalidTexelFormat
|
||||
};
|
||||
|
||||
// EGXPaletteFormat - GX's supported palette texel formats for C4/C8
|
||||
enum EGXPaletteFormat
|
||||
{
|
||||
ePalette_IA8 = 0,
|
||||
ePalette_RGB565 = 1,
|
||||
ePalette_RGB5A3 = 2
|
||||
};
|
||||
|
||||
#endif // ETEXELFORMAT
|
||||
|
||||
196
src/Core/Resource/Factory/CAnimSetLoader.cpp
Normal file
196
src/Core/Resource/Factory/CAnimSetLoader.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
#include "CAnimSetLoader.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
|
||||
CAnimSetLoader::CAnimSetLoader()
|
||||
{
|
||||
}
|
||||
|
||||
CAnimSet* CAnimSetLoader::LoadCorruptionCHAR(CInputStream& CHAR)
|
||||
{
|
||||
// For now, we only read enough to fetch the model
|
||||
CHAR.Seek(0x1, SEEK_CUR);
|
||||
set->nodes.resize(1);
|
||||
CAnimSet::SNode& node = set->nodes[0];
|
||||
|
||||
node.name = CHAR.ReadString();
|
||||
node.model = gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
|
||||
return set;
|
||||
}
|
||||
|
||||
CAnimSet* CAnimSetLoader::LoadReturnsCHAR(CInputStream& CHAR)
|
||||
{
|
||||
// For now, we only read enough to fetch the model
|
||||
CHAR.Seek(0x16, SEEK_CUR);
|
||||
set->nodes.resize(1);
|
||||
CAnimSet::SNode& node = set->nodes[0];
|
||||
|
||||
node.name = CHAR.ReadString();
|
||||
CHAR.Seek(0x14, SEEK_CUR);
|
||||
CHAR.ReadString();
|
||||
node.model = gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
|
||||
return set;
|
||||
}
|
||||
|
||||
void CAnimSetLoader::LoadPASDatabase(CInputStream& PAS4)
|
||||
{
|
||||
// For now, just parse the data; don't store it
|
||||
PAS4.Seek(0x4, SEEK_CUR); // Skipping PAS4 FourCC
|
||||
u32 anim_state_count = PAS4.ReadLong();
|
||||
PAS4.Seek(0x4, SEEK_CUR); // Skipping default anim state
|
||||
|
||||
for (u32 s = 0; s < anim_state_count; s++)
|
||||
{
|
||||
PAS4.Seek(0x4, SEEK_CUR); // Skipping unknown value
|
||||
u32 parm_info_count = PAS4.ReadLong();
|
||||
u32 anim_info_count = PAS4.ReadLong();
|
||||
|
||||
u32 skip = 0;
|
||||
for (u32 p = 0; p < parm_info_count; p++)
|
||||
{
|
||||
u32 type = PAS4.ReadLong();
|
||||
PAS4.Seek(0x8, SEEK_CUR);
|
||||
|
||||
switch (type) {
|
||||
case 0: // Int32
|
||||
case 1: // Uint32
|
||||
case 2: // Real32
|
||||
case 4: // Enum
|
||||
PAS4.Seek(0x8, SEEK_CUR);
|
||||
skip += 4;
|
||||
break;
|
||||
case 3: // Bool
|
||||
PAS4.Seek(0x2, SEEK_CUR);
|
||||
skip++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 a = 0; a < anim_info_count; a++)
|
||||
PAS4.Seek(0x4 + skip, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CAnimSet* CAnimSetLoader::LoadANCS(CInputStream& ANCS)
|
||||
{
|
||||
if (!ANCS.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + ANCS.GetSourceString());
|
||||
|
||||
u32 magic = ANCS.ReadLong();
|
||||
if (magic != 0x00010001)
|
||||
{
|
||||
Log::FileError(ANCS.GetSourceString(), "Invalid ANCS magic: " + TString::HexString(magic));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CAnimSetLoader loader;
|
||||
loader.set = new CAnimSet;
|
||||
|
||||
u32 node_count = ANCS.ReadLong();
|
||||
loader.set->nodes.resize(node_count);
|
||||
|
||||
for (u32 n = 0; n < node_count; n++)
|
||||
{
|
||||
CAnimSet::SNode *node = &loader.set->nodes[n];
|
||||
|
||||
ANCS.Seek(0x4, SEEK_CUR); // Skipping node self-index
|
||||
u16 unknown1 = ANCS.ReadShort();
|
||||
if (n == 0) loader.mVersion = (unknown1 == 0xA) ? eEchoes : ePrime; // Best version indicator we know of unfortunately
|
||||
node->name = ANCS.ReadString();
|
||||
node->model = gResCache.GetResource(ANCS.ReadLong(), "CMDL");
|
||||
node->skinID = ANCS.ReadLong();
|
||||
node->skelID = ANCS.ReadLong();
|
||||
|
||||
// Unfortunately that's all that's actually supported at the moment. Hope to expand later.
|
||||
// Since there's no size value I have to actually read the rest of the node to reach the next one
|
||||
u32 anim_count = ANCS.ReadLong();
|
||||
for (u32 a = 0; a < anim_count; a++)
|
||||
{
|
||||
ANCS.Seek(0x4, SEEK_CUR);
|
||||
if (loader.mVersion == ePrime) ANCS.Seek(0x1, SEEK_CUR);
|
||||
ANCS.ReadString();
|
||||
}
|
||||
|
||||
// PAS Database
|
||||
loader.LoadPASDatabase(ANCS);
|
||||
|
||||
// Particles
|
||||
u32 particle_count = ANCS.ReadLong();
|
||||
ANCS.Seek(particle_count * 4, SEEK_CUR);
|
||||
u32 swoosh_count = ANCS.ReadLong();
|
||||
ANCS.Seek(swoosh_count * 4, SEEK_CUR);
|
||||
if (unknown1 != 5) ANCS.Seek(0x4, SEEK_CUR);
|
||||
u32 electric_count = ANCS.ReadLong();
|
||||
ANCS.Seek(electric_count * 4, SEEK_CUR);
|
||||
if (loader.mVersion == eEchoes) {
|
||||
u32 spsc_count = ANCS.ReadLong();
|
||||
ANCS.Seek(spsc_count * 4, SEEK_CUR);
|
||||
}
|
||||
ANCS.Seek(0x4, SEEK_CUR);
|
||||
if (loader.mVersion == eEchoes) ANCS.Seek(0x4, SEEK_CUR);
|
||||
|
||||
u32 anim_count2 = ANCS.ReadLong();
|
||||
for (u32 a = 0; a < anim_count2; a++)
|
||||
{
|
||||
ANCS.ReadString();
|
||||
ANCS.Seek(0x18, SEEK_CUR);
|
||||
}
|
||||
|
||||
u32 EffectGroupCount = ANCS.ReadLong();
|
||||
for (u32 g = 0; g < EffectGroupCount; g++)
|
||||
{
|
||||
ANCS.ReadString();
|
||||
u32 EffectCount = ANCS.ReadLong();
|
||||
|
||||
for (u32 e = 0; e < EffectCount; e++)
|
||||
{
|
||||
ANCS.ReadString();
|
||||
ANCS.Seek(0x8, SEEK_CUR);
|
||||
if (loader.mVersion == ePrime) ANCS.ReadString();
|
||||
if (loader.mVersion == eEchoes) ANCS.Seek(0x4, SEEK_CUR);
|
||||
ANCS.Seek(0xC, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
ANCS.Seek(0x8, SEEK_CUR);
|
||||
|
||||
u32 unknown_count = ANCS.ReadLong();
|
||||
ANCS.Seek(unknown_count * 4, SEEK_CUR);
|
||||
|
||||
if (loader.mVersion == eEchoes)
|
||||
{
|
||||
ANCS.Seek(0x5, SEEK_CUR);
|
||||
u32 unknown_count2 = ANCS.ReadLong();
|
||||
ANCS.Seek(unknown_count2 * 0x1C, SEEK_CUR);
|
||||
}
|
||||
// Lots of work for data I'm not even using x.x
|
||||
}
|
||||
|
||||
return loader.set;
|
||||
}
|
||||
|
||||
CAnimSet* CAnimSetLoader::LoadCHAR(CInputStream &CHAR)
|
||||
{
|
||||
if (!CHAR.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + CHAR.GetSourceString());
|
||||
|
||||
CAnimSetLoader loader;
|
||||
u8 check = CHAR.ReadByte();
|
||||
|
||||
if (check == 0x5)
|
||||
{
|
||||
loader.mVersion = eCorruption;
|
||||
loader.set = new CAnimSet();
|
||||
return loader.LoadCorruptionCHAR(CHAR);
|
||||
}
|
||||
|
||||
if (check == 0x59)
|
||||
{
|
||||
loader.mVersion = eReturns;
|
||||
loader.set = new CAnimSet();
|
||||
return loader.LoadReturnsCHAR(CHAR);
|
||||
}
|
||||
|
||||
Log::FileError(CHAR.GetSourceString(), "CHAR has invalid first byte: " + TString::HexString(check));
|
||||
return nullptr;
|
||||
}
|
||||
24
src/Core/Resource/Factory/CAnimSetLoader.h
Normal file
24
src/Core/Resource/Factory/CAnimSetLoader.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CCHARACTERLOADER_H
|
||||
#define CCHARACTERLOADER_H
|
||||
|
||||
#include "../CAnimSet.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
class CAnimSetLoader
|
||||
{
|
||||
TResPtr<CAnimSet> set;
|
||||
CResCache *mpResCache;
|
||||
EGame mVersion;
|
||||
|
||||
CAnimSetLoader();
|
||||
CAnimSet* LoadCorruptionCHAR(CInputStream& CHAR);
|
||||
CAnimSet* LoadReturnsCHAR(CInputStream& CHAR);
|
||||
void LoadPASDatabase(CInputStream& PAS4);
|
||||
|
||||
public:
|
||||
static CAnimSet* LoadANCS(CInputStream& ANCS);
|
||||
static CAnimSet* LoadCHAR(CInputStream& CHAR);
|
||||
};
|
||||
|
||||
#endif // CCHARACTERLOADER_H
|
||||
618
src/Core/Resource/Factory/CAreaLoader.cpp
Normal file
618
src/Core/Resource/Factory/CAreaLoader.cpp
Normal file
@@ -0,0 +1,618 @@
|
||||
#include "CAreaLoader.h"
|
||||
#include "CCollisionLoader.h"
|
||||
#include "CModelLoader.h"
|
||||
#include "CMaterialLoader.h"
|
||||
#include "CScriptLoader.h"
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/CompressionUtil.h>
|
||||
#include <iostream>
|
||||
#include <Core/Log.h>
|
||||
|
||||
CAreaLoader::CAreaLoader()
|
||||
{
|
||||
mpMREA = nullptr;
|
||||
mHasDecompressedBuffer = false;
|
||||
mGeometryBlockNum = -1;
|
||||
mScriptLayerBlockNum = -1;
|
||||
mCollisionBlockNum = -1;
|
||||
mUnknownBlockNum = -1;
|
||||
mLightsBlockNum = -1;
|
||||
mEmptyBlockNum = -1;
|
||||
mPathBlockNum = -1;
|
||||
mOctreeBlockNum = -1;
|
||||
mScriptGeneratorBlockNum = -1;
|
||||
mFFFFBlockNum = -1;
|
||||
mUnknown2BlockNum = -1;
|
||||
mEGMCBlockNum = -1;
|
||||
mBoundingBoxesBlockNum = -1;
|
||||
mDependenciesBlockNum = -1;
|
||||
mGPUBlockNum = -1;
|
||||
mPVSBlockNum = -1;
|
||||
mRSOBlockNum = -1;
|
||||
}
|
||||
|
||||
CAreaLoader::~CAreaLoader()
|
||||
{
|
||||
if (mHasDecompressedBuffer)
|
||||
{
|
||||
delete mpMREA;
|
||||
delete[] mDecmpBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ PRIME ************
|
||||
void CAreaLoader::ReadHeaderPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP1)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
u32 mNumBlocks = mpMREA->ReadLong();
|
||||
|
||||
mGeometryBlockNum = mpMREA->ReadLong();
|
||||
mScriptLayerBlockNum = mpMREA->ReadLong();
|
||||
mCollisionBlockNum = mpMREA->ReadLong();
|
||||
mUnknownBlockNum = mpMREA->ReadLong();
|
||||
mLightsBlockNum = mpMREA->ReadLong();
|
||||
mEmptyBlockNum = mpMREA->ReadLong();
|
||||
mPathBlockNum = mpMREA->ReadLong();
|
||||
mOctreeBlockNum = mpMREA->ReadLong();
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(mNumBlocks, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadGeometryPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)");
|
||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mBlockMgr->ToNextBlock();
|
||||
|
||||
// Geometry
|
||||
for (u32 m = 0; m < mNumMeshes; m++) {
|
||||
std::cout << "\rLoading mesh " << std::dec << m + 1 << "/" << mNumMeshes;
|
||||
|
||||
CModel *pTerrainModel = CModelLoader::LoadWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, mVersion);
|
||||
mpArea->AddWorldModel(pTerrainModel);
|
||||
|
||||
if (mVersion >= eEchoes) {
|
||||
mBlockMgr->ToNextBlock();
|
||||
mBlockMgr->ToNextBlock();
|
||||
}
|
||||
}
|
||||
mpArea->MergeTerrain();
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadSCLYPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)");
|
||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
||||
|
||||
CFourCC SCLY(*mpMREA);
|
||||
if (SCLY != "SCLY")
|
||||
{
|
||||
Log::Error(mpMREA->GetSourceString() + " - Invalid SCLY magic: " + SCLY.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
mpMREA->Seek(0x4, SEEK_CUR);
|
||||
mNumLayers = mpMREA->ReadLong();
|
||||
mpArea->mScriptLayers.reserve(mNumLayers);
|
||||
|
||||
std::vector<u32> LayerSizes(mNumLayers);
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
LayerSizes[l] = mpMREA->ReadLong();
|
||||
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
{
|
||||
u32 next = mpMREA->Tell() + LayerSizes[l];
|
||||
|
||||
CScriptLayer *layer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
if (layer)
|
||||
mpArea->mScriptLayers.push_back(layer);
|
||||
|
||||
mpMREA->Seek(next, SEEK_SET);
|
||||
}
|
||||
|
||||
SetUpObjects();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadLightsPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)");
|
||||
mBlockMgr->ToBlock(mLightsBlockNum);
|
||||
|
||||
u32 babedead = mpMREA->ReadLong();
|
||||
if (babedead != 0xbabedead) return;
|
||||
|
||||
mpArea->mLightLayers.resize(2);
|
||||
|
||||
for (u32 ly = 0; ly < 2; ly++)
|
||||
{
|
||||
u32 NumLights = mpMREA->ReadLong();
|
||||
mpArea->mLightLayers[ly].resize(NumLights);
|
||||
|
||||
for (u32 l = 0; l < NumLights; l++)
|
||||
{
|
||||
ELightType Type = ELightType(mpMREA->ReadLong());
|
||||
CVector3f Color(*mpMREA);
|
||||
CVector3f Position(*mpMREA);
|
||||
CVector3f Direction(*mpMREA);
|
||||
float Multiplier = mpMREA->ReadFloat();
|
||||
float SpotCutoff = mpMREA->ReadFloat();
|
||||
mpMREA->Seek(0x9, SEEK_CUR);
|
||||
u32 FalloffType = mpMREA->ReadLong();
|
||||
mpMREA->Seek(0x4, SEEK_CUR);
|
||||
|
||||
// Relevant data is read - now we process and form a CLight out of it
|
||||
CLight *Light;
|
||||
|
||||
CColor LightColor = CColor(Color.x, Color.y, Color.z, 0.f);
|
||||
if (Multiplier < FLT_EPSILON)
|
||||
Multiplier = FLT_EPSILON;
|
||||
|
||||
// Local Ambient
|
||||
if (Type == eLocalAmbient)
|
||||
{
|
||||
Color *= Multiplier;
|
||||
|
||||
// Clamp
|
||||
if (Color.x > 1.f) Color.x = 1.f;
|
||||
if (Color.y > 1.f) Color.y = 1.f;
|
||||
if (Color.z > 1.f) Color.z = 1.f;
|
||||
CColor MultColor(Color.x, Color.y, Color.z, 1.f);
|
||||
|
||||
Light = CLight::BuildLocalAmbient(Position, MultColor);
|
||||
}
|
||||
|
||||
// Directional
|
||||
else if (Type == eDirectional)
|
||||
{
|
||||
Light = CLight::BuildDirectional(Position, Direction, LightColor);
|
||||
}
|
||||
|
||||
// Spot
|
||||
else if (Type == eSpot)
|
||||
{
|
||||
Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff);
|
||||
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC);
|
||||
}
|
||||
|
||||
// Custom
|
||||
else
|
||||
{
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
|
||||
Light = CLight::BuildCustom(Position, Direction, LightColor,
|
||||
DistAttenA, DistAttenB, DistAttenC,
|
||||
1.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
Light->SetLayer(ly);
|
||||
mpArea->mLightLayers[ly][l] = Light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ ECHOES ************
|
||||
void CAreaLoader::ReadHeaderEchoes()
|
||||
{
|
||||
// This function reads the header for Echoes and the Echoes demo disc
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP2)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
if (mVersion == eEchoes) mNumLayers = mpMREA->ReadLong();
|
||||
u32 numBlocks = mpMREA->ReadLong();
|
||||
|
||||
mGeometryBlockNum = mpMREA->ReadLong();
|
||||
mScriptLayerBlockNum = mpMREA->ReadLong();
|
||||
mScriptGeneratorBlockNum = mpMREA->ReadLong();
|
||||
mCollisionBlockNum = mpMREA->ReadLong();
|
||||
mUnknownBlockNum = mpMREA->ReadLong();
|
||||
mLightsBlockNum = mpMREA->ReadLong();
|
||||
mEmptyBlockNum = mpMREA->ReadLong();
|
||||
mPathBlockNum = mpMREA->ReadLong();
|
||||
mFFFFBlockNum = mpMREA->ReadLong();
|
||||
mUnknown2BlockNum = mpMREA->ReadLong();
|
||||
mEGMCBlockNum = mpMREA->ReadLong();
|
||||
if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong());
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(numBlocks, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
ReadCompressedBlocks();
|
||||
Decompress();
|
||||
}
|
||||
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadSCLYEchoes()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)");
|
||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
||||
|
||||
// SCLY
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
{
|
||||
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
|
||||
if (pLayer)
|
||||
mpArea->mScriptLayers.push_back(pLayer);
|
||||
|
||||
mBlockMgr->ToNextBlock();
|
||||
}
|
||||
|
||||
// SCGN
|
||||
mBlockMgr->ToBlock(mScriptGeneratorBlockNum);
|
||||
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
|
||||
if (pLayer)
|
||||
mpArea->mpGeneratorLayer = pLayer;
|
||||
|
||||
SetUpObjects();
|
||||
}
|
||||
|
||||
// ************ CORRUPTION ************
|
||||
void CAreaLoader::ReadHeaderCorruption()
|
||||
{
|
||||
// This function reads the header for MP3, the MP3 prototype, and DKCR
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP3/DKCR)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
mNumLayers = mpMREA->ReadLong();
|
||||
u32 NumSections = mpMREA->ReadLong();
|
||||
mClusters.resize(mpMREA->ReadLong());
|
||||
u32 SectionNumberCount = mpMREA->ReadLong();
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(NumSections, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
ReadCompressedBlocks();
|
||||
|
||||
for (u32 iNum = 0; iNum < SectionNumberCount; iNum++)
|
||||
{
|
||||
CFourCC Type(*mpMREA);
|
||||
u32 Num = mpMREA->ReadLong();
|
||||
|
||||
if (Type == "AABB") mBoundingBoxesBlockNum = Num;
|
||||
else if (Type == "COLI") mCollisionBlockNum = Num;
|
||||
else if (Type == "DEPS") mDependenciesBlockNum = Num;
|
||||
else if (Type == "EGMC") mEGMCBlockNum = Num;
|
||||
else if (Type == "GPUD") mGPUBlockNum = Num;
|
||||
else if (Type == "LITE") mLightsBlockNum = Num;
|
||||
else if (Type == "PFL2") mPathBlockNum = Num;
|
||||
else if (Type == "PVS!") mPVSBlockNum = Num;
|
||||
else if (Type == "ROCT") mOctreeBlockNum = Num;
|
||||
else if (Type == "RSOS") mRSOBlockNum = Num;
|
||||
else if (Type == "SOBJ") mScriptLayerBlockNum = Num;
|
||||
else if (Type == "SGEN") mScriptGeneratorBlockNum = Num;
|
||||
else if (Type == "WOBJ") mGeometryBlockNum = Num; // note WOBJ can show up multiple times, but is always 0
|
||||
}
|
||||
|
||||
mpMREA->SeekToBoundary(32);
|
||||
Decompress();
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadGeometryCorruption()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)");
|
||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mBlockMgr->ToNextBlock();
|
||||
|
||||
// Geometry
|
||||
u32 CurWOBJSection = 1;
|
||||
u32 CurGPUSection = mGPUBlockNum;
|
||||
|
||||
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
|
||||
{
|
||||
std::cout << "\rLoading mesh " << std::dec << iMesh + 1 << "/" << mNumMeshes;
|
||||
|
||||
CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
|
||||
mpArea->AddWorldModel(pWorldModel);
|
||||
|
||||
CurWOBJSection += 4;
|
||||
CurGPUSection = mBlockMgr->CurrentBlock();
|
||||
}
|
||||
|
||||
mpArea->MergeTerrain();
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadLightsCorruption()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)");
|
||||
mBlockMgr->ToBlock(mLightsBlockNum);
|
||||
|
||||
u32 babedead = mpMREA->ReadLong();
|
||||
if (babedead != 0xbabedead) return;
|
||||
|
||||
mpArea->mLightLayers.resize(4);
|
||||
|
||||
for (u32 iLayer = 0; iLayer < 4; iLayer++)
|
||||
{
|
||||
u32 NumLights = mpMREA->ReadLong();
|
||||
mpArea->mLightLayers[iLayer].resize(NumLights);
|
||||
|
||||
for (u32 iLight = 0; iLight < NumLights; iLight++)
|
||||
{
|
||||
ELightType Type = (ELightType) mpMREA->ReadLong();
|
||||
|
||||
float r = mpMREA->ReadFloat();
|
||||
float g = mpMREA->ReadFloat();
|
||||
float b = mpMREA->ReadFloat();
|
||||
float a = mpMREA->ReadFloat();
|
||||
CColor LightColor(r, g, b, a);
|
||||
|
||||
CVector3f Position(*mpMREA);
|
||||
CVector3f Direction(*mpMREA);
|
||||
mpMREA->Seek(0xC, SEEK_CUR);
|
||||
|
||||
float Multiplier = mpMREA->ReadFloat();
|
||||
float SpotCutoff = mpMREA->ReadFloat();
|
||||
mpMREA->Seek(0x9, SEEK_CUR);
|
||||
u32 FalloffType = mpMREA->ReadLong();
|
||||
mpMREA->Seek(0x18, SEEK_CUR);
|
||||
|
||||
// Relevant data is read - now we process and form a CLight out of it
|
||||
CLight *Light;
|
||||
|
||||
if (Multiplier < FLT_EPSILON)
|
||||
Multiplier = FLT_EPSILON;
|
||||
|
||||
// Local Ambient
|
||||
if (Type == eLocalAmbient)
|
||||
{
|
||||
Light = CLight::BuildLocalAmbient(Position, LightColor * Multiplier);
|
||||
}
|
||||
|
||||
// Directional
|
||||
else if (Type == eDirectional)
|
||||
{
|
||||
Light = CLight::BuildDirectional(Position, Direction, LightColor);
|
||||
}
|
||||
|
||||
// Spot
|
||||
else if (Type == eSpot)
|
||||
{
|
||||
Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff);
|
||||
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC);
|
||||
}
|
||||
|
||||
// Custom
|
||||
else
|
||||
{
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
|
||||
Light = CLight::BuildCustom(Position, Direction, LightColor,
|
||||
DistAttenA, DistAttenB, DistAttenC,
|
||||
1.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
Light->SetLayer(iLayer);
|
||||
mpArea->mLightLayers[iLayer][iLight] = Light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ COMMON ************
|
||||
void CAreaLoader::ReadCompressedBlocks()
|
||||
{
|
||||
mTotalDecmpSize = 0;
|
||||
|
||||
for (u32 c = 0; c < mClusters.size(); c++)
|
||||
{
|
||||
mClusters[c].BufferSize = mpMREA->ReadLong();
|
||||
mClusters[c].DecompressedSize = mpMREA->ReadLong();
|
||||
mClusters[c].CompressedSize = mpMREA->ReadLong();
|
||||
mClusters[c].NumSections = mpMREA->ReadLong();
|
||||
mTotalDecmpSize += mClusters[c].DecompressedSize;
|
||||
}
|
||||
|
||||
mpMREA->SeekToBoundary(32);
|
||||
}
|
||||
|
||||
void CAreaLoader::Decompress()
|
||||
{
|
||||
// This function decompresses compressed clusters into a buffer.
|
||||
// It should be called at the beginning of the first compressed cluster.
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Decompressing MREA data");
|
||||
if (mVersion < eEchoes) return;
|
||||
|
||||
// Decompress clusters
|
||||
mDecmpBuffer = new u8[mTotalDecmpSize];
|
||||
u32 Offset = 0;
|
||||
|
||||
for (u32 c = 0; c < mClusters.size(); c++)
|
||||
{
|
||||
SCompressedCluster *cc = &mClusters[c];
|
||||
|
||||
// Is it decompressed already?
|
||||
if (mClusters[c].CompressedSize == 0)
|
||||
{
|
||||
mpMREA->ReadBytes(mDecmpBuffer + Offset, cc->DecompressedSize);
|
||||
Offset += cc->DecompressedSize;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
u32 StartOffset = 32 - (mClusters[c].CompressedSize % 32); // For some reason they pad the beginning instead of the end
|
||||
if (StartOffset != 32)
|
||||
mpMREA->Seek(StartOffset, SEEK_CUR);
|
||||
|
||||
std::vector<u8> cmp(mClusters[c].CompressedSize);
|
||||
mpMREA->ReadBytes(cmp.data(), cmp.size());
|
||||
|
||||
bool Success = CompressionUtil::DecompressAreaLZO(cmp.data(), cmp.size(), mDecmpBuffer + Offset, cc->DecompressedSize);
|
||||
if (!Success)
|
||||
throw "Failed to decompress MREA!";
|
||||
|
||||
Offset += cc->DecompressedSize;
|
||||
}
|
||||
}
|
||||
|
||||
TString Source = mpMREA->GetSourceString();
|
||||
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::BigEndian);
|
||||
mpMREA->SetSourceString(Source.ToStdString());
|
||||
mBlockMgr->SetInputStream(mpMREA);
|
||||
mHasDecompressedBuffer = true;
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadCollision()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
|
||||
mBlockMgr->ToBlock(mCollisionBlockNum);
|
||||
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
|
||||
}
|
||||
|
||||
void CAreaLoader::SetUpObjects()
|
||||
{
|
||||
// Iterate over all objects
|
||||
for (u32 iLyr = 0; iLyr < mpArea->GetScriptLayerCount() + 1; iLyr++)
|
||||
{
|
||||
CScriptLayer *pLayer;
|
||||
if (iLyr < mpArea->GetScriptLayerCount()) pLayer = mpArea->mScriptLayers[iLyr];
|
||||
|
||||
else
|
||||
{
|
||||
pLayer = mpArea->GetGeneratorLayer();
|
||||
if (!pLayer) break;
|
||||
}
|
||||
|
||||
for (u32 iObj = 0; iObj < pLayer->GetNumObjects(); iObj++)
|
||||
{
|
||||
// Add object to object map
|
||||
CScriptObject *pObj = (*pLayer)[iObj];
|
||||
mpArea->mObjectMap[pObj->InstanceID()] = pObj;
|
||||
|
||||
// Store outgoing connections
|
||||
for (u32 iCon = 0; iCon < pObj->NumOutLinks(); iCon++)
|
||||
{
|
||||
SLink Connection = pObj->OutLink(iCon);
|
||||
|
||||
SLink NewConnection;
|
||||
NewConnection.State = Connection.State;
|
||||
NewConnection.Message = Connection.Message;
|
||||
NewConnection.ObjectID = pObj->InstanceID();
|
||||
mConnectionMap[Connection.ObjectID].push_back(NewConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store connections
|
||||
for (auto it = mpArea->mObjectMap.begin(); it != mpArea->mObjectMap.end(); it++)
|
||||
{
|
||||
u32 InstanceID = it->first;
|
||||
auto iConMap = mConnectionMap.find(InstanceID);
|
||||
|
||||
if (iConMap != mConnectionMap.end())
|
||||
{
|
||||
CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID);
|
||||
pObj->mInConnections = iConMap->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CGameArea* CAreaLoader::LoadMREA(CInputStream& MREA)
|
||||
{
|
||||
CAreaLoader Loader;
|
||||
|
||||
// Validation
|
||||
if (!MREA.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + MREA.GetSourceString());
|
||||
|
||||
u32 deadbeef = MREA.ReadLong();
|
||||
if (deadbeef != 0xdeadbeef)
|
||||
{
|
||||
Log::FileError(MREA.GetSourceString(), "Invalid MREA magic: " + TString::HexString(deadbeef));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Header
|
||||
Loader.mpArea = new CGameArea;
|
||||
u32 version = MREA.ReadLong();
|
||||
Loader.mVersion = GetFormatVersion(version);
|
||||
Loader.mpMREA = &MREA;
|
||||
|
||||
switch (Loader.mVersion)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
Loader.ReadHeaderPrime();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYPrime();
|
||||
Loader.ReadCollision();
|
||||
Loader.ReadLightsPrime();
|
||||
break;
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Loader.ReadHeaderEchoes();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
Loader.ReadLightsPrime();
|
||||
break;
|
||||
case eCorruptionProto:
|
||||
Loader.ReadHeaderCorruption();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
break;
|
||||
case eCorruption:
|
||||
case eReturns:
|
||||
Loader.ReadHeaderCorruption();
|
||||
Loader.ReadGeometryCorruption();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
if (Loader.mVersion == eCorruption) Loader.ReadLightsCorruption();
|
||||
break;
|
||||
default:
|
||||
Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + TString::HexString(version));
|
||||
Loader.mpArea.Delete();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
delete Loader.mBlockMgr;
|
||||
return Loader.mpArea;
|
||||
}
|
||||
|
||||
EGame CAreaLoader::GetFormatVersion(u32 version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 0xC: return ePrimeDemo;
|
||||
case 0xF: return ePrime;
|
||||
case 0x15: return eEchoesDemo;
|
||||
case 0x19: return eEchoes;
|
||||
case 0x1D: return eCorruptionProto;
|
||||
case 0x1E: return eCorruption;
|
||||
case 0x20: return eReturns;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
84
src/Core/Resource/Factory/CAreaLoader.h
Normal file
84
src/Core/Resource/Factory/CAreaLoader.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef CAREALOADER_H
|
||||
#define CAREALOADER_H
|
||||
|
||||
#include <FileIO/FileIO.h>
|
||||
#include "../CGameArea.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include "CBlockMgrIn.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Resource/script/SConnection.h>
|
||||
|
||||
class CAreaLoader
|
||||
{
|
||||
struct SCompressedCluster;
|
||||
|
||||
// Area data
|
||||
TResPtr<CGameArea> mpArea;
|
||||
CInputStream *mpMREA;
|
||||
CBlockMgrIn *mBlockMgr;
|
||||
EGame mVersion;
|
||||
u32 mNumMeshes;
|
||||
u32 mNumLayers;
|
||||
|
||||
// Object connections
|
||||
std::unordered_map<u32, std::vector<SLink>> mConnectionMap;
|
||||
|
||||
// Compression
|
||||
u8 *mDecmpBuffer;
|
||||
bool mHasDecompressedBuffer;
|
||||
std::vector<SCompressedCluster> mClusters;
|
||||
u32 mTotalDecmpSize;
|
||||
|
||||
// Block numbers
|
||||
u32 mGeometryBlockNum;
|
||||
u32 mScriptLayerBlockNum;
|
||||
u32 mCollisionBlockNum;
|
||||
u32 mUnknownBlockNum;
|
||||
u32 mLightsBlockNum;
|
||||
u32 mEmptyBlockNum;
|
||||
u32 mPathBlockNum;
|
||||
u32 mOctreeBlockNum;
|
||||
u32 mScriptGeneratorBlockNum;
|
||||
u32 mFFFFBlockNum;
|
||||
u32 mUnknown2BlockNum;
|
||||
u32 mEGMCBlockNum;
|
||||
u32 mBoundingBoxesBlockNum;
|
||||
u32 mDependenciesBlockNum;
|
||||
u32 mGPUBlockNum;
|
||||
u32 mPVSBlockNum;
|
||||
u32 mRSOBlockNum;
|
||||
|
||||
struct SCompressedCluster {
|
||||
u32 BufferSize, DecompressedSize, CompressedSize, NumSections;
|
||||
};
|
||||
|
||||
CAreaLoader();
|
||||
~CAreaLoader();
|
||||
|
||||
// Prime
|
||||
void ReadHeaderPrime();
|
||||
void ReadGeometryPrime();
|
||||
void ReadSCLYPrime();
|
||||
void ReadLightsPrime();
|
||||
|
||||
// Echoes
|
||||
void ReadHeaderEchoes();
|
||||
void ReadSCLYEchoes();
|
||||
|
||||
// Corruption
|
||||
void ReadHeaderCorruption();
|
||||
void ReadGeometryCorruption();
|
||||
void ReadLightsCorruption();
|
||||
|
||||
// Common
|
||||
void ReadCompressedBlocks();
|
||||
void Decompress();
|
||||
void ReadCollision();
|
||||
void SetUpObjects();
|
||||
|
||||
public:
|
||||
static CGameArea* LoadMREA(CInputStream& MREA);
|
||||
static EGame GetFormatVersion(u32 version);
|
||||
};
|
||||
|
||||
#endif // CAREALOADER_H
|
||||
58
src/Core/Resource/Factory/CBlockMgr.cpp
Normal file
58
src/Core/Resource/Factory/CBlockMgr.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "CBlockMgrIn.h"
|
||||
|
||||
CBlockMgrIn::CBlockMgrIn(unsigned long count, CInputStream* 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(CInputStream *in)
|
||||
{
|
||||
mpInputStream = in;
|
||||
}
|
||||
28
src/Core/Resource/Factory/CBlockMgrIn.h
Normal file
28
src/Core/Resource/Factory/CBlockMgrIn.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef CBLOCKMGRIN_H
|
||||
#define CBLOCKMGRIN_H
|
||||
|
||||
#include <FileIO/CInputStream.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
|
||||
{
|
||||
CInputStream *mpInputStream;
|
||||
unsigned long mBlockCount;
|
||||
std::vector<unsigned long> mBlockSizes;
|
||||
unsigned long mCurBlock;
|
||||
unsigned long mCurBlockStart;
|
||||
unsigned long mBlocksStart;
|
||||
|
||||
public:
|
||||
CBlockMgrIn(unsigned long count, CInputStream* src);
|
||||
void Init();
|
||||
void ToBlock(unsigned long block);
|
||||
void ToNextBlock();
|
||||
long NextOffset();
|
||||
long CurrentBlock();
|
||||
long CurrentBlockSize();
|
||||
void SetInputStream(CInputStream *in);
|
||||
};
|
||||
|
||||
#endif // CBLOCKMGRIN_H
|
||||
214
src/Core/Resource/Factory/CCollisionLoader.cpp
Normal file
214
src/Core/Resource/Factory/CCollisionLoader.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
#include "CCollisionLoader.h"
|
||||
#include <Core/Log.h>
|
||||
#include <iostream>
|
||||
|
||||
CCollisionLoader::CCollisionLoader()
|
||||
{
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionOctree* CCollisionLoader::ParseOctree(CInputStream&)
|
||||
{
|
||||
// Not using: Parameter 1 (CInputStream& - src)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionOctree::SBranch* CCollisionLoader::ParseOctreeBranch(CInputStream&)
|
||||
{
|
||||
// Not using: Parameter 1 (CInputStream& - src)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionOctree::SLeaf* CCollisionLoader::ParseOctreeLeaf(CInputStream&)
|
||||
{
|
||||
// Not using: Parameter 1 (CInputStream& - src)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CCollisionLoader::ParseOBBNode(CInputStream& DCLN)
|
||||
{
|
||||
bool b = false;
|
||||
|
||||
while (b == false)
|
||||
{
|
||||
DCLN.Seek(0x3C, SEEK_CUR);
|
||||
b = (DCLN.ReadByte() == 1);
|
||||
if (!b) ParseOBBNode(DCLN);
|
||||
}
|
||||
|
||||
u32 numFaces = DCLN.ReadLong();
|
||||
DCLN.Seek(numFaces * 2, SEEK_CUR);
|
||||
}
|
||||
|
||||
void CCollisionLoader::ReadPropertyFlags(CInputStream& src)
|
||||
{
|
||||
CCollisionMesh::SCollisionProperties property;
|
||||
|
||||
if (mVersion == ePrime)
|
||||
{
|
||||
u32 flag = src.ReadLong();
|
||||
property.Invert = (flag >> 25) & 0x1;
|
||||
}
|
||||
|
||||
else if (mVersion == eEchoes)
|
||||
{
|
||||
u64 flag = src.ReadLongLong();
|
||||
property.Invert = (flag >> 24) & 0x1;
|
||||
}
|
||||
|
||||
else if (mVersion == eReturns)
|
||||
{
|
||||
u64 flag = src.ReadLongLong();
|
||||
property.Invert = (flag >> 28) & 0x1;
|
||||
}
|
||||
|
||||
mProperties.push_back(property);
|
||||
}
|
||||
|
||||
void CCollisionLoader::LoadCollisionIndices(CInputStream &file, bool buildAABox)
|
||||
{
|
||||
// Properties
|
||||
u32 propSetCount = file.ReadLong();
|
||||
for (u32 iProp = 0; iProp < propSetCount; iProp++)
|
||||
ReadPropertyFlags(file);
|
||||
|
||||
// Property indices for vertices/lines/faces
|
||||
u32 vtxIndexCount = file.ReadLong();
|
||||
std::vector<u8> vtxIndices(vtxIndexCount);
|
||||
file.ReadBytes(vtxIndices.data(), vtxIndices.size());
|
||||
|
||||
u32 lineIndexCount = file.ReadLong();
|
||||
std::vector<u8> lineIndices(lineIndexCount);
|
||||
file.ReadBytes(lineIndices.data(), lineIndices.size());
|
||||
|
||||
u32 faceIndexCount = file.ReadLong();
|
||||
std::vector<u8> faceIndices(faceIndexCount);
|
||||
file.ReadBytes(faceIndices.data(), faceIndices.size());
|
||||
|
||||
// Lines
|
||||
mpMesh->mLineCount = file.ReadLong();
|
||||
mpMesh->mCollisionLines.resize(mpMesh->mLineCount);
|
||||
for (u32 iLine = 0; iLine < mpMesh->mLineCount; iLine++)
|
||||
{
|
||||
CCollisionMesh::CCollisionLine *pLine = &mpMesh->mCollisionLines[iLine];
|
||||
pLine->Vertices[0] = file.ReadShort();
|
||||
pLine->Vertices[1] = file.ReadShort();
|
||||
pLine->Properties = mProperties[lineIndices[iLine]];
|
||||
}
|
||||
|
||||
// Faces
|
||||
mpMesh->mFaceCount = file.ReadLong() / 3; // Not sure why they store it this way. It's inconsistent.
|
||||
mpMesh->mCollisionFaces.resize(mpMesh->mFaceCount);
|
||||
|
||||
for (u32 iFace = 0; iFace < mpMesh->mFaceCount; iFace++)
|
||||
{
|
||||
CCollisionMesh::CCollisionFace *pFace = &mpMesh->mCollisionFaces[iFace];
|
||||
pFace->Lines[0] = file.ReadShort();
|
||||
pFace->Lines[1] = file.ReadShort();
|
||||
pFace->Lines[2] = file.ReadShort();
|
||||
pFace->Properties = mProperties[faceIndices[iFace]];
|
||||
}
|
||||
|
||||
// Echoes introduces a new data chunk; don't know what it is yet, skipping for now
|
||||
if (mVersion >= eEchoes)
|
||||
{
|
||||
u32 unknownCount = file.ReadLong();
|
||||
file.Seek(unknownCount * 2, SEEK_CUR);
|
||||
}
|
||||
|
||||
// Vertices
|
||||
mpMesh->mVertexCount = file.ReadLong();
|
||||
mpMesh->mCollisionVertices.resize(mpMesh->mVertexCount);
|
||||
CAABox bounds;
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mpMesh->mVertexCount; iVtx++)
|
||||
{
|
||||
CCollisionMesh::CCollisionVertex *pVtx = &mpMesh->mCollisionVertices[iVtx];
|
||||
pVtx->Pos = CVector3f(file);
|
||||
pVtx->Properties = mProperties[vtxIndices[iVtx]];
|
||||
if (buildAABox) bounds.ExpandBounds(pVtx->Pos);
|
||||
}
|
||||
if (buildAABox) mpMesh->mAABox = bounds;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CCollisionMeshGroup* CCollisionLoader::LoadAreaCollision(CInputStream& MREA)
|
||||
{
|
||||
if (!MREA.IsValid()) return nullptr;
|
||||
CCollisionLoader loader;
|
||||
|
||||
MREA.Seek(0x8, SEEK_CUR);
|
||||
u32 deafbabe = MREA.ReadLong();
|
||||
if (deafbabe != 0xDEAFBABE)
|
||||
{
|
||||
Log::FileError(MREA.GetSourceString(), MREA.Tell() - 4, "Invalid collision magic: " + TString::HexString(deafbabe));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
loader.mVersion = GetFormatVersion(MREA.ReadLong());
|
||||
|
||||
loader.mpGroup = new CCollisionMeshGroup;
|
||||
loader.mpMesh = new CCollisionMesh;
|
||||
|
||||
// Octree - structure is known, but not coding this right now
|
||||
loader.mpMesh->mAABox = CAABox(MREA);
|
||||
MREA.Seek(0x4, SEEK_CUR);
|
||||
u32 octreeSize = MREA.ReadLong();
|
||||
MREA.Seek(octreeSize, SEEK_CUR); // Skipping the octree for now
|
||||
loader.mpMesh->mOctreeLoaded = false;
|
||||
|
||||
// Read collision indices and return
|
||||
loader.LoadCollisionIndices(MREA, false);
|
||||
loader.mpGroup->AddMesh(loader.mpMesh);
|
||||
return loader.mpGroup;
|
||||
}
|
||||
|
||||
CCollisionMeshGroup* CCollisionLoader::LoadDCLN(CInputStream &DCLN)
|
||||
{
|
||||
if (!DCLN.IsValid()) return nullptr;
|
||||
|
||||
CCollisionLoader loader;
|
||||
loader.mpGroup = new CCollisionMeshGroup;
|
||||
|
||||
u32 numMeshes = DCLN.ReadLong();
|
||||
|
||||
for (u32 iMesh = 0; iMesh < numMeshes; iMesh++)
|
||||
{
|
||||
u32 deafbabe = DCLN.ReadLong();
|
||||
|
||||
if (deafbabe != 0xDEAFBABE)
|
||||
{
|
||||
Log::FileError(DCLN.GetSourceString(), DCLN.Tell() - 4, "Invalid collision magic: " + TString::HexString(deafbabe));
|
||||
loader.mpGroup.Delete();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
loader.mVersion = GetFormatVersion(DCLN.ReadLong());
|
||||
|
||||
loader.mpMesh = new CCollisionMesh;
|
||||
loader.mpMesh->mOctreeLoaded = false;
|
||||
|
||||
if (loader.mVersion == eReturns)
|
||||
loader.mpMesh->mAABox = CAABox(DCLN);
|
||||
|
||||
// Read indices and return
|
||||
DCLN.Seek(0x4, SEEK_CUR);
|
||||
loader.LoadCollisionIndices(DCLN, loader.mVersion != eReturns);
|
||||
loader.mpGroup->AddMesh(loader.mpMesh);
|
||||
|
||||
// Parse OBB tree
|
||||
loader.ParseOBBNode(DCLN);
|
||||
}
|
||||
return loader.mpGroup;
|
||||
}
|
||||
|
||||
EGame CCollisionLoader::GetFormatVersion(u32 version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 0x2: return ePrime;
|
||||
case 0x3: return ePrime;
|
||||
case 0x4: return eEchoes;
|
||||
case 0x5: return eReturns;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
29
src/Core/Resource/Factory/CCollisionLoader.h
Normal file
29
src/Core/Resource/Factory/CCollisionLoader.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef CCOLLISIONLOADER_H
|
||||
#define CCOLLISIONLOADER_H
|
||||
|
||||
#include "../CCollisionMesh.h"
|
||||
#include "../CCollisionMeshGroup.h"
|
||||
#include "../EFormatVersion.h"
|
||||
|
||||
class CCollisionLoader
|
||||
{
|
||||
TResPtr<CCollisionMeshGroup> mpGroup;
|
||||
CCollisionMesh *mpMesh;
|
||||
EGame mVersion;
|
||||
std::vector<CCollisionMesh::SCollisionProperties> mProperties;
|
||||
|
||||
CCollisionLoader();
|
||||
CCollisionMesh::CCollisionOctree* ParseOctree(CInputStream& src);
|
||||
CCollisionMesh::CCollisionOctree::SBranch* ParseOctreeBranch(CInputStream& src);
|
||||
CCollisionMesh::CCollisionOctree::SLeaf* ParseOctreeLeaf(CInputStream& src);
|
||||
void ParseOBBNode(CInputStream& DCLN);
|
||||
void ReadPropertyFlags(CInputStream& src);
|
||||
void LoadCollisionIndices(CInputStream& file, bool buildAABox);
|
||||
|
||||
public:
|
||||
static CCollisionMeshGroup* LoadAreaCollision(CInputStream& MREA);
|
||||
static CCollisionMeshGroup* LoadDCLN(CInputStream& DCLN);
|
||||
static EGame GetFormatVersion(u32 version);
|
||||
};
|
||||
|
||||
#endif // CCOLLISIONLOADER_H
|
||||
118
src/Core/Resource/Factory/CFontLoader.cpp
Normal file
118
src/Core/Resource/Factory/CFontLoader.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "CFontLoader.h"
|
||||
#include <Core/Log.h>
|
||||
#include <iostream>
|
||||
|
||||
CFontLoader::CFontLoader()
|
||||
{
|
||||
}
|
||||
|
||||
CFont* CFontLoader::LoadFont(CInputStream& FONT)
|
||||
{
|
||||
// If I seek past a value without reading it, then it's because I don't know what it is
|
||||
mpFont->mUnknown = FONT.ReadLong();
|
||||
mpFont->mLineHeight = FONT.ReadLong();
|
||||
mpFont->mVerticalOffset = FONT.ReadLong();
|
||||
mpFont->mLineMargin = FONT.ReadLong();
|
||||
if (mVersion > ePrimeDemo) FONT.Seek(0x4, SEEK_CUR);
|
||||
FONT.Seek(0x2, SEEK_CUR);
|
||||
mpFont->mDefaultSize = FONT.ReadLong();
|
||||
mpFont->mFontName = FONT.ReadString();
|
||||
|
||||
if (mVersion <= eEchoes) mpFont->mpFontTexture = gResCache.GetResource(FONT.ReadLong(), "TXTR");
|
||||
else mpFont->mpFontTexture = gResCache.GetResource(FONT.ReadLongLong(), "TXTR");
|
||||
|
||||
mpFont->mTextureFormat = FONT.ReadLong();
|
||||
u32 NumGlyphs = FONT.ReadLong();
|
||||
mpFont->mGlyphs.reserve(NumGlyphs);
|
||||
|
||||
for (u32 iGlyph = 0; iGlyph < NumGlyphs; iGlyph++)
|
||||
{
|
||||
CFont::SGlyph Glyph;
|
||||
Glyph.Character = FONT.ReadShort();
|
||||
|
||||
float TexCoordL = FONT.ReadFloat();
|
||||
float TexCoordU = FONT.ReadFloat();
|
||||
float TexCoordR = FONT.ReadFloat();
|
||||
float TexCoordD = FONT.ReadFloat();
|
||||
Glyph.TexCoords[0] = CVector2f(TexCoordL, TexCoordU); // Upper-left
|
||||
Glyph.TexCoords[1] = CVector2f(TexCoordR, TexCoordU); // Upper-right
|
||||
Glyph.TexCoords[2] = CVector2f(TexCoordL, TexCoordD); // Lower-left
|
||||
Glyph.TexCoords[3] = CVector2f(TexCoordR, TexCoordD); // Lower-right
|
||||
|
||||
if (mVersion <= ePrime)
|
||||
{
|
||||
Glyph.RGBAChannel = 0;
|
||||
Glyph.LeftPadding = FONT.ReadLong();
|
||||
Glyph.PrintAdvance = FONT.ReadLong();
|
||||
Glyph.RightPadding = FONT.ReadLong();
|
||||
Glyph.Width = FONT.ReadLong();
|
||||
Glyph.Height = FONT.ReadLong();
|
||||
Glyph.BaseOffset = FONT.ReadLong();
|
||||
Glyph.KerningIndex = FONT.ReadLong();
|
||||
}
|
||||
else if (mVersion >= eEchoes)
|
||||
{
|
||||
Glyph.RGBAChannel = FONT.ReadByte();
|
||||
Glyph.LeftPadding = FONT.ReadByte();
|
||||
Glyph.PrintAdvance = FONT.ReadByte();
|
||||
Glyph.RightPadding = FONT.ReadByte();
|
||||
Glyph.Width = FONT.ReadByte();
|
||||
Glyph.Height = FONT.ReadByte();
|
||||
Glyph.BaseOffset = FONT.ReadByte();
|
||||
Glyph.KerningIndex = FONT.ReadShort();
|
||||
}
|
||||
mpFont->mGlyphs[Glyph.Character] = Glyph;
|
||||
}
|
||||
|
||||
u32 NumKerningPairs = FONT.ReadLong();
|
||||
mpFont->mKerningTable.reserve(NumKerningPairs);
|
||||
|
||||
for (u32 iKern = 0; iKern < NumKerningPairs; iKern++)
|
||||
{
|
||||
CFont::SKerningPair Pair;
|
||||
Pair.CharacterA = FONT.ReadShort();
|
||||
Pair.CharacterB = FONT.ReadShort();
|
||||
Pair.Adjust = FONT.ReadLong();
|
||||
mpFont->mKerningTable.push_back(Pair);
|
||||
}
|
||||
|
||||
return mpFont;
|
||||
}
|
||||
|
||||
CFont* CFontLoader::LoadFONT(CInputStream& FONT)
|
||||
{
|
||||
if (!FONT.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + FONT.GetSourceString());
|
||||
|
||||
CFourCC Magic(FONT);
|
||||
if (Magic != "FONT")
|
||||
{
|
||||
Log::FileError(FONT.GetSourceString(), "Invalid FONT magic: " + TString::HexString((u32) Magic.ToLong()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 FileVersion = FONT.ReadLong();
|
||||
EGame Version = GetFormatVersion(FileVersion);
|
||||
if (Version == eUnknownVersion)
|
||||
{
|
||||
Log::FileError(FONT.GetSourceString(), "Unsupported FONT version: " + TString::HexString(FileVersion));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CFontLoader Loader;
|
||||
Loader.mpFont = new CFont();
|
||||
Loader.mVersion = Version;
|
||||
return Loader.LoadFont(FONT);
|
||||
}
|
||||
|
||||
EGame CFontLoader::GetFormatVersion(u32 Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case 1: return ePrimeDemo;
|
||||
case 2: return ePrime;
|
||||
case 4: return eEchoes;
|
||||
case 5: return eCorruption;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
21
src/Core/Resource/Factory/CFontLoader.h
Normal file
21
src/Core/Resource/Factory/CFontLoader.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CFONTLOADER_H
|
||||
#define CFONTLOADER_H
|
||||
|
||||
#include "../CFont.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
class CFontLoader
|
||||
{
|
||||
TResPtr<CFont> mpFont;
|
||||
EGame mVersion;
|
||||
|
||||
CFontLoader();
|
||||
CFont* LoadFont(CInputStream& FONT);
|
||||
|
||||
public:
|
||||
static CFont* LoadFONT(CInputStream& FONT);
|
||||
static EGame GetFormatVersion(u32 Version);
|
||||
};
|
||||
|
||||
#endif // CFONTLOADER_H
|
||||
624
src/Core/Resource/Factory/CMaterialLoader.cpp
Normal file
624
src/Core/Resource/Factory/CMaterialLoader.cpp
Normal file
@@ -0,0 +1,624 @@
|
||||
#include "CMaterialLoader.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
#include <OpenGL/GLCommon.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
CMaterialLoader::CMaterialLoader()
|
||||
{
|
||||
mCorruptionFlags = 0;
|
||||
mHasOPAC = false;
|
||||
}
|
||||
|
||||
CMaterialLoader::~CMaterialLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void CMaterialLoader::ReadPrimeMatSet()
|
||||
{
|
||||
// Textures
|
||||
u32 numTextures = mpFile->ReadLong();
|
||||
mTextures.resize(numTextures);
|
||||
|
||||
for (u32 iTex = 0; iTex < numTextures; iTex++)
|
||||
{
|
||||
u32 TextureID = mpFile->ReadLong();
|
||||
mTextures[iTex] = gResCache.GetResource(TextureID, "TXTR");
|
||||
}
|
||||
|
||||
// Materials
|
||||
u32 numMats = mpFile->ReadLong();
|
||||
std::vector<u32> offsets(numMats);
|
||||
for (u32 iMat = 0; iMat < numMats; iMat++)
|
||||
offsets[iMat] = mpFile->ReadLong();
|
||||
|
||||
u32 matsStart = mpFile->Tell();
|
||||
mpSet->mMaterials.resize(numMats);
|
||||
for (u32 iMat = 0; iMat < numMats; iMat++)
|
||||
{
|
||||
mpSet->mMaterials[iMat] = ReadPrimeMaterial();
|
||||
mpSet->mMaterials[iMat]->mVersion = mVersion;
|
||||
mpSet->mMaterials[iMat]->mName = TString("Material #") + std::to_string(iMat + 1);
|
||||
mpFile->Seek(matsStart + offsets[iMat], SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
CMaterial* CMaterialLoader::ReadPrimeMaterial()
|
||||
{
|
||||
CMaterial *pMat = new CMaterial();
|
||||
pMat->mEnableBloom = false;
|
||||
|
||||
// Flags
|
||||
pMat->mOptions = (CMaterial::EMaterialOptions) (mpFile->ReadLong() & CMaterial::eAllSettings);
|
||||
|
||||
// Textures
|
||||
u32 NumTextures = mpFile->ReadLong();
|
||||
std::vector<u32> TextureIndices(NumTextures);
|
||||
|
||||
for (u32 iTex = 0; iTex < NumTextures; iTex++)
|
||||
{
|
||||
u32 Index = mpFile->ReadLong();
|
||||
TextureIndices[iTex] = Index;
|
||||
}
|
||||
|
||||
// Vertex description
|
||||
pMat->mVtxDesc = (EVertexDescription) mpFile->ReadLong();
|
||||
|
||||
// Unknowns
|
||||
if (mVersion >= eEchoesDemo)
|
||||
{
|
||||
pMat->mEchoesUnknownA = mpFile->ReadLong();
|
||||
pMat->mEchoesUnknownB = mpFile->ReadLong();
|
||||
}
|
||||
mpFile->Seek(0x4, SEEK_CUR); // Skipping group index
|
||||
|
||||
// Konst
|
||||
if (pMat->mOptions & CMaterial::eKonst)
|
||||
{
|
||||
u32 KonstCount = mpFile->ReadLong();
|
||||
|
||||
for (u32 iKonst = 0; iKonst < KonstCount; iKonst++)
|
||||
{
|
||||
if (iKonst >= 4) break;
|
||||
pMat->mKonstColors[iKonst] = CColor(*mpFile);
|
||||
}
|
||||
if (KonstCount > 4) mpFile->Seek(0x4 * (KonstCount - 4), SEEK_CUR);
|
||||
}
|
||||
|
||||
// Blend mode
|
||||
pMat->mBlendDstFac = glBlendFactor[mpFile->ReadShort()];
|
||||
pMat->mBlendSrcFac = glBlendFactor[mpFile->ReadShort()];
|
||||
|
||||
// Indirect texture
|
||||
if (pMat->mOptions & CMaterial::eIndStage)
|
||||
{
|
||||
u32 IndTexIndex = mpFile->ReadLong();
|
||||
pMat->mpIndirectTexture = mTextures[IndTexIndex];
|
||||
}
|
||||
|
||||
// Color channels
|
||||
u32 ChanCount = mpFile->ReadLong();
|
||||
pMat->mLightingEnabled = ((mpFile->ReadLong() & 0x1) == 1);
|
||||
mpFile->Seek((4 * ChanCount) - 4, SEEK_CUR);
|
||||
|
||||
// TEV
|
||||
u32 TevCount = mpFile->ReadLong();
|
||||
pMat->mPasses.resize(TevCount);
|
||||
|
||||
for (u32 iTev = 0; iTev < TevCount; iTev++)
|
||||
{
|
||||
CMaterialPass *pPass = new CMaterialPass(pMat);
|
||||
|
||||
u32 ColorIn = mpFile->ReadLong();
|
||||
u32 AlphaIn = mpFile->ReadLong();
|
||||
pPass->mColorOutput = (ETevOutput) ((mpFile->ReadLong() & 0x600) >> 9);
|
||||
pPass->mAlphaOutput = (ETevOutput) ((mpFile->ReadLong() & 0x600) >> 9);
|
||||
mpFile->Seek(0x1, SEEK_CUR); // Padding byte
|
||||
pPass->mKAlphaSel = (ETevKSel) mpFile->ReadByte();
|
||||
pPass->mKColorSel = (ETevKSel) mpFile->ReadByte();
|
||||
pPass->mRasSel = (ETevRasSel) (u8) mpFile->ReadByte();
|
||||
|
||||
for (u32 iInput = 0; iInput < 4; iInput++)
|
||||
{
|
||||
pPass->mColorInputs[iInput] = (ETevColorInput) ((ColorIn >> (iInput * 5)) & 0xF);
|
||||
pPass->mAlphaInputs[iInput] = (ETevAlphaInput) ((AlphaIn >> (iInput * 5)) & 0x7);
|
||||
}
|
||||
|
||||
pMat->mPasses[iTev] = pPass;
|
||||
}
|
||||
|
||||
std::vector<u8> TevCoordIndices(TevCount);
|
||||
for (u32 iTev = 0; iTev < TevCount; iTev++)
|
||||
{
|
||||
mpFile->Seek(0x2, SEEK_CUR);
|
||||
CMaterialPass *pPass = pMat->Pass(iTev);
|
||||
|
||||
u8 TexSel = mpFile->ReadByte();
|
||||
|
||||
if ((TexSel == 0xFF) || (TexSel >= mTextures.size()))
|
||||
pPass->mpTexture = nullptr;
|
||||
else
|
||||
pPass->mpTexture = mTextures[TextureIndices[TexSel]];
|
||||
|
||||
TevCoordIndices[iTev] = mpFile->ReadByte();
|
||||
}
|
||||
|
||||
// TexGens
|
||||
u32 TexGenCount = mpFile->ReadLong();
|
||||
std::vector<u32> TexGens(TexGenCount);
|
||||
|
||||
for (u32 iTex = 0; iTex < TexGenCount; iTex++)
|
||||
TexGens[iTex] = mpFile->ReadLong();
|
||||
|
||||
// UV animations
|
||||
mpFile->Seek(0x4, SEEK_CUR); // Skipping UV anims size
|
||||
u32 NumAnims = mpFile->ReadLong();
|
||||
|
||||
struct SUVAnim {
|
||||
s32 Mode; float Params[4];
|
||||
};
|
||||
std::vector <SUVAnim> Anims(NumAnims);
|
||||
|
||||
for (u32 iAnim = 0; iAnim < NumAnims; iAnim++)
|
||||
{
|
||||
Anims[iAnim].Mode = mpFile->ReadLong();
|
||||
|
||||
switch (Anims[iAnim].Mode)
|
||||
{
|
||||
case 3: // Rotation
|
||||
case 7: // ???
|
||||
Anims[iAnim].Params[0] = mpFile->ReadFloat();
|
||||
Anims[iAnim].Params[1] = mpFile->ReadFloat();
|
||||
break;
|
||||
case 2: // UV Scroll
|
||||
case 4: // U Scroll
|
||||
case 5: // V Scroll
|
||||
Anims[iAnim].Params[0] = mpFile->ReadFloat();
|
||||
Anims[iAnim].Params[1] = mpFile->ReadFloat();
|
||||
Anims[iAnim].Params[2] = mpFile->ReadFloat();
|
||||
Anims[iAnim].Params[3] = mpFile->ReadFloat();
|
||||
break;
|
||||
case 0: // Inverse ModelView Matrix
|
||||
case 1: // Inverse ModelView Matrix Translated
|
||||
case 6: // Model Matrix
|
||||
break;
|
||||
default:
|
||||
Log::FileError(mpFile->GetSourceString(), mpFile->Tell() - 4, "Unsupported animation mode encountered: " + TString::HexString((u32) Anims[iAnim].Mode));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Move TexGen and anims into passes
|
||||
for (u32 iPass = 0; iPass < pMat->mPasses.size(); iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = pMat->mPasses[iPass];
|
||||
u8 TexCoordIdx = TevCoordIndices[iPass];
|
||||
|
||||
if ((TexGens.size() == 0) || (TexCoordIdx == 0xFF))
|
||||
{
|
||||
pPass->mTexCoordSource = 0xFF;
|
||||
pPass->mAnimMode = eNoUVAnim;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
pPass->mTexCoordSource = (u8) ((TexGens[TexCoordIdx] & 0x1F0) >> 4);
|
||||
|
||||
// Next step - find which animation is used by this pass
|
||||
// Texture matrix is a reliable way to tell, because every UV anim mode generates a texture matrix
|
||||
u32 TexMtxIdx = ((TexGens[TexCoordIdx] & 0x3E00) >> 9) / 3;
|
||||
|
||||
if (TexMtxIdx == 10) pPass->mAnimMode = eNoUVAnim; // 10 is identity matrix; indicates no UV anim for this pass
|
||||
|
||||
else
|
||||
{
|
||||
pPass->mAnimMode = (EUVAnimMode) Anims[TexMtxIdx].Mode;
|
||||
|
||||
for (u32 iParam = 0; iParam < 4; iParam++)
|
||||
pPass->mAnimParams[iParam] = Anims[TexMtxIdx].Params[iParam];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pMat;
|
||||
}
|
||||
|
||||
void CMaterialLoader::ReadCorruptionMatSet()
|
||||
{
|
||||
u32 NumMats = mpFile->ReadLong();
|
||||
mpSet->mMaterials.resize(NumMats);
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
{
|
||||
u32 Size = mpFile->ReadLong();
|
||||
u32 Next = mpFile->Tell() + Size;
|
||||
mpSet->mMaterials[iMat] = ReadCorruptionMaterial();
|
||||
mpSet->mMaterials[iMat]->mVersion = mVersion;
|
||||
mpSet->mMaterials[iMat]->mName = TString("Material #") + std::to_string(iMat + 1);
|
||||
mpFile->Seek(Next, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
CMaterial* CMaterialLoader::ReadCorruptionMaterial()
|
||||
{
|
||||
CMaterial *pMat = new CMaterial();
|
||||
pMat->mOptions = CMaterial::eDepthWrite;
|
||||
pMat->mEnableBloom = true;
|
||||
|
||||
// Flags
|
||||
u32 Flags = mpFile->ReadLong();
|
||||
if (Flags & 0x8)
|
||||
{
|
||||
pMat->mBlendSrcFac = GL_SRC_ALPHA;
|
||||
pMat->mBlendDstFac = GL_ONE_MINUS_SRC_ALPHA;
|
||||
pMat->mOptions |= CMaterial::eTransparent;
|
||||
}
|
||||
else if (Flags & 0x20)
|
||||
{
|
||||
pMat->mBlendSrcFac = GL_ONE;
|
||||
pMat->mBlendDstFac = GL_ONE;
|
||||
pMat->mOptions |= CMaterial::eTransparent;
|
||||
}
|
||||
|
||||
if (Flags & 0x10) pMat->mOptions |= CMaterial::ePunchthrough;
|
||||
if (Flags & 0x100) pMat->mOptions |= CMaterial::eOccluder;
|
||||
mHas0x400 = ((Flags & 0x400) != 0);
|
||||
|
||||
mpFile->Seek(0x8, SEEK_CUR); // Don't know what any of this is
|
||||
pMat->mVtxDesc = (EVertexDescription) mpFile->ReadLong();
|
||||
mpFile->Seek(0xC, SEEK_CUR);
|
||||
|
||||
// Initialize all KColors to white
|
||||
pMat->mKonstColors[0] = CColor::skWhite;
|
||||
pMat->mKonstColors[1] = CColor::skWhite;
|
||||
pMat->mKonstColors[2] = CColor::skWhite;
|
||||
// Current usage of KColors:
|
||||
// 0 - INT OPAC (transparency)
|
||||
// 1 - CLR DIFB (lightmap multiplier)
|
||||
// 2 - CLR CLR (additive color)
|
||||
|
||||
while (true)
|
||||
{
|
||||
CFourCC Type = mpFile->ReadLong();
|
||||
|
||||
// END
|
||||
if (Type == "END ")
|
||||
break;
|
||||
|
||||
// INT
|
||||
if (Type == "INT ")
|
||||
{
|
||||
CFourCC IntType = mpFile->ReadLong();
|
||||
u8 IntVal = (u8) mpFile->ReadLong();
|
||||
|
||||
if (IntType == "OPAC")
|
||||
{
|
||||
pMat->mKonstColors[0] = CColor(1.f, 1.f, 1.f, (float) IntVal / 255);
|
||||
mHasOPAC = true;
|
||||
}
|
||||
}
|
||||
|
||||
// CLR
|
||||
if (Type == "CLR ")
|
||||
{
|
||||
CFourCC ClrType = mpFile->ReadLong();
|
||||
CColor ClrVal(*mpFile);
|
||||
|
||||
if (ClrType == "DIFB")
|
||||
{
|
||||
ClrVal.a = 0xFF;
|
||||
pMat->mKonstColors[1] = ClrVal;
|
||||
}
|
||||
|
||||
if (ClrType == "CLR ")
|
||||
{
|
||||
// I'm not sure what this does. It has a clear and obvious ingame effect
|
||||
// but I need to test it further to tell specifically what it's doing.
|
||||
// All attempts at implementing this just break things.
|
||||
}
|
||||
}
|
||||
|
||||
// PASS
|
||||
if (Type == "PASS")
|
||||
{
|
||||
CMaterialPass *pPass = new CMaterialPass(pMat);
|
||||
mPassOffsets.push_back(mpFile->Tell() - 4);
|
||||
|
||||
u32 Size = mpFile->ReadLong();
|
||||
u32 Next = Size + mpFile->Tell();
|
||||
|
||||
pPass->mPassType = mpFile->ReadLong();
|
||||
pPass->mSettings = (CMaterialPass::EPassSettings) mpFile->ReadLong();
|
||||
|
||||
u64 TextureID = mpFile->ReadLongLong();
|
||||
if (TextureID == 0xFFFFFFFFFFFFFFFF)
|
||||
{
|
||||
Log::FileWarning(mpFile->GetSourceString(), mPassOffsets.back(), "Skipping " + pPass->mPassType.ToString() + " pass with no texture");
|
||||
delete pPass;
|
||||
continue;
|
||||
}
|
||||
|
||||
pPass->mpTexture = gResCache.GetResource(TextureID, "TXTR");
|
||||
|
||||
pPass->mTexCoordSource = 4 + (u8) mpFile->ReadLong();
|
||||
u32 AnimSize = mpFile->ReadLong();
|
||||
|
||||
if (AnimSize > 0)
|
||||
{
|
||||
mpFile->Seek(0x4, SEEK_CUR);
|
||||
pPass->mAnimMode = (EUVAnimMode) mpFile->ReadLong();
|
||||
|
||||
switch (pPass->mAnimMode)
|
||||
{
|
||||
case 3: // Rotation
|
||||
case 7: // ???
|
||||
pPass->mAnimParams[0] = mpFile->ReadFloat();
|
||||
pPass->mAnimParams[1] = mpFile->ReadFloat();
|
||||
break;
|
||||
case 2: // UV Scroll
|
||||
case 4: // U Scroll
|
||||
case 5: // V Scroll
|
||||
pPass->mAnimParams[0] = mpFile->ReadFloat();
|
||||
pPass->mAnimParams[1] = mpFile->ReadFloat();
|
||||
pPass->mAnimParams[2] = mpFile->ReadFloat();
|
||||
pPass->mAnimParams[3] = mpFile->ReadFloat();
|
||||
break;
|
||||
case 0: // Inverse ModelView Matrix
|
||||
case 1: // Inverse ModelView Matrix Translated
|
||||
case 6: // Model Matrix
|
||||
case 10: // Yet-to-be-named
|
||||
break;
|
||||
default:
|
||||
Log::FileError(mpFile->GetSourceString(), mpFile->Tell() - 8, "Unsupported animation mode encountered: " + TString::HexString((u32) pPass->mAnimMode));
|
||||
break;
|
||||
}
|
||||
|
||||
// Hack until the correct way to determine tex coord source is figured out
|
||||
if ((pPass->mAnimMode < 2) || (pPass->mAnimMode == 6) || (pPass->mAnimMode == 7) || (pPass->mAnimMode == 10))
|
||||
pPass->mTexCoordSource = 1;
|
||||
}
|
||||
|
||||
else pPass->mAnimMode = eNoUVAnim;
|
||||
|
||||
pMat->mPasses.push_back(pPass);
|
||||
mpFile->Seek(Next, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
CreateCorruptionPasses(pMat);
|
||||
mHasOPAC = false;
|
||||
return pMat;
|
||||
}
|
||||
|
||||
void CMaterialLoader::CreateCorruptionPasses(CMaterial *pMat)
|
||||
{
|
||||
u32 NumPass = pMat->PassCount();
|
||||
bool Lightmap = false;
|
||||
bool AlphaBlended = ((pMat->mBlendSrcFac == GL_SRC_ALPHA) && (pMat->mBlendDstFac == GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPass; iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = pMat->Pass(iPass);
|
||||
CFourCC Type = pPass->Type();
|
||||
|
||||
// Color Map (Diffuse)
|
||||
if (Type == "CLR ")
|
||||
{
|
||||
if (Lightmap)
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eColor0RGB, eTextureRGB, ePrevRGB);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eRasRGB, eTextureRGB, ePrevRGB);
|
||||
pPass->SetRasSel(eRasColor0A0);
|
||||
}
|
||||
|
||||
|
||||
if (pMat->mOptions & CMaterial::ePunchthrough)
|
||||
{
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, eTextureAlpha);
|
||||
}
|
||||
else if (mHasOPAC)
|
||||
{
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, eKonstAlpha);
|
||||
pPass->SetKColorSel(eKonst0_RGB);
|
||||
pPass->SetKAlphaSel(eKonst0_A);
|
||||
}
|
||||
else
|
||||
{
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
}
|
||||
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
}
|
||||
|
||||
// Lightmap
|
||||
else if (Type == "DIFF")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eKonstRGB, eTextureRGB, eZeroRGB);
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, eKonstAlpha);
|
||||
pPass->SetColorOutput(eColor0Reg);
|
||||
pPass->SetAlphaOutput(eColor0Reg);
|
||||
pPass->SetKColorSel(eKonst1_RGB);
|
||||
pPass->SetKAlphaSel(eKonst1_A);
|
||||
pPass->SetRasSel(eRasColor0A0);
|
||||
Lightmap = true;
|
||||
}
|
||||
|
||||
// Bloom Lightmap
|
||||
else if (Type == "BLOL")
|
||||
{
|
||||
// Bloom maps work by writing to framebuffer alpha. Can't do this on alpha-blended mats.
|
||||
pPass->SetColorInputs(eZeroRGB, eZeroRGB, eZeroRGB, ePrevRGB);
|
||||
|
||||
if ((AlphaBlended) || (pMat->mOptions & CMaterial::ePunchthrough))
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
else
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, eTextureAlpha);
|
||||
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
}
|
||||
|
||||
// Rim Light Map
|
||||
else if (Type == "RIML")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eOneRGB, ePrevRGB, eTextureRGB);
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
}
|
||||
|
||||
// Emissive Map
|
||||
else if (Type == "INCA")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eTextureRGB, eOneRGB, ePrevRGB);
|
||||
|
||||
if ((pPass->mSettings & CMaterialPass::eEmissiveBloom) && (!AlphaBlended))
|
||||
{
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eTextureAlpha, eKonstAlpha, ePrevAlpha);
|
||||
pPass->SetKAlphaSel(eKonstOneFourth);
|
||||
}
|
||||
else
|
||||
{
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
}
|
||||
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
}
|
||||
|
||||
// Opacity Map
|
||||
else if (Type == "TRAN")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eZeroRGB, eZeroRGB, ePrevRGB);
|
||||
|
||||
if (pPass->mSettings & CMaterialPass::eInvertOpacityMap)
|
||||
pPass->SetAlphaInputs(eKonstAlpha, eZeroAlpha, eTextureAlpha, eZeroAlpha);
|
||||
else
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eKonstAlpha, eTextureAlpha, eZeroAlpha);
|
||||
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
}
|
||||
|
||||
// Specular Map
|
||||
else if (Type == "RFLV")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eZeroRGB, eZeroRGB, eTextureRGB);
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
pPass->SetColorOutput(eColor2Reg);
|
||||
pPass->SetAlphaOutput(eColor2Reg);
|
||||
}
|
||||
|
||||
// Reflection Map
|
||||
else if (Type == "RFLD")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eColor2RGB, eTextureRGB, ePrevRGB);
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
if (mHas0x400) pPass->SetEnabled(false);
|
||||
}
|
||||
|
||||
// Bloom
|
||||
else if (Type == "BLOI")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eZeroRGB, eZeroRGB, ePrevRGB);
|
||||
|
||||
// Comes out wrong every time even though this is exactly how the Dolphin shaders say this is done.
|
||||
if (AlphaBlended)
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
else
|
||||
pPass->SetAlphaInputs(eTextureAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
}
|
||||
|
||||
// X-Ray - since we don't support X-Ray previews, no effect
|
||||
else if (Type == "XRAY")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, eZeroRGB, eZeroRGB, ePrevRGB);
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, ePrevAlpha);
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetAlphaOutput(ePrevReg);
|
||||
}
|
||||
|
||||
// Toon? Don't know what it's for but got TEV setup from shader dumps
|
||||
else if (Type == "TOON")
|
||||
{
|
||||
pPass->SetColorInputs(eZeroRGB, ePrevRGB, eTextureRGB, eZeroRGB);
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, eTextureAlpha);
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
pPass->SetColorOutput(ePrevReg);
|
||||
}
|
||||
|
||||
else if (Type == "CUST") {}
|
||||
|
||||
else
|
||||
{
|
||||
Log::FileError(mpFile->GetSourceString(), mPassOffsets[iPass], "Unsupported material pass type: " + Type.ToString());
|
||||
pPass->mEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CMaterial* CMaterialLoader::LoadAssimpMaterial(const aiMaterial *pAiMat)
|
||||
{
|
||||
// todo: generate new material using import values.
|
||||
CMaterial *pMat = new CMaterial(mVersion, eNoAttributes);
|
||||
|
||||
aiString name;
|
||||
pAiMat->Get(AI_MATKEY_NAME, name);
|
||||
pMat->SetName(name.C_Str());
|
||||
|
||||
// Create generic custom pass that uses Konst color
|
||||
CMaterialPass *pPass = new CMaterialPass(pMat);
|
||||
pPass->SetColorInputs(eZeroRGB, eRasRGB, eKonstRGB, eZeroRGB);
|
||||
pPass->SetAlphaInputs(eZeroAlpha, eZeroAlpha, eZeroAlpha, eKonstAlpha);
|
||||
pPass->SetKColorSel(eKonst0_RGB);
|
||||
pPass->SetKAlphaSel(eKonstOne);
|
||||
pPass->SetRasSel(eRasColor0A0);
|
||||
pMat->mKonstColors[0] = CColor::RandomLightColor(false);
|
||||
pMat->mPasses.push_back(pPass);
|
||||
|
||||
return pMat;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CMaterialSet* CMaterialLoader::LoadMaterialSet(CInputStream& Mat, EGame Version)
|
||||
{
|
||||
CMaterialLoader Loader;
|
||||
Loader.mpSet = new CMaterialSet();
|
||||
Loader.mpFile = &Mat;
|
||||
Loader.mVersion = Version;
|
||||
|
||||
if ((Version >= ePrimeDemo) && (Version <= eEchoes))
|
||||
Loader.ReadPrimeMatSet();
|
||||
else
|
||||
Loader.ReadCorruptionMatSet();
|
||||
|
||||
return Loader.mpSet;
|
||||
}
|
||||
|
||||
CMaterialSet* CMaterialLoader::ImportAssimpMaterials(const aiScene *pScene, EGame targetVersion)
|
||||
{
|
||||
CMaterialLoader loader;
|
||||
loader.mVersion = targetVersion;
|
||||
|
||||
CMaterialSet *pOut = new CMaterialSet();
|
||||
pOut->mMaterials.reserve(pScene->mNumMaterials);
|
||||
|
||||
for (u32 iMat = 0; iMat < pScene->mNumMaterials; iMat++)
|
||||
{
|
||||
CMaterial *pMat = loader.LoadAssimpMaterial(pScene->mMaterials[iMat]);
|
||||
pOut->mMaterials.push_back(pMat);
|
||||
}
|
||||
|
||||
return pOut;
|
||||
}
|
||||
45
src/Core/Resource/Factory/CMaterialLoader.h
Normal file
45
src/Core/Resource/Factory/CMaterialLoader.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef CMATERIALLOADER_H
|
||||
#define CMATERIALLOADER_H
|
||||
|
||||
#include "../CMaterialSet.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <assimp/scene.h>
|
||||
|
||||
class CMaterialLoader
|
||||
{
|
||||
// Material data
|
||||
CMaterialSet *mpSet;
|
||||
CInputStream *mpFile;
|
||||
EGame mVersion;
|
||||
std::vector<TResPtr<CTexture>> mTextures;
|
||||
bool mHasOPAC;
|
||||
bool mHas0x400;
|
||||
|
||||
CColor mCorruptionColors[4];
|
||||
u8 mCorruptionInts[5];
|
||||
u32 mCorruptionFlags;
|
||||
std::vector<u32> mPassOffsets;
|
||||
|
||||
CMaterialLoader();
|
||||
~CMaterialLoader();
|
||||
|
||||
// Load Functions
|
||||
void ReadPrimeMatSet();
|
||||
CMaterial* ReadPrimeMaterial();
|
||||
|
||||
void ReadCorruptionMatSet();
|
||||
CMaterial* ReadCorruptionMaterial();
|
||||
void CreateCorruptionPasses(CMaterial *pMat);
|
||||
|
||||
CMaterial* LoadAssimpMaterial(const aiMaterial *pAiMat);
|
||||
|
||||
// Static
|
||||
public:
|
||||
static CMaterialSet* LoadMaterialSet(CInputStream& Mat, EGame Version);
|
||||
static CMaterialSet* ImportAssimpMaterials(const aiScene *pScene, EGame targetVersion);
|
||||
};
|
||||
|
||||
#endif // CMATERIALLOADER_H
|
||||
590
src/Core/Resource/Factory/CModelLoader.cpp
Normal file
590
src/Core/Resource/Factory/CModelLoader.cpp
Normal file
@@ -0,0 +1,590 @@
|
||||
#include "CModelLoader.h"
|
||||
#include "CMaterialLoader.h"
|
||||
#include <Core/Log.h>
|
||||
|
||||
CModelLoader::CModelLoader()
|
||||
{
|
||||
mFlags = eNoFlags;
|
||||
mNumVertices = 0;
|
||||
}
|
||||
|
||||
CModelLoader::~CModelLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void CModelLoader::LoadWorldMeshHeader(CInputStream &Model)
|
||||
{
|
||||
// I don't really have any need for most of this data, so
|
||||
Model.Seek(0x34, SEEK_CUR);
|
||||
mAABox = CAABox(Model);
|
||||
mpBlockMgr->ToNextBlock();
|
||||
}
|
||||
|
||||
void CModelLoader::LoadAttribArrays(CInputStream& Model)
|
||||
{
|
||||
// Positions
|
||||
if (mFlags & eShortPositions) // Shorts (DKCR only)
|
||||
{
|
||||
mPositions.resize(mpBlockMgr->CurrentBlockSize() / 0x6);
|
||||
float Divisor = 8192.f; // Might be incorrect! Needs verification via size comparison.
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
|
||||
{
|
||||
float x = Model.ReadShort() / Divisor;
|
||||
float y = Model.ReadShort() / Divisor;
|
||||
float z = Model.ReadShort() / Divisor;
|
||||
mPositions[iVtx] = CVector3f(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
else // Floats
|
||||
{
|
||||
mPositions.resize(mpBlockMgr->CurrentBlockSize() / 0xC);
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++)
|
||||
mPositions[iVtx] = CVector3f(Model);
|
||||
}
|
||||
|
||||
mpBlockMgr->ToNextBlock();
|
||||
|
||||
// Normals
|
||||
if (mFlags & eShortNormals) // Shorts
|
||||
{
|
||||
mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0x6);
|
||||
float Divisor = (mVersion < eReturns) ? 32768.f : 16384.f;
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
|
||||
{
|
||||
float x = Model.ReadShort() / Divisor;
|
||||
float y = Model.ReadShort() / Divisor;
|
||||
float z = Model.ReadShort() / Divisor;
|
||||
mNormals[iVtx] = CVector3f(x, y, z);
|
||||
}
|
||||
}
|
||||
else // Floats
|
||||
{
|
||||
mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0xC);
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++)
|
||||
mNormals[iVtx] = CVector3f(Model);
|
||||
}
|
||||
|
||||
mpBlockMgr->ToNextBlock();
|
||||
|
||||
// Colors
|
||||
mColors.resize(mpBlockMgr->CurrentBlockSize() / 4);
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mColors.size(); iVtx++)
|
||||
mColors[iVtx] = CColor(Model);
|
||||
|
||||
mpBlockMgr->ToNextBlock();
|
||||
|
||||
|
||||
// Float UVs
|
||||
mTex0.resize(mpBlockMgr->CurrentBlockSize() / 0x8);
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mTex0.size(); iVtx++)
|
||||
mTex0[iVtx] = CVector2f(Model);
|
||||
|
||||
mpBlockMgr->ToNextBlock();
|
||||
|
||||
// Short UVs
|
||||
if (mFlags & eHasTex1)
|
||||
{
|
||||
mTex1.resize(mpBlockMgr->CurrentBlockSize() / 0x4);
|
||||
float Divisor = (mVersion < eReturns) ? 32768.f : 8192.f;
|
||||
|
||||
for (u32 iVtx = 0; iVtx < mTex1.size(); iVtx++)
|
||||
{
|
||||
float x = Model.ReadShort() / Divisor;
|
||||
float y = Model.ReadShort() / Divisor;
|
||||
mTex1[iVtx] = CVector2f(x, y);
|
||||
}
|
||||
|
||||
mpBlockMgr->ToNextBlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CModelLoader::LoadSurfaceOffsets(CInputStream& Model)
|
||||
{
|
||||
mSurfaceCount = Model.ReadLong();
|
||||
mSurfaceOffsets.resize(mSurfaceCount);
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mSurfaceCount; iSurf++)
|
||||
mSurfaceOffsets[iSurf] = Model.ReadLong();
|
||||
|
||||
mpBlockMgr->ToNextBlock();
|
||||
}
|
||||
|
||||
SSurface* CModelLoader::LoadSurface(CInputStream& Model)
|
||||
{
|
||||
SSurface *pSurf = new SSurface;
|
||||
|
||||
// Surface header
|
||||
if (mVersion < eReturns)
|
||||
LoadSurfaceHeaderPrime(Model, pSurf);
|
||||
else
|
||||
LoadSurfaceHeaderDKCR(Model, pSurf);
|
||||
|
||||
bool HasAABB = (pSurf->AABox != CAABox::skInfinite);
|
||||
CMaterial *pMat = mMaterials[0]->MaterialByIndex(pSurf->MaterialID);
|
||||
|
||||
// Primitive table
|
||||
u8 Flag = Model.ReadByte();
|
||||
u32 NextSurface = mpBlockMgr->NextOffset();
|
||||
|
||||
while ((Flag != 0) && ((u32) Model.Tell() < NextSurface))
|
||||
{
|
||||
SSurface::SPrimitive Prim;
|
||||
Prim.Type = EGXPrimitiveType(Flag & 0xF8);
|
||||
u16 VertexCount = Model.ReadShort();
|
||||
|
||||
for (u16 iVtx = 0; iVtx < VertexCount; iVtx++)
|
||||
{
|
||||
CVertex Vtx;
|
||||
EVertexDescription VtxDesc = pMat->VtxDesc();
|
||||
|
||||
for (u32 iMtxAttr = 0; iMtxAttr < 8; iMtxAttr++)
|
||||
if (VtxDesc & (ePosMtx << iMtxAttr)) Model.Seek(0x1, SEEK_CUR);
|
||||
|
||||
// Only thing to do here is check whether each attribute is present, and if so, read it.
|
||||
// A couple attributes have special considerations; normals can be floats or shorts, as can tex0, depending on vtxfmt.
|
||||
// tex0 can also be read from either UV buffer; depends what the material says.
|
||||
|
||||
// Position
|
||||
if (VtxDesc & ePosition)
|
||||
{
|
||||
u16 PosIndex = Model.ReadShort() & 0xFFFF;
|
||||
Vtx.Position = mPositions[PosIndex];
|
||||
Vtx.ArrayPosition = PosIndex;
|
||||
|
||||
if (!HasAABB) pSurf->AABox.ExpandBounds(Vtx.Position);
|
||||
}
|
||||
|
||||
// Normal
|
||||
if (VtxDesc & eNormal)
|
||||
Vtx.Normal = mNormals[Model.ReadShort() & 0xFFFF];
|
||||
|
||||
// Color
|
||||
for (u32 c = 0; c < 2; c++)
|
||||
if (VtxDesc & (eColor0 << (c * 2)))
|
||||
Vtx.Color[c] = mColors[Model.ReadShort() & 0xFFFF];
|
||||
|
||||
// Tex Coords - these are done a bit differently in DKCR than in the Prime series
|
||||
if (mVersion < eReturns)
|
||||
{
|
||||
// Tex0
|
||||
if (VtxDesc & eTex0)
|
||||
{
|
||||
if ((mFlags & eHasTex1) && (pMat->Options() & CMaterial::eShortTexCoord))
|
||||
Vtx.Tex[0] = mTex1[Model.ReadShort() & 0xFFFF];
|
||||
else
|
||||
Vtx.Tex[0] = mTex0[Model.ReadShort() & 0xFFFF];
|
||||
}
|
||||
|
||||
// Tex1-7
|
||||
for (u32 iTex = 1; iTex < 7; iTex++)
|
||||
if (VtxDesc & (eTex0 << (iTex * 2)))
|
||||
Vtx.Tex[iTex] = mTex0[Model.ReadShort() & 0xFFFF];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Tex0-7
|
||||
for (u32 iTex = 0; iTex < 7; iTex++)
|
||||
{
|
||||
if (VtxDesc & (eTex0 << iTex * 2))
|
||||
{
|
||||
if (!mSurfaceUsingTex1)
|
||||
Vtx.Tex[iTex] = mTex0[Model.ReadShort() & 0xFFFF];
|
||||
else
|
||||
Vtx.Tex[iTex] = mTex1[Model.ReadShort() & 0xFFFF];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Prim.Vertices.push_back(Vtx);
|
||||
} // Vertex array end
|
||||
|
||||
// Update vertex/triangle count
|
||||
pSurf->VertexCount += VertexCount;
|
||||
|
||||
switch (Prim.Type)
|
||||
{
|
||||
case eGX_Triangles:
|
||||
pSurf->TriangleCount += VertexCount / 3;
|
||||
break;
|
||||
case eGX_TriangleFan:
|
||||
case eGX_TriangleStrip:
|
||||
pSurf->TriangleCount += VertexCount - 2;
|
||||
break;
|
||||
}
|
||||
|
||||
pSurf->Primitives.push_back(Prim);
|
||||
Flag = Model.ReadByte();
|
||||
} // Primitive table end
|
||||
|
||||
mpBlockMgr->ToNextBlock();
|
||||
return pSurf;
|
||||
}
|
||||
|
||||
void CModelLoader::LoadSurfaceHeaderPrime(CInputStream& Model, SSurface *pSurf)
|
||||
{
|
||||
pSurf->CenterPoint = CVector3f(Model);
|
||||
pSurf->MaterialID = Model.ReadLong();
|
||||
|
||||
Model.Seek(0xC, SEEK_CUR);
|
||||
u32 ExtraSize = Model.ReadLong();
|
||||
pSurf->ReflectionDirection = CVector3f(Model);
|
||||
if (mVersion >= eEchoesDemo) Model.Seek(0x4, SEEK_CUR); // Extra values in Echoes. Not sure what they are.
|
||||
bool HasAABox = (ExtraSize >= 0x18); // MREAs have a set of bounding box coordinates here.
|
||||
|
||||
// If this surface has a bounding box, we can just read it here. Otherwise we'll fill it in manually.
|
||||
if (HasAABox)
|
||||
{
|
||||
ExtraSize -= 0x18;
|
||||
pSurf->AABox = CAABox(Model);
|
||||
}
|
||||
else
|
||||
pSurf->AABox = CAABox::skInfinite;
|
||||
|
||||
Model.Seek(ExtraSize, SEEK_CUR);
|
||||
Model.SeekToBoundary(32);
|
||||
}
|
||||
|
||||
void CModelLoader::LoadSurfaceHeaderDKCR(CInputStream& Model, SSurface *pSurf)
|
||||
{
|
||||
pSurf->CenterPoint = CVector3f(Model);
|
||||
Model.Seek(0xE, SEEK_CUR);
|
||||
pSurf->MaterialID = (u32) Model.ReadShort();
|
||||
Model.Seek(0x2, SEEK_CUR);
|
||||
mSurfaceUsingTex1 = (Model.ReadByte() == 1);
|
||||
u32 ExtraSize = Model.ReadByte();
|
||||
|
||||
if (ExtraSize > 0)
|
||||
{
|
||||
ExtraSize -= 0x18;
|
||||
pSurf->AABox = CAABox(Model);
|
||||
}
|
||||
else
|
||||
pSurf->AABox = CAABox::skInfinite;
|
||||
|
||||
Model.Seek(ExtraSize, SEEK_CUR);
|
||||
Model.SeekToBoundary(32);
|
||||
}
|
||||
|
||||
SSurface* CModelLoader::LoadAssimpMesh(const aiMesh *pMesh, CMaterialSet *pSet)
|
||||
{
|
||||
// Create vertex description and assign it to material
|
||||
CMaterial *pMat = pSet->MaterialByIndex(pMesh->mMaterialIndex);
|
||||
EVertexDescription desc = pMat->VtxDesc();
|
||||
|
||||
if (desc == eNoAttributes)
|
||||
{
|
||||
if (pMesh->HasPositions()) desc |= ePosition;
|
||||
if (pMesh->HasNormals()) desc |= eNormal;
|
||||
|
||||
for (u32 iUV = 0; iUV < pMesh->GetNumUVChannels(); iUV++)
|
||||
desc |= (eTex0 << (iUV * 2));
|
||||
|
||||
pMat->SetVertexDescription(desc);
|
||||
|
||||
// TEMP - disable dynamic lighting on geometry with no normals
|
||||
if (!pMesh->HasNormals())
|
||||
{
|
||||
pMat->SetLightingEnabled(false);
|
||||
pMat->Pass(0)->SetColorInputs(eZeroRGB, eOneRGB, eKonstRGB, eZeroRGB);
|
||||
pMat->Pass(0)->SetRasSel(eRasColorNull);
|
||||
}
|
||||
}
|
||||
|
||||
// Create surface
|
||||
SSurface *pSurf = new SSurface();
|
||||
pSurf->MaterialID = pMesh->mMaterialIndex;
|
||||
|
||||
if (pMesh->mNumFaces > 0)
|
||||
{
|
||||
pSurf->Primitives.resize(1);
|
||||
SSurface::SPrimitive& prim = pSurf->Primitives[0];
|
||||
|
||||
// Check primitive type on first face
|
||||
u32 numIndices = pMesh->mFaces[0].mNumIndices;
|
||||
if (numIndices == 1) prim.Type = eGX_Points;
|
||||
else if (numIndices == 2) prim.Type = eGX_Lines;
|
||||
else if (numIndices == 3) prim.Type = eGX_Triangles;
|
||||
|
||||
// Generate bounding box, center point, and reflection projection
|
||||
pSurf->CenterPoint = CVector3f::skZero;
|
||||
pSurf->ReflectionDirection = CVector3f::skZero;
|
||||
|
||||
for (u32 iVtx = 0; iVtx < pMesh->mNumVertices; iVtx++)
|
||||
{
|
||||
aiVector3D aiPos = pMesh->mVertices[iVtx];
|
||||
pSurf->AABox.ExpandBounds(CVector3f(aiPos.x, aiPos.y, aiPos.z));
|
||||
|
||||
if (pMesh->HasNormals()) {
|
||||
aiVector3D aiNrm = pMesh->mNormals[iVtx];
|
||||
pSurf->ReflectionDirection += CVector3f(aiNrm.x, aiNrm.y, aiNrm.z);
|
||||
}
|
||||
}
|
||||
pSurf->CenterPoint = pSurf->AABox.Center();
|
||||
|
||||
if (pMesh->HasNormals())
|
||||
pSurf->ReflectionDirection /= (float) pMesh->mNumVertices;
|
||||
else
|
||||
pSurf->ReflectionDirection = CVector3f(1.f, 0.f, 0.f);
|
||||
|
||||
// Set vertex/triangle count
|
||||
pSurf->VertexCount = pMesh->mNumVertices;
|
||||
pSurf->TriangleCount = (prim.Type == eGX_Triangles ? pMesh->mNumFaces : 0);
|
||||
|
||||
// Create primitive
|
||||
for (u32 iFace = 0; iFace < pMesh->mNumFaces; iFace++)
|
||||
{
|
||||
for (u32 iIndex = 0; iIndex < numIndices; iIndex++)
|
||||
{
|
||||
u32 index = pMesh->mFaces[iFace].mIndices[iIndex];
|
||||
|
||||
// Create vertex and add it to the primitive
|
||||
CVertex vert;
|
||||
vert.ArrayPosition = index + mNumVertices;
|
||||
|
||||
if (pMesh->HasPositions()) {
|
||||
aiVector3D aiPos = pMesh->mVertices[index];
|
||||
vert.Position = CVector3f(aiPos.x, aiPos.y, aiPos.z);
|
||||
}
|
||||
|
||||
if (pMesh->HasNormals()) {
|
||||
aiVector3D aiNrm = pMesh->mNormals[index];
|
||||
vert.Normal = CVector3f(aiNrm.x, aiNrm.y, aiNrm.z);
|
||||
}
|
||||
|
||||
for (u32 iTex = 0; iTex < pMesh->GetNumUVChannels(); iTex++) {
|
||||
aiVector3D aiTex = pMesh->mTextureCoords[iTex][index];
|
||||
vert.Tex[iTex] = CVector2f(aiTex.x, aiTex.y);
|
||||
}
|
||||
|
||||
prim.Vertices.push_back(vert);
|
||||
}
|
||||
}
|
||||
|
||||
mNumVertices += pMesh->mNumVertices;
|
||||
}
|
||||
|
||||
return pSurf;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CModel* CModelLoader::LoadCMDL(CInputStream& CMDL)
|
||||
{
|
||||
CModelLoader Loader;
|
||||
Log::Write("Loading " + CMDL.GetSourceString());
|
||||
|
||||
// CMDL header - same across the three Primes, but different structure in DKCR
|
||||
u32 Magic = CMDL.ReadLong();
|
||||
|
||||
u32 Version, BlockCount, MatSetCount;
|
||||
CAABox AABox;
|
||||
|
||||
// 0xDEADBABE - Metroid Prime seres
|
||||
if (Magic == 0xDEADBABE)
|
||||
{
|
||||
Version = CMDL.ReadLong();
|
||||
u32 Flags = CMDL.ReadLong();
|
||||
AABox = CAABox(CMDL);
|
||||
BlockCount = CMDL.ReadLong();
|
||||
MatSetCount = CMDL.ReadLong();
|
||||
|
||||
if (Flags & 0x2) Loader.mFlags |= eShortNormals;
|
||||
if (Flags & 0x4) Loader.mFlags |= eHasTex1;
|
||||
}
|
||||
|
||||
// 0x9381000A - Donkey Kong Country Returns
|
||||
else if (Magic == 0x9381000A)
|
||||
{
|
||||
Version = Magic & 0xFFFF;
|
||||
u32 Flags = CMDL.ReadLong();
|
||||
AABox = CAABox(CMDL);
|
||||
BlockCount = CMDL.ReadLong();
|
||||
MatSetCount = CMDL.ReadLong();
|
||||
|
||||
// todo: unknown flags
|
||||
Loader.mFlags = eShortNormals | eHasTex1;
|
||||
if (Flags & 0x10) Loader.mFlags |= eHasVisGroups;
|
||||
if (Flags & 0x20) Loader.mFlags |= eShortPositions;
|
||||
|
||||
// Visibility group data
|
||||
// Skipping for now - should read in eventually
|
||||
if (Flags & 0x10)
|
||||
{
|
||||
CMDL.Seek(0x4, SEEK_CUR);
|
||||
u32 VisGroupCount = CMDL.ReadLong();
|
||||
|
||||
for (u32 iVis = 0; iVis < VisGroupCount; iVis++)
|
||||
{
|
||||
u32 NameLength = CMDL.ReadLong();
|
||||
CMDL.Seek(NameLength, SEEK_CUR);
|
||||
}
|
||||
|
||||
CMDL.Seek(0x14, SEEK_CUR); // no clue what any of this is!
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Log::FileError(CMDL.GetSourceString(), "Invalid CMDL magic: " + TString::HexString(Magic));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The rest is common to all CMDL versions
|
||||
Loader.mVersion = GetFormatVersion(Version);
|
||||
|
||||
if (Loader.mVersion == eUnknownVersion)
|
||||
{
|
||||
Log::FileError(CMDL.GetSourceString(), "Unsupported CMDL version: " + TString::HexString(Magic));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CModel *pModel = new CModel();
|
||||
Loader.mpModel = pModel;
|
||||
Loader.mpBlockMgr = new CBlockMgrIn(BlockCount, &CMDL);
|
||||
CMDL.SeekToBoundary(32);
|
||||
Loader.mpBlockMgr->Init();
|
||||
|
||||
// Materials
|
||||
Loader.mMaterials.resize(MatSetCount);
|
||||
for (u32 iMat = 0; iMat < MatSetCount; iMat++)
|
||||
{
|
||||
Loader.mMaterials[iMat] = CMaterialLoader::LoadMaterialSet(CMDL, Loader.mVersion);
|
||||
|
||||
if (Loader.mVersion < eCorruptionProto)
|
||||
Loader.mpBlockMgr->ToNextBlock();
|
||||
}
|
||||
|
||||
pModel->mMaterialSets = Loader.mMaterials;
|
||||
pModel->mHasOwnMaterials = true;
|
||||
if (Loader.mVersion >= eCorruptionProto) Loader.mpBlockMgr->ToNextBlock();
|
||||
|
||||
// Mesh
|
||||
Loader.LoadAttribArrays(CMDL);
|
||||
Loader.LoadSurfaceOffsets(CMDL);
|
||||
pModel->mSurfaces.reserve(Loader.mSurfaceCount);
|
||||
|
||||
for (u32 iSurf = 0; iSurf < Loader.mSurfaceCount; iSurf++)
|
||||
{
|
||||
SSurface *pSurf = Loader.LoadSurface(CMDL);
|
||||
pModel->mSurfaces.push_back(pSurf);
|
||||
pModel->mVertexCount += pSurf->VertexCount;
|
||||
pModel->mTriangleCount += pSurf->TriangleCount;
|
||||
}
|
||||
|
||||
pModel->mAABox = AABox;
|
||||
pModel->mHasOwnSurfaces = true;
|
||||
|
||||
// Cleanup
|
||||
delete Loader.mpBlockMgr;
|
||||
return pModel;
|
||||
}
|
||||
|
||||
CModel* CModelLoader::LoadWorldModel(CInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version)
|
||||
{
|
||||
CModelLoader Loader;
|
||||
Loader.mpBlockMgr = &BlockMgr;
|
||||
Loader.mVersion = Version;
|
||||
Loader.mFlags = eShortNormals;
|
||||
if (Version != eCorruptionProto) Loader.mFlags |= eHasTex1;
|
||||
Loader.mMaterials.resize(1);
|
||||
Loader.mMaterials[0] = &MatSet;
|
||||
|
||||
Loader.LoadWorldMeshHeader(MREA);
|
||||
Loader.LoadAttribArrays(MREA);
|
||||
Loader.LoadSurfaceOffsets(MREA);
|
||||
|
||||
CModel *pModel = new CModel();
|
||||
pModel->mMaterialSets.resize(1);
|
||||
pModel->mMaterialSets[0] = &MatSet;
|
||||
pModel->mHasOwnMaterials = false;
|
||||
pModel->mSurfaces.reserve(Loader.mSurfaceCount);
|
||||
pModel->mHasOwnSurfaces = true;
|
||||
|
||||
for (u32 iSurf = 0; iSurf < Loader.mSurfaceCount; iSurf++)
|
||||
{
|
||||
SSurface *pSurf = Loader.LoadSurface(MREA);
|
||||
pModel->mSurfaces.push_back(pSurf);
|
||||
pModel->mVertexCount += pSurf->VertexCount;
|
||||
pModel->mTriangleCount += pSurf->TriangleCount;
|
||||
}
|
||||
|
||||
pModel->mAABox = Loader.mAABox;
|
||||
return pModel;
|
||||
}
|
||||
|
||||
CModel* CModelLoader::LoadCorruptionWorldModel(CInputStream &MREA, CBlockMgrIn &BlockMgr, CMaterialSet &MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version)
|
||||
{
|
||||
CModelLoader Loader;
|
||||
Loader.mpBlockMgr = &BlockMgr;
|
||||
Loader.mVersion = Version;
|
||||
Loader.mFlags = eShortNormals;
|
||||
Loader.mMaterials.resize(1);
|
||||
Loader.mMaterials[0] = &MatSet;
|
||||
if (Version == eReturns) Loader.mFlags |= eHasTex1;
|
||||
|
||||
// Corruption/DKCR MREAs split the mesh header and surface offsets away from the actual geometry data so I need two section numbers to read it
|
||||
BlockMgr.ToBlock(HeaderSecNum);
|
||||
Loader.LoadWorldMeshHeader(MREA);
|
||||
Loader.LoadSurfaceOffsets(MREA);
|
||||
BlockMgr.ToBlock(GPUSecNum);
|
||||
Loader.LoadAttribArrays(MREA);
|
||||
|
||||
CModel *pModel = new CModel();
|
||||
pModel->mMaterialSets.resize(1);
|
||||
pModel->mMaterialSets[0] = &MatSet;
|
||||
pModel->mHasOwnMaterials = false;
|
||||
pModel->mSurfaces.reserve(Loader.mSurfaceCount);
|
||||
pModel->mHasOwnSurfaces = true;
|
||||
|
||||
for (u32 iSurf = 0; iSurf < Loader.mSurfaceCount; iSurf++)
|
||||
{
|
||||
SSurface *pSurf = Loader.LoadSurface(MREA);
|
||||
pModel->mSurfaces.push_back(pSurf);
|
||||
pModel->mVertexCount += pSurf->VertexCount;
|
||||
pModel->mTriangleCount += pSurf->TriangleCount;
|
||||
}
|
||||
|
||||
pModel->mAABox = Loader.mAABox;
|
||||
return pModel;
|
||||
}
|
||||
|
||||
CModel* CModelLoader::ImportAssimpNode(const aiNode *pNode, const aiScene *pScene, CMaterialSet& matSet)
|
||||
{
|
||||
CModelLoader loader;
|
||||
loader.mpModel = new CModel(&matSet, true);
|
||||
loader.mpModel->mSurfaces.reserve(pNode->mNumMeshes);
|
||||
|
||||
for (u32 iMesh = 0; iMesh < pNode->mNumMeshes; iMesh++)
|
||||
{
|
||||
u32 meshIndex = pNode->mMeshes[iMesh];
|
||||
const aiMesh *pMesh = pScene->mMeshes[meshIndex];
|
||||
SSurface *pSurf = loader.LoadAssimpMesh(pMesh, &matSet);
|
||||
|
||||
loader.mpModel->mSurfaces.push_back(pSurf);
|
||||
loader.mpModel->mAABox.ExpandBounds(pSurf->AABox);
|
||||
loader.mpModel->mVertexCount += pSurf->VertexCount;
|
||||
loader.mpModel->mTriangleCount += pSurf->TriangleCount;
|
||||
}
|
||||
|
||||
return loader.mpModel;
|
||||
}
|
||||
|
||||
EGame CModelLoader::GetFormatVersion(u32 Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case 0x2: return ePrime;
|
||||
case 0x3: return eEchoesDemo;
|
||||
case 0x4: return eEchoes;
|
||||
case 0x5: return eCorruption;
|
||||
case 0xA: return eReturns;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
66
src/Core/Resource/Factory/CModelLoader.h
Normal file
66
src/Core/Resource/Factory/CModelLoader.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef CMODELLOADER_H
|
||||
#define CMODELLOADER_H
|
||||
|
||||
#include "../model/CBasicModel.h"
|
||||
#include "../model/CModel.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include "CBlockMgrIn.h"
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <Common/EnumUtil.h>
|
||||
#include <assimp/scene.h>
|
||||
|
||||
class CModelLoader
|
||||
{
|
||||
public:
|
||||
enum EModelFlags
|
||||
{
|
||||
eNoFlags = 0x0,
|
||||
eShortPositions = 0x1,
|
||||
eShortNormals = 0x2,
|
||||
eHasTex1 = 0x4,
|
||||
eHasVisGroups = 0x8
|
||||
};
|
||||
|
||||
private:
|
||||
TResPtr<CModel> mpModel;
|
||||
std::vector<CMaterialSet*> mMaterials;
|
||||
CBlockMgrIn *mpBlockMgr;
|
||||
CAABox mAABox;
|
||||
EGame mVersion;
|
||||
|
||||
u32 mNumVertices;
|
||||
std::vector<CVector3f> mPositions;
|
||||
std::vector<CVector3f> mNormals;
|
||||
std::vector<CColor> mColors;
|
||||
std::vector<CVector2f> mTex0;
|
||||
std::vector<CVector2f> mTex1;
|
||||
bool mSurfaceUsingTex1;
|
||||
|
||||
u32 mSurfaceCount;
|
||||
std::vector<u32> mSurfaceOffsets;
|
||||
|
||||
EModelFlags mFlags;
|
||||
|
||||
CModelLoader();
|
||||
~CModelLoader();
|
||||
void LoadWorldMeshHeader(CInputStream& Model);
|
||||
void LoadAttribArrays(CInputStream& Model);
|
||||
void LoadAttribArraysDKCR(CInputStream& Model);
|
||||
void LoadSurfaceOffsets(CInputStream& Model);
|
||||
SSurface* LoadSurface(CInputStream& Model);
|
||||
void LoadSurfaceHeaderPrime(CInputStream& Model, SSurface *pSurf);
|
||||
void LoadSurfaceHeaderDKCR(CInputStream& Model, SSurface *pSurf);
|
||||
SSurface* LoadAssimpMesh(const aiMesh *pMesh, CMaterialSet *pSet);
|
||||
|
||||
public:
|
||||
static CModel* LoadCMDL(CInputStream& CMDL);
|
||||
static CModel* LoadWorldModel(CInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version);
|
||||
static CModel* LoadCorruptionWorldModel(CInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version);
|
||||
static CModel* ImportAssimpNode(const aiNode *pNode, const aiScene *pScene, CMaterialSet& matSet);
|
||||
static EGame GetFormatVersion(u32 Version);
|
||||
};
|
||||
|
||||
DEFINE_ENUM_FLAGS(CModelLoader::EModelFlags)
|
||||
|
||||
#endif // CMODELLOADER_H
|
||||
179
src/Core/Resource/Factory/CScanLoader.cpp
Normal file
179
src/Core/Resource/Factory/CScanLoader.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
#include "CScanLoader.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
|
||||
CScanLoader::CScanLoader()
|
||||
{
|
||||
}
|
||||
|
||||
CScan* CScanLoader::LoadScanMP1(CInputStream &SCAN)
|
||||
{
|
||||
// Basic support at the moment - don't read animation/scan image data
|
||||
SCAN.Seek(0x4, SEEK_CUR); // Skip FRME ID
|
||||
mpScan->mpStringTable = gResCache.GetResource(SCAN.ReadLong(), "STRG");
|
||||
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
|
||||
mpScan->mCategory = (CScan::ELogbookCategory) SCAN.ReadLong();
|
||||
mpScan->mIsImportant = (SCAN.ReadByte() == 1);
|
||||
mpScan->mVersion = ePrime;
|
||||
return mpScan;
|
||||
}
|
||||
|
||||
CScan* CScanLoader::LoadScanMP2(CInputStream& SCAN)
|
||||
{
|
||||
// The SCAN format in MP2 embeds a SNFO object using the same format as SCLY
|
||||
// However since the contents of the file are consistent there's no need to delegate to CScriptLoader
|
||||
SCAN.Seek(0x1, SEEK_CUR);
|
||||
u32 NumInstances = SCAN.ReadLong();
|
||||
|
||||
if (NumInstances != 1) {
|
||||
Log::FileError(SCAN.GetSourceString(), "SCAN has multiple instances");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 ScanInfoStart = SCAN.Tell();
|
||||
|
||||
CFourCC SNFO(SCAN);
|
||||
if (SNFO != "SNFO") {
|
||||
Log::FileError(SCAN.GetSourceString(), ScanInfoStart, "Unrecognized SCAN object type: " + SNFO.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SCAN.Seek(0x6, SEEK_CUR);
|
||||
u16 NumConnections = SCAN.ReadShort();
|
||||
if (NumConnections > 0) {
|
||||
Log::FileWarning(SCAN.GetSourceString(), ScanInfoStart, "SNFO object in SCAN has connections");
|
||||
SCAN.Seek(NumConnections * 0xC, SEEK_CUR);
|
||||
}
|
||||
|
||||
u32 BasePropID = SCAN.ReadLong();
|
||||
if (BasePropID != 0xFFFFFFFF) {
|
||||
Log::FileError(SCAN.GetSourceString(), SCAN.Tell() - 4, "Invalid base proprty ID: " + TString::HexString(BasePropID));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mpScan = new CScan();
|
||||
SCAN.Seek(0x2, SEEK_CUR);
|
||||
u16 NumProperties = SCAN.ReadShort();
|
||||
|
||||
switch (NumProperties)
|
||||
{
|
||||
case 0x14:
|
||||
LoadParamsMP2(SCAN);
|
||||
break;
|
||||
case 0x16:
|
||||
LoadParamsMP3(SCAN);
|
||||
break;
|
||||
default:
|
||||
Log::FileError(SCAN.GetSourceString(), SCAN.Tell() - 2, "Invalid SNFO property count: " + TString::HexString(NumProperties));
|
||||
delete mpScan;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mpScan;
|
||||
}
|
||||
|
||||
void CScanLoader::LoadParamsMP2(CInputStream& SCAN)
|
||||
{
|
||||
// Function begins after the SNFO property count
|
||||
for (u32 iProp = 0; iProp < 20; iProp++)
|
||||
{
|
||||
u32 PropertyID = SCAN.ReadLong();
|
||||
u16 PropertySize = SCAN.ReadShort();
|
||||
u32 Next = SCAN.Tell() + PropertySize;
|
||||
|
||||
switch (PropertyID)
|
||||
{
|
||||
case 0x2F5B6423:
|
||||
mpScan->mpStringTable = gResCache.GetResource(SCAN.ReadLong(), "STRG");
|
||||
break;
|
||||
|
||||
case 0xC308A322:
|
||||
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
|
||||
break;
|
||||
|
||||
case 0x7B714814:
|
||||
mpScan->mIsImportant = (SCAN.ReadByte() != 0);
|
||||
break;
|
||||
}
|
||||
|
||||
SCAN.Seek(Next, SEEK_SET);
|
||||
}
|
||||
|
||||
mpScan->mCategory = CScan::eNone;
|
||||
mpScan->mVersion = eEchoes;
|
||||
}
|
||||
|
||||
void CScanLoader::LoadParamsMP3(CInputStream& SCAN)
|
||||
{
|
||||
// Function begins after the SNFO property count
|
||||
// Function is near-identical to the MP2 one, but when I add support
|
||||
// for the other params, there will be more differences
|
||||
for (u32 iProp = 0; iProp < 20; iProp++)
|
||||
{
|
||||
u32 PropertyID = SCAN.ReadLong();
|
||||
u16 PropertySize = SCAN.ReadShort();
|
||||
u32 Next = SCAN.Tell() + PropertySize;
|
||||
|
||||
switch (PropertyID)
|
||||
{
|
||||
case 0x2F5B6423:
|
||||
mpScan->mpStringTable = gResCache.GetResource(SCAN.ReadLongLong(), "STRG");
|
||||
break;
|
||||
|
||||
case 0xC308A322:
|
||||
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
|
||||
break;
|
||||
|
||||
case 0x7B714814:
|
||||
mpScan->mIsImportant = (SCAN.ReadByte() != 0);
|
||||
break;
|
||||
}
|
||||
|
||||
SCAN.Seek(Next, SEEK_SET);
|
||||
}
|
||||
|
||||
mpScan->mCategory = CScan::eNone;
|
||||
mpScan->mVersion = eCorruption;
|
||||
}
|
||||
|
||||
// ************ STATIC/PUBLIC ************
|
||||
CScan* CScanLoader::LoadSCAN(CInputStream &SCAN)
|
||||
{
|
||||
if (!SCAN.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + SCAN.GetSourceString());
|
||||
|
||||
/* Switching to EGame enum here isn't really useful unfortunately
|
||||
* because the MP1 demo can be 1, 2, or 3, while MP1 is 5 and MP2+ is 2
|
||||
* MP1 is the only one that starts with 5 so that is a consistent check for now
|
||||
* Better version checks will be implemented when the other versions are
|
||||
* better-understood. */
|
||||
u32 fileVersion = SCAN.ReadLong();
|
||||
u32 magic = SCAN.ReadLong();
|
||||
|
||||
// Echoes+
|
||||
if (CFourCC(fileVersion) == "SCAN")
|
||||
{
|
||||
// The MP2 load function will check for MP3
|
||||
CScanLoader loader;
|
||||
loader.mVersion = eEchoes;
|
||||
return loader.LoadScanMP2(SCAN);
|
||||
}
|
||||
|
||||
if (magic != 0x0BADBEEF)
|
||||
{
|
||||
Log::FileError(SCAN.GetSourceString(), "Invalid SCAN magic: " + TString::HexString(magic));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (fileVersion != 5)
|
||||
{
|
||||
Log::FileError(SCAN.GetSourceString(), "Unsupported SCAN version: " + TString::HexString(fileVersion));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// MP1 SCAN - read the file!
|
||||
CScanLoader loader;
|
||||
loader.mVersion = ePrime;
|
||||
loader.mpScan = new CScan();
|
||||
return loader.LoadScanMP1(SCAN);
|
||||
}
|
||||
22
src/Core/Resource/Factory/CScanLoader.h
Normal file
22
src/Core/Resource/Factory/CScanLoader.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef CSCANLOADER_H
|
||||
#define CSCANLOADER_H
|
||||
|
||||
#include "../CScan.h"
|
||||
#include "../EFormatVersion.h"
|
||||
|
||||
class CScanLoader
|
||||
{
|
||||
TResPtr<CScan> mpScan;
|
||||
EGame mVersion;
|
||||
|
||||
CScanLoader();
|
||||
CScan* LoadScanMP1(CInputStream& SCAN);
|
||||
CScan* LoadScanMP2(CInputStream& SCAN);
|
||||
void LoadParamsMP2(CInputStream& SCAN);
|
||||
void LoadParamsMP3(CInputStream& SCAN);
|
||||
|
||||
public:
|
||||
static CScan* LoadSCAN(CInputStream& SCAN);
|
||||
};
|
||||
|
||||
#endif // CSCANLOADER_H
|
||||
520
src/Core/Resource/Factory/CScriptLoader.cpp
Normal file
520
src/Core/Resource/Factory/CScriptLoader.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
#include "CScriptLoader.h"
|
||||
#include "../script/CMasterTemplate.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
CScriptLoader::CScriptLoader()
|
||||
{
|
||||
mpObj = nullptr;
|
||||
}
|
||||
|
||||
CPropertyStruct* CScriptLoader::LoadStructMP1(CInputStream& SCLY, CStructTemplate *pTemp)
|
||||
{
|
||||
u32 structStart = SCLY.Tell();
|
||||
CPropertyStruct *propStruct = new CPropertyStruct();
|
||||
propStruct->mpTemplate = pTemp;
|
||||
|
||||
// Verify property count
|
||||
u32 propCount = pTemp->Count();
|
||||
|
||||
if (!pTemp->IsSingleProperty())
|
||||
{
|
||||
u32 filePropCount = SCLY.ReadLong();
|
||||
if (propCount != filePropCount)
|
||||
Log::FileWarning(SCLY.GetSourceString(), structStart, "Struct \"" + pTemp->Name() + "\" template prop count doesn't match file");
|
||||
}
|
||||
|
||||
// Parse properties
|
||||
propStruct->Reserve(propCount);
|
||||
|
||||
for (u32 iProp = 0; iProp < propCount; iProp++)
|
||||
{
|
||||
CPropertyBase *pProp = nullptr;
|
||||
CPropertyTemplate *pPropTmp = pTemp->PropertyByIndex(iProp);
|
||||
EPropertyType type = pPropTmp->Type();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
||||
case eBoolProperty: {
|
||||
bool v = (SCLY.ReadByte() == 1);
|
||||
pProp = new CBoolProperty(v);
|
||||
break;
|
||||
}
|
||||
case eByteProperty: {
|
||||
char v = SCLY.ReadByte();
|
||||
pProp = new CByteProperty(v);
|
||||
break;
|
||||
}
|
||||
case eShortProperty: {
|
||||
short v = SCLY.ReadShort();
|
||||
pProp = new CShortProperty(v);
|
||||
break;
|
||||
}
|
||||
case eLongProperty: {
|
||||
long v = SCLY.ReadLong();
|
||||
pProp = new CLongProperty(v);
|
||||
break;
|
||||
}
|
||||
case eBitfieldProperty: {
|
||||
long v = SCLY.ReadLong();
|
||||
pProp = new CBitfieldProperty(v);
|
||||
|
||||
// Validate
|
||||
u32 mask = 0;
|
||||
CBitfieldTemplate *pBitfieldTemp = static_cast<CBitfieldTemplate*>(pPropTmp);
|
||||
for (u32 iMask = 0; iMask < pBitfieldTemp->NumFlags(); iMask++)
|
||||
mask |= pBitfieldTemp->FlagMask(iMask);
|
||||
|
||||
u32 check = v & ~mask;
|
||||
if (check != 0) Log::FileWarning(SCLY.GetSourceString(), SCLY.Tell() - 4, "Bitfield property \"" + pBitfieldTemp->Name() + "\" in struct \"" + pTemp->Name() + "\" has flags set that aren't in the template: " + TString::HexString(check, true, true, 8));
|
||||
|
||||
break;
|
||||
}
|
||||
case eEnumProperty: {
|
||||
CEnumTemplate *pEnumTemp = static_cast<CEnumTemplate*>(pPropTmp);
|
||||
u32 ID = SCLY.ReadLong();
|
||||
u32 index = pEnumTemp->EnumeratorIndex(ID);
|
||||
if (index == -1) Log::FileError(SCLY.GetSourceString(), SCLY.Tell() - 4, "Enum property \"" + pEnumTemp->Name() + "\" in struct \"" + pTemp->Name() + "\" has invalid enumerator value: " + TString::HexString(ID, true, true, 8));
|
||||
pProp = new CEnumProperty(index);
|
||||
break;
|
||||
}
|
||||
case eFloatProperty: {
|
||||
float v = SCLY.ReadFloat();
|
||||
pProp = new CFloatProperty(v);
|
||||
break;
|
||||
}
|
||||
case eStringProperty: {
|
||||
TString v = SCLY.ReadString();
|
||||
pProp = new CStringProperty(v);
|
||||
break;
|
||||
}
|
||||
case eVector3Property: {
|
||||
CVector3f v(SCLY);
|
||||
pProp = new CVector3Property(v);
|
||||
break;
|
||||
}
|
||||
case eColorProperty: {
|
||||
CVector4f color(SCLY);
|
||||
CColor v(color.x, color.y, color.z, color.w);
|
||||
pProp = new CColorProperty(v);
|
||||
break;
|
||||
}
|
||||
case eFileProperty: {
|
||||
u32 ResID = SCLY.ReadLong();
|
||||
const TStringList& Extensions = static_cast<CFileTemplate*>(pPropTmp)->Extensions();
|
||||
|
||||
CResource *pRes = nullptr;
|
||||
|
||||
for (auto it = Extensions.begin(); it != Extensions.end(); it++)
|
||||
{
|
||||
const TString& ext = *it;
|
||||
if ((ext != "MREA") && (ext != "MLVL")) // Let's avoid recursion please
|
||||
pRes = gResCache.GetResource(ResID, ext);
|
||||
|
||||
if (pRes) break;
|
||||
}
|
||||
|
||||
pProp = new CFileProperty(pRes);
|
||||
break;
|
||||
}
|
||||
case eStructProperty: {
|
||||
CStructTemplate *StructTmp = pTemp->StructByIndex(iProp);
|
||||
pProp = LoadStructMP1(SCLY, StructTmp);
|
||||
break;
|
||||
}
|
||||
case eAnimParamsProperty: {
|
||||
pProp = new CAnimParamsProperty(CAnimationParameters(SCLY, mVersion));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pProp = new CUnknownProperty();
|
||||
break;
|
||||
}
|
||||
|
||||
if (pProp)
|
||||
{
|
||||
pProp->mpTemplate = pPropTmp;
|
||||
propStruct->mProperties.push_back(pProp);
|
||||
}
|
||||
}
|
||||
|
||||
return propStruct;
|
||||
}
|
||||
|
||||
CScriptObject* CScriptLoader::LoadObjectMP1(CInputStream& SCLY)
|
||||
{
|
||||
u32 objStart = SCLY.Tell();
|
||||
u8 type = SCLY.ReadByte();
|
||||
u32 size = SCLY.ReadLong();
|
||||
u32 end = SCLY.Tell() + size;
|
||||
|
||||
CScriptTemplate *pTemp = mpMaster->TemplateByID((u32) type);
|
||||
if (!pTemp)
|
||||
{
|
||||
// No valid template for this object; can't load
|
||||
Log::FileError(SCLY.GetSourceString(), objStart, "Invalid object ID encountered: " + TString::HexString(type));
|
||||
SCLY.Seek(end, SEEK_SET);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mpObj = new CScriptObject(mpArea, mpLayer, pTemp);
|
||||
mpObj->mInstanceID = SCLY.ReadLong();
|
||||
|
||||
// Load connections
|
||||
u32 numLinks = SCLY.ReadLong();
|
||||
mpObj->mOutConnections.reserve(numLinks);
|
||||
|
||||
for (u32 iLink = 0; iLink < numLinks; iLink++)
|
||||
{
|
||||
SLink link;
|
||||
link.State = SCLY.ReadLong();
|
||||
link.Message = SCLY.ReadLong();
|
||||
link.ObjectID = SCLY.ReadLong();
|
||||
mpObj->mOutConnections.push_back(link);
|
||||
}
|
||||
|
||||
// Load object...
|
||||
u32 count = SCLY.PeekLong();
|
||||
CStructTemplate *pBase = pTemp->BaseStructByCount(count);
|
||||
|
||||
if (!pBase) {
|
||||
Log::Error(pTemp->TemplateName() + " template doesn't match file property count (" + TString::FromInt32(count) + ")");
|
||||
pBase = pTemp->BaseStructByIndex(0);
|
||||
}
|
||||
mpObj->mpProperties = LoadStructMP1(SCLY, pBase);
|
||||
|
||||
// Cleanup and return
|
||||
SCLY.Seek(end, SEEK_SET);
|
||||
|
||||
mpObj->EvaluateProperties();
|
||||
return mpObj;
|
||||
}
|
||||
|
||||
CScriptLayer* CScriptLoader::LoadLayerMP1(CInputStream &SCLY)
|
||||
{
|
||||
u32 LayerStart = SCLY.Tell();
|
||||
|
||||
SCLY.Seek(0x1, SEEK_CUR); // One unknown byte at the start of each layer
|
||||
u32 NumObjects = SCLY.ReadLong();
|
||||
|
||||
mpLayer = new CScriptLayer();
|
||||
mpLayer->Reserve(NumObjects);
|
||||
|
||||
for (u32 iObj = 0; iObj < NumObjects; iObj++)
|
||||
{
|
||||
CScriptObject *pObj = LoadObjectMP1(SCLY);
|
||||
if (pObj)
|
||||
mpLayer->AddObject(pObj);
|
||||
}
|
||||
|
||||
// Layer sizes are always a multiple of 32 - skip end padding before returning
|
||||
u32 remaining = 32 - ((SCLY.Tell() - LayerStart) & 0x1F);
|
||||
SCLY.Seek(remaining, SEEK_CUR);
|
||||
return mpLayer;
|
||||
}
|
||||
|
||||
void CScriptLoader::LoadStructMP2(CInputStream& SCLY, CPropertyStruct *pStruct, CStructTemplate *pTemp)
|
||||
{
|
||||
// Verify property count
|
||||
u32 propCount = pTemp->Count();
|
||||
|
||||
if (!pTemp->IsSingleProperty())
|
||||
{
|
||||
u16 numProperties = SCLY.ReadShort();
|
||||
if ((numProperties != propCount) && (mVersion < eReturns))
|
||||
Log::FileWarning(SCLY.GetSourceString(), SCLY.Tell() - 2, "Struct \"" + pTemp->Name() + "\" template property count doesn't match file");
|
||||
propCount = numProperties;
|
||||
}
|
||||
|
||||
// Parse properties
|
||||
pStruct->Reserve(propCount);
|
||||
|
||||
for (u32 iProp = 0; iProp < propCount; iProp++)
|
||||
{
|
||||
CPropertyBase *pProp;
|
||||
CPropertyTemplate *pPropTemp;
|
||||
u32 propertyStart = SCLY.Tell();
|
||||
u32 propertyID = -1;
|
||||
u16 PropertyLength = 0;
|
||||
u32 NextProperty = 0;
|
||||
|
||||
if (pTemp->IsSingleProperty())
|
||||
{
|
||||
pProp = pStruct->PropertyByIndex(iProp);
|
||||
pPropTemp = pTemp->PropertyByIndex(iProp);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyID = SCLY.ReadLong();
|
||||
PropertyLength = SCLY.ReadShort();
|
||||
NextProperty = SCLY.Tell() + PropertyLength;
|
||||
|
||||
pProp = pStruct->PropertyByID(propertyID);
|
||||
pPropTemp = pTemp->PropertyByID(propertyID);
|
||||
}
|
||||
|
||||
if (!pPropTemp)
|
||||
Log::FileError(SCLY.GetSourceString(), propertyStart, "Can't find template for property " + TString::HexString(propertyID) + " - skipping");
|
||||
|
||||
else
|
||||
{
|
||||
switch (pPropTemp->Type())
|
||||
{
|
||||
|
||||
case eBoolProperty: {
|
||||
CBoolProperty *pBoolCast = static_cast<CBoolProperty*>(pProp);
|
||||
pBoolCast->Set( (SCLY.ReadByte() != 0) );
|
||||
break;
|
||||
}
|
||||
|
||||
case eByteProperty: {
|
||||
CByteProperty *pByteCast = static_cast<CByteProperty*>(pProp);
|
||||
pByteCast->Set(SCLY.ReadByte());
|
||||
break;
|
||||
}
|
||||
|
||||
case eShortProperty: {
|
||||
CShortProperty *pShortCast = static_cast<CShortProperty*>(pProp);
|
||||
pShortCast->Set(SCLY.ReadShort());
|
||||
break;
|
||||
}
|
||||
|
||||
case eLongProperty: {
|
||||
CLongProperty *pLongCast = static_cast<CLongProperty*>(pProp);
|
||||
pLongCast->Set(SCLY.ReadLong());
|
||||
break;
|
||||
}
|
||||
|
||||
case eBitfieldProperty: {
|
||||
CBitfieldProperty *pBitfieldCast = static_cast<CBitfieldProperty*>(pProp);
|
||||
pBitfieldCast->Set(SCLY.ReadLong());
|
||||
|
||||
// Validate
|
||||
u32 mask = 0;
|
||||
CBitfieldTemplate *pBitfieldTemp = static_cast<CBitfieldTemplate*>(pPropTemp);
|
||||
for (u32 iMask = 0; iMask < pBitfieldTemp->NumFlags(); iMask++)
|
||||
mask |= pBitfieldTemp->FlagMask(iMask);
|
||||
|
||||
u32 check = pBitfieldCast->Get() & ~mask;
|
||||
if (check != 0) Log::FileWarning(SCLY.GetSourceString(), SCLY.Tell() - 4, "Bitfield property \"" + pBitfieldTemp->Name() + "\" in struct \"" + pTemp->Name() + "\" has flags set that aren't in the template: " + TString::HexString(check, true, true, 8));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case eEnumProperty: {
|
||||
CEnumProperty *pEnumCast = static_cast<CEnumProperty*>(pProp);
|
||||
CEnumTemplate *pEnumTemp = static_cast<CEnumTemplate*>(pPropTemp);
|
||||
u32 ID = SCLY.ReadLong();
|
||||
u32 index = pEnumTemp->EnumeratorIndex(ID);
|
||||
if (index == -1) Log::FileError(SCLY.GetSourceString(), SCLY.Tell() - 4, "Enum property \"" + pEnumTemp->Name() + "\" in struct \"" + pTemp->Name() + "\" has invalid enumerator value: " + TString::HexString(ID, true, true, 8));
|
||||
pEnumCast->Set(index);
|
||||
break;
|
||||
}
|
||||
|
||||
case eFloatProperty: {
|
||||
CFloatProperty *pFloatCast = static_cast<CFloatProperty*>(pProp);
|
||||
pFloatCast->Set(SCLY.ReadFloat());
|
||||
break;
|
||||
}
|
||||
|
||||
case eStringProperty: {
|
||||
CStringProperty *pStringCast = static_cast<CStringProperty*>(pProp);
|
||||
pStringCast->Set(SCLY.ReadString());
|
||||
break;
|
||||
}
|
||||
|
||||
case eVector3Property: {
|
||||
CVector3Property *pVector3Cast = static_cast<CVector3Property*>(pProp);
|
||||
pVector3Cast->Set(CVector3f(SCLY));
|
||||
break;
|
||||
}
|
||||
|
||||
case eColorProperty: {
|
||||
CColorProperty *pColorCast = static_cast<CColorProperty*>(pProp);
|
||||
CVector4f Color(SCLY);
|
||||
pColorCast->Set(CColor(Color.x, Color.y, Color.z, Color.w));
|
||||
break;
|
||||
}
|
||||
|
||||
case eFileProperty: {
|
||||
CFileProperty *pFileCast = static_cast<CFileProperty*>(pProp);
|
||||
|
||||
CUniqueID ResID = (mVersion < eCorruptionProto ? SCLY.ReadLong() : SCLY.ReadLongLong());
|
||||
const TStringList& Extensions = static_cast<CFileTemplate*>(pPropTemp)->Extensions();
|
||||
|
||||
CResource *pRes = nullptr;
|
||||
|
||||
// Check for each extension individually until we find a match
|
||||
// This could be done better with a function to fetch the extension given the resource ID
|
||||
// and a "does resource exist" function, but this will do for now
|
||||
bool hasIgnoredExt = false;
|
||||
|
||||
if (ResID.IsValid())
|
||||
{
|
||||
for (auto it = Extensions.begin(); it != Extensions.end(); it++)
|
||||
{
|
||||
const TString& ext = *it;
|
||||
|
||||
if ((ext != "MREA") && (ext != "MLVL")) {
|
||||
pRes = gResCache.GetResource(ResID, ext);
|
||||
if (pRes) break;
|
||||
}
|
||||
|
||||
else
|
||||
hasIgnoredExt = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Property may have an incorrect extension listed - print error
|
||||
if ((!pRes) && (CUniqueID(ResID).IsValid()) && (!hasIgnoredExt))
|
||||
{
|
||||
TString ExtList;
|
||||
for (auto it = Extensions.begin(); it != Extensions.end(); it++)
|
||||
{
|
||||
if (it != Extensions.begin()) ExtList += "/";
|
||||
ExtList += *it;
|
||||
}
|
||||
Log::FileWarning(SCLY.GetSourceString(), "Incorrect resource type? " + ExtList + " " + TString::HexString(propertyID));
|
||||
}
|
||||
|
||||
pFileCast->Set(pRes);
|
||||
break;
|
||||
}
|
||||
|
||||
case eUnknownProperty: {
|
||||
CUnknownProperty *pUnknownCast = static_cast<CUnknownProperty*>(pProp);
|
||||
std::vector<u8> buf(PropertyLength);
|
||||
SCLY.ReadBytes(buf.data(), buf.size());
|
||||
pUnknownCast->Set(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case eStructProperty: {
|
||||
CPropertyStruct *pStructCast = static_cast<CPropertyStruct*>(pProp);
|
||||
LoadStructMP2(SCLY, pStructCast, static_cast<CStructTemplate*>(pPropTemp));
|
||||
break;
|
||||
}
|
||||
|
||||
case eArrayProperty: {
|
||||
CArrayProperty *pArrayCast = static_cast<CArrayProperty*>(pProp);
|
||||
std::vector<u8> buf(PropertyLength);
|
||||
SCLY.ReadBytes(buf.data(), buf.size());
|
||||
pArrayCast->Set(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAnimParamsProperty: {
|
||||
CAnimParamsProperty *pAnimCast = static_cast<CAnimParamsProperty*>(pProp);
|
||||
pAnimCast->Set(CAnimationParameters(SCLY, mVersion));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NextProperty > 0)
|
||||
SCLY.Seek(NextProperty, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
CScriptObject* CScriptLoader::LoadObjectMP2(CInputStream& SCLY)
|
||||
{
|
||||
u32 ObjStart = SCLY.Tell();
|
||||
u32 ObjectID = SCLY.ReadLong();
|
||||
u16 ObjectSize = SCLY.ReadShort();
|
||||
u32 ObjEnd = SCLY.Tell() + ObjectSize;
|
||||
|
||||
CScriptTemplate *pTemplate = mpMaster->TemplateByID(ObjectID);
|
||||
|
||||
if (!pTemplate)
|
||||
{
|
||||
Log::FileError(SCLY.GetSourceString(), ObjStart, "Invalid object ID encountered: " + CFourCC(ObjectID).ToString());
|
||||
SCLY.Seek(ObjEnd, SEEK_SET);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mpObj = new CScriptObject(mpArea, mpLayer, pTemplate);
|
||||
mpObj->mpTemplate = pTemplate;
|
||||
mpObj->mInstanceID = SCLY.ReadLong();
|
||||
|
||||
// Load connections
|
||||
u32 NumConnections = SCLY.ReadShort();
|
||||
mpObj->mOutConnections.reserve(NumConnections);
|
||||
|
||||
for (u32 iCon = 0; iCon < NumConnections; iCon++)
|
||||
{
|
||||
SLink con;
|
||||
con.State = SCLY.ReadLong();
|
||||
con.Message = SCLY.ReadLong();
|
||||
con.ObjectID = SCLY.ReadLong();
|
||||
mpObj->mOutConnections.push_back(con);
|
||||
}
|
||||
|
||||
// Load object
|
||||
SCLY.Seek(0x6, SEEK_CUR); // Skip base struct ID + size
|
||||
u16 numProps = SCLY.PeekShort();
|
||||
mpObj->CopyFromTemplate(pTemplate, (u32) numProps);
|
||||
|
||||
CStructTemplate *pBase = pTemplate->BaseStructByCount(numProps);
|
||||
LoadStructMP2(SCLY, mpObj->mpProperties, pBase);
|
||||
|
||||
// Cleanup and return
|
||||
SCLY.Seek(ObjEnd, SEEK_SET);
|
||||
mpObj->EvaluateProperties();
|
||||
return mpObj;
|
||||
}
|
||||
|
||||
CScriptLayer* CScriptLoader::LoadLayerMP2(CInputStream& SCLY)
|
||||
{
|
||||
CFourCC SCLY_Magic(SCLY);
|
||||
|
||||
if (SCLY_Magic == "SCLY") SCLY.Seek(0x6, SEEK_CUR);
|
||||
else if (SCLY_Magic == "SCGN") SCLY.Seek(0x2, SEEK_CUR);
|
||||
else
|
||||
{
|
||||
Log::FileError(SCLY.GetSourceString(), SCLY.Tell() - 4, "Invalid script layer magic: " + TString::HexString((u32) SCLY_Magic.ToLong()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 NumObjects = SCLY.ReadLong();
|
||||
|
||||
mpLayer = new CScriptLayer();
|
||||
mpLayer->Reserve(NumObjects);
|
||||
|
||||
for (u32 iObj = 0; iObj < NumObjects; iObj++)
|
||||
{
|
||||
CScriptObject *pObj = LoadObjectMP2(SCLY);
|
||||
if (pObj)
|
||||
mpLayer->AddObject(pObj);
|
||||
}
|
||||
|
||||
if (SCLY_Magic == "SCGN")
|
||||
{
|
||||
mpLayer->SetName("Generated");
|
||||
mpLayer->SetActive(true);
|
||||
}
|
||||
return mpLayer;
|
||||
}
|
||||
|
||||
CScriptLayer* CScriptLoader::LoadLayer(CInputStream &SCLY, CGameArea *pArea, EGame version)
|
||||
{
|
||||
if (!SCLY.IsValid()) return nullptr;
|
||||
|
||||
CScriptLoader Loader;
|
||||
Loader.mVersion = version;
|
||||
Loader.mpMaster = CMasterTemplate::GetMasterForGame(version);
|
||||
Loader.mpArea = pArea;
|
||||
|
||||
if (!Loader.mpMaster)
|
||||
{
|
||||
Log::Write("This game doesn't have a master template; couldn't load script layer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (version <= ePrime)
|
||||
return Loader.LoadLayerMP1(SCLY);
|
||||
else
|
||||
return Loader.LoadLayerMP2(SCLY);
|
||||
}
|
||||
34
src/Core/Resource/Factory/CScriptLoader.h
Normal file
34
src/Core/Resource/Factory/CScriptLoader.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef CSCRIPTLOADER_H
|
||||
#define CSCRIPTLOADER_H
|
||||
|
||||
#include "../script/CScriptObject.h"
|
||||
#include "../script/CScriptLayer.h"
|
||||
#include "../script/CMasterTemplate.h"
|
||||
#include "../CGameArea.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
class CScriptLoader
|
||||
{
|
||||
EGame mVersion;
|
||||
CScriptObject *mpObj;
|
||||
CScriptLayer *mpLayer;
|
||||
CGameArea *mpArea;
|
||||
CMasterTemplate *mpMaster;
|
||||
|
||||
CScriptLoader();
|
||||
|
||||
CPropertyStruct* LoadStructMP1(CInputStream& SCLY, CStructTemplate *tmp);
|
||||
CScriptObject* LoadObjectMP1(CInputStream& SCLY);
|
||||
CScriptLayer* LoadLayerMP1(CInputStream& SCLY);
|
||||
|
||||
void LoadStructMP2(CInputStream& SCLY, CPropertyStruct *pStruct, CStructTemplate *pTemp);
|
||||
CScriptObject* LoadObjectMP2(CInputStream& SCLY);
|
||||
CScriptLayer* LoadLayerMP2(CInputStream& SCLY);
|
||||
|
||||
void SetupAttribs();
|
||||
|
||||
public:
|
||||
static CScriptLayer* LoadLayer(CInputStream& SCLY, CGameArea *pArea, EGame version);
|
||||
};
|
||||
|
||||
#endif // CSCRIPTLOADER_H
|
||||
221
src/Core/Resource/Factory/CStringLoader.cpp
Normal file
221
src/Core/Resource/Factory/CStringLoader.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "CStringLoader.h"
|
||||
#include <Core/Log.h>
|
||||
|
||||
CStringLoader::CStringLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void CStringLoader::LoadPrimeDemoSTRG(CInputStream& STRG)
|
||||
{
|
||||
// This function starts at 0x4 in the file - right after the size
|
||||
// This STRG version only supports one language per file
|
||||
mpStringTable->mLangTables.resize(1);
|
||||
CStringTable::SLangTable* Lang = &mpStringTable->mLangTables[1];
|
||||
Lang->Language = "ENGL";
|
||||
u32 TableStart = STRG.Tell();
|
||||
|
||||
// Header
|
||||
u32 NumStrings = STRG.ReadLong();
|
||||
Lang->Strings.resize(NumStrings);
|
||||
mpStringTable->mNumStrings = NumStrings;
|
||||
|
||||
// String offsets (yeah, that wasn't much of a header)
|
||||
std::vector<u32> StringOffsets(NumStrings);
|
||||
for (u32 iOff = 0; iOff < StringOffsets.size(); iOff++)
|
||||
StringOffsets[iOff] = STRG.ReadLong();
|
||||
|
||||
// Strings
|
||||
for (u32 iStr = 0; iStr < NumStrings; iStr++)
|
||||
{
|
||||
STRG.Seek(TableStart + StringOffsets[iStr], SEEK_SET);
|
||||
Lang->Strings[iStr] = STRG.ReadWString();
|
||||
}
|
||||
}
|
||||
|
||||
void CStringLoader::LoadPrimeSTRG(CInputStream& STRG)
|
||||
{
|
||||
// This function starts at 0x8 in the file, after magic/version
|
||||
// Header
|
||||
u32 NumLanguages = STRG.ReadLong();
|
||||
u32 NumStrings = STRG.ReadLong();
|
||||
mpStringTable->mNumStrings = NumStrings;
|
||||
|
||||
// Language definitions
|
||||
mpStringTable->mLangTables.resize(NumLanguages);
|
||||
std::vector<u32> LangOffsets(NumLanguages);
|
||||
|
||||
for (u32 iLang = 0; iLang < NumLanguages; iLang++)
|
||||
{
|
||||
mpStringTable->mLangTables[iLang].Language = CFourCC(STRG);
|
||||
LangOffsets[iLang] = STRG.ReadLong();
|
||||
if (mVersion == eEchoes) STRG.Seek(0x4, SEEK_CUR); // Skipping strings size
|
||||
}
|
||||
|
||||
// String names
|
||||
if (mVersion == eEchoes)
|
||||
LoadNameTable(STRG);
|
||||
|
||||
// Strings
|
||||
u32 StringsStart = STRG.Tell();
|
||||
for (u32 iLang = 0; iLang < NumLanguages; iLang++)
|
||||
{
|
||||
STRG.Seek(StringsStart + LangOffsets[iLang], SEEK_SET);
|
||||
if (mVersion == ePrime) STRG.Seek(0x4, SEEK_CUR); // Skipping strings size
|
||||
|
||||
u32 LangStart = STRG.Tell();
|
||||
CStringTable::SLangTable* pLang = &mpStringTable->mLangTables[iLang];
|
||||
pLang->Strings.resize(NumStrings);
|
||||
|
||||
// Offsets
|
||||
std::vector<u32> StringOffsets(NumStrings);
|
||||
for (u32 iOff = 0; iOff < NumStrings; iOff++)
|
||||
StringOffsets[iOff] = STRG.ReadLong();
|
||||
|
||||
// The actual strings
|
||||
for (u32 iStr = 0; iStr < NumStrings; iStr++)
|
||||
{
|
||||
STRG.Seek(LangStart + StringOffsets[iStr], SEEK_SET);
|
||||
pLang->Strings[iStr] = STRG.ReadWString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CStringLoader::LoadCorruptionSTRG(CInputStream& STRG)
|
||||
{
|
||||
// This function starts at 0x8 in the file, after magic/version
|
||||
// Header
|
||||
u32 NumLanguages = STRG.ReadLong();
|
||||
u32 NumStrings = STRG.ReadLong();
|
||||
mpStringTable->mNumStrings = NumStrings;
|
||||
|
||||
// String names
|
||||
LoadNameTable(STRG);
|
||||
|
||||
// Language definitions
|
||||
mpStringTable->mLangTables.resize(NumLanguages);
|
||||
std::vector<std::vector<u32>> LangOffsets(NumLanguages);
|
||||
|
||||
for (u32 iLang = 0; iLang < NumLanguages; iLang++)
|
||||
mpStringTable->mLangTables[iLang].Language = CFourCC(STRG);
|
||||
|
||||
for (u32 iLang = 0; iLang < NumLanguages; iLang++)
|
||||
{
|
||||
LangOffsets[iLang].resize(NumStrings);
|
||||
|
||||
STRG.Seek(0x4, SEEK_CUR); // Skipping total string size
|
||||
|
||||
for (u32 iStr = 0; iStr < NumStrings; iStr++)
|
||||
LangOffsets[iLang][iStr] = STRG.ReadLong();
|
||||
}
|
||||
|
||||
// Strings
|
||||
u32 StringsStart = STRG.Tell();
|
||||
|
||||
for (u32 iLang = 0; iLang < NumLanguages; iLang++)
|
||||
{
|
||||
CStringTable::SLangTable *pLang = &mpStringTable->mLangTables[iLang];
|
||||
pLang->Strings.resize(NumStrings);
|
||||
|
||||
for (u32 iStr = 0; iStr < NumStrings; iStr++)
|
||||
{
|
||||
STRG.Seek(StringsStart + LangOffsets[iLang][iStr], SEEK_SET);
|
||||
STRG.Seek(0x4, SEEK_CUR); // Skipping string size
|
||||
|
||||
pLang->Strings[iStr] = STRG.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CStringLoader::LoadNameTable(CInputStream& STRG)
|
||||
{
|
||||
// Name table header
|
||||
u32 NameCount = STRG.ReadLong();
|
||||
u32 NameTableSize = STRG.ReadLong();
|
||||
u32 NameTableStart = STRG.Tell();
|
||||
u32 NameTableEnd = NameTableStart + NameTableSize;
|
||||
|
||||
// Name definitions
|
||||
struct SNameDef {
|
||||
u32 NameOffset, StringIndex;
|
||||
};
|
||||
std::vector<SNameDef> NameDefs(NameCount);
|
||||
|
||||
for (u32 iName = 0; iName < NameCount; iName++)
|
||||
{
|
||||
NameDefs[iName].NameOffset = STRG.ReadLong() + NameTableStart;
|
||||
NameDefs[iName].StringIndex = STRG.ReadLong();
|
||||
}
|
||||
|
||||
// Name strings
|
||||
mpStringTable->mStringNames.resize(mpStringTable->mNumStrings);
|
||||
for (u32 iName = 0; iName < NameCount; iName++)
|
||||
{
|
||||
SNameDef *pDef = &NameDefs[iName];
|
||||
STRG.Seek(pDef->NameOffset, SEEK_SET);
|
||||
mpStringTable->mStringNames[pDef->StringIndex] = STRG.ReadString();
|
||||
}
|
||||
STRG.Seek(NameTableEnd, SEEK_SET);
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CStringTable* CStringLoader::LoadSTRG(CInputStream& STRG)
|
||||
{
|
||||
// Verify that this is a valid STRG
|
||||
if (!STRG.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + STRG.GetSourceString());
|
||||
|
||||
u32 Magic = STRG.ReadLong();
|
||||
EGame Version = eUnknownVersion;
|
||||
|
||||
if (Magic != 0x87654321)
|
||||
{
|
||||
// Check for MP1 Demo STRG format - no magic/version; the first value is actually the filesize
|
||||
// so the best I can do is verify the first value actually points to the end of the file
|
||||
if (Magic <= (u32) STRG.Size())
|
||||
{
|
||||
STRG.Seek(Magic, SEEK_SET);
|
||||
if ((STRG.EoF()) || (STRG.ReadShort() == 0xFFFF))
|
||||
Version = ePrimeDemo;
|
||||
}
|
||||
|
||||
if (Version != ePrimeDemo)
|
||||
{
|
||||
Log::FileError(STRG.GetSourceString(), "Invalid STRG magic: " + TString::HexString(Magic));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
u32 FileVersion = STRG.ReadLong();
|
||||
Version = GetFormatVersion(FileVersion);
|
||||
|
||||
if (FileVersion == eUnknownVersion)
|
||||
{
|
||||
Log::FileError(STRG.GetSourceString(), "Unsupported STRG version: " + TString::HexString(FileVersion));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Valid; now we create the loader and call the function that reads the rest of the file
|
||||
CStringLoader Loader;
|
||||
Loader.mpStringTable = new CStringTable();
|
||||
Loader.mVersion = Version;
|
||||
|
||||
if (Version == ePrimeDemo) Loader.LoadPrimeDemoSTRG(STRG);
|
||||
else if (Version < eCorruption) Loader.LoadPrimeSTRG(STRG);
|
||||
else Loader.LoadCorruptionSTRG(STRG);
|
||||
|
||||
return Loader.mpStringTable;
|
||||
}
|
||||
|
||||
EGame CStringLoader::GetFormatVersion(u32 Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case 0x0: return ePrime;
|
||||
case 0x1: return eEchoes;
|
||||
case 0x3: return eCorruption;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
25
src/Core/Resource/Factory/CStringLoader.h
Normal file
25
src/Core/Resource/Factory/CStringLoader.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef CSTRINGLOADER_H
|
||||
#define CSTRINGLOADER_H
|
||||
|
||||
#include "../CStringTable.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/TResPtr.h>
|
||||
|
||||
class CStringLoader
|
||||
{
|
||||
TResPtr<CStringTable> mpStringTable;
|
||||
EGame mVersion;
|
||||
|
||||
CStringLoader();
|
||||
void LoadPrimeDemoSTRG(CInputStream& STRG);
|
||||
void LoadPrimeSTRG(CInputStream& STRG);
|
||||
void LoadCorruptionSTRG(CInputStream& STRG);
|
||||
void LoadNameTable(CInputStream& STRG);
|
||||
|
||||
public:
|
||||
static CStringTable* LoadSTRG(CInputStream& STRG);
|
||||
static EGame GetFormatVersion(u32 Version);
|
||||
};
|
||||
|
||||
#endif // CSTRINGLOADER_H
|
||||
711
src/Core/Resource/Factory/CTemplateLoader.cpp
Normal file
711
src/Core/Resource/Factory/CTemplateLoader.cpp
Normal file
@@ -0,0 +1,711 @@
|
||||
#include "CTemplateLoader.h"
|
||||
#include "CWorldLoader.h"
|
||||
#include <Core/Log.h>
|
||||
|
||||
void CTemplateLoader::LoadBitFlags(tinyxml2::XMLElement *pElem, CBitfieldTemplate *pTemp, const TString& templateName)
|
||||
{
|
||||
tinyxml2::XMLElement *pChild = pElem->FirstChildElement("bitflag");
|
||||
|
||||
while (pChild)
|
||||
{
|
||||
const char *kpMask = pChild->Attribute("mask");
|
||||
const char *kpName = pChild->Attribute("name");
|
||||
|
||||
if (kpMask && kpName)
|
||||
pTemp->mBitFlags.push_back(CBitfieldTemplate::SBitFlag(kpName, TString(kpMask).ToInt32()));
|
||||
|
||||
else
|
||||
{
|
||||
TString LogErrorBase = "Couldn't parse bit flag in " + templateName + "; ";
|
||||
|
||||
if (!kpMask && kpName) Log::Error(LogErrorBase + "no mask (" + kpName + ")");
|
||||
else if (kpMask && !kpName) Log::Error(LogErrorBase + "no name (mask " + kpMask + ")");
|
||||
else Log::Error(LogErrorBase + "no valid ID or name");
|
||||
}
|
||||
|
||||
pChild = pChild->NextSiblingElement("bitflag");
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateLoader::LoadEnumerators(tinyxml2::XMLElement *pElem, CEnumTemplate *pTemp, const TString& templateName)
|
||||
{
|
||||
tinyxml2::XMLElement *pChild = pElem->FirstChildElement("enumerator");
|
||||
|
||||
while (pChild)
|
||||
{
|
||||
const char *kpID = pChild->Attribute("value");
|
||||
const char *kpName = pChild->Attribute("name");
|
||||
|
||||
if (kpID && kpName)
|
||||
pTemp->mEnumerators.push_back(CEnumTemplate::SEnumerator(kpName, TString(kpID).ToInt32()));
|
||||
|
||||
else
|
||||
{
|
||||
TString LogErrorBase = "Couldn't parse enumerator in " + templateName + "; ";
|
||||
|
||||
if (!kpID && kpName) Log::Error(LogErrorBase + "no valid ID (" + kpName + ")");
|
||||
else if (kpID && !kpName) Log::Error(LogErrorBase + "no valid name (ID " + kpID + ")");
|
||||
else Log::Error(LogErrorBase + "no valid ID or name");
|
||||
}
|
||||
|
||||
pChild = pChild->NextSiblingElement("enumerator");
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateLoader::LoadStructProperties(tinyxml2::XMLElement *pElem, CStructTemplate *pTemp, const TString& templateName)
|
||||
{
|
||||
tinyxml2::XMLElement *pChild = pElem->FirstChildElement();
|
||||
|
||||
while (pChild)
|
||||
{
|
||||
CPropertyTemplate *pProp = LoadPropertyTemplate(pChild, templateName);
|
||||
|
||||
if (pProp)
|
||||
pTemp->mProperties.push_back(pProp);
|
||||
|
||||
pChild = pChild->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
|
||||
CPropertyTemplate* CTemplateLoader::LoadPropertyTemplate(tinyxml2::XMLElement *pElem, const TString& templateName)
|
||||
{
|
||||
const char *kpIDStr = pElem->Attribute("ID");
|
||||
const char *kpNameStr = pElem->Attribute("name");
|
||||
const char *kpTypeStr = pElem->Attribute("type");
|
||||
const char *kpExtensionsStr = pElem->Attribute("ext");
|
||||
const char *kpTemplateStr = pElem->Attribute("template");
|
||||
|
||||
// Get ID + name, find source template if it exists
|
||||
u32 ID = TString(kpIDStr).ToInt32();
|
||||
CPropertyTemplate *pSource = nullptr;
|
||||
TString name;
|
||||
|
||||
if (mpMaster->HasPropertyList())
|
||||
pSource = mpMaster->GetProperty(ID);
|
||||
|
||||
if (kpNameStr)
|
||||
name = kpNameStr;
|
||||
else if (pSource)
|
||||
name = pSource->Name();
|
||||
else
|
||||
name = TString::HexString(ID);
|
||||
|
||||
// Load Property
|
||||
if (strcmp(pElem->Name(), "property") == 0)
|
||||
{
|
||||
CPropertyTemplate *pProp;
|
||||
EPropertyType type = eInvalidProperty;
|
||||
|
||||
// Type
|
||||
if (kpTypeStr)
|
||||
type = PropStringToPropEnum(kpTypeStr);
|
||||
else if (pSource)
|
||||
type = pSource->Type();
|
||||
|
||||
// File property
|
||||
if (type == eFileProperty)
|
||||
{
|
||||
TStringList extensions;
|
||||
if (kpExtensionsStr)
|
||||
extensions = TString(kpExtensionsStr).Split(",");
|
||||
else if (pSource)
|
||||
extensions = static_cast<CFileTemplate*>(pSource)->Extensions();
|
||||
|
||||
pProp = new CFileTemplate(name, ID, extensions);
|
||||
}
|
||||
|
||||
// Regular property
|
||||
else
|
||||
pProp = new CPropertyTemplate(type, name, ID);
|
||||
|
||||
return pProp;
|
||||
}
|
||||
|
||||
// Load Struct
|
||||
else if (strcmp(pElem->Name(), "struct") == 0)
|
||||
{
|
||||
CStructTemplate *pStruct = new CStructTemplate();
|
||||
|
||||
pStruct->mPropID = ID;
|
||||
|
||||
// Read children properties
|
||||
// Priority: [Embedded] -> [Template] -> [Master]
|
||||
|
||||
// Embedded
|
||||
if (!pElem->NoChildren())
|
||||
LoadStructProperties(pElem, pStruct, templateName);
|
||||
|
||||
// Template
|
||||
else if (kpTemplateStr)
|
||||
{
|
||||
TString tempPath = mMasterDir + kpTemplateStr;
|
||||
|
||||
tinyxml2::XMLDocument structXML;
|
||||
structXML.LoadFile(*tempPath);
|
||||
|
||||
if (structXML.Error())
|
||||
Log::Error("Couldn't open struct XML: " + mMasterDir + kpTemplateStr);
|
||||
|
||||
else
|
||||
{
|
||||
tinyxml2::XMLElement *pRoot = structXML.FirstChildElement("struct");
|
||||
pStruct->mSourceFile = kpTemplateStr;
|
||||
|
||||
if (pRoot->Attribute("type"))
|
||||
pStruct->mIsSingleProperty = (strcmp(pRoot->Attribute("type"), "single") == 0);
|
||||
|
||||
if (pRoot->Attribute("name"))
|
||||
pStruct->mPropName = pRoot->Attribute("name");
|
||||
|
||||
LoadStructProperties(pRoot, pStruct, templateName);
|
||||
}
|
||||
}
|
||||
|
||||
// Master
|
||||
else if (pSource)
|
||||
{
|
||||
CStructTemplate *pSourceStruct = static_cast<CStructTemplate*>(pSource);
|
||||
|
||||
for (u32 iProp = 0; iProp < pSourceStruct->Count(); iProp++)
|
||||
pStruct->mProperties.push_back(pSourceStruct->PropertyByIndex(iProp));
|
||||
}
|
||||
|
||||
// If it's none of these, then it probably has no children because it's a property list entry.
|
||||
|
||||
// Single property?
|
||||
if (kpTypeStr)
|
||||
pStruct->mIsSingleProperty = (strcmp(kpTypeStr, "single") == 0);
|
||||
else if (pSource)
|
||||
pStruct->mIsSingleProperty = static_cast<CStructTemplate*>(pSource)->IsSingleProperty();
|
||||
|
||||
// Name
|
||||
if (!name.IsEmpty())
|
||||
pStruct->mPropName = name;
|
||||
|
||||
return pStruct;
|
||||
}
|
||||
|
||||
// Load Enum
|
||||
else if (strcmp(pElem->Name(), "enum") == 0)
|
||||
{
|
||||
CEnumTemplate *pEnum = new CEnumTemplate(ID);
|
||||
|
||||
// Read children enumerators
|
||||
// Priority: [Embedded] -> [Template]
|
||||
|
||||
// Embedded
|
||||
if (!pElem->NoChildren())
|
||||
LoadEnumerators(pElem, pEnum, templateName);
|
||||
|
||||
// Template
|
||||
else if (kpTemplateStr)
|
||||
{
|
||||
TString tempPath = mMasterDir + kpTemplateStr;
|
||||
|
||||
tinyxml2::XMLDocument enumXML;
|
||||
enumXML.LoadFile(*tempPath);
|
||||
|
||||
if (enumXML.Error())
|
||||
Log::Error("Couldn't open enum XML: " + mMasterDir + kpTemplateStr);
|
||||
|
||||
else
|
||||
{
|
||||
tinyxml2::XMLElement *pRoot = enumXML.FirstChildElement("enum");
|
||||
pEnum->mSourceFile = kpTemplateStr;
|
||||
|
||||
if (pRoot->Attribute("name"))
|
||||
pEnum->mPropName = pRoot->Attribute("name");
|
||||
|
||||
LoadEnumerators(pRoot, pEnum, kpTemplateStr );
|
||||
}
|
||||
}
|
||||
|
||||
// Name
|
||||
if (!name.IsEmpty())
|
||||
pEnum->mPropName = name;
|
||||
|
||||
return pEnum;
|
||||
}
|
||||
|
||||
|
||||
// Load Bitfield
|
||||
else if (strcmp(pElem->Name(), "bitfield") == 0)
|
||||
{
|
||||
CBitfieldTemplate *pBitfield = new CBitfieldTemplate(ID);
|
||||
|
||||
// Embedded
|
||||
if (!pElem->NoChildren())
|
||||
LoadBitFlags(pElem, pBitfield, templateName);
|
||||
|
||||
// Template
|
||||
else if (kpTemplateStr)
|
||||
{
|
||||
TString tempPath = mMasterDir + kpTemplateStr;
|
||||
|
||||
tinyxml2::XMLDocument bitfieldXML;
|
||||
bitfieldXML.LoadFile(*tempPath);
|
||||
|
||||
if (bitfieldXML.Error())
|
||||
Log::Error("Couldn't open bitfield XML: " + mMasterDir + kpTemplateStr);
|
||||
|
||||
else
|
||||
{
|
||||
tinyxml2::XMLElement *pRoot = bitfieldXML.FirstChildElement("bitfield");
|
||||
pBitfield->mSourceFile = kpTemplateStr;
|
||||
|
||||
if (pRoot->Attribute("name"))
|
||||
pBitfield->mPropName = pRoot->Attribute("name");
|
||||
|
||||
LoadBitFlags(pRoot, pBitfield, kpTemplateStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Name
|
||||
if (!name.IsEmpty())
|
||||
pBitfield->mPropName = name;
|
||||
|
||||
return pBitfield;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ************ SCRIPT OBJECT ************
|
||||
CScriptTemplate* CTemplateLoader::LoadScriptTemplate(tinyxml2::XMLDocument *pDoc, const TString& /*templateName*/, u32 objectID)
|
||||
{
|
||||
CScriptTemplate *pScript = new CScriptTemplate(mpMaster);
|
||||
pScript->mObjectID = objectID;
|
||||
|
||||
tinyxml2::XMLElement *pRoot = pDoc->FirstChildElement("ScriptTemplate");
|
||||
|
||||
// Name
|
||||
tinyxml2::XMLElement *pNameElem = pRoot->FirstChildElement("name");
|
||||
|
||||
if (pNameElem)
|
||||
pScript->mTemplateName = pNameElem->GetText();
|
||||
|
||||
// Properties
|
||||
tinyxml2::XMLElement *pPropsElem = pRoot->FirstChildElement("properties");
|
||||
|
||||
while (pPropsElem)
|
||||
{
|
||||
CScriptTemplate::SPropertySet set;
|
||||
|
||||
const char *kpVersion = pPropsElem->Attribute("version");
|
||||
set.SetName = (kpVersion ? kpVersion : "");
|
||||
set.pBaseStruct = new CStructTemplate();
|
||||
set.pBaseStruct->mIsSingleProperty = false;
|
||||
set.pBaseStruct->mPropID = -1;
|
||||
set.pBaseStruct->mPropName = pScript->mTemplateName;
|
||||
LoadStructProperties(pPropsElem, set.pBaseStruct, pScript->mTemplateName);
|
||||
pScript->mPropertySets.push_back(set);
|
||||
|
||||
pPropsElem = pPropsElem->NextSiblingElement("properties");
|
||||
}
|
||||
|
||||
// Editor Parameters
|
||||
tinyxml2::XMLElement *pEditor = pRoot->FirstChildElement("editor");
|
||||
|
||||
if (pEditor)
|
||||
{
|
||||
// Editor Properties
|
||||
tinyxml2::XMLElement *pEdProperties = pEditor->FirstChildElement("properties");
|
||||
tinyxml2::XMLElement *pEdProp = pEdProperties->FirstChildElement("property");
|
||||
|
||||
while (pEdProp)
|
||||
{
|
||||
const char *kpName = pEdProp->Attribute("name");
|
||||
const char *kpID = pEdProp->Attribute("ID");
|
||||
|
||||
if (kpName && kpID)
|
||||
{
|
||||
if (strcmp(kpName, "InstanceName") == 0)
|
||||
pScript->mNameIDString = kpID;
|
||||
else if (strcmp(kpName, "Position") == 0)
|
||||
pScript->mPositionIDString = kpID;
|
||||
else if (strcmp(kpName, "Rotation") == 0)
|
||||
pScript->mRotationIDString = kpID;
|
||||
else if (strcmp(kpName, "Scale") == 0)
|
||||
pScript->mScaleIDString = kpID;
|
||||
else if (strcmp(kpName, "Active") == 0)
|
||||
pScript->mActiveIDString = kpID;
|
||||
else if (strcmp(kpName, "LightParameters") == 0)
|
||||
pScript->mLightParametersIDString = kpID;
|
||||
}
|
||||
|
||||
pEdProp = pEdProp->NextSiblingElement("property");
|
||||
}
|
||||
|
||||
// Editor Assets
|
||||
tinyxml2::XMLElement *pEdAssets = pEditor->FirstChildElement("assets");
|
||||
tinyxml2::XMLElement *pAsset = pEdAssets->FirstChildElement();
|
||||
|
||||
while (pAsset)
|
||||
{
|
||||
const char *kpSource = pAsset->Attribute("source");
|
||||
const char *kpID = pAsset->GetText();
|
||||
|
||||
if (kpSource && kpID)
|
||||
{
|
||||
CScriptTemplate::SEditorAsset asset;
|
||||
|
||||
if (strcmp(pAsset->Name(), "animparams") == 0)
|
||||
asset.AssetType = CScriptTemplate::SEditorAsset::eAnimParams;
|
||||
else if (strcmp(pAsset->Name(), "model") == 0)
|
||||
asset.AssetType = CScriptTemplate::SEditorAsset::eModel;
|
||||
else if (strcmp(pAsset->Name(), "billboard") == 0)
|
||||
asset.AssetType = CScriptTemplate::SEditorAsset::eBillboard;
|
||||
else if (strcmp(pAsset->Name(), "collision") == 0)
|
||||
asset.AssetType = CScriptTemplate::SEditorAsset::eCollision;
|
||||
else
|
||||
{
|
||||
pAsset = pAsset->NextSiblingElement();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(kpSource, "property") == 0)
|
||||
asset.AssetSource = CScriptTemplate::SEditorAsset::eProperty;
|
||||
else if (strcmp(kpSource, "file") == 0)
|
||||
asset.AssetSource = CScriptTemplate::SEditorAsset::eFile;
|
||||
else
|
||||
{
|
||||
pAsset = pAsset->NextSiblingElement();
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *kpForce = pAsset->Attribute("force");
|
||||
if (kpForce)
|
||||
asset.ForceNodeIndex = TString(kpForce).ToInt32();
|
||||
else
|
||||
asset.ForceNodeIndex = -1;
|
||||
|
||||
asset.AssetLocation = kpID;
|
||||
pScript->mAssets.push_back(asset);
|
||||
}
|
||||
|
||||
pAsset = pAsset->NextSiblingElement();
|
||||
}
|
||||
|
||||
// Preview Scale
|
||||
tinyxml2::XMLElement *pPreviewScale = pEditor->FirstChildElement("preview_scale");
|
||||
|
||||
if (pPreviewScale)
|
||||
{
|
||||
const char *kpScale = pPreviewScale->GetText();
|
||||
|
||||
if (kpScale)
|
||||
pScript->mPreviewScale = std::stof(kpScale);
|
||||
}
|
||||
|
||||
// Rotation
|
||||
tinyxml2::XMLElement *pRotType = pEditor->FirstChildElement("rotation_type");
|
||||
|
||||
if (pRotType)
|
||||
{
|
||||
const char *kpType = pRotType->GetText();
|
||||
|
||||
if (kpType)
|
||||
{
|
||||
if (strcmp(kpType, "disabled") == 0) pScript->mRotationType = CScriptTemplate::eRotationDisabled;
|
||||
else pScript->mRotationType = CScriptTemplate::eRotationEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale
|
||||
tinyxml2::XMLElement *pScaleType = pEditor->FirstChildElement("scale_type");
|
||||
|
||||
if (pScaleType)
|
||||
{
|
||||
const char *kpType = pScaleType->GetText();
|
||||
|
||||
if (kpType)
|
||||
{
|
||||
if (strcmp(kpType, "disabled") == 0) pScript->mScaleType = CScriptTemplate::eScaleDisabled;
|
||||
else if (strcmp(kpType, "volume") == 0) pScript->mScaleType = CScriptTemplate::eScaleVolume;
|
||||
else pScript->mScaleType = CScriptTemplate::eScaleEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
// Preview Volume
|
||||
if (pScript->mScaleType == CScriptTemplate::eScaleVolume)
|
||||
{
|
||||
tinyxml2::XMLElement *pVolume = pEditor->FirstChildElement("preview_volume");
|
||||
|
||||
// Lambda to avoid duplicating volume shape code
|
||||
auto GetVolumeType = [](const char *kpType) -> EVolumeShape {
|
||||
if (strcmp(kpType, "none") == 0) return eNoShape;
|
||||
if (strcmp(kpType, "Box") == 0) return eBoxShape;
|
||||
if (strcmp(kpType, "AxisAlignedBox") == 0) return eAxisAlignedBoxShape;
|
||||
if (strcmp(kpType, "Ellipsoid") == 0) return eEllipsoidShape;
|
||||
if (strcmp(kpType, "Cylinder") == 0) return eCylinderShape;
|
||||
if (strcmp(kpType, "CylinderLarge") == 0) return eCylinderLargeShape;
|
||||
if (strcmp(kpType, "Conditional") == 0) return eConditionalShape;
|
||||
return eInvalidShape;
|
||||
};
|
||||
|
||||
const char *kpShape = pVolume->Attribute("shape");
|
||||
|
||||
if (kpShape)
|
||||
pScript->mVolumeShape = GetVolumeType(kpShape);
|
||||
|
||||
// Conditional
|
||||
if (pScript->mVolumeShape == eConditionalShape)
|
||||
{
|
||||
const char *kpID = pVolume->Attribute("propertyID");
|
||||
|
||||
if (kpID)
|
||||
{
|
||||
pScript->mVolumeConditionIDString = kpID;
|
||||
tinyxml2::XMLElement *pCondition = pVolume->FirstChildElement("condition");
|
||||
|
||||
while (pCondition)
|
||||
{
|
||||
const char *kpConditionValue = pCondition->Attribute("value");
|
||||
const char *kpConditionShape = pCondition->Attribute("shape");
|
||||
|
||||
if (kpConditionValue && kpConditionShape)
|
||||
{
|
||||
CScriptTemplate::SVolumeCondition condition;
|
||||
condition.Shape = GetVolumeType(kpConditionShape);
|
||||
|
||||
if (strcmp(kpConditionValue, "true") == 0)
|
||||
condition.Value = 1;
|
||||
else if (strcmp(kpConditionValue, "false") == 0)
|
||||
condition.Value = 0;
|
||||
else
|
||||
condition.Value = TString(kpConditionValue).ToInt32();
|
||||
|
||||
pScript->mVolumeConditions.push_back(condition);
|
||||
}
|
||||
|
||||
pCondition = pCondition->NextSiblingElement("condition");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pScript;
|
||||
}
|
||||
|
||||
// ************ MASTER ************
|
||||
void CTemplateLoader::LoadMasterTemplate(tinyxml2::XMLDocument *pDoc)
|
||||
{
|
||||
tinyxml2::XMLElement *pRoot = pDoc->FirstChildElement("MasterTemplate");
|
||||
mpMaster->mVersion = TString(pRoot->Attribute("version")).ToInt32();
|
||||
|
||||
tinyxml2::XMLElement *pElem = pRoot->FirstChildElement();
|
||||
|
||||
while (pElem)
|
||||
{
|
||||
// Properties
|
||||
if (strcmp(pElem->Name(), "properties") == 0)
|
||||
{
|
||||
TString propListPath = mMasterDir + pElem->GetText();
|
||||
|
||||
tinyxml2::XMLDocument propListXML;
|
||||
propListXML.LoadFile(*propListPath);
|
||||
|
||||
if (propListXML.Error())
|
||||
Log::Error("Couldn't open property list: " + propListPath);
|
||||
|
||||
else
|
||||
LoadPropertyList(&propListXML, propListPath);
|
||||
}
|
||||
|
||||
// Objects
|
||||
else if (strcmp(pElem->Name(), "objects") == 0)
|
||||
{
|
||||
tinyxml2::XMLElement *pObj = pElem->FirstChildElement("object");
|
||||
|
||||
while (pObj)
|
||||
{
|
||||
// ID can either be a hex number or an ASCII fourCC
|
||||
TString strID = pObj->Attribute("ID");
|
||||
u32 ID;
|
||||
|
||||
if (strID.IsHexString(true))
|
||||
ID = strID.ToInt32();
|
||||
else
|
||||
ID = CFourCC(strID).ToLong();
|
||||
|
||||
// Load up the object
|
||||
TString templateName = pObj->Attribute("template");
|
||||
TString templatePath = mMasterDir + templateName;
|
||||
|
||||
tinyxml2::XMLDocument scriptXML;
|
||||
scriptXML.LoadFile(*templatePath);
|
||||
|
||||
if (scriptXML.Error())
|
||||
Log::Error("Couldn't open script template: " + templatePath);
|
||||
|
||||
else
|
||||
{
|
||||
CScriptTemplate *pTemp = LoadScriptTemplate(&scriptXML, templateName, ID);
|
||||
|
||||
if (pTemp)
|
||||
{
|
||||
pTemp->mSourceFile = templateName;
|
||||
mpMaster->mTemplates[ID] = pTemp;
|
||||
}
|
||||
}
|
||||
|
||||
pObj = pObj->NextSiblingElement("object");
|
||||
}
|
||||
}
|
||||
|
||||
// States
|
||||
else if (strcmp(pElem->Name(), "states") == 0)
|
||||
{
|
||||
tinyxml2::XMLElement *pState = pElem->FirstChildElement("state");
|
||||
|
||||
while (pState)
|
||||
{
|
||||
TString strID = pState->Attribute("ID");
|
||||
u32 stateID;
|
||||
|
||||
if (strID.IsHexString(true))
|
||||
stateID = strID.ToInt32();
|
||||
else
|
||||
stateID = CFourCC(strID).ToLong();
|
||||
|
||||
TString stateName = pState->Attribute("name");
|
||||
mpMaster->mStates[stateID] = stateName;
|
||||
pState = pState->NextSiblingElement("state");
|
||||
}
|
||||
}
|
||||
|
||||
// Messages
|
||||
else if (strcmp(pElem->Name(), "messages") == 0)
|
||||
{
|
||||
tinyxml2::XMLElement *pMessage = pElem->FirstChildElement("message");
|
||||
|
||||
while (pMessage)
|
||||
{
|
||||
TString strID = pMessage->Attribute("ID");
|
||||
u32 messageID;
|
||||
|
||||
if (strID.IsHexString(true))
|
||||
messageID = strID.ToInt32();
|
||||
else
|
||||
messageID = CFourCC(strID).ToLong();
|
||||
|
||||
TString messageName = pMessage->Attribute("name");
|
||||
mpMaster->mMessages[messageID] = messageName;
|
||||
pMessage = pMessage->NextSiblingElement("message");
|
||||
}
|
||||
}
|
||||
|
||||
pElem = pElem->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateLoader::LoadPropertyList(tinyxml2::XMLDocument *pDoc, const TString& listName)
|
||||
{
|
||||
tinyxml2::XMLElement *pElem = pDoc->FirstChildElement()->FirstChildElement();
|
||||
|
||||
while (pElem)
|
||||
{
|
||||
CPropertyTemplate *pProp = LoadPropertyTemplate(pElem, listName);
|
||||
|
||||
if (pProp)
|
||||
mpMaster->mPropertyList[pProp->PropertyID()] = pProp;
|
||||
|
||||
pElem = pElem->NextSiblingElement();
|
||||
}
|
||||
|
||||
mpMaster->mHasPropList = true;
|
||||
}
|
||||
|
||||
CMasterTemplate* CTemplateLoader::LoadGame(tinyxml2::XMLNode *pNode)
|
||||
{
|
||||
tinyxml2::XMLElement *pGameElem = pNode->FirstChildElement();
|
||||
mpMaster = new CMasterTemplate();
|
||||
|
||||
// Parse game parameters
|
||||
while (pGameElem)
|
||||
{
|
||||
if (strcmp(pGameElem->Name(), "name") == 0)
|
||||
mpMaster->mGameName = pGameElem->GetText();
|
||||
|
||||
else if (strcmp(pGameElem->Name(), "mlvl") == 0)
|
||||
{
|
||||
u32 VersionNum = std::stoul(pGameElem->GetText(), 0, 16);
|
||||
mpMaster->mGame = CWorldLoader::GetFormatVersion(VersionNum);
|
||||
}
|
||||
|
||||
else if (strcmp(pGameElem->Name(), "master") == 0)
|
||||
{
|
||||
TString MasterPath = mTemplatesDir + pGameElem->GetText();
|
||||
mMasterDir = MasterPath.GetFileDirectory();
|
||||
|
||||
tinyxml2::XMLDocument MasterXML;
|
||||
MasterXML.LoadFile(*MasterPath);
|
||||
|
||||
if (MasterXML.Error())
|
||||
{
|
||||
Log::Error("Couldn't open master template at " + MasterPath + " - error " + std::to_string(MasterXML.ErrorID()));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
LoadMasterTemplate(&MasterXML);
|
||||
mpMaster->mSourceFile = pGameElem->GetText();
|
||||
}
|
||||
}
|
||||
pGameElem = pGameElem->NextSiblingElement();
|
||||
}
|
||||
|
||||
mpMaster->mFullyLoaded = true;
|
||||
return mpMaster;
|
||||
}
|
||||
|
||||
// ************ PUBLIC ************
|
||||
void CTemplateLoader::LoadGameList()
|
||||
{
|
||||
static const TString skTemplatesDir = "../templates/";
|
||||
static const TString skGameListPath = skTemplatesDir + "GameList.xml";
|
||||
Log::Write("Loading game list");
|
||||
|
||||
// Load Game List XML
|
||||
tinyxml2::XMLDocument GameListXML;
|
||||
GameListXML.LoadFile(*skGameListPath);
|
||||
|
||||
if (GameListXML.Error())
|
||||
{
|
||||
Log::Error("Couldn't open game list at " + skGameListPath + " - error " + std::to_string(GameListXML.ErrorID()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse
|
||||
tinyxml2::XMLNode *pNode = GameListXML.FirstChild()->NextSibling()->FirstChild();
|
||||
|
||||
while (pNode)
|
||||
{
|
||||
tinyxml2::XMLElement *pElement = pNode->ToElement();
|
||||
|
||||
// Game List version number
|
||||
if (strcmp(pElement->Name(), "version") == 0)
|
||||
{
|
||||
u32 VersionNum = std::stoul(pElement->GetText());
|
||||
CMasterTemplate::smGameListVersion = VersionNum;
|
||||
}
|
||||
|
||||
// Games
|
||||
else if (strcmp(pElement->Name(), "game") == 0)
|
||||
{
|
||||
CTemplateLoader Loader(skTemplatesDir);
|
||||
CMasterTemplate *pMaster = Loader.LoadGame(pNode);
|
||||
|
||||
if (!pMaster->IsLoadedSuccessfully())
|
||||
{
|
||||
Log::Error("Master template for " + pMaster->mGameName + " couldn't be loaded");
|
||||
delete pMaster;
|
||||
}
|
||||
|
||||
else
|
||||
CMasterTemplate::smMasterMap[pMaster->mGame] = pMaster;
|
||||
}
|
||||
|
||||
pNode = pNode->NextSibling();
|
||||
}
|
||||
}
|
||||
35
src/Core/Resource/Factory/CTemplateLoader.h
Normal file
35
src/Core/Resource/Factory/CTemplateLoader.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef CTEMPLATELOADER_H
|
||||
#define CTEMPLATELOADER_H
|
||||
|
||||
#include "../script/CMasterTemplate.h"
|
||||
#include "../script/CScriptTemplate.h"
|
||||
#include <tinyxml2.h>
|
||||
|
||||
class CTemplateLoader
|
||||
{
|
||||
CMasterTemplate *mpMaster;
|
||||
TString mTemplatesDir;
|
||||
TString mMasterDir;
|
||||
|
||||
// Constructor
|
||||
CTemplateLoader(const TString& templatesDir) : mTemplatesDir(templatesDir) {}
|
||||
|
||||
// Load Property
|
||||
void LoadBitFlags(tinyxml2::XMLElement *pElem, CBitfieldTemplate *pTemp, const TString& templateName);
|
||||
void LoadEnumerators(tinyxml2::XMLElement *pElem, CEnumTemplate *pTemp, const TString& templateName);
|
||||
void LoadStructProperties(tinyxml2::XMLElement *pElem, CStructTemplate *pTemp, const TString& templateName);
|
||||
CPropertyTemplate* LoadPropertyTemplate(tinyxml2::XMLElement *pElem, const TString& templateName);
|
||||
|
||||
// Load Script Object
|
||||
CScriptTemplate* LoadScriptTemplate(tinyxml2::XMLDocument *pDoc, const TString& templateName, u32 objectID);
|
||||
|
||||
// Load Master
|
||||
void LoadMasterTemplate(tinyxml2::XMLDocument *pDoc);
|
||||
void LoadPropertyList(tinyxml2::XMLDocument *pDoc, const TString& listName);
|
||||
CMasterTemplate* LoadGame(tinyxml2::XMLNode *pNode);
|
||||
|
||||
public:
|
||||
static void LoadGameList();
|
||||
};
|
||||
|
||||
#endif // CTEMPLATELOADER_H
|
||||
875
src/Core/Resource/Factory/CTextureDecoder.cpp
Normal file
875
src/Core/Resource/Factory/CTextureDecoder.cpp
Normal file
@@ -0,0 +1,875 @@
|
||||
#include <Common/CColor.h>
|
||||
#include <Core/Log.h>
|
||||
#include "CTextureDecoder.h"
|
||||
|
||||
// A cleanup is warranted at some point. Trying to support both partial + full decode ended up really messy.
|
||||
|
||||
// Number of pixels * this = number of bytes
|
||||
static const float gskPixelsToBytes[] = {
|
||||
2.f, 2.f, 2.f, 2.f, 4.f, 4.f, 0.f, 2.f, 4.f, 4.f, 0.5f
|
||||
};
|
||||
|
||||
// Bits per pixel for each GX texture format
|
||||
static const u32 gskSourceBpp[] = {
|
||||
4, 8, 8, 16, 4, 8, 16, 16, 16, 32, 4
|
||||
};
|
||||
|
||||
// Bits per pixel for each GX texture format when decoded
|
||||
static const u32 gskOutputBpp[] = {
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 4
|
||||
};
|
||||
|
||||
// Size of one pixel in output data in bytes
|
||||
static const u32 gskOutputPixelStride[] = {
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 8
|
||||
};
|
||||
|
||||
// Block width for each GX texture format
|
||||
static const u32 gskBlockWidth[] = {
|
||||
8, 8, 8, 4, 8, 8, 4, 4, 4, 4, 2
|
||||
};
|
||||
|
||||
// Block height for each GX texture format
|
||||
static const u32 gskBlockHeight[] = {
|
||||
8, 4, 4, 4, 8, 4, 4, 4, 4, 4, 2
|
||||
};
|
||||
|
||||
CTextureDecoder::CTextureDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
CTextureDecoder::~CTextureDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
CTexture* CTextureDecoder::CreateTexture()
|
||||
{
|
||||
CTexture *pTex = new CTexture;
|
||||
pTex->mSourceTexelFormat = mTexelFormat;
|
||||
pTex->mWidth = mWidth;
|
||||
pTex->mHeight = mHeight;
|
||||
pTex->mNumMipMaps = mNumMipMaps;
|
||||
pTex->mLinearSize = (u32) (mWidth * mHeight * gskPixelsToBytes[mTexelFormat]);
|
||||
pTex->mImgDataBuffer = mpDataBuffer;
|
||||
pTex->mImgDataSize = mDataBufferSize;
|
||||
pTex->mBufferExists = true;
|
||||
|
||||
switch (mTexelFormat) {
|
||||
case eGX_I4:
|
||||
case eGX_I8:
|
||||
case eGX_IA4:
|
||||
case eGX_IA8:
|
||||
pTex->mTexelFormat = eLuminanceAlpha;
|
||||
break;
|
||||
case eGX_RGB565:
|
||||
pTex->mTexelFormat = eRGB565;
|
||||
break;
|
||||
case eGX_C4:
|
||||
case eGX_C8:
|
||||
if (mPaletteFormat == ePalette_IA8) pTex->mTexelFormat = eLuminanceAlpha;
|
||||
if (mPaletteFormat == ePalette_RGB565) pTex->mTexelFormat = eRGB565;
|
||||
if (mPaletteFormat == ePalette_RGB5A3) pTex->mTexelFormat = eRGBA8;
|
||||
break;
|
||||
case eGX_RGB5A3:
|
||||
case eGX_RGBA8:
|
||||
pTex->mTexelFormat = eRGBA8;
|
||||
break;
|
||||
case eGX_CMPR:
|
||||
pTex->mTexelFormat = eDXT1;
|
||||
break;
|
||||
case eDXT1:
|
||||
pTex->mTexelFormat = eDXT1;
|
||||
pTex->mLinearSize = mWidth * mHeight / 2;
|
||||
break;
|
||||
default:
|
||||
pTex->mTexelFormat = mTexelFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
return pTex;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CTexture* CTextureDecoder::LoadTXTR(CInputStream& TXTR)
|
||||
{
|
||||
CTextureDecoder Decoder;
|
||||
Decoder.ReadTXTR(TXTR);
|
||||
Decoder.PartialDecodeGXTexture(TXTR);
|
||||
return Decoder.CreateTexture();
|
||||
}
|
||||
|
||||
CTexture* CTextureDecoder::DoFullDecode(CInputStream &TXTR)
|
||||
{
|
||||
CTextureDecoder Decoder;
|
||||
Decoder.ReadTXTR(TXTR);
|
||||
Decoder.FullDecodeGXTexture(TXTR);
|
||||
|
||||
CTexture *pTexture = Decoder.CreateTexture();
|
||||
pTexture->mTexelFormat = eRGBA8;
|
||||
return pTexture;
|
||||
}
|
||||
|
||||
CTexture* CTextureDecoder::LoadDDS(CInputStream &DDS)
|
||||
{
|
||||
CTextureDecoder Decoder;
|
||||
Decoder.ReadDDS(DDS);
|
||||
Decoder.DecodeDDS(DDS);
|
||||
return Decoder.CreateTexture();
|
||||
}
|
||||
|
||||
CTexture* CTextureDecoder::DoFullDecode(CTexture*)
|
||||
{
|
||||
// Not using parameter 1 (CTexture* - pTexture)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ************ READ ************
|
||||
void CTextureDecoder::ReadTXTR(CInputStream& TXTR)
|
||||
{
|
||||
// Read TXTR header
|
||||
Log::Write("Loading " + TXTR.GetSourceString());
|
||||
mTexelFormat = ETexelFormat(TXTR.ReadLong());
|
||||
mWidth = TXTR.ReadShort();
|
||||
mHeight = TXTR.ReadShort();
|
||||
mNumMipMaps = TXTR.ReadLong();
|
||||
|
||||
// For C4 and C8 images, read palette
|
||||
if ((mTexelFormat == eGX_C4) || (mTexelFormat == eGX_C8))
|
||||
{
|
||||
mHasPalettes = true;
|
||||
mPaletteFormat = EGXPaletteFormat(TXTR.ReadLong());
|
||||
TXTR.Seek(0x4, SEEK_CUR);
|
||||
|
||||
u32 PaletteEntryCount = (mTexelFormat == eGX_C4) ? 16 : 256;
|
||||
mPalettes.resize(PaletteEntryCount * 2);
|
||||
TXTR.ReadBytes(mPalettes.data(), mPalettes.size());
|
||||
|
||||
mPaletteInput.SetData(mPalettes.data(), mPalettes.size(), IOUtil::BigEndian);
|
||||
}
|
||||
else mHasPalettes = false;
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadDDS(CInputStream& DDS)
|
||||
{
|
||||
Log::Write("Loading " + DDS.GetSourceString());
|
||||
|
||||
// Header
|
||||
CFourCC Magic(DDS);
|
||||
if (Magic != "DDS ")
|
||||
{
|
||||
Log::FileError(DDS.GetSourceString(), "Invalid DDS magic: " + TString::HexString((u32) Magic.ToLong()));
|
||||
return;
|
||||
}
|
||||
|
||||
u32 ImageDataStart = DDS.Tell() + DDS.ReadLong();
|
||||
DDS.Seek(0x4, SEEK_CUR); // Skipping flags
|
||||
mHeight = (u16) DDS.ReadLong();
|
||||
mWidth = (u16) DDS.ReadLong();
|
||||
DDS.Seek(0x8, SEEK_CUR); // Skipping linear size + depth
|
||||
mNumMipMaps = DDS.ReadLong() + 1; // DDS doesn't seem to count the first mipmap
|
||||
DDS.Seek(0x2C, SEEK_CUR); // Skipping reserved
|
||||
|
||||
// Pixel Format
|
||||
DDS.Seek(0x4, SEEK_CUR); // Skipping size
|
||||
mDDSInfo.Flags = DDS.ReadLong();
|
||||
CFourCC Format(DDS);
|
||||
|
||||
if (Format == "DXT1") mDDSInfo.Format = SDDSInfo::DXT1;
|
||||
else if (Format == "DXT2") mDDSInfo.Format = SDDSInfo::DXT2;
|
||||
else if (Format == "DXT3") mDDSInfo.Format = SDDSInfo::DXT3;
|
||||
else if (Format == "DXT4") mDDSInfo.Format = SDDSInfo::DXT4;
|
||||
else if (Format == "DXT5") mDDSInfo.Format = SDDSInfo::DXT5;
|
||||
else
|
||||
{
|
||||
mDDSInfo.Format = SDDSInfo::RGBA;
|
||||
mDDSInfo.BitCount = DDS.ReadLong();
|
||||
mDDSInfo.RBitMask = DDS.ReadLong();
|
||||
mDDSInfo.GBitMask = DDS.ReadLong();
|
||||
mDDSInfo.BBitMask = DDS.ReadLong();
|
||||
mDDSInfo.ABitMask = DDS.ReadLong();
|
||||
mDDSInfo.RShift = CalculateShiftForMask(mDDSInfo.RBitMask);
|
||||
mDDSInfo.GShift = CalculateShiftForMask(mDDSInfo.GBitMask);
|
||||
mDDSInfo.BShift = CalculateShiftForMask(mDDSInfo.BBitMask);
|
||||
mDDSInfo.AShift = CalculateShiftForMask(mDDSInfo.ABitMask);
|
||||
mDDSInfo.RSize = CalculateMaskBitCount(mDDSInfo.RBitMask);
|
||||
mDDSInfo.GSize = CalculateMaskBitCount(mDDSInfo.GBitMask);
|
||||
mDDSInfo.BSize = CalculateMaskBitCount(mDDSInfo.BBitMask);
|
||||
mDDSInfo.ASize = CalculateMaskBitCount(mDDSInfo.ABitMask);
|
||||
}
|
||||
|
||||
// Skip the rest
|
||||
DDS.Seek(ImageDataStart, SEEK_SET);
|
||||
}
|
||||
|
||||
// ************ DECODE ************
|
||||
void CTextureDecoder::PartialDecodeGXTexture(CInputStream& TXTR)
|
||||
{
|
||||
// Get image data size, create output buffer
|
||||
u32 ImageStart = TXTR.Tell();
|
||||
TXTR.Seek(0x0, SEEK_END);
|
||||
u32 ImageSize = TXTR.Tell() - ImageStart;
|
||||
TXTR.Seek(ImageStart, SEEK_SET);
|
||||
|
||||
mDataBufferSize = ImageSize * (gskOutputBpp[mTexelFormat] / gskSourceBpp[mTexelFormat]);
|
||||
if ((mHasPalettes) && (mPaletteFormat == ePalette_RGB5A3)) mDataBufferSize *= 2;
|
||||
mpDataBuffer = new u8[mDataBufferSize];
|
||||
|
||||
CMemoryOutStream Out(mpDataBuffer, mDataBufferSize, IOUtil::SystemEndianness);
|
||||
|
||||
// Initializing more stuff before we start the mipmap loop
|
||||
u32 MipW = mWidth, MipH = mHeight;
|
||||
u32 MipOffset = 0;
|
||||
|
||||
u32 BWidth = gskBlockWidth[mTexelFormat];
|
||||
u32 BHeight = gskBlockHeight[mTexelFormat];
|
||||
|
||||
u32 PixelStride = gskOutputPixelStride[mTexelFormat];
|
||||
if (mHasPalettes && (mPaletteFormat == ePalette_RGB5A3))
|
||||
PixelStride = 4;
|
||||
|
||||
// With CMPR, we're using a little trick.
|
||||
// CMPR stores pixels in 8x8 blocks, with four 4x4 subblocks.
|
||||
// An easy way to convert it is to pretend each block is 2x2 and each subblock is one pixel.
|
||||
// So to do that we need to calculate the "new" dimensions of the image, 1/4 the size of the original.
|
||||
if (mTexelFormat == eGX_CMPR) {
|
||||
MipW /= 4;
|
||||
MipH /= 4;
|
||||
}
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
for (u32 BlockY = 0; BlockY < MipH; BlockY += BHeight)
|
||||
for (u32 BlockX = 0; BlockX < MipW; BlockX += BWidth) {
|
||||
for (u32 ImgY = BlockY; ImgY < BlockY + BHeight; ImgY++) {
|
||||
for (u32 ImgX = BlockX; ImgX < BlockX + BWidth; ImgX++)
|
||||
{
|
||||
u32 DstPos = ((ImgY * MipW) + ImgX) * PixelStride;
|
||||
Out.Seek(MipOffset + DstPos, SEEK_SET);
|
||||
|
||||
if (mTexelFormat == eGX_I4) ReadPixelsI4(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_I8) ReadPixelI8(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_IA4) ReadPixelIA4(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_IA8) ReadPixelIA8(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_C4) ReadPixelsC4(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_C8) ReadPixelC8(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_RGB565) ReadPixelRGB565(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_RGB5A3) ReadPixelRGB5A3(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_RGBA8) ReadPixelRGBA8(TXTR, Out);
|
||||
else if (mTexelFormat == eGX_CMPR) ReadSubBlockCMPR(TXTR, Out);
|
||||
|
||||
// I4 and C4 have 4bpp images, so I'm forced to read two pixels at a time.
|
||||
if ((mTexelFormat == eGX_I4) || (mTexelFormat == eGX_C4)) ImgX++;
|
||||
}
|
||||
}
|
||||
if (mTexelFormat == eGX_RGBA8) TXTR.Seek(0x20, SEEK_CUR);
|
||||
}
|
||||
|
||||
u32 MipSize = (u32) (MipW * MipH * gskPixelsToBytes[mTexelFormat]);
|
||||
if (mTexelFormat == eGX_CMPR) MipSize *= 16; // Since we're pretending the image is 1/4 its actual size, we have to multiply the size by 16 to get the correct offset
|
||||
|
||||
MipOffset += MipSize;
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
if (MipW < BWidth) MipW = BWidth;
|
||||
if (MipH < BHeight) MipH = BHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureDecoder::FullDecodeGXTexture(CInputStream& TXTR)
|
||||
{
|
||||
// Get image data size, create output buffer
|
||||
u32 ImageStart = TXTR.Tell();
|
||||
TXTR.Seek(0x0, SEEK_END);
|
||||
u32 ImageSize = TXTR.Tell() - ImageStart;
|
||||
TXTR.Seek(ImageStart, SEEK_SET);
|
||||
|
||||
mDataBufferSize = ImageSize * (32 / gskSourceBpp[mTexelFormat]);
|
||||
mpDataBuffer = new u8[mDataBufferSize];
|
||||
|
||||
CMemoryOutStream Out(mpDataBuffer, mDataBufferSize, IOUtil::SystemEndianness);
|
||||
|
||||
// Initializing more stuff before we start the mipmap loop
|
||||
u32 MipW = mWidth, MipH = mHeight;
|
||||
u32 MipOffset = 0;
|
||||
|
||||
u32 BWidth = gskBlockWidth[mTexelFormat];
|
||||
u32 BHeight = gskBlockHeight[mTexelFormat];
|
||||
|
||||
// With CMPR, we're using a little trick.
|
||||
// CMPR stores pixels in 8x8 blocks, with four 4x4 subblocks.
|
||||
// An easy way to convert it is to pretend each block is 2x2 and each subblock is one pixel.
|
||||
// So to do that we need to calculate the "new" dimensions of the image, 1/4 the size of the original.
|
||||
if (mTexelFormat == eGX_CMPR) {
|
||||
MipW /= 4;
|
||||
MipH /= 4;
|
||||
}
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
for (u32 BlockY = 0; BlockY < MipH; BlockY += BHeight)
|
||||
for (u32 BlockX = 0; BlockX < MipW; BlockX += BWidth) {
|
||||
for (u32 ImgY = BlockY; ImgY < BlockY + BHeight; ImgY++) {
|
||||
for (u32 ImgX = BlockX; ImgX < BlockX + BWidth; ImgX++)
|
||||
{
|
||||
u32 DstPos = (mTexelFormat == eGX_CMPR) ? ((ImgY * (MipW * 4)) + ImgX) * 16 : ((ImgY * MipW) + ImgX) * 4;
|
||||
Out.Seek(MipOffset + DstPos, SEEK_SET);
|
||||
|
||||
// I4/C4/CMPR require reading more than one pixel at a time
|
||||
if (mTexelFormat == eGX_I4)
|
||||
{
|
||||
u8 Byte = TXTR.ReadByte();
|
||||
Out.WriteLong( DecodePixelI4(Byte, 0).ToLongARGB() );
|
||||
Out.WriteLong( DecodePixelI4(Byte, 1).ToLongARGB() );
|
||||
}
|
||||
else if (mTexelFormat == eGX_C4)
|
||||
{
|
||||
u8 Byte = TXTR.ReadByte();
|
||||
Out.WriteLong( DecodePixelC4(Byte, 0, mPaletteInput).ToLongARGB() );
|
||||
Out.WriteLong( DecodePixelC4(Byte, 1, mPaletteInput).ToLongARGB() );
|
||||
}
|
||||
else if (mTexelFormat == eGX_CMPR) DecodeSubBlockCMPR(TXTR, Out, (u16) (MipW * 4));
|
||||
|
||||
else
|
||||
{
|
||||
CColor Pixel;
|
||||
|
||||
if (mTexelFormat == eGX_I8) Pixel = DecodePixelI8(TXTR.ReadByte());
|
||||
else if (mTexelFormat == eGX_IA4) Pixel = DecodePixelIA4(TXTR.ReadByte());
|
||||
else if (mTexelFormat == eGX_IA8) Pixel = DecodePixelIA8(TXTR.ReadShort());
|
||||
else if (mTexelFormat == eGX_C8) Pixel = DecodePixelC8(TXTR.ReadByte(), mPaletteInput);
|
||||
else if (mTexelFormat == eGX_RGB565) Pixel = DecodePixelRGB565(TXTR.ReadShort());
|
||||
else if (mTexelFormat == eGX_RGB5A3) Pixel = DecodePixelRGB5A3(TXTR.ReadShort());
|
||||
else if (mTexelFormat == eGX_RGBA8) Pixel = CColor(TXTR);
|
||||
|
||||
Out.WriteLong(Pixel.ToLongARGB());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mTexelFormat == eGX_RGBA8) TXTR.Seek(0x20, SEEK_CUR);
|
||||
}
|
||||
|
||||
u32 MipSize = MipW * MipH * 4;
|
||||
if (mTexelFormat == eGX_CMPR) MipSize *= 16;
|
||||
|
||||
MipOffset += MipSize;
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
if (MipW < BWidth) MipW = BWidth;
|
||||
if (MipH < BHeight) MipH = BHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureDecoder::DecodeDDS(CInputStream &DDS)
|
||||
{
|
||||
// Get image data size, create output buffer
|
||||
u32 ImageStart = DDS.Tell();
|
||||
DDS.Seek(0x0, SEEK_END);
|
||||
u32 ImageSize = DDS.Tell() - ImageStart;
|
||||
DDS.Seek(ImageStart, SEEK_SET);
|
||||
|
||||
mDataBufferSize = ImageSize;
|
||||
if (mDDSInfo.Format == SDDSInfo::DXT1) mDataBufferSize *= 8;
|
||||
else if (mDDSInfo.Format == SDDSInfo::RGBA) mDataBufferSize *= (32 / mDDSInfo.BitCount);
|
||||
else mDataBufferSize *= 4;
|
||||
mpDataBuffer = new u8[mDataBufferSize];
|
||||
|
||||
CMemoryOutStream Out(mpDataBuffer, mDataBufferSize, IOUtil::SystemEndianness);
|
||||
|
||||
// Initializing more stuff before we start the mipmap loop
|
||||
u32 MipW = mWidth, MipH = mHeight;
|
||||
u32 MipOffset = 0;
|
||||
|
||||
u32 BPP;
|
||||
switch (mDDSInfo.Format)
|
||||
{
|
||||
case SDDSInfo::RGBA:
|
||||
BPP = mDDSInfo.BitCount;
|
||||
break;
|
||||
case SDDSInfo::DXT1:
|
||||
BPP = 4;
|
||||
break;
|
||||
case SDDSInfo::DXT2:
|
||||
case SDDSInfo::DXT3:
|
||||
case SDDSInfo::DXT4:
|
||||
case SDDSInfo::DXT5:
|
||||
BPP = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
// For DXT* decodes we can use the same trick as CMPR
|
||||
if ((mDDSInfo.Format != SDDSInfo::RGBA) && (mDDSInfo.Format != SDDSInfo::DXT1))
|
||||
{
|
||||
MipW /= 4;
|
||||
MipH /= 4;
|
||||
}
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
// For DXT1 we can copy the image data as-is to load it
|
||||
if (mDDSInfo.Format == SDDSInfo::DXT1)
|
||||
{
|
||||
Out.Seek(MipOffset, SEEK_SET);
|
||||
u32 MipSize = MipW * MipH / 2;
|
||||
std::vector<u8> MipBuffer(MipSize);
|
||||
DDS.ReadBytes(MipBuffer.data(), MipBuffer.size());
|
||||
Out.WriteBytes(MipBuffer.data(), MipBuffer.size());
|
||||
MipOffset += MipSize;
|
||||
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
if (MipW % 4) MipW += (4 - (MipW % 4));
|
||||
if (MipH % 4) MipH += (4 - (MipH % 4));
|
||||
}
|
||||
|
||||
// Otherwise we do a full decode to RGBA8
|
||||
else
|
||||
{
|
||||
for (u32 y = 0; y < MipH; y++)
|
||||
{
|
||||
for (u32 x = 0; x < MipW; x++)
|
||||
{
|
||||
u32 OutPos = MipOffset;
|
||||
|
||||
if (mDDSInfo.Format == SDDSInfo::RGBA)
|
||||
{
|
||||
OutPos += ((y * MipW) + x) * 4;
|
||||
Out.Seek(OutPos, SEEK_SET);
|
||||
|
||||
CColor Pixel = DecodeDDSPixel(DDS);
|
||||
Out.WriteLong(Pixel.ToLongARGB());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
OutPos += ((y * (MipW * 4)) + x) * 16;
|
||||
Out.Seek(OutPos, SEEK_SET);
|
||||
|
||||
if (mDDSInfo.Format == SDDSInfo::DXT1)
|
||||
DecodeBlockBC1(DDS, Out, MipW * 4);
|
||||
else if ((mDDSInfo.Format == SDDSInfo::DXT2) || (mDDSInfo.Format == SDDSInfo::DXT3))
|
||||
DecodeBlockBC2(DDS, Out, MipW * 4);
|
||||
else if ((mDDSInfo.Format == SDDSInfo::DXT4) || (mDDSInfo.Format == SDDSInfo::DXT5))
|
||||
DecodeBlockBC3(DDS, Out, MipW * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 MipSize = (mWidth * mHeight) * 4;
|
||||
if (mDDSInfo.Format != SDDSInfo::RGBA) MipSize *= 16;
|
||||
MipOffset += MipSize;
|
||||
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDDSInfo.Format == SDDSInfo::DXT1)
|
||||
mTexelFormat = eDXT1;
|
||||
else
|
||||
mTexelFormat = eGX_RGBA8;
|
||||
}
|
||||
|
||||
// ************ READ PIXELS (PARTIAL DECODE) ************
|
||||
void CTextureDecoder::ReadPixelsI4(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
u8 px = src.ReadByte();
|
||||
dst.WriteByte(Extend4to8(px >> 4));
|
||||
dst.WriteByte(Extend4to8(px >> 4));
|
||||
dst.WriteByte(Extend4to8(px));
|
||||
dst.WriteByte(Extend4to8(px));
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelI8(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
u8 px = src.ReadByte();
|
||||
dst.WriteByte(px);
|
||||
dst.WriteByte(px);
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelIA4(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
// this can be left as-is for DDS conversion, but opengl doesn't support two components in one byte...
|
||||
u8 byte = src.ReadByte();
|
||||
u8 a = Extend4to8(byte >> 4);
|
||||
u8 l = Extend4to8(byte);
|
||||
dst.WriteShort((l << 8) | a);
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelIA8(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
dst.WriteShort(src.ReadShort());
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelsC4(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
// This isn't how C4 works, but due to the way Retro packed font textures (which use C4)
|
||||
// this is the only way to get them to decode correctly for now.
|
||||
// Commented-out code is proper C4 decoding. Dedicated font texture-decoding function
|
||||
// is probably going to be necessary in the future.
|
||||
u8 byte = src.ReadByte();
|
||||
u8 indices[2];
|
||||
indices[0] = (byte >> 4) & 0xF;
|
||||
indices[1] = byte & 0xF;
|
||||
|
||||
for (u32 i = 0; i < 2; i++)
|
||||
{
|
||||
u8 r, g, b, a;
|
||||
((indices[i] >> 3) & 0x1) ? r = 0xFF : r = 0x0;
|
||||
((indices[i] >> 2) & 0x1) ? g = 0xFF : g = 0x0;
|
||||
((indices[i] >> 1) & 0x1) ? b = 0xFF : b = 0x0;
|
||||
((indices[i] >> 0) & 0x1) ? a = 0xFF : a = 0x0;
|
||||
u32 rgba = (r << 24) | (g << 16) | (b << 8) | (a);
|
||||
dst.WriteLong(rgba);
|
||||
|
||||
/*paletteInput->Seek(indices[i] * 2, SEEK_SET);
|
||||
|
||||
if (paletteFormat == PaletteIA8) readPixelIA8(*paletteInput, dst);
|
||||
else if (paletteFormat == PaletteRGB565) readPixelRGB565(*paletteInput, dst);
|
||||
else if (paletteFormat == PaletteRGB5A3) readPixelRGB5A3(*paletteInput, dst);*/
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelC8(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
// DKCR fonts use C8 :|
|
||||
u8 index = src.ReadByte();
|
||||
|
||||
/*u8 r, g, b, a;
|
||||
((index >> 3) & 0x1) ? r = 0xFF : r = 0x0;
|
||||
((index >> 2) & 0x1) ? g = 0xFF : g = 0x0;
|
||||
((index >> 1) & 0x1) ? b = 0xFF : b = 0x0;
|
||||
((index >> 0) & 0x1) ? a = 0xFF : a = 0x0;
|
||||
u32 rgba = (r << 24) | (g << 16) | (b << 8) | (a);
|
||||
dst.WriteLong(rgba);*/
|
||||
|
||||
mPaletteInput.Seek(index * 2, SEEK_SET);
|
||||
|
||||
if (mPaletteFormat == ePalette_IA8) ReadPixelIA8(mPaletteInput, dst);
|
||||
else if (mPaletteFormat == ePalette_RGB565) ReadPixelRGB565(mPaletteInput, dst);
|
||||
else if (mPaletteFormat == ePalette_RGB5A3) ReadPixelRGB5A3(mPaletteInput, dst);
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelRGB565(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
// RGB565 can be used as-is.
|
||||
dst.WriteShort(src.ReadShort());
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelRGB5A3(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
u16 px = src.ReadShort();
|
||||
CColor c;
|
||||
|
||||
if (px & 0x8000) // RGB5
|
||||
{
|
||||
c.b = Extend5to8(px >> 10);
|
||||
c.g = Extend5to8(px >> 5);
|
||||
c.r = Extend5to8(px >> 0);
|
||||
c.a = 0xFF;
|
||||
}
|
||||
|
||||
else // RGB4A3
|
||||
{
|
||||
c.a = Extend3to8(px >> 12);
|
||||
c.b = Extend4to8(px >> 8);
|
||||
c.g = Extend4to8(px >> 4);
|
||||
c.r = Extend4to8(px >> 0);
|
||||
}
|
||||
|
||||
dst.WriteLong(c.ToLongARGB());
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadPixelRGBA8(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
u16 ar = src.ReadShort();
|
||||
src.Seek(0x1E, SEEK_CUR);
|
||||
u16 gb = src.ReadShort();
|
||||
src.Seek(-0x20, SEEK_CUR);
|
||||
u32 px = (ar << 16) | gb;
|
||||
dst.WriteLong(px);
|
||||
}
|
||||
|
||||
void CTextureDecoder::ReadSubBlockCMPR(CInputStream& src, COutputStream& dst)
|
||||
{
|
||||
dst.WriteShort(src.ReadShort());
|
||||
dst.WriteShort(src.ReadShort());
|
||||
|
||||
for (u32 byte = 0; byte < 4; byte++) {
|
||||
u8 b = src.ReadByte();
|
||||
b = ((b & 0x3) << 6) | ((b & 0xC) << 2) | ((b & 0x30) >> 2) | ((b & 0xC0) >> 6);
|
||||
dst.WriteByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
// ************ DECODE PIXELS (FULL DECODE TO RGBA8) ************
|
||||
CColor CTextureDecoder::DecodePixelI4(u8 Byte, u8 WhichPixel)
|
||||
{
|
||||
if (WhichPixel == 1) Byte >>= 4;
|
||||
u8 px = Extend4to8(Byte);
|
||||
return CColor(px, px, px, 0xFF);
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodePixelI8(u8 Byte)
|
||||
{
|
||||
return CColor(Byte, Byte, Byte, 0xFF);
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodePixelIA4(u8 Byte)
|
||||
{
|
||||
u8 Alpha = Extend4to8(Byte >> 4);
|
||||
u8 Lum = Extend4to8(Byte);
|
||||
return CColor(Lum, Lum, Lum, Alpha);
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodePixelIA8(u16 Short)
|
||||
{
|
||||
u8 Alpha = (Short >> 8) & 0xFF;
|
||||
u8 Lum = Short & 0xFF;
|
||||
return CColor(Lum, Lum, Lum, Alpha);
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodePixelC4(u8 Byte, u8 WhichPixel, CInputStream& PaletteStream)
|
||||
{
|
||||
if (WhichPixel == 1) Byte >>= 4;
|
||||
Byte &= 0xF;
|
||||
|
||||
PaletteStream.Seek(Byte * 2, SEEK_SET);
|
||||
if (mPaletteFormat == ePalette_IA8) return DecodePixelIA8(PaletteStream.ReadShort());
|
||||
else if (mPaletteFormat == ePalette_RGB565) return DecodePixelIA8(PaletteStream.ReadShort());
|
||||
else if (mPaletteFormat == ePalette_RGB5A3) return DecodePixelIA8(PaletteStream.ReadShort());
|
||||
else return CColor::skTransparentBlack;
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodePixelC8(u8 Byte, CInputStream& PaletteStream)
|
||||
{
|
||||
PaletteStream.Seek(Byte * 2, SEEK_SET);
|
||||
if (mPaletteFormat == ePalette_IA8) return DecodePixelIA8(PaletteStream.ReadShort());
|
||||
else if (mPaletteFormat == ePalette_RGB565) return DecodePixelIA8(PaletteStream.ReadShort());
|
||||
else if (mPaletteFormat == ePalette_RGB5A3) return DecodePixelIA8(PaletteStream.ReadShort());
|
||||
else return CColor::skTransparentBlack;
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodePixelRGB565(u16 Short)
|
||||
{
|
||||
u8 b = Extend5to8( (u8) (Short >> 11) );
|
||||
u8 g = Extend6to8( (u8) (Short >> 5) );
|
||||
u8 r = Extend5to8( (u8) (Short) );
|
||||
return CColor(r, g, b, 0xFF);
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodePixelRGB5A3(u16 Short)
|
||||
{
|
||||
if (Short & 0x8000) // RGB5
|
||||
{
|
||||
u8 b = Extend5to8( (u8) (Short >> 10));
|
||||
u8 g = Extend5to8( (u8) (Short >> 5));
|
||||
u8 r = Extend5to8( (u8) (Short) );
|
||||
return CColor(r, g, b, 0xFF);
|
||||
}
|
||||
|
||||
else // RGB4A3
|
||||
{
|
||||
u8 a = Extend3to8( (u8) (Short >> 12) );
|
||||
u8 b = Extend4to8( (u8) (Short >> 8) );
|
||||
u8 g = Extend4to8( (u8) (Short >> 4) );
|
||||
u8 r = Extend4to8( (u8) (Short) );
|
||||
return CColor(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureDecoder::DecodeSubBlockCMPR(CInputStream& src, COutputStream& dst, u16 Width)
|
||||
{
|
||||
CColor Palettes[4];
|
||||
u16 PaletteA = src.ReadShort();
|
||||
u16 PaletteB = src.ReadShort();
|
||||
Palettes[0] = DecodePixelRGB565(PaletteA);
|
||||
Palettes[1] = DecodePixelRGB565(PaletteB);
|
||||
|
||||
if (PaletteA > PaletteB)
|
||||
{
|
||||
Palettes[2] = (Palettes[0] * 0.666666666f) + (Palettes[1] * 0.333333333f);
|
||||
Palettes[3] = (Palettes[0] * 0.333333333f) + (Palettes[1] * 0.666666666f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Palettes[2] = (Palettes[0] * 0.5f) + (Palettes[1] * 0.5f);
|
||||
Palettes[3] = CColor::skTransparentBlack;
|
||||
}
|
||||
|
||||
for (u32 y = 0; y < 4; y++)
|
||||
{
|
||||
u8 Byte = src.ReadByte();
|
||||
|
||||
for (u32 x = 0; x < 4; x++)
|
||||
{
|
||||
u8 Shift = (u8) (6 - (x * 2));
|
||||
u8 PaletteIndex = (Byte >> Shift) & 0x3;
|
||||
CColor Pixel = Palettes[PaletteIndex];
|
||||
dst.WriteLong(Pixel.ToLongARGB());
|
||||
}
|
||||
|
||||
dst.Seek((Width - 4) * 4, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureDecoder::DecodeBlockBC1(CInputStream& src, COutputStream& dst, u32 Width)
|
||||
{
|
||||
// Very similar to the CMPR subblock function, but unfortunately a slight
|
||||
// difference in the order the pixel indices are read requires a separate function
|
||||
CColor Palettes[4];
|
||||
u16 PaletteA = src.ReadShort();
|
||||
u16 PaletteB = src.ReadShort();
|
||||
Palettes[0] = DecodePixelRGB565(PaletteA);
|
||||
Palettes[1] = DecodePixelRGB565(PaletteB);
|
||||
|
||||
if (PaletteA > PaletteB)
|
||||
{
|
||||
Palettes[2] = (Palettes[0] * 0.666666666f) + (Palettes[1] * 0.333333333f);
|
||||
Palettes[3] = (Palettes[0] * 0.333333333f) + (Palettes[1] * 0.666666666f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Palettes[2] = (Palettes[0] * 0.5f) + (Palettes[1] * 0.5f);
|
||||
Palettes[3] = CColor::skTransparentBlack;
|
||||
}
|
||||
|
||||
for (u32 y = 0; y < 4; y++)
|
||||
{
|
||||
u8 Byte = src.ReadByte();
|
||||
|
||||
for (u32 x = 0; x < 4; x++)
|
||||
{
|
||||
u8 Shift = (u8) (x * 2);
|
||||
u8 PaletteIndex = (Byte >> Shift) & 0x3;
|
||||
CColor Pixel = Palettes[PaletteIndex];
|
||||
dst.WriteLong(Pixel.ToLongARGB());
|
||||
}
|
||||
|
||||
dst.Seek((Width - 4) * 4, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureDecoder::DecodeBlockBC2(CInputStream& src, COutputStream& dst, u32 Width)
|
||||
{
|
||||
CColor CPalettes[4];
|
||||
u16 PaletteA = src.ReadShort();
|
||||
u16 PaletteB = src.ReadShort();
|
||||
CPalettes[0] = DecodePixelRGB565(PaletteA);
|
||||
CPalettes[1] = DecodePixelRGB565(PaletteB);
|
||||
|
||||
if (PaletteA > PaletteB)
|
||||
{
|
||||
CPalettes[2] = (CPalettes[0] * 0.666666666f) + (CPalettes[1] * 0.333333333f);
|
||||
CPalettes[3] = (CPalettes[0] * 0.333333333f) + (CPalettes[1] * 0.666666666f);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPalettes[2] = (CPalettes[0] * 0.5f) + (CPalettes[1] * 0.5f);
|
||||
CPalettes[3] = CColor::skTransparentBlack;
|
||||
}
|
||||
|
||||
for (u32 y = 0; y < 4; y++)
|
||||
{
|
||||
u8 Byte = src.ReadByte();
|
||||
|
||||
for (u32 x = 0; x < 4; x++)
|
||||
{
|
||||
u8 Shift = (u8) (x * 2);
|
||||
u8 PaletteIndex = (Byte >> Shift) & 0x3;
|
||||
CColor Pixel = CPalettes[PaletteIndex];
|
||||
dst.WriteLong(Pixel.ToLongARGB());
|
||||
}
|
||||
|
||||
dst.Seek((Width - 4) * 4, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureDecoder::DecodeBlockBC3(CInputStream& src, COutputStream& dst, u32 Width)
|
||||
{
|
||||
CColor Palettes[4];
|
||||
u16 PaletteA = src.ReadShort();
|
||||
u16 PaletteB = src.ReadShort();
|
||||
Palettes[0] = DecodePixelRGB565(PaletteA);
|
||||
Palettes[1] = DecodePixelRGB565(PaletteB);
|
||||
|
||||
if (PaletteA > PaletteB)
|
||||
{
|
||||
Palettes[2] = (Palettes[0] * 0.666666666f) + (Palettes[1] * 0.333333333f);
|
||||
Palettes[3] = (Palettes[0] * 0.333333333f) + (Palettes[1] * 0.666666666f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Palettes[2] = (Palettes[0] * 0.5f) + (Palettes[1] * 0.5f);
|
||||
Palettes[3] = CColor::skTransparentBlack;
|
||||
}
|
||||
|
||||
for (u32 y = 0; y < 4; y++)
|
||||
{
|
||||
u8 Byte = src.ReadByte();
|
||||
|
||||
for (u32 x = 0; x < 4; x++)
|
||||
{
|
||||
u8 Shift = (u8) (x * 2);
|
||||
u8 PaletteIndex = (Byte >> Shift) & 0x3;
|
||||
CColor Pixel = Palettes[PaletteIndex];
|
||||
dst.WriteLong(Pixel.ToLongARGB());
|
||||
}
|
||||
|
||||
dst.Seek((Width - 4) * 4, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
CColor CTextureDecoder::DecodeDDSPixel(CInputStream&)
|
||||
{
|
||||
// Not using parameter 1 (CInputStream& - DDS)
|
||||
return CColor::skWhite;
|
||||
}
|
||||
|
||||
// ************ UTILITY ************
|
||||
u8 CTextureDecoder::Extend3to8(u8 in)
|
||||
{
|
||||
in &= 0x7;
|
||||
return (in << 5) | (in << 2) | (in >> 1);
|
||||
}
|
||||
|
||||
u8 CTextureDecoder::Extend4to8(u8 in)
|
||||
{
|
||||
in &= 0xF;
|
||||
return (in << 4) | in;
|
||||
}
|
||||
|
||||
u8 CTextureDecoder::Extend5to8(u8 in)
|
||||
{
|
||||
in &= 0x1F;
|
||||
return (in << 3) | (in >> 2);
|
||||
}
|
||||
|
||||
u8 CTextureDecoder::Extend6to8(u8 in)
|
||||
{
|
||||
in &= 0x3F;
|
||||
return (in << 2) | (in >> 4);
|
||||
}
|
||||
|
||||
u32 CTextureDecoder::CalculateShiftForMask(u32 BitMask)
|
||||
{
|
||||
u32 Shift = 32;
|
||||
|
||||
while (BitMask)
|
||||
{
|
||||
BitMask <<= 1;
|
||||
Shift--;
|
||||
}
|
||||
return Shift;
|
||||
}
|
||||
|
||||
u32 CTextureDecoder::CalculateMaskBitCount(u32 BitMask)
|
||||
{
|
||||
u32 Count = 0;
|
||||
|
||||
while (BitMask)
|
||||
{
|
||||
if (BitMask & 0x1) Count++;
|
||||
BitMask >>= 1;
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
93
src/Core/Resource/Factory/CTextureDecoder.h
Normal file
93
src/Core/Resource/Factory/CTextureDecoder.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#ifndef CTEXTUREDECODER_H
|
||||
#define CTEXTUREDECODER_H
|
||||
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <Common/CColor.h>
|
||||
#include <Common/types.h>
|
||||
#include "../CTexture.h"
|
||||
#include "../ETexelFormat.h"
|
||||
|
||||
class CTextureDecoder
|
||||
{
|
||||
ETexelFormat mTexelFormat;
|
||||
u16 mWidth, mHeight;
|
||||
u32 mNumMipMaps;
|
||||
|
||||
bool mHasPalettes;
|
||||
EGXPaletteFormat mPaletteFormat;
|
||||
std::vector<u8> mPalettes;
|
||||
CMemoryInStream mPaletteInput;
|
||||
|
||||
struct SDDSInfo
|
||||
{
|
||||
enum { DXT1, DXT2, DXT3, DXT4, DXT5, RGBA } Format;
|
||||
u32 Flags;
|
||||
u32 BitCount;
|
||||
u32 RBitMask, GBitMask, BBitMask, ABitMask;
|
||||
u32 RShift, GShift, BShift, AShift;
|
||||
u32 RSize, GSize, BSize, ASize;
|
||||
} mDDSInfo;
|
||||
|
||||
u8 *mpDataBuffer;
|
||||
u32 mDataBufferSize;
|
||||
|
||||
// Private Functions
|
||||
CTextureDecoder();
|
||||
~CTextureDecoder();
|
||||
CTexture* CreateTexture();
|
||||
|
||||
// Read
|
||||
void ReadTXTR(CInputStream& TXTR);
|
||||
void ReadDDS(CInputStream& DDS);
|
||||
|
||||
// Decode
|
||||
void PartialDecodeGXTexture(CInputStream& TXTR);
|
||||
void FullDecodeGXTexture(CInputStream& TXTR);
|
||||
void DecodeDDS(CInputStream& DDS);
|
||||
|
||||
// Decode Pixels (preserve compression)
|
||||
void ReadPixelsI4(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelI8(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelIA4(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelIA8(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelsC4(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelC8(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelRGB565(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelRGB5A3(CInputStream& src, COutputStream& dst);
|
||||
void ReadPixelRGBA8(CInputStream& src, COutputStream& dst);
|
||||
void ReadSubBlockCMPR(CInputStream& src, COutputStream& dst);
|
||||
|
||||
// Decode Pixels (convert to RGBA8)
|
||||
CColor DecodePixelI4(u8 Byte, u8 WhichPixel);
|
||||
CColor DecodePixelI8(u8 Byte);
|
||||
CColor DecodePixelIA4(u8 Byte);
|
||||
CColor DecodePixelIA8(u16 Short);
|
||||
CColor DecodePixelC4(u8 Byte, u8 WhichPixel, CInputStream& PaletteStream);
|
||||
CColor DecodePixelC8(u8 Byte, CInputStream& PaletteStream);
|
||||
CColor DecodePixelRGB565(u16 Short);
|
||||
CColor DecodePixelRGB5A3(u16 Short);
|
||||
CColor DecodePixelRGBA8(CInputStream& src, COutputStream& dst);
|
||||
void DecodeSubBlockCMPR(CInputStream& src, COutputStream& dst, u16 Width);
|
||||
|
||||
void DecodeBlockBC1(CInputStream& src, COutputStream& dst, u32 Width);
|
||||
void DecodeBlockBC2(CInputStream& src, COutputStream& dst, u32 Width);
|
||||
void DecodeBlockBC3(CInputStream& src, COutputStream& dst, u32 Width);
|
||||
CColor DecodeDDSPixel(CInputStream& DDS);
|
||||
|
||||
// Static
|
||||
public:
|
||||
static CTexture* LoadTXTR(CInputStream& TXTR);
|
||||
static CTexture* LoadDDS(CInputStream& DDS);
|
||||
static CTexture* DoFullDecode(CInputStream& TXTR);
|
||||
static CTexture* DoFullDecode(CTexture *pTexture);
|
||||
|
||||
// Utility
|
||||
static u8 Extend3to8(u8 in);
|
||||
static u8 Extend4to8(u8 in);
|
||||
static u8 Extend5to8(u8 in);
|
||||
static u8 Extend6to8(u8 in);
|
||||
static u32 CalculateShiftForMask(u32 BitMask);
|
||||
static u32 CalculateMaskBitCount(u32 BitMask);
|
||||
};
|
||||
|
||||
#endif // CTEXTUREDECODER_H
|
||||
329
src/Core/Resource/Factory/CWorldLoader.cpp
Normal file
329
src/Core/Resource/Factory/CWorldLoader.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
#include "CWorldLoader.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
#include <iostream>
|
||||
|
||||
CWorldLoader::CWorldLoader()
|
||||
{
|
||||
}
|
||||
|
||||
void CWorldLoader::LoadPrimeMLVL(CInputStream& MLVL)
|
||||
{
|
||||
/**
|
||||
* This function loads MLVL files from Prime 1/2
|
||||
* Corruption isn't too different, but having to check for it on every file ID is obnoxious
|
||||
* We start immediately after the "version" value (0x8 in the file)
|
||||
*/
|
||||
// Header
|
||||
if (mVersion < eCorruptionProto)
|
||||
{
|
||||
mpWorld->mpWorldName = gResCache.GetResource(MLVL.ReadLong(), "STRG");
|
||||
if (mVersion == eEchoes) mpWorld->mpDarkWorldName = gResCache.GetResource(MLVL.ReadLong(), "STRG");
|
||||
if (mVersion > ePrime) mpWorld->mUnknown1 = MLVL.ReadLong();
|
||||
if (mVersion >= ePrime) mpWorld->mpSaveWorld = gResCache.GetResource(MLVL.ReadLong(), "SAVW");
|
||||
mpWorld->mpDefaultSkybox = gResCache.GetResource(MLVL.ReadLong(), "CMDL");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
mpWorld->mpWorldName = gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
|
||||
MLVL.Seek(0x4, SEEK_CUR); // Skipping unknown value
|
||||
mpWorld->mpSaveWorld = gResCache.GetResource(MLVL.ReadLongLong(), "SAVW");
|
||||
mpWorld->mpDefaultSkybox = gResCache.GetResource(MLVL.ReadLongLong(), "CMDL");
|
||||
}
|
||||
|
||||
// Memory relays - only in MP1
|
||||
if (mVersion == ePrime)
|
||||
{
|
||||
u32 NumMemoryRelays = MLVL.ReadLong();
|
||||
mpWorld->mMemoryRelays.reserve(NumMemoryRelays);
|
||||
|
||||
for (u32 iMem = 0; iMem < NumMemoryRelays; iMem++)
|
||||
{
|
||||
CWorld::SMemoryRelay MemRelay;
|
||||
MemRelay.InstanceID = MLVL.ReadLong();
|
||||
MemRelay.TargetID = MLVL.ReadLong();
|
||||
MemRelay.Message = MLVL.ReadShort();
|
||||
MemRelay.Unknown = MLVL.ReadByte();
|
||||
mpWorld->mMemoryRelays.push_back(MemRelay);
|
||||
}
|
||||
}
|
||||
|
||||
// Areas - here's the real meat of the file
|
||||
u32 NumAreas = MLVL.ReadLong();
|
||||
if (mVersion == ePrime) mpWorld->mUnknownAreas = MLVL.ReadLong();
|
||||
mpWorld->mAreas.resize(NumAreas);
|
||||
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
// Area header
|
||||
CWorld::SArea *pArea = &mpWorld->mAreas[iArea];
|
||||
|
||||
if (mVersion < eCorruptionProto)
|
||||
pArea->pAreaName = gResCache.GetResource(MLVL.ReadLong(), "STRG");
|
||||
else
|
||||
pArea->pAreaName = gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
|
||||
|
||||
pArea->Transform = CTransform4f(MLVL);
|
||||
pArea->AetherBox = CAABox(MLVL);
|
||||
|
||||
if (mVersion < eCorruptionProto)
|
||||
{
|
||||
pArea->FileID = MLVL.ReadLong() & 0xFFFFFFFF; // This is the MREA ID; not actually loading it for obvious reasons
|
||||
pArea->AreaID = MLVL.ReadLong() & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
pArea->FileID = MLVL.ReadLongLong();
|
||||
pArea->AreaID = MLVL.ReadLongLong();
|
||||
}
|
||||
|
||||
// Attached areas
|
||||
u32 NumAttachedAreas = MLVL.ReadLong();
|
||||
pArea->AttachedAreaIDs.reserve(NumAttachedAreas);
|
||||
for (u32 iAttached = 0; iAttached < NumAttachedAreas; iAttached++)
|
||||
pArea->AttachedAreaIDs.push_back( MLVL.ReadShort() );
|
||||
|
||||
if (mVersion < eCorruptionProto)
|
||||
MLVL.Seek(0x4, SEEK_CUR); // Skipping unknown value (always 0)
|
||||
|
||||
// Depedencies
|
||||
if (mVersion < eCorruptionProto)
|
||||
{
|
||||
u32 NumDependencies = MLVL.ReadLong();
|
||||
pArea->Dependencies.reserve(NumDependencies);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumDependencies; iDep++)
|
||||
{
|
||||
SDependency Dependency;
|
||||
Dependency.ResID = MLVL.ReadLong() & 0xFFFFFFFF;
|
||||
Dependency.ResType = MLVL.ReadLong();
|
||||
pArea->Dependencies.push_back(Dependency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependency offsets - indicates an offset into the dependency list where each layer's dependencies start
|
||||
* The count is the layer count + 1 because the last offset is for common dependencies, like terrain textures
|
||||
*/
|
||||
u32 NumDependencyOffsets = MLVL.ReadLong();
|
||||
pArea->Layers.resize(NumDependencyOffsets - 1);
|
||||
|
||||
for (u32 iOff = 0; iOff < NumDependencyOffsets; iOff++)
|
||||
{
|
||||
u32 *Target;
|
||||
if (iOff == NumDependencyOffsets - 1) Target = &pArea->CommonDependenciesStart;
|
||||
else Target = &pArea->Layers[iOff].LayerDependenciesStart;
|
||||
|
||||
*Target = MLVL.ReadLong();
|
||||
}
|
||||
}
|
||||
|
||||
// Docks
|
||||
u32 NumDocks = MLVL.ReadLong();
|
||||
pArea->Docks.resize(NumDocks);
|
||||
|
||||
for (u32 iDock = 0; iDock < NumDocks; iDock++)
|
||||
{
|
||||
u32 NumConnectingDocks = MLVL.ReadLong();
|
||||
|
||||
CWorld::SArea::SDock* pDock = &pArea->Docks[iDock];
|
||||
pDock->ConnectingDocks.reserve(NumConnectingDocks);
|
||||
|
||||
for (u32 iConnect = 0; iConnect < NumConnectingDocks; iConnect++)
|
||||
{
|
||||
CWorld::SArea::SDock::SConnectingDock ConnectingDock;
|
||||
ConnectingDock.AreaIndex = MLVL.ReadLong();
|
||||
ConnectingDock.DockIndex = MLVL.ReadLong();
|
||||
pDock->ConnectingDocks.push_back(ConnectingDock);
|
||||
}
|
||||
|
||||
u32 NumCoordinates = MLVL.ReadLong();
|
||||
if (NumCoordinates != 4) std::cout << "\rError: Dock coordinate count not 4\n";
|
||||
|
||||
for (u32 iCoord = 0; iCoord < NumCoordinates; iCoord++)
|
||||
pDock->DockCoordinates[iCoord] = CVector3f(MLVL);
|
||||
}
|
||||
|
||||
// Rels
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
u32 NumRels = MLVL.ReadLong();
|
||||
pArea->RelFilenames.resize(NumRels);
|
||||
|
||||
for (u32 iRel = 0; iRel < NumRels; iRel++)
|
||||
pArea->RelFilenames[iRel] = MLVL.ReadString();
|
||||
|
||||
u32 NumRelOffsets = MLVL.ReadLong(); // Don't know what these offsets correspond to
|
||||
pArea->RelOffsets.resize(NumRelOffsets);
|
||||
|
||||
for (u32 iOff = 0; iOff < NumRelOffsets; iOff++)
|
||||
pArea->RelOffsets[iOff] = MLVL.ReadLong();
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (mVersion >= eEchoes)
|
||||
pArea->InternalName = MLVL.ReadString();
|
||||
}
|
||||
|
||||
// MapWorld
|
||||
if (mVersion < eCorruptionProto)
|
||||
mpWorld->mpMapWorld = gResCache.GetResource(MLVL.ReadLong(), "MAPW");
|
||||
else
|
||||
mpWorld->mpMapWorld = gResCache.GetResource(MLVL.ReadLongLong(), "MAPW");
|
||||
MLVL.Seek(0x5, SEEK_CUR); // Unknown values which are always 0
|
||||
|
||||
// AudioGrps
|
||||
if (mVersion == ePrime)
|
||||
{
|
||||
u32 NumAudioGrps = MLVL.ReadLong();
|
||||
mpWorld->mAudioGrps.reserve(NumAudioGrps);
|
||||
|
||||
for (u32 iGrp = 0; iGrp < NumAudioGrps; iGrp++)
|
||||
{
|
||||
CWorld::SAudioGrp AudioGrp;
|
||||
AudioGrp.Unknown = MLVL.ReadLong();
|
||||
AudioGrp.ResID = MLVL.ReadLong() & 0xFFFFFFFF;
|
||||
mpWorld->mAudioGrps.push_back(AudioGrp);
|
||||
}
|
||||
|
||||
MLVL.Seek(0x1, SEEK_CUR); // Unknown values which are always 0
|
||||
}
|
||||
|
||||
// Layer flags
|
||||
MLVL.Seek(0x4, SEEK_CUR); // Skipping redundant area count
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
CWorld::SArea* pArea = &mpWorld->mAreas[iArea];
|
||||
u32 NumLayers = MLVL.ReadLong();
|
||||
if (NumLayers != pArea->Layers.size()) pArea->Layers.resize(NumLayers);
|
||||
|
||||
u64 LayerFlags = MLVL.ReadLongLong();
|
||||
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
|
||||
pArea->Layers[iLayer].EnabledByDefault = (((LayerFlags >> iLayer) & 0x1) == 1);
|
||||
}
|
||||
|
||||
// Layer names
|
||||
MLVL.Seek(0x4, SEEK_CUR); // Skipping redundant layer count
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
CWorld::SArea* pArea = &mpWorld->mAreas[iArea];
|
||||
u32 NumLayers = pArea->Layers.size();
|
||||
|
||||
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
|
||||
pArea->Layers[iLayer].LayerName = MLVL.ReadString();
|
||||
}
|
||||
|
||||
// Last part of the file is layer name offsets, but we don't need it
|
||||
// todo: Layer ID support for MP3
|
||||
}
|
||||
|
||||
void CWorldLoader::LoadReturnsMLVL(CInputStream& MLVL)
|
||||
{
|
||||
mpWorld->mpWorldName = gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
|
||||
|
||||
bool Check = (MLVL.ReadByte() != 0);
|
||||
if (Check)
|
||||
{
|
||||
MLVL.ReadString();
|
||||
MLVL.Seek(0x10, SEEK_CUR);
|
||||
}
|
||||
|
||||
mpWorld->mpSaveWorld = gResCache.GetResource(MLVL.ReadLongLong(), "SAVW");
|
||||
mpWorld->mpDefaultSkybox = gResCache.GetResource(MLVL.ReadLongLong(), "CMDL");
|
||||
|
||||
// Areas
|
||||
u32 NumAreas = MLVL.ReadLong();
|
||||
mpWorld->mAreas.resize(NumAreas);
|
||||
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
// Area header
|
||||
CWorld::SArea *pArea = &mpWorld->mAreas[iArea];
|
||||
|
||||
pArea->pAreaName = gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
|
||||
pArea->Transform = CTransform4f(MLVL);
|
||||
pArea->AetherBox = CAABox(MLVL);
|
||||
pArea->FileID = MLVL.ReadLongLong();
|
||||
pArea->AreaID = MLVL.ReadLongLong();
|
||||
|
||||
MLVL.Seek(0x4, SEEK_CUR);
|
||||
pArea->InternalName = MLVL.ReadString();
|
||||
}
|
||||
|
||||
// Layer flags
|
||||
MLVL.Seek(0x4, SEEK_CUR); // Skipping redundant area count
|
||||
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
CWorld::SArea* pArea = &mpWorld->mAreas[iArea];
|
||||
u32 NumLayers = MLVL.ReadLong();
|
||||
pArea->Layers.resize(NumLayers);
|
||||
|
||||
u64 LayerFlags = MLVL.ReadLongLong();
|
||||
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
|
||||
pArea->Layers[iLayer].EnabledByDefault = (((LayerFlags >> iLayer) & 0x1) == 1);
|
||||
}
|
||||
|
||||
// Layer names
|
||||
MLVL.Seek(0x4, SEEK_CUR); // Skipping redundant layer count
|
||||
for (u32 iArea = 0; iArea < NumAreas; iArea++)
|
||||
{
|
||||
CWorld::SArea* pArea = &mpWorld->mAreas[iArea];
|
||||
u32 NumLayers = pArea->Layers.size();
|
||||
|
||||
for (u32 iLayer = 0; iLayer < NumLayers; iLayer++)
|
||||
pArea->Layers[iLayer].LayerName = MLVL.ReadString();
|
||||
}
|
||||
|
||||
// Last part of the file is layer name offsets, but we don't need it
|
||||
// todo: Layer ID support
|
||||
}
|
||||
|
||||
CWorld* CWorldLoader::LoadMLVL(CInputStream& MLVL)
|
||||
{
|
||||
if (!MLVL.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + MLVL.GetSourceString());
|
||||
|
||||
u32 Magic = MLVL.ReadLong();
|
||||
if (Magic != 0xDEAFBABE)
|
||||
{
|
||||
Log::FileError(MLVL.GetSourceString(), "Invalid MLVL magic: " + TString::HexString(Magic));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 FileVersion = MLVL.ReadLong();
|
||||
EGame Version = GetFormatVersion(FileVersion);
|
||||
if (Version == eUnknownVersion)
|
||||
{
|
||||
Log::FileError(MLVL.GetSourceString(), "Unsupported MLVL version: " + TString::HexString(FileVersion));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Filestream is valid, magic+version are valid; everything seems good!
|
||||
CWorldLoader Loader;
|
||||
Loader.mpWorld = new CWorld();
|
||||
Loader.mpWorld->mWorldVersion = Version;
|
||||
Loader.mVersion = Version;
|
||||
|
||||
if (Version != eReturns)
|
||||
Loader.LoadPrimeMLVL(MLVL);
|
||||
else
|
||||
Loader.LoadReturnsMLVL(MLVL);
|
||||
|
||||
return Loader.mpWorld;
|
||||
}
|
||||
|
||||
EGame CWorldLoader::GetFormatVersion(u32 Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case 0xD: return ePrimeDemo;
|
||||
case 0x11: return ePrime;
|
||||
case 0x14: return eEchoesDemo;
|
||||
case 0x17: return eEchoes;
|
||||
case 0x19: return eCorruption;
|
||||
case 0x1B: return eReturns;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
23
src/Core/Resource/Factory/CWorldLoader.h
Normal file
23
src/Core/Resource/Factory/CWorldLoader.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef CWORLDLOADER_H
|
||||
#define CWORLDLOADER_H
|
||||
|
||||
#include "../CWorld.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
class CWorldLoader
|
||||
{
|
||||
TResPtr<CWorld> mpWorld;
|
||||
EGame mVersion;
|
||||
|
||||
CWorldLoader();
|
||||
void LoadPrimeMLVL(CInputStream& MLVL);
|
||||
void LoadReturnsMLVL(CInputStream& MLVL);
|
||||
|
||||
public:
|
||||
static CWorld* LoadMLVL(CInputStream& MLVL);
|
||||
static EGame GetFormatVersion(u32 Version);
|
||||
};
|
||||
|
||||
#endif // CWORLDLOADER_H
|
||||
52
src/Core/Resource/Model/CBasicModel.cpp
Normal file
52
src/Core/Resource/Model/CBasicModel.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "CBasicModel.h"
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
CBasicModel::CBasicModel() : CResource()
|
||||
{
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
mBuffered = false;
|
||||
}
|
||||
|
||||
CBasicModel::~CBasicModel()
|
||||
{
|
||||
if (mHasOwnSurfaces)
|
||||
for (u32 iSurf = 0; iSurf < mSurfaces.size(); iSurf++)
|
||||
delete mSurfaces[iSurf];
|
||||
}
|
||||
|
||||
u32 CBasicModel::GetVertexCount()
|
||||
{
|
||||
return mVertexCount;
|
||||
}
|
||||
|
||||
u32 CBasicModel::GetTriangleCount()
|
||||
{
|
||||
return mTriangleCount;
|
||||
}
|
||||
|
||||
CAABox CBasicModel::AABox()
|
||||
{
|
||||
return mAABox;
|
||||
}
|
||||
|
||||
bool CBasicModel::IsBuffered()
|
||||
{
|
||||
return mBuffered;
|
||||
}
|
||||
|
||||
u32 CBasicModel::GetSurfaceCount()
|
||||
{
|
||||
return mSurfaces.size();
|
||||
}
|
||||
|
||||
CAABox CBasicModel::GetSurfaceAABox(u32 Surface)
|
||||
{
|
||||
return mSurfaces[Surface]->AABox;
|
||||
}
|
||||
|
||||
SSurface* CBasicModel::GetSurface(u32 Surface)
|
||||
{
|
||||
return mSurfaces[Surface];
|
||||
}
|
||||
37
src/Core/Resource/Model/CBasicModel.h
Normal file
37
src/Core/Resource/Model/CBasicModel.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef CBASICMODEL_H
|
||||
#define CBASICMODEL_H
|
||||
|
||||
#include "../CResource.h"
|
||||
#include "SSurface.h"
|
||||
#include <Common/CAABox.h>
|
||||
#include <OpenGL/CVertexBuffer.h>
|
||||
|
||||
class CBasicModel : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eModel)
|
||||
protected:
|
||||
CAABox mAABox;
|
||||
u32 mVertexCount;
|
||||
u32 mTriangleCount;
|
||||
bool mBuffered;
|
||||
bool mHasOwnMaterials;
|
||||
bool mHasOwnSurfaces;
|
||||
|
||||
CVertexBuffer mVBO;
|
||||
std::vector<SSurface*> mSurfaces;
|
||||
|
||||
public:
|
||||
CBasicModel();
|
||||
~CBasicModel();
|
||||
|
||||
u32 GetVertexCount();
|
||||
u32 GetTriangleCount();
|
||||
CAABox AABox();
|
||||
bool IsBuffered();
|
||||
u32 GetSurfaceCount();
|
||||
CAABox GetSurfaceAABox(u32 Surface);
|
||||
SSurface* GetSurface(u32 Surface);
|
||||
virtual void ClearGLBuffer() = 0;
|
||||
};
|
||||
|
||||
#endif // CBASICMODEL_H
|
||||
209
src/Core/Resource/Model/CModel.cpp
Normal file
209
src/Core/Resource/Model/CModel.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#include "CModel.h"
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <OpenGL/GLCommon.h>
|
||||
|
||||
CModel::CModel() : CBasicModel()
|
||||
{
|
||||
mHasOwnMaterials = true;
|
||||
mHasOwnSurfaces = true;
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
}
|
||||
|
||||
CModel::CModel(CMaterialSet *pSet, bool ownsMatSet)
|
||||
{
|
||||
mHasOwnMaterials = ownsMatSet;
|
||||
mHasOwnSurfaces = true;
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
|
||||
mMaterialSets.resize(1);
|
||||
mMaterialSets[0] = pSet;
|
||||
}
|
||||
|
||||
CModel::~CModel()
|
||||
{
|
||||
if (mHasOwnMaterials)
|
||||
for (u32 m = 0; m < mMaterialSets.size(); m++)
|
||||
delete mMaterialSets[m];
|
||||
}
|
||||
|
||||
void CModel::BufferGL()
|
||||
{
|
||||
mVBO.Clear();
|
||||
mSubmeshIndexBuffers.clear();
|
||||
|
||||
mSubmeshIndexBuffers.resize(mSurfaces.size());
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mSurfaces.size(); iSurf++)
|
||||
{
|
||||
SSurface *pSurf = mSurfaces[iSurf];
|
||||
|
||||
u16 VBOStartOffset = (u16) mVBO.Size();
|
||||
mVBO.Reserve((u16) pSurf->VertexCount);
|
||||
|
||||
for (u32 iPrim = 0; iPrim < pSurf->Primitives.size(); iPrim++)
|
||||
{
|
||||
SSurface::SPrimitive *pPrim = &pSurf->Primitives[iPrim];
|
||||
CIndexBuffer *pIBO = InternalGetIBO(iSurf, pPrim->Type);
|
||||
pIBO->Reserve(pPrim->Vertices.size() + 1); // Allocate enough space for this primitive, plus the restart index
|
||||
|
||||
std::vector<u16> Indices(pPrim->Vertices.size());
|
||||
for (u32 iVert = 0; iVert < pPrim->Vertices.size(); iVert++)
|
||||
Indices[iVert] = mVBO.AddIfUnique(pPrim->Vertices[iVert], VBOStartOffset);
|
||||
|
||||
// then add the indices to the IBO. We convert some primitives to strips to minimize draw calls.
|
||||
switch (pPrim->Type) {
|
||||
case eGX_Triangles:
|
||||
pIBO->TrianglesToStrips(Indices.data(), Indices.size());
|
||||
break;
|
||||
case eGX_TriangleFan:
|
||||
pIBO->FansToStrips(Indices.data(), Indices.size());
|
||||
break;
|
||||
case eGX_Quads:
|
||||
pIBO->QuadsToStrips(Indices.data(), Indices.size());
|
||||
break;
|
||||
default:
|
||||
pIBO->AddIndices(Indices.data(), Indices.size());
|
||||
pIBO->AddIndex(0xFFFF); // primitive restart
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mBuffered = true;
|
||||
}
|
||||
|
||||
void CModel::ClearGLBuffer()
|
||||
{
|
||||
mVBO.Clear();
|
||||
mSubmeshIndexBuffers.clear();
|
||||
mBuffered = false;
|
||||
}
|
||||
|
||||
void CModel::Draw(ERenderOptions Options, u32 MatSet)
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
for (u32 iSurf = 0; iSurf < mSurfaces.size(); iSurf++)
|
||||
DrawSurface(Options, iSurf, MatSet);
|
||||
}
|
||||
|
||||
void CModel::DrawSurface(ERenderOptions Options, u32 Surface, u32 MatSet)
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
// Check that mat set index is valid
|
||||
if (MatSet >= mMaterialSets.size())
|
||||
MatSet = mMaterialSets.size() - 1;
|
||||
|
||||
// Bind material
|
||||
if ((Options & eNoMaterialSetup) == 0)
|
||||
{
|
||||
SSurface *pSurf = mSurfaces[Surface];
|
||||
CMaterial *pMat = mMaterialSets[MatSet]->MaterialByIndex(pSurf->MaterialID);
|
||||
|
||||
if ((!(Options & eEnableOccluders)) && (pMat->Options() & CMaterial::eOccluder))
|
||||
return;
|
||||
|
||||
pMat->SetCurrent(Options);
|
||||
}
|
||||
|
||||
// Draw IBOs
|
||||
mVBO.Bind();
|
||||
glLineWidth(1.f);
|
||||
|
||||
for (u32 iIBO = 0; iIBO < mSubmeshIndexBuffers[Surface].size(); iIBO++)
|
||||
{
|
||||
CIndexBuffer *pIBO = &mSubmeshIndexBuffers[Surface][iIBO];
|
||||
pIBO->DrawElements();
|
||||
}
|
||||
|
||||
mVBO.Unbind();
|
||||
}
|
||||
|
||||
void CModel::DrawWireframe(ERenderOptions Options, CColor WireColor /*= CColor::skWhite*/)
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
// Set up wireframe
|
||||
WireColor.a = 0;
|
||||
CDrawUtil::UseColorShader(WireColor);
|
||||
Options |= eNoMaterialSetup;
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
|
||||
// Draw surfaces
|
||||
for (u32 iSurf = 0; iSurf < mSurfaces.size(); iSurf++)
|
||||
DrawSurface(Options, iSurf, 0);
|
||||
|
||||
// Cleanup
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
}
|
||||
|
||||
u32 CModel::GetMatSetCount()
|
||||
{
|
||||
return mMaterialSets.size();
|
||||
}
|
||||
|
||||
u32 CModel::GetMatCount()
|
||||
{
|
||||
if (mMaterialSets.empty()) return 0;
|
||||
else return mMaterialSets[0]->NumMaterials();
|
||||
}
|
||||
|
||||
CMaterialSet* CModel::GetMatSet(u32 MatSet)
|
||||
{
|
||||
return mMaterialSets[MatSet];
|
||||
}
|
||||
|
||||
CMaterial* CModel::GetMaterialByIndex(u32 MatSet, u32 Index)
|
||||
{
|
||||
if (MatSet >= mMaterialSets.size())
|
||||
MatSet = mMaterialSets.size() - 1;
|
||||
|
||||
if (GetMatCount() == 0)
|
||||
return nullptr;
|
||||
|
||||
return mMaterialSets[MatSet]->MaterialByIndex(Index);
|
||||
}
|
||||
|
||||
CMaterial* CModel::GetMaterialBySurface(u32 MatSet, u32 Surface)
|
||||
{
|
||||
return GetMaterialByIndex(MatSet, mSurfaces[Surface]->MaterialID);
|
||||
}
|
||||
|
||||
bool CModel::HasTransparency(u32 MatSet)
|
||||
{
|
||||
if (MatSet >= mMaterialSets.size())
|
||||
MatSet = mMaterialSets.size() - 1;
|
||||
|
||||
for (u32 iMat = 0; iMat < mMaterialSets[MatSet]->NumMaterials(); iMat++)
|
||||
if (mMaterialSets[MatSet]->MaterialByIndex(iMat)->Options() & CMaterial::eTransparent ) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CModel::IsSurfaceTransparent(u32 Surface, u32 MatSet)
|
||||
{
|
||||
if (MatSet >= mMaterialSets.size())
|
||||
MatSet = mMaterialSets.size() - 1;
|
||||
|
||||
u32 matID = mSurfaces[Surface]->MaterialID;
|
||||
return (mMaterialSets[MatSet]->MaterialByIndex(matID)->Options() & CMaterial::eTransparent) != 0;
|
||||
}
|
||||
|
||||
CIndexBuffer* CModel::InternalGetIBO(u32 Surface, EGXPrimitiveType Primitive)
|
||||
{
|
||||
std::vector<CIndexBuffer> *pIBOs = &mSubmeshIndexBuffers[Surface];
|
||||
GLenum Type = GXPrimToGLPrim(Primitive);
|
||||
|
||||
for (u32 iIBO = 0; iIBO < pIBOs->size(); iIBO++)
|
||||
{
|
||||
if ((*pIBOs)[iIBO].GetPrimitiveType() == Type)
|
||||
return &(*pIBOs)[iIBO];
|
||||
}
|
||||
|
||||
pIBOs->emplace_back(CIndexBuffer(Type));
|
||||
return &pIBOs->back();
|
||||
}
|
||||
44
src/Core/Resource/Model/CModel.h
Normal file
44
src/Core/Resource/Model/CModel.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef CMODEL_H
|
||||
#define CMODEL_H
|
||||
|
||||
#include "CBasicModel.h"
|
||||
#include "SSurface.h"
|
||||
#include "../CMaterialSet.h"
|
||||
|
||||
#include <Core/ERenderOptions.h>
|
||||
#include <OpenGL/CIndexBuffer.h>
|
||||
#include <OpenGL/GLCommon.h>
|
||||
|
||||
class CModel : public CBasicModel
|
||||
{
|
||||
friend class CModelLoader;
|
||||
friend class CModelCooker;
|
||||
|
||||
std::vector<CMaterialSet*> mMaterialSets;
|
||||
std::vector<std::vector<CIndexBuffer>> mSubmeshIndexBuffers;
|
||||
bool mHasOwnMaterials;
|
||||
|
||||
public:
|
||||
CModel();
|
||||
CModel(CMaterialSet *pSet, bool ownsMatSet);
|
||||
~CModel();
|
||||
|
||||
void BufferGL();
|
||||
void ClearGLBuffer();
|
||||
void Draw(ERenderOptions Options, u32 MatSet);
|
||||
void DrawSurface(ERenderOptions Options, u32 Surface, u32 MatSet);
|
||||
void DrawWireframe(ERenderOptions Options, CColor WireColor = CColor::skWhite);
|
||||
|
||||
u32 GetMatSetCount();
|
||||
u32 GetMatCount();
|
||||
CMaterialSet* GetMatSet(u32 MatSet);
|
||||
CMaterial* GetMaterialByIndex(u32 MatSet, u32 Index);
|
||||
CMaterial* GetMaterialBySurface(u32 MatSet, u32 Surface);
|
||||
bool HasTransparency(u32 MatSet);
|
||||
bool IsSurfaceTransparent(u32 Surface, u32 MatSet);
|
||||
|
||||
private:
|
||||
CIndexBuffer* InternalGetIBO(u32 Surface, EGXPrimitiveType Primitive);
|
||||
};
|
||||
|
||||
#endif // MODEL_H
|
||||
199
src/Core/Resource/Model/CStaticModel.cpp
Normal file
199
src/Core/Resource/Model/CStaticModel.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "CStaticModel.h"
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <OpenGL/GLCommon.h>
|
||||
|
||||
CStaticModel::CStaticModel() : CBasicModel()
|
||||
{
|
||||
mpMaterial = nullptr;
|
||||
mTransparent = false;
|
||||
mHasOwnSurfaces = false;
|
||||
mHasOwnMaterials = false;
|
||||
}
|
||||
|
||||
CStaticModel::CStaticModel(CMaterial *pMat) : CBasicModel()
|
||||
{
|
||||
mpMaterial = pMat;
|
||||
mTransparent = ((pMat->Options() & CMaterial::eTransparent) != 0);
|
||||
mHasOwnSurfaces = false;
|
||||
mHasOwnMaterials = false;
|
||||
}
|
||||
|
||||
CStaticModel::~CStaticModel()
|
||||
{
|
||||
}
|
||||
|
||||
void CStaticModel::AddSurface(SSurface *pSurface)
|
||||
{
|
||||
mSurfaces.push_back(pSurface);
|
||||
mAABox.ExpandBounds(pSurface->AABox);
|
||||
|
||||
mVertexCount += pSurface->VertexCount;
|
||||
mTriangleCount += pSurface->TriangleCount;
|
||||
}
|
||||
|
||||
void CStaticModel::BufferGL()
|
||||
{
|
||||
mVBO.Clear();
|
||||
mIBOs.clear();
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mSurfaces.size(); iSurf++)
|
||||
{
|
||||
SSurface *pSurf = mSurfaces[iSurf];
|
||||
|
||||
u16 VBOStartOffset = (u16) mVBO.Size();
|
||||
mVBO.Reserve((u16) pSurf->VertexCount);
|
||||
|
||||
for (u32 iPrim = 0; iPrim < pSurf->Primitives.size(); iPrim++)
|
||||
{
|
||||
SSurface::SPrimitive *pPrim = &pSurf->Primitives[iPrim];
|
||||
CIndexBuffer *pIBO = InternalGetIBO(pPrim->Type);
|
||||
pIBO->Reserve(pPrim->Vertices.size() + 1); // Allocate enough space for this primitive, plus the restart index
|
||||
|
||||
// Next step: add new vertices to the VBO and create a small index buffer for the current primitive
|
||||
std::vector<u16> Indices(pPrim->Vertices.size());
|
||||
for (u32 iVert = 0; iVert < pPrim->Vertices.size(); iVert++)
|
||||
Indices[iVert] = mVBO.AddIfUnique(pPrim->Vertices[iVert], VBOStartOffset);
|
||||
|
||||
// then add the indices to the IBO. We convert some primitives to strips to minimize draw calls.
|
||||
switch (pPrim->Type) {
|
||||
case eGX_Triangles:
|
||||
pIBO->TrianglesToStrips(Indices.data(), Indices.size());
|
||||
break;
|
||||
case eGX_TriangleFan:
|
||||
pIBO->FansToStrips(Indices.data(), Indices.size());
|
||||
break;
|
||||
case eGX_Quads:
|
||||
pIBO->QuadsToStrips(Indices.data(), Indices.size());
|
||||
break;
|
||||
default:
|
||||
pIBO->AddIndices(Indices.data(), Indices.size());
|
||||
pIBO->AddIndex(0xFFFF); // primitive restart
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the number of submesh offset vectors matches the number of IBOs, then add the offsets
|
||||
while (mIBOs.size() > mSubmeshEndOffsets.size())
|
||||
mSubmeshEndOffsets.emplace_back(std::vector<u32>(mSurfaces.size()));
|
||||
|
||||
for (u32 iIBO = 0; iIBO < mIBOs.size(); iIBO++)
|
||||
mSubmeshEndOffsets[iIBO][iSurf] = mIBOs[iIBO].GetSize();
|
||||
}
|
||||
|
||||
mVBO.Buffer();
|
||||
|
||||
for (u32 iIBO = 0; iIBO < mIBOs.size(); iIBO++)
|
||||
mIBOs[iIBO].Buffer();
|
||||
|
||||
mBuffered = true;
|
||||
}
|
||||
|
||||
void CStaticModel::ClearGLBuffer()
|
||||
{
|
||||
mVBO.Clear();
|
||||
mIBOs.clear();
|
||||
mSubmeshEndOffsets.clear();
|
||||
mBuffered = false;
|
||||
}
|
||||
|
||||
void CStaticModel::Draw(ERenderOptions Options)
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
if ((Options & eNoMaterialSetup) == 0) mpMaterial->SetCurrent(Options);
|
||||
|
||||
// Draw IBOs
|
||||
mVBO.Bind();
|
||||
glLineWidth(1.f);
|
||||
|
||||
for (u32 iIBO = 0; iIBO < mIBOs.size(); iIBO++)
|
||||
{
|
||||
CIndexBuffer *pIBO = &mIBOs[iIBO];
|
||||
pIBO->Bind();
|
||||
glDrawElements(pIBO->GetPrimitiveType(), pIBO->GetSize(), GL_UNSIGNED_SHORT, (void*) 0);
|
||||
pIBO->Unbind();
|
||||
gDrawCount++;
|
||||
}
|
||||
|
||||
mVBO.Unbind();
|
||||
}
|
||||
|
||||
void CStaticModel::DrawSurface(ERenderOptions Options, u32 Surface)
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
mVBO.Bind();
|
||||
glLineWidth(1.f);
|
||||
if ((Options & eNoMaterialSetup) == 0) mpMaterial->SetCurrent(Options);
|
||||
|
||||
for (u32 iIBO = 0; iIBO < mIBOs.size(); iIBO++)
|
||||
{
|
||||
// Since there is a shared IBO for every mesh, we need two things to draw a single one: an offset and a size
|
||||
u32 Offset = 0;
|
||||
if (Surface > 0) Offset = mSubmeshEndOffsets[iIBO][Surface - 1];
|
||||
u32 Size = mSubmeshEndOffsets[iIBO][Surface] - Offset;
|
||||
|
||||
if (!Size) continue; // The chosen submesh doesn't use this IBO
|
||||
|
||||
// Now we have both, so we can draw
|
||||
mIBOs[iIBO].DrawElements(Offset, Size);
|
||||
gDrawCount++;
|
||||
}
|
||||
|
||||
mVBO.Unbind();
|
||||
}
|
||||
|
||||
void CStaticModel::DrawWireframe(ERenderOptions Options, CColor WireColor /*= CColor::skWhite*/)
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
// Set up wireframe
|
||||
WireColor.a = 0;
|
||||
CDrawUtil::UseColorShader(WireColor);
|
||||
Options |= eNoMaterialSetup;
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
|
||||
// Draw surfaces
|
||||
for (u32 iSurf = 0; iSurf < mSurfaces.size(); iSurf++)
|
||||
DrawSurface(Options, iSurf);
|
||||
|
||||
// Cleanup
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
}
|
||||
|
||||
CMaterial* CStaticModel::GetMaterial()
|
||||
{
|
||||
return mpMaterial;
|
||||
}
|
||||
|
||||
void CStaticModel::SetMaterial(CMaterial *pMat)
|
||||
{
|
||||
mpMaterial = pMat;
|
||||
mTransparent = ((pMat->Options() & CMaterial::eTransparent) != 0);
|
||||
}
|
||||
|
||||
bool CStaticModel::IsTransparent()
|
||||
{
|
||||
return mTransparent;
|
||||
}
|
||||
|
||||
bool CStaticModel::IsOccluder()
|
||||
{
|
||||
return ((mpMaterial->Options() & CMaterial::eOccluder) != 0);
|
||||
}
|
||||
|
||||
CIndexBuffer* CStaticModel::InternalGetIBO(EGXPrimitiveType Primitive)
|
||||
{
|
||||
GLenum type = GXPrimToGLPrim(Primitive);
|
||||
|
||||
for (u32 iIBO = 0; iIBO < mIBOs.size(); iIBO++)
|
||||
{
|
||||
if (mIBOs[iIBO].GetPrimitiveType() == type)
|
||||
return &mIBOs[iIBO];
|
||||
}
|
||||
|
||||
mIBOs.emplace_back(CIndexBuffer(type));
|
||||
return &mIBOs.back();
|
||||
}
|
||||
40
src/Core/Resource/Model/CStaticModel.h
Normal file
40
src/Core/Resource/Model/CStaticModel.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef CSTATICMODEL_H
|
||||
#define CSTATICMODEL_H
|
||||
|
||||
#include "CBasicModel.h"
|
||||
#include <Core/ERenderOptions.h>
|
||||
#include <OpenGL/CIndexBuffer.h>
|
||||
|
||||
// A CStaticModel is meant for meshes that don't move. It's built specifically with terrain in mind.
|
||||
// It only links to one material, and what it does best is combining submeshes from different models
|
||||
// into shared VBOs and IBOs. This allows for a significantly reduced number of draw calls.
|
||||
|
||||
class CStaticModel : public CBasicModel
|
||||
{
|
||||
CMaterial *mpMaterial;
|
||||
std::vector<CIndexBuffer> mIBOs;
|
||||
std::vector<std::vector<u32>> mSubmeshEndOffsets;
|
||||
bool mTransparent;
|
||||
|
||||
public:
|
||||
CStaticModel();
|
||||
CStaticModel(CMaterial *pMat);
|
||||
~CStaticModel();
|
||||
void AddSurface(SSurface *pSurface);
|
||||
|
||||
void BufferGL();
|
||||
void ClearGLBuffer();
|
||||
void Draw(ERenderOptions Options);
|
||||
void DrawSurface(ERenderOptions Options, u32 Surface);
|
||||
void DrawWireframe(ERenderOptions Options, CColor WireColor = CColor::skWhite);
|
||||
|
||||
CMaterial* GetMaterial();
|
||||
void SetMaterial(CMaterial *pMat);
|
||||
bool IsTransparent();
|
||||
bool IsOccluder();
|
||||
|
||||
private:
|
||||
CIndexBuffer* InternalGetIBO(EGXPrimitiveType Primitive);
|
||||
};
|
||||
|
||||
#endif // CSTATICMODEL_H
|
||||
42
src/Core/Resource/Model/CVertex.h
Normal file
42
src/Core/Resource/Model/CVertex.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef CVERTEX_H
|
||||
#define CVERTEX_H
|
||||
|
||||
#include <Common/CVector2f.h>
|
||||
#include <Common/CVector3f.h>
|
||||
#include <Common/CColor.h>
|
||||
|
||||
class CVertex
|
||||
{
|
||||
public:
|
||||
u32 ArrayPosition; // Position of this vertex in the input model file.
|
||||
// This is needed to resave without breaking rigging.
|
||||
CVector3f Position;
|
||||
CVector3f Normal;
|
||||
CColor Color[2];
|
||||
CVector2f Tex[8];
|
||||
u8 MatrixIndices[8];
|
||||
|
||||
CVertex() {}
|
||||
|
||||
CVertex(CVector3f& Pos)
|
||||
{
|
||||
Position = Pos;
|
||||
}
|
||||
|
||||
bool operator==(const CVertex& other) {
|
||||
return ((Position == other.Position) &&
|
||||
(Normal == other.Normal) &&
|
||||
(Color[0] == other.Color[0]) &&
|
||||
(Color[1] == other.Color[1]) &&
|
||||
(Tex[0] == other.Tex[0]) &&
|
||||
(Tex[1] == other.Tex[1]) &&
|
||||
(Tex[2] == other.Tex[2]) &&
|
||||
(Tex[3] == other.Tex[3]) &&
|
||||
(Tex[4] == other.Tex[4]) &&
|
||||
(Tex[5] == other.Tex[5]) &&
|
||||
(Tex[6] == other.Tex[6]) &&
|
||||
(Tex[7] == other.Tex[7]));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CVERTEX_H
|
||||
33
src/Core/Resource/Model/EVertexDescription.h
Normal file
33
src/Core/Resource/Model/EVertexDescription.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef EVERTEXDESCRIPTION
|
||||
#define EVERTEXDESCRIPTION
|
||||
|
||||
#include <Common/EnumUtil.h>
|
||||
|
||||
enum EVertexDescription
|
||||
{
|
||||
eNoAttributes = 0x0,
|
||||
ePosition = 0x3,
|
||||
eNormal = 0xC,
|
||||
eColor0 = 0x30,
|
||||
eColor1 = 0xC0,
|
||||
eTex0 = 0x300,
|
||||
eTex1 = 0xC00,
|
||||
eTex2 = 0x3000,
|
||||
eTex3 = 0xC000,
|
||||
eTex4 = 0x30000,
|
||||
eTex5 = 0xC0000,
|
||||
eTex6 = 0x300000,
|
||||
eTex7 = 0xC00000,
|
||||
ePosMtx = 0x1000000,
|
||||
eTex0Mtx = 0x2000000,
|
||||
eTex1Mtx = 0x4000000,
|
||||
eTex2Mtx = 0x8000000,
|
||||
eTex3Mtx = 0x10000000,
|
||||
eTex4Mtx = 0x20000000,
|
||||
eTex5Mtx = 0x40000000,
|
||||
eTex6Mtx = 0x80000000
|
||||
};
|
||||
DEFINE_ENUM_FLAGS(EVertexDescription)
|
||||
|
||||
#endif // EVERTEXDESCRIPTION
|
||||
|
||||
112
src/Core/Resource/Model/SSurface.cpp
Normal file
112
src/Core/Resource/Model/SSurface.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "SSurface.h"
|
||||
#include <Common/CRayCollisionTester.h>
|
||||
#include <Common/Math.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
|
||||
std::pair<bool,float> SSurface::IntersectsRay(const CRay& Ray, bool allowBackfaces, float LineThreshold)
|
||||
{
|
||||
bool Hit = false;
|
||||
float HitDist;
|
||||
|
||||
for (u32 iPrim = 0; iPrim < Primitives.size(); iPrim++)
|
||||
{
|
||||
SPrimitive *pPrim = &Primitives[iPrim];
|
||||
u32 NumVerts = pPrim->Vertices.size();
|
||||
|
||||
// Triangles
|
||||
if ((pPrim->Type == eGX_Triangles) || (pPrim->Type == eGX_TriangleFan) || (pPrim->Type == eGX_TriangleStrip))
|
||||
{
|
||||
u32 NumTris;
|
||||
|
||||
if (pPrim->Type == eGX_Triangles)
|
||||
NumTris = NumVerts / 3;
|
||||
else
|
||||
NumTris = NumVerts - 2;
|
||||
|
||||
for (u32 iTri = 0; iTri < NumTris; iTri++)
|
||||
{
|
||||
CVector3f vtxA, vtxB, vtxC;
|
||||
|
||||
// Get the three vertices that make up the current tri
|
||||
if (pPrim->Type == eGX_Triangles)
|
||||
{
|
||||
u32 VertIndex = iTri * 3;
|
||||
vtxA = pPrim->Vertices[VertIndex].Position;
|
||||
vtxB = pPrim->Vertices[VertIndex+1].Position;
|
||||
vtxC = pPrim->Vertices[VertIndex+2].Position;
|
||||
}
|
||||
|
||||
else if (pPrim->Type == eGX_TriangleFan)
|
||||
{
|
||||
vtxA = pPrim->Vertices[0].Position;
|
||||
vtxB = pPrim->Vertices[iTri+1].Position;
|
||||
vtxC = pPrim->Vertices[iTri+2].Position;
|
||||
}
|
||||
|
||||
else if (pPrim->Type = eGX_TriangleStrip)
|
||||
{
|
||||
if (iTri & 0x1)
|
||||
{
|
||||
vtxA = pPrim->Vertices[iTri+2].Position;
|
||||
vtxB = pPrim->Vertices[iTri+1].Position;
|
||||
vtxC = pPrim->Vertices[iTri].Position;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
vtxA = pPrim->Vertices[iTri].Position;
|
||||
vtxB = pPrim->Vertices[iTri+1].Position;
|
||||
vtxC = pPrim->Vertices[iTri+2].Position;
|
||||
}
|
||||
}
|
||||
|
||||
// Intersection test
|
||||
std::pair<bool,float> TriResult = Math::RayTriangleIntersection(Ray, vtxA, vtxB, vtxC, allowBackfaces);
|
||||
|
||||
if (TriResult.first)
|
||||
{
|
||||
if ((!Hit) || (TriResult.second < HitDist))
|
||||
{
|
||||
Hit = true;
|
||||
HitDist = TriResult.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lines
|
||||
if ((pPrim->Type == eGX_Lines) || (pPrim->Type == eGX_LineStrip))
|
||||
{
|
||||
u32 NumLines;
|
||||
|
||||
if (pPrim->Type == eGX_Lines)
|
||||
NumLines = NumVerts / 2;
|
||||
else
|
||||
NumLines = NumVerts - 1;
|
||||
|
||||
for (u32 iLine = 0; iLine < NumLines; iLine++)
|
||||
{
|
||||
CVector3f vtxA, vtxB;
|
||||
|
||||
// Get the two vertices that make up the current line
|
||||
u32 index = (pPrim->Type == eGX_Lines ? iLine * 2 : iLine);
|
||||
vtxA = pPrim->Vertices[index].Position;
|
||||
vtxB = pPrim->Vertices[index+1].Position;
|
||||
|
||||
// Intersection test
|
||||
std::pair<bool,float> result = Math::RayLineIntersection(Ray, vtxA, vtxB, LineThreshold);
|
||||
|
||||
if (result.first)
|
||||
{
|
||||
if ((!Hit) || (result.second < HitDist))
|
||||
{
|
||||
Hit = true;
|
||||
HitDist = result.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair<bool,float>(Hit, HitDist);
|
||||
}
|
||||
39
src/Core/Resource/Model/SSurface.h
Normal file
39
src/Core/Resource/Model/SSurface.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef SSURFACE_H
|
||||
#define SSURFACE_H
|
||||
|
||||
#include "../CMaterialSet.h"
|
||||
#include "CVertex.h"
|
||||
#include <Common/types.h>
|
||||
#include <Common/CAABox.h>
|
||||
#include <Common/CRay.h>
|
||||
#include <Common/CTransform4f.h>
|
||||
#include <Common/CVector3f.h>
|
||||
#include <Common/SRayIntersection.h>
|
||||
#include <OpenGL/GLCommon.h>
|
||||
#include <vector>
|
||||
|
||||
struct SSurface
|
||||
{
|
||||
u32 VertexCount;
|
||||
u32 TriangleCount;
|
||||
CAABox AABox;
|
||||
CVector3f CenterPoint;
|
||||
u32 MaterialID;
|
||||
CVector3f ReflectionDirection;
|
||||
|
||||
struct SPrimitive
|
||||
{
|
||||
EGXPrimitiveType Type;
|
||||
std::vector<CVertex> Vertices;
|
||||
};
|
||||
std::vector<SPrimitive> Primitives;
|
||||
|
||||
SSurface() {
|
||||
VertexCount = 0;
|
||||
TriangleCount = 0;
|
||||
}
|
||||
|
||||
std::pair<bool,float> IntersectsRay(const CRay& Ray, bool allowBackfaces = false, float LineThreshold = 0.02f);
|
||||
};
|
||||
|
||||
#endif // SSURFACE_H
|
||||
14
src/Core/Resource/SDependency.h
Normal file
14
src/Core/Resource/SDependency.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef SDEPENDENCY
|
||||
#define SDEPENDENCY
|
||||
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/types.h>
|
||||
|
||||
struct SDependency
|
||||
{
|
||||
u64 ResID;
|
||||
CFourCC ResType;
|
||||
};
|
||||
|
||||
#endif // SDEPENDENCY
|
||||
|
||||
14
src/Core/Resource/SNamedResource.h
Normal file
14
src/Core/Resource/SNamedResource.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef SNAMEDRESOURCE_H
|
||||
#define SNAMEDRESOURCE_H
|
||||
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/types.h>
|
||||
|
||||
struct SNamedResource
|
||||
{
|
||||
CFourCC resType;
|
||||
TString resName;
|
||||
u64 resID;
|
||||
};
|
||||
|
||||
#endif // SNAMEDRESOURCE_H
|
||||
16
src/Core/Resource/SResInfo.h
Normal file
16
src/Core/Resource/SResInfo.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef SRESINFO_H
|
||||
#define SRESINFO_H
|
||||
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/types.h>
|
||||
|
||||
struct SResInfo
|
||||
{
|
||||
bool compressed;
|
||||
CFourCC resType;
|
||||
u64 resID;
|
||||
u32 offset;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
#endif // SRESINFO_H
|
||||
128
src/Core/Resource/Script/CMasterTemplate.cpp
Normal file
128
src/Core/Resource/Script/CMasterTemplate.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "CMasterTemplate.h"
|
||||
#include "../factory/CWorldLoader.h"
|
||||
#include <Core/Log.h>
|
||||
|
||||
CMasterTemplate::CMasterTemplate()
|
||||
{
|
||||
mVersion = 0;
|
||||
mFullyLoaded = false;
|
||||
mHasPropList = false;
|
||||
}
|
||||
|
||||
CMasterTemplate::~CMasterTemplate()
|
||||
{
|
||||
for (auto it = mTemplates.begin(); it != mTemplates.end(); it++)
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
EGame CMasterTemplate::GetGame()
|
||||
{
|
||||
return mGame;
|
||||
}
|
||||
|
||||
CScriptTemplate* CMasterTemplate::TemplateByID(u32 ObjectID)
|
||||
{
|
||||
auto it = mTemplates.find(ObjectID);
|
||||
|
||||
if (it != mTemplates.end())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CScriptTemplate* CMasterTemplate::TemplateByID(const CFourCC& ObjectID)
|
||||
{
|
||||
return TemplateByID(ObjectID.ToLong());
|
||||
}
|
||||
|
||||
CScriptTemplate* CMasterTemplate::TemplateByIndex(u32 Index)
|
||||
{
|
||||
auto it = mTemplates.begin();
|
||||
return (std::next(it, Index))->second;
|
||||
}
|
||||
|
||||
TString CMasterTemplate::StateByID(u32 StateID)
|
||||
{
|
||||
auto it = mStates.find(StateID);
|
||||
|
||||
if (it != mStates.end())
|
||||
return it->second;
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
TString CMasterTemplate::StateByID(const CFourCC& State)
|
||||
{
|
||||
return StateByID(State.ToLong());
|
||||
}
|
||||
|
||||
TString CMasterTemplate::StateByIndex(u32 Index)
|
||||
{
|
||||
auto it = mStates.begin();
|
||||
return (std::next(it, Index))->second;
|
||||
}
|
||||
|
||||
TString CMasterTemplate::MessageByID(u32 MessageID)
|
||||
{
|
||||
auto it = mMessages.find(MessageID);
|
||||
|
||||
if (it != mMessages.end())
|
||||
return it->second;
|
||||
else
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
TString CMasterTemplate::MessageByID(const CFourCC& MessageID)
|
||||
{
|
||||
return MessageByID(MessageID.ToLong());
|
||||
}
|
||||
|
||||
TString CMasterTemplate::MessageByIndex(u32 Index)
|
||||
{
|
||||
auto it = mMessages.begin();
|
||||
return (std::next(it, Index))->second;
|
||||
}
|
||||
|
||||
CPropertyTemplate* CMasterTemplate::GetProperty(u32 PropertyID)
|
||||
{
|
||||
auto it = mPropertyList.find(PropertyID);
|
||||
|
||||
if (it != mPropertyList.end())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CMasterTemplate::HasPropertyList()
|
||||
{
|
||||
return mHasPropList;
|
||||
}
|
||||
|
||||
bool CMasterTemplate::IsLoadedSuccessfully()
|
||||
{
|
||||
return mFullyLoaded;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CMasterTemplate* CMasterTemplate::GetMasterForGame(EGame Game)
|
||||
{
|
||||
auto it = smMasterMap.find(Game);
|
||||
|
||||
if (it != smMasterMap.end())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::list<CMasterTemplate*> CMasterTemplate::GetMasterList()
|
||||
{
|
||||
std::list<CMasterTemplate*> list;
|
||||
|
||||
for (auto it = smMasterMap.begin(); it != smMasterMap.end(); it++)
|
||||
list.push_back(it->second);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
std::map<EGame, CMasterTemplate*> CMasterTemplate::smMasterMap;
|
||||
u32 CMasterTemplate::smGameListVersion;
|
||||
68
src/Core/Resource/Script/CMasterTemplate.h
Normal file
68
src/Core/Resource/Script/CMasterTemplate.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef CMASTERTEMPLATE_H
|
||||
#define CMASTERTEMPLATE_H
|
||||
|
||||
#include "CScriptTemplate.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <Common/types.h>
|
||||
#include <map>
|
||||
#include <tinyxml2.h>
|
||||
|
||||
class CMasterTemplate
|
||||
{
|
||||
friend class CTemplateLoader;
|
||||
friend class CTemplateWriter;
|
||||
|
||||
EGame mGame;
|
||||
TString mGameName;
|
||||
TString mSourceFile;
|
||||
u32 mVersion;
|
||||
bool mFullyLoaded;
|
||||
|
||||
std::map<u32, CScriptTemplate*> mTemplates;
|
||||
std::map<u32, TString> mStates;
|
||||
std::map<u32, TString> mMessages;
|
||||
|
||||
bool mHasPropList;
|
||||
std::map<u32, CPropertyTemplate*> mPropertyList;
|
||||
|
||||
static std::map<EGame, CMasterTemplate*> smMasterMap;
|
||||
static u32 smGameListVersion;
|
||||
|
||||
public:
|
||||
CMasterTemplate();
|
||||
~CMasterTemplate();
|
||||
EGame GetGame();
|
||||
u32 NumScriptTemplates();
|
||||
u32 NumStates();
|
||||
u32 NumMessages();
|
||||
CScriptTemplate* TemplateByID(u32 ObjectID);
|
||||
CScriptTemplate* TemplateByID(const CFourCC& ObjectID);
|
||||
CScriptTemplate* TemplateByIndex(u32 Index);
|
||||
TString StateByID(u32 StateID);
|
||||
TString StateByID(const CFourCC& StateID);
|
||||
TString StateByIndex(u32 Index);
|
||||
TString MessageByID(u32 MessageID);
|
||||
TString MessageByID(const CFourCC& MessageID);
|
||||
TString MessageByIndex(u32 Index);
|
||||
CPropertyTemplate* GetProperty(u32 PropertyID);
|
||||
bool HasPropertyList();
|
||||
bool IsLoadedSuccessfully();
|
||||
|
||||
static CMasterTemplate* GetMasterForGame(EGame Game);
|
||||
static std::list<CMasterTemplate*> GetMasterList();
|
||||
};
|
||||
|
||||
// ************ INLINE ************
|
||||
inline u32 CMasterTemplate::NumScriptTemplates() {
|
||||
return mTemplates.size();
|
||||
}
|
||||
|
||||
inline u32 CMasterTemplate::NumStates() {
|
||||
return mStates.size();
|
||||
}
|
||||
|
||||
inline u32 CMasterTemplate::NumMessages() {
|
||||
return mMessages.size();
|
||||
}
|
||||
|
||||
#endif // CMASTERTEMPLATE_H
|
||||
123
src/Core/Resource/Script/CProperty.cpp
Normal file
123
src/Core/Resource/Script/CProperty.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "CProperty.h"
|
||||
|
||||
// ************ CPropertyStruct ************
|
||||
CPropertyStruct::~CPropertyStruct()
|
||||
{
|
||||
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
CPropertyBase* CPropertyStruct::PropertyByIndex(u32 index)
|
||||
{
|
||||
return mProperties[index];
|
||||
}
|
||||
|
||||
CPropertyBase* CPropertyStruct::PropertyByID(u32 ID)
|
||||
{
|
||||
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
|
||||
{
|
||||
if ((*it)->ID() == ID)
|
||||
return *it;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CPropertyBase* CPropertyStruct::PropertyByIDString(const TIDString& str)
|
||||
{
|
||||
// Resolve namespace
|
||||
u32 nsStart = str.IndexOf(":");
|
||||
|
||||
// String has namespace; the requested property is within a struct
|
||||
if (nsStart != -1)
|
||||
{
|
||||
TString strStructID = str.Truncate(nsStart);
|
||||
if (!strStructID.IsHexString()) return nullptr;
|
||||
|
||||
u32 structID = strStructID.ToInt32();
|
||||
TString propName = str.ChopFront(nsStart + 1);
|
||||
|
||||
CPropertyStruct *pStruct = StructByID(structID);
|
||||
if (!pStruct) return nullptr;
|
||||
else return pStruct->PropertyByIDString(propName);
|
||||
}
|
||||
|
||||
// No namespace; fetch the property from this struct
|
||||
else
|
||||
{
|
||||
if (str.IsHexString())
|
||||
return PropertyByID(str.ToInt32());
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CPropertyStruct* CPropertyStruct::StructByIndex(u32 index)
|
||||
{
|
||||
CPropertyBase *pProp = PropertyByIndex(index);
|
||||
|
||||
if (pProp->Type() == eStructProperty)
|
||||
return static_cast<CPropertyStruct*>(pProp);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CPropertyStruct* CPropertyStruct::StructByID(u32 ID)
|
||||
{
|
||||
CPropertyBase *pProp = PropertyByID(ID);
|
||||
|
||||
if (pProp->Type() == eStructProperty)
|
||||
return static_cast<CPropertyStruct*>(pProp);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CPropertyStruct* CPropertyStruct::StructByIDString(const TIDString& str)
|
||||
{
|
||||
CPropertyBase *pProp = PropertyByIDString(str);
|
||||
|
||||
if (pProp->Type() == eStructProperty)
|
||||
return static_cast<CPropertyStruct*>(pProp);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CPropertyStruct* CPropertyStruct::CopyFromTemplate(CStructTemplate *pTemp)
|
||||
{
|
||||
CPropertyStruct *pStruct = new CPropertyStruct();
|
||||
pStruct->mpTemplate = pTemp;
|
||||
pStruct->Reserve(pTemp->Count());
|
||||
|
||||
for (u32 iProp = 0; iProp < pTemp->Count(); iProp++)
|
||||
{
|
||||
CPropertyTemplate *pPropTemp = pTemp->PropertyByIndex(iProp);
|
||||
CPropertyBase *pProp = nullptr;
|
||||
|
||||
switch (pPropTemp->Type())
|
||||
{
|
||||
case eBoolProperty: pProp = new CBoolProperty(false); break;
|
||||
case eByteProperty: pProp = new CByteProperty(0); break;
|
||||
case eShortProperty: pProp = new CShortProperty(0); break;
|
||||
case eLongProperty: pProp = new CLongProperty(0); break;
|
||||
case eEnumProperty: pProp = new CEnumProperty(0); break;
|
||||
case eBitfieldProperty: pProp = new CBitfieldProperty(0); break;
|
||||
case eFloatProperty: pProp = new CFloatProperty(0.f); break;
|
||||
case eStringProperty: pProp = new CStringProperty(""); break;
|
||||
case eVector3Property: pProp = new CVector3Property(CVector3f::skZero); break;
|
||||
case eColorProperty: pProp = new CColorProperty(CColor::skBlack); break;
|
||||
case eFileProperty: pProp = new CFileProperty(); break;
|
||||
case eArrayProperty: pProp = new CArrayProperty(); break;
|
||||
case eAnimParamsProperty: pProp = new CAnimParamsProperty(); break;
|
||||
case eUnknownProperty: pProp = new CUnknownProperty(); break;
|
||||
case eStructProperty: pProp = CPropertyStruct::CopyFromTemplate(static_cast<CStructTemplate*>(pPropTemp)); break;
|
||||
}
|
||||
|
||||
if (pProp)
|
||||
{
|
||||
pProp->SetTemplate(pPropTemp);
|
||||
pStruct->mProperties.push_back(pProp);
|
||||
}
|
||||
}
|
||||
|
||||
return pStruct;
|
||||
}
|
||||
103
src/Core/Resource/Script/CProperty.h
Normal file
103
src/Core/Resource/Script/CProperty.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef CPROPERTY
|
||||
#define CPROPERTY
|
||||
|
||||
/*
|
||||
* This header file declares some classes used to track script object properties
|
||||
* CPropertyBase, __CProperty (and typedefs), CPropertyStruct
|
||||
* It's a bit hard to read, should be reorganized at some point
|
||||
*/
|
||||
#include "../CResource.h"
|
||||
#include "../CAnimationParameters.h"
|
||||
#include "CPropertyTemplate.h"
|
||||
#include "EPropertyType.h"
|
||||
#include <Common/CColor.h>
|
||||
#include <Common/CVector3f.h>
|
||||
#include <Common/TString.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <list>
|
||||
|
||||
class CScriptTemplate;
|
||||
|
||||
typedef TString TIDString;
|
||||
|
||||
/*
|
||||
* CPropertyBase is the base class, containing just some virtual function definitions
|
||||
* Virtual destructor is mainly there to make cleanup easy; don't need to cast to delete
|
||||
*/
|
||||
class CPropertyBase
|
||||
{
|
||||
friend class CScriptLoader;
|
||||
protected:
|
||||
CPropertyTemplate *mpTemplate;
|
||||
public:
|
||||
virtual ~CPropertyBase() {}
|
||||
inline virtual EPropertyType Type() = 0;
|
||||
inline CPropertyTemplate *Template() { return mpTemplate; }
|
||||
inline void SetTemplate(CPropertyTemplate *_tmp) { mpTemplate = _tmp; }
|
||||
inline TString Name() { return mpTemplate->Name(); }
|
||||
inline u32 ID() { return mpTemplate->PropertyID(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* __CProperty is a template subclass for actual properties.
|
||||
* Don't use this class directly. Typedefs are provided for every possible property type.
|
||||
*/
|
||||
template <typename t, EPropertyType type>
|
||||
class __CProperty : public CPropertyBase
|
||||
{
|
||||
friend class CScriptLoader;
|
||||
t mValue;
|
||||
public:
|
||||
__CProperty() {}
|
||||
__CProperty(t v) { Set(v); }
|
||||
~__CProperty() {}
|
||||
inline EPropertyType Type() { return type; }
|
||||
inline t Get() { return mValue; }
|
||||
inline void Set(t v) { mValue = v; }
|
||||
};
|
||||
typedef __CProperty<bool, eBoolProperty> CBoolProperty;
|
||||
typedef __CProperty<char, eByteProperty> CByteProperty;
|
||||
typedef __CProperty<short, eShortProperty> CShortProperty;
|
||||
typedef __CProperty<long, eLongProperty> CLongProperty;
|
||||
typedef __CProperty<long, eEnumProperty> CEnumProperty;
|
||||
typedef __CProperty<long, eBitfieldProperty> CBitfieldProperty;
|
||||
typedef __CProperty<float, eFloatProperty> CFloatProperty;
|
||||
typedef __CProperty<TString, eStringProperty> CStringProperty;
|
||||
typedef __CProperty<CVector3f, eVector3Property> CVector3Property;
|
||||
typedef __CProperty<CColor, eColorProperty> CColorProperty;
|
||||
typedef __CProperty<TResPtr<CResource>, eFileProperty> CFileProperty;
|
||||
typedef __CProperty<CAnimationParameters, eAnimParamsProperty> CAnimParamsProperty;
|
||||
typedef __CProperty<std::vector<u8>, eArrayProperty> CArrayProperty;
|
||||
typedef __CProperty<std::vector<u8>, eUnknownProperty> CUnknownProperty;
|
||||
|
||||
/*
|
||||
* CPropertyStruct is for defining structs of properties.
|
||||
*/
|
||||
class CPropertyStruct : public CPropertyBase
|
||||
{
|
||||
friend class CScriptLoader;
|
||||
std::vector<CPropertyBase*> mProperties;
|
||||
public:
|
||||
// Destructor simply iterates through the list and deletes them. Nothing complicated.
|
||||
~CPropertyStruct();
|
||||
|
||||
// Inline
|
||||
EPropertyType Type() { return eStructProperty; }
|
||||
inline u32 Count() { return mProperties.size(); }
|
||||
inline void Reserve(u32 amount) { mProperties.reserve(amount); }
|
||||
inline CPropertyBase* operator[](u32 index) { return mProperties[index]; }
|
||||
|
||||
// Functions
|
||||
CPropertyBase* PropertyByIndex(u32 index);
|
||||
CPropertyBase* PropertyByID(u32 ID);
|
||||
CPropertyBase* PropertyByIDString(const TIDString& str);
|
||||
CPropertyStruct* StructByIndex(u32 index);
|
||||
CPropertyStruct* StructByID(u32 ID);
|
||||
CPropertyStruct* StructByIDString(const TIDString& str);
|
||||
|
||||
// Static
|
||||
static CPropertyStruct* CopyFromTemplate(CStructTemplate *pTemp);
|
||||
};
|
||||
|
||||
#endif // CPROPERTY
|
||||
|
||||
175
src/Core/Resource/Script/CPropertyTemplate.cpp
Normal file
175
src/Core/Resource/Script/CPropertyTemplate.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "CPropertyTemplate.h"
|
||||
#include <iostream>
|
||||
|
||||
EPropertyType PropStringToPropEnum(const TString& prop)
|
||||
{
|
||||
if (prop == "bool") return eBoolProperty;
|
||||
if (prop == "byte") return eByteProperty;
|
||||
if (prop == "short") return eShortProperty;
|
||||
if (prop == "long") return eLongProperty;
|
||||
if (prop == "enum") return eEnumProperty;
|
||||
if (prop == "bitfield") return eBitfieldProperty;
|
||||
if (prop == "float") return eFloatProperty;
|
||||
if (prop == "string") return eStringProperty;
|
||||
if (prop == "color") return eColorProperty;
|
||||
if (prop == "vector3f") return eVector3Property;
|
||||
if (prop == "file") return eFileProperty;
|
||||
if (prop == "struct") return eStructProperty;
|
||||
if (prop == "array") return eArrayProperty;
|
||||
if (prop == "animparams") return eAnimParamsProperty;
|
||||
if (prop == "unknown") return eUnknownProperty;
|
||||
return eInvalidProperty;
|
||||
}
|
||||
|
||||
TString PropEnumToPropString(EPropertyType prop)
|
||||
{
|
||||
switch (prop)
|
||||
{
|
||||
case eBoolProperty: return "bool";
|
||||
case eByteProperty: return "byte";
|
||||
case eShortProperty: return "short";
|
||||
case eLongProperty: return "long";
|
||||
case eEnumProperty: return "enum";
|
||||
case eBitfieldProperty: return "bitfield";
|
||||
case eFloatProperty: return "float";
|
||||
case eStringProperty: return "string";
|
||||
case eColorProperty: return "color";
|
||||
case eVector3Property: return "vector3f";
|
||||
case eFileProperty: return "file";
|
||||
case eStructProperty: return "struct";
|
||||
case eArrayProperty: return "array";
|
||||
case eAnimParamsProperty: return "animparams";
|
||||
case eUnknownProperty: return "unknown";
|
||||
|
||||
case eInvalidProperty:
|
||||
default:
|
||||
return "invalid";
|
||||
}
|
||||
}
|
||||
|
||||
/*******************
|
||||
* CStructTemplate *
|
||||
*******************/
|
||||
CStructTemplate::CStructTemplate() : CPropertyTemplate(-1)
|
||||
{
|
||||
mIsSingleProperty = false;
|
||||
mPropType = eStructProperty;
|
||||
}
|
||||
|
||||
CStructTemplate::~CStructTemplate()
|
||||
{
|
||||
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
EPropertyType CStructTemplate::Type() const
|
||||
{
|
||||
return eStructProperty;
|
||||
}
|
||||
|
||||
bool CStructTemplate::IsSingleProperty() const
|
||||
{
|
||||
return mIsSingleProperty;
|
||||
}
|
||||
|
||||
u32 CStructTemplate::Count() const
|
||||
{
|
||||
return mProperties.size();
|
||||
}
|
||||
|
||||
CPropertyTemplate* CStructTemplate::PropertyByIndex(u32 index)
|
||||
{
|
||||
if (mProperties.size() > index)
|
||||
return mProperties[index];
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CPropertyTemplate* CStructTemplate::PropertyByID(u32 ID)
|
||||
{
|
||||
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
|
||||
{
|
||||
if ((*it)->PropertyID() == ID)
|
||||
return *it;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CPropertyTemplate* CStructTemplate::PropertyByIDString(const TIDString& str)
|
||||
{
|
||||
// Resolve namespace
|
||||
u32 nsStart = str.IndexOf(":");
|
||||
u32 propStart = nsStart + 1;
|
||||
|
||||
// String has namespace; the requested property is within a struct
|
||||
if (nsStart != -1)
|
||||
{
|
||||
TString strStructID = str.SubString(0, nsStart);
|
||||
if (!strStructID.IsHexString()) return nullptr;
|
||||
|
||||
u32 structID = strStructID.ToInt32();
|
||||
TString propName = str.SubString(propStart, str.Length() - propStart);
|
||||
|
||||
CStructTemplate *pStruct = StructByID(structID);
|
||||
if (!pStruct) return nullptr;
|
||||
else return pStruct->PropertyByIDString(propName);
|
||||
}
|
||||
|
||||
// No namespace; fetch the property from this struct
|
||||
else
|
||||
{
|
||||
// ID string lookup
|
||||
if (str.IsHexString())
|
||||
return PropertyByID(str.ToInt32());
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CStructTemplate* CStructTemplate::StructByIndex(u32 index)
|
||||
{
|
||||
CPropertyTemplate *pProp = PropertyByIndex(index);
|
||||
|
||||
if (pProp->Type() == eStructProperty)
|
||||
return static_cast<CStructTemplate*>(pProp);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CStructTemplate* CStructTemplate::StructByID(u32 ID)
|
||||
{
|
||||
CPropertyTemplate *pProp = PropertyByID(ID);
|
||||
|
||||
if (pProp && pProp->Type() == eStructProperty)
|
||||
return static_cast<CStructTemplate*>(pProp);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CStructTemplate* CStructTemplate::StructByIDString(const TString& str)
|
||||
{
|
||||
CPropertyTemplate *pProp = PropertyByIDString(str);
|
||||
|
||||
if (pProp && pProp->Type() == eStructProperty)
|
||||
return static_cast<CStructTemplate*>(pProp);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ************ DEBUG ************
|
||||
void CStructTemplate::DebugPrintProperties(TString base)
|
||||
{
|
||||
base = base + Name() + "::";
|
||||
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
|
||||
{
|
||||
CPropertyTemplate *tmp = *it;
|
||||
if (tmp->Type() == eStructProperty)
|
||||
{
|
||||
CStructTemplate *tmp2 = static_cast<CStructTemplate*>(tmp);
|
||||
tmp2->DebugPrintProperties(base);
|
||||
}
|
||||
else
|
||||
std::cout << base << tmp->Name() << "\n";
|
||||
}
|
||||
}
|
||||
219
src/Core/Resource/Script/CPropertyTemplate.h
Normal file
219
src/Core/Resource/Script/CPropertyTemplate.h
Normal file
@@ -0,0 +1,219 @@
|
||||
#ifndef CPROPERTYTEMPLATE
|
||||
#define CPROPERTYTEMPLATE
|
||||
|
||||
#include "EPropertyType.h"
|
||||
#include <Common/TString.h>
|
||||
#include <Common/types.h>
|
||||
#include <vector>
|
||||
|
||||
typedef TString TIDString;
|
||||
|
||||
class CPropertyTemplate
|
||||
{
|
||||
friend class CTemplateLoader;
|
||||
friend class CTemplateWriter;
|
||||
|
||||
protected:
|
||||
EPropertyType mPropType;
|
||||
TString mPropName;
|
||||
u32 mPropID;
|
||||
public:
|
||||
CPropertyTemplate(u32 ID)
|
||||
: mPropID(ID)
|
||||
{
|
||||
}
|
||||
|
||||
CPropertyTemplate(EPropertyType type, TString name, u32 ID)
|
||||
: mPropType(type),
|
||||
mPropName(name),
|
||||
mPropID(ID)
|
||||
{
|
||||
}
|
||||
|
||||
virtual EPropertyType Type() const
|
||||
{
|
||||
return mPropType;
|
||||
}
|
||||
|
||||
inline TString Name() const
|
||||
{
|
||||
return mPropName;
|
||||
}
|
||||
|
||||
inline u32 PropertyID() const
|
||||
{
|
||||
return mPropID;
|
||||
}
|
||||
|
||||
inline void SetName(const TString& Name)
|
||||
{
|
||||
mPropName = Name;
|
||||
}
|
||||
};
|
||||
|
||||
class CFileTemplate : public CPropertyTemplate
|
||||
{
|
||||
friend class CTemplateLoader;
|
||||
friend class CTemplateWriter;
|
||||
|
||||
TStringList mAcceptedExtensions;
|
||||
public:
|
||||
CFileTemplate(u32 ID) : CPropertyTemplate(ID) { mPropType = eFileProperty; }
|
||||
|
||||
CFileTemplate(const TString& name, u32 ID, const TStringList& extensions)
|
||||
: CPropertyTemplate(ID)
|
||||
{
|
||||
mPropType = eFileProperty;
|
||||
mPropName = name;
|
||||
mAcceptedExtensions = extensions;
|
||||
}
|
||||
|
||||
EPropertyType Type() const
|
||||
{
|
||||
return eFileProperty;
|
||||
}
|
||||
|
||||
const TStringList& Extensions() const
|
||||
{
|
||||
return mAcceptedExtensions;
|
||||
}
|
||||
};
|
||||
|
||||
class CEnumTemplate : public CPropertyTemplate
|
||||
{
|
||||
friend class CTemplateLoader;
|
||||
friend class CTemplateWriter;
|
||||
|
||||
struct SEnumerator
|
||||
{
|
||||
TString Name;
|
||||
u32 ID;
|
||||
|
||||
SEnumerator(const TString& _name, u32 _ID)
|
||||
: Name(_name), ID(_ID) {}
|
||||
};
|
||||
std::vector<SEnumerator> mEnumerators;
|
||||
TString mSourceFile;
|
||||
|
||||
public:
|
||||
CEnumTemplate(u32 ID)
|
||||
: CPropertyTemplate(ID)
|
||||
{
|
||||
mPropType = eEnumProperty;
|
||||
}
|
||||
|
||||
CEnumTemplate(const TString& name, u32 ID)
|
||||
: CPropertyTemplate(eEnumProperty, name, ID)
|
||||
{}
|
||||
|
||||
EPropertyType Type() const
|
||||
{
|
||||
return eEnumProperty;
|
||||
}
|
||||
|
||||
u32 NumEnumerators()
|
||||
{
|
||||
return mEnumerators.size();
|
||||
}
|
||||
|
||||
u32 EnumeratorIndex(u32 enumID)
|
||||
{
|
||||
for (u32 iEnum = 0; iEnum < mEnumerators.size(); iEnum++)
|
||||
{
|
||||
if (mEnumerators[iEnum].ID == enumID)
|
||||
return iEnum;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 EnumeratorID(u32 enumIndex)
|
||||
{
|
||||
if (mEnumerators.size() > enumIndex)
|
||||
return mEnumerators[enumIndex].ID;
|
||||
|
||||
else return -1;
|
||||
}
|
||||
|
||||
TString EnumeratorName(u32 enumIndex)
|
||||
{
|
||||
if (mEnumerators.size() > enumIndex)
|
||||
return mEnumerators[enumIndex].Name;
|
||||
|
||||
else return "INVALID ENUM INDEX";
|
||||
}
|
||||
};
|
||||
|
||||
class CBitfieldTemplate : public CPropertyTemplate
|
||||
{
|
||||
friend class CTemplateLoader;
|
||||
friend class CTemplateWriter;
|
||||
|
||||
struct SBitFlag
|
||||
{
|
||||
TString Name;
|
||||
u32 Mask;
|
||||
|
||||
SBitFlag(const TString& _name, u32 _mask)
|
||||
: Name(_name), Mask(_mask) {}
|
||||
};
|
||||
std::vector<SBitFlag> mBitFlags;
|
||||
TString mSourceFile;
|
||||
|
||||
public:
|
||||
CBitfieldTemplate(u32 ID)
|
||||
: CPropertyTemplate(ID)
|
||||
{
|
||||
mPropType = eBitfieldProperty;
|
||||
}
|
||||
|
||||
CBitfieldTemplate(const TString& name, u32 ID)
|
||||
: CPropertyTemplate(eBitfieldProperty, name, ID)
|
||||
{}
|
||||
|
||||
EPropertyType Type() const
|
||||
{
|
||||
return eBitfieldProperty;
|
||||
}
|
||||
|
||||
u32 NumFlags()
|
||||
{
|
||||
return mBitFlags.size();
|
||||
}
|
||||
|
||||
TString FlagName(u32 index)
|
||||
{
|
||||
return mBitFlags[index].Name;
|
||||
}
|
||||
|
||||
u32 FlagMask(u32 index)
|
||||
{
|
||||
return mBitFlags[index].Mask;
|
||||
}
|
||||
};
|
||||
|
||||
class CStructTemplate : public CPropertyTemplate
|
||||
{
|
||||
friend class CTemplateLoader;
|
||||
friend class CTemplateWriter;
|
||||
|
||||
bool mIsSingleProperty;
|
||||
std::vector<CPropertyTemplate*> mProperties;
|
||||
TString mSourceFile;
|
||||
public:
|
||||
CStructTemplate();
|
||||
~CStructTemplate();
|
||||
|
||||
EPropertyType Type() const;
|
||||
bool IsSingleProperty() const;
|
||||
u32 Count() const;
|
||||
CPropertyTemplate* PropertyByIndex(u32 index);
|
||||
CPropertyTemplate* PropertyByID(u32 ID);
|
||||
CPropertyTemplate* PropertyByIDString(const TIDString& str);
|
||||
CStructTemplate* StructByIndex(u32 index);
|
||||
CStructTemplate* StructByID(u32 ID);
|
||||
CStructTemplate* StructByIDString(const TIDString& str);
|
||||
void DebugPrintProperties(TString base);
|
||||
};
|
||||
|
||||
#endif // CPROPERTYTEMPLATE
|
||||
|
||||
95
src/Core/Resource/Script/CScriptLayer.cpp
Normal file
95
src/Core/Resource/Script/CScriptLayer.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "CScriptLayer.h"
|
||||
|
||||
CScriptLayer::CScriptLayer()
|
||||
{
|
||||
mLayerName = "New Layer";
|
||||
mActive = true;
|
||||
mVisible = true;
|
||||
}
|
||||
|
||||
CScriptLayer::~CScriptLayer()
|
||||
{
|
||||
for (auto it = mObjects.begin(); it != mObjects.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
// ************* DATA MANIPULATION *************
|
||||
void CScriptLayer::AddObject(CScriptObject* object)
|
||||
{
|
||||
mObjects.push_back(object);
|
||||
}
|
||||
|
||||
void CScriptLayer::DeleteObjectByIndex(u32 index)
|
||||
{
|
||||
delete mObjects[index];
|
||||
mObjects.erase(mObjects.begin() + index, mObjects.begin() + index);
|
||||
}
|
||||
|
||||
void CScriptLayer::DeleteObjectByID(u32 ID)
|
||||
{
|
||||
for (auto it = mObjects.begin(); it != mObjects.end(); it++)
|
||||
{
|
||||
if ((*it)->InstanceID() == ID)
|
||||
{
|
||||
delete *it;
|
||||
mObjects.erase(it, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptLayer::Reserve(u32 amount)
|
||||
{
|
||||
mObjects.reserve(amount);
|
||||
}
|
||||
|
||||
// ************* GETTERS *************
|
||||
TString CScriptLayer::Name()
|
||||
{
|
||||
return mLayerName;
|
||||
}
|
||||
|
||||
bool CScriptLayer::IsActive()
|
||||
{
|
||||
return mActive;
|
||||
}
|
||||
|
||||
bool CScriptLayer::IsVisible()
|
||||
{
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
u32 CScriptLayer::GetNumObjects()
|
||||
{
|
||||
return mObjects.size();
|
||||
}
|
||||
|
||||
CScriptObject* CScriptLayer::ObjectByIndex(u32 index)
|
||||
{
|
||||
return mObjects[index];
|
||||
}
|
||||
|
||||
CScriptObject* CScriptLayer::ObjectByID(u32 ID)
|
||||
{
|
||||
for (auto it = mObjects.begin(); it != mObjects.end(); it++)
|
||||
if ((*it)->InstanceID() == ID)
|
||||
return *it;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ************* SETTERS *************
|
||||
void CScriptLayer::SetName(const TString& name)
|
||||
{
|
||||
mLayerName = name;
|
||||
}
|
||||
|
||||
void CScriptLayer::SetActive(bool active)
|
||||
{
|
||||
mActive = active;
|
||||
}
|
||||
|
||||
void CScriptLayer::SetVisible(bool visible)
|
||||
{
|
||||
mVisible = visible;
|
||||
}
|
||||
48
src/Core/Resource/Script/CScriptLayer.h
Normal file
48
src/Core/Resource/Script/CScriptLayer.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef SSCRIPTLAYER_H
|
||||
#define SSCRIPTLAYER_H
|
||||
|
||||
#include "CScriptObject.h"
|
||||
#include <Common/types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class CScriptLayer
|
||||
{
|
||||
TString mLayerName;
|
||||
bool mActive;
|
||||
bool mVisible;
|
||||
std::vector<CScriptObject*> mObjects;
|
||||
public:
|
||||
CScriptLayer();
|
||||
~CScriptLayer();
|
||||
|
||||
// Data Manipulation
|
||||
void AddObject(CScriptObject* object);
|
||||
void DeleteObjectByIndex(u32 index);
|
||||
void DeleteObjectByID(u32 ID);
|
||||
void Reserve(u32 amount);
|
||||
|
||||
// Getters and Setters
|
||||
TString Name();
|
||||
bool IsActive();
|
||||
bool IsVisible();
|
||||
u32 GetNumObjects();
|
||||
CScriptObject* ObjectByIndex(u32 index);
|
||||
CScriptObject* ObjectByID(u32 ID);
|
||||
|
||||
void SetName(const TString& name);
|
||||
void SetActive(bool active);
|
||||
void SetVisible(bool visible);
|
||||
|
||||
// Operators
|
||||
CScriptObject* operator[](u32 index);
|
||||
};
|
||||
|
||||
// ************* INLINE FUNCTIONS *************
|
||||
inline CScriptObject* CScriptLayer::operator[](u32 index)
|
||||
{
|
||||
return mObjects[index];
|
||||
}
|
||||
|
||||
|
||||
#endif // SSCRIPTLAYER_H
|
||||
226
src/Core/Resource/Script/CScriptObject.cpp
Normal file
226
src/Core/Resource/Script/CScriptObject.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "CScriptObject.h"
|
||||
#include "../CAnimSet.h"
|
||||
#include "CMasterTemplate.h"
|
||||
|
||||
CScriptObject::CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate)
|
||||
{
|
||||
mpTemplate = pTemplate;
|
||||
mpArea = pArea;
|
||||
mpLayer = pLayer;
|
||||
mpProperties = nullptr;
|
||||
mpTemplate->AddObject(this);
|
||||
mpDisplayModel = nullptr;
|
||||
mpBillboard = nullptr;
|
||||
mpCollision = nullptr;
|
||||
mHasInGameModel = false;
|
||||
}
|
||||
|
||||
CScriptObject::~CScriptObject()
|
||||
{
|
||||
if (mpProperties) delete mpProperties;
|
||||
mpTemplate->RemoveObject(this);
|
||||
}
|
||||
|
||||
// ************ DATA MANIPULATION ************
|
||||
void CScriptObject::CopyFromTemplate(CScriptTemplate *pTemp, u32 propCount)
|
||||
{
|
||||
CStructTemplate *pBaseStruct = pTemp->BaseStructByCount(propCount);
|
||||
delete mpProperties;
|
||||
mpProperties = CPropertyStruct::CopyFromTemplate(pBaseStruct);
|
||||
}
|
||||
|
||||
void CScriptObject::EvaluateProperties()
|
||||
{
|
||||
mpInstanceName = mpTemplate->FindInstanceName(mpProperties);
|
||||
mpPosition = mpTemplate->FindPosition(mpProperties);
|
||||
mpRotation = mpTemplate->FindRotation(mpProperties);
|
||||
mpScale = mpTemplate->FindScale(mpProperties);
|
||||
mpActive = mpTemplate->FindActive(mpProperties);
|
||||
mpLightParameters = mpTemplate->FindLightParameters(mpProperties);
|
||||
mHasInGameModel = mpTemplate->HasInGameModel(mpProperties);
|
||||
mVolumeShape = mpTemplate->VolumeShape(this);
|
||||
EvaluateDisplayModel();
|
||||
EvaluateBillboard();
|
||||
EvaluateCollisionModel();
|
||||
}
|
||||
|
||||
void CScriptObject::EvaluateDisplayModel()
|
||||
{
|
||||
mpDisplayModel = mpTemplate->FindDisplayModel(mpProperties);
|
||||
}
|
||||
|
||||
void CScriptObject::EvaluateBillboard()
|
||||
{
|
||||
mpBillboard = mpTemplate->FindBillboardTexture(mpProperties);
|
||||
}
|
||||
|
||||
void CScriptObject::EvaluateCollisionModel()
|
||||
{
|
||||
mpCollision = mpTemplate->FindCollision(mpProperties);
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
CPropertyBase* CScriptObject::PropertyByIndex(u32 index) const
|
||||
{
|
||||
return mpProperties->PropertyByIndex(index);
|
||||
}
|
||||
|
||||
CPropertyBase* CScriptObject::PropertyByIDString(const TString& str) const
|
||||
{
|
||||
return mpProperties->PropertyByIDString(str);
|
||||
}
|
||||
|
||||
CScriptTemplate* CScriptObject::Template() const
|
||||
{
|
||||
return mpTemplate;
|
||||
}
|
||||
|
||||
CMasterTemplate* CScriptObject::MasterTemplate() const
|
||||
{
|
||||
return mpTemplate->MasterTemplate();
|
||||
}
|
||||
|
||||
CGameArea* CScriptObject::Area() const
|
||||
{
|
||||
return mpArea;
|
||||
}
|
||||
|
||||
CScriptLayer* CScriptObject::Layer() const
|
||||
{
|
||||
return mpLayer;
|
||||
}
|
||||
|
||||
CPropertyStruct* CScriptObject::Properties() const
|
||||
{
|
||||
return mpProperties;
|
||||
}
|
||||
|
||||
u32 CScriptObject::NumProperties() const
|
||||
{
|
||||
return mpProperties->Count();
|
||||
}
|
||||
|
||||
u32 CScriptObject::ObjectTypeID() const
|
||||
{
|
||||
return mpTemplate->ObjectID();
|
||||
}
|
||||
|
||||
u32 CScriptObject::InstanceID() const
|
||||
{
|
||||
return mInstanceID;
|
||||
}
|
||||
|
||||
u32 CScriptObject::NumInLinks() const
|
||||
{
|
||||
return mInConnections.size();
|
||||
}
|
||||
|
||||
u32 CScriptObject::NumOutLinks() const
|
||||
{
|
||||
return mOutConnections.size();
|
||||
}
|
||||
|
||||
const SLink& CScriptObject::InLink(u32 index) const
|
||||
{
|
||||
return mInConnections[index];
|
||||
}
|
||||
|
||||
const SLink& CScriptObject::OutLink(u32 index) const
|
||||
{
|
||||
return mOutConnections[index];
|
||||
}
|
||||
|
||||
TString CScriptObject::InstanceName() const
|
||||
{
|
||||
if (mpInstanceName)
|
||||
return mpInstanceName->Get();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
CVector3f CScriptObject::Position() const
|
||||
{
|
||||
if (mpPosition)
|
||||
return mpPosition->Get();
|
||||
else
|
||||
return CVector3f::skZero;
|
||||
}
|
||||
|
||||
CVector3f CScriptObject::Rotation() const
|
||||
{
|
||||
if (mpRotation)
|
||||
return mpRotation->Get();
|
||||
else
|
||||
return CVector3f::skZero;
|
||||
}
|
||||
|
||||
CVector3f CScriptObject::Scale() const
|
||||
{
|
||||
if (mpScale)
|
||||
return mpScale->Get();
|
||||
else
|
||||
return CVector3f::skOne;
|
||||
}
|
||||
|
||||
bool CScriptObject::IsActive() const
|
||||
{
|
||||
if (mpActive)
|
||||
return mpActive->Get();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CScriptObject::HasInGameModel() const
|
||||
{
|
||||
return mHasInGameModel;
|
||||
}
|
||||
|
||||
void CScriptObject::SetPosition(const CVector3f& newPos)
|
||||
{
|
||||
if (mpPosition) mpPosition->Set(newPos);
|
||||
}
|
||||
|
||||
void CScriptObject::SetRotation(const CVector3f& newRot)
|
||||
{
|
||||
if (mpRotation) mpRotation->Set(newRot);
|
||||
}
|
||||
|
||||
void CScriptObject::SetScale(const CVector3f& newScale)
|
||||
{
|
||||
if (mpScale) mpScale->Set(newScale);
|
||||
}
|
||||
|
||||
void CScriptObject::SetName(const TString& newName)
|
||||
{
|
||||
if (mpInstanceName) mpInstanceName->Set(newName);
|
||||
}
|
||||
|
||||
void CScriptObject::SetActive(bool isActive)
|
||||
{
|
||||
if (mpActive) mpActive->Set(isActive);
|
||||
}
|
||||
|
||||
CPropertyStruct* CScriptObject::LightParameters() const
|
||||
{
|
||||
return mpLightParameters;
|
||||
}
|
||||
|
||||
CModel* CScriptObject::GetDisplayModel() const
|
||||
{
|
||||
return mpDisplayModel;
|
||||
}
|
||||
|
||||
CTexture* CScriptObject::GetBillboard() const
|
||||
{
|
||||
return mpBillboard;
|
||||
}
|
||||
|
||||
CCollisionMeshGroup* CScriptObject::GetCollision() const
|
||||
{
|
||||
return mpCollision;
|
||||
}
|
||||
|
||||
EVolumeShape CScriptObject::VolumeShape() const
|
||||
{
|
||||
return mVolumeShape;
|
||||
}
|
||||
84
src/Core/Resource/Script/CScriptObject.h
Normal file
84
src/Core/Resource/Script/CScriptObject.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef CSCRIPTOBJECT_H
|
||||
#define CSCRIPTOBJECT_H
|
||||
|
||||
#include "SConnection.h"
|
||||
#include "CProperty.h"
|
||||
#include "CPropertyTemplate.h"
|
||||
#include "CScriptTemplate.h"
|
||||
#include "../model/CModel.h"
|
||||
#include "../CCollisionMeshGroup.h"
|
||||
#include "../CGameArea.h"
|
||||
|
||||
class CScriptLayer;
|
||||
|
||||
class CScriptObject
|
||||
{
|
||||
friend class CScriptLoader;
|
||||
friend class CAreaLoader;
|
||||
|
||||
CScriptTemplate *mpTemplate;
|
||||
TResPtr<CGameArea> mpArea;
|
||||
CScriptLayer *mpLayer;
|
||||
|
||||
u32 mInstanceID;
|
||||
std::vector<SLink> mOutConnections;
|
||||
std::vector<SLink> mInConnections;
|
||||
CPropertyStruct *mpProperties;
|
||||
|
||||
CStringProperty *mpInstanceName;
|
||||
CVector3Property *mpPosition;
|
||||
CVector3Property *mpRotation;
|
||||
CVector3Property *mpScale;
|
||||
CBoolProperty *mpActive;
|
||||
CPropertyStruct *mpLightParameters;
|
||||
TResPtr<CModel> mpDisplayModel;
|
||||
TResPtr<CTexture> mpBillboard;
|
||||
TResPtr<CCollisionMeshGroup> mpCollision;
|
||||
bool mHasInGameModel;
|
||||
|
||||
EVolumeShape mVolumeShape;
|
||||
|
||||
public:
|
||||
CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate);
|
||||
~CScriptObject();
|
||||
|
||||
void CopyFromTemplate(CScriptTemplate *pTemp, u32 propCount);
|
||||
void EvaluateProperties();
|
||||
void EvaluateDisplayModel();
|
||||
void EvaluateBillboard();
|
||||
void EvaluateCollisionModel();
|
||||
|
||||
CScriptTemplate* Template() const;
|
||||
CMasterTemplate* MasterTemplate() const;
|
||||
CGameArea* Area() const;
|
||||
CScriptLayer* Layer() const;
|
||||
CPropertyStruct* Properties() const;
|
||||
u32 NumProperties() const;
|
||||
CPropertyBase* PropertyByIndex(u32 index) const;
|
||||
CPropertyBase* PropertyByIDString(const TIDString& str) const;
|
||||
u32 ObjectTypeID() const;
|
||||
u32 InstanceID() const;
|
||||
u32 NumInLinks() const;
|
||||
u32 NumOutLinks() const;
|
||||
const SLink& InLink(u32 index) const;
|
||||
const SLink& OutLink(u32 index) const;
|
||||
|
||||
CVector3f Position() const;
|
||||
CVector3f Rotation() const;
|
||||
CVector3f Scale() const;
|
||||
TString InstanceName() const;
|
||||
bool IsActive() const;
|
||||
bool HasInGameModel() const;
|
||||
void SetPosition(const CVector3f& newPos);
|
||||
void SetRotation(const CVector3f& newRot);
|
||||
void SetScale(const CVector3f& newScale);
|
||||
void SetName(const TString& newName);
|
||||
void SetActive(bool isActive);
|
||||
CPropertyStruct* LightParameters() const;
|
||||
CModel* GetDisplayModel() const;
|
||||
CTexture* GetBillboard() const;
|
||||
CCollisionMeshGroup* GetCollision() const;
|
||||
EVolumeShape VolumeShape() const;
|
||||
};
|
||||
|
||||
#endif // CSCRIPTOBJECT_H
|
||||
402
src/Core/Resource/Script/CScriptTemplate.cpp
Normal file
402
src/Core/Resource/Script/CScriptTemplate.cpp
Normal file
@@ -0,0 +1,402 @@
|
||||
#include "CScriptTemplate.h"
|
||||
#include "CScriptObject.h"
|
||||
#include "CMasterTemplate.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <Core/Log.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <Resource/CAnimSet.h>
|
||||
|
||||
CScriptTemplate::CScriptTemplate(CMasterTemplate *pMaster)
|
||||
{
|
||||
mpMaster = pMaster;
|
||||
mVisible = true;
|
||||
mPreviewScale = 1.f;
|
||||
mVolumeShape = eNoShape;
|
||||
}
|
||||
|
||||
CScriptTemplate::~CScriptTemplate()
|
||||
{
|
||||
for (u32 iSet = 0; iSet < mPropertySets.size(); iSet++)
|
||||
delete mPropertySets[iSet].pBaseStruct;
|
||||
}
|
||||
|
||||
CMasterTemplate* CScriptTemplate::MasterTemplate()
|
||||
{
|
||||
return mpMaster;
|
||||
}
|
||||
|
||||
EGame CScriptTemplate::Game()
|
||||
{
|
||||
return mpMaster->GetGame();
|
||||
}
|
||||
|
||||
TString CScriptTemplate::TemplateName(s32 propCount) const
|
||||
{
|
||||
// Return original name if there is only one property set
|
||||
// or if caller doesn't want to distinguish between sets
|
||||
if ((NumPropertySets() == 1) || (propCount == -1))
|
||||
return mTemplateName;
|
||||
|
||||
// Otherwise we return the template name with the set name appended
|
||||
for (auto it = mPropertySets.begin(); it != mPropertySets.end(); it++)
|
||||
if (it->pBaseStruct->Count() == propCount)
|
||||
return mTemplateName + " (" + it->SetName + ")";
|
||||
|
||||
return mTemplateName + " (Invalid)";
|
||||
}
|
||||
|
||||
TString CScriptTemplate::PropertySetNameByCount(s32 propCount) const
|
||||
{
|
||||
for (auto it = mPropertySets.begin(); it != mPropertySets.end(); it++)
|
||||
if (it->pBaseStruct->Count() == propCount)
|
||||
return it->SetName;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
TString CScriptTemplate::PropertySetNameByIndex(u32 index) const
|
||||
{
|
||||
if (index < NumPropertySets())
|
||||
return mPropertySets[index].SetName;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
u32 CScriptTemplate::NumPropertySets() const
|
||||
{
|
||||
return mPropertySets.size();
|
||||
}
|
||||
|
||||
CScriptTemplate::ERotationType CScriptTemplate::RotationType() const
|
||||
{
|
||||
return mRotationType;
|
||||
}
|
||||
|
||||
CScriptTemplate::EScaleType CScriptTemplate::ScaleType() const
|
||||
{
|
||||
return mScaleType;
|
||||
}
|
||||
|
||||
float CScriptTemplate::PreviewScale() const
|
||||
{
|
||||
return mPreviewScale;
|
||||
}
|
||||
|
||||
u32 CScriptTemplate::ObjectID() const
|
||||
{
|
||||
return mObjectID;
|
||||
}
|
||||
|
||||
void CScriptTemplate::SetVisible(bool visible)
|
||||
{
|
||||
mVisible = visible;
|
||||
}
|
||||
|
||||
bool CScriptTemplate::IsVisible() const
|
||||
{
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
void CScriptTemplate::DebugPrintProperties(int propCount)
|
||||
{
|
||||
CStructTemplate *pTemp = BaseStructByCount(propCount);
|
||||
if (pTemp) pTemp->DebugPrintProperties("");
|
||||
}
|
||||
|
||||
// ************ PROPERTY FETCHING ************
|
||||
template<typename t, EPropertyType propType>
|
||||
t TFetchProperty(CPropertyStruct *pProperties, const TIDString& ID)
|
||||
{
|
||||
if (ID.IsEmpty()) return nullptr;
|
||||
CPropertyBase *pProp = pProperties->PropertyByIDString(ID);
|
||||
|
||||
if (pProp && (pProp->Type() == propType))
|
||||
return static_cast<t>(pProp);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CStructTemplate* CScriptTemplate::BaseStructByCount(s32 propCount)
|
||||
{
|
||||
if (mPropertySets.size() == 1) return mPropertySets[0].pBaseStruct;
|
||||
|
||||
for (u32 iSet = 0; iSet < mPropertySets.size(); iSet++)
|
||||
if (mPropertySets[iSet].pBaseStruct->Count() == propCount)
|
||||
return mPropertySets[iSet].pBaseStruct;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CStructTemplate* CScriptTemplate::BaseStructByIndex(u32 index)
|
||||
{
|
||||
if (index < NumPropertySets())
|
||||
return mPropertySets[index].pBaseStruct;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EVolumeShape CScriptTemplate::VolumeShape(CScriptObject *pObj)
|
||||
{
|
||||
if (pObj->Template() != this)
|
||||
{
|
||||
Log::Error(pObj->Template()->TemplateName() + " instance somehow called VolumeShape() on " + TemplateName() + " template");
|
||||
return eInvalidShape;
|
||||
}
|
||||
|
||||
if (mVolumeShape == eConditionalShape)
|
||||
{
|
||||
CPropertyBase *pProp = pObj->Properties()->PropertyByIDString(mVolumeConditionIDString);
|
||||
|
||||
// Get value of the condition test property (only boolean, integral, and enum types supported)
|
||||
int v;
|
||||
switch (pProp->Type())
|
||||
{
|
||||
case eBoolProperty:
|
||||
v = (static_cast<CBoolProperty*>(pProp)->Get() ? 1 : 0);
|
||||
break;
|
||||
|
||||
case eByteProperty:
|
||||
v = (int) static_cast<CByteProperty*>(pProp)->Get();
|
||||
break;
|
||||
|
||||
case eShortProperty:
|
||||
v = (int) static_cast<CShortProperty*>(pProp)->Get();
|
||||
break;
|
||||
|
||||
case eLongProperty:
|
||||
v = (int) static_cast<CLongProperty*>(pProp)->Get();
|
||||
break;
|
||||
|
||||
case eEnumProperty: {
|
||||
CEnumProperty *pEnumCast = static_cast<CEnumProperty*>(pProp);
|
||||
CEnumTemplate *pEnumTemp = static_cast<CEnumTemplate*>(pEnumCast->Template());
|
||||
int index = static_cast<CEnumProperty*>(pProp)->Get();
|
||||
v = pEnumTemp->EnumeratorID(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Test and check whether any of the conditions are true
|
||||
for (auto it = mVolumeConditions.begin(); it != mVolumeConditions.end(); it++)
|
||||
{
|
||||
if (it->Value == v)
|
||||
return it->Shape;
|
||||
}
|
||||
|
||||
Log::Error(TemplateName() + " instance " + TString::HexString(pObj->InstanceID(), true, true, 8) + " has unexpected volume shape value of " + TString::HexString((u32) v, true, true));
|
||||
return eInvalidShape;
|
||||
}
|
||||
|
||||
else return mVolumeShape;
|
||||
}
|
||||
|
||||
CStringProperty* CScriptTemplate::FindInstanceName(CPropertyStruct *pProperties)
|
||||
{
|
||||
return TFetchProperty<CStringProperty*, eStringProperty>(pProperties, mNameIDString);
|
||||
}
|
||||
|
||||
CVector3Property* CScriptTemplate::FindPosition(CPropertyStruct *pProperties)
|
||||
{
|
||||
return TFetchProperty<CVector3Property*, eVector3Property>(pProperties, mPositionIDString);
|
||||
}
|
||||
|
||||
CVector3Property* CScriptTemplate::FindRotation(CPropertyStruct *pProperties)
|
||||
{
|
||||
return TFetchProperty<CVector3Property*, eVector3Property>(pProperties, mRotationIDString);
|
||||
}
|
||||
|
||||
CVector3Property* CScriptTemplate::FindScale(CPropertyStruct *pProperties)
|
||||
{
|
||||
return TFetchProperty<CVector3Property*, eVector3Property>(pProperties, mScaleIDString);
|
||||
}
|
||||
|
||||
CBoolProperty* CScriptTemplate::FindActive(CPropertyStruct *pProperties)
|
||||
{
|
||||
return TFetchProperty<CBoolProperty*, eBoolProperty>(pProperties, mActiveIDString);
|
||||
}
|
||||
|
||||
CPropertyStruct* CScriptTemplate::FindLightParameters(CPropertyStruct *pProperties)
|
||||
{
|
||||
return TFetchProperty<CPropertyStruct*, eStructProperty>(pProperties, mLightParametersIDString);
|
||||
}
|
||||
|
||||
// todo: merge these four functions, they have near-identical code
|
||||
CModel* CScriptTemplate::FindDisplayModel(CPropertyStruct *pProperties)
|
||||
{
|
||||
for (auto it = mAssets.begin(); it != mAssets.end(); it++)
|
||||
{
|
||||
if ((it->AssetType != SEditorAsset::eModel) && (it->AssetType != SEditorAsset::eAnimParams)) continue;
|
||||
CResource *pRes = nullptr;
|
||||
|
||||
// File
|
||||
if (it->AssetSource == SEditorAsset::eFile)
|
||||
{
|
||||
TString path = "../resources/" + it->AssetLocation;
|
||||
pRes = gResCache.GetResource(path);
|
||||
}
|
||||
|
||||
// Property
|
||||
else
|
||||
{
|
||||
CPropertyBase *pProp = pProperties->PropertyByIDString(it->AssetLocation);
|
||||
|
||||
if (pProp->Type() == eFileProperty)
|
||||
{
|
||||
CFileProperty *pFile = static_cast<CFileProperty*>(pProp);
|
||||
pRes = pFile->Get();
|
||||
}
|
||||
|
||||
else if (pProp->Type() == eAnimParamsProperty)
|
||||
{
|
||||
CAnimParamsProperty *pParams = static_cast<CAnimParamsProperty*>(pProp);
|
||||
pRes = pParams->Get().GetCurrentModel(it->ForceNodeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify resource exists + is correct type
|
||||
if (pRes && (pRes->Type() == eModel))
|
||||
return static_cast<CModel*>(pRes);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CTexture* CScriptTemplate::FindBillboardTexture(CPropertyStruct *pProperties)
|
||||
{
|
||||
for (auto it = mAssets.begin(); it != mAssets.end(); it++)
|
||||
{
|
||||
if (it->AssetType != SEditorAsset::eBillboard) continue;
|
||||
CResource *pRes = nullptr;
|
||||
|
||||
// File
|
||||
if (it->AssetSource == SEditorAsset::eFile)
|
||||
{
|
||||
TString path = "../resources/" + it->AssetLocation;
|
||||
pRes = gResCache.GetResource(path);
|
||||
}
|
||||
|
||||
// Property
|
||||
else
|
||||
{
|
||||
CPropertyBase *pProp = pProperties->PropertyByIDString(it->AssetLocation);
|
||||
|
||||
if (pProp->Type() == eFileProperty)
|
||||
{
|
||||
CFileProperty *pFile = static_cast<CFileProperty*>(pProp);
|
||||
pRes = pFile->Get();
|
||||
}
|
||||
}
|
||||
|
||||
// Verify resource exists + is correct type
|
||||
if (pRes && (pRes->Type() == eTexture))
|
||||
return static_cast<CTexture*>(pRes);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CCollisionMeshGroup* CScriptTemplate::FindCollision(CPropertyStruct *pProperties)
|
||||
{
|
||||
for (auto it = mAssets.begin(); it != mAssets.end(); it++)
|
||||
{
|
||||
if (it->AssetType != SEditorAsset::eCollision) continue;
|
||||
CResource *pRes = nullptr;
|
||||
|
||||
// File
|
||||
if (it->AssetSource == SEditorAsset::eFile)
|
||||
{
|
||||
TString path = "../resources/" + it->AssetLocation;
|
||||
pRes = gResCache.GetResource(path);
|
||||
}
|
||||
|
||||
// Property
|
||||
else
|
||||
{
|
||||
CPropertyBase *pProp = pProperties->PropertyByIDString(it->AssetLocation);
|
||||
|
||||
if (pProp->Type() == eFileProperty)
|
||||
{
|
||||
CFileProperty *pFile = static_cast<CFileProperty*>(pProp);
|
||||
pRes = pFile->Get();
|
||||
}
|
||||
}
|
||||
|
||||
// Verify resource exists + is correct type
|
||||
if (pRes && (pRes->Type() == eCollisionMeshGroup))
|
||||
return static_cast<CCollisionMeshGroup*>(pRes);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CScriptTemplate::HasInGameModel(CPropertyStruct *pProperties)
|
||||
{
|
||||
for (auto it = mAssets.begin(); it != mAssets.end(); it++)
|
||||
{
|
||||
if ((it->AssetType != SEditorAsset::eModel) && (it->AssetType != SEditorAsset::eAnimParams)) continue;
|
||||
if (it->AssetSource == SEditorAsset::eFile) continue;
|
||||
CResource *pRes = nullptr;
|
||||
|
||||
CPropertyBase *pProp = pProperties->PropertyByIDString(it->AssetLocation);
|
||||
|
||||
if (pProp->Type() == eFileProperty)
|
||||
{
|
||||
CFileProperty *pFile = static_cast<CFileProperty*>(pProp);
|
||||
pRes = pFile->Get();
|
||||
}
|
||||
|
||||
else if (pProp->Type() == eAnimParamsProperty)
|
||||
{
|
||||
CAnimParamsProperty *pParams = static_cast<CAnimParamsProperty*>(pProp);
|
||||
pRes = pParams->Get().GetCurrentModel(it->ForceNodeIndex);
|
||||
}
|
||||
|
||||
// Verify resource exists + is correct type
|
||||
if (pRes && (pRes->Type() == eModel))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CScriptTemplate::HasPosition()
|
||||
{
|
||||
return (!mPositionIDString.IsEmpty());
|
||||
}
|
||||
|
||||
// ************ OBJECT TRACKING ************
|
||||
u32 CScriptTemplate::NumObjects() const
|
||||
{
|
||||
return mObjectList.size();
|
||||
}
|
||||
|
||||
const std::list<CScriptObject*>& CScriptTemplate::ObjectList() const
|
||||
{
|
||||
return mObjectList;
|
||||
}
|
||||
|
||||
void CScriptTemplate::AddObject(CScriptObject *pObject)
|
||||
{
|
||||
mObjectList.push_back(pObject);
|
||||
}
|
||||
|
||||
void CScriptTemplate::RemoveObject(CScriptObject *pObject)
|
||||
{
|
||||
for (auto it = mObjectList.begin(); it != mObjectList.end(); it++)
|
||||
{
|
||||
if (*it == pObject)
|
||||
{
|
||||
mObjectList.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptTemplate::SortObjects()
|
||||
{
|
||||
// todo: make this function take layer names into account
|
||||
mObjectList.sort([](CScriptObject *pA, CScriptObject *pB) -> bool {
|
||||
return (pA->InstanceID() < pB->InstanceID());
|
||||
});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user