Jump to content
  • Advertisement
Sign in to follow this  
Darqtiger

LookAt Rotation Clamping

This topic is 3494 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've been struggling with this problem for a while. It essentially boils down to this: I have an object that is being tracked by a camera with a static location. I need to be able to place limits on how far the camera can rotate up/down and left/right while tracking the object. There is never a change to the roll. I've tried several implementations with rotation matrices, quaternions and euler angles. I've been able to get most of the attempts to do about 95% of what I'm looking for, but ultimately there is always some strange little glitch. usually having to do with when the object being tracked crosses the cameras Y plane. What I want to do is be able to calculate the absolute rotation between the LookAt vector and the Y axis, and the LookAt vector and the X axis. Then, if the current angles of pitch, or yaw are outside the clamping limits, clamp those values independently and reconstruct a new LookAt vector that. So the clamping might be from something like 30-80 degrees, 10-170 degrees, 320-40 degrees, etc. Mentally it makes sense to me that I should be able to get a rotation to any point in 3D space by only rotating around two axes. I could be completely wrong though. Math has never been my strong suit. Any suggestions on how to proceed? I'm not looking for the code to actually do it; I can very happily write that myself. Just the proper way to go about it. Ideas or pseudocode would be very much appreciated. Thanks for any thoughts.

Share this post


Link to post
Share on other sites
Advertisement
It sounds like you have the right idea; I'm guessing the problems are just due to getting the details of the implementation wrong.

You are correct that given two rotation angles (e.g. yaw and pitch) you can 'aim' anywhere (you can't define any orientation this way, but since you don't need roll this isn't important).

The way I would proceed would be to compute the yaw and pitch that will aim the object at the target, and then clamp to the desired ranges. It's the details, of course, that are the difficult part, but I can tell you that in theory at least this approach should work fine.

If you're having specific problems with this method, you might go ahead and post of some of your code.

Share this post


Link to post
Share on other sites
I'm trying to do the exact same thing, and am having the same problem. For me it boils down to the fact that I dont have the math to figure out how to compute yaw and pitch (independently) for an arbitrary Lookat point.

I can do rotation using a lookat matrix or using a rotation axis and theta, etc, but these normal methods aren't helping me figure out what to do.

Share this post


Link to post
Share on other sites
I'm struggling to understand what you want. You say you have a camera with a fixed position and you want it to track an object by roll and pitch. That's simple enough to do.

Then you say you are trying to find limits on the maximum roll and pitch that will be needed. Easy: they both need to be in the range 0-360 degrees. Anything less than that and it won't be able to look at all possible points.

Am I missing something?

Share this post


Link to post
Share on other sites
Quote:
Original post by yahastu
I'm struggling to understand what you want. You say you have a camera with a fixed position and you want it to track an object by roll and pitch. That's simple enough to do.
It's actually yaw and pitch (rather than roll and pitch).
Quote:
Then you say you are trying to find limits on the maximum roll and pitch that will be needed. Easy: they both need to be in the range 0-360 degrees. Anything less than that and it won't be able to look at all possible points.
I believe the OP wants to limit the range of rotation of the object in both the 'yaw' and 'pitch' directions. (For example, imagine a cannon mounted on a battleship - due to various obstructions, it will most likely only be able to pitch up and down so far, and only be able to yaw so far to the left and right.)

As for computing the yaw and pitch given a forward vector, what you're looking for is a Cartesian-to-spherical coordinate conversion. Unfortunately, there are many different ways that this conversion can be performed, so the examples that one finds online can sometimes be of little use.

I would recommend at least looking at some of these references though, just to get an understanding of the general algorithm. Generally, it's going to reduce to a couple of inverse trig functions. There's more than one way to do it, but I recommend using atan2() or equivalent for stability and ease of use.

For reference, here's the 'Cartesian-to-spherical' function from the CML; which axis is considered 'up' is an argument to the function, so it can be used regardless of which convention you prefer. (It actually has a couple of other options, but I've removed them for clarity.)

/* Convert Cartesian coordinates to spherical coordinates in R3 */
template < class VecT, typename Real > void
cartesian_to_spherical(
const VecT& v,
Real& radius,
Real& theta,
Real& phi,
// 'axis' indicates which axis should be considered 'up' for the
// purpose of the conversion (0 = x, 1 = y, 2 = z):
size_t axis,
Real tolerance)
{
typedef Real value_type;

size_t i, j, k;
cyclic_permutation(axis, i, j, k); // i=axis,j=(i+1)%3,k=(j+1)%3

value_type len = length(v[j],v[k]);
theta = len < tolerance ? value_type(0) : std::atan2(v[k],v[j]);
radius = length(v, len);
if (radius < tolerance) {
phi = value_type(0);
} else {
phi = std::atan2(len,v);
}
}



