Sign in to follow this  
Richter

Rotation destination problems

Recommended Posts

Hey everyone, I'm trying to implement something that has been driving me crazy. I have an object rotating on the Y-axis at all times, and when I enter a certain part of my application, I want the object to rotate and stop at a certain rotation I have defined in a script file. I have already accomplished this, but the problem I have been having is that I want to find the shortest direction to rotate in and I've tried several different approaches but still come up short because my algorithm still breaks depending on different rotational values for my object. Currently my algorithm is as follows: RotationCW = rotationDestination - objectRotation; RotationCCW = (rotationDestination - 2PI) - objectRotation; if( ABS(RotationCW) < ABS(RotationCCW) ) turnSign = SIGN(RotationCW); else turnSign = SIGN(RotationCCW); Now in my task that handles rotating to the destination within my task manager is the following: if( objectRotation != rotationDestination ) { angleAccel = turnSign * objectSpinSpeed; objectRotation += angleAccel * deltaTime; if( turnSign < 0 ) { if( objectRotation < rotationDestination ) objectRoation = roationDestination; } else { if( objectRotation > rotationDestination ) objectRotation = rotationDestination; } } else RemoveTask( STOP_ROTATION ); Please something help me, because my head is really hurting now. One problem I've been seeing over and over again is that my turnSign ends up negative when it shouldn't be.

Share this post


Link to post
Share on other sites
Hi. One way to test it is to ensure that all the rotational angles are in the 0...2PI range. Then you could do the following test:

// make sure rotations are in the 0...2PI range
while (rotationDestination < 0.0f) rotationDestination += 2*PI;
while (rotationDestination > 2*PI) rotationDestination -= 2*PI;
while (objectRotation < 0.0f) objectRotation += 2*PI;
while (objectRotation > 2*PI) objectRotation -= 2*PI;

float angle = rotationDestination - objectRotation;

if (angle < 0.0f)
angle += 2*PI;

if (angle < PI)
turnSign = 1; // rotate CCW
else
turnSign = -1; // rotate CW

Or if your angles take any possible values, for example objectRotation = 27.4663f and rotationDestination = -1204.2879f the while loops would take more time than necessary. In that case you could check the fractional portion.

float angle = rotationDestination - objectRotation;

float b = angle/2PI;
float f = fmodf(b, NULL); // get the fractional part

if (f < 0)
f += 1.0f;

if (f < 0.5f)
turnSign = 1;
else
turnSign = -1;

Share this post


Link to post
Share on other sites
Hey, thanks for posting, I've been having a huge headache all day with this problem. Several of my co-workers have been at it too. I am working with radians, except we're using fixed-point math, so that's why I've been having so much of a problem. I'll take another whack at it and post again should I have another problem.

Share this post


Link to post
Share on other sites
Hey again, I just tried it but it still doesn't go down the right path at all times. Our Radians are in the range of 0 - 1 or 0 to 1<<16. We have utilities to convert back and forth as well. What else is strange is that some of my destinations are behaving like their used to going CCW or CW. Whenever it goes in the opposite of what it is used to, the object snaps to the rotation, almost as if my update task wrecks it. If you can suggest how I should update the rotation it would help out a lot.

P.S. Sorry for my typos, it was really in the morning and I've been sick all day at work.

Share this post


Link to post
Share on other sites
Hi
Quote:
Our Radians are in the range of 0 - 1 or 0 to 1<<16

So can you say that the fixed point 0 is equivalent to "0 radians" and for example the fixed point 128 is equivalent to "PI radians"? Also destinationRotation and objectRotation represent a radiant value (coded into a fixed number)?

Do you have sin and cos functions available?

Do I understand this correct that when you assign a Task (START_ROTATION) your object should start the rotation. It gains rotational speed and when it reaches the destination position (destinationRotation) it stops the rotation and no longer moves until you send another START_ROTATION?

Share this post


Link to post
Share on other sites
Hey I wrote a small simulation of that rotation and you were right. The code I had provided was not correct, sorry about that.

rotation

This is the code that produces the correct behavior in my simulation:

void StartRotation(float destination)
{
rotationDestination = destination;

float a;

if (rotationDestination > objectRotation)
{
a = rotationDestination - objectRotation;

if (a < PI)
turnSign = 1;
else
turnSign = -1;
}
else
{
a = objectRotation - rotationDestination;

if (a < PI)
turnSign = -1;
else
turnSign = 1;
}

AddTask(START_ROTATION);
}

void Rotate()
{
if (objectRotation != rotationDestination)
{
objectRotationOld = objectRotation;

angleAccel = turnSign * objectSpinSpeed;
objectRotation += angleAccel * deltaTime;

if (objectRotation > 2*PI)
{
objectRotation -= 2*PI;
objectRotationOld = 0;
}

if (objectRotation < 0)
{
objectRotation += 2*PI;
objectRotationOld = 2*PI;
}

if ((rotationDestination - objectRotation < 0 && rotationDestination - objectRotationOld > 0) ||
(rotationDestination - objectRotation > 0 && rotationDestination - objectRotationOld < 0))
{
RemoveTask( STOP_ROTATION );
}
}
}

I don't know if it's the opmimal solution, but in my case I needed a new variable "objectRotationOld" that stores the "objectRotation" of the last frame.
Note that the function StartRotation(...) takes the new destination position the object should move to. The Function Rotate() is equivalent to what you do inside your task manager. I hope it will work this time.

Share this post


Link to post
Share on other sites
The task manager works as an event driven callback that is called every frame for a certain module that has a task manager object defined for it. We have tasks that we define in the usual enum fashion. We define what we want to happen one each task within the callback we make for the task manager.

I have a update rotation task that is called every frame, and when I enter different parts of the application, I will remove the update rotation task, calculate my stop rotation strategy values, then add the task for stopping the rotation. Sorry if the details are a little vague, but I can't give away too many details since I didn't write the task manager. Personally I would've wanted to make a badass FSM, cause I have one, except its in C++, and we're not allowed to use C++ here.

Share this post


Link to post
Share on other sites
Hey I tested it earlier this morning and it worked! Thanks so much. The only thing I don't understand though is that every algorithm I have tried doesn't work with 0 radians at all. But everything is great, I'm still sick, but it doesn't matter. I can finally move on with my life, and keep on working. Again, thank you so much.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this