netlib.narod.ru< Назад | Оглавление | Далее >

Работа с сетками

На нижнем уровне Direct3D не работает с сетками, а только с полигонами. Библиотека D3DX расширяет функциональность системы Direct3D, предоставляя вам ряд объектов, которые поддерживают хранение и визуализацию сеток.

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

X-файлы

X-файлы (известные своим расширением .X) — это универсальный формат хранения трехмерных моделей, являющийся собственностью Microsoft. (Извините, но мы не зайдем с визитом к агентам Малдеру и Скалли.) Это управляемый шаблонами и полностью расширяемый формат, а значит, вы можете использовать его для всех потребностей хранения данных. В нашем случае под хранением данных я подразумеваю хранение информации ваших трехмерных сеток.

Хотя я мог бы сейчас порассуждать о запутанности формата .X, это перегрузит главу информацией, которой смогут воспользоваться только профессиональные программисты и компьютерные художники. Взгляните праве в лицо — попытка вручную редактировать сетку, состоящую из тысяч вершин, смехотворна. Да и зачем решать эту приводящую в уныние задачу, когда можно воспользоваться такими программами, как trueSpace или MilkShape 3D и разрабатывать сетки в дружелюбной среде? Так я и подумал!

ПРИМЕЧАНИЕ
Если вас действительно заинтересовала запутанность формата X-файлов, вы, определенно, должны взглянуть на мою книгу «Профессиональная анимация с DirectX». В ней я детально рассматриваю использование X-файлов в ваших собственных проектах, от применения стандартных объектов данных DirectX до создания ваших собственных настраиваемых наборов данных! Дополнительную информацию о книге вы найдете в приложении A, «Список литературы».

Вместо этого, позвольте мне предоставить вам краткий обзор форматирования X-файлов; после этого вы сможете перейти к действительно интересным вещам — использованию моделей в ваших играх!

Формат X-файлов

X-файлы, идентифицируемые по расширению файла .X, очень гибкие. Они могут быть текстовыми, что упрощает редактирование файла, или двоичными, что уменьшает размер файла и упрощает его защиту от любопытных глаз. В целом формат .X базируется на шаблонах, которые похожи на структуры C.

Чтобы прочитать и обработать X-файл вы применяете небольшой набор COM-объектов, которые разбирают каждый объект данных, встречающийся в X-файле. Объекты данных обрабатываются как массивы байтов; вы просто выполняете приведение типа массива к годной для использования структуре, чтобы получить легкий доступ к содержащимся в объекте данным.

Эти объекты могут сильно изменяться, в зависимости от того, что хранится в X-файле. В нашем случае объекты представляют сетки и относящиеся к сеткам данные (такие, как структура костей, называемая иерархией фреймов, и данные анимации). Ваша задача — разобрать эти объекты, загрузить данные сеток, построить таблицы анимации и создать иерархии фреймов.

Я еще вернусь к сеткам и вопросам анимации. Сперва я хочу поговорить о такой вещи первостепенной важности, как упомянутая выше иерархия фреймов.

Использование иерархии фреймов

Вы будете использовать фреймы (frame) для группировки одного или нескольких объектов данных (обычно сеток), чтобы упростить управление ими. Вы можете также создать единственную сетку и использовать несколько фреймов, содержащих ссылку на сетку, что позволяет вам использовать одну сетку несколько раз.

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

По сути, вы создали 15 экземпляров сетки биллиардного шара из единственной сетки. Помимо использования фреймов для создания нескольких экземпляров единственной сетки, вы также можете применять их для организации иерархии фреймов. Иерархия фреймов (frame hierarchy) определяет структуру сцены или группировку сеток. Когда перемещается фрейм, перемещаются также и все встроенные в него фреймы.

Для примера представим как набор фреймов скелет человека (рис. 2.24). Вершиной иерархии является грудная клетка. Идя вниз от грудной клетки, вы подсоединяете каждую кость к предыдущей — то есть таз к грудной клетке, бедро к тазу, голень к бедру и стопу к голени. В обратном направлении (вверх) к грудной клетке подсоединяется рука, а к руке — кисть. Это упорядочивание продолжается пока не будут подсоединены все кости так, чтобы на конце цепочки всегда была грудная клетка.


Рис. 2.24. Ваш скелет является замечательным примером иерархи фреймов

