domingo, 15 de marzo de 2015

criptografía - funciones hash criptográficas

MD2 (acrónimo inglés de Message-Digest Algorithm 2, Algoritmo de Resumen del Mensaje 2) es una función de hash criptográfica desarrollada por Ronald Rivest en 1989. Elalgoritmo está optimizado para computadoras de 8 bits. El valor hash de cualquier mensaje se forma haciendo que el mensaje sea múltiplo de la longitud de bloque en el ordenador (128 bits o 16 bytes) y añadiéndole un checksum. Para el cálculo real, se utiliza un bloque auxiliar 48 bytes y una tabla de 256 bytes que contiene dígitos al azar del número pi.
Una vez que todos los bloques del mensaje alargado se han procesado, el primer bloque parcial del bloque auxiliar se convierte en el valor de hash del mensaje.
 Usaremos MD2, el formato de archivo Quake 2. Quake 2 puede ser viejo, pero vamos a utilizar MD2 debido a que el formato de archivo es abierto y sencillo y hay un montón de archivos MD2 en línea que otras personas han hecho.
Otra razón por la que estamos usando MD2 es porque Blender , un programa de modelado 3D de código abierto, es capaz de exportar a MD2. Los profesionales suelen utilizar 3ds Max o Maya para el modelado 3D. Pero esos programas cuestan dinero, por lo que prefieren seguir con Blender.
Por desgracia, en la actualidad, el exportador Blender MD2 es bastante temperamental y con errores. No pude conseguirlo exportar animaciones en la versión 2.44, que es actualmente la versión más reciente; Tuve que usar la versión 2.42. Con suerte, el exportador será mejorado en versiones posteriores de Blender.
El uso de Blender, hice el chico 3D para nuestro programa, incluyendo una textura para él. Nuestro programa tiene el hombre a pie, como se muestra a continuación:
Programa de animación pantalla
Esto es lo que la edición del tipo en Blender se ve así:
Edición de una animación en Blender

Cargando y Animación MD2 Archivos

Así que ahora que hemos hecho una animación MD2 de nuestro hombre, vamos a tener que cargarlo y animarlo. Miré en línea para el formato de archivo MD2, para que yo pudiera encontrar la manera de hacer eso. En el resto de esta lección, vamos a ver cómo funciona exactamente el formato de archivo MD2.
Vamos a poner todo el código específico para archivos MD2 en los archivos md2model.h y md2model.cpp. Tendremos un MD2Model clase que almacena toda la información acerca de una animación y se encarga de la elaboración de la animación. Veamos el archivo md2model.h para ver lo que la clase se ve así:
struct MD2Vertex {
    Pos Vec3f;
    Vec3f normal;
};

struct MD2Frame {
     Char nombre [16];
    MD2Vertex * vértices;
};

struct MD2TexCoord {
     float texCoordX;
     flotar texCoordY;
};

