Search code examples
c++openglphysicssdl-2glm-math

How to move car towards direction of tires in OpenGL


I am trying to get a car to move in OpenGL in a semi-realistic manner, with the car going roughly in the direction of it's front tires (which can turn, just like real car tires).

I don't know how to make the car go towards the tires. I am taking input from the keyboard (key down and key up) using SDL2 like this:

case SDLK_LEFT:
    car.setTireRotationSpeed(-TIRE_ROTATION_SPEED);
    break;
case SDLK_RIGHT:
    car.setTireRotationSpeed(TIRE_ROTATION_SPEED);
    break;
case SDLK_UP:
    car.setCarForwardSpeed(CAR_MOVEMENT_SPEED);
    break;
case SDLK_DOWN:
    car.setCarForwardSpeed(-CAR_MOVEMENT_SPEED);
    break;

And similarly for key release:

case SDLK_LEFT:
    if (car.getTireRotationSpeed() == -TIRE_ROTATION_SPEED)
        car.setTireRotationSpeed(0);
    break;
case SDLK_RIGHT:
    if (car.getTireRotationSpeed() == TIRE_ROTATION_SPEED)
        car.setTireRotationSpeed(0);
    break;
case SDLK_UP:
    if (car.getCarForwardSpeed() == CAR_MOVEMENT_SPEED)
        car.setCarForwardSpeed(0);
    break;
case SDLK_DOWN:
    if (car.getCarForwardSpeed() == -CAR_MOVEMENT_SPEED)
        car.setCarForwardSpeed(0);
    break;

This sets variables in the car that are used every frame to move the car/rotate the tires on the car (since the chasis and the tires are separately rendered) in a smooth fashion.

I am using glm-math (i.e. glm:vec3, glm::mat4, etc) to accomplish the transformations, and I am using shaders to draw and texture.

Every time the whole car is rendered, it goes through the following steps:

void Car::Render(Shader & shader, Camera & camera)
{
    incrementFrontTireRotation(getTireRotationSpeed());
    move();

    chasisTexture.Bind(0);
    shader.Update(getChasisTransform(), camera);
    chasisMesh.Draw();

    tireTexture.Bind(0);
    shader.Update(getTireTransform(Car::FRONT_RIGHT), camera);
    tireMesh.Draw();

    //Repeated for the other tires
}

Each of the shader.Update() calls takes a custom Transform class, which is simply a wrapper for the glm::translate, glm::rotate, and glm::scale and returns the glm::mat4 that represents the model's matrix transformations, based on the transformations I specify.

The incrementFrontTireRotation method and the move method are where I am having trouble.

void Car::incrementFrontTireRotation(float amount)
{
    if (amount > 0)
    {
        if (frontTireRotation.y + amount <= MAX_FRONT_TIRE_TURNED)
            frontTireRotation.y += amount;
        else
            frontTireRotation.y = MAX_FRONT_TIRE_TURNED;
    }
    else if (amount < 0)
    {
        if (frontTireRotation.y + amount >= -MAX_FRONT_TIRE_TURNED)
            frontTireRotation.y += amount;
        else
            frontTireRotation.y = -MAX_FRONT_TIRE_TURNED;
    }
    if (carForwardSpeed != 0)
        carRotation += glm::vec3(0, -amount, 0);
}

frontTireRotation is a glm::vec3 that is used for calculating the rotation of the front tires, since they can turn side to side. carRotation is intended to be the rotation of the car.

void Car::move()
{
    forward = glm::vec3(glm::rotate(-tireRotation, glm::vec3(0, 1.0f, 0)) * glm::vec4(forward, 1.0f));
    position += forward * carForwardSpeed;
}

My intent is to try to get the car to move in the same direction in which the front tires are facing. I am trying to do this with the forward vector, but it is having some weird results. The car does indeed turn, but after too much turning the car no longer moves the way the tires are pointing.

I know this is a lot. Let me know if I need to clarify anything.


Solution

  • The tires of a car should be aligned such that their axes meet at a common pivot like so:

    Car tires

    As you see, the two front tires must have a different angle. So the easiest way to handle turns is to consider a virtual central wheel whose angle you control via a steering variable.

    This steering then lets you calculate the radius of the turn circle on which the car must move. This will depend on the length of the car. Given this radius, you can calculate the pivot point. Then, when updating the car's location and rotation, all you need to do is rotate the entire car about the pivot. The rotation angle should be chosen in a way that the resulting circle segment is equal to the way the car should travel in a given frame time (i.e. angle * r = velocity * t, angle in radians).

    Some care must be taken when no or very little steering happens. In this case, the turn radius becomes infinite and you cannot do a reasonable rotation. Switching to a simple translation in this case can solve this problem.