lunes, 16 de mayo de 2016

IMGUI

Este es un ejemplo de una GUI tradicional:
Button *btn = new Button("hola", width, height);
btn->addClickCallback(onBtnClick);
window->addButton(btn, x, y);

void onBtnClick(Button *btn)
{
}
En resumen, primero se crea un botón, después se agrega un callback, que va a ser ejecutado cuando el botón es presionado, y luego se lo agrega a una ventana, la cual va a dibujar todos sus hijos en la posición correcta.

Entra Modo inmediato

if (Button(State, "hola", X, Y, Width, Height))
{
 ...
}
Si, eso solo.

Ademas de la simpleza del uso (nada le gana a una llamada a una función) el API no retiene ningún puntero, no hay peligro de dangling pointers o contadores de referencia que no se limpiaron correctamente.

Otra ventaja de usar solo una función es el menor uso de memoria. Solo necesitamos guardar el estado del objeto que esta siendo utilizado en un momento dado, ya que solo se puede interactuar con un objeto a la vez.

En una GUI tradicional también se podría hacer esto último, pero como esta atado a un diseño orientado a objetos, seguramente el estado va a estar guardado en cada elemento.

Falacia del ejemplo trivial

Hacer un botón y un campo de texto es muy sencillo y todavía no tuve la oportunidad de hacer algo mas complejo, como una lista de objetos desplazables, por ejemplo.


Quizás, cuando llegue ese momento pueda comparar una GUI en modo inmediato vs una en modo "retenido".

jueves, 5 de mayo de 2016

TTF Rasterizer

Motivación

Programar un rasterizer de TTF es posiblemente una de las cosas mas inútiles que hay, debido a que existen varias soluciones listas para integrar en un engine.
Por otro lado, desde un punto de vista educativo, el aprendizaje obtenido es bastante sorprendente.

Documentación

El formato TrueType (posteriormente llamado OpenType) se puede encontrar en la página de Apple y de forma mas escueta en la de Microsoft.
Es realmente triste que estos documentos estén tan pobremente detallados, mas de una vez tuve que ver código de otros rasterizers para comprobar si estaba entendiendo bien la especificación.

Recuerdo que el mayor inconveniente que tuve es que el archivo esta en formato big-endian, y este detalle solo estaba mencionado en el documento de Microsoft.

La información en un archivo TTF esta dividida en tablas y sin ninguna duda la mas importante es la que contiene los glyphs, la representación visual de un carácter.
Otra tabla, por ejemplo, une el código de un carácter (Unicode) con su correspondiente glyph

Cada glyph es un conjunto de puntos ordenados, que al ser unidos forman el contorno de un símbolo.

Estos puntos pueden estar en la curva o fuera de la curva, estos últimos especifican puntos de control de una curva bezier. Uno esperaría entonces que siempre que haya un punto de control, que se encuentre en el medio de 2 puntos en curva, marcando el inicio y fin de la misma.
Bezier
Pero no siempre es así y a veces hay mas de un punto fuera de la curva, entonces uno se pregunta, ¿de dónde cuernos saca los puntos de inicio y fin?
Como estos puntos se pueden calcular, no necesitan estar explicitados en el glyph. Este otro "pequeño" detalle también es difícil de entender leyendo solo la documentación.

Las curvas de la e y la o son mas angostas de lo que deberían.

Pasitos de bebé

Lo primero que intenté hacer fue poder visualizar algo de la información del archivo TTF que quería parsear; ver que los valores obtenidos tuvieran algo de sentido y después poder dibujar algo que se pareciera a un carácter.

La imagen de abajo muestra un contorno usando los puntos de control como si fueran rectas y en la parte de arriba ya con las curvas bezier.


Finalmente, una vez que tuve el contorno de un carácter pude rasterizar ese glyph
Contorno y rasterizer.
De ninguna manera esta biblioteca esta completa, ni desde un punto de capacidades, velocidad ni robustez. Por lo pronto le falta
Ademas, hay una fina linea entre las capacidades que debería brindar un elemento de texto y no el rasterizer, entre otras:
  • Parsear códigos de control, como espacios, saltos de linea y tabulación
  • justificación de texto
  • Negrita e itálica

API

Desde el punto de vista del programador que va a dibujar texto solo hay que usar 2 funciones, una para cargar la TTF y otra para dibujar el texto en una posición con un tamaño y un color.

ttf_font *TtfFont = CreateTtf(game, "freesans.ttf");
...

DrawText(game, TtfTont, "Hola", Size, Pos, Black);

Como mencioné en mi post anterior, estoy alejándome de objetos, herencia y polimorfismo y es por eso que el API son solo funciones. Esto me abre la puerta a poder hacer mi propia IMGUI, que seguramente este escribiendo en en futuro cercano :)

martes, 3 de mayo de 2016

Bienvenidos

Desde hace un tiempo que estoy buscando otras maneras de programar inspiradas por los blogs de Casey muratori y charlas de Mike Acton.
Para poner a prueba estas teorias me inventé un proyecto testigo donde puedo jugar con estas ideas hasta convencerme que realmente funcionan y son mejores a las que estoy acostumbrado.

El juego/engine que estoy haciendo es un plataformero que por ahora tiene los siguientes features:
  • Hot reloading
  • Editor de niveles en juego
  • Colisiones en 2D estilo plataformero (rigid bodies)
  • Render en OpenGL con shaders
  • TTF Rasterizer
  • Guardado y cargado de 1 nivel
  • Asset reloading en run time (shaders y fonts)
  • Logs a archivos
  • Build en Linux y Windows
Modo edición, probando el rasterizer de fuentes
El juego esta programado íntegramente en C y C++, aunque lo único que uso de C++ es la sobrecarga de operadores y funciones. No uso objetos, ni polimorfismo, ni templates, ni la std e intento usar lo menos posible las bibliotecas estándar de C. Ahora sigo usando SDL, pero estoy de a poco reemplazándola por código propio.

No usar ninguna biblioteca me brinda una puerta a poder aprender muchas cosas tecnológicas que por la naturaleza de mi trabajo solo la puedo aprender en mi tiempo libre, como es el caso de un Rasterizer de TrueType Fonts.

En este blog voy a ir compartiendo experiencias que vayan surgiendo en esta búsqueda de una "mejor" forma de programar.