Last time on the dev blog, we implemented the use of .lua writing and parsing for our game engine. This week, we turned to binary files on our quest for a more efficient system. Specifically, we added support for reading and writing binary files. Those being files of bytes that we can parse in at runtime in order to generate the meshes in our game, like the brain that you see above!
The data that comprises one of our meshes are the following:
In the above example, this file describes the "floor" in our game scene. At the moment, it has a picture of a dog and is composed of four Vertexes of which each describes the position, color, and UV data of each vertex. Then, six indexes that refer to the drawing order of the vertex to create the two triangles that make up the simple floor plane.
Why Binary Files?
After seeing the work we went through to make our mesh data as easily readable by the human eye in the previous post, you're probably wondering as to why would we go through the hassle to move this new format? There are two immediately discernible advantages, file size and process speed.
Because binary files are just a sequence of bytes, no alphabetical characters, the data is exactly as the computer reads it. Meaning, because we don't have to "transcribe" or "translate" into the more readable english .lua format that you saw previously, we can now write code that directly reads the values of the bytes contained within the file. This time, we don't need to write the visual representation of the letter / coordinate axis "z" along with it's value but rather, we can read a set of x bytes at a given position directly as the z value itself.
Continuing on the point that binary files are the exact representations of the data that they are, it makes processing and parsing much easier as well. For example, instead of having to search for the letter "x" and the corresponding value to the key (as lua represents our arrays as key-value pairs) we know directly that the first bytes in the vertex data sequence will correspond to the x value. Thus, we can interpret it exactly so in our read code. You'll see the direct evidence for this proposition later when we get to the timing of the process functions of this blog entry.
But don't worry! Human-readable formats like .lua still have their place here in the engine. When writing the data or storing the data, we will continue to use .lua. This still is the case because we're emphasizing readability and versatility when it comes to manipulating game data, like our meshes for example. However, at runtime, we're going to emphasize speed as we'd like our game to get up and running as soon as possible! To do this, we just have to make sure that we build our .lua readable data into their appropriate binary counterparts.
How about them Platform Specifics?
We can't forget the arbitrary platform specifics that we have imposed on ourselves throughout the engine though. And I'm speaking specificaly about the use of OpenGL for x86 builds, and Direct3D for x64.
Recall from the previous section that we saved the direct position, UV, and color data of a given vertex. Because our exporter for Maya outputs in OpenGL we must remember to build our binary files into say, the correct Direct3D requirements. To do that we:
We do these steps in our MeshBuilder code, where .lua is compiled into their binary representation.
Extracting Binary Data
Extracting the data from the raw loaded chunk of bytes is fairly straight forward if you're decently versed in pointer manipulation. The key to this process is understanding how the data is written to file. Knowing this we know the exact format, eliminating guesswork. We know the format is as follows:
Amount of Indexes | The Actual Index Data | The Amount of Vertexes | The Actual Vertex Data
With that in mind, as well as the exact data types that are used to write these to disk, it is fairly simple to do the arithmetic to get to the appropriate addresses in the loaded data to store into our Mesh representation
Earlier I mentioned how we'd now prove our assumption that reading binary data is faster than .lua script. The following are the results of timing the exact functions that do these tasks. The difference is easily seen!
This was done with the same mesh that you see in my first screenshot, the brain:
Lua Load Time: 0.016053 Seconds
Binary Load Time: 0.000545 Seconds
Loading via binary files are tens of times faster than that of Lua! But, it doesn't stop there. If we also compare file size we get the following:
Lua File Size: 434KB
Binary File Size: 91KB
The advantages are to speak for themselves.
The Camera: [Key : Action]
[W : Move Forwards]
[A : Move to the Left]
[S : Move to the Right]
[D: Move Backwards]
[Space: Move UP]
[Left / Right Ctrl: Move DOWN]
The Brain: [Key : Action]
[Arrow UP : Move Forwards]
[Arrow LEFT: Move to the Left]
[Arrow RIGHT : Move to the Right]
[Arrow DOWN: Move Backwards]
[Page UP: Move UP]
[Page DOWN: Move DOWN]
You can try out these games via the below links. The only difference is that Direct3D will be used in the x64 version, with OpenGL in the other. They have been built and verified to work on Windows.