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:
- cabezaDino
- PoliDino
- colaDino
- backlegFDino
- RespaldoRDino
- bodydownDino
- bodyupDino
- pierna delanteraFDino
- pata delanteraRDino
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:
- Se iniciará la ventana de OpenGL
- Todos los archivos se leerán y almacenarán en sus respectivos objetos.
- Se iniciará un ciclo while infinito
- La pantalla se borrará
- Se dibujará una línea que representa la pradera.
- Todas las partes serán actualizadas.
- Todas las partes serán dibujadas.
- El valor de traducción del cuerpo se reducirá y, si el dinosaurio está fuera de la ventana, se reiniciará.
- 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