Try it in your browser!
The demo consists of 3 separate scenes. You can switch between by simply clicking in the window. Further mouse and keyboard controls are listed in the demo window itself. Debug output from the demo is viewable in the Java applet console
I’ve tried to use good object-oriented programming as much as possible throughout. For example, each scene is represented as a hierarchical scenegraph and a Visitor model to perform rendering. The renderer itself is obtained from a Factory which allows for different types of renderers (OpenGL, DirectX, Software, etc.). As is standard practice with scenegraphs, a matrix stack is used to allow for model-level transformations independent to world-level transformations.
At the moment the only available renderer is software-based. But the architecture allows for OpenGL or DirectX-based rendering to be added in future. I use the
Graphics2D.drawLine() API call in Java to render the pixels. Everything else (e.g. z-buffer) is manually implemented. The engine supports wireframe-mode rendering and backface culling. There is a single source light in the scene whose direction can be modified (in the demo it’s always pointing forwards from the camera). The rasterizer currently uses flat shading - next would be to add Gouraud and Phong, not to mention texturing.
3D rotation on world objects are performed using matrix multiplication. The camera is rotated using Quaternions since this made it simpler to use the UVN camera model. The third scene in the demo above is animated; this is accomplished using a simple timer thread using which any number of events can be scheduled for execution at given time intervals.
When programming in Java it’s easy to not worry about memory usage and allocations since the garbage collector handles the freeing up of memory. But for graphics engines every allocation or garbage collection run will impact performance. So from the outset I’ve tried to pre-allocate memory where possible, by constructing temporary vectors and matrices for use during calculations later on. Thus, the vast majority of the matrix and vector multiplication which take place in the render loop don’t allocate any new memory when doing so.
However, when I finally started rendering things I found that the performance bottleneck was actually the rasterizer. The more polygons (triangles) that needed to be drawn and the larger they appeared on screen, the longer each frame took to render. Perhaps I can improve this by optimising the polygon order (e.g. render from farthest to nearest) and improving the raster algorithms. Eventually I want to switch to using OpenGL as the rendering system but since I’ve come so far with the software renderer it would be nice to see if I can improve it a bit more first.
The engine code is licensed under the LGPL and is available here.