Demo Scene (Windows) | Source (VS2010) | Video

Completed 23rd December 2011, Pastry3D is a software renderer written in C++ as part of the Introduction to 3D Graphics Programming module on my course at the University of Derby. Pastry3D can load and render multiple (simultaneous) animated MD2 models as points; wire frame; lit, flat shaded polygons,; smooth shaded with ambient, directional, point, spot, diffuse and specular lighting, texturing; and any 3D transformation.
Below are a summary of the original entries from this page, screenshots from various stages in the development, and links to downloads, videos, etc.

The Begining
We covered most of the maths for this beast of a project in the first year of the course, but this was still a huge challenge. I also invested the summer prior in looking into OpenGL (the first 3D Graphics API I’d ever used), and re-implemented all of the deprecated functionality there, including matrices, stacks, and shaders, but writing a software rendereris a whole other story. Still, this was a nice way to gain a greater insight into and apprecitation of the 3D graphics pipeline, and the results are very satisfying – even if the road towards them did lead me very close to tears, both of agony, and of joy.
From the beginning of this project I was using a model of Rainbow Dash (don’t judge me, kay?) created by Karol Pawlinski, which I converted to MD2 (module specifications) using a Blender plug-in and some difficulty; but I later switched to animated MD2 models of sonic and tails found here.

For the purposes of this module we were directed to use GdiPlus as the basis of our 3D renderer. This, however, meant being Windows dependant, and I soon decided that I’d rather my renderer be a separate library that could, potentially, work across different operating system, or even different platforms. Pastry3D has, therefore, been structured in such a way that it requires only a pointer to some bitmap data it can render to. In all of my demo applications this bitmap data is provided by the CreateDIBSection function. Final version demo applications use another project, Pie32, as a Win32 wrapper for Pastry3D, and to provide some basic framework features such as Entities, update and render loops, though this project was created quite quickly and lacks some more robust features that would have been useful in crafting the demo for my assignment.

Pixel Plotting
Without the aid of GdiPlus, I had to write my own functions to plot individual pixels, draw lines using Bresenham’s line drawing algorithm (I also experimented with this), and clip them, using Cohen-Sutherland line clipping. This actually proved useful, as I was able to work on a similar project for another module simultaneously, implementing the same algorithms in MIPS assembly.

Triangle Rasterization
A long time passed between my entry on triangle rasterization and it’s predecessor. During that time I was hard at work implementing triangle rasterization with faceted, gouraud shading, ambient, directional and point diffuse lighting, and Z-Buffering. I didn’t know it at the time, but this basically rose me up above the pass mark for the assignment – not that this would have stopped me.
For triangle rasterization I tried something a little off the beaten path. The method described here uses half-space functions to determine the pixels to plot. I chose to implement this over the traditional scan line method because of the solid explanation provided (I like to really understand what I’m programming), and interesting methods of optimization, whereby 8×8 blocks to be entirely filled can be detected earlier than blocks to be partially filled,cutting out a whole load of work.

Barycentric Coordinates
Using this slightly obscure method of rasterization of course meant I couldn’t follow the guidance provided on my course for interpolating shading, texture coordinates, and Z-coordinates for Z-buffering. Enter Barycentric Coordinates.
These are a way of describing a point’s position relative to the vertices of a simplex (a triangle for our purposes). Within a triangle a point has three barrycentric coordinates, each relative to the area occupied by a triangle defined by itself and two of the outer triangle’s vertices. This was the best explanation I found on the matter – I’m certainly not qualified to give such an explanation. What is important about this for interpolation though, is that the areas correspond to the influence that the value on the vertex opposite them has on the value of the point used to form the inner triangle. I really can’t word that better I’m afraid – you should check out this page instead if you want a good explanation of the calculations involved.

