1
\$\begingroup\$

Before anyone asks, this is for a university project and I have to use GLUT. I'm not tagging this as homework because this is not a requirement, nor will I have extra points for this.

With GLUT, for people unaware, you have:

  • glutIdleFunc(func): registers the render function and this means that when GLUT is not doing anything else, it will call this function. In other words, it will keep rendering as fast as possible.
  • glutTimerFunc(msecs, func, val): registers a function that will be called in msecs and the registering the same function recursively would allow for a fixed time based movement.

Now, I'm trying to avoid these functions for this purpose and most discussions/articles I found about GLUT, use them for time based movement. So I have come here once again... But if I'm not using any of those functions, how are the frames rendered? I just call glutPostRedisplay and a single frame will be rendered.

I have managed to implement time based movement in GLUT in a simple manner. May not be the best implementation, but it's not overly complex and works. Well, there's only a slight problem, that's why I'm here.

My camera movement is based on Newton's Second Law of Motion, where I calculate the displacement movement like this:

Vector3D displacement = (CurrentVelocity * elapsedTime) +
    (Acceleration * 0.5f * elapsedTime * elapsedTime);

The elapsedTime comes from my timer implementation and I think that's where the problem lies. The problem is that displacement should be positive or negative depending on the movement direction but if CurrentVelocity is a really low value (ie: -0.1f), the displacement will be positive when it should be negative. The rest of the code will accelerate or deacelerate the camera, so the problem only happens when accelerating because that's when CurrentVelocity will be small and what happens is that the camera moves slightly to the right (positive) and then to the left (negative) when it gains enough momentum to give a negative displacement value.

Now, when there's no movement in the scene, nothing is being rendered at all. When I press on of the movement keys, I do this:

CameraDirection.z = CAMERA_WALK_BACKWARD;
SceneTimer.PostRedisplayWindow();

That also comes from my timer class, I do that instead of calling glutPostRedisplay and the reason for that is this:

void Timer::PostRedisplayWindow(void) {
    currentTime = glutGet(GLUT_ELAPSED_TIME);
    glutPostRedisplay();
}

I need to get the currentTime (which will become the previousTime when I need to calculate the elapsed time) and then I call glutPostRedisplay. A single frame will be rendered, which means the renderScene will be called:

void renderScene(void) {
    (...)

    glLoadIdentity();

    elapsedTime = SceneTimer.GetElapsedTimeInSeconds();

    SceneCamera.Move(CameraDirection, elapsedTime);
    SceneCamera.LookAt();

    drawCompleteScene();

    glutSwapBuffers();

    checkAnimationRedisplay();
}

As you can see, the elapsed time is calculated and the camera is moved on that. Everything in the scene is drawn, swap buffers and then I have this extra function which will check if something needs to be animated, in this case, the camera needs:

void checkAnimationRedisplay(void) {
    if(SceneCamera.IsCameraMoving()) {
        glutPostRedisplay();
        return;
    }
}

It will keep calling glutPostRedisplay until the camera has stopped moving. Meaning, renderScene will be called every time, the elapsed time is calculated and the camera is moved based on time, everything's working; besides, of course, the little glitch already mentioned, little but the one that made me create this long question (sorry about that).

All that's missing is the most important function of this question, the one that calculates the elapsed time, simple as this:

float Timer::GetElapsedTimeInSeconds(void) { static int previousTime = 0; static int elapsedTime = 0;

previousTime = currentTime;
currentTime = glutGet(GLUT_ELAPSED_TIME);
elapsedTime = currentTime - previousTime;

return elapsedTime / 1000.0f;

}

