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

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 для поиска по всему списку.

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