📄 mlskinnedmesh.cpp
字号:
///////////////////////////////////////////////////////////////////////////////
//
// File: mlSkinnedmesh.cpp
//
// Author: Frank Luna (C) All Rights Reserved
//
// System: Athlon 1800+ XP, 512 DDR, Radeon 9500 Pro, Windows XP, MSVC++ 7.0
//
// Desc: Contains the implementation of SkinnedMesh.
//
///////////////////////////////////////////////////////////////////////////////
#include "mlSkinnedmesh.h"
#include <cassert>
using namespace dx_err;
SkinnedMesh::SkinnedMesh()
:_device(0), _skinnedMesh(0), _tex(0), _effect(0), _root(0),
_maxVertInfluences(0), _numBones(0), _skinInfo(0), _animCtrl(0), _hTech(0),
_hWorldViewProj(0), _hFinalTransforms(0), _hTex(0)
{
}
SkinnedMesh::~SkinnedMesh()
{
deleteDeviceObjects();
}
void SkinnedMesh::initDeviceObjects(IDirect3DDevice9* device,
const std::string& inputFilename)
{
_device = device;
AllocMeshHierarchy allocMeshHierarchy;
THROW_DXERR(D3DXLoadMeshHierarchyFromX(inputFilename.c_str(), D3DXMESH_MANAGED,
_device, &allocMeshHierarchy, 0, /* ignore user data */
&_root, &_animCtrl))
D3DXFRAME* f = findNodeWithMesh(_root);
if( f == 0 ) THROW_DXERR(E_FAIL);
D3DXMESHCONTAINER* meshContainer = f->pMeshContainer;
_skinInfo = meshContainer->pSkinInfo;
_skinInfo->AddRef();
_numBones = meshContainer->pSkinInfo->GetNumBones();
_finalTransforms.resize(_numBones);
_combinedTransforms.resize(_numBones);
buildSkinnedMesh(meshContainer->MeshData.pMesh);
buildCombinedTransforms();
buildEffect();
buildMaterials(meshContainer);
}
void SkinnedMesh::restoreDeviceObjects()
{
// Call after a reset to restore the effect.
if ( _effect )
THROW_DXERR( _effect->OnResetDevice() )
}
void SkinnedMesh::deleteDeviceObjects()
{
if( _root )
{
AllocMeshHierarchy allocMeshHierarchy;
THROW_DXERR( D3DXFrameDestroy(_root, &allocMeshHierarchy) )
_root = 0;
}
ReleaseCOM(_skinnedMesh);
ReleaseCOM(_skinInfo);
ReleaseCOM(_animCtrl);
ReleaseCOM(_effect);
}
void SkinnedMesh::invalidateDeviceObjects()
{
// Call before a reset device.
if ( _effect )
THROW_DXERR( _effect->OnLostDevice() );
}
HRESULT SkinnedMesh::confirmDevice(D3DCAPS9* pCaps)
{
////////////////////////////////////////////////////////////////////////
// User needs a graphics device that supports vertex shader version 2.0.
if( pCaps->VertexShaderVersion < D3DVS_VERSION(2, 0) )
return E_FAIL;
return D3D_OK;
}
void SkinnedMesh::frameMove(float deltaTime, D3DXMATRIX& worldViewProj)
{
//////////////////////////////////////////////////////////////////////
// Animate the mesh. The AnimationController has pointers to the
// hierarchy frame transform matrices. The AnimationController updates
// these matrices to reflect the given pose at the current time by
// interpolating between animation keyframes.
_animCtrl->AdvanceTime(deltaTime, 0);
//////////////////////////////////////////////////////////////////
// Now that the frames are updated to the current pose, recurse
// down the tree and generate a frame's combined-transform.
D3DXMATRIX identity;
D3DXMatrixIdentity(&identity);
combineTransforms(static_cast<FrameEx*>(_root), identity);
////////////////////////////////////////////////////////////
// Add the offset-transform, note that this premultiplies
// the combined-transform.
D3DXMATRIX offsetTemp, combinedTemp;
for(UINT i = 0; i < _numBones; ++i)
{
offsetTemp = *_skinInfo->GetBoneOffsetMatrix(i);
combinedTemp = *_combinedTransforms[i];
_finalTransforms[i] = offsetTemp * combinedTemp;
}
///////////////////////////////////////////////////////////////
// Finally, set the final-transform matrix array to the effect.
_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();
}
D3DXFRAME* SkinnedMesh::findNodeWithMesh(D3DXFRAME* frame)
{
// In this demo we stipulate that the input .X file contains only one
// mesh. So search for that one and only mesh.
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(ID3DXMesh* mesh)
{
// The vertex format of the source mesh does not include vertex weights
// nor bone index data, both of which are needed for vertex blending.
// Therefore, we must convert the source mesh to an "indexed-blended-mesh,"
// which does have the necessary data.
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,
&_skinnedMesh) )
// We do not need the bone table, so just release it.
ReleaseCOM(boneComboTable);
}
void SkinnedMesh::buildEffect()
{
ID3DXBuffer* errorBuffer = 0;
D3DXCreateEffectFromFile(_device, "vertBlendDynamic.txt", 0, 0,
D3DXSHADER_DEBUG, 0, &_effect, &errorBuffer);
if( errorBuffer )
{
::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
ReleaseCOM(errorBuffer);
ReleaseCOM(_effect);
}
else
{
_hTech = _effect->GetTechniqueByName("VertexBlendingTech");
_hWorldViewProj = _effect->GetParameterByName(0, "WorldViewProj");
_hFinalTransforms = _effect->GetParameterByName(0, "FinalTransforms");
_hTex = _effect->GetParameterByName(0, "Tex");
}
}
void SkinnedMesh::buildMaterials(D3DXMESHCONTAINER* meshContainer)
{
// In this demo we assume the skinned mesh has exactly one texture.
// So load and set that texture with the effect interface.
D3DXMATERIAL* mtrls = meshContainer->pMaterials;
DWORD numMtrls = meshContainer->NumMaterials;
assert(numMtrls == 1);
if( mtrls[0].pTextureFilename )
{
IDirect3DTexture9* tex = 0;
THROW_DXERR(
D3DXCreateTextureFromFile(_device, mtrls[0].pTextureFilename, &tex))
THROW_DXERR(_effect->SetTexture(_hTex, tex))
ReleaseCOM(tex);
}
}
void SkinnedMesh::buildCombinedTransforms()
{
// Get an array of pointers to the combined frame transformations. We
// store the pointers such that the ith pointer corresponds with the
// ith offset-matrix. Thus, given the ith bone, we can obtain its ith
// offset matrix and ith transform.
// We note that the skinInfo object converted the source mesh to a
// indexed-blended-mesh. It follows then that the bone indices for
// the vertices are relative to the skinInfo bone array. Therefore
// we base everything relative to the offset-matrix array since that
// is the array the bone indices of the vertices are relative to.
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::combineTransforms(FrameEx* frame,
D3DXMATRIX& P) // parent's combined transform
{
// Recurse down the tree, combining transforms as we go along.
// Any node N's combined transform C is determined by its local
// transform L, and the combined transform P of its parent.
// Thus C = L * P.
// Save some references to economize line space.
D3DXMATRIX& L = frame->TransformationMatrix;
D3DXMATRIX& C = frame->combinedTransform;
C = L * P;
FrameEx* sibling = (FrameEx*)frame->pFrameSibling;
FrameEx* firstChild = (FrameEx*)frame->pFrameFirstChild;
// Recurse down siblings.
if( sibling )
combineTransforms(sibling, P);
// Recurse to first child.
if( firstChild )
combineTransforms(firstChild , C);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -