Jump to content
  • Advertisement
Sign in to follow this  
Ripiz

[C++, DX9] GPU skinning problem (VS messes up vertexes)

This topic is 2989 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I tried to make GPU skinning/animations based on MultiAnimation sample in DX9 SDK, however shader messes up all vertexes.

Shader file(s):

skin.fx

#include "skin.vsh"


float4 lhtDir = { -0.57735026f, -0.57735026f, -0.57735026f, 1.0f }; // Light Direction
float4 lightDiffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; // Light Diffuse
float4 MaterialAmbient : MATERIALAMBIENT = { 1.0f, 1.0f, 1.0f, 1.0f };
float4 MaterialDiffuse : MATERIALDIFFUSE = { 1.0f, 1.0f, 1.0f, 1.0f };

float4x4 g_mWorld : WORLD;
float4x4 g_mViewProj : VIEWPROJECTION;

texture g_txScene;


//--------------------------------------------------------------------------------------
// Texture samplers
//--------------------------------------------------------------------------------------
sampler g_samScene =
sampler_state
{
Texture = <g_txScene>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Point;
};


//--------------------------------------------------------------------------------------
struct VS_INPUT
{
float4 Pos : POSITION;
float3 BlendWeights : BLENDWEIGHT;
float4 BlendIndices : BLENDINDICES;
float3 Normal : NORMAL;
float3 Tex0 : TEXCOORD0;
};

struct VS_OUTPUT
{
float4 Pos : POSITION;
float4 Diffuse : COLOR0;
float2 Tex0 : TEXCOORD0;
};


VS_OUTPUT VertScene( float4 Pos : POSITION,
float3 Normal : NORMAL,
float2 Tex0 : TEXCOORD0 )
{
VS_OUTPUT o;

o.Pos = mul( Pos, g_mWorld );
o.Pos = mul( o.Pos, g_mViewProj );
o.Tex0 = Tex0;
float3 N = normalize( mul( Normal, (float3x3)g_mWorld ) );

// Always fully lit the floor
o.Diffuse = 1.0f;

return o;
}

float4 PixScene( float4 Diffuse : COLOR0,
float2 Tex0 : TEXCOORD0 ) : COLOR0
{
return tex2D( g_samScene, Tex0 ) * Diffuse;
}


VS_OUTPUT VertSkinning( VS_INPUT i, uniform int iNumBones )
{
VS_OUTPUT o;
float3 Pos = 0.0f;
float3 Normal = 0.0f;
float LastWeight = 0.0f;

// skin VB inputs
VS_SKIN_INPUT vsi = { i.Pos, i.BlendWeights, i.BlendIndices, i.Normal };
VS_SKIN_OUTPUT vso = VS_Skin( vsi, iNumBones );

// transform position from world space into view and then projection space
o.Pos = mul( float4( vso.vPos.xyz, 1.0f ), g_mViewProj );

// normalize normals
Normal = normalize( vso.vNor );

// Shade (Ambient + etc.)
o.Diffuse = float4( MaterialAmbient.xyz + saturate( dot( Normal, lhtDir.xyz ) ) * MaterialDiffuse.xyz, 1.0 );

// copy the input texture coordinate through
o.Tex0 = i.Tex0.xy;

return o;
}


int CurNumBones = 2;

VertexShader vsArray20[ 4 ] = { compile vs_2_0 VertSkinning( 1 ),
compile vs_2_0 VertSkinning( 2 ),
compile vs_2_0 VertSkinning( 3 ),
compile vs_2_0 VertSkinning( 4 ) };


//--------------------------------------------------------------------------------------
// Techniques
//--------------------------------------------------------------------------------------


technique RenderScene
{
pass p0
{
VertexShader = compile vs_2_0 VertScene();
PixelShader = compile ps_2_0 PixScene();
}
}


technique Skinning
{
pass p0
{
VertexShader = ( vsArray20[ CurNumBones ] );
PixelShader = compile ps_2_0 PixScene();
}
}




skin.vsh

#ifndef VS_SKIN_VSH
#define VS_SKIN_VSH


#ifndef MATRIX_PALETTE_SIZE_DEFAULT
#define MATRIX_PALETTE_SIZE_DEFAULT 70
#endif

const int MATRIX_PALETTE_SIZE = MATRIX_PALETTE_SIZE_DEFAULT;
float4x3 amPalette[ MATRIX_PALETTE_SIZE_DEFAULT ];


//----------------------------------------------------------------------------
// Shader body - VS_ Skin
//----------------------------------------------------------------------------

// define the inputs -- caller must fill this, usually right from the VB
struct VS_SKIN_INPUT
{
float4 vPos;
float3 vBlendWeights;
float4 vBlendIndices;
float3 vNor;
};

// return skinned position and normal
struct VS_SKIN_OUTPUT
{
float4 vPos;
float3 vNor;
};

