microsoft / DirectXTK

The DirectX Tool Kit (aka DirectXTK) is a collection of helper classes for writing DirectX 11.x code in C++
https://walbourn.github.io/directxtk/
MIT License
2.57k stars 511 forks source link

Skinned model help #152

Closed dudedude1234 closed 5 years ago

dudedude1234 commented 5 years ago

I managed to build a skinned model file

pch.h `typedef struct ID3DXSkinInfo *LPD3DXSKININFO;

typedef struct ID3DXMesh *LPD3DXMESH;

typedef interface ID3DXBuffer ID3DXBuffer; typedef interface ID3DXBuffer *LPD3DXBUFFER;

typedef enum D3DXEFFECTDEFAULTTYPE { D3DXEDT_STRING = 1, D3DXEDT_FLOATS = 2, D3DXEDT_DWORD = 3, D3DXEDT_FORCEDWORD = 0x7fffffff } D3DXEFFECTDEFAULTTYPE, *LPD3DXEFFECTDEFAULTTYPE;

typedef interface ID3DXEffect ID3DXEffect; typedef interface ID3DXEffect *LPD3DXEFFECT;

typedef interface ID3DXAnimationSet ID3DXAnimationSet; typedef interface ID3DXAnimationSet *LPD3DXANIMATIONSET;

typedef struct ID3DXPatchMesh *LPD3DXPATCHMESH;

typedef struct D3DMATERIAL9 { D3DCOLORVALUE Diffuse; D3DCOLORVALUE Ambient; D3DCOLORVALUE Specular; D3DCOLORVALUE Emissive; float Power; } D3DMATERIAL9, *LPD3DMATERIAL9;

typedef enum D3DXMESHDATATYPE { D3DXMESHTYPE_MESH = 0x001, D3DXMESHTYPE_PATCHMESH = 0x003, D3DXMESHTYPE_FORCE_DWORD = 0x7fffffff } D3DXMESHDATATYPE, *LPD3DXMESHDATATYPE;

typedef struct D3DXEFFECTDEFAULT { LPSTR pParamName; D3DXEFFECTDEFAULTTYPE Type; DWORD NumBytes; LPVOID pValue; } D3DXEFFECTDEFAULT, *LPD3DXEFFECTDEFAULT;

typedef struct D3DXMATERIAL { D3DMATERIAL9 MatD3D; LPSTR pTextureFilename; } D3DXMATERIAL, *LPD3DXMATERIAL;

typedef struct D3DXMESHDATA { D3DXMESHDATATYPE Type; union { LPD3DXMESH pMesh; LPD3DXPATCHMESH pPatchMesh; }; } D3DXMESHDATA, *LPD3DXMESHDATA;

typedef struct D3DXEFFECTINSTANCE { LPSTR pEffectFilename; DWORD NumDefaults; LPD3DXEFFECTDEFAULT pDefaults; } D3DXEFFECTINSTANCE, *LPD3DXEFFECTINSTANCE;

typedef struct D3DXMESHCONTAINER { LPSTR Name; D3DXMESHDATA MeshData; LPD3DXMATERIAL pMaterials; LPD3DXEFFECTINSTANCE pEffects; DWORD NumMaterials; DWORD pAdjacency; LPD3DXSKININFO pSkinInfo; D3DXMESHCONTAINER pNextMeshContainer; } D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;

typedef enum D3DXPRIORITY_TYPE { D3DXPRIORITY_LOW = 0, D3DXPRIORITY_HIGH = 1, D3DXPRIORITY_FORCE_DWORD = 0x7fffffff } D3DXPRIORITY_TYPE, *LPD3DXPRIORITY_TYPE;

typedef struct D3DXFRAME { LPSTR Name; XMMATRIX TransformationMatrix; LPD3DXMESHCONTAINER pMeshContainer; D3DXFRAME pFrameSibling; D3DXFRAME pFrameFirstChild; } D3DXFRAME, *LPD3DXFRAME;

typedef struct D3DXKEY_CALLBACK { FLOAT Time; LPVOID pCallbackData; } D3DXKEY_CALLBACK, *LPD3DXKEY_CALLBACK;

LPD3DXFRAME D3DXFrameFind( In const D3DXFRAME *pFrameRoot, In LPCSTR Name );

struct BoxerCallbackInfo { float* theta; };`

