- •Введение
- •Глава 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
- •Предметный указатель
24
Использование вспомогательного кода книги
Т. к. это продвинутая книга, я предполагаю, что вы по крайней мере имеете опыт работы с DirectX. Я имею в виду, что вы достаточно опытны, чтобы понять, как Direct3D использует конвейер рендеринга, буферы вершин, текстуры и меши - основы необходимые для создания приложений Direct3D.
Чтобы ускорить разработку и уменьшить время написания тривиального, повторяющегося кода, я создал небольшой набор функций и объектов, которые используются на протяжении всей книги. Эти функции помогают вам иметь дело с теми особенностями Direct3D, которые вы используете чаще всего, такие как инициализация Direct3D, загрузка и визуализация (рендеринг) мешей.
Наряду с этими функциями, я также создал набор объектов, которые расширяют функциональность некоторых объектов D3DX. Эти объекты - расширенные версии объектов D3DXFRAME и D3DXMESHCONTAINER, а также объекты, которые помогут использовать .X файлы в ваших проектах.
Итак, чтобы не делать короткую историю длиннее, чем надо, давайте вернемся к делу и посмотрим, что за вспомогательными функциями и объектами вы располагаете, начав с вспомогательных объектов.
Использование вспомогательных объектов
Как я ранее замечал, я создал набор объектов, которые расширяют функциональность D3DXFRAME и D3DXMESHCONTAINER, которые являются частью библиотеки D3DX. Если вы незнакомы с этими объектами, позвольте мне дать их небольшой обзор.
Объект D3DXFRAME помогает сформировать иерархию ссылок на фреймы . Эти ссылки используются для соединения нескольких мешей вместе, в то время как каждый фрейм производит над присоединенным к нему мешем собственные преобразования. Этот путь использования фреймов, указывающих на меш, позволяет минимизировать количество мешей, т. к. можно делать ссылки на меш вместо того, чтобы перезагружать их.
Например, представьте машину, состоящую их корпуса и четырех колес. Корпус и колеса образуют 2 меша. Эти два меша используются совместно с пятью фреймами (один для корпуса и четыре для колес). При визуализации преобразования каждого фрейма используются для расположения и отрисовки меша исполь-
3. Frame — фрейм, невидимый куб, который предоставляет связи для объектов в сцене. Объекты (меши) могут быть помещены в сцену с указанием их пространственных отношений к существующему фрейму. Видимые объекты получают свои положения и ориентацию из фреймов. - Примеч. науч. ред.
Подготовкакизучениюкниги
зуемого фреймом. Это значит что один фрейм преобразовывает и отрисовывает корпус один раз, в то время как остальные фреймы преобразовывают и отрисовывают меш колеса четыре раза.
Что касается объекта D3DXMESHCONTAINER, он используется чтобы хранить меш и ссылки на серию других мешей (используя связанный список). Вы спросите: почему вместо этого объекта не использовать ID3DXBaseMesh? Функциональность D3DXMESHCONTAINER больше, чем вы можете ожидать. Во-первых, он может хранить любые типы мешей, будь то обычные (regular), скелетные4 (skinned) или прогрессивные5 (progressive). Во-вторых, объект D3DXMESHCONTAINER содержит информацию о материалах и данные эффектов.
Для получения полной информации об объектах D3DXMESHCONTAINER и D3DXFRAME обратитесь к документации DirectX SDK. А сейчас давайте посмотрим, как я реализовал расширение функциональности этих объектов (определенных в заголовочном файле \common\Direct3D.h компакт диска этой книги), начав с D3DXFRAME.
Расширение D3DXFRAME
Сам по себе объект D3DXFRAME очень полезен, но, к сожалению, он имеет некоторые недостатки, такие как отсутствие данных для преобразований при анимации меша, функций обрабатывающих данные анимации, конструктора и деструктора.
Чтобы исправить эти недостатки, я создал расширенную версию D3DXFRAME, которую назвал D3DXFRAME_EX. Этот новый объект содержит дополнение в виде двух объектов D3DMATRIX и шести функций. Два объекта матрицы содержат первоначальное преобразование фрейма (прежде чем любое анимационное преобразование наложено) и комбинированное преобразование всех родительских фреймов, к которым данный фрейм присоединен (в иерархии).
Вот как я определил структуру D3DXFRAME_EX, использующую два матричных объекта.
struct D3DXFRAME_EX : D3DXFRAME
{
D3DXMATRIX matCombined; // комбинированная матрица D3DXMATRIX matOriginal; // первоначальная матрица
4.Skinned mesh - скелетный меш, меш - который обтягивает иерархический скелет из костей. К каждой вершине меша может быть прикреплена одна или несколько костей. Используется для анимирования моделей. -
Примеч. науч. ред.
5.Progressive mesh - прогрессивный меш, это меш с возможностью динамически определять какое количество вершин и граней визуализировать. То есть возможно гибкое управление детализацией. - Примеч. науч. ред.
26 |
Глава 1 |
Вы узнаете об этих двух матрицах и как с ними работать далее в этой книге. На данный момент давайте просто перейдем к рассмотрению функций, начнем с конструктора. Конструктор предназначен для очистки данных структуры (включая изначальные данные базового объекта D3DXFRAME).
D3DXFRAME_EX()
{
Name - NULL; pMeshContainer = NULL;
pFrameSibling = pFrameFirstChild = NULL; D3DXMatrixIdentity(&matCombined); D3DXMatrixIdentity(&matOriginal); D3DXMatrixIdentity(&TransformationMatrix);
}
С другой стороны, деструктор предназначен для освобождения данных используемых объектом D3DXFRAME_EX.
~D3DXFRAME_EX()
{
delete [] Name; Name = NULL;
delete pFrameSibling; pFrameSibling = NULL; delete pFrameFirstChild; pFrameFirstChild = NULL;
}
Как вы можете видеть, конструктор и деструктор являются типичными - инициализация данных объекта и освобождение ресурсов, когда объект уничтожается. Далее идет набор функций, которые помогут вам найти заданный фрейм в иерархии, вернуть матрицы анимации к их первоначальному состоянию, обновить иерархию, после изменения преобразования и посчитать количество фреймов в иерархии.
Первая функция Find используется для нахождения заданного фрейма в иерархии, возвращая указатель на него. Если вы не знаете, каждый объект D3DXFRAME (и унаследованный от него D3DXFRAMEEX) имеет буфер данных Name, который можно заполнять любым текстом. Обычно фреймы называются так же как и кости, которые определяют иерархию (о чем я расскажу в главе 4 "Работа со скелетной анимацией").
Чтобы найти заданный фрейм (и получить указатель на него), просто вызовите функцию Find, указав имя фрейма, который необходимо найти, в качестве единственного параметра.
//Функция просматривает иерархию на совпадения имени фрейма D3DXFRAME_EX *Find(const char *FrameName)
{
D3DXFRAME_EX *pFrame, *pFramePtr;
Подготовкакизучениюкниги
//возвращаем этот экземпляр фрейма, если имя совпадает if(Name && FrameName && !strcmp(FrameName, Name))
return this;
//просмотр родственников
if((pFramePtr = (D3DXFRAME_EX*)pFrameSibling)) { if((pFrame = pFramePtr->Find(FrameName)))
return pFrame;
}
//просмотр потомков
if((pFramePtr = (D3DXFRAME_EX*)pFrameFirstChild)) { if((pFrame = pFramePtr->Find(FrameName)))
return pFrame;
}
//ничего не найдено return NULL;
}
Функция Find сравнивает переданное в качестве параметра имя с именем текущего фрейма; если они совпадают, возвращается указатель на фрейм. Если совпадений не найдено, тогда просматривается связанный список, используя для этого рекурсивные вызовы Find.
Далее в списке добавленных функций стоит Reset, которая просматривает всю иерархию фреймов (которая, кстати, является связанным списком потомков и родственников). Для каждого найденного фрейма она копирует первоначальное преобразование в текущее. Вот код:
//Устанавливает матрицы преобразования в изначальные void Reset()
{
//копируем оригинальную матрицу TransformationMatrix = matOriginal;
//сбрасываем родственные фреймы D3DXFRAME_EX *pFramePtr;
if((pFramePtr = (D3DXFRAME_EX*)pFrameSibling)) pFramePtr->Reset();
//сбрасываем дочерние фреймы
if((pFramePtr = (D3DXFRAME_EX*)pFrameFirstChild)) pFramePtr->Reset();
}
Обычно Reset используется для восстановления преобразований иерархии фреймов к тому состоянию, которое было при создании или загрузке фреймов. Повторюсь, полная иерархия фреймов описана в главе 4. Чтобы вас не запутывать,
Подготовкакизучениюкниги
void Count(DWORD *Num)
{
//Проверка на ошибку, if(!Num)
return;
//увеличить количество фреймов (*Num)+=1;
//Обработать родственные фреймы D3DXFRAME_EX *pFrame; if((pFrame=(D3DXFRAME_EX*)pFrameSibling))
pFrame->Count(Num);
//Обработать дочерние фреймы if((pFrame=(D3DXFRAME_EX*)pFrameFirstChild)) pFrame->Count(Num);
}
};
Это все в значительной степени описывает объект D3DXFRAME_EX. Если вы использовали объект D3DXFRAME (а вы должны были, если вы пользователь DX9), тогда все, что я только что показал вам, должно быть достаточно понятно.
Двигаясь дальше, позвольте мне представить вам следующий вспомогательный объект, который расширяет функциональность D3DXMESHCONTAINER.
Расширение D3DXMESHCONTAINER
Принимая во внимание то, что вы могли использовать объект ID3DXMesh, чтобы хранить данные ваших мешей, вы наверное обнаружили, как сложно хранить материал меша и данные эффектов раздельно. Но это еще не все. Как насчет использования других меш-объектов D3DX таких как ID3DXPMesh и ID3DXSkinMesh? Почему бы не сделать единый меш-объект, который содержал бы все эти типы мешей и данные о материалах?
На самом деле такой объект есть, он называется D3DXMESHCONTAINER! Объект D3DXMESHCONTAINER хранит указатели на данные меша (независимо от используемого типа меша), все материалы и данные эффектов. Он также содержит указатели на буфер смежности (adjacency buffer) и объект-скелетный меш. И как будто этого было недостаточно, D3DXMESHCONTAINER содержит указатели на связанный список меш-объектов.
30
Что бы я мог сделать, чтобы расширить функциональность уже существующую у D3DXMESHCONTAINER спросите вы? Ну, с одной стороны D3DXMESHCONTAINER не имеет конструктора и деструктора. Также отсутствуют данные текстур - есть только буфер, содержащий их имена, используемые мешем. Наконец, нет поддержки хранения анимационных данных скелетного меша.
Нет проблем, потому что расширение D3DXMESHCONTAINER очень просто! Новая версия, которую я назвал D3DXMESHCONTAINER_EX, добавляет всего четыре новых объекта данных и три функции. Объекты данных включают в себя массив объектов-текстур, объект-скелетный меш (для хранения анимированного скелетного меша) и два массива матриц.
Вот как я определил обьект D3DXMESHCONTAINER_EX и четыре ранее упомянутые переменные.
struct D3DXMESHCONTAINER_EX : D3DXMESHCONTAINER
{
IDirect3DTexture9 **pTextures; ID3DXMesh *pSkinMesh; D3DXMATRIX **ppFrameMatrices; D3DXMATRIX *pBoneMatrices;
Массив указателей pTexture содержит объекты-текстуры, используемые для визуализации меша. Я создаю массив pTexture, сначала загружая меш, а потом получая текстурные буфера (D3DXMESHCONTAINER::pMaterials) для имен файлов используемых текстур.
Что касается pSkinMesh, он требуется, только если вы используете скелетный меш (который я рассмотрю в главах 4-7). Видите ли, когда загружается скелетный меш, сами данные меша хранятся в D3DXMESHCONTAINER::MeshData::pMesh. Единственная проблема в том, что необходим еще один меш контейнер для хранения анимированного скелетного меша. Для этой цели и служит pSkinMesh.
Наконец, вы обнаружите массивы матриц ppFrameMatrices and pBoneMatrices. Не углубляясь, они тоже используются для скелетного меша, и эти массивы рассматриваются в главе 4. В данный момент важно понять, что скелетный меш анимируется прикреплением вершин меша к основной иерархии костей. Таким образом, вместе с костями двигаются и вершины, ppFrameMatrices и pBoneMatrices используются для связки вершин с костями.
Кроме переменных в D3DXMESHCONTAINER_EX есть еще несколько функций. Первые две - конструктор и деструктор:
Подготовкакизучениюкниги
D3DXMESHC0NTAINER_EX()
{
Name = NULL; MeshData.pMesh = NULL; pMaterials = NULL; pEffects = NULL; NumMaterials = 0; pAdjacency = NULL; pSkinInfo = NULL; pNextMeshContainer = NULL; pTextures = NULL; pSkinMesh = NULL; ppFrameMatrices = NULL;
pBoneMatrices = NULL;
}
~D3DXMESHCONTAINER_EX()
{
if(pTextures && NumMaterials) { for(DWORD i=0;i<NumMaterials;i++)
ReleaseCOM(pTextures[i]);
}
delete [] pTextures; pTextures = NULL; NumMaterials = 0;
delete [] Name; Name = NULL;
delete [] pMaterials; pMaterials = NULL; delete pEffects; pEffects = NULL;
delete [] pAdjacency; pAdjacency = NULL;
delete [] ppFrameMatrices; ppFrameMatrices = NULL; delete [] pBoneMatrices; pBoneMatrices = NULL;
ReleaseCOM(MeshData.pMesh);
ReleaseCOM(pSkinInfo);
ReleaseCOM(pSkinMesh);
delete pNextMeshContainer; pNextMeshContainer = NULL;
}
Конструктор и деструктор предназначены для инициализации и освобождения данных используемых объектом, соответственно. Обратите внимание на использование макроса ReleaseCOM, который я опишу в следующем разделе "Проверка вспомогательных функций". Вкратце, ReleaseCOM - макрос, который безопасно освобождает СOМ-интерфейс и устанавливает указатель интерфейса в NULL.
Третья функция в D3DXMESHCONTAINER_EX - Find, которая позволяет просматривать связанные списки мешей для поиска указанного меша, совсем как D3DXFRAME_EX::Find. Используются быстрее сравнение строк для проверки имен и рекурсивные вызовы Find для поиска по всему списку.