Skip to main content
GameDev.net gamedev.net
🔒 Locked

camera movement (strafe) problem

Started by Chett2001 Jul 28, 2007 at 12:15 PM 11 replies 1.7k views
Original Post
Chett2001
Chett2001
Hey ive been working on the controls for a simple FPS and i got stuck on trying to create strafe using the D and A keys, below is an example of how i done the regular backwards movement:
if( ::GetAsyncKeyState('S') & 0x8000f )
		{
			
			D3DXVECTOR3 vDirection;
			D3DXVec3Normalize(&vDirection,
                  &(target - pos)); 
			pos -= vDirection * (TimeDelta * 4); 
			D3DXMatrixLookAtLH(&V, &pos, ⌖, &up);

		}
This worked perfectly but i couldnt figure out how to translate the eye position to the 'right', i read that to create a 'right' vector you have to cross the 'up' vector with the 'look' vector which ive done, but after that im lost:

		if( ::GetAsyncKeyState('D') & 0x8000f )
		{
			
			D3DXVECTOR3 vRightStraffe;
			D3DXVec3Cross(&vRightStraffe, &up,⌖);
			pos += vRightStraffe * (TimeDelta * 4); 
			D3DXMatrixLookAtLH(&V, &pos, ⌖, &up);
		}
So can anyone point me out where im going wrong, as allways its appreciated.
Chett - "I look forward to helping all of you one day, but right now im just a noob learning his way through the perils of game Development."
Stani R
Stani R
Your code looks correct in that you are translating the position by the right vector. I think you also need to translate the point you are looking at, though. Otherwise you'll get a kind of fake orbiting effect.
jollyjeffers
jollyjeffers
As far as I can see this isn't a problem with the Direct3D API, so it doesn't belong in this forum. I'm moving it to 'Maths & Physics' where it should be more appropriate.


Cheers,
Jack
<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ |
Chett2001
Chett2001
O sorry, thanks for the response so far.. Here is the updated code as you said:

if( ::GetAsyncKeyState('D') & 0x8000f )		{			D3DXVECTOR3 vRightStraffe;			D3DXVec3Cross(&vRightStraffe, &up,⌖);			pos += vRightStraffe * (TimeDelta * 4); 			target += vRightStraffe * (TimeDelta * 4); 			D3DXMatrixLookAtLH(&V, &pos, ⌖, &up);		}


vector values:
D3DXVECTOR3 pos(0.0f, 0.0f, 11.9f);D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);


Im definately getting the fake orbit that you described earlier when i change the values of my position and target vectors, to something more like this:

D3DXVECTOR3 pos(5.0f, 0.0f, 11.9f);D3DXVECTOR3 target(5.0f, 0.0f, 0.0f);D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);


I notices that with the former D3DXVec3Cross produces 0,0,0.. proberbly why it doesnt show any movement in the camera. So where am i going wrong with this?
Chett - "I look forward to helping all of you one day, but right now im just a noob learning his way through the perils of game Development."
Zakwayda
Zakwayda
Quote:
D3DXVec3Cross(&vRightStraffe, &up,⌖);
This isn't going to give you anything meaningful; you need to cross with 'target - pos', not target (note that the former is a vector, while the latter is a point).

