PrimeWorldEditor/OpenGL/CShader.cpp

264 lines
7.4 KiB
C++

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