SkinnedMesh.cpp `

include "Mechanics/SkinnedMesh.h"

D3DXFRAME SkinnedMesh::findNodeWithMesh(D3DXFRAME frame) { if (frame->pMeshContainer) if (frame->pMeshContainer->MeshData.pMesh != 0)
return frame; D3DXFRAME* f = 0;
if (frame->pFrameSibling)
if (f = findNodeWithMesh(frame->pFrameSibling))
return f; if (frame->pFrameFirstChild)
if (f = findNodeWithMesh(frame->pFrameFirstChild))
return f; return 0; }

void SkinnedMesh::buildSkinnedMesh(ModelMesh mesh) { DWORD numBoneComboEntries = 0; ID3DXBuffer boneComboTable = 0; THROW_DXERR(_skinInfo->ConvertToIndexedBlendedMesh(mesh, D3DXMESH_MANAGED | D3DXMESH_WRITEONLY, SkinnedMesh::MAX_NUM_BONES_SUPPORTED, 0, // ignore adjacency in
0, // ignore adjacency out
0, // ignore face remap
0, // ignore vertex remap
&_maxVertInfluences,
&numBoneComboEntries,
&boneComboTable,
&m_Model) ) // We do not need the bone table, so just release it.
ReleaseCOM(boneComboTable); }

void SkinnedMesh::buildCombinedTransforms() { for (UINT i = 0; i < _numBones; ++i) {
// Find the frame that corresponds with the ith
// bone offset matrix.
const char boneName = _skinInfo->GetBoneName(i);
D3DXFRAME
frame = D3DXFrameFind(_root, boneName);
if( frame )
{
FrameEx frameEx = static_cast<FrameEx>( frame );
_combinedTransforms[i] = &frameEx->combinedTransform;
}
} }

void SkinnedMesh::frameMove(float deltaTime, meshMatrix worldViewProj) { _animCtrl->AdvanceTime(deltaTime, 0); meshMatrix identity; XMMatrixIdentity(&identity);
combineTransforms(static_cast<FrameEx>(_root), identity); meshMatrix offsetTemp, combinedTemp; for (UINT i = 0; i < _numBones; ++i) { offsetTemp = _skinInfo->GetBoneOffsetMatrix(i);
combinedTemp = _combinedTransforms[i];
_finalTransforms[i] = offsetTemp
combinedTemp; } _effect->SetMatrix(_hWorldViewProj, &worldViewProj);

_effect->SetMatrixArray(_hFinalTransforms, &_finalTransforms[0], _finalTransforms.size());

}

void SkinnedMesh::render() { _effect->SetTechnique(_hTech);
UINT numPasses = 0; _effect->Begin(&numPasses, 0); for (UINT i = 0; i < numPasses; ++i) { _effect->BeginPass(i); // Draw the one and only subset. _skinnedMesh->DrawSubset(0); _effect->EndPass(); } _effect->End(); }`

