Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DirectX. Продвинутая Анимация (2004) [rus].pdf
Скачиваний:
367
Добавлен:
16.08.2013
Размер:
8.39 Mб
Скачать

Работа соскелетнойанимацией

.

Как я уже упоминал, вам необходимо просто вызвать функцию UpdateHierarchy, используя корневой фрейм. Не указывайте матрицу преобразования в качестве второго параметра, он будет использован при рекурсивных вызовах. Если же вы зададите матрицу преобразования, весь меш будет сдвинут на нее. Это то же самое, что установить матрицу преобразования мира для расположения объекта при визуализации.

// pRootFrame = корневой фрейм-объект D3DXFRAME_EX UpdateHierarchy(pRootFrame);

После того как вы познакомились со скелетной структурой и работой с иерархиями костей, пришло время перейти ко второй части анимации - накладным скелетным мешам, которые меняют форму в соответствии с расположением иерархии костей.

Работа со скелетными мешами

В первой половине этой главы вы научились управлять иерархией костей, которые являются основой скелетной анимации. Это все хорошо, но игра с воображаемыми костями не очень занимательна. Людям, играющим в ваши игры, необходимо видеть всю тяжелую работу в виде визуализированных мешей, где и приходит время скелетных мешей.

Скелетные меши очень похожи на обычные, с которыми вы уже знакомы. Используя объект D3DXMESHCONTAINER_EX (как вы видели в главе 1), вы можете хранить данные ваших мешей от вершин и индексов до материалов и данных текстур, все это находится в удобном объекте ID3DXMesh. Что же касается фактических данных скелетного меша, они расположены в специальном объекте ID3DXSkinInfo.

Я пока пропущу описание объекта ID3DXSkinInfo и вместо этого объясню, чем же скелетные меши отличаются от остальных. Скелетные меши меняют форму в соответствии с расположением скелетной структуры. Вершины меша вращаются и поворачиваются вместе с костями. Вершины меша делают скелетный меш уникальным. Вы будете иметь дело с изменением положения вершин при работе со скелетными мешами.

Посмотрите на рис. 4.3, на котором изображен скелет, окруженный простейшим мешем.

На рис. 4.3 каждая вершина присоединена к кости. Когда кость двигается, вместе с ней двигаются и присоединенные к ней вершины. Например, если повернуть кость на 45 градусов по оси х, присоединенные к ней вершины также повернутся на 45 градусов, при этом точка скрепления костей будет является центом вращения.

136

Глава 4

Рис. 4.3. При присоединении к скелетной структуре меш изменяется таким образом, чтобы каждая вершина соединялась с костью

Внимательно посмотрев на рис. 4.3 вы обнаружите, что некоторые вершины присоединены к нескольким костям. Так и есть - вы можете присоединять вершину более чем к одной кости. На самом деле, при использовании DirectX вы можете присоединять вершину к неограниченному числу костей, используя методы, которые вы изучите в этой книге. При движении кости, к которой присоединена вершина, вершина наследует не все движения. Например, если кость поворачивается на 60 градусов по оси z, присоединенная к ней вершина может повернуться только на 25 процентов от этого, т.е вершина повернется только на 15 градусов по оси z.

Точное значение процентного соотношения наследуемого вершиной движения назьшается весом вершины. Для каждой вершины скелетного меша назначается ее вес при присоединении ее к кости. Для вершин, присоединенных только к одной кости, это значение обычно 1.0, это говорит о том, что вершина наследует все движения

Работасоскелетнойанимацией

137

кости. Для вершин, присоединенных к нескольким костям, веса делятся на количество костей и, обычно, вычисляются, используя расстояние до каждой кости. (Большинство программ трехмерного моделирования сделает это за вас.) Например, вершина присоединена к двум костям, это означает, что оба веса будут по 0.5. Вершина будет наследовать только 50 процентов движения каждой кости. Заметьте, что сумма весов одной вершины всегда равняется 1.

Цель использования весов скелета достаточно оригинальна. Позволяя определенным костям влиять на заданные вершины, вы можете создавать привлекательные эффекты, такие как морщинистая кожа, неровности мускулов, растяжение одежды, - все это в реальном времени при анимации ваших объектов!

Путь, используемый DirectX для обработки весов вершин, достаточно прост. После загрузки меша, который вы будете использовать как скелетный, и весов вершин (также называемых скелетными весами), вы можете преобразовать вершины, чтобы они соответствовали расположению, костей используя следующие шаги:

1.Перебрать все вершины. Для каждой вершины выполнить шаг 2.

2.Для каждой кости, к которой присоединена вершина, получить преобразование.

3.Каждое преобразование кости умножить на матрицу вершинных весов и наложить полученное преобразование на комбинированное преобразование вершин.

4.Повторить шаг 3 для каждой присоединенной кости, шаги 2-4 повторить для всех вершин. После завершения применить комбинированную матрицу преобразования на заданную вершину (из шага 1).

Ивсе таки, как получить веса вершин? Используя объект ID3DXSkinInfo, о котором я упоминал ранее, вы можете загружать веса из .X файлов. Скелетные веса хранятся в объекте Mesh, в конце его данных.