There's probably something wrong with my timer code right? I mean, moving forward or right (positive values), there's no problems. The movement is smooth and time based. For instance, if I set a movement speed of 5m/s, it will actually take 5 seconds to move 25meters (I'm assuming 1m = 1.0f).

So, what I'm doing wrong with the timer so that the negative values start moving to the right for a split-second and only then to the left as it should?

EDIT:

A little more code as requested, this is how the CurrentVelocity is how calculated and how the displacement is applied:

void Camera::Move(Vector3D direction, float elapsedTime) {
    // Move only if the velocity vector is not of zero length (guards against
    // floating point rounding erros
    if(CurrentVelocity.SquaredMagnitude() != 0.0f) {
        Vector3D displacement = (CurrentVelocity * elapsedTime) +
            (Acceleration * 0.5f * elapsedTime * elapsedTime);

        Vector3D ForwardVector = Reference;

        if(cameraType != CAMERA_TYPE_SPECTATOR) {
            ForwardVector = Vector3D::CrossProduct(WORLD_SKY_VECTOR, RightVector);
            ForwardVector.Normalize();
        }

        Position = Position + (RightVector * displacement.x);
        Position = Position + (WORLD_SKY_VECTOR * displacement.y);
        Position = Position + (ForwardVector * displacement.z);
    }

    UpdateCameraVelocity(direction, elapsedTime);
}

void Camera::UpdateCameraVelocity(const Vector3D direction, float elapsedTime) {
    // Accelerate camera if the direction is -1 or 1 or decelerate if it's 0
    if(direction.x != 0.0f) {
        // Accelerate the camera
        CurrentVelocity.x += direction.x * Acceleration.x * elapsedTime;

        // Don't let it go beyond the max velocity
        if(CurrentVelocity.x > MaxVelocity.x) {
            CurrentVelocity.x = MaxVelocity.x;
        } else if(CurrentVelocity.x < -MaxVelocity.x) {
            CurrentVelocity.x = -MaxVelocity.x;
        }
    } else {
        // Decelerate the camera in the correct direction
        if(CurrentVelocity.x > 0.0f) {
            CurrentVelocity.x -= Acceleration.x * elapsedTime;

            // Don't let it start move in the opposite direction
            if(CurrentVelocity.x < 0.0f) {
                CurrentVelocity.x = 0.0f;
            }
        } else {
            CurrentVelocity.x += Acceleration.x * elapsedTime;

            // Don't let it start move in the opposite direction
            if(CurrentVelocity.x > 0.0f) {
                CurrentVelocity.x = 0.0f;
            }
        }
    }

    // All code below was removed, it's similar for all other axis...
}

bool Camera::IsCameraMoving(void) {
    return CurrentVelocity.SquaredMagnitude() != 0.0f;
}
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Does the problem occur whenever you change the direction or only when changing from positive values to negative values? If it is always, it is most likely the last acceleration stored from last movement which is still slightly bigger than zero. How do you determine that the movement is over in IsCameraMoving() ?

Can you show the entire calculation of CurrentVelociy and Acceleration and how you apply the displacement?

Also double check that you reset all components of CameraDirection properly.

Edit:

Solution: The displacement calculation in Camera::Move() was wrong. Acceleration must also be multiplied with direction here as also done in Camera::UpdateCameraVelocity, otherwise it points into the wrong direction.

Vector3D displacement = (CurrentVelocity * elapsedTime) + (direction * Acceleration * 0.5f * elapsedTime * elapsedTime);

\$\endgroup\$
18
  • \$\begingroup\$ I'm not sure I understand your question... The Acceleration vector is a fixed, it never changes, so of course the "last acceleration" is stored, unless that was not what you meant. IsCameraMoving checks the CurrentVelocity vector length and if it's zero, it means the camera is stopped. CameraDirection values are set to 0 as soon as the keyboard button was released. I could post the whole code but I don't think it's relevant. I wasted too many hours looking for the problem and it's not on CurrentVelocity is calculated, CurrentVelocity will always be negative moving to the left... \$\endgroup\$
    – rfgamaral
    Commented Apr 10, 2011 at 12:09
  • \$\begingroup\$ ...or always positive moving to the right. And like I already said, Acceleration is not calculated is a fixed positive value for all 3 axis. But basically, CurrentVelocity is a multiplication of: direction (1 or -1) * acceleration (fixed positive value) * elapsed time. The problem is on the displacement that it should always be negative if I'm moving on -1 direction (left on the x axis). The formula left side (CurrentVelocity * elapsedTime) is always negative moving to the left and always positive to the right, the right side (Acceleration * 0.5f * elapsedTime * elapsedTime)... \$\endgroup\$
    – rfgamaral
    Commented Apr 10, 2011 at 12:13
  • \$\begingroup\$ ...is the one giving problems. it will always be a positive number since Acceleration is positive and all the next multiplications will be with positive numbers. When CurrentVelocity is a very small value (but negative, moving to the left), the left side will be negative but the right one will be of a positive number enough that the sum of the two will give a positive number, making the camera move slightly to the right side and only then to the left side when CurrentVelocity increases enough for the sum of both sides to give a negative value. \$\endgroup\$
    – rfgamaral
    Commented Apr 10, 2011 at 12:16
  • \$\begingroup\$ From the minimal part of code posted, one can tell if the acceleration was fixed, the camera would only move in one direction and never into the opposite direction. The key is the calculation of velocity and acceleration, it would be easier to help if you post that part :) \$\endgroup\$ Commented Apr 10, 2011 at 13:20
  • \$\begingroup\$ Thanks for posting the code. The problem is: the Acceleration is only adjusted into the right direction in the first formula, in the 2nd it has the wrong sign. 1) CurrentVelocity.x += direction.x * Acceleration.x * elapsedTime; 2) Vector3D displacement = (CurrentVelocity * elapsedTime) + (Acceleration * 0.5f * elapsedTime * elapsedTime); Adjust the direction of the Acceleration in the 2nd formula too: Vector3D displacement = (CurrentVelocity * elapsedTime) + (direction * Acceleration * 0.5f * elapsedTime * elapsedTime); \$\endgroup\$ Commented Apr 10, 2011 at 13:28
0
\$\begingroup\$

Wouldn't it be better (just cpu performance) this formula:

Vector3D displacement = ((CurrentVelocity + (direction * Acceleration * 0.5f * elapsedTime)) * elapsedTime);

Instead of given one

Vector3D displacement = (CurrentVelocity * elapsedTime) + (direction * Acceleration * 0.5f * elapsedTime * elapsedTime);

as it has 1 less operation?

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .