X File (DirectX 3D File Format) Loader
by bkenwright@xbdev.net
Now the .x file format is one of the most complex 3D file formats... but its
also one of the best. It allows you to store a number of specific
information in the 3D file....ranging from simple meshes, to bone
animation....to materials etc.
There are two ways to import .x files into your game!...or demo... the first
is to use the .x file parsers/loaders that come with directx... the second is to
use your own .x file loader, which is what where going to do here :)
First thing first....I'll start with the text file version of the .x file
format, as this will allow me to manually edit my demo's .x file.....
But once you understand how the hierarchy is set out in a .x file, and of
course how the skinning works animation is done - then you can implement the .x
binary version without to much difficulty. Now I think the .x binary
version will be a lot easier, and you won't be checking for all sorts of text
comments or errors...which are possible in the .txt file. One other thing
- the first version of the .x txt loader will be very powerful, but of course
it's a sort of tutorial in itself. So I'll make some assumptions...which
are the file parsing - I'll probably create a file parser/compiler for a later
version if I get time - but of course I'm limited by time and money - so if
donations will always help ;)
Its not going to be pretty this code... but I'll do my best to keep it simple
and sweet....so at the end, you'll feel like that .x file format is your best
friend. As I always say, its good to know how to do things without relying
on the directx api's... even if you choose to use them.
Here we goooOoooooo..........
Using 3D Milkshape and 3D studio max with a number of exporters...I exported
a number of .x files.... a simple cube... and had a look at it... and this is
what it looked like:
CubeA.x |
xof 0303txt 0032
template XSkinMeshHeader {
<3cf169ce-ff7c-44ab-93c0-f78f62d172e2>
WORD nMaxSkinWeightsPerVertex;
WORD nMaxSkinWeightsPerFace;
WORD nBones;
}
template VertexDuplicationIndices {
<b8d65549-d7c9-4995-89cf-53a9a8b031e3>
DWORD nIndices;
DWORD nOriginalVertices;
array DWORD indices[nIndices];
}
template SkinWeights {
<6f0d123b-bad2-4167-a0d0-80224f25fabb>
STRING transformNodeName;
DWORD nWeights;
array DWORD vertexIndices[nWeights];
array FLOAT weights[nWeights];
Matrix4x4 matrixOffset;
}
Frame Scene_Root {
FrameTransformMatrix {
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;
}
.................
Frame {
FrameTransformMatrix {
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;
}
Mesh {
24;
-0.500000;-0.500000;0.000000;,
........................
-0.500000;-0.500000;0.000000;,
-0.500000;-0.500000;1.010000;,
-0.500000;0.500000;1.010000;;
12;
3;0,2,3;,
.................
3;22,23,20;;
MeshNormals {
24;
0.000000;0.000000;-1.000000;,
.................
-1.000000;0.000000;0.000000;,
-1.000000;0.000000;0.000000;;
12;
3;0,2,3;,
..................
3;22,23,20;;
}
VertexDuplicationIndices {
24;
8;
0,
1,
.............
6;
}
MeshMaterialList {
1;
12;
0,
.................
Material {
0.721569;0.600000;0.894118;1.000000;;
0.000000;
0.721569;0.600000;0.894118;;
0.000000;0.000000;0.000000;;
}
}
}
}
}
AnimationSet {
Animation {
AnimationKey {
4;
2;
0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;,
16160;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;;
}
{ Scene_Root }
}
} |
CubeB.x |
xof 0303txt 0032
template XSkinMeshHeader {
<3cf169ce-ff7c-44ab-93c0-f78f62d172e2>
WORD nMaxSkinWeightsPerVertex;
WORD nMaxSkinWeightsPerFace;
WORD nBones;
}
template VertexDuplicationIndices {
<b8d65549-d7c9-4995-89cf-53a9a8b031e3>
DWORD nIndices;
DWORD nOriginalVertices;
array DWORD indices[nIndices];
}
template SkinWeights {
<6f0d123b-bad2-4167-a0d0-80224f25fabb>
STRING transformNodeName;
DWORD nWeights;
array DWORD vertexIndices[nWeights];
array FLOAT weights[nWeights];
Matrix4x4 matrixOffset;
}
Frame Box01 {
FrameTransformMatrix {
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.064346,-0.466481,0.018855,1.000000;;
}
Mesh {
8;
-0.500000;0.000000;-0.500000;,
0.500000;0.000000;0.500000;,
..............
12;
3;0,1,2;,
.............
3;4,2,7;;
MeshNormals {
6;
0.000000;-1.000000;0.000000;,
................
12;
3;0,0,0;,
................
}
MeshMaterialList {
1;
12;
0,
...............
Material {
0.894118;0.600000;0.721569;1.000000;;
0.000000;
0.894118;0.600000;0.721569;;
0.000000;0.000000;0.000000;;
}
}
}
} |
Ackkk.... one exporter gives you some Animation data even though there is
none in your file...and some exporters give you template definitions...e.g.
"template XSkinMeshHeader" and some don't.... so where going to have to be
careful when we import this data... and try and not be tricked :)
The basic thing to spot is the starting "{" and ending "}" and of course the
description....e.g. "Material", "Mesh", "MeshMaterialList" ..etc.
The tricky part is the fact that the data is usually arranged in a hierarchy...
which of course we'll use when we create structured characters with arms and
legs...
Lets start simple and work from there.... so a simple starting code would
look like this:
Code:
Download |
#include
<stdio.h> // use for file input/output
void
debug(char* str)
{
FILE* fp = fopen("debug.txt", "a+");
fprintf(fp, "%s", str);
fclose(fp);
}
void
main()
{
char buff[500]; //
large temp buffer;
FILE* f = fopen("cubeA.x", "r");
fread(buff, 1, 3, f); // 3 times 1 ascii
characters
fclose(f);
char aa[100];
sprintf(aa, "%c%c%c", buff[0], buff[1], buff[2]);
// display our 3 characters
debug(aa);
} |
Well its not much... but its a starting point... our journey has to begin
somewhere. And rather than give you hundreds of lines of code which I've
pre-written... I think we should all start at zero...heheh... I have to make you
suffer as well :)
So first things first... I've just read in the first 3 bytes of the .x file
format... and wrote them to a .txt file.... make sure we can open our .x file
okay. We should always check that the header of our .x file has "xof
0303txt" ... which tells us its a .x file...and its version and type...which is
of course txt. Keeping the code to its simplest...which I normally
do...I'll cut most of the error checking out... but of course you can always add
it in later... as this is a tutorial remember.
I've done the checking for .x file version 0303, but there are a few little
tiny differences in version 0302 which I came to notice later on.....but even
though I check for version 0303...I checked all the code with later and earlier
versions, and they seem to work no problem.
Lets read in the first few characters...and check its an okay file.... also,
lets do some very basic checking at load time..
Code:
Download |
#include
<stdio.h> // use for file input/output
#include
<string.h> // useful string functions such as
strlen(..)
void
debug(char* str)
{
FILE* fp = fopen("debug.txt", "a+");
fprintf(fp, "%s", str);
fclose(fp);
}
void
LoadXFile(char* szFileName)
{
char buff[500] = {'\0'};
// large temp buffer;
FILE* f = fopen(szFileName,
"r");
fread(buff, 1, strlen("xof 0303txt") , f);
fclose(f);
// Very simple error checking at the start of the
file loader.
// strcmp does not include the null character at
the end of strings.
if( strcmp(buff, "xof 0303txt") != 0 )
debug("Error - This file is not a compliant .x txt file");
// Write out read in data to our debug file
debug(buff);
}
void
main()
{
LoadXFile("cubeA.x");
} |
Output: debug.txt |
xof 0303txt |
Okay, well I've put the simple loading in another function... as we can build
towards making a re-usable object. Now this is where I make a decision...
I could do simple C functions...and sort of work from there... or do a CLoaderX
class and keep everything in that nice tidy class... which would be a lot better
in the future I think.
So another fork in the road... I've decided to work with a class... and keep our x
loader code all together in there. Eventually allowing us to plug our .x
file loader class into any game/demo/app we like in the future without much
problem. As easy as 'a'...'b'...'c'....
Hold on where going C++ .....
Code: Download |
#include
<stdio.h> // use for file input/output
#include
<string.h> // useful string functions such as
strlen(..)
void
debug(char* str)
{
FILE* fp = fopen("debug.txt", "a+");
fprintf(fp, "%s", str);
fclose(fp);
}
class
CXTxtLoader // Txt is for the txt 3D .x file
format
{
public:
bool Create(char*
szFileName)
{
buff[200];
FILE* f = fopen("cubeA.x", "r");
fread(buff, 1, strlen("xof 0303txt") , f);
fclose(f);
// Very simple error checking at the
start of the file loader.
// strcmp does not include the null
character at the end of strings.
if( strcmp(buff, "xof 0303txt")
!= 0 )
{
debug("Error - This file is not a compliant .x txt file");
return
false; // return
false - as there was a problem
};
return
true; // return true, as all went okay
}//
End of Create(..)
void Release()
{
// We will put any tidy up code in
here later on if we need to
}//
End of Release()
};
/**********************************************************************************/
/* Program Entry
Point */
/**********************************************************************************/
void
main()
{
CXTxtLoader l;
l.Create("cubeA.x");
l.Release();
} |
I've made some simple assumptions for our class from the start.... you call
"Create(..)" at the start with your filename...and "Release()" at the end when
you've finished with the loader... you could use the constructor to load the
file.... but you can't return a value to check if an error occurred. And
by using Release().. it allows us to release the resources when ever we want to.
Lets think about this.... if I look at the data... and of course the file
spec... we see that new identifiers are on new lines...e.g.
<Identifier> [name] {
<member 1>;
弯span>
<member n>;
}
So we should really read in line, by line and strip leading and ending white
spaces.
Lets do some functions.... GetLine(..), StringLeftTrim(..).. and
StringRightTrim(..)
[Parser 101]
At this point I made the loader read in data line by line,
and assumed that it had newline identifiers in certain places, which isn't part
of the .x file spec - so at a later date...when I rewrite a release .x loader
library maybe, I will write a text parser which will look for particular
identifier's, such as open { or close } brackets....commas...etc
Hmmmm... well I played around with the code... and of course I commented it
loads and loads.... I think its starting to show shape with those 3 functions
added in. As now it checks the file... and then reads in line after line
and writes it out to our debug.txt file... skipping comments and removing spaces
that might have been inserted.
Take your time with the code... you can download it if you prefer and twiddle
with it...
Code:
DownloadSource |
/**********************************************************************************/
/* main.cpp - .x txt file parser
tutorial */
/**********************************************************************************/
/////////////////////////////////////////////////////////////////////////////////
#include
<stdio.h> // use for file input/output
#include
<string.h> // useful string functions such as
strlen(..)
#include
<fstream> // used for reading line by line ->
towards c++
////////////////////////////////////////////////////////////////////////////////////
void
debug(char* str)
{
FILE* fp = fopen("debug.txt", "a+");
fprintf(fp, "%s\n", str);
fclose(fp);
}
////////////////////////////////////////////////////////////////////////////////////
#define
BUFFER_SIZE 5000
class
CXTxtLoader // Txt is for the txt 3D .x file
format
{
protected:
char m_szBuffer[BUFFER_SIZE];
// Large temporary buffer for reading data line by
line
FILE* m_fp;
public:
bool Create(char*
szFileName)
{
m_fp = fopen(szFileName, "r");
// Check that we opened the file okay
if( m_fp == NULL )
{
debug("Error opening the file");
return
false;
}
// Get the very first line of our .x
file
Getline();
if( strcmp(m_szBuffer, "xof
0303txt 0032") != 0 )
{
debug("Error - This file is not a compliant .x txt file");
debug(m_szBuffer);
return
false;
}
// For debugging...lets dump all the
lines to our debug file
// check that its all okay :)
while( Getline() )
{
debug(m_szBuffer);
}
fclose(m_fp);
return
true; // return true, as all went okay
}//
End of Create(..)
void Release()
{
// We will put any tidy up code in
here later on if we need to
}//
End of Release()
////////////////////////////////////////////////////////////////////////////////
char* Getline()
{
char *str = NULL;
// Check that we have a valid file
pointer
if(m_fp==NULL)
{
debug("Error - Invalid file pointer - CXTxtLoader::Getline()");
return
false;
}
while(1)
{
// Read a line in from the file,
if it returns NULL, then we've reached
// the end of the file..hence we
finish.
if ((str =
fgets(m_szBuffer,BUFFER_SIZE, m_fp))==NULL)
break;
int iLength = strlen(str);
// Length of our string
int iIndex = 0;
// Index into our string we've read in
// Skip white spaces
while(isspace(str[iIndex]))
iIndex++;
// Check that the line wasn't
just loads of white spaces...
if( (iLength - iIndex) <=
0 )
continue;
// Here's something you should
note, now I'm going to copy the string
// back into itself, but without
the white spaces... as its back into itself,
// you would usually use a temp
buffer ... but memmov(..) ensures that we
// don't overwrite or old data
memmove( (void*)m_szBuffer,
(void*)&m_szBuffer[iIndex], (iLength-iIndex)+1
);
// +1 on the end, is for the
NULL at the end of the string :)
str = m_szBuffer; // Set
our temp string pointer back to the beginning
// of the
string.
iLength = strlen(str); // Update
our string length
// Now when we use gets(..) to
read our line in...it includes the "\n"
// character, which we'll strip
off now.
str[iLength-1] = 0;
// Strip comments from our file
// Now there are two types of
commments.... either "//" or "#"
// So this is where we do the
stripping.
// ** -- ** -- ** I've only
tested for "//" at this early stage
// so the code is easy to
follow...we'll add a loop in later to test for
// "#" comments as well.
{
// Check our string to see
if its got a comment in it
char *comment_string
= strstr(str, "//");
// We have no comments in
the string
if( comment_string
== NULL )
break;
// This is a valid line.
// Get the offset into the
line of where the comment begins
int cindex = (int)(comment_string
- str);
// comment at beginning
then continue
if (cindex == 0)
continue;
// this line is a comment, ignore completely, get
another
else
{
// Comment at end,
strip it, insert null where it begins
comment_string[0] = 0;
// Remember
comment_string is a pointer to our original string, as is
// offset to the
point where the comment identifier is.
break;
} // End else
} // End of comment stripping
StringLeftTrim(str);
StringRightTrim(str);
}// End while loop
// We reached the end of file, hence
we return NULL
if( str == NULL )
return (NULL);
return (str);
}
// end Getline
////////////////////////////////////////////////////////////////////////////////
char *StringLeftTrim(char
*str)
{
// Strip whitespace from left side,
note is destructive
int iIndex = 0;
int iLength = strlen(str);
if (!str || iLength == 0)
return(str);
// Strip whitespace by advancing
pointer
while(isspace(str[iIndex]) &&
iIndex < iLength)
str[iIndex++] = 0; // not needed
actually
// Copy string to left
memmove((void *)str, (void
*)&str[iIndex], (iLength - iIndex)+1);
// Now return pointer our modified
string :)
return(str);
}
// End StringLeftTrim
////////////////////////////////////////////////////////////////////////////////
char *StringRightTrim(char
*str)
{
// Strip whitespace from right side,
note is destructive
int iLength = strlen(str);
if (!str || iLength == 0)
return(str);
// Index to end of string
int iIndex = iLength - 1;
// -1 because of the NULL terminator
// Strip whitespace by overwriting
nulls
while( isspace(str[iIndex]) &&
iIndex >= 0)
str[iIndex--] = 0;
// String doens't need to be moved, so
simply return pointer
// Well all was nice an easy :)
return(str);
}
// End of StringRightTrim
};
// End of CXTxtLoader class
/**********************************************************************************/
/* Program Entry
Point */
/**********************************************************************************/
void
main()
{
CXTxtLoader l;
l.Create("cubeA.x");
l.Release();
} |
So an example of what the output file format looks like:
Output: debug.txt |
template XSkinMeshHeader {
<3cf169ce-ff7c-44ab-93c0-f78f62d172e2>
WORD nMaxSkinWeightsPerVertex;
WORD nMaxSkinWeightsPerFace;
WORD nBones;
}
template VertexDuplicationIndices {
<b8d65549-d7c9-4995-89cf-53a9a8b031e3>
DWORD nIndices;
DWORD nOriginalVertices;
array DWORD indices[nIndices];
}
template SkinWeights {
<6f0d123b-bad2-4167-a0d0-80224f25fabb>
STRING transformNodeName;
DWORD nWeights;
array DWORD vertexIndices[nWeights];
array FLOAT weights[nWeights];
Matrix4x4 matrixOffset;
}
Frame Scene_Root {
FrameTransformMatrix {
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;
}
Frame Box01 {
FrameTransformMatrix {
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.064346,0.018855,-0.466481,1.000000;;
}
Frame {
FrameTransformMatrix {
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;
}
Mesh {
24;
-0.500000;-0.500000;0.000000;,............etc etc. etc...... |
The real thing to notice, is that white spaces have been removed!.. we have a
easy way of reading line after line... giving us so much power!.....waahhhooo....
so now we can start dissecting this baby!
We can see that the start of each line, begins with a descriptor... well not
all the lines... what I mean is... we can find the Mesh data by searching for
the "Mesh" keyword... and we can determine templates using the "template"
keyword... and "Frame" for frames etc.... so we should put together a few
#defines so we can list which one's where going to look for
So I looked around on the net and found a list of the .x file Template
names... and listed them below... of course we won't be using them all.... its
always good to include them for later use :)
It took me ages to type all these in.... but it saves a lot of time later...
and of course will hopefully allow us some flexibility later on....
#define
X_TEMPLATE 0xFF //
template
#define
X_HEADER 0x1 //
Header
#define
X_VECTOR 0x2 //
Vector
#define
X_COORDS2D 0x3 //
Coords2d
#define
X_QUATERNION 0x4 //
Quaternion
#define
X_MATRIX4X4 0x5 //
Matrix4x4
#define
X_COLORRGBA 0x6 //
ColorRGBA
#define
X_COLORRGB 0x7 //
ColorRGB
#define
X_INDEXEDCOLOR 0x8 //
IndexedColor
#define
X_BOOLEAN 0x9 //
Boolean
#define
X_BOOLEAN2D 0x10 //
Boolean2d
#define
X_MATERIAL 0x11 //
Material
#define
X_TEXTUREFILENAME 0x12 //
TextureFilename
#define
X_MESHFACE 0x13 //
MeshFace
#define
X_MESHFACEWRAPS 0x14 //
MeshFaceWraps
#define
X_MESHTEXTURECOORDS 0x15 //
MeshTextureCoords
#define
X_MESHNORMALS 0x16 //
MeshNormals
#define
X_MESHVERTEXCOLORS 0x17 //
MeshVertexColors
#define
X_MESHMATERIALLIST 0x18 //
MeshMaterialList
#define
X_MESH 0x19 //
Mesh
#define
X_FRAMETRANSFORMMATRIX 0x20 //
FrameTransformMatrix
#define
X_FRAME 0x21 //
Frame
#define
X_FLOATKEYS 0x22 //
FloatKeys
#define
X_TIMEDFLOATKEYS 0x23 //
TimedFloatKeys
#define
X_ANIMATIONKEY 0x24 //
AnimationKey
#define
X_ANIMATIONOPTIONS 0x25 //
AnimationOptions
#define
X_ANIMATION 0x26 //
Animation
#define
X_ANIMATIONSET 0x27 //
AnimationSet
struct
stXDefines
{
unsigned int
ID;
char* str;
};
stXDefines g_Defines[] =
{
{
X_TEMPLATE, "template" },
{
X_HEADER, "Header" },
{
X_VECTOR, "Vector" },
{
X_COORDS2D, "Coords2d" },
{
X_QUATERNION, "Quaternion" },
{
X_MATRIX4X4, "Matrix4x4" },
{
X_COLORRGBA, "ColorRGBA" },
{
X_COLORRGB, "ColorRGB" },
{
X_INDEXEDCOLOR, "IndexedColor" },
{
X_BOOLEAN, "Boolean" },
{
X_BOOLEAN2D, "Boolean2d" },
{
X_MATERIAL, "Material" },
{
X_TEXTUREFILENAME, "TextureFilename"
},
{
X_MESHFACE, "MeshFace" },
{
X_MESHFACEWRAPS, "MeshFaceWraps" },
{
X_MESHTEXTURECOORDS, "MeshTextureCoords" },
{
X_MESHNORMALS, "MeshNormals" },
{
X_MESHVERTEXCOLORS, "MeshVertexColors" },
{
X_MESHMATERIALLIST, "MeshMaterialList" },
{
X_MESH, "Mesh" },
{
X_FRAMETRANSFORMMATRIX, "FrameTransformMatrix"
},
{
X_FRAME, "Frame" },
{
X_FLOATKEYS, "FloatKeys" },
{
X_TIMEDFLOATKEYS, "TimedFloatKeys"
},
{
X_ANIMATIONKEY, "AnimationKey" },
{
X_ANIMATIONOPTIONS, "AnimationOptions" },
{
X_ANIMATION, "Animation" },
{
X_ANIMATIONSET, "AnimationSet" }
}; |
If the line doesn't begin with one of the defined strings... then its data..
and hence we just skip over it... as I'm going to try and design the parser so
that we can skip over parts of the data that we don't need. For example,
if we only want to read in the Mesh data, the we can just check for the "Mesh"
string and read it in for example... well that's what I hope.... we'll see how it
goes as we progress.
Each first line identify's the type of template that it belongs to.... so I
did a small function called "GetType(...)" which will check the first word
against our list of words in "g_Defines"
that I defined above, if it found it, it will return the index into the
list......else it will return false;
So I modified a few lines, so now it prints out all the lines that have
related "index" strings from our list:
Code: DownloadSourceCode |
//
... defines are shown above .....
#define
BUFFER_SIZE 5000
class
CXTxtLoader // Txt is for the txt 3D .x file
format
{
protected:
char m_szBuffer[BUFFER_SIZE];
// Large temporary buffer for reading data line by
line
FILE* m_fp;
public:
bool Create(char*
szFileName)
{
m_fp = fopen(szFileName, "r");
// Check that we opened the file okay
if( m_fp == NULL )
{
debug("Error opening the file");
return
false;
}
// Get the very first line of our .x
file
Getline();
if( strcmp(m_szBuffer, "xof
0303txt 0032") != 0 )
{
debug("Error - This file is not a compliant .x txt file");
debug(m_szBuffer);
return
false;
}
unsigned
int iTemplateIndex;
// For debugging...lets dump all the
lines to our debug file
// check that its all okay :)
while( Getline() )
{
debug(m_szBuffer);
if( GetType( &iTemplateIndex
) )
{
char buf[600];
sprintf(buf,
"\t\t********************%s******************",
g_Defines[iTemplateIndex].str);
debug(buf);
}
}
fclose(m_fp);
return
true; // return true, as all went okay
}//
End of Create(..)
void Release()
{
// We will put any tidy up code in
here later on if we need to
}//
End of Release()
////////////////////////////////////////////////////////////////////////////////
// Return true, if the read in line contains a
template pattern identifier
bool GetType(unsigned
int* iTempInx)
{
char szBuffer[BUFFER_SIZE];
int iIndex=0;
while( (m_szBuffer[iIndex] !=
'\n') &&
(m_szBuffer[iIndex] != ' ' ) &&
(m_szBuffer[iIndex] != 0 ) )
{
szBuffer[iIndex] = m_szBuffer[iIndex];
iIndex++;
}
szBuffer[iIndex] = 0; // Null
terminate our new string.
// szBuffer should contain a copy of
the first word on the line
// So lets check if its in our list
int iTemplates =
sizeof(g_Defines)/sizeof(stXDefines);
int iTemplateIndex = 0;
for(int
i=0; i<iTemplates; i++)
{
if(
strcmp(g_Defines[i].str, szBuffer) == 0 )
{
iTemplateIndex = i;
break;
}
}
if( iTemplateIndex > 0 )
{
*iTempInx = iTemplateIndex;
return
true;
}
return
false;
}
////////////////////////////////////////////////////////////////////////////////
char* Getline()
{
char *str = NULL;
// Check that we have a valid file
pointer
if(m_fp==NULL)
{
debug("Error - Invalid file pointer - CXTxtLoader::Getline()");
return
false;
}
while(1)
{
// Read a line in from the file,
if it returns NULL, then we've reached
// the end of the file..hence we
finish.
if ((str =
fgets(m_szBuffer,BUFFER_SIZE, m_fp))==NULL)
break;
int iLength = strlen(str);
// Length of our string
int iIndex = 0;
// Index into our string we've read in
// Skip white spaces
while(isspace(str[iIndex]))
iIndex++;
// Check that the line wasn't
just loads of white spaces...
if( (iLength - iIndex) <=
0 )
continue;
// Here's something you should
note, now I'm going to copy the string
// back into itself, but without
the white spaces... as its back into itself,
// you would usually use a temp
buffer ... but memmov(..) ensures that we
// don't overwrite or old data
memmove( (void*)m_szBuffer,
(void*)&m_szBuffer[iIndex], (iLength-iIndex)+1
);
// +1 on the end, is for the
NULL at the end of the string :)
str = m_szBuffer; // Set
our temp string pointer back to the beginning
// of the
string.
iLength = strlen(str); // Update
our string length
// Now when we use gets(..) to
read our line in...it includes the "\n"
// character, which we'll strip
off now.
str[iLength-1] = 0;
// Strip comments from our file
// Now there are two types of
commments.... either "//" or "#"
// So this is where we do the
stripping.
// ** -- ** -- ** I've only
tested for "//" at this early stage
// so the code is easy to
follow...we'll add a loop in later to test for
// "#" comments as well.
{
// Check our string to see
if its got a comment in it
char *comment_string
= strstr(str, "//");
// We have no comments in
the string
if( comment_string
== NULL )
break;
// This is a valid line.
// Get the offset into the
line of where the comment begins
int cindex = (int)(comment_string
- str);
// comment at beginning
then continue
if (cindex == 0)
continue;
// this line is a comment, ignore completely, get
another
else
{
// Comment at end,
strip it, insert null where it begins
comment_string[0] = 0;
// Remember comment_string
is a pointer to our original string, as is
// offset to the
point where the comment identifier is.
break;
} // End else
} // End of comment stripping
StringLeftTrim(str);
StringRightTrim(str);
}// End while loop
// We reached the end of file, hence
we return NULL
if( str == NULL )
return (NULL);
return (str);
}
// end Getline
////////////////////////////////////////////////////////////////////////////////
char *StringLeftTrim(char
*str)
{
// Strip whitespace from left side,
note is destructive
int iIndex = 0;
int iLength = strlen(str);
if (!str || iLength == 0)
return(str);
// Strip whitespace by advancing
pointer
while(isspace(str[iIndex]) &&
iIndex < iLength)
str[iIndex++] = 0; // not needed
actually
// Copy string to left
memmove((void *)str, (void
*)&str[iIndex], (iLength - iIndex)+1);
// Now return pointer our modified
string :)
return(str);
}
// End StringLeftTrim
////////////////////////////////////////////////////////////////////////////////
char *StringRightTrim(char
*str)
{
// Strip whitespace from right side,
note is destructive
int iLength = strlen(str);
if (!str || iLength == 0)
return(str);
// Index to end of string
int iIndex = iLength - 1;
// -1 because of the NULL terminator
// Strip whitespace by overwriting
nulls
while( isspace(str[iIndex]) &&
iIndex >= 0)
str[iIndex--] = 0;
// String doens't need to be moved, so
simply return pointer
// Well all was nice an easy :)
return(str);
}
// End of StringRightTrim
};
// End of CXTxtLoader class
/**********************************************************************************/
/* Program Entry
Point */
/**********************************************************************************/
void
main()
{
CXTxtLoader l;
l.Create("cubeA.x");
l.Release();
} |
The typical output at this simple stage is shown below:
Output: debug.txt |
template SkinWeights {
********************template******************
<6f0d123b-bad2-4167-a0d0-80224f25fabb>
STRING transformNodeName;
DWORD nWeights;
array DWORD vertexIndices[nWeights];
array FLOAT weights[nWeights];
Matrix4x4 matrixOffset;
********************Matrix4x4******************
}
Frame Scene_Root {
********************Frame******************
FrameTransformMatrix {
********************FrameTransformMatrix******************
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;
}
Frame Box01 {
********************Frame******************
FrameTransformMatrix {
********************FrameTransformMatrix******************
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.064346,0.018855,-0.466481,1.000000;;
}
Frame {
********************Frame******************
FrameTransformMatrix {
********************FrameTransformMatrix******************
1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;
}
Mesh {
********************Mesh******************
24;
-0.500000;-0.500000;0.000000;,
0.500000;-0.500000;0.000000;,
... etc etc etc.... |
The thing to notice, is that I've added the comments ...e.g.
"*********Mesh**********" on the following line, each time a template name is
found from our list. Of course it still has some things that we need to
fix, but where going along okay up to now :)
The next thing we have to check for is opening and closing curly brackets
"{"..."}" ... And possibly a name for the Mesh or Frame... such as "Box1"
Its worth getting a hold of the .x file spec in word format - then you can
check various things about it...for example, how the data for the 'Mesh'
template is layed out....its order as you say...for example, it starts with a
number defining how many faces it has...followed by a list of faces...etc....you
get the idea.
{ Continued with Part II }
|