Каждой кости в скелетной структуре соответствует объект SkinWeights. Внутри объекта SkinWeights находится имя кости и следующее за ним количество присоединенных вершин. Заголовок скелетного меша определяет количество костей, к которым может быть присоединена каждая вершина. Если какая то из вершин присоединена к двум костям, тогда все вершины должны быть присоединены к двум костям. Для того чтобы вершины могли иметь разное количество костей, вы можете присваивать вес 0 ненужным костям.

Как я упоминал ранее, объект SkinWeights включает количество вершин, присоединенных к кости. Он перечисляет массив индексов вершин. После массива индексов вершин расположен массив значений весов вершин. Наконец, далее идет обратное преобразование костей для помощи в расположении вершин относительно соединений костей.

138 Глава 4

Посмотрите на пример объекта шаблона SkinWeights:

SkinWeights { "Bip01_R_UpperArm"; 4; 0, 3449, 3429, 1738;

0.605239, 0.605239, 0.605239, 0.979129; -0.941743, -0.646748, 0.574719, 0.000000, -0.283133, -0.461979, -0.983825, 0.000000, 0.923060, -1.114919, 0.257891, 0.000000, -65.499557, 30.497688, 12.852692, 1.000000;;

}

В этом объекте используется кость "Bip01_R_UpperArm". К ней присоединены четыре вершины с индексами 0, 3449, 3429 и 1738. Вершина 0 имеет вес 0.605239, вершина 1 - 0.605239 и так далее. Матрица преобразования выравнивает перечисленные вершины относительно соединения костей. Эта матрица очень важна. Без нее вершины будут вращаться относительно центра мира, а не соединения костей.

К счастью, вам не нужно напрямую работать с шаблоном SkinWeights. Эти данные обрабатываются при загрузке скелетных мешей из .X файлов, используя функции D3DX.

Загрузка скелетных мешей из .X

Загрузка скелетных мешей из .X файлов очень похожа на загрузку обычных мешей. Используя специализированный анализатор .X файлов, вы должны перечислить объекты, содержащиеся в .X файле, при помощи функции ParseObject. Когда придет время обрабатывать объект Mesh, вместо вызова функции D3DXLoadMeshFromXof для загрузки данных меша используйте D3DXLoadSkinMeshFromXof, которая имеет дополнительный параметр - указатель на объект ID3DXSkinInfo. Посмотрите на прототип функции D3DXLoadSkinMeshFromXof, чтобы понять о чем я.

HRESULT D3DXLoadSkinMeshFromXof(

IDirectXFileData *pXofObjMesh, // Объект .X файла DWORD Options, // Опции загрузки

IDirect3DDevice9 *pDevice, // Используемое 3D устройство ID3DXBuffer **ppAdjacency, // объект буфера смежности ID3DXBuffer **ppMaterials, // объект буфера материала ID3DXBuffer **ppEffectInstances, // объекты экземпляров эффектов DWORD *pMatOut, // # материалов

ID3DXSkinInfo **ppSkinInfo, // объект информации скелета!!! ID3DXMesh **ppMesh); // загруженный объект меш

Работа со скелетнойанимацией

Когда вы готовы загрузить меш из перечисленного шаблона Mesh, вызовите функцию D3DXLoadSkinMeshFromXof вместо D3DXLoadMeshFromXof. Убедитесь, что вы указали объект ID3DXSkinInfo, заданный в прототипе. Неважно, содержит ли шаблон Mesh скелетный меш, функция D3DXLoadSkinMeshFromXof загружает как обычные, так и скелетные меши. Вот пример:

//Определить структуры меша и информации скелетного меша ID3DXMesh *pMesh;

ID3DXSkinInfo *pSkinInfo;

//Определить буферы для хранения данных материалов и смежностей ID3DXBuffer *pMaterialBuffer = NULL, *pAdjacencyBuffer = NULL;

//DWORD для хранения количества загруженных материалов

DWORD NumMaterials;

// Загрузить скелетный меш из IDirectXFileDataObject pDataObj D3DXLoadSkinMeshFromXof(pDataObj, D3DXMESH_SYSTEMMEM, \

pDevice, &pAdjacencyBuffer, \ &pMaterialBuffer, NULL, \ &NumMaterials, &pSkinInfo, &pMesh);

Использование функции D3DXLoadSkinnedMeshFromXof еще не означает, что загруженный меш является скелетным. Сначала вам необходимо проверить объект pSkinInfo. Если он установлен в NULL, тогда скелетный меш не был загружен. Если же он правильный объект (не NULL), тогда вам необходимо проверить существование костей.

Самым простым способом проверить наличие костей является вызов ID3DXSkinInfo::GetNumBones. Эта функция возвращает количество костей, загруженных из шаблона Mesh. Если количество костей 0, то тогда их нет, и вы можете освобождать объект ID3DXSkinInfo (используя Release). Если же кости существуют, вы можете продолжать использованиe скелетного меша.

Посмотрите на этот пример, который тестирует, является ли загруженный меш скелетным. Если да, то проверяется наличие в меше костей.

// Установить флаг, если меш скелетный и есть кости BOOL SkinnedMesh = FALSE;

if(pSkinInfo && pSkinInfo->GetNumBones()) SkinnedMesh = TRUE;

else {

// Освободить данные объекта информации скелетного меша if(pSkinInfo) {

pSkinInfo->Release(); pSkinInfo = NULL;

}

}

Соседние файлы в предмете Программирование на C++