"Ningún juego dura tanto hasta que te lo terminas como el que te programas tú mismo,
ninguno te absorbe tanto y ninguno te produce tanta satisfacción cuando lo has acabado"
Programacion de videojuegos
Lunes 21 de Agosto del 2017

Menu principal
Colaborar (con PayPal)

Para continuar con el trabajo de esta Web y poder pagar el hosting, viene bien la ayuda que sea. Gracias a todos.

Importe: 

Ultimas descargas
19.Jan

Clase que permite dibujar texto en OpenGL con mucha facilidad.Usa FreeType2.Para ver que hace y c...


19. Texto con FTGL Imprimir Correo electrónico
Videojuegos - Curso de Programación de juegos
Escrito por Vicengetorix   
En este capitulo veremos el uso de la libreria FTGL para renderizar texto con OpenGL.
Ya vimos en su momento la forma de poner texto en pantalla pero el metodo que usamos entonces puede ralentizarnos el programa y es poco versatil, permite hacer pocas cosas con el texto.


Si habeis navegado por internet buscando informacion sobre OpenGL, es probable que os hayais topado con el termino "textured fonts". Esto quiere decir escribir texto usando texturas (obvio), lo mismo que hacemos en los programas del curso con el cartel del logo de UnSitioWeb pero aplicado a letras.
Este metodo es mas rapido que los "raster fonts", escribir los pixels de las letras a pantalla directamente, que es lo que haciamos con la forma antigua de escribir del curso, porque aprovecha la aceleracion grafica de la tarjeta.

Para implementar texto con "textured fonts" habria que montarse un metodo para cargar la fuente del fichero en disco y luego los metodos necesarios para pintar texto usando la fuente cargada.
Esto es un trabajo arduo que, por otro lado ya esta hecho de forma altruista.
La libreria FreeType decodifica los ficheros de fuentes y es usada por FTGL para cargar fuentes para luego renderizarlas en OpenGL de varias posibles maneras.
FTGL, pues, carga la fuente y renderiza texto a la manera "raster", a la manera "texture" o de forma poligonal y en 3D. Ademas le aplica suavizado (antialiasing) si es necesario para que se vea mejor.
Todas estas razones me llevan a usar FTGL para el texto en nuestros programas.

El uso de FTGL es muy facil y en la documentacion de la web queda muy claro; aun asi lo veremos y aclararemos aguna cosa. No explotaremos todo el potencial de FTGL, cada uno que llegue donde le interese.

Partiremos del codigo del capitulo anterior, el 18. A medida que vayamos modificando el programa, no solo añadiremos cosas si no que tambien eliminaremos lo relativo a la forma antigua que teniamos de renderizar texto.
Necesitaremos la libreria FTGL, claro, pero de su web se pueden solo bajar las fuentes. Para agilizar el tema he compilado yo la libreria FTGL para VC2005  y DEV-C (MinGW). Se puede bajar de la zona de descargas, de aqui, con sus cabeceras y con la libreria FreeType2 que es usada por FTGL.

Con todo preparado comenzamos con las definiciones globales como de costumbre.

Lo primero incluir las librerias.

1
2
3
// Incluimos las librerias de FreeType y FTGL
#pragma comment( lib, "freetype2312MT.lib" )
#pragma comment( lib, "ftgl_static.lib" )

Recordatorio: esta forma de incluir librerias a traves del codigo solo funciona con VC.

Incluimos ahora el fichero de cabecera, no sin antes indicar con un define que vamos a usar una version de libreria estatica (no DLL).

1
2
3
4
5
6
7
// Definimos esto para que FTGL sepa que la 
// estamos usando de forma estatica.
// Si la hubieramos compilado con DLL no
// habria que definirlo.
#define FTGL_LIBRARY_STATIC 1
// Incluimos el fichero de cabecera
#include <FTGL/ftgl.h>

Eliminamos codigo usado en nuestra forma anterior de renderizar texto.

1
2
3
4
5
6
7
// Variable de tipo unsigned int para guardar 
// el identificativo base de las "display list"
// que generara para almacenar las letras.
// A partir de este, los siguientes, en orden,
// seran usados en estricto orden.
// Ya no es necesario al usar FTGL para el texto
//GLuint ListaFuente=0;

