The new D3D application framework is stable.
The new SkinMesh class is at the point where models can be completely Loaded.
I'm ready to implement rendering code, and then animation code.

I've been using the D3DXLoadMeshHierarchyFromX api function.
It basically performs the Xfile Template parsing, and construction of the Frame Hierarchy, but it still requires a fair bit of user code in the form of callbacks.

I've noticed that it does not handle XFiles which don't contain a SceneRoot frame.
These cause a crash within D3DXLoadMeshHierarchyFromX (which never returns), due to D3DXLoadMeshHierarchyFromX trying to destroy more Frames than it in fact allocated.. so there's still something to be said for parsing XFiles manually (microsoft coders are crackheads and/or assume that all XFiles contain a SceneRoot frame)..

Anyway, progress is being made, which has to be a good thing, right?


Merry Christmas to all, three hoes and stockings if you've been particularly good.
Posted on 2005-12-24 09:14:57 by Homer
Hi Homer,

The skeleton you use, and some of the functions, and callbacks related to it, are specifically DirectX 9.
My interest goes out for DirectX 8 in stead of DirectX 9. Because what works well under DirectX 8 should work well under DirectX 9 too.

When you are parsing xFiles manually, there isn't any difference between doing it under DirectX 8 and doing it under DirectX 9, is there?. So why use DirectX 9?

As I understand you are using the OA32 SDI-application. Can it be converted to ATC easily, which was and is my goal?

Friendly regards,
mdevries.
Posted on 2005-12-24 12:57:27 by mdevries
DX9 is slightly optimized, is supposed to use less memory, and supports more hadrware (and more capabilities of existing/new hardware).
Posted on 2005-12-24 18:12:49 by ti_mo_n
No, I had problems when using the OA32 SDIApp object as the basis for D3D applications.
I wrote my own OA32 D3DApp object instead, which implements everything you need for your application main window, d3d initialization and cleanup, it also contains a dialog and code for enumerating and selecting video display modes, and contains several "user callbacks", which are for rendering your stuff, and for loading and unloading "device dependant resources" on demand (stuff that gets trashed when the user alt-tabs or switches display modes / windowing mode at runtime).

The idea was that I make it work, figure out where Biterider went wrong, so he can port the changes back into his own object.

As for translating ANY oa32 stuff to atc, that is quite simple to do, but since oa has had several overhauls of its codebase, I no longer see a reason why you would want to convert them.
In terms of binary speed and size, oa32 is now on an even pegging with atc.
I am converted to oa32 and if you still have not taken the time to look at it, perhaps you should.

Note that oa32's entire d3d support was based on my atc classes for d3d9.
I've found no bugs in there at all, but just some stuff has been disabled with comments.
I've been working closely with Biterider so that any changes I make are perpetuated in the next public release.. note that Configuration File is used to store the most recent video mode which was used, and that the Run method is a wrapper to WinMain (and MessagePump Loop)..

Here is an example of a D3D application using my D3DApp object:

;Create an instance of the D3D Application Class
mov pApp,$New (D3DApp,Init,addr szConfigFileName)

;Set up the CallBacks to User Code
OCall pApp::D3DApp.SetUserRenderProc, addr MyRender
OCall pApp::D3DApp.SetUserReloadVolatiles, addr MyReCreateGeometry
OCall pApp::D3DApp.SetUserReleaseVolatiles, addr MyKillGeometry
;Let's get things moving
OCall pApp::D3DApp.Run
;Time to destroy the universe
Destroy pApp
;Clean up OOP Support
SysDone
invoke ExitProcess,eax

Posted on 2005-12-26 00:58:29 by Homer
Sounds quite reasonable, and convincing.

What about D3DXLoadMeshHierarchyFromX? Do you think it's worth using it. Or do you prefer a manually parsing of xFiles?

B.t.w: Can the project be downloaded by interested people?

Friendly regards,
mdevries.
Posted on 2005-12-26 08:54:32 by mdevries
I guess it depends apon how you look at this.
Basically using D3DXLoadMeshHierarchyFromX is a heck of a lot like writing your own xfile loader from the ground up (which incidentally I have done) because we are still implementing roughly two thirds of the code required.
The D3DXLoadMeshHierarchyFromX requires us to create an object instance containing pointers to the four main functions required to build a linkedlist hierarchy from the data stored in the xfile.
Not only do we have to provide an instance of the interface, we have to code the four methods that it contains pointers to.These are methods called createframe, createmeshcontainer, destroyframe, destroymeshcontainer.
What D3DXLoadMeshHierarchyFromX is really doing for us then is to perform all the file parsing for us, and via the interface and code we provide to create a data structure in memory  (by linking object instances via pointers) whose hierarchy corresponds to that encountered in the file.
So really, we could say D3DXLoadMeshHierarchyFromX just contains all the loop and logic code, and we just have to provide it the code to make and break objects.
The only files it has problems loading with it I've seen are exported via third party plugins of which I am a little dubious.
I should mention that its possible to embed custom data within an xfile and to add your own code for loading them but I haven't implemented that in the current example.
Not to use D3DXLoadMeshHierarchyFromX can't really be justified, at least not by me, not yet.

Posted on 2005-12-26 09:26:29 by Homer
Fair enough.

And what about my other question:

Can the project be downloaded by interested people?


I could add the word "yet" to the end of it.
So, can the project be downloaded by interested people yet?

Regards,
mdevries.
Posted on 2005-12-26 15:12:47 by mdevries
I'll make the project download available by tomorrow, since you asked.
Bear in mind that it is quite beta (although its perfectly stable) and subject to change without notification.. furthermore, you will be required to make several changes to OA32 includes (unless you wanna wait for the next oa32 public release).

I am willing to help you through the teething stage, as long as you are willing to accept that a teething stage exists !!

OA32's D3D includes are relatively untested, as usual we are pressing the envelope, and leaving footprints in the sand for those who may follow.

On the bright side, the number of changes is small and mostly only involve removing comments from api prototypes...

Posted on 2005-12-27 06:01:51 by Homer
I'm looking forward to it.
And maybe/hopefully some others too.

