5

I am making a rollercoaster inside of a skybox in OpenGL, and without much background on it's functions or computer graphics it is proving to be very difficult. I drew a rollercoaster using Catmull-Rom spline interpolation, and drew each point with glVertex3f. Now I want to call an update() function every 50ms to move the camera around the track. gluLookAt() is producing weird results, either removing the track from the screen, producing a black screen, etc. I think I need to move some of the matrix functions around but I am not sure where to put each one. Here is my code so far:

int main(int argc, char** argc)
{
    // ... load track, etc ...

    // Init currpos, nextpos, iter, up
    currpos = Vec3f(0, 0, 0);
    nextpos = currpos;
    iter = 0;
    up = Vec3f(0, 1, 0);

    deque<Vec3f> points;
    Vec3f newpt;

    // Loop through the points and interpolate 
    for (pointVectorIter pv = g_Track.points().begin(); pv != g_Track.points().end(); pv++)
    {
        Vec3f curr(*pv);            // Initialize the current point and a new point (to be drawn)
        points.push_back(curr);     // Push the current point onto the stack
        allpoints.push_back(curr);  // Add current point to the total stack

        if (points.size() == 4) // Check if there are 4 points in the stack, if so interpolate
        {
            for (float u = 0.0f; u < 1.0f; u += 0.01f)
            {
                newpt = interpolate(points[0], points[1], points[2], points[3], u);
                glColor3f(1, 1, 1);
                glVertex3f(newpt.x(), newpt.y(), newpt.z());

                allpoints.push_back(newpt);
            }

            points.pop_front();
        }
    }

    // glutInit, InitGL(), etc...
}

void InitGL(GLvoid)
{
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(100.0, (GLfloat)WINDOW_WIDTH / (GLfloat)WINDOW_HEIGHT, .0001, 999999);
    glMatrixMode(GL_MODELVIEW);
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
}

void display (void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(currpos.x(), currpos.y(), currpos.z(), nextpos.x(), nextpos.y(), nextpos.z(), up.x(), up.y(), up.z());

    glPushMatrix();
    glEnable(GL_TEXTURE_2D); // Enable texturing from now on

    /* draw skybox, this was from previous assignment and renders correctly */

    glPopMatrix();

    // now draw rollercoaster ...

    glPushMatrix();
    glBegin(GL_LINE_STRIP);

    deque<Vec3f> points;
    Vec3f newpt;

    for each (Vec3f pt in allpoints)
    {
        glColor3f(1, 1, 1);
        glVertex3f(pt.x(), pt.y(), pt.z());
    }

    glutTimerFunc(50, update, 1);
    glEnd();
    glPopMatrix();

    // Swap buffers, so one we just drew is displayed
    glutSwapBuffers();
}

void update(int a)
{
    if (iter < allpoints.size())
    {
        currpos = allpoints[iter];
        nextpos = allpoints[iter + 1];

        gaze = nextpos - currpos;
        gaze.Normalize();

        Vec3f::Cross3(binorm, gaze, up);
        binorm.Normalize();

        Vec3f::Cross3(up, binorm, gaze);
        up.Normalize();

        glutPostRedisplay();
    }

    iter++;
}

The idea is that I am keeping a global deque allpoints that includes the control points of the spline and the interpolated points. Once that is complete, I call update() every 50ms, and move the camera along each point in allpoints. In a previous version of the project, I could see that the rollercoaster was being drawn correctly. It is gluLookAt() that doesn't seem to work how I want it to. With the code above, the program starts with the camera looking at one side of the skybox with a part of the rollercoaster, and then when update() is called, the rollercoaster disappears but the camera does not move. I have been messing around with where I am putting the OpenGL matrix functions, and depending on where they are sometimes update() will cause a blank screen as well.

Logan Serman
  • 29,447
  • 27
  • 102
  • 141

4 Answers4

6

Besides the absence of glPopMatrix (which user971377 already spotted), you call glLoadIdentity in your drawing routine, which of course overwrites any changes you did on the modelview matrix in the update method (using gluLookAt).

Always keep in mind: gluLookAt, glOrtho, gluPerspective, glTranslate, glRotate, and all other matrix and transformation functions always work on the top element (changed by glPush/PopMatrix) of the currently selected matrix stack (changed by glMatrixMode). And they always multiply the current matrix, istead of replacing it. So like for gluPerspective, you should call glLoadIdentity before calling gluLookAt. And the whole camera change should be done in the rendering routine, istead of the update routine.