Empezamos a usar FTGL creando los objetos de fuente necesarios. En el constructor indicamos el fichero de fuente a cargar ( .TTF por ejemplo). En el directorio "C:\WINDOWS\Fonts" encontraremos bastantes.
Nosotros crearemos 3 objetos de fuente. Uno para la leyenda, otro para un texto grande que pondremos en pantalla y otro, de tipo "outline", para pintar un borde a las letras del segundo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Creamos un objeto de fuente en textura para 
// las letras de la leyenda.
FTTextureFont fuente("dibus/comic.TTF");
// FTGL permite fuentes tipo raster que
// no usaremos al ser mas lentas.
// Si se quiere comparar hay que comentar
// la linea anterior y descomentar esta.
//FTPixmapFont fuente("dibus/comic.TTF");
 
// Objeto texture font para el cartel grande
// que pondremos.
FTTextureFont fuente2("dibus/coop.TTF");
// Objeto outline font para el borde de las
// letras del cartel grande.
FTOutlineFont fuente3("dibus/coop.TTF");

Dos puntualizaciones. Los ficheros de fuente pueden ser estos u otros: usad los que querais.
Este codigo no comprueba que falle al cargar, eso alargaria el capitulo.
Otra cosa. He puesto una linea comentada con la creacion de un objeto tipo "Pixmap" que es un tipo de fuente "raster", preparada para sustituir a la primera fuente. Esto es para poder comparar la velociadad del programa usando "raster fonts" y usando "texture fonts" y asi entender la importancia del tema. Hay otra linea comentada en la funcion Escribe(...) que se deberia descomentar para que funcionase con "Pixmap" la leyenda del programa.

La funcion GeneraFuente() ya no es necesaria y la quitamos de nuestro codigo.

En la funcion Escribe(...) hay cambios que hacer.

No cambiamos nada hasta la funcion glOrtho que cambia. Al definir la proyeccion ortogonal debemos hacer un pequeño cambio para acomodarnos a como esta hecha FTGL, de forma que el origen de coordenadas pasara a estar abajo a la izquierda en vez de arriba a la izquierda. Esto nos obligara luego a hacer una resta cuando indiquemos la coordenada Y del texto a renderizar.

1
2
3
4
5
6
// Pongo proyeccion ortogonal.
// Damos la vuelta a la proyeccion.
// FTGL la usa al reves que nosotros,
// que solemos usar esta:
// glOrtho(0,rect.right,rect.bottom,0,-1,1)
glOrtho(0,rect.right,0,rect.bottom,-1,1);

Al terminar de usar FTGL volveremos a usar nuestras proyecciones habituales.

El siguiente bloque de codigo lo quitaremos al no ser ya util ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// La antigua forma de pintar texto no hace falta.
 
// Le indico donde va a empezar a pintar de la
// pantalla en base a los parametros de nuestra
// funcion.
//glRasterPos2i(x,y);
// Esta funcion guarda los valores de lista.
//glPushAttrib(GL_LIST_BIT);
// Establecemos el identificativo base de las listas
// a usar (-32 porque el caracter de la fuente con valor
// ASCII mas bajo es el espacio cuyo valor es 32, y de ahi
// para arriba.
//glListBase(ListaFuente-32);
// Ejecuto tantas listas como la longitud del texto a
// escribir, los indices son del tipo unsigned byte y
// los indices de las listas estan en el puntero que se
// pasa como tercer parametro(los codigos ASCII del texto).
// A estos indices se les suma la base establecida en la
// funcion anterior.
//glCallLists(strlen(texto), GL_UNSIGNED_BYTE, texto);
// Restablezco los valores de lista.
//glPopAttrib();

... y lo sustituiremos por el siguiente codigo.

Creamos un buffer para una cadena tipo UNICODE. Usaremos caracteres unicode para no tener problemas al usar tildes, la Ñ, apertura de exclamacion, apertura de interrogacion, dieresis, ... o cualquier caracter de cualquier idioma que no este entre los tipicos del ingles.
Si usamos estos caracteres y los pasamos a FTGL como normales (ASCII), pueden pintarse cosas raras o peor, fallar el programa. De esta forma no hay problemas.

1
2
3
4
5
// Creamos un buffer para guardar una cadena de
// caracteres unicode. Lo hacemos asi para evitar
// problemas desagradables si usamos caracteres con tilde
// o apertura de exclamacion o interrogacion, o dieresis.
wchar_t unicode[256];