// call this function to skin VB position and normal
VS_SKIN_OUTPUT VS_Skin( const VS_SKIN_INPUT vInput, int iNumBones )
{
VS_SKIN_OUTPUT vOutput = (VS_SKIN_OUTPUT) 0;

float fLastWeight = 1.0;
float fWeight;
float afBlendWeights[ 3 ] = (float[ 3 ]) vInput.vBlendWeights;
int aiIndices[ 4 ] = (int[ 4 ]) D3DCOLORtoUBYTE4( vInput.vBlendIndices );

for( int iBone = 0; (iBone < 3) && (iBone < iNumBones - 1); ++ iBone )
{
fWeight = afBlendWeights[ iBone ];
fLastWeight -= fWeight;
vOutput.vPos.xyz += mul( vInput.vPos, amPalette[ aiIndices[ iBone ] ] ) * fWeight;
vOutput.vNor += mul( vInput.vNor, amPalette[ aiIndices[ iBone ] ] ) * fWeight;
}

vOutput.vPos.xyz += mul( vInput.vPos, amPalette[ aiIndices[ iNumBones - 1 ] ] ) * fLastWeight;
vOutput.vNor += mul( vInput.vNor, amPalette[ aiIndices[ iNumBones - 1 ] ] ) * fLastWeight;

return vOutput;
}


#endif // #ifndef VS_SKIN_VSH




My code, hopefully nothing was forgotten (please ignore missing deconstructor):

_header.h

//includes DirectX headers, string, vector and etc
typedef D3DXVECTOR2 Vector2;
typedef D3DXVECTOR3 Vector3;
typedef D3DXVECTOR4 Vector4;
typedef D3DXMATRIX Matrix;




AllocateHierarchy.h

#pragma once
#include "_header.h"
#include "Material.h"
extern Handler_Texture *Textures;
extern ID3DXEffect *fxSkin;

struct Container : public D3DXMESHCONTAINER{
vector<Material> materials;
LPD3DXMESH m_pWorkingMesh;
D3DXMATRIX* m_amxBoneOffsets;
D3DXMATRIX** m_apmxBonePointers;
D3DXMATRIX* m_amxWorkingPalette;

DWORD m_dwNumPaletteEntries;
DWORD m_dwMaxNumFaceInfls;
DWORD m_dwNumAttrGroups;
LPD3DXBUFFER m_pBufBoneCombos;

void SetupBonePtrs( LPD3DXFRAME frame );
};

class AllocateHierarchy : public ID3DXAllocateHierarchy{
// callback to create a D3DXFRAME-derived object and initialize it
STDMETHOD( CreateFrame )( THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame );
// callback to create a D3DXMESHCONTAINER-derived object and initialize it
STDMETHOD( CreateMeshContainer )( THIS_ LPCSTR Name, CONST D3DXMESHDATA * pMeshData,
CONST D3DXMATERIAL * pMaterials, CONST D3DXEFFECTINSTANCE * pEffectInstances,
DWORD NumMaterials, CONST DWORD * pAdjacency, LPD3DXSKININFO pSkinInfo,
LPD3DXMESHCONTAINER * ppNewMeshContainer );
// callback to release a D3DXFRAME-derived object
STDMETHOD( DestroyFrame )( THIS_ LPD3DXFRAME pFrameToFree );
// callback to release a D3DXMESHCONTAINER-derived object
STDMETHOD( DestroyMeshContainer )( THIS_ LPD3DXMESHCONTAINER pMeshContainerToFree );
};




AllocateHierarchy.cpp
#include "AllocateHierarchy.h"

//-----------------------------------------------------------------------------
// Name: HeapCopy()
// Desc: Allocate buffer in memory and copy the content of sName to the
// buffer, then return the address of the buffer.
//-----------------------------------------------------------------------------
CHAR* HeapCopy( CHAR* sName ){
DWORD dwLen = ( DWORD )strlen( sName ) + 1;
CHAR* sNewName = new CHAR[ dwLen ];
if( sNewName )
strcpy_s( sNewName, dwLen, sName );
return sNewName;
}