Friendly regards,
mdevries
Posted on 2005-12-27 10:51:15 by mdevries
Today I implemented all the code for Rendering the skinmesh.
Argh, Grrr, it doesn't work.

I have spent my time translating the "Modular SkinMesh" source posted at flipcode.org, which is basically a modified version of the sdk skinmesh sample.. the author has moved a bunch of stuff around in order to make the skinmesh class less closely associated with the SDK Sample and more useful as a generic skinmesh class.
Of course, there's no binary included, because the bloody thing doesn't work.

Sigh.

Well, I have a bunch of broken skinmesh code, where do we go from here?
I suggest that it might be mutually beneficial to translate the original DX9 SDK skinmesh sample here, in public, with the hope that more eyes mean less bugfixes, and so that you guys can see what is involved in translating a cpp source to oa32.

As such, perhaps I shouldn't post my D3DApp class just yet, since that will only serve to confuse us during this process - the SDK skinmesh sample is closely tied to microsoft's own application framework, which will be an obstacle in itself.
We'd be better off to create a light framework, implementing only what the sample demands from it.
We might decide to rework my D3DApp with this in mind, but let's just begin with translating the sample code and worry about the gui framework along the way, shall we?

That being said, I feel a linear approach to the translation of the sample code is warranted.
Unless someone can tell me why I should not, I will begin posting slabs of the original file, in order of appearance, and follow each with the oa32 translation of it.

Here's the beginning of skinnedmesh.cpp
Please don't sue us microsoft, this is purely for educational purposes.

//--------------------------------------------------------------------------------------
// File: SkinnedMesh.cpp
//
// Starting point for new Direct3D applications
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#include "resource.h"

//#define DEBUG_VS   // Uncomment this line to debug vertex shaders
//#define DEBUG_PS   // Uncomment this line to debug pixel shaders

#define MESHFILENAME L"tiny\\tiny.x"


WCHAR g_wszShaderSource[4][30] =
{
    L"skinmesh1.vsh",
    L"skinmesh2.vsh",
    L"skinmesh3.vsh",
    L"skinmesh4.vsh"
};


// enum for various skinning modes possible
enum METHOD
{
    D3DNONINDEXED,
    D3DINDEXED,
    SOFTWARE,
    D3DINDEXEDVS,
    D3DINDEXEDHLSLVS,
    NONE
};


//--------------------------------------------------------------------------------------
// Name: struct D3DXFRAME_DERIVED
// Desc: Structure derived from D3DXFRAME so we can add some app-specific
//       info that will be stored with each frame
//--------------------------------------------------------------------------------------
struct D3DXFRAME_DERIVED: public D3DXFRAME
{
    D3DXMATRIXA16        CombinedTransformationMatrix;
};


//--------------------------------------------------------------------------------------
// Name: struct D3DXMESHCONTAINER_DERIVED
// Desc: Structure derived from D3DXMESHCONTAINER so we can add some app-specific
//       info that will be stored with each mesh
//--------------------------------------------------------------------------------------
struct D3DXMESHCONTAINER_DERIVED: public D3DXMESHCONTAINER
{
    LPDIRECT3DTEXTURE9*  ppTextures;       // array of textures, entries are NULL if no texture specified   
                               
    // SkinMesh info             
    LPD3DXMESH           pOrigMesh;
    LPD3DXATTRIBUTERANGE pAttributeTable;
    DWORD                NumAttributeGroups;
    DWORD                NumInfl;
    LPD3DXBUFFER         pBoneCombinationBuf;
    D3DXMATRIX**         ppBoneMatrixPtrs;
    D3DXMATRIX*          pBoneOffsetMatrices;
    DWORD                NumPaletteEntries;
    bool                 UseSoftwareVP;
    DWORD                iAttributeSW;     // used to denote the split between SW and HW if necessary for non-indexed skinning
};


Here's my version of the that stuff :


;--------------------------------------------------------------------------------------
; SkinnedMesh.inc
; OA32 Base Class for (you guessed it)
; Written by EvilHomer (and perhaps others?)
; Project started   : 28 December, 2005
; Project completed : (incomplete)
; This source is strongly based on Microsoft's SDK sample
; Don't sue me, this is a purely educational exercise.
;--------------------------------------------------------------------------------------

;#include "dxstdafx.h"
;#include "resource.h"

;//#define DEBUG_VS   // Uncomment this line to debug vertex shaders
;//#define DEBUG_PS   // Uncomment this line to debug pixel shaders

MESHFILENAME  textequ "tiny\tiny.x"

.data
g_wszShaderSource dw "skinmesh1.vsh",0
      dw "skinmesh2.vsh",0
      dw "skinmesh3.vsh",0
      dw "skinmesh4.vsh",0,0
.code

;Enumerate possible skinning methods
    D3DNONINDEXED equ 0
    D3DINDEXED equ 1
    SOFTWARE equ 2
    D3DINDEXEDVS equ 3
    D3DINDEXEDHLSLVS equ 4
    NONE equ d0h


;Structure extends D3DXFRAME so we can add some app-specific
;info that will be stored with each frame
;--------------------------------------------------------------------------------------
D3DXFRAME_DERIVED struct
  ;Original D3DXFRAME structure fields
  Base D3DXFRAME <>
  ;Per-Frame combined matrix (this frame and its parent)
  CombinedTransformationMatrix D3DXMATRIX <>
D3DXFRAME_DERIVED ends


;Structure extends D3DXMESHCONTAINER so we can add some app-specific
;info that will be stored with each mesh
;--------------------------------------------------------------------------------------
D3DXMESHCONTAINER_DERIVED struct
  ;Original D3DXMESHCONTAINER structure fields
  Base D3DXMESHCONTAINER <>
  ;Array of textures, entries are NULL if no texture specified 
  ppTextures LPDIRECT3DTEXTURE9 ?
                               
;SkinMesh info             
    pOrigMesh LPD3DXMESH ?
    pAttributeTable LPD3DXATTRIBUTERANGE ?
    NumAttributeGroups dd ?
    NumInfl dd ?
    pBoneCombinationBuf LPD3DXBUFFER ?
    ppBoneMatrixPtrs dd ? ;ptr to array of LPD3DXMATRIX
    pBoneOffsetMatrices LPD3DXMATRIX ? ;ptr to array of D3DXMATRIX
    NumPaletteEntries dd ?
    UseSoftwareVP dd ?
    iAttributeSW dd ?   ; used to denote the split between SW and HW if necessary for non-indexed skinning
D3DXMESHCONTAINER_DERIVED ends
;--------------------------------------------------------------------------------------


If I translate each chunk of code in this fashion, we can analyse and discuss the code as we go.
If anyone thinks I shouldn't be doing this here, please say so.


Posted on 2005-12-28 00:26:49 by Homer
All we have really done so far is to declare some equates and two structs.
These structs are really central to the entire project, but I'd rather defer discussion of the individual fields of these structs until we can see them being applied.

What we have done is to extend the original D3DXFRAME and D3DXMESHCONTAINER structs to incorporate some extra stuff.

What's a Frame? What's a MeshContainer?

Frames are "frames of reference" with regards to hierarchical rotation and translation.
We use frames to create a "frame hierarchy" representing the joints of the skinned mesh.
If you rotate or translate a frame, everything "below" that frame in the hierarchy is also rotated and translated.
Think about this - if you bend your knee, your lower leg, ankle and foot are displaced (they are all "below" the knee in your body hierarchy).
Frames can contain other frames, and may also contain a MeshContainer.
MeshContainers contain data specific to a single mesh.
Your skinnedmesh might just have one big mesh, or it might be comprised of several meshes.
That really depends on the modeller it was created with.
Just note that we can handle multiple meshes spread throughout the frame hierarchy, no problemo.

Frames are linked together via Pointers.
If we were to write our own SkinMesh Loader from the ground up, we would have to set up all those pointers, but we will be using D3DXLoadMeshHierarchyFromX, which does that for us.

Any Questions?
Posted on 2005-12-28 00:40:54 by Homer
Among the various parameters required by our call to D3DXLoadMeshHierarchyFromX is one called pAlloc.. this is a pointer to a COM interface called ID3DXAllocateHierarchy.

The interface inherits from IUnknown, but aside from that it just contains four pointers to some functions which we must provide the code for.

Here is the interface definition, directly from the SDK sample...


// Name: class CAllocateHierarchy
// Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create
//      frames and meshcontainers.
//--------------------------------------------------------------------------------------
class CAllocateHierarchy: public ID3DXAllocateHierarchy
{
public:
    STDMETHOD(CreateFrame)(THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame);
    STDMETHOD(CreateMeshContainer)(THIS_
        LPCSTR Name,
        CONST D3DXMESHDATA *pMeshData,
        CONST D3DXMATERIAL *pMaterials,
        CONST D3DXEFFECTINSTANCE *pEffectInstances,
        DWORD NumMaterials,
        CONST DWORD *pAdjacency,
        LPD3DXSKININFO pSkinInfo,
        LPD3DXMESHCONTAINER *ppNewMeshContainer);
    STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree);
    STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase);

    CAllocateHierarchy() {}
};




... and here is the OA32 version of it.
Note that we are defining the interface as an OA32 Object which inherits from the IUnknown object (OA32's vanilla implementation of IUnknown interface).
Note that when we define objects with OA32, we should declare a unique "class identifier".
The value of CAllocateHierarchyID is totally arbitrary, I tend to invent random numbers and hope that they don't collide with existing OA32 class identifiers :P
To define a class object under OA32, we use the format "Object ClassName, ClassID[, AncestorClass]"
Thus, CAllocateHierarchy inherits from IUnknown.
IUnknown is defined in the ComPrimers.inc file, so in order to successfully compile this code, you will need to ensure that you include the ComPrimers object near the top of your source (we'll cover this later when we start looking at the gui framework).. LoadObjects ComPrimers is what you need.


CAllocateHierarchyID equ 66676
Object CAllocateHierarchy,CAllocateHierarchyID, IUnknown
  InterfaceMethod CreateFrame,          Pointer, Pointer
  InterfaceMethod CreateMeshContainer,  Pointer, Pointer, Pointer, Pointer, dword, Pointer, Pointer, Pointer
  InterfaceMethod DestroyFrame,        Pointer
  InterfaceMethod DestroyMeshContainer, Pointer
ObjectEnd


Yay - we've defined the interface itself, but we have not defined the code for its four methods.
We'll get to that shortly.
The SDK sample defines a bunch of global variables, we should do that now too.

Posted on 2005-12-28 01:12:19 by Homer
Here's a crapload of global variables

ID3DXFont*              g_pFont = NULL;        // Font for drawing text
ID3DXSprite*            g_pTextSprite = NULL;  // Sprite for batching draw text calls
ID3DXEffect*            g_pEffect = NULL;      // D3DX effect interface
CD3DArcBall            g_ArcBall;              // Arcball for model control
bool                    g_bShowHelp = true;    // If true, it renders the UI control text
CDXUTDialogResourceManager g_DialogResourceManager; // manager for shared resources of dialogs
CD3DSettingsDlg        g_SettingsDlg;          // Device settings dialog
CDXUTDialog            g_HUD;                  // dialog for standard controls
CDXUTDialog            g_SampleUI;            // dialog for sample specific controls
LPD3DXFRAME            g_pFrameRoot = NULL;
ID3DXAnimationController* g_pAnimController = NULL;
D3DXVECTOR3            g_vObjectCenter;        // Center of bounding sphere of object
FLOAT                  g_fObjectRadius;        // Radius of bounding sphere of object
METHOD                  g_SkinningMethod = D3DNONINDEXED; // Current skinning method
D3DXMATRIXA16*          g_pBoneMatrices = NULL;
UINT                    g_NumBoneMatricesMax = 0;
IDirect3DVertexShader9* g_pIndexedVertexShader[4];
D3DXMATRIXA16          g_matView;              // View matrix
D3DXMATRIXA16          g_matProj;              // Projection matrix
D3DXMATRIXA16          g_matProjT;            // Transpose of projection matrix (for asm shader)
DWORD                  g_dwBehaviorFlags;      // Behavior flags of the 3D device
bool                    g_bUseSoftwareVP;      // Flag to indicate whether software vp is
                                                // required due to lack of hardware


and here's our translation

;--------------------------------------------------------------------------------------
; Global variables
;--------------------------------------------------------------------------------------
g_pFont dd NULL  ;    ID3DXFont for drawing text
g_pTextSprite dd NULL;  ID3DXSprite for batching draw text calls
g_pEffect dd NULL;    ID3DXEffect effect interface
g_ArcBall dd NULL; CD3DArcBall  for model control
g_bShowHelp dd TRUE;    If true, it renders the UI control text
g_DialogResourceManager dd NULL ;  CDXUTDialogResourceManager for shared resources of dialogs
g_SettingsDlg dd NULL ; CD3DSettingsDlg  Device settings dialog
g_HUD dd nULL; CDXUTDialog  dialog for standard controls
g_SampleUI dd NULL; CDXUTDialog dialog for sample specific controls
g_pFrameRoot LPD3DXFRAME NULL ;points to root frame
g_pAnimController dd NULL;ID3DXAnimationController
g_vObjectCenter D3DXVECTOR3 <>;        Center of bounding sphere of object
g_fObjectRadius REAL4 0.0f;        Radius of bounding sphere of object
g_SkinningMethod dd D3DNONINDEXED;  Current skinning method
g_pBoneMatrices LPD3DXMATRIX NULL ;Pointer to array of matrices
g_NumBoneMatricesMax dd 0;
g_pIndexedVertexShader dd 4 dup (0);IDirect3DVertexShader9
g_matView D3DXMATRIX <>;              // View matrix
g_matProj D3DXMATRIX <>;              // Projection matrix
g_matProjT D3DXMATRIX <>;            // Transpose of projection matrix (for asm shader)
g_dwBehaviorFlags dd NULL;      // Behavior flags of the 3D device
g_bUseSoftwareVP dd TRUE;      // Flag to indicate whether software vp isrequired due to lack of hardware


We can see from this that there are a number of support objects we'll need to implement, or avoid implementing... those whose names begin with CD3D or CDUT.
Generally, they comprise elements of the sample gui framework.

In the Modular version I originally translated, a lot of these fields had been moved into the SkinMesh class instead of being global like this.

Posted on 2005-12-28 01:26:44 by Homer

Here's the next chunk of original source.
There's another enumeration (we wil define), and a bunch of "forwards reference" prototypes (which for now, we will just comment out)


//--------------------------------------------------------------------------------------
// UI control IDs
//--------------------------------------------------------------------------------------
#define IDC_TOGGLEFULLSCREEN    1
#define IDC_TOGGLEREF          3
#define IDC_CHANGEDEVICE        4
#define IDC_METHOD              5



//--------------------------------------------------------------------------------------
// Forward declarations
//--------------------------------------------------------------------------------------
bool    CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext );
bool    CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext );
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
void    CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
void    CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext );
void    CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
void    CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );
void    CALLBACK OnLostDevice( void* pUserContext );
void    CALLBACK OnDestroyDevice( void* pUserContext );

