I remember watching a really nerdy youtube video many years ago with two people having a challenge to create a 3d rotating wire frame cube outputed onto console as fast as possible. At the time it intrigued me and I had a little think about how I would go about doing it. In the end I couldn’t quite work out how to do all of the transformations. That was until I was bored one day and I cam across this post on stack overflow. This did all of the cube related part but didn’t actually show how to transform the pixels. After having a read of wikipedia I had a good a understanding of how I could do the 3D projection transforms so I now had all of the parts. In an effort to try understand all of it I did end up taking bits from all of the above resources to make my solution.

First I defined a cube by getting the coordinates all of its corners:

List corners = new List{
           new Point3D(-1,-1,-1),
           new Point3D(1,-1,-1),           
           new Point3D(1,-1,1),
           new Point3D(-1,-1,1),
           new Point3D(-1,1,1),
           new Point3D(-1,1,-1),
           new Point3D(1,1,-1),
           new Point3D(1,1,1)};

Note that the cube has 2 unit long sides. Now I needed to convert these points into all of the different sides. This bit is shamelessly taken from stackoverflow:

var lines = from a in corners
                 from b in corners
                 where (a - b).Length == 2 && a.x + a.y + a.z > b.x + b.y + b.z
                 select new { a, b };

The where clause prevents any duplicate lines by only allowing lines going one direction. In order to prevent diagonal lines the length of the two points has to always be 2.

At this point I decided mainly after reading the stackoverflow post the best idea would be to split the line into lots of points as defined by a resolution variable. I would then translate the points with 3D projection to work out where to place them.

var point = line.a + (i * 1.0f / (resolution-1)) * (line.b - line.a);

In the above i is how far along the line the point is in increments of 1/resolution. It uses the simple formula of a point P between points A and B equals A + x(B-A) where x is less than 1.
The final task is now to turn all of the points into X,Y points by using the wikipedia article. At this point I have not shown what the Point3D class is made of and it can now be discussed. As it is a bit too long to paste straight into the blog I will just link to my github version of it. The -,+ and * operator are pretty self explanatory as well as the length method. RotateX, Y, Z are easy to understand when you think back to school maths. The previously linked wikipedia article does show how to derive this if you still can’t work it out.
The clever part is the project function:

float factor = fov / (viewDist + z);
Point3D p = new Point3D(x, y, z);
return new Point3D(p.x * factor + width / 2, -p.y * factor + height / 2, 1);

This is a slightly different form than the one mentioned in wikipedia but it does the same thing. This can all then be combined to make a rotating cube.

Advertisements