Jump to content

Building an .X File Frame Hierarchy

frame templates null template parent child file hierarchy name
Shows you how to load and create a frame hierarchy from an .X file.

4: Adsense

Building an .X File Frame Hierarchy
by Jim Adams

Copyright © 2001 by Jim Adams. All Rights Reserved.

Welcome to my second article describing the usage of .X files. My previous article, How to Parse .X Files, gave you a quick look at what's required to loading data
from an .X file, but gave no real information on what to do with that data. Well, this article is going to pick up the slack and show you how to load and create a frame hierarchy from an .X file.

What are Frame Hierarchies?

Frame hierarchies are essential to using skinned meshes; the hierarchy defines the underlying bone structure that a deformable mesh is attached to. In order for the mesh to deform, specific
vertices are connected to certain bones; as the bones move, so do their respectively attached vertices. This is basic knowledge of the skinned mesh object, and as such, I will not go into it further
at this point (rather leaving it to another article!)

What I do want to describe to you however is the way an .X file works with a hierarchy. If you're not already familiar with the way .X files store data, it's all done with templates. Not data can
exist in an .X file if its not stored in a template. There's a template for just about any kind of information you can think of, and with the .X file format, you can even create your own

The only template we're concerned with at this point is of course Frame. A Frame template has one purpose: to contain other templates, thus giving those other templates a base of reference. For
that reason, frame templates are typically referred to as reference frame templates, or frame reference templates.

When you embed multiple frame templates within each other, you are creating a frame hierarchy. The top-level frame template is considered the parent, while the embedded frame templates are called
the children. Take a look at this sample .X file to see what I mean:

xof 0303txt 0032