void    InitApp();
HRESULT LoadMesh( IDirect3DDevice9* pd3dDevice, WCHAR* strFileName, ID3DXMesh** ppMesh );
void    RenderText();
void    DrawMeshContainer( IDirect3DDevice9 *pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase );
void    DrawFrame( IDirect3DDevice9 *pd3dDevice, LPD3DXFRAME pFrame );
HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainer );
HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame );
void    UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );
void    UpdateSkinningMethod( LPD3DXFRAME pFrameBase );
HRESULT GenerateSkinnedMesh( IDirect3DDevice9 *pd3dDevice, D3DXMESHCONTAINER_DERIVED *pMeshContainer );
void    ReleaseAttributeTable( LPD3DXFRAME pFrameBase );



Here is our version:

;--------------------------------------------------------------------------------------
; UI control IDs
;--------------------------------------------------------------------------------------
IDC_TOGGLEFULLSCREEN    equ 1
IDC_TOGGLEREF          equ 3
IDC_CHANGEDEVICE        equ 4
IDC_METHOD              equ 5


;//--------------------------------------------------------------------------------------
;// Forward declarations
;//--------------------------------------------------------------------------------------
;bool    CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext );
;bool    CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext );
;HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
;HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
;void    CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
;void    CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
;LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext );
;void    CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
;void    CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );
;void    CALLBACK OnLostDevice( void* pUserContext );
;void    CALLBACK OnDestroyDevice( void* pUserContext );
;
;void    InitApp();
;HRESULT LoadMesh( IDirect3DDevice9* pd3dDevice, WCHAR* strFileName, ID3DXMesh** ppMesh );
;void    RenderText();
;void    DrawMeshContainer( IDirect3DDevice9 *pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase );
;void    DrawFrame( IDirect3DDevice9 *pd3dDevice, LPD3DXFRAME pFrame );
;HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainer );
;HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame );
;void    UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );
;void    UpdateSkinningMethod( LPD3DXFRAME pFrameBase );
;HRESULT GenerateSkinnedMesh( IDirect3DDevice9 *pd3dDevice, D3DXMESHCONTAINER_DERIVED *pMeshContainer );
;void    ReleaseAttributeTable( LPD3DXFRAME pFrameBase );


Finally, we get to some good stuff - some actual code !! About Time !!
Posted on 2005-12-28 01:35:44 by Homer
Our first actual code to translate is a helper procedure for allocating memory for a name string, and copying the name string into the allocated memory, and returning a pointer to the new copy.


//--------------------------------------------------------------------------------------
// Name: AllocateName()
// Desc: Allocates memory for a string to hold the name of a frame or mesh
//--------------------------------------------------------------------------------------
HRESULT AllocateName( LPCSTR Name, LPSTR *pNewName )
{
    UINT cbLength;

    if( Name != NULL )
    {
        cbLength = (UINT)strlen(Name) + 1;
        *pNewName = new CHAR;
        if (*pNewName == NULL)
            return E_OUTOFMEMORY;
        memcpy( *pNewName, Name, cbLength*sizeof(CHAR) );
    }
    else
    {
        *pNewName = NULL;
    }

    return S_OK;
}


