Quaternions yielding wrong result

Started by
4 comments, last by 0v3rloader 17 years ago
Hi, I am trying to employ quaternion-based rotation to a positional light, with no luck, and was hoping some of you would help me figuring out whether the math I've used is wrong or the programming has bugs. The problem I'm having is I simply can't perform any rotations. All comments/suggestions will be greatly appreciated! I am calculating the direction vector has follows:
	Vector4f rx(::g_axis_X, _dx), ry(::g_axis_Y, _dy), rz(::g_axis_Z, _dz);
	Vector3f v(::g_vector_down);

	rx.Mul(ry, rx);	// rx * ry (result into rx)
	rx.Mul(rz, rx);	// rx * rz (result into rx)
	rx.Mul(v, v);	// rx * v (result into v)

	// set light direction
	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, v);




Other relevant bits of code follow. template <typename T> Vector4
// Converts degrees to radians
static inline T Vector4<T>::DegreesToRadians(T degrees) throw()
	{ return(degrees * T(M_PI) / T(180)); }

// Construct from an axis and a rotation angle.
// NOTE: Vector-in must already be normalized.
Vector4<T>::Vector4(const Vector3<T>& axis, T degrees) throw()
{
	FromAxisAngle(axis, degrees);
}

// Assign new quaternion from an axis and an angle in degrees.
// NOTE: Vector-in *MUST ALREADY BE* normalized.
void Vector4<T>::FromAxisAngle(const Vector3<T>& axis, T degrees) throw()
{
	T sinAngle;
	degrees = DegreesToRadians(degrees) * T(0.5);
	sinAngle = sin(degrees);
	_x = axis._x * sinAngle; _y = axis._y * sinAngle;
	_z = axis._z * sinAngle; _w = cos(degrees);

}

// Calculates the inverse of the quaternion and stores it
// in another quaternion object.
void Vector4<T>::InvertTo(Vector4<T>& quat) const throw()
{
	T len = T(1) / (_x*_x + _y*_y + _z*_z + _w*_w);
	quat._x = _x*-len; quat._y = _y*-len; quat._z = _z*-len; quat._w = _w*len;
}

// Multiply by a quaternion and store in given quaternion object.
Vector4<T>& Vector4<T>::Mul(const Vector4<T>& mulQuat,
	Vector4<T>& outQuat) const throw()
{
	Vector3<T> vec1(*this), vec2(mulQuat), cross;

	outQuat._w = ((_w * mulQuat._w) - (vec1.Dot(vec2)));
		vec1.Cross(vec2, cross);

	vec1._x *= mulQuat._w; vec1._y *= mulQuat._w; vec1._z *= mulQuat._w;
	vec2._x *= _w; vec2._y *= _w; vec2._z *= _w;

	outQuat._x = (vec1._x + vec2._x + cross._x);
	outQuat._y = (vec1._y + vec2._y + cross._y);
	outQuat._z = (vec1._z + vec2._z + cross._z);

	return(outQuat);
}

// Multiply by a vector and store result in given vector object.
Vector3<T>& Vector4<T>::Mul(const Vector3<T>& mulVector, 
	Vector3<T>& outVector) const throw()
{
	Vector4<T> vectorQuat(mulVector), inverseQuat, resultQuat;

	InvertTo(inverseQuat);
	vectorQuat.Mul(inverseQuat, resultQuat);
	this->Mul(resultQuat, resultQuat);

	outVector._x = resultQuat._x;
	outVector._y = resultQuat._y;
	outVector._z = resultQuat._z;

	return(outVector);
}




template <typename T> Vector3
// Copy-constructor.
Vector3(const Vector3<T>& oV) throw()
	: _x(oV._x), _y(oV._y), _z(oV._z) {}

// Calculates cross between this vector and another. Stores
// result in another vector object.
Vector3<T>& Vector3<T>::Cross(const Vector3<T>& vector, Vector3<T>& cross) const throw()
{
	cross._x = (_y * vector._z) - (vector._z * _y);
	cross._y = (_z * vector._x) - (vector._x * _z);
	cross._z = (_x * vector._y) - (vector._y * _x);
	return(cross);
}

