www.xbdev.net xbdev - software development
Tuesday January 22, 2019
home | about | contact | Donations

Loading X File Mesh

by bkenwright@xbdev.net

 

 

Once you get your head around how exactly the Direct X File format works, you then have to think up a method of writing a loader!  But DirectX gives us a hand with this by supplying some pre-made x file parser API's which make reading in our .x file data a piece of cake.

 

Parsing X File with the DirectX API

 

The very first thing we need to get familar with, is the API's...which API's we'll use and what we'll use them for:

 

With the first few pieces of code, well I wrote them a long time ago,...and used the command prompt to compile them.  Of course I'm still using Visual Studio, so if you download them...you can simply create a blank project and import the .cpp file and compile it that way.

 

But once you understand how it all clicks together, you'll be writing your own reusable .x loader....and of course creating super cool games which make your friends drewl :)

 

Starting simple!  Its the best way...especially with tutorials.  So

 

Code: DownloadSourceCode

 

#include <windows.h>

#include <stdio.h>

 

#include <rmxftmpl.h>                // D3DRM_XTEMPLATES definition

 

#define ULONG_PTR unsigned long

 

#pragma comment(lib, "D3d8.lib")     //DirectX 8 Lib Files

#pragma comment(lib, "D3dx8.lib")

#include <d3dx8.h>                   //DirectX 8 Header Files

 

#include <Dxfile.h>

#pragma comment(lib, "D3dxof.lib")

 

typedef struct sFrame

{

} sFrame;

 

void ParseXFileData(IDirectXFileData* pData, sFrame* ParentFrame)

{

      DWORD dwSize;

      pData->GetName(NULL, &dwSize);

 

      if( dwSize )

      {

            char str[100];

            pData->GetName( str, &dwSize );

 

            //*  Ba-Bing - For each Data Type in your .x file - e.g. Mesh, Frame etc *//

            //*  a message box will pop up and tell you.                             *//

            MessageBox(0, str, str, 0);

      }

}// End of ParseXFileData(..)

 

bool ParseXFile(char* filename)

{

 

      IDirectXFile            *pDXFile = NULL;

      IDirectXFileEnumObject  *pDXEnum = NULL;

      IDirectXFileData        *pDXData = NULL;

     

      DirectXFileCreate(&pDXFile);              // requires Dxfile.h and D3dxof.lib

                                                // return 0 if okay

 

      // Tell direct3D which templates to use

      pDXFile->RegisterTemplates( (LPVOID)D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES );

      // Need to mention "rmxftmpl.h"

 

      pDXFile->CreateEnumObject( (LPVOID)filename, DXFILELOAD_FROMFILE, &pDXEnum );

 

      while( pDXEnum->GetNextDataObject(&pDXData) == 0 )

      {

            // Call our custom function that we defined above.

            ParseXFileData( pDXData, NULL );

      }

 

      // Tidy up

      pDXEnum->Release();

      pDXFile->Release();

 

      return true;

}// End of ParseXFile(..)

 

 

 

// Program entry point.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)

{

      ParseXFile("bomb.x");

 

    return 0;

}// End of WinMain(..)

 

Well that code above is what you call the simple code!...My God!....yup...DirectX gives you a lot to swallow in one go doesn't it.  But if you run that code, what happens is a messagebox will pop up for each data type.... when I say data type, I mean, Mesh...Material...MaterialList...etc

 

ParseXFile(..) is the simple setup function...passing the filename and template details to DirectX.  The parsing of our data is done in ParseXFileData(..).  When I say parse, I mean the data is extracted and put into some sort of data structure which we've defined already.  For example if we wanted to just read vertices in...then we would check in that function call for the 'Mesh' identifier...and read the data in and save it.  Then when the ParseXFile(..) function has finished, we have our data!  Ready to use.

 

Recursion!  Recursion...recursion... Now if your unfamiliar with the principle of functions calling themselves...well you'll have to get used to it.  Because the .x file format uses hierarchy...and the simplest way to parse hierarchy of files is by calling functions with themselves...and passing a pointer to the parent....hmmm....

 

Where the data would for example be:

 

struct stFrame

{

      char name[100];    // text name

      stFrame* pChild;   // pointer to child (if any) else null for no child

};

 

That's a typical example of a structure data which can have children.

 

