Initial commit of current work on Prime World Editor

This commit is contained in:
parax0
2015-07-26 17:39:49 -04:00
commit 66e8c2ebcb
305 changed files with 33469 additions and 0 deletions

36
Resource/CAnimSet.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include "CAnimSet.h"
#include <Core/CResCache.h>
CAnimSet::CAnimSet() : CResource()
{
}
CAnimSet::~CAnimSet()
{
}
EResType CAnimSet::Type()
{
return eCharacter;
}
u32 CAnimSet::getNodeCount()
{
return nodes.size();
}
std::string 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;
}

37
Resource/CAnimSet.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef CANIMSET_H
#define CANIMSET_H
#include <Common/types.h>
#include <Core/CToken.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
{
friend class CAnimSetLoader;
struct SNode
{
std::string name;
CModel *model;
u32 skinID;
u32 skelID;
CToken ModelToken;
SNode() { model = nullptr; }
};
std::vector<SNode> nodes;
public:
CAnimSet();
~CAnimSet();
EResType Type();
u32 getNodeCount();
std::string getNodeName(u32 node);
CModel* getNodeModel(u32 node);
};
#endif // CCHARACTERSET_H

119
Resource/CCollisionMesh.cpp Normal file
View File

@@ -0,0 +1,119 @@
#include "CCollisionMesh.h"
#include <Core/CRenderer.h>
CCollisionMesh::CCollisionMesh() : CResource()
{
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;
}
}
EResType CCollisionMesh::Type()
{
return eCollisionMesh;
}
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::DrawLines()
{
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];
}

89
Resource/CCollisionMesh.h Normal file
View File

@@ -0,0 +1,89 @@
#ifndef CCOLLISIONMESH_H
#define CCOLLISIONMESH_H
#include <Common/CAABox.h>
#include <OpenGL/CVertexBuffer.h>
#include <OpenGL/CIndexBuffer.h>
#include "CResource.h"
class CCollisionMesh : public CResource
{
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();
EResType Type();
void BufferGL();
void Draw();
void DrawLines();
};
#endif // CCOLLISIONMESH_H

182
Resource/CFont.cpp Normal file
View File

@@ -0,0 +1,182 @@
#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()
{
}
EResType CFont::Type()
{
return eFont;
}
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(std::string String, CRenderer *pRenderer, float,
CVector2f, CColor FillColor, CColor StrokeColor, u32 FontSize)
{
// Not using parameter 3 (float - AspectRatio)
// Not using parameter 4 (CVector2f - Position)
// 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;
}

73
Resource/CFont.h Normal file
View File

@@ -0,0 +1,73 @@
#ifndef CFONT_H
#define CFONT_H
#include "CResource.h"
#include "CTexture.h"
#include "model/CVertex.h"
#include <Common/types.h>
#include <Core/CToken.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
{
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.
std::string mFontName; // Self-explanatory
CTexture *mpFontTexture; // The texture used by this font
CToken mTextureToken; // Token for the 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();
EResType Type();
CResource* MakeCopy(CResCache *pCopyCache);
CVector2f RenderString(std::string 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

184
Resource/CGameArea.cpp Normal file
View File

@@ -0,0 +1,184 @@
#include "CGameArea.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];
}
EResType CGameArea::Type()
{
return eArea;
}
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 tm = 0; tm < mTerrainModels.size(); tm++)
{
CModel *mdl = mTerrainModels[tm];
u32 SubmeshCount = mdl->GetSurfaceCount();
for (u32 sub = 0; sub < SubmeshCount; sub++)
{
SSurface *s = mdl->GetSurface(sub);
CMaterial *mat = mMaterialSet->materials[s->MaterialID];
bool NewMat = true;
for (std::vector<CStaticModel*>::iterator it = mStaticTerrainModels.begin(); it != mStaticTerrainModels.end(); it++)
{
if ((*it)->GetMaterial() == mat)
{
// 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 *mdl = *it;
mdl->AddSurface(s);
mStaticTerrainModels.erase(it);
mStaticTerrainModels.push_back(mdl);
NewMat = false;
break;
}
}
if (NewMat)
{
CStaticModel *smdl = new CStaticModel(mat);
smdl->AddSurface(s);
mStaticTerrainModels.push_back(smdl);
}
}
}
}
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];
}
CCollisionMesh* 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;
}

66
Resource/CGameArea.h Normal file
View File

@@ -0,0 +1,66 @@
#ifndef CGAMEAREA_H
#define CGAMEAREA_H
#include "CCollisionMesh.h"
#include "CLight.h"
#include "CMaterialSet.h"
#include "model/CModel.h"
#include "model/CStaticModel.h"
#include "CResource.h"
#include "script/CScriptLayer.h"
#include <Common/types.h>
#include <Common/CTransform4f.h>
#include <unordered_map>
class CGameArea : public CResource
{
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
CCollisionMesh *mCollision;
// Lights
std::vector<std::vector<CLight*>> mLightLayers;
public:
CGameArea();
~CGameArea();
EResType Type();
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);
CCollisionMesh* 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

273
Resource/CLight.cpp Normal file
View File

@@ -0,0 +1,273 @@
#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);
mRadius = 0.f;
mIntensity = 0.f;
mFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
}
// ************ DATA MANIPULATION ************
// This function is reverse engineered from the kiosk demo's code
float CLight::CalculateRadius()
{
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()
{
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;
}
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()
{
if (mFlags & CLIGHT_NO_RADIUS)
{
mRadius = CalculateRadius();
mFlags &= ~CLIGHT_NO_RADIUS;
}
return mRadius * 2;
}
float CLight::GetIntensity()
{
if (mFlags & CLIGHT_NO_INTENSITY)
{
mIntensity = CalculateIntensity();
mFlags &= ~CLIGHT_NO_INTENSITY;
}
return mIntensity;
}
// ************ SETTERS ************
void CLight::SetPosition(const CVector3f& Position)
{
mPosition = Position;
}
void CLight::SetDirection(const CVector3f& Direction)
{
mDirection = Direction;
}
void CLight::SetColor(const CColor& Color)
{
mColor = Color;
mFlags = 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);

80
Resource/CLight.h Normal file
View File

@@ -0,0 +1,80 @@
#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 Prime 3 and DKCR; they
* have a new light structure.
*/
enum ELightType
{
eLocalAmbient = 0,
eDirectional = 1,
eSpot = 3,
eCustom = 2
};
class CLight
{
ELightType mType;
CVector3f mPosition;
CVector3f mDirection;
CColor mColor;
float mSpotCutoff;
CVector3f mDistAttenCoefficients;
CVector3f mAngleAttenCoefficients;
float mRadius;
float mIntensity;
u8 mFlags;
public:
CLight();
private:
// Data Manipulation
float CalculateRadius();
float CalculateIntensity();
CVector3f CalculateSpotAngleAtten();
public:
// Getters
ELightType GetType() const;
CVector3f GetPosition() const;
CVector3f GetDirection() const;
CColor GetColor() const;
CVector3f GetDistAttenuation() const;
CVector3f GetAngleAttenuation() const;
float GetRadius();
float GetIntensity();
// Setters
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

240
Resource/CMaterial.cpp Normal file
View File

@@ -0,0 +1,240 @@
#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()
{
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
delete mPasses[iPass];
delete mpShader;
}
void CMaterial::GenerateShader()
{
delete mpShader;
mpShader = CShaderGenerator::GenerateShader(*this);
if (!mpShader->IsValidProgram()) mShaderStatus = eShaderFailed;
else mShaderStatus = eShaderExists;
}
bool CMaterial::SetCurrent(ERenderOptions Options)
{
// Bind textures
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
mPasses[iPass]->LoadTexture(iPass);
// Skip material setup if the currently bound material is identical
if (sCurrentMaterial == HashParameters())
{
GLuint NumLightsLoc = CShader::CurrentShader()->GetUniformLocation("NumLights");
glUniform1i(NumLightsLoc, CGraphics::sNumLights);
return true;
}
// Shader setup
if (mShaderStatus == eNoShader) GenerateShader();
mpShader->SetCurrent();
if (mShaderStatus == eShaderFailed)
return false;
// Set blend equation - force to ZERO/ONE if alpha is disabled
if (Options & eNoAlpha)
glBlendFunc(GL_ONE, GL_ZERO);
else
glBlendFunc(mBlendSrcFac, mBlendDstFac);
// Set color mask. This is for rendering bloom - bloom maps render to the framebuffer alpha
// We can only render bloom on materials that aren't alpha blended.
bool AlphaBlended = ((mBlendSrcFac == GL_SRC_ALPHA) && (mBlendDstFac == GL_ONE_MINUS_SRC_ALPHA));
if ((mEnableBloom) && (Options & eEnableBloom) && (!AlphaBlended))
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
else
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
// 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);
GLuint NumLightsLoc = mpShader->GetUniformLocation("NumLights");
glUniform1i(NumLightsLoc, CGraphics::sNumLights);
CGraphics::UpdateVertexBlock();
CGraphics::UpdatePixelBlock();
sCurrentMaterial = HashParameters();
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 ************
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::SetOptions(EMaterialOptions Options)
{
mOptions = Options;
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;
}
// ************ STATIC ************
void CMaterial::KillCachedMaterial()
{
sCurrentMaterial = 0;
CShader::KillCachedShader();
}

107
Resource/CMaterial.h Normal file
View File

@@ -0,0 +1,107 @@
#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/CToken.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
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.
CTexture *mpIndirectTexture; // Optional texture used for the indirect stage for reflections
CToken mIndTextureToken; // Token for indirect texture
std::vector<CMaterialPass*> mPasses;
public:
CMaterial();
~CMaterial();
void GenerateShader();
bool SetCurrent(ERenderOptions Options);
u64 HashParameters();
void Update();
// Getters
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 SetOptions(EMaterialOptions Options);
void SetBlendMode(GLenum SrcFac, GLenum DstFac);
void SetKonst(CColor& Konst, u32 KIndex);
void SetIndTexture(CTexture *pTex);
void SetLightingEnabled(bool Enabled);
// Static
static void KillCachedMaterial();
};
DEFINE_ENUM_FLAGS(CMaterial::EMaterialOptions)
#endif // MATERIAL_H

296
Resource/CMaterialPass.cpp Normal file
View File

@@ -0,0 +1,296 @@
#include "CMaterialPass.h"
#include "CMaterial.h"
#include <Common/AnimUtil.h>
#include <Core/CGraphics.h>
CMaterialPass::CMaterialPass(CMaterial *pParent)
{
mPassType = "CUST";
mpTexture = nullptr;
mEnabled = true;
mpParentMat = pParent;
mColorOutput = ePrevReg;
mAlphaOutput = ePrevReg;
mKColorSel = eKonstOne;
mKAlphaSel = eKonstOne;
mRasSel = eRasColorNull;
mTexCoordSource = 0;
mAnimMode = eNoUVAnim;
for (u32 iParam = 0; iParam < 4; iParam++)
{
mColorInputs[iParam] = eZeroRGB;
mAlphaInputs[iParam] = eZeroAlpha;
mAnimParams[iParam] = 0.f;
}
}
CMaterialPass::~CMaterialPass()
{
}
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(CGraphics::sMVPBlock.ViewMatrix.ToGlmMat4()) * glm::transpose(CGraphics::sMVPBlock.ModelMatrix.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(CGraphics::sMVPBlock.ViewMatrix.ToGlmMat4()) * glm::transpose(CGraphics::sMVPBlock.ModelMatrix.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 & eUVScroll)
{
TexMtx[0][3] = (s * mAnimParams[2]) + mAnimParams[0];
TexMtx[1][3] = (s * mAnimParams[3]) + mAnimParams[1];
}
break;
}
case eUVRotation: // Mode 3
{
if (Options & eUVScroll)
{
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 & eUVScroll)
{
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(CGraphics::sMVPBlock.ModelMatrix.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(CGraphics::sMVPBlock.ViewMatrix.ToGlmMat4()) * glm::transpose(CGraphics::sMVPBlock.ModelMatrix.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;
mTexToken = CToken(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 ************
std::string 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();
}

127
Resource/CMaterialPass.h Normal file
View File

@@ -0,0 +1,127 @@
#ifndef CMATERIALPASS_H
#define CMATERIALPASS_H
#include <Common/CFourCC.h>
#include <Common/CHashFNV1A.h>
#include <Core/CToken.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
{
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
CTexture *mpTexture;
CToken mTexToken;
EUVAnimMode mAnimMode;
float mAnimParams[4];
bool mEnabled;
public:
CMaterialPass(CMaterial *pParent);
~CMaterialPass();
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 std::string 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 std::string PassTypeName(CFourCC Type);
};
#endif // CMATERIALPASS_H

11
Resource/CMaterialSet.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "CMaterialSet.h"
#include <Core/CResCache.h>
#include <iostream>
CMaterialSet::CMaterialSet() {}
CMaterialSet::~CMaterialSet()
{
for (u32 m = 0; m < materials.size(); m++)
delete materials[m];
}

19
Resource/CMaterialSet.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef CMATERIALSET_H
#define CMATERIALSET_H
#include <FileIO/CInputStream.h>
#include <Resource/CTexture.h>
#include <Resource/CMaterial.h>
#include "EFormatVersion.h"
class CMaterialSet
{
public:
std::vector<CTexture*> textures;
std::vector<CMaterial*> materials;
CMaterialSet();
~CMaterialSet();
};
#endif // CMATERIALSET_H

174
Resource/CPakFile.cpp Normal file
View 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
Resource/CPakFile.h Normal file
View 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

96
Resource/CResource.cpp Normal file
View File

@@ -0,0 +1,96 @@
#include "CResource.h"
#include <Core/CResCache.h>
#include <iostream>
CResource::CResource()
{
mRefCount = 0;
}
CResource::~CResource()
{
}
EResType CResource::Type()
{
return eResource;
}
std::string CResource::Source()
{
return StringUtil::GetFileNameWithExtension(mResSource);
}
std::string 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 == "AFSM") return eStateMachine;
if (Extension == "AGSC") return eAudioGrp;
if (Extension == "ANCS") return eCharacter;
if (Extension == "ANIM") return eAnimation;
if (Extension == "ATBL") return eAudioTable;
if (Extension == "CAUD") return eAudioData;
if (Extension == "CINF") return eSkeleton;
if (Extension == "CMDL") return eModel;
if (Extension == "CRSC") return eCollisionResponse;
if (Extension == "CSKR") return eSkin;
if (Extension == "CSMP") return eAudioSample;
if (Extension == "CSNG") return eMidi;
if (Extension == "CTWK") return eTweak;
if (Extension == "DCLN") return eCollisionMesh;
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;
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;
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;
}

33
Resource/CResource.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef CRESOURCE_H
#define CRESOURCE_H
#include "EResType.h"
#include <Common/CFourCC.h>
#include <Common/CUniqueID.h>
#include <Common/types.h>
#include <Common/StringUtil.h>
class CResCache;
class CResource
{
friend class CResCache;
std::string mResSource;
CUniqueID mID;
int mRefCount;
public:
CResource();
virtual ~CResource();
virtual EResType Type();
std::string Source();
std::string FullSource();
CUniqueID ResID();
void Lock();
void Release();
bool IsValidResource();
static EResType ResTypeForExtension(CFourCC Extension);
};
#endif // CRESOURCE_H

39
Resource/CScan.cpp Normal file
View File

@@ -0,0 +1,39 @@
#include "CScan.h"
CScan::CScan()
{
mpFrame = nullptr;
mpStringTable = nullptr;
mIsSlow = false;
mIsImportant = false;
mCategory = eNone;
}
CScan::~CScan()
{
}
EResType CScan::Type()
{
return eScan;
}
CStringTable* CScan::ScanText()
{
return mpStringTable;
}
bool CScan::IsImportant()
{
return mIsImportant;
}
bool CScan::IsSlow()
{
return mIsSlow;
}
CScan::ELogbookCategory CScan::LogbookCategory()
{
return mCategory;
}

42
Resource/CScan.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef CSCAN_H
#define CSCAN_H
#include "CResource.h"
#include "CStringTable.h"
#include <Core/CToken.h>
class CScan : public CResource
{
friend class CScanLoader;
public:
// This likely needs revising when MP2/MP3 support is added
enum ELogbookCategory
{
eNone,
ePirateData,
eChozoLore,
eCreatures,
eResearch
};
private:
CResource *mpFrame;
CStringTable *mpStringTable;
CToken mFrameToken;
CToken mStringToken;
bool mIsSlow;
bool mIsImportant;
ELogbookCategory mCategory;
public:
CScan();
~CScan();
EResType Type();
CStringTable* ScanText();
bool IsImportant();
bool IsSlow();
ELogbookCategory LogbookCategory();
};
#endif // CSCAN_H

57
Resource/CStringTable.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include "CStringTable.h"
CStringTable::CStringTable() : CResource()
{
}
CStringTable::~CStringTable()
{
}
EResType CStringTable::Type()
{
return eStringTable;
}
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;
}
std::wstring CStringTable::GetString(CFourCC Lang, u32 StringIndex)
{
for (u32 iLang = 0; iLang < GetLangCount(); iLang++)
{
if (GetLangTag(iLang) == Lang)
return GetString(iLang, StringIndex);
}
return std::wstring();
}
std::wstring CStringTable::GetString(u32 LangIndex, u32 StringIndex)
{
return mLangTables[LangIndex].Strings[StringIndex];
}
std::string CStringTable::GetStringName(u32 StringIndex)
{
return mStringNames[StringIndex];
}

39
Resource/CStringTable.h Normal file
View 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
{
friend class CStringLoader;
std::vector<std::string> mStringNames;
u32 mNumStrings;
struct SLangTable
{
CFourCC Language;
std::vector<std::wstring> Strings;
};
std::vector<SLangTable> mLangTables;
public:
CStringTable();
~CStringTable();
EResType Type();
CResource* MakeCopy(CResCache *pCopyCache);
// Getters
u32 GetStringCount();
u32 GetLangCount();
CFourCC GetLangTag(u32 Index);
std::wstring GetString(CFourCC Lang, u32 StringIndex);
std::wstring GetString(u32 LangIndex, u32 StringIndex);
std::string GetStringName(u32 StringIndex);
};
#endif // CSTRINGTABLE_H

337
Resource/CTexture.cpp Normal file
View File

@@ -0,0 +1,337 @@
#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();
}
EResType CTexture::Type()
{
return eTexture;
}
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;
}
}
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 = 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 += 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 += 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;
}
}

