Sign in to follow this  
Juliean

Simple screen panning easy solution?

Recommended Posts

Hello,

 

so I wanted screen panning for my editor. Hope thats even the right term, I click my mouse and the camera moves parallel to the screen as I drag the mouse. So thats what I've got so far:

 

D3DXVECTOR3 vDist3(0.0f, (float)vDist.y, -(float)vDist.x);
m_pCamera->Pan(vDist3);

Yeah, so if I look straight on the X-Axis this works fine, but as you can quess, as soon as I rotate the camera it doesn't work as expected.

 

Now I could use the law of sine to calculate two components of rotation along the x and z-axis, make a rotation matrix and transfer the vector with it, then somehow take into consideration the up-vector, etc... but I quess there is an easier solution using the view-matrix, isn't there? If so, how could this be done (I tried multiplying both the normal and inverse view matrix, but that just makes causes the camera to "zoom" out of the scene in huge steps)?

Share this post


Link to post
Share on other sites

You should have a direction (where the camera is pointing) and an up vector associated with your camera. Crossing the direction vector with the up vector should yield a vector that you can use for panning your camera side to side (e.g. scale it by the amount you need to pan, and add it to your camera's position).

Share this post


Link to post
Share on other sites

Ah, cross product, how comes i missed that. This does indeed work, thanks a lot, here is the solution:

        //access camerea attributes
        D3DXVECTOR3 vDir =     m_pCamera->GetDirection();
        D3DXVECTOR3 vUp = m_pCamera->GetUp();
        //Dir x Up - for horizontal panning
        D3DXVec3Cross(&vDir, &vDir, &vUp);
        //horizontal scale
        D3DXMATRIX mScale;
        D3DXMatrixScaling(&mScale, (float)vDist.x, (float)vDist.x, (float)vDist.x);
        D3DXVec3TransformCoord(&vDir, &vDir, &mScale);
        //vertical scale
        vUp.y *= (float)vDist.y;
        m_pCamera->Move(vDir + vUp);
 

Oh, while I am at it, what about rotation? I got it to work somehow, but...

 

 

        //access camera position
        D3DXVECTOR3 vPos(m_pCamera->GetPosition());
        D3DXMATRIX mRotation;
        D3DXMatrixIdentity(&mRotation);
        //rotation
        D3DXMatrixRotationYawPitchRoll(&mRotation, DEGTORAD(-vDist.x), 0.0f, DEGTORAD(vDist.y));
        D3DXVec3TransformCoord(&vPos, &vPos, &mRotation);
        m_pCamera->SetPosition(vPos);

Now it rotates but it really behaves weird, e.g. when the rotation radius gets smaller the nearer I get to one of the axis... any advice on that too?

Share this post


Link to post
Share on other sites

What I'd like to have is the camera circeling around its look-at position. Aside from the problem I described (thats really hard to describe in great detail, though if you'd try for yourself you'd notice it within a second) there is also the issue that if I e.g. rotate the camera to be at (0, Y, 0) it will make first a sharp turn around the y axis. What I'd like to have is a camera rotation like in maya or autodesk 3d where the camera smoothly rotates around its viewing point, if I make e.g. a 180° turn the camera will switch orientation and the scene will be viewed upside down. Any clue how to make something with the "built in" tools or do I have to get my pen and paper for some pages of sine-calculations with awkward corner-case handling (like the first camera I made...)?

Share this post


Link to post
Share on other sites

No, it should be relatively straightforward.

 

- Use a vector that points in the opposite direction of your camera's GetDirection,

- Now rotate that vector around the y-axis (or the z-axis, if z is up) by transforming it with the matrix from D3DXMatrixRotationY (or Z, etc...), which lets you say how many degrees you want it rotated

- Now negate that again and you have your camera's new direction

(note: you could probably skip the whole negate negate thing, and just rotate the direction vector in the opposite rotational direction that you want)

- And then determine the camera's new position based on the look-at position, that direction, and the camera distance

Share this post


Link to post
Share on other sites

Ok, using the direction vector at least makes it *theoretically* work for each camera position, but that still doesn't solve my issue. As long as I rotate it along one axis, everything is fine, say the z-axis. But as soon as I want to add another axis of movement, everything crashes and burns. It doesn't matter if I want to rotate x-y, y-z, or x-z, it will eigther way behave oddly:

 

        //access camera position
        D3DXVECTOR3 vDir(m_pCamera->GetDirection()), vLookAt(m_pCamera->GetLookAt());
        vDir = -vDir;
        D3DXMATRIX mRotation;
        //rotation
        D3DXMatrixRotationYawPitchRoll(&mRotation, DEGTORAD(vDist.x/10.0f), 0.0f, DEGTORAD(vDist.y/10.0f));
        D3DXVec3TransformCoord(&vDir, &vDir, &mRotation);
        m_pCamera->SetPosition(vLookAt += vDir*10.0f); //hard coded camera distance
 

The problem obviously is related to this (though I can't really put together why). If I look straigth along the x axis and want to rotate around it, no rotation happens. Otherwise, if I look straigth along the z axis and want to rotate around it, there is again no rotation at all. This seems to be the reason for the weird behaviour, since if I rotate around one axis I get closer/further away from the other, which overall changes the uhm rotation stiffness factor (don't know how else to call it) to change for this axis and so on. Obviously I can't simply rotate the direction, since I'd need to do a rotation based on the how far the camera is away from the certain axis... that can't be necessary, can it? oO

 

Edit: To give a better picture of the actual issue, I've uploaded a short video to my youtube channel: http://youtu.be/9wjMd-ZS0P4. You should see what I meant..

Edited by The King2

Share this post


Link to post
Share on other sites

Ah, I see, you're making a 3d editor type thing. I didn't get that you wanted to rotate the camera about an arbitrary axis.

 

I've never done this, but my first guess at how to implement it would be the following:

1) Create a 3d vector that corresponds to the direction you are dragging the mouse

