Creando animaciones usando Transformaciones en OpenGL

La animación es la ilusión de hacernos pensar que un objeto se mueve realmente en la pantalla. Pero debajo son solo algoritmos complejos que actualizan y dibujan diferentes objetos. 

Objetivo: Una animación compleja de un dinosaurio andante en 2D. Método: Uso de Transformaciones de partes individuales del cuerpo. Mira este video:

En OPENGL .

Algoritmo

Hay 3 transformaciones principales en gráficos por computadora: traducción, rotación y escala. Todo se puede implementar usando matemáticas muy simples.

Translation: X = x + tx,    tx is the amount of translation in x-axis
             Y = y + ty,    ty is the amount of translation in y-axis

Rotation: X = xcosA - ysinA,   A is the angle of rotation.
          Y = xsinA + ycosA

Scaling: X = x*Sx,      Sx is Scaling Factor
         Y = y*Sy,    Sy is Scaling Factor

Para dibujar figuras, se usará el algoritmo de dibujo de líneas de Bresenham junto con las ecuaciones anteriores para dibujar cada línea según sea necesario.

Implementación

El cuerpo del dinosaurio se divide en 8 partes principales: cabeza, cuerpo superior, cola, cuerpo inferior y las cuatro patas. Las partes se almacenan como archivos de texto con coordenadas separadas por comas, que se importan durante la ejecución del programa:

Los centros de rotación de cada objeto se almacenaron en un archivo separado:

Dado que todos los archivos se crearon a mano, hubo espacio para pequeños errores que se corrigieron en el siguiente archivo:

Nota: descargue todos los archivos anteriores antes de ejecutar el programa y manténgalos en el mismo directorio. Cada uno tendrá su propio objeto almacenando lo siguiente:

  • Todas las líneas del objeto en una gran array.
  • el numero de lineas
  • La cantidad actual de traducción
  • El centro de rotación
  • las compensaciones
  • La cantidad actual de rotación
  • La dirección de rotación

El programa funcionará en el siguiente orden:

  1. Se iniciará la ventana de OpenGL
  2. Todos los archivos se leerán y almacenarán en sus respectivos objetos.
  3. Se iniciará un ciclo while infinito
  4. La pantalla se borrará
  5. Se dibujará una línea que representa la pradera.
  6. Todas las partes serán actualizadas.
  7. Todas las partes serán dibujadas.
  8. El valor de traducción del cuerpo se reducirá y, si el dinosaurio está fuera de la ventana, se reiniciará.
  9. El ciclo irá a su próxima iteración.

Durante la actualización de un objeto, se comprueba el estado de rotación y, si supera el umbral de rotación, se invierte la dirección de rotación. El estado de rotación es 0 si no se va a rotar. 

C

#include <stdio.h>
#include <GL/glut.h>
#include <math.h>
 
// these are the parameters
#define maxHt 800
#define maxWd 600
#define maxLns 10000
#define transSpeed 1
#define rotSpeed 0.02
#define rotateLimit 0.2
#define boundLimitL -200
#define boundLimitR 500
#define grasslandy 230
 
// Structure for storing lines
typedef struct lines {
    int x1, x2, y1, y2;
} LINE;
 
// Object type structure for storing each body part
typedef struct objects {
    LINE edge[maxLns];
    int translation, cx, cy, xoffset, yoffset;
    float theta;
    int rotationState;
    int EdgeCount;
} Object;
 
// the different objects
Object Head, upBody, Tail, downBody, FlegF, FlegB, BlegF, BlegB;
// global
int dinoTranslate = 0;
 
// basic init function for OPENGL
void myInit(void)
{
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, maxHt, 0, maxWd);
    glClear(GL_COLOR_BUFFER_BIT);
}
 
// this function translates, and rotates a point according to an object and draws it
void rotateandshiftPt(int px, int py, Object obbj)
{
    int xf, yf;
 
    xf = obbj.cx + (int)((float)(px - obbj.cx) * cos(obbj.theta)) - ((float)(py - obbj.cy) * sin(obbj.theta));
    yf = obbj.cy + (int)((float)(px - obbj.cx) * sin(obbj.theta)) + ((float)(py - obbj.cy) * cos(obbj.theta));
    glBegin(GL_POINTS);
    glVertex2i(obbj.translation + xf + obbj.xoffset, yf + obbj.yoffset);
    glEnd();
}
 
