Sign in to follow this  

Vector rotation.

This topic is 4731 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

Hello everyone. I'm working on a bot for a first person shooter. In order to give the bot a smoother and controllable aiming system I'm using the quaternion class from the Wildmagic Math library along with the following aiming function. Here's the function that handles all my aiming.
bool Client::TurnTowardPosition(const Vector3f &_pos)
{
	Vector3f newFacing = (_pos - m_Position);
	newFacing.Normalize();

	// See how close we are to 
	float fDot = m_Facing.Dot(newFacing);

	// Determine the angle between the heading vector and the target
	float fAngle = Wml::Mathf::ACos(fDot);

	// Just snap the facing if it's very close.
	const double WeaponAimTolerance = 3.0f*Mathf::DEG_TO_RAD;
	if(fAngle < WeaponAimTolerance)
	{
		m_Facing = newFacing;
		return true;
	}

	int iSign = m_Facing.Sign(newFacing);

	Wml::Quaternionf quat;
	quat.GetRotationTo(m_Facing, newFacing);

	m_Facing = (quat * 0.65f) * m_Facing;
	m_Facing.Normalize();

	return false;
}



The line m_Facing = (quat * 0.65f) * m_Facing; is what I did in order to make the rotation happen over time. If I do m_Facing = quat* m_Facing; it does the entire rotation all in the same frame. There seems to be a pretty rare bug where the bot stops rotating. It appears that it happens when the desired facing vector faces opposite the current facing. Anyone know anything about this? For reference, here is the GetRotationTo function that I actually found within the Ogre3d engine.
void Quaternion<Real>::GetRotationTo(const Vector3<Real>& _from, const Vector3<Real>& _to)
{
	// Based on Stan Melax's article in Game Programming Gems
	Vector3<Real> c = _from.Cross(_to);

	// NB if the crossProduct approaches zero, we get unstable because ANY axis will do
	// when v0 == -v1
	Real d = _from.Dot(_to);
	// If dot == 1, vectors are the same
	if (d >= 1.0f)
	{
		*this = Quaternion<Real>::IDENTITY;
	}
	Real s = Math<Real>::Sqrt( (1+d)*2 );
	Real invs = 1 / s;

	X() = c.X() * invs;
	Y() = c.Y() * invs;
	Z() = c.Z() * invs;
	W() = s * (Real)0.5;
}



Could it be that the dot == 1.0 in this, so no quaternion is generated, and hence the bot does not rotate? The wierd thing is that when the bot stops rotating, he stops rotating until he dies, meaning, once he is broke, even as he runs around the level he keeps facing the direction he became "broke" in, even when newer facing vectors are not the opposite of current facing, which leads me to think possibly his facing gets screwed up initially and results in every further check to generate no rotation. When he dies and respawns, or acquires a target he works fine and starts rotating toward the target or waypoints. Any ideas?

Share this post


Link to post
Share on other sites
Quote:
m_Facing = (quat * 0.65f) * m_Facing


Is this working? It's my understanding that linear scaling of quaternions doesn't work this way - that's what slerp is for. I could be wrong, I suppose, but maybe this is a possible source of trouble in your code.

Also, I think Eberly's quaternion class has a function to do what your GetRotationTo() function is doing, so you might try using it instead. AFAIK the GPG version doesn't handle opposite vectors gracefully, which is exactly the problem you mentioned. So you might try using Eberly's function and see if that makes a difference.

Share this post


Link to post
Share on other sites
Well, that line is the difference between the bot snapping to his newFacing and rotating to it over time. The problem with slerp is that I don't have, nor want to mess with time. I want to define an fMaxRotationAngle per bot, and have the aim function rotate toward his aim points taking this limit into account.

I guess I could compute a time somehow if I know the distance and speed.

Admittedly I don't understand all the math behind quaternions, just how to use them for the most part. Does someone know how to take a quaternion that represents the entire rotation from current facing to new facing and clamp its angle to a maximum angle? Multiplying by a scaler seems to be a dirty hack.

Edit: You're right, the Align function does what my GetRotationTo function did, so I'm going to use that instead. Dunno how I missed that.

Share this post


Link to post
Share on other sites
jyk is correct - when a quaternion is no longer a unit quaternion then it does not represent a rotation.

What you could do is to compute the rotation (axis and angle) to map "m_Facing" onto "newFacing", then create a quaternion from the results, but use angle*0.65f for the angle when setting up the quaternion.

Something like:


// Assuming m_Facing and newFacing are already normalized and non-zero
Vector3f Axis = Vector3Cross( m_Facing, newFacing );
float Dot = Vector3Dot ( m_Facing, newFacing );
float Angle = 0.0f;

// Clamp dot to valid range incase of float accuracy
if( Dot < -1.0f )
Dot = -1.0f;
else if( Dot > 1.0f )
Dot = 1.0f;

// Valid rotation axis?
if( Axis.Length() > EPSILON )
{
// Compute final axis and angle
Axis.Normalize();
Angle = acos( Dot );
}
else
{
// Directions are the same/opposite so the cross product is undefined

// Something like this might work if your aim direction can never be vertically up:
Axis = Vector3f( 1.0f, 0.0f, 0.0f );
if( Dot > 0 )
Angle = 0; // Facing same direction
else
Angle = DegreesToRadians( 180.0f ); // Facing opposite directions
}

// We now have the axis and angle so create a quaternion that will
// apply this rotation over time
quaternion Q;
Q.SetupFromAxisAndAngle( Axis, Angle * 0.65f );

// Apply to current facing direction
m_Facing = Q * m_Facing;
m_Facing.Normalize();



Hope that helps.


Share this post


Link to post
Share on other sites
m_Facing = (quat * 0.65f) * m_Facing;
is wrong, should do turn instantly, unless this lib have very weird/insane overloaded operator* that return quaternion to scalar power.

If you scale quaternion it doesn't change rotation it stores (remember that quaternion can store rotation and scale), so it will have exactly same rotation even if you don't multiply, just different scale, that will go away at normalization. (unless wildmagic have wrong quaternion multiply)

You need to raise quaternion to power , that's is,
m_Facing = quat0.65f * m_Facing;

or to use something like slerp(identity,quat,0.65)

It's the same things, in fact.

As quick fix, instead of raising to power, You can try
if(quat.w<0){
quat.w=-quat.w;
quat.x=-quat.x;
quat.y=-quat.y;
quat.z=-quat.z;
}
quat.w+=1.0f;
// quat.normalize(); // uncomment this line if something works wrong, if wildmagic is incorrect.
m_Facing = quat * m_Facing;
m_Facing.Normalize();// to compensate roundoff errors.


- for unit-length quat, it will compute square root of quat. You can try using other numbers instead of 1.0f

As about rare bug, it is principial problem of quat.GetRotationTo, i think. It is just undefined if you are facing in opposite direction.
Fix:
if(fDot<0.99)m_Facing=m_Facing*any_small_turn;

where any_small_turn may be random or predefined, as you like.

Share this post


Link to post
Share on other sites
Thanks alot for the suggestions guys. I don't know why the scaler worked, just know that it did, but I've changed my implementation slightly based on what I've learned here. Appreciate it.

Share this post


Link to post
Share on other sites

This topic is 4731 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.

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