Re: Rotation always relative to the world



As far as I know you can pretty much copy and paste the code into your
application (someone at MSFT please correct me if I've missed something
important in one of the licenses). The ModelViewerCamera is fairly dependent
on the DXUT framework, so you may want to stick with just the ArcBall. I'll
run over the methods you'll want to call, or perhaps modify.

If I'm being too detailed on anything, or repeating stuff you already know,
bear with me. I'd rather answer your question all at once than waiting for
you to get stumped and dragging the answer out over weeks.

I'll start with a little bit of theory. Quaternions are the preferred way to
work with complex rotations in 3D most 3D graphics applications. A quaternion
consists of 4 numbers (floats) and represents a rotation about an arbitrary
axis. You can multiply quaternions together to create a new quaternion that
expresses the result of a rotation about the first, and then about the
second, and you can convert them to matrices. The mathematics is fairly
involved (complex numbers, etc) but that's OK since the
Microsoft.DirectX.Quaternion struct has methods and operators to hide all of
that.

ArcBall translates 2D mouse input into 3D quaternion math. It basically
models the mouse rolling a virtual ball around. So start with an ArcBall:

By default the ArcBall initializes itself to work with the currently active
Form. If you're using a different Form or an embedded Control to host your 3D
content then you'll have to either modify its constructor to use the target
Control's dimensions instead (the SetWindow call is the important bit), or
you could call SetWindow on it yourself.

SetWindow gives the ArcBall the dinensions of the rectangle that the mouse
will be acting in, allowing it to scale the movement of the scene to match
the mouse's progress across the screen.

ScreenToVector constructs a vector that goes from the ArcBall's center (the
origin) to the point on the virtual sphere's surface that you clicked. If you
click outside of the sphere, the method will snap the vector to the nearest
point on the sphere (this allows you to drag outside the virtual sphere and
turn the scene like a knob, rather than just rolling it-this is an extra
degree of control that you get for free).

QuaternionFromBallPoints is the real worker method here. It takes two points
on the sphere (which is the same thing as the vectors that ScreenToVector
returns) and generates a quaternion that will rotate the sphere in such a way
that the first point will move to the location of the second point. The
rotation will always go the short way around the sphere.

The first line is just a dot product that gets the cos of the angle between
the vectors. The second line gets a vector that's perpendicular to both, this
vector points in the direction about which the sphere must be rotated. The
third line packs this all into a Quaternion. While it looks like the first
three paramaters are the xyz of the axis of rotation and the last one is the
angle, it doesn't exactly work like that in all cases. In this case it just
happens to (and if you need help understanding this I suggest you get a good
math book, or talk to a friend that's good with numbers, it's really too big
of a subject to even begin to cover here). If you want to change things to
use an axis vector and an angle, and you don't want to worry about it being
set up right, then I suggest you construct your Quaternion with the
RotateAxis function instead.

The remaining methods are designed to convert mouse drags into rotations,
and they rely on the above methods. I would suggest using them as they are
meant to be used and not faking them out with your own angleX, angleZ
values-if you do that then you'll lose the ability to turn the scene like a
knob, plus you'll negate all of the code that exists to keep the rotation in
sync with the mouse.

OnBegin gets the point where the mouse button was pressed and converts it
into a vector (call this the starting vector) using ScreenToVector. It also
saves a copy of the current rotation state.

After an OnBegin, whenever OnMove is called, the ArcBall gets the vector to
the new point. It uses QuaternionFromBallPoints to convert the starting
vector and the current vector into a rotation (which, unsurprisingly, rotates
the starting vector into the position of the current vector) and appends this
rotation to the starting rotation state to get the new current rotation state.

OnEnd clears the dragging flag and makes the ArcBall stop updating the
current rotation.

At any point you can get a matrix for all of this by reading the
RotationMatrix property.

The HandleMessages method is in there to support the DXUT framework. Once
you get set up calling OnBegin, OnMove, and OnEnd you're pretty much done and
you can ignore it, unless you want to check out how DXUT calls the ArcBall
methods.

The ModelViewerCamera does some fancy math on top of the ArcBall to add
translation and motion velocity, and to remove jitters from the final matrix
that would come from floating-point error. Unless you need velocity-based
motion for your camera, ignore it completely.

I hope I've given you enough to implement your solution.
.