Sign in to follow this  
Kahsm

3D Quaternion Camera Problems (Full Source)

Recommended Posts

Greetings, I wrote a C# 3D free camera today using the Quaternion method I found here (C++ version) but have some (hopefully) small problems. First of all, if you have managed 9.0c installed, you can try it out by clicking here. The complete source is below. Instructions: A W S D for movement, R F to move the camera up and down + - to change the camera speed Arrow Keys or hold the right mouse button and move the mouse to look around. Note: Roll isn't implimented yet (not on purpose at least) The big problem is that if you move the mouse in a large circle either clockwise or counter-clockwise, then the object will start rotating on the Z axis (the camera is rolling), obviously it shouldn't. If you move your head in large circles clockwise, you don't end up upside-down (at least I don't). The camera's rotation method is based on rotating around it's local axis and this seems to be the problem. I'm not sure if this is related, but it seems to also be fish-eyed a bit when I render. If I strafe left and right then the object rotates around Y slightly like it would in a fish eye effect. This slight rotating when I move the object to the edges may have something to do with the camera roll? The C++ source has the same issues it seems. Also there's times when the the camera will just jump elsewhere. I havn't been able to mimic this in the C++ version though, so it might be a bug I'll ahve to find later. But if someone could help with that it'd be great. If someone has time to take a look and make some suggestions, that would be great.
	public class Camera
	{
		#region Private Data
		
		private DirectX.Matrix _viewMatrix;
		private bool _valid;

		private DirectX.Vector3		_cameraPosition;
		private DirectX.Quaternion	_cameraOrientation;

		#endregion
        
		#region Protected Data

		protected float	_moveSpeed;
		protected float	_slerpSpeed;
		protected float	_rotateRPM;

		#endregion

		#region Properties

		public DirectX.Matrix ViewMatrix 
		{
			get
			{
				if(!_valid)
				{
					UpdateViewMatrix();
				}

				return _viewMatrix;
			}
		}

		public float MoveSpeed
		{
			get { return _moveSpeed; }
			set { _moveSpeed = value; }
		}

		public float RotateRPM
		{
			get { return _rotateRPM; }
			set { _rotateRPM = value; }
		}
		
		public float SlerpSpeed
		{
			get { return _slerpSpeed; }
			set { _slerpSpeed = value; }
		}

		#endregion

		public enum Axis
		{
			X, Y, Z
		}
		
		public Camera()
		{
			_moveSpeed	= 10.0f;
			_slerpSpeed	= 0.1f;
			_rotateRPM	= 60.0f;	// roations per minute
			
			_viewMatrix			= DirectX.Matrix.Identity;
			_cameraOrientation	= DirectX.Quaternion.Identity;
			_cameraPosition		= new DirectX.Vector3(0, 0, 0);

			_valid = false;
		}

		public void MoveTo(
			float positionX, float positionY, float positionZ,
			float targetX, float targetY, float targetZ,
			float upVectorX, float upVectorY, float upVectorZ)
		{
			MoveTo(
				new DirectX.Vector3(positionX, positionY, positionZ),
				new DirectX.Vector3(targetX, targetY, targetZ),
				new DirectX.Vector3(upVectorX, upVectorY, upVectorZ));
		}

		public void MoveTo(DirectX.Vector3 position, DirectX.Vector3 target, DirectX.Vector3 upVector)
		{
			_viewMatrix = DirectX.Matrix.LookAtLH(position, target, upVector);		

			_cameraPosition		= position;
			_cameraOrientation	= DirectX.Quaternion.RotationMatrix(_viewMatrix);

			_valid = true;
		}

		public void Translate(float distance, Axis axis)
		{
			DirectX.Vector3 direction;

			switch(axis)
			{
				case Axis.X: direction = new DirectX.Vector3(_viewMatrix.M11, _viewMatrix.M21, _viewMatrix.M31); break;
				case Axis.Y: direction = new DirectX.Vector3(_viewMatrix.M12, _viewMatrix.M22, _viewMatrix.M32); break;
				case Axis.Z: direction = new DirectX.Vector3(_viewMatrix.M13, _viewMatrix.M23, _viewMatrix.M33); break;
				default: goto case Axis.X;
			}

			_cameraPosition += direction * distance * _moveSpeed;

			_valid = false;
		}

		public void Rotate(float angle, Axis axis)
		{
			angle *= (_rotateRPM / 60);

			switch(axis)
			{
				case Axis.X: Rotate(angle, new DirectX.Vector3(1.0f, 0.0f, 0.0f)); break;
				case Axis.Y: Rotate(angle, new DirectX.Vector3(0.0f, 1.0f, 0.0f)); break;
				case Axis.Z: Rotate(angle, new DirectX.Vector3(0.0f, 0.0f, 1.0f)); break;
			}
		}

		public void Slerp(DirectX.Quaternion targetOrientation)
		{
			_cameraOrientation = DirectX.Quaternion.Slerp(_cameraOrientation, targetOrientation, _slerpSpeed);
			_cameraOrientation = DirectX.Quaternion.Normalize(_cameraOrientation);

			_valid = false;
		}

		protected void Rotate(float angle, DirectX.Vector3 axisVector)
		{
			_cameraOrientation *= DirectX.Quaternion.RotationAxis(GetCameraAxis(axisVector), angle);

			_valid = false;
		}

		private DirectX.Vector3 GetCameraAxis(DirectX.Vector3 axisVector)
		{
			DirectX.Vector3 cameraAxis;
			DirectX.Matrix cameraRotation;

			cameraRotation = DirectX.Matrix.RotationQuaternion(_cameraOrientation);

			cameraAxis.X = axisVector.X * cameraRotation.M11 + axisVector.Y * cameraRotation.M21 + axisVector.Z * cameraRotation.M31 + cameraRotation.M41;
			cameraAxis.Y = axisVector.X * cameraRotation.M12 + axisVector.Y * cameraRotation.M22 + axisVector.Z * cameraRotation.M32 + cameraRotation.M42;
			cameraAxis.Z = axisVector.X * cameraRotation.M13 + axisVector.Y * cameraRotation.M23 + axisVector.Z * cameraRotation.M33 + cameraRotation.M43;

			return cameraAxis;
		}

		private void UpdateViewMatrix()
		{
			// Build new view matrix

			DirectX.Matrix cameraTranslation = DirectX.Matrix.Translation(-_cameraPosition.X, -_cameraPosition.Y, -_cameraPosition.Z);
			DirectX.Matrix cameraRotation = DirectX.Matrix.RotationQuaternion(_cameraOrientation);

			_viewMatrix = cameraTranslation * cameraRotation;

			_valid = true;
		}
	}

Thanks,

Share this post


Link to post
Share on other sites
Update:

So after playing with a coaster for awhile (pretending it was the front of my view frustrom) i have found that the z rotation is the nature of this type of camera. I have also read other posts with this "problem" with no answer.

Well the reason there are few answers to the problem is because it's not a problem. Type 1 error (rejecting a true hypothesis). The camera should rotate like that! It's not how we WANT it to rotate, but that's how that type of camera behaves.

So now I need to figure out how to change the quaternions so they rotate around axis that give a more perferable result.

Any suggestions?
If not, I'll keep you all updated.

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