Reading Simple Data From a Caligari TrueSpace FileThe Basics: There are two types of trueSpace file, *.scn and *.cob. These are Caligari Scenes, and Caligari Objects, respectively. The actual data in the files is not too complicated, because each file is a series of "chunks". Each chunk represents a different type of information. This allows us to scan through every chunk, only reading in the ones that are relevant. What We Need: The official specification for trueSpace files is 82 pages long. The font is 10pts. For anyone wanting to be able to import simple objects, this is very daunting. Even looking through the file for a while won't get you very far, there are masses of chunks, each more confusing than the last. But don't give up! It is actually very simple to read the objects, it is done in a few steps:
Getting Started: Create a new *.h file, call it something like "CaligariObjects.h". The first part is the inclusion guard. Then I typedef the BYTE symbol, which I like (although feel free to use the actual type name. The same goes for LPCTSTR).
Next you'll need a 'chunk header'. This comes at the top of every chunk in the file.
A stream of data follows each chunk header. Sometimes this is ints, and floats etc, but lots of chunks contain the same type of data, such as position and axis data. Here is the structure that is used to give a chunk it's name (not all chunks have names).
A lot of chunks that represent actual objects in the scenes can define their own axies.
A four by four matrix defines the position of chunks, but only the first three lines are saved, the last line is always assumed to be [0, 0, 0, 1].
Before we actually create an object class, we need to define a few simple data types. A face is basically a series of long ints, which represent positions in the vertex and UV arrays, there is usually only 3 or four pairs.
Some might say that representing a vertex in a struct is overkill, but it doesn't incur any memory cost, and due to the added simplicity you can actually get a significant speed increase. The same applies to the UV coord structure. This approach means that if you make a huge change, like adding the w coordinate to all vertices (for homogenous coordinates) you can easily change the code.
Now we create the actual TrueSpace object class. Later you could include the name and position, but this serves well as a starting point.
We've included the vector template, now typedef it for readability, and declare the big function we will use.
Now we create the loading function. Any file can contain a number of objects, so the best way to manage this is to use some sort of container for each object. Containers are a touchy subject, some people like the standard libraries, some people like MFC ones, and others use their own. In my opinion the standard library containers are that fastest and best. Now create a new *.cpp file, call it something like "CaligariObjects.cpp", and the following, including the header we've created, and starting the implementation of the function we've declared.
We first try to open the specified file.
Now we move the file pointer to the beginning of the chunk data.
Now we loop though every chunk, storing the data temporarily in 'ch'.
We only want "PolH" chunks, so here we test to see if we have one.
We will need a new caligari object, and we create it on the heap. It will later be added to the vector.
This section reads the chunk name data into a struct, but doesn't use it. Later on, you may want to name your objects so this has been included for detail.
I cannot find a way to easily implement the local axis system (if anyone can, please e-mail me), but we read the data anyway, just in case you want to use it in your own implementation.
Now we read the position. Try as I might, I am not good enough to translate this into simple data (like a translate x, y and z factor, a scale x, y, and z factor etc) so the objects I load are always at the origin, not rotated or scaled. If you find a way to make this matrix into simple values like those described, please e-mail me.
Don't be worried if this looks complex. First we read the number of vertices, then we get space for them (using the new operator), and then we get 'fread' to read them into our array, all at once.
Exactly the same as before applies to our UVs.
Here we get the number of faces and get memory for them, but they are slightly more difficult to read in, so we have to use another loop.
TrueSpace faces can be of different types. In the interest of simplicity, we will ignore the special 'hole' faces (which are holes in the previous face) however, when we do read these faces, we check their type, as some have extra data.
This is where we read the actual indices. Each one is actually a pair of 'longs' which are indices into the vertex and UV array, respectively.
We want to be able to read any version, so we must check the chunk version ID, and depending on the version, we might read extra data.
The easiest way to use this code is in an example. Create a new workspace, call it 'test' or something, and then add the CaligariObjects.h and CaligariObjects.cpp files to it.
Now you have the simple objects. They can very easily be drawn and implemented, but that would add too much to the tutorial. If anyone can find solutions to the problems concerning local axis and (the serious problem) the position/translation/scale matrix, please e-mail me. |