83
Resource/CTexture.h Normal file
View File

@@ -0,0 +1,83 @@
#ifndef CTEXTURE_H
#define CTEXTURE_H
#include <Common/types.h>
#include <FileIO/FileIO.h>
#include <gl/glew.h>
#include "CResource.h"
#include "ETexelFormat.h"
class CTexture : public CResource
{
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();
EResType Type();
bool BufferGL();
void Bind(u32 GLTextureUnit);
void Resize(u32 Width, u32 Height);
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

101
Resource/CWorld.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include "CWorld.h"
#include <Core/CResCache.h>
CWorld::CWorld() : CResource()
{
mWorldVersion = eUnknownVersion;
mpWorldName = nullptr;
mpDarkWorldName = nullptr;
mpSaveWorld = nullptr;
mpDefaultSkybox = nullptr;
mpMapWorld = nullptr;
}
CWorld::~CWorld()
{
}
EResType CWorld::Type()
{
return eWorld;
}
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];
}
std::string CWorld::GetAreaInternalName(u32 AreaIndex)
{
return mAreas[AreaIndex].InternalName;
}
CStringTable* CWorld::GetAreaName(u32 AreaIndex)
{
return mAreas[AreaIndex].pAreaName;
}

107
Resource/CWorld.h Normal file
View File

@@ -0,0 +1,107 @@
#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
{
friend class CWorldLoader;
// Instances of CResource pointers are placeholders for unimplemented resource types (eg CMapWorld)
EGame mWorldVersion;
CStringTable *mpWorldName;
CStringTable *mpDarkWorldName;
CResource *mpSaveWorld;
CModel *mpDefaultSkybox;
CResource *mpMapWorld;
CToken mResTokens[5];
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
{
std::string InternalName;
CStringTable *pAreaName;
CTransform4f Transform;
CAABox AetherBox;
u64 FileID; // Loading every single area as a CResource would be a very bad idea
u64 AreaID;
CToken AreaNameToken;
std::vector<u16> AttachedAreaIDs;
std::vector<SDependency> Dependencies;
std::vector<std::string> 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
{
std::string LayerName;
bool EnabledByDefault;
u8 LayerID[16];
u32 LayerDependenciesStart; // Offset into Dependencies vector
};
std::vector<SLayer> Layers;
};
std::vector<SArea> mAreas;
public:
CWorld();
~CWorld();
EResType Type();
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);
std::string GetAreaInternalName(u32 AreaIndex);
CStringTable* GetAreaName(u32 AreaIndex);
};
#endif // CWORLD_H

18
Resource/EFormatVersion.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef EFORMATVERSION_H
#define EFORMATVERSION_H
// Global version enum that can be easily shared between loaders
enum EGame
{
ePrimeKioskDemo = 0,
ePrime = 1,
eEchoesDemo = 2,
eEchoes = 3,
eCorruptionProto = 4,
eCorruption = 5,
eReturns = 6,
TropicalFreeze = 7,
eUnknownVersion = -1
};
#endif // EFORMATVERSION_H

50
Resource/EResType.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef ERESTYPE
#define ERESTYPE
enum EResType
{
eAnimation = 0,
eAnimEventData = 1,
eArea = 2,
eAudioData = 3,
eAudioGrp = 4,
eAudioSample = 5,
eAudioStream = 6,
eAudioTable = 7,
eCharacter = 8,
eCollisionMesh = 9,
eCollisionResponse = 10,
eDataDump = 11,
eDecal = 12,
eDependencyGroup = 13,
eFont = 14,
eGuiFrame = 15,
eHintSystem = 16,
eInvalidResType = 17,
eMapArea = 18,
eMapWorld = 19,
eMapUniverse = 20,
eMidi = 21,
eModel = 22,
eMusicTrack = 23,
eNavMesh = 24,
ePackFile = 25,
eParticle = 26,
eParticleElectric = 27,
eParticleSwoosh = 28,
eProjectile = 29,
eResource = 30,
eSaveWorld = 31,
eScan = 32,
eSkeleton = 33,
eSkin = 34,
eStateMachine = 35,
eStringTable = 36,
eTexture = 37,
eTweak = 38,
eVideo = 39,
eWorld = 40
};
#endif // ERESTYPE

106
Resource/ETevEnums.h Normal file
View 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
Resource/ETexelFormat.h Normal file
View 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

14
Resource/SDependency.h Normal file
View 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
Resource/SNamedResource.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef SNAMEDRESOURCE_H
#define SNAMEDRESOURCE_H
#include <Common/CFourCC.h>
#include <Common/types.h>
struct SNamedResource
{
CFourCC resType;
std::string resName;
u64 resID;
};
#endif // SNAMEDRESOURCE_H

16
Resource/SResInfo.h Normal file
View 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

View 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->materials.size();
for (u32 iMat = 0; iMat < NumMats; iMat++)
{
CMaterial *pMat = mpSet->materials[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->materials[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());
// 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(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 ePrimeKioskDemo:
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 ePrimeKioskDemo:
case ePrime:
case eEchoesDemo:
case eEchoes:
Cooker.WriteMaterialPrime(Out);
break;
// TODO: Corruption/Uncooked
}
}

View 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

View 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(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 = 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 += 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 ePrimeKioskDemo:
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 ePrimeKioskDemo:
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;
}
}

View File

@@ -0,0 +1,30 @@
#ifndef CMODELCOOKER_H
#define CMODELCOOKER_H
#include "../model/CModel.h"
#include "../EFormatVersion.h"
#include <FileIO/FileIO.h>
class CModelCooker
{
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

View 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]);
}

View 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

View File

@@ -0,0 +1,106 @@
#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;
}
}
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;
}
}

View File

@@ -0,0 +1,27 @@
#ifndef CTEXTUREENCODER_H
#define CTEXTUREENCODER_H
#include "../CTexture.h"
// Class contains basic functionality right now - only supports directly converting DXT1 to CMPR
// More advanced functions (including actual encoding!) coming later
class CTextureEncoder
{
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

View File

@@ -0,0 +1,199 @@
#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 = (CModel*) gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
node.ModelToken = CToken(node.model);
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 = (CModel*) gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
node.ModelToken = CToken(node.model);
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: " + StringUtil::ToHexString(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 = (CModel*) gResCache.GetResource(ANCS.ReadLong(), "CMDL");
node->skinID = ANCS.ReadLong();
node->skelID = ANCS.ReadLong();
node->ModelToken = CToken(node->model);
// 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: " + StringUtil::ToHexString(check));
return nullptr;
}

View File

@@ -0,0 +1,24 @@
#ifndef CCHARACTERLOADER_H
#define CCHARACTERLOADER_H
#include "../CAnimSet.h"
#include "../EFormatVersion.h"
#include <Core/CResCache.h>
class CAnimSetLoader
{
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

View File

@@ -0,0 +1,538 @@
#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;
SModelData *data = CModelLoader::LoadWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, mVersion);
CModel *pTerrainModel = new CModel(data, mpArea->mMaterialSet);
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);
}
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;
SModelData *pData = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
CModel *pWorldModel = new CModel(pData, mpArea->mMaterialSet);
mpArea->AddWorldModel(pWorldModel);
CurWOBJSection += 4;
CurGPUSection = mBlockMgr->CurrentBlock();
}
mpArea->MergeTerrain();
std::cout << "\n";
}
// ************ RETURNS ************
// ************ 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;
}
}
std::string Source = mpMREA->GetSourceString();
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::BigEndian);
mpMREA->SetSourceString(Source);
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: " + StringUtil::ToHexString(deadbeef));
return nullptr;
}
// Header
Loader.mpArea = new CGameArea;
u32 version = MREA.ReadLong();
Loader.mVersion = GetFormatVersion(version);
Loader.mpMREA = &MREA;
switch (Loader.mVersion)
{
case ePrimeKioskDemo:
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();
if (Loader.mVersion != eReturns) Loader.ReadCollision();
break;
default:
Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + StringUtil::ToHexString(version));
delete Loader.mpArea;
return nullptr;
}
// Cleanup
delete Loader.mBlockMgr;
return Loader.mpArea;
}
EGame CAreaLoader::GetFormatVersion(u32 version)
{
switch (version)
{
case 0xC: return ePrimeKioskDemo;
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;
}
}

View File

@@ -0,0 +1,82 @@
#ifndef CAREALOADER_H
#define CAREALOADER_H
#include <FileIO/FileIO.h>
#include "../CGameArea.h"
#include "../EFormatVersion.h"
#include "CBlockMgrIn.h"
#include <Core/CResCache.h>
class CAreaLoader
{
struct SCompressedCluster;
// Area data
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();
// Common
void ReadCompressedBlocks();
void Decompress();
void ReadCollision();
void SetUpObjects();
public:
static CGameArea* LoadMREA(CInputStream& MREA);
static EGame GetFormatVersion(u32 version);
};
#endif // CAREALOADER_H

View 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;
}

View 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

View File

