- •Введение
- •Глава 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
- •Предметный указатель
Комбинирование скелетных анимаций
D3DXMATRIX matResult = Matrix1 + Matrix2;
Выполнив эти действия, вы можете использовать матрицу matResult для преобразований; она является комбинированным преобразованием матриц Matrix1 и Matrix2. Чтобы соединить большее количество преобразований анимации, просто добавьте ещё одну матрицу к matResult и продолжайте делать это пока не соедините все используемые преобразования.
Теперь вы знаете, как можно соединять разнообразные преобразования наборов анимаций. Для упрощений вы можете расширить объекты анимаций, разработанные в главе 5.
Улучшение объектов скелетной анимации
После того как вы узнали насколько просто комбинировать несколько скелетных анимаций, почему бы вам не применить эти знания и не добавить их к объектам скелетной анимации и коду, рассмотренным в главе 5? Звучит заманчиво: добавив всего одну функцию к классу cAnimationCollection, вы будете комбинировать анимации совсем как профессионалы.
На самом деле, вместо того чтобы смешивать новый код с кодом cAnimationCollection, давайте просто унаследуем от него новый класс, который бы отвечал за комбинированные анимации. Этот новый унаследованный класс cBlendedAnimationCollection будет определяться так:
class cBlendedAnimationCollection : public cAnimationCollection
{
public:
void Blend(char *AnimationSetName, DWORD Time, BOOL Loop,
float Blend = 1.0f);
};
Ничего себе, вот это маленький класс! В классе cBlendedAnimationCollection объявлена только одна функция Blend, которая выполняет обязанности функции cAnimationCollection::Update. Вы спросите, почему просто не унаследовать функцию Update? При помощи cBlendedAnimationCollection вы сможете использовать как обычные наборы анимаций из главы 5, так и только что разработанные наборы комбинированных анимаций.
Давайте рассмотрим поподробнее функцию Blend, после чего я покажу вам, как использовать недавно созданный класс.
176
void cBlendedAnimationCollection::Blend( \ char *AnimationSetName, \ DWORD Time, BOOL Loop, \ float Blend)
{
У прототипа функции Blend имеется четыре параметра, первый из которых - AnimationSetName. При вызове Blend вам необходимо передать в качестве AnimationSetName имя набора анимаций, который вы будете смешивать с текущей. Помните, в главе 5, каждый набор анимаций, содержащийся в .X файле имеет собственное уникальное имя (определяемое именем экземпляра объекта AnimationSet). Вам необходимо задать в качестве AnimationSetName соответствующее имя из .X файла.
Я вернусь к наборам анимаций немного позже. А пока продолжим рассмотрение прототипа Blend. Вторым параметром Blend является Time, который представляет собой время в анимации, используемое для комбинирования. Если анимация длится 1000 миллисекунд, то вы можете изменять Time в диапазоне от 0 до 999. Задание значения большего, чем длительность анимации, вынудит функцию Blend использовать последний ключевой кадр для комбинирования анимаций.
А что относительно циклического повтора анимации? За это ответственен третий параметр - Loop. Если вы установите Loop в FALSE, тогда ваша анимация не будет обновляться, если задаваемое время больше, чем длительность анимации. Однако, если Loop установлен в TRUE, функция Blend проверяет величину времени таким образом, чтобы оно всегда попадало в продолжительность анимации.
Предыдущий абзац может показаться непонятным поначалу, поэтому для большей понятности представьте себе такую функцию:
void UpdateAnimation(DWORD Elapsed)
{
static DWORD AnimationTime = 0; // время анимации
// Вызвать Blend, используя AnimationTime в качестве времени анимации
AnimationBlend.Blend("Walk", AnimationTime, FALSE, 1.0f);
// Обновить время анимации AnimationTime += ELapsed;
}
Функция UpdateAnimation предназначена для отслеживания времени анимации при помощи статической переменной. Во время каждого вызова UpdateAnimation функция Blend используется для комбинирования анимации Walk на основе времени, задаваемого AnimationTime. Положим, что анимация Walk имеет продолжительность 1000 мил-
Комбинирование скелетныханимаций
лисекунд, а время между вызовами функции UpdateAnimation 50 мс, таким образом, анимация закончится после 20 вызовов функции. Это означает, что после 20 вызовов функции UpdateAnimation анимация остановится (т. к. Loop установлен в FALSE).
Вернувшись и установив Loop в TRUE, мы вынудим функцию проверять значения времени, для убеждения, что оно всегда меньше длительности анимации. Когда я говорю проверяем, я имею ввиду модульное вычисление. Я покажу вам, как использовать модульное вычисление немного позже; а пока вернемся к четвертому и последнему параметру.
Последним параметром является Blend, который представляет собой вещественное число, скаляр, используемый для изменения матрицы смешанного преобразования перед ее применением к скелетной структуре. Например, если вы комбинируете анимацию ходьбы, но хотите, чтобы только половина преобразования была применена, тогда установите Blend в 0.5.
Хорошо, достаточно о параметрах; давайте перейдем к коду функции! Если вы использовали функцию cAnimationCollection::Update, вы заметите, что большая часть кода функции Blend такая же. В начале кода вы найдете кусочек, который проверяет связанный список наборов анимаций для нахождения заданного параметром AnimationSetName.
cAnimationSet *AnimSet = m_AnimationSets;
// Если задано, искать соответствующее имя набора анимации if(AnimationSetName) {
// Найти соответствующее имя набора анимации while{AnimSet != NULL) {
//Остановиться, если совпадение найдено if(!stricmp(AnimSet->m_Name, AnimationSetName))
break;
//Перейти к следующему объекту набора анимаций AnimSet = AnimSet->m_Next;
}
}
// Вернуть, если ничего не найдено if(AnimSet == NULL)
return;
Если вы установите в качестве параметра AnimationSetName NULL, то функция Blend будет использовать первый набор анимаций, находящийся в связанном списке. Если же вы зададите имя в AnimationSetName и оно не будет найдено в связанном списке, то функция Blend завершит работу.
178 |
Глава 6 |
После того, как вы получили указатель на соответствующий объект набора анимаций, вы можете проверить время, основываясь на заданный параметр Time
изначение циклического флага Loop.
//Ограничить время длительностью анимации if(Time > AnimSet->m_Length)
Time = (Loop==TRUE)?Time % \
(AnimSet->m_Length+1):AnimSet->m_Length;
Всамом деле предыдущий маленький изящный кусок кода выполняет две вещи
взависимости от флага Loop. Если Loop установлен в FALSE, то Time сравнивается с продолжительностью анимации (AnimSet->m_Length). Если Time больше длительности анимации, тогда она устанавливается равной продолжительности анимации, таким образом блокируясь на последней секунде (далее - последнем ключевом кадре) анимации. Если Loop установлен в FALSE, то в результате модального вычисления значение Time всегда лежит в пределах длительности анимации (от 0 до AnimSet->m_Length).
После вычисления Time, используемого для анимации, просматривается список костей в скелетной структуре. Для каждой кости вам необходимо отследить все комбинированные преобразования соответствующих ключевых кадров. Для каждого ключевого кадра, найденного в анимации, вам необходимо прибавить (не умножить) его преобразование к преобразованиям скелетной структуры.
// Просмотреть все анимации
cAnimation *Anim = AnimSet->m_Animations; while(Anim) {
// Обрабатывать, если присоединена к кости if(Anim->m_Bone) {
//Сбросить преобразование D3DXMATRIX matAnimation; D3DXMatrixIdentity(&matAnimation);
//Наложить разнообразные матрицы для преобразования
После этого вы просматриваете каждый ключевой кадр (основываясь на их типах) и вычисляете преобразование, которое необходимо наложить на скелетную структуру. Для экономии места я только приведу код, который просматривает матричные ключи.
// Матрица
if(Anim->m_NumMatrixKeys && Anim->m_MatrixKeys) { // Сделать цикл для нахождения матричного ключа DWORD Key1 = 0, Кеу2 = 0;
for(DWORD i=0;i<Anim->m_NumMatrixKeys;i++) { if(Time >= Anim->m_MatrixKeys[i].m_Time)
Key1 = i;
}
Комбинированиескелетныханимаций
// Получить значение второго ключа
Кеу2 = (Key1>=(Anim->m_NumMatrixKeys-1))?Key1:Key1+1;
// Получить разность времен ключей
DWORD TimeDiff = Anim->m_MatrixKeys[Key2].m_Time-
Anim->m_MatrixKeys[Key1].m_Time; if(!TimeDiff)
TimeDiff = 1;
//Вычислить используемое значение скаляра float Scalar = (float)(Time - \
Anim->m_MatrixKeys[Key1].m_Time) / (float)TimeDiff;
//Вычислить интерполированную матрицу
D3DXMATRIX matDiff;
matDiff = Anim->m_MatrixKeys[Key2].m_matKey - \ Anim->m_MatrixKeys[Key1].m_matKey;
matDiff *= Scalar;
matDiff += Anim->m_MatrixKeys[Key1].m_matKey;
// Соединить с преобразованием matAnimation *= matDiff;
}
Я привел код, показанный в главе 5, так что я не буду объяснять его здесь еще раз. Вкратце, код ищет ключевые кадры и вычисляет соответствующее преобразование. Это преобразование хранится в матрице matAnimation.
Начиная отсюда, код будет совершенно отличен от функции cAnimationCollection::Update. Вместо того чтобы хранить матрицу преобразования (matAnimation) в объекте фрейма скелетной структуры, вычисляется разность между преобразованием
matAnimation и |
начальным преобразованием скелетной структуры (сохраненным |
в matOriginal при |
загрузке скелетной структуры). Эта разность масштабируется, |
используя вещественный параметр Blend, и результирующее преобразование добавляется (не умножается как при concoction) к преобразованию фрейма скелетной структуры. Это позволяет убедиться, что преобразования корректно комбинируются при использовании соответствующих значений.
После этого просматриваются ключевые кадры следующей кости, и цикл продолжается, пока все кости не будут обработаны.
// Получить разность преобразований
D3DXMATRIX matDiff = matAnimation - Anim->m_Bone->matOriginal;
// Скорректировать на значение смешивания matDiff *= Blend;
// Добавить к матрице преобразования Anim->m_Bone->TransformationMatrix += matDiff;
}
180
// Перейти к следующей анимации Anim = Anim->m_Next;
}
}
Мои поздравления, вы завершили создание функции Blend! Давайте попробуем ее
вработе! Предположим, что вы уже загрузили меш и иерархию фреймов, а теперь хотите загрузить последовательность анимаций из .X файла. Далее предположим, что
в.X файле (названном Anims.x) содержатся четыре набора анимаций - Stand, Walk, Wave и Shoot. Две из них используются для ног и две для рук и торса. Вот пример кода, загружающего наборы анимаций:
//pFrame = корневой фрейм в иерархии фреймов cBlendedAnimationSet BlendedAnims; BlendedAnims.Load("Anims.x");
//Нанести иерархию фреймов анимации BlendedAnims.Map(pFrame);
После того как вы загрузили набор анимаций, вы можете скомбинировать их перед обновлением и визуализацией скелетных мешей. Предположим, вы хотите скомбинировать анимации Walk и Shoot, обе из которых используют сто процентов преобразований. Для начала вам необходимо сбросить преобразования иерархии фреймов к их начальному состоянию. Это означает, что вам необходимо скопировать преобразование D3DXFRAME_EX::matOriginal в преобразование D3DXFRAME_EX::TransformationMatrix. Этот момент очень важен, т. к. является базой, к которой добавляются все преобразования при смешивании.
Замечание. Объект D3DXFRAME_EX является расширенной версией объекта DirectiD D3DXFRAME. Вы можете прочитать о нем в главе 1.
// Использовать D3DXFRAME_EX::Reset для сброса преобразований pFrame->Reset();
После того как преобразования были сброшены к их начальным состояниям, выможетекомбинировать наборыанимаций.
//AnimationTime = время с начала анимации
//смешать анимацию ходьбы
BlendedAnims.Blend("Walk", AnimationTime, TRUE, 1.0f);
// смешать анимацию стрельбы BlendedAnims.Blend("Shoot", AnimationTime, TRUE, 1.0f);