Рис. 2.24. Ваш скелет является замечательным примером иерархи фреймов. Каждая кость в иерархии возвращается к грудной клетке


Итак, у вас есть корневой фрейм (root frame) — грудная клетка. У корня нет родительского фрейма, значит он находится на верху иерархии и не принадлежит другому фрейму. Фреймы, которые подсоединены к другим фреймам, называются дочерними фреймами (child frame) или, иногда, узлами (node). Например, плечо является родителем предплечья, а кисть является потомком предплечья. Если следовать иерархии, кисть, предплечье и плечо являются потомками грудной клетки.

Когда перемещается фрейм, перемещаются и все его потомки. Если вы двигаете плечом, предплечье и кисть тоже перемещаются. С другой стороны (здесь нет игры слов), если вы двигаете кистью, положение меняет только она, потому что у нее нет дочерних фреймов (предплечье является родительским фреймом кисти).

У каждого фрейма есть собственная ориентация, которая в терминах X-файла называется преобразованием фрейма (frame transformation). Вы применяете преобразование к самому верхнему объекту в иерархии, для которого оно нужно. Преобразование распространяется от вершины иерархии вниз ко всем дочерним фреймам. Например, если вы поворачиваете плечо, преобразование вращения распространяется вниз к предплечью и кисти (и у каждого фрейма комбинируется с его собственным преобразованием).

Иерархия фреймов является основой для использования сложных сеток и анимационных техник. Фактически, она абсолютно необходима для использования таких вещей, как скелетные сетки (подробнее о скелетных сетках мы поговорим в разделе «Сетки в библиотеке D3DX» данной главы).

Другой причиной для использования иерархии фреймов является изоляция частей сцены. Благодаря ей вы можете модифицировать небольшой фрагмент сцены, перемещая указанные фреймы, в то время как остальная сцена остается неизменной. Например, если у вас есть фрейм, представляющий дом, и другой фрейм, представляющий дверь, то вы можете изменять фрейм двери не изменяя фрейм дома.

Создание X-файлов с сетками

Вы можете создавать собственные X-файлы различными способами. Microsoft написала несколько программ экспорта, которые вы можете использовать с программами моделирования, такими как 3D Studio Max или Maya. Если вы не можете приобрести эти замечательные программы моделирования, можно создавать модели вручную (самостоятельно добавляя данные каждой вершины и полигона в текстовый X-файл) или воспользоваться более дешевыми программами, такими как низкополигональный редактор моделей MilkShape 3D, разработанный Митом Кайрэганом.

Программа MilkShape 3D разрабатывалась как инструментальное средство создания моделей для игры Half-Life, но превратилась в нечто гораздо большее. Сейчас MilkShape 3D поддерживает множество форматов моделей (в основном для игр), но остается полезной программой.

Теперь кое-что проясняется и я покажу вам кота в мешке — в MilkShape 3D есть программа экспорта в X-файлы, написанная вашим любимым автором (правильно, мной!), которую вы можете использовать. Перейдите на официальный сайт MilkShape 3D (http://www.swissquake.ch/chumbalum-soft) чтобы загрузить его.

Если вы не хотите использовать для создания X-файлов программы сторонних производителей, единственным вариантом остается создание моделей вручную. Однако, поскольку самостоятельное создание моделей не является оптимальным решением, в этой книге оно не рассматривается.

Разбор X-файлов

Позднее в этой главе и далее во всей книге вам надо будет вручную выполнять разбор X-файлов, чтобы получить данные сеток. Для разбора X-файлов используется семейство объектов IDirectXFile, которые реализуют задачу открытия X-файла и перечисления содержащихся в файле объектов данных, предоставляя таким образом для вас легкий доступ к ним.

ПРИМЕЧАНИЕ
Чтобы использовать в вашем проекте компоненты IDirectXFile, вам необходимо включить в проект заголовочные файлы dxfile.h, rmxfguid.h и rmxftmpl.h, а также указать при компоновке библиотеки dxguid.lib и d3dxof.lib.

Разбор X-файлов не так труден, как может показаться с первого взгляда. Трок заключается в просмотре всей иерархии объектов данных и поиске тех объектов, которые вы хотите использовать — обычно это объекты сеток и фреймов. Главное помнить, что объекты могут быть вложенными и вместо объекта вы можете обнаружить ссылку на него (для которой необходимо выполнить разрешение, чтобы получить доступ к данным объекта). Организация объектов данных показана на рис. 2.25.


Рис. 2.25. X-файлы позволяют вам встраивать одни объекты в другие, создавая иерархию

Рис. 2.25. X-файлы позволяют вам встраивать одни объекты в другие, создавая иерархию


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

BOOL ParseXFile(char *Filename)
{
    IDirectXFile           *pDXFile = NULL;
    IDirectXFileEnumObject *pDXEnum = NULL;
    IDirectXFileData       *pDXData = NULL;

    // Создание объекта X-файла
    if(FAILED(DirectXFileCreate(&pDXFile)))
        return FALSE;

    // Регистрация используемых шаблонов
    // Используем стандартные шаблоны 
    // абстрактного режима из Direct3D
    if(FAILED(pDXFile->RegisterTemplates((LPVOID)
                D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES))) {
        pDXFile->Release();
        return FALSE;
    }

    // Создаем объект перечисления
    if(FAILED(pDXFile->CreateEnumObject((LPVOID)Filename,
                          DXFILELOAD_FROMFILE, &pDXEnum))) {
        pDXFile->Release();
        return FALSE;
    }

    // Перечисляем все объекты верхнего уровня
    while(SUCCEEDED(pDXEnum->GetNextDataObject(&pDXData))) {
        ParseXFileData(pDXData);
        ReleaseCOM(pDXData);
    }

    // Освобождаем объекты
    ReleaseCOM(pDXEnum);
    ReleaseCOM(pDXFile);

    // Возвращаем флаг успеха
    return TRUE;
}

void ParseXFileData(IDirectXFileData *pData)
{
    IDirectXFileObject        *pSubObj  = NULL;
    IDirectXFileData          *pSubData = NULL;
    IDirectXFileDataReference *pDataRef = NULL;
    const GUID *pType = NULL;
    char       *pName = NULL;
    DWORD      dwSize;
    char       *pBuffer;

    // Получаем тип объекта
    if(FAILED(pData->GetType(&pType)))
        return;

    // Получаем имя объекта (если есть)
    if(FAILED(pData->GetName(NULL, &dwSize)))
        return;

    if(dwSize) {
        if((pName = new char[dwSize]) != NULL)
            pData->GetName(pName, &dwSize);
    }

    // Если имени нет, задаем имя по умолчанию
    if(pName == NULL) {
        if((pName = new char[9]) == NULL)
            return;
        strcpy(pName, "Template");
    }

    // Смотрим чем является объект
    // Здесь вы перейдете к вашему собственному коду
    // Сканируем встроенные объекты
    while(SUCCEEDED(pData->GetNextObject(&pSubObj))) {
        // Обрабатываем встроенные ссылки
        if(SUCCEEDED(pSubObj->QueryInterface(
                           IID_IDirectXFileDataReference,
                           (void**)&pDataRef))) {
            if(SUCCEEDED(pDataRef->Resolve(&pSubData))) {
                ParseXFileData(pSubData);
                ReleaseCOM(pSubData);
            }
            ReleaseCOM(pDataRef);
        }
        // Обрабатываем встроенные объекты,
        // не являющиеся ссылками
        if(SUCCEEDED(pSubObj->QueryInterface(
                IID_IDirectXFileData, (void**)&pSubData))) {
            ParseXFileData(pSubData);
            ReleaseCOM(pSubData);
        }
        ReleaseCOM(pSubObj);
    }
    // Освобождаем буфер имени
    delete[] pName;
}

Функции ParseXFile и ParseXFileData работают сообща, чтобы выполнить разбор каждого объекта, содержащегося в X-файле. Функция ParseXFile открывает X-файл и выполняет перечисление, определяя верхние объекты иерархии. Каждый обнаруженный объект передается функции ParseXFileData.

Функция ParseXFileData обрабатывает данные объекта. Она начинается с получения типа объекта и имени экземпляра объекта (если оно есть). Теперь вы можете обработать данные объекта и позволить функции перечислить все дочерние объекты, используя рекурсию. Этот процесс продолжается, пока не будут обработаны все объекты данных.

Вы просто вызываете функцию ParseXFile, передавая ей имя файла, который хотите обработать, а эти две функции позаботятся обо всем остальном. Вы узнаете как использовать эти функции в разделе «Скелетные сетки», далее в этой главе.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz