My final project is an ant simulation called "Dant" ("Derek" + "ant"). Dant is written entirely in C++ and uses OpenGL for graphics. It is integrated with Syzygy (specifically, Aszgard), and is thus capable of running both in the ISL cube and on any computer with a working Syzygy or Aszgard. Here is a screenshot of Dant:
My original project proposal provides an overview of how Dant was intended to work at the beginning of the project. My abstract provides a much shorter summary. The project has, however, evolved substantially since these documents were written.
My project can be compiled in Aszgard by starting win_shell.bat, navigating to the dant directory (the parent directory of src, which contains my C++ code), and then issuing the commands "make clean" and "make". The generated executable is called "dant" (dant.exe on Windows). This is the only method of compiling I have ever tried; I have no idea how to compile Dant on other operating systems or in other environments.
Dant is very simple to use. Movement is handled by Syzygy; I have not modified the movement code originally present in OopSkel. In the cube, the wand's joystick controls movement; in the input simulator, clicking and dragging the mouse performs similarly. The world constructed by Dant is initially empty; the user can generate a new world at any time by pressing button zero. If the user fails to do this within a few seconds, Dant will automatically generate one anyways.
Almost every class and function has a Javadoc-style comment (of the form /** ... */) above it explaining its purpose and operation. For methods, these comments occur in the source files instead of the header files; keeping comments near the source code they describe increases the likelihood they will be up-do-date. Thus, browsing header files may not be the best way to become acquainted with the code structure. My recommendation is to use an automated tool for this purpose; I have already succeeded in getting Doxygen to generate useful documentation for this project, and would have included it on these web pages if it weren't so large.
My files are linked to each other only via includes. Each header file #includes all the other headers it needs. All CPP files are #included by dant.cpp. I know that this is a poor way to do things, but my knowledge of makefiles is insufficient to get any alternative to work.
My code contains several inheritance hierarchies, but they are extremely short--mere parent-child relationships, and exist primarily for extensibility. For example, I only have one type of shape: RectangularPrism. However, most of my code works with the generic AbstractShape, so only a couple changes need to be made to add a new shape, such as a pyramid.
I have made a strong effort to separate duties into their appropriate classes, but I have made some design mistakes, and my code contains several bidirectional dependencies. For example, AntSurface knows about and uses SurfaceThing, but SurfaceThing also knows about and uses AntSurface. To successfully compile with these bidirectional dependencies, most of my header files forward declare their classes. These forward declarations occur after the header guards, but must occur before #includes of other header files.
Inside the code, it's very easy to change the number of shapes, ants, and colonies. On my laptop, the simulation is capable of running at reasonable framerates with upwards of a thousand ants.
Most of the code in Dant deals only with generic shapes, surfaces, and "SurfaceThings". Once one successfully develops a class that extends AntSurface or AbstractShape, it is very easy to make it work with the rest of the program. With more difficulty, one could also add new creatures or behaviors.
You could be forgiven for thinking that the first image on the right shows shadows, but the truth is that I have no idea how to do that. Instead, practically every graphical thing in Dant uses random offsets to produce slightly different colors for its different parts. The colors in the second image, for instance, are definitely not shadows. Random offsets produce much of the visual benefit of shadows, such as depth perception, and also happen to be much easier to produce.
Some areas of the code could use a little extra explanation.
Due to some mysterious errors, I was forced to rename my Rectangle class to Rectiggle; the two names are used interchangeably throughout the documentation and code. A Rectangle consists of an initial point, "pos", plus two perpendicular unit direction vectors, "dir1" and "dir2". The normal of a Rectangle is always the cross product of dir1 and dir2, in that order. The edges of the rectangle parallel to dir1 have length dim1, while the edges parallel to dir2 have length dim2. The setup is diagrammed at right, except that dir1 and dir2 are unit vectors.