struct MD2Triangle {
     int vértices [3];   // Los índices de los vértices de este triángulo 
    int texCoords [3]; // Los índices de las coordenadas de la textura del triángulo 
};
En primer lugar, tenemos algunas estructuras que nuestro MD2Model clase utilizará. Tenemos vértices, los marcos, las coordenadas de texturas, y triángulos. Cada cuadro tiene un nombre, que suele indicar el tipo de animación en el que es (por ejemplo, "correr", "de pie"). Los marcos sólo almacenan las posiciones y las normales de cada uno de los vértices utilizando una vértices matriz. Cada trama tiene el mismo número de vértices, de manera que el vértice en el índice 5, marco 1, por ejemplo, representa la misma parte del modelo como el vértice en el índice 5, el bastidor 2, pero está en una posición diferente. Un triángulo se define por los índices de los vértices en los marcos ' vértices matrices, y los índices de las coordenadas de textura en una matriz que aparecerá en la MD2Model clase.
clase MD2Model {
     privado :
        MD2Frame * marcos;
        int numFrames;
        MD2TexCoord * texCoords;
        MD2Triangle * triángulos;
        int numTriangles;
Estas son las principales áreas que vamos a necesitar para dibujar el modelo. Tenemos una gran variedad de marcos, coordenadas de texturas, y triángulos.
        GLuint textureId;
Aquí, tenemos el id de la textura de la figura que vamos a animar.
        int startFrame; // El primer cuadro de la animación actual 
        int endFrame;    // El último cuadro de la animación actual
Estos son los marcos de inicio y finalización de utilizar para la animación.
        / * La posición en la animación actual. 0 indica el comienzo de
         * La animación, que es en el marco de partida, y 1 indica el
         * Final de la animación, que está justo cuando el marco de partida es
         * Alcanzado de nuevo. Siempre se encuentra entre 0 y 1.
         * / 
        flotar tiempo;
Er, acabo de leer los comentarios.
        MD2Model ();
     público :
        ~ MD2Model ();
He aquí nuestro constructor y destructor. El constructor es privado, porque sólo una especial MD2Model método será capaz de construir una MD2Model objeto.
        // Cambia a la animación dada 
        vacío (setAnimation const  char * nombre);
Este método le permitirá a establecer la animación actual, ya que el archivo MD2 puede almacenar en realidad varias animaciones en ciertos rangos de marcos. Nuestra animación, por ejemplo, va a ocupar marcos 40 a 45. Cada cuadro tiene un nombre, que nos permitirá identificar los marcos apropiados una la cadena de animación dado, como veremos más adelante.
        // Avanza la posición en la animación actual. Toda la animación 
        // dura una unidad de tiempo. 
        void adelantado ( float dt);
Este método se utiliza para avanzar la animación estado. Al llamar repetidamente avance , vamos a animamos a través de las diferentes posiciones de la figura 3D.
        // Llama el estado actual del modelo de animación. 
        void draw ();
Este método se encarga de la elaboración de hecho el modelo 3D.
        // Carga un MD2Model desde el archivo especificado. Devuelve NULL si no era 
        // un error al cargar la misma. 
        estática MD2Model * Carga ( const  char * filename);
};
La carga método va a cargar un archivo MD2 dado. Es un método estático, indicado por la palabra clave " estática ". Esto significa que podemos decir que es el uso de MD2Model :: load ("somefile.md2") , y que no tenga que llamar en un determinado MD2Model objeto. Básicamente, la carga es como una función normal, excepto que puede acceder a los campos privados de MD2Modelobjetos.
Ese es el archivo md2model.h. Ahora echemos un vistazo a md2model.cpp.
espacio de nombres {
     // ... 
}
Un pequeño matiz C ++: tenemos que poner todas las constantes y funciones no-clase en este espacio de nombres {} bloque para que podamos tener otras constantes y funciones con el mismo nombre en diferentes archivos. Podríamos, por ejemplo, tienen una función llamada " foo ", tanto en este bloque de espacio de nombres y en main.cpp.
    // Normales utilizadas en el formato de archivo MD2 
    flotador NORMALS [486] =
        {-0.525731f, 0.000000f, 0.850651f,
         -0.442863f, 0.238856f, 0.864188f,
         // ... 
         -0.688191f, -0.587785f, -0.425325f};
En lugar de almacenar directamente normales, MD2 tiene 162 normales y especiales sólo da los índices de las normales. Esta matriz contiene todas las normales que utiliza MD2.
Cuando cargamos en el archivo, vamos a tener que preocuparse por una pequeña cosa llamada "endianness". En el diseño de las CPUs, los diseñadores tuvieron que decidir si desea almacenar los números con su byte más significativo primero o el último. Por ejemplo, el entero corto 258 = 1 (256) + 2 podría ser almacenado con los bytes (1, 2), con el byte más significativo primero, o con los bytes (2, 1), con el byte menos significativo primero. El primer medio de almacenamiento se llama "big-endian"; el segundo se llama "little-endian". Así, la gente que diseñó CPUs, en su infinita sabiduría, decidieron ambos.Algunas CPUs, incluyendo el Pentium, números de tiendas en forma ascendente hacia la izquierda, y otros números CPUs almacenar en forma de big-endian. Estúpido como parece, que endianness es "mejor", ha sido la fuente de discusiones sin sentido. Por lo tanto, estamos atascados con los dos, un problema que ha sido programadores molestos para las edades pasado.
¿Qué tiene esto que ver con nada? El problema surge cuando un entero que requiere múltiples bytes se almacena en el archivo MD2. Se almacena en forma ascendente hacia la izquierda. Pero el equipo en el que cargamos el archivo podría no utilizar la forma little-endian. Así que cuando cargamos el archivo, tenemos que escribir nuestro código cuidadosamente para asegurarse de que el orden de bytes de ordenador en el que se ejecuta el programa, no importa.
    // Devuelve si el sistema es poco-endian 
    bool littleEndian () {
         // El valor a corto 1 tiene bytes (1, 0) en little-endian y (0, 1) en 
        // big-endian 
        cortos s = 1;
         retorno ((( Char *) y s) [0]) == 1;
    }
