miércoles, 8 de junio de 2011

GameEngine: Capitulo 6 . Texture Manager

Hola a todos,

Bienvenidos a una nueva entrega sobre como hacer tu propio game engine desde 0.

En este capítulo explicaremos la implementación del texture manager. A grandes rasgos es lo mismo que el soundmanager pero para texturas. Es decir, cargaremos las texturas mediante un archivo de loading y accederemos a ellas mediante tags.

La carga de texturas (por ahora) la implementaremos con SOIL (quien quiera hacerlo con otra libreria y pasarmelo , yo se lo publico). La libreria SOIL es una manera muy potente de cargar texturas para su uso en 2D/3D . Os la podeis bajar de aquí 

Como unir SOIL al proyecto.
Una vez tengáis instalada la librería (mirad por Internet, no os lo voy ha hacer yo todo :D ) tendremos que linkarla al proyecto. Como ya hicimos en algún capítulo anterior:
1.propiedades de proyecto > C/C++ > Directorios de inclusión adicionales > “la ruta donde tengais instalados los .h de SOIL”
2. propiedades de proyecto > Vinculador>.Entrada > Dependencias adicionales : SOIL.lib

Con esto os deberia de funcionar todo. Si teneis problemas poned un comentario en este post y lo miraremos.

Como implementar el TextureManager
El texture manager se compone de un .h y un .cpp c on una estructura interna muy parecida al Sound Manager , así como de cambios en el GraphicsManager (inicialización , carga, descarga ,etc..) y en el Graphics3DManager (texturizaremos el cubo que teniamos hasta ahora con un gráfico de madera)

El código es el siguiente:
TextureManager.h:
/**************************************************************************************************/
// Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
// para el blog LordPakus (http://lordpakus.blogspot.com/).
// Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// TextureManager.h : Código del gestor de texturas del game engine
// El código del cargador de texturas se obtiene de http://www.lonesock.net/soil.html
/**************************************************************************************************/

#ifndef __TextureManager__
#define __TextureManager__

#include "MyGL.h"
#include "FileManager.h" //Sirve para cargar el archivo de loading de texturas

#define MAX_ELEMENTS_LOADED 256 //por ahora hardcodeado, lo arreglaremos en algún momento.


class TextureManager
{
private:
// Constructor y destructor de la clase
static TextureManager instance;
TextureManager();
~TextureManager();

public:
static TextureManager& singleton();

public:
//Funcion para inicializar el motor de sonido
void Init(int* argc, char* argv[]);

//Funcion para desinicializar el motor de sonido
void DeInit();

public:
int LoadTexture( char cad[] );
void Texture(char id[]);

private:
FileManager filemanager;

char loaded[MAX_ELEMENTS_LOADED][256]; //Matriz donde guardamos los identificadores de textura y sus indices.

};


#endif // __FileManager__


TextureManager.cpp:
/**************************************************************************************************/
// Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
// para el blog LordPakus (http://lordpakus.blogspot.com/).
// Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// TextureManager.cpp : Código del gestor de texturas del game engine
// El código del cargador de texturas se obtiene de http://www.lonesock.net/soil.html
/**************************************************************************************************/
#include "TextureManager.h"
#include "MyGL.h"

#include <SOIL/SOIL.h> //Libreria usada para cargar texturas

#include <iostream> //Usada para imprimir por consola

using namespace std;

//Instancia única del sound manager
TextureManager TextureManager::instance;

TextureManager::TextureManager()
{
}

TextureManager::~TextureManager()
{
}

//Devolvemos el puntero al singleton
TextureManager& TextureManager::singleton()
{
return instance;
}

