Mercurial > hg > forks > gldragon
view glxdragon.cpp @ 6:4d6fec8f0c64
Implement optional support for vertex/fragment shaders. Cleanups.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 27 Oct 2019 22:09:38 +0200 |
parents | 5dcae4dddcd9 |
children | 95dd5417e7de |
line wrap: on
line source
// // Copyright (c) 2009, Thomas Trummer // All rights reserved. // // Port to libSDL2 and cleanups by Matti Hämäläinen <ccr@tnsp.org> // (C) Copyright 2019 Tecnic Software productions (TNSP) // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the <organization> nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY Thomas Trummer ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #include <SDL.h> #include <SDL_opengl.h> #include <GL/glu.h> #include <GL/glext.h> #include <iostream> #include <fstream> #include <string> #include <vector> #include <cstdio> #include <ctime> #define SET_FRAMES (180 * 2) bool opt_shaders = false; SDL_Window *sdl_window = NULL; SDL_GLContext sdl_glctx = NULL; struct Mesh { int nvertices, nfaces; std::vector<float> vertices; std::vector<unsigned> faces; GLuint id_prog, id_ps, id_vs; }; bool dmInitSDLGL(const int width, const int height, const char *title) { // Set GL attributes SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // Attempt to initialize libSDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) { printf("ERROR: Unable to initialize SDL: %s\n", SDL_GetError()); return false; } // Attempt to create a window if ((sdl_window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE)) == NULL) { printf("ERROR: Could not create SDL window: %s", SDL_GetError()); return false; } if ((sdl_glctx = SDL_GL_CreateContext(sdl_window)) == NULL) { printf("ERROR: Unable to create SDL OpenGL context: %s\n", SDL_GetError()); return false; } // Dump some information printf( "GL_VENDOR : %s\n" "GL_RENDERER : %s\n" "GL_VERSION : %s\n", glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_VERSION)); // Setup the window and view port glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, GLfloat(width) / GLfloat(height), 0.1f, 1000.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Enable back face culling glEnable(GL_CULL_FACE); // Enable smooth shading glShadeModel(GL_SMOOTH); // Enable the depth buffer glEnable(GL_DEPTH_TEST); // Setup depth buffer glClearDepth(1.0f); // Set the depth buffer function glDepthFunc(GL_LEQUAL); // Enable vertex and and normal arrays glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); // Set correct perspective correction glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); return true; } void dmDrawModelVA(const Mesh &mesh) { int maxIndices; glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); glVertexPointer(3, GL_FLOAT, 24, &mesh.vertices[0]); glNormalPointer(GL_FLOAT, 24, &mesh.vertices[3]); for (int n = 0; n < mesh.nfaces; n += maxIndices) { const int count = std::min(maxIndices, int(mesh.nfaces - n)); glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_INT, &mesh.faces[n * 3]); } } void dmPaintGL(Mesh &mesh) { glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, 1.0, 0.0, 1.0, -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); // Draw the background gradient glBegin(GL_QUADS); { glColor3ub(0x3B, 0x3B, 0x75); glVertex2f(0.0f, 0.0f); glVertex2f(1.0f, 0.0f); glColor3ub(0x00, 0x00, 0x00); glVertex2f(1.0f, 1.0f); glVertex2f(0.0f, 1.0f); } glEnd(); // Restore the 3D projection glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glEnable(GL_DEPTH_TEST); // Render the model if (opt_shaders) { // Enable shader program glUseProgram(mesh.id_prog); dmDrawModelVA(mesh); glUseProgram(0); } else { // Set the color of the model glEnable(GL_LIGHTING); glColor3ub(0x90, 0x80, 0x90); dmDrawModelVA(mesh); } } bool dmReadText(const std::string &filename, std::string &tstr) { std::ifstream in(filename.c_str()); if (!in.is_open()) { printf("ERROR: Unable to open file '%s'.\n", filename.c_str()); return false; } in.seekg(0, std::ios::end); tstr.reserve(in.tellg()); in.seekg(0, std::ios::beg); tstr.assign((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>()); return true; } bool dmLoadMesh(const std::string &filename, Mesh &mesh, int nvertices, int nfaces) { std::ifstream in(filename.c_str(), std::ios::binary); if (!in.is_open()) { printf("ERROR: Unable to open file '%s'.\n", filename.c_str()); return false; } mesh.nvertices = nvertices; mesh.vertices.resize(mesh.nvertices * 6); in.read(reinterpret_cast<char*>(&mesh.vertices[0]), mesh.nvertices * 6 * 4); mesh.nfaces = nfaces; mesh.faces.resize(mesh.nfaces * 3); for (int i = 0; i < nfaces; i++) { in.seekg(1, std::ios::cur); in.read(reinterpret_cast<char*>(&mesh.faces[i * 3]), 3 * 4); } return true; } GLuint dmSetCompileShader(const GLenum stype, const std::string &src) { GLuint shader = glCreateShader(stype); const char *tmp = src.c_str(); glShaderSource(shader, 1, &tmp, 0); glCompileShader(shader); return shader; } void dmLinkMeshShaders(Mesh &mesh) { mesh.id_prog = glCreateProgram(); glAttachShader(mesh.id_prog, mesh.id_ps); glAttachShader(mesh.id_prog, mesh.id_vs); glLinkProgram(mesh.id_prog); } int main(int argc, char *argv[]) { struct Mesh dragonMesh; std::string dragonVS, dragonFS; std::clock_t startTime; bool exitFlag = false; int steps = 0; // Check commandline argument for enabling shaders if (argc > 1 && (strstr(argv[1], "glsl") != NULL || strstr(argv[1], "sha") != NULL)) opt_shaders = true; if (!dmLoadMesh("dragon.mesh", dragonMesh, 100139, 200198)) goto exit; if (opt_shaders) { // Read shader files if (!dmReadText("dragon.frag", dragonFS) || !dmReadText("dragon.vert", dragonVS)) goto exit; } // Initialize SDL + OpenGL if (!dmInitSDLGL(1280, 960, "GLXDragon2")) goto exit; // According to our mode .. if (opt_shaders) { dragonMesh.id_ps = dmSetCompileShader(GL_FRAGMENT_SHADER, dragonFS); dragonMesh.id_vs = dmSetCompileShader(GL_VERTEX_SHADER, dragonVS); dmLinkMeshShaders(dragonMesh); } else { float specReflection[] = { 0.8f, 0.8f, 0.8f, 1.0f }; glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glMateriali(GL_FRONT, GL_SHININESS, 96); glMaterialfv(GL_FRONT, GL_SPECULAR, specReflection); glEnable(GL_LIGHT0); // Define the light components and position GLfloat ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; GLfloat diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat specular[] = { 0.5f, 0.5f, 0.5f, 1.0f }; GLfloat position[] = { 10.0f, 10.0f, 0.0f, 0.0f }; // Define the light components and position glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, specular); glLightfv(GL_LIGHT0, GL_POSITION, position); } // Define the camera gluLookAt(0, 0.12, 0.24, 0, 0.12, 0, 0, 1, 0); // Main loop starts startTime = std::clock(); while (!exitFlag) { SDL_Event event; // Check for quit events while (SDL_PollEvent(&event)) switch (event.type) { case SDL_QUIT: exitFlag = true; break; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_ESCAPE: case SDLK_q: exitFlag = true; break; } } // Render the next frame dmPaintGL(dragonMesh); // Draw the current frame SDL_GL_SwapWindow(sdl_window); // Rotate for 2 degrees glRotatef(2.0f, 0, 1, 0); // Return true if a full rotation was done if (steps++ == SET_FRAMES) { // Reset steps steps = 0; // Get the time it took to render a full turn double time = (double(std::clock() - startTime) * 1000.0f) / CLOCKS_PER_SEC; // Print the current frames per second printf("%.1lf ms for %d frames = %.1lf FPS\n", time, SET_FRAMES, (SET_FRAMES * 1000.0f) / time); // Restart the timer startTime = std::clock(); } } exit: if (sdl_glctx != NULL) SDL_GL_DeleteContext(sdl_glctx); if (sdl_window != NULL) SDL_DestroyWindow(sdl_window); SDL_Quit(); return 0; }