/** * From the OpenGL Programming wikibook: http://en.wikibooks.org/wiki/OpenGL_Programming * This file is in the public domain. * Contributors: Sylvain Beucler */ #include using namespace std; #include "SDL.h" #include /** * Store all the file's contents in memory, useful to pass shaders * source code to OpenGL. Using SDL_RWops for Android asset support. */ char* file_read(const char* filename, int* size) { SDL_RWops *rw = SDL_RWFromFile(filename, "rb"); if (rw == NULL) return NULL; Sint64 res_size = SDL_RWsize(rw); char* res = (char*)malloc(res_size + 1); Sint64 nb_read_total = 0, nb_read = 1; char* buf = res; while (nb_read_total < res_size && nb_read != 0) { nb_read = SDL_RWread(rw, buf, 1, (res_size - nb_read_total)); nb_read_total += nb_read; buf += nb_read; } SDL_RWclose(rw); if (nb_read_total != res_size) { free(res); return NULL; } res[nb_read_total] = '\0'; if (size != NULL) *size = nb_read_total; return res; } /** * Display compilation errors from the OpenGL shader compiler */ void print_log(GLuint object) { GLint log_length = 0; if (glIsShader(object)) { glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length); } else if (glIsProgram(object)) { glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length); } else { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "printlog: Not a shader or a program"); return; } char* log = (char*)malloc(log_length); if (glIsShader(object)) glGetShaderInfoLog(object, log_length, NULL, log); else if (glIsProgram(object)) glGetProgramInfoLog(object, log_length, NULL, log); SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "%s\n", log); free(log); } /** * Compile the shader from file 'filename', with error handling */ GLuint create_shader(const char* filename, GLenum type) { const GLchar* source = file_read(filename, NULL); if (source == NULL) { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Error opening %s: %s", filename, SDL_GetError()); return 0; } GLuint res = glCreateShader(type); // GLSL version const char* version; int profile; SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile); if (profile == SDL_GL_CONTEXT_PROFILE_ES) version = "#version 100\n"; // OpenGL ES 2.0 else version = "#version 120\n"; // OpenGL 2.1 // GLES2 precision specifiers const char* precision; precision = "#ifdef GL_ES \n" "# ifdef GL_FRAGMENT_PRECISION_HIGH \n" " precision highp float; \n" "# else \n" " precision mediump float; \n" "# endif \n" "#else \n" // Ignore unsupported precision specifiers "# define lowp \n" "# define mediump \n" "# define highp \n" "#endif \n"; const GLchar* sources[] = { version, precision, source }; glShaderSource(res, 3, sources, NULL); free((void*)source); glCompileShader(res); GLint compile_ok = GL_FALSE; glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok); if (compile_ok == GL_FALSE) { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "%s:\n", filename); print_log(res); glDeleteShader(res); return 0; } return res; } GLuint create_program(const char *vertexfile, const char *fragmentfile) { GLuint program = glCreateProgram(); GLuint shader; if(vertexfile) { shader = create_shader(vertexfile, GL_VERTEX_SHADER); if(!shader) return 0; glAttachShader(program, shader); } if(fragmentfile) { shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER); if(!shader) return 0; glAttachShader(program, shader); } glLinkProgram(program); GLint link_ok = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &link_ok); if (!link_ok) { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "glLinkProgram:"); print_log(program); glDeleteProgram(program); return 0; } return program; } #ifdef GL_GEOMETRY_SHADER GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) { GLuint program = glCreateProgram(); GLuint shader; if(vertexfile) { shader = create_shader(vertexfile, GL_VERTEX_SHADER); if(!shader) return 0; glAttachShader(program, shader); } if(geometryfile) { shader = create_shader(geometryfile, GL_GEOMETRY_SHADER); if(!shader) return 0; glAttachShader(program, shader); glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT, input); glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, output); glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT, vertices); } if(fragmentfile) { shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER); if(!shader) return 0; glAttachShader(program, shader); } glLinkProgram(program); GLint link_ok = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &link_ok); if (!link_ok) { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "glLinkProgram:"); print_log(program); glDeleteProgram(program); return 0; } return program; } #else GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) { SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Missing support for geometry shaders."); return 0; } #endif GLint get_attrib(GLuint program, const char *name) { GLint attribute = glGetAttribLocation(program, name); if(attribute == -1) SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Could not bind attribute %s", name); return attribute; } GLint get_uniform(GLuint program, const char *name) { GLint uniform = glGetUniformLocation(program, name); if(uniform == -1) SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Could not bind uniform %s", name); return uniform; } void print_opengl_info() { int major, minor, profile; SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile); const char* profile_str = ""; if (profile & SDL_GL_CONTEXT_PROFILE_CORE) profile_str = "CORE"; if (profile & SDL_GL_CONTEXT_PROFILE_COMPATIBILITY) profile_str = "COMPATIBILITY"; if (profile & SDL_GL_CONTEXT_PROFILE_ES) profile_str = "ES"; SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "OpenGL %d.%d %s", major, minor, profile_str); }