// Calculates dot between this vector and another.
// Does *NOT* calculate the angle in radians between the two vectors.
inline T Vector3<T>::Dot(const Vector3<T>& vector) const throw()
{
	return(_x*vector._x + _y*vector._y + _z*vector._z);
}




Global vectors/axes
CDECL const Vector3f g_vector_down(0.f, -1.f, 0.f);
CDECL const Vector3f g_axis_X(1.f, 0.f, 0.f);
CDECL const Vector3f g_axis_Y(0.f, 1.f, 0.f);
CDECL const Vector3f g_axis_Z(0.f, 0.f, 1.f);



[Edited by - 0v3rloader on April 17, 2007 9:56:23 AM]
Advertisement
This:
// Calculates cross between this vector and another. Stores// result in another vector object.Vector3<T>& Vector3<T>::Cross(const Vector3<T>& vector, Vector3<T>& cross) const throw(){	cross._x = (_y * vector._z) - (vector._z * _y);	cross._y = (_z * vector._x) - (vector._x * _z);	cross._z = (_x * vector._y) - (vector._y * _x);	return(cross);}


is wrong, as it always returns (0, 0, 0). Your quaternion multiplicaiton function also has problems as you write your result to one of the parameters as you calculate it, but are also using the same as input when e.g. you do.

Mul(resultQuat, resultQuat);

It's generally best practice to make any parameters you pass by reference const, returning results by value. Otherwise it's not obvious what code like the above does and such mistakes are made.
John BlackburneProgrammer, The Pitbull Syndicate
Quote:Original post by johnb
This:
*** Source Snippet Removed ***

is wrong, as it always returns (0, 0, 0). Your quaternion multiplicaiton function also has problems as you write your result to one of the parameters as you calculate it, but are also using the same as input when e.g. you do.

Mul(resultQuat, resultQuat);

It's generally best practice to make any parameters you pass by reference const, returning results by value. Otherwise it's not obvious what code like the above does and such mistakes are made.


You are absolutely right! Since I've corrected my Cross method everything works fine. You don't know how much of life saver you were... this was actually making me depressed!

Thanks a lot for your help, mate.


PS: The Mul method in Vector4 is taking the 2 parameter as an output one, therefore storing the result of the operation in it, which is why it isn't const. This is just a (very small) way of saving on object construction overheads. However, I also have a one-parameter version of the Mul method that returns the result and doesn't take any output parameters:
	Vector4<T> Mul(const Vector4<T> mulQuat);
Quote:Original post by 0v3rloader
PS: The Mul method in Vector4 is taking the 2 parameter as an output one, therefore storing the result of the operation in it, which is why it isn't const. This is just a (very small) way of saving on object construction overheads.


But then you do this
	outQuat._w = ((_w * mulQuat._w) - (vec1.Dot(vec2)));		vec1.Cross(vec2, cross);	vec1._x *= mulQuat._w; vec1._y *= mulQuat._w; vec1._z *= mulQuat._w;


i.e. modify outQuat._w, and immediately after use mulQuat._w. If they are the same quaternions then they are the same, so you are modifying w of your input before you use it ! Don't worry about construction overheads - you get for more performance back by making all parameters const anyway, as the compiler can do a lot more aggressive optimisation on such functions, including eliminating unnecessary constructors.
John BlackburneProgrammer, The Pitbull Syndicate
Quote:Original post by johnb
Quote:Original post by 0v3rloader
PS: The Mul method in Vector4 is taking the 2 parameter as an output one, therefore storing the result of the operation in it, which is why it isn't const. This is just a (very small) way of saving on object construction overheads.


But then you do this
*** Source Snippet Removed ***

i.e. modify outQuat._w, and immediately after use mulQuat._w. If they are the same quaternions then they are the same, so you are modifying w of your input before you use it ! Don't worry about construction overheads - you get for more performance back by making all parameters const anyway, as the compiler can do a lot more aggressive optimisation on such functions, including eliminating unnecessary constructors.


I see now... Thanks for the eye opener John!
Quote: Don't worry about construction overheads - you get for more performance back by making all parameters const anyway, as the compiler can do a lot more aggressive optimisation on such functions, including eliminating unnecessary constructors.


Do you know of any links where I could read on this sort of compiler optimizations?

This topic is closed to new replies.

Advertisement