Despues convertimos el texto a imprimir en pantalla a UNICODE con una funcion que hace eso. De la funcion MultiByteToWideChar se puede consultar en la web sus parametros pero lo que hace es coger un buffer de tipo char, convertirlo a UNICODE y dejar el resultado en un buffer de tipo wchar_t (unicode).

1
2
3
// Convertimos el texto a escribir a unicode y lo 
// guardamos en el buffer.
MultiByteToWideChar(CP_ACP, 0, texto, -1, unicode, 256);

256 es el tamaño del buffer unicode.

Luego indicamos donde vamos a pintar de la pantalla. Recordad el tema del cambio de origen de coordenadas para FTGL. Tendremos que hacer una resta para la coordenada Y.
La funcion Descender() del objeto de fuente retorna la distancia entre la linea base en que escribimos y la parte baja de la fuente (lo que baja la "j" del renglon, por ejemplo).

1
2
3
4
5
6
7
8
9
// Movemos lo que vamos a escribir a su sitio.
// Al cambiar la proyeccion, las coordenadas Y comienzan
// abajo en vez de arriba asi que para cabiarlo y darle
// las coordenadas empezando por arriba como en todo el programa,
// restaremos la coordenada que le damos (empezando arriba)
// del tamaño de nuestro area de dibujo.
// La funcion Descender() retorna la distancia de la fuente
// que baja de la linea de escritura.
glTranslatef(x, rect.bottom-y-fuente.Descender(), 0);

Esta seria la linea a descomentar si queremos probar los "PixmapFont":

1
2
3
4
// Si en vez de TextureFont queremos probar los PixmapFont
// (tipo raster) para ver como nos ralentiza el programa
// hay que comentar la linea anterior y descomentar esta.
//glRasterPos2i(x,rect.bottom-y-fuente.Descender() );

Al final, con todo preparado, por fin pintamos el texto, el que esta en el buffer de texto unicode.

1
2
// Por fin pintamos en pantalla el texto unicode.
fuente.Render(unicode);

El resto de la funcion Escribe(...) no cambia. Su funcionamiento externo sera exactamente igual que antes pero con mejor resultado.

Pasamos a la funcion Pinta() en la que renderizaremos un texto con borde.

Tras pintar el cartel del logo de UnSitioWeb pinto el texto.

Primero hago los preparativos habituales antes de pintar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Voy a pintar un cartel 
// Cambio a matriz de proyeccion
glMatrixMode(GL_PROJECTION);
// Cargo la matriz identidad
glLoadIdentity();
// Pongo la proyeccion ortogonal que
// FTGL necesita.
glOrtho(0,rect.right,0,rect.bottom,-1,1);
// Vuelvo a la matriz de modelo
glMatrixMode(GL_MODELVIEW);
// Cargo la matriz identidad
glLoadIdentity();
// Pongo un azul claro
glColor3f(0.3,0.5,0.9);
// Voy donde quiero pintar
glTranslatef(10,rect.bottom-120,0);
// Giro un poco el texto
glRotatef(10,0,0,-1);

Observad que nos hemos permitido, en la ultima funcion, rotar el texto un poco, eso se puede hacer ahora con los "Textured fonts".

Ahora hay que renderizar el texto. Lo haremos con caracteres UNICODE para poder escribir con los caracteres propios de idiomas diferentes del ingles. Para ello pondremos una "L" antes de la cadena a renderizar. Si no lo hicieramos y usaramos, por ejemplo, una "ñ", o renderizaria mal el texto o podria fallar el programa. Cuidado con esto.
Si estamos usando VC no hay problema poniendo una "L", pero si estuvieramos usando otro compilador, podria no entender la cadena con la "L" y dar un error de compilacion parecido a "illegal byte sequence" (MinGW). Deberiamos hacer la misma operacion que hemos hecho en la funcion Escribe(...).

1
2
3
4
5
// Pinto las letras. Uso la "L"
// para indicar que es una cadena unicode.
// En otros compiladores distintos de VC
// como MinGW no se puede hacer asi.
fuente2.Render(L"¡Letra FTGL!");

Tras esto, vamos a poner un borde amarillo a las letras, escribiendo lo mismo en el mismo sitio pero con un tipo de fuente "OutlineFont".

1
2
3
4
5
6
7
8
9
// Pongo el grosor de lineas a 2
glLineWidth(2);
// Pongo amarillo
glColor3f(1,1,0);
// Lo muevo un poquito adelante para
// asegurarme que se pinta por encima.
glTranslatef(0,0,0.1);
// Pinto el borde de las letras.
fuente3.Render(L"¡Letra FTGL!");

