view glxdragon.cpp @ 5:5dcae4dddcd9

Cleanups.
author Matti Hamalainen <ccr@tnsp.org>
date Sun, 27 Oct 2019 18:29:23 +0200
parents be31ff9e5f58
children 4d6fec8f0c64
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 <iostream>
#include <sstream>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <ctime>


#define SET_FRAMES (180 * 2)


SDL_Window *s_window = NULL;
SDL_GLContext s_context;


struct Mesh
{
    int nvertices, nfaces;
    std::vector<float>    vertices;
    std::vector<unsigned> faces;
};



bool dmInitSDLGL(const int width, const int height, const char *title)
{
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

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

    if ((s_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 ((s_context = SDL_GL_CreateContext(s_window)) == NULL)
    {
        printf("ERROR: Unable to create SDL OpenGL context: %s\n",
            SDL_GetError());
        return false;
    }

    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 dmInitScene(void)
{
    glEnable(GL_COLOR_MATERIAL);

    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glMateriali(GL_FRONT, GL_SHININESS, 96);

    float specReflection[] = { 0.8f, 0.8f, 0.8f, 1.0f };
    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);
}


void dmFinish()
{
    SDL_GL_DeleteContext(s_context);
    SDL_DestroyWindow(s_window);
    s_window = NULL;
    SDL_Quit();
}


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);
    glEnable(GL_LIGHTING);

    // Set the color of the model
    glColor3ub(0x90, 0x80, 0x90);

    // Draw the model using vertex arrays
    dmDrawModelVA(mesh);
}


void dmLoadMesh(const std::string &filename, Mesh &mesh, int nvertices, int nfaces)
{
    std::ifstream in(filename.c_str(), std::ios::binary);

    if (!in.is_open())
    {
        std::stringstream ss;
        ss << "Unable to open file: " << filename << '\n';
        throw std::runtime_error(ss.str());
    }

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


int main()
{
    try
    {
        struct Mesh dragonMesh;
        dmLoadMesh("dragon.mesh", dragonMesh, 100139, 200198);

        //if (!dmInitSDLGL(640, 480, "glxdragon"))
        if (!dmInitSDLGL(1280, 960, "glxdragon"))
            throw std::runtime_error("Fatal error.");

        dmInitScene();

        bool exitFlag = false;
        int steps = 0;
        std::clock_t 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(s_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();
            }
        }
    }
    catch(std::runtime_error & e)
    {
        std::cerr << e.what();
    }

    dmFinish();
}