开发者

How to manage code to display animation only once?

i am making a program to animate a ball over a field it goes like,

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

*/
}

I want whenever user press "right_Arrow" the whole scene get rotated by

some angle

problem:whenever the user presses "right_Arrow" key it rotate the scene by some angle but also display whole animation again...but i want animation to be displayed only once(after maximazing window) and then the user can rota开发者_如何学JAVAte the whole scene

by some angle without displaying any animation on pressing "right_Arrow" key

How should i do this in my code?


Again your problem lies in your mistake to do the animation completely in the display function. I extended my example I wrote you yesterday a bit; After starting the program, the animation will play for 5 seconds, then stop. Pressing [R] resets the animation (thus starts it again), pressing [+] / [-] rotates the scene around the Y axis.

http://homepages.physik.uni-muenchen.de/~Wolfgang.Draxinger/stuff/sinsphere_rot.c EDIT: commenting

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

This is all just boilerplate code, nothing special.

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 got an additional element duration; after that time the animation will stop playing, by testing if animation.time < animation.duration and only advance the animation step if so.

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 stores the rotation. This is really a dumbed down version, usually implements this through a view transformation matix and steppings for that.

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

Keyboard handler function should be obvious.

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 build us some nice vertex and index array containing sphere geometry. Exercise for the reader: Put it in a Vertex Buffer Object.

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

The idle function gets called after all input events have been processed. Input events are keypresses and the like. GLUT events only get processed after the display handler returns. So you must not implement the animation timer loop within the display handler. Instead you determine the time for a single display, then advance the loop by that timestep for the next display iteration. idle initiate the next display pass after event processing.

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

}

Instead of measureing the time spent in display a more accurate approach is measuring the time between each invocation of display to take time spent in other parts of the programm into account. This is an exercise left to the reader.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