## News:

11 March 2016 - Forum Rules

## Camera vectors rotation and vertical strafe

Started by Gemini, September 04, 2012, 04:39:56 PM

#### Gemini

I'm currently working on an editor for a game that uses static camera angles based on position and direction vectors (or eye and at, if you prefer). So far I got most of the implementation correctly, but I'm pretty much 3D-dumb and what I'd like to do now is kinda out of my reach due to crappy math knowledge: manipulating the camera via mouse dragging. At the moment I got a working zoom feature and horizontal strafe. What I'm missing is vertical strafe and rotation (or rather, a working rotation system).

Here's the current code ('????' lines basically translate as "I'm totally lost, please help me fill gap"):
`void Strafe(D3DXVECTOR3 &eye, D3DXVECTOR3 &at, float dx, float dy){ D3DXVECTOR3 t; // get direction and normalize it D3DXVec3Cross(&t,&(at-eye),&D3DXVECTOR3(0,-1.f,0)); D3DXVec3Normalize(&t,&t); // apply horizontal strafe eye-=t*dx; at-=t*dx; // apply vertical strafe ????}void CBackgroundPanel::OnMouseMove(int xPos, int yPos){ int diff_x=xPos-mouse_dragX; int diff_y=yPos-mouse_dragY; float x, y; D3DXVECTOR3 eye, at; D3DXVECTOR3 trans; switch(mouse_drag) { case MODE_CAMERA_ROTATE:#if 0 // convert eye and at vectors to float coordinates SetCameraVector(&eye,&at); x=(float)diff_x/ClientSize.cx; y=(float)diff_y/ClientSize.cy; // apply rotation ???? // back to 1.19.12 fixed point coordinates room->camera.pos[camera_index].camera_to_x=(int)(at.x*4096.f); room->camera.pos[camera_index].camera_to_y=(int)(at.y*4096.f); room->camera.pos[camera_index].camera_to_z=(int)(at.z*4096.f); // update mouse data and views mouse_dragX=xPos; mouse_dragY=yPos; Invalidate(); m_Mesh->Invalidate();#endif break; case MODE_CAMERA_MOVE: // convert eye and at vectors to float coordinates SetCameraVector(&eye,&at); x=(float)diff_x/ClientSize.cx; y=(float)diff_y/ClientSize.cy; // apply strafe Strafe(eye,at,x,y); // back to 1.19.12 fixed point coordinates room->camera.pos[camera_index].camera_from_x=(int)(eye.x*4096.f); room->camera.pos[camera_index].camera_from_y=(int)(eye.y*4096.f); room->camera.pos[camera_index].camera_from_z=(int)(eye.z*4096.f); room->camera.pos[camera_index].camera_to_x=(int)(at.x*4096.f); room->camera.pos[camera_index].camera_to_y=(int)(at.y*4096.f); room->camera.pos[camera_index].camera_to_z=(int)(at.z*4096.f); // update mouse data and views mouse_dragX=xPos; mouse_dragY=yPos; Invalidate(); m_Mesh->Invalidate(); break; case MODE_CAMERA_ZOOM: // convert eye and at vectors to float coordinates SetCameraVector(&eye,&at); //create direction vector D3DXVec3Normalize(&trans,&(at-eye)); eye+=trans*(((float)diff_x)/ClientSize.cx); at+=trans*(((float)diff_x)/ClientSize.cx); // back to 1.19.12 fixed point coordinates room->camera.pos[camera_index].camera_from_x=(int)(eye.x*4096.f); room->camera.pos[camera_index].camera_from_y=(int)(eye.y*4096.f); room->camera.pos[camera_index].camera_from_z=(int)(eye.z*4096.f); room->camera.pos[camera_index].camera_to_x=(int)(at.x*4096.f); room->camera.pos[camera_index].camera_to_y=(int)(at.y*4096.f); room->camera.pos[camera_index].camera_to_z=(int)(at.z*4096.f); // update mouse data and views mouse_dragX=xPos; mouse_dragY=yPos; Invalidate(); m_Mesh->Invalidate(); break; }}`

If the explanation about horizontal and vertical strafe isn't clear, please use this video as a reference. To make it simple, the camera follows mouse movements as if it all was a 2D scene with 3D objects.

#### Klarth

If you have no idea where to go, this might help...I'm rusty myself however.

So you're trying to rotate vector3 eye (which describes a direction) about vector3 at (which describes a point).  To do this, you must calculate and apply a rotation matrix, R, to vector3 eye via multiplication.  R will be a 4x4 matrix if you use the D3DX functions, so that means you will temporarily convert vector3 eye to a vector4 (set W to 1).  D3DXMatrixRotationYawPitchRoll can create a 4x4 rotation matrix for you.  You can then use D3DXMatrixMultiply (eye4 * R, ie. 1x4 vector * 4x4 matrix = 1x4 vector) to apply the results to your eye4.  Then pull the x, y, z (top three elements) from the matrix for your eye vector3, and apply the camera.  That's the general idea anyways.

