But I just can't do it...
Here is an extract of my code
AllocHierachy.h
#ifndef ALLOC_MESH_HIERARCHY_H
#define ALLOC_MESH_HIERARCHY_H
#include <d3dx9.h>
class AllocMeshHierarchy : public ID3DXAllocateHierarchy
{
public:
HRESULT STDMETHODCALLTYPE CreateFrame(THIS_ PCSTR Name, D3DXFRAME** ppNewFrame);
HRESULT STDMETHODCALLTYPE CreateMeshContainer(PCSTR Name, const D3DXMESHDATA* pMeshData,
const D3DXMATERIAL* pMaterials, const D3DXEFFECTINSTANCE* pEffectInstances, DWORD NumMaterials,
const DWORD *pAdjacency, ID3DXSkinInfo* pSkinInfo, D3DXMESHCONTAINER** ppNewMeshContainer);
HRESULT STDMETHODCALLTYPE DestroyFrame(THIS_ D3DXFRAME* pFrameToFree);
HRESULT STDMETHODCALLTYPE DestroyMeshContainer(THIS_ D3DXMESHCONTAINER* pMeshContainerBase);
};
#endif // ALLOC_MESH_HIERARCHY_H
AllocHierarchy.cpp
#include "stdafx.h"
#include "AllocMeshHierarchy.h"
#include "SkinnedMesh.h"
void CopyString(const char* input, char** output)
{
if( input )
{
UINT length = (UINT)::strlen(input) + 1; // add 1 for terminating null charater.
*output = new char[length];
::strcpy(*output, input);
}
else
{
*output = 0;
}
}
HRESULT AllocMeshHierarchy::CreateFrame(PCSTR Name, D3DXFRAME **ppNewFrame)
{
FrameEx *frameEx = new FrameEx();
if ( Name ) CopyString ( Name, &frameEx->Name );
else CopyString ("<no name>", &frameEx->Name );
frameEx->pMeshContainer = 0;
frameEx->pFrameSibling = 0;
frameEx->pFrameFirstChild = 0;
D3DXMatrixIdentity (&frameEx->TransformationMatrix);
D3DXMatrixIdentity (&frameEx->toRoot);
*ppNewFrame = frameEx;
return D3D_OK;
}
HRESULT AllocMeshHierarchy::CreateMeshContainer(PCTSTR Name,
const D3DXMESHDATA* pMeshData, const D3DXMATERIAL* pMaterials,
const D3DXEFFECTINSTANCE* pEffectInstances, DWORD NumMaterials,
const DWORD *pAdjacency, ID3DXSkinInfo* pSkinInfo,
D3DXMESHCONTAINER** ppNewMeshContainer)
{
*ppNewMeshContainer = 0;
MeshContainerEx *pMeshContainer = new MeshContainerEx();
ZeroMemory(pMeshContainer, sizeof(MeshContainerEx));
LPDIRECT3DDEVICE9 pDevice;
pMeshData->pMesh->GetDevice(&pDevice);
if ( Name != NULL )
{
CopyString( Name, &pMeshContainer->Name );
}
else
{
CopyString ("<no name>", &pMeshContainer->Name );
}
pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
// pMeshContainer->MeshData.pMesh = pMeshData->pMesh;
// pMeshData->pMesh->AddRef();
if (!(pMeshData->pMesh->GetFVF() & D3DFVF_NORMAL))
{
pMeshData->pMesh->CloneMeshFVF( pMeshData->pMesh->GetOptions(),
pMeshData->pMesh->GetFVF() | D3DFVF_NORMAL,
pDevice, &pMeshContainer->MeshData.pMesh);
D3DXComputeNormals( pMeshContainer->MeshData.pMesh, NULL );
}
else
{
pMeshContainer->MeshData.pMesh = pMeshData->pMesh;
pMeshContainer->MeshData.pMesh->AddRef();
}
DWORD dwNumFaces = pMeshData->pMesh->GetNumFaces();
pMeshContainer->NumMaterials = max (1, NumMaterials);
pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
pMeshContainer->pAdjacency = new DWORD[dwNumFaces * 3];
memcpy ( pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * dwNumFaces * 3);
if (NumMaterials > 0)
{
memcpy ( pMeshContainer->pMaterials, pMaterials, sizeof(D3DXMATERIAL) * NumMaterials);
D3DXMATERIAL *mtrls = pMeshContainer->pMaterials;
for (int i = 0; i < NumMaterials; i++)
{
D3DXMATERIAL mtrl = mtrls[i];
mtrl.MatD3D.Ambient = mtrl.MatD3D.Diffuse;
CopyString (mtrl.pTextureFilename, &mtrls[i].pTextureFilename);
pMeshContainer->mMaterials.push_back(mtrl.MatD3D);
if (mtrl.pTextureFilename != NULL)
{
LPDIRECT3DTEXTURE9 tex;
TCHAR texFilename[200];
_tcscpy (texFilename, "data\\");
_tcscat (texFilename, mtrl.pTextureFilename);
D3DXCreateTextureFromFile(pDevice, texFilename, &tex);
pMeshContainer->mTextures.push_back(tex);
}
}
}
else
{
pMeshContainer->mMaterials.clear();
pMeshContainer->mTextures.clear();
}
if ( pSkinInfo != NULL )
{
pMeshContainer->pSkinInfo = pSkinInfo;
pMeshContainer->pSkinInfo->AddRef();
pMeshContainer->m_pOrigMesh = pMeshData->pMesh;
pMeshContainer->m_pOrigMesh->AddRef();
DWORD dwNumBones = pSkinInfo->GetNumBones();
// pMeshContainer->m_pBoneOffsetMatrices = new D3DXMATRIX[dwNumBones];
for ( DWORD iBone = 0; iBone < dwNumBones; iBone++ )
{
pMeshContainer->m_pBoneOffsetMatrices.push_back(*(pMeshContainer->pSkinInfo->GetBoneOffsetMatrix(iBone)));
}
}
*ppNewMeshContainer = pMeshContainer;
pMeshContainer = NULL;
pDevice->Release();
pDevice = NULL;
return D3D_OK;
}
HRESULT AllocMeshHierarchy::DestroyFrame(D3DXFRAME *pFrameToFree)
{
delete [] pFrameToFree->Name;
pFrameToFree->Name = NULL;
delete pFrameToFree;
pFrameToFree = NULL;
return S_OK;
}
HRESULT AllocMeshHierarchy::DestroyMeshContainer(D3DXMESHCONTAINER *pMeshContainerBase)
{
MeshContainerEx *meshContainer = (MeshContainerEx *) pMeshContainerBase;
delete [] meshContainer->Name;
delete [] meshContainer->pAdjacency;
delete [] meshContainer->pMaterials;
delete [] meshContainer->m_pAttributeTable;
//delete [] meshContainer->m_pBoneOffsetMatrices;
//delete [] meshContainer->m_pBoneMatrices;
//delete [] meshContainer->m_ppBoneMatrixPtrs;
ReleaseCOM(meshContainer->m_pBoneCombinationBuf);
ReleaseCOM(meshContainer->m_pOrigMesh);
ReleaseCOM(meshContainer->MeshData.pMesh);
ReleaseCOM(meshContainer->pSkinInfo);
delete meshContainer;
return D3D_OK;
}
skinnedmesh.h
#ifndef SKINNED_MESH_H
#define SKINNED_MESH_H
#include "d3dUtil.h"
#define ReleaseCOM(x) { if(x){ x->Release();x = 0; } }
struct FrameEx : public D3DXFRAME
{
D3DXMATRIX toRoot;
};
struct MeshContainerEx : public D3DXMESHCONTAINER
{
std::vector<D3DMATERIAL9> mMaterials;
std::vector<IDirect3DTexture9*> mTextures;
LPD3DXMESH m_pOrigMesh;
std::vector<D3DXMATRIX> m_pBoneOffsetMatrices;
std::vector<D3DXMATRIX*> m_ppBoneMatrixPtrs;
DWORD m_dwNumPaletteEntries;
DWORD m_dwNumInfl;
LPD3DXATTRIBUTERANGE m_pAttributeTable;
LPD3DXBUFFER m_pBoneCombinationBuf;
DWORD dwNumBones;
std::vector<D3DXMATRIX> m_pBoneMatrices;
DWORD dwNumPaletteEntries;//?
DWORD m_dwNumAttributeGroups;
};
class SkinnedMesh
{
public:
SkinnedMesh(std::string XFilename);
~SkinnedMesh();
UINT numBones();
const D3DXMATRIX* getFinalXFormArray();
protected:
D3DXFRAME* findNodeWithMesh (D3DXFRAME *frame);
bool hasNormals(ID3DXMesh *mesh);
bool GenerateSkinnedMesh(LPD3DXFRAME pFrame);
void MapFramesToBones(LPD3DXFRAME pCurFrame);
void UpdateFrames();
public:
LPD3DXFRAME GetFrameRoot() { return mRoot; }
LPD3DXANIMATIONCONTROLLER GetAnimController() { return mAnimCtrl; }
protected:
D3DXFRAME* mRoot;
ID3DXAnimationController *mAnimCtrl;
static const int MAX_NUM_BONES_SUPPORTED = 35;
};
#endif
SkinnedMesh.cpp
#include "stdafx.h"
#include "AllocMeshHierarchy.h"
#include "SkinnedMesh.h"
extern LPDIRECT3DDEVICE9 g_pDevice;
SkinnedMesh::SkinnedMesh(std::string XFilename)
: mRoot(0), mAnimCtrl(0)
{
AllocMeshHierarchy allocMeshHierarchy;
HRESULT hr;
if (FAILED (hr=D3DXLoadMeshHierarchyFromX(XFilename.c_str(), NULL, g_pDevice,
&allocMeshHierarchy, NULL, (LPD3DXFRAME *)&mRoot, &mAnimCtrl)))
GenerateSkinnedMesh(mRoot);
MapFramesToBones(mRoot);
}
SkinnedMesh::~SkinnedMesh()
{
AllocMeshHierarchy alloc;
D3DXFrameDestroy(mRoot, &alloc);
ReleaseCOM(mAnimCtrl);
}
void SkinnedMesh::MapFramesToBones(LPD3DXFRAME pCurFrame)
{
UINT iBone, cBones;
FrameEx *pFrame = (FrameEx *)pCurFrame;
if (pCurFrame->pMeshContainer != NULL)
{
MeshContainerEx *pMeshContainer = (MeshContainerEx*) pCurFrame->pMeshContainer;
if (pMeshContainer->pSkinInfo != NULL)
{
cBones = pMeshContainer->pSkinInfo->GetNumBones();
//pMeshContainer->m_ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
for (iBone = 0; iBone < cBones; iBone++)
{
pFrame = (FrameEx *)D3DXFrameFind (mRoot, pMeshContainer->pSkinInfo->GetBoneName(iBone));
pMeshContainer->m_ppBoneMatrixPtrs.push_back(&pFrame->toRoot);
}
}
}
if (pCurFrame->pFrameFirstChild != NULL)
MapFramesToBones(pCurFrame->pFrameFirstChild);
if (pCurFrame->pFrameSibling != NULL)
MapFramesToBones(pCurFrame->pFrameSibling);
return;
}
bool SkinnedMesh::GenerateSkinnedMesh(LPD3DXFRAME pFrame)
{
if (pFrame == NULL)
return false;
MeshContainerEx *pCurMeshContainer = (MeshContainerEx *)pFrame->pMeshContainer;
while (pCurMeshContainer != NULL)
{
if (pCurMeshContainer->pSkinInfo != NULL)
{
pCurMeshContainer->MeshData.pMesh->Release();
if (FAILED (pCurMeshContainer->pSkinInfo->ConvertToBlendedMesh(
pCurMeshContainer->m_pOrigMesh,
D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,
pCurMeshContainer->pAdjacency,
NULL, NULL, NULL,
&pCurMeshContainer->m_dwNumInfl,
&pCurMeshContainer->m_dwNumAttributeGroups,
&pCurMeshContainer->m_pBoneCombinationBuf,
&pCurMeshContainer->MeshData.pMesh
)))
return false;
}
pCurMeshContainer = (MeshContainerEx *)pCurMeshContainer->pNextMeshContainer;
}
if (pFrame->pFrameFirstChild != NULL)
if (!GenerateSkinnedMesh (pFrame->pFrameFirstChild))
return false;
if (pFrame->pFrameSibling != NULL)
if (!GenerateSkinnedMesh (pFrame->pFrameSibling))
return false;
return true;
}
Display portion of my code base
DWORD CDynamicObject::m_dwNumTriangle = 0;
CDynamicObject::CDynamicObject(void) :
m_vPos(0.0f, 0.0f, 0.0f),
m_vScale(1.0f, 1.0f, 1.0f),
m_vRot(0.0f, 0.0f, 0.0f),
m_vRotCenter(0.0f, 0.0f, 0.0f),
m_vScaleCenter(0.0f, 0.0f, 0.0f)
{
m_pDevice = NULL;
m_pAnimController = NULL;
m_pMesh = NULL;
D3DXMatrixIdentity(&m_matWorld);
m_pCombineFrameMatrix = NULL;
m_pCombineObjectMatrix = NULL;
D3DXQuaternionIdentity(&m_quatRot);
m_pTrackArray = NULL;
SetWalking(false);
mhTech = g_pEffect->GetTechniqueByName("VBlend2Tech");
mhWVP = g_pEffect->GetParameterByName(0, "gWVP");
mhWorldInvTrans = g_pEffect->GetParameterByName(0, "gWorldInvTrans");
mhFinalXForms = g_pEffect->GetParameterByName(0, "gFinalXForms");
mhMtrl = g_pEffect->GetParameterByName(0, "gMtrl");
mhLight = g_pEffect->GetParameterByName(0, "gLight");
mhEyePos = g_pEffect->GetParameterByName(0, "gEyePosW");
mhWorld = g_pEffect->GetParameterByName(0, "gWorld");
mhTex = g_pEffect->GetParameterByName(0, "gTex");
g_pEffect->SetTechnique(mhTech);
}
CDynamicObject::~CDynamicObject(void)
{
}
bool CDynamicObject::Create(LPDIRECT3DDEVICE9 pDevice, SkinnedMesh *pMesh, float fAnimSpeed)
{
if(pMesh == NULL)
return false;
m_pDevice = pDevice;
m_pMesh = pMesh;
if(m_pMesh->GetAnimController() != NULL)
{
DWORD dwNumAnimSet = m_pMesh->GetAnimController()->GetMaxNumAnimationSets();
// create a copy of the animation controller
LPD3DXANIMATIONCONTROLLER pAC = m_pMesh->GetAnimController();
pAC->CloneAnimationController( pAC->GetMaxNumAnimationOutputs(), // >GetMaxNumMatrices(),//>GetMaxNumAnimationOutputs(),
pAC->GetMaxNumAnimationSets(),
pAC->GetMaxNumTracks(),
pAC->GetMaxNumEvents(),
&m_pAnimController );
// fill up all tracks with animation sets
for(DWORD i = 0; i < dwNumAnimSet; i++)
{
LPD3DXANIMATIONSET pSet;
m_pAnimController->GetAnimationSet(i, &pSet);
// associate each animation set to tracks
m_pAnimController->SetTrackAnimationSet(i, pSet);
// set track's speed
SetTrackDesc(i, fAnimSpeed);
pSet->Release();
pSet = NULL;
}
// set default, ie activate first track
SetTrackDesc(0, fAnimSpeed, 1.0f);
m_dwDefaultTrack = m_dwLastTrack = m_dwCurrentTrack = 0;
}
return true;
}
void CDynamicObject::Update(float dt)
{
if(m_vRot.x != 0.0f || m_vRot.y != 0.0f || m_vRot.z != 0.0f)
{
D3DXQuaternionRotationYawPitchRoll(&m_quatRot, m_vRot.x, m_vRot.y, m_vRot.z);
D3DXMatrixTransformation(&m_matWorld, &m_vScaleCenter, NULL, &m_vScale,
&m_vRotCenter, &m_quatRot, &m_vPos);
}
else
D3DXMatrixTransformation(&m_matWorld, &m_vScaleCenter, NULL, &m_vScale,
NULL, NULL, &m_vPos);
// check if current object is attach to other object
if(m_pCombineFrameMatrix != NULL)
m_matWorld *= *m_pCombineFrameMatrix;
if(m_pAnimController != NULL)
{
m_pAnimController->AdvanceTime(dt, 0);
}
UpdateFrame(m_pMesh->GetFrameRoot(), &m_matWorld); // No need to do this???
}
void CDynamicObject::Render()
{
// render all frames
RenderFrame(m_pMesh->GetFrameRoot());
}
void CDynamicObject::UpdateFrame(LPD3DXFRAME pFrame, D3DXMATRIX *pMat)
{
FrameEx *pCurFrame = (FrameEx *) pFrame;
if (pMat != NULL)
D3DXMatrixMultiply (&pCurFrame->toRoot, &pCurFrame->TransformationMatrix, pMat);
else
pCurFrame->toRoot = pCurFrame->TransformationMatrix;
if (pCurFrame->pFrameFirstChild != NULL)
UpdateFrame(pCurFrame->pFrameFirstChild, &pCurFrame->toRoot);
if (pCurFrame->pFrameSibling != NULL)
UpdateFrame(pCurFrame->pFrameSibling, pMat);
// Adjust gEyePosW, same as global view matrix position
buildViewMtx();
}
void CDynamicObject::buildViewMtx()
{
D3DXMATRIX view = g_View;
D3DXVECTOR3 v(view._41, view._42, view._43);
g_pEffect->SetValue(mhEyePos, &v, sizeof(D3DXVECTOR3));
}
void CDynamicObject::RenderFrame(LPD3DXFRAME pFrame) // Stack overrun <img src='http://public.gamedev.net/public/style_emoticons/<#EMO_DIR#>/sad.gif' class='bbc_emoticon' alt=':(' />
{
FrameEx *bone = (FrameEx *) m_pMesh->GetFrameRoot();
if (bone->pMeshContainer != NULL)
{
MeshContainerEx *boneMesh = (MeshContainerEx *) bone->pMeshContainer;
g_pEffect->SetMatrixArray(mhFinalXForms, (D3DXMATRIX*) &boneMesh->m_pBoneMatrices[0], boneMesh->dwNumBones);
for (int i = 0; i < boneMesh->m_dwNumAttributeGroups; i++)
{
g_pEffect->SetValue(mhLight, &mLight, sizeof(DirLight));
g_pEffect->SetMatrix(mhWVP, &(m_matWorld*g_View*g_Proj));
D3DXMATRIX worldInvTrans;
D3DXMatrixInverse (&worldInvTrans, 0, &m_matWorld);
D3DXMatrixTranspose (&worldInvTrans, &worldInvTrans);
g_pEffect->SetMatrix(mhWorldInvTrans, &worldInvTrans);
g_pEffect->SetMatrix(mhWorld, &m_matWorld);
Mtrl m;
m.ambient = boneMesh->mMaterials[i].Ambient;
m.diffuse = boneMesh->mMaterials[i].Diffuse;
m.spec = boneMesh->mMaterials[i].Specular;
m.specPower = boneMesh->mMaterials[i].Power;
g_pEffect->SetValue (mhMtrl, &m, sizeof(Mtrl));
g_pEffect->SetTexture(mhTex, boneMesh->mTextures[i]);
UINT numPasses = 0;
g_pEffect->Begin(&numPasses, 0);
g_pEffect->BeginPass(0);
boneMesh->MeshData.pMesh->DrawSubset(i);
g_pEffect->EndPass();
g_pEffect->End();
}
}
if (bone->pFrameSibling != NULL) RenderFrame ((FrameEx *)bone->pFrameSibling);
if (bone->pFrameFirstChild != NULL) RenderFrame ((FrameEx *)bone->pFrameFirstChild);
}
The shader part:
struct Mtrl
{
float4 ambient;
float4 diffuse;
float4 spec;
float specPower;
};
struct DirLight
{
float4 ambient;
float4 diffuse;
float4 spec;
float3 dirW;
};
uniform extern float4x4 gWorld;
uniform extern float4x4 gWorldInvTrans;
uniform extern float4x4 gWVP;
uniform extern float4x4 gFinalXForms[35];
uniform extern Mtrl gMtrl;
uniform extern DirLight gLight;
uniform extern float3 gEyePosW;
uniform extern texture gTex;
sampler TexS = sampler_state
{
Texture = <gTex>;
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
struct OutputVS
{
float4 posH : POSITION0;
float3 normalW : TEXCOORD0;
float3 toEyeW : TEXCOORD1;
float2 tex0 : TEXCOORD2;
};
OutputVS VBlend2VS ( float3 posL : POSITION0,
float3 normalL : NORMAL0,
float2 tex0 : TEXCOORD0,
float weight0 : BLENDWEIGHT0,
int4 boneIndex : BLENDINDICES0 )
{
OutputVS outVS = (OutputVS) 0;
float weight1 = 1.0f - weight0;
float4 p = weight0 * mul(float4(posL, 1.0f), gFinalXForms[boneIndex[0]]);
p += weight1 * mul(float4(posL, 1.0f), gFinalXForms[boneIndex[1]]);
p.w = 1.0f;
// We can use the same matrix to transform normals since we are assuming
// no scaling (i.e., rigid-body).
float4 n = weight0 * mul(float4(normalL, 0.0f), gFinalXForms[boneIndex[0]]);
n += weight1 * mul(float4(normalL, 0.0f), gFinalXForms[boneIndex[1]]);
n.w = 0.0f;
// Transform vertex position to world space.
outVS.normalW = mul (n, gWorldInvTrans).xyz;
// Transform vertex position to world space.
float3 posW = mul (p, gWorld).xyz;
// Compute the vector from the vertex to the eye.
outVS.toEyeW = gEyePosW - posW;
// Transform to homogeneous clip space.
outVS.posH = mul (p, gWVP);
outVS.tex0 = tex0;
return outVS;
}
float4 VBlend2PS(float3 normalW : TEXCOORD0, float3 toEyeW : TEXCOORD1, float2 tex0 : TEXCOORD2) : COLOR
{
// Interpolated normals can become unnormal--so normalize.
normalW = normalize(normalW);
toEyeW = normalize(toEyeW);
// Light vector is opposite the direction of the light.
float3 lightVecW = -gLight.dirW;
// Compute the reflection vector.
float3 r = reflect(-lightVecW, normalW);
// Determine how much (if any) specular light makes it into the eye.
float t = pow(max(dot(r, toEyeW), 0.0f), gMtrl.specPower);
// Determine the diffuse light intensity that strikes the vertex.
float s = max(dot(lightVecW, normalW), 0.0f);
// Compute the ambient, diffuse and specular terms separatly.
float3 spec = t*(gMtrl.spec*gLight.spec).rgb;
float3 diffuse = s*(gMtrl.diffuse*gLight.diffuse).rgb;
float3 ambient = gMtrl.ambient*gLight.ambient;
// Get the texture color.
float4 texColor = tex2D(TexS, tex0);
// Combine the color from lighting with the texture color.
float3 color = (ambient + diffuse)*texColor.rgb + spec;
// Sum all the terms together and copy over the diffuse alpha.
return float4(color, gMtrl.diffuse.a*texColor.a);
}
technique VBlend2Tech
{
pass P0
{
// Specify the vertex and pixel shader associated with this pass.
vertexShader = compile vs_2_0 VBlend2VS();
pixelShader = compile ps_2_0 VBlend2PS();
}
}
Note that I have a stack overrun in the last method. But you can ignore it for the moment. I'll give that a fix later
So you get a general picture of the flow of my engine. Please leave comments where you feel appropriate.
Thanks in advance
Jack






