Tag Archives: 3D coordinate rotation

Avoiding Cracking Under Pressure

Sometimes, when working on a project, you get stuck.

The Problem

Recently, I was working on (what should have been) an elementary programming exercise: get the player to rotate around an object in 3D when a key is held down. I’d immediately started tackling the problem from the angle of “OK, how do I rotate around an arbitrary axis?”. Big mistake.

It’s pretty easy to make an error (or twenty) when you’re writing this out in code: (Image source)

For days, I just could not get the code to work. The player quickly developed a knack for teleporting all over the place, but despite his new-found skills with wizardry, he still wouldn’t rotate around the ball.

The Solution

Eventually, I gave up and stepped away from the problem for a while. A few days later, after a full night’s rest, I sat down in front of my laptop, scrapped all the code I had, and started from scratch. I got it working in less than an hour.

Lesson learned: Sometimes, you need to step back and take a break. In retrospect, I feel kind of silly for getting stuck for a few days on this one. :)

Here’s the (somewhat unoptimized) code to calculate the offset to move by each frame:

// Helper method to re-calculate the translation vector each frame while the
// player is rotating around the ball
// returns the offset the camera should move by
Ogre::Vector3 FrameUpdateListener::calculateRotation() {
 // get player position
 const Ogre::Vector3 playerPosition = GameState::getInstance()->getPlayerCamera()->getPosition();

 // get ball position
 const Vector3D* const pBallPosition =
 GameState::getInstance()->getBall()->getBoundingVolume()->getCenter();
 const Ogre::Vector3 ballPosition(pBallPosition->x, pBallPosition->y, pBallPosition->z);

 // get vector for player relative to ball
 const Ogre::Vector3 localPlayerPosition = playerPosition - ballPosition;

 // get theta (angle to rotate by)
 const int direction = isRotatingClockwise ? 1 : -1;
 const Ogre::Radian theta(static_cast<Ogre::Real>(direction * 0.05f)); // angle to adjust by

 // store cos/sin in temp variables to save calculations
 const Ogre::Real cosTheta = Math::Cos(theta);
 const Ogre::Real sinTheta = Math::Sin(theta);

 // calculate new player position
 // (rotate player position around Y axis)
 Ogre::Vector3 newPlayerPosition;
 newPlayerPosition.x = cosTheta * localPlayerPosition.x - sinTheta * localPlayerPosition.z;
 newPlayerPosition.z = sinTheta * localPlayerPosition.x + cosTheta * localPlayerPosition.z;
 // newPlayerPosition.y = localPlayerPosition.y; // commented to save the calculation, uncomment if you need this later for some bizarre reason

 // calculate dx and dz
 Ogre::Real dx = newPlayerPosition.x - localPlayerPosition.x;
 Ogre::Real dz = newPlayerPosition.z - localPlayerPosition.z;

 // return the offset vector
 return Ogre::Vector3(dx, 0, dz);
}