Instead of doing any GL transformations in update you should rather change the variables on which the camera depends and set the camera (gluLookAt on the modelview matrix) in the display method. To demonstrate the standard use of these functions, your code should be something like:

void display()
{
    <general state setup (glClear, ...)>

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glLookAt(camera);     //view transformation (camera)

    //object 1
    glPushMatrix();       //save modelview
    glTranslate/glRotate/glScale;        //local model transformations
    <draw object 1>
    glPopMatrix();
    ...
    //object n
    glPushMatrix();       //save modelview
    glTranslate/glRotate/glScale;        //local model transformations
    <draw object n>
    glPopMatrix();

    gluSwapBuffers();
}

void update()
{
    camera = ...;
}

}

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • Sorry, the `glPopMatrix()` is at the end of the skybox code, before the roller coaster (since I did not include the skybox code, that is why I forgot it in the question) - is that where it should go, or should it be after the rollercoaster drawing? – Logan Serman Oct 11 '11 at 17:01
  • @Logan Whereas the control flow may be the same at runtime, the `glPopMatrix` should be in the same scope as the `glPushMatrix`, as both represent a unity and should always come in pair. – Christian Rau Oct 11 '11 at 17:03
  • I mean, should `glPopMatrix()` be after the commented skybox code (before the rc is drawn with `glVertex3f`) or after the loop to draw the rc with `glVertex3f`? – Logan Serman Oct 11 '11 at 17:05
  • @LoganSerman Depends. Regarding your code it should be after the skybox code and before the rc code. So this way you actually get the transformation you set in the update function. But now my second paragraph applies and you need to call `glLoadIdentity` before calling `gluLookAt`, as the latter always updates (and not replaces) the current matrix. But like said in the answer, you should rather set the camera in the display routine. – Christian Rau Oct 11 '11 at 17:09
  • Thanks. I moved gluLootAt to the end of `display`, so now the bottom of `display` looks like: `glutTimerFunc(50, update, 1); glLoadIdentity(); gluLookAt(...);` and `update` ends with just `glutPostRedisplay();`. Unfortunately the camera still does not move at all when I run the program. Thoughts? Thanks for the quick answers by the way, very helpful! – Logan Serman Oct 11 '11 at 17:12
  • @LoganSerman Why at the end and not the beginning? I will update my answer. – Christian Rau Oct 11 '11 at 17:14
  • I have edited my code based on your example and reflected my changes in the OP. Unfortunately now my screen is black again :( what am I missing? – Logan Serman Oct 11 '11 at 17:27
  • @LoganSerman Looks rather reasonable. Maybe some issue with the `glutTimerFunc`? Does it get called in the requested intervals? And by the way call this after the `glEnd`, this isn't an error, but really strange to call it inside the `glBegin/glEnd` block. – Christian Rau Oct 11 '11 at 17:31
  • If I place `glPushMatrix();` after `glClear` and before `glMatrixMode` in `display`, I can see a portion of the track but not the skybox (it is just a black background). The camera still does not move despite the timers functioning correctly. If I move `glPushMatrix` where it is in the OP, I cannot see anything. Does this help understand my problem? – Logan Serman Oct 11 '11 at 17:47
  • @LoganSerman A bit. Placing `glPushMatrix` there makes you pop away the `gluLookAt` changes after drawing the skybox. So you're drawing the roller coaster with the default view (which is just identity, looking from the origin along -z). I hope your camera update functions are Ok, though they look reasonable. – Christian Rau Oct 11 '11 at 17:54
  • @LoganSerman Wait, are you changing the `allpoints` array in every `display` call and then use this to source the camera data in `update`. Looks strange, although I don't completely see through your code due to SO's code indentation not working as it should. – Christian Rau Oct 11 '11 at 17:59
  • Okay, but nothing gives insight as to why I am getting a completely black screen with the code in the OP? Should I output anything to give you an idea of why this is happening (like the camera positions being generated by `update`?) And yes, the `allpoints` array is being generate every time `display` is called. It is looping through the control points in the track, interpolating new points (catmull-rom), and then adding those points to the array. I guess this means `allpoints` is adding a ton of elements every 50ms. I should create `allpoints` once, then display that every `display`, maybe? – Logan Serman Oct 11 '11 at 18:00
  • @LoganSerman I think I'm at a point where only seeing your whole code would help to solve the problem, as its likely to be related to your camera computation and the whole point array setup. – Christian Rau Oct 11 '11 at 18:06
  • Would you like me to e-mail you the .cpp? I do not want to waste a lot of your time digging through the entire source, although it really isn't that big. Let me know what you would like to do, and again thanks for all the help! – Logan Serman Oct 11 '11 at 18:07
  • @LoganSerman To be honest, I don't really have the time or desire to delve into your code, sorry. But the general advice I could give you is, that perhaps precomputing the whole curve as an array and then using this point array to draw the line strip and compute the camera should be a good idea, as your current configuration really looks a bit strange. And it shouldn't hurt performance wise, anyway. Sorry for not being of help anymore, but the general advices given in my answer still apply to your initial problem. And I would still be interested in any advances you may make on this issue. – Christian Rau Oct 11 '11 at 18:14
  • Yes, I did exactly that (precomputing the array) and reflected the changes in the OP. Still no dice on the black screen though. – Logan Serman Oct 11 '11 at 18:16
  • @LoganSerman Have you tried looking at it with a static camera, to see if the curve points are actually computed correctly? And also keep in mind, that setting the camera exactly on the curve makes the first part of the curve not visible (as it is clipped against the near plane), although this invisible part should be small. – Christian Rau Oct 11 '11 at 18:18
  • Yes, I have looked at it with a static camera and the curve looks good, it is moving the camera around it that is the problem. I feel like it is not a problem with the curve itself that is causing a black screen. I should at least be able to see the skybox and curve like I could before I implemented my code in the way you described (but that way, I could not move the camera around). I need the best of both worlds! – Logan Serman Oct 11 '11 at 18:29
3

Noticed in your code glPushMatrix(); is called with no glPopMatrix();

Just a thought, this might have something to do with you issue.

james82345
  • 530
  • 4
  • 13
0

gluLookAt always applies its result to current matrix, which in your case is GL_MODEL_VIEW. But when you render your roller coaster, you load identity in that matrix, which erase the value you put using gluLookAt.

In this case, you don't need to touch the model view. In fact, GL_MODEL_VIEW stands for model matrix multiply by view matrix. In this case, you can glPushMatrix() followed by glMulMatrix( myModelMatrix ) and after rendering glPopMatrix(). With this, you can keep your view matrix inside the GL_MODEL_VIEW and still use a different model matrix for each object

I also suggest you only change projection matrix once a frame, and not each frame.

crazyjul
  • 2,519
  • 19
  • 26
0

It's been a long time since I touched OpenGL, but here are a few things to consider:

  • With each call to display(), you are drawing the skybox with the current matrix then loading the identity matrix to draw the roller coaster. Perhaps load the identity within the push/pop so that the skybox is constant, but your prevailing tranformations on the roller coaster are applied.
  • Do you need to call gluPerspective and glMatrixMode with every call to display()?
  • Repeatedly calculating binorm from up and then up from binorm will probably give you unexpected results in terms of rotation of the camera around the screen's z axis.
  • The call to gluLookAt appears to have nextpos and currpos reversed, pointing the camera in the opposite direction.
  • (Opinion only) It may still look wierd with a completely stationary skybox. Matching camera rotation (but not translation) when drawing the skybox and roller coaster may look better.
Dale
  • 101
  • 1
  • 2
  • 2
  • Thanks, your suggestion is working okay (I think) as the rollercoaster itself is now rotating with the camera. The camera itself is acting up though, it doesn't follow the track and eventually spins out of control and goes outside of the skybox. I did switch the nextpos and currpos around, so perhaps this is due to the binorm/up vector calculations? Do you know how to calculate these, because what I have in my code is what was suggested by our TA. – Logan Serman Oct 11 '11 at 08:28
  • The calculation of up should only affect the camera rotaion, which doesn't explain it translating outside of the skybox. Do your control points use the same xyz orientation as OpenGL? – Dale Oct 12 '11 at 03:56
  • Meanwhile, if your roller coaster never faces exactly up or down between steps then you should be able to get away with keeping "up" constant. I'd even tweak the rail temporarily so that I could use this method until I managed to debug/test the camera's xyz position. Then I'd worry about a safer calculation of "up" afterwards. Unfortunately, I don't have a recommendation off hand for that calculation. – Dale Oct 12 '11 at 04:09