Mass refactoring part 1/2: establishing multiple subprojects, moving source files to their new location, adding resources/templates to version control

This commit is contained in:
parax0
2015-12-14 18:33:16 -07:00
parent b4b134d55b
commit 8805baaee1
1116 changed files with 129200 additions and 546 deletions

View File

@@ -0,0 +1,54 @@
#include "CAreaAttributes.h"
#include <Resource/script/CMasterTemplate.h>
#include <Resource/script/CScriptLayer.h>
CAreaAttributes::CAreaAttributes(CScriptObject *pObj)
{
SetObject(pObj);
}
CAreaAttributes::~CAreaAttributes()
{
}
void CAreaAttributes::SetObject(CScriptObject *pObj)
{
mpObj = pObj;
mGame = pObj->Template()->MasterTemplate()->GetGame();
}
bool CAreaAttributes::IsLayerEnabled()
{
return mpObj->Layer()->IsActive();
}
bool CAreaAttributes::IsSkyEnabled()
{
CPropertyStruct *pBaseStruct = mpObj->Properties();
switch (mGame)
{
case ePrime:
case eEchoes:
case eCorruption:
return static_cast<CBoolProperty*>(pBaseStruct->PropertyByIndex(1))->Get();
default:
return false;
}
}
CModel* CAreaAttributes::SkyModel()
{
CPropertyStruct *pBaseStruct = mpObj->Properties();
switch (mGame)
{
case ePrime:
case eEchoes:
return (CModel*) static_cast<CFileProperty*>(pBaseStruct->PropertyByIndex(7))->Get().RawPointer();
case eCorruption:
return (CModel*) static_cast<CFileProperty*>(pBaseStruct->PropertyByIndex(8))->Get().RawPointer();
default:
return nullptr;
}
}

View File

@@ -0,0 +1,20 @@
#ifndef CAREAATTRIBUTES_H
#define CAREAATTRIBUTES_H
#include <Resource/script/CScriptObject.h>
class CAreaAttributes
{
EGame mGame;
CScriptObject *mpObj;
public:
CAreaAttributes(CScriptObject *pObj);
~CAreaAttributes();
void SetObject(CScriptObject *pObj);
bool IsLayerEnabled();
bool IsSkyEnabled();
CModel* SkyModel();
};
#endif // CAREAATTRIBUTES_H

View File

@@ -0,0 +1,25 @@
#include "CLightParameters.h"
CLightParameters::CLightParameters(CPropertyStruct *pStruct, EGame game)
: mpStruct(pStruct), mGame(game)
{
}
CLightParameters::~CLightParameters()
{
}
int CLightParameters::LightLayerIndex()
{
if (!mpStruct) return 0;
CLongProperty *pParam;
if (mGame <= ePrime)
pParam = (CLongProperty*) mpStruct->PropertyByIndex(0xD);
else
pParam = (CLongProperty*) mpStruct->PropertyByID(0x1F715FD3);
if (!pParam) return 0;
else return pParam->Get();
}

View File

@@ -0,0 +1,18 @@
#ifndef CLIGHTPARAMETERS_H
#define CLIGHTPARAMETERS_H
#include <Resource/CGameArea.h>
#include <Resource/script/CProperty.h>
class CLightParameters
{
CPropertyStruct *mpStruct;
EGame mGame;
public:
CLightParameters(CPropertyStruct *pStruct, EGame game);
~CLightParameters();
int LightLayerIndex();
};
#endif // CLIGHTPARAMETERS_H

View File

@@ -0,0 +1,68 @@
#include "CRayCollisionTester.h"
#include <Scene/CSceneNode.h>
CRayCollisionTester::CRayCollisionTester(const CRay& Ray)
: mRay(Ray)
{
}
CRayCollisionTester::~CRayCollisionTester()
{
}
void CRayCollisionTester::AddNode(CSceneNode *pNode, u32 ComponentIndex, float Distance)
{
mBoxIntersectList.emplace_back(SRayIntersection());
SRayIntersection& Intersection = mBoxIntersectList.back();
Intersection.pNode = pNode;
Intersection.ComponentIndex = ComponentIndex;
Intersection.Distance = Distance;
}
void CRayCollisionTester::AddNodeModel(CSceneNode *pNode, CBasicModel *pModel)
{
// Check each of the model's surfaces and queue them for further testing if they hit
for (u32 iSurf = 0; iSurf < pModel->GetSurfaceCount(); iSurf++)
{
std::pair<bool,float> SurfResult = pModel->GetSurfaceAABox(iSurf).Transformed(pNode->Transform()).IntersectsRay(mRay);
if (SurfResult.first)
AddNode(pNode, iSurf, SurfResult.second);
}
}
SRayIntersection CRayCollisionTester::TestNodes(const SViewInfo& ViewInfo)
{
// Sort nodes by distance from ray
mBoxIntersectList.sort(
[](const SRayIntersection& A, SRayIntersection& B) -> bool
{
return (A.Distance < B.Distance);
});
// Now do more precise intersection tests on geometry
SRayIntersection Result;
Result.Hit = false;
for (auto iNode = mBoxIntersectList.begin(); iNode != mBoxIntersectList.end(); iNode++)
{
SRayIntersection& Intersection = *iNode;
// If we have a result, and the distance for the bounding box hit is further than the current result distance
// then we know that every remaining node is further away and there is no chance of finding a closer hit.
if ((Result.Hit) && (Result.Distance < Intersection.Distance))
break;
// Otherwise, more intersection tests...
CSceneNode *pNode = Intersection.pNode;
SRayIntersection MidResult = pNode->RayNodeIntersectTest(mRay, Intersection.ComponentIndex, ViewInfo);
if (MidResult.Hit)
{
if ((!Result.Hit) || (MidResult.Distance < Result.Distance))
Result = MidResult;
}
}
return Result;
}

View File

@@ -0,0 +1,35 @@
#ifndef CRAYCOLLISIONHELPER_H
#define CRAYCOLLISIONHELPER_H
#include "CAABox.h"
#include "CRay.h"
#include "CVector3f.h"
#include "SRayIntersection.h"
#include "types.h"
#include <Core/SViewInfo.h>
#include <Resource/model/CBasicModel.h>
#include <list>
class CSceneNode;
class CRayCollisionTester
{
CRay mRay;
std::list<SRayIntersection> mBoxIntersectList;
public:
CRayCollisionTester(const CRay& Ray);
~CRayCollisionTester();
const CRay& Ray() const;
void AddNode(CSceneNode *pNode, u32 AssetIndex, float Distance);
void AddNodeModel(CSceneNode *pNode, CBasicModel *pModel);
SRayIntersection TestNodes(const SViewInfo& ViewInfo);
};
inline const CRay& CRayCollisionTester::Ray() const
{
return mRay;
}
#endif // CRAYCOLLISIONHELPER_H

245
src/Core/Core.pro Normal file
View File

@@ -0,0 +1,245 @@
#-------------------------------------------------
#
# Project created by QtCreator 2015-12-13T15:31:43
#
#-------------------------------------------------
QT -= core gui
CONFIG += staticlib
TEMPLATE = lib
DESTDIR = $$PWD/../../build/Core
DEFINES += GLEW_STATIC
unix {
target.path = /usr/lib
INSTALLS += target
}
CONFIG (debug, debug|release) {
# Debug Config
OBJECTS_DIR = $$PWD/../../build/Core/debug
TARGET = Cored
# Debug Libs
LIBS += -L$$PWD/../../build/Common/ -lCommond \
-L$$PWD/../../externals/assimp/lib/ -lassimp-vc120-mtd \
-L$$PWD/../../externals/boost_1_56_0/lib32-msvc-12.0 -llibboost_filesystem-vc120-mt-gd-1_56 \
-L$$PWD/../../externals/FileIO/lib/ -lFileIOd \
-L$$PWD/../../externals/tinyxml2/lib/ -ltinyxml2d
}
CONFIG (release, debug|release) {
# Release Config
OBJECTS_DIR = $$PWD/../../build/Core/release
TARGET = Core
# Release Libs
LIBS += -L$$PWD/../../build/Common/ -lCommon \
-L$$PWD/../../externals/assimp/lib/ -lassimp-vc120-mt \
-L$$PWD/../../externals/boost_1_56_0/lib32-msvc-12.0 -llibboost_filesystem-vc120-mt-1_56 \
-L$$PWD/../../externals/FileIO/lib/ -lFileIO \
-L$$PWD/../../externals/tinyxml2/lib/ -ltinyxml2
}
# Debug/Release Libs
LIBS += -L$$PWD/../../externals/glew-1.9.0/lib/ -lglew32s \
-L$$PWD/../../externals/lzo-2.08/lib -llzo-2.08 \
-L$$PWD/../../externals/zlib/lib -lzdll
# Include Paths
INCLUDEPATH += $$PWD/../ \
$$PWD/../../externals/assimp/include \
$$PWD/../../externals/boost_1_56_0 \
$$PWD/../../externals/FileIO/include \
$$PWD/../../externals/glew-1.9.0/include \
$$PWD/../../externals/glm/glm \
$$PWD/../../externals/lzo-2.08/include \
$$PWD/../../externals/tinyxml2/include \
$$PWD/../../externals/zlib/include
# Source Files
HEADERS += \
Render/CCamera.h \
Render/CDrawUtil.h \
Render/CGraphics.h \
Render/CRenderBucket.h \
Render/CRenderer.h \
Render/ERenderCommand.h \
Render/ERenderOptions.h \
Render/IRenderable.h \
Render/SRenderablePtr.h \
Render/SViewInfo.h \
Resource/Cooker/CMaterialCooker.h \
Resource/Cooker/CModelCooker.h \
Resource/Cooker/CSectionMgrOut.h \
Resource/Cooker/CTemplateWriter.h \
Resource/Cooker/CTextureEncoder.h \
Resource/Cooker/CWorldCooker.h \
Resource/Factory/CAnimSetLoader.h \
Resource/Factory/CAreaLoader.h \
Resource/Factory/CBlockMgrIn.h \
Resource/Factory/CCollisionLoader.h \
Resource/Factory/CFontLoader.h \
Resource/Factory/CMaterialLoader.h \
Resource/Factory/CModelLoader.h \
Resource/Factory/CScanLoader.h \
Resource/Factory/CScriptLoader.h \
Resource/Factory/CStringLoader.h \
Resource/Factory/CTemplateLoader.h \
Resource/Factory/CTextureDecoder.h \
Resource/Factory/CWorldLoader.h \
Resource/Model/CBasicModel.h \
Resource/Model/CModel.h \
Resource/Model/CStaticModel.h \
Resource/Model/CVertex.h \
Resource/Model/EVertexDescription.h \
Resource/Model/SSurface.h \
Resource/Script/CMasterTemplate.h \
Resource/Script/CProperty.h \
Resource/Script/CPropertyTemplate.h \
Resource/Script/CScriptLayer.h \
Resource/Script/CScriptObject.h \
Resource/Script/CScriptTemplate.h \
Resource/Script/EObjectType.h \
Resource/Script/EPropertyType.h \
Resource/Script/EVolumeShape.h \
Resource/Script/SConnection.h \
Resource/CAnimationParameters.h \
Resource/CAnimSet.h \
Resource/CCollisionMesh.h \
Resource/CCollisionMeshGroup.h \
Resource/CFont.h \
Resource/CGameArea.h \
Resource/CLight.h \
Resource/CMaterial.h \
Resource/CMaterialPass.h \
Resource/CMaterialSet.h \
Resource/CPakFile.h \
Resource/CResCache.h \
Resource/CResource.h \
Resource/CScan.h \
Resource/CStringTable.h \
Resource/CTexture.h \
Resource/CWorld.h \
Resource/EFormatVersion.h \
Resource/EResType.h \
Resource/ETevEnums.h \
Resource/ETexelFormat.h \
Resource/SDependency.h \
Resource/SNamedResource.h \
Resource/SResInfo.h \
Resource/TResPtr.h \
Scene/CCollisionNode.h \
Scene/CLightNode.h \
Scene/CModelNode.h \
Scene/CRootNode.h \
Scene/CSceneManager.h \
Scene/CSceneNode.h \
Scene/CScriptNode.h \
Scene/CStaticNode.h \
Scene/ENodeType.h \
ScriptExtra/CDamageableTriggerExtra.h \
ScriptExtra/CDoorExtra.h \
ScriptExtra/CPointOfInterestExtra.h \
ScriptExtra/CScriptExtra.h \
ScriptExtra/CSpacePirateExtra.h \
ScriptExtra/CWaypointExtra.h \
CAreaAttributes.h \
CLightParameters.h \
CRayCollisionTester.h \
Log.h \
SRayIntersection.h \
OpenGL/CDynamicVertexBuffer.h \
OpenGL/CFramebuffer.h \
OpenGL/CGL.h \
OpenGL/CIndexBuffer.h \
OpenGL/CRenderbuffer.h \
OpenGL/CShader.h \
OpenGL/CShaderGenerator.h \
OpenGL/CUniformBuffer.h \
OpenGL/CVertexArrayManager.h \
OpenGL/CVertexBuffer.h \
OpenGL/GLCommon.h
SOURCES += \
Render/CCamera.cpp \
Render/CDrawUtil.cpp \
Render/CGraphics.cpp \
Render/CRenderBucket.cpp \
Render/CRenderer.cpp \
Resource/Cooker/CMaterialCooker.cpp \
Resource/Cooker/CModelCooker.cpp \
Resource/Cooker/CSectionMgrOut.cpp \
Resource/Cooker/CTemplateWriter.cpp \
Resource/Cooker/CTextureEncoder.cpp \
Resource/Cooker/CWorldCooker.cpp \
Resource/Factory/CAnimSetLoader.cpp \
Resource/Factory/CAreaLoader.cpp \
Resource/Factory/CBlockMgr.cpp \
Resource/Factory/CCollisionLoader.cpp \
Resource/Factory/CFontLoader.cpp \
Resource/Factory/CMaterialLoader.cpp \
Resource/Factory/CModelLoader.cpp \
Resource/Factory/CScanLoader.cpp \
Resource/Factory/CScriptLoader.cpp \
Resource/Factory/CStringLoader.cpp \
Resource/Factory/CTemplateLoader.cpp \
Resource/Factory/CTextureDecoder.cpp \
Resource/Factory/CWorldLoader.cpp \
Resource/Model/CBasicModel.cpp \
Resource/Model/CModel.cpp \
Resource/Model/CStaticModel.cpp \
Resource/Model/SSurface.cpp \
Resource/Script/CMasterTemplate.cpp \
Resource/Script/CProperty.cpp \
Resource/Script/CPropertyTemplate.cpp \
Resource/Script/CScriptLayer.cpp \
Resource/Script/CScriptObject.cpp \
Resource/Script/CScriptTemplate.cpp \
Resource/CAnimationParameters.cpp \
Resource/CAnimSet.cpp \
Resource/CCollisionMesh.cpp \
Resource/CCollisionMeshGroup.cpp \
Resource/CFont.cpp \
Resource/CGameArea.cpp \
Resource/CLight.cpp \
Resource/CMaterial.cpp \
Resource/CMaterialPass.cpp \
Resource/CMaterialSet.cpp \
Resource/CPakFile.cpp \
Resource/CResCache.cpp \
Resource/CResource.cpp \
Resource/CScan.cpp \
Resource/CStringTable.cpp \
Resource/CTexture.cpp \
Resource/CWorld.cpp \
Scene/CCollisionNode.cpp \
Scene/CLightNode.cpp \
Scene/CModelNode.cpp \
Scene/CSceneManager.cpp \
Scene/CSceneNode.cpp \
Scene/CScriptNode.cpp \
Scene/CStaticNode.cpp \
ScriptExtra/CDamageableTriggerExtra.cpp \
ScriptExtra/CDoorExtra.cpp \
ScriptExtra/CPointOfInterestExtra.cpp \
ScriptExtra/CScriptExtra.cpp \
ScriptExtra/CSpacePirateExtra.cpp \
ScriptExtra/CWaypointExtra.cpp \
CAreaAttributes.cpp \
CLightParameters.cpp \
CRayCollisionTester.cpp \
Log.cpp \
OpenGL/CDynamicVertexBuffer.cpp \
OpenGL/CFramebuffer.cpp \
OpenGL/CGL.cpp \
OpenGL/CIndexBuffer.cpp \
OpenGL/CRenderbuffer.cpp \
OpenGL/CShader.cpp \
OpenGL/CShaderGenerator.cpp \
OpenGL/CUniformBuffer.cpp \
OpenGL/CVertexArrayManager.cpp \
OpenGL/CVertexBuffer.cpp \
OpenGL/GLCommon.cpp

76
src/Core/Log.cpp Normal file
View File

@@ -0,0 +1,76 @@
#include <ctime>
#include <iostream>
#include <Common/TString.h>
#include <QMessageBox>
namespace Log
{
static const TString gskLogFilename = "primeworldeditor.log";
#pragma warning(push)
#pragma warning(disable: 4996) // Can't use fopen_s here without creating a separate init function for the log
FILE *gpLogFile = fopen(*gskLogFilename, "w");
#pragma warning(pop)
void Write(const TString& message)
{
if (gpLogFile)
{
time_t RawTime;
time(&RawTime);
tm pTimeInfo;
localtime_s(&pTimeInfo, &RawTime);
char Buffer[80];
strftime(Buffer, 80, "[%H:%M:%S]", &pTimeInfo);
fprintf(gpLogFile, "%s %s\n", Buffer, *message);
fflush(gpLogFile);
}
}
void Error(const TString& message)
{
Write("ERROR: " + message);
std::cout << "ERROR: " << message << "\n";
}
void Warning(const TString& message)
{
Write("Warning: " + message);
std::cout << "Warning: " << message << "\n";
}
void FileWrite(const TString& filename, const TString& message)
{
Write(filename + " : " + message);
}
void FileWrite(const TString& filename, unsigned long offset, const TString& message)
{
Write(filename + " : " + TString::HexString(offset) + " - " + message);
}
void FileError(const TString& filename, const TString& message)
{
Error(filename + " : " + message);
}
void FileError(const TString& filename, unsigned long offset, const TString& message)
{
Error(filename + " : " + TString::HexString(offset) + " - " + message);
}
void FileWarning(const TString& filename, const TString& message)
{
Warning(filename + " : " + message);
}
void FileWarning(const TString& filename, unsigned long offset, const TString& message)
{
Warning(filename + " : " + TString::HexString(offset) + " - " + message);
}
}

22
src/Core/Log.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef INFO
#define INFO
#include <Common/TString.h>
namespace Log
{
void Write(const TString& message);
void Error(const TString& message);
void Warning(const TString& message);
void FileWrite(const TString& filename, const TString& message);
void FileWrite(const TString& filename, unsigned long offset, const TString& message);
void FileError(const TString& filename, const TString& message);
void FileError(const TString& filename, unsigned long offset, const TString& message);
void FileWarning(const TString& filename, const TString& message);
void FileWarning(const TString& filename, unsigned long offset, const TString& message);
}
#endif // INFO

View File

@@ -0,0 +1,136 @@
#include "CDynamicVertexBuffer.h"
#include "CVertexArrayManager.h"
static const u32 gskAttribSize[] = {
0xC, 0xC, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8
};
CDynamicVertexBuffer::CDynamicVertexBuffer()
{
mAttribFlags = eNoAttributes;
mBufferedFlags = eNoAttributes;
mNumVertices = 0;
}
CDynamicVertexBuffer::~CDynamicVertexBuffer()
{
CVertexArrayManager::DeleteAllArraysForVBO(this);
ClearBuffers();
}
void CDynamicVertexBuffer::SetVertexCount(u32 NumVerts)
{
ClearBuffers();
mNumVertices = NumVerts;
InitBuffers();
}
void CDynamicVertexBuffer::Bind()
{
CVertexArrayManager::Current()->BindVAO(this);
}
void CDynamicVertexBuffer::Unbind()
{
glBindVertexArray(0);
}
void CDynamicVertexBuffer::SetActiveAttribs(u32 AttribFlags)
{
ClearBuffers();
mAttribFlags = (EVertexDescription) AttribFlags;
InitBuffers();
}
void CDynamicVertexBuffer::BufferAttrib(EVertexDescription Attrib, const void *pData)
{
u32 Index;
switch (Attrib)
{
case ePosition: Index = 0; break;
case eNormal: Index = 1; break;
case eColor0: Index = 2; break;
case eColor1: Index = 3; break;
case eTex0: Index = 4; break;
case eTex1: Index = 5; break;
case eTex2: Index = 6; break;
case eTex3: Index = 7; break;
case eTex4: Index = 8; break;
case eTex5: Index = 9; break;
case eTex6: Index = 10; break;
case eTex7: Index = 11; break;
default: return;
}
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[Index]);
glBufferSubData(GL_ARRAY_BUFFER, 0, gskAttribSize[Index] * mNumVertices, pData);
}
void CDynamicVertexBuffer::ClearBuffers()
{
for (u32 iAttrib = 0; iAttrib < 12; iAttrib++)
{
int bit = 1 << iAttrib;
if (mBufferedFlags & bit)
glDeleteBuffers(1, &mAttribBuffers[iAttrib]);
}
mBufferedFlags = eNoAttributes;
}
GLuint CDynamicVertexBuffer::CreateVAO()
{
GLuint VertexArray;
glGenVertexArrays(1, &VertexArray);
glBindVertexArray(VertexArray);
for (u32 iAttrib = 0; iAttrib < 12; iAttrib++)
{
bool HasAttrib = ((3 << (iAttrib * 2)) != 0);
if (HasAttrib)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
GLuint NumComponents;
GLenum DataType;
if ((iAttrib == 2) || (iAttrib == 3))
{
NumComponents = 4;
DataType = GL_UNSIGNED_BYTE;
}
else
{
NumComponents = gskAttribSize[iAttrib] / 4;
DataType = GL_FLOAT;
}
glVertexAttribPointer(iAttrib, NumComponents, DataType, GL_FALSE, 0, (void*) 0);
glEnableVertexAttribArray(iAttrib);
}
}
glBindVertexArray(0);
return VertexArray;
}
// ************ PRIVATE ************
void CDynamicVertexBuffer::InitBuffers()
{
if (mBufferedFlags) ClearBuffers();
for (u32 iAttrib = 0; iAttrib < 12; iAttrib++)
{
bool HasAttrib = ((3 << (iAttrib * 2)) != 0);
if (HasAttrib)
{
glGenBuffers(1, &mAttribBuffers[iAttrib]);
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glBufferData(GL_ARRAY_BUFFER, gskAttribSize[iAttrib] * mNumVertices, NULL, GL_DYNAMIC_DRAW);
}
}
mBufferedFlags = mAttribFlags;
}

View File

@@ -0,0 +1,32 @@
#ifndef CDYNAMICVERTEXBUFFER_H
#define CDYNAMICVERTEXBUFFER_H
#include <GL/glew.h>
#include <Common/types.h>
#include <Common/CVector2f.h>
#include <Common/CVector3f.h>
#include <Resource/model/EVertexDescription.h>
#include <vector>
class CDynamicVertexBuffer
{
EVertexDescription mAttribFlags;
EVertexDescription mBufferedFlags;
u32 mNumVertices;
GLuint mAttribBuffers[12];
public:
CDynamicVertexBuffer();
~CDynamicVertexBuffer();
void SetVertexCount(u32 NumVerts);
void Bind();
void Unbind();
void SetActiveAttribs(u32 AttribFlags);
void BufferAttrib(EVertexDescription Attrib, const void *pData);
void ClearBuffers();
GLuint CreateVAO();
private:
void InitBuffers();
};
#endif // CDYNAMICVERTEXBUFFER_H

View File

@@ -0,0 +1,111 @@
#include "CFramebuffer.h"
#include <iostream>
CFramebuffer::CFramebuffer()
{
mInitialized = false;
mWidth = 0;
mHeight = 0;
mpRenderbuffer = nullptr;
mpTexture = nullptr;
}
CFramebuffer::CFramebuffer(u32 Width, u32 Height)
{
mInitialized = false;
mWidth = 0;
mHeight = 0;
mpRenderbuffer = nullptr;
mpTexture = nullptr;
Resize(Width, Height);
}
CFramebuffer::~CFramebuffer()
{
if (mInitialized)
{
glDeleteFramebuffers(1, &mFramebuffer);
delete mpRenderbuffer;
delete mpTexture;
}
}
void CFramebuffer::Init()
{
if (!smStaticsInitialized)
{
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &smDefaultFramebuffer);
smStaticsInitialized = true;
}
if (!mInitialized)
{
glGenFramebuffers(1, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
mpRenderbuffer = new CRenderbuffer(mWidth, mHeight);
mpRenderbuffer->Bind();
glFramebufferRenderbuffer(
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mpRenderbuffer->BufferID()
);
mpTexture = new CTexture(mWidth, mHeight);
mpTexture->Bind(0);
glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mpTexture->TextureID(), 0
);
mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (mStatus != GL_FRAMEBUFFER_COMPLETE)
std::cout << "\rError: Framebuffer not complete\n";
mInitialized = true;
}
}
void CFramebuffer::Bind()
{
if (!mInitialized) Init();
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
}
void CFramebuffer::Resize(u32 Width, u32 Height)
{
if ((mWidth != Width) || (mHeight != Height))
{
mWidth = Width;
mHeight = Height;
if (mInitialized)
{
mpRenderbuffer->Resize(Width, Height);
mpTexture->Resize(Width, Height);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
mpRenderbuffer->Bind();
glFramebufferRenderbuffer(
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mpRenderbuffer->BufferID()
);
mpTexture->Bind(0);
glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mpTexture->TextureID(), 0
);
}
}
}
CTexture* CFramebuffer::Texture()
{
return mpTexture;
}
// ************ STATIC ************
void CFramebuffer::BindDefaultFramebuffer()
{
glBindFramebuffer(GL_FRAMEBUFFER, smDefaultFramebuffer);
}
GLint CFramebuffer::smDefaultFramebuffer;
bool CFramebuffer::smStaticsInitialized;

View File

@@ -0,0 +1,32 @@
#ifndef CFRAMEBUFFER_H
#define CFRAMEBUFFER_H
#include "CRenderbuffer.h"
#include <Resource/CTexture.h>
#include <gl/glew.h>
class CFramebuffer
{
GLuint mFramebuffer;
CRenderbuffer *mpRenderbuffer;
CTexture *mpTexture;
u32 mWidth, mHeight;
bool mInitialized;
GLenum mStatus;
static GLint smDefaultFramebuffer;
static bool smStaticsInitialized;
public:
CFramebuffer();
CFramebuffer(u32 Width, u32 Height);
~CFramebuffer();
void Init();
void Bind();
void Resize(u32 Width, u32 Height);
CTexture* Texture();
static void BindDefaultFramebuffer();
};
#endif // CFRAMEBUFFER_H

41
src/Core/OpenGL/CGL.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "CGL.h"
// ************ PUBLIC ************
void CGL::SetBlendMode(EBlendFactor Source, EBlendFactor Dest)
{
glBlendFuncSeparate(Source, Dest, eBlendZero, eBlendZero);
mBlendSrcFac = Source;
mBlendDstFac = Dest;
}
void CGL::SetOpaqueBlend()
{
SetBlendMode(eBlendOne, eBlendZero);
}
void CGL::SetAlphaBlend()
{
SetBlendMode(eBlendSrcAlpha, eBlendInvSrcAlpha);
}
void CGL::SetAdditiveBlend()
{
SetBlendMode(eBlendOne, eBlendOne);
}
// ************ PRIVATE ************
void CGL::Init()
{
if (!mInitialized)
{
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ZERO);
mBlendSrcFac = eBlendOne;
mBlendDstFac = eBlendZero;
mInitialized = true;
}
}
// ************ STATIC MEMBER INITIALIZATION ************
bool CGL::mInitialized;
EBlendFactor CGL::mBlendSrcFac;
EBlendFactor CGL::mBlendDstFac;

26
src/Core/OpenGL/CGL.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef CGL_H
#define CGL_H
#include "GLCommon.h"
#include <Common/types.h>
#include <GL/glew.h>
class CGL
{
public:
void SetBlendMode(EBlendFactor Source, EBlendFactor Dest);
void SetOpaqueBlend();
void SetAlphaBlend();
void SetAdditiveBlend();
private:
static void Init();
static bool mInitialized;
static EBlendFactor mBlendSrcFac, mBlendDstFac;
static u8 mColorMask;
static bool mDepthMask;
static bool mStencilMask;
};
#endif // CGL_H

View File

@@ -0,0 +1,156 @@
#include "CIndexBuffer.h"
CIndexBuffer::CIndexBuffer()
{
mBuffered = false;
}
CIndexBuffer::CIndexBuffer(GLenum type)
{
mPrimitiveType = type;
mBuffered = false;
}
CIndexBuffer::~CIndexBuffer()
{
if (mBuffered)
glDeleteBuffers(1, &mIndexBuffer);
}
void CIndexBuffer::AddIndex(u16 idx)
{
mIndices.push_back(idx);
}
void CIndexBuffer::AddIndices(u16 *indicesPtr, u32 count)
{
Reserve(count);
for (u32 i = 0; i < count; i++)
mIndices.push_back(*indicesPtr++);
}
void CIndexBuffer::Reserve(u32 size)
{
mIndices.reserve(mIndices.size() + size);
}
void CIndexBuffer::Clear()
{
if (mBuffered)
glDeleteBuffers(1, &mIndexBuffer);
mBuffered = false;
mIndices.clear();
}
void CIndexBuffer::Buffer()
{
if (mBuffered)
glDeleteBuffers(1, &mIndexBuffer);
glGenBuffers(1, &mIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndices.size() * sizeof(u16), mIndices.data(), GL_STATIC_DRAW);
mBuffered = true;
}
void CIndexBuffer::Bind()
{
if (!mBuffered) Buffer();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
}
void CIndexBuffer::Unbind()
{
}
void CIndexBuffer::DrawElements()
{
Bind();
glDrawElements(mPrimitiveType, mIndices.size(), GL_UNSIGNED_SHORT, (void*) 0);
Unbind();
}
void CIndexBuffer::DrawElements(u32 Offset, u32 Size)
{
Bind();
glDrawElements(mPrimitiveType, Size, GL_UNSIGNED_SHORT, (void*) (Offset * 2));
Unbind();
}
bool CIndexBuffer::IsBuffered()
{
return mBuffered;
}
u32 CIndexBuffer::GetSize()
{
return mIndices.size();
}
GLenum CIndexBuffer::GetPrimitiveType()
{
return mPrimitiveType;
}
void CIndexBuffer::SetPrimitiveType(GLenum type)
{
mPrimitiveType = type;
}
void CIndexBuffer::TrianglesToStrips(u16 *indicesPtr, u32 count)
{
Reserve(count + (count / 3));
for (u32 i = 0; i < count; i += 3)
{
mIndices.push_back(*indicesPtr++);
mIndices.push_back(*indicesPtr++);
mIndices.push_back(*indicesPtr++);
mIndices.push_back(0xFFFF);
}
}
void CIndexBuffer::FansToStrips(u16 *indicesPtr, u32 count)
{
Reserve(count);
u16 FirstIndex = *indicesPtr;
for (u32 i = 2; i < count; i += 3)
{
mIndices.push_back(indicesPtr[i - 1]);
mIndices.push_back(indicesPtr[i]);
mIndices.push_back(FirstIndex);
if (i + 1 < count)
mIndices.push_back(indicesPtr[i + 1]);
if (i + 2 < count)
mIndices.push_back(indicesPtr[i + 2]);
mIndices.push_back(0xFFFF);
}
}
void CIndexBuffer::QuadsToStrips(u16 *indicesPtr, u32 count)
{
Reserve((u32) (count * 1.25));
u32 i = 3;
for (; i < count; i += 4)
{
mIndices.push_back(indicesPtr[i - 2]);
mIndices.push_back(indicesPtr[i - 1]);
mIndices.push_back(indicesPtr[i - 3]);
mIndices.push_back(indicesPtr[i]);
mIndices.push_back(0xFFFF);
}
// if there's three indices present that indicates a single triangle
if (i == count)
{
mIndices.push_back(indicesPtr[i - 3]);
mIndices.push_back(indicesPtr[i - 2]);
mIndices.push_back(indicesPtr[i - 1]);
mIndices.push_back(0xFFFF);
}
}

View File

@@ -0,0 +1,39 @@
#ifndef CINDEXBUFFER_H
#define CINDEXBUFFER_H
#include <Common/types.h>
#include <Common/CVector3f.h>
#include <gl/glew.h>
class CIndexBuffer
{
GLuint mIndexBuffer;
std::vector<u16> mIndices;
GLenum mPrimitiveType;
bool mBuffered;
public:
CIndexBuffer();
CIndexBuffer(GLenum type);
~CIndexBuffer();
void AddIndex(u16 idx);
void AddIndices(u16 *indicesPtr, u32 count);
void Reserve(u32 size);
void Clear();
void Buffer();
void Bind();
void Unbind();
void DrawElements();
void DrawElements(u32 Offset, u32 Size);
bool IsBuffered();
u32 GetSize();
GLenum GetPrimitiveType();
void SetPrimitiveType(GLenum type);
void TrianglesToStrips(u16 *indicesPtr, u32 count);
void FansToStrips(u16 *indicesPtr, u32 count);
void QuadsToStrips(u16 *indicesPtr, u32 count);
};
#endif // CINDEXBUFFER_H

View File

@@ -0,0 +1,52 @@
#include "CRenderbuffer.h"
CRenderbuffer::CRenderbuffer()
{
mInitialized = false;
mWidth = 0;
mHeight = 0;
}
CRenderbuffer::CRenderbuffer(u32 Width, u32 Height)
{
mInitialized = false;
mWidth = Width;
mHeight = Height;
}
CRenderbuffer::~CRenderbuffer()
{
if (mInitialized)
glDeleteRenderbuffers(1, &mRenderbuffer);
}
void CRenderbuffer::Init()
{
glGenRenderbuffers(1, &mRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mWidth, mHeight);
mInitialized = true;
}
void CRenderbuffer::Resize(u32 Width, u32 Height)
{
mWidth = Width;
mHeight = Height;
if (mInitialized)
{
Bind();
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mWidth, mHeight);
}
}
void CRenderbuffer::Bind()
{
if (!mInitialized) Init();
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
}
void CRenderbuffer::Unbind()
{
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}

View File

@@ -0,0 +1,31 @@
#ifndef CRENDERBUFFER_H
#define CRENDERBUFFER_H
#include <GL/glew.h>
#include <Common/types.h>
class CRenderbuffer
{
GLuint mRenderbuffer;
u32 mWidth, mHeight;
bool mInitialized;
public:
CRenderbuffer();
CRenderbuffer(u32 Width, u32 Height);
~CRenderbuffer();
void Init();
void Resize(u32 Width, u32 Height);
void Bind();
void Unbind();
// Getters
GLuint BufferID();
};
inline GLuint CRenderbuffer::BufferID()
{
return mRenderbuffer;
}
#endif // CRENDERBUFFER_H

263
src/Core/OpenGL/CShader.cpp Normal file
View File

@@ -0,0 +1,263 @@
#include "CShader.h"
#include <Common/types.h>
#include <Core/CGraphics.h>
#include <FileIO/CTextInStream.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
bool gDebugDumpShaders = false;
u64 gFailedCompileCount = 0;
u64 gSuccessfulCompileCount = 0;
CShader* CShader::spCurrentShader = nullptr;
CShader::CShader()
{
mVertexShaderExists = false;
mPixelShaderExists = false;
mProgramExists = false;
}
CShader::CShader(const char *kpVertexSource, const char *kpPixelSource)
{
mVertexShaderExists = false;
mPixelShaderExists = false;
mProgramExists = false;
CompileVertexSource(kpVertexSource);
CompilePixelSource(kpPixelSource);
LinkShaders();
}
CShader::~CShader()
{
if (mVertexShaderExists) glDeleteShader(mVertexShader);
if (mPixelShaderExists) glDeleteShader(mPixelShader);
if (mProgramExists) glDeleteProgram(mProgram);
if (spCurrentShader == this) spCurrentShader = 0;
}
bool CShader::CompileVertexSource(const char* kpSource)
{
mVertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(mVertexShader, 1, (const GLchar**) &kpSource, NULL);
glCompileShader(mVertexShader);
// Shader should be compiled - check for errors
GLint CompileStatus;
glGetShaderiv(mVertexShader, GL_COMPILE_STATUS, &CompileStatus);
if (CompileStatus == GL_FALSE)
{
TString Out = "dump/BadVS_" + std::to_string(gFailedCompileCount) + ".txt";
std::cout << "ERROR: Unable to compile vertex shader; dumped to " << Out << "\n";
DumpShaderSource(mVertexShader, Out);
gFailedCompileCount++;
glDeleteShader(mVertexShader);
return false;
}
// Debug dump
else if (gDebugDumpShaders == true)
{
TString Out = "dump/VS_" + TString::FromInt64(gSuccessfulCompileCount, 8, 10) + ".txt";
std::cout << "Debug shader dumping enabled; dumped to " << Out << "\n";
DumpShaderSource(mVertexShader, Out);
gSuccessfulCompileCount++;
}
mVertexShaderExists = true;
return true;
}
bool CShader::CompilePixelSource(const char* kpSource)
{
mPixelShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(mPixelShader, 1, (const GLchar**) &kpSource, NULL);
glCompileShader(mPixelShader);
// Shader should be compiled - check for errors
GLint CompileStatus;
glGetShaderiv(mPixelShader, GL_COMPILE_STATUS, &CompileStatus);
if (CompileStatus == GL_FALSE)
{
TString Out = "dump/BadPS_" + TString::FromInt64(gFailedCompileCount, 8, 10) + ".txt";
std::cout << "ERROR: Unable to compile pixel shader; dumped to " << Out << "\n";
DumpShaderSource(mPixelShader, Out);
gFailedCompileCount++;
glDeleteShader(mPixelShader);
return false;
}
// Debug dump
else if (gDebugDumpShaders == true)
{
TString Out = "dump/PS_" + TString::FromInt64(gSuccessfulCompileCount, 8, 10) + ".txt";
std::cout << "Debug shader dumping enabled; dumped to " << Out << "\n";
DumpShaderSource(mPixelShader, Out);
gSuccessfulCompileCount++;
}
mPixelShaderExists = true;
return true;
}
bool CShader::LinkShaders()
{
if ((!mVertexShaderExists) || (!mPixelShaderExists)) return false;
mProgram = glCreateProgram();
glAttachShader(mProgram, mVertexShader);
glAttachShader(mProgram, mPixelShader);
glLinkProgram(mProgram);
glDeleteShader(mVertexShader);
glDeleteShader(mPixelShader);
mVertexShaderExists = false;
mPixelShaderExists = false;
// Shader should be linked - check for errors
GLint LinkStatus;
glGetProgramiv(mProgram, GL_LINK_STATUS, &LinkStatus);
if (LinkStatus == GL_FALSE)
{
TString Out = "dump/BadLink_" + TString::FromInt64(gFailedCompileCount, 8, 10) + ".txt";
std::cout << "ERROR: Unable to link shaders. Dumped error log to " << Out << "\n";
GLint LogLen;
glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &LogLen);
GLchar *InfoLog = new GLchar[LogLen];
glGetProgramInfoLog(mProgram, LogLen, NULL, InfoLog);
std::ofstream LinkOut;
LinkOut.open(*Out);
if (LogLen > 0)
LinkOut << InfoLog;
LinkOut.close();
delete[] InfoLog;
gFailedCompileCount++;
glDeleteProgram(mProgram);
return false;
}
mMVPBlockIndex = GetUniformBlockIndex("MVPBlock");
mVertexBlockIndex = GetUniformBlockIndex("VertexBlock");
mPixelBlockIndex = GetUniformBlockIndex("PixelBlock");
mLightBlockIndex = GetUniformBlockIndex("LightBlock");
mProgramExists = true;
return true;
}
bool CShader::IsValidProgram()
{
return mProgramExists;
}
GLuint CShader::GetProgramID()
{
return mProgram;
}
GLuint CShader::GetUniformLocation(const char* Uniform)
{
return glGetUniformLocation(mProgram, Uniform);
}
GLuint CShader::GetUniformBlockIndex(const char* UniformBlock)
{
return glGetUniformBlockIndex(mProgram, UniformBlock);
}
void CShader::SetCurrent()
{
if (spCurrentShader != this)
{
glUseProgram(mProgram);
spCurrentShader = this;
glUniformBlockBinding(mProgram, mMVPBlockIndex, CGraphics::MVPBlockBindingPoint());
glUniformBlockBinding(mProgram, mVertexBlockIndex, CGraphics::VertexBlockBindingPoint());
glUniformBlockBinding(mProgram, mPixelBlockIndex, CGraphics::PixelBlockBindingPoint());
glUniformBlockBinding(mProgram, mLightBlockIndex, CGraphics::LightBlockBindingPoint());
}
}
// ************ STATIC ************
CShader* CShader::FromResourceFile(const TString& ShaderName)
{
TString VertexShaderFilename = "../resources/shaders/" + ShaderName + ".vs";
TString PixelShaderFilename = "../resources/shaders/" + ShaderName + ".ps";
CTextInStream VertexShaderFile(VertexShaderFilename.ToStdString());
CTextInStream PixelShaderFile(PixelShaderFilename.ToStdString());
if (!VertexShaderFile.IsValid())
std::cout << "Error: Couldn't load vertex shader file for " << ShaderName << "\n";
if (!PixelShaderFile.IsValid())
std::cout << "Error: Couldn't load pixel shader file for " << ShaderName << "\n";
if ((!VertexShaderFile.IsValid()) || (!PixelShaderFile.IsValid())) return nullptr;
std::stringstream VertexShader;
while (!VertexShaderFile.EoF())
VertexShader << VertexShaderFile.GetString();
std::stringstream PixelShader;
while (!PixelShaderFile.EoF())
PixelShader << PixelShaderFile.GetString();
CShader *pShader = new CShader();
pShader->CompileVertexSource(VertexShader.str().c_str());
pShader->CompilePixelSource(PixelShader.str().c_str());
pShader->LinkShaders();
return pShader;
}
CShader* CShader::CurrentShader()
{
return spCurrentShader;
}
void CShader::KillCachedShader()
{
spCurrentShader = 0;
}
// ************ PRIVATE ************
void CShader::DumpShaderSource(GLuint Shader, const TString& Out)
{
GLint SourceLen;
glGetShaderiv(Shader, GL_SHADER_SOURCE_LENGTH, &SourceLen);
GLchar *Source = new GLchar[SourceLen];
glGetShaderSource(Shader, SourceLen, NULL, Source);
GLint LogLen;
glGetShaderiv(Shader, GL_INFO_LOG_LENGTH, &LogLen);
GLchar *InfoLog = new GLchar[LogLen];
glGetShaderInfoLog(Shader, LogLen, NULL, InfoLog);
std::ofstream ShaderOut;
ShaderOut.open(*Out);
if (SourceLen > 0)
ShaderOut << Source;
if (LogLen > 0)
ShaderOut << InfoLog;
ShaderOut.close();
delete[] Source;
delete[] InfoLog;
}

