|
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-ам