Header {





Frame SceneRoot {

  Frame ChildOfRoot {

    Frame ChildOfChildOfRoot {



  Frame ChildOfRoot2 {


  Frame SiblingOfChildOfRoot {



Above, I have defined four frames. The first, SceneRoot, is the root frame template, as well as being the parent of ChildOfRoot, ChildOfRoot2, and SiblingOfChildOfRoot. You can see that the third
frame, ChildOfChildOfRoot is actual the child of the ChildOfRoot template, which means the ChildOfRoot frame is also considered a parent.

You'll also notice the SiblingOfChildOfRoot frame; when multiple frames are on the same level as each other (being the children of a parent frame), they are all considered siblings. Those siblings
do not effect each other in any way; each sibling only worries about its parent.

You may be wondering why this embedding of templates is really necessary. Well, with the frame hierarchy, all child frames inherit the transformations that affect their parents. Thus, if you
change the orientation of the ChildOfRoot frame above, the ChildOfChildOfRoot frame will be altered as well (inheriting its parents transformation and adding it to its own). This is synonymous with
using skinned meshes - as the underlying bone structure moves, all attached joints must inherit their parents transformations. For instance, rotate your should and your whole arm moves with it.

Building a Frame Hierarchy

Ok, let's get to work. Building a frame hierarchy starts with a simple structure that contains the name of the frame, as well two pointers that form a linked list of child and sibling frames. This
structure is in itself a linked list, having one frame at the top (the root). Here's a structure that should do it all (including the constructor and destructor):

typedef struct sFrame


  char *Name;       // Name of frame

  sFrame *Child;    // Child linked list

  sFrame *Sibling;  // Sibling linked list


  sFrame()          // constructor


    Name  = NULL;

    Child = Sibling = NULL;


  ~sFrame()         // destructor


    delete Name;

    delete Child;

    delete Sibling;

    Name   = NULL;

    Child  = Sibling = NULL;


} sFrame;

Now at this point, we'll have to turn back to my previous article, How to Parse .X files, to grab the two functions that parse .X file templates. By modifying those two functions slightly, I have
added the ability to track the frame hierarchy as it is being built.

This works by first creating a scene root frame that all other frames are children to. Passing this frame to the newly written ParseXFileData function ensures that the function knows which frame
is the parent at the time. As the ParseXFileData comes across a frame template, it creates a new sFrame structure and adds it to the parent frame linked list as a child, as well as a sibling to any
other child frames that may exist. That newly created frame now becomes the parent of any subsequent embedded templates.

Here are the newly written functions from my previous article that parse and .X file, while at the same time building a frame hierarchy:

BOOL ParseXFile(char *Filename)


  IDirectXFile           *pDXFile = NULL;

  IDirectXFileEnumObject *pDXEnum = NULL;

  IDirectXFileData       *pDXData = NULL;

  // Create the .X file object


    return FALSE;

  // Register the templates in use

  // Use the standard retained mode templates from Direct3D

  if(FAILED(pDXFile->RegisterTemplates(                       \

           (LPVOID)D3DRM_XTEMPLATES,                          \

            D3DRM_XTEMPLATE_BYTES))) {


    return FALSE;


  // Create an enumeration object

  if(FAILED(pDXFile->CreateEnumObject((LPVOID)Filename,       \

             DXFILELOAD_FROMFILE, &pDXEnum))) {


    return FALSE;


  // Create a root frame

  sFrame *ParentFrame = new sFrame();

  ParentFrame->Name = new char[7];

  strcpy(ParentFrame->Name, "$ROOT$");

  // Enumerate all top-level templates

  while(SUCCEEDED(pDXEnum->GetNextDataObject(&pDXData))) {

    ParseXFileData(pDXData, ParentFrame);



  // Release objects



  // Delete the root when no longer needed

  delete ParentFrame;

  // Return a success

  return TRUE;


void ParseXFileData(IDirectXFileData *pData, sFrame *ParentFrame)


  IDirectXFileObject *pSubObj  = NULL;

  IDirectXFileData   *pSubData = NULL;

  IDirectXFileDataReference *pDataRef = NULL;

  const GUID *pType = NULL;

  char       *pName = NULL;

  DWORD       dwSize;

  sFrame     *Frame = NULL;

  sFrame     *SubFrame = NULL;


  // Get the template type



  // Get the template name (if any)

  if(FAILED(pData->GetName(NULL, &dwSize)))


  if(dwSize) {

    if((pName = new char[dwSize]) != NULL)

      pData->GetName(pName, &dwSize);


  // Give template a default name if none found

  if(pName == NULL) {

    if((pName = new char[9]) == NULL)


    strcpy(pName, "Template");


  // Set sub frame parent

  SubFrame = ParentFrame;

  // Process the frame templates

  if(*pType == TID_D3DRMFrame) {

    // Create a frame

    Frame = new sFrame();

    // Store the name

    Frame->Name = pName;

    pName = NULL;

    // Add to parent frame as sibling

    Frame->Sibling = ParentFrame->Child;

    ParentFrame->Child = Frame;

    // Set sub frame parent

    SubFrame = Frame;

    // Display a message describing which frame it was added to

    char Buffer[1024];

    sprintf(Buffer, "Child frame: %s\r\nParent frame: %s",    \

             Frame->Name, ParentFrame->Name);

    MessageBox(NULL, Buffer, "Added Frame", MB_OK);


  // Scan for embedded templates

  while(SUCCEEDED(pData->GetNextObject(&pSubObj))) {

    // Process embedded references

    if(SUCCEEDED(pSubObj->QueryInterface(                     \

         IID_IDirectXFileDataReference, (void**)&pDataRef))) {

      if(SUCCEEDED(pDataRef->Resolve(&pSubData))) {

        ParseXFileData(pSubData, SubFrame);





    // Process non-referenced embedded templates

    if(SUCCEEDED(pSubObj->QueryInterface(                     \

        IID_IDirectXFileData, (void**)&pSubData))) {

      ParseXFileData(pSubData, SubFrame);





  // Release name buffer

  delete pName;


If you get the accompanying source code package with this article, it contains a working example of the above code to demonstrate how to start using it in your own


Note: GameDev.net moderates article comments.