@@ -0,0 +1,136 @@
#include "CCollisionLoader.h"
#include <Core/Log.h>
#include <iostream>
CCollisionLoader::CCollisionLoader()
{
}
CCollisionMesh* 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: " + StringUtil::ToHexString(deafbabe));
return nullptr;
}
u32 version = MREA.ReadLong();
loader.version = ECollisionVersion(version);
if ((loader.version != Prime) && (loader.version != Echoes))
{
Log::FileError(MREA.GetSourceString(), MREA.Tell() - 4, "Unsupported collision version: " + StringUtil::ToHexString(version));
return nullptr;
}
loader.mesh = new CCollisionMesh;
CCollisionMesh *cmesh = loader.mesh;
// Octree - structure is known, but not coding this right now
cmesh->mAABox = CAABox(MREA);
MREA.Seek(0x4, SEEK_CUR);
u32 octreeSize = MREA.ReadLong();
MREA.Seek(octreeSize, SEEK_CUR); // Skipping the octree for now
cmesh->mOctreeLoaded = false;
// Properties
u32 propertySetCount = MREA.ReadLong();
for (u32 p = 0; p < propertySetCount; p++)
loader.readPropertyFlags(MREA);
// Property indices for vertices/lines/faces
u32 vtxIndexCount = MREA.ReadLong();
std::vector<u8> vtxIndices(vtxIndexCount);
MREA.ReadBytes(vtxIndices.data(), vtxIndices.size());
u32 lineIndexCount = MREA.ReadLong();
std::vector<u8> lineIndices(lineIndexCount);
MREA.ReadBytes(lineIndices.data(), lineIndices.size());
u32 faceIndexCount = MREA.ReadLong();
std::vector<u8> faceIndices(faceIndexCount);
MREA.ReadBytes(faceIndices.data(), faceIndices.size());
// Lines
cmesh->mLineCount = MREA.ReadLong();
cmesh->mCollisionLines.resize(cmesh->mLineCount);
for (u32 l = 0; l < cmesh->mLineCount; l++)
{
CCollisionMesh::CCollisionLine *Line = &cmesh->mCollisionLines[l];
Line->Vertices[0] = MREA.ReadShort();
Line->Vertices[1] = MREA.ReadShort();
Line->Properties = loader.properties[lineIndices[l]];
}
// Faces
cmesh->mFaceCount = MREA.ReadLong() / 3; // Not sure why they store it this way. It's inconsistent.
cmesh->mCollisionFaces.resize(cmesh->mFaceCount);
for (u32 f = 0; f < cmesh->mFaceCount; f++)
{
CCollisionMesh::CCollisionFace *face = &cmesh->mCollisionFaces[f];
face->Lines[0] = MREA.ReadShort();
face->Lines[1] = MREA.ReadShort();
face->Lines[2] = MREA.ReadShort();
face->Properties = loader.properties[faceIndices[f]];
}
// Echoes introduces a new data chunk; don't know what it is yet, skipping for now
if (loader.version == Echoes)
{
u32 unknown_count = MREA.ReadLong();
MREA.Seek(unknown_count * 2, SEEK_CUR);
}
// Vertices
cmesh->mVertexCount = MREA.ReadLong();
cmesh->mCollisionVertices.resize(cmesh->mVertexCount);
for (u32 v = 0; v < cmesh->mVertexCount; v++)
{
CCollisionMesh::CCollisionVertex *vtx = &cmesh->mCollisionVertices[v];
vtx->Pos = CVector3f(MREA);
vtx->Properties = loader.properties[vtxIndices[v]];
}
return cmesh;
}
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::readPropertyFlags(CInputStream& src)
{
CCollisionMesh::SCollisionProperties property;
if (version == Prime)
{
u32 flag = src.ReadLong();
property.Invert = (flag >> 25) & 0x1;
}
if (version == Echoes)
{
u64 flag = src.ReadLongLong();
property.Invert = (flag >> 24) & 0x1;
}
properties.push_back(property);
}

View File

@@ -0,0 +1,31 @@
#ifndef CCOLLISIONLOADER_H
#define CCOLLISIONLOADER_H
#include "../CCollisionMesh.h"
class CCollisionLoader
{
enum ECollisionVersion;
CCollisionMesh *mesh;
ECollisionVersion version;
std::vector<CCollisionMesh::SCollisionProperties> properties;
enum ECollisionVersion
{
Prime = 0x3,
Echoes = 0x4,
DonkeyKongCountryReturns = 0x5
};
CCollisionLoader();
CCollisionMesh::CCollisionOctree* parseOctree(CInputStream& src);
CCollisionMesh::CCollisionOctree::SBranch* parseOctreeBranch(CInputStream& src);
CCollisionMesh::CCollisionOctree::SLeaf* parseOctreeLeaf(CInputStream& src);
void readPropertyFlags(CInputStream& src);
public:
static CCollisionMesh* LoadAreaCollision(CInputStream& MREA);
};
#endif // CCOLLISIONLOADER_H

View File

@@ -0,0 +1,119 @@
#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 > ePrimeKioskDemo) FONT.Seek(0x4, SEEK_CUR);
FONT.Seek(0x2, SEEK_CUR);
mpFont->mDefaultSize = FONT.ReadLong();
mpFont->mFontName = FONT.ReadString();
if (mVersion <= eEchoes) mpFont->mpFontTexture = (CTexture*) gResCache.GetResource(FONT.ReadLong(), "TXTR");
else mpFont->mpFontTexture = (CTexture*) gResCache.GetResource(FONT.ReadLongLong(), "TXTR");
mpFont->mTextureToken = CToken(mpFont->mpFontTexture);
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: " + StringUtil::ToHexString((u32) Magic.ToLong()));
return nullptr;
}
u32 FileVersion = FONT.ReadLong();
EGame Version = GetFormatVersion(FileVersion);
if (Version == eUnknownVersion)
{
Log::FileError(FONT.GetSourceString(), "Unsupported FONT version: " + StringUtil::ToHexString(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 ePrimeKioskDemo;
case 2: return ePrime;
case 4: return eEchoes;
case 5: return eCorruption;
default: return eUnknownVersion;
}
}

View File

@@ -0,0 +1,21 @@
#ifndef CFONTLOADER_H
#define CFONTLOADER_H
#include "../CFont.h"
#include "../EFormatVersion.h"
#include <Core/CResCache.h>
class CFontLoader
{
CFont *mpFont;
EGame mVersion;
CFontLoader();
CFont* LoadFont(CInputStream& FONT);
public:
static CFont* LoadFONT(CInputStream& FONT);
static EGame GetFormatVersion(u32 Version);
};
#endif // CFONTLOADER_H

View File

@@ -0,0 +1,581 @@
#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()
{
}
CMaterialSet* CMaterialLoader::LoadMaterialSet(CInputStream& Mat, EGame Version)
{
CMaterialLoader Loader;
Loader.mpSet = new CMaterialSet();
Loader.mpFile = &Mat;
Loader.mVersion = Version;
if ((Version >= ePrimeKioskDemo) && (Version <= eEchoes))
Loader.ReadPrimeMatSet();
else
Loader.ReadCorruptionMatSet();
return Loader.mpSet;
}
void CMaterialLoader::ReadPrimeMatSet()
{
// Textures
u32 TexCount = mpFile->ReadLong();
mpSet->textures.resize(TexCount);
for (u32 t = 0; t < TexCount; t++)
{
u32 TextureID = mpFile->ReadLong();
mpSet->textures[t] = (CTexture*) gResCache.GetResource(TextureID, "TXTR");
}
// Materials
u32 MatCount = mpFile->ReadLong();
std::vector<u32> offsets(MatCount);
for (u32 m = 0; m < MatCount; m++)
offsets[m] = mpFile->ReadLong();
u32 mats_start = mpFile->Tell();
mpSet->materials.resize(MatCount);
for (u32 m = 0; m < MatCount; m++)
{
mpSet->materials[m] = ReadPrimeMaterial();
mpSet->materials[m]->mVersion = mVersion;
mpFile->Seek(mats_start + offsets[m], 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 = mpSet->textures[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 >= mpSet->textures.size()))
{
pPass->mpTexture = nullptr;
}
else
{
pPass->mpTexture = mpSet->textures[ TextureIndices[TexSel] ];
pPass->mTexToken = CToken(pPass->mpTexture);
}
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: " + StringUtil::ToHexString((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->materials.resize(NumMats);
for (u32 iMat = 0; iMat < NumMats; iMat++)
{
u32 Size = mpFile->ReadLong();
u32 Next = mpFile->Tell() + Size;
mpSet->materials[iMat] = ReadCorruptionMaterial();
mpSet->materials[iMat]->mVersion = mVersion;
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;
}
CTexture *pTex = (CTexture*) gResCache.GetResource(TextureID, "TXTR");
mpSet->textures.push_back(pTex);
pPass->mpTexture = pTex;
pPass->mTexToken = CToken(pTex);
pPass->mTexCoordSource = 4 + (u8) mpFile->ReadLong();
u32 AnimSize = mpFile->ReadLong();
if (AnimSize > 0)
{
u16 Unknown1 = mpFile->ReadShort();
u16 Unknown2 = mpFile->ReadShort();
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: " + StringUtil::ToHexString((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);
}
// 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;
}
}
}

View File

@@ -0,0 +1,38 @@
#ifndef CMATERIALLOADER_H
#define CMATERIALLOADER_H
#include <FileIO/FileIO.h>
#include "../CMaterialSet.h"
#include "../EFormatVersion.h"
#include <Core/CResCache.h>
class CMaterialLoader
{
// Material data
CMaterialSet *mpSet;
CInputStream *mpFile;
EGame mVersion;
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);
public:
static CMaterialSet* LoadMaterialSet(CInputStream& Mat, EGame Version);
};
#endif // CMATERIALLOADER_H

View File

@@ -0,0 +1,445 @@
#include "CModelLoader.h"
#include "CMaterialLoader.h"
#include <Core/Log.h>
CModelLoader::CModelLoader()
{
mFlags = eNoFlags;
}
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();
}
SModelData* CModelLoader::LoadSurfaces(CInputStream& Model)
{
// This function is meant to be called at the start of the first surface
SModelData *pData = new SModelData;
u32 Offset = Model.Tell();
// Surfaces
pData->mSurfaces.resize(mSurfaceCount);
for (u32 iSurf = 0; iSurf < mSurfaceCount; iSurf++)
{
SSurface *pSurf = new SSurface;
pData->mSurfaces[iSurf] = pSurf;
u32 NextSurface = mpBlockMgr->NextOffset();
// Surface header
if (mVersion < eReturns)
LoadSurfaceHeaderPrime(Model, pSurf);
else
LoadSurfaceHeaderDKCR(Model, pSurf);
bool HasAABB = (pSurf->AABox != CAABox::skInfinite);
CMaterial *pMat = mMaterials[0]->materials[pSurf->MaterialID];
// Primitive table
u8 Flag = Model.ReadByte();
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();
} // Submesh table end
return pData;
}
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);
}
// ************ 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: " + StringUtil::ToHexString(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: " + StringUtil::ToHexString(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);
SModelData *pData = Loader.LoadSurfaces(CMDL);
pModel->SetData(pData);
pModel->mAABox = AABox;
pModel->mHasOwnSurfaces = true;
// Cleanup
delete pData;
delete Loader.mpBlockMgr;
return pModel;
}
SModelData* 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);
SModelData *pData = Loader.LoadSurfaces(MREA);
pData->mAABox = Loader.mAABox;
return pData;
}
SModelData* 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);
SModelData *pData = Loader.LoadSurfaces(MREA);
pData->mAABox = Loader.mAABox;
return pData;
}
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;
}
}

View File

@@ -0,0 +1,63 @@
#ifndef CMODELLOADER_H
#define CMODELLOADER_H
#include "../model/SModelData.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>
class CModelLoader
{
public:
enum EModelFlags
{
eNoFlags = 0x0,
eShortPositions = 0x1,
eShortNormals = 0x2,
eHasTex1 = 0x4,
eHasVisGroups = 0x8
};
private:
CModel *mpModel;
std::vector<CMaterialSet*> mMaterials;
CBlockMgrIn *mpBlockMgr;
CAABox mAABox;
EGame mVersion;
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);
SModelData* LoadSurfaces(CInputStream& Model);
void LoadSurfaceHeaderPrime(CInputStream& Model, SSurface *pSurf);
void LoadSurfaceHeaderDKCR(CInputStream& Model, SSurface *pSurf);
public:
static CModel* LoadCMDL(CInputStream& CMDL);
static SModelData* LoadWorldModel(CInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version);
static SModelData* LoadCorruptionWorldModel(CInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version);
static EGame GetFormatVersion(u32 Version);
};
DEFINE_ENUM_FLAGS(CModelLoader::EModelFlags)
#endif // CMODELLOADER_H

View File

@@ -0,0 +1,150 @@
#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 = (CStringTable*) gResCache.GetResource(SCAN.ReadLong(), "STRG");
mpScan->mStringToken = CToken(mpScan->mpStringTable);
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
mpScan->mCategory = (CScan::ELogbookCategory) SCAN.ReadLong();
mpScan->mIsImportant = (SCAN.ReadByte() == 1);
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: " + StringUtil::ToHexString(BasePropID));
return nullptr;
}
mpScan = new CScan();
SCAN.Seek(0x2, SEEK_CUR);
u16 NumProperties = SCAN.ReadShort();
switch (NumProperties)
{
case 0x14:
LoadParamsMP2(SCAN);
break;
default:
Log::FileError(SCAN.GetSourceString(), SCAN.Tell() - 2, "Invalid SNFO property count: " + StringUtil::ToHexString(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 = (CStringTable*) gResCache.GetResource(SCAN.ReadLong(), "STRG");
mpScan->mStringToken = CToken(mpScan->mpStringTable);
break;
case 0xC308A322:
mpScan->mIsSlow = (SCAN.ReadLong() != 0);
break;
case 0x7B714814:
mpScan->mIsImportant = (SCAN.ReadByte() != 0);
break;
case 0x53336141:
u32 TextureID = SCAN.ReadLong();
if (TextureID != 0xFFFFFFFF)
Log::FileWarning(SCAN.GetSourceString(), "SCAN with texture found!");
break;
}
SCAN.Seek(Next, SEEK_SET);
}
mpScan->mCategory = CScan::eNone;
}
// ************ 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: " + StringUtil::ToHexString(magic));
return nullptr;
}
if (fileVersion != 5)
{
Log::FileError(SCAN.GetSourceString(), "Unsupported SCAN version: " + StringUtil::ToHexString(fileVersion));
return nullptr;
}
// MP1 SCAN - read the file!
CScanLoader loader;
loader.mVersion = ePrime;
loader.mpScan = new CScan();
return loader.LoadScanMP1(SCAN);
}

View File

@@ -0,0 +1,21 @@
#ifndef CSCANLOADER_H
#define CSCANLOADER_H
#include "../CScan.h"
#include "../EFormatVersion.h"
class CScanLoader
{
CScan *mpScan;
EGame mVersion;
CScanLoader();
CScan* LoadScanMP1(CInputStream& SCAN);
CScan* LoadScanMP2(CInputStream& SCAN);
void LoadParamsMP2(CInputStream& SCAN);
public:
static CScan* LoadSCAN(CInputStream& SCAN);
};
#endif // CSCANLOADER_H