//Sounds functions
void TextureManager::Init(int* argc, char* argv[])
{
char cad[256]; //Cadena donde guardaremos cada linea que leamos
char id[256]; //Cadena donde guardaremos el identificador de cada recurso
char ruta[256]; //Cadena donde guardaremos la ruta de cada recurso
int num_id; //número que nos asigna la función de carga del recurso.... un -1 significa que no lo ha podido cargar

cout << "Inicializamos el texture manager\n";

//alutInit(argc, argv) ;

filemanager.Init("Data\\LoadTexture.txt");


//Todo este código tendrá que ir al filemanager, pero por el momento lo dejamos aquí
//Leemos cada linea del archivo
while( filemanager.GetLine(cad) )
{
for( int i = 0 ; i < strlen(cad); ++i)
{
if ( cad[i] == '=' )
{
cad[i]='\0';
sprintf(id,"%s",cad);
sprintf(ruta,"%s",&cad[i+1]);
i = 257;
}
}

num_id = LoadTexture(ruta); //Cargamos el recurso
cout << "Intentamos cargar textura con ruta:" << ruta << "\n";

if (num_id == -1) //Si el recurso no se ha podido cargar, vamos a cargar el siguiente recurso
continue;

cout << "textura cargada con identificadores:" << id << " " << num_id << "\n";

sprintf( loaded[ num_id ] ,"%s" , id ); //Nos guardamos la referencia al recurso
}
}

void TextureManager::DeInit()
{
cout << "Desinicializamos el texture manager\n";
filemanager.DeInit();
}


//Carga el sonido cad y devuelve su posición en la que se ha guardado
int TextureManager::LoadTexture( char cad[] )
{
//TODO : Evaluar los casos en los que el archivo no se puede cargar y devolver un -1 en consecuencia
return ( SOIL_load_OGL_texture(
cad,
SOIL_LOAD_AUTO,
SOIL_CREATE_NEW_ID,
SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT)
);
}

//Función usada para seleccionar una textura en especial
void TextureManager::Texture(char id[])
{
int i; //Iterador
for ( i = 0 ; i < MAX_ELEMENTS_LOADED ; ++i ) //Para cada elemento cargado
{
if( !strcmp(loaded[i],id) ) //Hemos encontrado la cadena que nos interesa
{
continue;
}
}

if ( i == MAX_ELEMENTS_LOADED)
return;

//Texturizamos
glBindTexture(GL_TEXTURE_2D, i);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

}

GraphicsManager.cpp:
/**************************************************************************************************/
// Código creado por F.Bordas (LordPakus) como ejemplo de creación de un game engine
// para el blog LordPakus (http://lordpakus.blogspot.com/).
// Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/

/**************************************************************************************************/
// GraphicsManager.cpp : Código del motor gráfico.
/**************************************************************************************************/

#include "GraphicsManager.h"
#include "MyGL.h"

#include "Graphics3DManager.h" //Parte de 3D del motor gráfico
#include "TextureManager.h" //Inclusión del motor de texturas

#include <iostream> //Usada para imprimir por consola

using namespace std;

//Instancia única del graphics manager
GraphicsManager GraphicsManager::instance;

//Constructor
GraphicsManager::GraphicsManager()
{
}

//Destructor
GraphicsManager::~GraphicsManager()
{
}

//Devolvemos el puntero al singleton
GraphicsManager& GraphicsManager::singleton()
{
return instance;
}

//Funcion para inicializar el motor gráfico
void GraphicsManager::Init(int* argc, char* argv[])
{
cout << "Inicializamos el Graphics Manager\n";
//Creamos la ventana principal del programa utilizando la libreria GLUT
glutInit(argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(0, 0);
glutInitWindowSize(640, 480);
int mainWindow = glutCreateWindow("Lord Pakus Engine!!!"); //Viva la originalidad!!! :)
glutSetWindow(mainWindow);

//Asignamos todas las funciones gráficas de callback del glut
//a las funciones que definiremos posteriormente
glutDisplayFunc(Render);
// glutReshapeFunc(Reshape); //Por ahora la dejamos comentada por que no nos hace falta

TextureManager::singleton().Init(argc,argv); //Inicializamos las texturas
Graphics3DManager::singleton().Init(argc,argv); //Inicializamos la parte de 3D
}

//Funcion para desinicializar el bucle principal
void GraphicsManager::DeInit()
{
TextureManager::singleton().DeInit(); //Desinicializamos la parte de texturas
Graphics3DManager::singleton().DeInit(); //Desinicializamos la parte de 3D

cout << "Desinicializamos el Graphics Manager\n";
}

//CALLBACKS DE GLUT, por ahora

//Funcion que se ejecuta automaticamente desde glut (por ahora)
void GraphicsManager::Render()
{
//Pintado de la parte de 3D
Graphics3DManager::singleton().Render();

//IMPORTANTE: Función encargada de realizar el double-buffering
glutSwapBuffers();
}

//Funcion que se ejecuta automaticamente desde glut (por ahora). Por el momento la dejamos comentada por que no nos hace falta
//void GraphicsManager::Reshape()
//{
// //AQUI IRÁ NUESTRA FUNCIÓN DE REESCALADO. Por ahora vacia
//}


Graphics3DManager: Se aplica solo este cambio dentro de la función de pintado.
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f,1.0f,1.0f); //Limpiamos el buffer de color
TextureManager::singleton().Texture("wood"); //Usamos la textura

