|
|
| DirectX - скелетная анимация |
В статье описано моё понятие скелетной анимации прочитав часть книги «DirectX Продвинутая анимация» Джима Адамса.
Скелетная анимация – упорядоченное изменение костей скелета. Каждая кость изменяется относительно родительской и даёт порядок дочерним (если таковые окажутся) изменяться относительно неё. Скелет загрузим из .х файла (в нашем случае tiny.x поставляемым DirectX SDK) в котором помимо меша содержатся ещё данные для скелета: иерархия костей в которой соблюдается порядок родительская кость кроме своих данных содержит встроенные данные на дочернюю кость. Например кость кисти встроена в кость локтя и т.д. Данные состоят из имени и матрицы трансформации кости. Кости помещены в в .х-овский шаблон Frame.
Для построения скелета воспользуемся загрузчиком открывающим .х файл, создающий объект могущий глянуть на все объекты описанные в .х, и сохранить данные этих объектов.
if(FAILED(DirectXFileCreate(&pDXFile)))
return FALSE;
// Register the common templates
if(FAILED(pDXFile->RegisterTemplates((LPVOID)D3DRM_XTEMPLATES,
D3DRM_XTEMPLATE_BYTES)))
{
pDXFile->Release();
return FALSE;
}
// Create an enumeration object
if(FAILED(pDXFile->CreateEnumObject((LPVOID)Filename,
DXFILELOAD_FROMFILE,
&pDXEnum)))
{
pDXFile->Release();
return FALSE;
}
// Loop through all top-level objects, breaking on errors
BOOL ParseResult;
while(SUCCEEDED(pDXEnum->GetNextDataObject(&pDXData)))
{
ParseResult = ParseObject(pDXData, NULL, 0, Data, FALSE);
ReleaseCOM(pDXData);
if(ParseResult == FALSE)
break;
}
struct D3DXFRAME_EX : D3DXFRAME
{
D3DXMATRIX matOriginal; // Оригинал
D3DXMATRIX matCombined; // То что мы изменим
}
D3DXMESHCONTAINER_EX : D3DXMESHCONTAINER
{
ID3DXMesh *pSkinMesh; // меш для вывода
D3DXMATRIX *pBoneMatrices; // кости исходного меша
D3DXMATRIX **ppFrameMatrices; // cюда кидаем иерархию
}pMesh->ppFrameMatrices[i-ая кость] = &pFrame->matCombined;
![]() |
![]() |
![]() |
// Copy the bone matrices over (must have been combined before call DrawMesh)
for(DWORD i=0; i<количество костей; i++)
{
// Start with bone offset matrix
pMesh->pBoneMatrices[i] = (*pMesh->pSkinInfo->GetBoneOffsetMatrix(i));
// Apply frame transformation
pMesh->pBoneMatrices[i] *= (*pMesh->ppFrameMatrices[i]);
}void *SrcPtr, *DestPtr; pMesh->MeshData.pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&SrcPtr); pMesh->pSkinMesh->LockVertexBuffer(0, (void**)&DestPtr); pMesh->pSkinInfo->UpdateSkinnedMesh(pMesh->pBoneMatrices, NULL, SrcPtr, DestPtr);Последняя функция архиудобнейшая. Имея меш и его копию мы их блокируем и кидаем матрицу нужной позы(какая нам нравится) в копию.
pMesh->pSkinMesh-> DrawSubset()Всё что можно тут сделать так это найти по имени конкретную кость и изменить её.
/**Перед UpdateHierarchy() **/
D3DXFRAME_EX *pFrameTestScalling = g_Frame->Find("Bip01_L_Forearm");
D3DXMATRIX matTemp;
D3DXMatrixIdentity(&matTemp);
matTemp._11 = 1;
matTemp._22 = 1.01;
matTemp._33 = 1;
pFrameTestScalling->TransformationMatrix *= matTemp;//*/
Интересней если по этому принципу в иерархию добавлять матрицы тр., которые изменяют положение скелета в нужное нам время.
Для этого используем класс способный построить набор анимированной последовательности, и сопоставить её иерархии фреймов. Анимацию строим подобно фреймам в .х находим объект AnimationSet в котором расположены набор объектов Animation. Последний содержит имя кости тип анимации количествово и данные ключей. Ключ это метка определённого объекта Animation заданный в мс. Всё это добро помещаем в список AnimationSets и на начальном этапе соединяем к иерархии.
while(пройдёмся по объектам cAnimation)
{
// найдём совпадение в иерархии и запишем в наш список
сAnimationSet:: cAnimation:: D3DXFRAME_EX = RootFrame->Find(cAnimation::m_Name);
// перейдём к следующему объекту в списке
cAnimation = cAnimation::Next
}
Тип анимации оперделяет как поступить с костью
(0 – повернуть, 1 – масштабировать, 2 – переместить, 4 – трансформировать).
Количество ключей - число действий анимируемого объекта.
Данные есть матрица трансформации накладываемая на кость в это
действие(т.е. ключ).
Как только будем выводить скелет соответствующий данным времени
ключей, то у нас получится анимация. Правда переход между ключами
будет не слишком плавным,поэтому в эти моменты накладываем интерполированные матрицы
mat1, mat1, time1, time2 – матрицы и время 1-го и 2-го ключа
matTransformation = mat2 - mat1
time = time2 - time1;
Scalar = (текущее время - time1) / time
matTransformation *= Scala
matTransformation += time1
теперь иерархия будет содержать плавное преобразование хранящееся
в matTransformation
Проект MVC 6.0 и bin здесь 640 Kb на голом DirectX 9.0 т.е. если стоит Update нужно изменить порядок путей к .h-ам и lib-ам