Mercurial > hg > forks > gldragon
view gldragon.cpp @ 103:f88859a8fa4b
Add note about code origins to COPYING.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 06 Oct 2022 05:02:07 +0300 |
parents | 2b22a89dd811 |
children | 6520f00c68e4 |
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 "dmutil.h" #include "dmglrender.h" #include "dmply.h" /* Default settings etc. constants */ #define SET_DEF_WIDTH 1280 #define SET_DEF_HEIGHT 960 #define SET_FRAMES (360) #define SET_MAX_SHADER_SIZE (128 * 1024) /* Helpers */ bool dmInitSDL(DMSimpleRenderer &renderer, const int width, const int height, const int vsyncMode, 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; } // Part 1 of initialization if (!renderer.initRenderer1(title, width, height, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOW_SHOWN// | SDL_WINDOW_RESIZABLE )) return false; // Check if we want to attempt to use vsync switch (vsyncMode) { 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; } // Part 3 of initialization if (!renderer.initRenderer2()) return false; dmMsg("VSync mode : %s\n", msg.c_str()); return true; } int main(int argc, char *argv[]) { int frameDeltas[SET_FRAMES + 1]; bool exitFlag = false, pauseFlag = false, manualFlag = false, manualChanged = false, optShowHelp = false, optSetInputFilename = false, optUseShaders = false; int optWidth = SET_DEF_WIDTH, optHeight = SET_DEF_HEIGHT, optVSyncMode = 1, optPauseFrame = -1, optQuitFrame = -1; std::string optInputFilename = "dragon.scene", basePath; DMGLSimpleRenderer renderer; DMSimpleScene scene; int cycleTime = 0, cycleFrames = 0, totalTime = 0, totalFrames = 0; memset(&frameDeltas, 0, sizeof(frameDeltas)); // 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++; switch (opt[0]) { case 'g': optUseShaders = true; break; case 's': case 'm': case 'v': case 'p': case 'q': { std::string marg; if (opt[1] == 0) { if (narg < argc) marg = std::string(argv[++narg]); else { printf("Option '%s' requires an argument.\n", opt); goto exit; } } else marg = std::string(opt + 1); switch (opt[0]) { case 's': { std::vector<std::string> mtokens = dmStrSplit(marg, "xX:"); if (mtokens.size() != 2) { printf("Option expects argument of format <width>x<height> in pixels.\n" "For example: -s 640x480\n"); goto exit; } optWidth = std::stoi(mtokens[0], 0, 0); optHeight = std::stoi(mtokens[1], 0, 0); } break; case 'v': optVSyncMode = std::stoi(marg, 0, 0); break; case 'p': optPauseFrame = std::stoi(marg, 0, 0); break; case 'q': optQuitFrame = std::stoi(marg, 0, 0); break; } } break; default: printf("Unknown option '%s'.\n", arg); 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( "GLDragon - OpenGL PLY model viewer / simple benchmark\n" "Programmed and designed by Matti 'ccr' Hamalainen <ccr@tnsp.org>\n" "(C) Copyright 2019-2022 Tecnic Software productions (TNSP)\n" "See file \"COPYING\" for license information.\n" "Originally based on 'glxdragon' Copyright (c) 2009, Thomas Trummer\n" "\n" "Usage: %s [options] [<scenefile.scene>]\n" "\n" " -? Show this help\n" " -g Use GLSL shader instead of basic OpenGL lighting\n" " -s<w>x<h> Set window dimensions (default %d x %d)\n" " -v<0-3> Set vsync mode: 0 = do not attempt to set vsync mode\n" " (may be required for software rendering backends),\n" " 1 = no vsync, 2 = vsync, 3 = adaptive.\n" " Using vsync (2) will result in FPS being approximately\n" " whatever your monitor refresh rate is. The default\n" " value is 1 (no vsync).\n" " -p<frameN> Start and pause at cycle frame (%d .. %d).\n" " -q<frameN> Quit at frame (%d .. %d).\n" "\n" "Keyboard controls during runtime:\n" " p / space Toggle pause (does not/should not affect fps measurement)\n" " q / esc Quit\n" " arrow keys Enter manual mode and rotate left/right\n" "\n", argv[0], SET_DEF_WIDTH, SET_DEF_HEIGHT, 0, SET_FRAMES - 1, 0, SET_FRAMES - 1 ); 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; } if (optQuitFrame >= 0 && optPauseFrame >= 0) { dmError("Only one of 'quit frame' (-q) and 'pause frame' (-p) options can be set.\n"); 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 (!dmLoadFromPLY(model, 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; } } // Set shader usage renderer.useShaders = optUseShaders; // Initialize SDL + OpenGL if (!dmInitSDL(renderer, optWidth, optHeight, optVSyncMode, "GLDragon")) goto exit; // Compile shaders for scene if (!renderer.compileSceneShaders(scene)) goto exit; // Setup lights and camera renderer.setupLights(scene); renderer.setupCamera(scene.camera); // Check for pause frame if (optPauseFrame >= 0) { cycleFrames = optPauseFrame; pauseFlag = true; } // Main loop starts while (!exitFlag) { SDL_Event event; int frameStart, frameEnd, frameDelta; // 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_SPACE: case SDLK_p: if (manualFlag) pauseFlag = manualFlag = false; else pauseFlag = !pauseFlag; break; case SDLK_ESCAPE: case SDLK_q: exitFlag = true; break; case SDLK_RIGHT: manualChanged = manualFlag = true; if (++cycleFrames >= SET_FRAMES) cycleFrames = 0; break; case SDLK_LEFT: manualChanged = manualFlag = true; if (--cycleFrames < 0) cycleFrames = SET_FRAMES - 1; break; } } // Render the frame frameStart = SDL_GetTicks(); renderer.drawScene(scene, (float) cycleFrames / (float) SET_FRAMES); renderer.swapWindow(); frameEnd = SDL_GetTicks(); // Check for errors renderer.checkErrors(); frameDelta = frameEnd - frameStart; // Check for quit frame if (optQuitFrame >= 0 && cycleFrames == optQuitFrame) { exitFlag = true; } else if (manualFlag) { // Handle manual control mode if (manualChanged) { printf("%d ms frametime, frame #%d\n", frameDelta, cycleFrames); manualChanged = false; } SDL_Delay(25); } else if (!pauseFlag) { // Handle automatic mode totalFrames++; cycleFrames++; cycleTime += frameDelta; totalTime += frameDelta; memmove(&frameDeltas[0], &frameDeltas[1], sizeof(frameDeltas[0]) * (SET_FRAMES - 1)); frameDeltas[SET_FRAMES - 1] = frameDelta; if (cycleFrames >= SET_FRAMES) { float avgCycleFrame = cycleTime / SET_FRAMES; float maxJitter = 0, avgJitter = 0; for (int n = 0; n < SET_FRAMES; n++) { float mjitter = fabs(avgCycleFrame - frameDeltas[n]); if (mjitter > maxJitter) maxJitter = mjitter; avgJitter += mjitter; } avgJitter /= SET_FRAMES; // Print the current frames per second printf("%d ms for %d frames = %.1lf FPS [framejitter %.1lf ms avg, %.1lf ms max]\n", cycleTime, cycleFrames, (cycleFrames * 1000.0f) / cycleTime, avgJitter, maxJitter ); // Reset cycleFrames cycleFrames = 0; cycleTime = 0; } } else { SDL_Delay(100); } } // Show totals printf("%d ms total for %d total frames = %.2lf FPS average\n", totalTime, totalFrames, ((double) totalFrames * 1000.0f) / (double) totalTime); exit: renderer.shutdownRenderer(); SDL_Quit(); return 0; }