**Cubes** [A Graphics Codex Programming Project](../projects/index.html) ![Figure [teaser]: A dog modeled with translated, rotated, and scaled cubes. By the end of this project you'll know how to create scenes like this in three different ways and be familiar with our C++ and OpenGL tools.](dog.jpg border=1) *Prerequisites* - Read online before you start: - Graphics Codex Project [Recommended Tools](../tools/index.html) document - G3D [Developer Tools](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/devtools.html) - G3D [Build System Instructions](http://g3d.cs.williams.edu/g3d/G3D10/readme.md.html) - Read as you progress through this project in the [Graphics Codex](http://graphicscodex.com): - Preface - Introduction - C++ - Version Control Basics # Introduction Welcome to your first Graphics Codex Project! In this project you'll create 3D scenes from simple cubes in three ways, by directly editing a data file, by using a GUI scene editor, and by programmatically generating a data file. Programmatically constructing a scene in memory at run time is a fourth way, which you won't take on just yet. All projects in this series each have a five major sections: Introduction : Motivation for the project and your practical and educational goals. [Specification](#specification) : Precisely what you should implement, stated in a formal manner. [Report](#report) : Questions to answer that will ensure you've figured out both the basic and some advanced elements of the challenge. [Advice](#advice) : Implementation tips using the recommended tools. This is optional, but very helpful if you are using those tools. [Gallery](#gallery) : Images showing examples of what others have created for this project. The Cubes project introduces a number of tools and libraries that may be new to you. So, I include a tools Warmup section as well, which is structured as a tutorial. Most other project descriptions are shorter because they do not have tutorials. They require more active engagement on your part to to translate the specification into a program design and implementation plan, however. Educational Goals --------------------------------------------------------------------------------------------- In this project, you'll gain familiarity with: 1. *Some programmatic 3D modeling skills:* 1. Coordinate system and units 2. Positioning objects in 3D space 3. A first-person camera controller 4. The Model/Entity/Surface *scene graph* design pattern 2. *Some representative programming tools:* 1. The C++ programming language 2. The Subversion (svn) revision control system 3. The G3D library 4. The Doxygen documentation generation program 5. The Markdeep markup processor 3. *Scalable software development:* 1. Automatic memory management 2. Overview documentation 3. Entry point documentation 4. Building your own tools To achieve that excellence and deep knowledge instead of just learning a few tricks, it is important that you approach the work with the right expectations. You can simply trust me that it is worth spending time focusing on mathematical fundamentals, the details of physics, software design, and development workflow in order to produce 3D graphics. Or, you can read the optional Pedagogy section of this document, in which I explain the overarching educational goals of the project series and why I designed them this way. Warmup ============================================================================================ This walkthrough will guide you through the usual workflow for the specific [tools](../tools/index.html) that I recommend for these projects, particularly the G3D Innovation Engine, C++, and Subversion version control. Just doing the typing and clicking as directed will give you enough familiarity to then apply the information from the related chapters you've read. (If you're using a different toolset, make sure that you can perform equivalent tasks. If you prefer git to Subversion, the _Graphics Codex_ version control chapter gives a listing of the git commands that you'll need.) Where this walkthrough says to enter specific code, please actually type it--do not copy from this document and paste it into your editor. Typing the code yourself should prompt you think about what it means, and if you make a mistake will give you an opportunity to debug it. ## Create an Empty Project Create local repository/snapshot and workspace for your project, as discussed in the _Graphics Codex_ Version Control Basics chapter. In TortoiseSVN, right click in a folder and select "SVN checkout", then type in the remote URL of your project. Or, at the command line, use "svn co  url". Create a new project as described in the [G3D Creating a New Project](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/guidenewproject.html) documentation: If you're using Visual Studio, copy the contents of the G3D starter project directory. If you're using iCompile, run icompile in your project directory and respond to the prompts. You should now have a directory with subdirectories for data-files, source, and so on. In the root directory are ice.txt and/or the Visual Studio project files with names ending in .sln and .vxproj. Verify that your program is working and your system is configured correctly. Using Visual Studio, load the .sln file and press F5. With iCompile, run ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash icompile --run ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The program should compile, launch, and display a 3D scene. You can exit with the ESC key. If it didn't run correctly, debug your environment to ensure that it conforms to the one described in the [tools](../tools/index.html) document. ### Experiment with the GUI By default, [G3D::GApp](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_g_app.html) creates a [G3D::FirstPersonManipulator](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_first_person_manipulator.html) that allows you to move the 3D camera. I'm telling you the name of the class right now so that you'll know how to find and configure it later, but right now all that you need to know is...video game controls work. G3D initially launches with a scene's built-in cameras selected. Press F2 to switch to the Debug Camera. The manipulator on the camera uses common first-person PC video game controls. The W, A, S, and D keys on the keyboard will translate the camera forward, left, back, and right relative to its own axes. Try this now. If you press the right mouse button (or press "control" and the mouse button for a single-button mouse under OS X), then the mouse rotates the yaw and pitch of the camera. It requires you to press a button because otherwise using the mouse with the GUI would also move your viewpoint. There are some other modifiers; run the G3D viewer or read the documentation to find out about them. You can also use a USB game pad to navigate. G3D contains other manipulators with different control styles, and you can write your own or use none at all. This is only the default. Move the camera around a bit to get a feel for the controls. Exit your program by pressing the "ESC" key. ### Switch to the Optimized Build Target The G3D starter project has two build targets: Debug and Release. The Debug target allows detailed inspection of your code in the debugger and enables many consistency checks. The Release build runs much faster. You can switch between targets in Visual Studio from the (unlabelled) [Solution Configurations](https://msdn.microsoft.com/en-us/library/wx0123s5.aspx) list box. With iCompile: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash icompile --opt --run ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ builds and runs the Release build and the absence of --opt defaults to the Debug build. In general, it is safe to run in Release mode for most of development. You should immediately drop back to the debug build whenever something goes wrong with your program because it contains many assertions and input checks. Experiment with the developer tools provided within your window, and particularly try loading other scenes (via the SceneEditorWindow's scene drop-down list). Exit the program when you're ready. ### Commit the Files to Version Control Your _project_ is in version control, but your _files_ are just sitting in the local workspace right now. Following the process described in the Version Control Basics chapter, you'll now add and commit the files. When you add the source files to version control, you must be careful not to add the generated files. Generated files would consume too much space in the repository and create frequent merge conflicts. The easiest way to do this is to delete everything that you don't want added and then execute a recursive command on the whole tree. Delete the temp, build, x64, Debug, Release, .vs, or any other build directories. From the data-files directory, delete log.txt and g3d-license.txt. Delete any IDE temporary files, such as ones beginning with # or ending in ~, and the Visual Studio .pdb or .opendb files. You can keep the ice.csv file, which is a spreadsheet tracking your program's growth if you're using iCompile. Recursively add the remaining files. At the command line, execute: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash svn add * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ or, for TortoiseSVN, select all files and subdirectories in Explorer and right click to add. You can also simply right click and select "Commit" and then select the files that you wish to add from the dialog that will appear. Commit these files with: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash svn commit -m "Initial checkin" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ on the command line or right-clicking using TortoiseSVN and selecting "commit". Recall that Subversion automatically pushes each commit to the remote repository, so your files are now without an explicit "push" step. ## Create a new 3D Scene The App class in your program inherits from G3D::GApp, which creates a [G3D::Scene](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_scene.html) during initialization. That Scene parses files ending with the extension .Scene.Any, which are human-readable files with syntax similar to C++ code, JSON, and Python literals. These files can be produced by hand, by another program, or using the GUI provided by [G3D::SceneEditorWindow](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_scene_editor_window.html) in the default G3D::GApp. We'll begin by creating a scene by hand. In your editor, create a new file data-files/scene/whiteCube.Scene.Any with the contents: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -*- c++ -*- { name = "White Cube"; models = { cubeModel = ArticulatedModel::Specification { filename = "model/cube/cube.obj"; preprocess = { setMaterial(all(), Color3(1, 1, 0)); }; }; }; entities = { skybox = Skybox { texture = "cubemap/whiteroom/whiteroom-*.png"; }; sun = Light { attenuation = ( 0, 0, 1 ); bulbPower = Power3(4e+006); frame = CFrame::fromXYZYPRDegrees(-15, 207, -41, -164, -77, 77); shadowMapSize = Vector2int16(2048, 2048); spotHalfAngleDegrees = 5; spotSquare = true; type = "SPOT"; }; cube0 = VisibleEntity { model = "cubeModel"; frame = CFrame::fromXYZYPRDegrees(0, 0, 0, 0, 0, 0); }; camera = Camera { frame = CFrame::fromXYZYPRDegrees(0, 0, 5); }; }; }; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Launch your program and the new "White Cube" scene should appear in the scene drop down box of the GUI. ## Fix a Scene Error When you load the scene, you'll notice that cube in this scene is actually yellow and not white. You should fix that. You can either guess how, or look at the [G3D::UniversalMaterial::Specification](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_universal_material_1_1_specification.html#a12d19df20a48ad996ae5b6538bdd3d9b) documentation to see the arguments that can provided in the scene data file for constructing materials. When you've made your changes, press the reload button in the Scene Editor Window without ever exiting your program. ### Add the Scene file to SVN Whenever you create a new file, it is a good idea to add it to revision control right away so that you don't later forget. Execute: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash svn add data-files/scene/whiteCube.Scene.Any ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ or the TortoiseSVN equivalent. This command will mark the file for addition to your repository. You can see this by running svn status. However, they haven't actually been added yet. To do that, commit your changes with: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash svn commit -m "Added single cube scene" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ or the TortoiseSVN commit dialog. Now your file is in the remote repository. If you modify the file, then you will need to commit the new version. But you never need to add this file again. (From now on, I'm going to assume that you know to add and commit as necessary.) ## Debugging ### Fixing a Compile-Time Error End your program with the "ESC" key and open App.cpp in your editor. In the App::onInit method about 50 lines from the top, add this code: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ debugPrint("Target frame rate = %f Hz\n", realTimeTargetDuration()); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Compile your program. You should see an error that looks something like: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ none App.cpp(58): error C3861: 'debugPrint': identifier not found ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The exact wording of the error will depend on your compiler. The error is telling you that the code I told you to add is broken. Look at the [API index](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/apiindex.html) and you'll see that the correct function name is [G3D::debugPrintf](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/namespace_g3_d.html#a364214608a0b19645fd04f84254833d6). You don't need to type the G3D:: part. That's a C++ namespace, and I list it in the projects only so that you'll know when I'm referring to a G3D function, a built-in C++ function, or something that you're writing. Change the code to call debugPrintf instead of debugPrint, compile, and run. Look for the output of this command. It will appear in the console if you're compiling on a Unix system (OS X or Linux) and in the Debug pane of the Output window in Visual Studio. ### Debugging and Fixing a Run-Time Error Edit your App::onInit method again and add this line of code (anywhere): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ const shared_ptr< Entity >& sphere = scene()->entity("Sphere"); sphere->setFrame(Point3(0.0f, 1.5f, 0.0f)); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This line of code will move the object named "Sphere" to the coordinate (0, 1, 0) in meters. The only problem will be that there is no object named "Sphere" in the scene. When you compile and run your program, there will be an error (if you're in Release mode, it will probably crash). To debug the program, run it under the debugger in Debug mode. In Visual Studio, select "Debug" on the dropdown and press F5. With iCompile, run "icompile --lldb" and then press "r" when the debugger starts. When the program crashes while a debugger is attached, the debugger presents the error. It should be something like "e_Ptr was nullptr". In this case, we know exactly where the problem is. Pretend that we don't know, and let's see how you could figure it out on your own. In Visual Studio, look at the Call Stack window. In lldb, type bt. The call stack will look something like: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ none App::onInit() Line 63 G3D::GApp::beginRun() Line 1424 G3D::GApp::onRun() Line 785 G3D::GApp:run() Line 768 main(int argc, const char** argv) Line 42 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ followed by some operating system level call frames. This says that when your program crashed, it had started with main, which called GApp::run, and so on, until at line 63 the problem occured. In Visual Studio, double-clicking on the App::onInit line in the Visual Studio Call Stack window will bring up that line of code in your program. You can hover the mouse over the sphere variable to see its value...which is empty. That's why the program crashed. There was no sphere, so the sphere pointer is null. You can also look at the Autos or Locals window to see the values of other variables. The this pointer refers to your App instance. Expand the tree control for it so that you can see the current values of all of its properties. In lldb, the f command allows you to select a call frame by number. We're already on the top call frame, so there is no need to use it right now. The command fr v -a shows all local variables, and p sphere prints the value of the sphere variable. The program will break and enter the debugger whenever an exception, stack overflow, or segmentation fault occurs. You can also force entry to the debugger by setting a breakpoint ("F9" in Visual Studio, "break" in lldb) or breaking the program during execution (Ctrl-Break in Visual Studio, Ctrl-C in lldb). Remove the broken code that I told you to add for this exercise before continuing. ## Documentation ### Entry Points Generate documentation for your project using the Doxygen tool. On Windows, run: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash builddoc ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ in the same directory as your project files and the Doxyfile. On Unix platforms, run: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bash icompile --doc ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The documentation will appear in build/doc. It contains the contents of your doc-files directory and a series of HTML files that describe every class, method, and function. The index.html file is the starting point for reading the documentation. Wherever code is preceeded by a Doxygen style comment of the form /** ... */ in the header, those comments will appear in the generated documentation. Try adding documentation to your App class and then re-running the documentation generator. Within Doxygen comments you can use a subset of Markdeep formatting and HTML. ### Maintaining your Journal The [G3D Developer Tools](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/devtools.html) document summarizes the built-in features of G3D for supporting development of your program (versus supporting the program's own features). One of the most useful ones is the automatic journalling feature. Press the "F4" key to take a screenshot in any G3D program. This brings up a dialog that allows you to add a caption and descriptive text. It will then add the image to your journal and even add it to version control. You'll still want to write text and add diagrams and code listing directly in journal.md.html, but this tool makes it easy to document your features and debugging process. Specification ============================================================================================ *Track how much time you spend on this project*. You're required to include this in your final report. 1. Structure a directory in svn with exactly the following subdirectories, some of which may be empty for this project: 1. data-files -- _Files needed for running your program_ 2. source -- _Your .cpp and .h source code_ 3. doc-files -- _Files needed when viewing your documentation and report_ 4. journal -- _Log of your software development progress_ 2. Create the following scenes in human-readable .Scene.Any data files, using only the files models/crate/*, cubemap/whiteroom/whiteroom-*.png, and texture maps (images): 1. A single, white 1 m^3 cube rotated 45 degrees about the vertical axis, with center at (0m, 0m, -2m), created by manually editing a scene file. 2. A model of the Cornell Box that is pictured in Figure [fig:cornell-photo] created by a combination of the starter program's scene editing GUI and manual file editing. 3. A texture-mapped curving staircase with 50 steps created by writing a C++ helper function to generate the scene file on disk. - You may not use the GUI or manually edit this file. - The individual cubes must be stretched to rectangular slabs. - The material must be recognizable and appropriate for a staircase. E.g., wood, concrete, marble, or stone. 4. An interesting scene of your own design, containing at least 20 cubes. 3. The following two forms of documentation (these are requirements for every project, so I won't mention them in future projects explicitly): 1. Create overview and entry point (i.e., method, class, function, variable) documentation for your software using Doxygen. The entry point documentation is largely created for you from the starter project. 2. Maintain a journal in journal/journal.md.html separate from the report that demonstrates your progress, particularly describing key design decisions and changes and bug symptoms and solutions. ![A textured staircase](stairs.jpg width=150px border=1) Your solution stored in the remote repository should not have any unused files, dead code, or "TODO" comments. You are not required to remove empty or unnecessarily overridden methods (e.g., onGraphics3D) for this project, although you may with to remove them to simplify your code. Report ================================================================================== Prepare your report as a Markdeep document in doc-files/report.md.html. The report will appear in the build/doc directory when read. Knowing this allows you to insert relative links to Doyxgen-generated files (which will be in the same directory) and to embed images that you have stored in doc-files. Your report should be as brief as possible while covering the following points. 1. *System Overview*: Assume that someone who doesn't know anything about G3D or your program will have to modify it in the future. Describe the structure of your program for this person in your report, with links to major classes and methods. This should only take about one paragraph for describing the starter program and one more paragraph to describe the code that you added to it. I encourage you to use lists, tables, diagrams, and hyperlinks. 2. *Coordinate System*: Make simple, isometric view, labelled axis-diagrams of the 2D coordinate and 3D coordinate systems (by hand; don't write code for this) used for G3D, and include it in your report. On the 3D coordinate system, show the direction of increase of the yaw, roll, and pitch angles. I would personally either use a Markdeep diagram or draw this on paper and take a photograph. 3. *Results*: Some parts of the specification require you to create a scene. Include images of those scenes taken in a way that clearly illustrates that they satisfy the specification. For example, you may need to show a set of axes to make clear that the white cube has been appropriately oriented. Avoid capturing the GUI in your result images unless you specifically need to show the GUI for some reason. Crop images to an appropriate size. There are two kinds of visual results one might present in a report: _Scientific_ results demonstrate correctness (or not) of a system. They may be cropped and have their histogram adjusted in Photoshop, and you can draw obvious annotations on them (such as text and arrows that clearly are not part of the result). You may not resize them, paint on them, or otherwise modify them in a way that is misleading about the actual pixels produced by your program. This is an important science/engineering issue. _Art_ results can be manipulated as much as you wish. In fact, and important part of creating such results is knowing how much to program and how much to manually adjust. You may end up manually correcting every frame of an animation or compositing elements of different sequences together. If it is ambiguous whether an image is intended as art, just label clearly how you manipulated it. 4. Briefly explain the process that you used to make your custom scene. Include tools, assets, and planning steps. Think of this as the best notes that you'd write for someone else who was trying to make something similar, if you only had fifteen minutes to write down the instructions. 5. Typeset this equation into your documentation using LaTeX embedded in Markdeep: (you may not look at the source of this page, which as a Markdeep document itself, obviously has the solution embedded in it!) $$\frac{d f(x)}{d x} = \lim_{h\rightarrow0} \left[ \frac{f(x+h) - f(x - h)}{2h} \right]$$ 6. *Questions*: Knowing how to use documentation, experimentation, and reverse engineering to discover how a system works are important skills. In this project you copied a lot of code that I wrote. To gain mastery over that code, figure out the answers to the following questions and write them in your report. You're going to have to get your hands dirty on this--the answers aren't just sitting there. 1. What are the differences between the Scene* and shared_ptr data types in C++? 2. How does G3D know where the scene files are located? 3. Why should you to put your initialization code into App::onInit instead of constructor App::App? (Tip: There are many reasons. Try throwing an exception from each, and consider the implications of throwing an exception from a class's constructor.) 4. What is the call chain that invokes App::onGraphics3D? 5. Where is the file cube.obj stored on the file system? How did the Scene parser know to load it from there? 6. You can create a material in a scene file from a Color3. There are many more ways to construct a G3D material, however. One of these takes separate lambertian, glossy, emissive, and transmissive values. Speculate on why a homogeneous material (i.e., one without a pattern or image) would require four separate "colors" in its specification. 7. *Reflection*: Solidify your experience by briefly summarizing what you learned on this project. This can be a simple list with a sentece or two drawing it together. Include all aspects of the experience: the required reading, this document, programming, debugging, writing the report, and the skills, math, and ideas you encountered. 8. *Time*: Track the time that you spent on this project so that you can compare it to the baseline as one measure of productivity and workflow. You must report two numbers, in units of hours: 1. Report how many hours you spent on this project on *required* elements, which are the minimum needed to satisfy the specification. Try to *track this carefully* by marking the time that you start and stop working each session in your journal, instead of just estimating the time at the end of the project. If you spent time on e-mail, web browsing, or other tasks noncritical for completing the specification, exclude that time (and stop doing those things while you're working!) Include time that you spent reading documentation or re-reading _Graphics Codex_ content, but _not_ the original read-through of the required reading. Writing the report is part of the requirements. 2. Report how many additional hours you spent on *optional* elements, such as polishing your custom scene and report formatting. Advice ============================================================================================= ## Workflow I've mentored hundreds of people through this project and other introductory projects similar to it, from high school students through professional programmers learning about computer graphics for the first time. I've found that programmers with some expertise working on large systems, whether in school or at a company, complete it in under two hours. That's for people with no prior C++, G3D, or graphics experience. However, students with no significant systems programming experience typically average 14 hours to complete this project left on their own. That's way too much time! If you've never spent at least three months intensively programming on some system, never worked with version control or an integrated development environment, or never worked from specifications and wrote computer-science lab reports, then you are at risk for being in this group. Here's some advice for less-experienced systems programmers. Monitor your time investment. When it looks like you're going to take more than five hours at your current rate of progress to complete the project, immediately stop and seek advice or consider how your workflow could be improved. (And don't wait until five hours have passed! If you've spent two hours and aren't at least one third of the way through the project, stop then and consider how you're going to get across the finish line in another three hours.) Target the _minimal_ interpretation of the specification that you can get away with. You naturally want to do more, but finish the minimal version first and then return to areas you'd like to polish more. Always work from a single, persistent IDE instance. This will keep you from accidentally opening the same file in two different sessions. More importantly, it will reduce your development time. I've seen students who opened a source file, found the line they needed to change, edited it, closed the editor, and then compiled. The compiler would report an error on the very next line, so they re-opened the same file, searched for the line, etc...it took those students more than twice as long to debug a program as the ones who simply kept their files open and on the right line. Learn the keyboard shortcuts for your IDE. You're going to be copying and pasting, compiling, setting breakpoints, single-stepping, stoping your program, searching in files, etc. a _lot_ while working on these projects. If it takes you five seconds to scroll a window, select some text, pull down a menu, and then select a command to run on the text, you've just taken five seconds for something that takes a good programmer half a second. Every time that you re-type something instead of copying and pasting or using command completion, you're incuring more overhead and giving yourself an opportunity for errors that will consume more time to correct. Saved seconds on each small task add up to hours of saved time over the course of several projects. When trying to understand a library or language feature, imagine yourself in the API or compiler writer's place. How would _you_ have implemented it? What constraints force that design?) Draft your report immediately, before you've accomplished much of the specification. As part of your deliverable, it will guide you in how to spend your implementation effort. When your programs become larger, trying to explain how they work _before_ you implement them will also guide you to cleaner and simpler designs. Make very quick answers to the report questions at the beginning of the project. This will prime you to recognize the real answers as you encounter them while working on the project. Return at the end with those insights to make final answers. ## The Cornell Box The *Cornell Box* is a real-world box at Cornell University that has been long used for photorealistic rendering experiments. The idea is that by constructing a real scene containing only well-measured geometric primitives, we can create a perfect virtual replica and then measure rendered results against real photographs. There have been many variations on the Cornell Box. We'll model the specific one shown in Figure [fig:cornell-photo], and estimate the geometry rather than working from measurements. ![Figure [fig:cornell-photo]: A photograph of the real Cornell Box](cornell-box.png height=200px) ![Figure [fig:cornell-g3d]: Seven instances of cube.obj.](cornell-box-cubes.png height=200px) This Cornell Box can be modeled using seven instances of rotated, translated, and scaled cubes. When creating your Cornell Box, scale the _models_, but don't rotate and translate them. Instead rotate and translate the _entity_ placement. Were we animating the scene, that design would give us more intuitive control of the objects. It also lets us reuse objects. For example, all three white walls should be different entities that use the same model. The setMaterial command is one of many preprocessing options that can appear in a [G3D::ArticulatedModel::Specification](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_articulated_model_1_1_specification.html#details) in a scene file. To change the dimensions of a cube, you can use the transformGeometry command, which might make part of your data file look like: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ squishedCube = ArticulatedModel::Specification { filename = "models/cube/cube.obj"; preprocess = { setMaterial(all(), Color3(1, 0, 0)); transformGeometry(all(), Matrix4::scale(0.5, 1.0, 2.0)); }; }; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can chose the scale and need not worry about the precise colors and angles. Ensure that the walls have nonzero thickness. I chose 2 cm-thick walls for a 1 m^3 box, which is about the scale of the real-world Cornell Box. ## The Staircase You now need to create a staircase. Specifically, a staircase with some plausible material. To make a cube appear to be made out of wood instead of solid-colored plastic, we'll vary the surface color in a wood-like pattern. The easiest way to do this is to simply take a picture of some wood seen head on and paste it onto the faces of the cube. This process is called *texture mapping* and the images are called *textures* for historical reasons. It is very easy to texture map your cube. First, instead of the plain cube file, load model/crate/crate.obj into your staircase scene. You'll see that it is a staple video game asset: a cube with images painted on it, so that it looks like a shipping crate. To make it look like a solid block of color, you know that you can invoke the setMaterial command in the data file and pass a color as the third argument. You can also apply a texture by specifying a filename (as a string) instead of a color. Now, you just need to write code to generate a .Scene.Any file. There are many ways to write to disk from C++. I used the [G3D::TextOutput](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_text_output.html) class because it made it easy to embed large strings of text (e.g., for materials) that wasn't really being generated so much as dumped out to the file. Another good option is to directly construct a [G3D::Any](http://g3d.cs.williams.edu/g3d/G3D10/build/manual/class_g3_d_1_1_any.html) and allow it to serialize directly to file. ## A Custom Scene The single cube was my example to show the parameters you can adjust and how to initialize certain classes. The Cornell Box is a classic rendering test. It demonstrates that you have sufficient control of the coordinate system and features classes to model a given scene. The staircase demonstrates that you can make your process scale and understand it well enough to automate it, as well as introducing materials. For any rendering project you'd probably make simple scenes like this as test cases and benchmarks. Then you'd make a more interesting scene to produce visually compelling results and measure performance on more realistic data. In future projects I assume that you're using these techniques as needed to create test and benchmark scenes without it being explicitly required. You must now design an interesting scene of your own and model it using optionally texture-mapped cubes, lights, and a sky box. Have some fun. Experiment with materials and all of the options in the scene editor GUI, even though you might not know what most of them are intended for. For example, I decided to create the dog shown in Figure [fig:teaser]. (_You should not make the dog--you should make something else_.) I'm expecting something of about the complexity of my dog, although you're welcome to go beyond that if you enjoy the process, I'm not expecting the Taj Mahal for the first project; it just has to be more interesting than the Cornell Box! The crate.obj file forces you to have a single material on all cube faces because it is modeled as a single part. The crate-unique-materials.obj file instead defines unique materials for each face, allowing you to create a more complex texture mapping. You can see the names of each of the parts by clicking on them and then looking at the Info pane of the SceneEditorWindow. (setMaterial(all(), ...) sets the material for all parts, setMaterial("crate/front", ...) sets the material for the part named "crate/front", and so on.) If you're stumped for artistic inspiration, note that legos, Lincoln logs, and most other building toys, let alone most houses and other buildings are just scaled cubes. Look at the examples from previous years in the gallery section. ## Creating Images Visual communication and presenting your work effectively are important in any field. Learning how to compose images that read clearly, with good color palettes, camera positions, overlap, and lines is a valuable skill, and one that anyone can acquire with practice. In computational graphics in particular, it is important to leverage visual communication skills to present algorithms in a compelling way. On one hand, we'd like like algorithms to be judged by quantitative results and analysis. On the other hand, following such analysis is a large investment on the part of the audience, and a single image can prove that an algorithm is indeed sufficient for a task. As an audience member, if someone can't show you a picture demonstrating that his or her algorithm does what you want it to, why would you bother following an analysis of just how poorly suited it is? Most computer graphics papers and talks therefore begin with a single, visually compelling image, often called a teaser. If the teaser grabs you, then you will investigate the rest of the work to see how well the technique applies under specific targeted experiments. Those targeted experiments isolate a single phenomenon and explore how parameters and specific input scenarios affect it. They typically employ common datasets to allow comparison with previous techniques, the results of which are often shown side-by-side. The same process is also applied outside of pure research in the context of production and engineering. Say that a technical director at a film company is investigating new shadowing methods. He or she would render a few scenes from that company's previous film with the new method to show everyone what to expect from the new algorithms. He or she would then make specific images to investigate the algorithm more carefully. For example, the hard shadow of a single edge under a point light, the soft shadow of that edge under an area light, shadows from translucent objects, cast by and on curved surfaces and so on. Gallery ======================================================================= For your inspiration and to set your expectations, here are some results that my students produced for this assignment: - [Williams College CS371 2016](https://www.cs.williams.edu/~morgan/cs371-f16/gallery/0-cubes/index.html) - [Williams College CS371 2014](https://www.cs.williams.edu/~morgan/cs371-f14/gallery/0-Cubes/index.html) - [Williams College CS371 2012](https://www.cs.williams.edu/~morgan/cs371-f12/gallery/0-Cubes/index.html) - [Williams College CS371 2010](https://www.cs.williams.edu/~morgan/cs371-f10/gallery/0-Cubes/index.html) Below are some videos that I found inspirational for this project: ![_Automatic Photosphere_](https://youtu.be/XdhIu4-fb9c) ![_2019_](https://youtu.be/a5iMYXvXjbw) ![_The Parable of Brin_](https://youtu.be/8K8fhJO24ow) ![_January in Cubeland_](https://youtu.be/tnMebCgFPxs) Pedagogy ======================================================================= ## This is not a crash course I've structured these projects to be rewarding and maximize your learning. I hope that you'll enjoy them as well as increase your knowledge and skill set. I also respect your time and believe this is the fastest route to making you an excellent graphics programmer. A decade of data indicate that each project takes between three and twelve hours (depending on the project and your development pace) to complete. For most, you'll write between 100 and 400 lines of code. These projects are _not_ the fastest way for you to learn to make 3D scenes, images, movies, and games. There are plenty of online tutorials that will help you to accomplish that with much less effort on your part. There's a lot of value in following a "Unity 3D videos" or "DirectX in 30 days" course. But it is a different value than the one I'm provide. The value that you're getting from the thorough approach of the Graphics Codex Programming Projects is comprehensive understanding from first principles. Although you need specific tools to complete the exercises, what you're learning is real computational graphics, computer science, and software development. Your knowledge will then transcend any particular API, algorithm, or toolset. This gives you freedom and makes your skills applicable to new graphics ideas that haven't even been invented yet. It is also why I emphasize core physics, numerical methods, and algorithms over the latest real-time techniques, even though many of us are motivated by the power of real-time 3D graphics experiences. ## The value of fundamentals I've worked in this field for over two decades, including shipping many video games and core technologies and publishing many research papers. In that time, I've seen many low-level optimizations and APIs rise and fall in popularity. I recall the heydays of SGI, Gouraud and Phong shading, Glide, DOT3 bump mapping, WinG, register combiners, shadow volumes, parallax mapping, GGX, D3D, and Vulkan. Hopefully, you haven't heard of most of these, and never will. As solutions highly-optimized for a specific time, they don't have much value in the future. The hottest rendering trick of the moment often becomes embarassing old news by the following year. Only one trend has remained stable from the dawn of modern computational graphics in the 1960's through today: the increasing sophistication of both offline and real-time techniques at approximating real physics and core aesthic principles. I guarantee that techniques such as monte carlo sampling and local contrast will be more relevant for premiere graphics development in a decade than the current version of any game engine or hardware API. ## The art of designing programs You'll note that these projects provide terse specifications instead of implementation plans or programming tutorials. Working from specifications gives you many advantages in return for the effort that it requires of you. Part of the overall educational value of these projects is you learning to solve graphics problems _on your own_. By specifying the properties of your program but giving you the space to decide how to achieve them, I'm teaching you to design. The point of learning graphics _programming_ is not to make a picture (which is the goal of art), but to solve the problem of _how_ to make the picture. I give hints as to useful topics in the documentation and code in the G3D sample projects to guide you in the right direction. You'll quickly learn to navigate the documentation, library source code, and sample source code on your own. The advice sections give extensive design suggestions as well. But don't attempt to turn them into walkthroughs! They are intended as advice to help you solve design problems once you've already encountered them. If I've done a good job, then the advice won't make sense until you've tackled the program on your own. When you get stuck or wonder if there's a better way to approach a specific design point, that's when you should consult the advice section. Think of these as a sort of transcript of a professor's office hours or senior developer's code review. ## The precision of technical language A significant amount of a computer scientist's work in industry or academia is communication in natural language with other people. This comes in many forms, but technical precision is always to your advantage. The projects and _Graphics Codex_ as a whole use technical language carefully. This avoids ambiguity and allows concise communication. I hope that they have a certain warmth that conveys my excitement about graphics and encouragement for your exploration of the field. The specifications that I provide in the reports that you write for them also use technical language precisely. In pursuit of brevity and professionalism, they should lack the warmth and personality, however. Writing the report has many purposes. It helps ensure that you haven't missed something in your implementation. It teaches you how to test your implementation, with results that you'd demonstrate at an industry meeting or in an academic research paper to show that your program is correct. If you are using these projects in a course, then your instructor will probably also grade the report. ## Software development is a core skill Good software developers are massively more productive than average ones. While this is a topic of some political debate within the field, in practice I've never seen a software company in which everyone didn't know who the top developers were and recognize that they accomplished much more than their less-experienced colleagues. That's a positive sign. It would be very concerning for our discipline if people who worked hard for many years weren't able to outperform newer developers. Every field has its masters. These projects encourage you to learn to use your editor, debugger, profiler, documentation, and version control system with the facility that a master carpenter employs their tools or a professional musician their instrument. Invest time in accomplishing simple tasks quickly and complex tasks cleanly and without stress. One of the goals of writing the report is to reflect on your process. That's why I ask you about time spent. In the course of working through these projects, most people get much faster. The Meshes project takes some people five hours to complete. By the end of the sequence, they can return to it and complete it in about an hour. The craft of software development is, like most others, best learned through an in-person mentor. Unless you're enrolled in one of my courses, that is unfortunately something I can't provide in these projects. So, if you are working through these projects on your own (instead of in a course) and are relatively new to software development, then I recommend seeking out such a mentor and discussing the projects with them.