Our version of this procedure uses OA32's MemAlloc macro.
More specifically, I use $MemAlloc, which is a lot like $invoke..
MemAlloc allocates memory on the Process Heap.
This may not be the best place to shove strings, but it'll do the job.
Right now I'm not concerned about efficiency, just wanna make it work.

AllocateName proc uses esi,pName,ppNewName
    .if pName != NULL
        invoke lstrlen,pName
inc eax
mov esi,ppNewName
mov dword ptr,$MemAlloc(eax,MEM_INIT_ZERO)
        .if eax==NULL
            return E_OUTOFMEMORY
        .endif
invoke lstrcpy,eax,pName
    .else
        mov esi,ppNewName
        mov dword ptr,NULL
    .endif
    return S_OK
AllocateName endp


Please yell if you have any questions !!!
Posted on 2005-12-28 01:46:05 by Homer
It's time to define the four functions associated with the interface we defined earlier.
I'm going to diverge from the original source a little here, because it really doesn't matter what order we define these four functions, as long as we define them all.
Note that we will never be calling any of these functions ourselves.
D3DXLoadMeshHierarchyFromX will be calling them via our interface (which you should be seeing as just a table of pointers to our code by now).


//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateFrame()
// Desc:
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::CreateFrame( LPCSTR Name, LPD3DXFRAME *ppNewFrame )
{
    HRESULT hr = S_OK;
    D3DXFRAME_DERIVED *pFrame;

    *ppNewFrame = NULL;

    pFrame = new D3DXFRAME_DERIVED;
    if (pFrame == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }

    hr = AllocateName(Name, &pFrame->Name);
    if (FAILED(hr))
        goto e_Exit;

    // initialize other data members of the frame
    D3DXMatrixIdentity(&pFrame->TransformationMatrix);
    D3DXMatrixIdentity(&pFrame->CombinedTransformationMatrix);

    pFrame->pMeshContainer = NULL;
    pFrame->pFrameSibling = NULL;
    pFrame->pFrameFirstChild = NULL;

    *ppNewFrame = pFrame;
    pFrame = NULL;

e_Exit:
    delete pFrame;
    return hr;
}


Here's our version of this Class Method.
In our version, we don't bother setting a bunch of fields to NULL..
Instead, we zero out the entire Frame object when we allocate its memory..

