#include "../../../include/RenderBackend/OpenGL/PixelShader.hpp" #include "../../../include/RenderBackend/OpenGL/opengl.hpp" #include "../../../include/RenderBackend/OpenGL/CompiledShader.hpp" #include using namespace std; namespace amalgine { PixelShaderTooManyAttributes::PixelShaderTooManyAttributes(i32 maxPixelAttributes) : runtime_error(string("Attempting to define more than ") + to_string(maxPixelAttributes) + " pixel attributes") { } PixelShaderAttributeAlreadyDefined::PixelShaderAttributeAlreadyDefined(const std::string &attributeName) : runtime_error(string("A pixel attribute with the name ") + attributeName + " has already been defined") { } PixelShaderInvalidAttributeName::PixelShaderInvalidAttributeName(const std::string &attributeName) : runtime_error(string("Pixel attribute name ") + attributeName + " is invalid") { } PixelShaderFunctionAlreadyDefined::PixelShaderFunctionAlreadyDefined(const std::string &funcName) : runtime_error(string("Pixel shader function already defined: ") + funcName) { } PixelShader::PixelShader() : locationCounter(0), mainFuncDefined(false) { glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxPixelAttribs); writeHeader("#version 330 core\n\n"); } const string& PixelShader::getOutputAttributeName(i32 attributeIndex) { assert(attributeIndex < pixelAttributeNames.size()); return pixelAttributeNames[attributeIndex].name; } ShaderOutputVec4 PixelShader::defineOutputVec4(const std::string &name) { i32 attributeIndex = defineOutputVariable(name, "vec4"); return ShaderOutputVec4(this, attributeIndex); } // TODO: Generate warning if global variable is defined but not assigned to? ShaderGlobalVec3 PixelShader::defineGlobalVec3(const std::string &name) { if(!isShaderVariableNameValid(name.c_str())) throw PixelShaderInvalidAttributeName(name); if(globalAttributes.find(name) != globalAttributes.end()) throw PixelShaderAttributeAlreadyDefined(name); writeHeader("uniform vec3 "); writeHeader(name); writeHeader(";\n"); ShaderGlobalVec3 globalVec(name); globalAttributes[name] = globalVec.getVecObject(); return ShaderGlobalVec3(globalVec); } i32 PixelShader::defineOutputVariable(const string &variableName, const char *typeName) { if(!isShaderVariableNameValid(variableName.c_str())) throw PixelShaderInvalidAttributeName(variableName); if(locationCounter + 1 > maxPixelAttribs) throw PixelShaderTooManyAttributes(maxPixelAttribs); if(pixelAttributes.find(variableName) != pixelAttributes.end()) throw PixelShaderAttributeAlreadyDefined(variableName); i32 attributeIndex = locationCounter; pixelAttributes[variableName] = locationCounter; writeHeader("out "); writeHeader(typeName); writeHeader(" "); writeHeader(variableName); writeHeader(";\n"); ShaderAttribute shaderAttribute; shaderAttribute.name = variableName; shaderAttribute.typeName = typeName; pixelAttributeNames.push_back(shaderAttribute); ++locationCounter; return attributeIndex; } void PixelShader::defineMain(PixelShaderMainFunc mainFunc) { if(mainFuncDefined) throw PixelShaderFunctionAlreadyDefined("main"); writeBody("void main() {\n"); mainFunc(); mainFuncDefined = true; writeBody("}\n\n"); } void PixelShader::assign(const ShaderOutputVec4 &lhsVariable, const ShaderVec4 &rhsVariable) { writeBody(lhsVariable.getName()); writeBody(" = "); writeBody(rhsVariable.getOutput()); writeBody(";\n"); } string PixelShader::build() const { std::string result; result.reserve(header.size() + 2 + body.size()); result += header; result += "\n"; result += body; return result; } void PixelShader::writeHeader(const string &code) { header += code; } void PixelShader::writeBody(const string &code) { body += code; } Result PixelShader::compile() { GLuint fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER); string verterShaderSource = build(); const char *verterShaderSourcePtr = verterShaderSource.c_str(); printf("Vertex shader:\n%s\n", verterShaderSourcePtr); glShaderSource(fragmentShaderId, 1, &verterShaderSourcePtr, NULL); glCompileShader(fragmentShaderId); GLint status; glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &status); string compileLog = getShaderCompileLog((u32)fragmentShaderId); if(status == GL_TRUE) { if(!compileLog.empty()) printf("Pixel shader compile log:\n%s", compileLog.c_str()); return Result::Ok(new CompiledPixelShader(fragmentShaderId, move(pixelAttributes))); } else return Result::Err(compileLog); } }