miércoles, 11 de mayo de 2016

Construyendo nuestra propia Game Engine usando OpenGL Parte 2 ~ Preparando el entorno

En el anterior post dije que deberíamos disponer de los headers glcorearb.h y demás. Bien pues olvidar eso. En este tuto os enseñaré como instalar un environment para empezar a desarrollar cosas más interesantes. Para ello usaremos OpenGL y GLU(las librerías básicas) más GLUT (librería adicional pero que casi también es básica). Dejo el link para descargarlas: http://www.mediafire.com/download/sal0nahpaw1ihgj/BlexyGL.rar. Extrae el rar y empieza un proyecto en visual studio de tipo visual c > empty. Ahora nos vamos al directorio C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC y en la carpeta bin debemos copiar el archivo del rar llamado glut32.dll, y en la carpeta lib copiamos todos los archivos .lib del rar.
 Una vez hecho esto, debemos incluir el path del header glut.h en Project > Properties > VC++ Directories > Include Directories, y aplicamos y aceptamos. Ahora incluye el archivo glut.h en un .cpp e incluye este código el cual por ahora no explicaré y ejecuta el programa, si te sale una taza rotando y los frames enhorabuena, puedes seguir con el tuto. Aquí el código:

#include <glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

//  Avoid showing up the console window
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

//  Initialization
void init();

//  Callback functions
void display(void);
void reshape(int w, int h);
void idle(void);

//  Support Functions
void centerOnScreen();

//  define the window position on screen
int window_x;
int window_y;

//  variables representing the window size
int window_width = 512;
int window_height = 512;

//  variable representing the window title
char *window_title = "OpenGL FPS";

//  Tells whether to display the window full screen or not
//  Press Alt + Esc to exit a full screen.
int full_screen = 0;

//  Pointer to a font style..
//  Fonts supported by GLUT are: GLUT_BITMAP_8_BY_13,
//  GLUT_BITMAP_9_BY_15, GLUT_BITMAP_TIMES_ROMAN_10,
//  GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10,
//  GLUT_BITMAP_HELVETICA_12, and GLUT_BITMAP_HELVETICA_18.
GLvoid *font_style = GLUT_BITMAP_TIMES_ROMAN_24;

//  printf prints to file. printw prints to window
void printw(float x, float y, float z, char* format, ...);

//  The number of frames
int frameCount = 0;

//  Number of frames per second
float fps = 0;

//  currentTime - previousTime is the time elapsed
//  between every call of the Idle function
int currentTime = 0, previousTime = 0;

//  variables used for rotation
GLfloat rotate_x, rotate_y, rotate_z;

// Methods
void animateObject();
void drawObject();
void calculateFPS();
void drawFPS();

//-------------------------------------------------------------------------
//  Set OpenGL program initial state.
//-------------------------------------------------------------------------
void init()
{
//  Set the frame buffer clear color to black.
glClearColor(0.0, 0.0, 0.0, 0.0);
}

//-------------------------------------------------------------------------
//  This function is passed to glutDisplayFunc in order to display
// OpenGL contents on the window.
//-------------------------------------------------------------------------
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);

drawObject();
drawFPS();

glutSwapBuffers();
}

//-------------------------------------------------------------------------
//  Draws the object
//-------------------------------------------------------------------------
void drawObject()
{
//  Draw a wire teapot
glColor3f(0, 0, 1);
glutWireTeapot(0.5);
}

//-------------------------------------------------------------------------
//  Draw FPS
//-------------------------------------------------------------------------
void drawFPS()
{
//  Load the identity matrix so that FPS string being drawn
//  won't get animates
glLoadIdentity();

//  Print the FPS to the window
printw(-0.9, -0.9, 0, "FPS: %4.2f", fps);
}

//-------------------------------------------------------------------------
//  This function is called when OpenGL\GLUT is not working on
//  something else... It is mainly used for animation...
//
//  It's like the timers but time intervals are dependent on how busy
//  the app is, instead of having a constant value set by the user.
//-------------------------------------------------------------------------
void idle(void)
{
//  Animate the object
animateObject();

//  Calculate FPS
calculateFPS();

//  Call display function (draw the current frame)
glutPostRedisplay();
}

//-------------------------------------------------------------------------
//  Animate the object
//-------------------------------------------------------------------------
void animateObject()
{
//  Rotate the object around the y axis
rotate_y += 1;

if (rotate_y > 360)
rotate_y = 0;

//  Set rotation
glRotatef(rotate_x, 1, 0, 0);
glRotatef(rotate_y, 0, 1, 0);
glRotatef(rotate_z, 0, 0, 1);
}

//-------------------------------------------------------------------------
// Calculates the frames per second
//-------------------------------------------------------------------------
void calculateFPS()
{
//  Increase frame count
frameCount++;

//  Get the number of milliseconds since glutInit called
//  (or first call to glutGet(GLUT ELAPSED TIME)).
currentTime = glutGet(GLUT_ELAPSED_TIME);

//  Calculate time passed
int timeInterval = currentTime - previousTime;

if (timeInterval > 1000)
{
//  calculate the number of frames per second
fps = frameCount / (timeInterval / 1000.0f);

//  Set time
previousTime = currentTime;

//  Reset frame count
frameCount = 0;
}
}