Method CAllocateHierarchy.CreateFrame,uses esi,pName,ppNewFrame
local pFrame
local hr
    mov hr,S_OK

    ;Zero the output variable
    mov eax,ppNewFrame
    mov dword ptr, NULL

    mov pFrame,$MemAlloc(sizeof D3DXFRAME_DERIVED,MEM_INIT_ZERO)
    .if eax == NULL
        return E_OUTOFMEMORY
    .endif
   
    mov hr , $invoke (AllocateName,pName, addr .D3DXFRAME.pName
    .if (FAILED(eax)
        jmp e_Exit
    .endif

    ; initialize other data members of the frame
    mov eax,pFrame
    invoke D3DXMatrixIdentity,addr .D3DXFRAME.TransformationMatrix
    mov eax,pFrame
    invoke D3DXMatrixIdentity,addr .D3DXFRAME_DERIVED.CombinedTransformationMatrix

    ;Set the output variable
    mov eax,ppNewFrame
    m2m dword ptr,pFrame
    ;Return S_OK
    return hr

e_Exit: ;Something went wrong, lets deallocate Frame and return error
    MemFree pFrame
    return hr
MethodEnd


Questions?
Posted on 2005-12-28 02:07:30 by Homer
I said it didn't really matter what order these four functions were declared in, so here's the flipside to the previous function:


//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyFrame()
// Desc:
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyFrame(LPD3DXFRAME pFrameToFree)
{
    SAFE_DELETE_ARRAY( pFrameToFree->Name );
    SAFE_DELETE( pFrameToFree );
    return S_OK;
}


We will declare a helper macro called SafeFree
Our code looks like this:

SafeFree macro target
  .if target!=NULL
      MemFree target
      mov target,NULL
  .endif
endm

Method CAllocateHierarchy.DestroyFrame,uses esi,pFrameToFree
    mov eax,pFrameToFree
    SafeFree .D3DXFRAME.pName
    SafeFree pFrameToFree
    return S_OK;
MethodEnd


Well, that was pretty easy, huh?
There's not much involved in allocating and deallocating Frames :)

Two interface methods down, and two to go..

Posted on 2005-12-28 02:30:38 by Homer
It's time to get our hands dirty.
Here's the original version of the CreateMeshContainer method.
It will look scary at first, but there's nothing to be afraid of..


//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateMeshContainer()
// Desc:
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::CreateMeshContainer(
   LPCSTR Name,
   CONST D3DXMESHDATA *pMeshData,
   CONST D3DXMATERIAL *pMaterials,
   CONST D3DXEFFECTINSTANCE *pEffectInstances,
   DWORD NumMaterials,
   CONST DWORD *pAdjacency,
   LPD3DXSKININFO pSkinInfo,
   LPD3DXMESHCONTAINER *ppNewMeshContainer)
{
   HRESULT hr;
   D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;
   UINT NumFaces;
   UINT iMaterial;
   UINT iBone, cBones;
   LPDIRECT3DDEVICE9 pd3dDevice = NULL;

   LPD3DXMESH pMesh = NULL;

   *ppNewMeshContainer = NULL;

   // this sample does not handle patch meshes, so fail when one is found
   if (pMeshData->Type != D3DXMESHTYPE_MESH)
   {
       hr = E_FAIL;
       goto e_Exit;
   }

   // get the pMesh interface pointer out of the mesh data structure
   pMesh = pMeshData->pMesh;

   // this sample does not FVF compatible meshes, so fail when one is found
   if (pMesh->GetFVF() == 0)
   {
       hr = E_FAIL;
       goto e_Exit;
   }

   // allocate the overloaded structure to return as a D3DXMESHCONTAINER
   pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
   if (pMeshContainer == NULL)
   {
       hr = E_OUTOFMEMORY;
       goto e_Exit;
   }
   memset(pMeshContainer, 0, sizeof(D3DXMESHCONTAINER_DERIVED));

   // make sure and copy the name.  All memory as input belongs to caller, interfaces can be addref'd though
   hr = AllocateName(Name, &pMeshContainer->Name);
   if (FAILED(hr))
       goto e_Exit;        

   pMesh->GetDevice(&pd3dDevice);
   NumFaces = pMesh->GetNumFaces();

   // if no normals are in the mesh, add them
   if (!(pMesh->GetFVF() & D3DFVF_NORMAL))
   {
       pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

       // clone the mesh to make room for the normals
       hr = pMesh->CloneMeshFVF( pMesh->GetOptions(),
                                   pMesh->GetFVF() | D3DFVF_NORMAL,
                                   pd3dDevice, &pMeshContainer->MeshData.pMesh );
       if (FAILED(hr))
           goto e_Exit;

       // get the new pMesh pointer back out of the mesh container to use
       // NOTE: we do not release pMesh because we do not have a reference to it yet
       pMesh = pMeshContainer->MeshData.pMesh;

       // now generate the normals for the pmesh
       D3DXComputeNormals( pMesh, NULL );
   }
   else  // if no normals, just add a reference to the mesh for the mesh container
   {
       pMeshContainer->MeshData.pMesh = pMesh;
       pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

       pMesh->AddRef();
   }
       
   // allocate memory to contain the material information.  This sample uses
   //   the D3D9 materials and texture names instead of the EffectInstance style materials
   pMeshContainer->NumMaterials = max(1, NumMaterials);
   pMeshContainer->pMaterials = new D3DXMATERIAL;
   pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9;
   pMeshContainer->pAdjacency = new DWORD;
   if ((pMeshContainer->pAdjacency == NULL) || (pMeshContainer->pMaterials == NULL))
   {
       hr = E_OUTOFMEMORY;
       goto e_Exit;
   }

   memcpy(pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * NumFaces*3);
   memset(pMeshContainer->ppTextures, 0, sizeof(LPDIRECT3DTEXTURE9) * pMeshContainer->NumMaterials);

   // if materials provided, copy them
   if (NumMaterials > 0)            
   {
       memcpy(pMeshContainer->pMaterials, pMaterials, sizeof(D3DXMATERIAL) * NumMaterials);

       for (iMaterial = 0; iMaterial < NumMaterials; iMaterial++)
       {
           if (pMeshContainer->pMaterials.pTextureFilename != NULL)
           {
               WCHAR strTexturePath;
               WCHAR wszBuf;
               MultiByteToWideChar( CP_ACP, 0, pMeshContainer->pMaterials.pTextureFilename, -1, wszBuf, MAX_PATH );
               wszBuf = L'\0';
               DXUTFindDXSDKMediaFileCch( strTexturePath, MAX_PATH, wszBuf );
               if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, strTexturePath,
                                                       &pMeshContainer->ppTextures ) ) )
                   pMeshContainer->ppTextures = NULL;

               // don't remember a pointer into the dynamic memory, just forget the name after loading
               pMeshContainer->pMaterials.pTextureFilename = NULL;
           }
       }
   }
   else // if no materials provided, use a default one
   {
       pMeshContainer->pMaterials[0].pTextureFilename = NULL;
       memset(&pMeshContainer->pMaterials[0].MatD3D, 0, sizeof(D3DMATERIAL9));
       pMeshContainer->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
       pMeshContainer->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
       pMeshContainer->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
       pMeshContainer->pMaterials[0].MatD3D.Specular = pMeshContainer->pMaterials[0].MatD3D.Diffuse;
   }

   // if there is skinning information, save off the required data and then setup for HW skinning
   if (pSkinInfo != NULL)
   {
       // first save off the SkinInfo and original mesh data
       pMeshContainer->pSkinInfo = pSkinInfo;
       pSkinInfo->AddRef();

       pMeshContainer->pOrigMesh = pMesh;
       pMesh->AddRef();

       // Will need an array of offset matrices to move the vertices from the figure space to the bone's space
       cBones = pSkinInfo->GetNumBones();
       pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX;
       if (pMeshContainer->pBoneOffsetMatrices == NULL)
       {
           hr = E_OUTOFMEMORY;
           goto e_Exit;
       }

       // get each of the bone offset matrices so that we don't need to get them later
       for (iBone = 0; iBone < cBones; iBone++)
       {
           pMeshContainer->pBoneOffsetMatrices = *(pMeshContainer->pSkinInfo->GetBoneOffsetMatrix(iBone));
       }

       // GenerateSkinnedMesh will take the general skinning information and transform it to a HW friendly version
       hr = GenerateSkinnedMesh( pd3dDevice, pMeshContainer );
       if (FAILED(hr))
           goto e_Exit;
   }

   *ppNewMeshContainer = pMeshContainer;
   pMeshContainer = NULL;

e_Exit:
   SAFE_RELEASE(pd3dDevice);

   // call Destroy function to properly clean up the memory allocated
   if (pMeshContainer != NULL)
   {
       DestroyMeshContainer(pMeshContainer);
   }

   return hr;
}



We will implement another helper macro, whose job is to call the IUnknown::Release method apon a given COM interface instance as follows:


SafeRelease macro target
  .if target!=NULL
     ICall target::IUnknown.Release
     mov target,NULL
  .endif
endm



Now, here's our version of CreateMeshContainer method:


Method CAllocateHierarchy.CreateMeshContainer,uses esi,pName,pMeshData,pMaterials,pEffectInstances,NUmMaterials,pAdjacency,pSkinInfo,ppNewMeshContainer
local hr
local pMeshContainer
local pd3dDevice
local NumFaces
local iMaterial
local iBone
local cBones
local pMesh
local fvf
local options


   ;Zero some locals
   mov pd3dDevice,NULL
   mov pMesh,NULL
   mov pMeshContainer,NULL
   ;Zero the output variable
   mov eax,ppNewMeshContainer
   mov dword ptr,NULL

   ; this sample does not handle patch meshes, so fail when one is found
   mov eax,pMeshData
   .if .D3DXMESHDATA.dType!=D3DXMESHTYPE_MESH)