45
src/Core/OpenGL/CShader.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef CSHADER_H
#define CSHADER_H
#include <gl/glew.h>
#include <Common/TString.h>
class CShader
{
bool mVertexShaderExists;
bool mPixelShaderExists;
bool mProgramExists;
GLuint mVertexShader;
GLuint mPixelShader;
GLuint mProgram;
GLuint mMVPBlockIndex;
GLuint mVertexBlockIndex;
GLuint mPixelBlockIndex;
GLuint mLightBlockIndex;
static CShader* spCurrentShader;
public:
CShader();
CShader(const char* kpVertexSource, const char* kpPixelSource);
~CShader();
bool CompileVertexSource(const char* kpSource);
bool CompilePixelSource(const char* kpSource);
bool LinkShaders();
bool IsValidProgram();
GLuint GetProgramID();
GLuint GetUniformLocation(const char* kpUniform);
GLuint GetUniformBlockIndex(const char* kpUniformBlock);
void SetCurrent();
// Static
static CShader* FromResourceFile(const TString& ShaderName);
static CShader* CurrentShader();
static void KillCachedShader();
private:
void DumpShaderSource(GLuint Shader, const TString& Out);
};
#endif // CSHADER_H

View File

@@ -0,0 +1,443 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <gl/glew.h>
#include "CShaderGenerator.h"
const TString gkCoordSrc[] = {
"RawPosition.xyz",
"RawNormal.xyz",
"0.0, 0.0, 0.0",
"0.0, 0.0, 0.0",
"RawTex0.xy, 1.0",
"RawTex1.xy, 1.0",
"RawTex2.xy, 1.0",
"RawTex3.xy, 1.0",
"RawTex4.xy, 1.0",
"RawTex5.xy, 1.0",
"RawTex6.xy, 1.0",
"RawTex7.xy, 1.0"
};
const TString gkRasSel[] = {
"vec4(COLOR0A0.rgb, 1.0)",
"vec4(COLOR1A1.rgb, 1.0)",
"vec4(0.0, 0.0, 0.0, COLOR0A0.a)",
"vec4(0.0, 0.0, 0.0, COLOR1A1.a)",
"COLOR0A0",
"COLOR1A1",
"vec4(0.0, 0.0, 0.0, 0.0)"
};
const TString gkKonstColor[] = {
"1.0, 1.0, 1.0",
"0.875, 0.875, 0.875",
"0.75, 0.75, 0.75",
"0.625, 0.625, 0.625",
"0.5, 0.5, 0.5",
"0.375, 0.375, 0.375",
"0.25, 0.25, 0.25",
"0.125, 0.125, 0.125",
"",
"",
"",
"",
"KonstColors[0].rgb",
"KonstColors[1].rgb",
"KonstColors[2].rgb",
"KonstColors[3].rgb",
"KonstColors[0].rrr",
"KonstColors[1].rrr",
"KonstColors[2].rrr",
"KonstColors[3].rrr",
"KonstColors[0].ggg",
"KonstColors[1].ggg",
"KonstColors[2].ggg",
"KonstColors[3].ggg",
"KonstColors[0].bbb",
"KonstColors[1].bbb",
"KonstColors[2].bbb",
"KonstColors[3].bbb",
"KonstColors[0].aaa",
"KonstColors[1].aaa",
"KonstColors[2].aaa",
"KonstColors[3].aaa"
};
const TString gkKonstAlpha[] = {
"1.0",
"0.875",
"0.75",
"0.625",
"0.5",
"0.375",
"0.25",
"0.125",
"",
"",
"",
"",
"",
"",
"",
"",
"KonstColors[0].r",
"KonstColors[1].r",
"KonstColors[2].r",
"KonstColors[3].r",
"KonstColors[0].g",
"KonstColors[1].g",
"KonstColors[2].g",
"KonstColors[3].g",
"KonstColors[0].b",
"KonstColors[1].b",
"KonstColors[2].b",
"KonstColors[3].b",
"KonstColors[0].a",
"KonstColors[1].a",
"KonstColors[2].a",
"KonstColors[3].a"
};
const TString gkTevColor[] = {
"Prev.rgb",
"Prev.aaa",
"C0.rgb",
"C0.aaa",
"C1.rgb",
"C1.aaa",
"C2.rgb",
"C2.aaa",
"Tex.rgb",
"Tex.aaa",
"Ras.rgb",
"Ras.aaa",
"1.0, 1.0, 1.0",
"0.5, 0.5, 0.5",
"Konst.rgb",
"0.0, 0.0, 0.0"
};
const TString gkTevAlpha[] = {
"Prev.a",
"C0.a",
"C1.a",
"C2.a",
"Tex.a",
"Ras.a",
"Konst.a",
"0.0"
};
const TString gkTevRigid[] = {
"Prev",
"C0",
"C1",
"C2"
};
CShaderGenerator::CShaderGenerator()
{
}
CShaderGenerator::~CShaderGenerator()
{
}
bool CShaderGenerator::CreateVertexShader(const CMaterial& Mat)
{
std::stringstream ShaderCode;
ShaderCode << "#version 330 core\n"
<< "\n";
// Input
ShaderCode << "// Input\n";
EVertexDescription VtxDesc = Mat.VtxDesc();
if (VtxDesc & ePosition) ShaderCode << "layout(location = 0) in vec3 RawPosition;\n";
if (VtxDesc & eNormal) ShaderCode << "layout(location = 1) in vec3 RawNormal;\n";
if (VtxDesc & eColor0) ShaderCode << "layout(location = 2) in vec4 RawColor0;\n";
if (VtxDesc & eColor1) ShaderCode << "layout(location = 3) in vec4 RawColor1;\n";
if (VtxDesc & eTex0) ShaderCode << "layout(location = 4) in vec2 RawTex0;\n";
if (VtxDesc & eTex1) ShaderCode << "layout(location = 5) in vec2 RawTex1;\n";
if (VtxDesc & eTex2) ShaderCode << "layout(location = 6) in vec2 RawTex2;\n";
if (VtxDesc & eTex3) ShaderCode << "layout(location = 7) in vec2 RawTex3;\n";
if (VtxDesc & eTex4) ShaderCode << "layout(location = 8) in vec2 RawTex4;\n";
if (VtxDesc & eTex5) ShaderCode << "layout(location = 9) in vec2 RawTex5;\n";
if (VtxDesc & eTex6) ShaderCode << "layout(location = 10) in vec2 RawTex6;\n";
ShaderCode << "\n";
// Output
ShaderCode << "// Output\n";
if (VtxDesc & eNormal) ShaderCode << "out vec3 Normal;\n";
if (VtxDesc & eColor0) ShaderCode << "out vec4 Color0;\n";
if (VtxDesc & eColor1) ShaderCode << "out vec4 Color1;\n";
for (u32 iPass = 0; iPass < Mat.PassCount(); iPass++)
if (Mat.Pass(iPass)->TexCoordSource() != 0xFF)
ShaderCode << "out vec3 Tex" << iPass << ";\n";
ShaderCode << "out vec4 COLOR0A0;\n"
<< "out vec4 COLOR1A1;\n";
ShaderCode << "\n";
// Uniforms
ShaderCode << "// Uniforms\n"
<< "layout(std140) uniform MVPBlock\n"
<< "{\n"
<< " mat4 ModelMtx;\n"
<< " mat4 ViewMtx;\n"
<< " mat4 ProjMtx;\n"
<< "};\n"
<< "\n"
<< "layout(std140) uniform VertexBlock\n"
<< "{\n"
<< " mat4 TexMtx[10];\n"
<< " mat4 PostMtx[20];\n"
<< " vec4 COLOR0_Amb;\n"
<< " vec4 COLOR0_Mat;\n"
<< " vec4 COLOR1_Amb;\n"
<< " vec4 COLOR1_Mat;\n"
<< "};\n"
<< "\n"
<< "struct GXLight\n"
<< "{\n"
<< " vec4 Position;\n"
<< " vec4 Direction;\n"
<< " vec4 Color;\n"
<< " vec4 DistAtten;\n"
<< " vec4 AngleAtten;\n"
<< "};\n"
<< "layout(std140) uniform LightBlock {\n"
<< " GXLight Lights[8];\n"
<< "};\n"
<< "uniform int NumLights;\n"
<< "\n";
// Main
ShaderCode << "// Main\n"
<< "void main()\n"
<< "{\n"
<< " mat4 MV = ModelMtx * ViewMtx;\n"
<< " mat4 MVP = MV * ProjMtx;\n";
if (VtxDesc & ePosition) ShaderCode << " gl_Position = vec4(RawPosition, 1) * MVP;\n";
if (VtxDesc & eNormal) ShaderCode << " Normal = normalize(RawNormal.xyz * inverse(transpose(mat3(MV))));\n";
if (VtxDesc & eColor0) ShaderCode << " Color0 = RawColor0;\n";
if (VtxDesc & eColor1) ShaderCode << " Color1 = RawColor1;\n";
// Per-vertex lighting
ShaderCode << "\n"
<< " // Dynamic Lighting\n";
// This bit could do with some cleaning up
// It took a lot of experimentation to get dynamic lights working and I never went back and cleaned it up after
if (Mat.IsLightingEnabled())
{
ShaderCode << " vec4 Illum = vec4(0.0);\n"
<< " vec3 PositionMV = vec3(vec4(RawPosition, 1.0) * MV);\n"
<< " \n"
<< " for (int iLight = 0; iLight < NumLights; iLight++)\n"
<< " {\n"
<< " vec3 LightPosMV = vec3(Lights[iLight].Position * ViewMtx);\n"
<< " vec3 LightDirMV = normalize(Lights[iLight].Direction.xyz * inverse(transpose(mat3(ViewMtx))));\n"
<< " vec3 LightDist = LightPosMV.xyz - PositionMV.xyz;\n"
<< " float DistSquared = dot(LightDist, LightDist);\n"
<< " float Dist = sqrt(DistSquared);\n"
<< " LightDist /= Dist;\n"
<< " vec3 AngleAtten = Lights[iLight].AngleAtten.xyz;\n"
<< " AngleAtten = vec3(AngleAtten.x, AngleAtten.y, AngleAtten.z);\n"
<< " float Atten = max(0, dot(LightDist, LightDirMV.xyz));\n"
<< " Atten = max(0, dot(AngleAtten, vec3(1.0, Atten, Atten * Atten))) / dot(Lights[iLight].DistAtten.xyz, vec3(1.0, Dist, DistSquared));\n"
<< " float DiffuseAtten = max(0, dot(Normal, LightDist));\n"
<< " Illum += (Atten * DiffuseAtten * Lights[iLight].Color);\n"
<< " }\n"
<< " COLOR0A0 = COLOR0_Mat * (Illum + COLOR0_Amb);\n"
<< " COLOR1A1 = COLOR1_Mat * (Illum + COLOR1_Amb);\n"
<< " \n";
}
else
{
ShaderCode << " COLOR0A0 = COLOR0_Mat;\n"
<< " COLOR1A1 = COLOR1_Mat;\n"
<< "\n";
}
// Texture coordinate generation
ShaderCode << " \n"
<< " // TexGen\n";
u32 PassCount = Mat.PassCount();
for (u32 iPass = 0; iPass < PassCount; iPass++)
{
CMaterialPass *pPass = Mat.Pass(iPass);
if (pPass->TexCoordSource() == 0xFF) continue;
EUVAnimMode AnimMode = pPass->AnimMode();
if (AnimMode == eNoUVAnim) // No animation
ShaderCode << " Tex" << iPass << " = vec3(" << gkCoordSrc[pPass->TexCoordSource()] << ");\n";
else // Animation used - texture matrix at least, possibly normalize/post-transform
{
// Texture Matrix
ShaderCode << " Tex" << iPass << " = vec3(vec4(" << gkCoordSrc[pPass->TexCoordSource()] << ", 1.0) * TexMtx[" << iPass << "]).xyz;\n";
if ((AnimMode < 2) || (AnimMode > 5))
{
// Normalization + Post-Transform
ShaderCode << " Tex" << iPass << " = normalize(Tex" << iPass << ");\n";
ShaderCode << " Tex" << iPass << " = vec3(vec4(Tex" << iPass << ", 1.0) * PostMtx[" << iPass << "]).xyz;\n";
}
}
ShaderCode << "\n";
}
ShaderCode << "}\n\n";
// Done!
return mShader->CompileVertexSource(ShaderCode.str().c_str());
}
bool CShaderGenerator::CreatePixelShader(const CMaterial& Mat)
{
std::stringstream ShaderCode;
ShaderCode << "#version 330 core\n"
<< "\n";
EVertexDescription VtxDesc = Mat.VtxDesc();
if (VtxDesc & ePosition) ShaderCode << "in vec3 Position;\n";
if (VtxDesc & eNormal) ShaderCode << "in vec3 Normal;\n";
if (VtxDesc & eColor0) ShaderCode << "in vec4 Color0;\n";
if (VtxDesc & eColor1) ShaderCode << "in vec4 Color1;\n";
u32 PassCount = Mat.PassCount();
for (u32 iPass = 0; iPass < PassCount; iPass++)
if (Mat.Pass(iPass)->TexCoordSource() != 0xFF)
ShaderCode << "in vec3 Tex" << iPass << ";\n";
ShaderCode << "in vec4 COLOR0A0;\n"
<< "in vec4 COLOR1A1;\n"
<< "\n"
<< "out vec4 PixelColor;\n"
<< "\n"
<< "layout(std140) uniform PixelBlock {\n"
<< " vec4 KonstColors[4];\n"
<< " vec4 TevColor;\n"
<< " vec4 TintColor;\n"
<< "};\n\n";
for (u32 iPass = 0; iPass < PassCount; iPass++)
if (Mat.Pass(iPass)->Texture() != nullptr)
ShaderCode << "uniform sampler2D Texture" << iPass << ";\n";
ShaderCode <<"\n";
ShaderCode << "void main()\n"
<< "{\n"
<< " vec4 TevInA = vec4(0, 0, 0, 0), TevInB = vec4(0, 0, 0, 0), TevInC = vec4(0, 0, 0, 0), TevInD = vec4(0, 0, 0, 0);\n"
<< " vec4 Prev = vec4(0, 0, 0, 0), C0 = TevColor, C1 = C0, C2 = C0;\n"
<< " vec4 Ras = vec4(0, 0, 0, 1), Tex = vec4(0, 0, 0, 0);\n"
<< " vec4 Konst = vec4(1, 1, 1, 1);\n";
ShaderCode << " vec2 TevCoord = vec2(0, 0);\n"
<< " \n";
bool Lightmap = false;
for (u32 iPass = 0; iPass < PassCount; iPass++)
{
const CMaterialPass *pPass = Mat.Pass(iPass);
CFourCC PassType = pPass->Type();
ShaderCode << " // TEV Stage " << iPass << " - " << PassType.ToString() << "\n";
if (pPass->Type() == "DIFF") Lightmap = true;
if (!pPass->IsEnabled())
{
ShaderCode << " // Pass is disabled\n\n";
continue;
}
if (pPass->TexCoordSource() != 0xFF)
ShaderCode << " TevCoord = (Tex" << iPass << ".z == 0.0 ? Tex" << iPass << ".xy : Tex" << iPass << ".xy / Tex" << iPass << ".z);\n";
if (pPass->Texture() != nullptr)
ShaderCode << " Tex = texture(Texture" << iPass << ", TevCoord)";
// A couple pass types require special swizzles to access different texture color channels as alpha
if ((PassType == "TRAN") || (PassType == "INCA") || (PassType == "BLOI"))
ShaderCode << ".rgbr";
else if (PassType == "BLOL")
ShaderCode << ".rgbg";
ShaderCode << ";\n";
ShaderCode << " Konst = vec4(" << gkKonstColor[pPass->KColorSel()] << ", " << gkKonstAlpha[pPass->KAlphaSel()] << ");\n";
if (pPass->RasSel() != eRasColorNull)
ShaderCode << " Ras = " << gkRasSel[pPass->RasSel()] << ";\n";
for (u8 iInput = 0; iInput < 4; iInput++)
{
char TevChar = iInput + 0x41; // the current stage number represented as an ASCII letter; eg 0 is 'A'
ShaderCode << " TevIn" << TevChar << " = vec4("
<< gkTevColor[pPass->ColorInput(iInput) & 0xF]
<< ", "
<< gkTevAlpha[pPass->AlphaInput(iInput) & 0x7]
<< ");\n";
}
ShaderCode << " // RGB Combine\n"
<< " "
<< gkTevRigid[pPass->ColorOutput()]
<< ".rgb = ";
ShaderCode << "clamp(vec3(TevInD.rgb + ((1.0 - TevInC.rgb) * TevInA.rgb + TevInC.rgb * TevInB.rgb))";
if ((PassType == "CLR ") && (Lightmap)) ShaderCode << "* 2.0"; // Apply tevscale 2.0 on the color pass if lightmap is present
ShaderCode << ", vec3(0, 0, 0), vec3(1.0, 1.0, 1.0));\n";
ShaderCode << " // Alpha Combine\n"
<< " "
<< gkTevRigid[pPass->AlphaOutput()]
<< ".a = ";
ShaderCode << "clamp(TevInD.a + ((1.0 - TevInC.a) * TevInA.a + TevInC.a * TevInB.a), 0.0, 1.0);\n\n";
}
if (Mat.Options() & CMaterial::ePunchthrough)
{
if (Mat.Version() < eCorruptionProto)
{
ShaderCode << " if (Prev.a <= 0.25) discard;\n"
<< " else Prev.a = 1.0;\n\n";
}
else
{
ShaderCode << " if (Prev.a <= 0.75) discard;\n"
" else Prev.a = 0.0;\n\n";
}
}
ShaderCode << " PixelColor = Prev.rgba * TintColor;\n"
<< "}\n\n";
// Done!
return mShader->CompilePixelSource(ShaderCode.str().c_str());
}
CShader* CShaderGenerator::GenerateShader(const CMaterial& Mat)
{
CShaderGenerator Generator;
Generator.mShader = new CShader();
bool Success = Generator.CreateVertexShader(Mat);
if (Success) Success = Generator.CreatePixelShader(Mat);
Generator.mShader->LinkShaders();
return Generator.mShader;
}

View File

@@ -0,0 +1,22 @@
#ifndef SHADERGEN_H
#define SHADERGEN_H
#include <gl/glew.h>
#include "CShader.h"
#include <Resource/CMaterial.h>
class CShaderGenerator
{
CShader *mShader;
CShaderGenerator();
~CShaderGenerator();
bool CreateVertexShader(const CMaterial& Mat);
bool CreatePixelShader(const CMaterial& Mat);
public:
static CShader* GenerateShader(const CMaterial& Mat);
};
#endif // SHADERGEN_H

View File

@@ -0,0 +1,60 @@
#include "CUniformBuffer.h"
CUniformBuffer::CUniformBuffer()
{
glGenBuffers(1, &mUniformBuffer);
SetBufferSize(0);
}
CUniformBuffer::CUniformBuffer(u32 Size)
{
glGenBuffers(1, &mUniformBuffer);
SetBufferSize(Size);
}
CUniformBuffer::~CUniformBuffer()
{
glDeleteBuffers(1, &mUniformBuffer);
}
void CUniformBuffer::InitializeBuffer()
{
Bind();
glBufferData(GL_UNIFORM_BUFFER, mBufferSize, 0, GL_DYNAMIC_DRAW);
Unbind();
}
void CUniformBuffer::Bind()
{
glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer);
}
void CUniformBuffer::Unbind()
{
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
void CUniformBuffer::BindBase(GLuint index)
{
Bind();
glBindBufferBase(GL_UNIFORM_BUFFER, index, mUniformBuffer);
Unbind();
}
void CUniformBuffer::Buffer(void *pData)
{
Bind();
glBufferSubData(GL_UNIFORM_BUFFER, 0, mBufferSize, pData);
Unbind();
}
void CUniformBuffer::SetBufferSize(u32 Size)
{
mBufferSize = Size;
InitializeBuffer();
}
u32 CUniformBuffer::GetBufferSize()
{
return mBufferSize;
}

View File

@@ -0,0 +1,28 @@
#ifndef CUNIFORMBUFFER_H
#define CUNIFORMBUFFER_H
#include <gl/glew.h>
#include <Common/types.h>
class CUniformBuffer
{
GLuint mUniformBuffer;
u32 mBufferSize;
public:
CUniformBuffer();
CUniformBuffer(u32 Size);
~CUniformBuffer();
void Bind();
void Unbind();
void BindBase(GLuint index);
void Buffer(void *pData);
void SetBufferSize(u32 Size);
u32 GetBufferSize();
private:
void InitializeBuffer();
};
#endif // CUNIFORMBUFFER_H

View File

@@ -0,0 +1,105 @@
#include "CVertexArrayManager.h"
// ************ STATIC MEMBER INITIALIZATION ************
std::vector<CVertexArrayManager*> CVertexArrayManager::sVAManagers;
CVertexArrayManager *CVertexArrayManager::spCurrentManager;
// ************ CONSTRUCTORS/DESTRUCTORS ************
CVertexArrayManager::CVertexArrayManager()
{
mVectorIndex = sVAManagers.size();
sVAManagers.push_back(this);
}
CVertexArrayManager::~CVertexArrayManager()
{
for (auto it = mVBOMap.begin(); it != mVBOMap.end(); it = mVBOMap.begin())
DeleteVAO(it->first);
for (auto it = mDynamicVBOMap.begin(); it != mDynamicVBOMap.end(); it = mDynamicVBOMap.begin())
DeleteVAO(it->first);
sVAManagers.erase(sVAManagers.begin() + mVectorIndex);
if (sVAManagers.size() > mVectorIndex)
for (auto it = sVAManagers.begin() + mVectorIndex; it != sVAManagers.end(); it++)
(*it)->mVectorIndex--;
}
// ************ PUBLIC ************
void CVertexArrayManager::SetCurrent()
{
spCurrentManager = this;
}
void CVertexArrayManager::BindVAO(CVertexBuffer *pVBO)
{
auto it = mVBOMap.find(pVBO);
if (it != mVBOMap.end())
glBindVertexArray(it->second);
else
{
GLuint VAO = pVBO->CreateVAO();
mVBOMap[pVBO] = VAO;
glBindVertexArray(VAO);
}
}
void CVertexArrayManager::BindVAO(CDynamicVertexBuffer *pVBO)
{
// Overload for CDynamicVertexBuffer
auto it = mDynamicVBOMap.find(pVBO);
if (it != mDynamicVBOMap.end())
glBindVertexArray(it->second);
else
{
GLuint VAO = pVBO->CreateVAO();
mDynamicVBOMap[pVBO] = VAO;
glBindVertexArray(VAO);
}
}
void CVertexArrayManager::DeleteVAO(CVertexBuffer *pVBO)
{
auto it = mVBOMap.find(pVBO);
if (it != mVBOMap.end())
{
glDeleteVertexArrays(1, &it->second);
mVBOMap.erase(it);
}
}
void CVertexArrayManager::DeleteVAO(CDynamicVertexBuffer *pVBO)
{
// Overload for CDynamicVertexBuffer
auto it = mDynamicVBOMap.find(pVBO);
if (it != mDynamicVBOMap.end())
{
glDeleteVertexArrays(1, &it->second);
mDynamicVBOMap.erase(it);
}
}
// ************ STATIC ************
CVertexArrayManager* CVertexArrayManager::Current()
{
return spCurrentManager;
}
void CVertexArrayManager::DeleteAllArraysForVBO(CVertexBuffer *pVBO)
{
for (u32 iVAM = 0; iVAM < sVAManagers.size(); iVAM++)
sVAManagers[iVAM]->DeleteVAO(pVBO);
}
void CVertexArrayManager::DeleteAllArraysForVBO(CDynamicVertexBuffer *pVBO)
{
for (u32 iVAM = 0; iVAM < sVAManagers.size(); iVAM++)
sVAManagers[iVAM]->DeleteVAO(pVBO);
}

View File

@@ -0,0 +1,34 @@
#ifndef CVERTEXARRAYMANAGER_H
#define CVERTEXARRAYMANAGER_H
#include "CDynamicVertexBuffer.h"
#include "CVertexBuffer.h"
#include <unordered_map>
#include <vector>
#include <GL/glew.h>
class CVertexArrayManager
{
std::unordered_map<CVertexBuffer*, GLuint> mVBOMap;
std::unordered_map<CDynamicVertexBuffer*, GLuint> mDynamicVBOMap;
u32 mVectorIndex;
static std::vector<CVertexArrayManager*> sVAManagers;
static CVertexArrayManager *spCurrentManager;
public:
CVertexArrayManager();
~CVertexArrayManager();
void SetCurrent();
void BindVAO(CVertexBuffer *pVBO);
void BindVAO(CDynamicVertexBuffer *pVBO);
void DeleteVAO(CVertexBuffer *pVBO);
void DeleteVAO(CDynamicVertexBuffer *pVBO);
static CVertexArrayManager* Current();
static void DeleteAllArraysForVBO(CVertexBuffer *pVBO);
static void DeleteAllArraysForVBO(CDynamicVertexBuffer *pVBO);
};
#endif // CVERTEXARRAYMANAGER_H

View File

@@ -0,0 +1,230 @@
#include "CVertexBuffer.h"
#include "CVertexArrayManager.h"
#include <Core/CGraphics.h>
CVertexBuffer::CVertexBuffer()
{
mBuffered = false;
SetVertexDesc(ePosition | eNormal | eTex0 | eTex1 | eTex2 | eTex3 | eTex4 | eTex5 | eTex6 | eTex7);
}
CVertexBuffer::CVertexBuffer(EVertexDescription Desc)
{
mBuffered = false;
SetVertexDesc(Desc);
}
CVertexBuffer::~CVertexBuffer()
{
CVertexArrayManager::DeleteAllArraysForVBO(this);
if (mBuffered)
glDeleteBuffers(12, mAttribBuffers);
}
u16 CVertexBuffer::AddVertex(const CVertex& Vert)
{
if (mPositions.size() == 0xFFFF) throw std::overflow_error("VBO contains too many vertices");
if (mVtxDesc & ePosition) mPositions.push_back(Vert.Position);
if (mVtxDesc & eNormal) mNormals.push_back(Vert.Normal);
if (mVtxDesc & eColor0) mColors[0].push_back(Vert.Color[0]);
if (mVtxDesc & eColor1) mColors[1].push_back(Vert.Color[1]);
for (u32 iTex = 0; iTex < 8; iTex++)
if (mVtxDesc & (eTex0 << (iTex * 2))) mTexCoords[iTex].push_back(Vert.Tex[iTex]);
for (u32 iMtx = 0; iMtx < 8; iMtx++)
if (mVtxDesc & (ePosMtx << iMtx)) mTexCoords[iMtx].push_back(Vert.MatrixIndices[iMtx]);
return (mPositions.size() - 1);
}
u16 CVertexBuffer::AddIfUnique(const CVertex& Vert, u16 Start)
{
if (Start < mPositions.size())
{
for (u16 iVert = Start; iVert < mPositions.size(); iVert++)
{
// I use a bool because "continue" doesn't work properly within the iTex loop
bool Unique = false;
if (mVtxDesc & ePosition)
if (Vert.Position != mPositions[iVert]) Unique = true;
if ((!Unique) && (mVtxDesc & eNormal))
if (Vert.Normal != mNormals[iVert]) Unique = true;
if ((!Unique) && (mVtxDesc & eColor0))
if (Vert.Color[0] != mColors[0][iVert]) Unique = true;
if ((!Unique) && (mVtxDesc & eColor1))
if (Vert.Color[1] != mColors[1][iVert]) Unique = true;
if (!Unique)
for (u32 iTex = 0; iTex < 8; iTex++)
if ((mVtxDesc & (eTex0 << (iTex * 2))))
if (Vert.Tex[iTex] != mTexCoords[iTex][iVert])
{
Unique = true;
break;
}
if (!Unique) return iVert;
}
}
return AddVertex(Vert);
}
void CVertexBuffer::Reserve(u16 size)
{
u32 ReserveSize = mPositions.size() + size;
if (mVtxDesc & ePosition)
mPositions.reserve(ReserveSize);
if (mVtxDesc & eNormal)
mNormals.reserve(ReserveSize);
if (mVtxDesc & eColor0)
mColors[0].reserve(ReserveSize);
if (mVtxDesc & eColor1)
mColors[1].reserve(ReserveSize);
for (u32 iTex = 0; iTex < 8; iTex++)
if (mVtxDesc & (eTex0 << (iTex * 2)))
mTexCoords[iTex].reserve(ReserveSize);
}
void CVertexBuffer::Clear()
{
if (mBuffered)
glDeleteBuffers(12, mAttribBuffers);
mBuffered = false;
mPositions.clear();
mNormals.clear();
mColors[0].clear();
mColors[1].clear();
for (u32 iTex = 0; iTex < 8; iTex++)
mTexCoords[iTex].clear();
}
void CVertexBuffer::Buffer()
{
// Make sure we don't end up with two buffers for the same data...
if (mBuffered)
{
glDeleteBuffers(12, mAttribBuffers);
mBuffered = false;
}
// Generate buffers
glGenBuffers(12, mAttribBuffers);
for (u32 iAttrib = 0; iAttrib < 12; iAttrib++)
{
int Attrib = (ePosition << (iAttrib * 2));
bool HasAttrib = ((mVtxDesc & Attrib) != 0);
if (!HasAttrib) continue;
if (iAttrib < 2)
{
std::vector<CVector3f> *pBuffer = (iAttrib == 0) ? &mPositions : &mNormals;
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glBufferData(GL_ARRAY_BUFFER, pBuffer->size() * sizeof(CVector3f), pBuffer->data(), GL_STATIC_DRAW);
}
else if (iAttrib < 4)
{
u8 idx = (u8) (iAttrib - 2);
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glBufferData(GL_ARRAY_BUFFER, mColors[idx].size() * sizeof(CColor), mColors[idx].data(), GL_STATIC_DRAW);
}
else
{
u8 idx = (u8) (iAttrib - 4);
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glBufferData(GL_ARRAY_BUFFER, mTexCoords[idx].size() * sizeof(CVector2f), mTexCoords[idx].data(), GL_STATIC_DRAW);
}
}
mBuffered = true;
}
void CVertexBuffer::Bind()
{
if (!mBuffered) Buffer();
CVertexArrayManager::Current()->BindVAO(this);
}
void CVertexBuffer::Unbind()
{
glBindVertexArray(0);
}
bool CVertexBuffer::IsBuffered()
{
return mBuffered;
}
EVertexDescription CVertexBuffer::VertexDesc()
{
return mVtxDesc;
}
void CVertexBuffer::SetVertexDesc(EVertexDescription Desc)
{
Clear();
mVtxDesc = Desc;
}
u32 CVertexBuffer::Size()
{
return mPositions.size();
}
GLuint CVertexBuffer::CreateVAO()
{
GLuint VertexArray;
glGenVertexArrays(1, &VertexArray);
glBindVertexArray(VertexArray);
for (u32 iAttrib = 0; iAttrib < 12; iAttrib++)
{
int Attrib = (ePosition << (iAttrib * 2));
bool HasAttrib = ((mVtxDesc & Attrib) != 0);
if (!HasAttrib) continue;
if (iAttrib < 2)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glVertexAttribPointer(iAttrib, 3, GL_FLOAT, GL_FALSE, sizeof(CVector3f), (void*) 0);
glEnableVertexAttribArray(iAttrib);
}
else if (iAttrib < 4)
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glVertexAttribPointer(iAttrib, 1, GL_UNSIGNED_INT, GL_FALSE, sizeof(CColor), (void*) 0);
glEnableVertexAttribArray(iAttrib);
}
else
{
glBindBuffer(GL_ARRAY_BUFFER, mAttribBuffers[iAttrib]);
glVertexAttribPointer(iAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(CVector2f), (void*) 0);
glEnableVertexAttribArray(iAttrib);
}
}
glBindVertexArray(0);
return VertexArray;
}

View File

@@ -0,0 +1,37 @@
#ifndef CVERTEXBUFFER_H
#define CVERTEXBUFFER_H
#include <GL/glew.h>
#include <Resource/model/CVertex.h>
#include <Resource/model/EVertexDescription.h>
#include <vector>
class CVertexBuffer
{
EVertexDescription mVtxDesc; // Flags that indicate what vertex attributes are enabled on this vertex buffer
GLuint mAttribBuffers[12]; // Separate GL buffer for each attribute to allow not tracking unused attribs. No support for matrix indices currently.
std::vector<CVector3f> mPositions; // Vector of vertex positions
std::vector<CVector3f> mNormals; // Vector of vertex normals
std::vector<CColor> mColors[2]; // Vectors of vertex colors
std::vector<CVector2f> mTexCoords[8]; // Vectors of texture coordinates
bool mBuffered; // Bool value that indicates whether the attributes have been buffered.
public:
CVertexBuffer();
CVertexBuffer(EVertexDescription Desc);
~CVertexBuffer();
u16 AddVertex(const CVertex& vtx);
u16 AddIfUnique(const CVertex& vtx, u16 start);
void Reserve(u16 size);
void Clear();
void Buffer();
void Bind();
void Unbind();
bool IsBuffered();
EVertexDescription VertexDesc();
void SetVertexDesc(EVertexDescription Desc);
u32 Size();
GLuint CreateVAO();
};
#endif // CVERTEXBUFFER_H

View File

@@ -0,0 +1,37 @@
#include "GLCommon.h"
#include <stdexcept>
GLenum glBlendFactor[] = {
GL_ZERO, // GX_BL_ZERO
GL_ONE, // GX_BL_ONE
GL_SRC_COLOR, // GX_BL_SRCCLR / GX_BL_DSTCLR
GL_ONE_MINUS_SRC_COLOR, // GX_BL_INVSRCCLR / GX_BL_INVDSTCLR
GL_SRC_ALPHA, // GX_BL_SRCALPHA
GL_ONE_MINUS_SRC_ALPHA, // GX_BL_INVSRCALPHA
GL_DST_ALPHA, // GX_BL_DSTALPHA
GL_ONE_MINUS_DST_ALPHA // GX_BL_INVDSTALPHA
};
GLenum glZMode[] = {
GL_NEVER, // GX_NEVER
GL_LESS, // GX_LESS
GL_EQUAL, // GX_EQUAL
GL_LEQUAL, // GX_LEQUAL
GL_GREATER, // GX_GREATER
GL_NOTEQUAL, // GX_NEQUAL
GL_ALWAYS // GX_ALWAYS
};
GLenum GXPrimToGLPrim(EGXPrimitiveType t) {
switch (t) {
case eGX_Quads: return GL_TRIANGLE_STRIP; // Quads are converted to strips
case eGX_Triangles: return GL_TRIANGLE_STRIP; // Triangles are converted to strips
case eGX_TriangleStrip: return GL_TRIANGLE_STRIP;
case eGX_TriangleFan: return GL_TRIANGLE_STRIP; // Fans are converted to strips
case eGX_Lines: return GL_LINES;
case eGX_LineStrip: return GL_LINE_STRIP;
case eGX_Points: return GL_POINTS;
default: throw std::invalid_argument("Invalid GX primitive type");
}
}

View File

