lunes, 29 de agosto de 2011

GameEngine: Capitulo 28 . Usando XML

Hola a todos,

Bienvenidos a un nuevo capitulo de como crear tu propio game engine y no morir en el intento.

Antes que nada disculpad por la tardanza pero este capitulo ha costado un poco más de lo que esperaba.

El capitulo de hoy tratará sobre el uso de XML tal y como se explicó en el capitulo 9 del curso de programación:

Los cambios implementados han sido cambiar todo los archivos de configuración en txt por xml. La forma estandar de implementar estos cambios se han realizado de la siguiente manera ( esto es solo un ejemplo, no cabrian todos los cambios implementados ):


void GUIManager::Init(int* argc, char* argv[])
{
    char id[256];    //Cadena donde guardaremos el identificador de cada recurso   
    char ruta[256];    //Cadena donde guardaremos la ruta de cada recurso
       
    TiXmlDocument doc( "Data\\LoadGUI.xml");

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

    num_gui = 0;

    if ( !doc.LoadFile() )
    {
        printf( "Could not load test file 'Data\\LoadGUI.xml'. Error='%s'. Exiting.\n", doc.ErrorDesc() );
        exit( 1 );
    }

    TiXmlElement *root = doc.RootElement();
    for(TiXmlElement* element = root->FirstChildElement(); element; element = element->NextSiblingElement())
    {
        //Leemos el elemento
        sprintf(id,"%s", element->FirstChild("ID")->FirstChild()->Value());            //tomamos el ID
        sprintf(ruta,"%s",element->FirstChild("ROUTE")->FirstChild()->Value());        //tomamos la RUTA   
       
        LoadGUI(id,ruta);                //Cargamos el recurso
    }
}

LoadGUI
void GUIManager::LoadGUI( char id[], char ruta[] )
{
    char type[32],graf[32],var[32],max[32];
    int xi,yi,xf,yf;

    sprintf(loaded[num_gui].id,"%s",id);
    loaded[num_gui].num_item = 0;

    TiXmlDocument doc( ruta );

    cout << "Cargamos GUI: " << ruta <<"\n";

    if ( !doc.LoadFile() )
    {
        printf( "Could not load GUI FILE'. Error='%s'. Exiting.\n", doc.ErrorDesc() );
        exit( 1 );
    }

    TiXmlElement *root = doc.RootElement();
    for(TiXmlElement* element = root->FirstChildElement(); element; element = element->NextSiblingElement())
    {
        //Leemos el elemento
        sprintf(type,"%s", element->FirstChild("TYPE")->FirstChild()->Value());            //tomamos el ID

        sprintf(graf,"%s", element->FirstChild("GRAF")->FirstChild()->Value());   
        xi = atoi( element->FirstChild("xi")->FirstChild()->Value() );   
        yi = atoi( element->FirstChild("yi")->FirstChild()->Value() );   
        xf = atoi( element->FirstChild("xf")->FirstChild()->Value() );   
        yf = atoi( element->FirstChild("yf")->FirstChild()->Value() );

        if(!strcmp(type,"BAR"))
        {
            sprintf(var,"%s", element->FirstChild("VAR")->FirstChild()->Value());   
            sprintf(max,"%s", element->FirstChild("MAX")->FirstChild()->Value());   

            sprintf(loaded[num_gui].lista[loaded[num_gui].num_item].actual ,"%s" , var );
            sprintf(loaded[num_gui].lista[loaded[num_gui].num_item].max ,"%s" , max );
        }   

        //Introducimos la información de este item
        sprintf(loaded[num_gui].lista[loaded[num_gui].num_item].id ,"%s" , type );
        sprintf(loaded[num_gui].lista[loaded[num_gui].num_item].graf ,"%s" , graf );
        loaded[num_gui].lista[loaded[num_gui].num_item].xi = xi;
        loaded[num_gui].lista[loaded[num_gui].num_item].yi = yi;
        loaded[num_gui].lista[loaded[num_gui].num_item].xf = xf;
        loaded[num_gui].lista[loaded[num_gui].num_item].yf = yf;

        loaded[num_gui].num_item++;
    }
    num_gui++;
}

Los archivos de carga en XML son de este estilo:
<list>
    <element>
        <ID>PLAY</ID>
        <ROUTE>Data/GUI/play.xml</ROUTE>
    </element>
</list>

Y los archivos de configuración pueden ser de este estilo:
<list>
    <element>
        <TYPE>GRAF</TYPE>
        <GRAF>GUI</GRAF>
        <xi>0</xi>
        <yi>0</yi>
        <xf>640</xf>
        <yf>100</yf>
    </element>

    <element>
        <TYPE>BAR</TYPE>
        <GRAF>GUIAMMO </GRAF>
        <xi>100</xi>
        <yi>10</yi>
        <xf>500</xf>
        <yf>20</yf>
        <VAR>AMMO</VAR>
        <MAX>MAXAMMO</MAX>
    </element>

    <element>
        <TYPE>BAR</TYPE>
        <GRAF>GUILIFE </GRAF>
        <xi>100</xi>
        <yi>40</yi>
        <xf>500</xf>
        <yf>50</yf>
        <VAR>LIFE</VAR>
        <MAX>MAXLIFE</MAX>
    </element>
</list>

En breve os subiré el código a la zona de descargas....

Espero que os haya gustado y hayais aprendido.

Nos vemos

domingo, 28 de agosto de 2011

Relato: Ambientación de videojuego. Capitulo 7

Hola a todos,

He aquí un nuevo elemento de la historia.... ya os aviso que me estoy quedando sin ideas :D, no me iria mal que hicierais vuestra aportación... (si cuela , cuela :D) . Espero que os guste:

"
Extracto del manual de formación para oficiales del imperio. 
Decimonovena edición.  
Año 3223.

Capitulo 24. Mutantes.
El año 3205 marcó el inicio de la era mutante.Azok y sus vampiros colonizaron la primera colonia mutante a la que le siguieron muchas mas: hombres lobo, immortales, psiquicos, magos, videntes, etc....
Hay dos cosas a tener en cuenta de los mutantes:
1. Pueden pasar desapercibidos entre humanos (en el peor de los casos podrían ser considerados raros, pero humanos)
2. La leyenda que los envuelve tiene una parte de razón y una parte exageración.
Las comunidades mutantes se cuentan a cientos pero las mas peligrosas y a tener en cuenta son las siguientes:

HombresLobo: Homo Sapiens Lupinus. Originarios del continente Asiático. Separados de la rama humana hará unos 100.000 años.
Verdades: Grandes, peludos y de mandíbula grande y alargada. Grandes cazadores de vampiros. Buenos cazadores de cualquier presa en cualquier entorno. Excelentes cazarecompensas.
Mentiras: En contra de lo que dice la tradición popular, no se transforman, siempre tienen el mismo aspecto (parecen humanos).  La luna no les afecta para nada. Se les puede matar con cualquier arma, no hace falta que sea de plata.

