Patrón Singleton en OpenGL (Singleton Pattern for OpenGL in a Game Engine)

A menudo cuando se crea un programa en OpenGL si queremos usar elementos contenidos en variables o en objetos de alguna clase, entonces tendemos a usar variable globales. Y si además, hablamos de programas grandes o relativamente grandes es aún peor ya que no es muy conveniente usar en esos casos variables globales, o por lo menos, demasiadas.

En OpenGL sucede que en nuestra función main se invocan las funciones de callback y ahí no podemos crear una variable que afecte directamente en ellas con lo cual muchas veces se termina usando variables globales al principio del programa. Como una posible solución, que además usé para realizar mi proyecto de fin de grado es utilizar un patrón de diseño llamado Singleton. Es bastante frecuente usar una clase que utilice el patrón Singleton a la hora de crear motores gráficos y aquí se explicará un poco el tema y se expondrá un código de ejemplo para entender su estructura y funcionamiento.




El patron de diseno de Instancia única o Singleton es aquel que debe existir una única instancia de una clase que debe ser accesible a los clientes desde un punto de acceso bien conocido. Se usa cuando queremos facilitar el acceso a un objeto y que sea único, para ello implementamos una clase de instancia única que no tendra operaciones de construcción accesibles desde fuera de la misma clase y, por lo tanto, solo podra ser instanciada por ella misma.

El diseño de la clase quedaría como una clase Singleton que posee un objeto de la clase Singleton en el miembro privado y como miembro público un método estático que devuelve la referencia al miembro estático.



