The source code initially uploaded with this article has gone missing over the years. However the project later evolved to become the small3d game engine. More details, source code, documentation and sample games can be found here: https://www.gamedev.net/projects/515-small3d/.
If this is the first time you hear from me, please note that, instead of writing this article, I could have written the tenth part in a series of blog posts, reporting on the progress of a project which has lasted for more than a year now. I have decided to postpone producing that document however. I thought that it might be a better idea to write on gamedev.net, summarising everything I have been doing so far. Things have reached a point at which I think that my work can assist beginners in learning the basics of 3D game programming with OpenGL and C++, without using a ready-made game engine, a lot faster than I had to.
Indeed I have been educating myself in making cross-platform (Windows & Linux) 3D games with C++ and OpenGL and, since I am taking the time, I have decided to try to help others do the same, by open-sourcing the code, documenting it and providing detailed instructions (Docs/developer.txt) on how to set up the project on Windows and Linux. In this article, I will be presenting my motivation for following this self-made course and, also, introduce you to the existing code base and let you know how I think you should use it to learn 3D game programming with C++ and modern OpenGL with GLSL shaders, while also maintaining a certain level of backwards compatibility.
The Concept
In case it is not blatantly obvious, note that I am no recognised authority on the subject. By now I can say that I have had a pretty long career in traditional software development, building client/server applications, invoicing systems, content management systems, document management systems, that sort of thing. So, even though a lot of indie developers will understand my desire to make my own games, one may be wondering how someone with this sort of background can hope that others will learn from his work.
Already, an enormous collection of books, tutorials, courses and tools exists, provided by experts, who specialise on every aspect of video game development. I am using this material myself as I go along. The advantage of my "product", the niche it covers so to speak, is actually a consequence of my own lack of expertise, together with my lack of time to devote to learning everything that is needed.
If you are like me, you have probably dabbled a bit with game development in college (or high school) before deciding that it was not the best basket to put your eggs in when planning your career. In my case, this led to just focusing on getting my degree, getting a job, getting a better job, spending a lot of energy on the learning requirements for those jobs and realising at some point that, even though I did take math for some semesters, my professional programming activities never required anything more than basic arithmetic, so I would be lucky if I could still remember how to integrate a simple function or figure out the length of the hypotenuse of a right triangle, should the need ever arise.
This is not my first effort to produce something playable. I have tried many times in the past and it was not because of the lack of available information that I have given up but, rather, because of the size of it.
As an indie developer, especially as a hobbyist, you do not get to work as part of a team. This means that you will have to do everything yourself, art, 3D models, animation, rendering, game logic, collision detection, sound, menus and, finally, packaging. The problem that poses itself, as you can probably imagine or have experienced, is that this can seem to be an impossible task.
Sure you can buy a 1000-page book on OpenGL but when do you read it and experiment with what you have learned? Would you be doing that at the same time as you are learning Blender or should the latter wait until you can render a rotating object on the screen, using code? What libraries should you use?
One way out is to limit one's aspirations. You could settle for making 2D Javascript games for example. Actually I have made one of those at some point but that did not help me get rid of my wish to go 3D. Another way to go would have been to just find a ready-made game engine and learn how to use that, instead of trying to build everything on my own.
As a matter of fact, many comments I have been receiving over the past year were suggesting exactly that, also noting that this is the way many pros do it today anyway. This way I could just focus on my 3D modelling skills, download some sounds, throw everything in the engine and tell it what I want it to do, almost in plain English.
Somehow though, I thought that that would be missing the point. Were one to decide to quit his or her day job and try to make money out of game programming I would say sure, go for it. Take all the shortcuts you possibly can. Limit the scope of your project to the absolute necessary for releasing your first production as soon as possible, before your funds run out. Buy all the tools you can afford that will help you work faster and produce as much as possible with what you already know. Anyway, is that not what we do in more traditional sectors of the software industry, when clients are pressuring us and our budget is limited?
On the other hand, suppose that you are not ready to take that step. Many will tell you that doing so out of the blue is a very bad idea and I tend to agree. In that event there are benefits to providing oneself with a more, let us say "classical" education. Programming in C++, making your program compile on different platforms, using the OpenGL API and writing your own shaders will help you understand everything much better.
Sure you can just start by using a tool that will help you spit out one game for mobile devices per week but you know what? Tools change. Platforms change too, as we have witnessed in the last few years with the advent of mobile devices.
When that happens, I think that we have two choices: We either adapt to the new environment, or we join a tribe of technology X version Y developers who spend more time arguing about why their own IDE will outlive that of the rivalling camp than on furthering their knowledge and skills. The "classical" path yields therefore the advantage of being more future-proof.
The more you learn things bottom-up, the more you can be sure that your knowledge does not have an expiry date and, for the part of it that does, you can be sure that the rest will help you absorb anything new that is coming much faster.
As an example, math and physics skills practically never expire. Of course science is always advancing but a very good part of the kind of calculations we need to do for games has been around for centuries. OpenGL is much newer of course, it competes with DirectX and who knows? Maybe both will be replaced by something else someday. In the meantime however, whenever we want to simplify our development process and opt for ready-made rendering engines or simpler technologies, we are always using them, under the hood.
Finally, to my understanding, a lot of the higher-level languages have compilers and/or run on virtual machines developed in the likes of C or C++. The way I see it, when one of our high-level tools loses popularity or a new platform comes along, the new tools are closer to these foundations in the beginning and it is the people who are familiar with the foundations that can make the switch fast (for understanding WebGL or OpenGL ES for example, having worked on GLSL shaders with OpenGL 2.0 helps a lot, as far as I have been able to find out).
As hobbyists or aspiring indie developers with regular jobs, even if we are faced with a lack of time, I believe that the time that we do have is better spent teaching ourselves timeless principles than releasing a smart phone app next week. In addition to the benefits mentioned above, this path is also cheap (lots of open-source stuff available) and, should you need to interrupt it because of other work or family priorities, it will wait for you. It will not matter if a new mobile device has been released or if the company producing a game development IDE has released a new version or has shut down.
Having said that, please note that I am grateful that all of these tools exist and I admire the hard work and dedication people have put into developing them. I have used them myself and probably will do so again someday. On the other hand, being able to write my own 3D games without relying on them 100% will help me understand them better and get more out of them if I ever decide to work on a larger project.
For the time being however, I have chosen to go "low-level" and, as far as I have seen, it is not as hard as it seems. The tough part, and the one I hope I am able to help others with, is navigating through the process.
Learning by doing is in a way the only way to learn but, in order to do something, you need to have learned a bit of theory to begin with. When do you put down the math and OpenGL book and code a bit? When do you give Blender a go? And then, when do you go back to coding? It is these questions and my not answering them correctly all of the time that have taken me such a long time.
Hopefully, using the source code and a couple of my tips, you can get to this point a lot faster than I did. So what do we have right now? That would be a goat spinning over a prairie and moving its legs.
Don't laugh! There is a reason I have decided to write this article at this point and not at the end of the project. I'm not done yet as far as the project is concerned but I do consider myself capable of making a 3D game already.
You see, a few years ago, I developed the exact same game I am developing right now, only in 2D and Javascript. It looked like this: The learning challenges posed by that thing were quite modest.
Draw the background, draw the goat, draw the bug, animate the goat, do some minor collision detection, have the bug chase the goat, create the game menu and... presto! You're done.
It took me about a week to finish. For my C++ & OpenGL version however, things were different. For 70% of the past year I have been setting up the project, gathering the necessary libraries and then working out a way to have everything consistently compile and run on Windows (with OpenGL 3.3) and on clean installations of Linux (Fedora, Debian and Ubuntu, using OpenGL 2.1).
Then I got into the rendering stuff. Come to think of it now, one just needs to take the time to read a couple of chapters from "Game Coding Complete", this great tutorial I have found on line, the "Red Book" [2] and try things out at the same time. Math also helps so I would suggest "Essential Mathematics for Games & Interactive Applications" [3]. If that is too much, maybe try "3D Math Primer for Graphics and Game Development" [4].
The authors are very good at not only explaining the theory, but also describing what all the math does for the game, in layman's terms. Try to read the first chapter of [3] at some point though. It will give you a very good sense of what can happen to your vertices and collisions if you don't pay attention at how you handle floats in your program.
You may be thinking, "What are you telling me now Dimitri? You say that it is not so hard to do and now I have to study all of these books?" Boring no? One of the same.
Actually, that is not what I am saying at all. Before you get to studying, I would suggest that you download my source code and compile and run it on your machine. I have uploaded it to gamedev.net and it is accompanying this article. Then start trying to change some things here and there. Make the goat rotate in the opposite direction, make it fly, that sort of thing.
As far as the books are concerned, you don't need to study them cover to cover. As a matter of fact you probably do not need to have access to each and every one. The best way to learn depends on each person so I would just pick a couple of resources that I believe will answer my own questions (including many excellent articles from gamedev.net), helping me get a hold of the "knot" that is game development from one of its threads and then follow up from there.
The good thing is that, if you use my code to experiment, that will save you hours of trying to figure out how each element fits within a game. If you are learning about shaders, play with modifying my shaders. If you are learning how to model, just export your models as Wavefront files (see Assets/README for details) and try to load them into the program to see what they look like.
Using the Code
Downloading the code and following the instructions (Docs/developer.txt) should give you the following development stack to get you started:
I am using CMake, which produces a Visual Studio 2010 project in Windows. In Linux you have a choice between various kinds of setups, but in the instructions I describe how to set up a standard make project, an Eclipse project or a CodeBlocks project. They all work great.
I am also using the Boost libraries and, at times, I prefer them over the STL, even if many of their features have been standardised in it by now. The reason is that not all compilers offer support for the latest standard C++ features (sometimes gcc will not be able to compile something on Debian that VS can and vice-versa) so I have decided to play it safe. The Boost library also offers some pretty nice unit-testing capabilities. The project includes some unit tests but I am testing only part of the code.
I am not aiming for enterprise-grade code coverage neither do I think that it is necessary to have every little detail unit-tested. OpenGL is used for rendering of course and it sits very nicely on top of SDL. The latter makes the program run on both Linux and Windows with almost no changes to windowing logic. That is very useful because cross-compiling can pose quite a few challenges on its own, so it is good to be able to eliminate the windowing stuff out of the equation.
Finally Doxygen can produce html documentation of the code at any time, based on the comments written therein. And Valgrind is an excellent free tool for profiling. It only runs on Linux as far as I know, but if you correct your code there while using it, most of the benefits will be carried over to Windows as well.
It has recently helped me realise that using the Boost Tokenizer for string parsing content from model files was a bit too costly resource-wise and that it was better that I write the parsing code myself. Make sure you check out Kcachegrind too. It does a great job of visualising Valgrind information. As far as the structure of the program itself is concerned, it is minimalistic on purpose:
Basically, GameLogic decides what happens in the game (it is empty for the moment but that is the plan for the immediate future). It then should use PlayerView, which in turn renders the various WorldObjects (goat, tree, bug) on the screen, using the Renderer.
Each world object contains one or more Models (meshes exported in Wavefront format from Blender), depending on whether it is an animated object (like the goat) or a non-animated one (like a tree). An image object is used to load images from png files (using the libpng library), either to be used as textures for the Models or for other purposes, such as background (sky and ground).
The Configuration class is used by all objects in the game, providing services to them, such as figuring out which hard disk path the game is running from and where to find the various resources needed.
The GameLog class allows for logging to be performed, as the name implies and, finally, GameException is the exception thrown when something goes wrong and it contains information about the relevant error. That's it.
Admittedly the diagrams are missing some information, like the fact that I am also using the GLEW to discover the supported OpenGL features on each platform, but there is no need to get into this much detail in this article. A lot can be discovered by using and reading through the code.
By the way, even though it has taken me a long time to set up the project and write the program, I never hesitate to get rid of things that are no longer needed and I always think twice before using new features from the libraries or adding functionality that is not necessary. That is what I believe will make the code easy for me to go back to and possibly reuse, and also what I think will help you try things out as you begin your own learning adventure! Feel free to copy paste anything you would like.
Also, if you have any ideas about improvements and you make a pull request on GitHub, I will review and possibly integrate it into the code.
Creating the Models
I have left out Blender haven't I? Learning Blender can be fun if you start small and work your way up, step by step. Modelling a bug or bee for example should be quite trivial (you can even export the default cube that sits in the middle of the scene when you load the software).
There are many good books on Blender too. I have been using "Blender Foundations" [5] for example and I was very happy with it. I have to admit, as is the case with all my sources, I did not get to read it from start to finish. I might someday, once I have "digested" enough of the information I am currently absorbing, but we will see how it goes.
Wrapping Everything Up
In case you are wondering where the rest of the game is, as I have mentioned, I thought that it was an ideal time to post this article now before it is completed.
After all this time of slow progress, I have noticed that I am getting as productive as I was when I was developing the Javascript version. Figuring out how to create the game controls is not such a big deal with SDL (I am already using the Esc key to exit the demo). It is now just a simple matter of hooking that up with some reasonable moving around of the goat, designing a little bug in Blender and adding it to the scene.
The bug's AI should not pose that much of a challenge either. Check out the source code of the 2D version and add another dimension to its "thinking" and moving.
Perhaps the most challenging task left is implementing collision detection, so that a round of the game can end when the bug touches the goat and so that both the bug and the goat do not walk or fly through the tree.
Oh yes, the tree. Well we can also skip the tree. Or we can model and add it to the scene, but after the goat and bug, that should not be too difficult. It is not shown in the video, but note that I have also developed a function to render TrueType fonts on the screen. That should help with developing menus and messages to the user.
You might need to improve the positioning and sizing logic though. Of course, if finishing someone else's game is not your idea of fun, it might be an even better approach to modify the source code, in order to implement something completely different. In the meantime, I will be completing development, as initially planned. Once I have finished the game, I will post another article and the source code, completed with that "final 10%".
References
[1] Mike McShaffry, David "Rez" Graham, 2013, "Game Coding Complete", Fourth Edition, Course Technology, Cengage Learning, ISBN-13: 978-1133776574
[2] Dave Shreiner, Graham Sellers, John M. Kessenich, Bill M. Licea-Kane, 2013, "OpenGL Programming Guide", 8th Edition, Addison-Wesley Professional, ISBN-13: 978-0321773036
[3] James M. Van Verth, Lars M. Bishop, 2008, "Essential Mathematics for Games & Interactive Applications", 2nd Edition, Morgan Kaufman, ISBN-13: 978-0123742971
[4] Fletcher Dunn, Ian Parberry, 2002, "3D Math Primer For Graphics And Game Development (Wordware Game Math Library)", 1st Edition, Jones & Bartlett Learning, ISBN-13: 978-1556229114
[5] Roland Hess, 2010, "Blender Foundations - The Essential Guide to Learning Blender 2.6", Elsevier, ISBN: 978-0-240-81430-8
Nice article, I like the diagrams!
You decided to use libpng rather than load to an sdl_surface for your textures. Why did you decide this when you had already included SDL in your project? Just curious if there was a reason for doing this.