#include #include #include #include #include #include #include // only include this one in the source file with main()! #define INITIAL_FPS 60 SDL_Window* window = NULL; SDL_GLContext glcontext = NULL; GLuint program; GLuint vbo; GLuint ibo; GLuint projMatrLoc; static double FPS = INITIAL_FPS; static double FrameTime = 1.0 / INITIAL_FPS; static double PerformancePeriod; static void panicError ( void ) __attribute__((noreturn)); static double GetTime( void ) { return PerformancePeriod * SDL_GetPerformanceCounter(); } static void SetTitle ( SDL_Window *window ) { char title[48] = {0}; SDL_snprintf(title, sizeof(title), "Hello Triangles! Framelimit: %6.3f Hz", FPS); SDL_SetWindowTitle(window, title); } float toRad ( float deg ) { /* 180 deg = pi */ /* "deg" deg = x */ return deg * 3.141593f / 180.0f; } void resizeWindow ( int width, int height ) { float n = 1.0f; // near float f = 3.0f; // far float fov = toRad(90); // 90 or 65 for console float aspect = width / (float) height; float t = n * tanf (fov / 2.0f); float r = t * aspect; float projMatr[] = { n/r, 0, 0, 0, 0, n/t, 0, 0, 0, 0, (f+n)/(n-f), -1, 0, 0, 2.0f*n*f/(n-f), 0 }; // it's not rows, it's columns, 'GL_FALSE' SDL_LogVerbose(SDL_LOG_CATEGORY_RENDER, "n=%.1f f=%.1f fov=%.2f aspect=%.2f t=%.2f r=%.2f\n", n, f, fov, aspect, t, r); glUseProgram(program); glUniformMatrix4fv(projMatrLoc, 1, GL_FALSE, projMatr); glUseProgram(0); glViewport(0, 0, (GLsizei)width, (GLsizei)height); } void panicError ( void ) { SDL_GL_DestroyContext(glcontext); SDL_DestroyWindow(window); SDL_Quit(); exit(EXIT_FAILURE); } char *readFile ( const char *fileName ) { SDL_IOStream *ioStream = SDL_IOFromFile(fileName, "rb"); if (!ioStream) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "ioStream: %s.", SDL_GetError()); panicError(); } if (SDL_SeekIO(ioStream, 0, SDL_IO_SEEK_END) == -1) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_SeekIO_END: %s.", SDL_GetError()); panicError(); } long len = SDL_TellIO(ioStream); if (len == -1) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_TellIO: %s.", SDL_GetError()); panicError(); } if (SDL_SeekIO(ioStream, 0, SDL_IO_SEEK_SET) == -1) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_IO_SEEK_SET: %s.", SDL_GetError()); panicError(); } char *res = SDL_malloc(len + 1); if (!res) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_malloc: %s.", SDL_GetError()); panicError(); } if (SDL_ReadIO(ioStream, res, len) != (size_t)len) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_ReadIO: %s.", SDL_GetError()); panicError(); } if (!SDL_CloseIO(ioStream)) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_CloseIO: %s.", SDL_GetError()); panicError(); } res[len] = 0; return res; } GLuint createShader ( const char *shaderFile, GLenum shaderType ) { const char *strShaderType; if (shaderType == GL_VERTEX_SHADER) { strShaderType = "vertex"; } else if (shaderType == GL_GEOMETRY_SHADER) { strShaderType = "geometry"; } else if (shaderType == GL_FRAGMENT_SHADER) { strShaderType = "fragment"; } else { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Unrecognized shader type."); panicError(); } GLuint shader = glCreateShader(shaderType); if (!shader) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Error create shader type %s.", strShaderType); panicError(); } GLchar *content = readFile(shaderFile); glShaderSource(shader, 1, (const GLchar **)&content, NULL); SDL_free(content); glCompileShader(shader); GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLint infoLen; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); GLchar *info = SDL_malloc(sizeof(GLchar) * (infoLen + 1)); glGetShaderInfoLog(shader, infoLen, NULL, info); SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Filed to compile %s shader:\n%s.", strShaderType, info); panicError(); } return shader; } void createProgram ( GLuint *shaders, int len ) { program = glCreateProgram(); if (!program) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Can't to create shader program."); panicError(); } for (int i = 0; i < len; i++) { glAttachShader(program, shaders[i]); } glLinkProgram(program); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLint infoLen; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); GLchar *info = SDL_malloc(sizeof(GLchar) * (infoLen + 1)); glGetProgramInfoLog(program, infoLen, NULL, info); SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Link failed: %s", info); panicError(); } } void createBuffer ( void ) { GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; float vertices[] = { -1.0f, -1.0f, -2.0f, 1.0f, // left-bottom -1.0f, 1.0f, -2.0f, 1.0f, // left-top 1.0f, 1.0f, -2.0f, 1.0f, // right-top 1.0f, -1.0f, -2.0f, 1.0f, // right-bottom // colours 1.0f, 0.0f, 0.0f, 1.0f, // red 0.0f, 1.0f, 0.0f, 1.0f, // green 1.0f, 0.0f, 1.0f, 1.0f, // purple 1.0f, 1.0f, 0.0f, 1.0f // yellow }; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glGenBuffers(1, &ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void setUniformLocation ( void ) { projMatrLoc = glGetUniformLocation(program, "projMatr"); } void startShaderProg ( void ) { createBuffer(); GLuint shaders[] = { createShader("vertex.glsl", GL_VERTEX_SHADER), createShader("fragment.glsl", GL_FRAGMENT_SHADER) }; int len = sizeof(shaders) / sizeof(shaders[0]); createProgram(shaders, len); for (int i = 0; i < len; i++) { glDeleteShader(shaders[i]); } setUniformLocation(); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glDepthFunc(GL_LEQUAL); glDepthRange(0.0f, 1.0f); } void _GLBuffer( void ) { glClearColor(0.0f, 0.0f, 1.0f, 0.0f); glClearDepth(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(program); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void *) (4*4*sizeof(float))); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glUseProgram(0); } int main( int argc, char* argv[] ) { const int WIDTH = 640; const int HEIGHT = 480; int delayThreshold = 2; int r, g, b, alpha_s, depth_s, stencil_s, dbuffer, glVersionMa, glVersionMi, glAccel, glProfile = 0; double sleepTime = 0; double timeCounter = 0; bool loopShouldStop = false; SDL_SetHint(SDL_HINT_RENDER_VSYNC, 0); if (!SDL_Init(SDL_INIT_VIDEO)) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_Init failed: %s", SDL_GetError()); panicError(); } if (!SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Set atrribute SDL_GL_CONTEXT_PROFILE_MASK: %s", SDL_GetError()); panicError(); } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); PerformancePeriod = 1.0 / SDL_GetPerformanceFrequency(); timeCounter = GetTime(); window = SDL_CreateWindow("Hello SDL", WIDTH, HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!window) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_CreateWindow: %s", SDL_GetError()); panicError(); } glcontext = SDL_GL_CreateContext(window); if (!glcontext) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_GL_CreateContext: %s", SDL_GetError()); panicError(); } SetTitle(window); SDL_HideCursor(); glewExperimental = GL_TRUE; GLenum err = glewInit(); if (err != GLEW_OK) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "glewInit: %s", glewGetErrorString(err)); panicError(); } SDL_LogVerbose(SDL_LOG_CATEGORY_RENDER, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &glProfile); SDL_GL_GetAttribute(SDL_GL_ACCELERATED_VISUAL, &glAccel); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &glVersionMa); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &glVersionMi); SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r); SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g); SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b); SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alpha_s); SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth_s); SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_s); SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dbuffer); SDL_LogVerbose(SDL_LOG_CATEGORY_RENDER, "OpenGL Version: %d.%d, OpenGL Accelerated: %d, OpenGL Profile Mask: %d", glVersionMa, glVersionMi, glAccel, glProfile); SDL_LogVerbose(SDL_LOG_CATEGORY_RENDER, "Red size: %d, Green size: %d, Blue size: %d, Alpha size: %d, Depth size: %d, Stencil size: %d, DoubleBuffer: %d", r, g, b, alpha_s, depth_s, stencil_s, dbuffer); SDL_GL_SetSwapInterval(0); startShaderProg(); while (!loopShouldStop) { SDL_Event e; SDL_zero(e); while (SDL_PollEvent(&e)) { if (e.type == SDL_EVENT_QUIT) loopShouldStop = true; else if (e.type == SDL_EVENT_WINDOW_RESIZED) { resizeWindow(e.window.data1, e.window.data2); } else if (e.type == SDL_EVENT_KEY_DOWN) { if (e.key.key == SDLK_X) { FPS -= 0.1; if (FPS < 1) { FPS = 1; } FrameTime = 1.0 / FPS; SetTitle(window); } else if (e.key.key == SDLK_C) { FPS += 0.1; FrameTime = 1.0 / FPS; SetTitle(window); } if (e.key.key == SDLK_ESCAPE) loopShouldStop = true; } } _GLBuffer(); if (!SDL_GL_SwapWindow(window)) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_GL_SwapWwindow: %s", SDL_GetError()); panicError(); } sleepTime = timeCounter - GetTime(); if (sleepTime * 1000 > delayThreshold) { Uint32 ms = (Uint32)(sleepTime * 1000) - delayThreshold; SDL_Delay(ms); if (timeCounter < GetTime()) { delayThreshold++; SDL_Log("Slept too long. Increased threshold to %u ms", delayThreshold); } } while (timeCounter > GetTime()) { /* Waiting for the right moment. */ } timeCounter += FrameTime; } SDL_DestroyWindow(window); SDL_GL_DestroyContext(glcontext); SDL_Quit(); exit(EXIT_SUCCESS); }