// this function draws a line using Bresenhams
void drawLineBresenham(int x1, int y1, int x2, int y2, Object obbj)
{
    int Dx, Dy, Dxmul2, Dymul2, Pk, xtempi, ytempi;
    float lineSlope, xtemp, ytemp;
    Dx = abs(x2 - x1);
    Dy = abs(y2 - y1);
    Dxmul2 = 2 * Dx;
    Dymul2 = 2 * Dy;
    ytemp = (float)(y2 - y1);
    xtemp = (float)(x2 - x1);
    lineSlope = (ytemp / xtemp);
 
    if (lineSlope >= -1.0 && lineSlope <= 1.0) {
        Pk = Dymul2 - Dx;
        if (x1 > x2) {
            xtempi = x2;
            x2 = x1;
            x1 = xtempi;
            ytempi = y2;
            y2 = y1;
            y1 = ytempi;
        }
        for (xtempi = x1, ytempi = y1; xtempi <= x2; xtempi++) {
            rotateandshiftPt(xtempi, ytempi, obbj);
            if (Pk < 0) {
                Pk = Pk + Dymul2;
            } else {
                Pk = Pk + Dymul2 - Dxmul2;
                if (lineSlope >= 0.0 && lineSlope <= 1.0)
                    ytempi = ytempi + 1;
                else if (lineSlope < 0.0 && lineSlope >= -1.0)
                    ytempi = ytempi - 1;
            }
        }
    } else {
        Pk = Dxmul2 - Dy;
        if (y1 > y2) {
            xtempi = x2;
            x2 = x1;
            x1 = xtempi;
            ytempi = y2;
            y2 = y1;
            y1 = ytempi;
        }
        for (xtempi = x1, ytempi = y1; ytempi <= y2; ytempi++) {
            rotateandshiftPt(xtempi, ytempi, obbj);
            if (Pk < 0) {
                Pk = Pk + Dxmul2;
            } else {
                Pk = Pk + Dxmul2 - Dymul2;
                if (lineSlope > 1.0)
                    xtempi = xtempi + 1;
                else if (lineSlope < -1.0)
                    xtempi = xtempi - 1;
            }
        }
    }
}
// here all the edges are iterated and drawn
void drawObj(Object obbj)
{
    int i;
    for (i = 0; i < obbj.EdgeCount; i++) {
        drawLineBresenham(obbj.edge[i].x1, obbj.edge[i].y1, obbj.edge[i].x2, obbj.edge[i].y2, obbj);
    }
}
 
// in this function, an object is updated
void updateObj(Object* obbj)
{
    obbj->translation = dinoTranslate;
 
    if (obbj->rotationState == 1) {
        obbj->theta = obbj->theta + rotSpeed;
        if (obbj->theta >= (3.14159))
            obbj->theta = obbj->theta - (2.0 * 3.14159);
        if (obbj->theta > rotateLimit)
            obbj->rotationState = -1;
 
    } else if (obbj->rotationState == -1) {
        obbj->theta = obbj->theta - rotSpeed;
 
        if (obbj->theta <= (-3.14159))
            obbj->theta = (2.0 * 3.14159) + obbj->theta;
 
        if (obbj->theta < -rotateLimit)
            obbj->rotationState = 1;
    }
}
 
// The actual function where the Dinosaur is drawn
void drawDino(void)
{
    // an infinite while loop for moving the dinosaur
    while (1) {
        glClear(GL_COLOR_BUFFER_BIT);
        // draw grassland
        glLineWidth(5.0);
        glColor3f(0.0f, 1.0f, 0.3f);
        glBegin(GL_LINES);
        glVertex2i(0, grasslandy);
        glVertex2i(maxHt, grasslandy);
        glEnd();
        glPointSize(3.0);
        glColor3f(0.9f, 0.5f, 0.6f);
        // update all parts
 
        updateObj(&Head);
        updateObj(&upBody);
        updateObj(&Tail);
        updateObj(&downBody);
        updateObj(&FlegF);
        updateObj(&FlegB);
        updateObj(&BlegF);
        updateObj(&BlegB);
 
        // draw all parts, also draw joining parts
        drawObj(Head);
        drawObj(upBody);
        drawObj(Tail);
        drawObj(downBody);
        drawObj(FlegF);
        drawObj(FlegB);
        drawObj(BlegF);
        drawObj(BlegB);
 
        dinoTranslate--; // decreased because moving forward
        if (dinoTranslate <= boundLimitL) {
            dinoTranslate = boundLimitR;
            printf("\ntranslate %d", dinoTranslate);
        }
        printf("\ntranslate %d", dinoTranslate);
        glFlush();
    }
}
 
