- •Введение
- •Глава 1. Подготовка к изучению книги
- •Установка DirectX SDK
- •Выбор отладочных или рабочих версий библиотек
- •Настройка вашего компилятора
- •Установка директорий DirectX SDK
- •Привязывание к библиотекам DirectX
- •Установка используемого по умолчанию состояния символа
- •Использование вспомогательного кода книги
- •Использование вспомогательных объектов
- •Проверка вспомогательных функций
- •Двигаясь дальше по книге
- •Глава 2. Синхронизация анимации и движения
- •Использование движения, синхронизированного по времени
- •Считывание времени в Windows
- •Анимирование с использованием временных меток
- •Перемещение, синхронизированное со временем
- •Движение вдоль траекторий
- •Создание анализатора маршрутов .X файла
- •Создание внутриигровых кинематографических последовательностей
- •Посмотрите демонстрационные программы
- •TimedAnim
- •TimedMovement
- •Route
- •Cinematic
- •Глава 3. Использование формата файла .X
- •Работа с .X шаблонами и объектами данных
- •Определение шаблонов
- •Работа со стандартными шаблонами DirectX
- •Открытие .X файла
- •Перечисление объектов данных
- •Получение данных объекта
- •Создание класса .X анализатора
- •Загрузка мешей с использованием D3DX
- •Загрузка мешей, используя анализатор .X
- •Загрузка скелетных мешей
- •Загрузка анимации из .X
- •Загрузка специализированных данных из .X
- •Посмотрите демонстрационные программы
- •ParseFrame
- •Глава 4. Работа со скелетной анимацией
- •Начало скелетной анимации
- •Использование структур скелетов и иерархий костей
- •Использование скелетной структуры и скелетного меша
- •Загрузка иерархий из .X
- •Изменение положения костей
- •Обновление иерархии
- •Работа со скелетными мешами
- •Загрузка скелетных мешей из .X
- •Создание контейнера вторичного меша
- •Сопоставление костей фреймам
- •Обновление скелетного меша
- •Визуализация скелетных мешей
- •Глава 5. Использование скелетной анимации, основанной на ключевых кадрах
- •Использование наборов скелетных анимаций, основанных на ключевых кадрах
- •Использование ключей при анимации
- •Работа с четырьмя типами ключей
- •Считывание данных анимации из .X файлов
- •Прикрепление анимации к костям
- •Обновление анимации
- •Посмотрите демонстрационные программы
- •Глава 6. Комбинирование скелетных анимаций
- •Комбинирование скелетных анимаций
- •Соединение преобразований
- •Улучшение объектов скелетной анимации
- •Посмотрите демонстрационные программы
- •Глава 7. Создание кукольной анимации
- •Работа с физикой твердого тела
- •Создание твердого тела
- •Расположение и ориентирование твердых тел
- •Обработка движения твердых тел
- •Использование сил для создания движения
- •Соединение твердых тел с помощью пружин
- •Обеспечение обнаружения столкновений и ответной реакции
- •Создание систем кукольной анимации
- •Определение состояния твердого тела
- •Хранение костей
- •Создание класса управления куклой
- •Создание данных костей
- •Вычисление ограничивающего параллелепипеда кости
- •Установка сил
- •Объединение костей
- •Обработка столкновений
- •Восстановление соединений костей
- •Перестроение иерархии
- •Посмотрите демонстрационные программы
- •Глава 8. Работа с морфирующей анимацией
- •Морфинг в действии
- •Определение исходного и целевого меша
- •Морфинг мешей
- •Создание морфированного меша при помощи обработки
- •Визуализация морфированных мешей
- •Расчленение наборов
- •Создание морфирующего вершинного шейдера
- •Посмотрите демонстрационные программы
- •Глава 9. Использование морфирующей анимации, основанной на ключевых кадрах
- •Использование наборов морфируемой анимации
- •Создание шаблонов .X для морфируемой анимации
- •Загрузка данных морфируемой анимации
- •Визуализации морфированного меша
- •Получение данных морфируемого меша из альтернативных источников
- •Посмотрите демонстрационные программы
- •Глава 10. Комбинирование морфированных анимаций
- •Комбинирование морфированных анимаций
- •Использование базового меша в комбинированных морфированных анимациях
- •Вычисление разностей
- •Комбинирование разностей
- •Создание вершинных шейдеров комбинированного морфирования
- •Использование вершинного шейдера морфируемого комбинирования
- •Посмотрите демонстрационные программы
- •Глава 11. Морфируемая лицевая анимация
- •Основы лицевой анимации
- •Использование комбинированного морфирования
- •Использования фонем для речи
- •Создание лицевых мешей
- •Создание базового меша
- •Создание выражений лица
- •Создание мешей визем
- •Создание анимационных последовательностей
- •Создание последовательностей фонем
- •Использование анализатора файлов .X для последовательностей
- •Проигрывание лицевых последовательностей со звуком
- •Использование DirectShow для звука
- •Синхронизация анимации со звуком
- •Зацикливание воспроизведения звуков
- •Посмотрите демонстрационные программы
- •Глава 12. Использование частиц в анимации
- •Работа с частицами
- •Основы
- •Рисование частиц с помощью квадратных полигонов
- •Работа с точечными спрайтами
- •Улучшения визуализации частиц при помощи вершинных шейдеров
- •Оживление частиц
- •Передвижение частиц при помощи скорости
- •Использование интеллекта при обработке
- •Создание и уничтожение частиц
- •Управление частицами с помощью класса
- •Использование излучателей в проектах
- •Создание движков частиц в вершинных шейдерах
- •Посмотрите демонстрационные программы
- •Глава 13. Имитирование одежды и анимация мешей мягких тел
- •Имитация одежды в ваших проектах
- •Получение данных одежды из мешей
- •Приложение сил для создания движения
- •Воссоздание и визуализация меша одежды
- •Восстановление исходного меша
- •Добавление дополнительных пружин
- •Загрузка данных масс и пружин из .X файла
- •Создание анализатора .X данных одежды
- •Работа с обнаружением столкновений и реакцией на них
- •Определение объектов столкновений
- •Обнаружение и реакция на столкновения
- •Создание класса меша одежды
- •Использование мешей мягких тел
- •Восстановление мешей мягких тел
- •Посмотрите демонстрационные программы
- •Глава 14. Использование анимированных текстур
- •Использование анимации текстур в ваших проектах
- •Работа с преобразованиями текстур
- •Создание преобразования текстур
- •Установка матриц преобразования текстуры
- •Использование преобразования текстур в проектах
- •Использование файлов видео в качестве текстур
- •Импорт видео при помощи DirectShow
- •Создание специализированного фильтра
- •Работа со специализированным фильтром
- •Создание менеджера анимированных текстур
- •Окончание современной анимации
- •Веб-сайты
- •Рекомендуемые книги
- •DirectX 9.0 SDK
- •GoldWave Demo
- •Paint Shop Pro Trial Version
- •TrueSpace Demo
- •Microsoft Agent and LISET
- •Предметный указатель
226 |
Глава 7 |
Создание данных костей
Яне хочу бить мертвую лошадь, поэтому я упомяну только те части данных,
окоторых я не рассказывал ранее. Кроме положения и ориентации кости степень разрешения вращения определяет, насколько ваш меш сохраняет свою начальную форму со временем. Чем меньше значение степени разрешения, тем сильнее может искажаться тело. Чем больше разрешение, тем тело становится устойчивее к искажениям.
Ярасскажу о степени разрешения немного позже; а пока, я хочу поговорить о других вещах, таких как вычисление начальной ориентации, размера ограничивающего параллелепипеда каждой кости, локальных точек, формирующих углы ограничивающего параллелепипеда, коэффициента restitution и векторов родительского смещения.
Вы уже читали практически обо всех этих переменных, но я ничего не говорил
овычислении размера ограничивающего параллелепипеда и начальной ориентации каждой кости. Я начну с вычисления ориентации.
Помните, давно, в разделе "Расположение и ориентация твердых тел", я сказал, что можно использовать кватернион для ориентации твердого тела? Если в качестве источника кукольных костей вы используете иерархию фреймов, вам необходимо перейти от матрицы преобразования к кватерниону. Как же это сделать?
Здесь приходит на помощь D3DX! Используя комбинированную матрицу преобразования фрейма, вы можете вызвать функцию D3DXQuatemionRotationMatrix, которая преобразует матрицу в кватернион! Например, у вас есть указатель на фрейм D3DXFRAME_EX pFrame. Для создания кватерниона вы можете использовать следующий код:
//pFrarae = указатель на фрейм
//quatOrientation = результирующий кватернион D3DXQuaternionRotationMatrix(&quatOrientation, \
&m_Frame->matCombined); D3DXQuaternionInverse(&quatOrientation, &quatOrientation);
Вы заметите, что при преобразовании матрицы в кватернион я добавил вызов функции D3DXQuaternionInverse, которая обращает кватернион. Причиной является то, что кватернионы используют правостороннюю систему координат. Т. к. Direct3D (и мы вместе с ним) использует левостороннюю систему координат, кватернион должен быть соответственно преобразован (обращен).
После того как вы получили начальную ориентацию, с которой можете работать, вы можете вычислить размеры ограничивающего параллелепипеда кости; создайте набор точек, представляющих углы ограничивающего параллелепипеда и точки соединения (точки, присоединяющие кость к родителю); установите массу и коэффи-
Созданиекукольнойанимации
циент ограничения; создайте обратный тензор инерции. Исходный код, о котором я говорю, находится в функции cRagdoll::Create. Пожалуйста, проконсультируйтесь с подробно комментированным исходным кодом, чтобы увидеть, как устанавливаются соответствующие данные при создании кукольных костей.
А пока что позвольте мне объяснить, как вычислять размеры ограничивающих параллелепипедов.
Вычисление ограничивающего параллелепипеда кости
Для создания класса кости твердого тела, представляющего каждую кость, сначала необходимо получить обратную матрицу преобразования кости, используя функцию скелетного меша ID3DXSkinInfo::GetBoneOffsetMatrix. Эта обратная матрица преобразования кости ориентирует вершины скелетного меша относительно центра меша (в противоположность центру фрейма).
Помните, в главе 4 я объяснял, как должны быть расположены вершины скелетного меша относительно центра меша, чтобы они корректно вращались относительно центра кости? Весь процесс преобразования вершин заключается в наложении обратной матрицы преобразования с последующим поворотом кости и наложении преобразования перемещения, скомбинированного с родительским преобразованием фрейма.
После того как вы получили обратную матрицу преобразования кости, необходимо перебирать все вершины, присоединенные к ней. При этом каждую перебираемую вершину необходимо преобразовывать, используя обратное преобразование кости. Используя координаты только что преобразованной вершины, вы можете вычислить расширение ограничивающего параллелепипеда (который, в конце концов, будет содержать все вершины и точки соединения костей).
Функция cRagdoll::GetBoundBoxSize вычисляет ограничивающий параллелепипед. В качестве ее параметров задаются указатель на структуру фрейма (который представляет собой кость) и два вектора, содержащие размер ограничивающего параллелепипеда и смещение его центра относительно точки присоединения к родительской кости.
void cRagdoll::GetBoundingBoxSize(D3DXFRAME_EX *pFrame, D3DXVECTOR3 *vecSize,
D3DXVECTOR3 *vecJointOffset)
{
228 |
Глава 7 |
Ярасскажу о векторах размера и смещения немного позже. А пока, поместите
вначале функции GetBoundingBoxSize код, создающий и очищающий пару векторов, которые будут содержать координаты расширений твердого тела и в последствии будут использованы для создания восьми угловых точек твердого тела.
//Установить минимальные и максимальные координаты по умолчанию D3DXVECTOR3 vecMin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vecMax = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
Первой задачей GetBoundingBoxSize является поиск кости, имеющей такое же имя, как и заданный фрейм. Эта кость, точнее интерфейс объекта кости скелетного меша (ID3DXSkinInfo), делает запрос о вершинах, присоединенных к ней.
// Обрабатывать вершины кости, если кость задана if(pFrame->Name) {
//Получить указатель на интерфейс ID3DXSkinInfo ID3DXSkinInfo *pSkin = pMesh->pSkinInfo;
//Найти кость с таким же именем, как и у фрейма DWORD BoneNum = -1;
for(DWORD i=0;i<pSkin->GetNumBones();i++) { if(!strcmp(pSkin->GetBoneName(i), pFrame->Name)) { BoneNum = i;
break;
}
}
// Обработать вершины, если кость была найдена if(BoneNum != -1) {
После того как вы обнаружили ID3DXSkinInfo для интересующей кости, вы можете сделать ей запрос о количестве присоединенных к ней вершин и создать массивы DWORD и вещественных чисел для хранения индексов вершин и их весов.
// Получить количество присоединенных вершин
DWORD NumVertices = pSkin->GetNumBoneInfluences(BoneNum); if(NumVertices) {
// Получить влияния костей
DWORD *Vertices = new DWORD[NumVertices]; float *Weights = new float[NumVertices];
pSkin->GetBoneInfluence(BoneNum, Vertices, Weights);
Созданиекукольнойанимации
После того как вы получили индексы вершин, хранящиеся в буфере Vertices (который вы заполняете с помощью функции GetBoneInfluence), вы можете начать просматривать вершины, преобразовывать их с помощью обратного преобразования кости и использовать преобразованные вершины для вычисления размера ограничивающего параллелепипеда.
// Получить тип данных вершин
DWORD Stride = D3DXGetFVFVertexSize( \ pMesh->MeshData.pMesh->GetFVF());
// Получить обратную матрицу преобразования смещения кости D3DXMATRIX *matInvBone = \
pSkin->GetBoneOffsetMatrix(BoneNum);
//Заблокировать буфер вершин и просмотреть все вершины,
//присоединенные к кости
char *pVertices; pMesh->MeshData.pMesh->LockVertexBuffer( \
D3DLOCK_READONLY, (void**)&pVertices); for(i=0;i<NumVertices;i++) {
// Получить указатель на координаты вершин D3DXVECTOR3 *vecPtr = \
D3DXVECTOR3*)(pVertices+Vertices[i]*Stride);
// Преобразовать вершины на преобразование смещения кости D3DXVECTOR3 vecPos;
D3DXVec3TransformCoord(&vecPos, vecPtr, matInvBone);
// Получить минимальные/максимальные значения vecMin.x = min(vecMin.x, vecPos.x);
vecMin.y = min(vecMin.y, vecPos.y); vecMin.z = min(vecMin.z, vecPos.z);
vecMax.x = max(vecMax.x, vecPos.x); vecMax.y = max(vecMax.y, vecPos.y); vecMax.z = max(vecMax.z, vecPos.z);
}
pMesh->MeshData.pMesh->UnlockVertexBuffer();
// Освободить ресурсы delete [] Vertices; delete [] Weights;
}
}
}
В конце выполнения этого кусочка кода в созданных в начале функции векторах (vecMin и vecMax) будут храниться размеры ограничивающего параллелепипеда. Массив индексов вершин освобождается (как и весов вершин), и обработка продолжается нахождением точек присоединения кости к родителю и потомкам.
230 |
Глава7 |
// Учесть точки присоединения потомков в размерах if(pFrame->pFrameFirstChild) {
//Получить обратное преобразование кости для расположения
//дочерних соединений
D3DXMATRIX matInvFrame;
D3DXMatrixInverse(&matInvFrame,NULL,&pFrame->matCombined); // Перебрать все дочерние фреймы, присоединенные к текущему D3DXFRAME_EX *pFrameChild = \
D3DXFRAME_EX*)pFrame->pFrameFirstChild; while(pFrameChild) {
//Получить координаты вершины фрейма и преобразовать их D3DXVECTOR3 vecPos;
vecPos = D3DXVECTOR3(pFrameChild->matCombined._41, pFrameChild->matCombined._42, pFrameChild->matCombined._43);
D3DXVec3TransformCoord(&vecPos, &vecPos, &matInvFrame);
//Получить минимальные/максимальные значения
vecMin.x = min(vecMin.x, vecPos.x); vecMin.y = min(vecMin.y, vecPos.y); vecMin.z = min(vecMin.z, vecPos.z);
vecMax.x = max(vecMax.x, vecPos.x); vecMax.y = max(vecMax.y, vecPos.y); vecMax.z = max(vecMax.z, vecPos.z);
// Перейти к следующей дочерней кости
pFrameChild = (D3DXFRAME_EX*)pFrameChild->pFrameSibling;
}
}
Обычно для учета точек соединения берутся координаты присоединенной кости в мировом пространстве и преобразуются на обратную матрицу кости. После чего полученные координаты сравниваются с координатами, хранящимися в векторах vecMin и vecMax.
Теперь вы можете закончить функцию, сохранив размер параллелепипеда. Если его размер слишком мал, то он устанавливается в минимальное значение (при помощи макросаMINIMUM_BONE_SIZE,который устанавливает в 1.0)
//Установить размер ограничивающего параллелепипеда vecSize->x = (float)fabs(vecMax.x - vecMin.x); vecSize->y = (float)fabs(vecMax.y - vecMin.y); vecSize->z = (float)fabs(vecMax.z - vecMin.z);
//Убедиться, что каждая кость имеет минимальный размер if(vecSize->x < MINIMUM_BONE_SIZE) {
vecSize->x = MINIMUM_BONE_SIZE;
vecMax.x = MINIMUM_BONE_SIZE*0.5f;
}