@@ -0,0 +1,34 @@
#ifndef GLCOMMON_H
#define GLCOMMON_H
#include <GL/glew.h>
#include <Common/types.h>
enum EBlendFactor
{
eBlendZero = GL_ZERO,
eBlendOne = GL_ONE,
eBlendSrcColor = GL_SRC_COLOR,
eBlendInvSrcColor = GL_ONE_MINUS_SRC_COLOR,
eBlendSrcAlpha = GL_SRC_ALPHA,
eBlendInvSrcAlpha = GL_ONE_MINUS_SRC_ALPHA,
eBlendDstAlpha = GL_DST_ALPHA,
eBlendInvDstAlpha = GL_ONE_MINUS_DST_ALPHA
};
enum EGXPrimitiveType
{
eGX_Quads = 0x80,
eGX_Triangles = 0x90,
eGX_TriangleStrip = 0x98,
eGX_TriangleFan = 0xA0,
eGX_Lines = 0xA8,
eGX_LineStrip = 0xB0,
eGX_Points = 0xB8
};
extern GLenum glBlendFactor[];
extern GLenum glZMode[];
GLenum GXPrimToGLPrim(EGXPrimitiveType t);
#endif // GLCOMMON_H

370
src/Core/Render/CCamera.cpp Normal file
View File

@@ -0,0 +1,370 @@
#include "CCamera.h"
#include "CGraphics.h"
#include <Common/CQuaternion.h>
#include <Common/Math.h>
#include <gtc/matrix_transform.hpp>
CCamera::CCamera()
{
mMode = eFreeCamera;
mPosition = CVector3f(0);
mAspectRatio = 1.7777777f;
mYaw = -Math::skHalfPi;
mPitch = 0.0f;
SetOrbit(CVector3f(0), 5.f);
mMoveSpeed = 1.f;
mLookSpeed = 1.f;
mTransformDirty = true;
mViewDirty = true;
mProjectionDirty = true;
mFrustumPlanesDirty = true;
}
CCamera::CCamera(CVector3f Position, CVector3f /*Target*/)
{
// todo: make it actually look at the target!
// don't actually use this constructor, it's unfinished and won't work properly
mMode = eFreeCamera;
mMoveSpeed = 1.f;
mLookSpeed = 1.f;
mPosition = Position;
mYaw = -Math::skHalfPi;
mPitch = 0.0f;
}
void CCamera::Pan(float XAmount, float YAmount)
{
if (mMode == eFreeCamera)
{
mPosition += mRightVector * (XAmount * mMoveSpeed);
mPosition += mUpVector * (YAmount * mMoveSpeed);
mTransformDirty = true;
mViewDirty = true;
mFrustumPlanesDirty = true;
}
else
Rotate(-XAmount * 0.3f, YAmount * 0.3f);
}
void CCamera::Rotate(float XAmount, float YAmount)
{
mYaw -= (XAmount * mLookSpeed * 0.3f);
mPitch -= (YAmount * mLookSpeed * 0.3f);
ValidatePitch();
mTransformDirty = true;
mViewDirty = true;
mFrustumPlanesDirty = true;
}
void CCamera::Zoom(float Amount)
{
if (mMode == eFreeCamera)
mPosition += mDirection * (Amount * mMoveSpeed);
else
{
mOrbitDistance -= Amount * mMoveSpeed;
mTransformDirty = true;
}
mViewDirty = true;
mFrustumPlanesDirty = true;
}
void CCamera::Snap(CVector3f Position)
{
mPosition = Position;
mYaw = -Math::skHalfPi;
mPitch = 0.0f;
mTransformDirty = true;
mViewDirty = true;
mFrustumPlanesDirty = true;
}
void CCamera::ProcessKeyInput(EKeyInputs KeyFlags, double DeltaTime)
{
float FDeltaTime = (float) DeltaTime;
if (KeyFlags & eWKey) Zoom(FDeltaTime * 25.f);
if (KeyFlags & eSKey) Zoom(-FDeltaTime * 25.f);
if (KeyFlags & eQKey) Pan(0, -FDeltaTime * 25.f);
if (KeyFlags & eEKey) Pan(0, FDeltaTime * 25.f);
if (KeyFlags & eAKey) Pan(-FDeltaTime * 25.f, 0);
if (KeyFlags & eDKey) Pan(FDeltaTime * 25.f, 0);
}
void CCamera::ProcessMouseInput(EKeyInputs KeyFlags, EMouseInputs MouseFlags, float XMovement, float YMovement)
{
// Free Camera
if (mMode == eFreeCamera)
{
if (MouseFlags & eMiddleButton)
{
if (KeyFlags & eCtrlKey) Zoom(-YMovement * 0.2f);
else Pan(-XMovement, YMovement);
}
else if (MouseFlags & eRightButton) Rotate(XMovement, YMovement);
}
// Orbit Camera
else if (mMode == eOrbitCamera)
{
if ((MouseFlags & eMiddleButton) || (MouseFlags & eRightButton))
Pan(-XMovement, YMovement);
}
}
CRay CCamera::CastRay(CVector2f DeviceCoords) const
{
CMatrix4f InverseVP = (ViewMatrix().Transpose() * ProjectionMatrix().Transpose()).Inverse();
CVector3f RayOrigin = CVector3f(DeviceCoords.x, DeviceCoords.y, -1.f) * InverseVP;
CVector3f RayTarget = CVector3f(DeviceCoords.x, DeviceCoords.y, 0.f) * InverseVP;
CVector3f RayDir = (RayTarget - RayOrigin).Normalized();
CRay Ray;
Ray.SetOrigin(RayOrigin);
Ray.SetDirection(RayDir);
return Ray;
}
void CCamera::SetMoveMode(ECameraMoveMode Mode)
{
mMode = Mode;
mViewDirty = true;
mFrustumPlanesDirty = true;
if (mMode == eOrbitCamera)
mTransformDirty = true;
}
void CCamera::SetOrbit(const CVector3f& OrbitTarget, float Distance)
{
mOrbitTarget = OrbitTarget;
mOrbitDistance = Distance;
if (mMode == eOrbitCamera)
{
mTransformDirty = true;
mViewDirty = true;
mFrustumPlanesDirty = true;
}
}
void CCamera::SetOrbit(const CAABox& OrbitTarget, float DistScale /*= 4.f*/)
{
CVector3f Min = OrbitTarget.Min();
CVector3f Max = OrbitTarget.Max();
mOrbitTarget = OrbitTarget.Center();
// Find largest extent
CVector3f Extent = (Max - Min) / 2.f;
float Dist = 0.f;
if (Extent.x >= Extent.y && Extent.x >= Extent.z) Dist = Extent.x;
else if (Extent.y >= Extent.x && Extent.y >= Extent.z) Dist = Extent.y;
else Dist = Extent.z;
mOrbitDistance = Dist * DistScale;
if (mMode == eOrbitCamera)
{
mTransformDirty = true;
mViewDirty = true;
mFrustumPlanesDirty = true;
}
}
void CCamera::SetOrbitDistance(float Distance)
{
mOrbitDistance = Distance;
if (mMode == eOrbitCamera)
{
mTransformDirty = true;
mViewDirty = true;
mFrustumPlanesDirty = true;
}
}
void CCamera::LoadMatrices() const
{
CGraphics::sMVPBlock.ViewMatrix = ViewMatrix();
CGraphics::sMVPBlock.ProjectionMatrix = ProjectionMatrix();
CGraphics::UpdateMVPBlock();
}
// ************ GETTERS ************
CVector3f CCamera::Position() const
{
UpdateTransform();
return mPosition;
}
CVector3f CCamera::Direction() const
{
UpdateTransform();
return mDirection;
}
CVector3f CCamera::UpVector() const
{
UpdateTransform();
return mUpVector;
}
CVector3f CCamera::RightVector() const
{
UpdateTransform();
return mRightVector;
}
float CCamera::Yaw() const
{
return mYaw;
}
float CCamera::Pitch() const
{
return mPitch;
}
float CCamera::FieldOfView() const
{
return 55.f;
}
ECameraMoveMode CCamera::MoveMode() const
{
return mMode;
}
const CMatrix4f& CCamera::ViewMatrix() const
{
UpdateView();
return mViewMatrix;
}
const CMatrix4f& CCamera::ProjectionMatrix() const
{
UpdateProjection();
return mProjectionMatrix;
}
const CFrustumPlanes& CCamera::FrustumPlanes() const
{
UpdateFrustum();
return mFrustumPlanes;
}
// ************ SETTERS ************
void CCamera::SetYaw(float Yaw)
{
mYaw = Yaw;
mTransformDirty = true;
}
void CCamera::SetPitch(float Pitch)
{
mPitch = Pitch;
ValidatePitch();
mTransformDirty = true;
}
void CCamera::SetMoveSpeed(float MoveSpeed)
{
mMoveSpeed = MoveSpeed;
}
void CCamera::SetLookSpeed(float LookSpeed)
{
mLookSpeed = LookSpeed;
}
void CCamera::SetAspectRatio(float AspectRatio)
{
mAspectRatio = AspectRatio;
mProjectionDirty = true;
mFrustumPlanesDirty = true;
}
// ************ PRIVATE ************
void CCamera::ValidatePitch()
{
// This function mainly just exists to ensure the camera doesn't flip upside down
if (mPitch > Math::skHalfPi) mPitch = Math::skHalfPi;
if (mPitch < -Math::skHalfPi) mPitch = -Math::skHalfPi;
}
void CCamera::UpdateTransform() const
{
// Transform should be marked dirty when pitch, yaw, or orbit target/distance are changed
if (mTransformDirty)
{
mDirection = CVector3f(
cos(mPitch) * cos(mYaw),
cos(mPitch) * sin(mYaw),
sin(mPitch)
);
mRightVector = CVector3f(
cos(mYaw - Math::skHalfPi),
sin(mYaw - Math::skHalfPi),
0
);
mUpVector = mRightVector.Cross(mDirection);
// Update position
if (mMode == eOrbitCamera)
{
if (mOrbitDistance < 1.f) mOrbitDistance = 1.f;
mPosition = mOrbitTarget + (mDirection * -mOrbitDistance);
}
mViewDirty = true;
mFrustumPlanesDirty = true;
mTransformDirty = false;
}
}
void CCamera::UpdateView() const
{
// todo: don't use glm
UpdateTransform();
if (mViewDirty)
{
glm::vec3 glmpos(mPosition.x, mPosition.y, mPosition.z);
glm::vec3 glmdir(mDirection.x, mDirection.y, mDirection.z);
glm::vec3 glmup(mUpVector.x, mUpVector.y, mUpVector.z);
mViewMatrix = CMatrix4f::FromGlmMat4(glm::lookAt(glmpos, glmpos + glmdir, glmup)).Transpose();
mViewDirty = false;
}
}
void CCamera::UpdateProjection() const
{
if (mProjectionDirty)
{
mProjectionMatrix = Math::PerspectiveMatrix(55.f, mAspectRatio, 0.1f, 4096.f);
mProjectionDirty = false;
}
}
void CCamera::UpdateFrustum() const
{
UpdateTransform();
if (mFrustumPlanesDirty)
{
mFrustumPlanes.SetPlanes(mPosition, mDirection, 55.f, mAspectRatio, 0.1f, 4096.f);
mFrustumPlanesDirty = false;
}
}

99
src/Core/Render/CCamera.h Normal file
View File

@@ -0,0 +1,99 @@
#ifndef CCAMERA_H
#define CCAMERA_H
#include "CFrustumPlanes.h"
#include <Common/CAABox.h>
#include <Common/CMatrix4f.h>
#include <Common/CRay.h>
#include <Common/CVector2i.h>
#include <Common/CVector3f.h>
#include <Common/types.h>
#include <Common/EKeyInputs.h>
#include <Common/EMouseInputs.h>
enum ECameraMoveMode
{
eFreeCamera, eOrbitCamera
};
/* This class uses a lot of mutable members as an optimization so that they can
* be updated as infrequently as possible (eg only when the values are requested
* the next time after changes are made) while still always returning the correct
* value via the const get functions. They are not modified in const functions
* beyond ensuring that all data is valid and synced with everything else (eg
* mPosition is only modified to ensure it's correct in orbit mode given the
* target/distance/pitch/yaw; it won't be modified as a camera snap in a const
* function). */
class CCamera
{
ECameraMoveMode mMode;
mutable CVector3f mPosition;
mutable CVector3f mDirection;
mutable CVector3f mRightVector;
mutable CVector3f mUpVector;
float mAspectRatio;
float mYaw;
float mPitch;
CVector3f mOrbitTarget;
mutable float mOrbitDistance;
float mMoveSpeed;
float mLookSpeed;
mutable CMatrix4f mViewMatrix;
mutable CMatrix4f mProjectionMatrix;
mutable CFrustumPlanes mFrustumPlanes;
mutable bool mTransformDirty;
mutable bool mViewDirty;
mutable bool mProjectionDirty;
mutable bool mFrustumPlanesDirty;
public:
CCamera();
CCamera(CVector3f Position, CVector3f Target);
void Pan(float XAmount, float YAmount);
void Rotate(float XAmount, float YAmount);
void Zoom(float Amount);
void Snap(CVector3f Position);
void ProcessKeyInput(EKeyInputs KeyFlags, double DeltaTime);
void ProcessMouseInput(EKeyInputs KeyFlags, EMouseInputs MouseFlags, float XMovement, float YMovement);
CRay CastRay(CVector2f DeviceCoords) const;
void LoadMatrices() const;
void SetMoveMode(ECameraMoveMode Mode);
void SetOrbit(const CVector3f& OrbitTarget, float Distance);
void SetOrbit(const CAABox& OrbitTarget, float DistScale = 4.f);
void SetOrbitDistance(float Distance);
// Getters
CVector3f Position() const;
CVector3f Direction() const;
CVector3f UpVector() const;
CVector3f RightVector() const;
float Yaw() const;
float Pitch() const;
float FieldOfView() const;
ECameraMoveMode MoveMode() const;
const CMatrix4f& ViewMatrix() const;
const CMatrix4f& ProjectionMatrix() const;
const CFrustumPlanes& FrustumPlanes() const;
// Setters
void SetYaw(float Yaw);
void SetPitch(float Pitch);
void SetMoveSpeed(float MoveSpeed);
void SetLookSpeed(float LookSpeed);
void SetAspectRatio(float AspectRatio);
// Private
private:
void ValidatePitch();
void UpdateTransform() const;
void UpdateView() const;
void UpdateProjection() const;
void UpdateFrustum() const;
};
#endif // CCAMERA_H

View File

@@ -0,0 +1,578 @@
#include "CDrawUtil.h"
#include "CGraphics.h"
#include "CResCache.h"
#include <Common/CTransform4f.h>
#include <iostream>
#include "Log.h"
// ************ MEMBER INITIALIZATION ************
CVertexBuffer CDrawUtil::mGridVertices;
CIndexBuffer CDrawUtil::mGridIndices;
CDynamicVertexBuffer CDrawUtil::mSquareVertices;
CIndexBuffer CDrawUtil::mSquareIndices;
CDynamicVertexBuffer CDrawUtil::mLineVertices;
CIndexBuffer CDrawUtil::mLineIndices;
TResPtr<CModel> CDrawUtil::mpCubeModel;
CVertexBuffer CDrawUtil::mWireCubeVertices;
CIndexBuffer CDrawUtil::mWireCubeIndices;
TResPtr<CModel> CDrawUtil::mpSphereModel;
TResPtr<CModel> CDrawUtil::mpDoubleSidedSphereModel;
TResPtr<CModel> CDrawUtil::mpWireSphereModel;
CShader *CDrawUtil::mpColorShader;
CShader *CDrawUtil::mpColorShaderLighting;
CShader *CDrawUtil::mpBillboardShader;
CShader *CDrawUtil::mpLightBillboardShader;
CShader *CDrawUtil::mpTextureShader;
CShader *CDrawUtil::mpCollisionShader;
CShader *CDrawUtil::mpTextShader;
TResPtr<CTexture> CDrawUtil::mpCheckerTexture;
TResPtr<CTexture> CDrawUtil::mpLightTextures[4];
TResPtr<CTexture> CDrawUtil::mpLightMasks[4];
bool CDrawUtil::mDrawUtilInitialized = false;
// ************ PUBLIC ************
void CDrawUtil::DrawGrid()
{
Init();
mGridVertices.Bind();
CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity;
CGraphics::UpdateMVPBlock();
glBlendFunc(GL_ONE, GL_ZERO);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glLineWidth(1.0f);
UseColorShader(CColor(0.6f, 0.6f, 0.6f, 0.f));
mGridIndices.DrawElements(0, mGridIndices.GetSize() - 4);
glLineWidth(1.5f);
UseColorShader(CColor::skTransparentBlack);
mGridIndices.DrawElements(mGridIndices.GetSize() - 4, 4);
}
void CDrawUtil::DrawSquare()
{
// Overload with default tex coords
CVector2f TexCoords[4] = { CVector2f(0.f, 1.f), CVector2f(1.f, 1.f), CVector2f(1.f, 0.f), CVector2f(0.f, 0.f) };
DrawSquare(&TexCoords[0].x);
}
void CDrawUtil::DrawSquare(const CVector2f& TexUL, const CVector2f& TexUR, const CVector2f& TexBR, const CVector2f& TexBL)
{
// Overload with tex coords specified via parameters
// I don't think that parameters are guaranteed to be contiguous in memory, so:
CVector2f TexCoords[4] = { TexUL, TexUR, TexBR, TexBL };
DrawSquare(&TexCoords[0].x);
}
void CDrawUtil::DrawSquare(const float *pTexCoords)
{
Init();
// Set tex coords
for (u32 iTex = 0; iTex < 8; iTex++)
{
EVertexDescription TexAttrib = (EVertexDescription) (eTex0 << (iTex *2));
mSquareVertices.BufferAttrib(TexAttrib, pTexCoords);
}
// Draw
mSquareVertices.Bind();
mSquareIndices.DrawElements();
mSquareVertices.Unbind();
}
void CDrawUtil::DrawLine(const CVector3f& PointA, const CVector3f& PointB)
{
DrawLine(PointA, PointB, CColor::skWhite);
}
void CDrawUtil::DrawLine(const CVector2f& PointA, const CVector2f& PointB)
{
// Overload for 2D lines
DrawLine(CVector3f(PointA.x, PointA.y, 0.f), CVector3f(PointB.x, PointB.y, 0.f), CColor::skWhite);
}
void CDrawUtil::DrawLine(const CVector3f& PointA, const CVector3f& PointB, const CColor& LineColor)
{
Init();
// Copy vec3s into an array to ensure they are adjacent in memory
CVector3f Points[2] = { PointA, PointB };
mLineVertices.BufferAttrib(ePosition, Points);
// Draw
UseColorShader(LineColor);
mLineVertices.Bind();
mLineIndices.DrawElements();
mLineVertices.Unbind();
}
void CDrawUtil::DrawLine(const CVector2f& PointA, const CVector2f& PointB, const CColor& LineColor)
{
// Overload for 2D lines
DrawLine(CVector3f(PointA.x, PointA.y, 0.f), CVector3f(PointB.x, PointB.y, 0.f), LineColor);
}
void CDrawUtil::DrawCube()
{
Init();
mpCubeModel->Draw(eNoMaterialSetup, 0);
}
void CDrawUtil::DrawCube(const CColor& Color)
{
Init();
UseColorShader(Color);
DrawCube();
}
void CDrawUtil::DrawCube(const CVector3f& Position, const CColor& Color)
{
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Position).ToMatrix4f();
CGraphics::UpdateMVPBlock();
UseColorShader(Color);
DrawCube();
}
void CDrawUtil::DrawShadedCube(const CColor& Color)
{
Init();
UseColorShaderLighting(Color);
DrawCube();
}
void CDrawUtil::DrawWireCube()
{
Init();
glLineWidth(1.f);
mWireCubeVertices.Bind();
mWireCubeIndices.DrawElements();
mWireCubeVertices.Unbind();
}
void CDrawUtil::DrawWireCube(const CAABox& kAABox, const CColor& kColor)
{
Init();
// Calculate model matrix
CTransform4f Transform;
Transform.Scale(kAABox.Size());
Transform.Translate(kAABox.Center());
CGraphics::sMVPBlock.ModelMatrix = Transform.ToMatrix4f();
CGraphics::UpdateMVPBlock();
UseColorShader(kColor);
DrawWireCube();
}
void CDrawUtil::DrawSphere(bool DoubleSided)
{
Init();
if (!DoubleSided)
mpSphereModel->Draw(eNoMaterialSetup, 0);
else
mpDoubleSidedSphereModel->Draw(eNoMaterialSetup, 0);
}
void CDrawUtil::DrawSphere(const CColor &kColor)
{
Init();
UseColorShader(kColor);
DrawSphere(false);
}
void CDrawUtil::DrawWireSphere(const CVector3f& Position, float Radius, const CColor& Color /*= CColor::skWhite*/)
{
Init();
// Create model matrix
CTransform4f Transform;
Transform.Scale(Radius);
Transform.Translate(Position);
CGraphics::sMVPBlock.ModelMatrix = Transform.ToMatrix4f();
CGraphics::UpdateMVPBlock();
// Set other render params
UseColorShader(Color);
CMaterial::KillCachedMaterial();
glBlendFunc(GL_ONE, GL_ZERO);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
// Draw
mpWireSphereModel->Draw(eNoMaterialSetup, 0);
}
void CDrawUtil::DrawBillboard(CTexture* pTexture, const CVector3f& Position, const CVector2f& Scale /*= CVector2f::skOne*/, const CColor& Tint /*= CColor::skWhite*/)
{
Init();
// Create translation-only model matrix
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Position).ToMatrix4f();
CGraphics::UpdateMVPBlock();
// Set uniforms
mpBillboardShader->SetCurrent();
GLuint ScaleLoc = mpBillboardShader->GetUniformLocation("BillboardScale");
glUniform2f(ScaleLoc, Scale.x, Scale.y);
GLuint TintLoc = mpBillboardShader->GetUniformLocation("TintColor");
CVector4f Tint4f = Tint.ToVector4f();
glUniform4f(TintLoc, Tint4f.x, Tint4f.y, Tint4f.z, Tint4f.w);
pTexture->Bind(0);
// Set other properties
CMaterial::KillCachedMaterial();
glBlendFunc(GL_ONE, GL_ZERO);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
// Draw
DrawSquare();
}
void CDrawUtil::DrawLightBillboard(ELightType Type, const CColor& LightColor, const CVector3f& Position, const CVector2f& Scale /*= CVector2f::skOne*/, const CColor& Tint /*= CColor::skWhite*/)
{
Init();
// Create translation-only model matrix
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Position).ToMatrix4f();
CGraphics::UpdateMVPBlock();
// Set uniforms
mpLightBillboardShader->SetCurrent();
GLuint ScaleLoc = mpLightBillboardShader->GetUniformLocation("BillboardScale");
glUniform2f(ScaleLoc, Scale.x, Scale.y);
GLuint ColorLoc = mpLightBillboardShader->GetUniformLocation("LightColor");
CVector4f Color4f = LightColor.ToVector4f();
glUniform4f(ColorLoc, Color4f.x, Color4f.y, Color4f.z, Color4f.w);
GLuint TintLoc = mpLightBillboardShader->GetUniformLocation("TintColor");
CVector4f Tint4f = Tint.ToVector4f();
glUniform4f(TintLoc, Tint4f.x, Tint4f.y, Tint4f.z, Tint4f.w);
CTexture *pTexA = GetLightTexture(Type);
CTexture *pTexB = GetLightMask(Type);
pTexA->Bind(0);
pTexB->Bind(1);
GLuint TextureLoc = mpLightBillboardShader->GetUniformLocation("Texture");
GLuint MaskLoc = mpLightBillboardShader->GetUniformLocation("LightMask");
glUniform1i(TextureLoc, 0);
glUniform1i(MaskLoc, 1);
// Set other properties
CMaterial::KillCachedMaterial();
glBlendFunc(GL_ONE, GL_ZERO);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
// Draw
DrawSquare();
}
void CDrawUtil::UseColorShader(const CColor& kColor)
{
Init();
mpColorShader->SetCurrent();
GLuint ColorLoc = mpColorShader->GetUniformLocation("ColorIn");
CVector4f ColorVec4 = kColor.ToVector4f();
glUniform4f(ColorLoc, ColorVec4.x, ColorVec4.y, ColorVec4.z, ColorVec4.w);
CMaterial::KillCachedMaterial();
}
void CDrawUtil::UseColorShaderLighting(const CColor& kColor)
{
Init();
mpColorShaderLighting->SetCurrent();
GLuint NumLightsLoc = mpColorShaderLighting->GetUniformLocation("NumLights");
glUniform1i(NumLightsLoc, CGraphics::sNumLights);
GLuint ColorLoc = mpColorShaderLighting->GetUniformLocation("ColorIn");
CVector4f ColorVec4 = kColor.ToVector4f();
glUniform4f(ColorLoc, ColorVec4.x, ColorVec4.y, ColorVec4.z, ColorVec4.w);
CMaterial::KillCachedMaterial();
}
void CDrawUtil::UseTextureShader()
{
UseTextureShader(CColor::skWhite);
}
void CDrawUtil::UseTextureShader(const CColor& TintColor)
{
Init();
mpTextureShader->SetCurrent();
GLuint TintColorLoc = mpTextureShader->GetUniformLocation("TintColor");
CVector4f TintVec4 = TintColor.ToVector4f();
glUniform4f(TintColorLoc, TintVec4.x, TintVec4.y, TintVec4.z, TintVec4.w);
CMaterial::KillCachedMaterial();
}
void CDrawUtil::UseCollisionShader(const CColor& TintColor /*= CColor::skWhite*/)
{
Init();
mpCollisionShader->SetCurrent();
LoadCheckerboardTexture(0);
GLuint TintColorLoc = mpCollisionShader->GetUniformLocation("TintColor");
CVector4f Tint4f = TintColor.ToVector4f();
glUniform4f(TintColorLoc, Tint4f.x, Tint4f.y, Tint4f.z, Tint4f.w);
CMaterial::KillCachedMaterial();
}
CShader* CDrawUtil::GetTextShader()
{
Init();
return mpTextShader;
}
void CDrawUtil::LoadCheckerboardTexture(u32 GLTextureUnit)
{
Init();
mpCheckerTexture->Bind(GLTextureUnit);
}
CTexture* CDrawUtil::GetLightTexture(ELightType Type)
{
Init();
return mpLightTextures[Type];
}
CTexture* CDrawUtil::GetLightMask(ELightType Type)
{
Init();
return mpLightMasks[Type];
}
CModel* CDrawUtil::GetCubeModel()
{
Init();
return mpCubeModel;
}
// ************ PRIVATE ************
CDrawUtil::CDrawUtil()
{
}
void CDrawUtil::Init()
{
if (!mDrawUtilInitialized)
{
Log::Write("Initializing CDrawUtil");
InitGrid();
InitSquare();
InitLine();
InitCube();
InitWireCube();
InitSphere();
InitWireSphere();
InitShaders();
InitTextures();
mDrawUtilInitialized = true;
}
}
void CDrawUtil::InitGrid()
{
Log::Write("Creating grid");
mGridVertices.SetVertexDesc(ePosition);
mGridVertices.Reserve(64);
for (s32 i = -7; i < 8; i++)
{
if (i == 0) continue;
mGridVertices.AddVertex(CVector3f(-7.0f, float(i), 0.0f));
mGridVertices.AddVertex(CVector3f( 7.0f, float(i), 0.0f));
mGridVertices.AddVertex(CVector3f(float(i), -7.0f, 0.0f));
mGridVertices.AddVertex(CVector3f(float(i), 7.0f, 0.0f));
}
mGridVertices.AddVertex(CVector3f(-7.0f, 0, 0.0f));
mGridVertices.AddVertex(CVector3f( 7.0f, 0, 0.0f));
mGridVertices.AddVertex(CVector3f(0, -7.0f, 0.0f));
mGridVertices.AddVertex(CVector3f(0, 7.0f, 0.0f));
mGridIndices.Reserve(60);
for (u16 i = 0; i < 60; i++) mGridIndices.AddIndex(i);
mGridIndices.SetPrimitiveType(GL_LINES);
}
void CDrawUtil::InitSquare()
{
Log::Write("Creating square");
mSquareVertices.SetActiveAttribs(ePosition | eNormal |
eTex0 | eTex1 | eTex2 | eTex3 |
eTex4 | eTex5 | eTex6 | eTex7);
mSquareVertices.SetVertexCount(4);
CVector3f SquareVertices[] = {
CVector3f(-1.f, 1.f, 0.f),
CVector3f( 1.f, 1.f, 0.f),
CVector3f( 1.f, -1.f, 0.f),
CVector3f(-1.f, -1.f, 0.f)
};
CVector3f SquareNormals[] = {
CVector3f(0.f, 0.f, 1.f),
CVector3f(0.f, 0.f, 1.f),
CVector3f(0.f, 0.f, 1.f),
CVector3f(0.f, 0.f, 1.f)
};
CVector2f SquareTexCoords[] = {
CVector2f(0.f, 1.f),
CVector2f(1.f, 1.f),
CVector2f(1.f, 0.f),
CVector2f(0.f, 0.f)
};
mSquareVertices.BufferAttrib(ePosition, SquareVertices);
mSquareVertices.BufferAttrib(eNormal, SquareNormals);
for (u32 iTex = 0; iTex < 8; iTex++)
{
EVertexDescription Attrib = (EVertexDescription) (eTex0 << (iTex *2));
mSquareVertices.BufferAttrib(Attrib, SquareTexCoords);
}
mSquareIndices.Reserve(4);
mSquareIndices.SetPrimitiveType(GL_TRIANGLE_STRIP);
mSquareIndices.AddIndex(3);
mSquareIndices.AddIndex(2);
mSquareIndices.AddIndex(0);
mSquareIndices.AddIndex(1);
}
void CDrawUtil::InitLine()
{
Log::Write("Creating line");
mLineVertices.SetActiveAttribs(ePosition);
mLineVertices.SetVertexCount(2);
mLineIndices.Reserve(2);
mLineIndices.SetPrimitiveType(GL_LINES);
mLineIndices.AddIndex(0);
mLineIndices.AddIndex(1);
}
void CDrawUtil::InitCube()
{
Log::Write("Creating cube");
mpCubeModel = gResCache.GetResource("../resources/Cube.cmdl");
}
void CDrawUtil::InitWireCube()
{
Log::Write("Creating wire cube");
mWireCubeVertices.SetVertexDesc(ePosition);
mWireCubeVertices.Reserve(8);
mWireCubeVertices.AddVertex(CVector3f(-0.5f, -0.5f, -0.5f));
mWireCubeVertices.AddVertex(CVector3f(-0.5f, 0.5f, -0.5f));
mWireCubeVertices.AddVertex(CVector3f( 0.5f, 0.5f, -0.5f));
mWireCubeVertices.AddVertex(CVector3f( 0.5f, -0.5f, -0.5f));
mWireCubeVertices.AddVertex(CVector3f(-0.5f, -0.5f, 0.5f));
mWireCubeVertices.AddVertex(CVector3f( 0.5f, -0.5f, 0.5f));
mWireCubeVertices.AddVertex(CVector3f( 0.5f, 0.5f, 0.5f));
mWireCubeVertices.AddVertex(CVector3f(-0.5f, 0.5f, 0.5f));
u16 Indices[] = {
0, 1,
1, 2,
2, 3,
3, 0,
4, 5,
5, 6,
6, 7,
7, 4,
0, 4,
1, 7,
2, 6,
3, 5
};
mWireCubeIndices.AddIndices(Indices, sizeof(Indices) / sizeof(u16));
mWireCubeIndices.SetPrimitiveType(GL_LINES);
}
void CDrawUtil::InitSphere()
{
Log::Write("Creating sphere");
mpSphereModel = gResCache.GetResource("../resources/Sphere.cmdl");
mpDoubleSidedSphereModel = gResCache.GetResource("../resources/SphereDoubleSided.cmdl");
}
void CDrawUtil::InitWireSphere()
{
Log::Write("Creating wire sphere");
mpWireSphereModel = gResCache.GetResource("../resources/WireSphere.cmdl");
}
void CDrawUtil::InitShaders()
{
Log::Write("Creating shaders");
mpColorShader = CShader::FromResourceFile("ColorShader");
mpColorShaderLighting = CShader::FromResourceFile("ColorShaderLighting");
mpBillboardShader = CShader::FromResourceFile("BillboardShader");
mpLightBillboardShader = CShader::FromResourceFile("LightBillboardShader");
mpTextureShader = CShader::FromResourceFile("TextureShader");
mpCollisionShader = CShader::FromResourceFile("CollisionShader");
mpTextShader = CShader::FromResourceFile("TextShader");
}
void CDrawUtil::InitTextures()
{
Log::Write("Loading textures");
mpCheckerTexture = gResCache.GetResource("../resources/Checkerboard.txtr");
mpLightTextures[0] = gResCache.GetResource("../resources/LightAmbient.txtr");
mpLightTextures[1] = gResCache.GetResource("../resources/LightDirectional.txtr");
mpLightTextures[2] = gResCache.GetResource("../resources/LightCustom.txtr");
mpLightTextures[3] = gResCache.GetResource("../resources/LightSpot.txtr");
mpLightMasks[0] = gResCache.GetResource("../resources/LightAmbientMask.txtr");
mpLightMasks[1] = gResCache.GetResource("../resources/LightDirectionalMask.txtr");
mpLightMasks[2] = gResCache.GetResource("../resources/LightCustomMask.txtr");
mpLightMasks[3] = gResCache.GetResource("../resources/LightSpotMask.txtr");
}
void CDrawUtil::Shutdown()
{
if (mDrawUtilInitialized)
{
Log::Write("Shutting down");
delete mpColorShader;
delete mpColorShaderLighting;
delete mpTextureShader;
delete mpCollisionShader;
delete mpTextShader;
mDrawUtilInitialized = false;
}
}

118
src/Core/Render/CDrawUtil.h Normal file
View File

@@ -0,0 +1,118 @@
#ifndef CDRAWUTIL
#define CDRAWUTIL
#include <OpenGL/CVertexBuffer.h>
#include <OpenGL/CDynamicVertexBuffer.h>
#include <OpenGL/CIndexBuffer.h>
#include <Resource/model/CModel.h>
#include <Resource/CLight.h>
// todo: CDrawUtil should work with CRenderer to queue primitives for rendering
// rather than trying to draw them straight away, so that CDrawUtil functions can
// be called from anywhere in the codebase and still function correctly
class CDrawUtil
{
// 7x7 Grid
static CVertexBuffer mGridVertices;
static CIndexBuffer mGridIndices;
// Square
static CDynamicVertexBuffer mSquareVertices;
static CIndexBuffer mSquareIndices;
// Line
static CDynamicVertexBuffer mLineVertices;
static CIndexBuffer mLineIndices;
// Cube
static TResPtr<CModel> mpCubeModel;
// Wire Cube
static CVertexBuffer mWireCubeVertices;
static CIndexBuffer mWireCubeIndices;
// Sphere
static TResPtr<CModel> mpSphereModel;
static TResPtr<CModel> mpDoubleSidedSphereModel;
// Wire Sphere
static TResPtr<CModel> mpWireSphereModel;
// Shaders
static CShader *mpColorShader;
static CShader *mpColorShaderLighting;
static CShader *mpBillboardShader;
static CShader *mpLightBillboardShader;
static CShader *mpTextureShader;
static CShader *mpCollisionShader;
static CShader *mpTextShader;
// Textures
static TResPtr<CTexture> mpCheckerTexture;
static TResPtr<CTexture> mpLightTextures[4];
static TResPtr<CTexture> mpLightMasks[4];
// Have all the above members been initialized?
static bool mDrawUtilInitialized;
public:
static void DrawGrid();
static void DrawSquare();
static void DrawSquare(const CVector2f& TexUL, const CVector2f& TexUR, const CVector2f& TexBR, const CVector2f& TexBL);
static void DrawSquare(const float *pTexCoords);
static void DrawLine(const CVector3f& PointA, const CVector3f& PointB);
static void DrawLine(const CVector2f& PointA, const CVector2f& PointB);
static void DrawLine(const CVector3f& PointA, const CVector3f& PointB, const CColor& LineColor);
static void DrawLine(const CVector2f& PointA, const CVector2f& PointB, const CColor& LineColor);
static void DrawCube();
static void DrawCube(const CColor& Color);
static void DrawCube(const CVector3f& Position, const CColor& Color);
static void DrawShadedCube(const CColor& Color);
static void DrawWireCube();
static void DrawWireCube(const CAABox& AABox, const CColor& Color);
static void DrawSphere(bool DoubleSided = false);
static void DrawSphere(const CColor& Color);
static void DrawWireSphere(const CVector3f& Position, float Radius, const CColor& Color = CColor::skWhite);
static void DrawBillboard(CTexture* pTexture, const CVector3f& Position, const CVector2f& Scale = CVector2f::skOne, const CColor& Tint = CColor::skWhite);
static void DrawLightBillboard(ELightType Type, const CColor& LightColor, const CVector3f& Position, const CVector2f& Scale = CVector2f::skOne, const CColor& Tint = CColor::skWhite);
static void UseColorShader(const CColor& Color);
static void UseColorShaderLighting(const CColor& Color);
static void UseTextureShader();
static void UseTextureShader(const CColor& TintColor);
static void UseCollisionShader(const CColor& TintColor = CColor::skWhite);
static CShader* GetTextShader();
static void LoadCheckerboardTexture(u32 GLTextureUnit);
static CTexture* GetLightTexture(ELightType Type);
static CTexture* GetLightMask(ELightType Type);
static CModel* GetCubeModel();
private:
CDrawUtil(); // Private constructor to prevent class from being instantiated
static void Init();
static void InitGrid();
static void InitSquare();
static void InitLine();
static void InitCube();
static void InitWireCube();
static void InitSphere();
static void InitWireSphere();
static void InitShaders();
static void InitTextures();
public:
static void Shutdown();
};
#endif // CDRAWUTIL

View File