`//----------------------------------------------------------------------

void Boxer::LoadMesh() { auto device = m_deviceResources->GetD3DDevice(); m_states = std::make_unique(device);

m_fxFactory = std::make_unique<EffectFactory>(device);

switch (Boxers)
{
case GaryTurbo:
    SkinnedMesh::buildSkinnedMesh
    break;
}

int i = 0;

m_world = Matrix::Identity;

}

void Boxer::setupCallbackKeyframes(float theta) { // Remark: ‘theta’ is a pointer to the camera’s theta // coordinate. // Grab the current animation set for 'tiny.x'
// (we know there is only one.) ID3DXKeyframedAnimationSet
animSetTemp = 0; _animCtrl->GetAnimationSet( 0, (ID3DXAnimationSet*)&animSetTemp); // Compress it. ID3DXBuffer compressedInfo = 0;
animSetTemp->Compress(D3DXCOMPRESS_DEFAULT, 0.5f, 0, &compressedInfo); // Setup two callback keys.
UINT numCallbacks = 2;
D3DXKEY_CALLBACK keys[2]; // Make static so it does not pop off the stack.
static BoxerCallbackInfo CallbackData;

CallbackData.theta = theta;
// GetSourceTicksPerSecond() returns the number of   
// animation key frame ticks that occur per second. 
// Callback keyframe times are tick based.  
double ticks = animSetTemp->GetSourceTicksPerSecond(); 
// Set the first callback key to trigger a callback 
// half way through the animation sequence.   
keys[0].Time = float(animSetTemp->GetPeriod()/2.0f*ticks);  
keys[0].pCallbackData = (void*)&CallbackData;   
// Set the second callback key to trigger a callback  
// at the end of the animation sequence. 
keys[1].Time = animSetTemp->GetPeriod()*ticks; 
keys[1].pCallbackData = (void*)&CallbackData; 
// Create the ID3DXCompressedAnimationSet interface   
// with the callback keys. 
ID3DXCompressedAnimationSet* compressedAnimSet = 0;  
D3DXCreateCompressedAnimationSet(animSetTemp->GetName(),  
    animSetTemp->GetSourceTicksPerSecond(),   
    animSetTemp->GetPlaybackType(), 
    compressedInfo,  
    numCallbacks, keys, &compressedAnimSet); 
compressedInfo->Release();    
// Remove the old (non compressed) animation set. 
_animCtrl->UnregisterAnimationSet(animSetTemp); 
animSetTemp->Release(); 
// Add the new (compressed) animation set.  
_animCtrl->RegisterAnimationSet(compressedAnimSet);   
// Hook up the animation set to the first track. 
_animCtrl->SetTrackAnimationSet(0, compressedAnimSet); 
compressedAnimSet->Release(); 

}

void Boxer::playIdle() { ID3DXAnimationSet* loiter = 0; _animCtrl->GetAnimationSet(LOITER_INDEX, &loiter); _animCtrl->SetTrackAnimationSet(0, loiter);
_animCtrl->ResetTime(); }

void Boxer::Update() {

}

void Boxer::Render() { auto context = m_deviceResources->GetD3DDeviceContext(); SkinnedMesh->render(); }`

is there any way I can load this

void SkinnedMesh::buildSkinnedMesh(ModelMesh* mesh) { DWORD numBoneComboEntries = 0; ID3DXBuffer* boneComboTable = 0; THROW_DXERR(_skinInfo->ConvertToIndexedBlendedMesh(mesh, D3DXMESH_MANAGED | D3DXMESH_WRITEONLY, SkinnedMesh::MAX_NUM_BONES_SUPPORTED, 0, // ignore adjacency in 0, // ignore adjacency out 0, // ignore face remap 0, // ignore vertex remap &_maxVertInfluences, &numBoneComboEntries, &boneComboTable, &m_Model) ) // We do not need the bone table, so just release it. ReleaseCOM(boneComboTable); }

through this

switch (Boxers) { case GaryTurbo: SkinnedMesh::buildSkinnedMesh break; }

walbourn commented 5 years ago

A lot of that code is working with the legacy "Direct3D Effects" technology from Direct3D 9--for Direct3D 11 there is an Effects 11, which is a different solution for shader management than DirectXTK's BasicEffect, SkinnedEffect, etc.

The main issue is you need to solve to use SkinnedEffect is map the matrix array to bone indices. See the IEffectSkininng interface which is implemented by SkinnedEffect.

dudedude1234 commented 5 years ago

About this

// Simple time-based uniform scaling float s = 1 + sin(time * 1.7f) * 0.5f; XMMATRIX scale = XMMatrixScaling(s, s, s);

I setup this:

DX::StepTimer time

and I got this error:

E0349 no operator "*" matches these operands

And when I rendered the model

`m_model->Draw(context, *m_states, bones, m_world, m_view, m_proj);

`

I get this error:

E0312 no suitable user-defined conversion from "std::unique_ptr<DirectX::XMMATRIX [], aligned_deleter>" to "DirectX::XMMATRIX" exists

walbourn commented 5 years ago

Model::Draw doesn't take an array of matrices, so the 3rd parameter is not valid.

void XM_CALLCONV Draw(_In_ ID3D11DeviceContext* deviceContext, const CommonStates& states,
    FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection,
    bool wireframe = false, _In_opt_ std::function<void __cdecl()> setCustomState = nullptr) const;

You have to do something like:

m_model->UpdateEffects([&](IEffect* effect)
{
    auto skinnedEffect = dynamic_cast<IEffectSkinning*>(effect);
    if (skinnedEffect)
        skinnedEffect->SetBoneTransforms(bones.get(), SkinnedEffect::MaxBones);
});
m_model->Draw(context, *m_states, m_world, m_view, m_proj);
walbourn commented 3 years ago

With the October 2021 release of DirectX Tool Kit, there is now a DrawSkinned method for Model.

See https://github.com/microsoft/DirectXTK/wiki/Using-skinned-models