View File

@@ -0,0 +1,464 @@
#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 *tmp)
{
u32 StructStart = SCLY.Tell();
CPropertyStruct *PropStruct = new CPropertyStruct();
PropStruct->tmp = tmp;
// Verify property count
s32 TemplatePropCount = tmp->TemplateCount();
if (TemplatePropCount >= 0)
{
u32 FilePropCount = SCLY.ReadLong();
if (TemplatePropCount != FilePropCount)
Log::FileWarning(SCLY.GetSourceString(), StructStart, "Struct \"" + tmp->Name() + "\" template prop count doesn't match file");
}
// Parse properties
u32 PropCount = tmp->Count();
PropStruct->Reserve(PropCount);
for (u32 p = 0; p < PropCount; p++)
{
CPropertyBase *prop = nullptr;
CPropertyTemplate *proptmp = tmp->PropertyByIndex(p);
EPropertyType type = proptmp->Type();
switch (type)
{
case eBoolProperty: {
bool v = (SCLY.ReadByte() == 1);
prop = new CBoolProperty(v);
break;
}
case eByteProperty: {
char v = SCLY.ReadByte();
prop = new CByteProperty(v);
break;
}
case eShortProperty: {
short v = SCLY.ReadShort();
prop = new CShortProperty(v);
break;
}
case eLongProperty: {
long v = SCLY.ReadLong();
prop = new CLongProperty(v);
break;
}
case eFloatProperty: {
float v = SCLY.ReadFloat();
prop = new CFloatProperty(v);
break;
}
case eStringProperty: {
std::string v = SCLY.ReadString();
prop = new CStringProperty(v);
break;
}
case eVector3Property: {
CVector3f v(SCLY);
prop = new CVector3Property(v);
break;
}
case eColorProperty: {
CVector4f color(SCLY);
CColor v(color.x, color.y, color.z, color.w);
prop = new CColorProperty(v);
break;
}
case eFileProperty: {
u32 ResID = SCLY.ReadLong();
const CStringList& Extensions = static_cast<CFileTemplate*>(proptmp)->Extensions();
CResource *pRes = nullptr;
for (auto it = Extensions.begin(); it != Extensions.end(); it++)
{
const std::string& ext = *it;
if ((ext != "MREA") && (ext != "MLVL")) // Let's avoid recursion please
pRes = gResCache.GetResource(ResID, ext);
if (pRes) break;
}
prop = new CFileProperty(pRes);
break;
}
case eStructProperty: {
CStructTemplate *StructTmp = tmp->StructByIndex(p);
prop = LoadStructMP1(SCLY, StructTmp);
break;
}
}
if (prop)
{
prop->tmp = proptmp;
PropStruct->Properties.push_back(prop);
}
}
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 *tmp = mpMaster->TemplateByID((u32) type);
if (!tmp)
{
// No valid template for this object; can't load
Log::FileError(SCLY.GetSourceString(), ObjStart, "Invalid object ID encountered - " + StringUtil::ToHexString(type));
SCLY.Seek(end, SEEK_SET);
return nullptr;
}
mpObj = new CScriptObject(mpArea, mpLayer, tmp);
mpObj->mInstanceID = SCLY.ReadLong();
// Load connections
u32 numConnections = SCLY.ReadLong();
mpObj->mOutConnections.reserve(numConnections);
for (u32 c = 0; c < numConnections; c++)
{
SLink con;
con.State = SCLY.ReadLong();
con.Message = SCLY.ReadLong();
con.ObjectID = SCLY.ReadLong();
mpObj->mOutConnections.push_back(con);
}
// Load object...
CStructTemplate *base = tmp->BaseStruct();
mpObj->mpProperties = LoadStructMP1(SCLY, base);
SetupAttribs();
// Cleanup and return
SCLY.Seek(end, SEEK_SET);
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
if (!pTemp->IsSingleProperty())
{
u16 NumProperties = SCLY.ReadShort();
if ((pTemp->TemplateCount() >= 0) && (NumProperties != pTemp->TemplateCount()))
Log::FileWarning(SCLY.GetSourceString(), SCLY.Tell() - 2, "Struct \"" + pTemp->Name() + "\" template property count doesn't match file");
}
// Parse properties
u32 PropCount = pTemp->Count();
pStruct->Reserve(PropCount);
for (u32 p = 0; p < PropCount; p++)
{
CPropertyBase *pProp;
CPropertyTemplate *pPropTemp;
u32 PropertyStart = SCLY.Tell();
u32 PropertyID = -1;
u16 PropertyLength = 0;
u32 NextProperty = 0;
if (pTemp->IsSingleProperty())
{
pProp = pStruct->PropertyByIndex(p);
pPropTemp = pTemp->PropertyByIndex(p);
}
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 " + StringUtil::ToHexString(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 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 CStringList& 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 std::string& 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))
{
std::string 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 + " " + StringUtil::ToHexString(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;
}
}
}
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 = CScriptObject::CopyFromTemplate(pTemplate, mpArea, mpLayer);
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
CStructTemplate *pBase = pTemplate->BaseStruct();
SCLY.Seek(0x6, SEEK_CUR); // Skip base struct ID + size
LoadStructMP2(SCLY, mpObj->mpProperties, pBase);
SetupAttribs();
SCLY.Seek(ObjEnd, SEEK_SET);
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: " + StringUtil::ToHexString((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;
}
void CScriptLoader::SetupAttribs()
{
// Add template attributes
u32 numAttribs = mpObj->mpTemplate->AttribCount();
for (u32 a = 0; a < numAttribs; a++)
{
CAttribTemplate *AttribTmp = mpObj->mpTemplate->Attrib(a);
CPropertyBase *prop = mpObj->PropertyByName( AttribTmp->Target() );
// Check for static resource
CResource *res = nullptr;
std::string ResStr = AttribTmp->Resource();
if (!ResStr.empty())
res = gResCache.GetResource(ResStr);
mpObj->mAttribs.emplace_back(CScriptObject::SAttrib(AttribTmp->Type(), res, AttribTmp->Settings(), prop) );
mpObj->mAttribFlags |= AttribTmp->Type();
}
// Initial attribute evaluation
mpObj->EvaluateInstanceName();
mpObj->EvalutateXForm();
mpObj->EvaluateTevColor();
mpObj->EvaluateDisplayModel();
}
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);
}

View 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

