Mercurial > hg > forks > gldragon
view glxdragon.cpp @ 14:62be2036f604
Read mesh vertices/faces information from "<modelfilename>.info" instead of
having the values hardcoded.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 29 Oct 2019 12:48:36 +0200 |
parents | c1e8057cc4d0 |
children | 2d2aadfa3df3 |
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> /* Default settings etc. constants */ #define SET_DEF_WIDTH 1280 #define SET_DEF_HEIGHT 960 #define SET_FRAMES (180) /* Structures */ struct Mesh { int nvertices, nfaces; std::vector<float> vertices; std::vector<unsigned> faces; GLuint id_prog, id_fs, id_vs; }; /* Options */ bool optUseShaders = false; int optWidth = SET_DEF_WIDTH, optHeight = SET_DEF_HEIGHT, optVSyncMode = 1; std::string optModelPrefix = "dragon"; /* Globals */ SDL_Window *dmWindow = NULL; SDL_GLContext dmGLContext = NULL; bool dmInitSDLGL(const int width, const int height, const char *title) { int ret; std::string msg; // 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 ((dmWindow = 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 ((dmGLContext = SDL_GL_CreateContext(dmWindow)) == NULL) { printf("ERROR: Unable to create SDL OpenGL context: %s\n", SDL_GetError()); return false; } // Check if we want to attempt to use vsync switch (optVSyncMode) { case 3: ret = SDL_GL_SetSwapInterval(-1); msg = "adaptive vsync"; break; case 2: ret = SDL_GL_SetSwapInterval(1); msg = "synchronized (vsync)"; break; case 1: ret = SDL_GL_SetSwapInterval(0); msg = "immediate (no vsync)"; break; default: ret = -1; msg = "INVALID VSYNC MODE"; break; } if (ret != 0) { printf("ERROR: Could not set vsync mode to %s.\n", msg.c_str()); return false; } // Dump some information printf( "GL_VENDOR : %s\n" "GL_RENDERER : %s\n" "GL_VERSION : %s\n" "VSync mode : %s\n", glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_VERSION), msg.c_str()); // 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 (optUseShaders) { // 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, const std::string &infofilename, Mesh &mesh) { std::ifstream info(infofilename.c_str()); int required = 0, nline = 0; printf("INFO: Trying to read mesh meta from '%s'.\n", infofilename.c_str()); if (!info.is_open()) { printf("ERROR: Unable to open file '%s'.\n", infofilename.c_str()); return false; } while (required != 0x03) { std::string tmp; int value; char key; // Read one line if (!std::getline(info, tmp)) { printf("ERROR: Could not read from file '%s'.\n", infofilename.c_str()); return false; } nline++; // Skip empty lines and comments if (tmp.empty() || tmp.substr(0, 1) == "#") continue; if (sscanf(tmp.c_str(), "%c:%d", &key, &value) != 2) { printf("ERROR: Syntax error in '%s' line #%d\n'%s'\n", infofilename.c_str(), nline, tmp.c_str()); return false; } switch (key) { case 'v': mesh.nvertices = value; required |= 0x01; break; case 'f': mesh.nfaces = value; required |= 0x02; break; default: printf("ERROR: Syntax error in '%s' line #%d\nUnknown key value '%c'.\n", infofilename.c_str(), nline, key); break; } } if (mesh.nvertices < 3 || mesh.nfaces < 1) { printf("ERROR: Invalid nvertices (%d) and/or nfaces (%d) in '%s'.\n", mesh.nvertices, mesh.nfaces, infofilename.c_str()); return false; } printf("INFO: %d vertices, %d faces\n", mesh.nvertices, mesh.nfaces); printf("INFO: Trying to read mesh data from '%s'.\n", filename.c_str()); 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.vertices.resize(mesh.nvertices * 6); in.read(reinterpret_cast<char*>(&mesh.vertices[0]), mesh.nvertices * 6 * 4); mesh.faces.resize(mesh.nfaces * 3); for (int i = 0; i < mesh.nfaces; i++) { in.seekg(1, std::ios::cur); in.read(reinterpret_cast<char*>(&mesh.faces[i * 3]), 3 * 4); } return true; } GLuint dmCompileShader(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_fs); glAttachShader(mesh.id_prog, mesh.id_vs); glLinkProgram(mesh.id_prog); } int main(int argc, char *argv[]) { std::string modelVertStr, modelFragStr; struct Mesh modelMesh; bool exitFlag = false, optShowHelp = false; int startTime, cycleStart, cycleFrames = 0, totalFrames = 0; double totalTime; // Check commandline argument for enabling shaders for (int narg = 1; narg < argc; narg++) { char *arg = argv[narg]; if (arg[0] == '-') { char *opt = arg + 1; if ((opt[0] == '-' && opt[1] == 'h' && opt[2] == 'e') || opt[0] == '?' || (opt[0] == '-' && opt[1] == '?')) { optShowHelp = true; break; } else if (opt[0] == '-') opt++; if (opt[0] == 'g') optUseShaders = true; else switch (opt[0]) { case 'w': case 'h': case 'm': case 'v': if (opt[1] == 0) { printf("Option '%s' requires an argument.\n", opt); goto exit; } switch (opt[0]) { case 'w': optWidth = atoi(opt + 1); break; case 'h': optHeight = atoi(opt + 1); break; case 'm': optModelPrefix = std::string(opt + 1); break; case 'v': optVSyncMode = atoi(opt + 1); break; } break; default: printf("Unknown option '%s'.\n", opt); goto exit; } } } if (optShowHelp) { printf( "Usage: %s [options]\n" "-? Show this help\n" "-g Use GLSL shader instead of basic OpenGL lighting\n" "-w<width> Window width (default %d)\n" "-h<height> Window height (default %d)\n" "-m<modelfile> Set model filenames prefix. Using \"-mfoo\" will\n" " specify \"foo.mesh\", \"foo.frag\", \"foo.vert\".\n" "-v<1-3> Set vsync mode: 1 = no vsync, 2 = vsync, 3 = adaptive\n" " Default is no vsync.\n" "\n", argv[0], SET_DEF_WIDTH, SET_DEF_HEIGHT ); goto exit; } if (optWidth < 100 || optWidth > 8192 || optHeight < 100 || optHeight > 8192) { printf("ERROR: Invalid window width or height (%d x %d).\n", optWidth, optHeight); goto exit; } if (optModelPrefix.empty()) { printf("Model file prefix empty.\n"); goto exit; } if (!dmLoadMesh(optModelPrefix + ".mesh", optModelPrefix + ".info", modelMesh)) goto exit; if (optUseShaders) { // Read shader files if (!dmReadText(optModelPrefix + ".frag", modelFragStr) || !dmReadText(optModelPrefix + ".vert", modelVertStr)) goto exit; } // Initialize SDL + OpenGL if (!dmInitSDLGL(optWidth, optHeight, "GLXDragon2")) goto exit; // According to our mode .. if (optUseShaders) { modelMesh.id_fs = dmCompileShader(GL_FRAGMENT_SHADER, modelFragStr); modelMesh.id_vs = dmCompileShader(GL_VERTEX_SHADER, modelVertStr); dmLinkMeshShaders(modelMesh); } 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 = cycleStart = SDL_GetTicks(); 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(modelMesh); // Draw the current frame SDL_GL_SwapWindow(dmWindow); // Rotate for 2 degrees glRotatef(2.0f, 0, 1, 0); // Return true if a full rotation was done if (cycleFrames++ == SET_FRAMES) { // Reset cycleFrames totalFrames += cycleFrames; cycleFrames = 0; // Get the time it took to render a full turn int cycleEnd = SDL_GetTicks(); double cycleTime = cycleEnd - cycleStart; // Restart the timer cycleStart = SDL_GetTicks(); // Print the current frames per second printf("%.1lf ms for %d frames = %.1lf FPS\n", cycleTime, SET_FRAMES, (SET_FRAMES * 1000.0f) / cycleTime); } } // Show totals totalTime = SDL_GetTicks() - startTime; printf("%.1lf ms total for %d total frames = %.2lf FPS average\n", totalTime, totalFrames, (totalFrames * 1000.0f) / totalTime); exit: if (dmGLContext != NULL) SDL_GL_DeleteContext(dmGLContext); if (dmWindow != NULL) SDL_DestroyWindow(dmWindow); SDL_Quit(); return 0; }