Maybe its just me....but when I first stared out on my DirectX X file format adventures.... which seems like a very long time ago now - the keyword 'Frame' always used to confuse me....as I'd done a lot of keyframe animation stuff before then...and a Frame didn't mean a data object...but something to do with animation.... and it took me ages to hammer it into my head, that a 'Frame' is just a group of data!  Sort of a box....and inside this box you can have 'Mesh' data...or 'Matrix' data etc...or even more 'Frame's...which are children.....  Just thought I'd mention that here :)  But you'll soon come to grips with things once you get your feet wet and try out some coding examples ;)

 

 

Well the above code only displays a mesagebox for the top objects!  Not nested one's....as each Mesh or Frame usually has nested objects...which includes Material information...or Matrix Position etc....which I'll add in now:

 

Code: DownloadSourceCode

#include <windows.h>

#include <stdio.h>

 

#include <rmxftmpl.h>             // D3DRM_XTEMPLATES definition

 

#define ULONG_PTR unsigned long

 

#pragma comment(lib, "D3d8.lib")  // DirectX 8 lib files

#pragma comment(lib, "D3dx8.lib")

#include <d3dx8.h>                // DirectX 8 header file

 

#include <Dxfile.h>

#pragma comment(lib, "D3dxof.lib")

 

typedef struct sFrame

{

} sFrame;

 

void ParseXFileData(IDirectXFileData* pData, sFrame* ParentFrame)

{

      DWORD dwSize;

      pData->GetName(NULL, &dwSize);

 

      if( dwSize )

      {

            char str[100];

            pData->GetName( str, &dwSize );

 

            MessageBox(0, str, str, 0);

      }

 

      const GUID* pType = NULL;

      pData->GetType( &pType );

      // pType could now be set to a number of type, here are the most important ones

      // TID_D3DRMFrame

      // TID_D3DRMMesh

      // TID_D3DRMaterial

      // TID_D3DRMFrameTransformMatrix

 

      // TID_D3DRMAnimationSet

      // TID_D3DRMAnimationKey

      // TID_D3DRMAnimation

      // A list of them can be found in rmxfguid.h

     

      // Check if theres any embeded object now...so we don't just get the

      // top layer.

      IDirectXFileObject* pSubObj;

      while( pData->GetNextObject(&pSubObj) == 0 )

      {

            IDirectXFileData *pSubData = NULL;

            // Made up of 2 parts!...one that has more babies..I mean

            // children, and another that is just on its own.

            /*

            IDirectXFileDataReference *pDataRef = NULL;

            if( pSubObj->QueryInterface(IID_IDirectXFileDataReference, (void**)&pDataRef) == 0)

                  if(pDataRef->Resolve(&pSubData))

                        ParseXFileData(pSubData, NULL);

            */

           

            // This will loop deeper

            if( pSubObj->QueryInterface( IID_IDirectXFileData, (void**)&pSubData) == 0)

            {

                  ParseXFileData(pSubData, NULL);

                  pSubData->Release();

            }

           

            pSubObj->Release();

      }// End fo While loop

}// End of ParseXFileData(..)

 

bool ParseXFile(char* filename)

{

      IDirectXFile                  *pDXFile = NULL;

      IDirectXFileEnumObject  *pDXEnum = NULL;

      IDirectXFileData        *pDXData = NULL;

     

      DirectXFileCreate(&pDXFile);              // requires Dxfile.h and D3dxof.lib

                                                // return 0 if okay

 

      // Tell direct3D which templates to use

      pDXFile->RegisterTemplates( (LPVOID)D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES );

      // Need to mention "rmxftmpl.h"

 

      pDXFile->CreateEnumObject( (LPVOID)filename, DXFILELOAD_FROMFILE, &pDXEnum );

 

      while( pDXEnum->GetNextDataObject(&pDXData) == 0 )

      {

            ParseXFileData( pDXData, NULL );

      }

 

      // Tidy up

      pDXEnum->Release();

      pDXFile->Release();

 

      return true;

}// End of ParseXFile(..)

 

 

 

// Program entry point.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)

{

      ParseXFile("bomb.x");

 

    return 0;

}// End of WinMain(..)

 

 

As the code gets bigger and bigger...and all these MessageBox windows can get annoying eventually....so a better method I think is to write our data and feedback information to a debug text file.  Then we can analyse it and see what we have.

 

 

{  Adding debug(..)   }

 

 

 
 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.