http://msdn.microsoft.com/en-us/library/windows/desktop/bb206269%28v=vs.85%29.aspx gives an ok overview.  I think there's a way to do the same thing via View or Projection matrices as well, which may be simpler.

#### ETG

I'm assuming that your keeping two points, a camera position and a target position, and from that figuring the view direction and camera matrix from that?

For the strafe:
You crossed the view direction with a global up to get the side vector. You can do the same for the vertical direction too. Just cross your view direction with the side vector you just figured out to get the correct up vector and use that.

`void Strafe(D3DXVECTOR3 &eye, D3DXVECTOR3 &at, float dx, float dy){ D3DXVECTOR3 t, UpVec; // get direction and normalize it D3DXVec3Cross(&t,&(at-eye),&D3DXVECTOR3(0,-1.f,0)); D3DXVec3Normalize(&t,&t); // apply horizontal strafe eye-=t*dx; at-=t*dx; // apply vertical strafe        D3DXVec3Cross(&UpVec,&(at-eye),&t); D3DXVec3Normalize(&UpVec,&UpVec); eye-= UpVec * dy; at-= UpVec * dy;}`

For rotation,  I'd build a rotation matrix for the rotation you want, rotate you view vector by that matrix, then move the either the target or camera position point to the other point + the rotated vector.

You may want to keep track of the whole matrix for the camera. You could easily do a look at function to point the matrix at a target, and you'd have the local vectors in the matrix for panning and moving translations. It be easier to rotate too.

#### BRPXQZME

Probably shouldn't use the global direction, though. Just have the up vector to begin with so the camera can go freestylin (not really sure how the rest of this is set up, so I can't say what else to do).

I concur with the above two posts in that doing these camera moves with matrices is probably easier. But you do have to wrap your head around what the matrices mean first.
we are in a horrible and deadly danger

#### Gemini

Quote from: ETG on September 04, 2012, 11:36:12 PMI'm assuming that your keeping two points, a camera position and a target position, and from that figuring the view direction and camera matrix from that?
Yup, this is what I do for setting the projection and view matrices:
` D3DXMatrixPerspectiveFovRH(&matProjection, DEGTORAD(60.0f),(float)ClientSize.cx/(float)ClientSize.cy,IntToFloat(pos->view_r>>7),4096.0f); Device()->SetTransform(D3DTS_PROJECTION, &matProjection); // apply camera D3DXMatrixLookAtRH(&matView, &D3DXVECTOR3(IntToFloat(pos->camera_from_x),IntToFloat(pos->camera_from_y),IntToFloat(pos->camera_from_z)), &D3DXVECTOR3(IntToFloat(pos->camera_to_x),IntToFloat(pos->camera_to_y),IntToFloat(pos->camera_to_z)), &D3DXVECTOR3(0,-1.0f,0)); Device()->SetTransform(D3DTS_VIEW, &matView);`

QuoteJust cross your view direction with the side vector you just figured out to get the correct up vector and use that.
It works like a charm, yay! Now it's time to figure out how to manipulate my direction vector so that rotation happens the way it's supposed to.

#### ETG

Oh, remember that you won't be able to aim the camera perfectly up or down. That's the problem with using the global up vector. Ever notice in most first person games how you can't look directly up or down? That why. Usually it's not a problem, but if your doing a CAD like program, then you'll want your top view to be looking down, and you'll have to handle that one way or another.

#### BRPXQZME

It's called "gimbal lock" if you would like to look up the causes and solutions.
we are in a horrible and deadly danger

#### Gemini

I haven't had problems with gimbal lock so far because all I did was implementing a very broken horizontal rotation:
`void Turn(D3DXVECTOR3 &eye, D3DXVECTOR3 &at, float dx, float dy){ D3DXVECTOR3 t; // get direction D3DXVec3Cross(&t,&(at-eye),&D3DXVECTOR3(0,-1.f,0)); t.y=0; // constrain the vector to the XZ plane D3DXVec3Normalize(&t,&t); at-=t*dx;}`
This one makes the at vector rotate more or less as intended, but it keeps increasing the distance with eye, which is definitively not the intended behavior. <.<

#### ETG

Well, your not doing a real rotation, so it's no surprise it's getting thrown off. What you're doing is very quick and dirty. It's gonna give you problems if you try to do bigger rotations.

If you want to make it work, you gonna need to reset the distance when it goes off (or even every time to turn the camera.) To do this make a new vector between the points. Normalize that vector, then scale it by how long the distance should be. Then you can move the point to by setting it equal to the other point plus the vector you just set.

#### Gemini

Just a quick reply since I've been procrastinating like there's no tomorrow. So, the good news is I got even the rotation to work as ETG suggested. Code for horizontal rotation:
` D3DXVECTOR3 t; D3DXMATRIX my; D3DXVec3Normalize(&t,&(at-eye)); D3DXMatrixRotationY(&my,dx); D3DXVec3TransformCoord(&t,&t,&my); at=eye+t;`
At first I wasn't really getting the idea behind rotating two vectors, but then I found this really interesting explanation on how it's supposed to work.