view gldragon.cpp @ 80:98e077d9d361

List the keyboard controls in --help.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 07 Mar 2020 14:37:59 +0200
parents 3383e402817b
children 5372b7795426
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           (180)
#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[])
{
    bool
        exitFlag = false,
        pauseFlag = false,
        optShowHelp = false,
        optSetInputFilename = false,
        optUseShaders = false;

    int optWidth = SET_DEF_WIDTH,
        optHeight = SET_DEF_HEIGHT,
        optVSyncMode = 1;

    std::string optInputFilename = "dragon.scene", basePath;
    DMGLSimpleRenderer renderer;
    DMSimpleScene scene;
    int cycleTime = 0,
        cycleFrames = 0,
        totalTime = 0,
        totalFrames = 0;

    // 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':
                    {
                        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;
                        }
                    }
                    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(
            "Usage: %s [options] [<scenefile.scene>]\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"
            "\n"
            "Keyboard controls during runtime:\n"
            " p / space    Toggle pause (does not/should not affect fps measurement)\n"
            " q / esc      Quit\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 (!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);

    // 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:
                        pauseFlag = !pauseFlag;
                        break;

                    case SDLK_ESCAPE:
                    case SDLK_q:
                        exitFlag = true;
                        break;
                }
        }

        // Render the next frame
        frameStart = SDL_GetTicks();
        renderer.drawScene(scene, totalTime);

        // Draw the current frame
        renderer.swapWindow();

        // Rotate for 2 degrees
        if (!pauseFlag)
            renderer.animate(scene, totalTime);

        frameEnd = SDL_GetTicks();

        // Check for errors
        renderer.checkErrors();

        frameDelta = frameEnd - frameStart;

        // Return true if a full rotation was done
        if (!pauseFlag)
        {
            totalFrames++;
            cycleFrames++;

            cycleTime += frameDelta;
            totalTime += frameDelta;

            if (cycleFrames >= SET_FRAMES)
            {
                // Print the current frames per second
                printf("%d ms for %d frames = %.1lf FPS\n",
                    cycleTime, cycleFrames, (cycleFrames * 1000.0f) / cycleTime);

                // Reset cycleFrames
                cycleFrames = 0;
                cycleTime = 0;
            }
        }
    }

    // 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;
}