No hemos indicado hasta ahora el tamaño de las letras. Lo haremos en la funcion IniciaGL().

Lo primero aqui sera eliminar lo que ya no hace falta.

1
2
3
// Ya no hace falta esto.
// Genero la fuente con la que voy a escribir.
//GeneraFuente();

La preparacion de las fuentes es minima.
Le voy a indicar que no use display lists, que estan ya pasadas de moda y con los "OutlineFont" me han dado problemas (en algun PC me fallaba el programa).
Le indico que use UNICODE, que ya lo selecciona por defecto pero por si acaso.
Y le pongo el tamaño de la letra. Esta es una operacion pesada por lo que pongo el tamaño al inicializar y lo dejo fijo durante el programa. Si lo hiciera a cada frame, ralentizaria muchisimo.
El tamaño que indicamos es en altura, desde la letra que mas baja a la letra mas alta de todo el conjunto de caracteres de la fuente.
Luego, cuando indicamos donde se pinta la letra en el eje Y, estamos indicando la altura del renglon donde se escribe por lo que habra letras, como la "j" o la "g" que bajaran mas que la altura a la que le indicamos que escriba. Esta diferencia es la que nos retornaba la funcion Descender() que vimos en la funcion Escribe(...). La funcion Ascender() nos retornaria la distancia entre el renglon y la altura del caracter mas alto de la fuente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Preparo las fuentes que voy a usar
// Indico que no use Display List
fuente.UseDisplayList(false);
// Me aseguro que usa unicode
fuente.CharMap(ft_encoding_unicode);
// Le pongo el tamaño
fuente.FaceSize(12);
// Lo mismo para la segunda
fuente2.UseDisplayList(false);
fuente2.CharMap(ft_encoding_unicode);
fuente2.FaceSize(50);
// ...y para la tercera. Esta del mismo
// tamaño que la anterior porque es un
// OutlineFont que usaremos para el borde
// del texto escrito con la fuente 2.
fuente3.CharMap(ft_encoding_unicode);
fuente3.UseDisplayList(false);
fuente3.FaceSize(50);

Ya solo queda borrar una cosa antigua de la funcion ProcesaMensajes(...).

1
2
3
// Borro las listas.
// Ya no hace falta esto
//glDeleteLists(ListaFuente, 256-32);

Al terminar la escena se veria asi.




El codigo completo seria este: usw19.cpp .

Recuerdo de nuevo que FTGL, ya compilada, se puede bajar de la zona de descargas, de aqui.



¡Sólo los usuarios registrados pueden escribir comentarios!
+/- Comentarios
Buscar
Nicolas   |Registered |05-02-2012 05:32:43
Hola.
Yo estoy usando la librería GLM para cargar modelos 3d desde un .OBJ,
pero al agregar a mi programa un FTTextureFont el linker me tira varios errores
de definición. Ese problema lo pude solucionar al ver en el foro que había que
excluir algunos .lib pero al querer escribir algo en la pantalla deja de verse
lo que se esta pintando (lo que se escribe se ve). Como podría solucionarlo.
Vicengetorix   |SAdministrator |05-02-2012 17:59:29
Lo que te pasa, seguramente, es que se te ha olvidado restablecer la matriz de
proyección y de vista después de haberla cambiado para usar FTGL, por eso
luego no se ve lo que pintas.
Nicolas   |Registered |06-02-2012 02:02:14
Las matrices las estoy restableciendo bien, y estoy usando el código del curso
para escribir, y no puedo pintar nada ni de forma ortogonal ni en perspectiva.
No se si tenga algo que ver el .lib que tuve que excluir para poder compilarlo
(el . lib era libcmt.lib).
Y si escribo renderizando directamente se baja todo
el brillo de la escena
Vicengetorix   |SAdministrator |07-02-2012 19:39:31
La libreria excluida, seguro que no es. Si hay un problema de compilacion,
compila o no, pero ya esta.
Tiene que ser algo que modifica FTGL o GLM. Podria
ser la iluminacion por lo que dices del brillo, ... con esos datos no se me
ocurre mas.
Skal   |79.109.213.xxx |16-05-2010 15:41:51
Ya es casualidad, justamente hace apenas unas semanas que estaba indagando sobre
el uso de estas librerias xd.

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

 


Banner
Spanish Chinese (Simplified) English French German Japanese Portuguese Russian