simple 2d animation in glut

2019-08-14 06:48发布

I have an assignment to do, but i can't seem to trully comprehend it. The assignment is as follows : Adding a texture to background (solar system), adding texture to 2 objects (drawn shapes) and adding an animation where the two objects have to bounce from eachother & from far walls (as in end of screen).

I have managed to do everything except the animation. How can I do this kind of animation? p.s. animation in there is the best i could come up with.

#include <gl/glut.h>
#include <gl/gl.h >
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>


float x;
float y;



unsigned char *imageData;
int imageRows, imageCols;

extern void loadBMP(char *);
char cotton1[] = "cotton1.bmp";
char cotton2[] = "cotton2.bmp";
char fons[] = "solar.bmp";

GLuint texture[3];
float cube[1], Vcube[1];

/* GLUT callback Handlers */

void init()
{
cube[0]=0;
Vcube[0]=0.01;
cube[1]=0;
Vcube[1]=0.01;


glShadeModel(GL_SMOOTH);

glGenTextures( 3, &texture[0] );

   loadBMP(cotton1);
   glBindTexture( GL_TEXTURE_2D, texture[0] );
   glTexImage2D(GL_TEXTURE_2D, 0, 3, imageCols, imageRows,
         0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

     loadBMP(cotton2);
   glBindTexture( GL_TEXTURE_2D, texture[1] );
   glTexImage2D(GL_TEXTURE_2D, 0, 3, imageCols, imageRows,
         0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

        loadBMP(fons);
   glBindTexture( GL_TEXTURE_2D, texture[2] );
   glTexImage2D(GL_TEXTURE_2D, 0, 3, imageCols, imageRows,
         0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

}

static void 
resize(int width, int height)
{
    const float ar = (float) width / (float) height;

    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity() ;
}

static void 
idle(void)
{
    glutPostRedisplay();
}

void animation()
{
cube[1]+=Vcube[1];
if (cube[1]<0.1)
{ Vcube[1]+=Vcube[1]; }
if (cube[1]>0.095)
{ Vcube[1]=-0.01; }
if (cube[1]<0)
{ Vcube[1]=+0.01; }

glTranslatef(cube[1],0,0);
       Sleep(100);
       glutPostRedisplay();
}


void animation2()
{
  cube[0]+=Vcube[0];
if (cube[0]<(-0.1))
{ Vcube[0]-=0.01; }
if (cube[0]>0)
{ Vcube[0]-=0.01; }
if (cube[0]<0.1)
{ Vcube[0]+=0.01; }


glTranslatef(cube[0],0,0);
       Sleep(100);
       glutPostRedisplay();
}

void display() {


    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);  


//Background
    glLoadIdentity();
    glBindTexture( GL_TEXTURE_2D, texture[2]);
    glEnable( GL_TEXTURE_2D );

    glPushMatrix();
     glBegin( GL_QUADS );
      glTexCoord2f(1.0,1.0); glVertex2f(-1.0,1.0);
      glTexCoord2f(0.0,1.0); glVertex2f(1.0,1.0);
      glTexCoord2f(0.0,0.0); glVertex2f(1.0,-1.0);
      glTexCoord2f(1.0,0.0); glVertex2f(-1.0,-1.0);
      glEnd();
      glPopMatrix();
    glDisable(GL_TEXTURE_2D);

  animation();

//TEXTURE 1
    glBindTexture( GL_TEXTURE_2D, texture[0]);

    glEnable( GL_TEXTURE_2D );

    glPushMatrix();
    glBegin( GL_TRIANGLE_FAN );
glTexCoord2f(0.5f, 0.5f);  glVertex2f(  0.5f,  0.0f);   //center
glTexCoord2f(1.0f, 0.5f);  glVertex2f(  0.8f+x,  0.0f);   //right
glTexCoord2f(0.75f, 1.0f); glVertex2f(  0.55f+x, 0.3f+x);  //top right
glTexCoord2f(0.25f, 1.0f); glVertex2f(  0.35f-x, 0.3f+x);   //Top left
glTexCoord2f(0.0f, 0.5f);  glVertex2f(  0.25f-x, 0.0f);   //left
glTexCoord2f(0.25f, 0.0f); glVertex2f(  0.45f-x,-0.3f-x);   //bottom left
glTexCoord2f(0.75f, 0.0f); glVertex2f(  0.7f+x, -0.2f-x);   //bottom right
glTexCoord2f(1.0f, 0.5f);  glVertex2f(  0.8f+x,  0.0f);   //right
      glEnd();
      glPopMatrix();
    glDisable(GL_TEXTURE_2D);
//TEXTURE 2

    animation2();   
    glBindTexture( GL_TEXTURE_2D, texture[1]);

    glEnable( GL_TEXTURE_2D );
    glPushMatrix();
    glBegin( GL_TRIANGLE_FAN );
glTexCoord2f(0.5f, 0.5f);  glVertex2f( -0.5f,  0.0f);  //center
glTexCoord2f(1.0f, 0.5f);  glVertex2f( -0.2f+y,  0.0f);  //right
glTexCoord2f(0.75f, 1.0f); glVertex2f( -0.4f+y,  0.2f+y);  //top right
glTexCoord2f(0.25f, 1.0f); glVertex2f( -0.7f-y,  0.1f+y);  //Top left
glTexCoord2f(0.0f, 0.5f);  glVertex2f( -0.8f-y,  0.0f);  //left
glTexCoord2f(0.25f, 0.0f); glVertex2f( -0.7f-y, -0.1f-y);  //bottom left
glTexCoord2f(0.75f, 0.0f); glVertex2f( -0.3f+y, -0.2f-y);  //bottom right
glTexCoord2f(1.0f, 0.5f);  glVertex2f( -0.2f+y,  0.0f);  //right
      glEnd();
      glPopMatrix();
    glDisable(GL_TEXTURE_2D);
glutSwapBuffers();
glFlush();
}


static void 
key(unsigned char key, int a, int b)
{
    switch (key) 
    {
        case 27 : 
        case 'q':
            exit(0);
            break;

        case '+':
             if ((x+0.01)<0.98)
             x=x+0.01;
             if ((y+0.01)<0.98)
             y=y+0.01;         
            break;

        case '-':
             if ((x-0.1)>(-0.15))
                x=x-0.01;
             if ((y-0.1)>(-0.10))
                y=y-0.01;
           break;

        case 'o':
             if ((x+0.01)<0.98)
             x=x+0.01;
             break;
        case 'p':
             if ((x-0.1)>(-0.15))
             x=x-0.01;
             break;


        case '[':
             if ((y+0.01)<0.98)
             y=y+0.01;
             break;
        case ']':
             if ((y-0.1)>(-0.10))
             y=y-0.01;
             break;



    }
    glutPostRedisplay();
}

int main(int argc, char *argv[]) {
   glutInit(&argc, argv);   

   glutInitWindowSize(640, 640);   
   glutInitWindowPosition(50, 50); 
   glutCreateWindow("Assignment number 3"); 
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);



   glutReshapeFunc(resize);
   glutDisplayFunc(display);      
   glutKeyboardFunc(key);         
   glutIdleFunc(idle);

   glClearColor(1.0, 1.0, 1.0, 1.0);

   init();

   glutMainLoop();               
   return EXIT_SUCCESS;
}

1条回答
Viruses.
2楼-- · 2019-08-14 07:02

Problem 1: You mistake OpenGL for a scene graph. Take your animation1 function for example:

void animation2(
     )
{
    cube[0] += Vcube[0];
    if( cube[0] < ( -0.1 ) ) {
        Vcube[0] -= 0.01;
    }
    if( cube[0] > 0 ) {
        Vcube[0] -= 0.01;
    }
    if( cube[0] < 0.1 ) {
        Vcube[0] += 0.01;
    }


    glTranslatef( cube[0], 0, 0 );
    Sleep( 100 );
    glutPostRedisplay(  );
}

That glTranslatef there at the end will just trash around on whatever matrix is currently active in the OpenGL context. That's not how to do it.

Next problem: You're calling the animation functions from the drawing code. At the point of drawing all the scene state should be determined. Also, calling that animation function will sleep in your display function. That's not how to do it.

Okay, what to do: First put all the animation progressor functions into the idle loop. Don't sleep, instead measure the time between animation iterations and advance the animation accordingly. Don't call glutPostRedisplay in the animation functions. At the end of the idle handler yes, but not in the animators. In the drawing code use the evaluated animation state to place objects accordingly. Use the matrix stack (glPushMatrix, glPopMatrix) to keep things nicely separated.


#include <GL/glut.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* for gettimeofday */
#include <sys/time.h>

/* In general, littering your program with global variables should be avoided.
 * I admit, that sometimes even I don't adhere to this rule, and especially
 * using GLUT it takes to jump several very arcane hoops to avoid it.
 * So in this case, yes, having global variables is in order.
 *
 * The principle idea of global variables is to put data into them, that is
 * valid and the same for the whole of the program. More importantly they
 * must not be used to pass around data.
 *
 * It's also a good idea to make those variables static, so that they are
 * contained withing this compilation unit.
 */
static float x;
static float y;

/* This is not how globals should be used. They're used to pass data around between
 * functions. DON'T DO THAT!
 *
 * Also this misses the extern keyword. Unless the compilation unit loading the
 * bmp files declares those being extern, hence relying on another compilation unit
 * to expose them like this, this code is likely to break.

unsigned char *imageData;
int imageRows, imageCols;

extern void loadBMP(char *); 

 * BTW: You don't need the extern keyword here.

 * Instead have a nice little function that loads a BMP file and puts it into a
 * newly allocated texture object.
*/

GLuint loadBmpToTexture(char const * const filename)
{
    /* Implementation of this left as an exercise to the reader */
    return 0;
}

static double ftime(void)
{
    /* Now this is a bit complicated: There's no portable high resolution
     * timer function. On Linux and Unices (hence also MacOS X) you have
     * gettimeofday, on Windows there are the High Performance Counters.
     * ... Totally annoying. 
     * Look here for a comparison:
     * http://www.songho.ca/misc/timer/timer.html
     *
     * Since I'm on a Linux box this is using gettimeofday
     */

    struct timeval t;
    gettimeofday(&t, NULL);

    return 1.0*t.tv_sec + 1e-6*t.tv_usec;
}

/* In this variable we store the time of the last iteration of the animation
 * loop to determine the time to time difference for the next one. */
static double last_T;

/* Actually those should be of type char const * const
 * This is one of the finer details of C. The arrays like you've declared them
 * here are mutable, but of constant size.
 * However you normally don't want string constant be like this. The preferred
 * modus operandi is to have the string constants in read only memory and pointers
 * to them. Like this:
 */
char const * const cotton1 = "cotton1.bmp";
char const * const cotton2 = "cotton2.bmp";
char const * const fons = "solar.bmp";

/* Okay, now consider what would happen if you had several objects, not just two or
 * three? How would you keep track of all those indices? Really, that's bad style.
 * If you've data belonging together, like state of an object, put it into a struct
 * and then also use useful variable names.
 */

GLuint texture_background;

typedef struct s_Cube {
    float x, V_x;
    GLuint texture;
} Cube;

/* also we can statically initialize here */
Cube cube[2] = {
    {-0.05, 0.01, 0},
    {0.05, -0.02, 0}
};

/* GLUT callback Handlers */

static void init(void)
{
    /* loadBmpToTexture is defined to return 0 in case of failure
     * which is also the OpenGL default texture object, so this
     * fails safely. */
    texture_background = loadBmpToTexture(fons);
    cube[0].texture = loadBmpToTexture(cotton1);
    cube[1].texture = loadBmpToTexture(cotton2);

    glClearColor( 0.0, 0.5, 0.7, 1.0 );

    last_T = ftime();
}

static void animation(
    float const speed
     )
{
    /* The objective is to let the cubes bounce into each other
     * (collision) and with the walls. First the collision: */

    if( cube[0].x > cube[1].x && cube[0].V_x > 0 && cube[1].V_x < 0 ) {
        /* cubes bounced off each other. Exchange their velocities */
        double const V_x = cube[0].V_x;
        cube[0].V_x = cube[1].V_x;
        cube[1].V_x = V_x;

        double const x = cube[0].x;
        cube[0].x = cube[1].x;
        cube[1].x = x;
    }

    /* and the wall bounce */
    if( cube[0].x < -0.1 && cube[0].V_x < 0 ) {
        /* left cube bounced into left wall */
        cube[0].V_x *= -1;
    }

    if( cube[1].x > 0.1 && cube[1].V_x > 0 ) {
        /* right cube bounced into left wall */
        cube[1].V_x *= -1;
    }

    cube[0].x += speed * cube[0].V_x;
    cube[1].x += speed * cube[1].V_x;
}

/* Ideally we'd use a precise animation loop interleaved with event processing here.
 * Unfortunately GLUT doesn't offer those, so we use this arcane kludge.
 *
 * It would get a bit more robust by putting the whole timing into the display function
 * but better abandon GLUT and get a true event loop.
 */
static void idle(
    void )
{
    const double now_T = ftime();
    const double delta_T = now_T - last_T;
    last_T = now_T;

    const double speed = delta_T * 60;

    animation( speed );
    glutPostRedisplay(  );
}


static void display(void)
{
    /* We try to be as stateless as possible. Yes, in the face of a statefull
     * API, like OpenGL, this may sound a bit pedantic. */
    const int width = glutGet(GLUT_WINDOW_WIDTH);
    const int height = glutGet(GLUT_WINDOW_HEIGHT);
    const float ar = ( float ) width / ( float ) height;

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    /* It's really best practice to set everything related to drawing
     * – and that includes the projection – in the drawing function */
    glViewport( 0, 0, width, height );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glOrtho(-ar, ar, -1, 1, -1, 1);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
//Background
    if(texture_background) {
        glBindTexture( GL_TEXTURE_2D, texture_background );
        glEnable( GL_TEXTURE_2D );
        glBegin( GL_QUADS );
        glTexCoord2f( 1.0, 1.0 );
        glVertex2f( -1.0, 1.0 );
        glTexCoord2f( 0.0, 1.0 );
        glVertex2f( 1.0, 1.0 );
        glTexCoord2f( 0.0, 0.0 );
        glVertex2f( 1.0, -1.0 );
        glTexCoord2f( 1.0, 0.0 );
        glVertex2f( -1.0, -1.0 );
        glEnd();
        glDisable( GL_TEXTURE_2D );
    }

//TEXTURE 1
    glBindTexture( GL_TEXTURE_2D, cube[1].texture );
    glEnable( GL_TEXTURE_2D );
    /* Remember we're still in modelview matrix mode.
     * This push creates a copy of the currently modelview matrix,
     * for our disposal. With a following pop we restore to the
     * state saved now. Pushes and Pops nest. */
    glPushMatrix();

    /* This applies our animation position to the modelview matrix.
     * All geometry drawing to follow is subject to this additional
     * transformation, until the matrix changes again. */
    glTranslatef(cube[1].x, 0, 0);
    glBegin( GL_TRIANGLE_FAN );
    glTexCoord2f( 0.5f, 0.5f );
    glVertex2f( 0.5f, 0.0f );   //center
    glTexCoord2f( 1.0f, 0.5f );
    glVertex2f( 0.8f + x, 0.0f );   //right
    glTexCoord2f( 0.75f, 1.0f );
    glVertex2f( 0.55f + x, 0.3f + x );  //top right
    glTexCoord2f( 0.25f, 1.0f );
    glVertex2f( 0.35f - x, 0.3f + x );  //Top left
    glTexCoord2f( 0.0f, 0.5f );
    glVertex2f( 0.25f - x, 0.0f );  //left
    glTexCoord2f( 0.25f, 0.0f );
    glVertex2f( 0.45f - x, -0.3f - x ); //bottom left
    glTexCoord2f( 0.75f, 0.0f );
    glVertex2f( 0.7f + x, -0.2f - x );  //bottom right
    glTexCoord2f( 1.0f, 0.5f );
    glVertex2f( 0.8f + x, 0.0f );   //right
    glEnd(  );
    glPopMatrix(  );
    glDisable( GL_TEXTURE_2D );
//TEXTURE 2
    /* in the original code you didn't use the other texture, 
     * Probably because you lost track of variables and indices. */
    glBindTexture( GL_TEXTURE_2D, cube[0].texture ); 
    glEnable( GL_TEXTURE_2D );
    glPushMatrix();
    glTranslatef(cube[0].x, 0, 0);
    glBegin( GL_TRIANGLE_FAN );
    glTexCoord2f( 0.5f, 0.5f );
    glVertex2f( -0.5f, 0.0f );  //center
    glTexCoord2f( 1.0f, 0.5f );
    glVertex2f( -0.2f + y, 0.0f );  //right
    glTexCoord2f( 0.75f, 1.0f );
    glVertex2f( -0.4f + y, 0.2f + y );  //top right
    glTexCoord2f( 0.25f, 1.0f );
    glVertex2f( -0.7f - y, 0.1f + y );  //Top left
    glTexCoord2f( 0.0f, 0.5f );
    glVertex2f( -0.8f - y, 0.0f );  //left
    glTexCoord2f( 0.25f, 0.0f );
    glVertex2f( -0.7f - y, -0.1f - y ); //bottom left
    glTexCoord2f( 0.75f, 0.0f );
    glVertex2f( -0.3f + y, -0.2f - y ); //bottom right
    glTexCoord2f( 1.0f, 0.5f );
    glVertex2f( -0.2f + y, 0.0f );  //right
    glEnd();
    glPopMatrix();
    glDisable( GL_TEXTURE_2D );

    glutSwapBuffers();
    /* Your glFinish here was totally pointless.
     * First it would belong _before_ glutSwapBuffers.
     * Second glutSwapBuffers implies a glFinish, so it's totally redundant. */
}


static void key(
    unsigned char key,
    int a,
    int b )
{
    switch ( key ) {
    case 27:
    case 'q':
        exit( 0 );
        break;

    case '+':
        if( ( x + 0.01 ) < 0.98 )
            x = x + 0.01;
        if( ( y + 0.01 ) < 0.98 )
            y = y + 0.01;
        break;

    case '-':
        if( ( x - 0.1 ) > ( -0.15 ) )
            x = x - 0.01;
        if( ( y - 0.1 ) > ( -0.10 ) )
            y = y - 0.01;
        break;

    case 'o':
        if( ( x + 0.01 ) < 0.98 )
            x = x + 0.01;
        break;
    case 'p':
        if( ( x - 0.1 ) > ( -0.15 ) )
            x = x - 0.01;
        break;


    case '[':
        if( ( y + 0.01 ) < 0.98 )
            y = y + 0.01;
        break;
    case ']':
        if( ( y - 0.1 ) > ( -0.10 ) )
            y = y - 0.01;
        break;
    }
    glutPostRedisplay();
}

int main(
    int argc,
    char *argv[] )
{
    glutInit( &argc, argv );

    glutInitWindowSize( 640, 640 );
    glutInitWindowPosition( 50, 50 );
    /* glutInitDisplayMode must be called before calling glutCreateWindow
     * GLUT, like OpenGL is stateful */
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
    glutCreateWindow( "Assignment number 3" );

    glutDisplayFunc( display );
    glutKeyboardFunc( key );
    glutIdleFunc( idle );

    init();

    glutMainLoop(  );
    return EXIT_SUCCESS;
}
查看更多
登录 后发表回答