You’ll find that a lot of the time there’s a ton of work done outside of a game engine to make sure it’s as smooth as possible when it’s actually running. An example of this is something I did recently, which was loading a 3D model into my engine. If I were to take the basic file format (OBJ in this case), and try to load it in and use it as-is I would have to do a lot of work just to get it into the right shape. This meant that it took on average about five seconds to load in one 3D model of a character, one! Can you imagine a game that froze for five seconds every time it showed you a new model? It would be unplayable.
To explain why that is we need to briefly look at what the actual data that comprises a 3D model looks like. Below you’ll see the data for a simple cube.
Lots of numbers! Very surprising, I know. Luckily I’ve used my amazing artistic skills to separate this jumble into the four sections that we need to concern ourselves with.
- The red section of numbers are the vertex coordinates of the model. Each line is a separate coordinate and specifies the location of a vertex in model space.
- The blue section of numbers are the texture coordinates of the model. Each line is one set of uv coordinates, which point to a specific point on a texture. These are usually specified by the 3D modeller in the modelling application.
- The black section of numbers are the vertex normals of the model. Each line specifies what the normal is at a particular vertex.
The green section is where it all comes together. Each line specifies a single face of the object. As we can see each line is comprised of three sets of three numbers, each of the subsections being divided by forward slashes. The three numbers in each sub section are indexes into the previous groups of numbers. So the first subsection of the first face line is 5/1/1. This means that the first vertex of this face uses the fifth entry in the vertex coordinates section of numbers, which is (1, 1, -1). It also uses the first entry of the texture coordinates section, which is (0.748573, 0.750412). And finally it also uses the first entry of the vertex normals section, which is (0, 0, -1). So each face line will specify three groups of this information, which together constitute one face (triangle) of the model in question.
So hopefully you can now see that once we’ve loaded in the first three sections (position, uv coords, normals), we use the final face section to assemble them into the complete model.
After having looked this information up and discovering how to parse the information in an OBJ I began to write my model loader. Once it was finished I began to do some tests and it was a success! I had successfully loaded a model in OBJ format into my engine. However, there was a problem. The problem I mentioned at the beginning of this post. It took forever to load each new model. I went back and had a look at my loader and the problem became apparent quite quickly. Every time I wanted to load a model I would actually load in the OBJ file and parse it right there and then. This was very slow as it involved opening up a file, loading a huge amount of data from it, parsing it, then dumping the data. This wasn’t a problem when I was just loading cubes, but once I started on more complex models it became hugely noticeable.
After puzzling over it for a while I hit upon the idea that what I was doing when I parsed the data from the OBJ files wasn’t something that really had to be done while the game was running. If I did it offline, before I ever ran anything, I could load the model up, parse the data, and then store it in a file in exactly the shape I needed it when the game was running. I did so, storing it in binary for some extra speed, and the loading time turned from five seconds to imperceptible.
You’ll find with some digging that this is a pretty common problem for game engines, and that it’s just one example of how data needs to be optimised for best performance.
Hope it helps!