2) Cross this vector with the camera view direction

3) Use the result as the rotation axis for D3DXMatrixRotationAxis

4) transform the camera direction with this matrix, and recalculate camera position

 

To calculate (1), I would guess you can make a screenspace vector (dx, dy, 0) and transform by the inverse view matrix to get it in worldspace.

Share this post


Link to post
Share on other sites

Yeah, it worked as you described it, thanks!

 

Well, there still is one issue. When the camera moves to look along the y-axis, therefore in the moment where the camera should "flip", it simply dead-locks and spins around the y-axis like mad when I drag the mouse further in the same direction. I quess I'd need to flip the up-vector of the camera here, can you think of an easy solution here too? Thats the currect code:

 

 

        D3DXVECTOR3 vDir(m_pCamera->GetDirection()), vLookAt(m_pCamera->GetLookAt());
        D3DXVECTOR3 vDrag(vDist.x, vDist.y, 0.0f);

        D3DXMATRIX mRotation, mView(m_pCamera->GetViewMatrix());
        //inverse view
        D3DXMatrixInverse(&mView, NULL, &mView);
        //transform mouse drag vector to object space
        D3DXVec3TransformCoord(&vDrag, &vDrag, &mView);

        D3DXVECTOR3 vAxis;
        //create axis from drag an direction vector
        D3DXVec3Cross(&vAxis, &vDrag, &vDir);

        //rotate around axis
        D3DXMatrixRotationAxis(&mRotation, &vAxis, DEGTORAD(1.0f));
        //transform direction
        D3DXVec3TransformCoord(&vDir, &vDir, &mRotation);
        //recalculate position
        m_pCamera->SetPosition(vLookAt - vDir*10.0f);
 

EDIT: It also appears that it only works when the lookat-position is in the origin. As soon as translate the camera and try to rotate afterwards, I get weird locking behaviour, again. Any help there would also be great...

Share this post


Link to post
Share on other sites

A couple of notes:

- I think you should be normalizing some of your vectors (like vDrag).

- You need to be adjusting your camera's up vector as things are rotating (I guess apply the same matrix to it that you apply to the direction).

Share this post


Link to post
Share on other sites

I already added the normalizations, but this does only affect the rotation speeds, but granted, it is one thing less to care.

 

Adding the adjustement for the cameras up vector (strange, I tried that before and it didn't do anything) did improve it indeed, but there is still two problems:

 

- The old one that the whole thing breaks if the cameras look-at is not at (0, 0, 0) - some idea on that? Must have something to do with the view-matrix multiplacation, since this is the only thing that somewhat changes when the look-at point changes.

 

- Though now there is no such thing as locking, the camera transfers smoothly as I cross the 90° border on one axis, there still is some unwanted behaviour when rotation around the second axis. Say I have rotatet my camera to look from 45° on the z-axis. Trying to rotate on the y-axis now will make the camera "tilts", its hard to descibe.. I've made another video of how it is expected, and how it works now. Maybe its my fault because I didn't tell you exactly how I want it to rotate. I actually belive there is nothing wrong with the algorythm you provided me, I just think you need a different approach to simulate such a specific camera movement like in the first part of the video. But actually I'd much rather have this, I don't think the second one is all too handy.

 

Once again, this is the current code:

 

 

        //setup vectors
        D3DXVECTOR3 vDir(m_pCamera->GetDirection()), vLookAt(m_pCamera->GetLookAt()), vUp(m_pCamera->GetUp());
        D3DXVECTOR3 vDrag(vDist.x, vDist.y, 0.0f);

        D3DXMATRIX mRotation, mView(m_pCamera->GetViewMatrix()), mWorld;
        //inverse view
        D3DXMatrixInverse(&mView, NULL, &mView);
        //transform mouse drag vector to object space
        D3DXVec3TransformCoord(&vDrag, &vDrag, &mView);
        D3DXVec3Normalize(&vDrag, &vDrag);

        D3DXVECTOR3 vAxis;
        //create axis from drag an direction vector
        D3DXVec3Cross(&vAxis, &vDrag, &vDir);
        D3DXVec3Normalize(&vAxis, &vAxis);

        //rotate around axis
        D3DXMatrixRotationAxis(&mRotation, &vAxis, DEGTORAD(D3DXVec3Length(&vDrag)));
        //transform direction and up
        D3DXVec3TransformCoord(&vDir, &vDir, &mRotation);
        D3DXVec3TransformCoord(&vUp, &vUp, &mRotation);
        //recalculate position and up
        m_pCamera->SetPosition(vLookAt - vDir*10.0f);
        m_pCamera->SetUp(vUp);
 

Do you know how to solve these last two problems? After all you've helped me I'd still be glad if you could further help me with this, while I perfecly understand all you supply me I somehow can't put it all together :/

Share this post


Link to post
Share on other sites

Hmm, I don't really have any more suggestions (as I mentioned, I've never done this). Perhaps someone else who's implemented something like this can chime in.

 