mov hr,E_FAIL
       jmp e_Exit
   .endif

   ; get the pMesh interface pointer out of the mesh data structure
   m2m pMesh,.D3DXMESHDATA.pMesh

   ; this sample does not handle FVF compatible meshes, so fail when one is found
   ; NOTE : Use ICall when calling COM Interface Methods
   ICall pMesh::ID3DXMesh.GetFVF
   mov fvf,eax
   .if eax==0
       mov hr , E_FAIL
       jmp e_Exit
   .endif

   ; allocate the overloaded structure to return as a D3DXMESHCONTAINER
   mov pMeshContainer ,$MemAlloc(sizeof D3DXMESHCONTAINER_DERIVED,MEM_INIT_ZERO)
   .if eax==NULL    
       mov hr, E_OUTOFMEMORY
       jmp e_Exit
   .endif

   ; make sure to copy the name.  All memory as input belongs to caller, interfaces can be addref'd though
   mov hr , $invoke (AllocateName,pName, addr .D3DXMESHCONTAINER.pName)
   .if (FAILED(eax)
       jmp e_Exit
   .endif        

   ICall pMesh::ID3DXMesh.GetDevice,addr pd3dDevice
   ICall pMesh::ID3DXMesh.GetNumFaces
   mov NumFaces,eax

   ; if no normals are in the mesh, add them
   mov eax,fvf
   and eax,D3DFVF_NORMAL
   .if eax==NULL
       mov eax,pMeshContainer
       mov .D3DXMESHCONTAINER.MeshData.dType,D3DXMESHTYPE_MESH

       ; clone the mesh to make room for the normals
       ICall pMesh::ID3DXMesh.GetOptions
mov options,eax
       mov ebx,fvf
       or ebx,D3DFVF_NORMAL
       mov eax,pMeshContainer
ICall pMesh::ID3DXMesh.CloneMeshFVF,options,ebx,pd3dDevice,addr .D3DXMESHCONTAINER.MeshData.pMesh
       mov hr,eax
       .if FAILED(eax)
           jmp e_Exit
       .endif

       ; get the new pMesh pointer back out of the mesh container to use
       ; NOTE: we do not release pMesh because we do not have a reference to it yet
       mov eax,pMeshContainer    
       m2m pMesh , .D3DXMESHCONTAINER.MeshData.pMesh

       ; now generate the normals for the pmesh
       invoke D3DXComputeNormals, pMesh, NULL
   
   .else  ; if no normals, just add a reference to the mesh for the mesh container    
       mov eax,pMeshContainer    
       m2m .D3DXMESHCONTAINER.MeshData.pMesh,pMesh
       mov .D3DXMESHCONTAINER.MeshData.dType , D3DXMESHTYPE_MESH
       ICall pMesh::ID3DXMesh.AddRef
   .endif
       
   ; allocate memory to contain the material information.  This sample uses
   ;   the D3D9 materials and texture names instead of the EffectInstance style materials
   mov eax,pMeshContainer
   .if NumMaterials==NULL
      mov .D3DXMESHCONTAINER.NumMaterials,1
   .else
      m2m .D3DXMESHCONTAINER.NumMaterials,NumMaterials
   .endif

   mov esi,pMeshContainer
   mov eax,sizeof D3DXMATERIAL
   mul .D3DXMESHCONTAINER.NumMaterials
   mov .D3DXMESHCONTAINER.pMaterials,$MemAlloc(eax)
   mov eax,.D3DXMESHCONTAINER.NumMaterials
   shl eax,2
   mov .D3DXMESHCONTAINER.ppTextures,$MemAlloc(eax)
   mov eax,3
   mul NumFaces
   shl eax,2
   mov .D3DXMESHCONTAINER.pAdjacency,$MemAlloc(eax)
   .if eax==NUll || esi].D3DXMESHCONTAINER.pMaterials==NULL || .D3DXMESHCONTAINER.ppTextures==NULL
       mov hr , E_OUTOFMEMORY
       jmp e_Exit
   .endif

   mov eax,3
   mul NumFaces
   shl eax,2
   invoke RtlMoveMemory,.D3DXMESHCONTAINER.pAdjacency, pAdjacency, eax
   mov eax,.D3DXMESHCONTAINER.NumMaterials
   shl eax,2
   invoke RtlZeroMemory,.D3DXMESHCONTAINER.ppTextures, eax

   ; if materials provided, copy them
   .if NumMaterials > 0    
       mov eax,sizeof D3DXMATERIAL
       mul .D3DXMESHCONTAINER.NumMaterials
       invoke RtlMoveMemory,.D3DXMESHCONTAINER.pMaterials, pMaterials, eax

       xor eax,eax
       mov iMaterial,eax
       .while eax<NumMaterials
           mov esi,pMeshContainer
   mov ebx,sizeof D3DXMATERIAL
           mul ebx
           add eax,.D3DXMESHCONTAINER.pMaterials
           mov pmaterial.eax
           .if .D3DXMATERIAL.pTextureFilename!=NULL
               ;WCHAR strTexturePath;
               ;WCHAR wszBuf;
               ;MultiByteToWideChar( CP_ACP, 0, pMeshContainer->pMaterials.pTextureFilename, -1, wszBuf, MAX_PATH );
               ;wszBuf = L'\0';
               ;DXUTFindDXSDKMediaFileCch( strTexturePath, MAX_PATH, wszBuf );
pop ebx
push ebx
               shl ebx,2
               add ebx,.D3DXMESHCONTAINER.ppTextures
               invoke D3DXCreateTextureFromFile, pd3dDevice,.D3DXMATERIAL.pTextureFilename,ebx
.if FAILED(eax)
   pop ebx
   push ebx
                   shl ebx,2
                   add ebx,.D3DXMESHCONTAINER.ppTextures
                   mov dword ptr,NULL
.endif
               ; don't remember a pointer into the dynamic memory, just forget the name after loading
               mov eax,pmaterial
               mov .D3DXMATERIAL.pTextureFilename,NULL
           .endif
           inc iMaterial
           mov eax,iMaterial
       .endw
   
   .else ; if no materials provided, use a default one
       mov esi,pMeshContainer
       mov ebx,.D3DXMESHCONTAINER.pMaterials
       mov .D3DXMATERIAL.pTextureFilename,NULL
