1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details 2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt 3 4 #include "Program.h" 5 #include "FileSystem.h" 6 #include "StringF.h" 7 #include "StringRange.h" 8 #include "graphics/Graphics.h" 9 #include "utils.h" 10 11 #include <set> 12 13 namespace Graphics { 14 15 namespace OGL { 16 17 // #version 140 for OpenGL3.1 18 // #version 150 for OpenGL3.2 19 // #version 330 for OpenGL3.3 20 static const char *s_glslVersion = "#version 140\n"; 21 GLuint Program::s_curProgram = 0; 22 23 // Check and warn about compile & link errors check_glsl_errors(const char * filename,GLuint obj)24 static bool check_glsl_errors(const char *filename, GLuint obj) 25 { 26 //check if shader or program 27 bool isShader = (glIsShader(obj) == GL_TRUE); 28 29 int infologLength = 0; 30 char infoLog[1024]; 31 32 if (isShader) 33 glGetShaderInfoLog(obj, 1024, &infologLength, infoLog); 34 else 35 glGetProgramInfoLog(obj, 1024, &infologLength, infoLog); 36 37 GLint status; 38 if (isShader) 39 glGetShaderiv(obj, GL_COMPILE_STATUS, &status); 40 else 41 glGetProgramiv(obj, GL_LINK_STATUS, &status); 42 43 if (status == GL_FALSE) { 44 Error("Error compiling shader: %s:\n%sOpenGL vendor: %s\nOpenGL renderer string: %s", 45 filename, infoLog, glGetString(GL_VENDOR), glGetString(GL_RENDERER)); 46 return false; 47 } 48 49 #if 0 50 if (!isShader) { 51 // perform general validation that the program is usable 52 glValidateProgram(obj); 53 54 glGetProgramiv(obj, GL_VALIDATE_STATUS, &status); 55 56 if (status == GL_FALSE) { 57 Error("Error vaildating shader: %s:\n%sOpenGL vendor: %s\nOpenGL renderer string: %s", 58 filename, infoLog, glGetString(GL_VENDOR), glGetString(GL_RENDERER)); 59 return false; 60 } 61 } 62 #endif 63 64 // TODO: this code is obsolete 65 // Log warnings even if successfully compiled 66 // Sometimes the log is full of junk "success" messages so 67 // this is not a good use for OS::Warning 68 #ifndef NDEBUG 69 if (infologLength > 0) { 70 if (pi_strcasestr("infoLog", "warning")) 71 Output("%s: %s", filename, infoLog); 72 } 73 #endif 74 75 return true; 76 } 77 78 struct Shader { ShaderGraphics::OGL::Shader79 Shader(GLenum type, const std::string &filename, const std::string &defines) 80 { 81 RefCountedPtr<FileSystem::FileData> filecode = FileSystem::gameDataFiles.ReadFile(filename); 82 83 if (!filecode.Valid()) 84 Error("Could not load %s", filename.c_str()); 85 86 std::string strCode(filecode->AsStringRange().ToString()); 87 size_t found = strCode.find("#include"); 88 while (found != std::string::npos) { 89 // find the name of the file to include 90 const size_t begFilename = strCode.find_first_of("\"", found + 8) + 1; 91 const size_t endFilename = strCode.find_first_of("\"", begFilename + 1); 92 93 const std::string incFilename = strCode.substr(begFilename, endFilename - begFilename); 94 95 // check we haven't it already included it (avoids circular dependencies) 96 const std::set<std::string>::const_iterator foundIt = previousIncludes.find(incFilename); 97 if (foundIt != previousIncludes.end()) { 98 Error("Circular, or multiple, include of %s\n", incFilename.c_str()); 99 } else { 100 previousIncludes.insert(incFilename); 101 } 102 103 // build path for include 104 const std::string incPathBuffer = stringf("shaders/opengl/%0", incFilename); 105 106 // read included file 107 RefCountedPtr<FileSystem::FileData> incCode = FileSystem::gameDataFiles.ReadFile(incPathBuffer); 108 assert(incCode.Valid()); 109 110 if (incCode.Valid()) { 111 // replace the #include and filename with the included files text 112 strCode.replace(found, (endFilename + 1) - found, incCode->GetData(), incCode->GetSize()); 113 found = strCode.find("#include"); 114 } else { 115 Error("Could not load shader #include %s for shader %s\n", incPathBuffer.c_str(), filename.c_str()); 116 } 117 } 118 // Store the modified text with the included files (if any) 119 const StringRange code(strCode.c_str(), strCode.size()); 120 121 // Build the final shader text to be compiled 122 AppendSource(s_glslVersion); 123 AppendSource(defines.c_str()); 124 if (type == GL_VERTEX_SHADER) { 125 AppendSource("#define VERTEX_SHADER\n"); 126 } else { 127 AppendSource("#define FRAGMENT_SHADER\n"); 128 } 129 AppendSource(code.StripUTF8BOM()); 130 #if 0 131 static bool s_bDumpShaderSource = true; 132 if (s_bDumpShaderSource) { 133 const char SHADER_OUT_DIR_NAME[] = "shaders"; 134 const char SHADER_OGL_OUT_DIR_NAME[] = "shaders/opengl"; 135 FileSystem::userFiles.MakeDirectory(SHADER_OUT_DIR_NAME); 136 FileSystem::userFiles.MakeDirectory(SHADER_OGL_OUT_DIR_NAME); 137 const std::string outFilename(FileSystem::GetUserDir() + "/" + filename); 138 FILE *tmp = fopen(outFilename.c_str(), "wb"); 139 if(tmp) { 140 Output("%s", filename); 141 for( Uint32 i=0; i<blocks.size(); i++ ) { 142 const char *block = blocks[i]; 143 const GLint sizes = block_sizes[i]; 144 if(block && sizes>0) { 145 fprintf(tmp, "%.*s", sizes, block); 146 } 147 } 148 fclose(tmp); 149 } else { 150 Output("Could not open file %s", outFilename.c_str()); 151 } 152 } 153 #endif 154 shader = glCreateShader(type); 155 if (glIsShader(shader) != GL_TRUE) 156 throw ShaderException(); 157 158 Compile(shader); 159 160 if (!check_glsl_errors(filename.c_str(), shader)) 161 throw ShaderException(); 162 }; 163 ~ShaderGraphics::OGL::Shader164 ~Shader() 165 { 166 glDeleteShader(shader); 167 } 168 169 GLuint shader; 170 171 private: AppendSourceGraphics::OGL::Shader172 void AppendSource(const char *str) 173 { 174 blocks.push_back(str); 175 block_sizes.push_back(std::strlen(str)); 176 } 177 AppendSourceGraphics::OGL::Shader178 void AppendSource(StringRange str) 179 { 180 blocks.push_back(str.begin); 181 block_sizes.push_back(str.Size()); 182 } 183 CompileGraphics::OGL::Shader184 void Compile(GLuint shader_id) 185 { 186 assert(blocks.size() == block_sizes.size()); 187 glShaderSource(shader_id, blocks.size(), &blocks[0], &block_sizes[0]); 188 glCompileShader(shader_id); 189 } 190 191 std::vector<const char *> blocks; 192 std::vector<GLint> block_sizes; 193 std::set<std::string> previousIncludes; 194 }; 195 Program()196 Program::Program() : 197 m_name(""), 198 m_defines(""), 199 m_program(0), 200 success(false) 201 { 202 } 203 Program(const std::string & name,const std::string & defines)204 Program::Program(const std::string &name, const std::string &defines) : 205 m_name(name), 206 m_defines(defines), 207 m_program(0), 208 success(false) 209 { 210 LoadShaders(name, defines); 211 InitUniforms(); 212 } 213 ~Program()214 Program::~Program() 215 { 216 glDeleteProgram(m_program); 217 } 218 Reload()219 void Program::Reload() 220 { 221 Unuse(); 222 glDeleteProgram(m_program); 223 LoadShaders(m_name, m_defines); 224 InitUniforms(); 225 } 226 Use()227 void Program::Use() 228 { 229 if (s_curProgram != m_program) 230 glUseProgram(m_program); 231 s_curProgram = m_program; 232 } 233 Unuse()234 void Program::Unuse() 235 { 236 glUseProgram(0); 237 s_curProgram = 0; 238 } 239 240 //load, compile and link LoadShaders(const std::string & name,const std::string & defines)241 void Program::LoadShaders(const std::string &name, const std::string &defines) 242 { 243 PROFILE_SCOPED() 244 const std::string filename = std::string("shaders/opengl/") + name; 245 246 //load, create and compile shaders 247 Shader vs(GL_VERTEX_SHADER, filename + ".vert", defines); 248 Shader fs(GL_FRAGMENT_SHADER, filename + ".frag", defines); 249 250 //create program, attach shaders and link 251 m_program = glCreateProgram(); 252 if (glIsProgram(m_program) != GL_TRUE) 253 throw ProgramException(); 254 255 glAttachShader(m_program, vs.shader); 256 257 glAttachShader(m_program, fs.shader); 258 259 //extra attribs, if they exist 260 glBindAttribLocation(m_program, 0, "a_vertex"); 261 glBindAttribLocation(m_program, 1, "a_normal"); 262 glBindAttribLocation(m_program, 2, "a_color"); 263 glBindAttribLocation(m_program, 3, "a_uv0"); 264 glBindAttribLocation(m_program, 4, "a_uv1"); 265 glBindAttribLocation(m_program, 5, "a_tangent"); 266 glBindAttribLocation(m_program, 6, "a_transform"); 267 // a_transform @ 6 shadows (uses) 7, 8, and 9 268 // next available is layout (location = 10) 269 270 glBindFragDataLocation(m_program, 0, "frag_color"); 271 272 glLinkProgram(m_program); 273 274 success = check_glsl_errors(name.c_str(), m_program); 275 276 //shaders may now be deleted by Shader destructor 277 } 278 InitUniforms()279 void Program::InitUniforms() 280 { 281 PROFILE_SCOPED() 282 //Init generic uniforms, like matrices 283 uProjectionMatrix.Init("uProjectionMatrix", m_program); 284 uViewMatrix.Init("uViewMatrix", m_program); 285 uViewMatrixInverse.Init("uViewMatrixInverse", m_program); 286 uViewProjectionMatrix.Init("uViewProjectionMatrix", m_program); 287 uNormalMatrix.Init("uNormalMatrix", m_program); 288 289 //Light uniform parameters 290 char cLight[64]; 291 for (int i = 0; i < 4; i++) { 292 snprintf(cLight, 64, "uLight[%d]", i); 293 const std::string strLight(cLight); 294 lights[i].diffuse.Init((strLight + ".diffuse").c_str(), m_program); 295 lights[i].specular.Init((strLight + ".specular").c_str(), m_program); 296 lights[i].position.Init((strLight + ".position").c_str(), m_program); 297 } 298 299 invLogZfarPlus1.Init("invLogZfarPlus1", m_program); 300 diffuse.Init("material.diffuse", m_program); 301 emission.Init("material.emission", m_program); 302 specular.Init("material.specular", m_program); 303 shininess.Init("material.shininess", m_program); 304 texture0.Init("texture0", m_program); 305 texture1.Init("texture1", m_program); 306 texture2.Init("texture2", m_program); 307 texture3.Init("texture3", m_program); 308 texture4.Init("texture4", m_program); 309 texture5.Init("texture5", m_program); 310 texture6.Init("texture6", m_program); 311 heatGradient.Init("heatGradient", m_program); 312 heatingMatrix.Init("heatingMatrix", m_program); 313 heatingNormal.Init("heatingNormal", m_program); 314 heatingAmount.Init("heatingAmount", m_program); 315 sceneAmbient.Init("scene.ambient", m_program); 316 } 317 318 } // namespace OGL 319 320 } // namespace Graphics 321