//-----------------------------------------------------------------------------
// Name: CMultiAnimAllocateHierarchy::CreateFrame()
// Desc: Called by D3DX during the loading of a mesh hierarchy. The app can
// customize its behavior. At a minimum, the app should allocate a
// D3DXFRAME or a child of it and fill in the Name member.
//-----------------------------------------------------------------------------
HRESULT AllocateHierarchy::CreateFrame( THIS_ LPCSTR Name, LPD3DXFRAME * ppFrame ){
*ppFrame = NULL;

D3DXFRAME * pFrame = new D3DXFRAME;
ZeroMemory( pFrame, sizeof( D3DXFRAME ) );

if(Name)
pFrame->Name = ( CHAR * ) HeapCopy( ( CHAR * ) Name );
else{
// TODO: Add a counter to append to the string below
// so that we are using a different name for
// each bone.
pFrame->Name = (CHAR*)HeapCopy("<no_name>");
}

*ppFrame = pFrame;
return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CMultiAnimAllocateHierarchy::CreateMeshContainer()
// Desc: Called by D3DX during the loading of a mesh hierarchy. At a minumum,
// the app should allocate a D3DXMESHCONTAINER or a child of it and fill
// in the members based on the parameters here. The app can further
// customize the allocation behavior here. In our case, we initialize
// m_amxBoneOffsets from the skin info for convenience reason.
// Then we call ConvertToIndexedBlendedMesh to obtain a new mesh object
// that's compatible with the palette size we have to work with.
//-----------------------------------------------------------------------------
HRESULT AllocateHierarchy::CreateMeshContainer( THIS_ LPCSTR Name,
CONST D3DXMESHDATA *pMeshData,
CONST D3DXMATERIAL *pMaterials,
CONST D3DXEFFECTINSTANCE *pEffectInstances,
DWORD NumMaterials,
CONST DWORD *pAdjacency,
LPD3DXSKININFO pSkinInfo,
LPD3DXMESHCONTAINER *ppNewMeshContainer ){

Container * pMC = new Container;
ZeroMemory( pMC, sizeof( Container ) );
*ppNewMeshContainer = pMC;

if(!pSkinInfo)
return S_OK;

if( Name )
pMC->Name = ( CHAR * ) HeapCopy( ( CHAR * ) Name );
else
pMC->Name = ( CHAR * ) HeapCopy( "<no_name>" );

// copy the mesh over
pMC->MeshData.Type = pMeshData->Type;
pMC->MeshData.pMesh = pMeshData->pMesh;
pMC->MeshData.pMesh->AddRef();

DWORD dwNumFaces = pMC->MeshData.pMesh->GetNumFaces();
pMC->pAdjacency = new DWORD[ 3 * dwNumFaces ];

CopyMemory( pMC->pAdjacency, pAdjacency, 3 * sizeof( DWORD ) * dwNumFaces );

// ignore effects instances
pMC->pEffects = NULL;

// alloc and copy materials
pMC->NumMaterials = max( 1, NumMaterials );

if( NumMaterials > 0 ){
for( DWORD i = 0; i < NumMaterials; ++ i ){
D3DXMATERIAL M = pMaterials;
Material mat;

mat.Ambient.a=1.0f;
mat.Ambient.r=1.0f;
mat.Ambient.g=1.0f;
mat.Ambient.b=1.0f;

mat.Diffuse.a=1.0f;
mat.Diffuse.r=1.0f;
mat.Diffuse.g=1.0f;
mat.Diffuse.b=1.0f;

mat.Emissive.a=0.5f;
mat.Emissive.r=0.5f;
mat.Emissive.g=0.5f;
mat.Emissive.b=0.5f;

mat.Specular = pMaterials.MatD3D.Specular;

mat.Power=1.0f;

if(pMaterials.pTextureFilename){
string name = pMaterials.pTextureFilename;
size_t found;
found=name.find_last_of("/\\");
name = name.substr(found+1);
mat.texture=Textures->GetTexture(name);
}
else
mat.texture=NULL;
pMC->materials.push_back(mat);
}
}
else{
Material mat;

mat.Ambient.a=1.0f;
mat.Ambient.r=1.0f;
mat.Ambient.g=1.0f;
mat.Ambient.b=1.0f;

mat.Diffuse.a=1.0f;
mat.Diffuse.r=1.0f;
mat.Diffuse.g=1.0f;
mat.Diffuse.b=1.0f;

mat.Emissive.a=0.5f;
mat.Emissive.r=0.5f;
mat.Emissive.g=0.5f;
mat.Emissive.b=0.5f;

mat.Specular.a=0.0f;
mat.Specular.r=0.0f;
mat.Specular.g=0.0f;
mat.Specular.b=0.0f;

mat.Power=1.0f;

mat.texture=NULL;

pMC->materials.push_back(mat);
}

// save the skininfo object
pMC->pSkinInfo = pSkinInfo;
pSkinInfo->AddRef();

// Get the bone offset matrices from the skin info
pMC->m_amxBoneOffsets = new D3DXMATRIX[ pSkinInfo->GetNumBones() ];
for( DWORD i = 0; i < pSkinInfo->GetNumBones(); ++ i )
pMC->m_amxBoneOffsets[ i ] = * ( D3DXMATRIX * ) pSkinInfo->GetBoneOffsetMatrix( i );

//
// Determine the palette size we need to work with, then call ConvertToIndexedBlendedMesh
// to set up a new mesh that is compatible with the palette size.
//

int iPaletteSize;
fxSkin->GetInt("MATRIX_PALETTE_SIZE", &iPaletteSize);

pMC->m_dwNumPaletteEntries = iPaletteSize;

// generate the skinned mesh - creates a mesh with blend weights and indices
pMC->pSkinInfo->ConvertToIndexedBlendedMesh( pMC->MeshData.pMesh,
D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,
pMC->m_dwNumPaletteEntries,
pMC->pAdjacency,
NULL,
NULL,
NULL,
&pMC->m_dwMaxNumFaceInfls,
&pMC->m_dwNumAttrGroups,
&pMC->m_pBufBoneCombos,
&pMC->m_pWorkingMesh );

pMC->m_amxWorkingPalette = new D3DXMATRIX[ pMC->m_dwNumPaletteEntries ];

// ensure the proper vertex format for the mesh
DWORD dwOldFVF = pMC->m_pWorkingMesh->GetFVF();
DWORD dwNewFVF = ( dwOldFVF & D3DFVF_POSITION_MASK ) | D3DFVF_NORMAL | D3DFVF_TEX1 | D3DFVF_LASTBETA_UBYTE4;
if( dwNewFVF != dwOldFVF ){
LPD3DXMESH pMesh;
pMC->m_pWorkingMesh->CloneMeshFVF( pMC->m_pWorkingMesh->GetOptions(), dwNewFVF, device, &pMesh );

pMC->m_pWorkingMesh->Release();
pMC->m_pWorkingMesh = pMesh;

// if the loaded mesh didn't contain normals, compute them here
if( ! ( dwOldFVF & D3DFVF_NORMAL ) )
D3DXComputeNormals( pMC->m_pWorkingMesh, NULL );
}

// Interpret the UBYTE4 as a D3DCOLOR.
// The GeForce3 doesn't support the UBYTE4 decl type. So, we convert any
// blend indices to a D3DCOLOR semantic, and later in the shader convert
// it back using the D3DCOLORtoUBYTE4() intrinsic. Note that we don't
// convert any data, just the declaration.
D3DVERTEXELEMENT9 pDecl[ MAX_FVF_DECL_SIZE ];
D3DVERTEXELEMENT9 * pDeclCur;
pMC->m_pWorkingMesh->GetDeclaration( pDecl );

pDeclCur = pDecl;
while( pDeclCur->Stream != 0xff ){
if( ( pDeclCur->Usage == D3DDECLUSAGE_BLENDINDICES ) && ( pDeclCur->UsageIndex == 0 ) )
pDeclCur->Type = D3DDECLTYPE_D3DCOLOR;
pDeclCur++;
}

pMC->m_pWorkingMesh->UpdateSemantics( pDecl );

return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CMultiAnimAllocateHierarchy::DestroyFrame()
// Desc: Called by D3DX during the release of a mesh hierarchy. Here we should
// free all resources allocated in CreateFrame().
//-----------------------------------------------------------------------------
HRESULT AllocateHierarchy::DestroyFrame( THIS_ LPD3DXFRAME pFrameToFree ){
D3DXFRAME *pFrame = pFrameToFree;
if(pFrame->Name)
delete[] pFrame->Name;
delete pFrame;
return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CMultiAnimAllocateHierarchy::DestroyMeshContainer()
// Desc: Called by D3DX during the release of a mesh hierarchy. Here we should
// free all resources allocated in CreateMeshContainer().
//-----------------------------------------------------------------------------
HRESULT AllocateHierarchy::DestroyMeshContainer( THIS_ LPD3DXMESHCONTAINER pMeshContainerToFree ){
Container * pMC = ( Container * ) pMeshContainerToFree;

if( pMC->Name )
delete [] pMC->Name;

if( pMC->MeshData.pMesh )
pMC->MeshData.pMesh->Release();

if( pMC->pAdjacency )
delete [] pMC->pAdjacency;

if( pMC->pMaterials )
delete [] pMC->pMaterials;

if( pMC->pSkinInfo )
pMC->pSkinInfo->Release();

if( pMC->m_amxBoneOffsets )
delete [] pMC->m_amxBoneOffsets;

if( pMC->m_pWorkingMesh )
{
pMC->m_pWorkingMesh->Release();
pMC->m_pWorkingMesh = NULL;
}

pMC->m_dwNumPaletteEntries = 0;
pMC->m_dwMaxNumFaceInfls = 0;
pMC->m_dwNumAttrGroups = 0;

if( pMC->m_pBufBoneCombos )
{
pMC->m_pBufBoneCombos->Release();
pMC->m_pBufBoneCombos = NULL;
}

if( pMC->m_apmxBonePointers )
delete [] pMC->m_apmxBonePointers;

delete pMeshContainerToFree;

return S_OK;
}




Handler_Animation.h
#pragma once
#include "_header.h"
#include "AllocateHierarchy.h"

class AnimationInstance;

class Animation{
void Load();
void DrawFrames(D3DXFRAME *frame);
void DrawMeshFrame(D3DXFRAME *frame);
void UpdateFrame(LPD3DXFRAME frame, Matrix *mx);
void SetupBones(LPD3DXFRAME pFrame);

LPD3DXANIMATIONCONTROLLER baseController;
float time;
public:
Animation(string filename);
void AddTime(float time);
void Unload();
void Render(Matrix *mx);
AnimationInstance* NewInstance();
void UpdateFrames(Matrix *mx);

string filename;
LPD3DXFRAME baseFrame;
};

class Handler_Animation{
vector<Animation*> animations;
public:
Animation* GetAnimation(string filename);
void AddTime(float time);
void UnloadAnimations();
void DestroyAnimations();
};

class AnimationInstance{
public:
AnimationInstance();
void Render(Matrix *mx);
void Time(float time);

LPD3DXANIMATIONCONTROLLER controller;
Animation *animation;
};



Handler_Animation.cpp
#include "Handler_Animation.h"

Animation::Animation(string file){
filename = file;
baseFrame = 0;
baseController = 0;
time = 0;
}
void Animation::Load(){
AllocateHierarchy *hierarchy = new AllocateHierarchy();
D3DXLoadMeshHierarchyFromX(("data/models/"+filename).c_str(), 0, device, hierarchy, 0, &baseFrame, &baseController );
SetupBones(baseFrame);
delete hierarchy;
}

void Animation::SetupBones(LPD3DXFRAME frame){
if( frame->pMeshContainer ){
Container *container = (Container*)frame->pMeshContainer;
if(container->pSkinInfo){
if( container->m_apmxBonePointers )
delete[] container->m_apmxBonePointers;
DWORD dwNumBones = container->pSkinInfo->GetNumBones();
container->m_apmxBonePointers = new D3DXMATRIX* [ dwNumBones ];
for( DWORD i = 0; i < dwNumBones; ++ i ){
D3DXFRAME* pFrame = D3DXFrameFind( baseFrame, container->pSkinInfo->GetBoneName( i ) );
container->m_apmxBonePointers[ i ] = &pFrame->TransformationMatrix;
}
}
}
if( frame->pFrameSibling )
SetupBones( frame->pFrameSibling );
if( frame->pFrameFirstChild )
SetupBones( frame->pFrameFirstChild );
}

void Animation::AddTime(float t){
time += t;
if(time >= 30.0f){
if(baseController){
baseController->Release();
baseController = 0;
}
}
}

void Animation::Unload(){
}

void Animation::Render(Matrix *mx){
DrawFrames(baseFrame);
}

void Animation::UpdateFrames(Matrix *mx){
UpdateFrame(baseFrame, mx);
}

void Animation::UpdateFrame(LPD3DXFRAME frame, Matrix *mx){
D3DXMatrixMultiply( &frame->TransformationMatrix, &frame->TransformationMatrix, mx );

// transform siblings by the same matrix
if( frame->pFrameSibling )
UpdateFrame( frame->pFrameSibling, mx );

// transform children by the transformed matrix - hierarchical transformation
if( frame->pFrameFirstChild )
UpdateFrame( frame->pFrameFirstChild, &frame->TransformationMatrix );
}

void Animation::DrawFrames(D3DXFRAME *frame){
if(frame->pMeshContainer)
DrawMeshFrame(frame);

if(frame->pFrameSibling)
DrawFrames(frame->pFrameSibling);

if(frame->pFrameFirstChild)
DrawFrames(frame->pFrameFirstChild);
}

void Animation::DrawMeshFrame(D3DXFRAME* pFrame){
Container* pMC = ( Container* )pFrame->pMeshContainer;
D3DXMATRIX mx;

if( pMC->pSkinInfo == NULL )
return;

// get bone combinations
LPD3DXBONECOMBINATION pBC = ( LPD3DXBONECOMBINATION )( pMC->m_pBufBoneCombos->GetBufferPointer() );
DWORD dwAttrib, dwPalEntry;

// for each palette
for( dwAttrib = 0; dwAttrib < pMC->m_dwNumAttrGroups; ++ dwAttrib ){
// set each transform into the palette
for( dwPalEntry = 0; dwPalEntry < pMC->m_dwNumPaletteEntries; ++ dwPalEntry ){
DWORD dwMatrixIndex = pBC[ dwAttrib ].BoneId[ dwPalEntry ];
if( dwMatrixIndex != UINT_MAX )
D3DXMatrixMultiply( &pMC->m_amxWorkingPalette[ dwPalEntry ], &( pMC->m_amxBoneOffsets[ dwMatrixIndex ] ), pMC->m_apmxBonePointers[ dwMatrixIndex ] );
}


// set the matrix palette into the effect
fxSkin->SetMatrixArray( "amPalette", pMC->m_amxWorkingPalette, pMC->m_dwNumPaletteEntries );

// we're pretty much ignoring the materials we got from the x-file; just set
// the texture here
fxSkin->SetTexture( "g_txScene", pMC->materials[pBC[ dwAttrib ].AttribId].texture->GetTexture() );
//fxSkin->SetTexture( "g_txScene", pMC->m_apTextures[ pBC[ dwAttrib ].AttribId ] );

// set the current number of bones; this tells the effect which shader to use
fxSkin->SetInt( "CurNumBones", pMC->m_dwMaxNumFaceInfls - 1 );


UINT uiPasses, uiPass;
fxSkin->Begin( &uiPasses, 0 );
for( uiPass = 0; uiPass < uiPasses; ++ uiPass ){
fxSkin->BeginPass( uiPass );
pMC->m_pWorkingMesh->DrawSubset( dwAttrib );
fxSkin->EndPass();
}
fxSkin->End();

/*
UINT uiPasses, uiPass;

// run through each pass and draw
m_pMultiAnim->m_pEffect->Begin( &uiPasses, 0 );
for( uiPass = 0; uiPass < uiPasses; ++ uiPass ){
m_pMultiAnim->m_pEffect->BeginPass( uiPass );
pMC->m_pWorkingMesh->DrawSubset( dwAttrib );
m_pMultiAnim->m_pEffect->EndPass();
}
m_pMultiAnim->m_pEffect->End();
*/

}
}

AnimationInstance* Animation::NewInstance(){
if(!baseController)
Load();
AnimationInstance *instance = new AnimationInstance();
instance->animation = this;
baseController->CloneAnimationController(
baseController->GetMaxNumAnimationOutputs(),
baseController->GetMaxNumAnimationSets(),
baseController->GetMaxNumTracks(),
baseController->GetMaxNumEvents(),
&instance->controller);
return instance;
}

Animation* Handler_Animation::GetAnimation(string file){
for(unsigned int i = 0; i < animations.size(); i++){
if( animations->filename == file ){
return animations;
}
}
Animation *mdl=new Animation(file);
animations.push_back(mdl);
return mdl;
}

void Handler_Animation::AddTime(float t){
for(unsigned int i = 0; i < animations.size(); i++){
animations->AddTime(t);
}
}

void Handler_Animation::UnloadAnimations(){
for(unsigned int i = 0; i < animations.size(); i++){
animations->Unload();
}
}

void Handler_Animation::DestroyAnimations(){
for(unsigned int i = 0; i < animations.size(); i++){
animations->Unload();
delete animations;
}
}

AnimationInstance::AnimationInstance(){
animation = 0;
controller = 0;
}

void AnimationInstance::Render(Matrix *mx){
animation->UpdateFrames(mx);
animation->Render(mx);
}

void AnimationInstance::Time(float time){
controller->AdvanceTime(time, 0);
}



Main.cpp

//On program startup I create Handler_Animation object, it's supposed to handle models (loading, unloading, etc)
Animations = new Handler_Animation();

//Setting players model
AnimationInstance* playerModel = Animations->GetAnimation("tiny_4anim.x")->NewInstance(); //using same model from DX SDK MultiAnimation sample

//rendering the model
D3DXMATRIX matRotX,matRotY,matRotZ,matTrans;
D3DXMatrixRotationX( &matRotX, D3DX_PI/2 );
D3DXMatrixRotationY( &matRotY, 0 );
D3DXMatrixRotationZ( &matRotZ, cameraAngle + D3DX_PI/2);
D3DXMatrixTranslation(&matTrans, loc.x, loc.y, loc.z);
D3DXMATRIX matWorld=(matRotX*matRotY*matRotZ)*matTrans;
playerModel->Time(Time); //Time is last frame's elapsed time in seconds
playerModel->Render(&matWorld);




Vertex Shader turns all vertex positions into illegal ones:
http://dl.dropbox.com/u/2637453/VS.png

DirectX Debug Runtimes don't show anything.

That's all I think. Hope anyone can help me to fix this.
Thank you in advance.


P.S. If any extra information needed, let me know.

Share this post


Link to post
Share on other sites
Advertisement
Based on the pix screenshot your vertex input doesn’t contain the blend indices and weights. Have you set your vertex format/declaration correct?

Share this post


Link to post
Share on other sites
I am not sure...


// generate the skinned mesh - creates a mesh with blend weights and indices
pMC->pSkinInfo->ConvertToIndexedBlendedMesh( pMC->MeshData.pMesh,
D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,
pMC->m_dwNumPaletteEntries,
pMC->pAdjacency,
NULL,
NULL,
NULL,
&pMC->m_dwMaxNumFaceInfls,
&pMC->m_dwNumAttrGroups,
&pMC->m_pBufBoneCombos,
&pMC->m_pWorkingMesh );


I do have this function call, according to comment it does create weights. It fills pointers with data so I guess it's successful.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ripiz
It fills pointers with data so I guess it's successful.


Instead of guessing, why not check directly using the FAILED macro? You should also add such checks to the other functions you're calling.

I don't know if it will help with your current problem, but it's something you should always do regardless.

Share this post


Link to post
Share on other sites
After your mesh is completely generated you can use GetDeclaration to check if every element you need is there.

Share this post


Link to post
Share on other sites
Appears I wasn't setting World and ViewProj matrixes either, fixed that now, but it's still not working, maybe now anyone will be able to help? Link to download: http://dl.dropbox.com/u/2637453/demo2.rar

Share this post


Link to post
Share on other sites
Hi,

I downloaded and had a look through your code. I found the following things:

1. The DX9 animation mixer takes the last matrix value and multiplies in the current S,R,T matrix given a period within an animation and an animation set. This is why the vertices of your skinned mesh start out connected (at time 0.0) and fly away from each other as time progress. If your keyframes do not leave the bones in a "rest" position at the end of each keyframe, you will need to recursively go through the D3DXFRAME hierarchy and "reset" each D3DXFRAME to its original (or bind) matrix value.


//-----------------------------------------------------------------------------
// Name: struct SceneGraphFrame
// Desc: Inherits from D3DXFRAME. This represents an animation frame, or
// bone.
//-----------------------------------------------------------------------------
struct SceneGraphFrame : public D3DXFRAME
{
D3DXMATRIX OriginalMatrix;
};


void Animation::SetupBones(SceneGraphFrame *frame){
Container *container = (Container*)frame->pMeshContainer;
if( container ){
if(container->pSkinInfo){
if( container->m_apmxBonePointers )
delete[] container->m_apmxBonePointers;
DWORD dwNumBones = container->pSkinInfo->GetNumBones();
container->m_apmxBonePointers = new D3DXMATRIX* [ dwNumBones ];
for( DWORD i = 0; i < dwNumBones; ++ i ){
SceneGraphFrame* pFrame = (SceneGraphFrame *)D3DXFrameFind( (D3DXFRAME *)baseFrame, container->pSkinInfo->GetBoneName( i ) );
container->m_apmxBonePointers[ i ] = &pFrame->TransformationMatrix;
}
}
}

//
// Store this for a reset to bind position.
//
frame->OriginalMatrix = frame->TransformationMatrix;

if( frame->pFrameSibling )
SetupBones( (SceneGraphFrame *)frame->pFrameSibling );
if( frame->pFrameFirstChild )
SetupBones( (SceneGraphFrame *)frame->pFrameFirstChild );
}

void Animation::ResetFrames( SceneGraphFrame *pFrame )
{
//
// Transform the bone back to bind position
//
pFrame->TransformationMatrix = pFrame->OriginalMatrix;

//
// Reset siblings
//
if( pFrame->pFrameSibling )
ResetFrames( (SceneGraphFrame *) pFrame->pFrameSibling );

//
// Reset children
//
if( pFrame->pFrameFirstChild )
ResetFrames( (SceneGraphFrame *) pFrame->pFrameFirstChild );
}



2. Even after I manually reset the bones to bind at the start of each new frame, the character was not skinned correctly. The vertices no longer floated away from each other towards infinity, but the character was crushed and stretched inappropriately. I found you didn't need this in skin.vsh:

D3DCOLORtoUBYTE4() because it was messing up the aiIndices.

3. You have tons of heap leaks upon termination.

After all this I got Tiny to appear correctly skinned on the screen, but she does not animate still, there appears to be some other problems, I can have a look later.

Share this post


Link to post
Share on other sites
That's strange.. It's original shader from working sample I used (I only changed palette size), neither I was able to find where it resets matrix, so it's strange modifying those things kind of fixes animation.

This is the sample I used from DirectX SDK: http://dl.dropbox.com/u/2637453/C%2B%2B.rar
Because of DXUT, I had to keep weird folder structure, and because of weird folder structure animation won't have texture (or I'd have to copy even more folders), but skinning itself still works, just model is black.

I hope this "source" of my code will help to find where I made mistake while copying it over.


Yes I know about all leaks, I just wanted to make system work, before cleaning up stuff and optimizing, however it doesn't want to work.

Thank you a lot for your help.

Share this post


Link to post
Share on other sites
Yeah - you don't need to adjust the things I mentioned if you enable an animation track and play it, but your sample code doesn't do that. Tiny's keyframes reset the translation parts of her matrices for you. Just look at your UpdateFrames code, it basically reads:

ThisTime's Matrix equals LastTime's Matrix * MatrixAfterAnimationKeyframesForThisTime

That can never work if your bones have any amount of translation in them unless the MatrixAfterAnimationKeyframesForThisTime resets back to bind at some point, which the Tiny X file does. Without that, you will just keep accumulating the translation from this timestamp onto last timestamp's translation, which will shoot your vertices away from the origin to infinity.

Anyhow, you need to add code like this in Animation::NewInstance()


AnimationInstance* Animation::NewInstance(){
if(!baseController)
Load();
AnimationInstance *instance = new AnimationInstance();
instance->animation = this;
instance->controller = baseController;
baseController->AddRef();

DWORD dwTracks = instance->controller->GetMaxNumTracks();
for( DWORD i = 0; i < dwTracks; ++ i )
instance->controller->SetTrackEnable( i, FALSE );

LPD3DXANIMATIONCONTROLLER pAC = instance->controller;
LPD3DXANIMATIONSET pAS;

pAC->GetAnimationSet( 1, &pAS );
pAC->SetTrackAnimationSet( 0, pAS );
pAS->Release();

pAC->UnkeyAllTrackEvents( 0 );

pAC->SetTrackEnable( 0, TRUE );
pAC->SetTrackSpeed( 0, 1.0f );
pAC->SetTrackWeight( 0, 1.0f );
pAC->SetTrackPosition( 0, 0.0 );

pAC->Release();

return instance;
}






You do not need to clone the animation controller unless this is an instance copy and in either case you need to AddRef to your base controller (maybe a bug in your code somewhere) or else the D3DX9 library will access it after its been freed (0xfeeefee2).

You do need to animate Tiny or her vertices will shred across the screen (which is what they are doing for you now). BTW this may become an issue when you use other model's with your code and in this case the reset to bind comes into play. You can continue to change the semantic of the indices from D3DCOLOR to UBYTE4, I have no idea why I needed to do that to get the model in bind position without animations, but once the animations play, it is unnecessary.

I also noticed that you call UpdateFrames twice per frame - you don't need to do this.

Also you should check the return values from functions especially ones that return pointers. This will come in handy to prevent spurious access violations when you want the same code to run in parallel with a delayed loading thread.

You might also want to name your variables in a more meaningful way so that they can be searched on, it is difficult to generate meaningful searches for variables like 'loc'.

I also think that you will experience growing pains when you try to do either of the following things with this code:

1. Efficiently load multiple instance of the same mesh - since you have neutered most of the pieces critical to this from the multi-animation sample.

2. Load meshes that have frame or mesh container names that are non-unique or have spaces or non-printable characters in them. Since your bone setup is dependent on D3DXFrameFind() and so is the ID3DXAnimationController internally you will run into issues here.

3. Load any frame with more than one mesh container directly under it. Your drawMeshFrame function does not handle the ->Next field of a MeshContainer, nor does your AllocateHierarchy.

4. Your AllocateHierarchy also no longer handles a skinned character with more than 1 non-linked mesh container and for which the skin partition uses different numbers of bones. You neutered out the part of the Multi-Animation sample that correctly handled this situation. The DX9 sample would handle this by growing the "working" palette to the largest skin partition's size and also setting the numPaletteEntries to the minimum of the defined value and the largest skin partition.

5. You lost the animation blending pieces - I just used SetTrackXXX instead of KeyTrackXXX to show you how to fix your render, but eventually you will want the blending again.

[Edited by - Steve_Segreto on July 8, 2010 3:57:21 AM]

Share this post


Link to post
Share on other sites
Quote:
You do not need to clone the animation controller unless this is an instance copy

Well possibly I understood things wrong then, I though copy of animation controller allows me to set different animations for different 'characters' and reuse same mesh, without loading same mesh multiple times.

Quote:
I also noticed that you call UpdateFrames twice per frame - you don't need to do this.

After my last post I noticed that too, but as I mentioned earlier, first working system, then optimizations XD

Quote:
Also you should check the return values from functions especially ones that return pointers. This will come in handy to prevent spurious access violations when you want the same code to run in parallel with a delayed loading thread.

I plan to use enums to determine if texture/model is unloaded, loading or loaded, then use it if it's loaded, or load if it's unloaded.

Quote:
You might also want to name your variables in a more meaningful way so that they can be searched on, it is difficult to generate meaningful searches for variables like 'loc'.

'loc' stands for location, which is players position/location, I guess you use something else then, 'loc' was first what came to my mind

Quote:
I also think that you will experience growing pains

Without pain there's no gain D:
I got used already, as I always mess up things.

Quote:
1. Efficiently load multiple instance of the same mesh - since you have neutered most of the pieces critical to this from the multi-animation sample.

I made Handler_Animation class, which copies animation controller so it can be reused, but possibly I understood something wrong.

Quote:
2. Load meshes that have frame or mesh container names that are non-unique or have spaces or non-printable characters in them. Since your bone setup is dependent on D3DXFrameFind() and so is the ID3DXAnimationController internally you will run into issues here.

That should be fixable in 3DS Max, free models rarely suit my needs (and probably anyones else).

Quote:
3. Load any frame with more than one mesh container directly under it. Your drawMeshFrame function does not handle the ->Next field of a MeshContainer, nor does your AllocateHierarchy.

Sounds like I'll need this for 'overlays' like weapons, shield, armor or etc, will add it later on, extra thanks for pointing out this one.

Quote:
4. Your AllocateHierarchy also no longer handles a skinned character with more than 1 non-linked mesh container and for which the skin partition uses different numbers of bones. You neutered out the part of the Multi-Animation sample that correctly handled this situation. The DX9 sample would handle this by growing the "working" palette to the largest skin partition's size and also setting the numPaletteEntries to the minimum of the defined value and the largest skin partition.

Do you mean I have to set this to number of bones, instead of max number of bones?
fxSkin->SetMatrixArray( "amPalette", pMC->m_amxWorkingPalette, pMC->m_dwNumPaletteEntries );


Quote:
5. You lost the animation blending pieces - I just used SetTrackXXX instead of KeyTrackXXX to show you how to fix your render, but eventually you will want the blending again.

Some more pain for me, hopefully I'll figure it out.

Thanks a lot for your help. I'll go to try all these now :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!