View 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] = StringUtil::UTF8to16(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(NameCount);
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 = ePrimeKioskDemo;
}
if (Version != ePrimeKioskDemo)
{
Log::FileError(STRG.GetSourceString(), "Invalid STRG magic: " + StringUtil::ToHexString(Magic));
return nullptr;
}
}
else
{
u32 FileVersion = STRG.ReadLong();
Version = GetFormatVersion(FileVersion);
if (FileVersion == eUnknownVersion)
{
Log::FileError(STRG.GetSourceString(), "Unsupported STRG version: " + StringUtil::ToHexString(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 == ePrimeKioskDemo) 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;
}
}

View File

@@ -0,0 +1,24 @@
#ifndef CSTRINGLOADER_H
#define CSTRINGLOADER_H
#include "../CStringTable.h"
#include "../EFormatVersion.h"
#include <Core/CResCache.h>
class CStringLoader
{
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

View File

@@ -0,0 +1,644 @@
#include "CTemplateLoader.h"
#include "CWorldLoader.h"
#include "../script/EAttribType.h"
#include <Core/Log.h>
// ************ PROPERTY ************
CPropertyTemplate* CTemplateLoader::LoadPropertyTemplate(tinyxml2::XMLElement *pElem, const std::string& TemplateName)
{
const char *pElemName = pElem->Name();
// Load multi-property struct
if (strcmp(pElemName, "struct") == 0)
{
CStructTemplate *pStruct = LoadStructTemplate(pElem, TemplateName);
if (pStruct)
pStruct->mIsSingleProperty = false;
return pStruct;
}
else if (strcmp(pElemName, "property") == 0)
{
// Get name, type, and ID
std::string Name;
EPropertyType Type;
u32 ID;
GetPropertyInfo(pElem, Name, Type, ID);
// Error check
if (Type == eInvalidProperty)
{
const char *pType = pElem->Attribute("type");
if (pType)
Log::Error("Invalid property type in " + TemplateName + " template: " + pType);
else
Log::Error("Property " + Name + " in " + TemplateName + " template has no type");
}
// Load single-property struct
if (Type == eStructProperty)
{
CStructTemplate *pStruct = LoadStructTemplate(pElem, TemplateName);
pStruct->mIsSingleProperty = true;
return pStruct;
}
// Load file property
else if (Type == eFileProperty)
{
// Fetch file extension
CFileTemplate *pFile = nullptr;
const char *pExt = pElem->Attribute("ext");
if (pExt)
pFile = new CFileTemplate(Name, ID, StringUtil::Tokenize(pExt, ","));
else
{
CFileTemplate *pSrc = (CFileTemplate*) mpMaster->GetProperty(ID);
if (pSrc)
pFile = new CFileTemplate(Name, ID, pSrc->Extensions());
}
// Check if extensions are valid
if (!pFile)
{
Log::Error("File property " + Name + " in " + TemplateName + " template has no extension");
return nullptr;
}
else
return pFile;
}
// Load regular property
else
{
CPropertyTemplate *pProperty = new CPropertyTemplate(Type, Name, ID);
return pProperty;
}
}
return nullptr;
}
CStructTemplate* CTemplateLoader::LoadStructTemplate(tinyxml2::XMLElement *pElem, const std::string& TemplateName)
{
CStructTemplate *pStruct = new CStructTemplate();
// Get name, type, and ID
GetPropertyInfo(pElem, pStruct->mPropName, pStruct->mPropType, pStruct->mPropID);
const char *pTemp = pElem->Attribute("template");
if (!pTemp) pTemp = pElem->Attribute("target");
// Get source template from the master property list, if it exists
CStructTemplate *pSrc = (CStructTemplate*) mpMaster->GetProperty(pStruct->mPropID);
// "IsSingleProperty" means, does the struct contain multiple properties, each with separate IDs
// or does the entire struct as a whole count as just one property?
if (pSrc)
pStruct->mIsSingleProperty = pSrc->IsSingleProperty();
else
pStruct->mIsSingleProperty = (strcmp(pElem->Name(), "property") == 0);
// Read struct children. Priority is [Embedded -> Template -> Master].
// Embedded
if (!pElem->NoChildren())
{
// Get count
const char *pCount = pElem->Attribute("count");
if (pCount)
pStruct->mPropertyCount = std::stoul(pCount);
// Parse sub-elements
tinyxml2::XMLElement *pChild = pElem->FirstChildElement();
while (pChild)
{
CPropertyTemplate *pProp = LoadPropertyTemplate(pChild, TemplateName);
if (pProp)
pStruct->mProperties.push_back(pProp);
pChild = pChild->NextSiblingElement();
}
}
// Template
else if (pTemp)
{
// Get handle for XML
std::string TempPath = mMasterDir + pTemp;
tinyxml2::XMLDocument TempXML;
TempXML.LoadFile(TempPath.c_str());
if (TempXML.Error())
Log::Error("Couldn't open struct template: " + TempPath);
else
{
tinyxml2::XMLElement *pVersionElem = TempXML.FirstChildElement()->FirstChildElement("version");
tinyxml2::XMLElement *pPropertiesElem = TempXML.FirstChildElement()->FirstChildElement("properties");
if (!pVersionElem) Log::Error("Struct template has no version element: " + TempPath);
if (!pPropertiesElem) Log::Error("Struct template has no properties element: " + TempPath);
if (pVersionElem && pPropertiesElem)
{
// Get version number
u32 VersionNumber = std::stoul(pVersionElem->GetText());
// Get property count
const char *pCount = pPropertiesElem->Attribute("count");
if (pCount)
pStruct->mPropertyCount = std::stoul(pCount);
// Parse properties
tinyxml2::XMLElement *pPropElem = pPropertiesElem->FirstChildElement();
while (pPropElem)
{
if (!pPropElem) break;
CPropertyTemplate *pProp = LoadPropertyTemplate(pPropElem, TemplateName);
if (pProp)
pStruct->mProperties.push_back(pProp);
pPropElem = pPropElem->NextSiblingElement();
}
}
}
}
// Master
else if (pSrc)
{
pStruct->mPropertyCount = pSrc->TemplateCount();
for (u32 p = 0; p < pSrc->Count(); p++)
pStruct->mProperties.push_back(pSrc->PropertyByIndex(p));
}
// If it's none of these things, then it probably has no children because it's a property list entry
return pStruct;
}
void CTemplateLoader::GetPropertyInfo(tinyxml2::XMLElement *pElem, std::string& Name, EPropertyType& Type, u32& ID)
{
const char *pNameStr = pElem->Attribute("name");
const char *pTypeStr = pElem->Attribute("type");
const char *pIDStr = pElem->Attribute("ID");
bool IsBaseStruct = (strcmp(pElem->Name(), "properties") == 0);
// Fetch source template, if available
CPropertyTemplate *pSrcTmp;
if (pIDStr)
{
ID = std::stoul(pIDStr, 0, 16);
pSrcTmp = mpMaster->GetProperty(ID);
}
else
{
ID = 0xFFFFFFFF;
pSrcTmp = nullptr;
}
// Get name
if (pNameStr)
Name = pNameStr;
else if (pSrcTmp)
Name = pSrcTmp->Name();
else if (IsBaseStruct)
Name = "Base";
else
Name = "";
// Get type
if (strcmp(pElem->Name(), "struct") == 0)
Type = eStructProperty;
else if (IsBaseStruct)
Type = eStructProperty;
else if (pTypeStr)
Type = PropStringToPropEnum(pTypeStr);
else if (pSrcTmp)
Type = pSrcTmp->Type();
else
Type = eInvalidProperty;
}
// ************ SCRIPT OBJECT ************
CScriptTemplate* CTemplateLoader::LoadScriptTemplate(tinyxml2::XMLDocument *pDoc, const std::string& TemplateName, u32 ObjectID)
{
tinyxml2::XMLElement *pBaseElement = pDoc->FirstChildElement();
CScriptTemplate *pScript = new CScriptTemplate(mpMaster);
pScript->mObjectID = ObjectID;
pScript->mTemplateName = pBaseElement->Name();
// Properties?
tinyxml2::XMLElement *pProperties = pBaseElement->FirstChildElement("properties");
if (pProperties)
{
pScript->mpBaseStruct = LoadStructTemplate(pBaseElement->FirstChildElement("properties"), TemplateName);
pScript->mpBaseStruct->SetName(pScript->mTemplateName);
}
// Attribs?
tinyxml2::XMLElement *pAttributes = pBaseElement->FirstChildElement("attributes");
if (pAttributes) LoadScriptAttribs(pAttributes, pScript);
return pScript;
}
void CTemplateLoader::LoadScriptAttribs(tinyxml2::XMLElement *pElem, CScriptTemplate *pScript)
{
// Parsing attribs
tinyxml2::XMLElement *pAttrib = pElem->FirstChildElement("attrib");
while (pAttrib)
{
CAttribTemplate Attrib;
Attrib.ExtraSettings = -1;
const char *pType = pAttrib->Attribute("type");
if (!pType)
Log::Error("An attrib in " + pScript->TemplateName() + " template has no type set");
else
{
// Initialize attrib template values
Attrib.AttribType = AttribStringToAttribEnum(pType);
if (Attrib.AttribType == eInvalidAttrib)
Log::Error("An attrib in " + pScript->TemplateName() + " template has an invalid type: " + pType);
else
{
bool NoError = ParseAttribExtra(pAttrib, Attrib, pScript->TemplateName());
if (NoError)
{
Attrib.AttribTarget = pAttrib->Attribute("target");
CPropertyTemplate *pTargetProp = nullptr;
if (Attrib.ResFile.empty())
{
pTargetProp = pScript->mpBaseStruct->PropertyByName(Attrib.AttribTarget); // Ensure target is valid if it points to a property
if (!pTargetProp)
Log::Error("An attrib in " + pScript->TemplateName() + " template of type " + pType + " has an invalid target: " + Attrib.AttribTarget);
}
if ((pTargetProp) || (!Attrib.ResFile.empty()))
pScript->mAttribs.push_back(Attrib);
}
}
}
pAttrib = pAttrib->NextSiblingElement("attrib");
}
}
bool CTemplateLoader::ParseAttribExtra(tinyxml2::XMLElement *pElem, CAttribTemplate& Attrib, const std::string& TemplateName)
{
// This function is for parsing extra tags that some attribs have, such as "source" for models or "forcenode" for animsets
// AnimSet
if (Attrib.Type() == eAnimSetAttrib)
{
// Check res source
const char *pSource = pElem->Attribute("source");
if ((pSource) && (strcmp(pSource, "file") == 0))
{
const char *pFileName = pElem->Attribute("target");
if (pFileName)
Attrib.ResFile = std::string("../resources/") + pFileName;
else
{
Log::Error("An attrib in " + TemplateName + " template of type animset has an invalid target: \"" + pFileName + "\"");
return false;
}
}
// Check forcenode
const char *pForceNode = pElem->Attribute("forcenode");
if (pForceNode)
{
if (!StringUtil::IsHexString(pForceNode))
{
Log::Error("An animset attrib in " + TemplateName + " has an invalid \"forcenode\" setting: \"" + pForceNode + "\"");
return false;
}
else
Attrib.ExtraSettings = std::stoul(pForceNode);
}
}
// Model
if (Attrib.Type() == eModelAttrib)
{
// Check res source
const char *pSource = pElem->Attribute("source");
if ((pSource) && (strcmp(pSource, "file") == 0))
{
const char *pFileName = pElem->Attribute("target");
if (pFileName)
Attrib.ResFile = std::string("../resources/") + pFileName;
else
{
Log::Error("An attrib in " + TemplateName + " template of type model has an invalid target: \"" + pFileName + "\"");
return false;
}
}
}
// Volume
if (Attrib.Type() == eVolumeAttrib)
{
const char *pShape = pElem->Attribute("shape");
if (pShape)
{
if (strcmp(pShape, "Box") == 0)
Attrib.ExtraSettings = 0;
else if (strcmp(pShape, "OrientedBox") == 0)
Attrib.ExtraSettings = 1;
else if (strcmp(pShape, "Sphere") == 0)
Attrib.ExtraSettings = 2;
else
{
Log::Error("Volume attrib in " + TemplateName + " template has an invalid shape: " + pShape);
return false;
}
}
else
{
Log::Error("Volume attrib in " + TemplateName + " template has no shape attribute");
return false;
}
}
return true;
}
// ************ MASTER ************
void CTemplateLoader::LoadMasterTemplate(tinyxml2::XMLDocument *pDoc)
{
tinyxml2::XMLNode *pNode = pDoc->FirstChild()->NextSibling()->FirstChild();
FILE *f = fopen("a.txt", "w");
while (pNode)
{
tinyxml2::XMLElement *pElem = pNode->ToElement();
// Version
if (strcmp(pElem->Name(), "version") == 0)
{
u32 Version = std::stoul(pElem->GetText());
mpMaster->mVersion = Version;
}
// Properties
else if (strcmp(pElem->Name(), "properties") == 0)
{
std::string PropListPath = mMasterDir + pElem->GetText();
tinyxml2::XMLDocument PropListXML;
PropListXML.LoadFile(PropListPath.c_str());
if (PropListXML.Error())
Log::Error("Couldn't open property list: " + PropListPath);
else
LoadPropertyList(&PropListXML, PropListPath);
}
// Objects
else if (strcmp(pElem->Name(), "objects") == 0)
{
// Iterate categories
tinyxml2::XMLElement *pCat = pElem->FirstChildElement("category");
while (pCat)
{
CTemplateCategory Cat(pCat->Attribute("name"));
tinyxml2::XMLElement *pObj = pCat->FirstChildElement("object");
while (pObj)
{
// ID can either be a hex number or an ASCII fourCC
std::string StrID = pObj->Attribute("ID");
u32 ID;
if (StringUtil::IsHexString(StrID, true))
ID = std::stoul(StrID, 0, 16);
else
ID = CFourCC(StrID).ToLong();
// Load up the object
std::string TemplateName = pObj->Attribute("template");
std::string TemplatePath = mMasterDir + TemplateName;
tinyxml2::XMLDocument ObjectXML;
ObjectXML.LoadFile(TemplatePath.c_str());
if (ObjectXML.Error())
Log::Error("Couldn't open script template: " + TemplatePath);
else
{
CScriptTemplate *pTemp = LoadScriptTemplate(&ObjectXML, TemplateName, ID);
if (pTemp)
{
mpMaster->mTemplates[ID] = pTemp;
Cat.AddTemplate(pTemp);
std::string ID = "| <code>" + CFourCC(pTemp->ObjectID()).ToString() + "</code>\n";
std::string Name = "| [[" + pTemp->TemplateName() + " (Metroid Prime 2)|" + pTemp->TemplateName() + "]]\n";
fprintf(f, ID.c_str());
fprintf(f, Name.c_str());
fprintf(f, "| \n");
fprintf(f, "|-\n");
}
}
pObj = pObj->NextSiblingElement("object");
}
Cat.Sort();
mpMaster->mCategories.push_back(Cat);
pCat = pCat->NextSiblingElement("category");
}
}
// States
else if (strcmp(pElem->Name(), "states") == 0)
{
tinyxml2::XMLElement *pState = pElem->FirstChildElement("state");
while (pState)
{
std::string StrID = pState->Attribute("ID");
u32 StateID;
if (StringUtil::IsHexString(StrID, true))
StateID = std::stoul(StrID, 0, 16);
else
StateID = CFourCC(StrID).ToLong();
std::string StateName = pState->Attribute("name");
mpMaster->mStates[StateID] = StateName;
pState = pState->NextSiblingElement("state");
std::string ID = "| <code>" + StrID + "</code>\n";
std::string Name = "| " + StateName + "\n";
fprintf(f, ID.c_str());
fprintf(f, Name.c_str());
fprintf(f, "|-\n");
}
}
// Messages
else if (strcmp(pElem->Name(), "messages") == 0)
{
tinyxml2::XMLElement *pMessage = pElem->FirstChildElement("message");
while (pMessage)
{
std::string StrID = pMessage->Attribute("ID");
u32 MessageID;
if (StringUtil::IsHexString(StrID, true))
MessageID = std::stoul(StrID, 0, 16);
else
MessageID = CFourCC(StrID).ToLong();
std::string MessageName = pMessage->Attribute("name");
mpMaster->mMessages[MessageID] = MessageName;
pMessage = pMessage->NextSiblingElement("message");
std::string ID = "| <code>" + StrID + "</code>\n";
std::string Name = "| " + MessageName + "\n";
fprintf(f, ID.c_str());
fprintf(f, Name.c_str());
fprintf(f, "|-\n");
}
}
pNode = pNode->NextSibling();
}
}
void CTemplateLoader::LoadPropertyList(tinyxml2::XMLDocument *pDoc, const std::string& ListName)
{
tinyxml2::XMLElement *pElem = pDoc->FirstChildElement()->FirstChildElement();
while (pElem)
{
CPropertyTemplate *pProp = LoadPropertyTemplate(pElem, ListName);
if (pProp)
mpMaster->mPropertyList[pProp->PropertyID()] = pProp;
pElem = pElem->NextSiblingElement();
}
}
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)
{
std::string MasterPath = mTemplatesDir + pGameElem->GetText();
mMasterDir = StringUtil::GetFileDirectory(MasterPath);
tinyxml2::XMLDocument MasterXML;
MasterXML.LoadFile(MasterPath.c_str());
if (MasterXML.Error())
Log::Error("Couldn't open master template at " + MasterPath + " - error " + std::to_string(MasterXML.ErrorID()));
else
LoadMasterTemplate(&MasterXML);
}
pGameElem = pGameElem->NextSiblingElement();
}
mpMaster->mFullyLoaded = true;
return mpMaster;
}
// ************ PUBLIC ************
void CTemplateLoader::LoadGameList()
{
static const std::string TemplatesDir = "../templates/";
static const std::string GameListPath = TemplatesDir + "GameList.xml";
Log::Write("Loading game list");
// Load Game List XML
tinyxml2::XMLDocument GameListXML;
GameListXML.LoadFile(GameListPath.c_str());
if (GameListXML.Error())
{
Log::Error("Couldn't open game list at " + GameListPath + " - 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(TemplatesDir);
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();
}
}

View File

@@ -0,0 +1,36 @@
#ifndef CTEMPLATELOADER_H
#define CTEMPLATELOADER_H
#include "../script/CMasterTemplate.h"
#include "../script/CScriptTemplate.h"
#include <tinyxml2.h>
class CTemplateLoader
{
CMasterTemplate *mpMaster;
std::string mTemplatesDir;
std::string mMasterDir;
// Constructor
CTemplateLoader(const std::string& TemplatesDir) : mTemplatesDir(TemplatesDir) {}
// Load Property
CPropertyTemplate* LoadPropertyTemplate(tinyxml2::XMLElement *pElem, const std::string& TemplateName);
CStructTemplate* LoadStructTemplate(tinyxml2::XMLElement *pElem, const std::string& TemplateName);
void GetPropertyInfo(tinyxml2::XMLElement *pElem, std::string& Name, EPropertyType& Type, u32& ID);
// Load Script Object
CScriptTemplate* LoadScriptTemplate(tinyxml2::XMLDocument *pDoc, const std::string& TemplateName, u32 ObjectID);
void LoadScriptAttribs(tinyxml2::XMLElement *pElem, CScriptTemplate *pScript);
bool ParseAttribExtra(tinyxml2::XMLElement *pElem, CAttribTemplate& Attrib, const std::string& TemplateName);
// Load Master
void LoadMasterTemplate(tinyxml2::XMLDocument *pDoc);
void LoadPropertyList(tinyxml2::XMLDocument *pDoc, const std::string& ListName);
CMasterTemplate* LoadGame(tinyxml2::XMLNode *pNode);
public:
static void LoadGameList();
};
#endif // CTEMPLATELOADER_H

View 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[] = {
1.f, 1.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: " + StringUtil::ToHexString((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;
}

View 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

View File

@@ -0,0 +1,341 @@
#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 = (CStringTable*) gResCache.GetResource(MLVL.ReadLong(), "STRG");
if (mVersion == eEchoes) mpWorld->mpDarkWorldName = (CStringTable*) gResCache.GetResource(MLVL.ReadLong(), "STRG");
if (mVersion > ePrime) mpWorld->mUnknown1 = MLVL.ReadLong();
if (mVersion >= ePrime) mpWorld->mpSaveWorld = gResCache.GetResource(MLVL.ReadLong(), "SAVW");
mpWorld->mpDefaultSkybox = (CModel*) gResCache.GetResource(MLVL.ReadLong(), "CMDL");
}
else
{
mpWorld->mpWorldName = (CStringTable*) gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
MLVL.Seek(0x4, SEEK_CUR); // Skipping unknown value
mpWorld->mpSaveWorld = gResCache.GetResource(MLVL.ReadLongLong(), "SAVW");
mpWorld->mpDefaultSkybox = (CModel*) 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 = (CStringTable*) gResCache.GetResource(MLVL.ReadLong(), "STRG");
else
pArea->pAreaName = (CStringTable*) gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
pArea->AreaNameToken = CToken(pArea->pAreaName);
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
mpWorld->mResTokens[0] = CToken(mpWorld->mpWorldName);
mpWorld->mResTokens[1] = CToken(mpWorld->mpDarkWorldName);
mpWorld->mResTokens[2] = CToken(mpWorld->mpSaveWorld);
mpWorld->mResTokens[3] = CToken(mpWorld->mpDefaultSkybox);
mpWorld->mResTokens[4] = CToken(mpWorld->mpMapWorld);
}
void CWorldLoader::LoadReturnsMLVL(CInputStream& MLVL)
{
mpWorld->mpWorldName = (CStringTable*) gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
bool Check = MLVL.ReadByte();
if (Check)
{
MLVL.ReadString();
MLVL.Seek(0x10, SEEK_CUR);
}
mpWorld->mpSaveWorld = gResCache.GetResource(MLVL.ReadLongLong(), "SAVW");
mpWorld->mpDefaultSkybox = (CModel*) 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 = (CStringTable*) gResCache.GetResource(MLVL.ReadLongLong(), "STRG");
pArea->Transform = CTransform4f(MLVL);
pArea->AetherBox = CAABox(MLVL);
pArea->FileID = MLVL.ReadLongLong();
pArea->AreaID = MLVL.ReadLongLong();
pArea->AreaNameToken = CToken(pArea->pAreaName);
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
mpWorld->mResTokens[0] = CToken(mpWorld->mpWorldName);
mpWorld->mResTokens[1] = CToken(mpWorld->mpDarkWorldName);
mpWorld->mResTokens[2] = CToken(mpWorld->mpSaveWorld);
mpWorld->mResTokens[3] = CToken(mpWorld->mpDefaultSkybox);
mpWorld->mResTokens[4] = CToken(mpWorld->mpMapWorld);
}
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: " + StringUtil::ToHexString(Magic));
return nullptr;
}
u32 FileVersion = MLVL.ReadLong();
EGame Version = GetFormatVersion(FileVersion);
if (Version == eUnknownVersion)
{
Log::FileError(MLVL.GetSourceString(), "Unsupported MLVL version: " + StringUtil::ToHexString(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 ePrimeKioskDemo;
case 0x11: return ePrime;
case 0x14: return eEchoesDemo;
case 0x17: return eEchoes;
case 0x19: return eCorruption;
case 0x1B: return eReturns;
default: return eUnknownVersion;
}
}

View 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
{
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

View File

@@ -0,0 +1,57 @@
#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];
}
EResType CBasicModel::Type()
{
return eModel;
}
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];
}

