Beruflich Dokumente
Kultur Dokumente
Home Articles Questions & Answers Learning Zones Features Help! The Lounge Search
» Multimedia » OpenGL » General
Licence CPOL
Article Browse Code Stats Revisions (12) 4.91 (60 votes) 24 Sponsored Links
Is your email address OK? You are signed up for our newsletters but your email address has not been
reconfirmed in a long time. To make this warning go away please click here to have a confirmation email sent
so we can confirm your email address and continue sending you your newsletters. Alternatively, you can update
your subscriptions.
Contents
For easy reference, here is what is covered in the article:
• Introduction
• Who is this Article for?
• Background
• Select and Move Process Overview
• Virtual Planes based Translation Controller
1. The Design
2. Download Source Overview
3. The Classes
4. How the Classes are used in the Demo Application
5. Some Implementation Details
6. Translation along a Single Axis
7. Special Case: When the Viewing Plane is Orthogonal to the Virtual Plane
8. Some Room for Improvement
• Using the Demo Project
• A Slightly Bigger Picture
• Supplementary Information
1. Shooting a Pick Ray from the Mouse Pointer
2. OpenGL Picking by Color Code
3. Ray Plane Intersection Test
4. OpenGL Modelview Matrix and the Camera Basis Vectors
• References
• Further Reading
Introduction
There are many applications available for interactive 3D modelling and scene construction that provide
the three fundamental manipulation tasks of selecting, positioning and rotating 3D objects. This article
describes a strategy used to translate 3D objects with the standard 2D mouse and provides an old
fashioned example implementation using OpenGL 2.1 in a C++/CLI Windows Forms application to
illustrate the basic principles involved. I've tried to keep it as basic as possible in the hope it can help
you create your own implementation.
See Also
• Articles like this
• Articles by this author
1. Axis Constraint Drop Down Menu: ‘Transform In XY Plane’ is the default selection when you See Also...
start the application (as shown). The other transform constraint options available in the drop down
Mouse Selection in OpenGL Scene
list are X Axis, Y Axis, Z Axis, XZ Plane or YZ Plane.
One another approach for picking
2. Move Object button. Left click on this button to toggle ‘Object Move Mode’ to ‘On’ (A barely
objects with...
visible blue outline on the button indicates ‘On”) “Hey I never said I know how to built interfaces
well, did I?” GLFW: A (Simple) OpenGL
3. Left click and drag on an object to select and move it with the mouse cursor. As Michael ‘Jacko’ Framework Library
Jackson would say “This Is It!” Or as I would say “This article shows you how to go about An easy to use library to quickly
implementing this part”. The object will feel like it’s glued to the mouse cursor as you drag it setup and...
around in the XY plane, or any other 3D plane or axis you choose to constrain movement to. And Simple OpenGL Test Framework
you'll pretty much be able to move the object from any sane camera viewing position or angle. (a A simple OpenGL framework for fast
lot like how you can do it in any good quality 3D modelling package). prototyping...
This scenario is a combination of WIMP (1 & 2) and Direct Manipulation (3). Pixel Shader for Edge Detection and
Cartoon Effect
There is also another more intuitive scenario that involves Direct Manipulation alone, where an object is Implementation of Sobel Edge
moved via a 3-axis widget. I won't be touching this one yet (it might be in part 2 down the track). In Detection and...
Figure 1, the axis aligned triads (one in the bottom left corner of the viewport and one attached to the Writing a Platform and GUI Toolkit
centre of the object) are not interactive (i.e. not widgets by a conventional UI definition); they just independent OpenGL class
provide visual cues to orientation. Article showing how to write OS and
GUI...
I will be concentrating on the principle on which both these scenarios are based. And that is: The use of
axis aligned planes to move a 3D object with the mouse pointer. In the demo, you can watch the plane
in action by selecting ‘Show Plane’ from the view context menu (right click anywhere in the viewport to Announcements
bring up this context menu). If you grab the demo to see it in action immediately (as we all do), but run
into problems the Using the Demo Project section may be a good first stop for help. Build Azure App - Win a Laptop
This article aims to provide a simple, clear and practical description and example to developers, who are
not experts in 3D interactive techniques, and would like to implement a 3D translation controller that The Daily Insider
behaves like it was built by an expert (Of course, you be the judge as to whether that is achieved). The
article should at least help get you started with your own implementation. You may feel more 30 free programming books
comfortable with this article (and download source), with the better knowledge you have of the Daily News: Signup now.
following:
• OpenGL: mainly because the demo source in implemented in OpenGL 2.1, but other APIs can be
similarly implemented
• Windows Forms and user interface development (The functionality is hosted in a C++/CLI
Windows Forms application built in Visual C++ 8.0 on Window Vista and XP)
• 3D graphics: particularly perspective and orthographic 3D projections, the rendering loop (AKA the
so called game loop), and
• Some typical strategies for the integration of all the above into an application.
Background
This could be huge, but it’s not going to be. See the Further Reading section for a sample of the
background to this topic (or rather the haystack that this needle is in). Continue reading here for the
concentrated version. Conventional 3D interactive techniques with the 2D mouse rely on some form of
guiding entity or extra abstract object to assist the manipulation of 3D objects. For example,
descriptions exist based on the well known idea of a virtual sphere, aka arcball or trackball, to rotate 3D
objects. Not so well or as often described clearly enough is the idea of using axis aligned planes and
lines of one form or another to translate objects. You can see another implementation in the tutorial
article entitled 3D Manipulators using XNA. A survey of literature on 3D interactive techniques indicates
that this idea was one of the earliest Direct Manipulation techniques for the translation of 3D objects,
and is still the most popular in 3D modelling tools, at least in outward appearances (in the form of some
kind of 3-axis translation widget provided to the user), and where implementation details may vary.
Today the idea of using planes for translation seems an obvious extension of the idea of a virtual sphere
for rotation (whereas over a decade ago it may or may not have been so obvious?).
Our translation controller is initialized at step 3 and used to move the object at step 4 until step 5.
A modular approach is taken in the design so that the translation controller can either be used as a
standalone unit or it can be combined with an actual visible guiding entity in the form of a 3D widget
(highly recommended) to help reduce the cognitive load on the user during the process and increase the
robustness of the implementation.
A detailed description of the virtual plane based translation controller is given below and consists of the
following:
• The Design
• The Code
◦ The Classes
◦ Using the Classes in an Application
◦ Implementation Details
◦ Translation along a Single Axis
◦ Special Case: When the Viewing Plane is Orthogonal to the Virtual Plane
◦ Some Room for Improvement
The Design
a. Perform a Ray-Plane intersection test: shooting a ray from m1’ to intersect with the virtual
plane to get the intersection point p1’.
b. Move the object by vector p1’ – p1.
c. Make p1 = p1’ in preparation for the next movement of the mouse cursor (i.e. for the next
iteration through steps 4(a), (b) and (c)).
Note: At step (3c), moving the height of the virtual plane to the height of p1 is important for
maintaining a consistent positional correlation of the 2D mouse pointer with the 3D position on the
object being moved in views where the perspective projection is applied, and typically not necessary for
views that utilize the orthographic projection.
A Special Case
There is one special case the algorithm will need to handle: when the camera/viewing plane is
orthogonal to the virtual plane. For more information see the Special Case section.
Summary views of the 3 classes (in Ray, Plane and MouseTranslationController) that make up
the translation controller are listed below.
MouseTranslationController.h
Vector3^ displacement;
static Vector3^ lastposition;
void CreatePlane();
void CreateRay(int x2D, int y2D);
void SetPlaneOrientation(DWORD theTranslationConstraint);
void InitializePlane(DWORD theTranslationConstraint);
void ApplyTranslationConstraint(Vector3^ intersectPos);
property Vector3^ PlanePosition;
property Vector3^ Position;
public:
MouseTranslationController(void);
void Initialize(int x2D, int y2D,
DWORD theTranslationConstraint,Vector3^ planePos);
void Update(int x2D, int y2D);
void DrawPlane();
Later we will see that the application directly uses only 3 functions during the move object operation:
Initialize( ), Update() and the Displacement property.
Primitives.h
Plane(void);
Plane(Vector3^ norm,Vector3^ pos, DWORD axes);
void DrawNormal();
void Draw()
property DWORD TransformAxes;
};
//======================================================================================
// Ray defined by 2 points (can we call it a segment?)
//
// The 2 points p0 and p1 are expressed in non-opengl Cartesian co-ordinates.
// where the Z axis in vertical. In opengl the Y axis is vertical.
//======================================================================================
public:
Vector3^ p0;
Vector3^ p1;
Ray(void);
Ray(Vector3^ P0,Vector3^ P1);
Ray(int x2D, int y2D);
There is nothing particularly interesting in the Plane and Ray primitives, except maybe the
Intersects() function, otherwise these classes are much like any other structs/classes typically found
to represent a ray and plane.
The Code (2): How the Classes are Used in the Demo Application
Before getting to the code, let’s look at how the TranslationController would typically fit into the
application (in informal pseudocode):
Here is a little bit more detail about the parameters the translation controller uses:
Here is the how the translation controller is actually integrated into the demo. In the implementation
Initialize() is called in the render loop and Update() is called when the mouse moves (i.e. in the
view panel’s MouseMove() event handler). As for the render loop: typically there tend to be two main
options to choose from when deciding where to implement a render loop (AKA the so called game loop)
for a graphics API in a Forms Application: In a control’s paint method or in a timer control. The example
uses a Timer control. The timer1 code below was generated by dragging a Timer control onto the
form in the Designer, setting the Interval property to 1ms and selecting its Tick event to be
generated.
// Draw stuff
...
...
m_OpenglView->SwapOpenGLBuffers();
}
3(a) Shoot a ray from m1 to intersect with the object to get the 3D position of the pixel at the
intersection point p1 is performed by the forms PickAnObject() function. If an object has been
picked, the position on the object that the ray hit the object is stored the form’s
Mouse3dPickPosition property. Color coded picking is the strategy used to select an object (any
picking strategy can be used). The object’s color is used to identify and select the picked object (Note:
you may be aware that the GetPixelColorAnd3DPostion() function breaks some rules of ‘good’
programming practice, but hopefully at least, its name does convey its purpose).
3(b) the alignment of the virtual plane and 3(c) the adjustment of the height of the plane to p1 is
performed in the translation controller Initialize() function.
As shown below, the parameters passed to the function when it is called, are:
Let’s split a hair or two for a moment. If you recall we said earlier that the height of the virtual plane is
set to the height of picked point. This is a minimum requirement of the design. In the implementation,
however, the picked point is the point used to define the virtual plane. The main reason for this is simply
to render a small movable portion of the [infinite] virtual plane centred at this point (for a bit of visual
feedback of what’s happening).
4(a) The Ray-Plane intersection test is performed in translationController Update(). The 2D mouse
position on the screen is passed to this function. The mouse ray in propagated; the intersection point of
the mouse ray with the virtual plane is found; the intersection point is modified based on axis
constraints; and the point defining the plane and displacement vector are updated.
ApplyTranslationConstraint() may seem more complex that it actually is. If you look at it in the
source code you will see that all it does is generate the new object translation position a little differently
when translation is restricted to one axis only: basically the position is only updated along that one axis
to which translation is restricted.
Once the Move Object operation has started, Update() continues to be called each time the mouse is
moved, generating a new incremental displacement vector, until the left mouse button is released.
This is handled by the viewport control’s MouseMove event handler (m_OpenglView is our viewport
that inherits from Panel). So each time the mouse moves (in Object Move Mode with left mouse button
down) the Scene::MoveSelectionSetDelta() function is ultimately called (it is buried down a bit in
other functions as shown in the snippet below).
view1_MouseMove();
calls --> ProcessMouseMove();
calls -> EditModeMouseMove()
calls ->
UpdateTranslationController();
m_scene->MoveSelectionSetDelta();
void Scene::MoveSelectionSetDelta(Vector3^ deltaV)
{
if(deltaV == nullptr)return;
UpdateSelSetWorldPos(deltaV->X, deltaV->Z, deltaV->Y);
}
The diagram shows an example of the process where: for translations along the single axis Y, the X and
Z components of the plane’s normal are set to the X and Z values of the initial intersecting (or pick) ray.
This has the effect of rotating the plane around the Y axis so that the plane’s local XZ components are
parallel/co-planar with the local XZ components of the viewing plane (in views with orthogonal*
projections). The code for this is in the translation controller’s SetPlaneOrientation() function.
An alternative method of using lines to handle translation along a single axis can be found in the tutorial
article entitled 3D Manipulators mentioned previously (in the Background section of the article).
*Note: A ray cast from the mouse into a view with perspective projection applied, typically is not
orthogonal to the viewing plane (but it’s usually close in our case). In contrast, (and at the risk of
stating the obvious) the ray cast into a view with orthogonal projection is always orthogonal to the
viewing plane.
The Special Case
There is a special case that needs to be handled: when the viewing plane is orthogonal to the virtual
plane in a view that has an orthographic projection applied to it. The Ray-plane intersection test fails
because the virtual plane’s normal is perpendicular to the ray and thus the ray will not intersect the
plane. There are 12 possible instances when this special case can occur (Figure 4). The most obvious
solution is to align the virtual plain to the axes that most appropriately matches the movement
constraint and state of the viewing plane for translation to occur. We could use a process of elimination
to find this alignment: where the virtual plane is aligned to each of the 3 possible axes alignments and
performing the intersection test until the test does not fail. In the best case scenario, we can perform
the test once and in the worst case scenario it is performed 3 times. For a worst case example: Suppose
the user has constrained the translation to the x axis. The intersection test will fail for the ray with the
plane aligned to the XY axes and YZ axes, and succeed for the XZ axes. Using the process of elimination
can also end up being the least efficient strategy in the worst case.
We can validate the input early to reduce unnecessary processing. That is, we obtain the
position/orientation of the viewing plane and align the virtual plane to the axes that ensure it is parallel
to the viewing plane; this ensures the intersection test will succeed. It is a simple solution, if a
somewhat tedious one.
Here are two ways of accessing the state of the viewing plane:
1. We could get it abstractly: via a camera class/object if we have one or via custom
transformation/viewing matrix states built on top of OpenGL 3.0, or
2. We could get it directly via the current state of the opengl (pre 3.0) modelview matrix. The demo
source uses the latter method (accessing the modelview matrix), in the translation controller’s
InitializePlane() function. For more information, see OpenGL Modelview Matrix and the
Camera Basis Vectors in the supplementary section.
The translation controller has not been integrated into the demo application for efficiency:
• Where Update() continues to be called via the MouseMove handler, it may be more logically
correct to pick an object and initialize the translation controller in a MouseDown event handler
instead of in the render loop (where it is like ‘once removed’ from the direct input event handler).
• Explore Rendering loop options: This is probably more of an investigative issue than an
improvement. We use a System.Windows.Forms.Timer Control as our so called game loop.
Another popular option (that I haven't tried with this implementation) is to inherit from a Forms
Control (e.g. Panel control), then override and use the control’s Paint method as the so called
rendering loop. In both these cases, the code is executed using the application's UI thread. The UI
thread is an obvious first choice and testing showed the most computationally expensive part of
the algorithm to be the continual ray-plain intersection test (where a frame may take 1.67 ms to
render [approx 60fps] and vary, the intersection test can take 0.06 ms). Yet another option is to
use multithreading. There are 2 other timers that can be used in this case:
System.Timers.Timer and System.Threading.Timer if you think you need them (like
people at Intel would).
• OpenGL 3.0. If you feel the need to be proactive and OpenGL 2.1 is too old fashioned for you, then
it is likely you would want to implement this design via the programmable pipeline (i.e. shaders);
do stuff like store or derive your own matrix versions/states to suit your particular implementation
requirements. The demo uses as much as possible of what opengl offers (fixed pipeline or not):
The more that is off loaded to the OpenGL API means less code for us to write ( a simple and
perhaps fatal approach? But hopefully you should be able to get the gist of this article despite the
implementation).
• I've reviewed and re-reviewed this article to eliminate errors (ad infinitum). But it’s never as good
as a peer review (i.e. someone else's pair of eyes reviewing it; you know, like software testing:
you don't want your programmer testing his own program if you can avoid it- he/she/I can tend to
be instinctively biased apparently). So if you notice any errors (blatant or otherwise misleading) or
have suggested improvements to benefit other readers, let us know.
• The computer system’s color resolution must be set to 32 bit (High) for the color code picking to
work.
• .NET Framework (at least 2.0) must be installed on your system.
• A screenshot of the demo with a 10 X 10 portion of the current virtual plane and plane normal. Its
visibility can be toggled with the ‘Show Plane’ context menu item.
• Top right of screenshot shows the Camera menu strip and Objects menu strip. The slightly visible
blue border around the Move Object button indicates it is currently active.
• Bottom right of screenshot shows all the viewing options available on the context menu. Right
mouse click in the viewport and the context menu appears for you to select an option.
1. Click on the Move Object button on the Object menu strip to turn on Move Object mode.
2. Select the plane/axis (you want to constrain object movement to) from the axis constraint drop
down menu list.
3. Select the object by moving the mouse pointer over the object and pressing the left mouse button
down. Hold the left mouse button down.
4. Move the object by dragging the mouse with the left mouse button down.
5. Release the left mouse button after the object has been moved.
• Open: allows you to load 3ds, obj, x or im model files from a file open dialog box.
• New: clears all the models in the current scene.
• The camera will always be pointing to the origin so do not pan the camera too far from it for the
best viewing experience (when rotating the camera around).
• There is one model supplied for viewing. If you decide to load some other models beware: the
application will not zoom to its extents or location when you load it. So you may have to manually
zoom or move the camera to see very large/very small models or models not located near the
origin.
• Zooming: Click on the zoom button on the Camera menu strip. Drag the mouse vertically in the
viewport to zoom in/out (with left mouse button down). Note: It is not true zooming (of the fov),
but a translation (dolly) of the ‘camera’ backwards and forwards that produces the zooming effect.
• Rotating: Click on the rotate button on the Camera menu strip. Drag the mouse around in the
viewport with left mouse button down to get a feel for how it rotates. The ability to roll the camera
is not implemented.
• Panning: Click on the pan button on the Camera menu strip. Again, drag the mouse around in the
viewport with left mouse button down to get a feel for how it pans.
• If you lose sight of your object in the application (yes, unfortunately, you can lose it!) just do File-
>New to clear the scene and File->Open and reload an object (A reset button would be better,
but...?).
The appropriate DLLs have been included with the demo package enabling it to also run on Windows XP
as well as Vista.
You could assume that the closer your system specs are to system 1 the better it will run. There is a
noticeable time lag between the mouse movement and object movement on the older test system
(system 3), probably making the translation controller, in its current form on that system, unusable for
practical purposes.
Supplementary Information
This section contains information that was produced in the writing of this article. The topics would be
familiar to most experienced programmers who would have seen them covered in one form or another
before (so feel free to criticize). Thus, it may be particularly more useful to beginners and the not so
experienced.
Picking can be implemented a number of ways. In our example color coded picking is used. When an
object is loaded into the scene it is assigned its own wireframe color (i.e. this is the color of the objects
wireframe when drawn in wireframe mode). Before picking takes place each object in the scene is drawn
with this color as shown below. Picture (a) shows normal rendering of the models in textured and
wireframe mode. Picture (b) shows how the objects are rendered for color coded picking.
(a) textured and wireframe rendering
positionOut->X = (float)posX;
positionOut->Y = (float)posZ;
positionOut->Z = (float)posY;
array<unsigned char>^ color = gcnew array<unsigned char>(3);
color[0] = pixel[0];
color[1] = pixel[1];
color[2] = pixel[2];
return color;
}
This is not a tutorial showing you how to perform a ray-plane Intersection, but rather a brief description
of the thought process that was involved in understanding the code that is used in the application. ‘Real
and proper’ ray/plane information can be found by following the links in the references section at the
end of the article. The Algebraic approach is taken using basic arithmetic and vector addition (and
applies equally well to 2D and 3D vectors)
What We Have
1. The two nonzero vectors a and b are perpendicular if and only if the dot product a.b = 0. (Here
we have the choice of either going deeper into the realm of simple theorem proofs of trigonometry
and pythagoras to convince ourselves how and why this is so, or we could just have faith in our
mathematicians and accept it as a rule that can be useful to us in many ways. What follows next is
an application of its usefulness)
2. The dot product a.b = 0 is one way of defining a plane in space. By thinking of one of the vectors
as the normal of a plane (let’s say the vector a is now the normal), then all of the vectors b that
satisfy the dot product equation sit in a particular plane. Obviously this also means that all the
pairs of points used to define a vector b also lie in that plane.
Vectors
A vector can be thought of in two ways: either a point at <x,y,z> or a line going from the origin
<0,0,0> to the point <x,y,z>.
• w = P0-V0
• v = V1-V0
• u = P1-P0
Looking at the diagram with basic vector math knowledge, we see that:
• w + su = v (3)
Because the aim is to get an answer in point form the following expression (aka definition of a line) is
usually used as the final equation to solve:
• P0 + su = V1 (4)
This can be thought of as ‘move the start point P0 by the vector su to get to the end point V1, where s is
usually described as some scalar value (s can be thought of as (some scalar value that when multiplied
by u represents) the portion (i.e. shorter piece) of u that ends at the intersection point V1).
Applying (1) and (2) to the problem where v is perpendicular to N (and . represents the dot product)
• N.v = 0
We substitute the value of v from (3) to obtain
• N.(w + su) = 0
=> N.w + N.su = 0
Use the fact that the dot product is commutative (i.e. N.su is the same as sN.u)
Even though there is no camera as far as OpenGL is concerned, it is useful to think of the modelview
matrix as where you stand with the camera and where you are pointing it. This makes the handling of
the special case in our implementation (i.e. when the viewing plane is orthogonal to virtual plane of
constraint) a little more simple, even if somewhat tedious to arrive at. If you are performing scaling that
may affect the modelview matrix, it may be a good idea to store the orientation values it contains before
the scaling is applied to it (Since the deprecation of the fixed pipeline in OpenGL 3.0 encourages the
developer to implement all there own matrix manipulations, it seems storing the various states of
particular matrices you may need is now obligatory).
In our old fashioned OpenGL 2.1 example no scaling is performed that effects the opengl modelview
matrix, so we grab the ‘camera basis vectors’ directly from the modelview matrix as is. Also the camera
cannot roll: this also helps, as we shall soon see. The elimination of scaling and rolling is deliberate (If I
were in OpenGL 3.0 I would store a matrix similar to this state specifically for the special cases in the
implementation).
Here is how we find out where the camera is currently pointing (i.e. its pitch, yaw and roll). Suppose we
retrieve the modelview matrix like so:
The following sub matrix contains the basis vectors that provide us with the orientation of the camera.
Where the basis vectors (are often called the) RightVector [Rx, Ry, Rz], UpVector [Ux, Uy, Uz] and
LookAtVector [Ax, Ay, Az] are shown in the diagrams below. The second diagram is an example to
get (re-)orientated: It shows the state of the camera rotation sub-matrix when the camera is in the front
view position (a typical initial state with no rotations applied; pitch, yaw and roll all equal zero; and it is
the Identity matrix).
Once we have this knowledge, it is simple to check the orientation of the camera and make some
decisions based on it. The tedious part is finding the minimal number of the most convenient variables
(out of the nine in the sub matrix) to check in order to find the camera orientation. Another feature (or
rather lack of) of our camera that helps us is the fact that the camera does not roll (around its LookAt
vector), it can only pitch (around its Right vector) and yaw (around its Up vector). Here is what we
can end up with:
We can confirm this as a reasonable solution by looking at the composition of the basis vector submatrix
for the camera. In keeping to the old fashioned theme (i.e. opengl 2.1) our camera is also a basic old
fashioned one. The following functions are used to rotate the camera so that the modelview matrix is
multiplied by them with the product replacing the current matrix.
And note that since rolling is not currently implemented, the last function glRotate*(a,0,0,1) is
ignored (thus z is always 0). As you will see, this fact will be used to our advantage.
After the rotations are applied to the modelview matrix, the (de)composition of the basis vectors can be
expressed in the following way:
Where x is the camera’s pitch angle, y is the camera’s yaw angle, and z is the camera’s roll angle.
Then through observation and a little analysis, the following correlations between elements in the
modelview matrix and the camera orientation were discovered and used:
1. Camera pitch of 90 or -90 degrees from the Front View (Identity position). The key here is that the
camera cannot roll (around the LookAt vector). This means z = 0 degrees is an invariant (does not
change from its original value in the front view (identity matrix). Thus substituting Sin(z) = Sin(0)
= 0 and Cos(0) = 1 into the composition of m6 simplifies it to Sin(x). Note that x is the pitch of
the camera in degrees relative to the identity pitch (when the camera is in the front view position)
and is independent of the global axes. That is, it works for the camera pitching to the Top View
from any arbitrary position/orientation of the camera. The example below illustrates the case of
the camera rotating around the global X axis from the Front View to the Top View.
An example: the camera manipulated to the Top View position that results in a 90 degree pitch of the
camera.
2. Camera pitch of 0 from the Front View (Identity position); That is, the camera viewing plane is
orthogonal to the XY plane; the fact that the camera cannot roll is used again here to give us m6
= Sin(x). So m6 = 0 when the camera is not pitched at all.
It can still rotate (or yaw) in the XY plane while m6 = 0, so finally we need to test for when the
camera viewing plane is orthogonal to the YZ plane (back, front view) and XZ (left, right view).
3. Arbitrary camera yaw angle. m8 (Ax) = sin y.
This time we use the actual sine of the angle of orientation of the camera as it rotates around the
Z axis (opengl y axis) in the XY plane. So in the front view (0 degrees) and back view (180
degrees) m8 = 0, at the left view (90 degrees) m8 = 1 and right view (-90 or 270 degrees) m8 =
-1.
That will handle the orthogonal views (front, back, left and right) in the XY plane. For example, when the
camera is at the Left (or Right) View and the object translation is restricted to the XY plane, we flip the
plane to the YZ plane position and restrict the object translation to the Y axis only. Similarly for the
camera at the Front (or Back) View, where the object translation is restricted to the XY plane, we flip the
plane to the XZ plane position and restrict the object translation to the X axis only.
What we haven't yet handled are all the arbitrary cases in between the orthogonal views when the
camera rotates around the Z axis that points upwards (opengl y axis). Here is how: Basically if the yaw
is less than 45 degrees around from the orthogonal view for the back and front views, the plane is
flipped to the XZ alignment and object translation is restricted to the X axis. And when the yaw is less
than 45 degrees around from the left or right view (yellow in the diagram), the plane is flipped to the YZ
alignment and object translation is restricted to the Y axis.
This idea ends up as the following code implementation (in the TranslationController’s
InitializePlane() function):
References
This article actually tries to simplify and expand on the somewhat convoluted description I gave here.
This article is still not as simple as I want, but I hope it has helped you.
Further Reading
For more information on the context of this article, some search keywords and a bibliography are
provided below.
Keywords
Batagelo, H.C., Shin Ting, W., Application-independent accurate mouse placements on surfaces
of arbitrary geometry ,SIGRAPI,2007, proceedings of the XX Brazilian Symposium on Computer
Graphics and Image Processing, IEEE Computer Society Washington, DC, USA
Bier, E.A., Skitters and jacks: interactive 3D positioning tools. In Proceedings of the 1986
Workshop on Interactive 3D Graphics, pages 183– 196. ACM: New York, October 1986.
Bowman, D., Kruijff,E., LaViola,J., Poupyrev,I., Mine, M., 3D User interface design: fundamental
techniques, theory and practice. Course presented at the SIGGRAPH 2000, ACM [notes].
Conner,B.D, Snibbe,S.S, Herndon,K.P , Robbins.D.C, Robert C. Zeleznik.R.C , Van Dam .A, Three-
dimensional widgets, Proceedings of the 1992 symposium on Interactive 3D graphics, p.183-188,
June 1992, Cambridge, Massachusetts, United States
Dachselt R., Hinz M.,: Three-Dimensional Widgets Revisited - Towards Future Standardization.
In IEEE VR 2005 Workshop ‘New Directions in 3D User Interfaces’ (2005), Shaker Verlag.
http://en.wikipedia.org/wiki/Direct_manipulation
Frohlich, David M., The history and future of direct manipulation, Behaviour & Information
Technology 12, 6 (1993), 315-329.
Hand,C., A survey of 3D interaction techniques. Computer Graphics Forum. v16 i5. 269-281.
[1] Hinckley, K., Pausch, R., Goble, J.C., Kassell, N.F., A survey of design issues in spatial input,
Proceedings of the 7th annual ACM symposium on User interface software and technology, p.213-222,
November 02-04, 1994, Marina del Rey, California, United States
Hutchins E.L., Hollan J.D, Norman D.A.: Direct Manipulation Interfaces . HUMAN-COMPUTER
INTERACTION, 1985, Volume 1, pp. 311-338 1985, Lawrence Erlbaum Associates, Inc.
Nielson, G.M., Olsen,D.R., Jr. Direct manipulation techniques for 3D objects using 2D locator
devices. In 1986 Symposium on Inter-active 3D Graphics, pages 175–182. ACM: New York, October
1986.
Oh,J., Stuerzlinger,W., Moving objects with 2D input devices in CAD systems and Desktop
Virtual Environments, Proceedings of Graphics Interface 2005, May 09-11, 2005, Victoria, British
Columbia
Stuerzlinger,W., Dadgari,D., Oh,J., Reality-Based Object Movement Techniques for 3D, CHI 2006
Workshop: "What is the Next Generation of Human-Computer Interaction?", April 2006.
Wu,S., Malheiros, M.,: Interactive 3D Geometric Modelers with 2D UI. WSCG 2002: 559-
History
th
• 7 April, 2009: Initial post
th
• 9 April, 2009: Minor changes to article
License
This article, along with any associated source code and files, is licensed under The Code Project Open
License (CPOL)
Steve Katic
Australia
Member
FAQ Search
New Message Msgs 1 to 24 of 24 (Total in Forum: 24) (Refresh) First Prev Next
Re: Source codel to dll [modified] Steve Katic 0:45 14 Apr '10
How to run run the demo Member 2602751 8:46 3 Dec '09
Re: How to run run the demo [modified] Steve Katic 15:33 3 Dec '09
Re: How to run run the demo Steve Katic 2:28 8 Dec '09
Re: Can't run the demo [modified] Steve Katic 8:01 5 Aug '09
Re: Can't run the demo [modified] Steve Katic 14:25 15 Aug '09
How can I change this to MFC frame neudgj 20:20 24 Jul '09
What kind of software did you use to draw the picture? tyjiang 22:19 10 Apr '09
Re: What kind of software did you use to draw the picture? Steve Katic 15:58 11 Apr '09
Great article Daniel Vaughan 9:58 8 Apr '09
Last Visit: 21:00 31 Dec '99 Last Update: 8:43 8 Feb '11 1
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.