This repository has been archived on 2022-10-27. You can view files and clone it, but cannot push or open issues or pull requests.
space-war/shader.cpp

278 lines
8.8 KiB
C++

#include "shader.hpp"
SourceCode::SourceCode(const std::string& code)
: code_(code) {}
Shader::ShaderStruct::ShaderStruct(const SourceCode& code, Shader::ShaderStruct::Type type) {
if (code.Empty()) { return; }
switch (type) {
case ShaderStruct::Vertex:
Log("compiling vertex shader ...");
GL_CALL(shader_ = glCreateShader(GL_VERTEX_SHADER));
break;
case Fragment:
Log("compiling fragment shader ...");
GL_CALL(shader_ = glCreateShader(GL_FRAGMENT_SHADER));
break;
case Geometry:
Log("compiling geometry shader ...");
GL_CALL(shader_ = glCreateShader(GL_GEOMETRY_SHADER));
break;
}
if (shader_ == 0) {
Log("create shader failed");
} else {
const GLchar* const sources[] = {code.GetCode().c_str()};
GL_CALL(glShaderSource(shader_, 1, sources, nullptr));
GL_CALL(glCompileShader(shader_));
GLint err;
GL_CALL(glGetShaderiv(shader_, GL_COMPILE_STATUS, &err));
if (err == GL_FALSE) {
char buf[1024] = {0};
GL_CALL(glGetShaderInfoLog(shader_, sizeof(buf), nullptr, buf));
Log("shader compile failed:\n%s", buf);
}
}
}
Shader::ShaderStruct::~ShaderStruct() {
GL_CALL(glDeleteShader(shader_));
}
Shader::Shader(const SourceCode* vertex,
const SourceCode* fragment,
const SourceCode* geometry) {
if (vertex) {
shaders_[0].reset(new ShaderStruct(*vertex, ShaderStruct::Vertex));
}
if (fragment) {
shaders_[1].reset(new ShaderStruct(*fragment, ShaderStruct::Fragment));
}
if (geometry) {
shaders_[2].reset(new ShaderStruct(*geometry, ShaderStruct::Geometry));
}
GL_CALL(program_ = glCreateProgram());
if (program_ == 0) {
Log("create shader program failed");
} else {
for (const auto& shader : shaders_) {
if (shader) {
GL_CALL(glAttachShader(program_, shader->Get()));
}
}
GL_CALL(glLinkProgram(program_));
GLint err;
GL_CALL(glGetProgramiv(program_, GL_LINK_STATUS, &err));
if (err == GL_FALSE) {
char buf[1024] = {0};
GL_CALL(glGetProgramInfoLog(program_, sizeof(buf), nullptr, buf));
Log("link shader program failed:\n%s", buf);
}
}
}
Shader::~Shader() {
for (auto& shader : shaders_)
shader.reset();
GL_CALL(glDeleteProgram(program_));
}
void Shader::SetInt(const std::string& name, int i) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform1i(loc, i));
}
}
void Shader::SetInt2(const std::string& name, int i1, int i2) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform2i(loc, i1, i2));
}
}
void Shader::SetInt3(const std::string& name, int i1, int i2, int i3) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform3i(loc, i1, i2, i3));
}
}
void Shader::SetInt4(const std::string& name, int i1, int i2, int i3, int i4) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform4i(loc, i1, i2, i3, i4));
}
}
void Shader::SetFloat(const std::string& name, float f) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform1f(loc, f));
}
}
void Shader::SetFloat2(const std::string& name, float f1, float f2) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform2f(loc, f1, f2));
}
}
void Shader::SetFloat3(const std::string& name, float f1, float f2, float f3) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform3f(loc, f1, f2, f3));
}
}
void Shader::SetFloat4(const std::string& name, float f1, float f2, float f3, float f4) {
GLint loc;
GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
if (loc == -1) {
Log("uniform variable %s is not found", name.c_str());
} else {
Use();
GL_CALL(glUniform4f(loc, f1, f2, f3, f4));
}
}
// void Shader::SetMat4(const std::string& name, const glm::mat4& mat, bool transpose) {
// GLint loc;
// GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
// if (loc == -1) {
// Log("uniform variable %s is not found", name.c_str());
// } else {
// Use();
// GL_CALL(glUniformMatrix4fv(loc, 1, transpose ? GL_TRUE : GL_FALSE, glm::value_ptr(mat)));
// }
// }
// void Shader::SetFloatVec3(const std::string& name, const glm::vec3& data) {
// GLint loc;
// GL_CALL(loc = glGetUniformLocation(program_, name.c_str()));
// if (loc == -1) {
// Log("uniform variable %s is not found", name.c_str());
// } else {
// Use();
// GL_CALL(glUniform3fv(loc, 1, glm::value_ptr(data)));
// }
// }
SourceCodePtr CreateSourceCodeFromFile(const std::string& filename) {
std::ifstream file(filename);
if (file.fail()) {
Log("SourceCodeRes %s load failed", filename.c_str());
return nullptr;
}
std::string code((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
return CreateSourceCode(code);
}
void parseShaderFile(const std::string& code,
std::string& vertexCode,
std::string& geometryCode,
std::string& fragmentCode) {
std::string::size_type pos[3] = {code.find("#type vertex"),
code.find("#type geometry"),
code.find("#type fragment")};
/* 1 - vertex shader code
2 - tessellation shader code
3 - fragment shader code */
std::string* codes[3] = {&vertexCode,
&geometryCode,
&fragmentCode};
// sort three code by their position
if (pos[0] > pos[1]) {
std::swap(pos[0], pos[1]);
std::swap(codes[0], codes[1]);
}
if (pos[1] > pos[2]) {
std::swap(pos[1], pos[2]);
std::swap(codes[1], codes[2]);
}
if (pos[0] > pos[1]) {
std::swap(pos[0], pos[1]);
std::swap(codes[0], codes[1]);
}
// split file in codes
if (pos[0] != std::string::npos) {
if (pos[1] != std::string::npos) {
std::copy_n(code.begin() + pos[0], pos[1] - pos[0], std::back_inserter(*codes[0]));
} else {
std::copy(code.begin() + pos[0], code.end(), std::back_inserter(*codes[0]));
}
}
if (pos[1] != std::string::npos) {
if (pos[2] != std::string::npos) {
std::copy_n(code.begin() + pos[1], pos[2] - pos[1], std::back_inserter(*codes[1]));
} else {
std::copy(code.begin() + pos[1], code.end(), std::back_inserter(*codes[1]));
}
}
if (pos[2] != std::string::npos) {
std::copy(code.begin() + pos[2], code.end(), std::back_inserter(*codes[2]));
}
for (auto& c : codes) {
c->erase(0, c->find_first_of('\n'));
}
}
SourceCodeGroup CreateSourceCodeFromGroup(const std::string& code) {
std::string vertex, geometry, fragment;
parseShaderFile(code, vertex, geometry, fragment);
SourceCodeGroup group;
if (!vertex.empty())
group.vertexCode = CreateSourceCode(vertex);
if (!geometry.empty())
group.geometryCode = CreateSourceCode(geometry);
if (!fragment.empty())
group.fragmentCode = CreateSourceCode(fragment);
return group;
}
SourceCodeGroup CreateSourceCodeFromGroupFile(const std::string& filename) {
std::ifstream file(filename);
if (file.fail()) {
Log("SourceCodeRes %s load failed", filename.c_str());
return {nullptr, nullptr, nullptr};
}
std::string code((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
return CreateSourceCodeFromGroup(code);
}