View 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
{
protected:
CAABox mAABox;
u32 mVertexCount;
u32 mTriangleCount;
bool mBuffered;
bool mHasOwnMaterials;
bool mHasOwnSurfaces;
CVertexBuffer mVBO;
std::vector<SSurface*> mSurfaces;
public:
CBasicModel();
~CBasicModel();
EResType Type();
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

207
Resource/model/CModel.cpp Normal file
View File

@@ -0,0 +1,207 @@
#include "CModel.h"
#include <Core/CRenderer.h>
#include <OpenGL/GLCommon.h>
CModel::CModel() : CBasicModel()
{
mHasOwnMaterials = true;
mHasOwnSurfaces = true;
}
CModel::CModel(SModelData *pModelData) : CBasicModel()
{
SetData(pModelData);
mHasOwnMaterials = false;
mHasOwnSurfaces = true;
}
CModel::CModel(SModelData *data, CMaterialSet *pMatSet) : CBasicModel()
{
SetData(data);
mMaterialSets.resize(1);
mMaterialSets[0] = pMatSet;
mHasOwnMaterials = false;
mHasOwnSurfaces = true;
}
CModel::~CModel()
{
if (mHasOwnMaterials)
for (u32 m = 0; m < mMaterialSets.size(); m++)
delete mMaterialSets[m];
}
void CModel::SetData(SModelData *pModelData)
{
mAABox = pModelData->mAABox;
mSurfaces = pModelData->mSurfaces;
mVertexCount = 0;
mTriangleCount = 0;
for (u32 iSurf = 0; iSurf < mSurfaces.size(); iSurf++)
{
SSurface *pSurf = mSurfaces[iSurf];
mVertexCount += pSurf->VertexCount;
mTriangleCount += pSurf->TriangleCount;
}
}
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
SSurface *pSurf = mSurfaces[Surface];
CMaterial *pMat = mMaterialSets[MatSet]->materials[pSurf->MaterialID];
if ((Options & eNoMaterialSetup) == 0)
{
if ((!(Options & eEnableOccluders)) && (pMat->Options() & CMaterial::eOccluder))
return;
pMat->SetCurrent(Options);
}
// Draw IBOs
mVBO.Bind();
for (u32 iIBO = 0; iIBO < mSubmeshIndexBuffers[Surface].size(); iIBO++)
{
CIndexBuffer *pIBO = &mSubmeshIndexBuffers[Surface][iIBO];
pIBO->DrawElements();
}
mVBO.Unbind();
}
u32 CModel::GetMatSetCount()
{
return mMaterialSets.size();
}
u32 CModel::GetMatCount()
{
if (mMaterialSets.empty()) return 0;
else return mMaterialSets[0]->materials.size();
}
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]->materials[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]->materials.size(); iMat++)
if (mMaterialSets[MatSet]->materials[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]->materials[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();
}

46
Resource/model/CModel.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef CMODEL_H
#define CMODEL_H
#include "CBasicModel.h"
#include "SModelData.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(SModelData *pModelData);
CModel(SModelData *pModelData, CMaterialSet *pMatSet);
~CModel();
void SetData(SModelData *pModelData);
void BufferGL();
void ClearGLBuffer();
void Draw(ERenderOptions Options, u32 MatSet);
void DrawSurface(ERenderOptions Options, u32 Surface, u32 MatSet);
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

View File

@@ -0,0 +1,177 @@
#include "CStaticModel.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();
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();
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();
}
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();
}

View File

@@ -0,0 +1,39 @@
#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);
CMaterial* GetMaterial();
void SetMaterial(CMaterial *pMat);
bool IsTransparent();
bool IsOccluder();
private:
CIndexBuffer* InternalGetIBO(EGXPrimitiveType Primitive);
};
#endif // CSTATICMODEL_H

42
Resource/model/CVertex.h Normal file
View 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

View 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

View File

@@ -0,0 +1,13 @@
#ifndef SMODELDATA_H
#define SMODELDATA_H
#include "SSurface.h"
#include <Common/CAABox.h>
struct SModelData
{
CAABox mAABox;
std::vector<SSurface*> mSurfaces;
};
#endif // SMODELDATA_H

View File

@@ -0,0 +1,89 @@
#include "SSurface.h"
#include <Common/CRayCollisionTester.h>
#include <Common/Math.h>
#include <Core/CDrawUtil.h>
std::pair<bool,float> SSurface::IntersectsRay(const CRay& Ray, const CTransform4f& Transform)
{
//CDrawUtil::DrawWireCube(AABox.Transformed(Transform), CColor::skRed);
bool Hit = false;
float HitDist;
for (u32 iPrim = 0; iPrim < Primitives.size(); iPrim++)
{
SPrimitive *pPrim = &Primitives[iPrim];
u32 NumVerts = pPrim->Vertices.size();
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;
CColor LineColor;
if (pPrim->Type == eGX_Triangles)
LineColor = CColor::skRed;
else if (pPrim->Type == eGX_TriangleStrip)
LineColor = CColor::skYellow;
else if (pPrim->Type == eGX_TriangleFan)
LineColor = CColor::skGreen;
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);
if (TriResult.first)
{
if ((!Hit) || (TriResult.second < HitDist))
{
Hit = true;
HitDist = TriResult.second;
}
}
}
}
// todo: Intersection tests for line primitives
}
return std::pair<bool,float>(Hit, HitDist);
}

39
Resource/model/SSurface.h Normal file
View 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, const CTransform4f& Transform = CTransform4f::skIdentity);
};
#endif // SSURFACE_H

View File

@@ -0,0 +1,112 @@
#include "CMasterTemplate.h"
#include "../factory/CWorldLoader.h"
#include <Core/Log.h>
CMasterTemplate::CMasterTemplate()
{
mVersion = 0;
mFullyLoaded = 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;
}
std::string CMasterTemplate::StateByID(u32 StateID)
{
auto it = mStates.find(StateID);
if (it != mStates.end())
return it->second;
else
return "Invalid";
}
std::string CMasterTemplate::StateByID(const CFourCC& State)
{
return StateByID(State.ToLong());
}
std::string CMasterTemplate::StateByIndex(u32 Index)
{
auto it = mStates.begin();
return (std::next(it, Index))->second;
}
std::string CMasterTemplate::MessageByID(u32 MessageID)
{
auto it = mMessages.find(MessageID);
if (it != mMessages.end())
return it->second;
else
return "Invalid";
}
std::string CMasterTemplate::MessageByID(const CFourCC& MessageID)
{
return MessageByID(MessageID.ToLong());
}
std::string 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::IsLoadedSuccessfully()
{
return mFullyLoaded;
}
// ************ STATIC ************
std::unordered_map<EGame, CMasterTemplate*> CMasterTemplate::smMasterMap;
u32 CMasterTemplate::smGameListVersion;
CMasterTemplate* CMasterTemplate::GetMasterForGame(EGame Game)
{
auto it = smMasterMap.find(Game);
if (it != smMasterMap.end())
return it->second;
else
return nullptr;
}

View File

@@ -0,0 +1,66 @@
#ifndef CMASTERTEMPLATE_H
#define CMASTERTEMPLATE_H
#include "CScriptTemplate.h"
#include "CTemplateCategory.h"
#include "../EFormatVersion.h"
#include <Common/types.h>
#include <unordered_map>
#include <tinyxml2.h>
class CMasterTemplate
{
friend class CTemplateLoader;
EGame mGame;
std::string mGameName;
u32 mVersion;
bool mFullyLoaded;
std::unordered_map<u32, CScriptTemplate*> mTemplates;
std::unordered_map<u32, std::string> mStates;
std::unordered_map<u32, std::string> mMessages;
std::vector<CTemplateCategory> mCategories;
bool mHasPropList;
std::unordered_map<u32, CPropertyTemplate*> mPropertyList;
static std::unordered_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);
std::string StateByID(u32 StateID);
std::string StateByID(const CFourCC& StateID);
std::string StateByIndex(u32 Index);
std::string MessageByID(u32 MessageID);
std::string MessageByID(const CFourCC& MessageID);
std::string MessageByIndex(u32 Index);
CPropertyTemplate* GetProperty(u32 PropertyID);
bool IsLoadedSuccessfully();
static CMasterTemplate* GetMasterForGame(EGame Game);
};
// ************ 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

227
Resource/script/CProperty.h Normal file
View File

@@ -0,0 +1,227 @@
#ifndef CPROPERTY
#define CPROPERTY
/*
* This header file declares some classes used to track script object properties
* CPropertyBase, __CProperty (and typedefs), CPropertyStruct
*/
#include "../CResource.h"
#include "CScriptTemplate.h"
#include "EPropertyType.h"
#include <Common/CColor.h>
#include <Common/CVector3f.h>
#include <Core/CToken.h>
#include <string>
#include <list>
/*
* 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 *tmp;
public:
virtual ~CPropertyBase() {}
virtual EPropertyType Type() = 0;
CPropertyTemplate *Template() { return tmp; }
void SetTemplate(CPropertyTemplate *_tmp) { tmp = _tmp; }
std::string Name() { return tmp->Name(); }
u32 ID() { return tmp->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 Value;
public:
__CProperty() {}
__CProperty(t v) { Set(v); }
~__CProperty() {}
EPropertyType Type() { return type; }
t Get() { return Value; }
void Set(t v) { Value = v; }
};
typedef __CProperty<bool, eBoolProperty> CBoolProperty;
typedef __CProperty<char, eByteProperty> CByteProperty;
typedef __CProperty<short, eShortProperty> CShortProperty;
typedef __CProperty<long, eLongProperty> CLongProperty;
typedef __CProperty<float, eFloatProperty> CFloatProperty;
typedef __CProperty<std::string, eStringProperty> CStringProperty;
typedef __CProperty<CVector3f, eVector3Property> CVector3Property;
typedef __CProperty<CColor, eColorProperty> CColorProperty;
typedef __CProperty<CResource*, eFileProperty> CFileProperty;
typedef __CProperty<std::vector<u8>, eUnknownProperty> CUnknownProperty;
/*
* Template specialization for CFileProperty to allow a token for resources
*/
template <>
class __CProperty<CResource*, eFileProperty> : public CPropertyBase
{
CResource *Value;
CToken mToken;
public:
__CProperty<CResource*, eFileProperty>() {
Value = nullptr;
}
__CProperty<CResource*, eFileProperty>(CResource* v) {
Value = v;
mToken = CToken(v);
}
~__CProperty<CResource*, eFileProperty>() {}
EPropertyType Type() { return eFileProperty; }
CResource* Get() { return Value; }
void Set(CResource *v)
{
if (Value != v)
{
Value = v;
mToken = CToken(v);
}
}
};
/*
* CPropertyStruct is for defining structs of properties.
*/
class CPropertyStruct : public CPropertyBase
{
friend class CScriptLoader;
std::vector<CPropertyBase*> Properties;
public:
// Destructor simply iterates through the list and deletes them. Nothing complicated.
~CPropertyStruct()
{
for (auto it = Properties.begin(); it != Properties.end(); it++)
delete *it;
}
EPropertyType Type() { return eStructProperty; }
u32 Count() { return Properties.size(); }
void Reserve(u32 amount) { Properties.reserve(amount); }
CPropertyBase* PropertyByIndex(u32 index) { return Properties[index]; }
CPropertyBase* PropertyByName(std::string name)
{
// Resolve namespace
std::string::size_type NsStart = name.find_first_of("::");
std::string::size_type PropStart = NsStart + 2;
// Namespace; the requested property is within a struct
if (NsStart != std::string::npos)
{
std::string StructName = name.substr(0, NsStart);
std::string PropName = name.substr(PropStart, name.length() - PropStart);
CPropertyStruct *Struct = StructByName(StructName);
if (!Struct) return nullptr;
else return Struct->PropertyByName(PropName);
}
// No namespace; fetch the property from this struct
else
{
// ID string lookup
if (StringUtil::IsHexString(name))
return PropertyByID(std::stoul(name, 0, 16));
// Name lookup
else
{
for (auto it = Properties.begin(); it != Properties.end(); it++)
{
if ((*it)->Name() == name)
return *it;
}
return nullptr;
}
}
}
CPropertyBase* PropertyByID(u32 ID)
{
for (auto it = Properties.begin(); it != Properties.end(); it++)
{
if ((*it)->ID() == ID)
return *it;
}
return nullptr;
}
CPropertyStruct* StructByIndex(u32 index)
{
CPropertyBase *prop = PropertyByIndex(index);
if (prop->Type() == eStructProperty)
return static_cast<CPropertyStruct*>(prop);
else
return nullptr;
}
CPropertyStruct* StructByName(std::string name)
{
CPropertyBase *prop = PropertyByName(name);
if (prop->Type() == eStructProperty)
return static_cast<CPropertyStruct*>(prop);
else
return nullptr;
}
CPropertyStruct* StructByID(u32 ID)
{
CPropertyBase *prop = PropertyByID(ID);
if (prop->Type() == eStructProperty)
return static_cast<CPropertyStruct*>(prop);
else
return nullptr;
}
inline CPropertyBase* operator[](u32 index) { return Properties[index]; }
static CPropertyStruct* CopyFromTemplate(CStructTemplate *pTemp)
{
CPropertyStruct *pStruct = new CPropertyStruct();
pStruct->tmp = 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 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 eUnknownProperty: pProp = new CUnknownProperty(); break;
case eStructProperty: pProp = CPropertyStruct::CopyFromTemplate(static_cast<CStructTemplate*>(pPropTemp)); break;
}
if (pProp)
{
pProp->SetTemplate(pPropTemp);
pStruct->Properties.push_back(pProp);
}
}
return pStruct;
}
};
#endif // CPROPERTY

