changeset 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 7b138613e2fc
children d6ffc59bb84d
files Makefile.gen dmglrender.cpp dmglrender.h dmrender.h gldragon.cpp
diffstat 5 files changed, 551 insertions(+), 336 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.gen	Sat Dec 14 14:08:51 2019 +0200
+++ b/Makefile.gen	Sat Dec 14 16:39:20 2019 +0200
@@ -44,7 +44,7 @@
 	$(COMPILE_CXX_OBJ)
 
 
-$(BINPATH)gldragon$(BINEXT): $(addprefix $(OBJPATH), gldragon.o dmscene.o dmutil.o)
+$(BINPATH)gldragon$(BINEXT): $(addprefix $(OBJPATH), gldragon.o dmglrender.o dmscene.o dmutil.o)
 	$(LINK_CXX_BIN) $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS)
 
 $(BINPATH)ply2bin$(BINEXT): $(addprefix $(OBJPATH), ply2bin.o dmscene.o dmutil.o)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmglrender.cpp	Sat Dec 14 16:39:20 2019 +0200
@@ -0,0 +1,366 @@
+//
+// 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmglrender.h	Sat Dec 14 16:39:20 2019 +0200
@@ -0,0 +1,40 @@
+//
+// 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.
+//
+#ifndef DMGLRENDER_H
+#define DMGLRENDER_H 1
+
+#include "dmrender.h"
+#include <SDL_opengl.h>
+
+
+struct DMGLSimpleRenderer : DMSimpleRenderer
+{
+    SDL_GLContext glContext;
+
+    DMGLSimpleRenderer()
+    {
+        glContext = NULL;
+    }
+
+    bool checkErrors(void);
+    bool initRender1(void);
+    bool initRender2(SDL_Window *window);
+    bool initRender3(const int width, const int height);
+    void shutdownRenderer(void);
+
+    void drawModel(const DMSimpleScene &scene, const DMModel &model, const float time);
+    void drawScene(const DMSimpleScene &scene, const float time);
+
+    bool compileModelShaders(DMModel &model);
+    bool setupLight(const int n, DMLight &light);
+    bool setupCamera(DMCamera &camera);
+    bool animate(DMSimpleScene &scene, const float time);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmrender.h	Sat Dec 14 16:39:20 2019 +0200
@@ -0,0 +1,116 @@
+//
+// GLDragon - OpenGL PLY model viewer / simple benchmark
+// -- Basic renderer template
+// 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.
+//
+#ifndef DMRENDER_H
+#define DMRENDER_H 1
+
+#include "dmscene.h"
+#include <SDL.h>
+
+
+struct DMSimpleRenderer
+{
+    bool useShaders;
+
+    DMSimpleRenderer()
+    {
+        useShaders = false;
+    }
+
+    virtual bool checkErrors(void)
+    {
+        return true;
+    }
+
+    virtual bool initRender1(void)
+    {
+        return false;
+    }
+
+    virtual bool initRender2(SDL_Window *window)
+    {
+        (void) window;
+        return false;
+    }
+
+    virtual bool initRender3(const int width, const int height)
+    {
+        (void) width;
+        (void) height;
+
+        return false;
+    }
+
+    virtual void shutdownRenderer(void)
+    {
+    }
+
+    virtual void drawModel(const DMSimpleScene &scene, const DMModel &model, const float time)
+    {
+        (void) scene;
+        (void) model;
+        (void) time;
+    }
+
+    virtual void drawScene(const DMSimpleScene &scene, const float time)
+    {
+        (void) scene;
+        (void) time;
+    }
+
+    virtual bool compileModelShaders(DMModel &model)
+    {
+        (void) model;
+        return false;
+    }
+
+    virtual bool compileSceneShaders(DMSimpleScene &scene)
+    {
+        if (useShaders)
+        {
+            for (DMModel &model : scene.models)
+            {
+                if (!compileModelShaders(model))
+                    return false;
+            }
+        }
+        return true;
+    }
+
+    virtual bool setupLight(const int n, DMLight &light)
+    {
+        (void) n;
+        (void) light;
+        return true;
+    }
+
+    virtual bool setupLights(DMSimpleScene &scene)
+    {
+        for (size_t n = 0; n < scene.lights.size(); n++)
+        {
+            if (!setupLight(n, scene.lights[n]))
+                return false;
+        }
+        return true;
+    }
+
+    virtual bool setupCamera(DMCamera &camera)
+    {
+        (void) camera;
+        return true;
+    }
+
+    virtual bool animate(DMSimpleScene &scene, const float time)
+    {
+        (void) scene;
+        (void) time;
+        return true;
+    }
+};
+
+#endif
--- a/gldragon.cpp	Sat Dec 14 14:08:51 2019 +0200
+++ b/gldragon.cpp	Sat Dec 14 16:39:20 2019 +0200
@@ -8,11 +8,8 @@
 // Originally based on 'glxdragon' Copyright (c) 2009, Thomas Trummer
 //
 #include <SDL.h>
-#include <SDL_opengl.h>
-#include <GL/glu.h>
-
 #include "dmutil.h"
-#include "dmscene.h"
+#include "dmglrender.h"
 
 
 /* Default settings etc. constants
@@ -34,96 +31,11 @@
 /* Globals
  */
 SDL_Window *dmWindow = NULL;
-SDL_GLContext dmGLContext = NULL;
 
 
 /* Helpers
  */
-bool dmGLCheckErrors(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;
-}
-
-
-#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
-
-
-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());
-    }
-}
-
-
-void * dmGLGetProcAddr(const std::string &name)
-{
-    void *ptr = SDL_GL_GetProcAddress(name.c_str());
-
-    if (ptr == NULL)
-        dmMsg(" - '%s' NOT supported.\n");
-
-    return ptr;
-}
-
-
-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
-
-
-bool dmInitGLExtensions(void)
-{
-    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 dmInitSDLGL(const int width, const int height, const char *title)
+bool dmInitSDL(DMSimpleRenderer &renderer, const int width, const int height, const char *title)
 {
     int ret;
     std::string msg;
@@ -136,19 +48,9 @@
         return false;
     }
 
-    // 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);
+    // Part 1 of initialization
+    if (!renderer.initRender1())
+        return false;
 
     // Attempt to create a window
     if ((dmWindow = SDL_CreateWindow(title,
@@ -161,12 +63,10 @@
         return false;
     }
 
-    if ((dmGLContext = SDL_GL_CreateContext(dmWindow)) == NULL)
-    {
-        dmError("Unable to create SDL OpenGL context: %s\n",
-            SDL_GetError());
+    // Part 2 of initialization
+    if (!renderer.initRender2(dmWindow))
         return false;
-    }
+
 
     // Check if we want to attempt to use vsync
     switch (optVSyncMode)
@@ -204,211 +104,13 @@
         return false;
     }
 
-    // Get/initialize OpenGL extension function pointers
-    if (optUseShaders && !dmInitGLExtensions())
-        return false;
-
-    // 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));
-    dmMsg("VSync mode  : %s\n", msg.c_str());
-
-    if (!dmGLCheckErrors())
+    // Part 3 of initialization
+    if (!renderer.initRender3(width, height))
         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 dmGLCheckErrors();
-}
-
-
-void dmDrawModel(const DMSimpleScene &scene, const DMModel &model)
-{
-    int maxIndices;
-
-    if (optUseShaders)
-    {
-        // 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 (optUseShaders)
-    {
-        glUseProgram(0);
-    }
-}
-
+    dmMsg("VSync mode  : %s\n", msg.c_str());
 
-void dmDrawScene(const DMSimpleScene &scene)
-{
-    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();
-        dmDrawModel(scene, model);
-        glPopMatrix();
-    }
-}
-
-
-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;
-    }
-}
-
-
-void dmLinkModelShaders(DMModel &model)
-{
-    model.id_prog = glCreateProgram();
-    glAttachShader(model.id_prog, model.id_fs);
-    glAttachShader(model.id_prog, model.id_vs);
-    glLinkProgram(model.id_prog);
-}
-
-
-void dmSetupLight(const int n, const 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;
 }
 
 
