view dmglrender.cpp @ 62:baccf2044289

Move the OpenGL rendering, setup etc. into a separate module/class, perhaps facilitating other types of renderers in future .. maybe.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 14 Dec 2019 16:39:20 +0200
parents
children d6ffc59bb84d
line wrap: on
line source

//
// GLDragon - OpenGL PLY model viewer / simple benchmark
// -- OpenGL rendering of DMSimpleScene
// 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.
//
#include "dmglrender.h"
#include <GL/glu.h>


#ifdef GL_GLEXT_PROTOTYPES
#define DM_GLEXT_INIT(extproctype, extprocname) /* stub */
#else
#define DM_GLEXT_INIT(extproctype, extprocname) extproctype extprocname = NULL;
#include "dmglexts.h"
#undef DM_GLEXT_INIT


static void dmGLCheckExtension(const std::string &name, bool &status)
{
    if (!SDL_GL_ExtensionSupported(name.c_str()))
    {
        status = false;
        dmMsg(" - '%s' NOT supported.\n",
            name.c_str());
    }
}


static void * dmGLGetProcAddr(const std::string &name)
{
    void *ptr = SDL_GL_GetProcAddress(name.c_str());

    if (ptr == NULL)
        dmMsg(" - '%s' NOT supported.\n");

    return ptr;
}


static void * dmGLExtInit(const std::string &name, bool &status)
{
    void *ptr;
    bool ok =
        (ptr = dmGLGetProcAddr(name)) != NULL ||
        (ptr = dmGLGetProcAddr(name + "EXT")) != NULL ||
        (ptr = dmGLGetProcAddr(name + "ARB")) != NULL;

    if (!ok)
        status = false;

    return ptr;
}
#endif


static 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 compilation error:\n%s\n",
                buf);
            delete[] buf;
        }
        else
        {
            dmError("Shader compilation error occured, but no error information got.\n");
        }
        return false;
    }
}


bool DMGLSimpleRenderer::checkErrors(void)
{
    bool ok = true;
    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR)
    {
        dmError("OpenGL error code: 0x%x (%d)\n", err);
        ok = false;
    }
    return ok;
}


bool DMGLSimpleRenderer::initRender1(void)
{
    // 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);

    return true;
}


bool DMGLSimpleRenderer::initRender2(SDL_Window *window)
{
    // Create OpenGL context
    if ((glContext = SDL_GL_CreateContext(window)) == NULL)
    {
        dmError("Unable to create SDL OpenGL context: %s\n",
            SDL_GetError());
        return false;
    }

    // If shaders are disabled, do not initialize OpenGL extensions
    if (!useShaders)
        return true;

    bool status = true;
    dmMsg("Checking for required OpenGL extensions ..\n");

#ifndef GL_GLEXT_PROTOTYPES
    dmGLCheckExtension("GL_ARB_shader_objects", status);
    dmGLCheckExtension("GL_ARB_shading_language_100", status);
    dmGLCheckExtension("GL_ARB_vertex_shader", status);
    dmGLCheckExtension("GL_ARB_fragment_shader", status);
    if (!status)
    {
        dmError("One or more of the required OpenGL extensions not supported.\n");
        return false;
    }

#define DM_GLEXT_INIT(extproctype, extprocname) \
    extprocname = (extproctype) dmGLExtInit(#extprocname, status);
#include "dmglexts.h"
#endif

    return status;
}


bool DMGLSimpleRenderer::initRender3(const int width, const int height)
{
    // 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));

    if (!checkErrors())
        return false;

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

    glEnable(GL_COLOR_MATERIAL);

    // 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 checkErrors();
}


bool DMGLSimpleRenderer::compileModelShaders(DMModel &model)
{
    if (useShaders)
    {
        if (!dmCompileShader(GL_FRAGMENT_SHADER, model.fragShaderStr, model.id_fs) ||
            !dmCompileShader(GL_VERTEX_SHADER, model.vertShaderStr, model.id_vs))
            return false;

        model.id_prog = glCreateProgram();
        glAttachShader(model.id_prog, model.id_fs);
        glAttachShader(model.id_prog, model.id_vs);
        glLinkProgram(model.id_prog);
    }
    return true;
}


void DMGLSimpleRenderer::shutdownRenderer(void)
{
    if (glContext != NULL)
        SDL_GL_DeleteContext(glContext);
}


void DMGLSimpleRenderer::drawModel(const DMSimpleScene &scene, const DMModel &model, const float time)
{
    int maxIndices;

    if (useShaders)
    {
        // Enable shader program
        glUseProgram(model.id_prog);
        glUniform1i(glGetUniformLocation(model.id_prog, "nlights"), scene.lights.size());
    }

    // Set the material of the model
    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 (useShaders)
    {
        glUseProgram(0);
    }
}


void DMGLSimpleRenderer::drawScene(const DMSimpleScene &scene, const float time)
{
    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();
        drawModel(scene, model, time);
        glPopMatrix();
    }
}


bool DMGLSimpleRenderer::setupLight(const int n, 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);
    return true;
}


bool DMGLSimpleRenderer::setupCamera(DMCamera &camera)
{
    (void) camera;

    gluLookAt(0, 0.12, 0.24, 0, 0.12, 0, 0, 1, 0);
    return true;
}


bool DMGLSimpleRenderer::animate(DMSimpleScene &scene, const float time)
{
    (void) scene;
    (void) time;

    glRotatef(2.0f, 0, 1, 0);
    return true;
}