Código (este código ha sido modificado del ejemplo de la página http://www.swiftless.com/tutorials/opengl/camera2.html):


singleton.hpp

#ifndef _SINGLETON_HPP_
#define _SINGLETON_HPP_

#include <GL/gl.h>
#include <GL/glut.h>
#include <iostream>
#include <cstdlib>
#include <cmath>

using namespace std;

namespace game{

class Singleton
{
private:
    static Singleton *_instancia;

    float positionz[10];
    float positionx[10];
    float xpos, ypos, zpos, xrot, yrot, lastx, lasty;
public:
    static Singleton* getInstancia()
    {
        if(!_instancia)
            _instancia = new Singleton();
        return _instancia;
    }

    //Métodos
    void IniciarJuego(int argc, char *argv[]);

    void display();
    static void displayCallback();

    void mouseMovement(int x, int y);
    static void mouseMovementCallback(int x, int y);

    void keyboard(unsigned char key, int x, int y);
    static void keyboardCallback(unsigned char key, int x, int y);

    void reshape(int w, int h);
    static void reshapeCallback(int w, int h);

    void init();
    void cubepositions();
    void cube();
    void enable();
    void camera();
};

}

#endif

singleton.cpp

#include "singleton.hpp"

using namespace std;
using namespace game;

namespace game{

Singleton *Singleton::_instancia = NULL;

void Singleton::IniciarJuego(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("A basic OpenGL Window");
    init();
    glutDisplayFunc(displayCallback);
    glutIdleFunc(displayCallback);
    glutReshapeFunc(reshapeCallback);

    glutPassiveMotionFunc(mouseMovementCallback); //check for mouse movement

    glutKeyboardFunc(keyboardCallback);
    glutMainLoop();
}

void Singleton::cubepositions()
{
    for (int i=0;i<10;i++)
    {
        positionz[i] = rand()%5 + 5;
        positionx[i] = rand()%5 + 5;
    }
}

//draw the cube
void Singleton::cube()
{
    for (int i = 0; i < 10; i++)
    {
        glPushMatrix();
        glTranslated(-positionx[i + 1] * 10, 0, -positionz[i + 1] *10); //translate the cube
        glutSolidCube(2); //draw the cube
        glPopMatrix();
    }
}

void Singleton::init()
{
    xpos = 0; ypos = 0; zpos = 0; xrot = 0; yrot = 0;
    cubepositions();
}

void Singleton::enable()
{
    glEnable (GL_DEPTH_TEST); //enable the depth testing
    glEnable (GL_LIGHTING); //enable the lighting
    glEnable (GL_LIGHT0); //enable LIGHT0, our Diffuse Light
    glShadeModel (GL_SMOOTH); //set the shader to smooth shader
}

void Singleton::camera()
{
    glRotatef(xrot, 1.0, 0.0, 0.0);  //rotate our camera on teh x-axis (left and right)
    glRotatef(yrot, 0.0, 1.0, 0.0);  //rotate our camera on the y-axis (up and down)
    glTranslated(-xpos, -ypos, -zpos); //translate the screen to the position of our camera
}

void Singleton::display()
{
    glClearColor (0.0,0.0,0.0,1.0); //clear the screen to black
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear the color buffer and the depth buffer
    glLoadIdentity(); 
    camera();
    enable();
    cube(); //call the cube drawing function
    glutSwapBuffers(); //swap the buffers
}

void Singleton::displayCallback()
{
    _instancia->display();
}

void Singleton::reshape(int w, int h)
{
    glViewport (0, 0, (GLsizei)w, (GLsizei)h); //set the viewport to the current window specifications
    glMatrixMode (GL_PROJECTION); //set the matrix to projection

    glLoadIdentity ();
    gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 1000.0); //set the perspective (angle of sight, width, height, , depth)
    glMatrixMode (GL_MODELVIEW); //set the matrix back to model
}

void Singleton::reshapeCallback(int w, int h)
{
    _instancia->reshape(w, h);
}

void Singleton::keyboard(unsigned char key, int x, int y)
{
    if (key=='q')
    {
        xrot += 1;
        if (xrot >360) xrot -= 360;
    }

    if (key=='z')
    {
        xrot -= 1;
        if (xrot < -360) xrot += 360;
    }

    if (key=='w')
    {
        float xrotrad, yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xrotrad = (xrot / 180 * 3.141592654f);
        xpos += float(sin(yrotrad));
        zpos -= float(cos(yrotrad));
        ypos -= float(sin(xrotrad));
    }

    if (key=='s')
    {
        float xrotrad, yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xrotrad = (xrot / 180 * 3.141592654f);
        xpos -= float(sin(yrotrad));
        zpos += float(cos(yrotrad));
        ypos += float(sin(xrotrad));
    }

    if (key=='d')
    {
        float yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xpos += float(cos(yrotrad)) * 0.2;
        zpos += float(sin(yrotrad)) * 0.2;
    }

    if (key=='a')
    {
        float yrotrad;
        yrotrad = (yrot / 180 * 3.141592654f);
        xpos -= float(cos(yrotrad)) * 0.2;
        zpos -= float(sin(yrotrad)) * 0.2;
    }

    if (key==27)
    {
        exit(0);
    }
}

void Singleton::keyboardCallback(unsigned char key, int x, int y)
{
    _instancia->keyboard(key, x, y);
}

void Singleton::mouseMovement(int x, int y)
{
    int diffx=x-lastx; //check the difference between the current x and the last x position
    int diffy=y-lasty; //check the difference between the current y and the last y position
    lastx=x; //set lastx to the current x position
    lasty=y; //set lasty to the current y position
    xrot += (float) diffy; //set the xrot to xrot with the addition of the difference in the y position
    yrot += (float) diffx;    //set the xrot to yrot with the addition of the difference in the x position
}

void Singleton::mouseMovementCallback(int x, int y)
{
    _instancia->mouseMovement(x, y);
}

}

main.cpp

#include "singleton.hpp"

using namespace std;
using namespace game;

int main(int argc, char **argv)
{
    Singleton *juego = Singleton::getInstancia();
    juego->IniciarJuego(argc, argv);

    return 0;
}

makefile

Singleton:
    g++ -Wall -std=c++11 -O2 -lGL -lGLU -lglut main.cpp singleton.cpp -o singleton


Comentarios

Entradas populares