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.
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
Publicar un comentario