www.xbdev.net xbdev - software development
Tuesday September 17, 2019
home | about | contact | Donations

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);

}

 

 

Output: debug.txt
xof

 

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>;

<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 }

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.