@@ -0,0 +1,178 @@
#include "CGraphics.h"
#include <OpenGL/CShader.h>
#include <Resource/CMaterial.h>
#include "Log.h"
// ************ MEMBER INITIALIZATION ************
CUniformBuffer* CGraphics::mpMVPBlockBuffer;
CUniformBuffer* CGraphics::mpVertexBlockBuffer;
CUniformBuffer* CGraphics::mpPixelBlockBuffer;
CUniformBuffer* CGraphics::mpLightBlockBuffer;
u32 CGraphics::mContextIndices = 0;
u32 CGraphics::mActiveContext = -1;
bool CGraphics::mInitialized = false;
std::vector<CVertexArrayManager*> CGraphics::mVAMs;
CGraphics::SMVPBlock CGraphics::sMVPBlock;
CGraphics::SVertexBlock CGraphics::sVertexBlock;
CGraphics::SPixelBlock CGraphics::sPixelBlock;
CGraphics::SLightBlock CGraphics::sLightBlock;
CGraphics::ELightingMode CGraphics::sLightMode;
u32 CGraphics::sNumLights;
const CColor CGraphics::skDefaultAmbientColor = CColor(0.5f, 0.5f, 0.5f, 1.f);
CColor CGraphics::sAreaAmbientColor = CColor::skBlack;
float CGraphics::sWorldLightMultiplier;
CLight CGraphics::sDefaultDirectionalLights[3] = {
*CLight::BuildDirectional(CVector3f(0), CVector3f (0.f, -0.866025f, -0.5f), CColor(0.3f, 0.3f, 0.3f, 1.f)),
*CLight::BuildDirectional(CVector3f(0), CVector3f(-0.75f, 0.433013f, -0.5f), CColor(0.3f, 0.3f, 0.3f, 1.f)),
*CLight::BuildDirectional(CVector3f(0), CVector3f( 0.75f, 0.433013f, -0.5f), CColor(0.3f, 0.3f, 0.3f, 1.f))
};
// ************ FUNCTIONS ************
void CGraphics::Initialize()
{
if (!mInitialized)
{
Log::Write("Initializing GLEW");
glewExperimental = true;
glewInit();
glGetError(); // This is to work around a glew bug - error is always set after initializing
Log::Write("Creating uniform buffers");
mpMVPBlockBuffer = new CUniformBuffer(sizeof(sMVPBlock));
mpVertexBlockBuffer = new CUniformBuffer(sizeof(sVertexBlock));
mpPixelBlockBuffer = new CUniformBuffer(sizeof(sPixelBlock));
mpLightBlockBuffer = new CUniformBuffer(sizeof(sLightBlock));
sLightMode = eWorldLighting;
sNumLights = 0;
sWorldLightMultiplier = 1.f;
mInitialized = true;
}
mpMVPBlockBuffer->BindBase(0);
mpVertexBlockBuffer->BindBase(1);
mpPixelBlockBuffer->BindBase(2);
mpLightBlockBuffer->BindBase(3);
}
void CGraphics::Shutdown()
{
if (mInitialized)
{
Log::Write("Shutting down CGraphics");
delete mpMVPBlockBuffer;
delete mpVertexBlockBuffer;
delete mpPixelBlockBuffer;
delete mpLightBlockBuffer;
mInitialized = false;
}
}
void CGraphics::UpdateMVPBlock()
{
mpMVPBlockBuffer->Buffer(&sMVPBlock);
}
void CGraphics::UpdateVertexBlock()
{
mpVertexBlockBuffer->Buffer(&sVertexBlock);
}
void CGraphics::UpdatePixelBlock()
{
mpPixelBlockBuffer->Buffer(&sPixelBlock);
}
void CGraphics::UpdateLightBlock()
{
mpLightBlockBuffer->Buffer(&sLightBlock);
}
GLuint CGraphics::MVPBlockBindingPoint()
{
return 0;
}
GLuint CGraphics::VertexBlockBindingPoint()
{
return 1;
}
GLuint CGraphics::PixelBlockBindingPoint()
{
return 2;
}
GLuint CGraphics::LightBlockBindingPoint()
{
return 3;
}
u32 CGraphics::GetContextIndex()
{
for (u32 iCon = 0; iCon < 32; iCon++)
{
u32 Mask = (1 << iCon);
if ((mContextIndices & Mask) == 0)
{
mContextIndices |= Mask;
CVertexArrayManager *pVAM = new CVertexArrayManager;
if (mVAMs.size() >= iCon) mVAMs.resize(iCon + 1);
mVAMs[iCon] = pVAM;
return iCon;
}
}
return -1;
}
u32 CGraphics::GetActiveContext()
{
return mActiveContext;
}
void CGraphics::ReleaseContext(u32 Index)
{
if (Index < 32) mContextIndices &= ~(1 << Index);
if (mActiveContext == Index) mActiveContext = -1;
delete mVAMs[Index];
}
void CGraphics::SetActiveContext(u32 Index)
{
mActiveContext = Index;
mVAMs[Index]->SetCurrent();
CMaterial::KillCachedMaterial();
CShader::KillCachedShader();
}
void CGraphics::SetDefaultLighting()
{
sNumLights = 0; // CLight load function increments the light count by 1, which is why I set it to 0
sDefaultDirectionalLights[0].Load();
sDefaultDirectionalLights[1].Load();
sDefaultDirectionalLights[2].Load();
UpdateLightBlock();
sVertexBlock.COLOR0_Amb = CColor::skGray.ToVector4f();
UpdateVertexBlock();
}
void CGraphics::SetupAmbientColor()
{
if (sLightMode == eWorldLighting)
sVertexBlock.COLOR0_Amb = sAreaAmbientColor.ToVector4f() * sWorldLightMultiplier;
else
sVertexBlock.COLOR0_Amb = skDefaultAmbientColor.ToVector4f();
}
void CGraphics::SetIdentityMVP()
{
sMVPBlock.ModelMatrix = CMatrix4f::skIdentity;
sMVPBlock.ViewMatrix = CMatrix4f::skIdentity;
sMVPBlock.ProjectionMatrix = CMatrix4f::skIdentity;
}

View File

@@ -0,0 +1,99 @@
#ifndef CGRAPHICS_H
#define CGRAPHICS_H
#include <Common/CColor.h>
#include <Common/CMatrix4f.h>
#include <Common/CVector3f.h>
#include <Common/CVector4f.h>
#include <GL/glew.h>
#include <OpenGL/CUniformBuffer.h>
#include <OpenGL/CVertexArrayManager.h>
#include <Resource/CLight.h>
class CGraphics
{
static CUniformBuffer *mpMVPBlockBuffer;
static CUniformBuffer *mpVertexBlockBuffer;
static CUniformBuffer *mpPixelBlockBuffer;
static CUniformBuffer *mpLightBlockBuffer;
static u32 mContextIndices;
static u32 mActiveContext;
static bool mInitialized;
static std::vector<CVertexArrayManager*> mVAMs;
public:
// SMVPBlock
struct SMVPBlock
{
CMatrix4f ModelMatrix;
CMatrix4f ViewMatrix;
CMatrix4f ProjectionMatrix;
};
static SMVPBlock sMVPBlock;
// SVertexBlock
struct SVertexBlock
{
CMatrix4f TexMatrices[10];
CMatrix4f PostMatrices[20];
CVector4f COLOR0_Amb;
CVector4f COLOR0_Mat;
CVector4f COLOR1_Amb;
CVector4f COLOR1_Mat;
};
static SVertexBlock sVertexBlock;
// SPixelBlock
struct SPixelBlock
{
CVector4f Konst[4];
CVector4f TevColor;
CVector4f TintColor;
};
static SPixelBlock sPixelBlock;
// SLightBlock
struct SLightBlock
{
struct SGXLight
{
CVector4f Position;
CVector4f Direction;
CVector4f Color;
CVector4f DistAtten;
CVector4f AngleAtten;
};
SGXLight Lights[8];
};
static SLightBlock sLightBlock;
// Lighting-related
enum ELightingMode { eNoLighting, eBasicLighting, eWorldLighting };
static ELightingMode sLightMode;
static u32 sNumLights;
static const CColor skDefaultAmbientColor;
static CColor sAreaAmbientColor;
static float sWorldLightMultiplier;
static CLight sDefaultDirectionalLights[3];
// Functions
static void Initialize();
static void Shutdown();
static void UpdateMVPBlock();
static void UpdateVertexBlock();
static void UpdatePixelBlock();
static void UpdateLightBlock();
static GLuint MVPBlockBindingPoint();
static GLuint VertexBlockBindingPoint();
static GLuint PixelBlockBindingPoint();
static GLuint LightBlockBindingPoint();
static u32 GetContextIndex();
static u32 GetActiveContext();
static void ReleaseContext(u32 Index);
static void SetActiveContext(u32 Index);
static void SetDefaultLighting();
static void SetupAmbientColor();
static void SetIdentityMVP();
};
#endif // CGRAPHICS_H

View File

@@ -0,0 +1,89 @@
#include "CRenderBucket.h"
#include <algorithm>
#include "CDrawUtil.h"
#include "CGraphics.h"
#include "CRenderer.h"
CRenderBucket::CRenderBucket()
{
mEstSize = 0;
mSize = 0;
}
void CRenderBucket::SetSortType(ESortType Type)
{
mSortType = Type;
}
void CRenderBucket::Add(const SRenderablePtr& ptr)
{
if (mSize >= mEstSize)
mRenderables.push_back(ptr);
else
mRenderables[mSize] = ptr;
mSize++;
}
void CRenderBucket::Sort(CCamera* pCamera)
{
struct {
CCamera *pCamera;
bool operator()(SRenderablePtr left, SRenderablePtr right) {
CVector3f cPos = pCamera->Position();
CVector3f cDir = pCamera->Direction();
CVector3f distL = left.AABox.ClosestPointAlongVector(cDir) - cPos;
float dotL = distL.Dot(cDir);
CVector3f distR = right.AABox.ClosestPointAlongVector(cDir) - cPos;
float dotR = distR.Dot(cDir);
return (dotL > dotR);
}
} backToFront;
backToFront.pCamera = pCamera;
if (mSortType == BackToFront)
std::stable_sort(mRenderables.begin(), mRenderables.begin() + mSize, backToFront);
// Test: draw node bounding boxes + vertices used for sorting
/*for (u32 iNode = 0; iNode < mNodes.size(); iNode++)
{
SMeshPointer *pNode = &mNodes[iNode];
CVector3f Vert = pNode->AABox.ClosestPointAlongVector(Camera.GetDirection());
CDrawUtil::DrawWireCube(pNode->AABox, CColor::skWhite);
CVector3f Dist = Vert - Camera.GetPosition();
float Dot = Dist.Dot(Camera.GetDirection());
if (Dot < 0.f) Dot = -Dot;
if (Dot > 50.f) Dot = 50.f;
float Intensity = 1.f - (Dot / 50.f);
CColor CubeColor(Intensity, Intensity, Intensity, 1.f);
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Vert).ToMatrix4f();
CGraphics::UpdateMVPBlock();
CDrawUtil::DrawCube(CubeColor);
}*/
}
void CRenderBucket::Clear()
{
mEstSize = mSize;
if (mRenderables.size() > mSize) mRenderables.resize(mSize);
mSize = 0;
}
void CRenderBucket::Draw(const SViewInfo& ViewInfo)
{
ERenderOptions Options = ViewInfo.pRenderer->RenderOptions();
for (u32 n = 0; n < mSize; n++)
{
if (mRenderables[n].Command == eDrawMesh)
mRenderables[n].pRenderable->Draw(Options, mRenderables[n].ComponentIndex, ViewInfo);
else if (mRenderables[n].Command == eDrawSelection)
mRenderables[n].pRenderable->DrawSelection();
// todo: implementation for eDrawExtras
}
}

View File

@@ -0,0 +1,33 @@
#ifndef CRENDERBUCKET_H
#define CRENDERBUCKET_H
#include "CCamera.h"
#include "ERenderOptions.h"
#include "SRenderablePtr.h"
#include <Common/types.h>
#include <vector>
class CRenderBucket
{
public:
enum ESortType
{
BackToFront,
FrontToBack
};
private:
ESortType mSortType;
std::vector<SRenderablePtr> mRenderables;
u32 mEstSize;
u32 mSize;
public:
CRenderBucket();
void SetSortType(ESortType Type);
void Add(const SRenderablePtr& ptr);
void Sort(CCamera* pCamera);
void Clear();
void Draw(const SViewInfo& ViewInfo);
};
#endif // CRENDERBUCKET_H

View File

@@ -0,0 +1,388 @@
#include "CRenderer.h"
#include "CDrawUtil.h"
#include "CGraphics.h"
#include "CResCache.h"
#include <Common/AnimUtil.h>
#include <Common/CTransform4f.h>
#include <Resource/factory/CTextureDecoder.h>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <gtx/transform.hpp>
// ************ STATIC MEMBER INITIALIZATION ************
u32 CRenderer::sNumRenderers = 0;
// ************ INITIALIZATION ************
CRenderer::CRenderer()
{
mOptions = eDrawWorld | eDrawObjects | eDrawLights | eDrawSky |
eEnableUVScroll | eEnableBackfaceCull;
mBloomMode = eNoBloom;
mDrawGrid = true;
mInitialized = false;
mContextIndex = -1;
mOpaqueBucket.SetSortType(CRenderBucket::FrontToBack);
mTransparentBucket.SetSortType(CRenderBucket::BackToFront);
sNumRenderers++;
}
CRenderer::~CRenderer()
{
sNumRenderers--;
if (sNumRenderers == 0)
{
CGraphics::Shutdown();
CDrawUtil::Shutdown();
}
}
void CRenderer::Init()
{
if (!mInitialized)
{
CVector4f ClearVec = mClearColor.ToVector4f();
glClearColor(ClearVec.x, ClearVec.y, ClearVec.z, ClearVec.w);
mContextIndex = CGraphics::GetContextIndex();
mInitialized = true;
}
}
// ************ GETTERS/SETTERS ************
ERenderOptions CRenderer::RenderOptions() const
{
return mOptions;
}
void CRenderer::ToggleWorld(bool b)
{
if (b) mOptions |= eDrawWorld;
else mOptions &= ~eDrawWorld;
}
void CRenderer::ToggleWorldCollision(bool b)
{
if (b) mOptions |= eDrawWorldCollision;
else mOptions &= ~eDrawWorldCollision;
}
void CRenderer::ToggleObjects(bool b)
{
if (b) mOptions |= eDrawObjects;
else mOptions &= ~eDrawObjects;
}
void CRenderer::ToggleObjectCollision(bool b)
{
if (b) mOptions |= eDrawObjectCollision;
else mOptions &= ~eDrawObjectCollision;
}
void CRenderer::ToggleLights(bool b)
{
if (b) mOptions |= eDrawLights;
else mOptions &= ~eDrawLights;
}
void CRenderer::ToggleSky(bool b)
{
if (b) mOptions |= eDrawSky;
else mOptions &= ~eDrawSky;
}
void CRenderer::ToggleBackfaceCull(bool b)
{
if (b) mOptions |= eEnableBackfaceCull;
else mOptions &= ~eEnableBackfaceCull;
}
void CRenderer::ToggleUVAnimation(bool b)
{
if (b) mOptions |= eEnableUVScroll;
else mOptions &= ~eEnableUVScroll;
}
void CRenderer::ToggleGrid(bool b)
{
mDrawGrid = b;
}
void CRenderer::ToggleOccluders(bool b)
{
if (b) mOptions |= eEnableOccluders;
else mOptions &= ~eEnableOccluders;
}
void CRenderer::ToggleAlphaDisabled(bool b)
{
if (b) mOptions |= eNoAlpha;
else mOptions &= ~eNoAlpha;
}
void CRenderer::SetBloom(EBloomMode BloomMode)
{
mBloomMode = BloomMode;
if (BloomMode != eNoBloom)
mOptions |= eEnableBloom;
else
mOptions &= ~eEnableBloom;
}
void CRenderer::SetFont(CFont* /*pFont*/)
{
}
void CRenderer::SetClearColor(CColor Clear)
{
mClearColor = Clear;
CVector4f ClearVec = Clear.ToVector4f();
ClearVec.w = 0.f;
glClearColor(ClearVec.x, ClearVec.y, ClearVec.z, ClearVec.w);
}
void CRenderer::SetViewportSize(u32 Width, u32 Height)
{
mViewportWidth = Width;
mViewportHeight = Height;
mBloomHScale = ((float) Width / 640);
mBloomVScale = ((float) Height / 528);
mBloomWidth = (u32) (320 * mBloomHScale);
mBloomHeight = (u32) (224 * mBloomVScale);
mBloomHScale = 1.f / mBloomHScale;
mBloomVScale = 1.f / mBloomVScale;
}
// ************ RENDER ************
void CRenderer::RenderBuckets(const SViewInfo& ViewInfo)
{
if (!mInitialized) Init();
mSceneFramebuffer.Bind();
// Set backface culling
if (mOptions & eEnableBackfaceCull) glEnable(GL_CULL_FACE);
else glDisable(GL_CULL_FACE);
// Render scene to texture
glDepthRange(0.f, 1.f);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
mOpaqueBucket.Draw(ViewInfo);
mOpaqueBucket.Clear();
mTransparentBucket.Sort(ViewInfo.pCamera);
mTransparentBucket.Draw(ViewInfo);
mTransparentBucket.Clear();
}
void CRenderer::RenderBloom()
{
// Check to ensure bloom is enabled
if (mBloomMode == eNoBloom) return;
// Setup
static const float skHOffset[6] = { -0.008595f, -0.005470f, -0.002345f,
0.002345f, 0.005470f, 0.008595f };
static const float skVOffset[6] = { -0.012275f, -0.007815f, -0.003350f,
0.003350f, 0.007815f, 0.012275f };
static const CColor skTintColors[6] = { CColor((u8) 17, 17, 17, 255),
CColor((u8) 53, 53, 53, 255),
CColor((u8) 89, 89, 89, 255),
CColor((u8) 89, 89, 89, 255),
CColor((u8) 53, 53, 53, 255),
CColor((u8) 17, 17, 17, 255) };
u32 BloomWidth = (mBloomMode == eBloom ? mBloomWidth : mViewportWidth);
u32 BloomHeight = (mBloomMode == eBloom ? mBloomHeight : mViewportHeight);
float BloomHScale = (mBloomMode == eBloom ? mBloomHScale : 0);
float BloomVScale = (mBloomMode == eBloom ? mBloomVScale : 0);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, BloomWidth, BloomHeight);
glClearColor(0.f, 0.f, 0.f, 0.f);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_FALSE);
CGraphics::SetIdentityMVP();
CGraphics::UpdateMVPBlock();
// Pass 1: Alpha-blend the scene texture on a black background
mBloomFramebuffers[0].Resize(BloomWidth, BloomHeight);
mBloomFramebuffers[0].Bind();
glClear(GL_COLOR_BUFFER_BIT);
CDrawUtil::UseTextureShader();
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
mSceneFramebuffer.Texture()->Bind(0);
CDrawUtil::DrawSquare();
// Pass 2: Horizontal blur
mBloomFramebuffers[1].Resize(BloomWidth, BloomHeight);
mBloomFramebuffers[1].Bind();
CDrawUtil::UseTextureShader(CColor::skGray);
glBlendFunc(GL_ONE, GL_ZERO);
mBloomFramebuffers[0].Texture()->Bind(0);
CDrawUtil::DrawSquare();
for (u32 iPass = 0; iPass < 6; iPass++)
{
CDrawUtil::UseTextureShader(skTintColors[iPass]);
CVector3f Translate(skHOffset[iPass] * BloomHScale, 0.f, 0.f);
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Translate).ToMatrix4f();
CGraphics::UpdateMVPBlock();
glBlendFunc(GL_ONE, GL_ONE);
CDrawUtil::DrawSquare();
}
// Pass 3: Vertical blur
mBloomFramebuffers[2].Resize(BloomWidth, BloomHeight);
mBloomFramebuffers[2].Bind();
glClear(GL_COLOR_BUFFER_BIT);
CDrawUtil::UseTextureShader(CColor::skGray);
glBlendFunc(GL_ONE, GL_ZERO);
mBloomFramebuffers[1].Texture()->Bind(0);
CDrawUtil::DrawSquare();
for (u32 iPass = 0; iPass < 6; iPass++)
{
CDrawUtil::UseTextureShader(skTintColors[iPass]);
CVector3f Translate(0.f, skVOffset[iPass] * BloomVScale, 0.f);
CGraphics::sMVPBlock.ModelMatrix = CTransform4f::TranslationMatrix(Translate).ToMatrix4f();
CGraphics::UpdateMVPBlock();
glBlendFunc(GL_ONE, GL_ONE);
CDrawUtil::DrawSquare();
}
// Render result onto main scene framebuffer
mSceneFramebuffer.Bind();
glViewport(0, 0, mViewportWidth, mViewportHeight);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
CGraphics::SetIdentityMVP();
CGraphics::UpdateMVPBlock();
CDrawUtil::UseTextureShader();
glBlendFunc(GL_ONE, GL_ONE);
mBloomFramebuffers[2].Texture()->Bind(0);
CDrawUtil::DrawSquare();
if (mBloomMode == eBloomMaps)
{
// Bloom maps are in the framebuffer alpha channel.
// White * dst alpha = bloom map colors
CDrawUtil::UseColorShader(CColor::skWhite);
glBlendFunc(GL_DST_ALPHA, GL_ZERO);
CDrawUtil::DrawSquare();
}
// Clean up
glEnable(GL_DEPTH_TEST);
}
void CRenderer::RenderSky(CModel *pSkyboxModel, const SViewInfo& ViewInfo)
{
if (!mInitialized) Init();
if (!pSkyboxModel) return;
glEnable(GL_CULL_FACE);
CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity;
CGraphics::sVertexBlock.COLOR0_Amb = CVector4f(1.f, 1.f, 1.f, 1.f);
CGraphics::sPixelBlock.TevColor = CVector4f(1.f, 1.f, 1.f, 1.f);
CGraphics::sPixelBlock.TintColor = CColor::skWhite.ToVector4f();
CGraphics::sNumLights = 0;
CGraphics::UpdateVertexBlock();
CGraphics::UpdatePixelBlock();
CGraphics::UpdateLightBlock();
// Load rotation-only view matrix
CGraphics::sMVPBlock.ViewMatrix = ViewInfo.RotationOnlyViewMatrix;
CGraphics::UpdateMVPBlock();
glDepthRange(1.f, 1.f);
pSkyboxModel->Draw(mOptions, 0);
}
void CRenderer::AddOpaqueMesh(IRenderable *pRenderable, int AssetID, CAABox& AABox, ERenderCommand Command)
{
SRenderablePtr ptr;
ptr.pRenderable = pRenderable;
ptr.ComponentIndex = AssetID;
ptr.AABox = AABox;
ptr.Command = Command;
mOpaqueBucket.Add(ptr);
}
void CRenderer::AddTransparentMesh(IRenderable *pRenderable, int AssetID, CAABox& AABox, ERenderCommand Command)
{
SRenderablePtr ptr;
ptr.pRenderable = pRenderable;
ptr.ComponentIndex = AssetID;
ptr.AABox = AABox;
ptr.Command = Command;
mTransparentBucket.Add(ptr);
}
void CRenderer::BeginFrame()
{
if (!mInitialized) Init();
CGraphics::SetActiveContext(mContextIndex);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mDefaultFramebuffer);
mSceneFramebuffer.Resize(mViewportWidth, mViewportHeight);
mSceneFramebuffer.Bind();
glViewport(0, 0, mViewportWidth, mViewportHeight);
InitFramebuffer();
}
void CRenderer::EndFrame()
{
// Render result to screen
glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFramebuffer);
InitFramebuffer();
glViewport(0, 0, mViewportWidth, mViewportHeight);
CGraphics::SetIdentityMVP();
CGraphics::UpdateMVPBlock();
glDisable(GL_DEPTH_TEST);
CDrawUtil::UseTextureShader();
glBlendFunc(GL_ONE, GL_ZERO);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
mSceneFramebuffer.Texture()->Bind(0);
CDrawUtil::DrawSquare();
glEnable(GL_DEPTH_TEST);
gDrawCount = 0;
}
void CRenderer::ClearDepthBuffer()
{
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
}
// ************ PRIVATE ************
void CRenderer::InitFramebuffer()
{
CVector4f Clear = mClearColor.ToVector4f();
Clear.w = 0.f;
glClearColor(Clear.x, Clear.y, Clear.z, Clear.w);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
u32 gDrawCount;

View File

@@ -0,0 +1,91 @@
#ifndef CRENDERER_H
#define CRENDERER_H
#include <gl/glew.h>
#include "CCamera.h"
#include "CGraphics.h"
#include "CRenderBucket.h"
#include "ERenderOptions.h"
#include "ERenderCommand.h"
#include "SRenderablePtr.h"
#include "SViewInfo.h"
#include <Common/CAABox.h>
#include <Common/CColor.h>
#include <Common/CMatrix4f.h>
#include <Common/types.h>
#include <OpenGL/CFramebuffer.h>
#include <Resource/CFont.h>
#include <Resource/CLight.h>
#include <Resource/CTexture.h>
#include <Scene/CSceneNode.h>
class CRenderer
{
public:
enum EBloomMode {
eNoBloom, eBloom, eBloomMaps, eFakeBloom
};
private:
ERenderOptions mOptions;
EBloomMode mBloomMode;
bool mDrawGrid;
CColor mClearColor;
u32 mContextIndex;
CRenderBucket mOpaqueBucket;
CRenderBucket mTransparentBucket;
bool mInitialized;
u32 mViewportWidth, mViewportHeight;
u32 mBloomWidth, mBloomHeight;
float mBloomHScale, mBloomVScale;
CFramebuffer mSceneFramebuffer;
CFramebuffer mBloomFramebuffers[3];
GLint mDefaultFramebuffer;
// Static Members
static u32 sNumRenderers;
public:
// Initialization
CRenderer();
~CRenderer();
void Init();
// Getters/Setters
ERenderOptions RenderOptions() const;
void ToggleWorld(bool b);
void ToggleWorldCollision(bool b);
void ToggleObjects(bool b);
void ToggleObjectCollision(bool b);
void ToggleLights(bool b);
void ToggleSky(bool b);
void ToggleBackfaceCull(bool b);
void ToggleUVAnimation(bool b);
void ToggleGrid(bool b);
void ToggleOccluders(bool b);
void ToggleAlphaDisabled(bool b);
void SetBloom(EBloomMode BloomMode);
void SetFont(CFont *pFont);
void SetClearColor(CColor Clear);
void SetViewportSize(u32 Width, u32 Height);
// Render
void RenderBuckets(const SViewInfo& ViewInfo);
void RenderBloom();
void RenderSky(CModel *pSkyboxModel, const SViewInfo& ViewInfo);
void AddOpaqueMesh(IRenderable *pRenderable, int AssetID, CAABox& AABox, ERenderCommand Command);
void AddTransparentMesh(IRenderable *pRenderable, int AssetID, CAABox& AABox, ERenderCommand Command);
void BeginFrame();
void EndFrame();
void ClearDepthBuffer();
// Private
private:
void InitFramebuffer();
};
extern u32 gDrawCount;
#endif // RENDERMANAGER_H

View File

@@ -0,0 +1,11 @@
#ifndef ERENDERCOMMAND
#define ERENDERCOMMAND
enum ERenderCommand
{
eDrawMesh,
eDrawSelection
};
#endif // ERENDERCOMMAND

View File

@@ -0,0 +1,25 @@
#ifndef ERENDEROPTIONS
#define ERENDEROPTIONS
#include <Common/EnumUtil.h>
enum ERenderOptions
{
eNoRenderOptions = 0x0,
eDrawWorld = 0x1,
eDrawWorldCollision = 0x2,
eDrawObjects = 0x4,
eDrawObjectCollision = 0x8,
eDrawLights = 0x10,
eDrawSky = 0x20,
eEnableUVScroll = 0x40,
eEnableBackfaceCull = 0x80,
eEnableOccluders = 0x100,
eNoMaterialSetup = 0x200,
eEnableBloom = 0x400,
eNoAlpha = 0x800
};
DEFINE_ENUM_FLAGS(ERenderOptions)
#endif // ERENDEROPTIONS

View File

@@ -0,0 +1,20 @@
#ifndef IRENDERABLE_H
#define IRENDERABLE_H
#include "ERenderOptions.h"
#include "SViewInfo.h"
#include <Common/types.h>
class CRenderer;
class IRenderable
{
public:
IRenderable() {}
virtual ~IRenderable() {}
virtual void AddToRenderer(CRenderer* pRenderer, const SViewInfo& ViewInfo) = 0;
virtual void Draw(ERenderOptions /*Options*/, int /*ComponentIndex*/, const SViewInfo& /*ViewInfo*/) {}
virtual void DrawSelection() {}
};
#endif // IRENDERABLE_H

View File

@@ -0,0 +1,18 @@
#ifndef SRENDERABLEPTR_H
#define SRENDERABLEPTR_H
#include <Common/CAABox.h>
#include <Common/types.h>
#include <Core/ERenderCommand.h>
#include <Scene/CSceneNode.h>
#include <Resource/CMaterial.h>
struct SRenderablePtr
{
IRenderable *pRenderable;
u32 ComponentIndex;
CAABox AABox;
ERenderCommand Command;
};
#endif // SRENDERABLEPTR_H

View File

@@ -0,0 +1,20 @@
#ifndef SVIEWINFO
#define SVIEWINFO
#include "CFrustumPlanes.h"
#include <Common/CMatrix4f.h>
#include <Common/CRay.h>
struct SViewInfo
{
class CSceneManager *pScene;
class CRenderer *pRenderer;
class CCamera *pCamera;
bool GameMode;
CFrustumPlanes ViewFrustum;
CMatrix4f RotationOnlyViewMatrix;
};
#endif // SVIEWINFO

View File

@@ -0,0 +1,31 @@
#include "CAnimSet.h"
#include <Core/CResCache.h>
CAnimSet::CAnimSet() : CResource()
{
}
CAnimSet::~CAnimSet()
{
}
u32 CAnimSet::getNodeCount()
{
return nodes.size();
}
TString CAnimSet::getNodeName(u32 node)
{
if (node >= nodes.size())
return nodes[0].name;
else
return nodes[node].name;
}
CModel* CAnimSet::getNodeModel(u32 node)
{
if (node >= nodes.size())
return nodes[0].model;
else
return nodes[node].model;
}

View File

@@ -0,0 +1,36 @@
#ifndef CANIMSET_H
#define CANIMSET_H
#include <Common/types.h>
#include <Core/TResPtr.h>
#include <vector>
#include "model/CModel.h"
#include "CResource.h"
// will expand later! this is where animation support will come in
class CAnimSet : public CResource
{
DECLARE_RESOURCE_TYPE(eAnimSet)
friend class CAnimSetLoader;
struct SNode
{
TString name;
TResPtr<CModel> model;
u32 skinID;
u32 skelID;
SNode() { model = nullptr; }
};
std::vector<SNode> nodes;
public:
CAnimSet();
~CAnimSet();
u32 getNodeCount();
TString getNodeName(u32 node);
CModel* getNodeModel(u32 node);
};
#endif // CCHARACTERSET_H

View File

@@ -0,0 +1,138 @@
#include "CAnimationParameters.h"
#include "CAnimSet.h"
#include <Core/CResCache.h>
#include <Core/Log.h>
#include <iostream>
CAnimationParameters::CAnimationParameters()
{
mGame = ePrime;
mpCharSet = nullptr;
mNodeIndex = 0;
mUnknown1 = 0;
mUnknown2 = 0;
mUnknown3 = 0;
mUnknown4 = 0;
}
CAnimationParameters::CAnimationParameters(CInputStream& SCLY, EGame game)
{
mGame = game;
mpCharSet = nullptr;
mNodeIndex = 0;
mUnknown1 = 0;
mUnknown2 = 0;
mUnknown3 = 0;
mUnknown4 = 0;
if (game <= eEchoes)
{
u32 animSetID = SCLY.ReadLong();
mNodeIndex = SCLY.ReadLong();
mUnknown1 = SCLY.ReadLong();
mpCharSet = gResCache.GetResource(animSetID, "ANCS");
}
else if (game <= eCorruption)
{
u64 charID = SCLY.ReadLongLong();
mUnknown1 = SCLY.ReadLong();
mpCharSet = gResCache.GetResource(charID, "CHAR");
}
else if (game == eReturns)
{
SCLY.Seek(-6, SEEK_CUR);
u32 offset = SCLY.Tell();
u32 propID = SCLY.ReadLong();
SCLY.Seek(2, SEEK_CUR);
mUnknown1 = (u32) SCLY.ReadByte();
mUnknown1 &= 0xFF;
if (mUnknown1 == 0x60)
{
u64 charID = SCLY.ReadLongLong();
mUnknown2 = SCLY.ReadLong();
mUnknown3 = SCLY.ReadLong();
mUnknown4 = SCLY.ReadLong();
mpCharSet = gResCache.GetResource(charID, "CHAR");
}
else if (mUnknown1 != 0x80)
{
Log::FileError(SCLY.GetSourceString(), offset,
"Unexpected AnimationParameters byte: " + TString::HexString(mUnknown1, true, true, 2) + " (property " + TString::HexString(propID, true, true, 8) + ")");
}
}
}
CModel* CAnimationParameters::GetCurrentModel(s32 nodeIndex)
{
if (!mpCharSet) return nullptr;
if (mpCharSet->Type() != eAnimSet) return nullptr;
if (nodeIndex == -1) nodeIndex = mNodeIndex;
CAnimSet *pSet = static_cast<CAnimSet*>(mpCharSet.RawPointer());
if (pSet->getNodeCount() <= (u32) nodeIndex) return nullptr;
return pSet->getNodeModel(nodeIndex);
}
// ************ GETTERS ************
EGame CAnimationParameters::Version()
{
return mGame;
}
CResource* CAnimationParameters::Resource()
{
return mpCharSet;
}
u32 CAnimationParameters::CharacterIndex()
{
return mNodeIndex;
}
u32 CAnimationParameters::Unknown(u32 index)
{
switch (index)
{
case 0: return mUnknown1;
case 1: return mUnknown2;
case 2: return mUnknown3;
case 3: return mUnknown4;
default: return 0;
}
}
// ************ SETTERS ************
void CAnimationParameters::SetResource(CResource *pRes)
{
if ((pRes->Type() == eAnimSet) || (pRes->Type() == eCharacter))
{
mpCharSet = pRes;
mNodeIndex = 0;
}
else
Log::Error("Resource with invalid type passed to CAnimationParameters: " + pRes->Source());
}
void CAnimationParameters::SetNodeIndex(u32 index)
{
mNodeIndex = index;
}
void CAnimationParameters::SetUnknown(u32 index, u32 value)
{
switch (index)
{
case 0: mUnknown1 = value;
case 1: mUnknown2 = value;
case 2: mUnknown3 = value;
case 3: mUnknown4 = value;
}
}

View File

@@ -0,0 +1,37 @@
#ifndef CANIMATIONPARAMETERS_H
#define CANIMATIONPARAMETERS_H
#include "CResource.h"
#include "model/CModel.h"
#include <Core/TResPtr.h>
#include "EFormatVersion.h"
class CAnimationParameters
{
EGame mGame;
TResPtr<CResource> mpCharSet;
u32 mNodeIndex;
u32 mUnknown1;
u32 mUnknown2;
u32 mUnknown3;
u32 mUnknown4;
public:
CAnimationParameters();
CAnimationParameters(CInputStream& SCLY, EGame game);
CModel* GetCurrentModel(s32 nodeIndex = -1);
// Getters
EGame Version();
CResource* Resource();
u32 CharacterIndex();
u32 Unknown(u32 index);
// Setters
void SetResource(CResource *pRes);
void SetNodeIndex(u32 index);
void SetUnknown(u32 index, u32 value);
};
#endif // CANIMATIONPARAMETERS_H

View File

@@ -0,0 +1,114 @@
#include "CCollisionMesh.h"
#include <Core/CRenderer.h>
CCollisionMesh::CCollisionMesh()
{
mVBO.SetVertexDesc(ePosition);
mVertexCount = 0;
mLineCount = 0;
mFaceCount = 0;
mBuffered = false;
mIBO.SetPrimitiveType(GL_TRIANGLES);
}
CCollisionMesh::~CCollisionMesh()
{
if (mBuffered)
{
mIBO.Clear();
mVBO.Clear();
mBuffered = false;
}
}
void CCollisionMesh::BufferGL()
{
if (mBuffered)
{
mIBO.Clear();
mVBO.Clear();
mBuffered = false;
}
// Add all the verts to our VBO, first...
mVBO.Reserve(mCollisionVertices.size());
for (u16 v = 0; v < mCollisionVertices.size(); v++)
mVBO.AddVertex(CVertex(mCollisionVertices[v].Pos));
// Then add all the relevant indices to the IBO
mIBO.Reserve(mCollisionFaces.size() * 3);
for (u32 v = 0; v < mCollisionFaces.size(); v++)
{
u16 Verts[3];
CCollisionFace *Face = &mCollisionFaces[v];
CCollisionLine *LineA = GetLine(Face->Lines[0]);
CCollisionLine *LineB = GetLine(Face->Lines[1]);
Verts[0] = LineA->Vertices[0];
Verts[1] = LineA->Vertices[1];
// We have two vertex indices; the last one is one of the ones on line B, but we're not sure which one
if ((LineB->Vertices[0] != Verts[0]) &&
(LineB->Vertices[0] != Verts[1]))
Verts[2] = LineB->Vertices[0];
else
Verts[2] = LineB->Vertices[1];
// Some faces have a property that indicates they need to be inverted
if (!Face->Properties.Invert)
mIBO.AddIndices(&Verts[0], 3);
else {
mIBO.AddIndex(Verts[2]);
mIBO.AddIndex(Verts[1]);
mIBO.AddIndex(Verts[0]);
}
}
// Buffer, and done
mVBO.Buffer();
mIBO.Buffer();
mBuffered = true;
}
void CCollisionMesh::Draw()
{
if (!mBuffered) BufferGL();
mVBO.Bind();
mIBO.Bind();
glDrawElements(GL_TRIANGLES, mIBO.GetSize(), GL_UNSIGNED_SHORT, (void*) 0);
gDrawCount++;
mIBO.Unbind();
mVBO.Unbind();
}
void CCollisionMesh::DrawWireframe()
{
if (!mBuffered) BufferGL();
mVBO.Bind();
mIBO.Bind();
for (u32 f = 0; f < mFaceCount; f++)
{
glDrawElements(GL_LINE_LOOP, 3, GL_UNSIGNED_SHORT, (void*) (f * 6));
gDrawCount++;
}
mIBO.Unbind();
mVBO.Unbind();
}
CCollisionMesh::CCollisionVertex* CCollisionMesh::GetVertex(u16 index)
{
return &mCollisionVertices[index];
}
CCollisionMesh::CCollisionLine* CCollisionMesh::GetLine(u16 index)
{
return &mCollisionLines[index];
}
CCollisionMesh::CCollisionFace* CCollisionMesh::GetFace(u16 index)
{
return &mCollisionFaces[index];
}

View File

@@ -0,0 +1,88 @@
#ifndef CCOLLISIONMESH_H
#define CCOLLISIONMESH_H
#include <Common/CAABox.h>
#include <OpenGL/CVertexBuffer.h>
#include <OpenGL/CIndexBuffer.h>
#include "CResource.h"
class CCollisionMesh
{
friend class CCollisionLoader;
class CCollisionOctree
{
friend class CCollisionLoader;
struct SOctreeNode {};
struct SLeaf : public SOctreeNode
{
CAABox AABox;
std::vector<u16> FaceIndices;
};
struct SBranch : public SOctreeNode
{
u16 Flags;
SOctreeNode *pChildren[8];
};
SOctreeNode* Root;
};
struct SCollisionProperties
{
// todo: figure out what the other properties are
bool Invert;
};
class CCollisionVertex
{
public:
SCollisionProperties Properties;
CVector3f Pos;
};
class CCollisionLine
{
public:
SCollisionProperties Properties;
u16 Vertices[2];
};
class CCollisionFace
{
public:
SCollisionProperties Properties;
u16 Lines[3];
};
CVertexBuffer mVBO;
CIndexBuffer mIBO;
u32 mVertexCount;
u32 mLineCount;
u32 mFaceCount;
bool mBuffered;
CAABox mAABox;
CCollisionOctree *mOctree;
std::vector<u32> mFlags;
std::vector<CCollisionVertex> mCollisionVertices;
std::vector<CCollisionLine> mCollisionLines;
std::vector<CCollisionFace> mCollisionFaces;
bool mOctreeLoaded;
CCollisionVertex *GetVertex(u16 index);
CCollisionLine *GetLine(u16 index);
CCollisionFace *GetFace(u16 index);
public:
CCollisionMesh();
~CCollisionMesh();
void BufferGL();
void Draw();
void DrawWireframe();
};
#endif // CCOLLISIONMESH_H

View File

@@ -0,0 +1,38 @@
#include "CCollisionMeshGroup.h"
CCollisionMeshGroup::CCollisionMeshGroup()
{
}
CCollisionMeshGroup::~CCollisionMeshGroup()
{
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
delete *it;
}
u32 CCollisionMeshGroup::NumMeshes()
{
return mMeshes.size();
}
CCollisionMesh* CCollisionMeshGroup::MeshByIndex(u32 index)
{
return mMeshes[index];
}
void CCollisionMeshGroup::AddMesh(CCollisionMesh *pMesh)
{
mMeshes.push_back(pMesh);
}
void CCollisionMeshGroup::Draw()
{
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
(*it)->Draw();
}
void CCollisionMeshGroup::DrawWireframe()
{
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
(*it)->DrawWireframe();
}

View File

@@ -0,0 +1,25 @@
#ifndef CCOLLISIONMESHGROUP_H
#define CCOLLISIONMESHGROUP_H
#include "CResource.h"
#include "CCollisionMesh.h"
#include <Core/TResPtr.h>
#include <vector>
class CCollisionMeshGroup : public CResource
{
DECLARE_RESOURCE_TYPE(eCollisionMeshGroup)
std::vector<CCollisionMesh*> mMeshes;
public:
CCollisionMeshGroup();
~CCollisionMeshGroup();
u32 NumMeshes();
CCollisionMesh* MeshByIndex(u32 index);
void AddMesh(CCollisionMesh *pMesh);
void Draw();
void DrawWireframe();
};
#endif // CCOLLISIONMESHGROUP_H

175
src/Core/Resource/CFont.cpp Normal file
View File

@@ -0,0 +1,175 @@
#include "CFont.h"
#include <Core/CDrawUtil.h>
#include <Core/CRenderer.h>
#include <Core/CResCache.h>
#include <Common/AnimUtil.h>
CDynamicVertexBuffer CFont::smGlyphVertices;
CIndexBuffer CFont::smGlyphIndices;
bool CFont::smBuffersInitialized = false;
CFont::CFont() : CResource()
{
}
CFont::~CFont()
{
}
inline float PtsToFloat(s32 pt)
{
// This is a bit of an arbitrary number but it works
// 1 / (1280 / 1.333333f / 2)
return 0.00208333f * pt;
}
CVector2f CFont::RenderString(const TString& String, CRenderer* /*pRenderer*/, float /*AspectRatio*/,
CVector2f /*Position*/, CColor FillColor, CColor StrokeColor, u32 FontSize)
{
// WIP
if (!smBuffersInitialized) InitBuffers();
// Shader setup
CShader *pTextShader = CDrawUtil::GetTextShader();
pTextShader->SetCurrent();
GLuint ModelMtxLoc = pTextShader->GetUniformLocation("ModelMtx");
GLuint ColorLoc = pTextShader->GetUniformLocation("FontColor");
GLuint LayerLoc = pTextShader->GetUniformLocation("RGBALayer");
CVector4f FillColor4f = FillColor.ToVector4f();
CVector4f StrokeColor4f = StrokeColor.ToVector4f();
mpFontTexture->Bind(0);
smGlyphVertices.Bind();
glDisable(GL_DEPTH_TEST);
// Initialize some more stuff before we start the character loop
CVector2f PrintHead(-1.f, 1.f);
CTransform4f PtScale = CTransform4f::ScaleMatrix(PtsToFloat(1));
SGlyph *pPrevGlyph = nullptr;
float Scale;
if (FontSize == CFONT_DEFAULT_SIZE) Scale = 1.f;
else Scale = (float) FontSize / (mDefaultSize != 0 ? mDefaultSize : 18);
for (u32 iChar = 0; iChar < String.Length(); iChar++)
{
// Get character, check for newline
char Char = String[iChar];
if (Char == '\n')
{
pPrevGlyph = nullptr;
PrintHead.x = -1;
PrintHead.y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
continue;
}
// Get glyph
auto iGlyph = mGlyphs.find(Char);
if (iGlyph == mGlyphs.end()) continue;
SGlyph *pGlyph = &iGlyph->second;
// Apply left padding and kerning
PrintHead.x += PtsToFloat(pGlyph->LeftPadding) * Scale;
if (pPrevGlyph)
{
if (pPrevGlyph->KerningIndex != -1)
{
for (u32 iKern = pPrevGlyph->KerningIndex; iKern < mKerningTable.size(); iKern++)
{
if (mKerningTable[iKern].CharacterA != pPrevGlyph->Character) break;
if (mKerningTable[iKern].CharacterB == String[iChar])
{
PrintHead.x += PtsToFloat(mKerningTable[iKern].Adjust) * Scale;
break;
}
}
}
}
// Add a newline if this character goes over the right edge of the screen
if (PrintHead.x + ((PtsToFloat(pGlyph->PrintAdvance) + PtsToFloat(pGlyph->RightPadding)) * Scale) > 1)
{
PrintHead.x = -1;
PrintHead.y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
if (Char == ' ') continue;
}
float XTrans = PrintHead.x;
float YTrans = PrintHead.y + ((PtsToFloat(pGlyph->BaseOffset * 2) - PtsToFloat(mVerticalOffset * 2)) * Scale);
CTransform4f GlyphTransform = PtScale;
GlyphTransform.Scale(CVector3f((float) pGlyph->Width / 2, (float) pGlyph->Height, 1.f));
GlyphTransform.Scale(Scale);
GlyphTransform.Translate(CVector3f(XTrans, YTrans, 0.f));
CMatrix4f Glyph4f = GlyphTransform.ToMatrix4f();
// Get glyph layer
u8 GlyphLayer = pGlyph->RGBAChannel;
if (mTextureFormat == 3) GlyphLayer *= 2;
else if (mTextureFormat == 8) GlyphLayer = 3;
// Load shader uniforms, buffer texture
glUniformMatrix4fv(ModelMtxLoc, 1, GL_FALSE, (GLfloat*) &Glyph4f);
smGlyphVertices.BufferAttrib(eTex0, &pGlyph->TexCoords);
// Draw fill
glUniform1i(LayerLoc, GlyphLayer);
glUniform4fv(ColorLoc, 1, &FillColor4f.x);
smGlyphIndices.DrawElements();
// Draw stroke
if ((mTextureFormat == 1) || (mTextureFormat == 3) || (mTextureFormat == 8))
{
u8 StrokeLayer;
if (mTextureFormat == 1) StrokeLayer = 1;
else if (mTextureFormat == 3) StrokeLayer = GlyphLayer + 1;
else if (mTextureFormat == 8) StrokeLayer = GlyphLayer - 2;
glUniform1i(LayerLoc, StrokeLayer);
glUniform4fv(ColorLoc, 1, &StrokeColor4f.x);
smGlyphIndices.DrawElements();
}
// Update print head
PrintHead.x += PtsToFloat(pGlyph->PrintAdvance) * Scale;
PrintHead.x += PtsToFloat(pGlyph->RightPadding) * Scale;
pPrevGlyph = pGlyph;
}
glEnable(GL_DEPTH_TEST);
return PrintHead;
}
void CFont::InitBuffers()
{
smGlyphVertices.SetActiveAttribs(ePosition | eTex0);
smGlyphVertices.SetVertexCount(4);
CVector3f Vertices[4] = {
CVector3f( 0.f, 0.f, 0.f),
CVector3f( 2.f, 0.f, 0.f),
CVector3f( 0.f, -2.f, 0.f),
CVector3f( 2.f, -2.f, 0.f)
};
smGlyphVertices.BufferAttrib(ePosition, Vertices);
CVector2f TexCoords[4] = {
CVector2f(0.f, 0.f),
CVector2f(1.f, 0.f),
CVector2f(0.f, 1.f),
CVector2f(1.f, 1.f)
};
smGlyphVertices.BufferAttrib(eTex0, TexCoords);
smGlyphIndices.Reserve(4);
smGlyphIndices.AddIndex(0);
smGlyphIndices.AddIndex(2);
smGlyphIndices.AddIndex(1);
smGlyphIndices.AddIndex(3);
smGlyphIndices.SetPrimitiveType(GL_TRIANGLE_STRIP);
smBuffersInitialized = true;
}

72
src/Core/Resource/CFont.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef CFONT_H
#define CFONT_H
#include "CResource.h"
#include "CTexture.h"
#include "model/CVertex.h"
#include <Common/types.h>
#include <Core/TResPtr.h>
#include <OpenGL/CDynamicVertexBuffer.h>
#include <OpenGL/CIndexBuffer.h>
#include <string>
#include <unordered_map>
#define CFONT_DEFAULT_SIZE -1
class CRenderer;
class CFont : public CResource
{
DECLARE_RESOURCE_TYPE(eFont)
friend class CFontLoader;
static CDynamicVertexBuffer smGlyphVertices; // This is the vertex buffer used to draw glyphs. It has two attributes - Pos and Tex0. Tex0 should be updated for each glyph.
static CIndexBuffer smGlyphIndices; // This is the index buffer used to draw glyphs. It uses a triangle strip.
static bool smBuffersInitialized; // This bool indicates whether the vertex/index buffer have been initialized. Checked at the start of RenderString().
u32 mUnknown; // Value at offset 0x8. Not sure what this is. Including for experimentation purposes.
u32 mLineHeight; // Height of each line, in points
u32 mLineMargin; // Gap between lines, in points - this is added to the line height
u32 mVerticalOffset; // In points. This is used to reposition glyphs after the per-glyph vertical offset is applied
u32 mDefaultSize; // In points.
TString mFontName; // Self-explanatory
TResPtr<CTexture> mpFontTexture; // The texture used by this font
u32 mTextureFormat; // Indicates which layers on the texture are for what - multiple glyph layers or fill/stroke
struct SGlyph
{
u16 Character; // The UTF-16 character that this glyph corresponds to
CVector2f TexCoords[4]; // The format only lists the min/max X/Y values; tracking absolute coordinates in memory is faster
s32 LeftPadding; // The amount of padding applied left of this glyph, in points
s32 RightPadding; // The amount of padding applied right of this glyph, in points
u32 Width; // The width of the glyph, in points
u32 Height; // The height of the glyph, in points
u32 PrintAdvance; // How far the print head advances horizontally after printing this glyph, in points
u32 BaseOffset; // Vertical offset for this glyph, in points; the font-wide offset is added to this
u32 KerningIndex; // Index into the kerning table of the first kerning pair for this glyph. -1 if no pairs.
u8 RGBAChannel; // Fonts can store multiple glyphs in the same space on different RGBA channels. This value corresponds to R, G, B, or A.
};
std::unordered_map<u16, SGlyph> mGlyphs;
struct SKerningPair
{
u16 CharacterA; // Left character
u16 CharacterB; // Right character
s32 Adjust; // The horizontal offset to apply to CharacterB if this pair is encountered, in points
};
std::vector<SKerningPair> mKerningTable; // The kerning table should be laid out in alphabetical order for the indices to work properly
public:
CFont();
~CFont();
CResource* MakeCopy(CResCache *pCopyCache);
CVector2f RenderString(const TString& String, CRenderer *pRenderer, float AspectRatio,
CVector2f Position = CVector2f(0,0),
CColor FillColor = CColor::skWhite, CColor StrokeColor = CColor::skBlack,
u32 FontSize = CFONT_DEFAULT_SIZE);
private:
void InitBuffers();
};
#endif // CFONT_H

View File

@@ -0,0 +1,180 @@
#include "CGameArea.h"
#include "script/CScriptLayer.h"
#include <Core/CRenderer.h>
CGameArea::CGameArea() : CResource()
{
mVertexCount = 0;
mTriangleCount = 0;
mTerrainMerged = false;
mMaterialSet = nullptr;
mpGeneratorLayer = nullptr;
mCollision = nullptr;
}
CGameArea::~CGameArea()
{
ClearTerrain();
delete mCollision;
delete mpGeneratorLayer;
for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++)
delete mScriptLayers[iSCLY];
for (u32 lyr = 0; lyr < mLightLayers.size(); lyr++)
for (u32 lit = 0; lit < mLightLayers[lyr].size(); lit++)
delete mLightLayers[lyr][lit];
}
void CGameArea::AddWorldModel(CModel *mdl)
{
mTerrainModels.push_back(mdl);
mVertexCount += mdl->GetVertexCount();
mTriangleCount += mdl->GetTriangleCount();
mAABox.ExpandBounds(mdl->AABox());
}
void CGameArea::MergeTerrain()
{
if (mTerrainMerged) return;
// Nothing really complicated here - iterate through every terrain submesh, add each to a static model
for (u32 iMdl = 0; iMdl < mTerrainModels.size(); iMdl++)
{
CModel *pMdl = mTerrainModels[iMdl];
u32 SubmeshCount = pMdl->GetSurfaceCount();
for (u32 iSurf = 0; iSurf < SubmeshCount; iSurf++)
{
SSurface *pSurf = pMdl->GetSurface(iSurf);
CMaterial *pMat = mMaterialSet->MaterialByIndex(pSurf->MaterialID);
bool newMat = true;
for (std::vector<CStaticModel*>::iterator it = mStaticTerrainModels.begin(); it != mStaticTerrainModels.end(); it++)
{
if ((*it)->GetMaterial() == pMat)
{
// When we append a new submesh to an existing static model, we bump it to the back of the vector.
// This is because mesh ordering actually matters sometimes
// (particularly with multi-layered transparent meshes)
// so we need to at least try to maintain it.
// This is maybe not the most efficient way to do this, but it works.
CStaticModel *pStatic = *it;
pStatic->AddSurface(pSurf);
mStaticTerrainModels.erase(it);
mStaticTerrainModels.push_back(pStatic);
newMat = false;
break;
}
}
if (newMat)
{
CStaticModel *pStatic = new CStaticModel(pMat);
pStatic->AddSurface(pSurf);
mStaticTerrainModels.push_back(pStatic);
}
}
}
}
void CGameArea::ClearTerrain()
{
for (u32 t = 0; t < mTerrainModels.size(); t++)
delete mTerrainModels[t];
mTerrainModels.clear();
for (u32 s = 0; s < mStaticTerrainModels.size(); s++)
delete mStaticTerrainModels[s];
mStaticTerrainModels.clear();
if (mMaterialSet) delete mMaterialSet;
mVertexCount = 0;
mTriangleCount = 0;
mTerrainMerged = false;
mAABox = CAABox::skInfinite;
}
void CGameArea::ClearScriptLayers()
{
for (auto it = mScriptLayers.begin(); it != mScriptLayers.end(); it++)
delete *it;
mScriptLayers.clear();
delete mpGeneratorLayer;
mpGeneratorLayer = nullptr;
}
CTransform4f CGameArea::GetTransform()
{
return mTransform;
}
u32 CGameArea::GetTerrainModelCount()
{
return mTerrainModels.size();
}
u32 CGameArea::GetStaticModelCount()
{
return mStaticTerrainModels.size();
}
CModel* CGameArea::GetTerrainModel(u32 mdl)
{
return mTerrainModels[mdl];
}
CStaticModel* CGameArea::GetStaticModel(u32 mdl)
{
return mStaticTerrainModels[mdl];
}
CCollisionMeshGroup* CGameArea::GetCollision()
{
return mCollision;
}
u32 CGameArea::GetScriptLayerCount()
{
return mScriptLayers.size();
}
CScriptLayer* CGameArea::GetScriptLayer(u32 index)
{
return mScriptLayers[index];
}
CScriptLayer* CGameArea::GetGeneratorLayer()
{
return mpGeneratorLayer;
}
CScriptObject* CGameArea::GetInstanceByID(u32 InstanceID)
{
auto it = mObjectMap.find(InstanceID);
if (it != mObjectMap.end()) return it->second;
else return nullptr;
}
u32 CGameArea::GetLightLayerCount()
{
return mLightLayers.size();
}
u32 CGameArea::GetLightCount(u32 layer)
{
if (mLightLayers.empty()) return 0;
return mLightLayers[layer].size();
}
CLight* CGameArea::GetLight(u32 layer, u32 light)
{
return mLightLayers[layer][light];
}
CAABox CGameArea::AABox()
{
return mAABox;
}

