view dmglrender.cpp @ 107:2b30217a3c39 default tip

Fix verbose build echos.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 29 Feb 2024 21:48:47 +0200
parents 50a69d327b4f
children
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>


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


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::initRenderer1(const char *title,
        const int width, const int height,
        const int sdlWindowHPos, const int sdlWindowVPos,
        const int sdlFlags)
{
    // 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 (!DMSimpleRenderer::initRenderer1(
        title,
        width, height,
        sdlWindowHPos, sdlWindowVPos,
        sdlFlags | SDL_WINDOW_OPENGL))
        return false;

    // Create OpenGL context
    if ((sdlGLContext = SDL_GL_CreateContext(sdlWindow)) == 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");

    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"
#undef DM_GLEXT_INIT

    return status;
}


#define TWIDTH 8
#define THEIGHT 8
static char texSrc8[TWIDTH * THEIGHT + 1] =
    "..%##%.."
    ".%####%."
    "%#*..*#%"
    "##....##"
    "########"
    "##....##"
    "##....##"
    "##....##";

static Uint32 texSrc32[TWIDTH * THEIGHT];


bool DMGLSimpleRenderer::initRenderer2(void)
{
    // 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, windowWidth, windowHeight);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(45.0f, GLfloat(windowWidth) / GLfloat(windowHeight), 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);


    // Create texture bitmap
    for (int yc = 0; yc < THEIGHT; yc++)
    {
        Uint8 *dp8 = ((Uint8 *) texSrc8) + yc * TWIDTH;
        Uint32 *dp32 = ((Uint32 *) texSrc32) + yc * TWIDTH;

        for (int xc = 0; xc < TWIDTH; xc++)
        {
            Uint8 col = dp8[xc];
            switch (col)
            {
                case '.': col = 0; break;
                case '#': col = 255; break;
                case '*': col = 128; break;
                default: col = 192; break;
            }
            dp8[xc] = col;
            dp32[xc] = col | (col << 8) | (col << 16);
        }
    }

    // Upload to GPU texture
    glGenTextures(1, &tex8);
    glBindTexture(GL_TEXTURE_2D, tex8);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, TWIDTH, THEIGHT, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texSrc8);
    glBindTexture(GL_TEXTURE_2D, 0);

    glGenTextures(2, &tex32);
    glBindTexture(GL_TEXTURE_2D, tex32);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TWIDTH, THEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, texSrc32);
    glBindTexture(GL_TEXTURE_2D, 0);

    return checkErrors();
}


bool DMGLSimpleRenderer::compileModelShaders(DMModel &model)
{
    if (useShaders)
    {
        GLuint id_fs, id_vs;

        if (!dmCompileShader(GL_FRAGMENT_SHADER, model.fragShaderStr, id_fs) ||
            !dmCompileShader(GL_VERTEX_SHADER, model.vertShaderStr, id_vs))
            return false;

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


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

    DMSimpleRenderer::shutdownRenderer();
}


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());
        glUniform1f(glGetUniformLocation(model.id_prog, "ftime"), time);
    }

    // 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)
{
    float qx, qy, qw, qh;

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

    // Draw texture #1
    glColor3ub(0xff, 0xff, 0xff);

    qh = 0.35f;
    qw = qh * 0.6f;
    qx = 0;
    qy = 0;

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex8);
    glBegin(GL_QUADS);
    glTexCoord2i(-1,  0); glVertex2f(qx     , qy);
    glTexCoord2i( 0,  0); glVertex2f(qx + qw, qy);
    glTexCoord2i( 0, -1); glVertex2f(qx + qw, qy + qh);
    glTexCoord2i(-1, -1); glVertex2f(qx     , qy + qh);
    glEnd();

    // Draw texture #2
    qh = 0.25f;
    qw = qh * 0.6f;
    qx = 1.0f - qw;
    qy = 1.0f - qh;

    glBindTexture(GL_TEXTURE_2D, tex32);
    glBegin(GL_QUADS);
    glTexCoord2i(-1,  0); glVertex2f(qx     , qy);
    glTexCoord2i( 0,  0); glVertex2f(qx + qw, qy);
    glTexCoord2i( 0, -1); glVertex2f(qx + qw, qy + qh);
    glTexCoord2i(-1, -1); glVertex2f(qx     , qy + qh);
    glEnd();
    glBindTexture(GL_TEXTURE_2D, 0);
    glDisable(GL_TEXTURE_2D);

    // Restore the 3D projection
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);

    // Draw models
    glPushMatrix();
    glRotatef(time * 360.0, 0, 1, 0);

    for (const DMModel &model : scene.models)
    {
        glPushMatrix();
        drawModel(scene, model, time);
        glPopMatrix();
    }

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