Euler Rotation

When it comes to working with **3D graphics with C# **the best and most powerful tool to use would be

DirectX.

Microsoft’s GDI+ would be a poor choice since it comes with no 3D support

whatsoever. However, what’s to say we can’t try to use **GDI+ to draw some basic 3D shapes**?

For simple 3D plotting in a C# Form or PictureBox using DirectX would be too much. So the basic

gist of what we need to do to plot 3D points with GDI+ is to:

- Create a Point3D class to hold X, Y, and Z values
- Handle all the math for 3D transformations with those Point3D’s
- Set up a Camera class to establish the point of view
- Use some math to a Point3D into a regular Point

To demonstrate the procedures we are going to create a cube and rotate it in all directions...

There is no direct way to convert a point in 3D space into a 2D point simply because that does

not make any sense. In order to convert between spaces we need to know from where we are

looking at the 3D point. For that we’ll need two values, the camera’s Z position and the zoom.

To calculate the zoom I found using the monitor’s width gives a pretty “square” 3D drawing.

(Less distortion in other words)

double zoom = (double)Screen.PrimaryScreen.Bounds.Width / 1.5;

So with the zoom we can now calculate the camera’s Z position. For this example, I use the

cube’s top right point and the cube’s center as a reference in order the keep the camera at the

same relative distance. That prevents the cube from looking like it is getting smaller and bigger

when it rotates.

Math3D.Point3D anchorPoint = (Math3D.Point3D)cubePoints[4]; //anchor point

double cameraZ = -(((anchorPoint.X - cubeOrigin.X) * zoom) / cubeOrigin.X) +

anchorPoint.Z; Math3D.Camera camera1 = new Math3D.Camera();

camera1.Position = new Math3D.Point3D(cubeOrigin.X, cubeOrigin.Y, cameraZ);

Now, with perspective values, we are ready to take our 3D points and get a simple (X, Y) value

out of them. Cold hard math here, if you can understand it that is good, if not just go with it.

//Assume

//----------

//point3D is a Point3D object with values X, Y, and Z

//camera1 is a Camera object with a Point3D Position value

//drawOrigin is a Point that represents the center of where the cube will be drawn

//cubeOrigin is the middle of the cube in 3D spaces (width / 2, height / 2, depth / 2)

Point point2D = new Point();

if (point3D.Z - camera1.Position.Z >= 0)

{

point2D.X = (int)((double)-(point3D.X - camera1.Position.X) / (-0.1f) *

zoom) + drawOrigin.X;

point2D.Y = (int)((double)(point3D.Y - camera1.Position.Y) / (-0.1f) *

zoom) + drawOrigin.Y;

}

else

{

Point tmpOrigin = new Point();

tmpOrigin.X = (int)((double)(cubeOrigin.X - camera1.Position.X) /

(double)(cubeOrigin.Z - camera1.Position.Z) * zoom) + drawOrigin.X;

tmpOrigin.Y = (int)((double)-(cubeOrigin.Y - camera1.Position.Y) /

(double)(cubeOrigin.Z - camera1.Position.Z) * zoom) + drawOrigin.Y;

float x = (float)((vec.X - camera1.Position.X) / (point3D.Z -

camera1.Position.Z) * zoom + drawOrigin.X);

float y = (float)(-(vec.Y - camera1.Position.Y) / (point3D.Z -

camera1.Position.Z) * zoom + drawOrigin.Y);

point2D.X = (int)x;

point2D.Y = (int)y;

}

The basis for drawing in 3D in C# is set, so now we can use it to create a Cube C# class. The cube will have width,

height and depth and will thus have 6 faces.

The starting cube can simply be defined by creating an array for 24 totals points and then defined like so:

Math3D.Point3D[] verts = new Math3D.Point3D[24];

//front face

verts[0] = new Math3D.Point3D(0, 0, 0);

verts[1] = new Math3D.Point3D(0, height, 0);

verts[2] = new Math3D.Point3D(width, height, 0);

verts[3] = new Math3D.Point3D(width, 0, 0);

//back face

verts[4] = new Math3D.Point3D(0, 0, depth);

verts[5] = new Math3D.Point3D(0, height, depth);

verts[6] = new Math3D.Point3D(width, height, depth);

verts[7] = new Math3D.Point3D(width, 0, depth);

//etc... (rest of code can be downloaded)

We will use this array of points later when we want to rotate the cube, but for now they are ready to be

drawn. The drawing has no real trick to it; the lines just have to be drawn in the correct order:

//Back Face g.DrawLine(Pens.Black, point3D[0], point3D[1]); g.DrawLine(Pens.Black, point3D[1], point3D[2]); g.DrawLine(Pens.Black, point3D[2], point3D[3]); g.DrawLine(Pens.Black, point3D[3], point3D[0]); //Front Face g.DrawLine(Pens.Black, point3D[4], point3D[5]); g.DrawLine(Pens.Black, point3D[5], point3D[6]); g.DrawLine(Pens.Black, point3D[6], point3D[7]); g.DrawLine(Pens.Black, point3D[7], point3D[4]); //etc...

Alas we have a cube in C#! By the way, g is a Graphics object from wherever you want, be the Form,

PictureBox, or like in my example a Bitmap.

To rotate the cube we’ll employ the simplest form of 3D rotation: Euler rotation. For those unfamiliar

with Euler rotation, the idea is to basically turn the X, Y, and Z values of a 3D point into a matrix like

[ x ]

[ y ]

[ z ]

And multiply that by one of 3 matrices depending about which axis we are rotating

For x-axis rotation we have the matrix:

[ 1 0 0 ]

[ 0 cos(x) sin(x)]

[ 0 -sin(x) cos(x)]

For y-axis:

[ cos(x) 0 sin(x)]

[ 0 1 0 ]

[-sin(x) 0 cos(x)]

And for z-axis:

[ cos(x) sin(x) 0]

[ -sin(x) cos(x) 0]

[ 0 0 1]

The value x being the degree of rotation. So for example, we could multiple matrixX times

matrixOurValues and the resulting matrix will have the rotated X, Y, and Z values. Easy huh?

For those of us who can’t remember how to multiply matrices together, we are in luck, I looked it up

and wrote out the three C# functions (the actual code is in the download):

`public static Point3D RotateX(Point3D point3D, double degrees)`

public static Point3D RotateY(Point3D point3D, double degrees)

public static Point3D RotateZ(Point3D point3D, double degrees)

The only thing we need to do for rotating the cube now is to loop through that array of 24 points and

rotate each of the points the same number of degrees. The only tricky part is that we want to first

translate (move) all the points so that the cube’s origin is at (0, 0, 0), then we rotate, then translate back

to the original position. That makes it so that the cube will stay in place as it rotates.

Some math-savvy people will quickly see there is a fatal flaw to this kind of rotation. Matrix

multiplication is not like normal multiplication where A * B = B * A. In multiplying matrices, A * B is not

the same thing as B * A. In other words applying different degrees of X, Y, and Z rotations in different

orders will make the outcome different.

This problem is referred to as Gimbal Lock, where we rotate one way, then want to rotate on an axis but

the resulting rotation seems to be on a different axis.

For a way to avoid Gimbal Lock visit the improved 3D Graphics Visual C# Kicks article

Take a look at the attached C# project and application to see how it all comes together. You will also see

that despite the intense mathematics the program runs quite smoothly.

Once the math and the basics are in place, doing some basic **3D plotting with GDI+** becomes relatively

easy. As I mentioned before, once you want to get into more complicated 3D shapes and movements

then you’ll have to take a deep breath and integrate DirectX with C#...