View File

@@ -0,0 +1,68 @@
#ifndef CGAMEAREA_H
#define CGAMEAREA_H
#include "CCollisionMeshGroup.h"
#include "CLight.h"
#include "CMaterialSet.h"
#include "model/CModel.h"
#include "model/CStaticModel.h"
#include "CResource.h"
#include <Common/types.h>
#include <Common/CTransform4f.h>
#include <unordered_map>
class CScriptLayer;
class CScriptObject;
class CGameArea : public CResource
{
DECLARE_RESOURCE_TYPE(eArea)
friend class CAreaLoader;
u32 mVertexCount;
u32 mTriangleCount;
bool mTerrainMerged;
CTransform4f mTransform;
CAABox mAABox;
// Geometry
CMaterialSet *mMaterialSet;
std::vector<CModel*> mTerrainModels; // TerrainModels is the original version of each model; this is used by the editor (bounding box checks, material editing, etc)
std::vector<CStaticModel*> mStaticTerrainModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
// Script
std::vector<CScriptLayer*> mScriptLayers;
CScriptLayer *mpGeneratorLayer;
std::unordered_map<u32, CScriptObject*> mObjectMap;
// Collision
CCollisionMeshGroup *mCollision;
// Lights
std::vector<std::vector<CLight*>> mLightLayers;
public:
CGameArea();
~CGameArea();
void AddWorldModel(CModel *mdl);
void MergeTerrain();
void ClearTerrain();
void ClearScriptLayers();
// Getters
CTransform4f GetTransform();
u32 GetTerrainModelCount();
u32 GetStaticModelCount();
CModel* GetTerrainModel(u32 mdl);
CStaticModel* GetStaticModel(u32 mdl);
CCollisionMeshGroup* GetCollision();
u32 GetScriptLayerCount();
CScriptLayer* GetScriptLayer(u32 index);
CScriptLayer* GetGeneratorLayer();
CScriptObject* GetInstanceByID(u32 InstanceID);
u32 GetLightLayerCount();
u32 GetLightCount(u32 layer);
CLight* GetLight(u32 layer, u32 light);
CAABox AABox();
};
#endif // CGAMEAREA_H

View File

@@ -0,0 +1,283 @@
#include "CLight.h"
#include <cmath>
#include <float.h>
#include <Core/CGraphics.h>
#define CLIGHT_NO_RADIUS 0x40
#define CLIGHT_NO_INTENSITY 0x80
CLight::CLight()
{
mPosition = skDefaultLightPos;
mDirection = skDefaultLightDir;
mDistAttenCoefficients = CVector3f(0.f, 1.f, 0.f);
mAngleAttenCoefficients = CVector3f(0.f, 1.f, 0.f);
mCachedRadius = 0.f;
mCachedIntensity = 0.f;
mDirtyFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
}
// ************ DATA MANIPULATION ************
// This function is reverse engineered from the kiosk demo's code
float CLight::CalculateRadius() const
{
if ((mDistAttenCoefficients.y >= FLT_EPSILON) ||
(mDistAttenCoefficients.z >= FLT_EPSILON))
{
float Intensity = GetIntensity();
if (mDistAttenCoefficients.z > FLT_EPSILON)
{
if (Intensity <= FLT_EPSILON)
return 0.f;
float IntensityMod = (Intensity * 5.f / 255.f * mDistAttenCoefficients.z);
return sqrt(Intensity / IntensityMod);
}
else
{
if (mDistAttenCoefficients.y <= FLT_EPSILON)
return 0.f;
float IntensityMod = (Intensity * 5.f) / 255.f;
if (IntensityMod < 0.2f)
IntensityMod = 0.2f;
return Intensity / (IntensityMod * mDistAttenCoefficients.y);
}
}
else return 3000000000000000000000000000000000000.f;
}
// This function is also reverse engineered from the kiosk demo's code
float CLight::CalculateIntensity() const
{
float Multiplier = (mType == eCustom) ? mAngleAttenCoefficients.x : 1.0f;
float ColorR = float(mColor.r) / 255.f;
float ColorG = float(mColor.g) / 255.f;
float ColorB = float(mColor.b) / 255.f;
// Get the color component with the greatest numeric value
float Greatest = (ColorG >= ColorB) ? ColorG : ColorB;
Greatest = (ColorR >= Greatest) ? ColorR : Greatest;
return Greatest * Multiplier;
}
// As is this one... partly
CVector3f CLight::CalculateSpotAngleAtten()
{
if (mType != eSpot) return CVector3f(1.f, 0.f, 0.f);
if ((mSpotCutoff < 0.f) || (mSpotCutoff > 90.f))
return CVector3f(1.f, 0.f, 0.f);
float RadianCutoff = (mSpotCutoff * 3.1415927f) / 180.f;
float RadianCosine = cosf(RadianCutoff);
float InvCosine = 1.f - RadianCosine;
return CVector3f(0.f, -RadianCosine / InvCosine, 1.f / InvCosine);
}
// ************ GETTERS ************
ELightType CLight::GetType() const
{
return mType;
}
u32 CLight::GetLayerIndex() const
{
return mLayerIndex;
}
CVector3f CLight::GetPosition() const
{
return mPosition;
}
CVector3f CLight::GetDirection() const
{
return mDirection;
}
CColor CLight::GetColor() const
{
return mColor;
}
CVector3f CLight::GetDistAttenuation() const
{
return mDistAttenCoefficients;
}
CVector3f CLight::GetAngleAttenuation() const
{
return mAngleAttenCoefficients;
}
float CLight::GetRadius() const
{
if (mDirtyFlags & CLIGHT_NO_RADIUS)
{
mCachedRadius = CalculateRadius();
mDirtyFlags &= ~CLIGHT_NO_RADIUS;
}
return mCachedRadius * 2;
}
float CLight::GetIntensity() const
{
if (mDirtyFlags & CLIGHT_NO_INTENSITY)
{
mCachedIntensity = CalculateIntensity();
mDirtyFlags &= ~CLIGHT_NO_INTENSITY;
}
return mCachedIntensity;
}
// ************ SETTERS ************
void CLight::SetLayer(u32 index)
{
mLayerIndex = index;
}
void CLight::SetPosition(const CVector3f& Position)
{
mPosition = Position;
}
void CLight::SetDirection(const CVector3f& Direction)
{
mDirection = Direction;
}
void CLight::SetColor(const CColor& Color)
{
mColor = Color;
mDirtyFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
}
void CLight::SetSpotCutoff(float Cutoff)
{
mSpotCutoff = Cutoff * 0.5f;
CalculateSpotAngleAtten();
}
void CLight::SetDistAtten(float DistCoefA, float DistCoefB, float DistCoefC)
{
mDistAttenCoefficients.x = DistCoefA;
mDistAttenCoefficients.y = DistCoefB;
mDistAttenCoefficients.z = DistCoefC;
}
void CLight::SetAngleAtten(float AngleCoefA, float AngleCoefB, float AngleCoefC)
{
mAngleAttenCoefficients.x = AngleCoefA;
mAngleAttenCoefficients.y = AngleCoefB;
mAngleAttenCoefficients.z = AngleCoefC;
}
// ************ OTHER ************
void CLight::Load() const
{
u8 Index = (u8) CGraphics::sNumLights;
if (Index >= 8) return;
CGraphics::SLightBlock::SGXLight *Light = &CGraphics::sLightBlock.Lights[Index];
CVector3f PosView = CGraphics::sMVPBlock.ViewMatrix * mPosition;
CVector3f DirView = CGraphics::sMVPBlock.ViewMatrix * mDirection;
switch (mType)
{
case eLocalAmbient:
// LocalAmbient is already accounted for in CGraphics::sAreaAmbientColor
return;
case eDirectional:
Light->Position = CVector4f(-mDirection * 1048576.f, 1.f);
Light->Direction = CVector4f(mDirection, 0.f);
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
Light->DistAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
Light->AngleAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
break;
case eSpot:
Light->Position = CVector4f(mPosition, 1.f);
Light->Direction = CVector4f(mDirection, 0.f);
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
Light->DistAtten = mDistAttenCoefficients;
Light->AngleAtten = mAngleAttenCoefficients;
break;
case eCustom:
Light->Position = CVector4f(mPosition, 1.f);
Light->Direction = CVector4f(mDirection, 0.f);
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
Light->DistAtten = mDistAttenCoefficients;
Light->AngleAtten = mAngleAttenCoefficients;
break;
default:
return;
}
CGraphics::sNumLights++;
}
// ************ STATIC ************
CLight* CLight::BuildLocalAmbient(const CVector3f& Position, const CColor& Color)
{
CLight *Light = new CLight;
Light->mType = eLocalAmbient;
Light->mPosition = Position;
Light->mDirection = skDefaultLightDir;
Light->mColor = Color;
Light->mSpotCutoff = 0.f;
return Light;
}
CLight* CLight::BuildDirectional(const CVector3f& Position, const CVector3f& Direction, const CColor& Color)
{
CLight *Light = new CLight;
Light->mType = eDirectional;
Light->mPosition = Position;
Light->mDirection = Direction;
Light->mColor = Color;
Light->mSpotCutoff = 0.f;
return Light;
}
CLight* CLight::BuildSpot(const CVector3f& Position, const CVector3f& Direction, const CColor& Color, float Cutoff)
{
CLight *Light = new CLight;
Light->mType = eSpot;
Light->mPosition = Position;
Light->mDirection = -Direction.Normalized();
Light->mColor = Color;
Light->mSpotCutoff = Cutoff * 0.5f;
Light->mAngleAttenCoefficients = Light->CalculateSpotAngleAtten();
return Light;
}
CLight* CLight::BuildCustom(const CVector3f& Position, const CVector3f& Direction, const CColor& Color,
float DistAttenA, float DistAttenB, float DistAttenC,
float AngleAttenA, float AngleAttenB, float AngleAttenC)
{
CLight *Light = new CLight;
Light->mType = eCustom;
Light->mPosition = Position;
Light->mDirection = Direction;
Light->mColor = Color;
Light->mSpotCutoff = 0.f;
Light->mDistAttenCoefficients.x = DistAttenA;
Light->mDistAttenCoefficients.y = DistAttenB;
Light->mDistAttenCoefficients.z = DistAttenC;
Light->mAngleAttenCoefficients.x = AngleAttenA;
Light->mAngleAttenCoefficients.y = AngleAttenB;
Light->mAngleAttenCoefficients.z = AngleAttenC * AngleAttenC;
return Light;
}
// ************ CONSTANTS ************
const CVector3f CLight::skDefaultLightPos(0.f, 0.f, 0.f);
const CVector3f CLight::skDefaultLightDir(0.f,-1.f, 0.f);

View File

@@ -0,0 +1,81 @@
#ifndef CLIGHT_H
#define CLIGHT_H
#include <FileIO/CInputStream.h>
#include <Common/CColor.h>
#include <Common/CVector3f.h>
#include <GL/glew.h>
/* CLight is currently heavily based on the lights system from Metroid Prime,
* including code reverse engineered from the game's executable. Not yet sure
* how much needs to be modified to properly support DKCR. */
enum ELightType
{
eLocalAmbient = 0,
eDirectional = 1,
eSpot = 3,
eCustom = 2
};
class CLight
{
ELightType mType;
u32 mLayerIndex;
CVector3f mPosition;
CVector3f mDirection;
CColor mColor;
float mSpotCutoff;
CVector3f mDistAttenCoefficients;
CVector3f mAngleAttenCoefficients;
mutable float mCachedRadius;
mutable float mCachedIntensity;
mutable u8 mDirtyFlags;
public:
CLight();
private:
// Data Manipulation
float CalculateRadius() const;
float CalculateIntensity() const;
CVector3f CalculateSpotAngleAtten();
public:
// Getters
ELightType GetType() const;
u32 GetLayerIndex() const;
CVector3f GetPosition() const;
CVector3f GetDirection() const;
CColor GetColor() const;
CVector3f GetDistAttenuation() const;
CVector3f GetAngleAttenuation() const;
float GetRadius() const;
float GetIntensity() const;
// Setters
void SetLayer(u32 index);
void SetPosition(const CVector3f& Position);
void SetDirection(const CVector3f& Direction);
void SetColor(const CColor& Color);
void SetSpotCutoff(float Cutoff);
void SetDistAtten(float DistCoefA, float DistCoefB, float DistCoefC);
void SetAngleAtten(float AngleCoefA, float AngleCoefB, float AngleCoefC);
// Other
void Load() const;
// Static
static CLight* BuildLocalAmbient(const CVector3f& Position, const CColor& Color);
static CLight* BuildDirectional(const CVector3f& Position, const CVector3f& Direction, const CColor& Color);
static CLight* BuildSpot(const CVector3f& Position, const CVector3f& Direction, const CColor& Color, float Cutoff);
static CLight* BuildCustom(const CVector3f& Position, const CVector3f& Direction, const CColor& Color,
float DistAttenA, float DistAttenB, float DistAttenC,
float AngleAttenA, float AngleAttenB, float AngleAttenC);
// Constants
static const CVector3f skDefaultLightPos;
static const CVector3f skDefaultLightDir;
};
#endif // CLIGHT_H

View File

@@ -0,0 +1,350 @@
#include "CMaterial.h"
#include <Common/CHashFNV1A.h>
#include <Core/CDrawUtil.h>
#include <Core/CRenderer.h>
#include <Core/CResCache.h>
#include <OpenGL/GLCommon.h>
#include <OpenGL/CShaderGenerator.h>
#include <iostream>
#include <gl/glew.h>
u64 CMaterial::sCurrentMaterial = 0;
CColor CMaterial::sCurrentTint = CColor::skWhite;
CMaterial::CMaterial()
{
mpShader = nullptr;
mShaderStatus = eNoShader;
mRecalcHash = true;
mEnableBloom = false;
mVersion = eUnknownVersion;
mOptions = eNoSettings;
mVtxDesc = eNoAttributes;
mBlendSrcFac = GL_ONE;
mBlendDstFac = GL_ZERO;
mLightingEnabled = true;
mEchoesUnknownA = 0;
mEchoesUnknownB = 0;
mpIndirectTexture = nullptr;
}
CMaterial::CMaterial(EGame version, EVertexDescription vtxDesc)
{
mpShader = nullptr;
mShaderStatus = eNoShader;
mRecalcHash = true;
mEnableBloom = (version == eCorruption);
mVersion = version;
mOptions = eDepthWrite;
mVtxDesc = vtxDesc;
mBlendSrcFac = GL_ONE;
mBlendDstFac = GL_ZERO;
mLightingEnabled = true;
mEchoesUnknownA = 0;
mEchoesUnknownB = 0;
mpIndirectTexture = nullptr;
}
CMaterial::~CMaterial()
{
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
delete mPasses[iPass];
delete mpShader;
}
CMaterial* CMaterial::Clone()
{
CMaterial *pOut = new CMaterial();
pOut->mName = mName;
pOut->mEnableBloom = mEnableBloom;
pOut->mVersion = mVersion;
pOut->mOptions = mOptions;
pOut->mVtxDesc = mVtxDesc;
for (u32 iKonst = 0; iKonst < 4; iKonst++)
pOut->mKonstColors[iKonst] = mKonstColors[iKonst];
pOut->mBlendSrcFac = mBlendSrcFac;
pOut->mBlendDstFac = mBlendDstFac;
pOut->mLightingEnabled = mLightingEnabled;
pOut->mEchoesUnknownA = mEchoesUnknownA;
pOut->mEchoesUnknownB = mEchoesUnknownB;
pOut->mpIndirectTexture = mpIndirectTexture;
pOut->mPasses.resize(mPasses.size());
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
pOut->mPasses[iPass] = mPasses[iPass]->Clone(pOut);
return pOut;
}
void CMaterial::GenerateShader()
{
delete mpShader;
mpShader = CShaderGenerator::GenerateShader(*this);
if (!mpShader->IsValidProgram()) mShaderStatus = eShaderFailed;
else mShaderStatus = eShaderExists;
}
bool CMaterial::SetCurrent(ERenderOptions Options)
{
// Bind textures
const char *skpSamplers[8] = {
"Texture0", "Texture1", "Texture2", "Texture3",
"Texture4", "Texture5", "Texture6", "Texture7"
};
// Skip material setup if the currently bound material is identical
if (sCurrentMaterial != HashParameters())
{
// Shader setup
if (mShaderStatus == eNoShader) GenerateShader();
mpShader->SetCurrent();
if (mShaderStatus == eShaderFailed)
return false;
// Set RGB blend equation - force to ZERO/ONE if alpha is disabled
GLenum srcRGB, dstRGB, srcAlpha, dstAlpha;
if (Options & eNoAlpha) {
srcRGB = GL_ONE;
dstRGB = GL_ZERO;
} else {
srcRGB = mBlendSrcFac;
dstRGB = mBlendDstFac;
}
// Set alpha blend equation
bool AlphaBlended = ((mBlendSrcFac == GL_SRC_ALPHA) && (mBlendDstFac == GL_ONE_MINUS_SRC_ALPHA));
if ((mEnableBloom) && (Options & eEnableBloom) && (!AlphaBlended)) {
srcAlpha = mBlendSrcFac;
dstAlpha = mBlendDstFac;
} else {
srcAlpha = GL_ZERO;
dstAlpha = GL_ZERO;
}
glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
// Set konst inputs
for (u32 iKonst = 0; iKonst < 4; iKonst++)
CGraphics::sPixelBlock.Konst[iKonst] = mKonstColors[iKonst].ToVector4f();
// Set color channels
// COLOR0_Amb is initialized by the node instead of by the material
CGraphics::sVertexBlock.COLOR0_Mat = CColor::skWhite.ToVector4f();
// Set depth write - force on if alpha is disabled (lots of weird depth issues otherwise)
if ((mOptions & eDepthWrite) || (Options & eNoAlpha)) glDepthMask(GL_TRUE);
else glDepthMask(GL_FALSE);
// Load uniforms
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
mPasses[iPass]->SetAnimCurrent(Options, iPass);
sCurrentMaterial = HashParameters();
}
// If the passes are otherwise the same, update UV anims that use the model matrix
else
{
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
{
EUVAnimMode mode = mPasses[iPass]->AnimMode();
if ((mode == eInverseMV) || (mode == eInverseMVTranslated) ||
(mode == eModelMatrix) || (mode == eSimpleMode))
mPasses[iPass]->SetAnimCurrent(Options, iPass);
}
}
// Bind textures
CShader *pShader = CShader::CurrentShader();
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
{
mPasses[iPass]->LoadTexture(iPass);
GLint sampler = pShader->GetUniformLocation(skpSamplers[iPass]);
glUniform1i(sampler, iPass);
}
// Bind num lights
GLuint NumLightsLoc = pShader->GetUniformLocation("NumLights");
glUniform1i(NumLightsLoc, CGraphics::sNumLights);
// Update shader blocks
CGraphics::UpdateVertexBlock();
CGraphics::UpdatePixelBlock();
return true;
}
u64 CMaterial::HashParameters()
{
if (mRecalcHash)
{
CHashFNV1A Hash;
Hash.Init64();
Hash.HashLong(mVersion);
Hash.HashLong(mOptions);
Hash.HashLong(mVtxDesc);
Hash.HashData(mKonstColors, sizeof(CColor) * 4);
Hash.HashLong(mBlendSrcFac);
Hash.HashLong(mBlendDstFac);
Hash.HashByte(mLightingEnabled);
Hash.HashLong(mEchoesUnknownA);
Hash.HashLong(mEchoesUnknownB);
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
mPasses[iPass]->HashParameters(Hash);
mParametersHash = Hash.GetHash64();
mRecalcHash = false;
}
return mParametersHash;
}
void CMaterial::Update()
{
mRecalcHash = true;
mShaderStatus = eNoShader;
}
// ************ GETTERS ************
TString CMaterial::Name() const
{
return mName;
}
EGame CMaterial::Version() const
{
return mVersion;
}
CMaterial::EMaterialOptions CMaterial::Options() const
{
return mOptions;
}
EVertexDescription CMaterial::VtxDesc() const
{
return mVtxDesc;
}
GLenum CMaterial::BlendSrcFac() const {
return mBlendSrcFac;
}
GLenum CMaterial::BlendDstFac() const {
return mBlendDstFac;
}
CColor CMaterial::Konst(u32 KIndex) const
{
if (KIndex > 3) return CColor::skTransparentBlack;
else return mKonstColors[KIndex];
}
CTexture* CMaterial::IndTexture() const
{
return mpIndirectTexture;
}
bool CMaterial::IsLightingEnabled() const
{
return mLightingEnabled;
}
u32 CMaterial::EchoesUnknownA() const
{
return mEchoesUnknownA;
}
u32 CMaterial::EchoesUnknownB() const
{
return mEchoesUnknownB;
}
u32 CMaterial::PassCount() const
{
return mPasses.size();
}
CMaterialPass* CMaterial::Pass(u32 PassIndex) const
{
return mPasses[PassIndex];
}
// ************ SETTERS ************
void CMaterial::SetName(const TString& name)
{
mName = name;
}
void CMaterial::SetOptions(EMaterialOptions Options)
{
mOptions = Options;
mRecalcHash = true;
}
void CMaterial::SetVertexDescription(EVertexDescription desc)
{
mVtxDesc = desc;
mRecalcHash = true;
}
void CMaterial::SetBlendMode(GLenum SrcFac, GLenum DstFac)
{
mBlendSrcFac = SrcFac;
mBlendDstFac = DstFac;
mRecalcHash = true;
}
void CMaterial::SetKonst(CColor& Konst, u32 KIndex)
{
mKonstColors[KIndex] = Konst;
mRecalcHash = true;
}
void CMaterial::SetIndTexture(CTexture *pTex)
{
mpIndirectTexture = pTex;
}
void CMaterial::SetLightingEnabled(bool Enabled)
{
mLightingEnabled = Enabled;
mRecalcHash = true;
}
void CMaterial::SetNumPasses(u32 NumPasses)
{
if (NumPasses < mPasses.size())
{
for (u32 iPass = NumPasses; iPass < mPasses.size(); iPass++)
delete mPasses[iPass];
}
u32 OldCount = mPasses.size();
mPasses.resize(NumPasses);
if (NumPasses > OldCount)
{
for (u32 iPass = OldCount; iPass < NumPasses; iPass++)
mPasses[iPass] = new CMaterialPass(this);
}
mRecalcHash = true;
}
// ************ STATIC ************
void CMaterial::KillCachedMaterial()
{
sCurrentMaterial = 0;
}

View File