Vampiros: Homo Sapiens Cruentus. Originarios de las selvas de Asia. Separados del resto de humanos hace 40.000 años.Perseguidos durante milenios por el depredador de la zona (el hombre lobo) evolucionarion hacia una especie nocturna de pequeño tamaño y grandes incisivos que se alimentaba de la sangre de sus presas de forma rápida para que les diera el tiempo para escapar de los hombres lobo.(no tenían tiempo para engullir la carne)
Verdades: Necesitan algo de sangre en su dieta pero no es vital de necesidad. Saben esconderse muy bien y son excelentes espias.
Mentiras: Pueden comer cualquier cosa.  El Sol no los mata (aunque no les gusta).

Psiquicos y magos: Homo Sapiens Magicis: No son una raza como tal si no una desviación del homo sapiens normal. Se basan en una extrema empatia con los dioses emergentes.Tienen todo un abanico de poderes y limitaciones. Su poder se basa en la comunidad, si están aislados carecen de poder. No se sabe hasta que punto las leyendas son verdad o mentira.


"

Nos vemos, a ver si os animais a colaborar y ampliais la historia...

sábado, 27 de agosto de 2011

Math Engine : Capitulo 4. Clase LPData

Capítulo perteneciente al math engine LordPakus

Hola a todos,

Bienvenidos a un nuevo capitulo de como crear tu propio math engine. El capitulo de hoy es la presentación del tercer (y si no estoy equivocado, último) tipo de dato que se usará en el math engine: el LPData es decir, series de datos. Con series de datos se representarán tanto muestreo de señales (con lo que se podrán realizar FFT  y otras transformaciones, de audio sobretodo) como series de datos estadísticos o muestreo de funciones. (maximos, mínimos, medias, rectas de regresión, integración, derivación,etc....)

Por ahora solo os pongo la estructura, que funciones se irán implementando lo dejo para vosotros para que me lo vayais diciendo a medida que las vayais necesitando.

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


/**************************************************************************************************/
// LPData.h
/**************************************************************************************************/

#ifndef __LPData__
#define __LPData__

class LPData
{
public:
    LPData();
    ~LPData();

    //Funciones anexas.
    void Set(int t, float* vector);                //Funcion para inicializar un vector de n elementos
    void Delete();                                //Función para limpiar la variable vector
    void print();                                //Función para imprimir el vector por la consola
    void random(int t,float min, float max);    //Función para rellenar de random un vector de t posiciones

    float Max();
    float Min();
    float Average();

    //OPERACIONES DE AMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPData vector);           
    void operator -=(LPData vector);   
   
    //Operaciones con escalares ( sumar , restar , etc ... todo un vector por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);


    //OPERACIONES QUE DEVUELVEN ALGO (NO ACUMULADORES: +,-,*, etc...)

public:
    int n;         //número de elementos del vector
    float *v;    //puntero a memoria dinámica
};

#endif

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

#include "LPData.h"
#include <math.h>
#include <iostream>                //Usada para imprimir por consola

using namespace std;

//Constructor
LPData::LPData()
{
    n = 0;
    v = NULL;
}

//Destructor
LPData::~LPData()
{
    //if (n)
    //{
    //    if( v != NULL )
    //        free(v);
    //}
}

//Funcion para inicializar una serie de datos de n elementos
void LPData::Set(int t, float* vector)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    //Copiamos el vector externo en el vector interno
    memcpy(v,vector,sizeof(float) * n );
}

//Función para limpiar la variable vector
void LPData::Delete()
{
    n=0;
    free(v);
}

//Imprimimos el valor del vector
void LPData::print()
{
    for(int i = 0 ; i < n ; ++i)
    {
        cout << (float) v[i] << " ";
    }
}

void LPData::random(int t, float min, float max)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    for(int i = 0 ; i < n ; ++i)
        v[i] = (float)(rand()%(int)(max*1000 - min*1000))/1000 + min;

}

float LPData::Max()
{
    float max;

    max = v[0];
    for(int i = 1 ; i < n ; ++i)
        if( v[i] > max )
            max = v[i];

    return max;
}

float LPData::Min()
{
    float min;

    min = v[0];
    for(int i = 1 ; i < n ; ++i)
        if( v[i] < min )
            min = v[i];

    return min;
}

float LPData::Average()
{
    float ave = 0;

    for(int i = 0 ; i < n ; ++i)
        ave += v[i];
   
    ave /= n;

    return ave;
}

//Sobrecarga operador +=
void LPData::operator +=(LPData vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE SERIES DE DATOS\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] += vector.v[i];
   
}

//Sobrecarga operador -=
void LPData::operator -=(LPData vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE SERIES DE DATOS\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] -= vector.v[i];
}

//Sobrecarga operador +=
void LPData::operator +=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] += scal;
}

//Sobrecarga operador -=
void LPData::operator -=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] -= scal;
}

//Sobrecarga operador *=
void LPData::operator *=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] *= scal;
   
}

//Sobrecarga operador /=
void LPData::operator /=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] /= scal;
   
}




Salvo fallo de planificación a partir de ahora solo es ir "rellenando" la estructura que tenemos con nuevas funcionalidades e implementaciones más eficientes.

Espero que os haya gustado,

Nos vemos

viernes, 26 de agosto de 2011

Math Engine : Capitulo 3. Clase LPMatrix

Capítulo perteneciente al math engine LordPakus

Hola a todos,

Bienvenidos a un nuevo capitulo de como crear un math engine. El capitulo de hoy se encargará de mostrar el tipo de variable que seguramente más se usará en las aplicaciones de cálculo, las matrices.

Dado que es totalmente imposible poner todas las funciones que afectan a matrices (solamente con factorizaciones podria llenar miles de lineas de código) este capitulo lo dejaré totalmente abierto (como el del LPVector) para que me vayais proponiendo las funciones que querais que introduzcamos en este módulo.

No obstante, si quereis ir probando código aquí os dejo lo que he implementado hasta el momento:
LPMatrix.h:
/**************************************************************************************************/
//        Código creado por F.Bordas (LordPakus) como ejemplo de creación de un math engine
//        para el blog LordPakus (http://lordpakus.blogspot.com/).
//        Prohibida la distribución fuera de este blog sin el permiso expreso del autor
/**************************************************************************************************/


/**************************************************************************************************/
// LPMatrix.h
/**************************************************************************************************/

#ifndef __LPMatrix__
#define __LPMatrix__

class LPMatrix
{
public:
    LPMatrix();
    ~LPMatrix();

    //Funciones anexas.
    void Set(int t, int k, float* vector); //Funcion para inicializar una matriz de t*k elementos
    void Delete();                    //Función para limpiar la variable matriz
    void print();                    //Función para imprimir la matriz por la consola


    //OPERACIONES DE ACUMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPMatrix matrix);           
    void operator -=(LPMatrix matrix);           
   
    //Operaciones con escalares ( sumar , restar , etc ... todo una matriz por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);

public:
    int m,n;      //dimensiones de la matriz
    float **p;    //puntero a memoria dinámica para matrices
};

#endif

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

#include "LPMatrix.h"
#include <math.h>
#include <iostream>                //Usada para imprimir por consola