@@ -421,6 +123,7 @@
         optShowHelp = false,
         optSetInputFilename = false;
     std::string optInputFilename = "dragon.scene", basePath;
+    DMGLSimpleRenderer renderer;
     DMSimpleScene scene;
 
     // Check commandline argument for enabling shaders
@@ -559,30 +262,20 @@
         }
     }
 
+    // Set shader usage
+    renderer.useShaders = optUseShaders;
+
     // Initialize SDL + OpenGL
-    if (!dmInitSDLGL(optWidth, optHeight, "GLDragon"))
+    if (!dmInitSDL(renderer, optWidth, optHeight, "GLDragon"))
         goto exit;
 
-    // According to our mode ..
-    if (optUseShaders)
-    {
-        for (DMModel &model : scene.models)
-        {
-            if (!dmCompileShader(GL_FRAGMENT_SHADER, model.fragShaderStr, model.id_fs) ||
-                !dmCompileShader(GL_VERTEX_SHADER, model.vertShaderStr, model.id_vs))
-                goto exit;
+    // Compile shaders for scene
+    if (!renderer.compileSceneShaders(scene))
+        goto exit;
 
-            dmLinkModelShaders(model);
-        }
-    }
-
-    // Setup lights
-    for (size_t n = 0; n < scene.lights.size(); n++)
-        dmSetupLight(n, scene.lights[n]);
-
-    // Define the camera
-    gluLookAt(0, 0.12, 0.24, 0, 0.12, 0, 0, 1, 0);
-
+    // Setup lights and camera
+    renderer.setupLights(scene);
+    renderer.setupCamera(scene.camera);
 
     // Main loop starts
     startTime = cycleStart = SDL_GetTicks();
@@ -610,16 +303,17 @@
         }
 
         // Render the next frame
-        dmDrawScene(scene);
+        totalTime = SDL_GetTicks() - startTime;
+        renderer.drawScene(scene, totalTime);
 
         // Draw the current frame
         SDL_GL_SwapWindow(dmWindow);
 
         // Rotate for 2 degrees
-        glRotatef(2.0f, 0, 1, 0);
+        renderer.animate(scene, totalTime);
 
         // Check for errors
-        dmGLCheckErrors();
+        renderer.checkErrors();
 
         // Return true if a full rotation was done
         totalFrames++;
@@ -647,8 +341,7 @@
         totalTime, totalFrames, (totalFrames * 1000.0f) / totalTime);
 
 exit:
-    if (dmGLContext != NULL)
-        SDL_GL_DeleteContext(dmGLContext);
+    renderer.shutdownRenderer();
 
     if (dmWindow != NULL)
         SDL_DestroyWindow(dmWindow);