@@ -0,0 +1,113 @@
#ifndef MATERIAL_H
#define MATERIAL_H
#include "CMaterialPass.h"
#include "CTexture.h"
#include "EFormatVersion.h"
#include "model/EVertexDescription.h"
#include <Common/CColor.h>
#include <Common/EnumUtil.h>
#include <Common/types.h>
#include <Core/TResPtr.h>
#include <Core/ERenderOptions.h>
#include <FileIO/CInputStream.h>
#include <OpenGL/CShader.h>
#include <Resource/CTexture.h>
class CMaterialSet;
class CMaterial
{
public:
friend class CMaterialLoader;
friend class CMaterialCooker;
// Enums
enum EMaterialOptions
{
eNoSettings = 0,
eKonst = 0x8,
eTransparent = 0x10,
ePunchthrough = 0x20,
eReflection = 0x40,
eDepthWrite = 0x80,
eSurfaceReflection = 0x100,
eOccluder = 0x200,
eIndStage = 0x400,
eLightmap = 0x800,
eShortTexCoord = 0x2000,
eAllSettings = 0x2FF8
};
private:
enum EShaderStatus
{
eNoShader, eShaderExists, eShaderFailed
};
// Statics
static u64 sCurrentMaterial; // The hash for the currently bound material
static CColor sCurrentTint; // The tint for the currently bound material
// Members
TString mName; // Name of the material
CShader *mpShader; // This material's generated shader. Created with GenerateShader().
EShaderStatus mShaderStatus; // A status variable so that PWE won't crash if a shader fails to compile.
u64 mParametersHash; // A hash of all the parameters that can identify this TEV setup.
bool mRecalcHash; // Indicates the hash needs to be recalculated. Set true when parameters are changed.
bool mEnableBloom; // Bool that toggles bloom on or off. On by default on MP3 materials, off by default on MP1 materials.
EGame mVersion;
EMaterialOptions mOptions; // See the EMaterialOptions enum above
EVertexDescription mVtxDesc; // Descriptor of vertex attributes used by this material
CColor mKonstColors[4]; // Konst color values for TEV
GLenum mBlendSrcFac; // Source blend factor
GLenum mBlendDstFac; // Dest blend factor
bool mLightingEnabled; // Color channel control flags; indicate whether lighting is enabled
u32 mEchoesUnknownA; // First unknown value introduced in Echoes. Included for cooking.
u32 mEchoesUnknownB; // Second unknown value introduced in Echoes. Included for cooking.
TResPtr<CTexture> mpIndirectTexture; // Optional texture used for the indirect stage for reflections
std::vector<CMaterialPass*> mPasses;
public:
CMaterial();
CMaterial(EGame version, EVertexDescription vtxDesc);
~CMaterial();
CMaterial* Clone();
void GenerateShader();
bool SetCurrent(ERenderOptions Options);
u64 HashParameters();
void Update();
// Getters
TString Name() const;
EGame Version() const;
EMaterialOptions Options() const;
EVertexDescription VtxDesc() const;
GLenum BlendSrcFac() const;
GLenum BlendDstFac() const;
CColor Konst(u32 KIndex) const;
CTexture* IndTexture() const;
bool IsLightingEnabled() const;
u32 EchoesUnknownA() const;
u32 EchoesUnknownB() const;
u32 PassCount() const;
CMaterialPass* Pass(u32 PassIndex) const;
// Setters
void SetName(const TString& name);
void SetOptions(EMaterialOptions Options);
void SetVertexDescription(EVertexDescription desc);
void SetBlendMode(GLenum SrcFac, GLenum DstFac);
void SetKonst(CColor& Konst, u32 KIndex);
void SetIndTexture(CTexture *pTex);
void SetLightingEnabled(bool Enabled);
void SetNumPasses(u32 NumPasses);
// Static
static void KillCachedMaterial();
};
DEFINE_ENUM_FLAGS(CMaterial::EMaterialOptions)
#endif // MATERIAL_H

View File

@@ -0,0 +1,320 @@
#include "CMaterialPass.h"
#include "CMaterial.h"
#include <Common/AnimUtil.h>
#include <Core/CGraphics.h>
CMaterialPass::CMaterialPass(CMaterial *pParent)
{
mPassType = "CUST";
mSettings = eNoPassSettings;
mpTexture = nullptr;
mEnabled = true;
mpParentMat = pParent;
mColorOutput = ePrevReg;
mAlphaOutput = ePrevReg;
mKColorSel = eKonstOne;
mKAlphaSel = eKonstOne;
mRasSel = eRasColorNull;
mTexCoordSource = 0xFF;
mAnimMode = eNoUVAnim;
for (u32 iParam = 0; iParam < 4; iParam++)
{
mColorInputs[iParam] = eZeroRGB;
mAlphaInputs[iParam] = eZeroAlpha;
mAnimParams[iParam] = 0.f;
}
}
CMaterialPass::~CMaterialPass()
{
}
CMaterialPass* CMaterialPass::Clone(CMaterial *pParent)
{
CMaterialPass *pOut = new CMaterialPass(pParent);
pOut->mPassType = mPassType;
pOut->mSettings = mSettings;
for (u32 iIn = 0; iIn < 4; iIn++) {
pOut->mColorInputs[iIn] = mColorInputs[iIn];
pOut->mAlphaInputs[iIn] = mAlphaInputs[iIn];
}
pOut->mColorOutput = mColorOutput;
pOut->mAlphaOutput = mAlphaOutput;
pOut->mKColorSel = mKColorSel;
pOut->mKAlphaSel = mKAlphaSel;
pOut->mRasSel = mRasSel;
pOut->mTexCoordSource = mTexCoordSource;
pOut->mpTexture = mpTexture;
pOut->mAnimMode = mAnimMode;
for (u32 iParam = 0; iParam < 4; iParam++)
pOut->mAnimParams[iParam] = mAnimParams[iParam];
pOut->mEnabled = mEnabled;
return pOut;
}
void CMaterialPass::HashParameters(CHashFNV1A &Hash)
{
if (mEnabled)
{
Hash.HashLong(mPassType.ToLong());
Hash.HashLong(mSettings);
Hash.HashData(&mColorInputs[0], sizeof(ETevColorInput) * 4);
Hash.HashData(&mAlphaInputs[0], sizeof(ETevAlphaInput) * 4);
Hash.HashLong(mColorOutput);
Hash.HashLong(mAlphaOutput);
Hash.HashLong(mKColorSel);
Hash.HashLong(mKAlphaSel);
Hash.HashLong(mRasSel);
Hash.HashLong(mTexCoordSource);
Hash.HashLong(mAnimMode);
Hash.HashData(mAnimParams, sizeof(float) * 4);
Hash.HashByte(mEnabled);
}
}
void CMaterialPass::LoadTexture(u32 PassIndex)
{
if (mpTexture)
mpTexture->Bind(PassIndex);
}
void CMaterialPass::SetAnimCurrent(ERenderOptions Options, u32 PassIndex)
{
if (mAnimMode == eNoUVAnim) return;
float s = AnimUtil::SecondsMod900();
const CMatrix4f& ModelMtx = CGraphics::sMVPBlock.ModelMatrix;
const CMatrix4f& ViewMtx = CGraphics::sMVPBlock.ViewMatrix;
CMatrix4f TexMtx = CMatrix4f::skIdentity;
CMatrix4f PostMtx = CMatrix4f::skIdentity;
switch (mAnimMode)
{
case eInverseMV: // Mode 0
case eSimpleMode: // Mode 10 - maybe not correct?
{
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
mtx[0][3] = mtx[1][3] = mtx[2][3] = 0.f;
TexMtx = CMatrix4f::FromGlmMat4(mtx);
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f);
break;
}
case eInverseMVTranslated: // Mode 1
{
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
TexMtx = CMatrix4f::FromGlmMat4(mtx);
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
case eUVScroll: // Mode 2
{
if (Options & eEnableUVScroll)
{
TexMtx[0][3] = (s * mAnimParams[2]) + mAnimParams[0];
TexMtx[1][3] = (s * mAnimParams[3]) + mAnimParams[1];
}
break;
}
case eUVRotation: // Mode 3
{
if (Options & eEnableUVScroll)
{
float Angle = (s * mAnimParams[1]) + mAnimParams[0];
float ACos = cos(Angle);
float ASin = sin(Angle);
float TransX = (1.f - (ACos - ASin)) * 0.5f;
float TransY = (1.f - (ASin + ACos)) * 0.5f;
TexMtx = CMatrix4f(ACos, -ASin, 0.0f, TransX,
ASin, ACos, 0.0f, TransY,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
break;
}
case eHFilmstrip: // Mode 4
case eVFilmstrip: // Mode 5
{
if (Options & eEnableUVScroll)
{
float Offset = mAnimParams[2] * mAnimParams[0] * (mAnimParams[3] + s);
Offset = (float)(short)(float)(mAnimParams[1] * fmod(Offset, 1.0f)) * mAnimParams[2];
if (mAnimMode == eHFilmstrip) TexMtx[0][3] = Offset;
if (mAnimMode == eVFilmstrip) TexMtx[1][3] = Offset;
}
break;
}
case eModelMatrix: // Mode 6
{
// It looks ok, but I can't tell whether it's correct...
TexMtx = CMatrix4f::FromGlmMat4(glm::transpose(ModelMtx.ToGlmMat4()));
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, TexMtx[0][3] * 0.50000001f,
0.0f, 0.5f, 0.0f, TexMtx[1][3] * 0.50000001f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f);
TexMtx[0][3] = 0.f;
TexMtx[1][3] = 0.f;
TexMtx[2][3] = 0.f;
}
case eConvolutedModeA: // Mode 7
{
CMatrix4f view = CGraphics::sMVPBlock.ViewMatrix;
// Oh god I seriously need a CMatrix4f inverse function.
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
mtx[0][3] = mtx[1][3] = mtx[2][3] = 0.f;
TexMtx = CMatrix4f::FromGlmMat4(mtx);
float xy = (view[3][0] + view[3][1]) * 0.025f * mAnimParams[1];
xy = (xy - (int) xy);
float z = view[3][2] * 0.05f * mAnimParams[1];
z = (z - (int) z);
float halfA = mAnimParams[0] * 0.5f;
PostMtx = CMatrix4f(halfA, 0.0f, 0.0f, xy,
0.0f, 0.0f, halfA, z,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f);
break;
}
case eConvolutedModeB: // Mode 8 (MP3/DKCR only)
{
// todo
break;
}
default:
break;
}
CGraphics::sVertexBlock.TexMatrices[PassIndex] = TexMtx;
CGraphics::sVertexBlock.PostMatrices[PassIndex] = PostMtx;
}
// ************ SETTERS ************
void CMaterialPass::SetType(CFourCC Type)
{
mPassType = Type;
mpParentMat->Update();
}
void CMaterialPass::SetColorInputs(ETevColorInput InputA, ETevColorInput InputB, ETevColorInput InputC, ETevColorInput InputD)
{
mColorInputs[0] = InputA;
mColorInputs[1] = InputB;
mColorInputs[2] = InputC;
mColorInputs[3] = InputD;
mpParentMat->Update();
}
void CMaterialPass::SetAlphaInputs(ETevAlphaInput InputA, ETevAlphaInput InputB, ETevAlphaInput InputC, ETevAlphaInput InputD)
{
mAlphaInputs[0] = InputA;
mAlphaInputs[1] = InputB;
mAlphaInputs[2] = InputC;
mAlphaInputs[3] = InputD;
mpParentMat->Update();
}
void CMaterialPass::SetColorOutput(ETevOutput OutputReg)
{
mColorOutput = OutputReg;
mpParentMat->Update();
}
void CMaterialPass::SetAlphaOutput(ETevOutput OutputReg)
{
mAlphaOutput = OutputReg;
mpParentMat->Update();
}
void CMaterialPass::SetKColorSel(ETevKSel Sel)
{
mKColorSel = Sel;
mpParentMat->Update();
}
void CMaterialPass::SetKAlphaSel(ETevKSel Sel)
{
// Konst RGB is invalid for alpha, so reset to One if that's the selection
if ((Sel >= eKonst0_RGB) && (Sel <= eKonst3_RGB))
Sel = eKonstOne;
mKAlphaSel = Sel;
mpParentMat->Update();
}
void CMaterialPass::SetRasSel(ETevRasSel Sel)
{
mRasSel = Sel;
mpParentMat->Update();
}
void CMaterialPass::SetTexCoordSource(u32 Source)
{
mTexCoordSource = Source;
mpParentMat->Update();
}
void CMaterialPass::SetTexture(CTexture *pTex)
{
mpTexture = pTex;
}
void CMaterialPass::SetAnimMode(EUVAnimMode Mode)
{
mAnimMode = Mode;
mpParentMat->Update();
}
void CMaterialPass::SetAnimParam(u32 ParamIndex, float Value)
{
mAnimParams[ParamIndex] = Value;
}
void CMaterialPass::SetEnabled(bool Enabled)
{
mEnabled = Enabled;
mpParentMat->Update();
}
// ************ STATIC ************
TString CMaterialPass::PassTypeName(CFourCC Type)
{
if (Type == "CUST") return "Custom";
if (Type == "DIFF") return "Light";
if (Type == "RIML") return "Rim Light";
if (Type == "BLOL") return "Bloom Light";
// BLOD
if (Type == "CLR ") return "Diffuse";
if (Type == "TRAN") return "Opacity";
if (Type == "INCA") return "Emissive";
if (Type == "RFLV") return "Specular";
if (Type == "RFLD") return "Reflection";
// LRLD
// LURD
if (Type == "BLOI") return "Bloom Diffuse";
if (Type == "XRAY") return "X-Ray";
// TOON
return Type.ToString();
}

View File

@@ -0,0 +1,128 @@
#ifndef CMATERIALPASS_H
#define CMATERIALPASS_H
#include <Common/CFourCC.h>
#include <Common/CHashFNV1A.h>
#include <Core/TResPtr.h>
#include <Core/ERenderOptions.h>
#include <Resource/CTexture.h>
#include "ETevEnums.h"
class CMaterial;
class CMaterialPass
{
friend class CMaterialLoader;
friend class CMaterialCooker;
public:
enum EPassSettings
{
eNoPassSettings = 0x0,
eEmissiveBloom = 0x4,
eInvertOpacityMap = 0x10
};
private:
CMaterial *mpParentMat;
CFourCC mPassType;
EPassSettings mSettings;
ETevColorInput mColorInputs[4];
ETevAlphaInput mAlphaInputs[4];
ETevOutput mColorOutput;
ETevOutput mAlphaOutput;
ETevKSel mKColorSel;
ETevKSel mKAlphaSel;
ETevRasSel mRasSel;
u32 mTexCoordSource; // Should maybe be an enum but worried about conflicts with EVertexDescriptionn
TResPtr<CTexture> mpTexture;
EUVAnimMode mAnimMode;
float mAnimParams[4];
bool mEnabled;
public:
CMaterialPass(CMaterial *pParent);
~CMaterialPass();
CMaterialPass* Clone(CMaterial *pParent);
void HashParameters(CHashFNV1A& Hash);
void LoadTexture(u32 PassIndex);
void SetAnimCurrent(ERenderOptions Options, u32 PassIndex);
// Setters
void SetType(CFourCC Type);
void SetColorInputs(ETevColorInput InputA, ETevColorInput InputB, ETevColorInput InputC, ETevColorInput InputD);
void SetAlphaInputs(ETevAlphaInput InputA, ETevAlphaInput InputB, ETevAlphaInput InputC, ETevAlphaInput InputD);
void SetColorOutput(ETevOutput OutputReg);
void SetAlphaOutput(ETevOutput OutputReg);
void SetKColorSel(ETevKSel Sel);
void SetKAlphaSel(ETevKSel Sel);
void SetRasSel(ETevRasSel Sel);
void SetTexCoordSource(u32 Source);
void SetTexture(CTexture *pTex);
void SetAnimMode(EUVAnimMode Mode);
void SetAnimParam(u32 ParamIndex, float Value);
void SetEnabled(bool Enabled);
// Getters
inline CFourCC Type() const {
return mPassType;
}
inline TString NamedType() const {
return PassTypeName(mPassType);
}
inline ETevColorInput ColorInput(u32 Input) const {
return mColorInputs[Input];
}
inline ETevAlphaInput AlphaInput(u32 Input) const {
return mAlphaInputs[Input];
}
inline ETevOutput ColorOutput() const {
return mColorOutput;
}
inline ETevOutput AlphaOutput() const {
return mAlphaOutput;
}
inline ETevKSel KColorSel() const {
return mKColorSel;
}
inline ETevKSel KAlphaSel() const {
return mKAlphaSel;
}
inline ETevRasSel RasSel() const {
return mRasSel;
}
inline u32 TexCoordSource() const {
return mTexCoordSource;
}
inline CTexture* Texture() const {
return mpTexture;
}
inline EUVAnimMode AnimMode() const {
return mAnimMode;
}
inline float AnimParam(u32 ParamIndex) const {
return mAnimParams[ParamIndex];
}
inline bool IsEnabled() const {
return mEnabled;
}
// Static
static TString PassTypeName(CFourCC Type);
};
#endif // CMATERIALPASS_H

View File

@@ -0,0 +1,49 @@
#include "CMaterialSet.h"
#include <Core/CResCache.h>
#include <iostream>
CMaterialSet::CMaterialSet()
{
}
CMaterialSet::~CMaterialSet()
{
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
delete mMaterials[iMat];
}
CMaterialSet* CMaterialSet::Clone()
{
CMaterialSet *pOut = new CMaterialSet();
pOut->mMaterials.resize(mMaterials.size());
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
pOut->mMaterials[iMat] = mMaterials[iMat]->Clone();
return pOut;
}
u32 CMaterialSet::NumMaterials()
{
return mMaterials.size();
}
CMaterial* CMaterialSet::MaterialByIndex(u32 index)
{
if (index >= NumMaterials()) return nullptr;
return mMaterials[index];
}
CMaterial* CMaterialSet::MaterialByName(const TString& name)
{
for (auto it = mMaterials.begin(); it != mMaterials.end(); it++)
if ((*it)->Name() == name) return *it;
return nullptr;
}
u32 CMaterialSet::MaterialIndexByName(const TString& name)
{
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
if (mMaterials[iMat]->Name() == name) return iMat;
return -1;
}

View File

@@ -0,0 +1,26 @@
#ifndef CMATERIALSET_H
#define CMATERIALSET_H
#include <FileIO/CInputStream.h>
#include <Resource/CTexture.h>
#include <Resource/CMaterial.h>
#include "EFormatVersion.h"
class CMaterialSet
{
friend class CMaterialLoader;
friend class CMaterialCooker;
std::vector<CMaterial*> mMaterials;
public:
CMaterialSet();
~CMaterialSet();
CMaterialSet* Clone();
u32 NumMaterials();
CMaterial* MaterialByIndex(u32 index);
CMaterial* MaterialByName(const TString& name);
u32 MaterialIndexByName(const TString& name);
};
#endif // CMATERIALSET_H

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

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

View File

@@ -0,0 +1,233 @@
#include "CResCache.h"
#include "Log.h"
#include <Common/TString.h>
#include <FileIO/FileIO.h>
#include <iostream>
#include <string>
// Loaders
#include <Resource/factory/CAreaLoader.h>
#include <Resource/factory/CAnimSetLoader.h>
#include <Resource/factory/CCollisionLoader.h>
#include <Resource/factory/CFontLoader.h>
#include <Resource/factory/CModelLoader.h>
#include <Resource/factory/CScanLoader.h>
#include <Resource/factory/CStringLoader.h>
#include <Resource/factory/CTextureDecoder.h>
#include <Resource/factory/CWorldLoader.h>
CResCache::CResCache()
{
mpPak = nullptr;
}
CResCache::~CResCache()
{
Clean();
}
void CResCache::Clean()
{
if (mResourceCache.empty()) return;
Log::Write("Cleaning unused resources");
// I couldn't get this to work properly using reverse iterators, lol.
// Resources get cached after their dependencies, which is why I go backwards
// while loop is to ensure -all- unused resources are cleaned. Not sure of a better way to do it.
int numResourcesCleaned = 1;
while (numResourcesCleaned)
{
numResourcesCleaned = 0;
for (auto it = mResourceCache.end(); it != mResourceCache.begin();)
{
it--;
if (it->second->mRefCount <= 0)
{
delete it->second;
it = mResourceCache.erase(it);
numResourcesCleaned++;
}
}
}
Log::Write(std::to_string(mResourceCache.size()) + " resources loaded");
}
void CResCache::SetFolder(TString path)
{
path.EnsureEndsWith("/");
mResSource.Path = path;
mResSource.Source = SResSource::Folder;
Log::Write("Set resource folder: " + path);
}
void CResCache::SetPak(const TString& path)
{
CFileInStream *pakfile = new CFileInStream(path.ToStdString(), IOUtil::BigEndian);
if (!pakfile->IsValid())
{
Log::Error("Couldn't load pak file: " + path);
delete pakfile;
return;
}
if (mpPak) delete mpPak;
mpPak = new CPakFile(pakfile);
mResSource.Path = path;
mResSource.Source = SResSource::PakFile;
Log::Write("Loaded pak file: " + path);
}
void CResCache::SetResSource(SResSource& ResSource)
{
mResSource = ResSource;
}
SResSource CResCache::GetResSource()
{
return mResSource;
}
TString CResCache::GetSourcePath()
{
return mResSource.Path;
}
CResource* CResCache::GetResource(CUniqueID ResID, CFourCC type)
{
if (!ResID.IsValid()) return nullptr;
auto got = mResourceCache.find(ResID.ToLongLong());
if (got != mResourceCache.end())
return got->second;
std::vector<u8> *pBuffer = nullptr;
TString Source;
// Load from pak
if (mResSource.Source == SResSource::PakFile)
{
pBuffer = mpPak->getResource(ResID.ToLongLong(), type);
Source = ResID.ToString() + "." + type.ToString();
}
// Load from folder
else
{
Source = mResSource.Path + ResID.ToString() + "." + type.ToString();
CFileInStream file(Source.ToStdString(), IOUtil::BigEndian);
if (!file.IsValid())
{
Log::Error("Couldn't open resource: " + ResID.ToString() + "." + type.ToString());
return nullptr;
}
pBuffer = new std::vector<u8>;
pBuffer->resize(file.Size());
file.ReadBytes(pBuffer->data(), pBuffer->size());
}
if (!pBuffer) return nullptr;
// Load resource
CMemoryInStream mem(pBuffer->data(), pBuffer->size(), IOUtil::BigEndian);
mem.SetSourceString(*Source.GetFileName());
CResource *Res = nullptr;
bool SupportedFormat = true;
if (type == "CMDL") Res = CModelLoader::LoadCMDL(mem);
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(mem);
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(mem);
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(mem);
else if (type == "MREA") Res = CAreaLoader::LoadMREA(mem);
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(mem);
else if (type == "STRG") Res = CStringLoader::LoadSTRG(mem);
else if (type == "FONT") Res = CFontLoader::LoadFONT(mem);
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(mem);
else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(mem);
else SupportedFormat = false;
// Log errors
if (!SupportedFormat)
Log::Write("Unsupported format; unable to load " + type.ToString() + " " + ResID.ToString());
if (!Res) Res = new CResource(); // Default for invalid resource or unsupported format
// Add to cache and cleanup
Res->mID = ResID;
Res->mResSource = Source;
mResourceCache[ResID.ToLongLong()] = Res;
delete pBuffer;
return Res;
}
CResource* CResCache::GetResource(const TString& ResPath)
{
// Since this function takes a string argument it always loads directly from a file - no pak
CUniqueID ResID = ResPath.Hash64();
auto got = mResourceCache.find(ResID.ToLongLong());
if (got != mResourceCache.end())
return got->second;
CFileInStream file(ResPath.ToStdString(), IOUtil::BigEndian);
if (!file.IsValid())
{
Log::Error("Couldn't open resource: " + ResPath);
return nullptr;
}
// Save old ResSource to restore later
const SResSource OldSource = mResSource;
mResSource.Source = SResSource::Folder;
mResSource.Path = ResPath.GetFileDirectory();
// Load resource
CResource *Res = nullptr;
CFourCC type = ResPath.GetFileExtension().ToUpper();
bool SupportedFormat = true;
if (type == "CMDL") Res = CModelLoader::LoadCMDL(file);
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(file);
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(file);
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(file);
else if (type == "MREA") Res = CAreaLoader::LoadMREA(file);
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(file);
else if (type == "FONT") Res = CFontLoader::LoadFONT(file);
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(file);
else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(file);
else SupportedFormat = false;
if (!Res) Res = new CResource(); // Default for unsupported formats
// Add to cache and cleanup
Res->mID = *ResPath;
Res->mResSource = ResPath;
mResourceCache[ResID.ToLongLong()] = Res;
mResSource = OldSource;
return Res;
}
void CResCache::CacheResource(CResource *pRes)
{
u64 ID = pRes->ResID().ToLongLong();
auto got = mResourceCache.find(ID);
if (got != mResourceCache.end())
mResourceCache[ID] = pRes;
}
void CResCache::DeleteResource(CUniqueID ResID)
{
auto got = mResourceCache.find(ResID.ToLongLong());
if (got != mResourceCache.end())
{
delete got->second;
mResourceCache.erase(got, got);
}
}
CResCache gResCache;

View File

@@ -0,0 +1,41 @@
#ifndef CRESCACHE_H
#define CRESCACHE_H
#include <Common/types.h>
#include <Common/TString.h>
#include <Resource/CPakFile.h>
#include <Resource/CResource.h>
#include <unordered_map>
struct SResSource
{
TString Path;
enum {
Folder, PakFile
} Source;
};
class CResCache
{
std::unordered_map<u64, CResource*> mResourceCache;
CPakFile *mpPak;
SResSource mResSource;
public:
CResCache();
~CResCache();
void Clean();
void SetFolder(TString path);
void SetPak(const TString& path);
void SetResSource(SResSource& ResSource);
SResSource GetResSource();
TString GetSourcePath();
CResource* GetResource(CUniqueID ResID, CFourCC type);
CResource* GetResource(const TString& ResPath);
void CacheResource(CResource *pRes);
void DeleteResource(CUniqueID ResID);
};
extern CResCache gResCache;
#endif // CRESCACHE_H

View File

@@ -0,0 +1,110 @@
#include "CResource.h"
#include <Core/CResCache.h>
#include <iostream>
CResource::CResource()
{
mRefCount = 0;
}
CResource::~CResource()
{
}
TString CResource::Source()
{
return mResSource.GetFileName();
}
TString CResource::FullSource()
{
return mResSource;
}
CUniqueID CResource::ResID()
{
return mID;
}
void CResource::Lock()
{
mRefCount++;
}
void CResource::Release()
{
mRefCount--;
}
bool CResource::IsValidResource()
{
return (Type() != eResource);
}
// ************ STATIC ************
EResType CResource::ResTypeForExtension(CFourCC Extension)
{
Extension = Extension.ToUpper();
if (Extension < "FONT")
{
if (Extension < "CSKR")
{
if (Extension == "AFSM") return eStateMachine;
if (Extension == "AGSC") return eAudioGrp;
if (Extension == "ANCS") return eAnimSet;
if (Extension == "ANIM") return eAnimation;
if (Extension == "ATBL") return eAudioTable;
if (Extension == "CAUD") return eAudioData;
if (Extension == "CHAR") return eAnimSet;
if (Extension == "CINF") return eSkeleton;
if (Extension == "CMDL") return eModel;
if (Extension == "CRSC") return eCollisionResponse;
}
else
{
if (Extension == "CSKR") return eSkin;
if (Extension == "CSMP") return eAudioSample;
if (Extension == "CSNG") return eMidi;
if (Extension == "CTWK") return eTweak;
if (Extension == "DCLN") return eCollisionMeshGroup;
if (Extension == "DGRP") return eDependencyGroup;
if (Extension == "DSP ") return eMusicTrack;
if (Extension == "DUMB") return eDataDump;
if (Extension == "ELSC") return eParticleElectric;
if (Extension == "EVNT") return eAnimEventData;
}
}
else
{
if (Extension < "PAK ")
{
if (Extension == "FONT") return eFont;
if (Extension == "FRME") return eGuiFrame;
if (Extension == "FSM2") return eStateMachine;
if (Extension == "HINT") return eHintSystem;
if (Extension == "MAPA") return eMapArea;
if (Extension == "MAPW") return eMapWorld;
if (Extension == "MAPU") return eMapUniverse;
if (Extension == "MLVL") return eWorld;
if (Extension == "MREA") return eArea;
if (Extension == "NTWK") return eTweak;
}
else
{
if (Extension == "PAK ") return ePackFile;
if (Extension == "PART") return eParticle;
if (Extension == "PATH") return eNavMesh;
if (Extension == "SAVW") return eSaveWorld;
if (Extension == "SCAN") return eScan;
if (Extension == "STRG") return eStringTable;
if (Extension == "STRM") return eAudioStream;
if (Extension == "SWHC") return eParticleSwoosh;
if (Extension == "THP ") return eVideo;
if (Extension == "TXTR") return eTexture;
if (Extension == "WPSC") return eProjectile;
}
}
return eInvalidResType;
}

View File

@@ -0,0 +1,49 @@
#ifndef CRESOURCE_H
#define CRESOURCE_H
#include "EResType.h"
#include <Common/CFourCC.h>
#include <Common/CUniqueID.h>
#include <Common/types.h>
#include <Common/TString.h>
class CResCache;
// This macro creates functions that allow us to easily identify this resource type.
// Must be included on every CResource subclass.
#define DECLARE_RESOURCE_TYPE(ResTypeEnum) \
public: \
virtual EResType Type() const \
{ \
return ResTypeEnum; \
} \
\
static EResType StaticType() \
{ \
return ResTypeEnum; \
} \
\
private: \
class CResource
{
DECLARE_RESOURCE_TYPE(eResource)
friend class CResCache;
TString mResSource;
CUniqueID mID;
int mRefCount;
public:
CResource();
virtual ~CResource();
TString Source();
TString FullSource();
CUniqueID ResID();
void Lock();
void Release();
bool IsValidResource();
static EResType ResTypeForExtension(CFourCC Extension);
};
#endif // CRESOURCE_H

View File

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

43
src/Core/Resource/CScan.h Normal file
View File

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

View File

@@ -0,0 +1,52 @@
#include "CStringTable.h"
CStringTable::CStringTable() : CResource()
{
}
CStringTable::~CStringTable()
{
}
CResource* CStringTable::MakeCopy(CResCache*)
{
// Not using parameter 1 (CResCache* - pResCache)
return new CStringTable(*this);
}
// ************ SETTERS ************
u32 CStringTable::GetStringCount()
{
return mNumStrings;
}
u32 CStringTable::GetLangCount()
{
return mLangTables.size();
}
CFourCC CStringTable::GetLangTag(u32 Index)
{
return mLangTables[Index].Language;
}
TWideString CStringTable::GetString(CFourCC Lang, u32 StringIndex)
{
for (u32 iLang = 0; iLang < GetLangCount(); iLang++)
{
if (GetLangTag(iLang) == Lang)
return GetString(iLang, StringIndex);
}
return std::wstring();
}
TWideString CStringTable::GetString(u32 LangIndex, u32 StringIndex)
{
return mLangTables[LangIndex].Strings[StringIndex];
}
TString CStringTable::GetStringName(u32 StringIndex)
{
return mStringNames[StringIndex];
}

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
{
DECLARE_RESOURCE_TYPE(eStringTable)
friend class CStringLoader;
std::vector<TString> mStringNames;
u32 mNumStrings;
struct SLangTable
{
CFourCC Language;
std::vector<TWideString> Strings;
};
std::vector<SLangTable> mLangTables;
public:
CStringTable();
~CStringTable();
CResource* MakeCopy(CResCache *pCopyCache);
// Getters
u32 GetStringCount();
u32 GetLangCount();
CFourCC GetLangTag(u32 Index);
TWideString GetString(CFourCC Lang, u32 StringIndex);
TWideString GetString(u32 LangIndex, u32 StringIndex);
TString GetStringName(u32 StringIndex);
};
#endif // CSTRINGTABLE_H

View File

@@ -0,0 +1,375 @@
#include "CTexture.h"
CTexture::CTexture() : CResource()
{
mTexelFormat = eRGBA8;
mSourceTexelFormat = eRGBA8;
mWidth = 0;
mHeight = 0;
mNumMipMaps = 0;
mLinearSize = 0;
mBufferExists = false;
mImgDataBuffer = nullptr;
mImgDataSize = 0;
mGLBufferExists = false;
}
CTexture::CTexture(const CTexture& Source)
{
mTexelFormat = Source.mTexelFormat;
mSourceTexelFormat = Source.mSourceTexelFormat;
mWidth = Source.mWidth;
mHeight = Source.mHeight;
mLinearSize = Source.mLinearSize;
mBufferExists = Source.mBufferExists;
mImgDataSize = Source.mImgDataSize;
mImgDataBuffer = new u8[mImgDataSize];
memcpy(mImgDataBuffer, Source.mImgDataBuffer, mImgDataSize);
mGLBufferExists = false;
}
CTexture::CTexture(u32 Width, u32 Height)
{
mTexelFormat = eRGBA8;
mSourceTexelFormat = eRGBA8;
mWidth = (u16) Width;
mHeight = (u16) Height;
mNumMipMaps = 1;
mLinearSize = Width * Height * 4;
mBufferExists = false;
mImgDataBuffer = nullptr;
mImgDataSize = 0;
mGLBufferExists = false;
}
CTexture::~CTexture()
{
DeleteBuffers();
}
bool CTexture::BufferGL()
{
glGenTextures(1, &mTextureID);
glBindTexture(GL_TEXTURE_2D, mTextureID);
GLenum GLFormat, GLType;
bool IsCompressed = false;
switch (mTexelFormat) {
case eLuminance:
GLFormat = GL_LUMINANCE;
GLType = GL_UNSIGNED_BYTE;
break;
case eLuminanceAlpha:
GLFormat = GL_LUMINANCE_ALPHA;
GLType = GL_UNSIGNED_BYTE;
break;
case eRGB565:
GLFormat = GL_RGB;
GLType = GL_UNSIGNED_SHORT_5_6_5;
break;
case eRGBA4:
GLFormat = GL_RGBA;
GLType = GL_UNSIGNED_SHORT_4_4_4_4;
break;
case eRGBA8:
GLFormat = GL_RGBA;
GLType = GL_UNSIGNED_BYTE;
break;
case eDXT1:
GLFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
IsCompressed = true;
break;
}
// The smallest mipmaps are probably not being loaded correctly, because mipmaps in GX textures have a minimum size depending on the format, and these don't.
// Not sure specifically what accomodations should be made to fix that though so whatever.
u32 MipSize = mLinearSize;
u32 MipOffset = 0;
u16 MipW = mWidth, MipH = mHeight;
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
{
GLvoid *pData = (mBufferExists) ? (mImgDataBuffer + MipOffset) : NULL;
if (!IsCompressed)
glTexImage2D(GL_TEXTURE_2D, iMip, GLFormat, MipW, MipH, 0, GLFormat, GLType, pData);
else
glCompressedTexImage2D(GL_TEXTURE_2D, iMip, GLFormat, MipW, MipH, 0, MipSize, pData);
MipW /= 2;
MipH /= 2;
MipOffset += MipSize;
MipSize /= 4;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mNumMipMaps - 1);
// Linear filtering on mipmaps:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// Anisotropic filtering:
float MaxAnisotropy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisotropy);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, MaxAnisotropy);
mGLBufferExists = true;
return true;
}
void CTexture::Bind(u32 GLTextureUnit)
{
glActiveTexture(GL_TEXTURE0 + GLTextureUnit);
if (!mGLBufferExists)
BufferGL();
glBindTexture(GL_TEXTURE_2D, mTextureID);
}
void CTexture::Resize(u32 Width, u32 Height)
{
if ((mWidth != Width) || (mHeight != Height))
{
DeleteBuffers();
mWidth = (u16) Width;
mHeight = (u16) Height;
mNumMipMaps = 1;
}
}
float CTexture::ReadTexelAlpha(const CVector2f& TexCoord)
{
// todo: support texel formats other than DXT1
// DXT1 is definitely the most complicated one anyway; try reusing CTextureDecoder functions for other formats
u32 TexelX = (u32) ((mWidth - 1) * TexCoord.x);
u32 TexelY = (u32) ((mHeight - 1) * (1.f - fmodf(TexCoord.y, 1.f)));
if (mTexelFormat == eDXT1 && mBufferExists)
{
CMemoryInStream Buffer(mImgDataBuffer, mImgDataSize, IOUtil::SystemEndianness);
// 8 bytes per 4x4 16-pixel block, left-to-right top-to-bottom
u32 BlockIdxX = TexelX / 4;
u32 BlockIdxY = TexelY / 4;
u32 BlocksPerRow = mWidth / 4;
u32 BufferPos = (8 * BlockIdxX) + (8 * BlockIdxY * BlocksPerRow);
Buffer.Seek(BufferPos, SEEK_SET);
u16 PaletteA = Buffer.ReadShort();
u16 PaletteB = Buffer.ReadShort();
if (PaletteA > PaletteB)
{
// No palette colors have alpha
return 1.f;
}
// We only care about alpha, which is only present on palette index 3.
// We don't need to calculate/decode the actual palette colors.
u32 BlockCol = (TexelX & 0xF) / 4;
u32 BlockRow = (TexelY & 0xF) / 4;
Buffer.Seek(BlockRow, SEEK_CUR);
u8 Row = Buffer.ReadByte();
u8 Shift = (u8) (6 - (BlockCol * 2));
u8 PaletteIndex = (Row >> Shift) & 0x3;
return (PaletteIndex == 3 ? 0.f : 1.f);
}
return 1.f;
}
bool CTexture::WriteDDS(COutputStream& out)
{
if (!out.IsValid()) return false;
CopyGLBuffer();
out.WriteString("DDS ", 4); // "DDS " fourCC
out.WriteLong(0x7C); // dwSize
out.WriteLong(0x21007); // dwFlags
out.WriteLong(mHeight); // dwHeight
out.WriteLong(mWidth); // dwWidth
out.WriteLong(mLinearSize); // dwPitchOrLinearSize
out.WriteLong(0); // dwDepth
out.WriteLong(mNumMipMaps - 1); // dwMipMapCount
for (u32 i = 0; i < 11; i++)
out.WriteLong(0); // dwReserved1[11]
// DDS_PIXELFORMAT
out.WriteLong(32); // DDS_PIXELFORMAT.dwSize
u32 pfFlags = 0, pfBpp = 0, pfRBitMask = 0, pfGBitMask = 0, pfBBitMask = 0, pfABitMask = 0;
switch (mTexelFormat) {
case eLuminance:
pfFlags = 0x20000;
pfBpp = 0x8;
pfRBitMask = 0xFF;
break;
case eLuminanceAlpha:
pfFlags = 0x20001;
pfBpp = 0x10;
pfRBitMask = 0x00FF;
pfABitMask = 0xFF00;
break;
case eRGBA4:
pfFlags = 0x41;
pfBpp = 0x10;
pfRBitMask = 0x0F00;
pfGBitMask = 0x00F0;
pfBBitMask = 0x000F;
pfABitMask = 0xF000;
break;
case eRGB565:
pfFlags = 0x40;
pfBpp = 0x10;
pfRBitMask = 0xF800;
pfGBitMask = 0x7E0;
pfBBitMask = 0x1F;
break;
case eRGBA8:
pfFlags = 0x41;
pfBpp = 0x20;
pfRBitMask = 0x00FF0000;
pfGBitMask = 0x0000FF00;
pfBBitMask = 0x000000FF;
pfABitMask = 0xFF000000;
break;
case eDXT1:
pfFlags = 0x4;
break;
}
out.WriteLong(pfFlags); // DDS_PIXELFORMAT.dwFlags
(mTexelFormat == eDXT1) ? out.WriteString("DXT1", 4) : out.WriteLong(0); // DDS_PIXELFORMAT.dwFourCC
out.WriteLong(pfBpp); // DDS_PIXELFORMAT.dwRGBBitCount
out.WriteLong(pfRBitMask); // DDS_PIXELFORMAT.dwRBitMask
out.WriteLong(pfGBitMask); // DDS_PIXELFORMAT.dwGBitMask
out.WriteLong(pfBBitMask); // DDS_PIXELFORMAT.dwBBitMask
out.WriteLong(pfABitMask); // DDS_PIXELFORMAT.dwABitMask
out.WriteLong(0x401000); // dwCaps
out.WriteLong(0); // dwCaps2
out.WriteLong(0); // dwCaps3
out.WriteLong(0); // dwCaps4
out.WriteLong(0); // dwReserved2
out.WriteBytes(mImgDataBuffer, mImgDataSize); // Image data
return true;
}
// ************ STATIC ************
u32 CTexture::FormatBPP(ETexelFormat Format)
{
switch (Format)
{
case eGX_I4: return 4;
case eGX_I8: return 8;
case eGX_IA4: return 8;
case eGX_IA8: return 16;
case eGX_C4: return 4;
case eGX_C8: return 8;
case eGX_RGB565: return 16;
case eGX_RGB5A3: return 16;
case eGX_RGBA8: return 32;
case eGX_CMPR: return 4;
case eLuminance: return 8;
case eLuminanceAlpha: return 16;
case eRGBA4: return 16;
case eRGB565: return 16;
case eRGBA8: return 32;
case eDXT1: return 4;
default: return 0;
}
}
// ************ PRIVATE ************
void CTexture::CalcLinearSize()
{
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
mLinearSize = (u32) (mWidth * mHeight * BytesPerPixel);
}
u32 CTexture::CalcTotalSize()
{
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
u32 MipW = mWidth, MipH = mHeight;
u32 Size = 0;
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
{
Size += (u32) (MipW * MipH * BytesPerPixel);
MipW /= 2;
MipH /= 2;
}
return Size;
}
void CTexture::CopyGLBuffer()
{
if (!mGLBufferExists) return;
// Clear existing buffer
if (mBufferExists)
{
delete[] mImgDataBuffer;
mBufferExists = false;
mImgDataBuffer = nullptr;
mImgDataSize = 0;
}
// Calculate buffer size
mImgDataSize = CalcTotalSize();
mImgDataBuffer = new u8[mImgDataSize];
mBufferExists = true;
// Get texture
u32 MipW = mWidth, MipH = mHeight, MipOffset = 0;
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
glBindTexture(GL_TEXTURE_2D, mTextureID);
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
{
void *pData = mImgDataBuffer + MipOffset;
glGetTexImage(GL_TEXTURE_2D, iMip, GL_RGBA, GL_UNSIGNED_BYTE, pData);
MipOffset += (u32) (MipW * MipH * BytesPerPixel);
MipW /= 2;
MipH /= 2;
}
mTexelFormat = eRGBA8;
mLinearSize = mWidth * mHeight * 4;
}
void CTexture::DeleteBuffers()
{
if (mBufferExists)
{
delete[] mImgDataBuffer;
mBufferExists = false;
mImgDataBuffer = nullptr;
mImgDataSize = 0;
}
if (mGLBufferExists)
{
glDeleteTextures(1, &mTextureID);
mGLBufferExists = false;
}
}

