One of the most important parts of model loading is just getting the file into memory. One of the ways to do this is to use a set of functions from the standard C library, collectively known as the FILE * functions. You have probably heard of them, perhaps even used them before.
This section is intended to give you a basic overview of the most important functions and datatypes used for I/O. The first thing you need to do before you can use FILE * I/O is include the appropriate header. You will need <stdio. h> for C programming or <cstdio> for C++.
The first and most important part of the FILE* I/O is not a function at all. Rather, it is a datatype called FILE. FILE is always used as a pointer (FILE *) and holds exactly what it sounds like, a pointer to the file called a file pointer. You need a separate file pointer for each file you want to have open; each file pointer can only point to a single file at a time.
So now you have a datatype that points to the file, but how do you use it? The first thing you need to do is open a file. The function you want here is fopen(). fopen takes just two parameters. The first is a constant string (const char *) that contains the file name of the file you desire to open, the second, also a constant string (const char *), is the mode you want to open the file in. The mode parameter can take many forms, depending on what you want to do with the file. There are modes for reading, and writing, and binary and text files.
How do you determine which mode to use? Simple, just check Tables 3.1 and 3.2 for a list of all the mode strings that can be used with fopen. The mode string will consist of one part from Table 3.1, which tells fopen which type of access you want, whether it be reading, writing,
~ГГ^Э-
appending, or a combination. The second part of the mode string comes from Table 3.2. The second part, also known as the translation mode, tells fopen whether to look at it as a binary or text file.
The fopen function will return the file pointer (FILE *) for your file.
This file pointer will be used whenever you work with the file, whether it is to read data from it or write new data back. Here are a few examples of opening a file in different modes:
FILE * f = fopen("file. txt", "w+t");
This will create and open the file called file. txt for reading and writing in text mode. Anything currently contained within file. txt will be destroyed.
FILE * f = fopen("file. bin", "rb");
Table 3.1 Access Modes for fopen()
Mode String Use
"r" Opens the file for reading only. If the file does not exist, a
new one will not be created and the call to fopen will fail.
"w" Opens a blank file for writing. If the target file contains
information, it is wiped out. Be careful when using this mode; you could wipe out a file.
"a" Opens a file for writing, much the same as the "w"
mode. However, "a" will not destroy the data in the file. Any new data will be added to the end of the file, also known as appending the data.
Opens an existing file for reading and writing. As with "r", the file must exist or fopen will fail. Works the same as "r+" with two major differences. If the file does not exist, fopen will create a blank one. If the file does exist, all data will be erased. Another function to be careful with. |
Opens the file for both reading and appending, or writing to the end of the file. |
r+" "w+" "a+" |
Table 3.2 Translation Modes for fopen()
Mode String Use
"t" "b" |
“t” stands for text mode. In text mode, each byte of the file will be treated as its own character. You will generally use this format if the file was or needs to be human-readable.
“b” is for binary. A file opened in binary mode will be treated as raw data and is generally not human-readable.
This will open the file file. bin for reading only, in binary mode. This is the most common mode used when loading and using 3D models within your games for several reasons. First, most model files are in binary form (but not all of them; the next chapter contains a file format that is text-based). Second, you rarely need to write to a model file when working with it in your game. Generally, you only need to load and use, rather than modify, the data it contains. Don’t forget to determine whether your file pointer is valid (not zero) before you try to use it.
Now that you have an open file, you need to be able to retrieve data from it. When working with model files, I like to read in the whole file as a chunk of unformatted data, and then sort it out later. To do this
you use fread().
fread is used for just what it sounds like: file reading. This function reads raw, unformatted data into an array. This is a quick and easy way to get data from the file into memory. The fopen function takes four parameters, as follows:
■ The first parameter is a pointer to a buffer in which to store the new data.
■ The second and third parameters will tell fread how much data you want. These parameters contain the number of bytes in a single “item,” or chunk, of data, and the number of items you would like to read, respectively. Be sure your buffer is large enough to hold all of your data; nothing is worse than overwriting an array and losing data.
■ The fourth and final parameter is a file pointer. The parameter should contain the file pointer that you created when you opened the file using fopen. You can check fread to make sure it read everything. The fread function returns the number of items actually read. If this return value and the third parameter of fread are equal, all the data was read successfully.
Here is a snippet of code that would read 100 bytes of unformatted data from one of the files you opened previously.
byte Buffer[100]; fread(Buffer, 1, 100, f);
This will store 100 bytes of data (one byte per item * 100 items) read from the file that f points to, in Buffer, an array of 100 bytes.
Just as fread is used for reading unformatted data from a file, fwrite is used for writing unformatted data. The parameters for fwrite are exactly the same as fread. The first parameter is the buffer containing the data to write to the file, the second and third parameters hold the sizes of the data to be written, and the fourth is a file pointer so fwrite knows which file to output the data to. Here is code to take the data you just read in (now stored in Buffer) and write it back to the file.
fwrite(Buffer, 1, 100, f);
Sometimes you need to read or write formatted data to a file. In these cases, fread and fwrite will not work. You will need to look toward functions such as fscanf and fprintf for formatted input and output.
These functions are used just like their text counterparts scanf/sscanf and printf/sprintf. Other functions are in place to read or write single variables to files such as fputc/fgetc (single characters) and fputs/fgets (strings).
All of this is very useful, but what if you need to write or read data to or from other parts of the file? You simply use the fseek() function. fseek will move your file pointer to a new place in the file. The fseek function takes three variables. The first parameter takes the file pointer you want to manipulate. The second argument takes the number of bytes you want to move, relative to the origin. The origin point is given by the third parameter. The origin can be one of three constant values:
■ SEEK_BEGIN places the origin at the beginning of the file. In this case, fseek will move the specified number of bytes from the start of the file.
■ The final constant for the origin is SEEK_SET. SEEK_SET will tell fseek to move the specified number of bytes from the current location in the file. After fseek is called, you may return to reading and writing your file. This time, the file is coming from or going to a new location.
When you are done reading or writing from a file, you need to close it. Failing to close a file leaves some memory allocated, thus creating a memory leak in your program. To close a file you opened with fopen, you use fclose. fclose() takes a single parameter, the file pointer of the file you want to close. You can also use the _fcloseall() to close all open files; it takes no parameters at all and returns the number of files it successfully closed. Here, you see the simple code necessary to close the file f once you are done working with it.
fclose(f);
Well, there you have it. A short introduction to file I/O using FILE *. This should be plenty to get you started loading your own 3D models. If you want to know more, or would like to learn about other methods of file I/O, I would suggest picking up a book on C or C++. Any good C/C++ book will contain a section pertaining to file I/O, whether covering FILE * or another method.
With that out of the way, and a review of file I/O complete, you are ready to move onto the hard part—deciphering the MD2 format.