Esta función será comprobar si estamos en un sistema little endian o big-endian. Si el primer byte del corto número entero 1 es un 1, entonces estamos en una máquina little-endian; de lo contrario, estamos en una máquina big-endian.
    // Convierte una matriz de cuatro caracteres en un número entero, utilizando little-endian forma 
    int Toint ( const  char * bytes) {
         retorno ( int ) ((( unsigned  char ) bytes [3] << 24) |
                     (( unsigned  char ) bytes [2] << 16) |
                     (( unsigned  char ) bytes [1] << 8) |
                     ( unsigned  char ) bytes [0]);
    }
    
    // Convierte una matriz de dos caracteres para un corto, utilizando el formulario little-endian 
    corto toShort ( const  char * bytes) {
         retorno ( corto ) ((( unsigned  char ) bytes [1] << 8) |
                       ( unsigned  char ) bytes [0]);
    }
    
    // Convierte una matriz de dos caracteres a un corto sin signo, utilizando little-endian 
    // formar 
    sin firmar  corto toUShort ( const  char * bytes) {
         vuelta ( sin firmar  corto ) ((( unsigned  char ) bytes [1] << 8) |
                                ( unsigned  char ) bytes [0]);
    }
Aquí, tenemos funciones que convertirán una secuencia de bytes en un int , un corto , o un corto sin signo . Ellos usan el << operador BitShift, que, básicamente, sólo empuja un número de 0 bits en el final del número. Por ejemplo, el número binario 1001101 bit desplazado por 5 es 100110100000. Todos los bits "extra" en la parte delantera se acaban de extraer. Tenga en cuenta que las funciones funcionan independientemente del orden de bits de la máquina en la que se ejecuta el programa.
    // Convierte una matriz de cuatro caracteres a un flotador, utilizando little-endian forma 
    flotador estar flotando ( const  char * bytes) {
         float f;
         si (littleEndian ()) {
            (( Char *) y f) [0] = bytes [0];
            (( Char *) y f) [1] = bytes [1];
            (( Char *) y f) [2] = bytes [2];
            (( Char *) y f) [3] = bytes [3];
        }
        otra cosa {
            (( Char *) y f) [0] = bytes [3];
            (( Char *) y f) [1] = bytes [2];
            (( Char *) y f) [2] = bytes [1];
            (( Char *) y f) [3] = bytes [0];
        }
        volver f;
    }
Ni siquiera flotar s son inmunes a la cuestión endianness. Para convertir cuatro bytes en un flotador , comprobamos si estamos en una máquina little-endian y luego establecemos cada byte del flotador fsegún corresponda.
    // Lee los siguientes cuatro bytes como un entero, utilizando little-endian forma 
    int readInt (ifstream y entrada) {
         char buffer [4];
        input.read (buffer, 4);
        volver Toint (buffer);
    }
    
    // Lee los siguientes dos bytes como un corto, utilizando el formulario little-endian 
    corto readShort (ifstream y entrada) {
         char buffer [2];
        input.read (buffer, 2);
        volver toShort (buffer);
    }
    
    // Lee los siguientes dos bytes como un corto sin signo, mediante el formulario little-endian 
    sin firmar  corto readUShort (ifstream y entrada) {
         char buffer [2];
        input.read (buffer, 2);
        volver toUShort (buffer);
    }
    
    // Lee los siguientes cuatro bytes como un flotador, mediante el formulario little-endian 
    flotador readFloat (ifstream y entrada) {
         char buffer [4];
        input.read (buffer, 4);
        volver estar flotando (buffer);
    }
    
    // Las llamadas readFloat tres veces y devuelve los resultados como un objeto Vec3f
    Vec3f readVec3f (ifstream y entrada) {
        float x = readFloat (entrada);
         flotar y = readFloat (entrada);
         float z = readFloat (entrada);
         volver Vec3f (x, y, z);
    }