View File

@@ -0,0 +1,85 @@
#ifndef CTEXTURE_H
#define CTEXTURE_H
#include <Common/types.h>
#include <Common/CVector2f.h>
#include <FileIO/FileIO.h>
#include <gl/glew.h>
#include "CResource.h"
#include "ETexelFormat.h"
class CTexture : public CResource
{
DECLARE_RESOURCE_TYPE(eTexture)
friend class CTextureDecoder;
friend class CTextureEncoder;
ETexelFormat mTexelFormat; // Format of decoded image data
ETexelFormat mSourceTexelFormat; // Format of input TXTR file
u16 mWidth, mHeight; // Image dimensions
u32 mNumMipMaps; // The number of mipmaps this texture has
u32 mLinearSize; // The size of the top level mipmap, in bytes
bool mBufferExists; // Boolean that indicates whether image data buffer has valid data
u8 *mImgDataBuffer; // Pointer to image data buffer
u32 mImgDataSize; // Size of image data buffer
bool mGLBufferExists; // Boolean that indicates whether GL buffer has valid data
GLuint mTextureID; // ID for texture GL buffer
public:
CTexture();
CTexture(const CTexture& Source);
CTexture(u32 Width, u32 Height);
~CTexture();
bool BufferGL();
void Bind(u32 GLTextureUnit);
void Resize(u32 Width, u32 Height);
float ReadTexelAlpha(const CVector2f& TexCoord);
bool WriteDDS(COutputStream& out);
// Getters
ETexelFormat TexelFormat();
ETexelFormat SourceTexelFormat();
u32 Width();
u32 Height();
u32 NumMipMaps();
GLuint TextureID();
// Static
static u32 FormatBPP(ETexelFormat Format);
// Private
private:
void CalcLinearSize();
u32 CalcTotalSize();
void CopyGLBuffer();
void DeleteBuffers();
};
inline ETexelFormat CTexture::TexelFormat() {
return mTexelFormat;
}
inline ETexelFormat CTexture::SourceTexelFormat() {
return mSourceTexelFormat;
}
inline u32 CTexture::Width() {
return (u32) mWidth;
}
inline u32 CTexture::Height() {
return (u32) mHeight;
}
inline u32 CTexture::NumMipMaps() {
return mNumMipMaps;
}
inline GLuint CTexture::TextureID() {
return mTextureID;
}
#endif // CTEXTURE_H

View File

@@ -0,0 +1,97 @@
#include "CWorld.h"
#include "script/CScriptLayer.h"
#include <Core/CResCache.h>
CWorld::CWorld() : CResource()
{
mWorldVersion = eUnknownVersion;
mpWorldName = nullptr;
mpDarkWorldName = nullptr;
mpSaveWorld = nullptr;
mpDefaultSkybox = nullptr;
mpMapWorld = nullptr;
}
CWorld::~CWorld()
{
}
void CWorld::SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex)
{
// The AreaIndex parameter is a placeholder until an improved world loader is implemented.
// For now it's the easiest/fastest way to do this because this function is called from
// the start window and the start window already knows the area index.
SArea& AreaInfo = mAreas[AreaIndex];
for (u32 iLyr = 0; iLyr < pArea->GetScriptLayerCount(); iLyr++)
{
CScriptLayer *pLayer = pArea->GetScriptLayer(iLyr);
SArea::SLayer& LayerInfo = AreaInfo.Layers[iLyr];
pLayer->SetName(LayerInfo.LayerName);
pLayer->SetActive(LayerInfo.EnabledByDefault);
}
}
// ************ GETTERS ************
// World
EGame CWorld::Version()
{
return mWorldVersion;
}
CStringTable* CWorld::GetWorldName()
{
return mpWorldName;
}
CStringTable* CWorld::GetDarkWorldName()
{
return mpDarkWorldName;
}
CResource* CWorld::GetSaveWorld()
{
return mpSaveWorld;
}
CModel* CWorld::GetDefaultSkybox()
{
return mpDefaultSkybox;
}
CResource* CWorld::GetMapWorld()
{
return mpMapWorld;
}
// Area
u32 CWorld::GetNumAreas()
{
return mAreas.size();
}
u64 CWorld::GetAreaResourceID(u32 AreaIndex)
{
return mAreas[AreaIndex].FileID;
}
u32 CWorld::GetAreaAttachedCount(u32 AreaIndex)
{
return mAreas[AreaIndex].AttachedAreaIDs.size();
}
u32 CWorld::GetAreaAttachedID(u32 AreaIndex, u32 AttachedIndex)
{
return (u32) mAreas[AreaIndex].AttachedAreaIDs[AttachedIndex];
}
TString CWorld::GetAreaInternalName(u32 AreaIndex)
{
return mAreas[AreaIndex].InternalName;
}
CStringTable* CWorld::GetAreaName(u32 AreaIndex)
{
return mAreas[AreaIndex].pAreaName;
}

105
src/Core/Resource/CWorld.h Normal file
View File

@@ -0,0 +1,105 @@
#ifndef CWORLD_H
#define CWORLD_H
#include "CResource.h"
#include "CGameArea.h"
#include "CStringTable.h"
#include "SDependency.h"
#include "model/CModel.h"
#include <Common/CTransform4f.h>
class CWorld : public CResource
{
DECLARE_RESOURCE_TYPE(eWorld)
friend class CWorldLoader;
// Instances of CResource pointers are placeholders for unimplemented resource types (eg CMapWorld)
EGame mWorldVersion;
TResPtr<CStringTable> mpWorldName;
TResPtr<CStringTable> mpDarkWorldName;
TResPtr<CResource> mpSaveWorld;
TResPtr<CModel> mpDefaultSkybox;
TResPtr<CResource> mpMapWorld;
u32 mUnknown1;
u32 mUnknownAreas;
u32 mUnknownAGSC;
struct SAudioGrp
{
u32 ResID;
u32 Unknown;
};
std::vector<SAudioGrp> mAudioGrps;
struct SMemoryRelay
{
u32 InstanceID;
u32 TargetID;
u16 Message;
u8 Unknown;
};
std::vector<SMemoryRelay> mMemoryRelays;
struct SArea
{
TString InternalName;
TResPtr<CStringTable> pAreaName;
CTransform4f Transform;
CAABox AetherBox;
u64 FileID; // Loading every single area as a CResource would be a very bad idea
u64 AreaID;
std::vector<u16> AttachedAreaIDs;
std::vector<SDependency> Dependencies;
std::vector<TString> RelFilenames;
std::vector<u32> RelOffsets;
u32 CommonDependenciesStart;
struct SDock
{
struct SConnectingDock
{
u32 AreaIndex;
u32 DockIndex;
};
std::vector<SConnectingDock> ConnectingDocks;
CVector3f DockCoordinates[4];
};
std::vector<SDock> Docks;
struct SLayer
{
TString LayerName;
bool EnabledByDefault;
u8 LayerID[16];
u32 LayerDependenciesStart; // Offset into Dependencies vector
};
std::vector<SLayer> Layers;
};
std::vector<SArea> mAreas;
public:
CWorld();
~CWorld();
void SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex);
// Setters
EGame Version();
CStringTable* GetWorldName();
CStringTable* GetDarkWorldName();
CResource* GetSaveWorld();
CModel* GetDefaultSkybox();
CResource* GetMapWorld();
u32 GetNumAreas();
u64 GetAreaResourceID(u32 AreaIndex);
u32 GetAreaAttachedCount(u32 AreaIndex);
u32 GetAreaAttachedID(u32 AreaIndex, u32 AttachedIndex);
TString GetAreaInternalName(u32 AreaIndex);
CStringTable* GetAreaName(u32 AreaIndex);
};
#endif // CWORLD_H

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->mMaterials.size();
for (u32 iMat = 0; iMat < NumMats; iMat++)
{
CMaterial *pMat = mpSet->mMaterials[iMat];
u32 NumPasses = pMat->PassCount();
for (u32 iPass = 0; iPass < NumPasses; iPass++)
{
CTexture *pTex = pMat->Pass(iPass)->Texture();
if (pTex)
mTextureIDs.push_back(pTex->ResID().ToLong());
}
}
// Sort/remove duplicates
std::sort(mTextureIDs.begin(), mTextureIDs.end());
mTextureIDs.erase(std::unique(mTextureIDs.begin(), mTextureIDs.end()), mTextureIDs.end());
// Write texture IDs
Out.WriteLong(mTextureIDs.size());
for (u32 iTex = 0; iTex < mTextureIDs.size(); iTex++)
Out.WriteLong(mTextureIDs[iTex]);
// Write material offset filler
Out.WriteLong(NumMats);
u32 MatOffsetsStart = Out.Tell();
for (u32 iMat = 0; iMat < NumMats; iMat++)
Out.WriteLong(0);
// Write materials
u32 MatsStart = Out.Tell();
std::vector<u32> MatEndOffsets(NumMats);
for (u32 iMat = 0; iMat < NumMats; iMat++)
{
mpMat = mpSet->mMaterials[iMat];
WriteMaterialPrime(Out);
MatEndOffsets[iMat] = Out.Tell() - MatsStart;
}
// Write material offsets
u32 MatsEnd = Out.Tell();
Out.Seek(MatOffsetsStart, SEEK_SET);
for (u32 iMat = 0; iMat < NumMats; iMat++)
Out.WriteLong(MatEndOffsets[iMat]);
// Done!
Out.Seek(MatsEnd, SEEK_SET);
}
void CMaterialCooker::WriteMatSetCorruption(COutputStream&)
{
// Not using parameter 1 (COutputStream& - Out)
// todo
}
void CMaterialCooker::WriteMaterialPrime(COutputStream& Out)
{
// Gather data from the passes before we start writing
u32 TexFlags = 0;
u32 NumKonst = 0;
std::vector<u32> TexIndices;
for (u32 iPass = 0; iPass < mpMat->mPasses.size(); iPass++)
{
CMaterialPass *pPass = mpMat->Pass(iPass);
if ((pPass->KColorSel() >= 0xC) || (pPass->KAlphaSel() >= 0x10))
{
// Determine the highest Konst index being used
u32 KColorIndex = pPass->KColorSel() % 4;
u32 KAlphaIndex = pPass->KAlphaSel() % 4;
if (KColorIndex >= NumKonst)
NumKonst = KColorIndex + 1;
if (KAlphaIndex >= NumKonst)
NumKonst = KAlphaIndex + 1;
}
CTexture *pPassTex = pPass->Texture();
if (pPassTex != nullptr)
{
TexFlags |= (1 << iPass);
u32 TexID = pPassTex->ResID().ToLong();
for (u32 iTex = 0; iTex < mTextureIDs.size(); iTex++)
{
if (mTextureIDs[iTex] == TexID)
{
TexIndices.push_back(iTex);
break;
}
}
}
}
// Get group index
u32 GroupIndex;
u64 MatHash = mpMat->HashParameters();
bool NewHash = true;
for (u32 iHash = 0; iHash < mMaterialHashes.size(); iHash++)
{
if (mMaterialHashes[iHash] == MatHash)
{
GroupIndex = iHash;
NewHash = false;
break;
}
}
if (NewHash)
{
GroupIndex = mMaterialHashes.size();
mMaterialHashes.push_back(MatHash);
}
// Start writing!
// Generate flags value
bool HasKonst = (NumKonst > 0);
u32 Flags;
if (mVersion <= ePrime)
Flags = 0x1003;
else
Flags = 0x4002;
Flags |= (HasKonst << 3) | mpMat->Options() | (TexFlags << 16);
Out.WriteLong(Flags);
// Texture indices
Out.WriteLong(TexIndices.size());
for (u32 iTex = 0; iTex < TexIndices.size(); iTex++)
Out.WriteLong(TexIndices[iTex]);
// Vertex description
EVertexDescription Desc = mpMat->VtxDesc();
if (mVersion < eEchoes)
Desc = (EVertexDescription) (Desc & 0x00FFFFFF);
Out.WriteLong(Desc);
// Echoes unknowns
if (mVersion == eEchoes)
{
Out.WriteLong(mpMat->EchoesUnknownA());
Out.WriteLong(mpMat->EchoesUnknownB());
}
// Group index
Out.WriteLong(GroupIndex);
// Konst
if (HasKonst)
{
Out.WriteLong(NumKonst);
for (u32 iKonst = 0; iKonst < NumKonst; iKonst++)
Out.WriteLong( mpMat->Konst(iKonst).AsLongRGBA() );
}
// Blend Mode
// Some modifications are done to convert the GLenum to the corresponding GX enum
u16 BlendSrcFac = (u16) mpMat->BlendSrcFac();
u16 BlendDstFac = (u16) mpMat->BlendDstFac();
if (BlendSrcFac >= 0x300) BlendSrcFac -= 0x2FE;
if (BlendDstFac >= 0x300) BlendDstFac -= 0x2FE;
Out.WriteShort(BlendDstFac);
Out.WriteShort(BlendSrcFac);
// Color Channels
Out.WriteLong(1);
Out.WriteLong(0x3000 | (mpMat->IsLightingEnabled() ? 1 : 0));
// TEV
u32 NumPasses = mpMat->PassCount();
Out.WriteLong(NumPasses);
for (u32 iPass = 0; iPass < NumPasses; iPass++)
{
CMaterialPass *pPass = mpMat->Pass(iPass);
u32 ColorInputFlags = ((pPass->ColorInput(0)) |
(pPass->ColorInput(1) << 5) |
(pPass->ColorInput(2) << 10) |
(pPass->ColorInput(3) << 15));
u32 AlphaInputFlags = ((pPass->AlphaInput(0)) |
(pPass->AlphaInput(1) << 5) |
(pPass->AlphaInput(2) << 10) |
(pPass->AlphaInput(3) << 15));
u32 ColorOpFlags = 0x100 | (pPass->ColorOutput() << 9);
u32 AlphaOpFlags = 0x100 | (pPass->AlphaOutput() << 9);
Out.WriteLong(ColorInputFlags);
Out.WriteLong(AlphaInputFlags);
Out.WriteLong(ColorOpFlags);
Out.WriteLong(AlphaOpFlags);
Out.WriteByte(0); // Padding
Out.WriteByte(pPass->KAlphaSel());
Out.WriteByte(pPass->KColorSel());
Out.WriteByte(pPass->RasSel());
}
// TEV Tex/UV input selection
u32 CurTexIdx = 0;
for (u32 iPass = 0; iPass < NumPasses; iPass++)
{
Out.WriteShort(0); // Padding
if (mpMat->Pass(iPass)->Texture())
{
Out.WriteByte((u8) CurTexIdx);
Out.WriteByte((u8) CurTexIdx);
CurTexIdx++;
}
else
Out.WriteShort((u16) 0xFFFF);
}
// TexGen
u32 NumTexCoords = CurTexIdx; // TexIdx is currently equal to the tex coord count
Out.WriteLong(NumTexCoords);
u32 CurTexMtx = 0;
for (u32 iPass = 0; iPass < NumPasses; iPass++)
{
CMaterialPass *pPass = mpMat->Pass(iPass);
if (pPass->Texture() == nullptr) continue;
u32 AnimType = pPass->AnimMode();
u32 CoordSource = pPass->TexCoordSource();
u32 TexMtxIdx, PostMtxIdx;
bool Normalize;
// No animation - set TexMtx and PostMtx to identity, disable normalization
if (AnimType == eNoUVAnim)
{
TexMtxIdx = 30;
PostMtxIdx = 61;
Normalize = false;
}
// Animation - set parameters as the animation mode needs them
else
{
TexMtxIdx = CurTexMtx;
if ((AnimType < 2) || (AnimType > 5))
{
PostMtxIdx = CurTexMtx;
Normalize = true;
}
else
{
PostMtxIdx = 61;
Normalize = false;
}
CurTexMtx += 3;
}
u32 TexGenFlags = (CoordSource << 4) | (TexMtxIdx << 9) | (Normalize << 14) | (PostMtxIdx << 15);
Out.WriteLong(TexGenFlags);
}
// Animations
u32 AnimSizeOffset = Out.Tell();
u32 NumAnims = CurTexMtx; // CurTexMtx is currently equal to the anim count
Out.WriteLong(0); // Anim size filler
u32 AnimsStart = Out.Tell();
Out.WriteLong(NumAnims);
for (u32 iPass = 0; iPass < NumPasses; iPass++)
{
CMaterialPass *pPass = mpMat->Pass(iPass);
u32 AnimMode = pPass->AnimMode();
if (AnimMode == eNoUVAnim) continue;
Out.WriteLong(AnimMode);
if ((AnimMode > 1) && (AnimMode != 6))
{
Out.WriteFloat(pPass->AnimParam(0));
Out.WriteFloat(pPass->AnimParam(1));
if ((AnimMode == 2) || (AnimMode == 4) || (AnimMode == 5))
{
Out.WriteFloat(pPass->AnimParam(2));
Out.WriteFloat(pPass->AnimParam(3));
}
}
}
u32 AnimsEnd = Out.Tell();
u32 AnimsSize = AnimsEnd - AnimsStart;
Out.Seek(AnimSizeOffset, SEEK_SET);
Out.WriteLong(AnimsSize);
Out.Seek(AnimsEnd, SEEK_SET);
// Done!
}
void CMaterialCooker::WriteMaterialCorruption(COutputStream&)
{
// Not using parameter 1 (COutputStream& - Out)
// todo
}
// ************ STATIC ************
void CMaterialCooker::WriteCookedMatSet(CMaterialSet *pSet, EGame Version, COutputStream &Out)
{
CMaterialCooker Cooker;
Cooker.mpSet = pSet;
Cooker.mVersion = Version;
switch (Version)
{
case ePrimeDemo:
case ePrime:
case eEchoesDemo:
case eEchoes:
Cooker.WriteMatSetPrime(Out);
break;
}
}
void CMaterialCooker::WriteCookedMaterial(CMaterial *pMat, EGame Version, COutputStream &Out)
{
CMaterialCooker Cooker;
Cooker.mpMat = pMat;
Cooker.mVersion = Version;
switch (Version)
{
case ePrimeDemo:
case ePrime:
case eEchoesDemo:
case eEchoes:
Cooker.WriteMaterialPrime(Out);
break;
// TODO: Corruption/Uncooked
}
}

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((u16) 0x8000);
u32 PrimTableSizeOffset = Out.Tell();
Out.WriteShort(0);
Out.WriteLongLong(0);
Out.WriteLong(0);
pSurface->ReflectionDirection.Write(Out);
Out.WriteToBoundary(32, 0);
u32 PrimTableStart = Out.Tell();
EVertexDescription MatAttribs = mpModel->GetMaterialBySurface(0, iSurf)->VtxDesc();
for (u32 iPrim = 0; iPrim < pSurface->Primitives.size(); iPrim++)
{
SSurface::SPrimitive *pPrimitive = &pSurface->Primitives[iPrim];
Out.WriteByte((u8) pPrimitive->Type);
Out.WriteShort((u16) pPrimitive->Vertices.size());
for (u32 iVert = 0; iVert < pPrimitive->Vertices.size(); iVert++)
{
CVertex *pVert = &pPrimitive->Vertices[iVert];
if (mVersion == eEchoes)
{
for (u32 iMtxAttribs = 0; iMtxAttribs < 8; iMtxAttribs++)
if (MatAttribs & (ePosMtx << iMtxAttribs))
Out.WriteByte(pVert->MatrixIndices[iMtxAttribs]);
}
u16 VertexIndex = (u16) pVert->ArrayPosition;
if (MatAttribs & ePosition)
Out.WriteShort(VertexIndex);
if (MatAttribs & eNormal)
Out.WriteShort(VertexIndex);
if (MatAttribs & eColor0)
Out.WriteShort(VertexIndex);
if (MatAttribs & eColor1)
Out.WriteShort(VertexIndex);
u16 TexOffset = 0;
for (u32 iTex = 0; iTex < 8; iTex++)
{
if (MatAttribs & (eTex0 << (iTex * 2)))
{
Out.WriteShort(VertexIndex + TexOffset);
TexOffset += (u16) mNumVertices;
}
}
}
}
Out.WriteToBoundary(32, 0);
u32 PrimTableEnd = Out.Tell();
u32 PrimTableSize = PrimTableEnd - PrimTableStart;
Out.Seek(PrimTableSizeOffset, SEEK_SET);
Out.WriteShort((u16) PrimTableSize);
Out.Seek(PrimTableEnd, SEEK_SET);
SectionMgr.AddSize(Out);
SurfaceEndOffsets[iSurf] = Out.Tell() - SurfacesStart;
}
// Done writing the file - now we go back to fill in surface offsets + section sizes
Out.Seek(SurfaceOffsetsStart, SEEK_SET);
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
Out.WriteLong(SurfaceEndOffsets[iSurf]);
Out.Seek(SectionSizesOffset, SEEK_SET);
SectionMgr.WriteSizes(Out);
// Done!
}
void CModelCooker::WriteCookedModel(CModel *pModel, EGame Version, COutputStream& CMDL)
{
CModelCooker Cooker;
Cooker.mpModel = pModel;
Cooker.mVersion = Version;
switch (Version)
{
case ePrimeDemo:
case ePrime:
case eEchoesDemo:
case eEchoes:
Cooker.WriteModelPrime(CMDL);
break;
}
}
void CModelCooker::WriteUncookedModel(CModel* /*pModel*/, COutputStream& /*EMDL*/)
{
}
u32 CModelCooker::GetCMDLVersion(EGame Version)
{
switch (Version)
{
case ePrimeDemo:
case ePrime:
return 0x2;
case eEchoesDemo:
return 0x3;
case eEchoes:
return 0x4;
case eCorruptionProto:
case eCorruption:
return 0x5;
case eReturns:
return 0xA;
default:
return 0;
}
}

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
{
TResPtr<CModel> mpModel;
EGame mVersion;
u32 mNumMatSets;
u32 mNumSurfaces;
u32 mNumVertices;
u8 mVertexFormat;
std::vector<CVertex> mVertices;
EVertexDescription mVtxAttribs;
CModelCooker();
void GenerateSurfaceData();
void WriteEditorModel(COutputStream& Out);
void WriteModelPrime(COutputStream& Out);
public:
static void WriteCookedModel(CModel *pModel, EGame Version, COutputStream& Out);
static void WriteUncookedModel(CModel *pModel, COutputStream& Out);
static u32 GetCMDLVersion(EGame Version);
};
#endif // CMODELCOOKER_H

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,580 @@
#include "CTemplateWriter.h"
#include "../cooker/CWorldCooker.h"
#include <tinyxml2.h>
#include <boost/filesystem.hpp>
using namespace tinyxml2;
CTemplateWriter::CTemplateWriter()
{
}
void CTemplateWriter::SaveAllTemplates()
{
// Create directory
std::list<CMasterTemplate*> masterList = CMasterTemplate::GetMasterList();
TString out = "../templates/";
boost::filesystem::create_directory(out.ToStdString());
// Resave master templates
for (auto it = masterList.begin(); it != masterList.end(); it++)
SaveGameTemplates(*it, out);
// Resave game list
XMLDocument gameList;
XMLDeclaration *pDecl = gameList.NewDeclaration();
gameList.LinkEndChild(pDecl);
XMLElement *pBase = gameList.NewElement("GameList");
pBase->SetAttribute("version", 3);
gameList.LinkEndChild(pBase);
for (auto it = masterList.begin(); it != masterList.end(); it++)
{
CMasterTemplate *pMaster = *it;
XMLElement *pGame = gameList.NewElement("game");
XMLElement *pGameName = gameList.NewElement("name");
pGameName->SetText(*pMaster->mGameName);
XMLElement *pWorldVersion = gameList.NewElement("mlvl");
u32 versionNumber = CWorldCooker::GetMLVLVersion(pMaster->GetGame());
pWorldVersion->SetText(*TString::HexString(versionNumber, true, true, 2));
XMLElement *pTempPath = gameList.NewElement("master");
pTempPath->SetText(*pMaster->mSourceFile);
pGame->LinkEndChild(pGameName);
pGame->LinkEndChild(pWorldVersion);
pGame->LinkEndChild(pTempPath);
pBase->LinkEndChild(pGame);
}
gameList.SaveFile(*(out + "GameList.xml"));
}
void CTemplateWriter::SaveGameTemplates(CMasterTemplate *pMaster, const TString& dir)
{
// Create directory
TString outFile = dir + pMaster->mSourceFile;
TString outDir = outFile.GetFileDirectory();
boost::filesystem::create_directory(outDir.ToStdString());
// Resave script templates
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
SaveScriptTemplate(it->second, outDir);
// Resave master template
XMLDocument master;
XMLDeclaration *pDecl = master.NewDeclaration();
master.LinkEndChild(pDecl);
XMLElement *pBase = master.NewElement("MasterTemplate");
pBase->SetAttribute("version", 3);
master.LinkEndChild(pBase);
// Write property list
if (!pMaster->mPropertyList.empty())
{
SavePropertyList(pMaster, outDir);
XMLElement *pPropList = master.NewElement("properties");
pPropList->SetText("Properties.xml");
pBase->LinkEndChild(pPropList);
}
// Write script objects
XMLElement *pObjects = master.NewElement("objects");
pBase->LinkEndChild(pObjects);
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
{
TString objID;
u32 intID = (it->second)->ObjectID();
if (intID <= 0xFF) objID = TString::HexString(intID, true, true, 2);
else objID = CFourCC(intID).ToString();
XMLElement *pObj = master.NewElement("object");
pObj->SetAttribute("ID", *objID);
pObj->SetAttribute("template", *(it->second)->mSourceFile);
pObjects->LinkEndChild(pObj);
}
// Write script states/messages
std::map<u32, TString> *pMaps[2] = { &pMaster->mStates, &pMaster->mMessages };
TString types[2] = { "state", "message" };
for (u32 iScr = 0; iScr < 2; iScr++)
{
XMLElement *pElem = master.NewElement(*(types[iScr] + "s"));
pBase->LinkEndChild(pElem);
for (auto it = pMaps[iScr]->begin(); it != pMaps[iScr]->end(); it++)
{
TString ID;
if (it->first <= 0xFF) ID = TString::HexString(it->first, true, true, 2);
else ID = CFourCC(it->first).ToString();
XMLElement *pSubElem = master.NewElement(*types[iScr]);
pSubElem->SetAttribute("ID", *ID);
pSubElem->SetAttribute("name", *(it->second));
pElem->LinkEndChild(pSubElem);
}
}
// Save file
master.SaveFile(*outFile);
}
void CTemplateWriter::SavePropertyList(CMasterTemplate *pMaster, const TString& dir)
{
// Create XML
XMLDocument list;
XMLDeclaration *pDecl = list.NewDeclaration();
list.LinkEndChild(pDecl);
XMLElement *pBase = list.NewElement("Properties");
pBase->SetAttribute("version", 3);
list.LinkEndChild(pBase);
// Write properties
for (auto it = pMaster->mPropertyList.begin(); it != pMaster->mPropertyList.end(); it++)
{
CPropertyTemplate *pTemp = it->second;
if (pTemp->Type() == eStructProperty)
{
CStructTemplate *pStructTemp = static_cast<CStructTemplate*>(pTemp);
XMLElement *pElem = list.NewElement("struct");
pElem->SetAttribute("ID", *TString::HexString(pTemp->PropertyID(), true, true, 8));
pElem->SetAttribute("name", *pTemp->Name());
if (!pStructTemp->mSourceFile.IsEmpty())
{
SaveStructTemplate(pStructTemp, pMaster, dir);
pElem->SetAttribute("template", *pStructTemp->mSourceFile);
}
pBase->LinkEndChild(pElem);
}
else
{
XMLElement *pElem = list.NewElement("property");
pElem->SetAttribute("ID", *TString::HexString(pTemp->PropertyID(), true, true, 8));
pElem->SetAttribute("name", *pTemp->Name());
pElem->SetAttribute("type", *PropEnumToPropString(pTemp->Type()));
if (pTemp->Type() == eFileProperty)
{
// Construct extension list string
CFileTemplate *pFileProp = static_cast<CFileTemplate*>(pTemp);
const TStringList& extensions = pFileProp->Extensions();
TString strList = "";
for (auto it = extensions.begin(); it != extensions.end();)
{
strList += *it;
it++;
if (it != extensions.end()) strList += ",";
}
pElem->SetAttribute("ext", *strList);
}
pBase->LinkEndChild(pElem);
}
}
list.SaveFile(*(dir + "Properties.xml"));
}
void CTemplateWriter::SaveScriptTemplate(CScriptTemplate *pTemp, const TString& dir)
{
// Create directory
TString outFile = dir + pTemp->mSourceFile;
TString outDir = outFile.GetFileDirectory();
boost::filesystem::create_directory(*outDir);
// Create new document
XMLDocument scriptXML;
XMLDeclaration *pDecl = scriptXML.NewDeclaration();
scriptXML.LinkEndChild(pDecl);
// Base element
XMLElement *pBase = scriptXML.NewElement("ScriptTemplate");
pBase->SetAttribute("version", 3.0f);
scriptXML.LinkEndChild(pBase);
// Write object name
XMLElement *pName = scriptXML.NewElement("name");
pName->SetText(*pTemp->TemplateName());
pBase->LinkEndChild(pName);
// Write properties
for (auto it = pTemp->mPropertySets.begin(); it != pTemp->mPropertySets.end(); it++)
{
XMLElement *pProperties = scriptXML.NewElement("properties");
pProperties->SetAttribute("version", *it->SetName);
SaveProperties(&scriptXML, pProperties, it->pBaseStruct, pTemp->MasterTemplate(), dir);
pBase->LinkEndChild(pProperties);
}
// Write editor properties
XMLElement *pEditor = scriptXML.NewElement("editor");
pBase->LinkEndChild(pEditor);
// Editor Properties
XMLElement *pEditorProperties = scriptXML.NewElement("properties");
pEditor->LinkEndChild(pEditorProperties);
TString propNames[6] = {
"InstanceName", "Position", "Rotation",
"Scale", "Active", "LightParameters"
};
TIDString *pPropStrings[6] = {
&pTemp->mNameIDString, &pTemp->mPositionIDString, &pTemp->mRotationIDString,
&pTemp->mScaleIDString, &pTemp->mActiveIDString, &pTemp->mLightParametersIDString
};
for (u32 iProp = 0; iProp < 6; iProp++)
{
if (!pPropStrings[iProp]->IsEmpty())
{
XMLElement *pProperty = scriptXML.NewElement("property");
pProperty->SetAttribute("name", *propNames[iProp]);
pProperty->SetAttribute("ID", **pPropStrings[iProp]);
pEditorProperties->LinkEndChild(pProperty);
}
}
// Editor Assets
XMLElement *pAssets = scriptXML.NewElement("assets");
pEditor->LinkEndChild(pAssets);
for (auto it = pTemp->mAssets.begin(); it != pTemp->mAssets.end(); it++)
{
TString source = (it->AssetSource == CScriptTemplate::SEditorAsset::eFile ? "file" : "property");
TString type;
switch (it->AssetType)
{
case CScriptTemplate::SEditorAsset::eModel: type = "model"; break;
case CScriptTemplate::SEditorAsset::eAnimParams: type = "animparams"; break;
case CScriptTemplate::SEditorAsset::eBillboard: type = "billboard"; break;
case CScriptTemplate::SEditorAsset::eCollision: type = "collision"; break;
}
s32 force = -1;
if (it->AssetSource == CScriptTemplate::SEditorAsset::eAnimParams) force = it->ForceNodeIndex;
XMLElement *pAsset = scriptXML.NewElement(*type);
pAsset->SetAttribute("source", *source);
if (force >= 0) pAsset->SetAttribute("force", std::to_string(force).c_str());
pAsset->SetText(*it->AssetLocation);
pAssets->LinkEndChild(pAsset);
}
// Preview Scale
if (pTemp->mPreviewScale != 1.f)
{
XMLElement *pPreviewScale = scriptXML.NewElement("preview_scale");
pEditor->LinkEndChild(pPreviewScale);
pPreviewScale->SetText(pTemp->mPreviewScale);
}
// Rot/Scale Type
XMLElement *pRotType = scriptXML.NewElement("rotation_type");
pEditor->LinkEndChild(pRotType);
pRotType->SetText(pTemp->mRotationType == CScriptTemplate::eRotationEnabled ? "enabled" : "disabled");
XMLElement *pScaleType = scriptXML.NewElement("scale_type");
pEditor->LinkEndChild(pScaleType);
if (pTemp->mScaleType != CScriptTemplate::eScaleVolume)
pScaleType->SetText(pTemp->mScaleType == CScriptTemplate::eScaleEnabled ? "enabled" : "disabled");
else
{
pScaleType->SetText("volume");
// Volume Preview
XMLElement *pVolume = scriptXML.NewElement("preview_volume");
pEditor->LinkEndChild(pVolume);
// Enum -> String conversion lambda to avoid redundant code
auto GetVolumeString = [](EVolumeShape shape) -> TString
{
switch (shape)
{
case eBoxShape: return "Box";
case eAxisAlignedBoxShape: return "AxisAlignedBox";
case eEllipsoidShape: return "Ellipsoid";
case eCylinderShape: return "Cylinder";
case eCylinderLargeShape: return "CylinderLarge";
case eConditionalShape: return "Conditional";
default: return "INVALID";
}
};
pVolume->SetAttribute("shape", *GetVolumeString(pTemp->mVolumeShape));
if (pTemp->mVolumeShape == eConditionalShape)
{
pVolume->SetAttribute("propertyID", *pTemp->mVolumeConditionIDString);
// Find conditional test property
CPropertyTemplate *pProp;
for (auto it = pTemp->mPropertySets.begin(); it != pTemp->mPropertySets.end(); it++)
{
pProp = it->pBaseStruct->PropertyByIDString(pTemp->mVolumeConditionIDString);
if (pProp) break;
}
// Write conditions
for (auto it = pTemp->mVolumeConditions.begin(); it != pTemp->mVolumeConditions.end(); it++)
{
// Value should be an integer, or a boolean condition?
TString strVal;
if (pProp->Type() == eBoolProperty)
strVal = (it->Value == 1 ? "true" : "false");
else
strVal = TString::HexString((u32) it->Value, true, true, (it->Value > 0xFF ? 8 : 2));
XMLElement *pCondition = scriptXML.NewElement("condition");
pCondition->SetAttribute("value", *strVal);
pCondition->SetAttribute("shape", *GetVolumeString(it->Shape));
pVolume->LinkEndChild(pCondition);
}
}
}
// Write to file
scriptXML.SaveFile(*outFile);
}
void CTemplateWriter::SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir)
{
// Create directory
TString outFile = dir + pTemp->mSourceFile;
TString outDir = outFile.GetFileDirectory();
TString name = pTemp->mSourceFile.GetFileName();
boost::filesystem::create_directory(outDir.ToStdString());
// Create new document and write struct properties to it
XMLDocument structXML;
XMLDeclaration *pDecl = structXML.NewDeclaration();
structXML.LinkEndChild(pDecl);
XMLElement *pBase = structXML.NewElement("struct");
pBase->SetAttribute("name", *name);
pBase->SetAttribute("type", (pTemp->IsSingleProperty() ? "single" : "multi"));
SaveProperties(&structXML, pBase, pTemp, pMaster, dir);
structXML.LinkEndChild(pBase);
structXML.SaveFile(*outFile);
}
void CTemplateWriter::SaveEnumTemplate(CEnumTemplate *pTemp, const TString& dir)
{
// Create directory
TString outFile = dir + pTemp->mSourceFile;
TString outDir = outFile.GetFileDirectory();
TString name = pTemp->mSourceFile.GetFileName(false);
boost::filesystem::create_directory(*outDir);
// Create new document and write enumerators to it
XMLDocument enumXML;
XMLDeclaration *pDecl = enumXML.NewDeclaration();
enumXML.LinkEndChild(pDecl);
XMLElement *pBase = enumXML.NewElement("enum");
pBase->SetAttribute("name", *name);
SaveEnumerators(&enumXML, pBase, pTemp);
enumXML.LinkEndChild(pBase);
enumXML.SaveFile(*outFile);
}
void CTemplateWriter::SaveBitfieldTemplate(CBitfieldTemplate *pTemp, const TString& dir)
{
// Create directory
TString outFile = dir + pTemp->mSourceFile;
TString outDir = outFile.GetFileDirectory();
TString name = pTemp->mSourceFile.GetFileName();
boost::filesystem::create_directory(*outDir);
// Create new document and write enumerators to it
XMLDocument bitfieldXML;
XMLDeclaration *pDecl = bitfieldXML.NewDeclaration();
bitfieldXML.LinkEndChild(pDecl);
XMLElement *pBase = bitfieldXML.NewElement("bitfield");
pBase->SetAttribute("name", *name);
SaveBitFlags(&bitfieldXML, pBase, pTemp);
bitfieldXML.LinkEndChild(pBase);
bitfieldXML.SaveFile(*outFile);
}
void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir)
{
for (u32 iProp = 0; iProp < pTemp->Count(); iProp++)
{
CPropertyTemplate *pProp = pTemp->PropertyByIndex(iProp);
u32 propID = (pProp->PropertyID() == 0xFFFFFFFF ? iProp : pProp->PropertyID());
TString strID = TString::HexString(propID, true, true, (propID > 0xFF ? 8 : 2));
if (pProp->Type() == eStructProperty)
{
CStructTemplate *pStructTemp = static_cast<CStructTemplate*>(pProp);
bool isExternal = (!pStructTemp->mSourceFile.IsEmpty());
XMLElement *pElem = pDoc->NewElement("struct");
pElem->SetAttribute("ID", *strID);
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1) || pTemp->IsSingleProperty())
{
pElem->SetAttribute("name", *pProp->Name());
}
if (!isExternal) {
TString type = pStructTemp->IsSingleProperty() ? "single" : "multi";
pElem->SetAttribute("type", *type);
}
// Only save properties if this is a multi struct, or if there is no master property list
if (!pMaster->HasPropertyList() || !pStructTemp->IsSingleProperty())
{
// Embed struct or save to external XML?
if (!pStructTemp->mSourceFile.IsEmpty())
{
SaveStructTemplate(pStructTemp, pMaster, dir);
pElem->SetAttribute("template", *pStructTemp->mSourceFile);
}
else
{
SaveProperties(pDoc, pElem, pStructTemp, pMaster, dir);
}
}
pParent->LinkEndChild(pElem);
}
else if (pProp->Type() == eEnumProperty)
{
CEnumTemplate *pEnumTemp = static_cast<CEnumTemplate*>(pProp);
bool isExternal = (!pEnumTemp->mSourceFile.IsEmpty());
XMLElement *pElem = pDoc->NewElement("enum");
pElem->SetAttribute("ID", *strID);
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1))
pElem->SetAttribute("name", *pProp->Name());
if (isExternal)
{
SaveEnumTemplate(pEnumTemp, dir);
pElem->SetAttribute("template", *pEnumTemp->mSourceFile);
}
else
{
SaveEnumerators(pDoc, pElem, pEnumTemp);
}
pParent->LinkEndChild(pElem);
}
else if (pProp->Type() == eBitfieldProperty)
{
CBitfieldTemplate *pBitfieldTemp = static_cast<CBitfieldTemplate*>(pProp);
bool isExternal = (!pBitfieldTemp->mSourceFile.IsEmpty());
XMLElement *pElem = pDoc->NewElement("bitfield");
pElem->SetAttribute("ID", *strID);
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1))
pElem->SetAttribute("name", *pProp->Name());
if (isExternal)
{
SaveBitfieldTemplate(pBitfieldTemp, dir);
pElem->SetAttribute("template", *pBitfieldTemp->mSourceFile);
}
else
{
SaveBitFlags(pDoc, pElem, pBitfieldTemp);
}
pParent->LinkEndChild(pElem);
}
else
{
XMLElement *pElem = pDoc->NewElement("property");
pElem->SetAttribute("ID", *strID);
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1) || pTemp->IsSingleProperty())
{
pElem->SetAttribute("name", *pProp->Name());
pElem->SetAttribute("type", *PropEnumToPropString(pProp->Type()));
if (pProp->Type() == eFileProperty)
{
// Construct extension list string
CFileTemplate *pFileProp = static_cast<CFileTemplate*>(pProp);
const TStringList& extensions = pFileProp->Extensions();
TString strList = "";
for (auto it = extensions.begin(); it != extensions.end();)
{
strList += *it;
it++;
if (it != extensions.end()) strList += ",";
}
pElem->SetAttribute("ext", *strList);
}
}
pParent->LinkEndChild(pElem);
}
}
}
void CTemplateWriter::SaveEnumerators(XMLDocument *pDoc, XMLElement *pParent, CEnumTemplate *pTemp)
{
for (u32 iEnum = 0; iEnum < pTemp->NumEnumerators(); iEnum++)
{
XMLElement *pElem = pDoc->NewElement("enumerator");
pElem->SetAttribute("ID", *TString::HexString(pTemp->EnumeratorID(iEnum), true, true, 8));
pElem->SetAttribute("name", *pTemp->EnumeratorName(iEnum));
pParent->LinkEndChild(pElem);
}
}
void CTemplateWriter::SaveBitFlags(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CBitfieldTemplate *pTemp)
{
for (u32 iFlag = 0; iFlag < pTemp->NumFlags(); iFlag++)
{
XMLElement *pElem = pDoc->NewElement("bitflag");
pElem->SetAttribute("mask", *TString::HexString(pTemp->FlagMask(iFlag), true, true, 8));
pElem->SetAttribute("name", *pTemp->FlagName(iFlag));
pParent->LinkEndChild(pElem);
}
}