//-------------------------------------------------------------------------
//  This function is passed to the glutReshapeFunc and is called
//  whenever the window is resized.
//-------------------------------------------------------------------------
void reshape(int w, int h)
{
//  Stay updated with the window width and height
window_width = w;
window_height = h;

//  Reset viewport
glViewport(0, 0, window_width, window_height);
}

//-------------------------------------------------------------------------
//  This function sets the window x and y coordinates
//  such that the window becomes centered
//-------------------------------------------------------------------------
void centerOnScreen()
{
window_x = (glutGet(GLUT_SCREEN_WIDTH) - window_width) / 2;
window_y = (glutGet(GLUT_SCREEN_HEIGHT) - window_height) / 2;
}

//-------------------------------------------------------------------------
//  Draws a string at the specified coordinates.
//-------------------------------------------------------------------------
void printw(float x, float y, float z, char* format, ...)
{
va_list args; //  Variable argument list
int len; // String length
int i; //  Iterator
char * text; // Text

//  Initialize a variable argument list
va_start(args, format);

//  Return the number of characters in the string referenced the list of arguments.
//  _vscprintf doesn't count terminating '\0' (that's why +1)
len = _vscprintf(format, args) + 1;

//  Allocate memory for a string of the specified size
text = (char *)malloc(len * sizeof(char));

//  Write formatted output using a pointer to the list of arguments
vsprintf_s(text, len, format, args);

//  End using variable argument list
va_end(args);

//  Specify the raster position for pixel operations.
glRasterPos3f(x, y, z);

//  Draw the characters one by one
for (i = 0; text[i] != '\0'; i++)
glutBitmapCharacter(font_style, text[i]);

//  Free the allocated memory for the string
free(text);
}

//-------------------------------------------------------------------------
//  Program Main method.
//-------------------------------------------------------------------------
void main(int argc, char **argv)
{
//  Connect to the windowing system
glutInit(&argc, argv);

//  create a window with the specified dimensions
glutInitWindowSize(window_width, window_height);

//  Set the window x and y coordinates such that the
//  window becomes centered
centerOnScreen();

//  Position Window
glutInitWindowPosition(window_x, window_y);

//  Set Display mode
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);

//  Create window with the specified title
glutCreateWindow(window_title);

//  View in full screen if the full_screen flag is on
if (full_screen)
glutFullScreen();

//  Set OpenGL program initial state.
init();

// Set the callback functions
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutIdleFunc(idle);

//  Start GLUT event processing loop
glutMainLoop();
}

lunes, 9 de mayo de 2016

Construyendo nuestra propia Game Engine usando OpenGL Parte 1 ~ Estructura

Debo aclarar que sí, estoy completamente loco, así que ahórrate los comentarios tipo "Hacer una game engine tan decente como la que planteas es imposible en solitario", porque solo me darás motivos de reírme de tu cordura.

Dicho esto, estos meses que he estado más inactivo en el grupo de Facebook de Unity3D en Español han sido porque he estado estudiando arduamente para encajar todas las piezas de estos puzzles llamados motores de videojuegos, usando OpenGL y el poderío de C++. Y no solo estudiando sobre como encajar estas piezas con tan solo descripciones detalladas de lo que se busca, si no desarrollando estructuras de código. Pero claro... Game Engines hay muchas y seguramente mejor que mi estructura. Entonces, ¿por qué hago esto? Por ti, lector empedernido por los sistemas 3D, la diferencia es que, aunque sí, quizá haya muchas engines Open Source, no lo explican del tu a tu sobre su mismo desarrollo estilo blog(al menos no que yo conozca, y mira que me he masticado repositorios del Github para poder encajar las piezas). Con esto dicho, queda claro que lo que busco no es competir contra las Engines calidad AAA, ni siquiera pretendo realizar un producto que de una u otra manera sea distribuido para uso comercial. Solo quiero demostrar a los mas curiosos como se hace una buena base de una game engine desde usar tan solo lo mas necesario(las librerías de OpenGL) y qu emejor manera que realizarla uno mismo y encima escribiendo un blog en el cual a parte de enseñar nuevas funciones que engordarán al framebuffer más y más, se muestra como la fina cordura del sujeto de pruebas va decayendo hasta convertirse en un GLdisable. Empecemos:

Primero y antes que nada un poquito de música motivadora para el que se le haga mas ameno con ella.
 ______________________________________________________________________ ______________________________________________________________________

Bien, después de esto, lo siguiente que necesito que hagas son varias cosas:

1.- Si tienes conocimientos previos de haber usado alguna otra Engine, desapréndelos.
2.- Tener conocimientos de nivel medio de C++.
3.- Saber realizar operaciones matemáticas de nivel básico y medio.
3.- Saber por encima la estructura de la GPU y tu computadora en general.
4.- Saber que OpenGL es solo una interfaz para no tener que escribir una versión de los drivers para cada vendedor de GPU's.
5.- Saber que son los drivers.
6.- Tener en tu poder la última versión de los archivos glcorearb.h, glext.h, wglext.h y glxext.h e incluirlos en un header dentro de tu proyecto al cual yo llamaré por ejemplo gllib.h.
7.- Disponer de la última versión de drivers de tu GPU y tener en tu poder la versión 4.5 del OpenGL(toda tarjeta gráfica fabricada desde finales de 2014 debería tener dicha tecnología).
8.- Tener descargada la última Update de Visual Stuido Community 2015 y empezar un empty project en c++ en el cual incluirás la librería gllib.h.

Si no dispones de estos atributos, no sigas con este tutorial.