View 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 *************
std::string 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(std::string name)
{
mLayerName = name;
}
void CScriptLayer::SetActive(bool active)
{
mActive = active;
}
void CScriptLayer::SetVisible(bool visible)
{
mVisible = visible;
}

View File

@@ -0,0 +1,48 @@
#ifndef SSCRIPTLAYER_H
#define SSCRIPTLAYER_H
#include "CScriptObject.h"
#include <Common/types.h>
#include <string>
#include <vector>
class CScriptLayer
{
std::string 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
std::string Name();
bool IsActive();
bool IsVisible();
u32 GetNumObjects();
CScriptObject* ObjectByIndex(u32 index);
CScriptObject* ObjectByID(u32 ID);
void SetName(std::string 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

View File

@@ -0,0 +1,287 @@
#include "CScriptObject.h"
#include "../CAnimSet.h"
#include "CMasterTemplate.h"
CScriptObject::CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate)
{
mpArea = pArea;
mpLayer = pLayer;
mpTemplate = pTemplate;
mpProperties = nullptr;
mAttribFlags = 0;
mpTemplate->AddObject(this);
}
CScriptObject::~CScriptObject()
{
if (mpProperties) delete mpProperties;
mpTemplate->RemoveObject(this);
}
// ************ DATA MANIPULATION ************
void CScriptObject::EvalutateXForm()
{
// Reset XForm values to defaults
mPosition = CVector3f(0);
mRotation = CVector3f(0);
mScale = CVector3f(1);
mVolumeSize = CVector3f(0);
mVolumeShape = -1;
// Look for PRS attribs
for (u32 a = 0; a < mAttribs.size(); a++)
{
if ((mAttribs[a].Type == ePositionAttrib) ||
(mAttribs[a].Type == eRotationAttrib) ||
(mAttribs[a].Type == eScaleAttrib) ||
(mAttribs[a].Type == eVolumeAttrib))
{
CVector3Property *attrib = static_cast<CVector3Property*>(mAttribs[a].Prop);
if (mAttribs[a].Type == ePositionAttrib)
mPosition = attrib->Get();
else if (mAttribs[a].Type == eRotationAttrib)
mRotation = attrib->Get();
else if (mAttribs[a].Type == eScaleAttrib)
mScale = attrib->Get();
else if (mAttribs[a].Type == eVolumeAttrib) {
mVolumeSize = attrib->Get();
mVolumeShape = mAttribs[a].Settings;
}
}
}
}
void CScriptObject::EvaluateInstanceName()
{
// Reset instance name to default
mInstanceName = mpTemplate->TemplateName();
// Simply look for an instance name - set if we find it
for (u32 a = 0; a < mAttribs.size(); a++)
{
if (mAttribs[a].Type == eNameAttrib)
{
CStringProperty *str = static_cast<CStringProperty*>(mAttribs[a].Prop);
mInstanceName = str->Get();
return;
}
}
}
void CScriptObject::EvaluateTevColor()
{
// Evaluate the TEV color initializer - this is used for beam troopers
mTevColor = CColor::skWhite; // Initialize to white in case there's no vulnerability attrib
for (u32 a = 0; a < mAttribs.size(); a++)
{
if (mAttribs[a].Type == eVulnerabilityAttrib)
{
CPropertyStruct* vuln = static_cast<CPropertyStruct*>(mAttribs[a].Prop);
u32 Power = static_cast<CLongProperty*>(vuln->PropertyByIndex(0))->Get();
u32 Ice = static_cast<CLongProperty*>(vuln->PropertyByIndex(1))->Get();
u32 Wave = static_cast<CLongProperty*>(vuln->PropertyByIndex(2))->Get();
u32 Plasma = static_cast<CLongProperty*>(vuln->PropertyByIndex(3))->Get();
if (Plasma != 2) mTevColor = CColor::skRed;
else if (Ice != 2) mTevColor = CColor::skWhite;
else if (Power != 2) mTevColor = CColor::skYellow;
else if (Wave != 2) mTevColor = CColor::skPurple;
else mTevColor = CColor::skWhite;
break;
}
}
}
void CScriptObject::EvaluateDisplayModel()
{
// Look for animset or model
for (u32 a = 0; a < mAttribs.size(); a++)
{
// Evaluate AnimSet attrib
if (mAttribs[a].Type == eAnimSetAttrib)
{
// Get the AnimationParameters struct so we can fetch relevant values from it...
SAttrib *Attrib = &mAttribs[a];
CPropertyStruct *AnimParams = static_cast<CPropertyStruct*>(Attrib->Prop);
EGame game = mpTemplate->MasterTemplate()->GetGame();
CResource *ANCS;
if (Attrib->Res)
ANCS = Attrib->Res;
else if (game <= eCorruption)
ANCS = static_cast<CFileProperty*>( (*AnimParams)[0] )->Get();
else
ANCS = static_cast<CFileProperty*>( (*AnimParams)[1] )->Get();
if ((ANCS) && (ANCS->Type() == eCharacter))
{
// Get animset + node index and return the relevant model
CAnimSet *set = static_cast<CAnimSet*>(ANCS);
u32 node;
if (mpTemplate->MasterTemplate()->GetGame() >= eCorruptionProto)
node = 0;
else if (Attrib->Settings == -1)
node = static_cast<CLongProperty*>( (*AnimParams)[1] )->Get();
else
node = Attrib->Settings;
CModel *model = set->getNodeModel(node);
if (model && (model->Type() == eModel))
{
mpDisplayModel = model;
return;
}
}
}
// Evaluate Model attrib
else if (mAttribs[a].Type == eModelAttrib)
{
SAttrib *Attrib = &mAttribs[a];
CResource *CMDL;
if (Attrib->Res)
CMDL = Attrib->Res;
else
CMDL = static_cast<CFileProperty*>(Attrib->Prop)->Get();
if (CMDL && (CMDL->Type() == eModel))
{
mpDisplayModel = static_cast<CModel*>(CMDL);
return;
}
}
}
// No valid display asset
mpDisplayModel = nullptr;
return;
}
// ************ GETTERS ************
CPropertyBase* CScriptObject::PropertyByIndex(u32 index)
{
return mpProperties->PropertyByIndex(index);
}
CPropertyBase* CScriptObject::PropertyByName(std::string name)
{
return mpProperties->PropertyByName(name);
}
CScriptTemplate* CScriptObject::Template()
{
return mpTemplate;
}
CMasterTemplate* CScriptObject::MasterTemplate()
{
return mpTemplate->MasterTemplate();
}
CGameArea* CScriptObject::Area()
{
return mpArea;
}
CScriptLayer* CScriptObject::Layer()
{
return mpLayer;
}
CPropertyStruct* CScriptObject::Properties()
{
return mpProperties;
}
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];
}
// Attribs
CVector3f CScriptObject::GetPosition() const
{
return mPosition;
}
CVector3f CScriptObject::GetRotation() const
{
return mRotation;
}
CVector3f CScriptObject::GetScale() const
{
return mScale;
}
CVector3f CScriptObject::GetVolume() const
{
return mVolumeSize;
}
u32 CScriptObject::GetVolumeShape() const
{
return mVolumeShape;
}
std::string CScriptObject::GetInstanceName() const
{
return mInstanceName;
}
CColor CScriptObject::GetTevColor() const
{
return mTevColor;
}
CModel* CScriptObject::GetDisplayModel() const
{
return mpDisplayModel;
}
int CScriptObject::GetAttribFlags() const
{
return mAttribFlags;
}
// ************ STATIC ************
CScriptObject* CScriptObject::CopyFromTemplate(CScriptTemplate *pTemp, CGameArea *pArea, CScriptLayer *pLayer)
{
CScriptObject *pObj = new CScriptObject(pArea, pLayer, pTemp);
CStructTemplate *pBaseStruct = pTemp->BaseStruct();
pObj->mpProperties = CPropertyStruct::CopyFromTemplate(pBaseStruct);
return pObj;
}

View File

@@ -0,0 +1,93 @@
#ifndef CSCRIPTOBJECT_H
#define CSCRIPTOBJECT_H
#include "SConnection.h"
#include "CProperty.h"
#include "CScriptTemplate.h"
#include "EAttribType.h"
#include "../model/CModel.h"
class CGameArea;
class CScriptLayer;
class CScriptObject
{
friend class CScriptLoader;
friend class CAreaLoader;
CScriptTemplate *mpTemplate;
CGameArea *mpArea;
CScriptLayer *mpLayer;
u32 mInstanceID;
std::vector<SLink> mOutConnections;
std::vector<SLink> mInConnections;
CPropertyStruct *mpProperties;
CVector3f mPosition, mRotation, mScale;
CVector3f mVolumeSize;
u32 mVolumeShape;
std::string mInstanceName;
CColor mTevColor;
CModel* mpDisplayModel;
struct SAttrib
{
EAttribType Type;
u32 Settings;
CResource *Res;
CToken ResToken;
CPropertyBase *Prop;
// Convenience constructor
SAttrib(EAttribType type, CResource *res, u32 settings, CPropertyBase *prop) {
Type = type;
Res = res;
ResToken = CToken(res);
Settings = settings;
Prop = prop;
}
};
std::vector<SAttrib> mAttribs;
int mAttribFlags; // int container for EAttribType flags
public:
CScriptObject(CGameArea *pArea, CScriptLayer *pLayer, CScriptTemplate *pTemplate);
~CScriptObject();
void EvaluateDisplayModel();
void EvaluateInstanceName();
void EvaluateTevColor();
void EvalutateXForm();
CScriptTemplate* Template();
CMasterTemplate* MasterTemplate();
CGameArea* Area();
CScriptLayer* Layer();
CPropertyStruct* Properties();
u32 ObjectTypeID() const;
u32 InstanceID() const;
u32 NumInLinks() const;
u32 NumOutLinks() const;
const SLink& InLink(u32 index) const;
const SLink& OutLink(u32 index) const;
CPropertyBase* PropertyByIndex(u32 index);
CPropertyBase* PropertyByName(std::string name);
CVector3f GetPosition() const;
CVector3f GetRotation() const;
CVector3f GetScale() const;
CVector3f GetVolume() const;
u32 GetVolumeShape() const;
std::string GetInstanceName() const;
CColor GetTevColor() const;
CModel* GetDisplayModel() const;
int GetAttribFlags() const;
// Static
static CScriptObject* CopyFromTemplate(CScriptTemplate *pTemp, CGameArea *pArea, CScriptLayer *pLayer);
};
#endif // CSCRIPTOBJECT_H

View File

@@ -0,0 +1,316 @@
#include "CScriptTemplate.h"
#include "CScriptObject.h"
#include "CMasterTemplate.h"
#include <iostream>
#include <string>
#include <Core/Log.h>
#include <Core/CResCache.h>
EPropertyType PropStringToPropEnum(std::string prop)
{
if (prop == "bool") return eBoolProperty;
if (prop == "byte") return eByteProperty;
if (prop == "short") return eShortProperty;
if (prop == "long") return eLongProperty;
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 == "unknown") return eUnknownProperty;
return eInvalidProperty;
}
std::string PropEnumToPropString(EPropertyType prop)
{
switch (prop)
{
case eBoolProperty: return "bool";
case eByteProperty: return "byte";
case eShortProperty: return "short";
case eLongProperty: return "long";
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 eUnknownProperty: return "unknown";
case eInvalidProperty:
default:
return "invalid";
}
}
EAttribType AttribStringToAttribEnum(const std::string& Attrib)
{
if (Attrib == "name") return eNameAttrib;
if (Attrib == "position") return ePositionAttrib;
if (Attrib == "rotation") return eRotationAttrib;
if (Attrib == "scale") return eScaleAttrib;
if (Attrib == "model") return eModelAttrib;
if (Attrib == "animset") return eAnimSetAttrib;
if (Attrib == "volume") return eVolumeAttrib;
if (Attrib == "vulnerability") return eVulnerabilityAttrib;
return eInvalidAttrib;
}
std::string AttribEnumToAttribString(EAttribType Attrib)
{
switch (Attrib)
{
case eNameAttrib: return "name";
case ePositionAttrib: return "position";
case eRotationAttrib: return "rotation";
case eScaleAttrib: return "scale";
case eModelAttrib: return "model";
case eAnimSetAttrib: return "animset";
case eVolumeAttrib: return "volume";
case eVulnerabilityAttrib: return "vulnerability";
case eInvalidAttrib:
default:
return "invalid";
}
}
/*******************
* CStructTemplate *
*******************/
CStructTemplate::CStructTemplate() : CPropertyTemplate(-1)
{
mIsSingleProperty = false;
mPropertyCount = -1;
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;
}
s32 CStructTemplate::TemplateCount() const
{
return mPropertyCount;
}
u32 CStructTemplate::Count() const
{
return mProperties.size();
}
CPropertyTemplate* CStructTemplate::PropertyByIndex(u32 index)
{
if (mProperties.size() > index)
return mProperties[index];
else
return nullptr;
}
CPropertyTemplate* CStructTemplate::PropertyByName(std::string name)
{
// Resolve namespace
std::string::size_type NsStart = name.find_first_of("::");
std::string::size_type PropStart = NsStart + 2;
// Namespace; the requested property is within a struct
if (NsStart != std::string::npos)
{
std::string StructName = name.substr(0, NsStart);
std::string PropName = name.substr(PropStart, name.length() - PropStart);
CStructTemplate *tmp = StructByName(StructName);
if (!tmp) return nullptr;
else return tmp->PropertyByName(PropName);
}
// No namespace; fetch the property from this struct
else
{
// ID string lookup
if (StringUtil::IsHexString(name))
return PropertyByID(std::stoul(name, 0, 16));
// Name lookup
else
{
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
{
if ((*it)->Name() == name)
return *it;
}
return nullptr;
}
}
}
CPropertyTemplate* CStructTemplate::PropertyByID(u32 ID)
{
for (auto it = mProperties.begin(); it != mProperties.end(); it++)
{
if ((*it)->PropertyID() == ID)
return *it;
}
return nullptr;
}
CStructTemplate* CStructTemplate::StructByIndex(u32 index)
{
CPropertyTemplate *prop = PropertyByIndex(index);
if (prop->Type() == eStructProperty)
return static_cast<CStructTemplate*>(prop);
else
return nullptr;
}
CStructTemplate* CStructTemplate::StructByName(std::string name)
{
CPropertyTemplate *prop = PropertyByName(name);
if (prop && prop->Type() == eStructProperty)
return static_cast<CStructTemplate*>(prop);
else
return nullptr;
}
CStructTemplate* CStructTemplate::StructByID(u32 ID)
{
CPropertyTemplate *prop = PropertyByID(ID);
if (prop && prop->Type() == eStructProperty)
return static_cast<CStructTemplate*>(prop);
else
return nullptr;
}
// ************ DEBUG ************
void CStructTemplate::DebugPrintProperties(std::string 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";
}
}
/*******************
* CScriptTemplate *
*******************/
CScriptTemplate::CScriptTemplate(CMasterTemplate *pMaster)
{
mpBaseStruct = nullptr;
mpMaster = pMaster;
mVisible = true;
}
CScriptTemplate::~CScriptTemplate()
{
if (mpBaseStruct)
delete mpBaseStruct;
}
CMasterTemplate* CScriptTemplate::MasterTemplate()
{
return mpMaster;
}
std::string CScriptTemplate::TemplateName() const
{
return mTemplateName;
}
CStructTemplate* CScriptTemplate::BaseStruct()
{
return mpBaseStruct;
}
u32 CScriptTemplate::AttribCount() const
{
return mAttribs.size();
}
CAttribTemplate* CScriptTemplate::Attrib(u32 index)
{
if (mAttribs.size() > index)
return &mAttribs[index];
else
return nullptr;
}
u32 CScriptTemplate::ObjectID() const
{
return mObjectID;
}
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());
});
}
void CScriptTemplate::SetVisible(bool Visible)
{
mVisible = Visible;
}
bool CScriptTemplate::IsVisible()
{
return mVisible;
}
// Debug function
void CScriptTemplate::DebugPrintProperties()
{
mpBaseStruct->DebugPrintProperties("");
}