// TAn object is stored using this function
void storeObj(char* str, Object* obbj)
{
    obbj->theta = 0.0;
 
    FILE* fp;
    fp = fopen(str, "r");
    if (fp == NULL) {
        printf("Could not open file");
        return;
    }
    obbj->EdgeCount = 0;
    int count = 0, x1, y1, x2, y2;
    while (!feof(fp)) {
        count++;
        if (count > 2) {
            x1 = x2;
            y1 = y2;
            count = 2;
        }
        if (count == 1) {
            fscanf(fp, "%d, %d", &x1, &y1);
        } else {
            fscanf(fp, "%d, %d", &x2, &y2);
            printf("\n%d, %d", x2, y2);
            obbj->edge[obbj->EdgeCount].x1 = x1;
            obbj->edge[obbj->EdgeCount].y1 = y1;
            obbj->edge[obbj->EdgeCount].x2 = x2;
            obbj->edge[obbj->EdgeCount].y2 = y2;
            obbj->EdgeCount++;
        }
    }
 
    // printf("\nPolygon stored!");
    fclose(fp);
}
 
// All parts are stored.
void storeAllParts()
{
    FILE* fp, *fp2;
    int cx, cy;
    fp = fopen("centrePts.txt", "r");
    fp2 = fopen("offsetDino.txt", "r");
    if (fp == NULL || fp2 == NULL) {
        printf("Could not open file");
        return;
    }
    // parts
    //----------------
    // head+neck
    storeObj("headDino.txt", &Head);
    fscanf(fp, "%d, %d", &cx, &cy);
    Head.cx = cx;
    Head.cy = cy;
    fscanf(fp2, "%d, %d", &cx, &cy);
    Head.xoffset = cx;
    Head.yoffset = cy;
    Head.rotationState = 1;
 
    // upper body boundary(only translation)
    storeObj("bodyupDino.txt", &upBody);
    upBody.cx = 0;
    upBody.cy = 0;
    fscanf(fp2, "%d, %d", &cx, &cy);
    upBody.xoffset = cx;
    upBody.yoffset = cy;
    upBody.rotationState = 0;
 
    // tail
    storeObj("tailDino.txt", &Tail);
    fscanf(fp, "%d, %d", &cx, &cy);
    Tail.cx = cx;
    Tail.cy = cy;
    fscanf(fp2, "%d, %d", &cx, &cy);
    Tail.xoffset = cx;
    Tail.yoffset = cy;
    Tail.rotationState = -1;
 
    // back leg front
    storeObj("backlegFDino.txt", &BlegF);
    fscanf(fp, "%d, %d", &cx, &cy);
    BlegF.cx = cx;
    BlegF.cy = cy;
    fscanf(fp2, "%d, %d", &cx, &cy);
    BlegF.xoffset = cx;
    BlegF.yoffset = cy;
    BlegF.rotationState = -1;
 
    // back leg rear
    storeObj("backlegRDino.txt", &BlegB);
    fscanf(fp, "%d, %d", &cx, &cy);
    BlegB.cx = cx;
    BlegB.cy = cy;
    fscanf(fp2, "%d, %d", &cx, &cy);
    BlegB.xoffset = cx;
    BlegB.yoffset = cy;
    BlegB.rotationState = 1;
 
    // lower body boundary(only translation)
    storeObj("bodydownDino.txt", &downBody);
    downBody.cx = 0;
    downBody.cy = 0;
    fscanf(fp2, "%d, %d", &cx, &cy);
    downBody.xoffset = cx;
    downBody.yoffset = cy;
    downBody.rotationState = 0;
 
    // front leg rear
    storeObj("frontlegRDino.txt", &FlegB);
    fscanf(fp, "%d, %d", &cx, &cy);
    FlegB.cx = cx;
    FlegB.cy = cy;
    fscanf(fp2, "%d, %d", &cx, &cy);
    FlegB.xoffset = cx;
    FlegB.yoffset = cy;
    FlegB.rotationState = -1;
 
    // front leg front
    storeObj("frontlegFDino.txt", &FlegF);
    fscanf(fp, "%d, %d", &cx, &cy);
    FlegF.cx = cx;
    FlegF.cy = cy;
    fscanf(fp2, "%d, %d", &cx, &cy);
    FlegF.xoffset = cx;
    FlegF.yoffset = cy;
    FlegF.rotationState = 1;
 
    //------------------------
    fclose(fp);
}
 
void main(int argc, char** argv)
{
 
    storeAllParts();
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(maxHt, maxWd);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Walking dinosaur");
    myInit();
    glutDisplayFunc(drawDino); // actual loop call
    glutMainLoop();
}

Salida: Esta es una captura de pantalla de muestra:

  

No olvide descargar los archivos antes de ejecutar el programa. Este artículo es una contribución de Suprotik Dey . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks. Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *