Не следует бить мертвую лошадь (как я могу сделать такую ужасную вещь?), но я хочу слегка коснуться загрузки иерархий фреймов из .X файлов. Хотя в главе 3 детально описаны использование .X файлов и загрузка иерархий фреймов, я хочу еще раз вернуться к этому, используя специальные структуры для хранения иерархии.

Для хранения иерархии фреймов вам необходимо использовать структуру D3DXFRAMБ (или показанную в главе 1 D3DXFRAMБ_БX). Как я уже замечал ранее, структура D3DXFRAMБ (или унаследованная D3DXFRAMБ_БX) содержит два указателя, предназначенньгх для создания иерархии - pFrameSibling и pFrameFirstChild. Ваша задача связать каждый загружаемый из .X файла фрейм, используя эти два указателя.

Переберите все объекты данных из заданного .X файла, начав с корневого объекта фрейма. Когда вы обнаружите объект Frame, привяжите его как родственный или дочерний к предыдущему кадру. Продолжайте выполнять эти операции, пока не загрузите все фреймы. В этом примере используйте структуру D3DXFRAMEEX для хранения фреймов в иерархии.

Т. к. в главе 3 содержалось намного больше информации об анализе .X файлов, я просто слегка напомню вам. Обычно вы открываете .X файл и перебираете каждый объект в нем. Для каждого найденного объекта Frame создаете соответствующий объект D3DXFRAME (или D3DXFRAMEEX) и привязываете его в иерархию объектов.

Для обработки .X файлов, вы можете создать класс, который бы выполнял всю работу за вас. Вы можете просто создать функцию ParseObject, принадлежащую классу, которая бы давала доступ к данным всех объектов. Опять же в главе 3 приведено подробное объяснение использования этого класса.

А теперь давайте посмотрим на функцию ParseObject, которая вызывается для всех перечисляемых объектов.

BOOL cXFrameParser::ParseObject(

IDirectXFileData *pDataObj, IDirectXFileData *pParentDataObj, DWORD Depth,

void **Data, BOOL Reference)

{

const GUID *Type = GetObjectGUID(pDataObj);

// Если объект типа Frame (не ссылочный) // тогда добавить его в иерархию

if(*Type == TID_D3DRMFrame && Reference == FALSE) { // создать контейнер фрейма

D3DXFRAME_EX *pFrame = new D3DXFRAME_EX();

// Если есть, получить имя фрейма pFrame->Name = GetObjectName(pDataObj);

// Привязать объект в иерархию

if(Data == NULL) {

// Привязать как родственника корневого pFrame->pFrameSibling = m_RootFrame; m_RootFrame = pFrame; pFrame = NULL; Data = (void**)&m_RootFrame; } else {

// Привязать как дочерний текущего D3DXFRAME_EX *pFramePtr = (D3DXFRAME_EX*)*Data; pFrame->pFrameSibling = pFramePtr->pFrameFirstChild;

pFramePtr->pFrameFirstChild = pFrame; pFrame = NULL; Data = (void**)&pFramePtr->pFrameFirstChild;

}

}

// Загрузить матрицу преобразования фрейма

if(*Type==TID_D3DRMFrameTransforiTMatrix && Reference==FALSE) { D3DXFRAME_EX *Frame = (D3DXFRAME_EX*)*Data; if(Frame) {

Frame->TransformationMatrix = *(D3DXMATRIX*) \

GetObjectData(pDataObj, NULL); Frame->matOriginal = Frame->TransformationMatrix;

}

}

/ / Анализировать дочерние объекты

return ParseChildObjects(pDataObj, Depth, Data, Reference);

}

Если вы еще не читали главу 3 (стыдно, если нет!), некоторые части приведенного выше кода могут немного смущать вас. Обычно функция ParseObject вызывается для каждого перечисляемого объекта. Внутри функции ParseObject вы проверяете тип текущего перечисляемого объекта (используя его шаблонный GUID). Если тип является Frame, вы создаете структуру фрейма и загружаете его имя в нее.

Далее вы привязываете фрейм в иерархию фреймов, что и выглядит немного странным. Класс cXFrameParser содержит два указателя - один для созданного корневого фрейм-объекта (m_RootFrame, член класса) и один для объекта данных (Data), который передается в качестве параметра функции ParseObject при каждом ее вызове. Указатель данных содержит данные последнего загруженного фрейм-объекта.

Как только вы начинаете анализировать .X файл, указатель Data установлен в NULL, означая, что не было загружено никаких фрейм-объектов. Когда вы загружаете фрейм-объект, вы проверяете указатель данных, чтобы определить указывает ли он на фрейм. Если нет, то предполагается, что текущий фрейм является родственником корневого фрейма. Если же указатель данных указывает на фрейм, то полагается что текущий перечисляемый фрейм является потомком указываемого указателем данных фрейма.

Знание того, является ли текущий фрейм родственным или дочерним, считается определяющим при создании иерархии. Родственные фреймы связываются, используя указатель pFrameSibling структуры D3DXFRAME, в то время как дочерние фреймы связываются, используя pFrameFirstChild. Сразу же после загрузки фрейма указатель данных устанавливается в новый фрейм или в предыдущий родственный фрейм. В конце концов, все фреймы связываются между собой как родственники или потомки.

Вы заметите также, что функция ParseObject содержит код для загрузки матрицы преобразования (трансформации) фрейма (представленной шаблоном FrameTransformMatrix). Обычно объект FrameTransformMatrix встраивается в объект Frame. Объект FrameTransformMatrix определяет начальное положение загруженного фрейма.

Примененная к скелетной анимации, эта матрица преобразования фрейма определяет начальную позу вашей скелетной структуры. Например, стандартная скелетная структура может находится в позе, в которой тело расположено вертикально, а руки расставлены. Однако предположим, что все ваши анимации рассчитаны на персонаж, находящийся в другой позе, возможно с руками по швам и чуть согнутыми ногами. Вместо того чтобы менять положение всех вершин или костей, чтобы они соответствовали позе, перед сохранением .X файла в вашей программе трехмерного моделирования вы можете изменить преобразования фреймов. Таким образом, все движения костей будут происходить относительно этой позы. Это становится более понятным после того, как вы попробуете управлять положением костей при анимации, так что я пока пропущу эту тему. Просто запомните, что внутри каждой загружаемой структуры фрейма есть место для хранения начальной матрицы преобразования (в объекте D3DXFRAME::TransformationMatrix).

После выполнения всего сказанного иерархия фреймов будет загружена. Корневой фрейм хранится в mRootFrame связанного списка объектов D3DXFRAMEEX в классе загрузки фреймов. Вашей задачей является использование этого указателя в ваших программах. После этого вы можете начать разбираться с положением костей.

Использование скелетной структуры и скелетного меша || Оглавление || Изменение положения костей