using namespace std;

//Constructor
LPMatrix::LPMatrix()
{
    n = 0;
    m = 0;
    p = NULL;
}

//Destructor
LPMatrix::~LPMatrix()
{

}

//Funciones anexas.
void LPMatrix::Set(int t, int k, float* vector) //Funcion para inicializar una matriz de t*k elementos
{
    //Inicializamos el número de elementos del vector
    n = t;
    m = k;

    //Creamos un vector de memoria dinámica donde almacenaremos los punteros subsiguientes.
    p = (float**) malloc( sizeof(float *) * n );
   
    //Creamos cada una de las filas o columnas (como queramos verlo) de la matriz
    for(int i = 0 ; i < n ; ++i )
        p[i] = (float*) malloc( sizeof(float) * m );
   
    //Copiamos el vector externo en el vector interno
    for(int i = 0 ; i < n*m ; ++i )
        p[i/m][i%m] = vector[i];
   
}

void LPMatrix::Delete()                    //Función para limpiar la variable matriz
{
    for(int i = 0 ; i < n ; ++i )
        free(p[i]);

    n=0;
    m=0;

    free(p);
}

void LPMatrix::print()                    //Función para imprimir la matriz por la consola
{
    for(int i = 0 ; i < n*m ; ++i)
    {
        if(!(i%m))
            cout << "\n";

        cout << (float) p[i/m][i%m] << " ";
    }
}


//OPERACIONES DE ACUMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
//Operaciones vectoriales
void LPMatrix::operator +=(LPMatrix matrix)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if ( (n != matrix.n) || (m != matrix.m) )
    {
        cout << "ERROR DIMENSIONAL ENTRE MATRICES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] += matrix.p[i][j];
}

void LPMatrix::operator -=(LPMatrix matrix)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if ( (n != matrix.n) || (m != matrix.m) )
    {
        cout << "ERROR DIMENSIONAL ENTRE MATRICES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] -= matrix.p[i][j];
}

   
//Operaciones con escalares ( sumar , restar , etc ... todo una matriz por un escalar)
void LPMatrix::operator +=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] += scal;
}

void LPMatrix::operator -=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] -= scal;
}

void LPMatrix::operator *=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] *= scal;
}

void LPMatrix::operator /=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        for(int j = 0 ; j < m ; ++j)
            p[i][j] /= scal;
}

main.cpp
int main (int argc, char* argv[])
{
    LPMatrix m1,m2;
    float mat1[15] = { 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f , 1.0f ,1.1f ,1.5f };
    float mat2[15] = { 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f , 3.0f ,4.1f ,0.5f };
   
    m1.Set(3,5,mat1);
    m2.Set(3,5,mat2);

    cout << "Matriz 1:";
    m1.print();
    cout << "\n";

    cout << "Matriz 2:";
    m2.print();
    cout << "\n";   

   
    cout << "Suma de matrices:";
    m2 += m1;
    m2.print();
    cout << "\n";   

    //Eliminamos las matrices
    m1.Delete();
    m2.Delete();

    system("PAUSE");
}

Espero que os haya servido y os haya gustado.

Nos vemos

Relato: Ambientación de videojuego. Capitulo 6

Hola a todos,

Aquí os dejo otro capitulo de la ambientación de mi videojuego, recordad que si teneis ideas para mejorar la trama podeis hacermelas llegar.

Espero que os guste.

"
 Transcripción de la reunión entre el general del imperio Arakanian y el autoproclomado rey de la colonia pirata de Rufus II , conocido como Azok.
Fecha de la grabación: año 3205 (dia y mes indeterminado)
Arakanian: Quiero que sepa que toda esta conversación está siendo grabada.
Azok: Lo se. Me parece bien.
Arakanian: Antes que nada me gustaria dejar las cosas claras. En el comunicado que envió a toda la flota decia, dejeme citarle textualmente, que : "...Yo, Azok, rey de los vampiros, proclamo que el planeta Rufus II será conocido a partir de ahora como Fangania y será considerado como la tierra patria para la nación vampira...". Realmente se cree esas tonterias que salen de su boca o solo son para impresionar al populacho??
Azok: Que parte no se cree?? La de que soy el rey escogido por los vampiros o que existe una nación vampira?
Arakanian: Ambos sabemos que el cuentecito eso de los vampiros es una manera bastante burda de hacer que nadie vaya a molestarlos. Es la manera fácil para que un grupo de piratas, borrachos prostitutas y malhechores en general queden fuera la ley y nadie se atreva a husmear en su asuntos....
Azok: Si , tiene toda la razón, todo es un cuento. Hablando de cuentos... se ha planteado nunca por que una figura mitológica como puede ser un dragón se repite en tantas culturas diferentes y tan alejadas?? Es curioso que dos culturas tan diferentes como las antiguas asiática y europea compartan el mito de los dragones con parecidos en la descripción tan impresionantes...ya ni hablemos de culturas aborigenes de America y sus repitles alados...a veces, solo se me ocurre como solución que los dragones existieron de verdad y que todas esas culturas los vieron y los describieron a su manera...
Arakanian: Si claro, dragones, duendes, gnomos y gigantes.... que sepa que se están arriesgando a una guerra en que solo podrán perder....respetese un poco a usted mismo y a mi tiempo y dejese de tonterias...
Azok: Arriesgese a una guerra con la nación vampira y descubrirá que hay varios mitos y leyendas que son bastante verdaderos, no estamos solos, y usted eso ya lo sospechaba, por eso está aqui en vez de haber atacado sin más.
Arakanian: Claro claro, no se en que estaria pensando, quien está con ustedes? HombresLobo? No Muertos?? Espectros y fantasmas??
Azok: Aparte de ellos, Aleph, nuestro Dios. No se sorprenda, solo somos 50.000 y ya hemos tenido emergencias conscientes de nuestro Dios, a ustedes les hizo falta 10.000 millones de habitantes para obtener las primeras emergencias....cada especie funciona de manera diferente...
Arakanian: De que narices está hablando? Me está hablando como si no se considerara humano...
Azok: Me sorprende que despues de 1000 años aún  hayan entendido el funcionamiento del "orbis insanit", ya lo entenderis cuando esteis preparados y si, evidentemente, no nos consideramos humanos por que no lo somos... Geneticamente hablando al menos , no lo somos. Nos separamos de la raza humana hará unos 40.000 años aunque hemos tenido algún que otro hibrido y procesos evolutivos paralelos......
Arkanian:  dejese ya de chachara, me aburre. Que sepa que apartir de ahora su "nación" está en guerra con el imperio terraqueo. Atengase a las consecuencias.
Azok: Llevo 2000 años ateniendome a las consecuencias....
*EOF*
"

jueves, 25 de agosto de 2011

Relato: Ambientación de videojuego. Capitulo 5

Aquí os dejo otro capitulo de la ambientación de videojuego que tengo en mente, recordad que si teneis nuevas ideas o propuestas me las podeis hacer llegar sin problemas y las iré integrando a la trama.

