# Restricting movement to 3D axis

A cube is constructed in a 3D and can be rotated to view any side. I want to be able to drag the cube to any position with the mouse while keeping the Y-coordinate the same. I used project and unproject functions to map the mouse to the 3d world. This method seems to work fine when the green face is parallel to the camera. However, when i rotate the cube say 45 degrees around the x-axis and the cube face is now at an angle to the camera, the y-coordinates do not track the mouse. when the camera is parallel to the red face, the tracking in the y-axis is almost nothing.

When i dont fix the y-coordinate, the cube tracks the mouse perfectly, however, the y-coordinates are changed as well. How can i go about keeping the y-axis fixed and yet having the mouse cube track the mouse?

Several solutions could be used here to accomplish this task, depending on how much work you want to put into it.

Plane Intersection

If keeping the cube at the position of the mouse is important, you will likely be looking for this method. Essentially, when you first receive a mouse click, you will construct a plane at the Y level of the object (assuming Y is up). Then during your mouse drag you perform a ray-cast each step from camera origin to projected mouse coordinates. Your cube can then be moved to the intersect point of this ray on the plane.

For some detail on the math involved: http://geomalgorithms.com/a05-_intersect-1.html#Line-Plane%20Intersection

An easier method

An alternate method you could try reduces the complexity a bit, but does not allow for the cube to follow the mouse correctly on the screen. As you are wanting the move the cube along only the two axes (X and Z), you can map mouse movement along it's 2 axes (X and Y) fairly easily. To make the movement feel more natural, you'd likely want to modify the effect of the mouse offset to take the camera yaw into account. To accomplish this, you can take the offset X and Y of the mouse, rotate them around 0,0 by the camera's yaw rotation and then apply those new X and Y values to the cube.

• i considered the plane intersection, but this has a few drawbacks in that the when the y axis isnt perpendicular to the screen(world is rotated), the plane intersection will not give an accurate location, the further up the screen the worse it is. A planeintersection method also creates problems when rotating @JonBee – introVertice Feb 16 '16 at 0:03

I agree with JonBee that a Plane test should work well.

In response to your comment, the world-space intersection that you calculate resides on the plane that you want the cube to move in regardless of the cube's current orientation. The thing I think you have overlooked is that the plane you need to construct has "Up" (0, 1, 0) for its' normal and should not be oriented to a cube face.

In any subsequent frame, intersecting the mouse ray with that plane will produce a coordinate with the same Y-component as the entire plane (including the originally clicked point). That means that whatever world-space delta you calculate will automatically have a Y-component of 0. Render the cube at (actualPosition + delta) and, when the drag ends, add delta to actualPosition and reset delta to 0.

At time 1, your mouse coordinates are

m_1 = [x_1, y_1]

At time 2, they are

m_2 = [x_2, y_2].

First, project these into camera-centered coordinates to produce two 3D points:

p_1 = project(m_1, depth=0); p_2 = project(m_2, depth=0);

Note: take care to make sure these points are in the camera's reference frame, not the world. From there, the motion of the mouse in camera coordinates is then:

dp = p_2 - p_1

Now, dp is a 3D vector in camera coordinates in camera space. Transform dp to world space using the camera's rotation matrix R, which is a 3x3 matrix defining the orientation of the camera:

dw = R * dp

Now you have a 3D vector dw in world space. Project this vector onto the plane of interest to find the motion along the plane. Supposing the plane is world-oriented (that is, the world coordinates are the plane coordinates), then you simply have to zero out the y component of dw giving a vector on the plane:

motion = [dw.x, dw.z].

If the plane you want to move on is anything else, you simply have to first rotate dw into the reference frame of the plane, and then project it along the normal of the plane.