push ebx
invoke RtlZeroMemory,addr .D3DXMATERIAL.MatD3D,sizeof D3DMATERIAL9
pop ebx
fld fHalf
fst .D3DXMATERIAL.MatD3D.Diffuse.r
fst .D3DXMATERIAL.MatD3D.Diffuse.g
fst .D3DXMATERIAL.MatD3D.Diffuse.b
fst .D3DXMATERIAL.MatD3D.Specular.r
fst .D3DXMATERIAL.MatD3D.Specular.g
fstp .D3DXMATERIAL.MatD3D.Specular.b
   .endif

   ; if there is skinning information, save off the required data and then setup for HW skinning
   .if pSkinInfo != NULL    
       ; first save off the SkinInfo and original mesh data
       mov ebx,pMeshContainer
       m2m .D3DXMESHCONTAINER.pSkinInfo , pSkinInfo
       ICall pSkinInfo::ID3DXSkinInfo.AddRef

       mov ebx,pMeshContainer
       m2m .D3DXMESHCONTAINER.pOrigMesh , pMesh
ICall pMesh::ID3DXMesh.AddRef

       ; Will need an array of offset matrices to move the vertices from the figure space to the bone's space
ICall pSkinInfo::ID3DXSkinInfo.GetNumBones
       mov cBones,eax
mov eax,sizeof D3DXMATRIX
       mul cBones
       mov esi,pMeshContainer
       mov .D3DXMESHCONTAINER.pBoneOffsetMatrices,$MemAlloc(eax)
       .if eax==NULL        
           mov hr , E_OUTOFMEMORY
           jmp e_Exit
       .endif

       ; get each of the bone offset matrices so that we don't need to get them later
mov ebx,.D3DXMESHCONTAINER.pBoneOffsetMatrices
xor eax,eax
       mov iBone,eax
       .while eax<cBones
           push ebx
   ICall .D3DXMESHCONTAINER.pSkinInfo::ID3DXSkinInfo.GetBoneOffsetMatrix,iBone
           pop ebx
           mov dword ptr,eax
           add ebx,4
   inc iBone
           mov eax,iBone
       .endw

       ; GenerateSkinnedMesh will take the general skinning information and transform it to a HW friendly version
       mov hr , $invoke (GenerateSkinnedMesh, pd3dDevice, pMeshContainer)
       .if FAILED(eax)
           jmp e_Exit
       .endif
   .endif

   mov eax,ppNewMeshContainer
   m2m dword ptr,pMeshContainer
   return hr

e_Exit:
   SafeRelease pd3dDevice

   ; call Destroy function to properly clean up the memory allocated
   .if pMeshContainer != NULL  
       ICall DestroyMeshContainer,pMeshContainer
   .endif

   return hr
MethodEnd


Wow, that was pretty hefty, but not too difficult .. take a little time to stare at this one for a while.


Posted on 2005-12-28 03:31:52 by Homer
We only have one of the four required interface methods left !!!


//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyMeshContainer()
// Desc:
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER pMeshContainerBase)
{
    UINT iMaterial;
    D3DXMESHCONTAINER_DERIVED *pMeshContainer = (D3DXMESHCONTAINER_DERIVED*)pMeshContainerBase;

    SAFE_DELETE_ARRAY( pMeshContainer->Name );
    SAFE_DELETE_ARRAY( pMeshContainer->pAdjacency );
    SAFE_DELETE_ARRAY( pMeshContainer->pMaterials );
    SAFE_DELETE_ARRAY( pMeshContainer->pBoneOffsetMatrices );

    // release all the allocated textures
    if (pMeshContainer->ppTextures != NULL)
    {
        for (iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++)
        {
            SAFE_RELEASE( pMeshContainer->ppTextures );
        }
    }

    SAFE_DELETE_ARRAY( pMeshContainer->ppTextures );
    SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
    SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
    SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
    SAFE_RELEASE( pMeshContainer->pSkinInfo );
    SAFE_RELEASE( pMeshContainer->pOrigMesh );
    SAFE_DELETE( pMeshContainer );
    return S_OK;
}


And our version,


Method CAllocateHierarchy.DestroyMeshContainer,uses esi,pMeshContainer

    mov esi,pMeshContainer
    SafeFree .D3DXMESHCONTAINER.pName
    SafeFree .D3DXMESHCONTAINER.pAdjacency
    SafeFree .D3DXMESHCONTAINER.pMaterials
    SafeFree .D3DXMESHCONTAINER.pBoneOffsetMatrices

    ; release all the allocated textures
    .if .D3DXMESHCONTAINER.ppTextures != NULL
        xor ecx,ecx
mov ebx,.D3DXMESHCONTAINER.ppTextures
        .while ecx<.D3DXMESHCONTAINER.NumMaterials
            push ebx
            push ecx
            shl ecx,2
            SafeRelease dword ptr
            pop ecx
            pop ebx
        .endw
    .endif

    SafeFree .D3DXMESHCONTAINER.ppTextures
    SafeFree .D3DXMESHCONTAINER.ppBoneMatrixPtrs
    SafeRelease .D3DXMESHCONTAINER.pBoneCombinationBuf
    SafeRelease .D3DXMESHCONTAINER.MeshData.pMesh
    SafeRelease .D3DXMESHCONTAINER.pSkinInfo
    SafeRelease .D3DXMESHCONTAINER.pOrigMesh
    SafeFree pMeshContainer
    return S_OK
MethodEnd


Say, we're beginning to make some progress huh?
So far the only thing that is preventing this useless garbage from compiling is a missing procedure called GenerateSkinnedMesh :)

Any Questions?
Posted on 2005-12-28 03:32:30 by Homer
We have arrived at WinMain in the SDK sample.

From here on, things will be weird, because we will attempt to avoid implementing mountains of unnecessary generic app support code.

Things are also weird because the application-specific code and the skinmesh code are presented together, whereas we would ultimately like to separate SkinMesh class code from the application junk, so that we have a reusable SkinMesh class object we can instance in future apps.. We'll cross each bridge as we come to it, but really its time to start looking at the minimal framework requirements...
Posted on 2005-12-28 03:41:44 by Homer