Perspective Correct Texturing
At the beginning of the next entry I noticed a rather harsh transition in the lighting of the wing right next to the body in the images above, owing to the fact that the two are of separate models. Relevant? Not really. Rainbow Dash was looking mighty fine by this point anyway, as a result of implementing both texturing and lights.
Loading UV coordinates from an MD2 model and interpolating them is no big deal, so I won’t go to any lengths to explain that here. The image above is actually a screenshot from before I implemented Perspective Correct Texturing, but as you can see it doesn’t really make much difference in complex models like this one. Where Perspective Correct Texturing becomes a necessity is on large, flat surfaces such as the rainbow-striped cube which I’ll post below.

Without perspective correct texturing you’d see some pretty strange things happening where the lines meet the diagonal edge dividing each face. I’ll leave this to you to look up rather than pollute this page with any more ugly pictures of cubes, and move on to something much, much cooler.

This is the point at which I found a whole bundle of animated MD models of sonic characters over on the forums of some obscure game (via Google) that were presumably ripped from official games. I find it’s much easier to work when the prospect of an awesome outcome is dangling just in front of me, so I began at once to adapt Pastry3D to handle animated MD2s. I swapped out my BufferedObject for an AnimatedObject that holds an std::vector of BufferedObjects and is capable of spitting out nw BufferedObjects by interpolating vertices and normals between the keyframes of a model’s animation. Getting the additional frame data from the model during loading should have been child’s play, but it did require something of a refactor that turned out to be quite time consuming. Success, however, was sweet: One Miles “Tails” Prower classic, rotating on the spot, running, textured, lit, and laggy.

The Lost Chapters
Somewhere in the blackout between the previous log and project completion, during which I did little more than work and sleep, lie the final steps of Pastry3D. It was during this time that I implemented spot lights, specular lighting, experiemented with compiler optimizations, and refactored the entire project before starting on the demo application I had to provide with my assignment sollution. I really can’t say much about this period, so I’ll let you imagine me programming to Eye of the Tiger, or the A-Team theme or something.

The Refactor
As I said, the entire project was refactored, and parts were completely rewritten. My Renderer itself underwent significant changes, with the aim of becoming more of a state machine, by using Member Function Pointers to indicate the current render mode, cutting out a significant number of conditionals previously present. I also made other minor changes to allow me to enable specific compiler optimizations such as Fast floating point calculations, which previously caused artifacts owing to Z-Buffering calculations being performed in Screenspace. Z-Buffering now uses Cameraspace Z-Coordinates and the renderer runs much more smoothly.
I also created a rough framework and Win32 wrapper, Pie32, to act as a bridge between the Win32 message loop and Pastry3D, and provide basic functionality that would be required in my test and demo applications. This works well, but lacks critical features that would have been necessary for a larger project, such as a game. Such features include the ability to create Entities that do not load and render 3D models to the scene (actually you can do this, but not gracefully), or to retrieve Entities from their Parents’ collections by name,rather than index.

The Demo
The final step: the creation of a demo application which showed off as much of the functionality of Pastry3D as possible I won’t detail it here, but I’ll provide lot’s of screenshots, downloads, and eventually a video for your delectation.
I did fix a minor bug at this stage to do with the multiplication order of matrices in my Matrix4Stack class, to allow my nested Entities to function correctly, similar to a scene graph. An example of this in action can be seen in my demo application, where sonic and tails rotate and move with the platform on which they are standing.

The completed solution compiles with no errors or warnings (barring a silly warning about the lack of a built-in safety net for fill_n), should run well on any windows pc with a decent enough processor, and produces pretty renderings like those seen below.

– Updated with downloads for demo application and source as a vs2010 solution. Fixed a bug whereby resize messages sent before initialization in Windows 8 and XP cause the demo application to crash shortly after opening.
11/01/2012 – Updated with embedded YouTube video and link.

5 thoughts on “Pastry3D

  1. Pingback: Pastry3D Video Uploaded | Confect's Codex

  2. Pingback: Pastry3D Updated | Confect's Codex

  3. Pingback: Four days | Confect's Codex

  4. Pingback: Pastry3D: 20% Cooler | Confect's Codex

  5. Pingback: Still Alive | Confect's Codex

Your thoughts:

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s