/** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson ** ** This file is part of Supermodel. ** ** Supermodel is free software: you can redistribute it and/or modify it under ** the terms of the GNU General Public License as published by the Free ** Software Foundation, either version 3 of the License, or (at your option) ** any later version. ** ** Supermodel is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ** more details. ** ** You should have received a copy of the GNU General Public License along ** with Supermodel. If not, see . **/ /* * Shader.cpp * * OpenGL shader management. * * To-Do List * ---------- * - Mesa crashes because, evidently, the function pointers are invalid. Mesa * returns the following information: * Vendor: Mesa project: www.mesa3d.org * Renderer: Mesa GLX Indirect * Version: 1.2 (1.5 Mesa 6.5.1) * Shading Language Version: (null) * Maximum Vertex Array Size: -1 vertices * Maximum Texture Size: 2048 texels * Maximum Vertex Attributes: 16 * Maximum Vertex Uniforms: 16 * - Check for OpenGL 2.0 and perhaps check some of the function pointers, * which will be NULL, if GL 2.0 and shaders are not supported. * - Keep in mind that all these checks should probably go somewhere else... * - Turn this into a class. */ #include #include #include "Pkgs/glew.h" #include "Supermodel.h" // Load a source file. Pointer returned must be freed by caller. Returns NULL if failed. static char *LoadShaderSource(const char *file) { FILE *fp; char *buf; int size; // Open shader and get the file size fp = fopen(file, "r"); if (NULL == fp) { ErrorLog("Unable to open shader source file: %s", file); return NULL; } fseek(fp, 0, SEEK_END); size = ftell(fp); rewind(fp); // Allocate memory and read it in buf = new(std::nothrow) char[size+1]; if (NULL == buf) { ErrorLog("Insufficient memory to load shader source file: %s", file); fclose(fp); return NULL; } buf[size] = '\0'; // for safety, actual size might be smaller once newline characters are converted size = fread(buf, sizeof(char), size, fp); buf[size] = '\0'; fclose(fp); return buf; } bool LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint *fragmentShaderPtr, std::string vsFile, std::string fsFile, const char *vsString, const char *fsString) { char infoLog[2048]; const char *vsSource, *fsSource; // source code GLuint shaderProgram, vertexShader, fragmentShader; GLint result, len; bool ret = OKAY; // Load shaders from files if specified if (!vsFile.empty()) vsSource = LoadShaderSource(vsFile.c_str()); else vsSource = vsString; if (!fsFile.empty()) fsSource = LoadShaderSource(fsFile.c_str()); else fsSource = fsString; if (vsSource == NULL || fsSource == NULL) { ret = FAIL; goto Quit; } // Ensure that shader support exists if ((glCreateProgram==NULL) || (glCreateShader==NULL) || (glShaderSource==NULL) || (glCompileShader==NULL)) { ret = FAIL; ErrorLog("OpenGL 2.x does not appear to be present. Unable to proceed."); goto Quit; } // Create the shaders and shader program shaderProgram = glCreateProgram(); vertexShader = glCreateShader(GL_VERTEX_SHADER); fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); *shaderProgramPtr = shaderProgram; *vertexShaderPtr = vertexShader; *fragmentShaderPtr = fragmentShader; // Attempt to compile vertex shader glShaderSource(vertexShader, 1, (const GLchar **) &vsSource, NULL); glCompileShader(vertexShader); glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result); if (!result) // failed to compile { glGetShaderInfoLog(vertexShader, 2048, &len, infoLog); ErrorLog("Vertex shader failed to compile. Your OpenGL driver said:\n%s", infoLog); ret = FAIL; // error } // Attempt to compile fragment shader glShaderSource(fragmentShader, 1, (const GLchar **) &fsSource, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result); if (!result) // failed to compile { glGetShaderInfoLog(fragmentShader, 2048, &len, infoLog); ErrorLog("Fragment shader failed to compile. Your OpenGL driver said:\n%s", infoLog); ret = FAIL; // error } // Link glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &result); if (result == GL_FALSE) { glGetProgramInfoLog(shaderProgram, 2048, &len, infoLog); ErrorLog("Failed to link shader objects. Your OpenGL driver said:\n%s\n", infoLog); ret = FAIL; // error } // Enable the shader (if no errors) if (ret == OKAY) glUseProgram(shaderProgram); // Clean up and quit Quit: if ((vsSource != NULL) && !vsFile.empty()) // loaded from file, must delete delete [] vsSource; if ((fsSource != NULL) && !fsFile.empty()) // "" delete [] fsSource; return ret; } void DestroyShaderProgram(GLuint shaderProgram, GLuint vertexShader, GLuint fragmentShader) { // In case LoadShaderProgram() failed above due to lack of OpenGL 2.0+ functions... if ((glUseProgram==NULL) || (glDeleteShader==NULL) || (glDeleteProgram==NULL)) return; glUseProgram(0); // return to fixed function pipeline glDeleteShader(vertexShader); glDeleteShader(fragmentShader); glDeleteProgram(shaderProgram); }