glBegin(GL_QUADS); // Start Drawing The Cube
//glColor3f(0.0f,1.0f,0.0f); // Set The Color To Green
glTexCoord2f( 0.0f, 0.0f); glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Top)
glTexCoord2f( 0.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Top)
glTexCoord2f( 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Quad (Top)
glTexCoord2f( 1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Quad (Top)
//glColor3f(1.0f,0.5f,0.0f); // Set The Color To Orange
glTexCoord2f( 0.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 1.0f); // Top Right Of The Quad (Bottom)
glTexCoord2f( 0.0f, 1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); // Top Left Of The Quad (Bottom)
glTexCoord2f( 1.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Bottom)
glTexCoord2f( 1.0f, 0.0f); glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Bottom)
//glColor3f(1.0f,0.0f,0.0f); // Set The Color To Red
glTexCoord2f( 0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Front)
glTexCoord2f( 0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Front)
glTexCoord2f( 1.0f, 1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Front)
glTexCoord2f( 1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Front)

//glColor3f(1.0f,1.0f,0.0f); // Set The Color To Yellow
glTexCoord2f( 0.0f, 0.0f); glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Back)
glTexCoord2f( 0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Back)
glTexCoord2f( 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Back)
glTexCoord2f( 1.0f, 0.0f); glVertex3f( 1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Back)

//glColor3f(0.0f,0.0f,1.0f); // Set The Color To Blue
glTexCoord2f( 0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Left)
glTexCoord2f( 0.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Left)
glTexCoord2f( 1.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Left)
glTexCoord2f( 1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Left)

//glColor3f(1.0f,0.0f,1.0f); // Set The Color To Violet
glTexCoord2f( 0.0f, 0.0f); glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Right)
glTexCoord2f( 0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Right)
glTexCoord2f( 1.0f, 1.0f); glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Right)
glTexCoord2f( 1.0f, 0.0f); glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Right)
glEnd(); // Done Drawing The Quad
glDisable(GL_TEXTURE_2D);



Espero que os haya gustado. Aunque este capitulo os parezca una tonteria es el que nos servirá de base para absolutamente todo el módulo de Graphics2DManager y lo usaremos con cierta recurrencia para múltiples utilidades (graficos 2D, fuentes bitmapeadas , HUD , texturización de modelos 3D ,etc...)

Probad que os funcione y si veis que os falla, lo miramo.

LordPakusBlog
Nos vemosss

3 comentarios :

  1. En mi juego uso un std::map , lo ves como una buena alternativa o es mejor hacerlo como tu, una matriz de chars con los indices de las texturas????

    ResponderEliminar
  2. el std::map es estatico con un string como key i dentro el puntero a la Texture*.

    Por cierto, muy interesante tu blog, animos!!!

    ResponderEliminar
  3. Sip Thati, mi idea inicial era hacerlo con un map pero me parecio que más de uno se perderia por las ramas, así que decidí hacerlo más cutre para que todo el mundo lo entendiera.

    Tu opción es claramente mejor, pero no te preocupes mucho, en los últimos capitulos (que aún estoy trabajando en ello) he implementado un lector de xml, así que toda esta estructura cambia radicalmente.

    No obstante, gracias por tus animos y por tu aportación, en la versión final no te preocupes que estará con maps.

    Gracias por todo,

    Nos vemossss

    ResponderEliminar

Entradas populares