如何管理代码只显示动画一次?

时间:2011-05-11 09:47:37

标签: c++ opengl animation

我正在制作一个程序,以便在场上制作一个球 它就像,

void display()
{
/*
code to draw a field

*/
loop:1 to 5
loop:1 to 6
/*
here comes the code to animate the ball over the field

*/
}

我希望每当用户按下“right_Arrow”时整个场景都会被旋转 一些角度
问题:每当用户按下“right_Arrow”键时,它会以一定的角度旋转场景,但也会再次显示整个动画......但是我希望动画只显示一次(在最大化窗口后),然后用户可以旋转整个场景<登记/> 按某个角度,按“right_Arrow”键时不显示任何动画 我应该如何在我的代码中执行此操作?

1 个答案:

答案 0 :(得分:1)

同样,你的问题在于你在显示功能中完全做动画的错误。我延伸了我昨天给你写的一些例子;启动程序后,动画将播放5秒钟,然后停止。按[R]重置动画(因此再次启动),按[+] / [ - ]绕Y轴旋转场景。

http://homepages.physik.uni-muenchen.de/~Wolfgang.Draxinger/stuff/sinsphere_rot.c 编辑:评论

/* This is ANSI-C - don't try to compile with a C++ compiler, it will fail! */
#include <GL/glut.h>

#include <stdlib.h>

#include <sys/time.h>
#include <math.h>

#define M_PI    3.1415926535897932384626433832795029L
#define M_PI_2  1.5707963267948966192313216916397514L

# define timersub(a, b, result)                                               \
  do {                                                                        \
    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
    if ((result)->tv_usec < 0) {                                              \
      --(result)->tv_sec;                                                     \
      (result)->tv_usec += 1000000;                                           \
    }                                                                         \
  } while (0)

void idle(void);
void animate(float dT);
void display(void);
void keyboard(unsigned char key, int x, int y);
void init_sphere(unsigned int rings, unsigned int sectors);
void draw_sphere(void);

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutCreateWindow("SinSphere");
    glutIdleFunc(idle);
    glutKeyboardFunc(keyboard);
    glutDisplayFunc(display);

    init_sphere(10, 30);

    glutMainLoop();

    return 0;
}

这只是样板代码,没什么特别的。

struct AnimationState
{
    float time;
    float duration;
    float sphere_speed;
    float sphere_path_radius;
    float sphere_path_bobbing;
    float sphere_position[3];
};

static struct AnimationState animation = {
    0.,
    5., /* play for 5 seconds */
    0.1, 3., 1.,
    {1., 0., 0.}
};

AnimationState获得了一个额外的元素duration;在那段时间之后,动画将停止播放,通过测试是否animation.time < animation.duration,如果是,则仅推进动画步骤。

void animate(float dT)
{
    if(animation.time < animation.duration) {
        animation.time += dT;

        animation.sphere_position[0] = animation.sphere_path_radius * cos(2*M_PI * animation.time * animation.sphere_speed);
        animation.sphere_position[1] = animation.sphere_path_bobbing * sin(2*M_PI * animation.time * 5 * animation.sphere_speed);
        animation.sphere_position[2] = animation.sphere_path_radius * sin(2*M_PI * animation.time * animation.sphere_speed);
    }
}

struct ViewState {
    float rotation;
    float rotation_step;
};

static struct ViewState view = {
    0.,
    0.1
};

ViewState存储旋转。这实际上是一个愚蠢的版本,通常通过视图转换matix和steppings来实现。

void keyboard(unsigned char key, int x, int y)
{
    switch(key) {
    case 'R':
    case 'r': /* restart animation */
        animation.time = 0.;
        break;

    case '+':
        view.rotation += view.rotation_step;
        break;

    case '-':
        view.rotation -= view.rotation_step;
        break;
    }
    glutPostRedisplay();
}

键盘处理函数应该是显而易见的。

GLfloat *sphere_vertices_normals;
unsigned int sphere_quads = 0;
GLushort *sphere_indices;

void init_sphere(unsigned int rings, unsigned int sectors)
{
    float const R = 1./(float)(rings-1);
    float const S = 1./(float)(sectors-1);
    int r, s;

    sphere_vertices_normals = malloc(sizeof(GLfloat)*3 * rings*sectors);

    GLfloat *v = sphere_vertices_normals;
    for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
        float const y = sin( -M_PI_2 + M_PI * r * R );

        float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );

        float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

        v[0] = x;
        v[1] = y;
        v[2] = z;

        v+=3;
    }

    sphere_indices = malloc(sizeof(GLushort) *  rings * sectors * 4);
    GLushort *i = sphere_indices;
    for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
        *i++ = r * sectors + s;
        *i++ = r * sectors + (s+1);
        *i++ = (r+1) * sectors + (s+1);
        *i++ = (r+1) * sectors + s; 
        sphere_quads++;
    }
}

init_sphere构建一些包含球体几何的漂亮顶点和索引数组。为读者练习:将其放在顶点缓冲区对象中。

void draw_sphere()
{
    glTranslatef(animation.sphere_position[0], animation.sphere_position[1], animation.sphere_position[2]);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, sphere_vertices_normals);
    glNormalPointer(GL_FLOAT, 0, sphere_vertices_normals);
    glDrawElements(GL_QUADS, sphere_quads*4, GL_UNSIGNED_SHORT, sphere_indices);
}

void idle()
{
    glutPostRedisplay();
}

在处理完所有输入事件后调用idle函数。输入事件是按键等。只有在显示处理程序返回后才会处理GLUT事件。因此,您不得在显示处理程序中实现动画计时器循环。相反,您确定单个显示的时间,然后按该时间步长推进循环以进行下一次显示迭代。 idle在事件处理后启动下一个显示传递。

static GLfloat const light_pos[4] = {-1., 1., 1., 0.};
static GLfloat const light_color[4] = {1., 1., 1., 1.};

void display()
{
    static struct timeval delta_T = {0., 0.};
    struct timeval time_frame_begin, time_frame_end;

    int win_width, win_height;
    float win_aspect;

    gettimeofday(&time_frame_begin, 0);

    animate(delta_T.tv_sec + delta_T.tv_usec * 1.e-6);

    win_width = glutGet(GLUT_WINDOW_WIDTH);
    win_height = glutGet(GLUT_WINDOW_HEIGHT);
    win_aspect = (float)win_width/(float)win_height;

    glViewport(0, 0, win_width, win_height);
    glClearColor(0.6, 0.6, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-win_aspect, win_aspect, -1., 1., 1., 10.);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0,0,-5.5);

    glRotatef(view.rotation * 180./M_PI, 0, 1, 0);

    glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);

    glPushMatrix();

    glEnable(GL_DEPTH_TEST);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    draw_sphere();

    glPopMatrix();

    glutSwapBuffers();

    gettimeofday(&time_frame_end, 0);
    timersub(&time_frame_end, &time_frame_begin, &delta_T);

}

不是衡量display花费的时间,而是更精确的方法是测量每次调用display之间的时间,以便花费在程序其他部分花费的时间。这是一个留给读者的练习。

相关问题