Estas funciones hacen que sea conveniente para leer los próximos bytes de un archivo como int , corto , corto sin signo , flotador , o Vec3f .
    // Hace que la imagen en una textura, y devuelve el id de la textura
    GLuint LoadTexture (Imagen * Imagen) {
        // ...
    }
}
Aquí está nuestra LoadTexture función de la lección en texturas.
MD2Model :: ~ MD2Model () {
     si (! fotogramas = NULL) {
         para ( int i = 0; i borro
[] marcos [i] .vertices; } eliminar [] marcos; } si (! texCoords = NULL) { delete [] texCoords; } si (! triángulos = NULL) { delete [] triángulos; } }
Así es destructor de la clase, lo que libera la memoria utilizada por todos los vértices, los marcos, las coordenadas de texturas, y triángulos.
MD2Model :: MD2Model () {
    marcos = NULL;
    texCoords = NULL;
    triángulos = NULL;
    tiempo = 0;
}
El constructor inicializa algunos de los campos. El constructor no hace mucho; la acción es en la carga de método.
// Carga el modelo MD2 
MD2Model * MD2Model :: load ( const  char * filename) {
    ifstream de entrada;
    input.open (filename, istream :: binario);
    
    Char buffer [64];
    input.read (buffer, 4); // debe ser "IPD2", si se trata de un archivo MD2 
    si (tampón [0] = 'I' || amortiguar [1] = 'D' ||!
        buffer [2]! = || buffer 'P' [3]! = '2') {
        devolver NULL;
    }
    si (readInt (entrada) = 8!) { // El número de versión 
        de retorno NULL;
    }
Aquí está el método que se carga en un archivo MD2. En primer lugar, comprobamos que los primeros cuatro bytes del archivo son "IPD2", que deben ser los primeros cuatro bytes de cada archivo MD2.Luego, comprobamos que los próximos cuatro bytes, interpretados como un entero, son el número 8, que deben ser para los archivos MD2 que estamos cargando.
    int textureWidth = readInt (entrada);    // La anchura de las texturas 
    int textureHeight = readInt (entrada);   // La altura del texturas 
    readInt (entrada);                       // El número de bytes por trama 
    int numTextures = readInt (entrada) ;     // El número de texturas 
    si (numTextures = 1!) {
         retorno NULL;
    }
    int numVertices = readInt (entrada);     // El número de vértices 
    int numTexCoords = readInt (entrada);    // El número de coordenadas de textura 
    int numTriangles = readInt (entrada);    // El número de triángulos 
    readInt (entrada);                       // El número de comandos de OpenGL 
    int numFrames = readInt (entrada);       // El número de fotogramas
El formato de archivo MD2 dicta que el siguiente en el archivo, debe haber cierta información acerca de la animación en un cierto orden. Leemos en esta información y la almacenamos en variables. Parte de la información que no necesitamos, por lo que no almacenamos en cualquier lugar.
    // Compensaciones (número de bytes a partir del principio del archivo al principio 
    // de donde aparecen ciertos datos) 
    int textureOffset = readInt (entrada);   // El desplazamiento a las texturas 
    int texCoordOffset = readInt (entrada); // El desplazamiento a las coordenadas de textura 
    int triangleOffset = readInt (entrada); // El desplazamiento a los triángulos 
    int frameOffset = readInt (entrada);     // El desplazamiento a la marcos 
    readInt (entrada);                       // El desplazamiento a la OpenGL comandos 
    readInt ( de entrada);                       // El desplazamiento hasta el final del archivo
Siguiente en el archivo MD2 debe haber ciertos valores que indican el número de bytes desde el comienzo del archivo donde aparecen ciertos datos.
    // Cargar la textura
    input.seekg (textureOffset, ios_base :: beg);
    input.read (buffer, 64);
    si (strlen (buffer) <5 -="" 0="" 4="" bmp="" buffer="" class="codekeyword" n="" span="" strcmp="" strlen="" style="color: #0000d0;" tamp="">devolver
NULL; } Imagen * image = loadBMP (buffer); GLuint textureId = LoadTexture (imagen); borrar la imagen; MD2Model * Modelo = nuevo MD2Model (); modelo-> textureId = textureId;
Nosotros vamos a donde está indicada la textura, y la carga en los próximos 64 bytes como una cadena. La cadena es un nombre de archivo donde la textura para el modelo es. Nos aseguramos de que la textura es un mapa de bits y lo cargamos en.
    // Cargar las coordenadas de textura
    input.seekg (texCoordOffset, ios_base :: beg);
    modelo-> texCoords = nuevo MD2TexCoord [numTexCoords];
     para ( int i = 0; i  texCoords + i;
        texCoord-> texCoordX = ( float ) readShort (entrada) / textureWidth;
        texCoord-> texCoordY = 1 - ( float ) readShort (entrada) / textureHeight;
    }
A continuación, cargamos en las coordenadas de textura. Cada coordenada de textura se representa como dos cortos s. Para llegar desde cada uno corto a la apropiada flotador , tenemos que dividir por la anchura o la altura de la textura que encontramos en el principio del archivo. Para la coordenada y, tenemos que tener 1 menos la coordenada porque el archivo MD2 mide la coordenada y de la parte superior de la textura, mientras que OpenGL mide desde el fondo de la textura.
    // Cargue los triángulos
    input.seekg (triangleOffset, ios_base :: beg);
    modelo-> triángulos = nuevo MD2Triangle [numTriangles];
    modelo-> numTriangles = numTriangles;
    para ( int i = 0; i  triángulos + i;
        para ( int j = 0; j <3 j="" ngulo="" tri=""> vértices [j] = readUShort (entrada);
        }
        para ( int j = 0; j <3 j="" ngulo="" tri=""> texCoords [j] = readUShort (entrada);
        }
    }
Ahora, cargamos en los triángulos, que son sólo un montón de índices de vértices y coordenadas de textura.
    // Cargue los marcos
    input.seekg (frameOffset, ios_base :: beg);
    Modelo-> marcos = nuevo MD2Frame [numFrames];
    modelo-> numFrames = numFrames;
    para ( int i = 0; i  marcos + i;
        marco> vértices = nuevo MD2Vertex [numVertices];
        Escala Vec3f = readVec3f (entrada);
        Traducción Vec3f = readVec3f (entrada);
        input.read (marco> nombre, 16);
        
        para ( int j = 0; j  vértices + j;
            input.read (buffer, 3);
            Vec3f v (( unsigned  char ) buffer [0],
                    ( unsigned  char ) buffer [1],
                    ( unsigned  char ) buffer [2]);
            vertex-> pos = traducción + Vec3f (escala [0] * v [0],
                                              escalar [1] * v [1],
                                              escala [2] * v [2]);
            input.read (buffer, 1);
            int normalIndex = ( int ) (( unsigned  char ) buffer [0]);
            vertex-> normales = Vec3f (NORMALS [3 * normalIndex],
                                   NORMALS [3 * normalIndex + 1],
                                   NORMALS [3 * normalIndex + 2]);
        }
    }
Ahora, cargamos en los marcos. Cada trama comienza con seis carrozas, indicando vectores por los que pueda escalar y traducir los vértices. Entonces, hay 16 bytes que indican el nombre del marco. Luego vienen los vértices. Para cada vértice, tenemos dos caracteres sin signo que indica la posición, que podemos convertir en balsas por escalamiento y traducirlos. Entonces, tenemos un carácter sin signo que da la vertor normal, como un índice en la NORMALS matriz que vimos anteriormente.
    modelo-> startFrame = 0;
    modelo-> endFrame = numFrames - 1;
    volver modelo;
}
Por último se exponen los fotogramas inicial y final y volvemos al modelo.
vacío MD2Model :: setAnimation ( const  char * nombre) {
     / * Los nombres de marcos normalmente comienzan con el nombre de la animación en
     * Que se encuentran, por ejemplo, "correr", y son seguidos por un no-alfabético
     * Carácter. Normalmente, indican su número de fotograma de la animación,
     * Por ejemplo, "run_1", "run_2", etc.
     * / 
    bool encontrado = false;
     para ( int i = 0; i si
(strlen (marco> nombre)> strlen (nombre) && strncmp (marco> nombre, nombre, strlen (nombre)) == 0 && ! Isalpha (marco> nombre [strlen (nombre)])) { si (! encontrado) { encontrado = true; startFrame = i; } otra cosa { endFrame = i; } } otra cosa si (encontrado) { descanso ; } } }
Esta función se da cuenta de los marcos de inicio y fin de la animación indicado usando los nombres de los diferentes marcos, que siguen el modelo sugerido por el comentario.
vacío MD2Model :: adelantado ( float dt) {
     si (dt <0 class="codekeyword" span="" style="color: #0000d0;">volver
; } + tiempo = dt; Si (tiempo <1000000000 -="(" class="codekeyword" span="" style="color: #0000d0;" tiempo="">int ) tiempo; } otra cosa { tiempo = 0; } }
Ahora, tenemos un método para avanzar en la animación, lo que hacemos al aumentar el tiempo de campo. Para mantenerlo entre 0 y 1, utilizamos el tiempo - = (int) tiempo (a menos que el tiempo es muy grande, en cuyo caso podríamos tener problemas convertirlo en un entero).
vacío MD2Model :: draw () {
    glEnable (GL_TEXTURE_2D);
    glBindTexture (GL_TEXTURE_2D, textureId);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Aquí es donde se dibuja el modelo 3D. Comenzamos diciendo OpenGL la textura y el tipo de mapeado de texturas que queremos utilizar.
    // Averiguar los dos cuadros entre los que estamos interpolación 
    int frameIndex1 = ( int ) (hora * (endFrame - startFrame + 1)) + startFrame;
     si (frameIndex1> endFrame) {
        frameIndex1 = startFrame;
    }
    
    int frameIndex2;
     si (frameIndex1 otra cosa
{ frameIndex2 = startFrame; } MD2Frame * frame1 = frames + frameIndex1; MD2Frame * frame2 = frames + frameIndex2;
Ahora, utilizando el tiempo de campo, que sepamos los dos cuadros entre los que queremos interpolar.
    // Calcula la fracción que estamos entre los dos marcos 
    flotan frac =
        (Tiempo - ( float ) (frameIndex1 - startFrame) /
         ( float ) (endFrame - startFrame + 1)) * (endFrame - startFrame + 1);
Ahora, podemos averiguar qué fracción estamos entre los dos marcos. 0 significa que estamos en el primer cuadro, 1 significa que estamos en el segundo, y 0,5 significa que estamos a mitad de camino en el medio.
    // Dibuja el modelo como una interpolación entre los dos marcos
    glBegin (GL_TRIANGLES);
    para ( int i = 0; i para
( int j = 0; j <3 j="" md2vertex="" v1="frame1-"> vértices + Triángulo> vértices [j]; MD2Vertex * v2 = frame2-> vértices + Triángulo> vértices [j]; Vec3f pos = v1> pos * (1 - frac) + V2-> pos * frac;
Ahora, vamos a través de los triángulos, y para cada vértice, tomamos la posición de ser una interpolación entre sus posiciones en los dos marcos.
            Vec3f normales = v1> * normal (1 - frac) + V2-> normales * frac;
             si (normal [0] == 0 && normal [1] == 0 && normal [2] == 0) {
                Vec3f normal = (0, 0, 1);
            }
            glNormal3f (normal [0], normal [1], normal [2]);
Hacemos lo mismo para los vectores normales. Si la media pasa a ser el vector cero, podemos cambiar a un vector arbitrario, ya que el vector cero no tiene ninguna dirección y no se puede utilizar como un vector normal. En realidad hay una mejor manera de promediar dos direcciones, pero vamos a seguir con un promedio lineal porque es más fácil.
            MD2TexCoord * TexCoord = texCoords + Triángulo> texCoords [j];
            glTexCoord2f (texCoord-> texCoordX, texCoord-> texCoordY);
            glVertex3f (pos [0], POS [1], pos [2]);
        }
    }
    glEnd ();
Ahora, sólo encontramos la textura apropiada coordinar y llamar glTexCoord2f y glVertex3f .

No hay comentarios:

Publicar un comentario