I also have to comment on the whole 'target-position' camera representation issue (it's a bit of a pet peeve of mine). I think this got started due to the convenience of using a 'look at' function to set up the view transform, and was then propagated via some online tutorials, but it's really a nonsensical way to represent a camera (especially an FPS camera).

It's not that it can't or doesn't work (it can and it does), but it's inelegant and roundabout, and can lead to problems such as the one you're dealing with now.

For simple FPS motion, I strongly recommend using a pair of Euler angles (yaw and pitch) instead, and constructing both the movement vectors and the view transform directly from these angles (feel free to ask if you need further details on how to do this).
Chett2001
Chett2001
Quote:
Original post by jyk
Quote:
D3DXVec3Cross(&vRightStraffe, &up,⌖);
This isn't going to give you anything meaningful; you need to cross with 'target - pos', not target (note that the former is a vector, while the latter is a point).

I also have to comment on the whole 'target-position' camera representation issue (it's a bit of a pet peeve of mine). I think this got started due to the convenience of using a 'look at' function to set up the view transform, and was then propagated via some online tutorials, but it's really a nonsensical way to represent a camera (especially an FPS camera).

It's not that it can't or doesn't work (it can and it does), but it's inelegant and roundabout, and can lead to problems such as the one you're dealing with now.

For simple FPS motion, I strongly recommend using a pair of Euler angles (yaw and pitch) instead, and constructing both the movement vectors and the view transform directly from these angles (feel free to ask if you need further details on how to do this).


This is my first venture into this type of thing so further help or documentation would be very useful if there is a better way of doing things that isnt to complicated. As far as this way goes I dont get exactly what you mean by a 'point', D3DXVec3Dot takes 2 vectors, or have i misunderstood what your saying :0.

[Edited by - Chett2001 on July 28, 2007 1:14:50 PM]
Chett - "I look forward to helping all of you one day, but right now im just a noob learning his way through the perils of game Development."
Zakwayda
Zakwayda
Quote:
Original post by Chett2001
This is my first venture into this type of thing so further help or documentation would be very useful if there is a better way of doing things that isnt to complicated.
Here are a couple of links:

Thread 1
Thread 2

The first thread includes a detailed explanation of how to implement FPS-style motion using Euler angles, and the second thread includes working source code for both FPS and 6DOF motion.

Again, you can certainly stick with your current method, but doing it the 'right way' from the outset will probably serve you better in the long run.
Zakwayda
Zakwayda
Quote:
Original post by Chett2001
As far as this way goes I dont get exactly what you mean by a 'point', D3DXVec3Dot takes 2 vectors, or have i misunderstood what your saying :0.
It's just a mathematical issue - it doesn't have any direct bearing on the code or the types used. For the time being at least, you can simply ignore the 'point-vector' comment.
Stani R
Stani R
I'm a bit shaky on the camera issue, but I was writing camera code just yesterday, so hopefully anything I say won't be too wrong. Keep in mind that I'm working with Java (minor issue) and OpenGL (slightly more problematic so try to think along). I'm also using quaternions for rotations, but I apply them separately as axis-angles (at least on the interface side) before caching the result so I think I understand what I'm talking about... probably.

To represent a camera with pitch and yaw instead of target-lookat is relatively straightforward.

First, you will keep track of three variables.

float pitch               // changed by up/downfloat yaw                 // changed by left/right Vector3f cameraPosition   // changed by movement


The basic algorithm for getting the view matrix is then something like this:

translate(cameraPosition);rotate(yaw, 0.0f, 1.0f, 0.0f);   // axis-angle rotation around y-axisrotate(pitch, 1.0f, 0.0f, 0.0f); // axis-angle rotation around x-axis


Whether you need to apply these in this order or in reverse order is something you should already know. I invert the transform matrix so I apply them in this order even though OpenGL needs view transformation applied in reverse order.

The following snipplet shows how I then handle strafing in my FreeformCamera class. You'll notice that it's pretty much the same thing you do. You can get the facing vector by applying the above rotations to a matrix and extracting the facing vector from there, but keep in mind that DirectX matrices are of a different major order than OpenGL matrices.

public void scrollLeftRight(float delta) {        Vector3f facing = new Vector3f();	facing.x = cachedTransform.m20;	facing.y = cachedTransform.m21;	facing.z = cachedTransform.m22;			Vector3f up = new Vector3f(0f, 1f, 0f);	Vector3f right = new Vector3f();			Vector3f.cross(facing, up, right);		move(-delta * right.x, 0, -delta * right.z);		}


For reference, here is the whole beast. It probably will be more in the way than help you, and the code is suitably ugly, but feel free to rip out anything if it helps you at all. Just about the only thing I can say about it is that it seems to work just fine. [grin] At the very least, it puts the above snipplets in perspective.

public class FreeformCamera extends Camera{	private Matrix4f cachedTransform = new Matrix4f();	private Vector3f position = new Vector3f();	private Quaternion orientation = new Quaternion();		/** the view matrix, inverse of camera's transform */	private Matrix4f cachedView = new Matrix4f(); 		private BoundingSphere bounds;			/** how much more to zoom */ 	private float zoomBuffer;		/** how much to zoom per step */	private float zoomStep;	/** the field of view angle for perspective */	private float fieldOfView = 45.0f; 		/** aspect ratio */	private float aspect = 1.35f;		/** far clipping plane */	private float clipPlaneFar = 1000000f; 	/** near clipping plane */	private float clipPlaneNear = 20f;		private float pitch = 0f;	private float yaw = 0f;	private float roll = 0f; // here for completeness, won't be implemented	private SolConfiguration conf;			public FreeformCamera(SolConfiguration conf) {		super(conf);		this.conf = conf;		zoomStep = 10f * conf.wheelSensitivity;	}	public void update() {		if( zoomBuffer > 0 )		{			doZoom(zoomStep);			zoomBuffer -= zoomStep;		}		else if( zoomBuffer < 0 ) {			doZoom(-zoomStep);			zoomBuffer += zoomStep;		}				if(Math.abs(zoomBuffer) < zoomStep)			zoomBuffer = 0;	}	public void setClipPlaneFar(float clipPlaneFar) {		this.clipPlaneFar = clipPlaneFar;	}	public void setClipPlaneNear(float clipPlaneNear) {		this.clipPlaneNear = clipPlaneNear;	}	public void setFieldOfView(float fieldOfView) {		this.fieldOfView = fieldOfView;	}		public void setAspect(float aspect) {		this.aspect = aspect;	}	/**	* load the view transform	* 	*/	public void loadView() {		GL11.glLoadIdentity();		FloatBuffer buf = BufferUtils.createFloatBuffer(16);		cachedView.store(buf);		buf.rewind();		GL11.glMultMatrix(buf); // add model transform			}	/**	* Set perspective transformation.	*	*/	public void setPerspective() {		GL11.glMatrixMode(GL11.GL_PROJECTION);		GL11.glLoadIdentity();		GLU.gluPerspective(			fieldOfView ,			aspect,			clipPlaneNear,			clipPlaneFar);				GL11.glMatrixMode(GL11.GL_MODELVIEW);	}	/**	* Set perspective transformation for use with picking.	*	*/	public void setPickPerspective(float x, float y) {		IntBuffer viewport = BufferUtils.createIntBuffer(16);//      IntBuffer viewport = ByteBuffer.allocateDirect(64).order(ByteOrder.nativeOrder()).asIntBuffer();		GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);		int[] view = new int[4];		viewport.get(view);					GL11.glMatrixMode(GL11.GL_PROJECTION);		GL11.glLoadIdentity();		GLU.gluPickMatrix(x, y, 5.0f, 5.0f, view);		GLU.gluPerspective(fieldOfView, aspect,				clipPlaneNear,clipPlaneFar);			GL11.glMatrixMode(GL11.GL_MODELVIEW);			}	/**	 * caches the transform, caches the view matrix, and	 * calculates camera orientation independent of parent	 * orientation	 * 	 */	private void cacheTransform() {		Matrix4f tmp = new Matrix4f();				tmp.translate(position);//		// clear parent orientation for camera//		tmp.m00 = 1; tmp.m01 = 0; tmp.m02 = 0;//		tmp.m10 = 0; tmp.m11 = 1; tmp.m12 = 0;//		tmp.m20 = 0; tmp.m21 = 0; tmp.m22 = 1;				Matrix4f.mul(tmp, MathHelper.quatToMatrix(orientation), tmp);				Matrix4f.load(tmp, cachedTransform);				// cache the view matrix		Matrix4f.invert(cachedTransform, cachedView);			}		/**	 * call this instead of rotate	 * 	 * @param delta	 */	public void pitch(float delta) {		orientation = new Quaternion();		pitch += delta;		if (pitch >= 360f) {			pitch -= 360f;	}		else if (pitch <= 0f) {			pitch += 360f;	}				rotate(yaw, 0.0f, 1.0f, 0.0f);		rotate(pitch, 1.0f, 0.0f, 0.0f);	}		/**	 * call this instead of rotate	 * 	 * @param delta	 */	public void yaw(float delta) {		orientation = new Quaternion();		yaw += delta;		if (yaw >= 360f) {			yaw -= 360f;		}		else if (yaw <= 0f) {			yaw += 360f;		}				rotate(yaw, 0.0f, 1.0f, 0.0f);		rotate(pitch, 1.0f, 0.0f, 0.0f);	}		/**	 * scroll left/right along right vector	 * 	 * @param delta	 */	public void scrollLeftRight(float delta) {		Vector3f facing = new Vector3f();		facing.x = cachedTransform.m20;		facing.y = cachedTransform.m21;		facing.z = cachedTransform.m22;				Vector3f up = new Vector3f(0f, 1f, 0f);		Vector3f right = new Vector3f();				Vector3f.cross(facing, up, right);				move(-delta * right.x, 0, -delta * right.z);				}			/**	 * scroll forward/back 	 * 	 * @param delta	 */	public void scrollForwardBack(float delta) {		Vector3f facing = new Vector3f();		facing.x = cachedTransform.m20;		facing.y = 0f; // only along plane		facing.z = cachedTransform.m22;				move(-delta * facing.x, 0, -delta * facing.z);				}			public void zoom(float delta) {		if( delta < -120f * conf.wheelSensitivity )			delta = -120f * conf.wheelSensitivity;				if( delta > 120f * conf.wheelSensitivity )			delta = 120f * conf.wheelSensitivity;				zoomBuffer += delta;	}		/**	 * moves camera forwards and backwards	 * 	 * @param delta	 */	private void doZoom(float delta) {		Vector3f facing = new Vector3f();		facing.x = cachedTransform.m20;		facing.y = cachedTransform.m21;		facing.z = cachedTransform.m22;				//move(-delta * facing.x, 0, -delta * facing.z);			move(-delta * facing.x, -delta * facing.y, -delta * facing.z);	}	public float getPitch() {		return pitch;	}	public float getRoll() {		return roll;	}	public float getYaw() {		return yaw;	}	public Matrix4f getCachedView() {		return cachedView;	}		public BoundingSphere getBounds() {		return bounds;	}	public void move(Vector3f dist) {		Vector3f.add(position, dist, position);				cacheTransform();	}		public void move(float dx, float dy, float dz) {		position.x += dx;		position.y += dy;		position.z += dz;				cacheTransform();	}		/**	 * wrapper	 * 	 * @param angle degrees of rotation	 * @param x axis component	 * @param y axis component	 * @param z axis component	 */	private void rotate(float angle, float x, float y, float z) {		rotate(angle, new Vector3f(x, y, z));			}		/**	 * 	 * add axis-angle rotation	 * 	 * @param angle degrees of rotation	 * @param axis defines axis from origin to this point	 */	private void rotate(float angle, Vector3f axis) {		Quaternion rot = new Quaternion();				axis.normalise();				Vector4f a1 = new Vector4f(axis.x, axis.y, axis.z, 				angle * Constants.DEG2RAD);				rot.setFromAxisAngle(a1);				orientation = Quaternion.mul(orientation, rot, null);						cacheTransform();	}		public Quaternion getOrientation() {		return orientation;	}		public Vector3f getFacing() {		Vector3f facing = new Vector3f();		facing.x = cachedTransform.m20;		facing.y = cachedTransform.m21;		facing.z = cachedTransform.m22;		return facing;	}		public Vector3f getPosition() {		return position;	}}
Chett2001
Chett2001
Gah, i would like to try all your different methods out really i would but i cant even manage to get my original code working.. It simply doesnt do anything at the moment, no movement nothing. Besides when i change the starting position from D3DXVECTOR3 pos(0.0f, 0.0f, 11.9f) to something like D3DXVECTOR3 pos(5.0f, 0.0f, 11.9f) and then it only does a sort of orbit as a previous poster describes, surely there is something wrong with my implementation, can anyone see whats wrong and explain exactly what i need to do? Here is the current code:

D3DXVECTOR3 pos(0.0f, 0.0f, 11.9f);D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);if( ::GetAsyncKeyState('D') & 0x8000f )		{			D3DXVECTOR3 vRightStraffe;			D3DXVec3Cross(&vRightStraffe, &up,⌖);			pos += vRightStraffe * (TimeDelta * 4); 			target += vRightStraffe * (TimeDelta * 4); 			D3DXMatrixLookAtLH(&V, &pos, ⌖, &up);			}


An answer in DirectX would be extremely preferable as i only have 6 months divided experience in DirectX !!
Chett - "I look forward to helping all of you one day, but right now im just a noob learning his way through the perils of game Development."
Zakwayda
Zakwayda
Quote:
D3DXVec3Cross(&vRightStraffe, &up,⌖);
Re-read my first reply, and then try modifying the above code accordingly. I can't guarantee it'll fix the problem (there may be problems elsewhere in your code as well), but it should be a step in the right direction.

(I may have misled you when I said you could ignore my 'point-vector' comment. I just meant you could safely ignore the rational behind the proposed change - not the proposed change itself.)
bronxbomber92
bronxbomber92
The easiest thing might be just storing your location and view in a matrix..

This gives the basic principles behind matrices (I'm sure their are better resources out there though ;) ). http://www.morrowland.com/apron/tutorials/gl/gl_matrix.php
Chett2001
Chett2001
I forgot to say that was the problem jyk, here was the correct code.. I wasnt Normalising correctly as you said. Thanks.

if( ::GetAsyncKeyState('D') & 0x8000f )		{			D3DXVECTOR3 vDirection;			D3DXVec3Normalize(&vDirection,                  &(target - pos)); //create direction vector			D3DXVec3Cross(&vDirection,&vDirection,&up); 			pos -= vDirection * (TimeDelta*4);			target -= vDirection * (TimeDelta*4);			D3DXMatrixLookAtLH(&V, &pos, ⌖, &up);		}
Chett - "I look forward to helping all of you one day, but right now im just a noob learning his way through the perils of game Development."

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.