Oh, for the problem with look-at not being at (0, 0, 0), I think you want to use D3DXVec3TransformNormal when transforming vDrag. Actually, you want to use this for all your transformations, since you are applying them to direction vectors, not absolute positions. So that might fix a bunch of your problems!

Share this post


Link to post
Share on other sites

D3DXVec3TransformNormal indeed was the right choice, this did fix the issue with not looking at (0, 0, 0).

 

As for the other problem, I have finally found a solution. Instead of rotating the up vector, I'd first make a cross product of the old up vector and the direction. Then I'd eliminate the y coordinate. The cross product of this x-z aligned vector and the direction is our new up vector. Now it works perfectly, without any issues whatsoever. Oh, another change needed to be made to the panning, instead of "Dir x Up", I made a screen space drag vector and transformed it using the inverse view matrix. Previously, the obvious problem with this was also that i used TransformCoord and not TransformNormal.

 

Thank you a lot for your help, that was quite a good exercise for my math skills, too. Given that there are nearly no functioning samples for this online, I want to share my final code in case someone stumbles over this.

 

void SceneWindowCtrl::OnMoveCamera(const Vector2& vDist)
{
    //panning
    if(Input::KeyHold(Keys::STRG))
    {
        //access camerea attributes
        D3DXVECTOR3 vDir(m_pCamera->GetDirection()), vUp(m_pCamera->GetUp());
        D3DXMATRIX mView(m_pCamera->GetViewMatrix());
        D3DXMatrixInverse(&mView, NULL, &mView);

        //screen space drag vector
        D3DXVECTOR3 vDrag((float)vDist.x, (float) vDist.y, 0.0f);
        //transform drag vector to world space
        D3DXVec3TransformNormal(&vDrag, &vDrag, &mView);

        m_pCamera->Move(vDrag);
    }
    //rotating
    else
    {
        //setup vectors
        D3DXVECTOR3 vDir(m_pCamera->GetDirection()), vLookAt(m_pCamera->GetLookAt()), vUp(m_pCamera->GetUp());
        D3DXVECTOR3 vDrag(vDist.x, vDist.y, 0.0f);

        D3DXMATRIX mRotation, mView(m_pCamera->GetViewMatrix()), mWorld;
        //inverse view
        D3DXMatrixInverse(&mView, NULL, &mView);
        //transform mouse drag vector to object space
        D3DXVec3TransformNormal(&vDrag, &vDrag, &mView);
        D3DXVec3Normalize(&vDrag, &vDrag);

        D3DXVECTOR3 vAxis;
        //create axis from drag an direction vector
        D3DXVec3Cross(&vAxis, &vDrag, &vDir);
        D3DXVec3Normalize(&vAxis, &vAxis);

        //rotate around axis
        D3DXMatrixRotationAxis(&mRotation, &vAxis, DEGTORAD(D3DXVec3Length(&vDrag)));
        //transform direction and up
        D3DXVec3TransformNormal(&vDir, &vDir, &mRotation);
        //recalculate position
        const float cameraDist = m_pCamera->GetDistance();
        m_pCamera->SetPosition(vLookAt - vDir*cameraDist);

        //recalculate up
        D3DXVECTOR3 vUpAxis;
        //calculate "rotation" axis for up vector
        D3DXVec3Cross(&vUpAxis, &vUp, &vDir);
        vUpAxis = D3DXVECTOR3(vUpAxis.x, 0.0f, vUpAxis.z);
        D3DXVec3Normalize(&vUpAxis, &vUpAxis);

        //cross direction and up axis for up vector
        D3DXVec3Cross(&vUp, &vDir, &vUpAxis);
        m_pCamera->SetUp(vUp);
    }
}
Edited by The King2

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