mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-19 01:46:27 +00:00
Mass refactoring part 1/2: establishing multiple subprojects, moving source files to their new location, adding resources/templates to version control
This commit is contained in:
54
src/Core/CAreaAttributes.cpp
Normal file
54
src/Core/CAreaAttributes.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
20
src/Core/CAreaAttributes.h
Normal file
20
src/Core/CAreaAttributes.h
Normal 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
|
||||
25
src/Core/CLightParameters.cpp
Normal file
25
src/Core/CLightParameters.cpp
Normal 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();
|
||||
}
|
||||
18
src/Core/CLightParameters.h
Normal file
18
src/Core/CLightParameters.h
Normal 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
|
||||
68
src/Core/CRayCollisionTester.cpp
Normal file
68
src/Core/CRayCollisionTester.cpp
Normal 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;
|
||||
}
|
||||
35
src/Core/CRayCollisionTester.h
Normal file
35
src/Core/CRayCollisionTester.h
Normal 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
245
src/Core/Core.pro
Normal 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
76
src/Core/Log.cpp
Normal 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
22
src/Core/Log.h
Normal 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
|
||||
|
||||
136
src/Core/OpenGL/CDynamicVertexBuffer.cpp
Normal file
136
src/Core/OpenGL/CDynamicVertexBuffer.cpp
Normal 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;
|
||||
}
|
||||
32
src/Core/OpenGL/CDynamicVertexBuffer.h
Normal file
32
src/Core/OpenGL/CDynamicVertexBuffer.h
Normal 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
|
||||
111
src/Core/OpenGL/CFramebuffer.cpp
Normal file
111
src/Core/OpenGL/CFramebuffer.cpp
Normal 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;
|
||||
32
src/Core/OpenGL/CFramebuffer.h
Normal file
32
src/Core/OpenGL/CFramebuffer.h
Normal 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
41
src/Core/OpenGL/CGL.cpp
Normal 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
26
src/Core/OpenGL/CGL.h
Normal 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
|
||||
156
src/Core/OpenGL/CIndexBuffer.cpp
Normal file
156
src/Core/OpenGL/CIndexBuffer.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
39
src/Core/OpenGL/CIndexBuffer.h
Normal file
39
src/Core/OpenGL/CIndexBuffer.h
Normal 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
|
||||
52
src/Core/OpenGL/CRenderbuffer.cpp
Normal file
52
src/Core/OpenGL/CRenderbuffer.cpp
Normal 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);
|
||||
}
|
||||
31
src/Core/OpenGL/CRenderbuffer.h
Normal file
31
src/Core/OpenGL/CRenderbuffer.h
Normal 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
263
src/Core/OpenGL/CShader.cpp
Normal 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
45
src/Core/OpenGL/CShader.h
Normal 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
|
||||
443
src/Core/OpenGL/CShaderGenerator.cpp
Normal file
443
src/Core/OpenGL/CShaderGenerator.cpp
Normal 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;
|
||||
}
|
||||
22
src/Core/OpenGL/CShaderGenerator.h
Normal file
22
src/Core/OpenGL/CShaderGenerator.h
Normal 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
|
||||
60
src/Core/OpenGL/CUniformBuffer.cpp
Normal file
60
src/Core/OpenGL/CUniformBuffer.cpp
Normal 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;
|
||||
}
|
||||
28
src/Core/OpenGL/CUniformBuffer.h
Normal file
28
src/Core/OpenGL/CUniformBuffer.h
Normal 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
|
||||
105
src/Core/OpenGL/CVertexArrayManager.cpp
Normal file
105
src/Core/OpenGL/CVertexArrayManager.cpp
Normal 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);
|
||||
}
|
||||
34
src/Core/OpenGL/CVertexArrayManager.h
Normal file
34
src/Core/OpenGL/CVertexArrayManager.h
Normal 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
|
||||
230
src/Core/OpenGL/CVertexBuffer.cpp
Normal file
230
src/Core/OpenGL/CVertexBuffer.cpp
Normal 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;
|
||||
}
|
||||
37
src/Core/OpenGL/CVertexBuffer.h
Normal file
37
src/Core/OpenGL/CVertexBuffer.h
Normal 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
|
||||
37
src/Core/OpenGL/GLCommon.cpp
Normal file
37
src/Core/OpenGL/GLCommon.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
34
src/Core/OpenGL/GLCommon.h
Normal file
34
src/Core/OpenGL/GLCommon.h
Normal 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
370
src/Core/Render/CCamera.cpp
Normal 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
99
src/Core/Render/CCamera.h
Normal 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
|
||||
578
src/Core/Render/CDrawUtil.cpp
Normal file
578
src/Core/Render/CDrawUtil.cpp
Normal 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
118
src/Core/Render/CDrawUtil.h
Normal 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
|
||||
|
||||
178
src/Core/Render/CGraphics.cpp
Normal file
178
src/Core/Render/CGraphics.cpp
Normal 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;
|
||||
}
|
||||
99
src/Core/Render/CGraphics.h
Normal file
99
src/Core/Render/CGraphics.h
Normal 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
|
||||
89
src/Core/Render/CRenderBucket.cpp
Normal file
89
src/Core/Render/CRenderBucket.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
33
src/Core/Render/CRenderBucket.h
Normal file
33
src/Core/Render/CRenderBucket.h
Normal 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
|
||||
388
src/Core/Render/CRenderer.cpp
Normal file
388
src/Core/Render/CRenderer.cpp
Normal 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;
|
||||
91
src/Core/Render/CRenderer.h
Normal file
91
src/Core/Render/CRenderer.h
Normal 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
|
||||
11
src/Core/Render/ERenderCommand.h
Normal file
11
src/Core/Render/ERenderCommand.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef ERENDERCOMMAND
|
||||
#define ERENDERCOMMAND
|
||||
|
||||
enum ERenderCommand
|
||||
{
|
||||
eDrawMesh,
|
||||
eDrawSelection
|
||||
};
|
||||
|
||||
#endif // ERENDERCOMMAND
|
||||
|
||||
25
src/Core/Render/ERenderOptions.h
Normal file
25
src/Core/Render/ERenderOptions.h
Normal 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
|
||||
|
||||
20
src/Core/Render/IRenderable.h
Normal file
20
src/Core/Render/IRenderable.h
Normal 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
|
||||
18
src/Core/Render/SRenderablePtr.h
Normal file
18
src/Core/Render/SRenderablePtr.h
Normal 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
|
||||
20
src/Core/Render/SViewInfo.h
Normal file
20
src/Core/Render/SViewInfo.h
Normal 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
|
||||
|
||||
31
src/Core/Resource/CAnimSet.cpp
Normal file
31
src/Core/Resource/CAnimSet.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "CAnimSet.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
CAnimSet::CAnimSet() : CResource()
|
||||
{
|
||||
}
|
||||
|
||||
CAnimSet::~CAnimSet()
|
||||
{
|
||||
}
|
||||
|
||||
u32 CAnimSet::getNodeCount()
|
||||
{
|
||||
return nodes.size();
|
||||
}
|
||||
|
||||
TString CAnimSet::getNodeName(u32 node)
|
||||
{
|
||||
if (node >= nodes.size())
|
||||
return nodes[0].name;
|
||||
else
|
||||
return nodes[node].name;
|
||||
}
|
||||
|
||||
CModel* CAnimSet::getNodeModel(u32 node)
|
||||
{
|
||||
if (node >= nodes.size())
|
||||
return nodes[0].model;
|
||||
else
|
||||
return nodes[node].model;
|
||||
}
|
||||
36
src/Core/Resource/CAnimSet.h
Normal file
36
src/Core/Resource/CAnimSet.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef CANIMSET_H
|
||||
#define CANIMSET_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <vector>
|
||||
#include "model/CModel.h"
|
||||
#include "CResource.h"
|
||||
|
||||
// will expand later! this is where animation support will come in
|
||||
class CAnimSet : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eAnimSet)
|
||||
friend class CAnimSetLoader;
|
||||
|
||||
struct SNode
|
||||
{
|
||||
TString name;
|
||||
TResPtr<CModel> model;
|
||||
u32 skinID;
|
||||
u32 skelID;
|
||||
|
||||
SNode() { model = nullptr; }
|
||||
};
|
||||
std::vector<SNode> nodes;
|
||||
|
||||
public:
|
||||
CAnimSet();
|
||||
~CAnimSet();
|
||||
|
||||
u32 getNodeCount();
|
||||
TString getNodeName(u32 node);
|
||||
CModel* getNodeModel(u32 node);
|
||||
};
|
||||
|
||||
#endif // CCHARACTERSET_H
|
||||
138
src/Core/Resource/CAnimationParameters.cpp
Normal file
138
src/Core/Resource/CAnimationParameters.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "CAnimationParameters.h"
|
||||
#include "CAnimSet.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
#include <iostream>
|
||||
|
||||
CAnimationParameters::CAnimationParameters()
|
||||
{
|
||||
mGame = ePrime;
|
||||
mpCharSet = nullptr;
|
||||
mNodeIndex = 0;
|
||||
mUnknown1 = 0;
|
||||
mUnknown2 = 0;
|
||||
mUnknown3 = 0;
|
||||
mUnknown4 = 0;
|
||||
}
|
||||
|
||||
CAnimationParameters::CAnimationParameters(CInputStream& SCLY, EGame game)
|
||||
{
|
||||
mGame = game;
|
||||
mpCharSet = nullptr;
|
||||
mNodeIndex = 0;
|
||||
mUnknown1 = 0;
|
||||
mUnknown2 = 0;
|
||||
mUnknown3 = 0;
|
||||
mUnknown4 = 0;
|
||||
|
||||
if (game <= eEchoes)
|
||||
{
|
||||
u32 animSetID = SCLY.ReadLong();
|
||||
mNodeIndex = SCLY.ReadLong();
|
||||
mUnknown1 = SCLY.ReadLong();
|
||||
|
||||
mpCharSet = gResCache.GetResource(animSetID, "ANCS");
|
||||
}
|
||||
|
||||
else if (game <= eCorruption)
|
||||
{
|
||||
u64 charID = SCLY.ReadLongLong();
|
||||
mUnknown1 = SCLY.ReadLong();
|
||||
|
||||
mpCharSet = gResCache.GetResource(charID, "CHAR");
|
||||
}
|
||||
|
||||
else if (game == eReturns)
|
||||
{
|
||||
SCLY.Seek(-6, SEEK_CUR);
|
||||
u32 offset = SCLY.Tell();
|
||||
u32 propID = SCLY.ReadLong();
|
||||
SCLY.Seek(2, SEEK_CUR);
|
||||
|
||||
mUnknown1 = (u32) SCLY.ReadByte();
|
||||
mUnknown1 &= 0xFF;
|
||||
|
||||
if (mUnknown1 == 0x60)
|
||||
{
|
||||
u64 charID = SCLY.ReadLongLong();
|
||||
mUnknown2 = SCLY.ReadLong();
|
||||
mUnknown3 = SCLY.ReadLong();
|
||||
mUnknown4 = SCLY.ReadLong();
|
||||
|
||||
mpCharSet = gResCache.GetResource(charID, "CHAR");
|
||||
}
|
||||
|
||||
else if (mUnknown1 != 0x80)
|
||||
{
|
||||
Log::FileError(SCLY.GetSourceString(), offset,
|
||||
"Unexpected AnimationParameters byte: " + TString::HexString(mUnknown1, true, true, 2) + " (property " + TString::HexString(propID, true, true, 8) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CModel* CAnimationParameters::GetCurrentModel(s32 nodeIndex)
|
||||
{
|
||||
if (!mpCharSet) return nullptr;
|
||||
if (mpCharSet->Type() != eAnimSet) return nullptr;
|
||||
if (nodeIndex == -1) nodeIndex = mNodeIndex;
|
||||
|
||||
CAnimSet *pSet = static_cast<CAnimSet*>(mpCharSet.RawPointer());
|
||||
if (pSet->getNodeCount() <= (u32) nodeIndex) return nullptr;
|
||||
return pSet->getNodeModel(nodeIndex);
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
EGame CAnimationParameters::Version()
|
||||
{
|
||||
return mGame;
|
||||
}
|
||||
|
||||
CResource* CAnimationParameters::Resource()
|
||||
{
|
||||
return mpCharSet;
|
||||
}
|
||||
|
||||
u32 CAnimationParameters::CharacterIndex()
|
||||
{
|
||||
return mNodeIndex;
|
||||
}
|
||||
|
||||
u32 CAnimationParameters::Unknown(u32 index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return mUnknown1;
|
||||
case 1: return mUnknown2;
|
||||
case 2: return mUnknown3;
|
||||
case 3: return mUnknown4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CAnimationParameters::SetResource(CResource *pRes)
|
||||
{
|
||||
if ((pRes->Type() == eAnimSet) || (pRes->Type() == eCharacter))
|
||||
{
|
||||
mpCharSet = pRes;
|
||||
mNodeIndex = 0;
|
||||
}
|
||||
else
|
||||
Log::Error("Resource with invalid type passed to CAnimationParameters: " + pRes->Source());
|
||||
}
|
||||
|
||||
void CAnimationParameters::SetNodeIndex(u32 index)
|
||||
{
|
||||
mNodeIndex = index;
|
||||
}
|
||||
|
||||
void CAnimationParameters::SetUnknown(u32 index, u32 value)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: mUnknown1 = value;
|
||||
case 1: mUnknown2 = value;
|
||||
case 2: mUnknown3 = value;
|
||||
case 3: mUnknown4 = value;
|
||||
}
|
||||
}
|
||||
37
src/Core/Resource/CAnimationParameters.h
Normal file
37
src/Core/Resource/CAnimationParameters.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef CANIMATIONPARAMETERS_H
|
||||
#define CANIMATIONPARAMETERS_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "model/CModel.h"
|
||||
#include <Core/TResPtr.h>
|
||||
#include "EFormatVersion.h"
|
||||
|
||||
class CAnimationParameters
|
||||
{
|
||||
EGame mGame;
|
||||
TResPtr<CResource> mpCharSet;
|
||||
|
||||
u32 mNodeIndex;
|
||||
u32 mUnknown1;
|
||||
u32 mUnknown2;
|
||||
u32 mUnknown3;
|
||||
u32 mUnknown4;
|
||||
|
||||
public:
|
||||
CAnimationParameters();
|
||||
CAnimationParameters(CInputStream& SCLY, EGame game);
|
||||
CModel* GetCurrentModel(s32 nodeIndex = -1);
|
||||
|
||||
// Getters
|
||||
EGame Version();
|
||||
CResource* Resource();
|
||||
u32 CharacterIndex();
|
||||
u32 Unknown(u32 index);
|
||||
|
||||
// Setters
|
||||
void SetResource(CResource *pRes);
|
||||
void SetNodeIndex(u32 index);
|
||||
void SetUnknown(u32 index, u32 value);
|
||||
};
|
||||
|
||||
#endif // CANIMATIONPARAMETERS_H
|
||||
114
src/Core/Resource/CCollisionMesh.cpp
Normal file
114
src/Core/Resource/CCollisionMesh.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "CCollisionMesh.h"
|
||||
#include <Core/CRenderer.h>
|
||||
|
||||
CCollisionMesh::CCollisionMesh()
|
||||
{
|
||||
mVBO.SetVertexDesc(ePosition);
|
||||
mVertexCount = 0;
|
||||
mLineCount = 0;
|
||||
mFaceCount = 0;
|
||||
mBuffered = false;
|
||||
mIBO.SetPrimitiveType(GL_TRIANGLES);
|
||||
}
|
||||
|
||||
CCollisionMesh::~CCollisionMesh()
|
||||
{
|
||||
if (mBuffered)
|
||||
{
|
||||
mIBO.Clear();
|
||||
mVBO.Clear();
|
||||
mBuffered = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CCollisionMesh::BufferGL()
|
||||
{
|
||||
if (mBuffered)
|
||||
{
|
||||
mIBO.Clear();
|
||||
mVBO.Clear();
|
||||
mBuffered = false;
|
||||
}
|
||||
|
||||
// Add all the verts to our VBO, first...
|
||||
mVBO.Reserve(mCollisionVertices.size());
|
||||
for (u16 v = 0; v < mCollisionVertices.size(); v++)
|
||||
mVBO.AddVertex(CVertex(mCollisionVertices[v].Pos));
|
||||
|
||||
// Then add all the relevant indices to the IBO
|
||||
mIBO.Reserve(mCollisionFaces.size() * 3);
|
||||
for (u32 v = 0; v < mCollisionFaces.size(); v++)
|
||||
{
|
||||
u16 Verts[3];
|
||||
|
||||
CCollisionFace *Face = &mCollisionFaces[v];
|
||||
CCollisionLine *LineA = GetLine(Face->Lines[0]);
|
||||
CCollisionLine *LineB = GetLine(Face->Lines[1]);
|
||||
Verts[0] = LineA->Vertices[0];
|
||||
Verts[1] = LineA->Vertices[1];
|
||||
|
||||
// We have two vertex indices; the last one is one of the ones on line B, but we're not sure which one
|
||||
if ((LineB->Vertices[0] != Verts[0]) &&
|
||||
(LineB->Vertices[0] != Verts[1]))
|
||||
Verts[2] = LineB->Vertices[0];
|
||||
else
|
||||
Verts[2] = LineB->Vertices[1];
|
||||
|
||||
// Some faces have a property that indicates they need to be inverted
|
||||
if (!Face->Properties.Invert)
|
||||
mIBO.AddIndices(&Verts[0], 3);
|
||||
else {
|
||||
mIBO.AddIndex(Verts[2]);
|
||||
mIBO.AddIndex(Verts[1]);
|
||||
mIBO.AddIndex(Verts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer, and done
|
||||
mVBO.Buffer();
|
||||
mIBO.Buffer();
|
||||
mBuffered = true;
|
||||
}
|
||||
|
||||
void CCollisionMesh::Draw()
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
mVBO.Bind();
|
||||
mIBO.Bind();
|
||||
|
||||
glDrawElements(GL_TRIANGLES, mIBO.GetSize(), GL_UNSIGNED_SHORT, (void*) 0);
|
||||
gDrawCount++;
|
||||
mIBO.Unbind();
|
||||
mVBO.Unbind();
|
||||
}
|
||||
|
||||
void CCollisionMesh::DrawWireframe()
|
||||
{
|
||||
if (!mBuffered) BufferGL();
|
||||
|
||||
mVBO.Bind();
|
||||
mIBO.Bind();
|
||||
for (u32 f = 0; f < mFaceCount; f++)
|
||||
{
|
||||
glDrawElements(GL_LINE_LOOP, 3, GL_UNSIGNED_SHORT, (void*) (f * 6));
|
||||
gDrawCount++;
|
||||
}
|
||||
mIBO.Unbind();
|
||||
mVBO.Unbind();
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionVertex* CCollisionMesh::GetVertex(u16 index)
|
||||
{
|
||||
return &mCollisionVertices[index];
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionLine* CCollisionMesh::GetLine(u16 index)
|
||||
{
|
||||
return &mCollisionLines[index];
|
||||
}
|
||||
|
||||
CCollisionMesh::CCollisionFace* CCollisionMesh::GetFace(u16 index)
|
||||
{
|
||||
return &mCollisionFaces[index];
|
||||
}
|
||||
88
src/Core/Resource/CCollisionMesh.h
Normal file
88
src/Core/Resource/CCollisionMesh.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef CCOLLISIONMESH_H
|
||||
#define CCOLLISIONMESH_H
|
||||
|
||||
#include <Common/CAABox.h>
|
||||
#include <OpenGL/CVertexBuffer.h>
|
||||
#include <OpenGL/CIndexBuffer.h>
|
||||
#include "CResource.h"
|
||||
|
||||
class CCollisionMesh
|
||||
{
|
||||
friend class CCollisionLoader;
|
||||
|
||||
class CCollisionOctree
|
||||
{
|
||||
friend class CCollisionLoader;
|
||||
struct SOctreeNode {};
|
||||
|
||||
struct SLeaf : public SOctreeNode
|
||||
{
|
||||
CAABox AABox;
|
||||
std::vector<u16> FaceIndices;
|
||||
};
|
||||
|
||||
struct SBranch : public SOctreeNode
|
||||
{
|
||||
u16 Flags;
|
||||
SOctreeNode *pChildren[8];
|
||||
};
|
||||
|
||||
SOctreeNode* Root;
|
||||
};
|
||||
|
||||
struct SCollisionProperties
|
||||
{
|
||||
// todo: figure out what the other properties are
|
||||
bool Invert;
|
||||
};
|
||||
|
||||
class CCollisionVertex
|
||||
{
|
||||
public:
|
||||
SCollisionProperties Properties;
|
||||
CVector3f Pos;
|
||||
};
|
||||
|
||||
class CCollisionLine
|
||||
{
|
||||
public:
|
||||
SCollisionProperties Properties;
|
||||
u16 Vertices[2];
|
||||
};
|
||||
|
||||
class CCollisionFace
|
||||
{
|
||||
public:
|
||||
SCollisionProperties Properties;
|
||||
u16 Lines[3];
|
||||
};
|
||||
|
||||
CVertexBuffer mVBO;
|
||||
CIndexBuffer mIBO;
|
||||
u32 mVertexCount;
|
||||
u32 mLineCount;
|
||||
u32 mFaceCount;
|
||||
bool mBuffered;
|
||||
|
||||
CAABox mAABox;
|
||||
CCollisionOctree *mOctree;
|
||||
std::vector<u32> mFlags;
|
||||
std::vector<CCollisionVertex> mCollisionVertices;
|
||||
std::vector<CCollisionLine> mCollisionLines;
|
||||
std::vector<CCollisionFace> mCollisionFaces;
|
||||
bool mOctreeLoaded;
|
||||
|
||||
CCollisionVertex *GetVertex(u16 index);
|
||||
CCollisionLine *GetLine(u16 index);
|
||||
CCollisionFace *GetFace(u16 index);
|
||||
|
||||
public:
|
||||
CCollisionMesh();
|
||||
~CCollisionMesh();
|
||||
|
||||
void BufferGL();
|
||||
void Draw();
|
||||
void DrawWireframe();
|
||||
};
|
||||
|
||||
#endif // CCOLLISIONMESH_H
|
||||
38
src/Core/Resource/CCollisionMeshGroup.cpp
Normal file
38
src/Core/Resource/CCollisionMeshGroup.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "CCollisionMeshGroup.h"
|
||||
|
||||
CCollisionMeshGroup::CCollisionMeshGroup()
|
||||
{
|
||||
}
|
||||
|
||||
CCollisionMeshGroup::~CCollisionMeshGroup()
|
||||
{
|
||||
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
u32 CCollisionMeshGroup::NumMeshes()
|
||||
{
|
||||
return mMeshes.size();
|
||||
}
|
||||
|
||||
CCollisionMesh* CCollisionMeshGroup::MeshByIndex(u32 index)
|
||||
{
|
||||
return mMeshes[index];
|
||||
}
|
||||
|
||||
void CCollisionMeshGroup::AddMesh(CCollisionMesh *pMesh)
|
||||
{
|
||||
mMeshes.push_back(pMesh);
|
||||
}
|
||||
|
||||
void CCollisionMeshGroup::Draw()
|
||||
{
|
||||
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
|
||||
(*it)->Draw();
|
||||
}
|
||||
|
||||
void CCollisionMeshGroup::DrawWireframe()
|
||||
{
|
||||
for (auto it = mMeshes.begin(); it != mMeshes.end(); it++)
|
||||
(*it)->DrawWireframe();
|
||||
}
|
||||
25
src/Core/Resource/CCollisionMeshGroup.h
Normal file
25
src/Core/Resource/CCollisionMeshGroup.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef CCOLLISIONMESHGROUP_H
|
||||
#define CCOLLISIONMESHGROUP_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CCollisionMesh.h"
|
||||
#include <Core/TResPtr.h>
|
||||
#include <vector>
|
||||
|
||||
class CCollisionMeshGroup : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eCollisionMeshGroup)
|
||||
std::vector<CCollisionMesh*> mMeshes;
|
||||
|
||||
public:
|
||||
CCollisionMeshGroup();
|
||||
~CCollisionMeshGroup();
|
||||
|
||||
u32 NumMeshes();
|
||||
CCollisionMesh* MeshByIndex(u32 index);
|
||||
void AddMesh(CCollisionMesh *pMesh);
|
||||
void Draw();
|
||||
void DrawWireframe();
|
||||
};
|
||||
|
||||
#endif // CCOLLISIONMESHGROUP_H
|
||||
175
src/Core/Resource/CFont.cpp
Normal file
175
src/Core/Resource/CFont.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "CFont.h"
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <Common/AnimUtil.h>
|
||||
|
||||
CDynamicVertexBuffer CFont::smGlyphVertices;
|
||||
CIndexBuffer CFont::smGlyphIndices;
|
||||
bool CFont::smBuffersInitialized = false;
|
||||
|
||||
CFont::CFont() : CResource()
|
||||
{
|
||||
}
|
||||
|
||||
CFont::~CFont()
|
||||
{
|
||||
}
|
||||
|
||||
inline float PtsToFloat(s32 pt)
|
||||
{
|
||||
// This is a bit of an arbitrary number but it works
|
||||
// 1 / (1280 / 1.333333f / 2)
|
||||
return 0.00208333f * pt;
|
||||
}
|
||||
|
||||
CVector2f CFont::RenderString(const TString& String, CRenderer* /*pRenderer*/, float /*AspectRatio*/,
|
||||
CVector2f /*Position*/, CColor FillColor, CColor StrokeColor, u32 FontSize)
|
||||
{
|
||||
// WIP
|
||||
if (!smBuffersInitialized) InitBuffers();
|
||||
|
||||
// Shader setup
|
||||
CShader *pTextShader = CDrawUtil::GetTextShader();
|
||||
pTextShader->SetCurrent();
|
||||
|
||||
GLuint ModelMtxLoc = pTextShader->GetUniformLocation("ModelMtx");
|
||||
GLuint ColorLoc = pTextShader->GetUniformLocation("FontColor");
|
||||
GLuint LayerLoc = pTextShader->GetUniformLocation("RGBALayer");
|
||||
CVector4f FillColor4f = FillColor.ToVector4f();
|
||||
CVector4f StrokeColor4f = StrokeColor.ToVector4f();
|
||||
mpFontTexture->Bind(0);
|
||||
smGlyphVertices.Bind();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
// Initialize some more stuff before we start the character loop
|
||||
CVector2f PrintHead(-1.f, 1.f);
|
||||
CTransform4f PtScale = CTransform4f::ScaleMatrix(PtsToFloat(1));
|
||||
SGlyph *pPrevGlyph = nullptr;
|
||||
|
||||
float Scale;
|
||||
if (FontSize == CFONT_DEFAULT_SIZE) Scale = 1.f;
|
||||
else Scale = (float) FontSize / (mDefaultSize != 0 ? mDefaultSize : 18);
|
||||
|
||||
for (u32 iChar = 0; iChar < String.Length(); iChar++)
|
||||
{
|
||||
// Get character, check for newline
|
||||
char Char = String[iChar];
|
||||
|
||||
if (Char == '\n')
|
||||
{
|
||||
pPrevGlyph = nullptr;
|
||||
PrintHead.x = -1;
|
||||
PrintHead.y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get glyph
|
||||
auto iGlyph = mGlyphs.find(Char);
|
||||
if (iGlyph == mGlyphs.end()) continue;
|
||||
SGlyph *pGlyph = &iGlyph->second;
|
||||
|
||||
// Apply left padding and kerning
|
||||
PrintHead.x += PtsToFloat(pGlyph->LeftPadding) * Scale;
|
||||
|
||||
if (pPrevGlyph)
|
||||
{
|
||||
if (pPrevGlyph->KerningIndex != -1)
|
||||
{
|
||||
for (u32 iKern = pPrevGlyph->KerningIndex; iKern < mKerningTable.size(); iKern++)
|
||||
{
|
||||
if (mKerningTable[iKern].CharacterA != pPrevGlyph->Character) break;
|
||||
if (mKerningTable[iKern].CharacterB == String[iChar])
|
||||
{
|
||||
PrintHead.x += PtsToFloat(mKerningTable[iKern].Adjust) * Scale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a newline if this character goes over the right edge of the screen
|
||||
if (PrintHead.x + ((PtsToFloat(pGlyph->PrintAdvance) + PtsToFloat(pGlyph->RightPadding)) * Scale) > 1)
|
||||
{
|
||||
PrintHead.x = -1;
|
||||
PrintHead.y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
|
||||
|
||||
if (Char == ' ') continue;
|
||||
}
|
||||
|
||||
float XTrans = PrintHead.x;
|
||||
float YTrans = PrintHead.y + ((PtsToFloat(pGlyph->BaseOffset * 2) - PtsToFloat(mVerticalOffset * 2)) * Scale);
|
||||
|
||||
CTransform4f GlyphTransform = PtScale;
|
||||
GlyphTransform.Scale(CVector3f((float) pGlyph->Width / 2, (float) pGlyph->Height, 1.f));
|
||||
GlyphTransform.Scale(Scale);
|
||||
GlyphTransform.Translate(CVector3f(XTrans, YTrans, 0.f));
|
||||
CMatrix4f Glyph4f = GlyphTransform.ToMatrix4f();
|
||||
|
||||
// Get glyph layer
|
||||
u8 GlyphLayer = pGlyph->RGBAChannel;
|
||||
if (mTextureFormat == 3) GlyphLayer *= 2;
|
||||
else if (mTextureFormat == 8) GlyphLayer = 3;
|
||||
|
||||
// Load shader uniforms, buffer texture
|
||||
glUniformMatrix4fv(ModelMtxLoc, 1, GL_FALSE, (GLfloat*) &Glyph4f);
|
||||
smGlyphVertices.BufferAttrib(eTex0, &pGlyph->TexCoords);
|
||||
|
||||
// Draw fill
|
||||
glUniform1i(LayerLoc, GlyphLayer);
|
||||
glUniform4fv(ColorLoc, 1, &FillColor4f.x);
|
||||
smGlyphIndices.DrawElements();
|
||||
|
||||
// Draw stroke
|
||||
if ((mTextureFormat == 1) || (mTextureFormat == 3) || (mTextureFormat == 8))
|
||||
{
|
||||
u8 StrokeLayer;
|
||||
if (mTextureFormat == 1) StrokeLayer = 1;
|
||||
else if (mTextureFormat == 3) StrokeLayer = GlyphLayer + 1;
|
||||
else if (mTextureFormat == 8) StrokeLayer = GlyphLayer - 2;
|
||||
|
||||
glUniform1i(LayerLoc, StrokeLayer);
|
||||
glUniform4fv(ColorLoc, 1, &StrokeColor4f.x);
|
||||
smGlyphIndices.DrawElements();
|
||||
}
|
||||
|
||||
// Update print head
|
||||
PrintHead.x += PtsToFloat(pGlyph->PrintAdvance) * Scale;
|
||||
PrintHead.x += PtsToFloat(pGlyph->RightPadding) * Scale;
|
||||
pPrevGlyph = pGlyph;
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
return PrintHead;
|
||||
}
|
||||
|
||||
void CFont::InitBuffers()
|
||||
{
|
||||
smGlyphVertices.SetActiveAttribs(ePosition | eTex0);
|
||||
smGlyphVertices.SetVertexCount(4);
|
||||
|
||||
CVector3f Vertices[4] = {
|
||||
CVector3f( 0.f, 0.f, 0.f),
|
||||
CVector3f( 2.f, 0.f, 0.f),
|
||||
CVector3f( 0.f, -2.f, 0.f),
|
||||
CVector3f( 2.f, -2.f, 0.f)
|
||||
};
|
||||
smGlyphVertices.BufferAttrib(ePosition, Vertices);
|
||||
|
||||
CVector2f TexCoords[4] = {
|
||||
CVector2f(0.f, 0.f),
|
||||
CVector2f(1.f, 0.f),
|
||||
CVector2f(0.f, 1.f),
|
||||
CVector2f(1.f, 1.f)
|
||||
};
|
||||
smGlyphVertices.BufferAttrib(eTex0, TexCoords);
|
||||
|
||||
smGlyphIndices.Reserve(4);
|
||||
smGlyphIndices.AddIndex(0);
|
||||
smGlyphIndices.AddIndex(2);
|
||||
smGlyphIndices.AddIndex(1);
|
||||
smGlyphIndices.AddIndex(3);
|
||||
smGlyphIndices.SetPrimitiveType(GL_TRIANGLE_STRIP);
|
||||
|
||||
smBuffersInitialized = true;
|
||||
}
|
||||
72
src/Core/Resource/CFont.h
Normal file
72
src/Core/Resource/CFont.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef CFONT_H
|
||||
#define CFONT_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CTexture.h"
|
||||
#include "model/CVertex.h"
|
||||
#include <Common/types.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <OpenGL/CDynamicVertexBuffer.h>
|
||||
#include <OpenGL/CIndexBuffer.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#define CFONT_DEFAULT_SIZE -1
|
||||
|
||||
class CRenderer;
|
||||
|
||||
class CFont : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eFont)
|
||||
friend class CFontLoader;
|
||||
static CDynamicVertexBuffer smGlyphVertices; // This is the vertex buffer used to draw glyphs. It has two attributes - Pos and Tex0. Tex0 should be updated for each glyph.
|
||||
static CIndexBuffer smGlyphIndices; // This is the index buffer used to draw glyphs. It uses a triangle strip.
|
||||
static bool smBuffersInitialized; // This bool indicates whether the vertex/index buffer have been initialized. Checked at the start of RenderString().
|
||||
|
||||
u32 mUnknown; // Value at offset 0x8. Not sure what this is. Including for experimentation purposes.
|
||||
u32 mLineHeight; // Height of each line, in points
|
||||
u32 mLineMargin; // Gap between lines, in points - this is added to the line height
|
||||
u32 mVerticalOffset; // In points. This is used to reposition glyphs after the per-glyph vertical offset is applied
|
||||
u32 mDefaultSize; // In points.
|
||||
TString mFontName; // Self-explanatory
|
||||
TResPtr<CTexture> mpFontTexture; // The texture used by this font
|
||||
u32 mTextureFormat; // Indicates which layers on the texture are for what - multiple glyph layers or fill/stroke
|
||||
|
||||
struct SGlyph
|
||||
{
|
||||
u16 Character; // The UTF-16 character that this glyph corresponds to
|
||||
CVector2f TexCoords[4]; // The format only lists the min/max X/Y values; tracking absolute coordinates in memory is faster
|
||||
s32 LeftPadding; // The amount of padding applied left of this glyph, in points
|
||||
s32 RightPadding; // The amount of padding applied right of this glyph, in points
|
||||
u32 Width; // The width of the glyph, in points
|
||||
u32 Height; // The height of the glyph, in points
|
||||
u32 PrintAdvance; // How far the print head advances horizontally after printing this glyph, in points
|
||||
u32 BaseOffset; // Vertical offset for this glyph, in points; the font-wide offset is added to this
|
||||
u32 KerningIndex; // Index into the kerning table of the first kerning pair for this glyph. -1 if no pairs.
|
||||
u8 RGBAChannel; // Fonts can store multiple glyphs in the same space on different RGBA channels. This value corresponds to R, G, B, or A.
|
||||
};
|
||||
std::unordered_map<u16, SGlyph> mGlyphs;
|
||||
|
||||
struct SKerningPair
|
||||
{
|
||||
u16 CharacterA; // Left character
|
||||
u16 CharacterB; // Right character
|
||||
s32 Adjust; // The horizontal offset to apply to CharacterB if this pair is encountered, in points
|
||||
};
|
||||
std::vector<SKerningPair> mKerningTable; // The kerning table should be laid out in alphabetical order for the indices to work properly
|
||||
|
||||
|
||||
public:
|
||||
CFont();
|
||||
~CFont();
|
||||
CResource* MakeCopy(CResCache *pCopyCache);
|
||||
CVector2f RenderString(const TString& String, CRenderer *pRenderer, float AspectRatio,
|
||||
CVector2f Position = CVector2f(0,0),
|
||||
CColor FillColor = CColor::skWhite, CColor StrokeColor = CColor::skBlack,
|
||||
u32 FontSize = CFONT_DEFAULT_SIZE);
|
||||
private:
|
||||
void InitBuffers();
|
||||
};
|
||||
|
||||
#endif // CFONT_H
|
||||
180
src/Core/Resource/CGameArea.cpp
Normal file
180
src/Core/Resource/CGameArea.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#include "CGameArea.h"
|
||||
#include "script/CScriptLayer.h"
|
||||
#include <Core/CRenderer.h>
|
||||
|
||||
CGameArea::CGameArea() : CResource()
|
||||
{
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
mTerrainMerged = false;
|
||||
mMaterialSet = nullptr;
|
||||
mpGeneratorLayer = nullptr;
|
||||
mCollision = nullptr;
|
||||
}
|
||||
|
||||
CGameArea::~CGameArea()
|
||||
{
|
||||
ClearTerrain();
|
||||
|
||||
delete mCollision;
|
||||
delete mpGeneratorLayer;
|
||||
|
||||
for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++)
|
||||
delete mScriptLayers[iSCLY];
|
||||
|
||||
for (u32 lyr = 0; lyr < mLightLayers.size(); lyr++)
|
||||
for (u32 lit = 0; lit < mLightLayers[lyr].size(); lit++)
|
||||
delete mLightLayers[lyr][lit];
|
||||
}
|
||||
|
||||
void CGameArea::AddWorldModel(CModel *mdl)
|
||||
{
|
||||
mTerrainModels.push_back(mdl);
|
||||
mVertexCount += mdl->GetVertexCount();
|
||||
mTriangleCount += mdl->GetTriangleCount();
|
||||
mAABox.ExpandBounds(mdl->AABox());
|
||||
}
|
||||
|
||||
void CGameArea::MergeTerrain()
|
||||
{
|
||||
if (mTerrainMerged) return;
|
||||
|
||||
// Nothing really complicated here - iterate through every terrain submesh, add each to a static model
|
||||
for (u32 iMdl = 0; iMdl < mTerrainModels.size(); iMdl++)
|
||||
{
|
||||
CModel *pMdl = mTerrainModels[iMdl];
|
||||
u32 SubmeshCount = pMdl->GetSurfaceCount();
|
||||
|
||||
for (u32 iSurf = 0; iSurf < SubmeshCount; iSurf++)
|
||||
{
|
||||
SSurface *pSurf = pMdl->GetSurface(iSurf);
|
||||
CMaterial *pMat = mMaterialSet->MaterialByIndex(pSurf->MaterialID);
|
||||
|
||||
bool newMat = true;
|
||||
for (std::vector<CStaticModel*>::iterator it = mStaticTerrainModels.begin(); it != mStaticTerrainModels.end(); it++)
|
||||
{
|
||||
if ((*it)->GetMaterial() == pMat)
|
||||
{
|
||||
// When we append a new submesh to an existing static model, we bump it to the back of the vector.
|
||||
// This is because mesh ordering actually matters sometimes
|
||||
// (particularly with multi-layered transparent meshes)
|
||||
// so we need to at least try to maintain it.
|
||||
// This is maybe not the most efficient way to do this, but it works.
|
||||
CStaticModel *pStatic = *it;
|
||||
pStatic->AddSurface(pSurf);
|
||||
mStaticTerrainModels.erase(it);
|
||||
mStaticTerrainModels.push_back(pStatic);
|
||||
newMat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newMat)
|
||||
{
|
||||
CStaticModel *pStatic = new CStaticModel(pMat);
|
||||
pStatic->AddSurface(pSurf);
|
||||
mStaticTerrainModels.push_back(pStatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameArea::ClearTerrain()
|
||||
{
|
||||
for (u32 t = 0; t < mTerrainModels.size(); t++)
|
||||
delete mTerrainModels[t];
|
||||
mTerrainModels.clear();
|
||||
|
||||
for (u32 s = 0; s < mStaticTerrainModels.size(); s++)
|
||||
delete mStaticTerrainModels[s];
|
||||
mStaticTerrainModels.clear();
|
||||
|
||||
if (mMaterialSet) delete mMaterialSet;
|
||||
|
||||
mVertexCount = 0;
|
||||
mTriangleCount = 0;
|
||||
mTerrainMerged = false;
|
||||
mAABox = CAABox::skInfinite;
|
||||
}
|
||||
|
||||
void CGameArea::ClearScriptLayers()
|
||||
{
|
||||
for (auto it = mScriptLayers.begin(); it != mScriptLayers.end(); it++)
|
||||
delete *it;
|
||||
mScriptLayers.clear();
|
||||
delete mpGeneratorLayer;
|
||||
mpGeneratorLayer = nullptr;
|
||||
}
|
||||
|
||||
CTransform4f CGameArea::GetTransform()
|
||||
{
|
||||
return mTransform;
|
||||
}
|
||||
|
||||
u32 CGameArea::GetTerrainModelCount()
|
||||
{
|
||||
return mTerrainModels.size();
|
||||
}
|
||||
|
||||
u32 CGameArea::GetStaticModelCount()
|
||||
{
|
||||
return mStaticTerrainModels.size();
|
||||
}
|
||||
|
||||
CModel* CGameArea::GetTerrainModel(u32 mdl)
|
||||
{
|
||||
return mTerrainModels[mdl];
|
||||
}
|
||||
|
||||
CStaticModel* CGameArea::GetStaticModel(u32 mdl)
|
||||
{
|
||||
return mStaticTerrainModels[mdl];
|
||||
}
|
||||
|
||||
CCollisionMeshGroup* CGameArea::GetCollision()
|
||||
{
|
||||
return mCollision;
|
||||
}
|
||||
|
||||
u32 CGameArea::GetScriptLayerCount()
|
||||
{
|
||||
return mScriptLayers.size();
|
||||
}
|
||||
|
||||
CScriptLayer* CGameArea::GetScriptLayer(u32 index)
|
||||
{
|
||||
return mScriptLayers[index];
|
||||
}
|
||||
|
||||
CScriptLayer* CGameArea::GetGeneratorLayer()
|
||||
{
|
||||
return mpGeneratorLayer;
|
||||
}
|
||||
|
||||
CScriptObject* CGameArea::GetInstanceByID(u32 InstanceID)
|
||||
{
|
||||
auto it = mObjectMap.find(InstanceID);
|
||||
if (it != mObjectMap.end()) return it->second;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
u32 CGameArea::GetLightLayerCount()
|
||||
{
|
||||
return mLightLayers.size();
|
||||
}
|
||||
|
||||
u32 CGameArea::GetLightCount(u32 layer)
|
||||
{
|
||||
if (mLightLayers.empty()) return 0;
|
||||
return mLightLayers[layer].size();
|
||||
}
|
||||
|
||||
CLight* CGameArea::GetLight(u32 layer, u32 light)
|
||||
{
|
||||
return mLightLayers[layer][light];
|
||||
}
|
||||
|
||||
CAABox CGameArea::AABox()
|
||||
{
|
||||
return mAABox;
|
||||
}
|
||||
68
src/Core/Resource/CGameArea.h
Normal file
68
src/Core/Resource/CGameArea.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef CGAMEAREA_H
|
||||
#define CGAMEAREA_H
|
||||
|
||||
#include "CCollisionMeshGroup.h"
|
||||
#include "CLight.h"
|
||||
#include "CMaterialSet.h"
|
||||
#include "model/CModel.h"
|
||||
#include "model/CStaticModel.h"
|
||||
#include "CResource.h"
|
||||
#include <Common/types.h>
|
||||
#include <Common/CTransform4f.h>
|
||||
#include <unordered_map>
|
||||
|
||||
class CScriptLayer;
|
||||
class CScriptObject;
|
||||
|
||||
class CGameArea : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eArea)
|
||||
friend class CAreaLoader;
|
||||
|
||||
u32 mVertexCount;
|
||||
u32 mTriangleCount;
|
||||
bool mTerrainMerged;
|
||||
CTransform4f mTransform;
|
||||
CAABox mAABox;
|
||||
|
||||
// Geometry
|
||||
CMaterialSet *mMaterialSet;
|
||||
std::vector<CModel*> mTerrainModels; // TerrainModels is the original version of each model; this is used by the editor (bounding box checks, material editing, etc)
|
||||
std::vector<CStaticModel*> mStaticTerrainModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
|
||||
// Script
|
||||
std::vector<CScriptLayer*> mScriptLayers;
|
||||
CScriptLayer *mpGeneratorLayer;
|
||||
std::unordered_map<u32, CScriptObject*> mObjectMap;
|
||||
|
||||
// Collision
|
||||
CCollisionMeshGroup *mCollision;
|
||||
// Lights
|
||||
std::vector<std::vector<CLight*>> mLightLayers;
|
||||
|
||||
public:
|
||||
CGameArea();
|
||||
~CGameArea();
|
||||
|
||||
void AddWorldModel(CModel *mdl);
|
||||
void MergeTerrain();
|
||||
void ClearTerrain();
|
||||
void ClearScriptLayers();
|
||||
|
||||
// Getters
|
||||
CTransform4f GetTransform();
|
||||
u32 GetTerrainModelCount();
|
||||
u32 GetStaticModelCount();
|
||||
CModel* GetTerrainModel(u32 mdl);
|
||||
CStaticModel* GetStaticModel(u32 mdl);
|
||||
CCollisionMeshGroup* GetCollision();
|
||||
u32 GetScriptLayerCount();
|
||||
CScriptLayer* GetScriptLayer(u32 index);
|
||||
CScriptLayer* GetGeneratorLayer();
|
||||
CScriptObject* GetInstanceByID(u32 InstanceID);
|
||||
u32 GetLightLayerCount();
|
||||
u32 GetLightCount(u32 layer);
|
||||
CLight* GetLight(u32 layer, u32 light);
|
||||
CAABox AABox();
|
||||
};
|
||||
|
||||
#endif // CGAMEAREA_H
|
||||
283
src/Core/Resource/CLight.cpp
Normal file
283
src/Core/Resource/CLight.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "CLight.h"
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
#include <Core/CGraphics.h>
|
||||
|
||||
#define CLIGHT_NO_RADIUS 0x40
|
||||
#define CLIGHT_NO_INTENSITY 0x80
|
||||
|
||||
CLight::CLight()
|
||||
{
|
||||
mPosition = skDefaultLightPos;
|
||||
mDirection = skDefaultLightDir;
|
||||
mDistAttenCoefficients = CVector3f(0.f, 1.f, 0.f);
|
||||
mAngleAttenCoefficients = CVector3f(0.f, 1.f, 0.f);
|
||||
mCachedRadius = 0.f;
|
||||
mCachedIntensity = 0.f;
|
||||
mDirtyFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
|
||||
}
|
||||
|
||||
// ************ DATA MANIPULATION ************
|
||||
|
||||
// This function is reverse engineered from the kiosk demo's code
|
||||
float CLight::CalculateRadius() const
|
||||
{
|
||||
if ((mDistAttenCoefficients.y >= FLT_EPSILON) ||
|
||||
(mDistAttenCoefficients.z >= FLT_EPSILON))
|
||||
{
|
||||
float Intensity = GetIntensity();
|
||||
|
||||
if (mDistAttenCoefficients.z > FLT_EPSILON)
|
||||
{
|
||||
if (Intensity <= FLT_EPSILON)
|
||||
return 0.f;
|
||||
|
||||
float IntensityMod = (Intensity * 5.f / 255.f * mDistAttenCoefficients.z);
|
||||
return sqrt(Intensity / IntensityMod);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (mDistAttenCoefficients.y <= FLT_EPSILON)
|
||||
return 0.f;
|
||||
|
||||
float IntensityMod = (Intensity * 5.f) / 255.f;
|
||||
if (IntensityMod < 0.2f)
|
||||
IntensityMod = 0.2f;
|
||||
|
||||
return Intensity / (IntensityMod * mDistAttenCoefficients.y);
|
||||
}
|
||||
}
|
||||
|
||||
else return 3000000000000000000000000000000000000.f;
|
||||
}
|
||||
|
||||
// This function is also reverse engineered from the kiosk demo's code
|
||||
float CLight::CalculateIntensity() const
|
||||
{
|
||||
float Multiplier = (mType == eCustom) ? mAngleAttenCoefficients.x : 1.0f;
|
||||
float ColorR = float(mColor.r) / 255.f;
|
||||
float ColorG = float(mColor.g) / 255.f;
|
||||
float ColorB = float(mColor.b) / 255.f;
|
||||
|
||||
// Get the color component with the greatest numeric value
|
||||
float Greatest = (ColorG >= ColorB) ? ColorG : ColorB;
|
||||
Greatest = (ColorR >= Greatest) ? ColorR : Greatest;
|
||||
|
||||
return Greatest * Multiplier;
|
||||
}
|
||||
|
||||
// As is this one... partly
|
||||
CVector3f CLight::CalculateSpotAngleAtten()
|
||||
{
|
||||
if (mType != eSpot) return CVector3f(1.f, 0.f, 0.f);
|
||||
|
||||
if ((mSpotCutoff < 0.f) || (mSpotCutoff > 90.f))
|
||||
return CVector3f(1.f, 0.f, 0.f);
|
||||
|
||||
float RadianCutoff = (mSpotCutoff * 3.1415927f) / 180.f;
|
||||
float RadianCosine = cosf(RadianCutoff);
|
||||
float InvCosine = 1.f - RadianCosine;
|
||||
|
||||
return CVector3f(0.f, -RadianCosine / InvCosine, 1.f / InvCosine);
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
ELightType CLight::GetType() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
u32 CLight::GetLayerIndex() const
|
||||
{
|
||||
return mLayerIndex;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetPosition() const
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetDirection() const
|
||||
{
|
||||
return mDirection;
|
||||
}
|
||||
|
||||
CColor CLight::GetColor() const
|
||||
{
|
||||
return mColor;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetDistAttenuation() const
|
||||
{
|
||||
return mDistAttenCoefficients;
|
||||
}
|
||||
|
||||
CVector3f CLight::GetAngleAttenuation() const
|
||||
{
|
||||
return mAngleAttenCoefficients;
|
||||
}
|
||||
|
||||
float CLight::GetRadius() const
|
||||
{
|
||||
if (mDirtyFlags & CLIGHT_NO_RADIUS)
|
||||
{
|
||||
mCachedRadius = CalculateRadius();
|
||||
mDirtyFlags &= ~CLIGHT_NO_RADIUS;
|
||||
}
|
||||
|
||||
return mCachedRadius * 2;
|
||||
}
|
||||
|
||||
float CLight::GetIntensity() const
|
||||
{
|
||||
if (mDirtyFlags & CLIGHT_NO_INTENSITY)
|
||||
{
|
||||
mCachedIntensity = CalculateIntensity();
|
||||
mDirtyFlags &= ~CLIGHT_NO_INTENSITY;
|
||||
}
|
||||
|
||||
return mCachedIntensity;
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CLight::SetLayer(u32 index)
|
||||
{
|
||||
mLayerIndex = index;
|
||||
}
|
||||
|
||||
void CLight::SetPosition(const CVector3f& Position)
|
||||
{
|
||||
mPosition = Position;
|
||||
}
|
||||
|
||||
void CLight::SetDirection(const CVector3f& Direction)
|
||||
{
|
||||
mDirection = Direction;
|
||||
}
|
||||
|
||||
void CLight::SetColor(const CColor& Color)
|
||||
{
|
||||
mColor = Color;
|
||||
mDirtyFlags = CLIGHT_NO_RADIUS | CLIGHT_NO_INTENSITY;
|
||||
}
|
||||
|
||||
void CLight::SetSpotCutoff(float Cutoff)
|
||||
{
|
||||
mSpotCutoff = Cutoff * 0.5f;
|
||||
CalculateSpotAngleAtten();
|
||||
}
|
||||
|
||||
void CLight::SetDistAtten(float DistCoefA, float DistCoefB, float DistCoefC)
|
||||
{
|
||||
mDistAttenCoefficients.x = DistCoefA;
|
||||
mDistAttenCoefficients.y = DistCoefB;
|
||||
mDistAttenCoefficients.z = DistCoefC;
|
||||
}
|
||||
|
||||
void CLight::SetAngleAtten(float AngleCoefA, float AngleCoefB, float AngleCoefC)
|
||||
{
|
||||
mAngleAttenCoefficients.x = AngleCoefA;
|
||||
mAngleAttenCoefficients.y = AngleCoefB;
|
||||
mAngleAttenCoefficients.z = AngleCoefC;
|
||||
}
|
||||
|
||||
// ************ OTHER ************
|
||||
void CLight::Load() const
|
||||
{
|
||||
u8 Index = (u8) CGraphics::sNumLights;
|
||||
if (Index >= 8) return;
|
||||
|
||||
CGraphics::SLightBlock::SGXLight *Light = &CGraphics::sLightBlock.Lights[Index];
|
||||
CVector3f PosView = CGraphics::sMVPBlock.ViewMatrix * mPosition;
|
||||
CVector3f DirView = CGraphics::sMVPBlock.ViewMatrix * mDirection;
|
||||
|
||||
switch (mType)
|
||||
{
|
||||
case eLocalAmbient:
|
||||
// LocalAmbient is already accounted for in CGraphics::sAreaAmbientColor
|
||||
return;
|
||||
case eDirectional:
|
||||
Light->Position = CVector4f(-mDirection * 1048576.f, 1.f);
|
||||
Light->Direction = CVector4f(mDirection, 0.f);
|
||||
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
|
||||
Light->DistAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
|
||||
Light->AngleAtten = CVector4f(1.f, 0.f, 0.f, 0.f);
|
||||
break;
|
||||
case eSpot:
|
||||
Light->Position = CVector4f(mPosition, 1.f);
|
||||
Light->Direction = CVector4f(mDirection, 0.f);
|
||||
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
|
||||
Light->DistAtten = mDistAttenCoefficients;
|
||||
Light->AngleAtten = mAngleAttenCoefficients;
|
||||
break;
|
||||
case eCustom:
|
||||
Light->Position = CVector4f(mPosition, 1.f);
|
||||
Light->Direction = CVector4f(mDirection, 0.f);
|
||||
Light->Color = mColor.ToVector4f() * CGraphics::sWorldLightMultiplier;
|
||||
Light->DistAtten = mDistAttenCoefficients;
|
||||
Light->AngleAtten = mAngleAttenCoefficients;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
CGraphics::sNumLights++;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CLight* CLight::BuildLocalAmbient(const CVector3f& Position, const CColor& Color)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eLocalAmbient;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = skDefaultLightDir;
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = 0.f;
|
||||
return Light;
|
||||
}
|
||||
|
||||
CLight* CLight::BuildDirectional(const CVector3f& Position, const CVector3f& Direction, const CColor& Color)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eDirectional;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = Direction;
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = 0.f;
|
||||
return Light;
|
||||
}
|
||||
|
||||
CLight* CLight::BuildSpot(const CVector3f& Position, const CVector3f& Direction, const CColor& Color, float Cutoff)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eSpot;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = -Direction.Normalized();
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = Cutoff * 0.5f;
|
||||
Light->mAngleAttenCoefficients = Light->CalculateSpotAngleAtten();
|
||||
return Light;
|
||||
}
|
||||
|
||||
CLight* CLight::BuildCustom(const CVector3f& Position, const CVector3f& Direction, const CColor& Color,
|
||||
float DistAttenA, float DistAttenB, float DistAttenC,
|
||||
float AngleAttenA, float AngleAttenB, float AngleAttenC)
|
||||
{
|
||||
CLight *Light = new CLight;
|
||||
Light->mType = eCustom;
|
||||
Light->mPosition = Position;
|
||||
Light->mDirection = Direction;
|
||||
Light->mColor = Color;
|
||||
Light->mSpotCutoff = 0.f;
|
||||
Light->mDistAttenCoefficients.x = DistAttenA;
|
||||
Light->mDistAttenCoefficients.y = DistAttenB;
|
||||
Light->mDistAttenCoefficients.z = DistAttenC;
|
||||
Light->mAngleAttenCoefficients.x = AngleAttenA;
|
||||
Light->mAngleAttenCoefficients.y = AngleAttenB;
|
||||
Light->mAngleAttenCoefficients.z = AngleAttenC * AngleAttenC;
|
||||
return Light;
|
||||
}
|
||||
|
||||
// ************ CONSTANTS ************
|
||||
const CVector3f CLight::skDefaultLightPos(0.f, 0.f, 0.f);
|
||||
const CVector3f CLight::skDefaultLightDir(0.f,-1.f, 0.f);
|
||||
81
src/Core/Resource/CLight.h
Normal file
81
src/Core/Resource/CLight.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef CLIGHT_H
|
||||
#define CLIGHT_H
|
||||
|
||||
#include <FileIO/CInputStream.h>
|
||||
#include <Common/CColor.h>
|
||||
#include <Common/CVector3f.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
/* CLight is currently heavily based on the lights system from Metroid Prime,
|
||||
* including code reverse engineered from the game's executable. Not yet sure
|
||||
* how much needs to be modified to properly support DKCR. */
|
||||
enum ELightType
|
||||
{
|
||||
eLocalAmbient = 0,
|
||||
eDirectional = 1,
|
||||
eSpot = 3,
|
||||
eCustom = 2
|
||||
};
|
||||
|
||||
class CLight
|
||||
{
|
||||
ELightType mType;
|
||||
u32 mLayerIndex;
|
||||
CVector3f mPosition;
|
||||
CVector3f mDirection;
|
||||
CColor mColor;
|
||||
float mSpotCutoff;
|
||||
CVector3f mDistAttenCoefficients;
|
||||
CVector3f mAngleAttenCoefficients;
|
||||
|
||||
mutable float mCachedRadius;
|
||||
mutable float mCachedIntensity;
|
||||
mutable u8 mDirtyFlags;
|
||||
|
||||
public:
|
||||
CLight();
|
||||
|
||||
private:
|
||||
// Data Manipulation
|
||||
float CalculateRadius() const;
|
||||
float CalculateIntensity() const;
|
||||
CVector3f CalculateSpotAngleAtten();
|
||||
|
||||
public:
|
||||
// Getters
|
||||
ELightType GetType() const;
|
||||
u32 GetLayerIndex() const;
|
||||
CVector3f GetPosition() const;
|
||||
CVector3f GetDirection() const;
|
||||
CColor GetColor() const;
|
||||
CVector3f GetDistAttenuation() const;
|
||||
CVector3f GetAngleAttenuation() const;
|
||||
float GetRadius() const;
|
||||
float GetIntensity() const;
|
||||
|
||||
// Setters
|
||||
void SetLayer(u32 index);
|
||||
void SetPosition(const CVector3f& Position);
|
||||
void SetDirection(const CVector3f& Direction);
|
||||
void SetColor(const CColor& Color);
|
||||
void SetSpotCutoff(float Cutoff);
|
||||
void SetDistAtten(float DistCoefA, float DistCoefB, float DistCoefC);
|
||||
void SetAngleAtten(float AngleCoefA, float AngleCoefB, float AngleCoefC);
|
||||
|
||||
// Other
|
||||
void Load() const;
|
||||
|
||||
// Static
|
||||
static CLight* BuildLocalAmbient(const CVector3f& Position, const CColor& Color);
|
||||
static CLight* BuildDirectional(const CVector3f& Position, const CVector3f& Direction, const CColor& Color);
|
||||
static CLight* BuildSpot(const CVector3f& Position, const CVector3f& Direction, const CColor& Color, float Cutoff);
|
||||
static CLight* BuildCustom(const CVector3f& Position, const CVector3f& Direction, const CColor& Color,
|
||||
float DistAttenA, float DistAttenB, float DistAttenC,
|
||||
float AngleAttenA, float AngleAttenB, float AngleAttenC);
|
||||
|
||||
// Constants
|
||||
static const CVector3f skDefaultLightPos;
|
||||
static const CVector3f skDefaultLightDir;
|
||||
};
|
||||
|
||||
#endif // CLIGHT_H
|
||||
350
src/Core/Resource/CMaterial.cpp
Normal file
350
src/Core/Resource/CMaterial.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
#include "CMaterial.h"
|
||||
#include <Common/CHashFNV1A.h>
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <Core/CRenderer.h>
|
||||
#include <Core/CResCache.h>
|
||||
#include <OpenGL/GLCommon.h>
|
||||
#include <OpenGL/CShaderGenerator.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <gl/glew.h>
|
||||
|
||||
u64 CMaterial::sCurrentMaterial = 0;
|
||||
CColor CMaterial::sCurrentTint = CColor::skWhite;
|
||||
|
||||
CMaterial::CMaterial()
|
||||
{
|
||||
mpShader = nullptr;
|
||||
mShaderStatus = eNoShader;
|
||||
mRecalcHash = true;
|
||||
mEnableBloom = false;
|
||||
mVersion = eUnknownVersion;
|
||||
mOptions = eNoSettings;
|
||||
mVtxDesc = eNoAttributes;
|
||||
mBlendSrcFac = GL_ONE;
|
||||
mBlendDstFac = GL_ZERO;
|
||||
mLightingEnabled = true;
|
||||
mEchoesUnknownA = 0;
|
||||
mEchoesUnknownB = 0;
|
||||
mpIndirectTexture = nullptr;
|
||||
}
|
||||
|
||||
CMaterial::CMaterial(EGame version, EVertexDescription vtxDesc)
|
||||
{
|
||||
mpShader = nullptr;
|
||||
mShaderStatus = eNoShader;
|
||||
mRecalcHash = true;
|
||||
mEnableBloom = (version == eCorruption);
|
||||
mVersion = version;
|
||||
mOptions = eDepthWrite;
|
||||
mVtxDesc = vtxDesc;
|
||||
mBlendSrcFac = GL_ONE;
|
||||
mBlendDstFac = GL_ZERO;
|
||||
mLightingEnabled = true;
|
||||
mEchoesUnknownA = 0;
|
||||
mEchoesUnknownB = 0;
|
||||
mpIndirectTexture = nullptr;
|
||||
}
|
||||
|
||||
CMaterial::~CMaterial()
|
||||
{
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
delete mPasses[iPass];
|
||||
|
||||
delete mpShader;
|
||||
}
|
||||
|
||||
CMaterial* CMaterial::Clone()
|
||||
{
|
||||
CMaterial *pOut = new CMaterial();
|
||||
pOut->mName = mName;
|
||||
pOut->mEnableBloom = mEnableBloom;
|
||||
pOut->mVersion = mVersion;
|
||||
pOut->mOptions = mOptions;
|
||||
pOut->mVtxDesc = mVtxDesc;
|
||||
for (u32 iKonst = 0; iKonst < 4; iKonst++)
|
||||
pOut->mKonstColors[iKonst] = mKonstColors[iKonst];
|
||||
pOut->mBlendSrcFac = mBlendSrcFac;
|
||||
pOut->mBlendDstFac = mBlendDstFac;
|
||||
pOut->mLightingEnabled = mLightingEnabled;
|
||||
pOut->mEchoesUnknownA = mEchoesUnknownA;
|
||||
pOut->mEchoesUnknownB = mEchoesUnknownB;
|
||||
pOut->mpIndirectTexture = mpIndirectTexture;
|
||||
|
||||
pOut->mPasses.resize(mPasses.size());
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
pOut->mPasses[iPass] = mPasses[iPass]->Clone(pOut);
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
void CMaterial::GenerateShader()
|
||||
{
|
||||
delete mpShader;
|
||||
mpShader = CShaderGenerator::GenerateShader(*this);
|
||||
|
||||
if (!mpShader->IsValidProgram()) mShaderStatus = eShaderFailed;
|
||||
else mShaderStatus = eShaderExists;
|
||||
}
|
||||
|
||||
bool CMaterial::SetCurrent(ERenderOptions Options)
|
||||
{
|
||||
// Bind textures
|
||||
const char *skpSamplers[8] = {
|
||||
"Texture0", "Texture1", "Texture2", "Texture3",
|
||||
"Texture4", "Texture5", "Texture6", "Texture7"
|
||||
};
|
||||
|
||||
// Skip material setup if the currently bound material is identical
|
||||
if (sCurrentMaterial != HashParameters())
|
||||
{
|
||||
// Shader setup
|
||||
if (mShaderStatus == eNoShader) GenerateShader();
|
||||
mpShader->SetCurrent();
|
||||
|
||||
if (mShaderStatus == eShaderFailed)
|
||||
return false;
|
||||
|
||||
// Set RGB blend equation - force to ZERO/ONE if alpha is disabled
|
||||
GLenum srcRGB, dstRGB, srcAlpha, dstAlpha;
|
||||
|
||||
if (Options & eNoAlpha) {
|
||||
srcRGB = GL_ONE;
|
||||
dstRGB = GL_ZERO;
|
||||
} else {
|
||||
srcRGB = mBlendSrcFac;
|
||||
dstRGB = mBlendDstFac;
|
||||
}
|
||||
|
||||
// Set alpha blend equation
|
||||
bool AlphaBlended = ((mBlendSrcFac == GL_SRC_ALPHA) && (mBlendDstFac == GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
if ((mEnableBloom) && (Options & eEnableBloom) && (!AlphaBlended)) {
|
||||
srcAlpha = mBlendSrcFac;
|
||||
dstAlpha = mBlendDstFac;
|
||||
} else {
|
||||
srcAlpha = GL_ZERO;
|
||||
dstAlpha = GL_ZERO;
|
||||
}
|
||||
|
||||
glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
|
||||
// Set konst inputs
|
||||
for (u32 iKonst = 0; iKonst < 4; iKonst++)
|
||||
CGraphics::sPixelBlock.Konst[iKonst] = mKonstColors[iKonst].ToVector4f();
|
||||
|
||||
// Set color channels
|
||||
// COLOR0_Amb is initialized by the node instead of by the material
|
||||
CGraphics::sVertexBlock.COLOR0_Mat = CColor::skWhite.ToVector4f();
|
||||
|
||||
// Set depth write - force on if alpha is disabled (lots of weird depth issues otherwise)
|
||||
if ((mOptions & eDepthWrite) || (Options & eNoAlpha)) glDepthMask(GL_TRUE);
|
||||
else glDepthMask(GL_FALSE);
|
||||
|
||||
// Load uniforms
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
mPasses[iPass]->SetAnimCurrent(Options, iPass);
|
||||
|
||||
sCurrentMaterial = HashParameters();
|
||||
}
|
||||
|
||||
// If the passes are otherwise the same, update UV anims that use the model matrix
|
||||
else
|
||||
{
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
{
|
||||
EUVAnimMode mode = mPasses[iPass]->AnimMode();
|
||||
|
||||
if ((mode == eInverseMV) || (mode == eInverseMVTranslated) ||
|
||||
(mode == eModelMatrix) || (mode == eSimpleMode))
|
||||
mPasses[iPass]->SetAnimCurrent(Options, iPass);
|
||||
}
|
||||
}
|
||||
|
||||
// Bind textures
|
||||
CShader *pShader = CShader::CurrentShader();
|
||||
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
{
|
||||
mPasses[iPass]->LoadTexture(iPass);
|
||||
GLint sampler = pShader->GetUniformLocation(skpSamplers[iPass]);
|
||||
glUniform1i(sampler, iPass);
|
||||
}
|
||||
|
||||
// Bind num lights
|
||||
GLuint NumLightsLoc = pShader->GetUniformLocation("NumLights");
|
||||
glUniform1i(NumLightsLoc, CGraphics::sNumLights);
|
||||
|
||||
// Update shader blocks
|
||||
CGraphics::UpdateVertexBlock();
|
||||
CGraphics::UpdatePixelBlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 CMaterial::HashParameters()
|
||||
{
|
||||
if (mRecalcHash)
|
||||
{
|
||||
CHashFNV1A Hash;
|
||||
Hash.Init64();
|
||||
|
||||
Hash.HashLong(mVersion);
|
||||
Hash.HashLong(mOptions);
|
||||
Hash.HashLong(mVtxDesc);
|
||||
Hash.HashData(mKonstColors, sizeof(CColor) * 4);
|
||||
Hash.HashLong(mBlendSrcFac);
|
||||
Hash.HashLong(mBlendDstFac);
|
||||
Hash.HashByte(mLightingEnabled);
|
||||
Hash.HashLong(mEchoesUnknownA);
|
||||
Hash.HashLong(mEchoesUnknownB);
|
||||
|
||||
for (u32 iPass = 0; iPass < mPasses.size(); iPass++)
|
||||
mPasses[iPass]->HashParameters(Hash);
|
||||
|
||||
mParametersHash = Hash.GetHash64();
|
||||
mRecalcHash = false;
|
||||
}
|
||||
|
||||
return mParametersHash;
|
||||
}
|
||||
|
||||
void CMaterial::Update()
|
||||
{
|
||||
mRecalcHash = true;
|
||||
mShaderStatus = eNoShader;
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
TString CMaterial::Name() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
EGame CMaterial::Version() const
|
||||
{
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
CMaterial::EMaterialOptions CMaterial::Options() const
|
||||
{
|
||||
return mOptions;
|
||||
}
|
||||
|
||||
EVertexDescription CMaterial::VtxDesc() const
|
||||
{
|
||||
return mVtxDesc;
|
||||
}
|
||||
|
||||
GLenum CMaterial::BlendSrcFac() const {
|
||||
return mBlendSrcFac;
|
||||
}
|
||||
|
||||
GLenum CMaterial::BlendDstFac() const {
|
||||
return mBlendDstFac;
|
||||
}
|
||||
|
||||
CColor CMaterial::Konst(u32 KIndex) const
|
||||
{
|
||||
if (KIndex > 3) return CColor::skTransparentBlack;
|
||||
else return mKonstColors[KIndex];
|
||||
}
|
||||
|
||||
CTexture* CMaterial::IndTexture() const
|
||||
{
|
||||
return mpIndirectTexture;
|
||||
}
|
||||
|
||||
bool CMaterial::IsLightingEnabled() const
|
||||
{
|
||||
return mLightingEnabled;
|
||||
}
|
||||
|
||||
u32 CMaterial::EchoesUnknownA() const
|
||||
{
|
||||
return mEchoesUnknownA;
|
||||
}
|
||||
|
||||
u32 CMaterial::EchoesUnknownB() const
|
||||
{
|
||||
return mEchoesUnknownB;
|
||||
}
|
||||
|
||||
u32 CMaterial::PassCount() const
|
||||
{
|
||||
return mPasses.size();
|
||||
}
|
||||
|
||||
CMaterialPass* CMaterial::Pass(u32 PassIndex) const
|
||||
{
|
||||
return mPasses[PassIndex];
|
||||
}
|
||||
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CMaterial::SetName(const TString& name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
void CMaterial::SetOptions(EMaterialOptions Options)
|
||||
{
|
||||
mOptions = Options;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetVertexDescription(EVertexDescription desc)
|
||||
{
|
||||
mVtxDesc = desc;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetBlendMode(GLenum SrcFac, GLenum DstFac)
|
||||
{
|
||||
mBlendSrcFac = SrcFac;
|
||||
mBlendDstFac = DstFac;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetKonst(CColor& Konst, u32 KIndex)
|
||||
{
|
||||
mKonstColors[KIndex] = Konst;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetIndTexture(CTexture *pTex)
|
||||
{
|
||||
mpIndirectTexture = pTex;
|
||||
}
|
||||
|
||||
void CMaterial::SetLightingEnabled(bool Enabled)
|
||||
{
|
||||
mLightingEnabled = Enabled;
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
void CMaterial::SetNumPasses(u32 NumPasses)
|
||||
{
|
||||
if (NumPasses < mPasses.size())
|
||||
{
|
||||
for (u32 iPass = NumPasses; iPass < mPasses.size(); iPass++)
|
||||
delete mPasses[iPass];
|
||||
}
|
||||
|
||||
u32 OldCount = mPasses.size();
|
||||
mPasses.resize(NumPasses);
|
||||
|
||||
if (NumPasses > OldCount)
|
||||
{
|
||||
for (u32 iPass = OldCount; iPass < NumPasses; iPass++)
|
||||
mPasses[iPass] = new CMaterialPass(this);
|
||||
}
|
||||
|
||||
mRecalcHash = true;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
void CMaterial::KillCachedMaterial()
|
||||
{
|
||||
sCurrentMaterial = 0;
|
||||
}
|
||||
113
src/Core/Resource/CMaterial.h
Normal file
113
src/Core/Resource/CMaterial.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#ifndef MATERIAL_H
|
||||
#define MATERIAL_H
|
||||
|
||||
#include "CMaterialPass.h"
|
||||
#include "CTexture.h"
|
||||
#include "EFormatVersion.h"
|
||||
#include "model/EVertexDescription.h"
|
||||
#include <Common/CColor.h>
|
||||
#include <Common/EnumUtil.h>
|
||||
#include <Common/types.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <Core/ERenderOptions.h>
|
||||
#include <FileIO/CInputStream.h>
|
||||
#include <OpenGL/CShader.h>
|
||||
#include <Resource/CTexture.h>
|
||||
|
||||
class CMaterialSet;
|
||||
|
||||
class CMaterial
|
||||
{
|
||||
public:
|
||||
friend class CMaterialLoader;
|
||||
friend class CMaterialCooker;
|
||||
|
||||
// Enums
|
||||
enum EMaterialOptions
|
||||
{
|
||||
eNoSettings = 0,
|
||||
eKonst = 0x8,
|
||||
eTransparent = 0x10,
|
||||
ePunchthrough = 0x20,
|
||||
eReflection = 0x40,
|
||||
eDepthWrite = 0x80,
|
||||
eSurfaceReflection = 0x100,
|
||||
eOccluder = 0x200,
|
||||
eIndStage = 0x400,
|
||||
eLightmap = 0x800,
|
||||
eShortTexCoord = 0x2000,
|
||||
eAllSettings = 0x2FF8
|
||||
};
|
||||
|
||||
private:
|
||||
enum EShaderStatus
|
||||
{
|
||||
eNoShader, eShaderExists, eShaderFailed
|
||||
};
|
||||
|
||||
// Statics
|
||||
static u64 sCurrentMaterial; // The hash for the currently bound material
|
||||
static CColor sCurrentTint; // The tint for the currently bound material
|
||||
|
||||
// Members
|
||||
TString mName; // Name of the material
|
||||
CShader *mpShader; // This material's generated shader. Created with GenerateShader().
|
||||
EShaderStatus mShaderStatus; // A status variable so that PWE won't crash if a shader fails to compile.
|
||||
u64 mParametersHash; // A hash of all the parameters that can identify this TEV setup.
|
||||
bool mRecalcHash; // Indicates the hash needs to be recalculated. Set true when parameters are changed.
|
||||
bool mEnableBloom; // Bool that toggles bloom on or off. On by default on MP3 materials, off by default on MP1 materials.
|
||||
|
||||
EGame mVersion;
|
||||
EMaterialOptions mOptions; // See the EMaterialOptions enum above
|
||||
EVertexDescription mVtxDesc; // Descriptor of vertex attributes used by this material
|
||||
CColor mKonstColors[4]; // Konst color values for TEV
|
||||
GLenum mBlendSrcFac; // Source blend factor
|
||||
GLenum mBlendDstFac; // Dest blend factor
|
||||
bool mLightingEnabled; // Color channel control flags; indicate whether lighting is enabled
|
||||
u32 mEchoesUnknownA; // First unknown value introduced in Echoes. Included for cooking.
|
||||
u32 mEchoesUnknownB; // Second unknown value introduced in Echoes. Included for cooking.
|
||||
TResPtr<CTexture> mpIndirectTexture; // Optional texture used for the indirect stage for reflections
|
||||
|
||||
std::vector<CMaterialPass*> mPasses;
|
||||
|
||||
public:
|
||||
CMaterial();
|
||||
CMaterial(EGame version, EVertexDescription vtxDesc);
|
||||
~CMaterial();
|
||||
CMaterial* Clone();
|
||||
void GenerateShader();
|
||||
bool SetCurrent(ERenderOptions Options);
|
||||
u64 HashParameters();
|
||||
void Update();
|
||||
|
||||
// Getters
|
||||
TString Name() const;
|
||||
EGame Version() const;
|
||||
EMaterialOptions Options() const;
|
||||
EVertexDescription VtxDesc() const;
|
||||
GLenum BlendSrcFac() const;
|
||||
GLenum BlendDstFac() const;
|
||||
CColor Konst(u32 KIndex) const;
|
||||
CTexture* IndTexture() const;
|
||||
bool IsLightingEnabled() const;
|
||||
u32 EchoesUnknownA() const;
|
||||
u32 EchoesUnknownB() const;
|
||||
u32 PassCount() const;
|
||||
CMaterialPass* Pass(u32 PassIndex) const;
|
||||
|
||||
// Setters
|
||||
void SetName(const TString& name);
|
||||
void SetOptions(EMaterialOptions Options);
|
||||
void SetVertexDescription(EVertexDescription desc);
|
||||
void SetBlendMode(GLenum SrcFac, GLenum DstFac);
|
||||
void SetKonst(CColor& Konst, u32 KIndex);
|
||||
void SetIndTexture(CTexture *pTex);
|
||||
void SetLightingEnabled(bool Enabled);
|
||||
void SetNumPasses(u32 NumPasses);
|
||||
|
||||
// Static
|
||||
static void KillCachedMaterial();
|
||||
};
|
||||
DEFINE_ENUM_FLAGS(CMaterial::EMaterialOptions)
|
||||
|
||||
#endif // MATERIAL_H
|
||||
320
src/Core/Resource/CMaterialPass.cpp
Normal file
320
src/Core/Resource/CMaterialPass.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
#include "CMaterialPass.h"
|
||||
#include "CMaterial.h"
|
||||
#include <Common/AnimUtil.h>
|
||||
#include <Core/CGraphics.h>
|
||||
|
||||
CMaterialPass::CMaterialPass(CMaterial *pParent)
|
||||
{
|
||||
mPassType = "CUST";
|
||||
mSettings = eNoPassSettings;
|
||||
mpTexture = nullptr;
|
||||
mEnabled = true;
|
||||
mpParentMat = pParent;
|
||||
|
||||
mColorOutput = ePrevReg;
|
||||
mAlphaOutput = ePrevReg;
|
||||
mKColorSel = eKonstOne;
|
||||
mKAlphaSel = eKonstOne;
|
||||
mRasSel = eRasColorNull;
|
||||
mTexCoordSource = 0xFF;
|
||||
mAnimMode = eNoUVAnim;
|
||||
|
||||
for (u32 iParam = 0; iParam < 4; iParam++)
|
||||
{
|
||||
mColorInputs[iParam] = eZeroRGB;
|
||||
mAlphaInputs[iParam] = eZeroAlpha;
|
||||
mAnimParams[iParam] = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
CMaterialPass::~CMaterialPass()
|
||||
{
|
||||
}
|
||||
|
||||
CMaterialPass* CMaterialPass::Clone(CMaterial *pParent)
|
||||
{
|
||||
CMaterialPass *pOut = new CMaterialPass(pParent);
|
||||
pOut->mPassType = mPassType;
|
||||
pOut->mSettings = mSettings;
|
||||
for (u32 iIn = 0; iIn < 4; iIn++) {
|
||||
pOut->mColorInputs[iIn] = mColorInputs[iIn];
|
||||
pOut->mAlphaInputs[iIn] = mAlphaInputs[iIn];
|
||||
}
|
||||
pOut->mColorOutput = mColorOutput;
|
||||
pOut->mAlphaOutput = mAlphaOutput;
|
||||
pOut->mKColorSel = mKColorSel;
|
||||
pOut->mKAlphaSel = mKAlphaSel;
|
||||
pOut->mRasSel = mRasSel;
|
||||
pOut->mTexCoordSource = mTexCoordSource;
|
||||
pOut->mpTexture = mpTexture;
|
||||
pOut->mAnimMode = mAnimMode;
|
||||
for (u32 iParam = 0; iParam < 4; iParam++)
|
||||
pOut->mAnimParams[iParam] = mAnimParams[iParam];
|
||||
pOut->mEnabled = mEnabled;
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
void CMaterialPass::HashParameters(CHashFNV1A &Hash)
|
||||
{
|
||||
if (mEnabled)
|
||||
{
|
||||
Hash.HashLong(mPassType.ToLong());
|
||||
Hash.HashLong(mSettings);
|
||||
Hash.HashData(&mColorInputs[0], sizeof(ETevColorInput) * 4);
|
||||
Hash.HashData(&mAlphaInputs[0], sizeof(ETevAlphaInput) * 4);
|
||||
Hash.HashLong(mColorOutput);
|
||||
Hash.HashLong(mAlphaOutput);
|
||||
Hash.HashLong(mKColorSel);
|
||||
Hash.HashLong(mKAlphaSel);
|
||||
Hash.HashLong(mRasSel);
|
||||
Hash.HashLong(mTexCoordSource);
|
||||
Hash.HashLong(mAnimMode);
|
||||
Hash.HashData(mAnimParams, sizeof(float) * 4);
|
||||
Hash.HashByte(mEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void CMaterialPass::LoadTexture(u32 PassIndex)
|
||||
{
|
||||
if (mpTexture)
|
||||
mpTexture->Bind(PassIndex);
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAnimCurrent(ERenderOptions Options, u32 PassIndex)
|
||||
{
|
||||
if (mAnimMode == eNoUVAnim) return;
|
||||
|
||||
float s = AnimUtil::SecondsMod900();
|
||||
const CMatrix4f& ModelMtx = CGraphics::sMVPBlock.ModelMatrix;
|
||||
const CMatrix4f& ViewMtx = CGraphics::sMVPBlock.ViewMatrix;
|
||||
|
||||
CMatrix4f TexMtx = CMatrix4f::skIdentity;
|
||||
CMatrix4f PostMtx = CMatrix4f::skIdentity;
|
||||
|
||||
switch (mAnimMode)
|
||||
{
|
||||
|
||||
case eInverseMV: // Mode 0
|
||||
case eSimpleMode: // Mode 10 - maybe not correct?
|
||||
{
|
||||
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
mtx[0][3] = mtx[1][3] = mtx[2][3] = 0.f;
|
||||
TexMtx = CMatrix4f::FromGlmMat4(mtx);
|
||||
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
case eInverseMVTranslated: // Mode 1
|
||||
{
|
||||
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
TexMtx = CMatrix4f::FromGlmMat4(mtx);
|
||||
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
case eUVScroll: // Mode 2
|
||||
{
|
||||
if (Options & eEnableUVScroll)
|
||||
{
|
||||
TexMtx[0][3] = (s * mAnimParams[2]) + mAnimParams[0];
|
||||
TexMtx[1][3] = (s * mAnimParams[3]) + mAnimParams[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case eUVRotation: // Mode 3
|
||||
{
|
||||
if (Options & eEnableUVScroll)
|
||||
{
|
||||
float Angle = (s * mAnimParams[1]) + mAnimParams[0];
|
||||
|
||||
float ACos = cos(Angle);
|
||||
float ASin = sin(Angle);
|
||||
float TransX = (1.f - (ACos - ASin)) * 0.5f;
|
||||
float TransY = (1.f - (ASin + ACos)) * 0.5f;
|
||||
|
||||
TexMtx = CMatrix4f(ACos, -ASin, 0.0f, TransX,
|
||||
ASin, ACos, 0.0f, TransY,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case eHFilmstrip: // Mode 4
|
||||
case eVFilmstrip: // Mode 5
|
||||
{
|
||||
if (Options & eEnableUVScroll)
|
||||
{
|
||||
float Offset = mAnimParams[2] * mAnimParams[0] * (mAnimParams[3] + s);
|
||||
Offset = (float)(short)(float)(mAnimParams[1] * fmod(Offset, 1.0f)) * mAnimParams[2];
|
||||
if (mAnimMode == eHFilmstrip) TexMtx[0][3] = Offset;
|
||||
if (mAnimMode == eVFilmstrip) TexMtx[1][3] = Offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case eModelMatrix: // Mode 6
|
||||
{
|
||||
// It looks ok, but I can't tell whether it's correct...
|
||||
TexMtx = CMatrix4f::FromGlmMat4(glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
PostMtx = CMatrix4f(0.5f, 0.0f, 0.0f, TexMtx[0][3] * 0.50000001f,
|
||||
0.0f, 0.5f, 0.0f, TexMtx[1][3] * 0.50000001f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
TexMtx[0][3] = 0.f;
|
||||
TexMtx[1][3] = 0.f;
|
||||
TexMtx[2][3] = 0.f;
|
||||
}
|
||||
|
||||
case eConvolutedModeA: // Mode 7
|
||||
{
|
||||
CMatrix4f view = CGraphics::sMVPBlock.ViewMatrix;
|
||||
|
||||
// Oh god I seriously need a CMatrix4f inverse function.
|
||||
glm::mat4 mtx = glm::inverse(glm::transpose(ViewMtx.ToGlmMat4()) * glm::transpose(ModelMtx.ToGlmMat4()));
|
||||
mtx[0][3] = mtx[1][3] = mtx[2][3] = 0.f;
|
||||
TexMtx = CMatrix4f::FromGlmMat4(mtx);
|
||||
|
||||
float xy = (view[3][0] + view[3][1]) * 0.025f * mAnimParams[1];
|
||||
xy = (xy - (int) xy);
|
||||
|
||||
float z = view[3][2] * 0.05f * mAnimParams[1];
|
||||
z = (z - (int) z);
|
||||
|
||||
float halfA = mAnimParams[0] * 0.5f;
|
||||
|
||||
PostMtx = CMatrix4f(halfA, 0.0f, 0.0f, xy,
|
||||
0.0f, 0.0f, halfA, z,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
case eConvolutedModeB: // Mode 8 (MP3/DKCR only)
|
||||
{
|
||||
// todo
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CGraphics::sVertexBlock.TexMatrices[PassIndex] = TexMtx;
|
||||
CGraphics::sVertexBlock.PostMatrices[PassIndex] = PostMtx;
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
void CMaterialPass::SetType(CFourCC Type)
|
||||
{
|
||||
mPassType = Type;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetColorInputs(ETevColorInput InputA, ETevColorInput InputB, ETevColorInput InputC, ETevColorInput InputD)
|
||||
{
|
||||
mColorInputs[0] = InputA;
|
||||
mColorInputs[1] = InputB;
|
||||
mColorInputs[2] = InputC;
|
||||
mColorInputs[3] = InputD;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAlphaInputs(ETevAlphaInput InputA, ETevAlphaInput InputB, ETevAlphaInput InputC, ETevAlphaInput InputD)
|
||||
{
|
||||
mAlphaInputs[0] = InputA;
|
||||
mAlphaInputs[1] = InputB;
|
||||
mAlphaInputs[2] = InputC;
|
||||
mAlphaInputs[3] = InputD;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetColorOutput(ETevOutput OutputReg)
|
||||
{
|
||||
mColorOutput = OutputReg;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAlphaOutput(ETevOutput OutputReg)
|
||||
{
|
||||
mAlphaOutput = OutputReg;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetKColorSel(ETevKSel Sel)
|
||||
{
|
||||
mKColorSel = Sel;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetKAlphaSel(ETevKSel Sel)
|
||||
{
|
||||
// Konst RGB is invalid for alpha, so reset to One if that's the selection
|
||||
if ((Sel >= eKonst0_RGB) && (Sel <= eKonst3_RGB))
|
||||
Sel = eKonstOne;
|
||||
|
||||
mKAlphaSel = Sel;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetRasSel(ETevRasSel Sel)
|
||||
{
|
||||
mRasSel = Sel;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetTexCoordSource(u32 Source)
|
||||
{
|
||||
mTexCoordSource = Source;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetTexture(CTexture *pTex)
|
||||
{
|
||||
mpTexture = pTex;
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAnimMode(EUVAnimMode Mode)
|
||||
{
|
||||
mAnimMode = Mode;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
void CMaterialPass::SetAnimParam(u32 ParamIndex, float Value)
|
||||
{
|
||||
mAnimParams[ParamIndex] = Value;
|
||||
}
|
||||
|
||||
void CMaterialPass::SetEnabled(bool Enabled)
|
||||
{
|
||||
mEnabled = Enabled;
|
||||
mpParentMat->Update();
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
TString CMaterialPass::PassTypeName(CFourCC Type)
|
||||
{
|
||||
if (Type == "CUST") return "Custom";
|
||||
if (Type == "DIFF") return "Light";
|
||||
if (Type == "RIML") return "Rim Light";
|
||||
if (Type == "BLOL") return "Bloom Light";
|
||||
// BLOD
|
||||
if (Type == "CLR ") return "Diffuse";
|
||||
if (Type == "TRAN") return "Opacity";
|
||||
if (Type == "INCA") return "Emissive";
|
||||
if (Type == "RFLV") return "Specular";
|
||||
if (Type == "RFLD") return "Reflection";
|
||||
// LRLD
|
||||
// LURD
|
||||
if (Type == "BLOI") return "Bloom Diffuse";
|
||||
if (Type == "XRAY") return "X-Ray";
|
||||
// TOON
|
||||
return Type.ToString();
|
||||
}
|
||||
128
src/Core/Resource/CMaterialPass.h
Normal file
128
src/Core/Resource/CMaterialPass.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#ifndef CMATERIALPASS_H
|
||||
#define CMATERIALPASS_H
|
||||
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/CHashFNV1A.h>
|
||||
#include <Core/TResPtr.h>
|
||||
#include <Core/ERenderOptions.h>
|
||||
#include <Resource/CTexture.h>
|
||||
#include "ETevEnums.h"
|
||||
|
||||
class CMaterial;
|
||||
|
||||
class CMaterialPass
|
||||
{
|
||||
friend class CMaterialLoader;
|
||||
friend class CMaterialCooker;
|
||||
|
||||
public:
|
||||
enum EPassSettings
|
||||
{
|
||||
eNoPassSettings = 0x0,
|
||||
eEmissiveBloom = 0x4,
|
||||
eInvertOpacityMap = 0x10
|
||||
};
|
||||
|
||||
private:
|
||||
CMaterial *mpParentMat;
|
||||
CFourCC mPassType;
|
||||
EPassSettings mSettings;
|
||||
|
||||
ETevColorInput mColorInputs[4];
|
||||
ETevAlphaInput mAlphaInputs[4];
|
||||
ETevOutput mColorOutput;
|
||||
ETevOutput mAlphaOutput;
|
||||
ETevKSel mKColorSel;
|
||||
ETevKSel mKAlphaSel;
|
||||
ETevRasSel mRasSel;
|
||||
u32 mTexCoordSource; // Should maybe be an enum but worried about conflicts with EVertexDescriptionn
|
||||
TResPtr<CTexture> mpTexture;
|
||||
EUVAnimMode mAnimMode;
|
||||
float mAnimParams[4];
|
||||
bool mEnabled;
|
||||
|
||||
public:
|
||||
CMaterialPass(CMaterial *pParent);
|
||||
~CMaterialPass();
|
||||
CMaterialPass* Clone(CMaterial *pParent);
|
||||
void HashParameters(CHashFNV1A& Hash);
|
||||
void LoadTexture(u32 PassIndex);
|
||||
void SetAnimCurrent(ERenderOptions Options, u32 PassIndex);
|
||||
|
||||
// Setters
|
||||
void SetType(CFourCC Type);
|
||||
void SetColorInputs(ETevColorInput InputA, ETevColorInput InputB, ETevColorInput InputC, ETevColorInput InputD);
|
||||
void SetAlphaInputs(ETevAlphaInput InputA, ETevAlphaInput InputB, ETevAlphaInput InputC, ETevAlphaInput InputD);
|
||||
void SetColorOutput(ETevOutput OutputReg);
|
||||
void SetAlphaOutput(ETevOutput OutputReg);
|
||||
void SetKColorSel(ETevKSel Sel);
|
||||
void SetKAlphaSel(ETevKSel Sel);
|
||||
void SetRasSel(ETevRasSel Sel);
|
||||
void SetTexCoordSource(u32 Source);
|
||||
void SetTexture(CTexture *pTex);
|
||||
void SetAnimMode(EUVAnimMode Mode);
|
||||
void SetAnimParam(u32 ParamIndex, float Value);
|
||||
void SetEnabled(bool Enabled);
|
||||
|
||||
// Getters
|
||||
inline CFourCC Type() const {
|
||||
return mPassType;
|
||||
}
|
||||
|
||||
inline TString NamedType() const {
|
||||
return PassTypeName(mPassType);
|
||||
}
|
||||
|
||||
inline ETevColorInput ColorInput(u32 Input) const {
|
||||
return mColorInputs[Input];
|
||||
}
|
||||
|
||||
inline ETevAlphaInput AlphaInput(u32 Input) const {
|
||||
return mAlphaInputs[Input];
|
||||
}
|
||||
|
||||
inline ETevOutput ColorOutput() const {
|
||||
return mColorOutput;
|
||||
}
|
||||
|
||||
inline ETevOutput AlphaOutput() const {
|
||||
return mAlphaOutput;
|
||||
}
|
||||
|
||||
inline ETevKSel KColorSel() const {
|
||||
return mKColorSel;
|
||||
}
|
||||
|
||||
inline ETevKSel KAlphaSel() const {
|
||||
return mKAlphaSel;
|
||||
}
|
||||
|
||||
inline ETevRasSel RasSel() const {
|
||||
return mRasSel;
|
||||
}
|
||||
|
||||
inline u32 TexCoordSource() const {
|
||||
return mTexCoordSource;
|
||||
}
|
||||
|
||||
inline CTexture* Texture() const {
|
||||
return mpTexture;
|
||||
}
|
||||
|
||||
inline EUVAnimMode AnimMode() const {
|
||||
return mAnimMode;
|
||||
}
|
||||
|
||||
inline float AnimParam(u32 ParamIndex) const {
|
||||
return mAnimParams[ParamIndex];
|
||||
}
|
||||
|
||||
inline bool IsEnabled() const {
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
// Static
|
||||
static TString PassTypeName(CFourCC Type);
|
||||
};
|
||||
|
||||
#endif // CMATERIALPASS_H
|
||||
49
src/Core/Resource/CMaterialSet.cpp
Normal file
49
src/Core/Resource/CMaterialSet.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "CMaterialSet.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <iostream>
|
||||
|
||||
CMaterialSet::CMaterialSet()
|
||||
{
|
||||
}
|
||||
|
||||
CMaterialSet::~CMaterialSet()
|
||||
{
|
||||
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
|
||||
delete mMaterials[iMat];
|
||||
}
|
||||
|
||||
CMaterialSet* CMaterialSet::Clone()
|
||||
{
|
||||
CMaterialSet *pOut = new CMaterialSet();
|
||||
|
||||
pOut->mMaterials.resize(mMaterials.size());
|
||||
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
|
||||
pOut->mMaterials[iMat] = mMaterials[iMat]->Clone();
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
u32 CMaterialSet::NumMaterials()
|
||||
{
|
||||
return mMaterials.size();
|
||||
}
|
||||
|
||||
CMaterial* CMaterialSet::MaterialByIndex(u32 index)
|
||||
{
|
||||
if (index >= NumMaterials()) return nullptr;
|
||||
return mMaterials[index];
|
||||
}
|
||||
|
||||
CMaterial* CMaterialSet::MaterialByName(const TString& name)
|
||||
{
|
||||
for (auto it = mMaterials.begin(); it != mMaterials.end(); it++)
|
||||
if ((*it)->Name() == name) return *it;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 CMaterialSet::MaterialIndexByName(const TString& name)
|
||||
{
|
||||
for (u32 iMat = 0; iMat < mMaterials.size(); iMat++)
|
||||
if (mMaterials[iMat]->Name() == name) return iMat;
|
||||
return -1;
|
||||
}
|
||||
26
src/Core/Resource/CMaterialSet.h
Normal file
26
src/Core/Resource/CMaterialSet.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CMATERIALSET_H
|
||||
#define CMATERIALSET_H
|
||||
|
||||
#include <FileIO/CInputStream.h>
|
||||
#include <Resource/CTexture.h>
|
||||
#include <Resource/CMaterial.h>
|
||||
#include "EFormatVersion.h"
|
||||
|
||||
class CMaterialSet
|
||||
{
|
||||
friend class CMaterialLoader;
|
||||
friend class CMaterialCooker;
|
||||
|
||||
std::vector<CMaterial*> mMaterials;
|
||||
|
||||
public:
|
||||
CMaterialSet();
|
||||
~CMaterialSet();
|
||||
CMaterialSet* Clone();
|
||||
u32 NumMaterials();
|
||||
CMaterial* MaterialByIndex(u32 index);
|
||||
CMaterial* MaterialByName(const TString& name);
|
||||
u32 MaterialIndexByName(const TString& name);
|
||||
};
|
||||
|
||||
#endif // CMATERIALSET_H
|
||||
174
src/Core/Resource/CPakFile.cpp
Normal file
174
src/Core/Resource/CPakFile.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include "CPakFile.h"
|
||||
#include <Common/types.h>
|
||||
#include <FileIO/CMemoryInStream.h>
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <zlib.h>
|
||||
#include <lzo/lzo1x.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
CPakFile::CPakFile()
|
||||
{
|
||||
pak = nullptr;
|
||||
}
|
||||
|
||||
CPakFile::CPakFile(CInputStream* pakfile)
|
||||
{
|
||||
pak = pakfile;
|
||||
if (!pak->IsValid()) return;
|
||||
|
||||
version = pak->ReadLong();
|
||||
pak->Seek(0x4, SEEK_CUR);
|
||||
|
||||
u32 namedResCount = pak->ReadLong();
|
||||
NamedResTable.resize(namedResCount);
|
||||
|
||||
for (u32 n = 0; n < namedResCount; n++)
|
||||
{
|
||||
SNamedResource *res = &NamedResTable[n];
|
||||
res->resType = CFourCC(*pak);
|
||||
res->resID = (u64) pak->ReadLong();
|
||||
u32 resNameLength = pak->ReadLong();
|
||||
res->resName = pak->ReadString(resNameLength);
|
||||
}
|
||||
|
||||
u32 resCount = pak->ReadLong();
|
||||
ResInfoTable.resize(resCount);
|
||||
|
||||
for (u32 r = 0; r < resCount; r++)
|
||||
{
|
||||
SResInfo *res = &ResInfoTable[r];
|
||||
res->compressed = (pak->ReadLong() != 0);
|
||||
res->resType = CFourCC(*pak);
|
||||
res->resID = (u64) pak->ReadLong();
|
||||
res->size = pak->ReadLong();
|
||||
res->offset = pak->ReadLong();
|
||||
}
|
||||
}
|
||||
|
||||
CPakFile::~CPakFile()
|
||||
{
|
||||
if (pak) delete pak;
|
||||
}
|
||||
|
||||
std::vector<SNamedResource> CPakFile::getNamedResources()
|
||||
{
|
||||
return NamedResTable;
|
||||
}
|
||||
|
||||
SResInfo CPakFile::getResourceInfo(u64 assetID, CFourCC assetType)
|
||||
{
|
||||
// TODO: figure out how the game finds assets in paks, implement similar system to speed things up
|
||||
if (ResInfoTable.empty())
|
||||
return SResInfo();
|
||||
|
||||
for (u32 r = 0; r < ResInfoTable.size(); r++)
|
||||
{
|
||||
if (((u64) (ResInfoTable[r].resID & 0xFFFFFFFF) == (u64) (assetID & 0xFFFFFFFF)) && (ResInfoTable[r].resType == assetType))
|
||||
return ResInfoTable[r];
|
||||
}
|
||||
|
||||
return SResInfo();
|
||||
}
|
||||
|
||||
std::vector<u8>* CPakFile::getResource(u64 assetID, CFourCC assetType)
|
||||
{
|
||||
SResInfo info = getResourceInfo(assetID, assetType);
|
||||
|
||||
// make sure SResInfo is valid
|
||||
if ((u64) (info.resID & 0xFFFFFFFF) != (u64) (assetID & 0xFFFFFFFF)) return nullptr;
|
||||
else return getResource(info);
|
||||
}
|
||||
|
||||
std::vector<u8>* CPakFile::getResource(SResInfo& info)
|
||||
{
|
||||
pak->Seek(info.offset, SEEK_SET);
|
||||
std::vector<u8> *res_buf = new std::vector<u8>;
|
||||
|
||||
if (info.compressed)
|
||||
{
|
||||
u32 decmp_size = pak->ReadLong();
|
||||
res_buf->resize(decmp_size);
|
||||
|
||||
std::vector<u8> cmp_buf(info.size - 4);
|
||||
pak->ReadBytes(&cmp_buf[0], info.size - 4);
|
||||
|
||||
bool dcmp = decompress(cmp_buf.data(), cmp_buf.size(), res_buf->data(), res_buf->size());
|
||||
|
||||
if (!dcmp) {
|
||||
std::cout << "Error: Unable to decompress " << info.resType.ToString() << " 0x" << std::hex << std::setw(8) << std::setfill('0') << info.resID << std::dec << "\n";
|
||||
delete res_buf;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
res_buf->resize(info.size);
|
||||
pak->ReadBytes(res_buf->data(), info.size);
|
||||
}
|
||||
|
||||
return res_buf;
|
||||
}
|
||||
|
||||
bool CPakFile::decompress(u8 *src, u32 src_len, u8 *dst, u32 dst_len)
|
||||
{
|
||||
if ((src[0] == 0x78) && (src[1] == 0xda))
|
||||
{
|
||||
// zlib
|
||||
z_stream z;
|
||||
z.zalloc = Z_NULL;
|
||||
z.zfree = Z_NULL;
|
||||
z.opaque = Z_NULL;
|
||||
z.avail_in = src_len;
|
||||
z.next_in = src;
|
||||
z.avail_out = dst_len;
|
||||
z.next_out = dst;
|
||||
|
||||
s32 ret = inflateInit(&z);
|
||||
|
||||
if (ret == Z_OK)
|
||||
{
|
||||
ret = inflate(&z, Z_NO_FLUSH);
|
||||
|
||||
if ((ret == Z_OK) || (ret == Z_STREAM_END))
|
||||
ret = inflateEnd(&z);
|
||||
}
|
||||
|
||||
if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
|
||||
std::cout << "zlib error: " << std::dec << ret << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
else return true;
|
||||
}
|
||||
|
||||
else {
|
||||
// LZO
|
||||
lzo_uint decmp;
|
||||
s32 ret;
|
||||
u8 *src_end = src + src_len;
|
||||
u8 *dst_end = dst + dst_len;
|
||||
lzo_init();
|
||||
|
||||
while ((src < src_end) && (dst < dst_end)) {
|
||||
short block_size;
|
||||
memcpy(&block_size, src, 2);
|
||||
if (IOUtil::SystemEndianness == IOUtil::LittleEndian) IOUtil::SwapBytes(block_size);
|
||||
src += 2;
|
||||
|
||||
ret = lzo1x_decompress(src, block_size, dst, &decmp, LZO1X_MEM_DECOMPRESS);
|
||||
if (ret != LZO_E_OK) break;
|
||||
src += block_size;
|
||||
dst += decmp;
|
||||
}
|
||||
|
||||
if (ret != LZO_E_OK) {
|
||||
std::cout << "LZO error: " << std::dec << ret << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
else return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
31
src/Core/Resource/CPakFile.h
Normal file
31
src/Core/Resource/CPakFile.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef CPAKFILE_H
|
||||
#define CPAKFILE_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <FileIO/CFileInStream.h>
|
||||
#include "SNamedResource.h"
|
||||
#include "SResInfo.h"
|
||||
#include <vector>
|
||||
|
||||
class CPakFile
|
||||
{
|
||||
private:
|
||||
u32 version;
|
||||
std::vector<SNamedResource> NamedResTable;
|
||||
std::vector<SResInfo> ResInfoTable;
|
||||
CInputStream* pak;
|
||||
|
||||
bool decompress(u8 *src, u32 src_len, u8 *dst, u32 dst_len);
|
||||
|
||||
public:
|
||||
CPakFile();
|
||||
CPakFile(CInputStream* pakfile);
|
||||
~CPakFile();
|
||||
|
||||
std::vector<SNamedResource> getNamedResources();
|
||||
SResInfo getResourceInfo(u64 assetID, CFourCC assetType);
|
||||
std::vector<u8>* getResource(u64 assetID, CFourCC assetType);
|
||||
std::vector<u8>* getResource(SResInfo& info);
|
||||
};
|
||||
|
||||
#endif // CPAKFILE_H
|
||||
233
src/Core/Resource/CResCache.cpp
Normal file
233
src/Core/Resource/CResCache.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "CResCache.h"
|
||||
#include "Log.h"
|
||||
#include <Common/TString.h>
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// Loaders
|
||||
#include <Resource/factory/CAreaLoader.h>
|
||||
#include <Resource/factory/CAnimSetLoader.h>
|
||||
#include <Resource/factory/CCollisionLoader.h>
|
||||
#include <Resource/factory/CFontLoader.h>
|
||||
#include <Resource/factory/CModelLoader.h>
|
||||
#include <Resource/factory/CScanLoader.h>
|
||||
#include <Resource/factory/CStringLoader.h>
|
||||
#include <Resource/factory/CTextureDecoder.h>
|
||||
#include <Resource/factory/CWorldLoader.h>
|
||||
|
||||
CResCache::CResCache()
|
||||
{
|
||||
mpPak = nullptr;
|
||||
}
|
||||
|
||||
CResCache::~CResCache()
|
||||
{
|
||||
Clean();
|
||||
}
|
||||
|
||||
void CResCache::Clean()
|
||||
{
|
||||
if (mResourceCache.empty()) return;
|
||||
Log::Write("Cleaning unused resources");
|
||||
|
||||
// I couldn't get this to work properly using reverse iterators, lol.
|
||||
// Resources get cached after their dependencies, which is why I go backwards
|
||||
// while loop is to ensure -all- unused resources are cleaned. Not sure of a better way to do it.
|
||||
int numResourcesCleaned = 1;
|
||||
|
||||
while (numResourcesCleaned)
|
||||
{
|
||||
numResourcesCleaned = 0;
|
||||
|
||||
for (auto it = mResourceCache.end(); it != mResourceCache.begin();)
|
||||
{
|
||||
it--;
|
||||
if (it->second->mRefCount <= 0)
|
||||
{
|
||||
delete it->second;
|
||||
it = mResourceCache.erase(it);
|
||||
numResourcesCleaned++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::Write(std::to_string(mResourceCache.size()) + " resources loaded");
|
||||
}
|
||||
|
||||
void CResCache::SetFolder(TString path)
|
||||
{
|
||||
path.EnsureEndsWith("/");
|
||||
mResSource.Path = path;
|
||||
mResSource.Source = SResSource::Folder;
|
||||
Log::Write("Set resource folder: " + path);
|
||||
}
|
||||
|
||||
void CResCache::SetPak(const TString& path)
|
||||
{
|
||||
CFileInStream *pakfile = new CFileInStream(path.ToStdString(), IOUtil::BigEndian);
|
||||
if (!pakfile->IsValid())
|
||||
{
|
||||
Log::Error("Couldn't load pak file: " + path);
|
||||
delete pakfile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mpPak) delete mpPak;
|
||||
mpPak = new CPakFile(pakfile);
|
||||
mResSource.Path = path;
|
||||
mResSource.Source = SResSource::PakFile;
|
||||
Log::Write("Loaded pak file: " + path);
|
||||
}
|
||||
|
||||
void CResCache::SetResSource(SResSource& ResSource)
|
||||
{
|
||||
mResSource = ResSource;
|
||||
}
|
||||
|
||||
SResSource CResCache::GetResSource()
|
||||
{
|
||||
return mResSource;
|
||||
}
|
||||
|
||||
TString CResCache::GetSourcePath()
|
||||
{
|
||||
return mResSource.Path;
|
||||
}
|
||||
|
||||
CResource* CResCache::GetResource(CUniqueID ResID, CFourCC type)
|
||||
{
|
||||
if (!ResID.IsValid()) return nullptr;
|
||||
|
||||
auto got = mResourceCache.find(ResID.ToLongLong());
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
return got->second;
|
||||
|
||||
std::vector<u8> *pBuffer = nullptr;
|
||||
TString Source;
|
||||
|
||||
// Load from pak
|
||||
if (mResSource.Source == SResSource::PakFile)
|
||||
{
|
||||
pBuffer = mpPak->getResource(ResID.ToLongLong(), type);
|
||||
Source = ResID.ToString() + "." + type.ToString();
|
||||
}
|
||||
|
||||
// Load from folder
|
||||
else
|
||||
{
|
||||
Source = mResSource.Path + ResID.ToString() + "." + type.ToString();
|
||||
CFileInStream file(Source.ToStdString(), IOUtil::BigEndian);
|
||||
if (!file.IsValid())
|
||||
{
|
||||
Log::Error("Couldn't open resource: " + ResID.ToString() + "." + type.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pBuffer = new std::vector<u8>;
|
||||
pBuffer->resize(file.Size());
|
||||
file.ReadBytes(pBuffer->data(), pBuffer->size());
|
||||
}
|
||||
if (!pBuffer) return nullptr;
|
||||
|
||||
// Load resource
|
||||
CMemoryInStream mem(pBuffer->data(), pBuffer->size(), IOUtil::BigEndian);
|
||||
mem.SetSourceString(*Source.GetFileName());
|
||||
CResource *Res = nullptr;
|
||||
bool SupportedFormat = true;
|
||||
|
||||
if (type == "CMDL") Res = CModelLoader::LoadCMDL(mem);
|
||||
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(mem);
|
||||
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(mem);
|
||||
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(mem);
|
||||
else if (type == "MREA") Res = CAreaLoader::LoadMREA(mem);
|
||||
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(mem);
|
||||
else if (type == "STRG") Res = CStringLoader::LoadSTRG(mem);
|
||||
else if (type == "FONT") Res = CFontLoader::LoadFONT(mem);
|
||||
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(mem);
|
||||
else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(mem);
|
||||
else SupportedFormat = false;
|
||||
|
||||
// Log errors
|
||||
if (!SupportedFormat)
|
||||
Log::Write("Unsupported format; unable to load " + type.ToString() + " " + ResID.ToString());
|
||||
|
||||
if (!Res) Res = new CResource(); // Default for invalid resource or unsupported format
|
||||
|
||||
// Add to cache and cleanup
|
||||
Res->mID = ResID;
|
||||
Res->mResSource = Source;
|
||||
mResourceCache[ResID.ToLongLong()] = Res;
|
||||
delete pBuffer;
|
||||
return Res;
|
||||
}
|
||||
|
||||
CResource* CResCache::GetResource(const TString& ResPath)
|
||||
{
|
||||
// Since this function takes a string argument it always loads directly from a file - no pak
|
||||
CUniqueID ResID = ResPath.Hash64();
|
||||
|
||||
auto got = mResourceCache.find(ResID.ToLongLong());
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
return got->second;
|
||||
|
||||
CFileInStream file(ResPath.ToStdString(), IOUtil::BigEndian);
|
||||
if (!file.IsValid())
|
||||
{
|
||||
Log::Error("Couldn't open resource: " + ResPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Save old ResSource to restore later
|
||||
const SResSource OldSource = mResSource;
|
||||
mResSource.Source = SResSource::Folder;
|
||||
mResSource.Path = ResPath.GetFileDirectory();
|
||||
|
||||
// Load resource
|
||||
CResource *Res = nullptr;
|
||||
CFourCC type = ResPath.GetFileExtension().ToUpper();
|
||||
bool SupportedFormat = true;
|
||||
|
||||
if (type == "CMDL") Res = CModelLoader::LoadCMDL(file);
|
||||
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(file);
|
||||
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(file);
|
||||
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(file);
|
||||
else if (type == "MREA") Res = CAreaLoader::LoadMREA(file);
|
||||
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(file);
|
||||
else if (type == "FONT") Res = CFontLoader::LoadFONT(file);
|
||||
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(file);
|
||||
else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(file);
|
||||
else SupportedFormat = false;
|
||||
|
||||
if (!Res) Res = new CResource(); // Default for unsupported formats
|
||||
|
||||
// Add to cache and cleanup
|
||||
Res->mID = *ResPath;
|
||||
Res->mResSource = ResPath;
|
||||
mResourceCache[ResID.ToLongLong()] = Res;
|
||||
mResSource = OldSource;
|
||||
return Res;
|
||||
}
|
||||
|
||||
void CResCache::CacheResource(CResource *pRes)
|
||||
{
|
||||
u64 ID = pRes->ResID().ToLongLong();
|
||||
auto got = mResourceCache.find(ID);
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
mResourceCache[ID] = pRes;
|
||||
}
|
||||
|
||||
void CResCache::DeleteResource(CUniqueID ResID)
|
||||
{
|
||||
auto got = mResourceCache.find(ResID.ToLongLong());
|
||||
|
||||
if (got != mResourceCache.end())
|
||||
{
|
||||
delete got->second;
|
||||
mResourceCache.erase(got, got);
|
||||
}
|
||||
}
|
||||
|
||||
CResCache gResCache;
|
||||
41
src/Core/Resource/CResCache.h
Normal file
41
src/Core/Resource/CResCache.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef CRESCACHE_H
|
||||
#define CRESCACHE_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <Common/TString.h>
|
||||
#include <Resource/CPakFile.h>
|
||||
#include <Resource/CResource.h>
|
||||
#include <unordered_map>
|
||||
|
||||
struct SResSource
|
||||
{
|
||||
TString Path;
|
||||
enum {
|
||||
Folder, PakFile
|
||||
} Source;
|
||||
};
|
||||
|
||||
class CResCache
|
||||
{
|
||||
std::unordered_map<u64, CResource*> mResourceCache;
|
||||
CPakFile *mpPak;
|
||||
SResSource mResSource;
|
||||
|
||||
public:
|
||||
CResCache();
|
||||
~CResCache();
|
||||
void Clean();
|
||||
void SetFolder(TString path);
|
||||
void SetPak(const TString& path);
|
||||
void SetResSource(SResSource& ResSource);
|
||||
SResSource GetResSource();
|
||||
TString GetSourcePath();
|
||||
CResource* GetResource(CUniqueID ResID, CFourCC type);
|
||||
CResource* GetResource(const TString& ResPath);
|
||||
void CacheResource(CResource *pRes);
|
||||
void DeleteResource(CUniqueID ResID);
|
||||
};
|
||||
|
||||
extern CResCache gResCache;
|
||||
|
||||
#endif // CRESCACHE_H
|
||||
110
src/Core/Resource/CResource.cpp
Normal file
110
src/Core/Resource/CResource.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "CResource.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <iostream>
|
||||
|
||||
CResource::CResource()
|
||||
{
|
||||
mRefCount = 0;
|
||||
}
|
||||
|
||||
CResource::~CResource()
|
||||
{
|
||||
}
|
||||
|
||||
TString CResource::Source()
|
||||
{
|
||||
return mResSource.GetFileName();
|
||||
}
|
||||
|
||||
TString CResource::FullSource()
|
||||
{
|
||||
return mResSource;
|
||||
}
|
||||
|
||||
CUniqueID CResource::ResID()
|
||||
{
|
||||
return mID;
|
||||
}
|
||||
|
||||
void CResource::Lock()
|
||||
{
|
||||
mRefCount++;
|
||||
}
|
||||
|
||||
void CResource::Release()
|
||||
{
|
||||
mRefCount--;
|
||||
}
|
||||
|
||||
bool CResource::IsValidResource()
|
||||
{
|
||||
return (Type() != eResource);
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
EResType CResource::ResTypeForExtension(CFourCC Extension)
|
||||
{
|
||||
Extension = Extension.ToUpper();
|
||||
|
||||
if (Extension < "FONT")
|
||||
{
|
||||
if (Extension < "CSKR")
|
||||
{
|
||||
if (Extension == "AFSM") return eStateMachine;
|
||||
if (Extension == "AGSC") return eAudioGrp;
|
||||
if (Extension == "ANCS") return eAnimSet;
|
||||
if (Extension == "ANIM") return eAnimation;
|
||||
if (Extension == "ATBL") return eAudioTable;
|
||||
if (Extension == "CAUD") return eAudioData;
|
||||
if (Extension == "CHAR") return eAnimSet;
|
||||
if (Extension == "CINF") return eSkeleton;
|
||||
if (Extension == "CMDL") return eModel;
|
||||
if (Extension == "CRSC") return eCollisionResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Extension == "CSKR") return eSkin;
|
||||
if (Extension == "CSMP") return eAudioSample;
|
||||
if (Extension == "CSNG") return eMidi;
|
||||
if (Extension == "CTWK") return eTweak;
|
||||
if (Extension == "DCLN") return eCollisionMeshGroup;
|
||||
if (Extension == "DGRP") return eDependencyGroup;
|
||||
if (Extension == "DSP ") return eMusicTrack;
|
||||
if (Extension == "DUMB") return eDataDump;
|
||||
if (Extension == "ELSC") return eParticleElectric;
|
||||
if (Extension == "EVNT") return eAnimEventData;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Extension < "PAK ")
|
||||
{
|
||||
if (Extension == "FONT") return eFont;
|
||||
if (Extension == "FRME") return eGuiFrame;
|
||||
if (Extension == "FSM2") return eStateMachine;
|
||||
if (Extension == "HINT") return eHintSystem;
|
||||
if (Extension == "MAPA") return eMapArea;
|
||||
if (Extension == "MAPW") return eMapWorld;
|
||||
if (Extension == "MAPU") return eMapUniverse;
|
||||
if (Extension == "MLVL") return eWorld;
|
||||
if (Extension == "MREA") return eArea;
|
||||
if (Extension == "NTWK") return eTweak;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Extension == "PAK ") return ePackFile;
|
||||
if (Extension == "PART") return eParticle;
|
||||
if (Extension == "PATH") return eNavMesh;
|
||||
if (Extension == "SAVW") return eSaveWorld;
|
||||
if (Extension == "SCAN") return eScan;
|
||||
if (Extension == "STRG") return eStringTable;
|
||||
if (Extension == "STRM") return eAudioStream;
|
||||
if (Extension == "SWHC") return eParticleSwoosh;
|
||||
if (Extension == "THP ") return eVideo;
|
||||
if (Extension == "TXTR") return eTexture;
|
||||
if (Extension == "WPSC") return eProjectile;
|
||||
}
|
||||
}
|
||||
|
||||
return eInvalidResType;
|
||||
}
|
||||
49
src/Core/Resource/CResource.h
Normal file
49
src/Core/Resource/CResource.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef CRESOURCE_H
|
||||
#define CRESOURCE_H
|
||||
|
||||
#include "EResType.h"
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/CUniqueID.h>
|
||||
#include <Common/types.h>
|
||||
#include <Common/TString.h>
|
||||
|
||||
class CResCache;
|
||||
|
||||
// This macro creates functions that allow us to easily identify this resource type.
|
||||
// Must be included on every CResource subclass.
|
||||
#define DECLARE_RESOURCE_TYPE(ResTypeEnum) \
|
||||
public: \
|
||||
virtual EResType Type() const \
|
||||
{ \
|
||||
return ResTypeEnum; \
|
||||
} \
|
||||
\
|
||||
static EResType StaticType() \
|
||||
{ \
|
||||
return ResTypeEnum; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
|
||||
class CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eResource)
|
||||
friend class CResCache;
|
||||
|
||||
TString mResSource;
|
||||
CUniqueID mID;
|
||||
int mRefCount;
|
||||
|
||||
public:
|
||||
CResource();
|
||||
virtual ~CResource();
|
||||
TString Source();
|
||||
TString FullSource();
|
||||
CUniqueID ResID();
|
||||
void Lock();
|
||||
void Release();
|
||||
bool IsValidResource();
|
||||
static EResType ResTypeForExtension(CFourCC Extension);
|
||||
};
|
||||
|
||||
#endif // CRESOURCE_H
|
||||
39
src/Core/Resource/CScan.cpp
Normal file
39
src/Core/Resource/CScan.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "CScan.h"
|
||||
|
||||
CScan::CScan()
|
||||
{
|
||||
mpFrame = nullptr;
|
||||
mpStringTable = nullptr;
|
||||
mIsSlow = false;
|
||||
mIsImportant = false;
|
||||
mCategory = eNone;
|
||||
}
|
||||
|
||||
CScan::~CScan()
|
||||
{
|
||||
}
|
||||
|
||||
EGame CScan::Version()
|
||||
{
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
CStringTable* CScan::ScanText()
|
||||
{
|
||||
return mpStringTable;
|
||||
}
|
||||
|
||||
bool CScan::IsImportant()
|
||||
{
|
||||
return mIsImportant;
|
||||
}
|
||||
|
||||
bool CScan::IsSlow()
|
||||
{
|
||||
return mIsSlow;
|
||||
}
|
||||
|
||||
CScan::ELogbookCategory CScan::LogbookCategory()
|
||||
{
|
||||
return mCategory;
|
||||
}
|
||||
43
src/Core/Resource/CScan.h
Normal file
43
src/Core/Resource/CScan.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef CSCAN_H
|
||||
#define CSCAN_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CStringTable.h"
|
||||
#include "EFormatVersion.h"
|
||||
#include <Core/TResPtr.h>
|
||||
|
||||
class CScan : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eScan)
|
||||
friend class CScanLoader;
|
||||
|
||||
public:
|
||||
// This likely needs revising when MP2/MP3 support is added
|
||||
enum ELogbookCategory
|
||||
{
|
||||
eNone,
|
||||
ePirateData,
|
||||
eChozoLore,
|
||||
eCreatures,
|
||||
eResearch
|
||||
};
|
||||
|
||||
private:
|
||||
EGame mVersion;
|
||||
TResPtr<CResource> mpFrame;
|
||||
TResPtr<CStringTable> mpStringTable;
|
||||
bool mIsSlow;
|
||||
bool mIsImportant;
|
||||
ELogbookCategory mCategory;
|
||||
|
||||
public:
|
||||
CScan();
|
||||
~CScan();
|
||||
EGame Version();
|
||||
CStringTable* ScanText();
|
||||
bool IsImportant();
|
||||
bool IsSlow();
|
||||
ELogbookCategory LogbookCategory();
|
||||
};
|
||||
|
||||
#endif // CSCAN_H
|
||||
52
src/Core/Resource/CStringTable.cpp
Normal file
52
src/Core/Resource/CStringTable.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "CStringTable.h"
|
||||
|
||||
CStringTable::CStringTable() : CResource()
|
||||
{
|
||||
}
|
||||
|
||||
CStringTable::~CStringTable()
|
||||
{
|
||||
}
|
||||
|
||||
CResource* CStringTable::MakeCopy(CResCache*)
|
||||
{
|
||||
// Not using parameter 1 (CResCache* - pResCache)
|
||||
return new CStringTable(*this);
|
||||
}
|
||||
|
||||
// ************ SETTERS ************
|
||||
u32 CStringTable::GetStringCount()
|
||||
{
|
||||
return mNumStrings;
|
||||
}
|
||||
|
||||
u32 CStringTable::GetLangCount()
|
||||
{
|
||||
return mLangTables.size();
|
||||
}
|
||||
|
||||
CFourCC CStringTable::GetLangTag(u32 Index)
|
||||
{
|
||||
return mLangTables[Index].Language;
|
||||
}
|
||||
|
||||
TWideString CStringTable::GetString(CFourCC Lang, u32 StringIndex)
|
||||
{
|
||||
for (u32 iLang = 0; iLang < GetLangCount(); iLang++)
|
||||
{
|
||||
if (GetLangTag(iLang) == Lang)
|
||||
return GetString(iLang, StringIndex);
|
||||
}
|
||||
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
TWideString CStringTable::GetString(u32 LangIndex, u32 StringIndex)
|
||||
{
|
||||
return mLangTables[LangIndex].Strings[StringIndex];
|
||||
}
|
||||
|
||||
TString CStringTable::GetStringName(u32 StringIndex)
|
||||
{
|
||||
return mStringNames[StringIndex];
|
||||
}
|
||||
39
src/Core/Resource/CStringTable.h
Normal file
39
src/Core/Resource/CStringTable.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef CSTRINGTABLE_H
|
||||
#define CSTRINGTABLE_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include <Common/types.h>
|
||||
#include <Common/CFourCC.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class CStringTable : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eStringTable)
|
||||
friend class CStringLoader;
|
||||
|
||||
std::vector<TString> mStringNames;
|
||||
u32 mNumStrings;
|
||||
|
||||
struct SLangTable
|
||||
{
|
||||
CFourCC Language;
|
||||
std::vector<TWideString> Strings;
|
||||
};
|
||||
std::vector<SLangTable> mLangTables;
|
||||
|
||||
public:
|
||||
CStringTable();
|
||||
~CStringTable();
|
||||
CResource* MakeCopy(CResCache *pCopyCache);
|
||||
|
||||
// Getters
|
||||
u32 GetStringCount();
|
||||
u32 GetLangCount();
|
||||
CFourCC GetLangTag(u32 Index);
|
||||
TWideString GetString(CFourCC Lang, u32 StringIndex);
|
||||
TWideString GetString(u32 LangIndex, u32 StringIndex);
|
||||
TString GetStringName(u32 StringIndex);
|
||||
};
|
||||
|
||||
#endif // CSTRINGTABLE_H
|
||||
375
src/Core/Resource/CTexture.cpp
Normal file
375
src/Core/Resource/CTexture.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
#include "CTexture.h"
|
||||
|
||||
CTexture::CTexture() : CResource()
|
||||
{
|
||||
mTexelFormat = eRGBA8;
|
||||
mSourceTexelFormat = eRGBA8;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mNumMipMaps = 0;
|
||||
mLinearSize = 0;
|
||||
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
|
||||
CTexture::CTexture(const CTexture& Source)
|
||||
{
|
||||
mTexelFormat = Source.mTexelFormat;
|
||||
mSourceTexelFormat = Source.mSourceTexelFormat;
|
||||
mWidth = Source.mWidth;
|
||||
mHeight = Source.mHeight;
|
||||
mLinearSize = Source.mLinearSize;
|
||||
|
||||
mBufferExists = Source.mBufferExists;
|
||||
mImgDataSize = Source.mImgDataSize;
|
||||
mImgDataBuffer = new u8[mImgDataSize];
|
||||
memcpy(mImgDataBuffer, Source.mImgDataBuffer, mImgDataSize);
|
||||
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
|
||||
CTexture::CTexture(u32 Width, u32 Height)
|
||||
{
|
||||
mTexelFormat = eRGBA8;
|
||||
mSourceTexelFormat = eRGBA8;
|
||||
mWidth = (u16) Width;
|
||||
mHeight = (u16) Height;
|
||||
mNumMipMaps = 1;
|
||||
mLinearSize = Width * Height * 4;
|
||||
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
|
||||
CTexture::~CTexture()
|
||||
{
|
||||
DeleteBuffers();
|
||||
}
|
||||
|
||||
bool CTexture::BufferGL()
|
||||
{
|
||||
glGenTextures(1, &mTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||
|
||||
GLenum GLFormat, GLType;
|
||||
bool IsCompressed = false;
|
||||
|
||||
switch (mTexelFormat) {
|
||||
|
||||
case eLuminance:
|
||||
GLFormat = GL_LUMINANCE;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case eLuminanceAlpha:
|
||||
GLFormat = GL_LUMINANCE_ALPHA;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case eRGB565:
|
||||
GLFormat = GL_RGB;
|
||||
GLType = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
case eRGBA4:
|
||||
GLFormat = GL_RGBA;
|
||||
GLType = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
break;
|
||||
case eRGBA8:
|
||||
GLFormat = GL_RGBA;
|
||||
GLType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case eDXT1:
|
||||
GLFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
IsCompressed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// The smallest mipmaps are probably not being loaded correctly, because mipmaps in GX textures have a minimum size depending on the format, and these don't.
|
||||
// Not sure specifically what accomodations should be made to fix that though so whatever.
|
||||
u32 MipSize = mLinearSize;
|
||||
u32 MipOffset = 0;
|
||||
u16 MipW = mWidth, MipH = mHeight;
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
GLvoid *pData = (mBufferExists) ? (mImgDataBuffer + MipOffset) : NULL;
|
||||
|
||||
if (!IsCompressed)
|
||||
glTexImage2D(GL_TEXTURE_2D, iMip, GLFormat, MipW, MipH, 0, GLFormat, GLType, pData);
|
||||
else
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, iMip, GLFormat, MipW, MipH, 0, MipSize, pData);
|
||||
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
MipOffset += MipSize;
|
||||
MipSize /= 4;
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mNumMipMaps - 1);
|
||||
|
||||
// Linear filtering on mipmaps:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
|
||||
// Anisotropic filtering:
|
||||
float MaxAnisotropy;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &MaxAnisotropy);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, MaxAnisotropy);
|
||||
|
||||
mGLBufferExists = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTexture::Bind(u32 GLTextureUnit)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + GLTextureUnit);
|
||||
|
||||
if (!mGLBufferExists)
|
||||
BufferGL();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||
}
|
||||
|
||||
void CTexture::Resize(u32 Width, u32 Height)
|
||||
{
|
||||
if ((mWidth != Width) || (mHeight != Height))
|
||||
{
|
||||
DeleteBuffers();
|
||||
mWidth = (u16) Width;
|
||||
mHeight = (u16) Height;
|
||||
mNumMipMaps = 1;
|
||||
}
|
||||
}
|
||||
|
||||
float CTexture::ReadTexelAlpha(const CVector2f& TexCoord)
|
||||
{
|
||||
// todo: support texel formats other than DXT1
|
||||
// DXT1 is definitely the most complicated one anyway; try reusing CTextureDecoder functions for other formats
|
||||
u32 TexelX = (u32) ((mWidth - 1) * TexCoord.x);
|
||||
u32 TexelY = (u32) ((mHeight - 1) * (1.f - fmodf(TexCoord.y, 1.f)));
|
||||
|
||||
if (mTexelFormat == eDXT1 && mBufferExists)
|
||||
{
|
||||
CMemoryInStream Buffer(mImgDataBuffer, mImgDataSize, IOUtil::SystemEndianness);
|
||||
|
||||
// 8 bytes per 4x4 16-pixel block, left-to-right top-to-bottom
|
||||
u32 BlockIdxX = TexelX / 4;
|
||||
u32 BlockIdxY = TexelY / 4;
|
||||
u32 BlocksPerRow = mWidth / 4;
|
||||
|
||||
u32 BufferPos = (8 * BlockIdxX) + (8 * BlockIdxY * BlocksPerRow);
|
||||
Buffer.Seek(BufferPos, SEEK_SET);
|
||||
|
||||
u16 PaletteA = Buffer.ReadShort();
|
||||
u16 PaletteB = Buffer.ReadShort();
|
||||
|
||||
if (PaletteA > PaletteB)
|
||||
{
|
||||
// No palette colors have alpha
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
// We only care about alpha, which is only present on palette index 3.
|
||||
// We don't need to calculate/decode the actual palette colors.
|
||||
u32 BlockCol = (TexelX & 0xF) / 4;
|
||||
u32 BlockRow = (TexelY & 0xF) / 4;
|
||||
|
||||
Buffer.Seek(BlockRow, SEEK_CUR);
|
||||
u8 Row = Buffer.ReadByte();
|
||||
u8 Shift = (u8) (6 - (BlockCol * 2));
|
||||
u8 PaletteIndex = (Row >> Shift) & 0x3;
|
||||
return (PaletteIndex == 3 ? 0.f : 1.f);
|
||||
}
|
||||
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
bool CTexture::WriteDDS(COutputStream& out)
|
||||
{
|
||||
if (!out.IsValid()) return false;
|
||||
|
||||
CopyGLBuffer();
|
||||
|
||||
out.WriteString("DDS ", 4); // "DDS " fourCC
|
||||
out.WriteLong(0x7C); // dwSize
|
||||
out.WriteLong(0x21007); // dwFlags
|
||||
out.WriteLong(mHeight); // dwHeight
|
||||
out.WriteLong(mWidth); // dwWidth
|
||||
out.WriteLong(mLinearSize); // dwPitchOrLinearSize
|
||||
out.WriteLong(0); // dwDepth
|
||||
out.WriteLong(mNumMipMaps - 1); // dwMipMapCount
|
||||
|
||||
for (u32 i = 0; i < 11; i++)
|
||||
out.WriteLong(0); // dwReserved1[11]
|
||||
|
||||
// DDS_PIXELFORMAT
|
||||
out.WriteLong(32); // DDS_PIXELFORMAT.dwSize
|
||||
|
||||
u32 pfFlags = 0, pfBpp = 0, pfRBitMask = 0, pfGBitMask = 0, pfBBitMask = 0, pfABitMask = 0;
|
||||
switch (mTexelFormat) {
|
||||
case eLuminance:
|
||||
pfFlags = 0x20000;
|
||||
pfBpp = 0x8;
|
||||
pfRBitMask = 0xFF;
|
||||
break;
|
||||
case eLuminanceAlpha:
|
||||
pfFlags = 0x20001;
|
||||
pfBpp = 0x10;
|
||||
pfRBitMask = 0x00FF;
|
||||
pfABitMask = 0xFF00;
|
||||
break;
|
||||
case eRGBA4:
|
||||
pfFlags = 0x41;
|
||||
pfBpp = 0x10;
|
||||
pfRBitMask = 0x0F00;
|
||||
pfGBitMask = 0x00F0;
|
||||
pfBBitMask = 0x000F;
|
||||
pfABitMask = 0xF000;
|
||||
break;
|
||||
case eRGB565:
|
||||
pfFlags = 0x40;
|
||||
pfBpp = 0x10;
|
||||
pfRBitMask = 0xF800;
|
||||
pfGBitMask = 0x7E0;
|
||||
pfBBitMask = 0x1F;
|
||||
break;
|
||||
case eRGBA8:
|
||||
pfFlags = 0x41;
|
||||
pfBpp = 0x20;
|
||||
pfRBitMask = 0x00FF0000;
|
||||
pfGBitMask = 0x0000FF00;
|
||||
pfBBitMask = 0x000000FF;
|
||||
pfABitMask = 0xFF000000;
|
||||
break;
|
||||
case eDXT1:
|
||||
pfFlags = 0x4;
|
||||
break;
|
||||
}
|
||||
|
||||
out.WriteLong(pfFlags); // DDS_PIXELFORMAT.dwFlags
|
||||
(mTexelFormat == eDXT1) ? out.WriteString("DXT1", 4) : out.WriteLong(0); // DDS_PIXELFORMAT.dwFourCC
|
||||
out.WriteLong(pfBpp); // DDS_PIXELFORMAT.dwRGBBitCount
|
||||
out.WriteLong(pfRBitMask); // DDS_PIXELFORMAT.dwRBitMask
|
||||
out.WriteLong(pfGBitMask); // DDS_PIXELFORMAT.dwGBitMask
|
||||
out.WriteLong(pfBBitMask); // DDS_PIXELFORMAT.dwBBitMask
|
||||
out.WriteLong(pfABitMask); // DDS_PIXELFORMAT.dwABitMask
|
||||
|
||||
out.WriteLong(0x401000); // dwCaps
|
||||
out.WriteLong(0); // dwCaps2
|
||||
out.WriteLong(0); // dwCaps3
|
||||
out.WriteLong(0); // dwCaps4
|
||||
out.WriteLong(0); // dwReserved2
|
||||
|
||||
out.WriteBytes(mImgDataBuffer, mImgDataSize); // Image data
|
||||
return true;
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
u32 CTexture::FormatBPP(ETexelFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case eGX_I4: return 4;
|
||||
case eGX_I8: return 8;
|
||||
case eGX_IA4: return 8;
|
||||
case eGX_IA8: return 16;
|
||||
case eGX_C4: return 4;
|
||||
case eGX_C8: return 8;
|
||||
case eGX_RGB565: return 16;
|
||||
case eGX_RGB5A3: return 16;
|
||||
case eGX_RGBA8: return 32;
|
||||
case eGX_CMPR: return 4;
|
||||
case eLuminance: return 8;
|
||||
case eLuminanceAlpha: return 16;
|
||||
case eRGBA4: return 16;
|
||||
case eRGB565: return 16;
|
||||
case eRGBA8: return 32;
|
||||
case eDXT1: return 4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ PRIVATE ************
|
||||
void CTexture::CalcLinearSize()
|
||||
{
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
mLinearSize = (u32) (mWidth * mHeight * BytesPerPixel);
|
||||
}
|
||||
|
||||
u32 CTexture::CalcTotalSize()
|
||||
{
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
u32 MipW = mWidth, MipH = mHeight;
|
||||
u32 Size = 0;
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
Size += (u32) (MipW * MipH * BytesPerPixel);
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
}
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
void CTexture::CopyGLBuffer()
|
||||
{
|
||||
if (!mGLBufferExists) return;
|
||||
|
||||
// Clear existing buffer
|
||||
if (mBufferExists)
|
||||
{
|
||||
delete[] mImgDataBuffer;
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
}
|
||||
|
||||
// Calculate buffer size
|
||||
mImgDataSize = CalcTotalSize();
|
||||
mImgDataBuffer = new u8[mImgDataSize];
|
||||
mBufferExists = true;
|
||||
|
||||
// Get texture
|
||||
u32 MipW = mWidth, MipH = mHeight, MipOffset = 0;
|
||||
float BytesPerPixel = FormatBPP(mTexelFormat) / 8.f;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureID);
|
||||
|
||||
for (u32 iMip = 0; iMip < mNumMipMaps; iMip++)
|
||||
{
|
||||
void *pData = mImgDataBuffer + MipOffset;
|
||||
|
||||
glGetTexImage(GL_TEXTURE_2D, iMip, GL_RGBA, GL_UNSIGNED_BYTE, pData);
|
||||
|
||||
MipOffset += (u32) (MipW * MipH * BytesPerPixel);
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
}
|
||||
|
||||
mTexelFormat = eRGBA8;
|
||||
mLinearSize = mWidth * mHeight * 4;
|
||||
}
|
||||
|
||||
void CTexture::DeleteBuffers()
|
||||
{
|
||||
if (mBufferExists)
|
||||
{
|
||||
delete[] mImgDataBuffer;
|
||||
mBufferExists = false;
|
||||
mImgDataBuffer = nullptr;
|
||||
mImgDataSize = 0;
|
||||
}
|
||||
|
||||
if (mGLBufferExists)
|
||||
{
|
||||
glDeleteTextures(1, &mTextureID);
|
||||
mGLBufferExists = false;
|
||||
}
|
||||
}
|
||||
85
src/Core/Resource/CTexture.h
Normal file
85
src/Core/Resource/CTexture.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef CTEXTURE_H
|
||||
#define CTEXTURE_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include <Common/CVector2f.h>
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <gl/glew.h>
|
||||
#include "CResource.h"
|
||||
#include "ETexelFormat.h"
|
||||
|
||||
class CTexture : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eTexture)
|
||||
friend class CTextureDecoder;
|
||||
friend class CTextureEncoder;
|
||||
|
||||
ETexelFormat mTexelFormat; // Format of decoded image data
|
||||
ETexelFormat mSourceTexelFormat; // Format of input TXTR file
|
||||
u16 mWidth, mHeight; // Image dimensions
|
||||
u32 mNumMipMaps; // The number of mipmaps this texture has
|
||||
u32 mLinearSize; // The size of the top level mipmap, in bytes
|
||||
|
||||
bool mBufferExists; // Boolean that indicates whether image data buffer has valid data
|
||||
u8 *mImgDataBuffer; // Pointer to image data buffer
|
||||
u32 mImgDataSize; // Size of image data buffer
|
||||
|
||||
bool mGLBufferExists; // Boolean that indicates whether GL buffer has valid data
|
||||
GLuint mTextureID; // ID for texture GL buffer
|
||||
|
||||
public:
|
||||
CTexture();
|
||||
CTexture(const CTexture& Source);
|
||||
CTexture(u32 Width, u32 Height);
|
||||
~CTexture();
|
||||
|
||||
bool BufferGL();
|
||||
void Bind(u32 GLTextureUnit);
|
||||
void Resize(u32 Width, u32 Height);
|
||||
float ReadTexelAlpha(const CVector2f& TexCoord);
|
||||
bool WriteDDS(COutputStream& out);
|
||||
|
||||
// Getters
|
||||
ETexelFormat TexelFormat();
|
||||
ETexelFormat SourceTexelFormat();
|
||||
u32 Width();
|
||||
u32 Height();
|
||||
u32 NumMipMaps();
|
||||
GLuint TextureID();
|
||||
|
||||
// Static
|
||||
static u32 FormatBPP(ETexelFormat Format);
|
||||
|
||||
// Private
|
||||
private:
|
||||
void CalcLinearSize();
|
||||
u32 CalcTotalSize();
|
||||
void CopyGLBuffer();
|
||||
void DeleteBuffers();
|
||||
};
|
||||
|
||||
inline ETexelFormat CTexture::TexelFormat() {
|
||||
return mTexelFormat;
|
||||
}
|
||||
|
||||
inline ETexelFormat CTexture::SourceTexelFormat() {
|
||||
return mSourceTexelFormat;
|
||||
}
|
||||
|
||||
inline u32 CTexture::Width() {
|
||||
return (u32) mWidth;
|
||||
}
|
||||
|
||||
inline u32 CTexture::Height() {
|
||||
return (u32) mHeight;
|
||||
}
|
||||
|
||||
inline u32 CTexture::NumMipMaps() {
|
||||
return mNumMipMaps;
|
||||
}
|
||||
|
||||
inline GLuint CTexture::TextureID() {
|
||||
return mTextureID;
|
||||
}
|
||||
|
||||
#endif // CTEXTURE_H
|
||||
97
src/Core/Resource/CWorld.cpp
Normal file
97
src/Core/Resource/CWorld.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "CWorld.h"
|
||||
#include "script/CScriptLayer.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
CWorld::CWorld() : CResource()
|
||||
{
|
||||
mWorldVersion = eUnknownVersion;
|
||||
mpWorldName = nullptr;
|
||||
mpDarkWorldName = nullptr;
|
||||
mpSaveWorld = nullptr;
|
||||
mpDefaultSkybox = nullptr;
|
||||
mpMapWorld = nullptr;
|
||||
}
|
||||
|
||||
CWorld::~CWorld()
|
||||
{
|
||||
}
|
||||
|
||||
void CWorld::SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex)
|
||||
{
|
||||
// The AreaIndex parameter is a placeholder until an improved world loader is implemented.
|
||||
// For now it's the easiest/fastest way to do this because this function is called from
|
||||
// the start window and the start window already knows the area index.
|
||||
SArea& AreaInfo = mAreas[AreaIndex];
|
||||
|
||||
for (u32 iLyr = 0; iLyr < pArea->GetScriptLayerCount(); iLyr++)
|
||||
{
|
||||
CScriptLayer *pLayer = pArea->GetScriptLayer(iLyr);
|
||||
SArea::SLayer& LayerInfo = AreaInfo.Layers[iLyr];
|
||||
|
||||
pLayer->SetName(LayerInfo.LayerName);
|
||||
pLayer->SetActive(LayerInfo.EnabledByDefault);
|
||||
}
|
||||
}
|
||||
|
||||
// ************ GETTERS ************
|
||||
// World
|
||||
EGame CWorld::Version()
|
||||
{
|
||||
return mWorldVersion;
|
||||
}
|
||||
|
||||
CStringTable* CWorld::GetWorldName()
|
||||
{
|
||||
return mpWorldName;
|
||||
}
|
||||
|
||||
CStringTable* CWorld::GetDarkWorldName()
|
||||
{
|
||||
return mpDarkWorldName;
|
||||
}
|
||||
|
||||
CResource* CWorld::GetSaveWorld()
|
||||
{
|
||||
return mpSaveWorld;
|
||||
}
|
||||
|
||||
CModel* CWorld::GetDefaultSkybox()
|
||||
{
|
||||
return mpDefaultSkybox;
|
||||
}
|
||||
|
||||
CResource* CWorld::GetMapWorld()
|
||||
{
|
||||
return mpMapWorld;
|
||||
}
|
||||
|
||||
// Area
|
||||
u32 CWorld::GetNumAreas()
|
||||
{
|
||||
return mAreas.size();
|
||||
}
|
||||
|
||||
u64 CWorld::GetAreaResourceID(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].FileID;
|
||||
}
|
||||
|
||||
u32 CWorld::GetAreaAttachedCount(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].AttachedAreaIDs.size();
|
||||
}
|
||||
|
||||
u32 CWorld::GetAreaAttachedID(u32 AreaIndex, u32 AttachedIndex)
|
||||
{
|
||||
return (u32) mAreas[AreaIndex].AttachedAreaIDs[AttachedIndex];
|
||||
}
|
||||
|
||||
TString CWorld::GetAreaInternalName(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].InternalName;
|
||||
}
|
||||
|
||||
CStringTable* CWorld::GetAreaName(u32 AreaIndex)
|
||||
{
|
||||
return mAreas[AreaIndex].pAreaName;
|
||||
}
|
||||
105
src/Core/Resource/CWorld.h
Normal file
105
src/Core/Resource/CWorld.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#ifndef CWORLD_H
|
||||
#define CWORLD_H
|
||||
|
||||
#include "CResource.h"
|
||||
#include "CGameArea.h"
|
||||
#include "CStringTable.h"
|
||||
#include "SDependency.h"
|
||||
#include "model/CModel.h"
|
||||
#include <Common/CTransform4f.h>
|
||||
|
||||
class CWorld : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eWorld)
|
||||
friend class CWorldLoader;
|
||||
|
||||
// Instances of CResource pointers are placeholders for unimplemented resource types (eg CMapWorld)
|
||||
EGame mWorldVersion;
|
||||
TResPtr<CStringTable> mpWorldName;
|
||||
TResPtr<CStringTable> mpDarkWorldName;
|
||||
TResPtr<CResource> mpSaveWorld;
|
||||
TResPtr<CModel> mpDefaultSkybox;
|
||||
TResPtr<CResource> mpMapWorld;
|
||||
|
||||
u32 mUnknown1;
|
||||
u32 mUnknownAreas;
|
||||
|
||||
u32 mUnknownAGSC;
|
||||
struct SAudioGrp
|
||||
{
|
||||
u32 ResID;
|
||||
u32 Unknown;
|
||||
};
|
||||
std::vector<SAudioGrp> mAudioGrps;
|
||||
|
||||
struct SMemoryRelay
|
||||
{
|
||||
u32 InstanceID;
|
||||
u32 TargetID;
|
||||
u16 Message;
|
||||
u8 Unknown;
|
||||
};
|
||||
std::vector<SMemoryRelay> mMemoryRelays;
|
||||
|
||||
struct SArea
|
||||
{
|
||||
TString InternalName;
|
||||
TResPtr<CStringTable> pAreaName;
|
||||
CTransform4f Transform;
|
||||
CAABox AetherBox;
|
||||
u64 FileID; // Loading every single area as a CResource would be a very bad idea
|
||||
u64 AreaID;
|
||||
|
||||
std::vector<u16> AttachedAreaIDs;
|
||||
std::vector<SDependency> Dependencies;
|
||||
std::vector<TString> RelFilenames;
|
||||
std::vector<u32> RelOffsets;
|
||||
u32 CommonDependenciesStart;
|
||||
|
||||
struct SDock
|
||||
{
|
||||
struct SConnectingDock
|
||||
{
|
||||
u32 AreaIndex;
|
||||
u32 DockIndex;
|
||||
};
|
||||
std::vector<SConnectingDock> ConnectingDocks;
|
||||
CVector3f DockCoordinates[4];
|
||||
};
|
||||
std::vector<SDock> Docks;
|
||||
|
||||
struct SLayer
|
||||
{
|
||||
TString LayerName;
|
||||
bool EnabledByDefault;
|
||||
u8 LayerID[16];
|
||||
u32 LayerDependenciesStart; // Offset into Dependencies vector
|
||||
};
|
||||
std::vector<SLayer> Layers;
|
||||
};
|
||||
std::vector<SArea> mAreas;
|
||||
|
||||
|
||||
public:
|
||||
CWorld();
|
||||
~CWorld();
|
||||
|
||||
void SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex);
|
||||
|
||||
// Setters
|
||||
EGame Version();
|
||||
CStringTable* GetWorldName();
|
||||
CStringTable* GetDarkWorldName();
|
||||
CResource* GetSaveWorld();
|
||||
CModel* GetDefaultSkybox();
|
||||
CResource* GetMapWorld();
|
||||
|
||||
u32 GetNumAreas();
|
||||
u64 GetAreaResourceID(u32 AreaIndex);
|
||||
u32 GetAreaAttachedCount(u32 AreaIndex);
|
||||
u32 GetAreaAttachedID(u32 AreaIndex, u32 AttachedIndex);
|
||||
TString GetAreaInternalName(u32 AreaIndex);
|
||||
CStringTable* GetAreaName(u32 AreaIndex);
|
||||
};
|
||||
|
||||
#endif // CWORLD_H
|
||||
364
src/Core/Resource/Cooker/CMaterialCooker.cpp
Normal file
364
src/Core/Resource/Cooker/CMaterialCooker.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#include "CMaterialCooker.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
CMaterialCooker::CMaterialCooker()
|
||||
{
|
||||
mpMat = nullptr;
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMatSetPrime(COutputStream& Out)
|
||||
{
|
||||
// Gather texture list from the materials before starting
|
||||
mTextureIDs.clear();
|
||||
u32 NumMats = mpSet->mMaterials.size();
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
{
|
||||
CMaterial *pMat = mpSet->mMaterials[iMat];
|
||||
|
||||
u32 NumPasses = pMat->PassCount();
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CTexture *pTex = pMat->Pass(iPass)->Texture();
|
||||
if (pTex)
|
||||
mTextureIDs.push_back(pTex->ResID().ToLong());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort/remove duplicates
|
||||
std::sort(mTextureIDs.begin(), mTextureIDs.end());
|
||||
mTextureIDs.erase(std::unique(mTextureIDs.begin(), mTextureIDs.end()), mTextureIDs.end());
|
||||
|
||||
// Write texture IDs
|
||||
Out.WriteLong(mTextureIDs.size());
|
||||
|
||||
for (u32 iTex = 0; iTex < mTextureIDs.size(); iTex++)
|
||||
Out.WriteLong(mTextureIDs[iTex]);
|
||||
|
||||
// Write material offset filler
|
||||
Out.WriteLong(NumMats);
|
||||
u32 MatOffsetsStart = Out.Tell();
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
Out.WriteLong(0);
|
||||
|
||||
// Write materials
|
||||
u32 MatsStart = Out.Tell();
|
||||
std::vector<u32> MatEndOffsets(NumMats);
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
{
|
||||
mpMat = mpSet->mMaterials[iMat];
|
||||
WriteMaterialPrime(Out);
|
||||
MatEndOffsets[iMat] = Out.Tell() - MatsStart;
|
||||
}
|
||||
|
||||
// Write material offsets
|
||||
u32 MatsEnd = Out.Tell();
|
||||
Out.Seek(MatOffsetsStart, SEEK_SET);
|
||||
|
||||
for (u32 iMat = 0; iMat < NumMats; iMat++)
|
||||
Out.WriteLong(MatEndOffsets[iMat]);
|
||||
|
||||
// Done!
|
||||
Out.Seek(MatsEnd, SEEK_SET);
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMatSetCorruption(COutputStream&)
|
||||
{
|
||||
// Not using parameter 1 (COutputStream& - Out)
|
||||
// todo
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMaterialPrime(COutputStream& Out)
|
||||
{
|
||||
// Gather data from the passes before we start writing
|
||||
u32 TexFlags = 0;
|
||||
u32 NumKonst = 0;
|
||||
std::vector<u32> TexIndices;
|
||||
|
||||
for (u32 iPass = 0; iPass < mpMat->mPasses.size(); iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
|
||||
if ((pPass->KColorSel() >= 0xC) || (pPass->KAlphaSel() >= 0x10))
|
||||
{
|
||||
// Determine the highest Konst index being used
|
||||
u32 KColorIndex = pPass->KColorSel() % 4;
|
||||
u32 KAlphaIndex = pPass->KAlphaSel() % 4;
|
||||
|
||||
if (KColorIndex >= NumKonst)
|
||||
NumKonst = KColorIndex + 1;
|
||||
if (KAlphaIndex >= NumKonst)
|
||||
NumKonst = KAlphaIndex + 1;
|
||||
}
|
||||
|
||||
CTexture *pPassTex = pPass->Texture();
|
||||
if (pPassTex != nullptr)
|
||||
{
|
||||
TexFlags |= (1 << iPass);
|
||||
u32 TexID = pPassTex->ResID().ToLong();
|
||||
|
||||
for (u32 iTex = 0; iTex < mTextureIDs.size(); iTex++)
|
||||
{
|
||||
if (mTextureIDs[iTex] == TexID)
|
||||
{
|
||||
TexIndices.push_back(iTex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get group index
|
||||
u32 GroupIndex;
|
||||
u64 MatHash = mpMat->HashParameters();
|
||||
bool NewHash = true;
|
||||
|
||||
for (u32 iHash = 0; iHash < mMaterialHashes.size(); iHash++)
|
||||
{
|
||||
if (mMaterialHashes[iHash] == MatHash)
|
||||
{
|
||||
GroupIndex = iHash;
|
||||
NewHash = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NewHash)
|
||||
{
|
||||
GroupIndex = mMaterialHashes.size();
|
||||
mMaterialHashes.push_back(MatHash);
|
||||
}
|
||||
|
||||
// Start writing!
|
||||
// Generate flags value
|
||||
bool HasKonst = (NumKonst > 0);
|
||||
u32 Flags;
|
||||
|
||||
if (mVersion <= ePrime)
|
||||
Flags = 0x1003;
|
||||
else
|
||||
Flags = 0x4002;
|
||||
|
||||
Flags |= (HasKonst << 3) | mpMat->Options() | (TexFlags << 16);
|
||||
|
||||
Out.WriteLong(Flags);
|
||||
|
||||
// Texture indices
|
||||
Out.WriteLong(TexIndices.size());
|
||||
for (u32 iTex = 0; iTex < TexIndices.size(); iTex++)
|
||||
Out.WriteLong(TexIndices[iTex]);
|
||||
|
||||
// Vertex description
|
||||
EVertexDescription Desc = mpMat->VtxDesc();
|
||||
|
||||
if (mVersion < eEchoes)
|
||||
Desc = (EVertexDescription) (Desc & 0x00FFFFFF);
|
||||
|
||||
Out.WriteLong(Desc);
|
||||
|
||||
// Echoes unknowns
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
Out.WriteLong(mpMat->EchoesUnknownA());
|
||||
Out.WriteLong(mpMat->EchoesUnknownB());
|
||||
}
|
||||
|
||||
// Group index
|
||||
Out.WriteLong(GroupIndex);
|
||||
|
||||
// Konst
|
||||
if (HasKonst)
|
||||
{
|
||||
Out.WriteLong(NumKonst);
|
||||
for (u32 iKonst = 0; iKonst < NumKonst; iKonst++)
|
||||
Out.WriteLong( mpMat->Konst(iKonst).AsLongRGBA() );
|
||||
}
|
||||
|
||||
// Blend Mode
|
||||
// Some modifications are done to convert the GLenum to the corresponding GX enum
|
||||
u16 BlendSrcFac = (u16) mpMat->BlendSrcFac();
|
||||
u16 BlendDstFac = (u16) mpMat->BlendDstFac();
|
||||
if (BlendSrcFac >= 0x300) BlendSrcFac -= 0x2FE;
|
||||
if (BlendDstFac >= 0x300) BlendDstFac -= 0x2FE;
|
||||
Out.WriteShort(BlendDstFac);
|
||||
Out.WriteShort(BlendSrcFac);
|
||||
|
||||
// Color Channels
|
||||
Out.WriteLong(1);
|
||||
Out.WriteLong(0x3000 | (mpMat->IsLightingEnabled() ? 1 : 0));
|
||||
|
||||
// TEV
|
||||
u32 NumPasses = mpMat->PassCount();
|
||||
Out.WriteLong(NumPasses);
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
|
||||
u32 ColorInputFlags = ((pPass->ColorInput(0)) |
|
||||
(pPass->ColorInput(1) << 5) |
|
||||
(pPass->ColorInput(2) << 10) |
|
||||
(pPass->ColorInput(3) << 15));
|
||||
u32 AlphaInputFlags = ((pPass->AlphaInput(0)) |
|
||||
(pPass->AlphaInput(1) << 5) |
|
||||
(pPass->AlphaInput(2) << 10) |
|
||||
(pPass->AlphaInput(3) << 15));
|
||||
|
||||
u32 ColorOpFlags = 0x100 | (pPass->ColorOutput() << 9);
|
||||
u32 AlphaOpFlags = 0x100 | (pPass->AlphaOutput() << 9);
|
||||
|
||||
Out.WriteLong(ColorInputFlags);
|
||||
Out.WriteLong(AlphaInputFlags);
|
||||
Out.WriteLong(ColorOpFlags);
|
||||
Out.WriteLong(AlphaOpFlags);
|
||||
Out.WriteByte(0); // Padding
|
||||
Out.WriteByte(pPass->KAlphaSel());
|
||||
Out.WriteByte(pPass->KColorSel());
|
||||
Out.WriteByte(pPass->RasSel());
|
||||
}
|
||||
|
||||
// TEV Tex/UV input selection
|
||||
u32 CurTexIdx = 0;
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
Out.WriteShort(0); // Padding
|
||||
|
||||
if (mpMat->Pass(iPass)->Texture())
|
||||
{
|
||||
Out.WriteByte((u8) CurTexIdx);
|
||||
Out.WriteByte((u8) CurTexIdx);
|
||||
CurTexIdx++;
|
||||
}
|
||||
|
||||
else
|
||||
Out.WriteShort((u16) 0xFFFF);
|
||||
}
|
||||
|
||||
// TexGen
|
||||
u32 NumTexCoords = CurTexIdx; // TexIdx is currently equal to the tex coord count
|
||||
Out.WriteLong(NumTexCoords);
|
||||
u32 CurTexMtx = 0;
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
if (pPass->Texture() == nullptr) continue;
|
||||
|
||||
u32 AnimType = pPass->AnimMode();
|
||||
u32 CoordSource = pPass->TexCoordSource();
|
||||
|
||||
u32 TexMtxIdx, PostMtxIdx;
|
||||
bool Normalize;
|
||||
|
||||
// No animation - set TexMtx and PostMtx to identity, disable normalization
|
||||
if (AnimType == eNoUVAnim)
|
||||
{
|
||||
TexMtxIdx = 30;
|
||||
PostMtxIdx = 61;
|
||||
Normalize = false;
|
||||
}
|
||||
|
||||
// Animation - set parameters as the animation mode needs them
|
||||
else
|
||||
{
|
||||
TexMtxIdx = CurTexMtx;
|
||||
|
||||
if ((AnimType < 2) || (AnimType > 5))
|
||||
{
|
||||
PostMtxIdx = CurTexMtx;
|
||||
Normalize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PostMtxIdx = 61;
|
||||
Normalize = false;
|
||||
}
|
||||
CurTexMtx += 3;
|
||||
}
|
||||
|
||||
u32 TexGenFlags = (CoordSource << 4) | (TexMtxIdx << 9) | (Normalize << 14) | (PostMtxIdx << 15);
|
||||
Out.WriteLong(TexGenFlags);
|
||||
}
|
||||
|
||||
// Animations
|
||||
u32 AnimSizeOffset = Out.Tell();
|
||||
u32 NumAnims = CurTexMtx; // CurTexMtx is currently equal to the anim count
|
||||
Out.WriteLong(0); // Anim size filler
|
||||
u32 AnimsStart = Out.Tell();
|
||||
Out.WriteLong(NumAnims);
|
||||
|
||||
for (u32 iPass = 0; iPass < NumPasses; iPass++)
|
||||
{
|
||||
CMaterialPass *pPass = mpMat->Pass(iPass);
|
||||
u32 AnimMode = pPass->AnimMode();
|
||||
if (AnimMode == eNoUVAnim) continue;
|
||||
|
||||
Out.WriteLong(AnimMode);
|
||||
|
||||
if ((AnimMode > 1) && (AnimMode != 6))
|
||||
{
|
||||
Out.WriteFloat(pPass->AnimParam(0));
|
||||
Out.WriteFloat(pPass->AnimParam(1));
|
||||
|
||||
if ((AnimMode == 2) || (AnimMode == 4) || (AnimMode == 5))
|
||||
{
|
||||
Out.WriteFloat(pPass->AnimParam(2));
|
||||
Out.WriteFloat(pPass->AnimParam(3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 AnimsEnd = Out.Tell();
|
||||
u32 AnimsSize = AnimsEnd - AnimsStart;
|
||||
Out.Seek(AnimSizeOffset, SEEK_SET);
|
||||
Out.WriteLong(AnimsSize);
|
||||
Out.Seek(AnimsEnd, SEEK_SET);
|
||||
|
||||
// Done!
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteMaterialCorruption(COutputStream&)
|
||||
{
|
||||
// Not using parameter 1 (COutputStream& - Out)
|
||||
// todo
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
void CMaterialCooker::WriteCookedMatSet(CMaterialSet *pSet, EGame Version, COutputStream &Out)
|
||||
{
|
||||
CMaterialCooker Cooker;
|
||||
Cooker.mpSet = pSet;
|
||||
Cooker.mVersion = Version;
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Cooker.WriteMatSetPrime(Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CMaterialCooker::WriteCookedMaterial(CMaterial *pMat, EGame Version, COutputStream &Out)
|
||||
{
|
||||
CMaterialCooker Cooker;
|
||||
Cooker.mpMat = pMat;
|
||||
Cooker.mVersion = Version;
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Cooker.WriteMaterialPrime(Out);
|
||||
break;
|
||||
// TODO: Corruption/Uncooked
|
||||
}
|
||||
}
|
||||
27
src/Core/Resource/Cooker/CMaterialCooker.h
Normal file
27
src/Core/Resource/Cooker/CMaterialCooker.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef CMATERIALCOOKER_H
|
||||
#define CMATERIALCOOKER_H
|
||||
|
||||
#include "../CMaterial.h"
|
||||
#include "../CMaterialSet.h"
|
||||
#include "../EFormatVersion.h"
|
||||
|
||||
class CMaterialCooker
|
||||
{
|
||||
CMaterialSet *mpSet;
|
||||
CMaterial *mpMat;
|
||||
EGame mVersion;
|
||||
std::vector<u32> mTextureIDs;
|
||||
std::vector<u64> mMaterialHashes;
|
||||
|
||||
CMaterialCooker();
|
||||
void WriteMatSetPrime(COutputStream& Out);
|
||||
void WriteMatSetCorruption(COutputStream& Out);
|
||||
void WriteMaterialPrime(COutputStream& Out);
|
||||
void WriteMaterialCorruption(COutputStream& Out);
|
||||
|
||||
public:
|
||||
static void WriteCookedMatSet(CMaterialSet *pSet, EGame Version, COutputStream& Out);
|
||||
static void WriteCookedMaterial(CMaterial *pMat, EGame Version, COutputStream& Out);
|
||||
};
|
||||
|
||||
#endif // CMATERIALCOOKER_H
|
||||
275
src/Core/Resource/Cooker/CModelCooker.cpp
Normal file
275
src/Core/Resource/Cooker/CModelCooker.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include "CModelCooker.h"
|
||||
#include "CMaterialCooker.h"
|
||||
#include "CSectionMgrOut.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
CModelCooker::CModelCooker()
|
||||
{
|
||||
}
|
||||
|
||||
bool SortVertsByArrayPos(const CVertex& A, const CVertex& B) {
|
||||
return (A.ArrayPosition < B.ArrayPosition);
|
||||
}
|
||||
|
||||
bool CheckDuplicateVertsByArrayPos(const CVertex& A, const CVertex& B) {
|
||||
return (A.ArrayPosition == B.ArrayPosition);
|
||||
}
|
||||
|
||||
void CModelCooker::GenerateSurfaceData()
|
||||
{
|
||||
// Need to gather metadata from the model before we can start
|
||||
mNumMatSets = mpModel->mMaterialSets.size();
|
||||
mNumSurfaces = mpModel->mSurfaces.size();
|
||||
mNumVertices = mpModel->mVertexCount;
|
||||
mVertices.resize(mNumVertices);
|
||||
|
||||
// Get vertex attributes
|
||||
mVtxAttribs = eNoAttributes;
|
||||
|
||||
for (u32 iMat = 0; iMat < mpModel->GetMatCount(); iMat++)
|
||||
{
|
||||
CMaterial *pMat = mpModel->GetMaterialByIndex(0, iMat);
|
||||
mVtxAttribs |= pMat->VtxDesc();
|
||||
}
|
||||
|
||||
// Get vertices
|
||||
u32 MaxIndex = 0;
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
{
|
||||
u32 NumPrimitives = mpModel->mSurfaces[iSurf]->Primitives.size();
|
||||
|
||||
for (u32 iPrim = 0; iPrim < NumPrimitives; iPrim++)
|
||||
{
|
||||
SSurface::SPrimitive *pPrim = &mpModel->mSurfaces[iSurf]->Primitives[iPrim];
|
||||
u32 NumVerts = pPrim->Vertices.size();
|
||||
|
||||
for (u32 iVtx = 0; iVtx < NumVerts; iVtx++)
|
||||
{
|
||||
u32 VertIndex = pPrim->Vertices[iVtx].ArrayPosition;
|
||||
mVertices[VertIndex] = pPrim->Vertices[iVtx];
|
||||
|
||||
if (VertIndex > MaxIndex) MaxIndex = VertIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mVertices.resize(MaxIndex + 1);
|
||||
mNumVertices = mVertices.size();
|
||||
}
|
||||
|
||||
void CModelCooker::WriteEditorModel(COutputStream& /*Out*/)
|
||||
{
|
||||
}
|
||||
|
||||
void CModelCooker::WriteModelPrime(COutputStream& Out)
|
||||
{
|
||||
GenerateSurfaceData();
|
||||
|
||||
// Header
|
||||
Out.WriteLong(0xDEADBABE);
|
||||
Out.WriteLong(GetCMDLVersion(mVersion));
|
||||
Out.WriteLong(5);
|
||||
mpModel->mAABox.Write(Out);
|
||||
|
||||
u32 NumSections = mNumMatSets + mNumSurfaces + 6;
|
||||
Out.WriteLong(NumSections);
|
||||
Out.WriteLong(mNumMatSets);
|
||||
|
||||
u32 SectionSizesOffset = Out.Tell();
|
||||
for (u32 iSec = 0; iSec < NumSections; iSec++)
|
||||
Out.WriteLong(0);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
|
||||
std::vector<u32> SectionSizes;
|
||||
SectionSizes.reserve(NumSections);
|
||||
|
||||
CSectionMgrOut SectionMgr;
|
||||
SectionMgr.SetSectionCount(NumSections);
|
||||
SectionMgr.Init(Out);
|
||||
|
||||
// Materials
|
||||
for (u32 iSet = 0; iSet < mNumMatSets; iSet++)
|
||||
{
|
||||
CMaterialCooker::WriteCookedMatSet(mpModel->mMaterialSets[iSet], mVersion, Out);
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
}
|
||||
|
||||
// Vertices
|
||||
for (u32 iPos = 0; iPos < mNumVertices; iPos++)
|
||||
mVertices[iPos].Position.Write(Out);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Normals
|
||||
for (u32 iNrm = 0; iNrm < mNumVertices; iNrm++)
|
||||
mVertices[iNrm].Normal.Write(Out);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Colors
|
||||
for (u32 iColor = 0; iColor < mNumVertices; iColor++)
|
||||
mVertices[iColor].Color[0].Write(Out);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Float UV coordinates
|
||||
for (u32 iTexSlot = 0; iTexSlot < 8; iTexSlot++)
|
||||
{
|
||||
bool HasTexSlot = (mVtxAttribs & (eTex0 << (iTexSlot * 2))) != 0;
|
||||
if (HasTexSlot)
|
||||
{
|
||||
for (u32 iTex = 0; iTex < mNumVertices; iTex++)
|
||||
mVertices[iTex].Tex[iTexSlot].Write(Out);
|
||||
}
|
||||
}
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
SectionMgr.AddSize(Out); // Skipping short UV coordinates
|
||||
|
||||
// Surface offsets
|
||||
Out.WriteLong(mNumSurfaces);
|
||||
u32 SurfaceOffsetsStart = Out.Tell();
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
Out.WriteLong(0);
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
SectionMgr.AddSize(Out);
|
||||
|
||||
// Surfaces
|
||||
u32 SurfacesStart = Out.Tell();
|
||||
std::vector<u32> SurfaceEndOffsets(mNumSurfaces);
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
{
|
||||
SSurface *pSurface = mpModel->GetSurface(iSurf);
|
||||
|
||||
pSurface->CenterPoint.Write(Out);
|
||||
Out.WriteLong(pSurface->MaterialID);
|
||||
Out.WriteShort((u16) 0x8000);
|
||||
u32 PrimTableSizeOffset = Out.Tell();
|
||||
Out.WriteShort(0);
|
||||
Out.WriteLongLong(0);
|
||||
Out.WriteLong(0);
|
||||
pSurface->ReflectionDirection.Write(Out);
|
||||
Out.WriteToBoundary(32, 0);
|
||||
|
||||
u32 PrimTableStart = Out.Tell();
|
||||
EVertexDescription MatAttribs = mpModel->GetMaterialBySurface(0, iSurf)->VtxDesc();
|
||||
|
||||
for (u32 iPrim = 0; iPrim < pSurface->Primitives.size(); iPrim++)
|
||||
{
|
||||
SSurface::SPrimitive *pPrimitive = &pSurface->Primitives[iPrim];
|
||||
Out.WriteByte((u8) pPrimitive->Type);
|
||||
Out.WriteShort((u16) pPrimitive->Vertices.size());
|
||||
|
||||
for (u32 iVert = 0; iVert < pPrimitive->Vertices.size(); iVert++)
|
||||
{
|
||||
CVertex *pVert = &pPrimitive->Vertices[iVert];
|
||||
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
for (u32 iMtxAttribs = 0; iMtxAttribs < 8; iMtxAttribs++)
|
||||
if (MatAttribs & (ePosMtx << iMtxAttribs))
|
||||
Out.WriteByte(pVert->MatrixIndices[iMtxAttribs]);
|
||||
}
|
||||
|
||||
u16 VertexIndex = (u16) pVert->ArrayPosition;
|
||||
|
||||
if (MatAttribs & ePosition)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
if (MatAttribs & eNormal)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
if (MatAttribs & eColor0)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
if (MatAttribs & eColor1)
|
||||
Out.WriteShort(VertexIndex);
|
||||
|
||||
u16 TexOffset = 0;
|
||||
for (u32 iTex = 0; iTex < 8; iTex++)
|
||||
{
|
||||
if (MatAttribs & (eTex0 << (iTex * 2)))
|
||||
{
|
||||
Out.WriteShort(VertexIndex + TexOffset);
|
||||
TexOffset += (u16) mNumVertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Out.WriteToBoundary(32, 0);
|
||||
u32 PrimTableEnd = Out.Tell();
|
||||
u32 PrimTableSize = PrimTableEnd - PrimTableStart;
|
||||
Out.Seek(PrimTableSizeOffset, SEEK_SET);
|
||||
Out.WriteShort((u16) PrimTableSize);
|
||||
Out.Seek(PrimTableEnd, SEEK_SET);
|
||||
|
||||
SectionMgr.AddSize(Out);
|
||||
SurfaceEndOffsets[iSurf] = Out.Tell() - SurfacesStart;
|
||||
}
|
||||
|
||||
// Done writing the file - now we go back to fill in surface offsets + section sizes
|
||||
Out.Seek(SurfaceOffsetsStart, SEEK_SET);
|
||||
|
||||
for (u32 iSurf = 0; iSurf < mNumSurfaces; iSurf++)
|
||||
Out.WriteLong(SurfaceEndOffsets[iSurf]);
|
||||
|
||||
Out.Seek(SectionSizesOffset, SEEK_SET);
|
||||
SectionMgr.WriteSizes(Out);
|
||||
|
||||
// Done!
|
||||
}
|
||||
|
||||
void CModelCooker::WriteCookedModel(CModel *pModel, EGame Version, COutputStream& CMDL)
|
||||
{
|
||||
CModelCooker Cooker;
|
||||
Cooker.mpModel = pModel;
|
||||
Cooker.mVersion = Version;
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Cooker.WriteModelPrime(CMDL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CModelCooker::WriteUncookedModel(CModel* /*pModel*/, COutputStream& /*EMDL*/)
|
||||
{
|
||||
}
|
||||
|
||||
u32 CModelCooker::GetCMDLVersion(EGame Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
return 0x2;
|
||||
case eEchoesDemo:
|
||||
return 0x3;
|
||||
case eEchoes:
|
||||
return 0x4;
|
||||
case eCorruptionProto:
|
||||
case eCorruption:
|
||||
return 0x5;
|
||||
case eReturns:
|
||||
return 0xA;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
30
src/Core/Resource/Cooker/CModelCooker.h
Normal file
30
src/Core/Resource/Cooker/CModelCooker.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef CMODELCOOKER_H
|
||||
#define CMODELCOOKER_H
|
||||
|
||||
#include "../model/CModel.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <FileIO/FileIO.h>
|
||||
|
||||
class CModelCooker
|
||||
{
|
||||
TResPtr<CModel> mpModel;
|
||||
EGame mVersion;
|
||||
u32 mNumMatSets;
|
||||
u32 mNumSurfaces;
|
||||
u32 mNumVertices;
|
||||
u8 mVertexFormat;
|
||||
std::vector<CVertex> mVertices;
|
||||
EVertexDescription mVtxAttribs;
|
||||
|
||||
CModelCooker();
|
||||
void GenerateSurfaceData();
|
||||
void WriteEditorModel(COutputStream& Out);
|
||||
void WriteModelPrime(COutputStream& Out);
|
||||
|
||||
public:
|
||||
static void WriteCookedModel(CModel *pModel, EGame Version, COutputStream& Out);
|
||||
static void WriteUncookedModel(CModel *pModel, COutputStream& Out);
|
||||
static u32 GetCMDLVersion(EGame Version);
|
||||
};
|
||||
|
||||
#endif // CMODELCOOKER_H
|
||||
33
src/Core/Resource/Cooker/CSectionMgrOut.cpp
Normal file
33
src/Core/Resource/Cooker/CSectionMgrOut.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "CSectionMgrOut.h"
|
||||
|
||||
CSectionMgrOut::CSectionMgrOut()
|
||||
{
|
||||
mSectionCount = 0;
|
||||
mCurSectionStart = 0;
|
||||
mCurSectionIndex = 0;
|
||||
}
|
||||
|
||||
void CSectionMgrOut::SetSectionCount(u32 Count)
|
||||
{
|
||||
mSectionCount = Count;
|
||||
mSectionSizes.resize(Count);
|
||||
}
|
||||
|
||||
void CSectionMgrOut::Init(const COutputStream& OutputStream)
|
||||
{
|
||||
mCurSectionStart = OutputStream.Tell();
|
||||
mCurSectionIndex = 0;
|
||||
}
|
||||
|
||||
void CSectionMgrOut::AddSize(COutputStream& OutputStream)
|
||||
{
|
||||
mSectionSizes[mCurSectionIndex] = OutputStream.Tell() - mCurSectionStart;
|
||||
mCurSectionIndex++;
|
||||
mCurSectionStart = OutputStream.Tell();
|
||||
}
|
||||
|
||||
void CSectionMgrOut::WriteSizes(COutputStream& OutputStream)
|
||||
{
|
||||
for (u32 iSec = 0; iSec < mSectionCount; iSec++)
|
||||
OutputStream.WriteLong(mSectionSizes[iSec]);
|
||||
}
|
||||
24
src/Core/Resource/Cooker/CSectionMgrOut.h
Normal file
24
src/Core/Resource/Cooker/CSectionMgrOut.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CBLOCKMGROUT_H
|
||||
#define CBLOCKMGROUT_H
|
||||
|
||||
#include <FileIO/COutputStream.h>
|
||||
#include <Common/types.h>
|
||||
#include <vector>
|
||||
|
||||
// Small class to manage file sections for CMDL/MREA output
|
||||
class CSectionMgrOut
|
||||
{
|
||||
u32 mSectionCount;
|
||||
u32 mCurSectionStart;
|
||||
u32 mCurSectionIndex;
|
||||
std::vector<u32> mSectionSizes;
|
||||
|
||||
public:
|
||||
CSectionMgrOut();
|
||||
void SetSectionCount(u32 Count);
|
||||
void Init(const COutputStream& OutputStream);
|
||||
void AddSize(COutputStream& OutputStream);
|
||||
void WriteSizes(COutputStream& OutputStream);
|
||||
};
|
||||
|
||||
#endif // CBLOCKMGROUT_H
|
||||
580
src/Core/Resource/Cooker/CTemplateWriter.cpp
Normal file
580
src/Core/Resource/Cooker/CTemplateWriter.cpp
Normal file
@@ -0,0 +1,580 @@
|
||||
#include "CTemplateWriter.h"
|
||||
#include "../cooker/CWorldCooker.h"
|
||||
#include <tinyxml2.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
using namespace tinyxml2;
|
||||
|
||||
CTemplateWriter::CTemplateWriter()
|
||||
{
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveAllTemplates()
|
||||
{
|
||||
// Create directory
|
||||
std::list<CMasterTemplate*> masterList = CMasterTemplate::GetMasterList();
|
||||
TString out = "../templates/";
|
||||
boost::filesystem::create_directory(out.ToStdString());
|
||||
|
||||
// Resave master templates
|
||||
for (auto it = masterList.begin(); it != masterList.end(); it++)
|
||||
SaveGameTemplates(*it, out);
|
||||
|
||||
// Resave game list
|
||||
XMLDocument gameList;
|
||||
|
||||
XMLDeclaration *pDecl = gameList.NewDeclaration();
|
||||
gameList.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = gameList.NewElement("GameList");
|
||||
pBase->SetAttribute("version", 3);
|
||||
gameList.LinkEndChild(pBase);
|
||||
|
||||
for (auto it = masterList.begin(); it != masterList.end(); it++)
|
||||
{
|
||||
CMasterTemplate *pMaster = *it;
|
||||
|
||||
XMLElement *pGame = gameList.NewElement("game");
|
||||
|
||||
XMLElement *pGameName = gameList.NewElement("name");
|
||||
pGameName->SetText(*pMaster->mGameName);
|
||||
|
||||
XMLElement *pWorldVersion = gameList.NewElement("mlvl");
|
||||
u32 versionNumber = CWorldCooker::GetMLVLVersion(pMaster->GetGame());
|
||||
pWorldVersion->SetText(*TString::HexString(versionNumber, true, true, 2));
|
||||
|
||||
XMLElement *pTempPath = gameList.NewElement("master");
|
||||
pTempPath->SetText(*pMaster->mSourceFile);
|
||||
|
||||
pGame->LinkEndChild(pGameName);
|
||||
pGame->LinkEndChild(pWorldVersion);
|
||||
pGame->LinkEndChild(pTempPath);
|
||||
pBase->LinkEndChild(pGame);
|
||||
}
|
||||
|
||||
gameList.SaveFile(*(out + "GameList.xml"));
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveGameTemplates(CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pMaster->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
boost::filesystem::create_directory(outDir.ToStdString());
|
||||
|
||||
// Resave script templates
|
||||
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
|
||||
SaveScriptTemplate(it->second, outDir);
|
||||
|
||||
// Resave master template
|
||||
XMLDocument master;
|
||||
|
||||
XMLDeclaration *pDecl = master.NewDeclaration();
|
||||
master.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = master.NewElement("MasterTemplate");
|
||||
pBase->SetAttribute("version", 3);
|
||||
master.LinkEndChild(pBase);
|
||||
|
||||
// Write property list
|
||||
if (!pMaster->mPropertyList.empty())
|
||||
{
|
||||
SavePropertyList(pMaster, outDir);
|
||||
|
||||
XMLElement *pPropList = master.NewElement("properties");
|
||||
pPropList->SetText("Properties.xml");
|
||||
pBase->LinkEndChild(pPropList);
|
||||
}
|
||||
|
||||
// Write script objects
|
||||
XMLElement *pObjects = master.NewElement("objects");
|
||||
pBase->LinkEndChild(pObjects);
|
||||
|
||||
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
|
||||
{
|
||||
TString objID;
|
||||
u32 intID = (it->second)->ObjectID();
|
||||
if (intID <= 0xFF) objID = TString::HexString(intID, true, true, 2);
|
||||
else objID = CFourCC(intID).ToString();
|
||||
|
||||
XMLElement *pObj = master.NewElement("object");
|
||||
pObj->SetAttribute("ID", *objID);
|
||||
pObj->SetAttribute("template", *(it->second)->mSourceFile);
|
||||
pObjects->LinkEndChild(pObj);
|
||||
}
|
||||
|
||||
// Write script states/messages
|
||||
std::map<u32, TString> *pMaps[2] = { &pMaster->mStates, &pMaster->mMessages };
|
||||
TString types[2] = { "state", "message" };
|
||||
|
||||
for (u32 iScr = 0; iScr < 2; iScr++)
|
||||
{
|
||||
XMLElement *pElem = master.NewElement(*(types[iScr] + "s"));
|
||||
pBase->LinkEndChild(pElem);
|
||||
|
||||
for (auto it = pMaps[iScr]->begin(); it != pMaps[iScr]->end(); it++)
|
||||
{
|
||||
TString ID;
|
||||
if (it->first <= 0xFF) ID = TString::HexString(it->first, true, true, 2);
|
||||
else ID = CFourCC(it->first).ToString();
|
||||
|
||||
XMLElement *pSubElem = master.NewElement(*types[iScr]);
|
||||
pSubElem->SetAttribute("ID", *ID);
|
||||
pSubElem->SetAttribute("name", *(it->second));
|
||||
pElem->LinkEndChild(pSubElem);
|
||||
}
|
||||
}
|
||||
|
||||
// Save file
|
||||
master.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SavePropertyList(CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
// Create XML
|
||||
XMLDocument list;
|
||||
|
||||
XMLDeclaration *pDecl = list.NewDeclaration();
|
||||
list.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = list.NewElement("Properties");
|
||||
pBase->SetAttribute("version", 3);
|
||||
list.LinkEndChild(pBase);
|
||||
|
||||
// Write properties
|
||||
for (auto it = pMaster->mPropertyList.begin(); it != pMaster->mPropertyList.end(); it++)
|
||||
{
|
||||
CPropertyTemplate *pTemp = it->second;
|
||||
|
||||
if (pTemp->Type() == eStructProperty)
|
||||
{
|
||||
CStructTemplate *pStructTemp = static_cast<CStructTemplate*>(pTemp);
|
||||
|
||||
XMLElement *pElem = list.NewElement("struct");
|
||||
pElem->SetAttribute("ID", *TString::HexString(pTemp->PropertyID(), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->Name());
|
||||
|
||||
if (!pStructTemp->mSourceFile.IsEmpty())
|
||||
{
|
||||
SaveStructTemplate(pStructTemp, pMaster, dir);
|
||||
pElem->SetAttribute("template", *pStructTemp->mSourceFile);
|
||||
}
|
||||
|
||||
pBase->LinkEndChild(pElem);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
XMLElement *pElem = list.NewElement("property");
|
||||
pElem->SetAttribute("ID", *TString::HexString(pTemp->PropertyID(), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->Name());
|
||||
pElem->SetAttribute("type", *PropEnumToPropString(pTemp->Type()));
|
||||
|
||||
if (pTemp->Type() == eFileProperty)
|
||||
{
|
||||
// Construct extension list string
|
||||
CFileTemplate *pFileProp = static_cast<CFileTemplate*>(pTemp);
|
||||
const TStringList& extensions = pFileProp->Extensions();
|
||||
|
||||
TString strList = "";
|
||||
|
||||
for (auto it = extensions.begin(); it != extensions.end();)
|
||||
{
|
||||
strList += *it;
|
||||
it++;
|
||||
if (it != extensions.end()) strList += ",";
|
||||
}
|
||||
|
||||
pElem->SetAttribute("ext", *strList);
|
||||
}
|
||||
|
||||
pBase->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
|
||||
list.SaveFile(*(dir + "Properties.xml"));
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveScriptTemplate(CScriptTemplate *pTemp, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
boost::filesystem::create_directory(*outDir);
|
||||
|
||||
// Create new document
|
||||
XMLDocument scriptXML;
|
||||
|
||||
XMLDeclaration *pDecl = scriptXML.NewDeclaration();
|
||||
scriptXML.LinkEndChild(pDecl);
|
||||
|
||||
// Base element
|
||||
XMLElement *pBase = scriptXML.NewElement("ScriptTemplate");
|
||||
pBase->SetAttribute("version", 3.0f);
|
||||
scriptXML.LinkEndChild(pBase);
|
||||
|
||||
// Write object name
|
||||
XMLElement *pName = scriptXML.NewElement("name");
|
||||
pName->SetText(*pTemp->TemplateName());
|
||||
pBase->LinkEndChild(pName);
|
||||
|
||||
// Write properties
|
||||
for (auto it = pTemp->mPropertySets.begin(); it != pTemp->mPropertySets.end(); it++)
|
||||
{
|
||||
XMLElement *pProperties = scriptXML.NewElement("properties");
|
||||
pProperties->SetAttribute("version", *it->SetName);
|
||||
SaveProperties(&scriptXML, pProperties, it->pBaseStruct, pTemp->MasterTemplate(), dir);
|
||||
pBase->LinkEndChild(pProperties);
|
||||
}
|
||||
|
||||
// Write editor properties
|
||||
XMLElement *pEditor = scriptXML.NewElement("editor");
|
||||
pBase->LinkEndChild(pEditor);
|
||||
|
||||
// Editor Properties
|
||||
XMLElement *pEditorProperties = scriptXML.NewElement("properties");
|
||||
pEditor->LinkEndChild(pEditorProperties);
|
||||
|
||||
TString propNames[6] = {
|
||||
"InstanceName", "Position", "Rotation",
|
||||
"Scale", "Active", "LightParameters"
|
||||
};
|
||||
|
||||
TIDString *pPropStrings[6] = {
|
||||
&pTemp->mNameIDString, &pTemp->mPositionIDString, &pTemp->mRotationIDString,
|
||||
&pTemp->mScaleIDString, &pTemp->mActiveIDString, &pTemp->mLightParametersIDString
|
||||
};
|
||||
|
||||
for (u32 iProp = 0; iProp < 6; iProp++)
|
||||
{
|
||||
if (!pPropStrings[iProp]->IsEmpty())
|
||||
{
|
||||
XMLElement *pProperty = scriptXML.NewElement("property");
|
||||
pProperty->SetAttribute("name", *propNames[iProp]);
|
||||
pProperty->SetAttribute("ID", **pPropStrings[iProp]);
|
||||
pEditorProperties->LinkEndChild(pProperty);
|
||||
}
|
||||
}
|
||||
|
||||
// Editor Assets
|
||||
XMLElement *pAssets = scriptXML.NewElement("assets");
|
||||
pEditor->LinkEndChild(pAssets);
|
||||
|
||||
for (auto it = pTemp->mAssets.begin(); it != pTemp->mAssets.end(); it++)
|
||||
{
|
||||
TString source = (it->AssetSource == CScriptTemplate::SEditorAsset::eFile ? "file" : "property");
|
||||
TString type;
|
||||
|
||||
switch (it->AssetType)
|
||||
{
|
||||
case CScriptTemplate::SEditorAsset::eModel: type = "model"; break;
|
||||
case CScriptTemplate::SEditorAsset::eAnimParams: type = "animparams"; break;
|
||||
case CScriptTemplate::SEditorAsset::eBillboard: type = "billboard"; break;
|
||||
case CScriptTemplate::SEditorAsset::eCollision: type = "collision"; break;
|
||||
}
|
||||
|
||||
s32 force = -1;
|
||||
if (it->AssetSource == CScriptTemplate::SEditorAsset::eAnimParams) force = it->ForceNodeIndex;
|
||||
|
||||
XMLElement *pAsset = scriptXML.NewElement(*type);
|
||||
pAsset->SetAttribute("source", *source);
|
||||
if (force >= 0) pAsset->SetAttribute("force", std::to_string(force).c_str());
|
||||
pAsset->SetText(*it->AssetLocation);
|
||||
pAssets->LinkEndChild(pAsset);
|
||||
}
|
||||
|
||||
// Preview Scale
|
||||
if (pTemp->mPreviewScale != 1.f)
|
||||
{
|
||||
XMLElement *pPreviewScale = scriptXML.NewElement("preview_scale");
|
||||
pEditor->LinkEndChild(pPreviewScale);
|
||||
pPreviewScale->SetText(pTemp->mPreviewScale);
|
||||
}
|
||||
|
||||
// Rot/Scale Type
|
||||
XMLElement *pRotType = scriptXML.NewElement("rotation_type");
|
||||
pEditor->LinkEndChild(pRotType);
|
||||
pRotType->SetText(pTemp->mRotationType == CScriptTemplate::eRotationEnabled ? "enabled" : "disabled");
|
||||
|
||||
XMLElement *pScaleType = scriptXML.NewElement("scale_type");
|
||||
pEditor->LinkEndChild(pScaleType);
|
||||
|
||||
if (pTemp->mScaleType != CScriptTemplate::eScaleVolume)
|
||||
pScaleType->SetText(pTemp->mScaleType == CScriptTemplate::eScaleEnabled ? "enabled" : "disabled");
|
||||
|
||||
else
|
||||
{
|
||||
pScaleType->SetText("volume");
|
||||
|
||||
// Volume Preview
|
||||
XMLElement *pVolume = scriptXML.NewElement("preview_volume");
|
||||
pEditor->LinkEndChild(pVolume);
|
||||
|
||||
// Enum -> String conversion lambda to avoid redundant code
|
||||
auto GetVolumeString = [](EVolumeShape shape) -> TString
|
||||
{
|
||||
switch (shape)
|
||||
{
|
||||
case eBoxShape: return "Box";
|
||||
case eAxisAlignedBoxShape: return "AxisAlignedBox";
|
||||
case eEllipsoidShape: return "Ellipsoid";
|
||||
case eCylinderShape: return "Cylinder";
|
||||
case eCylinderLargeShape: return "CylinderLarge";
|
||||
case eConditionalShape: return "Conditional";
|
||||
default: return "INVALID";
|
||||
}
|
||||
};
|
||||
|
||||
pVolume->SetAttribute("shape", *GetVolumeString(pTemp->mVolumeShape));
|
||||
|
||||
if (pTemp->mVolumeShape == eConditionalShape)
|
||||
{
|
||||
pVolume->SetAttribute("propertyID", *pTemp->mVolumeConditionIDString);
|
||||
|
||||
// Find conditional test property
|
||||
CPropertyTemplate *pProp;
|
||||
|
||||
for (auto it = pTemp->mPropertySets.begin(); it != pTemp->mPropertySets.end(); it++)
|
||||
{
|
||||
pProp = it->pBaseStruct->PropertyByIDString(pTemp->mVolumeConditionIDString);
|
||||
if (pProp) break;
|
||||
}
|
||||
|
||||
// Write conditions
|
||||
for (auto it = pTemp->mVolumeConditions.begin(); it != pTemp->mVolumeConditions.end(); it++)
|
||||
{
|
||||
// Value should be an integer, or a boolean condition?
|
||||
TString strVal;
|
||||
|
||||
if (pProp->Type() == eBoolProperty)
|
||||
strVal = (it->Value == 1 ? "true" : "false");
|
||||
else
|
||||
strVal = TString::HexString((u32) it->Value, true, true, (it->Value > 0xFF ? 8 : 2));
|
||||
|
||||
XMLElement *pCondition = scriptXML.NewElement("condition");
|
||||
pCondition->SetAttribute("value", *strVal);
|
||||
pCondition->SetAttribute("shape", *GetVolumeString(it->Shape));
|
||||
pVolume->LinkEndChild(pCondition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write to file
|
||||
scriptXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
TString name = pTemp->mSourceFile.GetFileName();
|
||||
boost::filesystem::create_directory(outDir.ToStdString());
|
||||
|
||||
// Create new document and write struct properties to it
|
||||
XMLDocument structXML;
|
||||
|
||||
XMLDeclaration *pDecl = structXML.NewDeclaration();
|
||||
structXML.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = structXML.NewElement("struct");
|
||||
pBase->SetAttribute("name", *name);
|
||||
pBase->SetAttribute("type", (pTemp->IsSingleProperty() ? "single" : "multi"));
|
||||
SaveProperties(&structXML, pBase, pTemp, pMaster, dir);
|
||||
structXML.LinkEndChild(pBase);
|
||||
|
||||
structXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveEnumTemplate(CEnumTemplate *pTemp, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
TString name = pTemp->mSourceFile.GetFileName(false);
|
||||
boost::filesystem::create_directory(*outDir);
|
||||
|
||||
// Create new document and write enumerators to it
|
||||
XMLDocument enumXML;
|
||||
|
||||
XMLDeclaration *pDecl = enumXML.NewDeclaration();
|
||||
enumXML.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = enumXML.NewElement("enum");
|
||||
pBase->SetAttribute("name", *name);
|
||||
SaveEnumerators(&enumXML, pBase, pTemp);
|
||||
enumXML.LinkEndChild(pBase);
|
||||
|
||||
enumXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveBitfieldTemplate(CBitfieldTemplate *pTemp, const TString& dir)
|
||||
{
|
||||
// Create directory
|
||||
TString outFile = dir + pTemp->mSourceFile;
|
||||
TString outDir = outFile.GetFileDirectory();
|
||||
TString name = pTemp->mSourceFile.GetFileName();
|
||||
boost::filesystem::create_directory(*outDir);
|
||||
|
||||
// Create new document and write enumerators to it
|
||||
XMLDocument bitfieldXML;
|
||||
|
||||
XMLDeclaration *pDecl = bitfieldXML.NewDeclaration();
|
||||
bitfieldXML.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pBase = bitfieldXML.NewElement("bitfield");
|
||||
pBase->SetAttribute("name", *name);
|
||||
SaveBitFlags(&bitfieldXML, pBase, pTemp);
|
||||
bitfieldXML.LinkEndChild(pBase);
|
||||
|
||||
bitfieldXML.SaveFile(*outFile);
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveProperties(XMLDocument *pDoc, XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir)
|
||||
{
|
||||
for (u32 iProp = 0; iProp < pTemp->Count(); iProp++)
|
||||
{
|
||||
CPropertyTemplate *pProp = pTemp->PropertyByIndex(iProp);
|
||||
u32 propID = (pProp->PropertyID() == 0xFFFFFFFF ? iProp : pProp->PropertyID());
|
||||
TString strID = TString::HexString(propID, true, true, (propID > 0xFF ? 8 : 2));
|
||||
|
||||
if (pProp->Type() == eStructProperty)
|
||||
{
|
||||
CStructTemplate *pStructTemp = static_cast<CStructTemplate*>(pProp);
|
||||
bool isExternal = (!pStructTemp->mSourceFile.IsEmpty());
|
||||
|
||||
XMLElement *pElem = pDoc->NewElement("struct");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1) || pTemp->IsSingleProperty())
|
||||
{
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
}
|
||||
|
||||
if (!isExternal) {
|
||||
TString type = pStructTemp->IsSingleProperty() ? "single" : "multi";
|
||||
pElem->SetAttribute("type", *type);
|
||||
}
|
||||
|
||||
// Only save properties if this is a multi struct, or if there is no master property list
|
||||
if (!pMaster->HasPropertyList() || !pStructTemp->IsSingleProperty())
|
||||
{
|
||||
// Embed struct or save to external XML?
|
||||
if (!pStructTemp->mSourceFile.IsEmpty())
|
||||
{
|
||||
SaveStructTemplate(pStructTemp, pMaster, dir);
|
||||
pElem->SetAttribute("template", *pStructTemp->mSourceFile);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SaveProperties(pDoc, pElem, pStructTemp, pMaster, dir);
|
||||
}
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
|
||||
else if (pProp->Type() == eEnumProperty)
|
||||
{
|
||||
CEnumTemplate *pEnumTemp = static_cast<CEnumTemplate*>(pProp);
|
||||
bool isExternal = (!pEnumTemp->mSourceFile.IsEmpty());
|
||||
|
||||
XMLElement *pElem = pDoc->NewElement("enum");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1))
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
|
||||
if (isExternal)
|
||||
{
|
||||
SaveEnumTemplate(pEnumTemp, dir);
|
||||
pElem->SetAttribute("template", *pEnumTemp->mSourceFile);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SaveEnumerators(pDoc, pElem, pEnumTemp);
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
else if (pProp->Type() == eBitfieldProperty)
|
||||
{
|
||||
CBitfieldTemplate *pBitfieldTemp = static_cast<CBitfieldTemplate*>(pProp);
|
||||
bool isExternal = (!pBitfieldTemp->mSourceFile.IsEmpty());
|
||||
|
||||
XMLElement *pElem = pDoc->NewElement("bitfield");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1))
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
|
||||
if (isExternal)
|
||||
{
|
||||
SaveBitfieldTemplate(pBitfieldTemp, dir);
|
||||
pElem->SetAttribute("template", *pBitfieldTemp->mSourceFile);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SaveBitFlags(pDoc, pElem, pBitfieldTemp);
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
else
|
||||
{
|
||||
XMLElement *pElem = pDoc->NewElement("property");
|
||||
pElem->SetAttribute("ID", *strID);
|
||||
|
||||
if ((!pMaster->HasPropertyList()) || (pProp->PropertyID() == -1) || pTemp->IsSingleProperty())
|
||||
{
|
||||
pElem->SetAttribute("name", *pProp->Name());
|
||||
pElem->SetAttribute("type", *PropEnumToPropString(pProp->Type()));
|
||||
|
||||
if (pProp->Type() == eFileProperty)
|
||||
{
|
||||
// Construct extension list string
|
||||
CFileTemplate *pFileProp = static_cast<CFileTemplate*>(pProp);
|
||||
const TStringList& extensions = pFileProp->Extensions();
|
||||
|
||||
TString strList = "";
|
||||
|
||||
for (auto it = extensions.begin(); it != extensions.end();)
|
||||
{
|
||||
strList += *it;
|
||||
it++;
|
||||
if (it != extensions.end()) strList += ",";
|
||||
}
|
||||
|
||||
pElem->SetAttribute("ext", *strList);
|
||||
}
|
||||
}
|
||||
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveEnumerators(XMLDocument *pDoc, XMLElement *pParent, CEnumTemplate *pTemp)
|
||||
{
|
||||
for (u32 iEnum = 0; iEnum < pTemp->NumEnumerators(); iEnum++)
|
||||
{
|
||||
XMLElement *pElem = pDoc->NewElement("enumerator");
|
||||
pElem->SetAttribute("ID", *TString::HexString(pTemp->EnumeratorID(iEnum), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->EnumeratorName(iEnum));
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
|
||||
void CTemplateWriter::SaveBitFlags(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CBitfieldTemplate *pTemp)
|
||||
{
|
||||
for (u32 iFlag = 0; iFlag < pTemp->NumFlags(); iFlag++)
|
||||
{
|
||||
XMLElement *pElem = pDoc->NewElement("bitflag");
|
||||
pElem->SetAttribute("mask", *TString::HexString(pTemp->FlagMask(iFlag), true, true, 8));
|
||||
pElem->SetAttribute("name", *pTemp->FlagName(iFlag));
|
||||
pParent->LinkEndChild(pElem);
|
||||
}
|
||||
}
|
||||
24
src/Core/Resource/Cooker/CTemplateWriter.h
Normal file
24
src/Core/Resource/Cooker/CTemplateWriter.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CTEMPLATEWRITER_H
|
||||
#define CTEMPLATEWRITER_H
|
||||
|
||||
#include "../script/CMasterTemplate.h"
|
||||
#include "../script/CScriptTemplate.h"
|
||||
|
||||
class CTemplateWriter
|
||||
{
|
||||
CTemplateWriter();
|
||||
|
||||
public:
|
||||
static void SaveAllTemplates();
|
||||
static void SaveGameTemplates(CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SavePropertyList(CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SaveScriptTemplate(CScriptTemplate *pTemp, const TString& dir);
|
||||
static void SaveStructTemplate(CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SaveEnumTemplate(CEnumTemplate *pTemp, const TString& dir);
|
||||
static void SaveBitfieldTemplate(CBitfieldTemplate *pTemp, const TString& dir);
|
||||
static void SaveProperties(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CStructTemplate *pTemp, CMasterTemplate *pMaster, const TString& dir);
|
||||
static void SaveEnumerators(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CEnumTemplate *pTemp);
|
||||
static void SaveBitFlags(tinyxml2::XMLDocument *pDoc, tinyxml2::XMLElement *pParent, CBitfieldTemplate *pTemp);
|
||||
};
|
||||
|
||||
#endif // CTEMPLATEWRITER_H
|
||||
108
src/Core/Resource/Cooker/CTextureEncoder.cpp
Normal file
108
src/Core/Resource/Cooker/CTextureEncoder.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "CTextureEncoder.h"
|
||||
#include <iostream>
|
||||
|
||||
CTextureEncoder::CTextureEncoder()
|
||||
{
|
||||
mpTexture = nullptr;
|
||||
}
|
||||
|
||||
void CTextureEncoder::WriteTXTR(COutputStream& TXTR)
|
||||
{
|
||||
// Only DXT1->CMPR supported at the moment
|
||||
TXTR.WriteLong(mOutputFormat);
|
||||
TXTR.WriteShort(mpTexture->mWidth);
|
||||
TXTR.WriteShort(mpTexture->mHeight);
|
||||
TXTR.WriteLong(mpTexture->mNumMipMaps);
|
||||
|
||||
u32 MipW = mpTexture->Width() / 4;
|
||||
u32 MipH = mpTexture->Height() / 4;
|
||||
CMemoryInStream Image(mpTexture->mImgDataBuffer, mpTexture->mImgDataSize, IOUtil::LittleEndian);
|
||||
u32 MipOffset = Image.Tell();
|
||||
|
||||
for (u32 iMip = 0; iMip < mpTexture->mNumMipMaps; iMip++)
|
||||
{
|
||||
for (u32 BlockY = 0; BlockY < MipH; BlockY += 2)
|
||||
for (u32 BlockX = 0; BlockX < MipW; BlockX += 2)
|
||||
for (u32 ImgY = BlockY; ImgY < BlockY + 2; ImgY++)
|
||||
for (u32 ImgX = BlockX; ImgX < BlockX + 2; ImgX++)
|
||||
{
|
||||
u32 SrcPos = ((ImgY * MipW) + ImgX) * 8;
|
||||
Image.Seek(MipOffset + SrcPos, SEEK_SET);
|
||||
|
||||
ReadSubBlockCMPR(Image, TXTR);
|
||||
}
|
||||
|
||||
MipOffset += MipW * MipH * 8;
|
||||
MipW /= 2;
|
||||
MipH /= 2;
|
||||
if (MipW < 2) MipW = 2;
|
||||
if (MipH < 2) MipH = 2;
|
||||
}
|
||||
}
|
||||
|
||||
void CTextureEncoder::DetermineBestOutputFormat()
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
void CTextureEncoder::ReadSubBlockCMPR(CInputStream& Source, COutputStream& Dest)
|
||||
{
|
||||
Dest.WriteShort(Source.ReadShort());
|
||||
Dest.WriteShort(Source.ReadShort());
|
||||
|
||||
for (u32 byte = 0; byte < 4; byte++) {
|
||||
u8 b = Source.ReadByte();
|
||||
b = ((b & 0x3) << 6) | ((b & 0xC) << 2) | ((b & 0x30) >> 2) | ((b & 0xC0) >> 6);
|
||||
Dest.WriteByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
void CTextureEncoder::EncodeTXTR(COutputStream& TXTR, CTexture *pTex)
|
||||
{
|
||||
if (pTex->mTexelFormat != eDXT1)
|
||||
{
|
||||
std::cout << "\rError: Unsupported texel format for decoding\n";
|
||||
return;
|
||||
}
|
||||
|
||||
CTextureEncoder Encoder;
|
||||
Encoder.mpTexture = pTex;
|
||||
Encoder.mSourceFormat = eDXT1;
|
||||
Encoder.mOutputFormat = eGX_CMPR;
|
||||
Encoder.WriteTXTR(TXTR);
|
||||
}
|
||||
|
||||
void CTextureEncoder::EncodeTXTR(COutputStream& TXTR, CTexture *pTex, ETexelFormat /*OutputFormat*/)
|
||||
{
|
||||
// todo: support for encoding a specific format
|
||||
EncodeTXTR(TXTR, pTex);
|
||||
}
|
||||
|
||||
ETexelFormat CTextureEncoder::GetGXFormat(ETexelFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case eLuminance: return eGX_I8;
|
||||
case eLuminanceAlpha: return eGX_IA8;
|
||||
case eRGBA4: return eGX_RGB5A3;
|
||||
case eRGB565: return eGX_RGB565;
|
||||
case eRGBA8: return eGX_RGBA8;
|
||||
case eDXT1: return eGX_CMPR;
|
||||
default: return eInvalidTexelFormat;
|
||||
}
|
||||
}
|
||||
|
||||
ETexelFormat CTextureEncoder::GetFormat(ETexelFormat Format)
|
||||
{
|
||||
switch (Format)
|
||||
{
|
||||
case eGX_I4: return eLuminance;
|
||||
case eGX_I8: return eLuminance;
|
||||
case eGX_IA4: return eLuminanceAlpha;
|
||||
case eGX_IA8: return eLuminanceAlpha;
|
||||
// todo rest of these
|
||||
case eGX_CMPR: return eDXT1;
|
||||
default: return eInvalidTexelFormat;
|
||||
}
|
||||
}
|
||||
27
src/Core/Resource/Cooker/CTextureEncoder.h
Normal file
27
src/Core/Resource/Cooker/CTextureEncoder.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef CTEXTUREENCODER_H
|
||||
#define CTEXTUREENCODER_H
|
||||
|
||||
#include "../CTexture.h"
|
||||
#include <Core/TResPtr.h>
|
||||
|
||||
// Class contains basic functionality right now - only supports directly converting DXT1 to CMPR
|
||||
// More advanced functions (including actual encoding!) coming later
|
||||
class CTextureEncoder
|
||||
{
|
||||
TResPtr<CTexture> mpTexture;
|
||||
ETexelFormat mSourceFormat;
|
||||
ETexelFormat mOutputFormat;
|
||||
|
||||
CTextureEncoder();
|
||||
void WriteTXTR(COutputStream& TXTR);
|
||||
void DetermineBestOutputFormat();
|
||||
void ReadSubBlockCMPR(CInputStream& Source, COutputStream& Dest);
|
||||
|
||||
public:
|
||||
static void EncodeTXTR(COutputStream& TXTR, CTexture *pTex);
|
||||
static void EncodeTXTR(COutputStream& TXTR, CTexture *pTex, ETexelFormat OutputFormat);
|
||||
static ETexelFormat GetGXFormat(ETexelFormat Format);
|
||||
static ETexelFormat GetFormat(ETexelFormat Format);
|
||||
};
|
||||
|
||||
#endif // CTEXTUREENCODER_H
|
||||
19
src/Core/Resource/Cooker/CWorldCooker.cpp
Normal file
19
src/Core/Resource/Cooker/CWorldCooker.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "CWorldCooker.h"
|
||||
|
||||
CWorldCooker::CWorldCooker()
|
||||
{
|
||||
}
|
||||
|
||||
u32 CWorldCooker::GetMLVLVersion(EGame version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case ePrimeDemo: return 0xD;
|
||||
case ePrime: return 0x11;
|
||||
case eEchoesDemo: return 0x14;
|
||||
case eEchoes: return 0x17;
|
||||
case eCorruption: return 0x19;
|
||||
case eReturns: return 0x1B;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
14
src/Core/Resource/Cooker/CWorldCooker.h
Normal file
14
src/Core/Resource/Cooker/CWorldCooker.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef CWORLDCOOKER_H
|
||||
#define CWORLDCOOKER_H
|
||||
|
||||
#include <Common/types.h>
|
||||
#include "../EFormatVersion.h"
|
||||
|
||||
class CWorldCooker
|
||||
{
|
||||
CWorldCooker();
|
||||
public:
|
||||
static u32 GetMLVLVersion(EGame version);
|
||||
};
|
||||
|
||||
#endif // CWORLDCOOKER_H
|
||||
18
src/Core/Resource/EFormatVersion.h
Normal file
18
src/Core/Resource/EFormatVersion.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef EFORMATVERSION_H
|
||||
#define EFORMATVERSION_H
|
||||
|
||||
// Global version enum that can be easily shared between loaders
|
||||
enum EGame
|
||||
{
|
||||
ePrimeDemo = 0,
|
||||
ePrime = 1,
|
||||
eEchoesDemo = 2,
|
||||
eEchoes = 3,
|
||||
eCorruptionProto = 4,
|
||||
eCorruption = 5,
|
||||
eReturns = 6,
|
||||
TropicalFreeze = 7,
|
||||
eUnknownVersion = -1
|
||||
};
|
||||
|
||||
#endif // EFORMATVERSION_H
|
||||
51
src/Core/Resource/EResType.h
Normal file
51
src/Core/Resource/EResType.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef ERESTYPE
|
||||
#define ERESTYPE
|
||||
|
||||
enum EResType
|
||||
{
|
||||
eAnimation = 0,
|
||||
eAnimEventData = 1,
|
||||
eAnimSet = 2,
|
||||
eArea = 3,
|
||||
eAudioData = 4,
|
||||
eAudioGrp = 5,
|
||||
eAudioSample = 6,
|
||||
eAudioStream = 7,
|
||||
eAudioTable = 8,
|
||||
eCharacter = 9,
|
||||
eCollisionMeshGroup = 10,
|
||||
eCollisionResponse = 11,
|
||||
eDataDump = 12,
|
||||
eDecal = 13,
|
||||
eDependencyGroup = 14,
|
||||
eFont = 15,
|
||||
eGuiFrame = 16,
|
||||
eHintSystem = 17,
|
||||
eInvalidResType = 18,
|
||||
eMapArea = 19,
|
||||
eMapWorld = 20,
|
||||
eMapUniverse = 21,
|
||||
eMidi = 22,
|
||||
eModel = 23,
|
||||
eMusicTrack = 24,
|
||||
eNavMesh = 25,
|
||||
ePackFile = 26,
|
||||
eParticle = 27,
|
||||
eParticleElectric = 28,
|
||||
eParticleSwoosh = 29,
|
||||
eProjectile = 30,
|
||||
eResource = 31,
|
||||
eSaveWorld = 32,
|
||||
eScan = 33,
|
||||
eSkeleton = 34,
|
||||
eSkin = 35,
|
||||
eStateMachine = 36,
|
||||
eStringTable = 37,
|
||||
eTexture = 38,
|
||||
eTweak = 39,
|
||||
eVideo = 40,
|
||||
eWorld = 41
|
||||
};
|
||||
|
||||
#endif // ERESTYPE
|
||||
|
||||
106
src/Core/Resource/ETevEnums.h
Normal file
106
src/Core/Resource/ETevEnums.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef ETEVENUMS
|
||||
#define ETEVENUMS
|
||||
|
||||
enum ETevColorInput
|
||||
{
|
||||
ePrevRGB = 0x0,
|
||||
ePrevAAA = 0x1,
|
||||
eColor0RGB = 0x2,
|
||||
eColor0AAA = 0x3,
|
||||
eColor1RGB = 0x4,
|
||||
eColor1AAA = 0x5,
|
||||
eColor2RGB = 0x6,
|
||||
eColor2AAA = 0x7,
|
||||
eTextureRGB = 0x8,
|
||||
eTextureAAA = 0x9,
|
||||
eRasRGB = 0xA,
|
||||
eRasAAA = 0xB,
|
||||
eOneRGB = 0xC,
|
||||
eHalfRGB = 0xD,
|
||||
eKonstRGB = 0xE,
|
||||
eZeroRGB = 0xF
|
||||
};
|
||||
|
||||
enum ETevAlphaInput
|
||||
{
|
||||
ePrevAlpha = 0x0,
|
||||
eColor0Alpha = 0x1,
|
||||
eColor1Alpha = 0x2,
|
||||
eColor2Alpha = 0x3,
|
||||
eTextureAlpha = 0x4,
|
||||
eRasAlpha = 0x5,
|
||||
eKonstAlpha = 0x6,
|
||||
eZeroAlpha = 0x7
|
||||
};
|
||||
|
||||
enum ETevOutput
|
||||
{
|
||||
ePrevReg = 0x0,
|
||||
eColor0Reg = 0x1,
|
||||
eColor1Reg = 0x2,
|
||||
eColor2Reg = 0x3
|
||||
};
|
||||
|
||||
enum ETevKSel
|
||||
{
|
||||
eKonstOne = 0x0,
|
||||
eKonstSevenEighths = 0x1,
|
||||
eKonstThreeFourths = 0x2,
|
||||
eKonstFiveEighths = 0x3,
|
||||
eKonstOneHalf = 0x4,
|
||||
eKonstThreeEighths = 0x5,
|
||||
eKonstOneFourth = 0x6,
|
||||
eKonstOneEighth = 0x7,
|
||||
eKonst0_RGB = 0xC,
|
||||
eKonst1_RGB = 0xD,
|
||||
eKonst2_RGB = 0xE,
|
||||
eKonst3_RGB = 0xF,
|
||||
eKonst0_R = 0x10,
|
||||
eKonst1_R = 0x11,
|
||||
eKonst2_R = 0x12,
|
||||
eKonst3_R = 0x13,
|
||||
eKonst0_G = 0x14,
|
||||
eKonst1_G = 0x15,
|
||||
eKonst2_G = 0x16,
|
||||
eKonst3_G = 0x17,
|
||||
eKonst0_B = 0x18,
|
||||
eKonst1_B = 0x19,
|
||||
eKonst2_B = 0x1A,
|
||||
eKonst3_B = 0x1B,
|
||||
eKonst0_A = 0x1C,
|
||||
eKonst1_A = 0x1D,
|
||||
eKonst2_A = 0x1E,
|
||||
eKonst3_A = 0x1F
|
||||
};
|
||||
|
||||
enum ETevRasSel
|
||||
{
|
||||
eRasColor0 = 0x0,
|
||||
eRasColor1 = 0x1,
|
||||
eRasAlpha0 = 0x2,
|
||||
eRasAlpha1 = 0x3,
|
||||
eRasColor0A0 = 0x4,
|
||||
eRasColor1A1 = 0x5,
|
||||
eRasColorZero = 0x6,
|
||||
eRasAlphaBump = 0x7,
|
||||
eRasAlphaBumpN = 0x8,
|
||||
eRasColorNull = 0xFF
|
||||
};
|
||||
|
||||
enum EUVAnimMode
|
||||
{
|
||||
eInverseMV = 0x0,
|
||||
eInverseMVTranslated = 0x1,
|
||||
eUVScroll = 0x2,
|
||||
eUVRotation = 0x3,
|
||||
eHFilmstrip = 0x4,
|
||||
eVFilmstrip = 0x5,
|
||||
eModelMatrix = 0x6,
|
||||
eConvolutedModeA = 0x7,
|
||||
eConvolutedModeB = 0x8,
|
||||
eSimpleMode = 0xA,
|
||||
eNoUVAnim = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
#endif // ETEVENUMS
|
||||
|
||||
38
src/Core/Resource/ETexelFormat.h
Normal file
38
src/Core/Resource/ETexelFormat.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef ETEXELFORMAT
|
||||
#define ETEXELFORMAT
|
||||
|
||||
// ETexelFormat - supported internal formats for decoded textures
|
||||
enum ETexelFormat
|
||||
{
|
||||
// Supported texel formats in GX using Retro's numbering
|
||||
eGX_I4 = 0x0,
|
||||
eGX_I8 = 0x1,
|
||||
eGX_IA4 = 0x2,
|
||||
eGX_IA8 = 0x3,
|
||||
eGX_C4 = 0x4,
|
||||
eGX_C8 = 0x5,
|
||||
eGX_C14x2 = 0x6,
|
||||
eGX_RGB565 = 0x7,
|
||||
eGX_RGB5A3 = 0x8,
|
||||
eGX_RGBA8 = 0x9,
|
||||
eGX_CMPR = 0xA,
|
||||
// Supported internal texel formats for decoded textures
|
||||
eLuminance,
|
||||
eLuminanceAlpha,
|
||||
eRGBA4,
|
||||
eRGB565,
|
||||
eRGBA8,
|
||||
eDXT1,
|
||||
eInvalidTexelFormat
|
||||
};
|
||||
|
||||
// EGXPaletteFormat - GX's supported palette texel formats for C4/C8
|
||||
enum EGXPaletteFormat
|
||||
{
|
||||
ePalette_IA8 = 0,
|
||||
ePalette_RGB565 = 1,
|
||||
ePalette_RGB5A3 = 2
|
||||
};
|
||||
|
||||
#endif // ETEXELFORMAT
|
||||
|
||||
196
src/Core/Resource/Factory/CAnimSetLoader.cpp
Normal file
196
src/Core/Resource/Factory/CAnimSetLoader.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
#include "CAnimSetLoader.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Core/Log.h>
|
||||
|
||||
CAnimSetLoader::CAnimSetLoader()
|
||||
{
|
||||
}
|
||||
|
||||
CAnimSet* CAnimSetLoader::LoadCorruptionCHAR(CInputStream& CHAR)
|
||||
{
|
||||
// For now, we only read enough to fetch the model
|
||||
CHAR.Seek(0x1, SEEK_CUR);
|
||||
set->nodes.resize(1);
|
||||
CAnimSet::SNode& node = set->nodes[0];
|
||||
|
||||
node.name = CHAR.ReadString();
|
||||
node.model = gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
|
||||
return set;
|
||||
}
|
||||
|
||||
CAnimSet* CAnimSetLoader::LoadReturnsCHAR(CInputStream& CHAR)
|
||||
{
|
||||
// For now, we only read enough to fetch the model
|
||||
CHAR.Seek(0x16, SEEK_CUR);
|
||||
set->nodes.resize(1);
|
||||
CAnimSet::SNode& node = set->nodes[0];
|
||||
|
||||
node.name = CHAR.ReadString();
|
||||
CHAR.Seek(0x14, SEEK_CUR);
|
||||
CHAR.ReadString();
|
||||
node.model = gResCache.GetResource(CHAR.ReadLongLong(), "CMDL");
|
||||
return set;
|
||||
}
|
||||
|
||||
void CAnimSetLoader::LoadPASDatabase(CInputStream& PAS4)
|
||||
{
|
||||
// For now, just parse the data; don't store it
|
||||
PAS4.Seek(0x4, SEEK_CUR); // Skipping PAS4 FourCC
|
||||
u32 anim_state_count = PAS4.ReadLong();
|
||||
PAS4.Seek(0x4, SEEK_CUR); // Skipping default anim state
|
||||
|
||||
for (u32 s = 0; s < anim_state_count; s++)
|
||||
{
|
||||
PAS4.Seek(0x4, SEEK_CUR); // Skipping unknown value
|
||||
u32 parm_info_count = PAS4.ReadLong();
|
||||
u32 anim_info_count = PAS4.ReadLong();
|
||||
|
||||
u32 skip = 0;
|
||||
for (u32 p = 0; p < parm_info_count; p++)
|
||||
{
|
||||
u32 type = PAS4.ReadLong();
|
||||
PAS4.Seek(0x8, SEEK_CUR);
|
||||
|
||||
switch (type) {
|
||||
case 0: // Int32
|
||||
case 1: // Uint32
|
||||
case 2: // Real32
|
||||
case 4: // Enum
|
||||
PAS4.Seek(0x8, SEEK_CUR);
|
||||
skip += 4;
|
||||
break;
|
||||
case 3: // Bool
|
||||
PAS4.Seek(0x2, SEEK_CUR);
|
||||
skip++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 a = 0; a < anim_info_count; a++)
|
||||
PAS4.Seek(0x4 + skip, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CAnimSet* CAnimSetLoader::LoadANCS(CInputStream& ANCS)
|
||||
{
|
||||
if (!ANCS.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + ANCS.GetSourceString());
|
||||
|
||||
u32 magic = ANCS.ReadLong();
|
||||
if (magic != 0x00010001)
|
||||
{
|
||||
Log::FileError(ANCS.GetSourceString(), "Invalid ANCS magic: " + TString::HexString(magic));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CAnimSetLoader loader;
|
||||
loader.set = new CAnimSet;
|
||||
|
||||
u32 node_count = ANCS.ReadLong();
|
||||
loader.set->nodes.resize(node_count);
|
||||
|
||||
for (u32 n = 0; n < node_count; n++)
|
||||
{
|
||||
CAnimSet::SNode *node = &loader.set->nodes[n];
|
||||
|
||||
ANCS.Seek(0x4, SEEK_CUR); // Skipping node self-index
|
||||
u16 unknown1 = ANCS.ReadShort();
|
||||
if (n == 0) loader.mVersion = (unknown1 == 0xA) ? eEchoes : ePrime; // Best version indicator we know of unfortunately
|
||||
node->name = ANCS.ReadString();
|
||||
node->model = gResCache.GetResource(ANCS.ReadLong(), "CMDL");
|
||||
node->skinID = ANCS.ReadLong();
|
||||
node->skelID = ANCS.ReadLong();
|
||||
|
||||
// Unfortunately that's all that's actually supported at the moment. Hope to expand later.
|
||||
// Since there's no size value I have to actually read the rest of the node to reach the next one
|
||||
u32 anim_count = ANCS.ReadLong();
|
||||
for (u32 a = 0; a < anim_count; a++)
|
||||
{
|
||||
ANCS.Seek(0x4, SEEK_CUR);
|
||||
if (loader.mVersion == ePrime) ANCS.Seek(0x1, SEEK_CUR);
|
||||
ANCS.ReadString();
|
||||
}
|
||||
|
||||
// PAS Database
|
||||
loader.LoadPASDatabase(ANCS);
|
||||
|
||||
// Particles
|
||||
u32 particle_count = ANCS.ReadLong();
|
||||
ANCS.Seek(particle_count * 4, SEEK_CUR);
|
||||
u32 swoosh_count = ANCS.ReadLong();
|
||||
ANCS.Seek(swoosh_count * 4, SEEK_CUR);
|
||||
if (unknown1 != 5) ANCS.Seek(0x4, SEEK_CUR);
|
||||
u32 electric_count = ANCS.ReadLong();
|
||||
ANCS.Seek(electric_count * 4, SEEK_CUR);
|
||||
if (loader.mVersion == eEchoes) {
|
||||
u32 spsc_count = ANCS.ReadLong();
|
||||
ANCS.Seek(spsc_count * 4, SEEK_CUR);
|
||||
}
|
||||
ANCS.Seek(0x4, SEEK_CUR);
|
||||
if (loader.mVersion == eEchoes) ANCS.Seek(0x4, SEEK_CUR);
|
||||
|
||||
u32 anim_count2 = ANCS.ReadLong();
|
||||
for (u32 a = 0; a < anim_count2; a++)
|
||||
{
|
||||
ANCS.ReadString();
|
||||
ANCS.Seek(0x18, SEEK_CUR);
|
||||
}
|
||||
|
||||
u32 EffectGroupCount = ANCS.ReadLong();
|
||||
for (u32 g = 0; g < EffectGroupCount; g++)
|
||||
{
|
||||
ANCS.ReadString();
|
||||
u32 EffectCount = ANCS.ReadLong();
|
||||
|
||||
for (u32 e = 0; e < EffectCount; e++)
|
||||
{
|
||||
ANCS.ReadString();
|
||||
ANCS.Seek(0x8, SEEK_CUR);
|
||||
if (loader.mVersion == ePrime) ANCS.ReadString();
|
||||
if (loader.mVersion == eEchoes) ANCS.Seek(0x4, SEEK_CUR);
|
||||
ANCS.Seek(0xC, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
ANCS.Seek(0x8, SEEK_CUR);
|
||||
|
||||
u32 unknown_count = ANCS.ReadLong();
|
||||
ANCS.Seek(unknown_count * 4, SEEK_CUR);
|
||||
|
||||
if (loader.mVersion == eEchoes)
|
||||
{
|
||||
ANCS.Seek(0x5, SEEK_CUR);
|
||||
u32 unknown_count2 = ANCS.ReadLong();
|
||||
ANCS.Seek(unknown_count2 * 0x1C, SEEK_CUR);
|
||||
}
|
||||
// Lots of work for data I'm not even using x.x
|
||||
}
|
||||
|
||||
return loader.set;
|
||||
}
|
||||
|
||||
CAnimSet* CAnimSetLoader::LoadCHAR(CInputStream &CHAR)
|
||||
{
|
||||
if (!CHAR.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + CHAR.GetSourceString());
|
||||
|
||||
CAnimSetLoader loader;
|
||||
u8 check = CHAR.ReadByte();
|
||||
|
||||
if (check == 0x5)
|
||||
{
|
||||
loader.mVersion = eCorruption;
|
||||
loader.set = new CAnimSet();
|
||||
return loader.LoadCorruptionCHAR(CHAR);
|
||||
}
|
||||
|
||||
if (check == 0x59)
|
||||
{
|
||||
loader.mVersion = eReturns;
|
||||
loader.set = new CAnimSet();
|
||||
return loader.LoadReturnsCHAR(CHAR);
|
||||
}
|
||||
|
||||
Log::FileError(CHAR.GetSourceString(), "CHAR has invalid first byte: " + TString::HexString(check));
|
||||
return nullptr;
|
||||
}
|
||||
24
src/Core/Resource/Factory/CAnimSetLoader.h
Normal file
24
src/Core/Resource/Factory/CAnimSetLoader.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CCHARACTERLOADER_H
|
||||
#define CCHARACTERLOADER_H
|
||||
|
||||
#include "../CAnimSet.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include <Core/CResCache.h>
|
||||
|
||||
class CAnimSetLoader
|
||||
{
|
||||
TResPtr<CAnimSet> set;
|
||||
CResCache *mpResCache;
|
||||
EGame mVersion;
|
||||
|
||||
CAnimSetLoader();
|
||||
CAnimSet* LoadCorruptionCHAR(CInputStream& CHAR);
|
||||
CAnimSet* LoadReturnsCHAR(CInputStream& CHAR);
|
||||
void LoadPASDatabase(CInputStream& PAS4);
|
||||
|
||||
public:
|
||||
static CAnimSet* LoadANCS(CInputStream& ANCS);
|
||||
static CAnimSet* LoadCHAR(CInputStream& CHAR);
|
||||
};
|
||||
|
||||
#endif // CCHARACTERLOADER_H
|
||||
618
src/Core/Resource/Factory/CAreaLoader.cpp
Normal file
618
src/Core/Resource/Factory/CAreaLoader.cpp
Normal file
@@ -0,0 +1,618 @@
|
||||
#include "CAreaLoader.h"
|
||||
#include "CCollisionLoader.h"
|
||||
#include "CModelLoader.h"
|
||||
#include "CMaterialLoader.h"
|
||||
#include "CScriptLoader.h"
|
||||
#include <Common/CFourCC.h>
|
||||
#include <Common/CompressionUtil.h>
|
||||
#include <iostream>
|
||||
#include <Core/Log.h>
|
||||
|
||||
CAreaLoader::CAreaLoader()
|
||||
{
|
||||
mpMREA = nullptr;
|
||||
mHasDecompressedBuffer = false;
|
||||
mGeometryBlockNum = -1;
|
||||
mScriptLayerBlockNum = -1;
|
||||
mCollisionBlockNum = -1;
|
||||
mUnknownBlockNum = -1;
|
||||
mLightsBlockNum = -1;
|
||||
mEmptyBlockNum = -1;
|
||||
mPathBlockNum = -1;
|
||||
mOctreeBlockNum = -1;
|
||||
mScriptGeneratorBlockNum = -1;
|
||||
mFFFFBlockNum = -1;
|
||||
mUnknown2BlockNum = -1;
|
||||
mEGMCBlockNum = -1;
|
||||
mBoundingBoxesBlockNum = -1;
|
||||
mDependenciesBlockNum = -1;
|
||||
mGPUBlockNum = -1;
|
||||
mPVSBlockNum = -1;
|
||||
mRSOBlockNum = -1;
|
||||
}
|
||||
|
||||
CAreaLoader::~CAreaLoader()
|
||||
{
|
||||
if (mHasDecompressedBuffer)
|
||||
{
|
||||
delete mpMREA;
|
||||
delete[] mDecmpBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ PRIME ************
|
||||
void CAreaLoader::ReadHeaderPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP1)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
u32 mNumBlocks = mpMREA->ReadLong();
|
||||
|
||||
mGeometryBlockNum = mpMREA->ReadLong();
|
||||
mScriptLayerBlockNum = mpMREA->ReadLong();
|
||||
mCollisionBlockNum = mpMREA->ReadLong();
|
||||
mUnknownBlockNum = mpMREA->ReadLong();
|
||||
mLightsBlockNum = mpMREA->ReadLong();
|
||||
mEmptyBlockNum = mpMREA->ReadLong();
|
||||
mPathBlockNum = mpMREA->ReadLong();
|
||||
mOctreeBlockNum = mpMREA->ReadLong();
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(mNumBlocks, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadGeometryPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)");
|
||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mBlockMgr->ToNextBlock();
|
||||
|
||||
// Geometry
|
||||
for (u32 m = 0; m < mNumMeshes; m++) {
|
||||
std::cout << "\rLoading mesh " << std::dec << m + 1 << "/" << mNumMeshes;
|
||||
|
||||
CModel *pTerrainModel = CModelLoader::LoadWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, mVersion);
|
||||
mpArea->AddWorldModel(pTerrainModel);
|
||||
|
||||
if (mVersion >= eEchoes) {
|
||||
mBlockMgr->ToNextBlock();
|
||||
mBlockMgr->ToNextBlock();
|
||||
}
|
||||
}
|
||||
mpArea->MergeTerrain();
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadSCLYPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)");
|
||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
||||
|
||||
CFourCC SCLY(*mpMREA);
|
||||
if (SCLY != "SCLY")
|
||||
{
|
||||
Log::Error(mpMREA->GetSourceString() + " - Invalid SCLY magic: " + SCLY.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
mpMREA->Seek(0x4, SEEK_CUR);
|
||||
mNumLayers = mpMREA->ReadLong();
|
||||
mpArea->mScriptLayers.reserve(mNumLayers);
|
||||
|
||||
std::vector<u32> LayerSizes(mNumLayers);
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
LayerSizes[l] = mpMREA->ReadLong();
|
||||
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
{
|
||||
u32 next = mpMREA->Tell() + LayerSizes[l];
|
||||
|
||||
CScriptLayer *layer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
if (layer)
|
||||
mpArea->mScriptLayers.push_back(layer);
|
||||
|
||||
mpMREA->Seek(next, SEEK_SET);
|
||||
}
|
||||
|
||||
SetUpObjects();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadLightsPrime()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)");
|
||||
mBlockMgr->ToBlock(mLightsBlockNum);
|
||||
|
||||
u32 babedead = mpMREA->ReadLong();
|
||||
if (babedead != 0xbabedead) return;
|
||||
|
||||
mpArea->mLightLayers.resize(2);
|
||||
|
||||
for (u32 ly = 0; ly < 2; ly++)
|
||||
{
|
||||
u32 NumLights = mpMREA->ReadLong();
|
||||
mpArea->mLightLayers[ly].resize(NumLights);
|
||||
|
||||
for (u32 l = 0; l < NumLights; l++)
|
||||
{
|
||||
ELightType Type = ELightType(mpMREA->ReadLong());
|
||||
CVector3f Color(*mpMREA);
|
||||
CVector3f Position(*mpMREA);
|
||||
CVector3f Direction(*mpMREA);
|
||||
float Multiplier = mpMREA->ReadFloat();
|
||||
float SpotCutoff = mpMREA->ReadFloat();
|
||||
mpMREA->Seek(0x9, SEEK_CUR);
|
||||
u32 FalloffType = mpMREA->ReadLong();
|
||||
mpMREA->Seek(0x4, SEEK_CUR);
|
||||
|
||||
// Relevant data is read - now we process and form a CLight out of it
|
||||
CLight *Light;
|
||||
|
||||
CColor LightColor = CColor(Color.x, Color.y, Color.z, 0.f);
|
||||
if (Multiplier < FLT_EPSILON)
|
||||
Multiplier = FLT_EPSILON;
|
||||
|
||||
// Local Ambient
|
||||
if (Type == eLocalAmbient)
|
||||
{
|
||||
Color *= Multiplier;
|
||||
|
||||
// Clamp
|
||||
if (Color.x > 1.f) Color.x = 1.f;
|
||||
if (Color.y > 1.f) Color.y = 1.f;
|
||||
if (Color.z > 1.f) Color.z = 1.f;
|
||||
CColor MultColor(Color.x, Color.y, Color.z, 1.f);
|
||||
|
||||
Light = CLight::BuildLocalAmbient(Position, MultColor);
|
||||
}
|
||||
|
||||
// Directional
|
||||
else if (Type == eDirectional)
|
||||
{
|
||||
Light = CLight::BuildDirectional(Position, Direction, LightColor);
|
||||
}
|
||||
|
||||
// Spot
|
||||
else if (Type == eSpot)
|
||||
{
|
||||
Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff);
|
||||
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC);
|
||||
}
|
||||
|
||||
// Custom
|
||||
else
|
||||
{
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
|
||||
Light = CLight::BuildCustom(Position, Direction, LightColor,
|
||||
DistAttenA, DistAttenB, DistAttenC,
|
||||
1.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
Light->SetLayer(ly);
|
||||
mpArea->mLightLayers[ly][l] = Light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ ECHOES ************
|
||||
void CAreaLoader::ReadHeaderEchoes()
|
||||
{
|
||||
// This function reads the header for Echoes and the Echoes demo disc
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP2)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
if (mVersion == eEchoes) mNumLayers = mpMREA->ReadLong();
|
||||
u32 numBlocks = mpMREA->ReadLong();
|
||||
|
||||
mGeometryBlockNum = mpMREA->ReadLong();
|
||||
mScriptLayerBlockNum = mpMREA->ReadLong();
|
||||
mScriptGeneratorBlockNum = mpMREA->ReadLong();
|
||||
mCollisionBlockNum = mpMREA->ReadLong();
|
||||
mUnknownBlockNum = mpMREA->ReadLong();
|
||||
mLightsBlockNum = mpMREA->ReadLong();
|
||||
mEmptyBlockNum = mpMREA->ReadLong();
|
||||
mPathBlockNum = mpMREA->ReadLong();
|
||||
mFFFFBlockNum = mpMREA->ReadLong();
|
||||
mUnknown2BlockNum = mpMREA->ReadLong();
|
||||
mEGMCBlockNum = mpMREA->ReadLong();
|
||||
if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong());
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(numBlocks, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
if (mVersion == eEchoes)
|
||||
{
|
||||
ReadCompressedBlocks();
|
||||
Decompress();
|
||||
}
|
||||
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadSCLYEchoes()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)");
|
||||
mBlockMgr->ToBlock(mScriptLayerBlockNum);
|
||||
|
||||
// SCLY
|
||||
for (u32 l = 0; l < mNumLayers; l++)
|
||||
{
|
||||
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
|
||||
if (pLayer)
|
||||
mpArea->mScriptLayers.push_back(pLayer);
|
||||
|
||||
mBlockMgr->ToNextBlock();
|
||||
}
|
||||
|
||||
// SCGN
|
||||
mBlockMgr->ToBlock(mScriptGeneratorBlockNum);
|
||||
CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
|
||||
|
||||
if (pLayer)
|
||||
mpArea->mpGeneratorLayer = pLayer;
|
||||
|
||||
SetUpObjects();
|
||||
}
|
||||
|
||||
// ************ CORRUPTION ************
|
||||
void CAreaLoader::ReadHeaderCorruption()
|
||||
{
|
||||
// This function reads the header for MP3, the MP3 prototype, and DKCR
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA header (MP3/DKCR)");
|
||||
mpArea->mTransform = CTransform4f(*mpMREA);
|
||||
mNumMeshes = mpMREA->ReadLong();
|
||||
mNumLayers = mpMREA->ReadLong();
|
||||
u32 NumSections = mpMREA->ReadLong();
|
||||
mClusters.resize(mpMREA->ReadLong());
|
||||
u32 SectionNumberCount = mpMREA->ReadLong();
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
mBlockMgr = new CBlockMgrIn(NumSections, mpMREA);
|
||||
mpMREA->SeekToBoundary(32);
|
||||
|
||||
ReadCompressedBlocks();
|
||||
|
||||
for (u32 iNum = 0; iNum < SectionNumberCount; iNum++)
|
||||
{
|
||||
CFourCC Type(*mpMREA);
|
||||
u32 Num = mpMREA->ReadLong();
|
||||
|
||||
if (Type == "AABB") mBoundingBoxesBlockNum = Num;
|
||||
else if (Type == "COLI") mCollisionBlockNum = Num;
|
||||
else if (Type == "DEPS") mDependenciesBlockNum = Num;
|
||||
else if (Type == "EGMC") mEGMCBlockNum = Num;
|
||||
else if (Type == "GPUD") mGPUBlockNum = Num;
|
||||
else if (Type == "LITE") mLightsBlockNum = Num;
|
||||
else if (Type == "PFL2") mPathBlockNum = Num;
|
||||
else if (Type == "PVS!") mPVSBlockNum = Num;
|
||||
else if (Type == "ROCT") mOctreeBlockNum = Num;
|
||||
else if (Type == "RSOS") mRSOBlockNum = Num;
|
||||
else if (Type == "SOBJ") mScriptLayerBlockNum = Num;
|
||||
else if (Type == "SGEN") mScriptGeneratorBlockNum = Num;
|
||||
else if (Type == "WOBJ") mGeometryBlockNum = Num; // note WOBJ can show up multiple times, but is always 0
|
||||
}
|
||||
|
||||
mpMREA->SeekToBoundary(32);
|
||||
Decompress();
|
||||
mBlockMgr->Init();
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadGeometryCorruption()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)");
|
||||
mBlockMgr->ToBlock(mGeometryBlockNum);
|
||||
|
||||
// Materials
|
||||
mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion);
|
||||
mBlockMgr->ToNextBlock();
|
||||
|
||||
// Geometry
|
||||
u32 CurWOBJSection = 1;
|
||||
u32 CurGPUSection = mGPUBlockNum;
|
||||
|
||||
for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++)
|
||||
{
|
||||
std::cout << "\rLoading mesh " << std::dec << iMesh + 1 << "/" << mNumMeshes;
|
||||
|
||||
CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion);
|
||||
mpArea->AddWorldModel(pWorldModel);
|
||||
|
||||
CurWOBJSection += 4;
|
||||
CurGPUSection = mBlockMgr->CurrentBlock();
|
||||
}
|
||||
|
||||
mpArea->MergeTerrain();
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadLightsCorruption()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)");
|
||||
mBlockMgr->ToBlock(mLightsBlockNum);
|
||||
|
||||
u32 babedead = mpMREA->ReadLong();
|
||||
if (babedead != 0xbabedead) return;
|
||||
|
||||
mpArea->mLightLayers.resize(4);
|
||||
|
||||
for (u32 iLayer = 0; iLayer < 4; iLayer++)
|
||||
{
|
||||
u32 NumLights = mpMREA->ReadLong();
|
||||
mpArea->mLightLayers[iLayer].resize(NumLights);
|
||||
|
||||
for (u32 iLight = 0; iLight < NumLights; iLight++)
|
||||
{
|
||||
ELightType Type = (ELightType) mpMREA->ReadLong();
|
||||
|
||||
float r = mpMREA->ReadFloat();
|
||||
float g = mpMREA->ReadFloat();
|
||||
float b = mpMREA->ReadFloat();
|
||||
float a = mpMREA->ReadFloat();
|
||||
CColor LightColor(r, g, b, a);
|
||||
|
||||
CVector3f Position(*mpMREA);
|
||||
CVector3f Direction(*mpMREA);
|
||||
mpMREA->Seek(0xC, SEEK_CUR);
|
||||
|
||||
float Multiplier = mpMREA->ReadFloat();
|
||||
float SpotCutoff = mpMREA->ReadFloat();
|
||||
mpMREA->Seek(0x9, SEEK_CUR);
|
||||
u32 FalloffType = mpMREA->ReadLong();
|
||||
mpMREA->Seek(0x18, SEEK_CUR);
|
||||
|
||||
// Relevant data is read - now we process and form a CLight out of it
|
||||
CLight *Light;
|
||||
|
||||
if (Multiplier < FLT_EPSILON)
|
||||
Multiplier = FLT_EPSILON;
|
||||
|
||||
// Local Ambient
|
||||
if (Type == eLocalAmbient)
|
||||
{
|
||||
Light = CLight::BuildLocalAmbient(Position, LightColor * Multiplier);
|
||||
}
|
||||
|
||||
// Directional
|
||||
else if (Type == eDirectional)
|
||||
{
|
||||
Light = CLight::BuildDirectional(Position, Direction, LightColor);
|
||||
}
|
||||
|
||||
// Spot
|
||||
else if (Type == eSpot)
|
||||
{
|
||||
Light = CLight::BuildSpot(Position, Direction.Normalized(), LightColor, SpotCutoff);
|
||||
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (250.f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
Light->SetDistAtten(DistAttenA, DistAttenB, DistAttenC);
|
||||
}
|
||||
|
||||
// Custom
|
||||
else
|
||||
{
|
||||
float DistAttenA = (FalloffType == 0) ? (2.f / Multiplier) : 0.f;
|
||||
float DistAttenB = (FalloffType == 1) ? (249.9998f / Multiplier) : 0.f;
|
||||
float DistAttenC = (FalloffType == 2) ? (25000.f / Multiplier) : 0.f;
|
||||
|
||||
Light = CLight::BuildCustom(Position, Direction, LightColor,
|
||||
DistAttenA, DistAttenB, DistAttenC,
|
||||
1.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
Light->SetLayer(iLayer);
|
||||
mpArea->mLightLayers[iLayer][iLight] = Light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ COMMON ************
|
||||
void CAreaLoader::ReadCompressedBlocks()
|
||||
{
|
||||
mTotalDecmpSize = 0;
|
||||
|
||||
for (u32 c = 0; c < mClusters.size(); c++)
|
||||
{
|
||||
mClusters[c].BufferSize = mpMREA->ReadLong();
|
||||
mClusters[c].DecompressedSize = mpMREA->ReadLong();
|
||||
mClusters[c].CompressedSize = mpMREA->ReadLong();
|
||||
mClusters[c].NumSections = mpMREA->ReadLong();
|
||||
mTotalDecmpSize += mClusters[c].DecompressedSize;
|
||||
}
|
||||
|
||||
mpMREA->SeekToBoundary(32);
|
||||
}
|
||||
|
||||
void CAreaLoader::Decompress()
|
||||
{
|
||||
// This function decompresses compressed clusters into a buffer.
|
||||
// It should be called at the beginning of the first compressed cluster.
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Decompressing MREA data");
|
||||
if (mVersion < eEchoes) return;
|
||||
|
||||
// Decompress clusters
|
||||
mDecmpBuffer = new u8[mTotalDecmpSize];
|
||||
u32 Offset = 0;
|
||||
|
||||
for (u32 c = 0; c < mClusters.size(); c++)
|
||||
{
|
||||
SCompressedCluster *cc = &mClusters[c];
|
||||
|
||||
// Is it decompressed already?
|
||||
if (mClusters[c].CompressedSize == 0)
|
||||
{
|
||||
mpMREA->ReadBytes(mDecmpBuffer + Offset, cc->DecompressedSize);
|
||||
Offset += cc->DecompressedSize;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
u32 StartOffset = 32 - (mClusters[c].CompressedSize % 32); // For some reason they pad the beginning instead of the end
|
||||
if (StartOffset != 32)
|
||||
mpMREA->Seek(StartOffset, SEEK_CUR);
|
||||
|
||||
std::vector<u8> cmp(mClusters[c].CompressedSize);
|
||||
mpMREA->ReadBytes(cmp.data(), cmp.size());
|
||||
|
||||
bool Success = CompressionUtil::DecompressAreaLZO(cmp.data(), cmp.size(), mDecmpBuffer + Offset, cc->DecompressedSize);
|
||||
if (!Success)
|
||||
throw "Failed to decompress MREA!";
|
||||
|
||||
Offset += cc->DecompressedSize;
|
||||
}
|
||||
}
|
||||
|
||||
TString Source = mpMREA->GetSourceString();
|
||||
mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::BigEndian);
|
||||
mpMREA->SetSourceString(Source.ToStdString());
|
||||
mBlockMgr->SetInputStream(mpMREA);
|
||||
mHasDecompressedBuffer = true;
|
||||
}
|
||||
|
||||
void CAreaLoader::ReadCollision()
|
||||
{
|
||||
Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)");
|
||||
mBlockMgr->ToBlock(mCollisionBlockNum);
|
||||
mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA);
|
||||
}
|
||||
|
||||
void CAreaLoader::SetUpObjects()
|
||||
{
|
||||
// Iterate over all objects
|
||||
for (u32 iLyr = 0; iLyr < mpArea->GetScriptLayerCount() + 1; iLyr++)
|
||||
{
|
||||
CScriptLayer *pLayer;
|
||||
if (iLyr < mpArea->GetScriptLayerCount()) pLayer = mpArea->mScriptLayers[iLyr];
|
||||
|
||||
else
|
||||
{
|
||||
pLayer = mpArea->GetGeneratorLayer();
|
||||
if (!pLayer) break;
|
||||
}
|
||||
|
||||
for (u32 iObj = 0; iObj < pLayer->GetNumObjects(); iObj++)
|
||||
{
|
||||
// Add object to object map
|
||||
CScriptObject *pObj = (*pLayer)[iObj];
|
||||
mpArea->mObjectMap[pObj->InstanceID()] = pObj;
|
||||
|
||||
// Store outgoing connections
|
||||
for (u32 iCon = 0; iCon < pObj->NumOutLinks(); iCon++)
|
||||
{
|
||||
SLink Connection = pObj->OutLink(iCon);
|
||||
|
||||
SLink NewConnection;
|
||||
NewConnection.State = Connection.State;
|
||||
NewConnection.Message = Connection.Message;
|
||||
NewConnection.ObjectID = pObj->InstanceID();
|
||||
mConnectionMap[Connection.ObjectID].push_back(NewConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store connections
|
||||
for (auto it = mpArea->mObjectMap.begin(); it != mpArea->mObjectMap.end(); it++)
|
||||
{
|
||||
u32 InstanceID = it->first;
|
||||
auto iConMap = mConnectionMap.find(InstanceID);
|
||||
|
||||
if (iConMap != mConnectionMap.end())
|
||||
{
|
||||
CScriptObject *pObj = mpArea->GetInstanceByID(InstanceID);
|
||||
pObj->mInConnections = iConMap->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ STATIC ************
|
||||
CGameArea* CAreaLoader::LoadMREA(CInputStream& MREA)
|
||||
{
|
||||
CAreaLoader Loader;
|
||||
|
||||
// Validation
|
||||
if (!MREA.IsValid()) return nullptr;
|
||||
Log::Write("Loading " + MREA.GetSourceString());
|
||||
|
||||
u32 deadbeef = MREA.ReadLong();
|
||||
if (deadbeef != 0xdeadbeef)
|
||||
{
|
||||
Log::FileError(MREA.GetSourceString(), "Invalid MREA magic: " + TString::HexString(deadbeef));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Header
|
||||
Loader.mpArea = new CGameArea;
|
||||
u32 version = MREA.ReadLong();
|
||||
Loader.mVersion = GetFormatVersion(version);
|
||||
Loader.mpMREA = &MREA;
|
||||
|
||||
switch (Loader.mVersion)
|
||||
{
|
||||
case ePrimeDemo:
|
||||
case ePrime:
|
||||
Loader.ReadHeaderPrime();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYPrime();
|
||||
Loader.ReadCollision();
|
||||
Loader.ReadLightsPrime();
|
||||
break;
|
||||
case eEchoesDemo:
|
||||
case eEchoes:
|
||||
Loader.ReadHeaderEchoes();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
Loader.ReadLightsPrime();
|
||||
break;
|
||||
case eCorruptionProto:
|
||||
Loader.ReadHeaderCorruption();
|
||||
Loader.ReadGeometryPrime();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
break;
|
||||
case eCorruption:
|
||||
case eReturns:
|
||||
Loader.ReadHeaderCorruption();
|
||||
Loader.ReadGeometryCorruption();
|
||||
Loader.ReadSCLYEchoes();
|
||||
Loader.ReadCollision();
|
||||
if (Loader.mVersion == eCorruption) Loader.ReadLightsCorruption();
|
||||
break;
|
||||
default:
|
||||
Log::FileError(MREA.GetSourceString(), "Unsupported MREA version: " + TString::HexString(version));
|
||||
Loader.mpArea.Delete();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
delete Loader.mBlockMgr;
|
||||
return Loader.mpArea;
|
||||
}
|
||||
|
||||
EGame CAreaLoader::GetFormatVersion(u32 version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 0xC: return ePrimeDemo;
|
||||
case 0xF: return ePrime;
|
||||
case 0x15: return eEchoesDemo;
|
||||
case 0x19: return eEchoes;
|
||||
case 0x1D: return eCorruptionProto;
|
||||
case 0x1E: return eCorruption;
|
||||
case 0x20: return eReturns;
|
||||
default: return eUnknownVersion;
|
||||
}
|
||||
}
|
||||
84
src/Core/Resource/Factory/CAreaLoader.h
Normal file
84
src/Core/Resource/Factory/CAreaLoader.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef CAREALOADER_H
|
||||
#define CAREALOADER_H
|
||||
|
||||
#include <FileIO/FileIO.h>
|
||||
#include "../CGameArea.h"
|
||||
#include "../EFormatVersion.h"
|
||||
#include "CBlockMgrIn.h"
|
||||
#include <Core/CResCache.h>
|
||||
#include <Resource/script/SConnection.h>
|
||||
|
||||
class CAreaLoader
|
||||
{
|
||||
struct SCompressedCluster;
|
||||
|
||||
// Area data
|
||||
TResPtr<CGameArea> mpArea;
|
||||
CInputStream *mpMREA;
|
||||
CBlockMgrIn *mBlockMgr;
|
||||
EGame mVersion;
|
||||
u32 mNumMeshes;
|
||||
u32 mNumLayers;
|
||||
|
||||
// Object connections
|
||||
std::unordered_map<u32, std::vector<SLink>> mConnectionMap;
|
||||
|
||||
// Compression
|
||||
u8 *mDecmpBuffer;
|
||||
bool mHasDecompressedBuffer;
|
||||
std::vector<SCompressedCluster> mClusters;
|
||||
u32 mTotalDecmpSize;
|
||||
|
||||
// Block numbers
|
||||
u32 mGeometryBlockNum;
|
||||
u32 mScriptLayerBlockNum;
|
||||
u32 mCollisionBlockNum;
|
||||
u32 mUnknownBlockNum;
|
||||
u32 mLightsBlockNum;
|
||||
u32 mEmptyBlockNum;
|
||||
u32 mPathBlockNum;
|
||||
u32 mOctreeBlockNum;
|
||||
u32 mScriptGeneratorBlockNum;
|
||||
u32 mFFFFBlockNum;
|
||||
u32 mUnknown2BlockNum;
|
||||
u32 mEGMCBlockNum;
|
||||
u32 mBoundingBoxesBlockNum;
|
||||
u32 mDependenciesBlockNum;
|
||||
u32 mGPUBlockNum;
|
||||
u32 mPVSBlockNum;
|
||||
u32 mRSOBlockNum;
|
||||
|
||||
struct SCompressedCluster {
|
||||
u32 BufferSize, DecompressedSize, CompressedSize, NumSections;
|
||||
};
|
||||
|
||||
CAreaLoader();
|
||||
~CAreaLoader();
|
||||
|
||||
// Prime
|
||||
void ReadHeaderPrime();
|
||||
void ReadGeometryPrime();
|
||||
void ReadSCLYPrime();
|
||||
void ReadLightsPrime();
|
||||
|
||||
// Echoes
|
||||
void ReadHeaderEchoes();
|
||||
void ReadSCLYEchoes();
|
||||
|
||||
// Corruption
|
||||
void ReadHeaderCorruption();
|
||||
void ReadGeometryCorruption();
|
||||
void ReadLightsCorruption();
|
||||
|
||||
// Common
|
||||
void ReadCompressedBlocks();
|
||||
void Decompress();
|
||||
void ReadCollision();
|
||||
void SetUpObjects();
|
||||
|
||||
public:
|
||||
static CGameArea* LoadMREA(CInputStream& MREA);
|
||||
static EGame GetFormatVersion(u32 version);
|
||||
};
|
||||
|
||||
#endif // CAREALOADER_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user