View File

@@ -0,0 +1,24 @@
#ifndef CTEMPLATEWRITER_H
#define CTEMPLATEWRITER_H
#include "../script/CMasterTemplate.h"
#include "../script/CScriptTemplate.h"
class CTemplateWriter
{
CTemplateWriter();
public:
static void SaveAllTemplates();
static void SaveGameTemplates(CMasterTemplate *pMaster, const TString& dir);
static void SavePropertyList(CMasterTemplate *pMaster, const TString& dir);
static void SaveScriptTemplate(CScriptTemplate *pTemp, const TString& dir);
static void SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir);
static void SaveEnumTemplate(CEnumTemplate *pTemp, const TString& dir);
static void SaveBitfieldTemplate(CBitfieldTemplate *pTemp, const TString& dir);
static void SaveProperties(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir);
static void SaveEnumerators(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CEnumTemplate *pTemp);
static void SaveBitFlags(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CBitfieldTemplate *pTemp);
};
#endif // CTEMPLATEWRITER_H

View File

@@ -0,0 +1,108 @@
#include "CTextureEncoder.h"
#include <iostream>
CTextureEncoder::CTextureEncoder()
{
mpTexture = nullptr;
}
void CTextureEncoder::WriteTXTR(COutputStream& TXTR)
{
// Only DXT1->CMPR supported at the moment
TXTR.WriteLong(mOutputFormat);
TXTR.WriteShort(mpTexture->mWidth);
TXTR.WriteShort(mpTexture->mHeight);
TXTR.WriteLong(mpTexture->mNumMipMaps);
u32 MipW = mpTexture->Width() / 4;
u32 MipH = mpTexture->Height() / 4;
CMemoryInStream Image(mpTexture->mImgDataBuffer, mpTexture->mImgDataSize, IOUtil::LittleEndian);
u32 MipOffset = Image.Tell();
for (u32 iMip = 0; iMip < mpTexture->mNumMipMaps; iMip++)
{
for (u32 BlockY = 0; BlockY < MipH; BlockY += 2)
for (u32 BlockX = 0; BlockX < MipW; BlockX += 2)
for (u32 ImgY = BlockY; ImgY < BlockY + 2; ImgY++)
for (u32 ImgX = BlockX; ImgX < BlockX + 2; ImgX++)
{
u32 SrcPos = ((ImgY * MipW) + ImgX) * 8;
Image.Seek(MipOffset + SrcPos, SEEK_SET);
ReadSubBlockCMPR(Image, TXTR);
}
MipOffset += MipW * MipH * 8;
MipW /= 2;
MipH /= 2;
if (MipW < 2) MipW = 2;
if (MipH < 2) MipH = 2;
}
}
void CTextureEncoder::DetermineBestOutputFormat()
{
// todo
}
void CTextureEncoder::ReadSubBlockCMPR(CInputStream& Source, COutputStream& Dest)
{
Dest.WriteShort(Source.ReadShort());
Dest.WriteShort(Source.ReadShort());
for (u32 byte = 0; byte < 4; byte++) {
u8 b = Source.ReadByte();
b = ((b & 0x3) << 6) | ((b & 0xC) << 2) | ((b & 0x30) >> 2) | ((b & 0xC0) >> 6);
Dest.WriteByte(b);
}
}
// ************ STATIC ************
void CTextureEncoder::EncodeTXTR(COutputStream& TXTR, CTexture *pTex)
{
if (pTex->mTexelFormat != eDXT1)
{
std::cout << "\rError: Unsupported texel format for decoding\n";
return;
}
CTextureEncoder Encoder;
Encoder.mpTexture = pTex;
Encoder.mSourceFormat = eDXT1;
Encoder.mOutputFormat = eGX_CMPR;
Encoder.WriteTXTR(TXTR);
}
void CTextureEncoder::EncodeTXTR(COutputStream& TXTR, CTexture *pTex, ETexelFormat /*OutputFormat*/)
{
// todo: support for encoding a specific format
EncodeTXTR(TXTR, pTex);
}
ETexelFormat CTextureEncoder::GetGXFormat(ETexelFormat Format)
{
switch (Format)
{
case eLuminance: return eGX_I8;
case eLuminanceAlpha: return eGX_IA8;
case eRGBA4: return eGX_RGB5A3;
case eRGB565: return eGX_RGB565;
case eRGBA8: return eGX_RGBA8;
case eDXT1: return eGX_CMPR;
default: return eInvalidTexelFormat;
}
}
ETexelFormat CTextureEncoder::GetFormat(ETexelFormat Format)
{
switch (Format)
{
case eGX_I4: return eLuminance;
case eGX_I8: return eLuminance;
case eGX_IA4: return eLuminanceAlpha;
case eGX_IA8: return eLuminanceAlpha;
// todo rest of these
case eGX_CMPR: return eDXT1;
default: return eInvalidTexelFormat;
}
}

View File

@@ -0,0 +1,27 @@
#ifndef CTEXTUREENCODER_H
#define CTEXTUREENCODER_H
#include "../CTexture.h"
#include <Core/TResPtr.h>
// Class contains basic functionality right now - only supports directly converting DXT1 to CMPR
// More advanced functions (including actual encoding!) coming later
class CTextureEncoder
{
TResPtr<CTexture> mpTexture;
ETexelFormat mSourceFormat;
ETexelFormat mOutputFormat;
CTextureEncoder();
void WriteTXTR(COutputStream& TXTR);
void DetermineBestOutputFormat();
void ReadSubBlockCMPR(CInputStream& Source, COutputStream& Dest);
public:
static void EncodeTXTR(COutputStream& TXTR, CTexture *pTex);
static void EncodeTXTR(COutputStream& TXTR, CTexture *pTex, ETexelFormat OutputFormat);
static ETexelFormat GetGXFormat(ETexelFormat Format);
static ETexelFormat GetFormat(ETexelFormat Format);
};
#endif // CTEXTUREENCODER_H

View File

@@ -0,0 +1,19 @@
#include "CWorldCooker.h"
CWorldCooker::CWorldCooker()
{
}
u32 CWorldCooker::GetMLVLVersion(EGame version)
{
switch (version)
{
case ePrimeDemo: return 0xD;
case ePrime: return 0x11;
case eEchoesDemo: return 0x14;
case eEchoes: return 0x17;
case eCorruption: return 0x19;
case eReturns: return 0x1B;
default: return 0;
}
}

View File

@@ -0,0 +1,14 @@
#ifndef CWORLDCOOKER_H
#define CWORLDCOOKER_H
#include <Common/types.h>
#include "../EFormatVersion.h"
class CWorldCooker
{
CWorldCooker();
public:
static u32 GetMLVLVersion(EGame version);
};
#endif // CWORLDCOOKER_H

View File

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

View File

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

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

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

View File

@@ -0,0 +1,196 @@
#include "CAnimSetLoader.h"
#include <Core/CResCache.h>
#include <Core/Log.h>
CAnimSetLoader::CAnimSetLoader()
{
}
CAnimSet* CAnimSetLoader::LoadCorruptionCHAR(CInputStream& CHAR)
{
// For now, we only read enough to fetch the model
CHAR.Seek(0x1, SEEK_CUR);
set->nodes.resize(1);
CAnimSet::SNode& node = set->nodes[0];
node.name = CHAR.ReadString();
node.model = gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
return set;
}
CAnimSet* CAnimSetLoader::LoadReturnsCHAR(CInputStream& CHAR)
{
// For now, we only read enough to fetch the model
CHAR.Seek(0x16, SEEK_CUR);
set->nodes.resize(1);
CAnimSet::SNode& node = set->nodes[0];
node.name = CHAR.ReadString();
CHAR.Seek(0x14, SEEK_CUR);
CHAR.ReadString();
node.model = gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
return set;
}
void CAnimSetLoader::LoadPASDatabase(CInputStream& PAS4)
{
// For now, just parse the data; don't store it
PAS4.Seek(0x4, SEEK_CUR); // Skipping PAS4 FourCC
u32 anim_state_count = PAS4.ReadLong();
PAS4.Seek(0x4, SEEK_CUR); // Skipping default anim state
for (u32 s = 0; s < anim_state_count; s++)
{
PAS4.Seek(0x4, SEEK_CUR); // Skipping unknown value
u32 parm_info_count = PAS4.ReadLong();
u32 anim_info_count = PAS4.ReadLong();
u32 skip = 0;
for (u32 p = 0; p < parm_info_count; p++)
{
u32 type = PAS4.ReadLong();
PAS4.Seek(0x8, SEEK_CUR);
switch (type) {
case 0: // Int32
case 1: // Uint32
case 2: // Real32
case 4: // Enum
PAS4.Seek(0x8, SEEK_CUR);
skip += 4;
break;
case 3: // Bool
PAS4.Seek(0x2, SEEK_CUR);
skip++;
break;
}
}
for (u32 a = 0; a < anim_info_count; a++)
PAS4.Seek(0x4 + skip, SEEK_CUR);
}
}
// ************ STATIC ************
CAnimSet* CAnimSetLoader::LoadANCS(CInputStream& ANCS)
{
if (!ANCS.IsValid()) return nullptr;
Log::Write("Loading " + ANCS.GetSourceString());
u32 magic = ANCS.ReadLong();
if (magic != 0x00010001)
{
Log::FileError(ANCS.GetSourceString(), "Invalid ANCS magic: " + TString::HexString(magic));
return nullptr;
}
CAnimSetLoader loader;
loader.set = new CAnimSet;
u32 node_count = ANCS.ReadLong();
loader.set->nodes.resize(node_count);
for (u32 n = 0; n < node_count; n++)
{
CAnimSet::SNode *node = &loader.set->nodes[n];
ANCS.Seek(0x4, SEEK_CUR); // Skipping node self-index
u16 unknown1 = ANCS.ReadShort();
if (n == 0) loader.mVersion = (unknown1 == 0xA) ? eEchoes : ePrime; // Best version indicator we know of unfortunately
node->name = ANCS.ReadString();
node->model = gResCache.GetResource(ANCS.ReadLong(), "CMDL");
node->skinID = ANCS.ReadLong();
node->skelID = ANCS.ReadLong();
// Unfortunately that's all that's actually supported at the moment. Hope to expand later.
// Since there's no size value I have to actually read the rest of the node to reach the next one
u32 anim_count = ANCS.ReadLong();
for (u32 a = 0; a < anim_count; a++)
{
ANCS.Seek(0x4, SEEK_CUR);
if (loader.mVersion == ePrime) ANCS.Seek(0x1, SEEK_CUR);
ANCS.ReadString();
}
// PAS Database
loader.LoadPASDatabase(ANCS);
// Particles
u32 particle_count = ANCS.ReadLong();
ANCS.Seek(particle_count * 4, SEEK_CUR);
u32 swoosh_count = ANCS.ReadLong();
ANCS.Seek(swoosh_count * 4, SEEK_CUR);
if (unknown1 != 5) ANCS.Seek(0x4, SEEK_CUR);
u32 electric_count = ANCS.ReadLong();
ANCS.Seek(electric_count * 4, SEEK_CUR);
if (loader.mVersion == eEchoes) {
u32 spsc_count = ANCS.ReadLong();
ANCS.Seek(spsc_count * 4, SEEK_CUR);
}
ANCS.Seek(0x4, SEEK_CUR);
if (loader.mVersion == eEchoes) ANCS.Seek(0x4, SEEK_CUR);
u32 anim_count2 = ANCS.ReadLong();
for (u32 a = 0; a < anim_count2; a++)
{
ANCS.ReadString();
ANCS.Seek(0x18, SEEK_CUR);
}
u32 EffectGroupCount = ANCS.ReadLong();
for (u32 g = 0; g < EffectGroupCount; g++)
{
ANCS.ReadString();
u32 EffectCount = ANCS.ReadLong();
for (u32 e = 0; e < EffectCount; e++)
{
ANCS.ReadString();
ANCS.Seek(0x8, SEEK_CUR);
if (loader.mVersion == ePrime) ANCS.ReadString();
if (loader.mVersion == eEchoes) ANCS.Seek(0x4, SEEK_CUR);
ANCS.Seek(0xC, SEEK_CUR);
}
}
ANCS.Seek(0x8, SEEK_CUR);
u32 unknown_count = ANCS.ReadLong();
ANCS.Seek(unknown_count * 4, SEEK_CUR);
if (loader.mVersion == eEchoes)
{
ANCS.Seek(0x5, SEEK_CUR);
u32 unknown_count2 = ANCS.ReadLong();
ANCS.Seek(unknown_count2 * 0x1C, SEEK_CUR);
}
// Lots of work for data I'm not even using x.x
}
return loader.set;
}
CAnimSet* CAnimSetLoader::LoadCHAR(CInputStream &CHAR)
{
if (!CHAR.IsValid()) return nullptr;
Log::Write("Loading " + CHAR.GetSourceString());
CAnimSetLoader loader;
u8 check = CHAR.ReadByte();
if (check == 0x5)
{
loader.mVersion = eCorruption;
loader.set = new CAnimSet();
return loader.LoadCorruptionCHAR(CHAR);
}
if (check == 0x59)
{
loader.mVersion = eReturns;
loader.set = new CAnimSet();
return loader.LoadReturnsCHAR(CHAR);
}
Log::FileError(CHAR.GetSourceString(), "CHAR has invalid first byte: " + TString::HexString(check));
return nullptr;
}

View File

@@ -0,0 +1,24 @@
#ifndef CCHARACTERLOADER_H
#define CCHARACTERLOADER_H
#include "../CAnimSet.h"
#include "../EFormatVersion.h"
#include <Core/CResCache.h>
class CAnimSetLoader
{
TResPtr<CAnimSet> set;
CResCache *mpResCache;
EGame mVersion;
CAnimSetLoader();
CAnimSet* LoadCorruptionCHAR(CInputStream& CHAR);
CAnimSet* LoadReturnsCHAR(CInputStream& CHAR);
void LoadPASDatabase(CInputStream& PAS4);
public:
static CAnimSet* LoadANCS(CInputStream& ANCS);
static CAnimSet* LoadCHAR(CInputStream& CHAR);
};
#endif // CCHARACTERLOADER_H

View File

@@ -0,0 +1,618 @@
#include "CAreaLoader.h"
#include "CCollisionLoader.h"
#include "CModelLoader.h"
#include "CMaterialLoader.h"
#include "CScriptLoader.h"
#include <Common/CFourCC.h>
#include <Common/CompressionUtil.h>
#include <iostream>
#include <Core/Log.h>
CAreaLoader::CAreaLoader()
{
mpMREA = nullptr;
mHasDecompressedBuffer = false;
mGeometryBlockNum = -1;
mScriptLayerBlockNum = -1;
mCollisionBlockNum = -1;
mUnknownBlockNum = -1;
mLightsBlockNum = -1;
mEmptyBlockNum = -1;
mPathBlockNum = -1;
mOctreeBlockNum = -1;
mScriptGeneratorBlockNum = -1;
mFFFFBlockNum = -1;
mUnknown2BlockNum = -1;
mEGMCBlockNum = -1;
mBoundingBoxesBlockNum = -1;
mDependenciesBlockNum = -1;
mGPUBlockNum = -1;
mPVSBlockNum = -1;
mRSOBlockNum = -1;
}
CAreaLoader::~CAreaLoader()
{
if (mHasDecompressedBuffer)
{
delete mpMREA;
delete[] mDecmpBuffer;
}
}
// ************ PRIME ************
void CAreaLoader::ReadHeaderPrime()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP1)");
mpArea->mTransform = CTransform4f(*mpMREA);
mNumMeshes = mpMREA->ReadLong();
u32 mNumBlocks = mpMREA->ReadLong();
mGeometryBlockNum = mpMREA->ReadLong();
mScriptLayerBlockNum = mpMREA->ReadLong();
mCollisionBlockNum = mpMREA->ReadLong();
mUnknownBlockNum = mpMREA->ReadLong();
mLightsBlockNum = mpMREA->ReadLong();
mEmptyBlockNum = mpMREA->ReadLong();
mPathBlockNum = mpMREA->ReadLong();
mOctreeBlockNum = mpMREA->ReadLong();
mBlockMgr = new CBlockMgrIn(mNumBlocks, mpMREA);
mpMREA->SeekToBoundary(32);
mBlockMgr->Init();
}
void CAreaLoader::ReadGeometryPrime()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)");
mBlockMgr->ToBlock(mGeometryBlockNum);
// Materials
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
mBlockMgr->ToNextBlock();
// Geometry
for (u32 m = 0; m < mNumMeshes; m++) {
std::cout << "\rLoading mesh " << std::dec << m + 1 << "/" << mNumMeshes;
CModel *pTerrainModel = CModelLoader::LoadWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, mVersion);
mpArea->AddWorldModel(pTerrainModel);
if (mVersion >= eEchoes) {
mBlockMgr->ToNextBlock();
mBlockMgr->ToNextBlock();
}
}
mpArea->MergeTerrain();
std::cout << "\n";
}
void CAreaLoader::ReadSCLYPrime()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)");
mBlockMgr->ToBlock(mScriptLayerBlockNum);
CFourCC SCLY(*mpMREA);
if (SCLY != "SCLY")
{
Log::Error(mpMREA->GetSourceString() + " - Invalid SCLY magic: " + SCLY.ToString());
return;
}
mpMREA->Seek(0x4, SEEK_CUR);
mNumLayers = mpMREA->ReadLong();
mpArea->mScriptLayers.reserve(mNumLayers);
std::vector<u32> LayerSizes(mNumLayers);
for (u32 l = 0; l < mNumLayers; l++)
LayerSizes[l] = mpMREA->ReadLong();
for (u32 l = 0; l < mNumLayers; l++)
{
u32 next = mpMREA->Tell() + LayerSizes[l];
CScriptLayer *layer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
if (layer)
mpArea->mScriptLayers.push_back(layer);
mpMREA->Seek(next, SEEK_SET);
}
SetUpObjects();
}
void CAreaLoader::ReadLightsPrime()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)");
mBlockMgr->ToBlock(mLightsBlockNum);
u32 babedead = mpMREA->ReadLong();
if (babedead != 0xbabedead) return;
mpArea->mLightLayers.resize(2);
for (u32 ly = 0; ly < 2; ly++)
{
u32 NumLights = mpMREA->ReadLong();
mpArea->mLightLayers[ly].resize(NumLights);
for (u32 l = 0; l < NumLights; l++)
{
ELightType Type = ELightType(mpMREA->ReadLong());
CVector3f Color(*mpMREA);
CVector3f Position(*mpMREA);
CVector3f Direction(*mpMREA);
float Multiplier = mpMREA->ReadFloat();
float SpotCutoff = mpMREA->ReadFloat();
mpMREA->Seek(0x9, SEEK_CUR);
u32 FalloffType = mpMREA->ReadLong();
mpMREA->Seek(0x4, SEEK_CUR);
// Relevant data is read - now we process and form a CLight out of it
CLight *Light;
CColor LightColor = CColor(Color.x, Color.y, Color.z, 0.f);
if (Multiplier < FLT_EPSILON)
Multiplier = FLT_EPSILON;
// Local Ambient
if (Type == eLocalAmbient)
{
Color *= Multiplier;
// Clamp
if (Color.x > 1.f) Color.x = 1.f;
if (Color.y > 1.f) Color.y = 1.f;
if (Color.z > 1.f) Color.z = 1.f;
CColor MultColor(Color.x, Color.y, Color.z, 1.f);
Light = CLight::BuildLocalAmbient(Position, MultColor);
}
// Directional
else if (Type == eDirectional)
{
Light = CLight::BuildDirectional(Position, Direction, LightColor);
}
// Spot
else if (Type == eSpot)
{
Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff);
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f;
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC);
}
// Custom
else
{
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f;
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
Light = CLight::BuildCustom(Position, Direction, LightColor,
DistAttenA, DistAttenB, DistAttenC,
1.f, 0.f, 0.f);
}
Light->SetLayer(ly);
mpArea->mLightLayers[ly][l] = Light;
}
}
}
// ************ ECHOES ************
void CAreaLoader::ReadHeaderEchoes()
{
// This function reads the header for Echoes and the Echoes demo disc
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP2)");
mpArea->mTransform = CTransform4f(*mpMREA);
mNumMeshes = mpMREA->ReadLong();
if (mVersion == eEchoes) mNumLayers = mpMREA->ReadLong();
u32 numBlocks = mpMREA->ReadLong();
mGeometryBlockNum = mpMREA->ReadLong();
mScriptLayerBlockNum = mpMREA->ReadLong();
mScriptGeneratorBlockNum = mpMREA->ReadLong();
mCollisionBlockNum = mpMREA->ReadLong();
mUnknownBlockNum = mpMREA->ReadLong();
mLightsBlockNum = mpMREA->ReadLong();
mEmptyBlockNum = mpMREA->ReadLong();
mPathBlockNum = mpMREA->ReadLong();
mFFFFBlockNum = mpMREA->ReadLong();
mUnknown2BlockNum = mpMREA->ReadLong();
mEGMCBlockNum = mpMREA->ReadLong();
if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong());
mpMREA->SeekToBoundary(32);
mBlockMgr = new CBlockMgrIn(numBlocks, mpMREA);
mpMREA->SeekToBoundary(32);
if (mVersion == eEchoes)
{
ReadCompressedBlocks();
Decompress();
}
mBlockMgr->Init();
}
void CAreaLoader::ReadSCLYEchoes()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)");
mBlockMgr->ToBlock(mScriptLayerBlockNum);
// SCLY
for (u32 l = 0; l < mNumLayers; l++)
{
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
if (pLayer)
mpArea->mScriptLayers.push_back(pLayer);
mBlockMgr->ToNextBlock();
}
// SCGN
mBlockMgr->ToBlock(mScriptGeneratorBlockNum);
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
if (pLayer)
mpArea->mpGeneratorLayer = pLayer;
SetUpObjects();
}
// ************ CORRUPTION ************
void CAreaLoader::ReadHeaderCorruption()
{
// This function reads the header for MP3, the MP3 prototype, and DKCR
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP3/DKCR)");
mpArea->mTransform = CTransform4f(*mpMREA);
mNumMeshes = mpMREA->ReadLong();
mNumLayers = mpMREA->ReadLong();
u32 NumSections = mpMREA->ReadLong();
mClusters.resize(mpMREA->ReadLong());
u32 SectionNumberCount = mpMREA->ReadLong();
mpMREA->SeekToBoundary(32);
mBlockMgr = new CBlockMgrIn(NumSections, mpMREA);
mpMREA->SeekToBoundary(32);
ReadCompressedBlocks();
for (u32 iNum = 0; iNum < SectionNumberCount; iNum++)
{
CFourCC Type(*mpMREA);
u32 Num = mpMREA->ReadLong();
if (Type == "AABB") mBoundingBoxesBlockNum = Num;
else if (Type == "COLI") mCollisionBlockNum = Num;
else if (Type == "DEPS") mDependenciesBlockNum = Num;
else if (Type == "EGMC") mEGMCBlockNum = Num;
else if (Type == "GPUD") mGPUBlockNum = Num;
else if (Type == "LITE") mLightsBlockNum = Num;
else if (Type == "PFL2") mPathBlockNum = Num;
else if (Type == "PVS!") mPVSBlockNum = Num;
else if (Type == "ROCT") mOctreeBlockNum = Num;
else if (Type == "RSOS") mRSOBlockNum = Num;
else if (Type == "SOBJ") mScriptLayerBlockNum = Num;
else if (Type == "SGEN") mScriptGeneratorBlockNum = Num;
else if (Type == "WOBJ") mGeometryBlockNum = Num; // note WOBJ can show up multiple times, but is always 0
}
mpMREA->SeekToBoundary(32);
Decompress();
mBlockMgr->Init();
}
void CAreaLoader::ReadGeometryCorruption()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)");
mBlockMgr->ToBlock(mGeometryBlockNum);
// Materials
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
mBlockMgr->ToNextBlock();
// Geometry
u32 CurWOBJSection = 1;
u32 CurGPUSection = mGPUBlockNum;
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
{
std::cout << "\rLoading mesh " << std::dec << iMesh + 1 << "/" << mNumMeshes;
CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
mpArea->AddWorldModel(pWorldModel);
CurWOBJSection += 4;
CurGPUSection = mBlockMgr->CurrentBlock();
}
mpArea->MergeTerrain();
std::cout << "\n";
}
void CAreaLoader::ReadLightsCorruption()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)");
mBlockMgr->ToBlock(mLightsBlockNum);
u32 babedead = mpMREA->ReadLong();
if (babedead != 0xbabedead) return;
mpArea->mLightLayers.resize(4);
for (u32 iLayer = 0; iLayer < 4; iLayer++)
{
u32 NumLights = mpMREA->ReadLong();
mpArea->mLightLayers[iLayer].resize(NumLights);
for (u32 iLight = 0; iLight < NumLights; iLight++)
{
ELightType Type = (ELightType) mpMREA->ReadLong();
float r = mpMREA->ReadFloat();
float g = mpMREA->ReadFloat();
float b = mpMREA->ReadFloat();
float a = mpMREA->ReadFloat();
CColor LightColor(r, g, b, a);
CVector3f Position(*mpMREA);
CVector3f Direction(*mpMREA);
mpMREA->Seek(0xC, SEEK_CUR);
float Multiplier = mpMREA->ReadFloat();
float SpotCutoff = mpMREA->ReadFloat();
mpMREA->Seek(0x9, SEEK_CUR);
u32 FalloffType = mpMREA->ReadLong();
mpMREA->Seek(0x18, SEEK_CUR);
// Relevant data is read - now we process and form a CLight out of it
CLight *Light;
if (Multiplier < FLT_EPSILON)
Multiplier = FLT_EPSILON;
// Local Ambient
if (Type == eLocalAmbient)
{
Light = CLight::BuildLocalAmbient(Position, LightColor * Multiplier);
}
// Directional
else if (Type == eDirectional)
{
Light = CLight::BuildDirectional(Position, Direction, LightColor);
}
// Spot
else if (Type == eSpot)
{
Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff);
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f;
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC);
}
// Custom
else
{
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f;
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
Light = CLight::BuildCustom(Position, Direction, LightColor,
DistAttenA, DistAttenB, DistAttenC,
1.f, 0.f, 0.f);
}
Light->SetLayer(iLayer);
mpArea->mLightLayers[iLayer][iLight] = Light;
}
}
}
// ************ COMMON ************
void CAreaLoader::ReadCompressedBlocks()
{
mTotalDecmpSize = 0;
for (u32 c = 0; c < mClusters.size(); c++)
{
mClusters[c].BufferSize = mpMREA->ReadLong();
mClusters[c].DecompressedSize = mpMREA->ReadLong();
mClusters[c].CompressedSize = mpMREA->ReadLong();
mClusters[c].NumSections = mpMREA->ReadLong();
mTotalDecmpSize += mClusters[c].DecompressedSize;
}
mpMREA->SeekToBoundary(32);
}
void CAreaLoader::Decompress()
{
// This function decompresses compressed clusters into a buffer.
// It should be called at the beginning of the first compressed cluster.
Log::FileWrite(mpMREA->GetSourceString(), "Decompressing MREA data");
if (mVersion < eEchoes) return;
// Decompress clusters
mDecmpBuffer = new u8[mTotalDecmpSize];
u32 Offset = 0;
for (u32 c = 0; c < mClusters.size(); c++)
{
SCompressedCluster *cc = &mClusters[c];
// Is it decompressed already?
if (mClusters[c].CompressedSize == 0)
{
mpMREA->ReadBytes(mDecmpBuffer + Offset, cc->DecompressedSize);
Offset += cc->DecompressedSize;
}
else
{
u32 StartOffset = 32 - (mClusters[c].CompressedSize % 32); // For some reason they pad the beginning instead of the end
if (StartOffset != 32)
mpMREA->Seek(StartOffset, SEEK_CUR);
std::vector<u8> cmp(mClusters[c].CompressedSize);
mpMREA->ReadBytes(cmp.data(), cmp.size());
bool Success = CompressionUtil::DecompressAreaLZO(cmp.data(), cmp.size(), mDecmpBuffer + Offset, cc->DecompressedSize);
if (!Success)
throw "Failed to decompress MREA!";
Offset += cc->DecompressedSize;
}
}
TString Source = mpMREA->GetSourceString();
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::BigEndian);
mpMREA->SetSourceString(Source.ToStdString());
mBlockMgr->SetInputStream(mpMREA);
mHasDecompressedBuffer = true;
}
void CAreaLoader::ReadCollision()
{
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
mBlockMgr->ToBlock(mCollisionBlockNum);
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
}
void CAreaLoader::SetUpObjects()
{
// Iterate over all objects
for (u32 iLyr = 0; iLyr < mpArea->GetScriptLayerCount() + 1; iLyr++)
{
CScriptLayer *pLayer;
if (iLyr < mpArea->GetScriptLayerCount()) pLayer = mpArea->mScriptLayers[iLyr];
else
{
pLayer = mpArea->GetGeneratorLayer();
if (!pLayer) break;
}
for (u32 iObj = 0; iObj < pLayer->GetNumObjects(); iObj++)
{
// Add object to object map
CScriptObject *pObj = (*pLayer)[iObj];
mpArea->mObjectMap[pObj->InstanceID()] = pObj;
// Store outgoing connections
for (u32 iCon = 0; iCon < pObj->NumOutLinks(); iCon++)
{
SLink Connection = pObj->OutLink(iCon);
SLink NewConnection;
NewConnection.State = Connection.State;
NewConnection.Message = Connection.Message;
NewConnection.ObjectID = pObj->InstanceID();
mConnectionMap[Connection.ObjectID].push_back(NewConnection);
}
}
}
// Store connections
for (auto it = mpArea->mObjectMap.begin(); it != mpArea->mObjectMap.end(); it++)
{
u32 InstanceID = it->first;
auto iConMap = mConnectionMap.find(InstanceID);
if (iConMap != mConnectionMap.end())
{
CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID);
pObj->mInConnections = iConMap->second;
}
}
}
// ************ STATIC ************
CGameArea* CAreaLoader::LoadMREA(CInputStream& MREA)
{
CAreaLoader Loader;
// Validation
if (!MREA.IsValid()) return nullptr;
Log::Write("Loading " + MREA.GetSourceString());
u32 deadbeef = MREA.ReadLong();
if (deadbeef != 0xdeadbeef)
{
Log::FileError(MREA.GetSourceString(), "Invalid MREA magic: " + TString::HexString(deadbeef));
return nullptr;
}
// Header
Loader.mpArea = new CGameArea;
u32 version = MREA.ReadLong();
Loader.mVersion = GetFormatVersion(version);
Loader.mpMREA = &MREA;
switch (Loader.mVersion)
{
case ePrimeDemo:
case ePrime:
Loader.ReadHeaderPrime();
Loader.ReadGeometryPrime();
Loader.ReadSCLYPrime();
Loader.ReadCollision();
Loader.ReadLightsPrime();
break;
case eEchoesDemo:
case eEchoes:
Loader.ReadHeaderEchoes();
Loader.ReadGeometryPrime();
Loader.ReadSCLYEchoes();
Loader.ReadCollision();
Loader.ReadLightsPrime();
break;
case eCorruptionProto:
Loader.ReadHeaderCorruption();
Loader.ReadGeometryPrime();
Loader.ReadSCLYEchoes();
Loader.ReadCollision();
break;
case eCorruption:
case eReturns:
Loader.ReadHeaderCorruption();
Loader.ReadGeometryCorruption();
Loader.ReadSCLYEchoes();
Loader.ReadCollision();
if (Loader.mVersion == eCorruption) Loader.ReadLightsCorruption();
break;
default:
Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + TString::HexString(version));
Loader.mpArea.Delete();
return nullptr;
}
// Cleanup
delete Loader.mBlockMgr;
return Loader.mpArea;
}
EGame CAreaLoader::GetFormatVersion(u32 version)
{
switch (version)
{
case 0xC: return ePrimeDemo;
case 0xF: return ePrime;
case 0x15: return eEchoesDemo;
case 0x19: return eEchoes;
case 0x1D: return eCorruptionProto;
case 0x1E: return eCorruption;
case 0x20: return eReturns;
default: return eUnknownVersion;
}
}

View File

@@ -0,0 +1,84 @@
#ifndef CAREALOADER_H
#define CAREALOADER_H
#include <FileIO/FileIO.h>
#include "../CGameArea.h"
#include "../EFormatVersion.h"
#include "CBlockMgrIn.h"
#include <Core/CResCache.h>
#include <Resource/script/SConnection.h>
class CAreaLoader
{
struct SCompressedCluster;
// Area data
TResPtr<CGameArea> mpArea;
CInputStream *mpMREA;
CBlockMgrIn *mBlockMgr;
EGame mVersion;
u32 mNumMeshes;
u32 mNumLayers;
// Object connections
std::unordered_map<u32, std::vector<SLink>> mConnectionMap;
// Compression
u8 *mDecmpBuffer;
bool mHasDecompressedBuffer;
std::vector<SCompressedCluster> mClusters;
u32 mTotalDecmpSize;
// Block numbers
u32 mGeometryBlockNum;
u32 mScriptLayerBlockNum;
u32 mCollisionBlockNum;
u32 mUnknownBlockNum;
u32 mLightsBlockNum;
u32 mEmptyBlockNum;
u32 mPathBlockNum;
u32 mOctreeBlockNum;
u32 mScriptGeneratorBlockNum;
u32 mFFFFBlockNum;
u32 mUnknown2BlockNum;
u32 mEGMCBlockNum;
u32 mBoundingBoxesBlockNum;
u32 mDependenciesBlockNum;
u32 mGPUBlockNum;
u32 mPVSBlockNum;
u32 mRSOBlockNum;
struct SCompressedCluster {
u32 BufferSize, DecompressedSize, CompressedSize, NumSections;
};
CAreaLoader();
~CAreaLoader();
// Prime
void ReadHeaderPrime();
void ReadGeometryPrime();
void ReadSCLYPrime();
void ReadLightsPrime();
// Echoes
void ReadHeaderEchoes();
void ReadSCLYEchoes();
// Corruption
void ReadHeaderCorruption();
void ReadGeometryCorruption();
void ReadLightsCorruption();
// Common
void ReadCompressedBlocks();
void Decompress();
void ReadCollision();
void SetUpObjects();
public:
static CGameArea* LoadMREA(CInputStream& MREA);
static EGame GetFormatVersion(u32 version);
};
#endif // CAREALOADER_H

Some files were not shown because too many files have changed in this diff Show More