"
Informe de transcripción de conversación entre el preso FV-6544 (antes conocido como Dr. Rusfert) y el agente del CCH  John R. Smith Jr.
Fechada a dia 7 de mayo de 2250.

[...]
Doctor:....como hormiguitas... igualito que hormiguitas....
Agente: Por favor, Doctor, centrese, estoy aquí para que nos ayudemos mutuamente....no para que me suelte una retahila de sandeces sin sentido....
Doctor: Sandeces sin sentido?? Cuantas neuronas son necesarias para formar un pensamiento?? Cuantas homigas son necesarias para tener un hormiguero?? Cuantos creyentes ha de tener un Dios para ser Dios? No... no,amigo mio, no son sandeces.....
Agente: Por favor, doctor, estoy aquí para que me explique lo que ha descubierto de la "orbis insanit". Me dicen sus carceleros que en sus momentos de cordura parece que tenga respuestas para varias de nuestras preguntas....
Doctor: Depende... diez mil millones?, mil millones? cien mil?, depende de cada uno de los elementos y de su empatia con el resto....
Agente: DOCTOR!!No está en la universidad, ni yo soy uno de sus alumnos, REACCIONE. Está en la carcel siquiatrica y si no me ayuda estará aquí por mucho tiempo....
Doctor: Una hormiga no es un hormigero, una neurona no es un cerebro , un hombre no es un Dios.....
Agente: Acaso muchos hombres hacen un Dios? No se ni para que le sigo el juego....
Doctor: miles de átomos son una celula, miles de celulas un organismo....por que no miles de organismos un Dios?
Agente: Suponiendo que eso fuera verdad.... que tendria que ver con la enfermedad??
Doctor: Las celulas del cerebro se especializan... las hormigas se reparten los trabajos....
Agente. y?
Doctor: ....como hormiguitas... igualito que hormiguitas....
Agente: Perfecto, ya ha vuelto ha empezar.... Carcelero, lleveselo, no creo que podamos hacer nada por el.

*EOF*
"

Nos vemos

miércoles, 24 de agosto de 2011

Math Engine : Capitulo 2. Clase LPVector

Capítulo perteneciente al math engine LordPakus

Hola a todos,

Bienvenidos a un nuevo capitulo del math engine.

El capitulo de hoy trata sobre la clase LPVector que es la que se encarga de almacenar un vector de cualquier tamaño y poder opera con el.

La lista de operaciones que he considerado imprescindibles son las siguientes, todas las que querais que pongamos adicionales las ponemos... a la que tengamos una lista estable de operaciones nos dedicaremos a optimizarlas con SSE.:
//Funciones anexas.
    void Set(int t, float* vector); //Funcion para inicializar un vector de n elementos
    void Delete();                    //Función para limpiar la variable vector
    void print();                    //Función para imprimir el vector por la consola


    //OPERACIONES DE AMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPVector vector);           
    void operator -=(LPVector vector);           
    void sqrt_vec();                        //Calculamos la raíz cuadrada de todo los elementos del vector
    void normalize();                        //Normaliza el vector (hace que el módulo sea 1)

    //Operaciones con escalares ( sumar , restar , etc ... todo un vector por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);


    //OPERACIONES QUE DEVUELVEN ALGO (NO ACUMULADORES: +,-,*, etc...)
    //Operaciones vectoriales que devuelven escalares
    float operator ^(LPVector vector);            //Operador producto escalar.
    float module();                                //Calcula el módulo del vector

El código se organiza en dos archivos LPVector (.cpp y .h) junto con los cambios del main para demostrar que funcionan:

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

/**************************************************************************************************/
// main.cpp : Main de nuestro MathEngine
/**************************************************************************************************/

//Include del Math engine
#include "LPMath.h"

//Delete console
//#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

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

using namespace std;

int main (int argc, char* argv[])
{
    LPVector v1,v2;
    float vec1[4] = { 1.0f ,1.1f ,1.5f ,2.5f };
    float vec2[4] = { 3.0f ,4.1f ,0.5f ,0.4f };

    float n;

    //Creamos los vectores
    v1.Set(4,vec1);
    v2.Set(4,vec2);

    cout << "Vector 1: ";
    v1.print();
    cout << "\n";

    cout << "Vector 2: ";
    v2.print();
    cout << "\n";
   
    n = v1^v2;
    cout << "Producto escalar: " <<    n << "\n";

    n = v1.module();
    cout << "Modulo vector 1: " <<    n << "\n";

    n = v2.module();
    cout << "Modulo vector 2: " <<    n << "\n";

    v1.normalize();
    n = v1.module();
    cout << "Modulo vector 1 normalizado: " <<    n << "\n";

    v2.normalize();
    n = v2.module();
    cout << "Modulo vector 2 normalizado: " <<    n << "\n";

    v2 += v1;
    cout << "Suma de vectores: ";
    v2.print();
    cout << "\n";

    n = v2.module();
    cout << "Modulo de suma de vectores: " <<    n << "\n";

    v2.normalize();
    n = v2.module();
    cout << "Modulo de suma de vectores normalizado: " <<    n << "\n";


    //Eliminamos los vectores
    v1.Delete();
    v2.Delete();

    system("PAUSE");
}

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

#include "LPVector.h"
#include <math.h>
#include <iostream>                //Usada para imprimir por consola

using namespace std;

//Constructor
LPVector::LPVector()
{
    n = 0;
    v = NULL;
}

//Destructor
LPVector::~LPVector()
{
    //if (n)
    //{
    //    if( v != NULL )
    //        free(v);
    //}
}

//Funcion para inicializar un vector de n elementos
void LPVector::Set(int t, float* vector)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    //Copiamos el vector externo en el vector interno
    memcpy(v,vector,sizeof(float) * n );
}

//Función para limpiar la variable vector
void LPVector::Delete()
{
    n=0;
    free(v);
}

//Imprimimos el valor del vector
void LPVector::print()
{
    for(int i = 0 ; i < n ; ++i)
    {
        cout << (float) v[i] << " ";
    }
}

void LPVector::random(int t, float min, float max)
{
    n = t; //Inicializamos el número de elementos del vector

    //Creamos un vector de memoria dinámica.
    v = (float*) malloc( sizeof(float) * n );

    for(int i = 0 ; i < n ; ++i)
        v[i] = (float)(rand()%(int)(max*1000 - min*1000))/1000 + min;

}

void LPVector::cross(LPVector vector)
{
    //POR IMPLEMENTAR
}

void LPVector::sqrt_vec()
{
    for(int i = 0 ; i < n ; ++i)
        v[i] = sqrt(v[i]);
}

void LPVector::normalize()
{
    //Dividimos cada componente por el módulo
    *this /= this->module();
}

//Sobrecarga operador +=
void LPVector::operator +=(LPVector vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] += vector.v[i];
  
}

//Sobrecarga operador -=
void LPVector::operator -=(LPVector vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] -= vector.v[i];
}

//Sobrecarga operador +=
void LPVector::operator +=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] += scal;
}

//Sobrecarga operador -=
void LPVector::operator -=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] -= scal;
}

//Sobrecarga operador *=
void LPVector::operator *=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] *= scal;
  
}

//Sobrecarga operador /=
void LPVector::operator /=(float scal)
{
    for(int i = 0 ; i < n ; ++i)
        v[i] /= scal;
  
}
//Sobrecarga operador *= //Este operador es el que nos devolverá matrices como resultado del producto de una columna por un fila.
                         //Es decir, es el producto vectorial. Ya lo veremos a su momento
/*void LPVector::operator *=(LPVector vector)
{
    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return;
    }

    for(int i = 0 ; i < n ; ++i)
        v[i] += vector.v[i];
}
*/

//Sobrecarga operador ^ A partir de ahora se entederá que es el producto escalar.
float LPVector::operator ^(LPVector vector)
{
    float a = 0.0;

    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return 0.0;
    }

    for(int i = 0 ; i < n ; ++i)
        a += v[i] * vector.v[i];

    return a;
}

float LPVector::module()
{
    float a = 0.0;

    for(int i = 0 ; i < n ; ++i)
        a += v[i] * v[i];

    return sqrt(a);
}

float LPVector::distance(LPVector vector)
{
    float a = 0.0;
    float temp = 0.0;

    //Si las dimensiones son diferentes , tenemos un problema grave.
    if (n != vector.n)
    {
        cout << "ERROR DIMENSIONAL ENTRE VECTORES\n";
        return (-1.0);
    }

    for(int i = 0 ; i < n ; ++i)
    {
        temp = (v[i] - vector.v[i]);
        a+=temp*temp;
    }

    return sqrt(a);
}


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


/**************************************************************************************************/
// LPVector.h
/**************************************************************************************************/

#ifndef __LPVector__
#define __LPVector__

class LPVector
{
public:
    LPVector();
    ~LPVector();

    //Funciones anexas.
    void Set(int t, float* vector); //Funcion para inicializar un vector de n elementos
    void Delete();                    //Función para limpiar la variable vector
    void print();                    //Función para imprimir el vector por la consola
    void random(int t,float min, float max);                //Función para rellenar de random un vector de t posiciones

    //OPERACIONES DE AMULACIÓN DE RESULTADO (+=,-=,etc...), devuelven void como norma general
    //Operaciones vectoriales
    void operator +=(LPVector vector);          
    void operator -=(LPVector vector);  
    void cross(LPVector vector);            //Calculamos el producto vectorial (cross) POR HACER
    void sqrt_vec();                        //Calculamos la raíz cuadrada de todo los elementos del vector
    void normalize();                        //Normaliza el vector (hace que el módulo sea 1)
          

    //Operaciones con escalares ( sumar , restar , etc ... todo un vector por un escalar)
    void operator +=(float scal);
    void operator -=(float scal);
    void operator *=(float scal);
    void operator /=(float scal);


    //OPERACIONES QUE DEVUELVEN ALGO (NO ACUMULADORES: +,-,*, etc...)
    //Operaciones vectoriales que devuelven escalares
    float operator ^(LPVector vector);            //Operador producto escalar.
    float module();                                //Calcula el módulo del vector
    float distance(LPVector vector);

public:
    int n;         //número de elementos del vector
    float *v;    //puntero a memoria dinámica
};

#endif
Cualquier otra cosa que se os ocurra que le pongamos o cualquier fallo que veais, hacedmelo llegar

Espero que os haya gustado,

Nos vemos

EDIT:
27-08-2011:Se han añadido las funciones siguientes:
       - vector de randoms
       - distancia entre vectores
       - se deja a la espera el cross product (alguíen tiene el código y me lo quiere pasar??)

lunes, 22 de agosto de 2011

Math Engine : Capitulo 1. Sobrecarga de operadores

Capítulo perteneciente al math engine LordPakus

Hola a todos (disculpad por el parón, pero el niño me está "robando" algo más de tiempo del que me pensaba :D )

Bienvenidos al primer capitulo del Math Engine. En este capitulo no haremos mucho más que iniciar las bases del proyecto.

Conceptos fundamentales:
1. Todo el código se escribirá en un inicio en C/C++
2. Todo el código se intentará encapsular en clases (aunque en prinicipio no habrá gestores como en el game engine, usease el MathEngine será una colección de funcionalidades matematicas no un motor que correrá de fondo gestionandolo todo).
3. Todas las optimizaciones irán con ifdef (así se podrán hacer diferentes compilaciones optimizadas en función de la arquitectura en que se trabaje : tipo de ensamblador, tipo de procesador, etc...). Las optimizaciones mínimas que se tienen en cuenta ahora mismo son paralelización multicore y ensamblador vectorial SSE.
4. El código que gestione el usuario ha de ser mínimo , claro y sencillo.
5. Habrá 3 grandes bloques de trabajo: operaciones entre vectores, operaciones entre matrices y operaciones vector-matriz.
6. Las operaciones se irán implementado a medida que vosotros las vayais pidiendo así que si os interesa algún función en particular no dudeis en pedirla.


Dicha toda esta parrafada, expondremos el capitulo como tal.