View File

@@ -0,0 +1,141 @@
#ifndef CSCRIPTTEMPLATE_H
#define CSCRIPTTEMPLATE_H
#include "EPropertyType.h"
#include "EAttribType.h"
#include <Common/CFourCC.h>
#include <Common/types.h>
#include <list>
#include <vector>
#include <tinyxml2.h>
#include <Resource/CResource.h>
class CMasterTemplate;
/**
* CPropertyTemplate and CStructTemplate each define the layout of a single property/struct.
* The reason they're classes instead of structs is so their internal values can't be externally modified.
* CFileTemplate is a very simple subclass with one extra value - a file extension fourCC
*/
class CPropertyTemplate
{
friend class CTemplateLoader;
protected:
EPropertyType mPropType;
std::string mPropName;
u32 mPropID;
public:
CPropertyTemplate(u32 ID) { mPropID = ID; }
CPropertyTemplate(EPropertyType type, std::string name, u32 ID) : mPropType(type), mPropName(name), mPropID(ID) {}
virtual EPropertyType Type() const { return mPropType; }
inline std::string Name() const { return mPropName; }
inline u32 PropertyID() const { return mPropID; }
inline void SetName(const std::string& Name) { mPropName = Name; }
};
class CFileTemplate : public CPropertyTemplate
{
friend class CTemplateLoader;
CStringList mAcceptedExtensions;
public:
CFileTemplate(u32 ID) : CPropertyTemplate(ID) { mPropType = eFileProperty; }
CFileTemplate(std::string name, u32 ID, const CStringList& extensions)
: CPropertyTemplate(ID) {
mPropType = eFileProperty; mPropName = name; mAcceptedExtensions = extensions;
}
EPropertyType Type() const { return eFileProperty; }
const CStringList& Extensions() const { return mAcceptedExtensions; }
};
class CStructTemplate : public CPropertyTemplate
{
friend class CTemplateLoader;
bool mIsSingleProperty;
s32 mPropertyCount;
std::vector<CPropertyTemplate*> mProperties;
public:
CStructTemplate();
~CStructTemplate();
EPropertyType Type() const;
bool IsSingleProperty() const;
s32 TemplateCount() const;
u32 Count() const;
CPropertyTemplate* PropertyByIndex(u32 index);
CPropertyTemplate* PropertyByName(std::string name);
CPropertyTemplate* PropertyByID(u32 ID);
CStructTemplate* StructByIndex(u32 index);
CStructTemplate* StructByName(std::string name);
CStructTemplate* StructByID(u32 ID);
void DebugPrintProperties(std::string base);
};
/**
* CAttribTemplate defines editor attributes.
* They enable PWE to access and use object properties for use in the world editor.
*/
class CAttribTemplate
{
friend class CTemplateLoader;
EAttribType AttribType;
std::string AttribTarget;
std::string ResFile;
u32 ExtraSettings;
public:
CAttribTemplate() {}
EAttribType Type() const { return AttribType; }
std::string Target() const { return AttribTarget; }
std::string Resource() const { return ResFile; }
u32 Settings() const { return ExtraSettings; }
};
/**
* CScriptTemplate is a class that encases the data contained in one of the XML templates.
* It essentially sets the layout of any given script object.
*
* It contains any data that applies globally to every instance of the object, such as
* property names, editor attribute properties, etc.
*/
class CScriptObject;
class CScriptTemplate
{
friend class CTemplateLoader;
CMasterTemplate *mpMaster;
CStructTemplate *mpBaseStruct;
std::list<CScriptObject*> mObjectList;
std::string mTemplateName;
std::vector<CAttribTemplate> mAttribs;
u32 mObjectID;
bool mVisible;
public:
CScriptTemplate(CMasterTemplate *pMaster);
~CScriptTemplate();
CMasterTemplate* MasterTemplate();
std::string TemplateName() const;
CStructTemplate* BaseStruct();
u32 AttribCount() const;
CAttribTemplate* Attrib(u32 index);
u32 ObjectID() const;
u32 NumObjects() const;
const std::list<CScriptObject*>& ObjectList() const;
void AddObject(CScriptObject *pObject);
void RemoveObject(CScriptObject *pObject);
void SortObjects();
void SetVisible(bool Visible);
bool IsVisible();
void DebugPrintProperties();
};
#endif // CSCRIPTTEMPLATE_H

View File

@@ -0,0 +1,46 @@
#ifndef CTEMPLATECATEGORY_H
#define CTEMPLATECATEGORY_H
#include "CScriptTemplate.h"
#include <algorithm>
class CTemplateCategory
{
std::string mCategoryName;
std::vector<CScriptTemplate*> mTemplates;
public:
CTemplateCategory() {}
inline CTemplateCategory(const std::string& Name) {
SetName(Name);
}
inline void SetName(const std::string& Name) {
mCategoryName = Name;
}
inline void AddTemplate(CScriptTemplate *pTmp) {
mTemplates.push_back(pTmp);
}
inline void Sort() {
std::sort(mTemplates.begin(), mTemplates.end(), [](CScriptTemplate* pA, CScriptTemplate* pB) -> bool {
return (pA->TemplateName() < pB->TemplateName());
});
}
inline u32 NumTemplates() {
return mTemplates.size();
}
inline CScriptTemplate* GetTemplate(u32 index) {
return mTemplates[index];
}
inline CScriptTemplate* operator[](u32 index) {
return mTemplates[index];
}
};
#endif // CTEMPLATECATEGORY_H

View File

@@ -0,0 +1,26 @@
#ifndef EATTRIBTYPE
#define EATTRIBTYPE
#include <Common/EnumUtil.h>
#include <string>
enum EAttribType
{
eNameAttrib = 0x1,
ePositionAttrib = 0x2,
eRotationAttrib = 0x4,
eScaleAttrib = 0x8,
eModelAttrib = 0x10,
eAnimSetAttrib = 0x20,
eVolumeAttrib = 0x40,
eVulnerabilityAttrib = 0x80,
eInvalidAttrib = 0x80000000
};
DEFINE_ENUM_FLAGS(EAttribType)
// functions defined in CScriptTemplate.cpp
EAttribType AttribStringToAttribEnum(const std::string& Attrib);
std::string AttribEnumToAttribString(EAttribType Attrib);
#endif // EATTRIBTYPE

View File

@@ -0,0 +1,130 @@
#ifndef EOBJECTTYPE_H
#define EOBJECTTYPE_H
// dunno if this is actually needed, but here it is.
enum EObjectType
{
Actor = 0x0,
Waypoint = 0x2,
DoorArea = 0x3,
Trigger = 0x4,
Timer = 0x5,
Counter = 0x6,
Effect = 0x7,
Platform = 0x8,
Sound = 0x9,
Generator = 0xA,
Dock = 0xB,
Camera = 0xC,
CameraWaypoint = 0xD,
NewIntroBoss = 0xE,
SpawnPoint = 0xF,
CameraHint = 0x10,
Pickup = 0x11,
MemoryRelay = 0x13,
RandomRelay = 0x14,
Relay = 0x15,
Beetle = 0x16,
HUDMemo = 0x17,
CameraFilterKeyframe = 0x18,
CameraBlurKeyframe = 0x19,
DamageableTrigger = 0x1A,
Debris = 0x1B,
CameraShaker = 0x1C,
ActorKeyFrame = 0x1D,
Water = 0x20,
Warwasp = 0x21,
SpacePirate = 0x24,
FlyingPirate = 0x25,
ElitePirate = 0x26,
MetroidBeta = 0x27,
ChozoGhost = 0x28,
CoverPoint = 0x2A,
SpiderBallWaypoint = 0x2C,
BloodFlower = 0x2D,
FlickerBat = 0x2E,
PathCamera = 0x2F,
GrapplePoint = 0x30,
PuddleSpore = 0x31,
SpiderBallAttractionSurface = 0x33,
PuddleToadGamma = 0x34,
Fog = 0x35,
FireFlea = 0x36,
MetareeAlpha = 0x37,
ActorRotate = 0x39,
SpecialFunction = 0x3A,
SpankWeed = 0x3B,
Zoomer = 0x3D,
PlayerHint = 0x3E,
Ripper = 0x3F,
PickupGenerator = 0x40,
PointOfInterest = 0x42,
Drone = 0x43,
MetroidAlpha = 0x44,
DebrisExtended = 0x45,
Steam = 0x46,
Ripple = 0x47,
BallTrigger = 0x48,
TargetingPoint = 0x49,
ElectroMagneticPulse = 0x4A,
IceSheegoth = 0x4B,
PlayerActor = 0x4C,
Flaahgra = 0x4D,
AreaAttributes = 0x4E,
FishCloud = 0x4F,
FishCloudModifier = 0x50,
VisorFlare = 0x51,
VisorGoo = 0x53,
JellyZap = 0x54,
ControllerAction = 0x55,
Switch = 0x56,
PlayerStateChange = 0x57,
Thardus = 0x58,
WallCrawlerSwarm = 0x5A,
AIJumpPoint = 0x5B,
FlaahgraTentacle = 0x5C,
RoomAcoustics = 0x5D,
ColorModulate = 0x5E,
ThardusRockProjectile = 0x5F,
Midi = 0x60,
StreamedAudio = 0x61,
WorldTeleporter = 0x62,
Repulsor = 0x63,
GunTurret = 0x64,
Babygoth = 0x66,
Eyeball = 0x67,
RadialKnockback = 0x68,
CameraPitchVolume = 0x69,
EnvFxDensityController = 0x6A,
Magdolite = 0x6B,
TeamAIMgr = 0x6C,
SnakeWeedSwarm = 0x6D,
ActorContraption = 0x6E,
Oculus = 0x6F,
Geemer = 0x70,
SpindleCamera = 0x71,
AtomicAlpha = 0x72,
CameraHintTrigger = 0x73,
RumbleEffect = 0x74,
AmbientAI = 0x75,
AtomicBeta = 0x77,
Puffer = 0x79,
Tryclops = 0x7A,
Ridley = 0x7B,
Seedling = 0x7C,
ThermalHeatFader = 0x7D,
Burrower = 0x7F,
ScriptBeam = 0x81,
WorldLightFader = 0x82,
MetroidPrimeStage2 = 0x83,
MetroidPrimeRelay = 0x84,
MazeNode = 0x85,
OmegaPirate = 0x86,
PhazonPool = 0x87,
PhazonHealingNodule = 0x88,
NewCameraShaker = 0x89,
ShadowProjector = 0x8A,
BeamEnergyBall = 0x8B
};
#endif // EOBJECTTYPE_H

View File

@@ -0,0 +1,28 @@
#ifndef EPROPERTYTYPE
#define EPROPERTYTYPE
#include <string>
enum EPropertyType
{
eBoolProperty,
eByteProperty,
eShortProperty,
eLongProperty,
eFloatProperty,
eStringProperty,
eVector3Property,
eColorProperty,
eEnumProperty,
eFileProperty,
eStructProperty,
eUnknownProperty,
eInvalidProperty
};
// functions defined in CScriptTemplate.cpp
EPropertyType PropStringToPropEnum(std::string prop);
std::string PropEnumToPropString(EPropertyType prop);
#endif // EPROPERTYTYPE

View File

@@ -0,0 +1,13 @@
#ifndef SCONNECTION_H
#define SCONNECTION_H
#include <Common/types.h>
struct SLink
{
u32 State;
u32 Message;
u32 ObjectID; // not a pointer because it can refer to objects outside the current area
};
#endif // SCONNECTION_H