Mercurial > hg > forks > gldragon
view gldragon.cpp @ 48:0ae1ff609626
Implement diffuse setting for models in scenefile. Also refactor
parsing/handling of ambient/diffuse/specular values and unify it for both
models and lights.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 06 Dec 2019 01:12:23 +0200 |
parents | 9909014498f0 |
children | 2e2c3fe311da |
line wrap: on
line source
// // GLDragon - OpenGL PLY model viewer / simple benchmark // Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> // (C) Copyright 2019 Tecnic Software productions (TNSP) // // See file "COPYING" for license information. // // Originally based on 'glxdragon' Copyright (c) 2009, Thomas Trummer // #include <SDL.h> #include <SDL_opengl.h> #include <GL/glu.h> #include "dmutil.h" #include "dmmodel.h" /* Default settings etc. constants */ #define SET_DEF_WIDTH 1280 #define SET_DEF_HEIGHT 960 #define SET_FRAMES (180) #define SET_MAX_SHADER_SIZE (128 * 1024) /* Options */ bool optUseShaders = false; int optWidth = SET_DEF_WIDTH, optHeight = SET_DEF_HEIGHT, optVSyncMode = 1; /* Globals */ SDL_Window *dmWindow = NULL; SDL_GLContext dmGLContext = NULL; /* Helpers */ #ifdef GL_GLEXT_PROTOTYPES #define DM_GLEXT_INIT(extproctype, extprocname) /* stub */ #else // GL_GLEXT_PROTOTYPES not defined #define DM_GLEXT_INIT(extproctype, extprocname) extproctype extprocname = NULL; #include "dmglexts.h" #undef DM_GLEXT_INIT void * dmGLExtTry(const std::string &name) { bool res = SDL_GL_ExtensionSupported(name.c_str()); void *ptr = SDL_GL_GetProcAddress(name.c_str()); dmMsg(" - Checking '%s' : %s : %p\n", name.c_str(), res ? "YES" : "no", ptr); res = true; if (res && ptr != NULL) return ptr; else return NULL; } void * dmGLExtInit(const std::string &name, bool &status) { void *ptr; if ((ptr = dmGLExtTry(name)) != NULL) return ptr; else if ((ptr = dmGLExtTry(name + "EXT")) != NULL) return ptr; else if ((ptr = dmGLExtTry(name + "ARB")) != NULL) return ptr; status = false; return NULL; } #define DM_GLEXT_INIT(extproctype, extprocname) \ extprocname = (extproctype) dmGLExtInit(#extprocname, status); #endif bool dmInitGLExtensions(void) { #ifdef GL_GLEXT_PROTOTYPES return true; #else bool status = true; #include "dmglexts.h" return status; #endif } bool dmInitSDLGL(const int width, const int height, const char *title) { int ret; std::string msg; // Attempt to initialize libSDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) { dmError("Unable to initialize SDL: %s\n", SDL_GetError()); return false; } // 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_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); //SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // 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) { dmError("Could not create SDL window: %s", SDL_GetError()); return false; } if ((dmGLContext = SDL_GL_CreateContext(dmWindow)) == NULL) { dmError("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; case 0: msg = "vsync handling disabled"; ret = 0; break; default: ret = -1; msg = "INVALID VSYNC MODE"; break; } if (ret != 0) { dmError("Could not set vsync mode to %s.\n", msg.c_str()); return false; } // Get/initialize OpenGL extension function pointers if (optUseShaders && !dmInitGLExtensions()) return false; // Dump some information dmMsg("GL_VENDOR : %s\n", glGetString(GL_VENDOR)); dmMsg("GL_RENDERER : %s\n", glGetString(GL_RENDERER)); dmMsg("GL_VERSION : %s\n", glGetString(GL_VERSION)); dmMsg("VSync mode : %s\n", 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); // Enable normal rescaling glEnable(GL_RESCALE_NORMAL); // 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 dmDrawModel(const DMSimpleScene &scene, const DMModel &model) { int maxIndices; if (optUseShaders) { // Enable shader program glUseProgram(model.id_prog); glUniform1i(glGetUniformLocation(model.id_prog, "nlights"), scene.lights.size()); } // Set the material of the model glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glMateriali(GL_FRONT, GL_SHININESS, model.material.shininess); glMaterialfv(GL_FRONT, GL_SPECULAR, model.material.specular.values); glColor4fv(model.material.diffuse.values); // Render the model glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); // Add transforms if (model.scaleSet) glScalef(model.scale.x, model.scale.y, model.scale.z); if (model.translateSet) glTranslatef(model.translate.x, model.translate.y, model.translate.z); if (model.rotateSet) { glRotatef(model.rotate.x, 1.0f, 0.0f, 0.0f); glRotatef(model.rotate.y, 0.0f, 1.0f, 0.0f); glRotatef(model.rotate.z, 0.0f, 0.0f, 1.0f); } glVertexPointer(3, GL_FLOAT, 0, &model.vertices[0]); glNormalPointer( GL_FLOAT, 0, &model.normals[0]); for (int n = 0; n < model.nfaces; n += maxIndices) { const int count = std::min(maxIndices, model.nfaces - n); glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_INT, &model.faces[n * 3]); } // Restore if (optUseShaders) { glUseProgram(0); } } void dmDrawScene(const DMSimpleScene &scene) { 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(); // Draw the background gradient glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); 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); glEnable(GL_LIGHTING); // Draw models for (const DMModel &model : scene.models) { glPushMatrix(); dmDrawModel(scene, model); glPopMatrix(); } } bool dmCompileShader(const GLenum stype, const std::string &src, GLuint &shader) { GLint status; const char *tmp = src.c_str(); shader = glCreateShader(stype); glShaderSource(shader, 1, &tmp, 0); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_TRUE) return true; else { GLint bufLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &bufLen); if (bufLen > 0) { char *buf = new char[bufLen]; glGetShaderInfoLog(shader, bufLen, NULL, buf); dmError("Shader compliation error:\n%s\n", buf); delete buf; } else { dmError("Shader compilation error occured, but no error information got.\n"); } return false; } } void dmLinkModelShaders(DMModel &model) { model.id_prog = glCreateProgram(); glAttachShader(model.id_prog, model.id_fs); glAttachShader(model.id_prog, model.id_vs); glLinkProgram(model.id_prog); } void dmSetupLight(const int n, const DMLight &light) { glEnable(GL_LIGHT0 + n); glLightfv(GL_LIGHT0 + n, GL_AMBIENT, light.color.ambient.values); glLightfv(GL_LIGHT0 + n, GL_DIFFUSE, light.color.diffuse.values); glLightfv(GL_LIGHT0 + n, GL_SPECULAR, light.color.specular.values); glLightfv(GL_LIGHT0 + n, GL_POSITION, light.position.values); } int main(int argc, char *argv[]) { int startTime, cycleStart, cycleFrames = 0, totalFrames = 0; double totalTime; bool exitFlag = false, optShowHelp = false, optSetInputFilename = false; std::string optInputFilename = "dragon.scene", basePath; DMSimpleScene scene; // 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 'v': optVSyncMode = atoi(opt + 1); break; } break; default: printf("Unknown option '%s'.\n", opt); goto exit; } } else { if (optSetInputFilename) { dmError("Please specify only one scene file.\n"); goto exit; } optSetInputFilename = true; optInputFilename = std::string(arg); if (optInputFilename.empty()) { dmError("Invalid input filename.\n"); goto exit; } } } if (optShowHelp) { printf( "Usage: %s [options] [<scenefile.scene>]\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" "-v<0-3> Set vsync mode: 0 = do not attempt to set vsync mode\n" " (may be required for Mesa software rendering),\n" " 1 = no vsync, 2 = vsync, 3 = adaptive. Default is 1\n" " (no vsync). Using vsync will result in FPS being\n" " approx whatever your monitor refresh rate is.\n" "\n", argv[0], SET_DEF_WIDTH, SET_DEF_HEIGHT ); goto exit; } if (optWidth < 100 || optWidth > 8192 || optHeight < 100 || optHeight > 8192) { dmError("Invalid window width or height (%d x %d).\n", optWidth, optHeight); goto exit; } // Load the scene if (!scene.loadInfo(optInputFilename)) goto exit; if (scene.models.size() == 0) { dmError("Scenefile '%s' contains no models.\n", optInputFilename.c_str()); goto exit; } // Define a default light if none defined in scene file if (scene.lights.size() == 0) { DMLight light; // Default light scene.lights.push_back(light); } dmMsg("Loading %ld model(s) ..\n", scene.models.size()); basePath = dmGetPath(optInputFilename); dmMsg("Scene base path '%s'\n", basePath.c_str()); for (DMModel &model : scene.models) { if (!model.loadFromPLY(basePath + model.modelFile)) goto exit; if (optUseShaders) { std::string fragFile = model.fragShaderFile.empty() ? "shader.frag" : basePath + model.fragShaderFile, vertFile = model.vertShaderFile.empty() ? "shader.vert" : basePath + model.vertShaderFile; if (!dmReadText(fragFile, model.fragShaderStr, SET_MAX_SHADER_SIZE) || !dmReadText(vertFile, model.vertShaderStr, SET_MAX_SHADER_SIZE)) goto exit; } } // Initialize SDL + OpenGL if (!dmInitSDLGL(optWidth, optHeight, "GLDragon")) goto exit; // According to our mode .. if (optUseShaders) { for (DMModel &model : scene.models) { if (!dmCompileShader(GL_FRAGMENT_SHADER, model.fragShaderStr, model.id_fs) || !dmCompileShader(GL_VERTEX_SHADER, model.vertShaderStr, model.id_vs)) goto exit; dmLinkModelShaders(model); } } // Setup lights for (size_t n = 0; n < scene.lights.size(); n++) dmSetupLight(n, scene.lights[n]); // 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 dmDrawScene(scene); // 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 totalFrames++; if (cycleFrames++ == SET_FRAMES) { // Reset 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; }