Lo primero será crear el proyecto. Deberiais saber hacerlo pero si no teneis mucha memoria :D, podeis mirar como se creó el proyecto en el game engine (http://lordpakus.blogspot.com/2011/05/gameengine-capitulo-1.html).

Una vez tengais el proyecto creado cread los siguientes archivos y copiad el siguiente código. Vereis que no es más un código muy sencillo de operación entre vectores.

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

/**************************************************************************************************/
// main.cpp : Main de nuestro MathEngine
/**************************************************************************************************/

//Include del Math engine
#include "LPMath.h"

//Delete console
//#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

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

using namespace std;

int main (int argc, char* argv[])
{
    LPVector4 v1,v2;

    v1.Set(0.0f,1.0f,2.0f,3.0f);
    v2.Set(1.1f,1.2f,1.3f,1.4f);

    v1 += v2;

    cout << "Vector 1: ";
    v1.print();
    cout << "\n";

    cout << "Vector 2: ";
    v2.print();
}

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

//Lista de todos los includes necesarios para el math engine.

#include "LPVector4.h"


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


/**************************************************************************************************/
// LPVector.h
/**************************************************************************************************/

#ifndef __LPVector4__
#define __LPVector4__

class LPVector4
{
public:
    void Set(float x, float y , float z , float t); //Funcion para inicializar un vector de 4 elementos
    void print();                                    //Función para imprimir el vector por la consola

    void operator +=(const LPVector4 v);            //Sobrecarga de operador +=
   

public:
    float xv,yv,zv,tv;
};

#endif



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

#include "LPVector4.h"
#include <iostream>                //Usada para imprimir por consola

using namespace std;

void LPVector4::Set(float x, float y , float z , float t) //Funcion para inicializar un vector de 4 elementos
{
    xv = x;
    yv = y;
    zv = z;
    tv = t;
}

//Imprimimos el valor del vector
void LPVector4::print()
{
    cout << "x: " << xv << " y: " << yv << " z: " << zv << " t: " << tv;
}

//Sobrecarga operador +=
void LPVector4::operator +=(const LPVector4 v)
{
    xv += v.xv;
    yv += v.yv;
    zv += v.zv;
    tv += v.tv;
}





Para probar el ejecutable debereis hacerlo desde una consola para que os permita ver el resultado.




Por ahora lo único que os debe interesar es como se ha hecho la sobrecarga del operador += aplicado a los vectores ya que esta sobrecarga es de lo que más se usara en el MathEngine para hacer que el código quede limpio de cara al usuario.

Sin más, aqui os dejo, espero que os lo hayais pasado bien y hayais aprendido

Nos vemos

lunes, 8 de agosto de 2011

Curso de programación: Capítulo 9. Usar XML


Tal y como os prometí, aquí teneis un minitutorial de como usar xml como sistema de gestión de carga.

He usado la libreria tinyXML ya que es la que me ha parecido más sencilla de instalar. De hecho solamente es incluir los .cpp y los .h y compilar. La podeis obtener de http://www.grinninglizard.com/tinyxml/  Por lo que he visto tiene algún fallito y alguna cosa que no cumple el standard pero para lo que a nosotros nos atañe  (archivos de carga y poco más) va a ir perfecto.

El ejemplo lo he implementado modificando la función de carga del TextureManager para que cargue un .xml en vez de un .txt. Espero no tener que explicar nada ya que el ejemplo es bastante autoexplicativo, pero bueno.. si alguien tiene dudas, no vacileís en hacermelas llegar.


void TextureManager::Init(int* argc, char* argv[])
{
    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
   
    TiXmlDocument doc( "Data\\LoadTexture.xml");

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

    if ( !doc.LoadFile() )
    {
        printf( "Could not load test file 'Data\\LoadTexture.txt'. Error='%s'. Exiting.\n", doc.ErrorDesc() );
        exit( 1 );
    }

    TiXmlElement *root = doc.RootElement();
    for(TiXmlElement* element = root->FirstChildElement(); element; element = element->NextSiblingElement())
    {
        //Leemos el elemento
        sprintf(id,"%s", element->FirstChild("ID")->FirstChild()->Value());            //tomamos el ID
        sprintf(ruta,"%s",element->FirstChild("ROUTE")->FirstChild()->Value());        //tomamos la RUTA   
       
        num_id = LoadTexture(ruta) - 1;                //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
    }
}

El archivo de carga LoadTexture deja de ser un txt para ser un xml :
<list>
<element>
    <ID>INTRO</ID>
    <ROUTE>Data/Texture/intro.png</ROUTE>
</element>

<element>
    <ID>STATS</ID>
    <ROUTE>Data/Texture/Stats.png</ROUTE>
</element>

<element>
    <ID>PACK</ID>
    <ROUTE>Data/Texture/pack.png</ROUTE>
</element>

<element>
    <ID>FONT</ID>
    <ROUTE>Data/Texture/Font.png</ROUTE>
</element>

<element>
    <ID>PLAYER</ID>
    <ROUTE>Data/Texture/Characters/Player.png</ROUTE>
</element>

<element>
    <ID>FOE1</ID>
    <ROUTE>Data/Texture/Characters/Foe1.png</ROUTE>
</element>
<element>
    <ID>FOE2</ID>
    <ROUTE>Data/Texture/Characters/Foe2.png</ROUTE>
</element>
<element>
    <ID>FOE3</ID>
    <ROUTE>Data/Texture/Characters/Foe3.png</ROUTE>
</element>
<element>
    <ID>MOUSE</ID>
    <ROUTE>Data/Texture/Mouse.png</ROUTE>
</element>
<element>
    <ID>MATERIAL0</ID>
    <ROUTE>Data/Texture/Materials/material00.png</ROUTE>
</element>
<element>
    <ID>MATERIAL1</ID>
    <ROUTE>Data/Texture/Materials/material01.png</ROUTE>
</element>
<element>
    <ID>MATERIAL2</ID>
    <ROUTE>Data/Texture/Materials/material02.png</ROUTE>
</element>
<element>
    <ID>MATERIAL3</ID>
    <ROUTE>Data/Texture/Materials/material03.png</ROUTE>
</element>
<element>
    <ID>MATERIAL4</ID>
    <ROUTE>Data/Texture/Materials/material04.png</ROUTE>
</element>
<element>
    <ID>MATERIAL5</ID>
    <ROUTE>Data/Texture/Materials/material05.png</ROUTE>
</element>
<element>
    <ID>AMMO</ID>
    <ROUTE>Data/Texture/Items/Ammo.png</ROUTE>
</element>
<element>
    <ID>COIN</ID>
    <ROUTE>Data/Texture/Items/Coin.png</ROUTE>
</element>
<element>
    <ID>EXIT</ID>
    <ROUTE>Data/Texture/Items/Exit.png</ROUTE>
</element>
<element>
    <ID>LIFE</ID>
    <ROUTE>Data/Texture/Items/Life.png</ROUTE>
</element>
<element>
    <ID>GUI</ID>
    <ROUTE>Data/Texture/GUI/GUI.png</ROUTE>
</element>
<element>
    <ID>GUIAMMO</ID>
    <ROUTE>Data/Texture/GUI/Ammo.png</ROUTE>
</element>
<element>
    <ID>GUILIFE</ID>
    <ROUTE>Data/Texture/GUI/Life.png</ROUTE>
</element>
</list>


Podeis observer que, por ahora al menos, el filemanager queda obsoleto (es posible que le demos otro uso más adelante) y que la forma de cargar información es ,cuanto mínimo, un poco más limpia. Para sistemas más complejos de datos (GUI por ejemplo) este sistema nos irá muy bien.

Espero que os lo hayais pasado bien y que hayais aprendido.


LordPakusBlog

Relato: Ambientación de videojuego. Capitulo 4

Nuevo capitulo que explica cosas que  más o menos ya sabiamos desde otro ángulo de vista. Recordad que si teneis ideas o propuestas no dudeis en hacermelas llegar.

"
Registro de conversaciones de la caja negra del transporte militar de clase media, modelo Hermes, número de identificación XZ-700, encontrada  en la superficie del planeta "Nueva Velos".

[ORDENADOR]
Combustible al 100%.
Motor calentado.
Escotillas presurizadas.
Anclajes abiertos.
Podemos despegar....

[CAPITÁN A COPILOTO] Que no salga de aquí, pero la verdad, ya estoy un poco harto de tantos viajecitos arriba y abajo con toda la bodega llena de locos. Yo me alisté para morir en combate, no para morir de aburrimiento.

[COPILOTO] Procura que no sea hoy el dia que mueras ni de una cosa ni de la otra, quieres que lo lleve yo?, te veo cansado...

[CAPITAN] No, tranquiilo... es el viaje número 30 que hacemos esta semana, me se el camino de memoria.

[CAPITAN AL RESTO DE NAVES]. Pilotos, despeguen y vayan al punto de reunión , esperen para realizar el salto hacía Nueva Velos. Les espero ahí.

[1 hora más tarde]

[PILOTO NAVE VY-320 A CAPITAN]. Señor, los loco... los pasajeros están extremadamente alborotados y violentos, se han desecho de las cadenas. Parece que el relajante no les ha hecho mucho efecto. Si no se arregla la situación vamos a tener que abrir fuego.... ordenes?

[CAPITAN] Antes que nada  recuerde que es un piloto militar del imperio. Mantenga un poco la compostura. Se le ha entrenado para ello. En segundo lugar, no dispare si no es extremadamente necesario, no quiera ni saber la de papeleo que tendría que rellenar si se me muere uno solo de eso tarados. Los periodos de violencia son normales antes del salto. A la que salten, se relajarán automáticamente, ya lo verá, parece casi mágico......

[CAPITAN A TODA LA FLOTA]. Preparense para el salto en 3,2,1....

[NAVE FG-930]: Dios mio, han entrado arggg....***silencio***
[NAVE DE-675]: Acaban de entrar en la sala de mandos señor... [disparo][disparo][disparo]

[CAPITAN A TODA LA FLOTA]. 0...salten....

[TURBULENCIAS INREGISTRABLES DEL SALTO]

[CAPITAN A TODA LA FLOTA]. Revisen todos los sistemas, asegurense que la tripulación y los pasajeros están bien.... Respondan con informe.

[SILENCIO]

[CAPITAN A TODA LA FLOTA]: RESPONDAN CON INFORME.

[SILENCIO]

[CAPITÁN A COPILOTO]: Revisaste la radio antes de salir??

[PORTAZO]

[COPILOTO]: argggg

*EOF*
"

Si teneis nuevas ideas o sugerencias hacedmelas llegar y las iré incluyendo,

Nos vemos

domingo, 7 de agosto de 2011

Curso de programación: Capítulo 8. Bibliotecas, librerias y linkado


Hola a todos....

Un compañero ,lector del blog, ha echo un petición muy acertada que no se como no se me había ocurrido antes. Cito textualmente:
"Pako, te propongo que hagas un capítulo esplicando cómo se linkan las bibliotecas, qué son los ficheros lib, h, dll. Así como las propiedades de los proyectos (casi todo el mundo usa MVS2010). "

Así pues, vamos a por trabajo:

Breve descripción de nomenclatura:
Archivos .c o .cpp: Archivos de código de C o C++ respectivamente, archivos donde escribiremos nuestros programas y funciones.

Archivos .h : Archivos de cabecera o Headers. En estos archivos escribiremos todo aquello que tecnicamente no es código pero si el cual el código no tendria mucho sentido: Cabeceras de funciones, definición de clases, defines, inclusión de ficheros externos,  etc... es decir. Es una foma de tener el código más estructurado. Podeis leer cualquier .h del game engine para ver un ejemplo de lo que me refiero.

Archivos .lib : Archivos de libreria estática.  Básicamente es una forma de distribuir el código de una aplicación grande  sin tener que dar todo el código fuente, o sencillamente de no tener que recompilar una libreria cada vez que compilemos una aplicación. Con esta manera de trabajar con librerias el código de la libreria queda insertado dentro del código de la aplicación haciendo que nosotros solo veamos un archivo .exe .

Archivo .dll : Dinamyc Link Library. Dicho de otra manera, libreria dinámica. Es decir, cuando compilemos nuestro proyecto tendremos, por un lado el ejecutable y por otro lado el .dll de la libreria que será usado por el .exe.

Que es mejor .lib o .dll?? : Depende. Ambas son maneras de trabajar con librerias igual de válidas.  Si el rendimiento es un factor clave y tenemos una aplicación que muy raramente cambiará de libreria, entonces  la lib tiene preferencia. Si estamos delante de una aplicación de la cual se espera que va a cambiar con frecuencia de libreria, vale más la pena una .dll, por que así no hace falta ni recompilar. Por otro lado si la extensión de la libreria es bastante grande (del orden de los megas) y tenemos un conjunto de aplicaciones que usan la misma libreria, nos vale la pena las .dll para reaprovechar el espacio(una .dll sirve para todas las aplicaciones).


Dicho esto, como se linka una libreria??
Hay 2 maneras de linkar una libreria:
1. Si la libreria nos la proporcionan con el código fuente (.c, .cpp y .h). Lo único que hemos de hacer es añadir a nuestro proyecto las rutas de inclusión. En Visual Studio se hace así:
           - Botón derecho encima del proyecto. Seleccionad propiedades.
           - Propiedades de configuración >> C/C++
           - Dentro de Directorios de inclusión adicionales añadís vuestra ruta donde teneis los .h
           - Añadid al proyecto todos los .c/.cpp/.h de la libreria.
           - Compilad

2. Si la libreria es estática o dinámica primero de todo tendreis que mirar que la tengais bien instalada (normalmente se tienen que copiar ciertos archivos en c:/system , cuando os bajeis la libreria tendria que tener un README con las instrucciones) . En el caso de las .dll podeis dejarlas al lado del .exe para poder distribuirlo junto. En el código solo debereis usar una linea de este estilo al inicio del main.cpp:
                    #pragma comment(lib, "corona.lib")
Si no os funcionara decidlo que buscariamos solución.

Respecto a las propiedades de los proyectos, prefiero dejarlo como tema abierto. Creo que no hay mejor manera de aprender como funcionan que inspeccionando por vuestra cuenta. Si teneis problemas decidlo y os echaré un cable siempre que pueda.

Espero que os haya servido y que hayais aprendido.

Nos vemos
LordPakusBlog

viernes, 5 de agosto de 2011

Curso de programación: Capítulo 7. Introducción al ensamblador SIMD-SSE


El capitulo de hoy trata sobre las instrucciones de ensamblador SSE. Toda la información la he sacado de mi PFC que para más información lo podeís obtener de http://upcommons.upc.edu/pfc/handle/2099.1/10988 . Espero que os guste.

Hace unos años, los fabricantes de procesadores, debido a que los problemas a resolver por el cálculo computacional eran cada vez de mayores dimensiones y que la tecnología de fabricación de chips permitía una mayor densidad de transistores, optaron por proveer a los procesadores de instrucciones paralelas. Estas instrucciones tienen la particularidad de poder operar con un cierto conjunto de datos a la vez usando una sola instrucción.
En la siguiente figura puede verse el esquema básico de funcionamiento de una instrucción
SIMD donde X[1..4] e Y[1..4] son los vectores con las variables a tratar y OP es una
operación genérica (suma, resta , multiplicación, división, etc.….):

Dependiendo del fabricante y del propósito del procesador cada juego de instrucciones
tiene sus particularidades. Así , en los PowerPC estas instrucciones son llamadas AltiVec / VMX (VMX es el nombre que le dio IBM y Altivec el nombre comercial que le dio Motorola), de la misma manera que en los procesadores Sparc se bautizaron con el nombre de Visual Instruction Set (VIS) o en el caso de los Alpha cuyas instrucciones vectoriales fueron llamadas Motion Video Instructions (MVI), todo esto solo por poner ejemplos debido a que hay innumerable cantidad de conjuntos de instrucciones SIMD (MAX, MDMX, ASP, etc…) . Todos estos conjuntos de instrucciones son ejemplos de paquetes de instrucciones SIMD usadas en el cálculo computacional en concreto y en la industria del software en general.(por ejemplo en los videojuegos)

Para el caso concreto que nos interesa (SSE de Intel) el número de operaciones depende del tipo de datos con el que se trabaje.

Con el tiempo han ido evolucionando apareciendo nuevas versiones con más caracteristicas. Graficamente podreis ver esta evolución en este esquema:

Las operaciones SSE más tipicas que se pueden utilizar en el desarrollode un proyecto son las siguientes:
Movimiento de datos: MOVSS y MOVAPS, MOVUPS, MOVLPS, MOVHPS,
MOVLHPS, MOVHLPS
Operaciones aritméticas: ADDSS, SUBSS, MULSS, DIVSS, RCPSS,
SQRTSS, MAXSS, MINSS, RSQRTSS y ADDPS, SUBPS, MULPS, DIVPS,
RCPPS, SQRTPS, MAXPS, MINPS, RSQRTPS
Comparación: CMPSS, COMISS, UCOMISS y CMPPS
Movimientos intraregistro: SHUFPS, UNPCKHPS, UNPCKLPS
Conversión de tipos de dato: CVTSI2SS, CVTSS2SI, CVTTSS2SI y
CVTPI2PS, CVTPS2PI, CVTTPS2PI
Opeaciones lógicas bit a bit: ANDPS, ORPS, XORPS, ANDNPS


Como usar las instrucciones SSE dentro de nuestro código?
Depende del compilador.. yo os lo explicaré como funciona para Visual Studio (C++) pero para todos los compiladores funciona más o menos igual.

Antes que nada, el mundo al revés, un ejemplo:
inline void Diagonal3p(TYPE ****mat, int i)
{
int ii,jj;
int ind;
TYPE **A = mat[0][i];
TYPE* x0 = (TYPE*) &A[0][0];
TYPE* x1 = (TYPE*) &A[1][0];
TYPE* x2 = (TYPE*) &A[2][0];

__asm //Calculamos los elementos de la primera fila
{
mov edx, x0
mov edi, x1
movups xmm0, [edx] //Cargamos el primer vector
movups xmm1, [edi] //Cargamos el segundo vector
movups xmm4, xmm0 //Copiamos
movups xmm5, xmm0 //Copiamos
movups xmm6, xmm0 //Copiamos
shufps xmm4, xmm4, 0x00 //Broadcast
shufps xmm5, xmm5, 0x55 //Broadcast
shufps xmm6, xmm6, 0xAA //Broadcast
mulps xmm4, xmm0 //xmm4 *= xmm0
mulps xmm5, xmm0 //xmm5 *= xmm0
mulps xmm6, xmm0 //xmm6 *= xmm0
movups xmm2, xmm1 //Copiamos
movups xmm3, xmm1 //Copiamos
movups xmm0, xmm1 //Copiamos
shufps xmm2, xmm2, 0x00 //Broadcast
shufps xmm3, xmm3, 0x55 //Broadcast
shufps xmm0, xmm0, 0xAA //Broadcast
mulps xmm2, xmm1 //xmm7 *=xmm1
mulps xmm3, xmm1 //xmm7 *=xmm1
mulps xmm0, xmm1 //xmm7 *=xmm1
addps xmm4, xmm2 //xmm4 += xmm7
mov edi, x2
addps xmm5, xmm3 //xmm5 += xmm7
movups xmm2, [edi] //Cargamos el tercer vector
addps xmm6, xmm0 //xmm4 += xmm7
movups xmm0, xmm2 //Copiamos
movups xmm1, xmm2 //Copiamos
movups xmm7, xmm2 //Copiamos
shufps xmm0, xmm0, 0x00 //Broadcast
shufps xmm1, xmm1, 0x55 //Broadcast
shufps xmm7, xmm7, 0xAA //Broadcast
mulps xmm0, xmm2 //xmm7 *=xmm1
mulps xmm1, xmm2 //xmm7 *=xmm1
mulps xmm7, xmm2 //xmm7 *=xmm1
addps xmm4, xmm0 //xmm4 += xmm7
addps xmm5, xmm1 //xmm4 += xmm7
addps xmm6, xmm7 //xmm4 += xmm7
}

A = mat[i][i];
x0 = (TYPE*) &A[0][0];
x1 = (TYPE*) &A[1][0];
x2 = (TYPE*) &A[2][0];
__asm
{
mov edx, x0
movups xmm0, [edx]
subps xmm0,xmm4
movups xmm1,xmm0
shufps xmm1,xmm1,0x00
sqrtps xmm1,xmm1
divps xmm0,xmm1
movups xmm4,xmm0 //xmm4 = A[0][0..4] ya calculado
movups [edx],xmm0
mov edx, x1
movups xmm0, [edx]
movups xmm3,xmm4
shufps xmm3,xmm3,0x55
mulps xmm3,xmm4
subps xmm0,xmm3
subps xmm0,xmm5
movups xmm1,xmm0
shufps xmm1,xmm1,0x55
sqrtps xmm1,xmm1
divps xmm0,xmm1
movups xmm5,xmm0 //xmm5 = A[0][0..4] ya calculado
movups [edx],xmm0
mov edx, x2
movups xmm0, [edx]
movups xmm3,xmm4
shufps xmm3,xmm3,0xAA
mulps xmm3,xmm4
subps xmm0,xmm3
movups xmm3,xmm5
shufps xmm3,xmm3,0xAA
mulps xmm3,xmm5
subps xmm0,xmm3
subps xmm0,xmm6
movups xmm1,xmm0
shufps xmm0,xmm0,0xAA
sqrtps xmm0,xmm0
divps xmm1,xmm0
movups [edx],xmm1
}

POR AQUÍ SEGUIRIA EL CÓDIGO PERO TAMPOCO NOS IMPORTA

Si os fijais, para implementar SSE solo teneis que seguir unos sencillos pasos:
  1. Escribir dentro de la etiqueta _asm
  2. movups. Para mover datos
  3. shufps para mover datos dentro de un mismo registro
  4. mulps . Para multiplicar
  5. divps. Para dividir
  6. addps Para sumar
  7. subps. Para restar
  8. sqrtps . Para realizar raíces cuadradas
  9. De normal se tiene un mínimo de 8 registros de 128bits(4 floats o ints por registro) (pueden ser más en según que condiciones, pero se pierde portabilidad)
  10. Para dudas seguir el ejemplo y buscar en la documentación de intel :D
Si os interesa el tema decidlo e intentaré hacer algún programilla que hago uso extensivo de las SSE para que veais la diferencia de rendimiento (según como esté montado puede ser de varios ordenes de magnitud de diferencia).

Espero que os haya gustado y que hayais aprendido. Si teneis dudas, preguntas o sugerencias no vacileis en hacermelas llegar. En próximos capitulos ahondaré más en el tema.

LordPakusBlog

Entradas populares