Rome_Leader
Rome_Leader

Reputation: 2700

Worm-Crawling Motion in OpenGL?

I am trying to work out an efficient method of performing the following behavior in OpenGL.

I am animating a worm-like fellow (basically I want a rectangle with a visible eye of some sort - nothing complicated) that should move by expanding to twice its length at the front, and retracting to its original size from the back. The worm will reverse direction when it reaches the window edge.

I am having major difficulty in getting the motion correct, and somewhat minor difficulty positioning the eye. If I simply draw two polygons one after the other with their relative positions, can I expect the latter to be painted over the former? My motion at the moment presents an infinite loop which I am having trouble altering to another method conceptually. I cannot figure out the loop structure to have the doubling/contracting motion and approach termination efficiently.

Current code below:

#define GLUT_DISABLE_ATEXIT_HACK
#include <GL/glut.h>
#include <GL/gl.h>

float wormX = 0;
float wormY = 0;
float wormWidth = 100;
float wormHeight = 75;
bool wormCrawl = true;

void timer( int value )
{
const int w = glutGet(GLUT_WINDOW_WIDTH);
if( wormX + wormWidth >= w / 2 ) //If worm hits edge, reverse direction!
{
    wormX = 2;
}

while( wormCrawl )
{
    wormX += 2; //Move the worm
    wormWidth = 2*wormWidth; //Extend the front
    wormWidth = (1/2)*wormWidth; //Retract the back

}

glutTimerFunc( 16, timer, 0 );
glutPostRedisplay();
}

void display()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);

const int w = glutGet(GLUT_WINDOW_WIDTH);
const int h = glutGet(GLUT_WINDOW_HEIGHT);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho( -w/2, w/2, -h/2, h/2, -1.0, 1.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

const float leftCorner = wormX;
const float rightCorner = leftCorner + wormWidth;
const float bottomCorner = wormY;
const float topCorner = bottomCorner + wormHeight;
//Worm
glBegin(GL_POLYGON);
    glColor3f(0.0, 1.0, 0.0);
    glVertex2d(leftCorner, topCorner);
    glVertex2d(rightCorner, topCorner);
    glVertex2d(rightCorner, bottomCorner);
    glVertex2d(leftCorner, bottomCorner);
glEnd();
//Worm eye
glBegin(GL_POLYGON);
    glColor3f(0.0, 0.0, 0.0);
    glVertex2d(leftCorner, topCorner);
    glVertex2d(leftCorner + 10, topCorner);
    glVertex2d(leftCorner + 10, bottomCorner);
    glVertex2d(leftCorner, bottomCorner);
glEnd();

glutSwapBuffers();
}

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(0, 0);
glutCreateWindow ("Inchworm Crawl");

glutDisplayFunc(display);
glutTimerFunc( 0, timer, 0 );
glutMainLoop();

return 0;
}

Upvotes: 0

Views: 748

Answers (1)

Daniel
Daniel

Reputation: 588

so first off, we want to make life much easier, you should try abstracting your worm to a class. I'm assuming you probably want to carry on developing on this afterwards but in any case its still good practise. This will also make our draw calls much easier.

Okay, so assuming we now have our Worm class that class will have its own draw function, we want to try to keep our draw calls in the display() function in our main as simple as possible. Imagine what you would have to do if you wanted to draw another worm.

so in your worm class your draw() method will look something like this:

void Worm::draw()
{
    const float leftCorner = wormX;
    const float rightCorner = leftCorner + wormWidth;
    const float bottomCorner = wormY;
    const float topCorner = bottomCorner + wormHeight;
    //Worm
    glBegin(GL_POLYGON);
        glColor3f(0.0, 1.0, 0.0);
        glVertex2d(leftCorner, topCorner);
        glVertex2d(rightCorner, topCorner);
        glVertex2d(rightCorner, bottomCorner);
        glVertex2d(leftCorner, bottomCorner);
    glEnd();
    //Worm eye
    glBegin(GL_POLYGON);
        glColor3f(0.0, 0.0, 0.0);
        glVertex2d(leftCorner, topCorner);
        glVertex2d(leftCorner + 10, topCorner);
        glVertex2d(leftCorner + 10, bottomCorner);
        glVertex2d(leftCorner, bottomCorner);
    glEnd();
}

Okay so that's just the basics of it, but I still haven't answered your actual question. I'm not sure which version of freeglut you are using, but hopefully you have the latest. In general if you are planning on doing more than just rendering, you should try to stay away from glutmainloop();. It basically stops you from being able to do any logic outside of your render loop, which effectively means everything in your program is going to be locked to the frame rate. instead you should look at writing a game loop in your main function which then calls glutmainloopevent() each time you want to render a frame.

So now more onto the movement, once you have created your game loop, logically you can put all of your update functionality in here, and at the end of the loop, you can call glutmainloopevent() note that this loop will also be a semi-infinite loop. It will run while some flag is true and then you can terminate it once your worm has wriggled around enough. :P

So now to our worm class we can add some more functionality for moving our worm and increasing his width we can call it something like move(). And also to simplify things we can add a flag to our worm that tells us if he should be shrinking or growing. This will just be a Boolean that is initialized to false on creation of our worm object.

void Worm::move(bool direction)
{
    if(growing)
    {
        wormWidth += 1;
        if(wormWidth > thresholdWidth)
        {
            growing = false;
        }
    }
    else
    {
        wormWidth += 1;
        if(wormWidth > thresholdWidth)
        {
            growing = false;
        }
    }
    if(direction)
    {
        wormx += 1;
    }
    else
    {
        wormx -= 1;
    }
}

Now I haven't tested this could, but I would imagine that it should do something close to what you are looking for. I don't currently have glut setup on my work pc nor openGL for that matter. But if this doesn't do exactly what you are looking for, just drop me some comments I can take a look when I get home from work.

One last thing to note, you will call your move function in your game loop as well, for now for your game loop you can probably just have a while loop doing this, but eventually you can set it to only update X amount of times per second and draw Y amount of times per second.

One thing to note is that because you aren't drawing more than one thing per frame its not going to make a difference. You can just keep drawing your worm, that's what the gLClear() function is doing for you, it is clearing everything currently in the color buffer, if you were busy with an application that explicitly used 3D you would need to add the depth buffer as well, otherwise things wouldn't be drawn in the correct order and you might get z-fighting or some things being draw over by things that are actually behind them.

Hope I didn't get to tangential and that this post can be of some value to you!

Goodluck!

Upvotes: 1

Related Questions