Max van der Hart
Max van der Hart

Reputation: 51

3D Cube rotation around x and y axis

I'm writing a WPF application that uses Viewport 3D to display a cube, made up of 6 different colored-faces. In the end I'm trying to create a rotatable Rubik's Cube.

I have built in two methods to rotate the camera around the cube, one using 'WASD' keys and one by dragging the mouse.

Rotating the camera with respect to the y-axis works fine (so 'horizontally'). However, when trying to rotate the camera with respect to the x-axis it only works properly when the camera hasn't been rotated horizontally yet. If it has been, the cube appears to always rotate the same face upwards, no matter where it is.
As an example, let's say we're using the standard Rubik's Cube. The white face will be facing upwards and the blue face will be the front-facing face. When the camera hasn't been rotated yet and I press the 'W' key or drag the mouse upwards, the blue face moves in the direction of the white face, so the cube appears to be rotating upwards, which is what I want. If however I first rotate the camera 180 degrees horizontally, so that the white face is still facing upwards but the front-facing face is now the green face and I use the 'W' key or drag the mouse upwards the rotation will still be that of the blue face moving towards the white face, so that the cube appears to be rotating downwards.

My guess is that this has to do with the x-axis staying in the same place while the camera rotates, but I'm not sure how to fix it. Any help would be much appreciated.

This is my XAML code for the camera:

                <Viewport3D.Camera>
                <PerspectiveCamera 
              Position = "2, 2, 5"
              LookDirection = "-2, -2, -5"
              UpDirection = "0, 1, 0"
                NearPlaneDistance="1"
                FarPlaneDistance="20"
              FieldOfView = "80">
                    <PerspectiveCamera.Transform>
                        <Transform3DGroup>
                            <RotateTransform3D>
                                <!--Take care of x rotation of camera-->
                                <RotateTransform3D.Rotation>
                                    <AxisAngleRotation3D x:Name="rotateX" Axis="0 1 0 " Angle="0" />
                                </RotateTransform3D.Rotation>
                            </RotateTransform3D>
                            <RotateTransform3D>
                                <!--Take care of y rotation of camera-->
                                <RotateTransform3D.Rotation>
                                    <AxisAngleRotation3D x:Name="rotateY" Axis="1 0 0 " Angle="0" />
                                </RotateTransform3D.Rotation>
                            </RotateTransform3D>
                        </Transform3DGroup>
                    </PerspectiveCamera.Transform>
                </PerspectiveCamera>
            </Viewport3D.Camera>

This is my code to rotate the camera when dragging the mouse (CanvasCube is the canvas that contains my Viewport3D):

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
    {
        this.Cursor = Cursors.Hand;
        oldMouseX = e.GetPosition(CanvasCube).X;
        oldMouseY = e.GetPosition(CanvasCube).Y;
        mousePressed = true;
    }

    private void Window_MouseMove(object sender, MouseEventArgs e)
    {
        if (mousePressed)
        {

            if(e.GetPosition(CanvasCube).X > oldMouseX)
            {
                rotateX.Angle --;
                oldMouseX = e.GetPosition(CanvasCube).X;
            }
            else if (e.GetPosition(CanvasCube).X < oldMouseX)
            {
                rotateX.Angle ++;
                oldMouseX = e.GetPosition(CanvasCube).X;
            }
           if (e.GetPosition(CanvasCube).Y > oldMouseY)
            {
                rotateY.Angle--;
                oldMouseY = e.GetPosition(CanvasCube).Y;
            }
            else if (e.GetPosition(CanvasCube).Y < oldMouseY)
            {
                rotateY.Angle++;
                oldMouseY = e.GetPosition(CanvasCube).Y;
            }
        }
    }

And this is my code to rotate the camera using the 'WASD' keys:

        private void Window_KeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.W:
                angleUp();
                break;
            case Key.S:
                angleDown();
                break;
            case Key.A:
                angleRight();
                break;
            case Key.D:
                angleLeft();
                break;}}
    private void angleUp()
    {
        rotateY.Angle+=2;
    }

    private void angleDown()
    {
        rotateY.Angle-=2;
    }

    private void angleRight()
    {
        rotateX.Angle+=2;
    }

    private void angleLeft()
    {
        rotateX.Angle-=2;
    }

P.S. I hope the problem is clear this way. I find it quite hard to explain. If not, I'd be glad to explain further to clarify.

Upvotes: 4

Views: 1838

Answers (1)

Robin Bennett
Robin Bennett

Reputation: 3231

I managed to do this with a single RotationTransform by changing the angle of the transform.

<RotateTransform3D >
    <RotateTransform3D.Rotation>
        <AxisAngleRotation3D Axis="{Binding AxisVector}"
                             Angle="{Binding Rotation}"/>
    </RotateTransform3D.Rotation>
</RotateTransform3D>

And then specifying the Axis and Angle like this:

private bool dragging;
private Point dragStart;
private Point dragTotal;
private double rotation;
private Vector3D axis;

private void Viewport3DOnPreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (!this.dragging) return;

        var pos = e.GetPosition(this.Viewport3D);

        var x = pos.X - this.dragStart.X;
        var y = pos.Y - this.dragStart.Y;

        this.Rotation =  Math.Sqrt(x * x + y * y);

        this.AxisVector = new Vector3D(y, 0, -x);
    }

    private void Viewport3DOnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        this.dragging = true;
        this.dragStart = e.GetPosition(this.Viewport3D);
        this.dragStart.Offset(-this.dragTotal.X, -this.dragTotal.Y);
    }

    private void Viewport3DOnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        this.dragging = false;

        var dragEnd = e.GetPosition(this.Viewport3D);
        this.dragTotal.X = dragEnd.X - this.dragStart.X;
        this.dragTotal.Y = dragEnd.Y - this.dragStart.Y;
    }

It doesn't seem to matter that the Axis has a variable length.

Let me know if you need any more of the code.

Upvotes: 1

Related Questions