[Edit: A couple of other notes. In the above code, theta corresponds to yaw, more or less, and phi to pitch. The input vector needn't be unit-length; also, the 'radius' output variable isn't really of interest in this case, so it can be ignored.]

Share this post


Link to post
Share on other sites
Quote:
I believe the OP wants to limit the range of rotation of the object in both the 'yaw' and 'pitch' directions. (For example, imagine a cannon mounted on a battleship - due to various obstructions, it will most likely only be able to pitch up and down so far, and only be able to yaw so far to the left and right.)


That's a really good example of what I've been trying to do.

Quote:
As for computing the yaw and pitch given a forward vector, what you're looking for is a Cartesian-to-spherical coordinate conversion. Unfortunately, there are many different ways that this conversion can be performed, so the examples that one finds online can sometimes be of little use.


I came to the same conclusion after seeing another of your posts elsewhere on the forums. Below is roughly the current state of my test:


void Camera::LookAt( Vector3 & pAt )
{
mLookAt = Normalize(pAt - mPosition);
mTarget = pAt;
}

void Camera::Update()
{
Vector3 vTarget = mTarget;
Vector3 vDifference = mPosition - vTarget;

float theta = ATan2( vDifference.Y(), vDifference.X() );
float phi = ATan2( Sqrt( vDifference.X() * vDifference.X() + vDifference.Y() * vDifference.Y()), vDifference.Z() );

theta *= RADIANS_TO_DEGREES;
phi *= RADIANS_TO_DEGREES;

float originalTheta = theta;
float originalPhi = phi;

if(theta < 0.0f)
{
theta += 360.0f;
theta = CircleClamp( pitchLimit.Min(), pitchLimit.Max(), theta );

if( theta > 179.999f )
{
theta -= 360.0f;
}
}
else
{
theta = CircleClamp( pitchLimit.Min(), pitchLimit.Max(), theta );

if( theta > 179.999f )
{
theta -= 360.0f;
}
}

if(phi < 0.0f)
{
phi += 360.0f;
phi = CircleClamp( yawLimit.Min(), yawLimit.Max(), phi );

if( phi > 179.999f )
{
phi -= 360.0f;
}
}
else
{
phi = CircleClamp( yawLimit.Min(), yawLimit.Max(), phi );

if( phi > 179.999f )
{
phi -= 360.0f;
}
}

theta1 *= DEGREES_TO_RADIANS;
phi *= DEGREES_TO_RADIANS;

vTarget.SetX( Sin( theta ) * Cos( phi ) );
vTarget.SetY( Sin( theta ) * Sin( phi ) );
vTarget.SetZ( Cos( theta ) );

mLookAt = vTarget;
}




Everything looks right up to the point where I calculate the new target. The angles I extract look right. The angles that are calculated from clamping look right. From everything I've read today, I think the last section is the proper way to calculate the new lookAt point. I don't multiply through with the radius, because I don't think it should be required.

When I was testing it earlier, I wasn't getting the right visual output in my program after applying the clamping and reconstruction the lookAt point. I did find one error which has been corrected, but it had to do with the clamping code, and not the reconstruction code.

Share this post


Link to post
Share on other sites
So, I've finally figured it out. Is it the right solution? Maybe not. It does work for my scenario though. I thought I'd post the results here just in case it might help someone else.



void Camera::LookAt( Vector3 & pAt )
{
mLookAt = Normalize(pAt - mPosition);
mTarget = pAt;
}

void Camera::Update()
{
Vector3 vTarget = mTarget;
Vector3 vDifference = vTarget - mPosition;
vDifference = Normalize( vDifference );

float pitch = ASin( vDifference.Y() );
float yaw = ATan2( vDifference.Z(), vDifference.X() );

pitch *= RADIANS_TO_DEGREES;
yaw *= RADIANS_TO_DEGREES;

float originalPitch = pitch;
float originalYaw = yaw;

const AngleLimits & pitchLimit = mAngleLimitsX;
const AngleLimits & yawLimit = mAngleLimitsY;

struct Local
{
static void ApplyClamping( const AngleLimits & limits, float & angle )
{
if(angle < 0.0f)
{
angle += 360.0f;
angle = CircleClamp( limits.X(), limits.Y(), angle );

if( angle > 179.999f )
{
angle -= 360.0f;
}
}
else
{
angle = CircleClamp( limits.X(), limits.Y(), angle );

if( angle > 179.999f )
{
angle -= 360.0f;
}
}
}
};

Local::ApplyClamping( pitchLimit, pitch );
Local::ApplyClamping( yawLimit, yaw );

pitch *= DEGREES_TO_RADIANS;
yaw *= DEGREES_TO_RADIANS;

vTarget.SetX( Cos( yaw ) );
vTarget.SetY( Sin( pitch ) );
vTarget.SetZ( Sin( yaw ) );

vTarget = Normalize( vTarget );

LookAt( mPosition + vTarget );
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!