glut 中的简单 2d 动画 [英] simple 2d animation in glut

查看:37
本文介绍了glut 中的简单 2d 动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一项任务要做,但我似乎无法真正理解它.分配如下:向背景(太阳系)添加纹理,向 2 个对象(绘制的形状)添加纹理,并添加两个对象必须相互反弹的动画.从远处的墙壁(如屏幕末端).

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:您将 OpenGL 误认为是场景图.以您的 animation1 函数为例:

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(  );
}

最后的 glTranslatef 只会在 OpenGL 上下文中当前活动的任何矩阵上乱扔垃圾.这不是怎么做的.

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.

好的,怎么做:首先将所有动画进度器函数放入空闲循环中.不要睡觉,而是测量动画迭代之间的时间并相应地推进动画.不要在动画函数中调用 glutPostRedisplay.在空闲处理程序的末尾是,但不是在动画师中.在绘图代码中,使用评估的动画状态相应地放置对象.使用矩阵堆栈(glPushMatrix、glPopMatrix)将事物很好地分开.

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;
}

这篇关于glut 中的简单 2d 动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