2D rotation of a quad

Viewed 177

I am working on a basic simulation program using C++ and I have a renderer that uses OpenGL. I am rendering quads on the screen which have a dynamic location in the simulation. My goal is to change the orientation of the quad when it is moving in the simulation. For each quad, I have a variable (m_Rotation) which holds the current rotation of it and I calculate the required rotation using trigonometry and put the value in a variable (m_ProjectedRotation). In the render loop, I use the following code to change the orientation in the movement:

if(abs(m_ProjectedRotation - m_Rotation)>5.0f)
{   
    if ((360.0f - m_Rotation + m_ProjectedRotation) > (m_ProjectedRotation - m_Rotation))
    {
        m_Rotation += 5.0f;
        if (m_Rotation > 360)
        {
              m_Rotation = fmod(m_Rotation, 360);
        }
    }
    else 
    {
        m_Rotation -= 5.0f;
    }
                
}

I want the quad to rotate itself according to the closest angle(e.g. if the current angle is 330, and the destination angle is 30, the quad should increase its angle until it reaches 30 not decreasing the angle since it reaches 30. Because it has a smaller angle to rotate). In some conditions, my quad rotates itself counterclockwise even tough the clockwise rotation has shorter rotation and vice versa. I believe the condition for rotation:

(360.0f - m_Rotation + m_ProjectedRotation) > (m_ProjectedRotation - m_Rotation)

should be something different to show the required behavior. However, I couldn't figure it out. How should I update this code to get what I want?

2 Answers

I believe the correct solution should be as follows:

Let's call the two angles from and to. I assume both are in positive degrees as per your question. There are two cases:

  • the absolute distance |to - from| is less than 180.
    This means there is less degrees to travel by to - from than in the other direction, and that is the way you should choose.
    In this case, you should rotate by sign(to-from) * deltaRotation, where sign(x) = 1 if x > 0 and -1 otherwise. To see the need of the sign function, look at the following 2 examples, where |to - from| < 180:
    • from = 10, to = 20. to - from = 10 > 0, so you should increase rotation.
    • from = 20, to = 10. to - from = -10 < 0, you should decrease rotation.
  • |to - from| is more than 180. In this case, the direction should be the inverse, and you should rotate by - sign(to-form) * deltaRotation, note the minus sign. You could also express this as sign(from-to) * deltaRotation, swapping from and to, but I left them as before for explicitness.
    • from = 310, to = 10. Then, to - from = -300 < 0, you should increase rotation (Formally, -sign(to-from) = -sign(-300) = -(-1) = 1)
    • from = 10, to = 310. Then, to - from = 300 > 0, you should decrease rotation (Formally, -sign(to-from) = -sign(300) = -1)

Writing this in C++ you can encapsulate this logic in such a function:

int shorterRotationSign(float from, float to) {
    if(fabs(to - from) <= 180) {
        return to - from > 0 ? 1 : -1;
    }
    return to - from > 0 ? -1 : 1;
}

Which you will use like this:

m_Rotation += 5.0f * shorterRotationSign(m_Rotation, m_ProjectedRotation);

m_Rotation = fmod(m_Rotation + 360, 360);

The goal of the last line is to normalize negative angles and ones greater than 360.

(IMO This is more of a mathematical question than one about opengl.)

I would do something like that:

auto diff = abs(m_ProjectedRotation - m_Rotation);
if(diff > 5.0f)
{   
    diff = fmod(diff, 360.0f); 
    auto should_rotate_forward = (diff > 180.0f) ^ (m_ProjectedRotation > m_Rotation);
    auto offset = 360.0f + 5.0f * (should_rotate_forward ? 1.0f : -1.0f);
    m_Rotation = fmod(m_Rotation + offset, 360.0f);            
}

diff is the absolute angle of your rotation. You then make it in a [0; 360) range by doing diff = fmod(diff, 360.0f).

should_rotate_forward determines whether you should decrease or increase the current angle. Note the ^ is a XOR operation

offset is basically either -5.0 or 5.0 depending on condition, but there's also +360.0f so that if for example m_Rotation == 1.0 and offset == -5.0 so fmod(m_Rotation + offset, 360.0f) would be -4.0 while you want 356.0, so you add full 360 rotation and after fmod everything is positive and in [0; 360) range

Related