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

Конструирование сцен

К этому моменту мы инициализировали стандартные интерфейсы Direct3D. Все обсужденные ранее этапы выполнялись классами RMApp и RMWin. Теперь настало время создать сцену.

Функция CreateScene()

Рассматривая функцию CreateDevice(), мы узнали, что в конце она вызывает функцию CreateScene(). Функция CreateScene() класса RMWin объявлена как чисто виртуальная. Это значит, что классы, производные от RMWin должны предоставлять свою версию функции CreateScene(). Функция CreateScene() отвечает за создание любых сеток, источников света и иерархий фреймов, отображаемых приложением.

Перед тем, как рассмотреть функцию CreateScene(), необходимо упомянуть, что класс SampleWin наследует от класса RMWin несколько важных переменных. Функция CreateScene() может обращаться к этим переменным, поскольку они объявлены как защищенные члены класса. Перечислим эти переменные:

Листинг 4.3 содержит код функции CreateScene() нашего приложения.

Листинг 4.3. Функция SampleWin::CreateScene()

BOOL SampleWin::CreateScene()
{
    HRESULT r;
    // ------СЕТКА--------
    d3drm->CreateMeshBuilder(&meshbuilder);
    r = meshbuilder->Load(meshname, NULL, D3DRMLOAD_FROMFILE,
            NULL, NULL);
    if (r != D3DRM_OK)
    {
        CString msg;
        msg.Format("Failed to load file '%s'\n", meshname);
        AfxMessageBox(msg);
        return FALSE;
    }
    ScaleMesh(meshbuilder, D3DVALUE(25));

    LPDIRECT3DRMFRAME meshframe;
    d3drm->CreateFrame(scene, &meshframe);
    meshframe->AddVisual(meshbuilder);
    meshframe->SetRotation(scene,
            D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
            D3DVALUE(.1));
    meshframe->Release();
    meshframe = 0;

    // --------ЗОНАЛЬНЫЙ СВЕТ--------
    LPDIRECT3DRMLIGHT slight;
    d3drm->CreateLightRGB(D3DRMLIGHT_SPOT,
            D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00),
            &slight);

    LPDIRECT3DRMFRAME slightframe;
    d3drm->CreateFrame(scene, &slightframe);
    slightframe->AddLight(slight);
    slightframe->SetPosition (scene,
            D3DVALUE(0),D3DVALUE(20),D3DVALUE(-20));
    slightframe->SetOrientation(scene,
            D3DVALUE(0), D3DVALUE(-20), D3DVALUE(20),
            D3DVALUE(0), D3DVALUE(1), D3DVALUE(0));
    slightframe->AddMoveCallback(MoveLight, NULL);
    slight->Release();
    slight = 0;
    slightframe->Release();
    slightframe = 0;

    //------ КАМЕРА ----------
    d3drm->CreateFrame(scene, &camera);
    camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50));
    d3drm->CreateViewport(device, camera, 0, 0,
            device->GetWidth(), device->GetHeight(),
            &viewport);

    return TRUE;
}

Показанная на листинге 4.3 функция CreateScene() выполняет следующие действия:

  1. Создание сетки и настройка ее параметров.
  2. Создание источника зонального освещения и настройка его параметров.
  3. Создание и настройка порта просмотра.

Создание сетки

Первый этап требует создания указателя на интерфейс Direct3DRMMeshBuilder. Функция Load() интерфейса Direct3DRMMeshBuilder применяется для загрузки сетки из файла. Если вызов функции Load() завершается неудачно (файл отсутствует или имеет неправильный формат), выводится окно сообщения и функция возвращает FALSE, сигнализируя о необходимости прекратить выполнение приложения. Давайте взглянем на эту часть кода:

d3drm->CreateMeshBuilder(&meshbuilder);
r = meshbuilder->Load(meshname, NULL, D3DRMLOAD_FROMFILE,
                 NULL, NULL );
if (r != D3DRM_OK)
{
    CString msg;
    msg.Format("Failed to load file '%s'\n", meshname);
    AfxMessageBox(msg);
    return FALSE;
}
ScaleMesh(meshbuilder, D3DVALUE(25));

Если сетка успешно загружена, она масштабируется функцией ScaleMesh(). ScaleMesh() — это удобная функция, предоставляемая классом RMWin. Мы используем здесь эту функцию для масштабирования сетки, чтобы гарантировать корректное отображение сетки, независимо от ее оригинальных размеров. Функция ScaleMesh() будет описана позже в этой главе.

Затем конструктор сеток добавляется к фрейму. Эта часть кода выглядит следующим образом:

LPDIRECT3DRMFRAME meshframe;
d3drm->CreateFrame(scene, &meshframe);
meshframe->AddVisual(meshbuilder);
meshframe->SetRotation(scene,
        D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
        D3DVALUE(0.1));
meshframe->Release();
meshframe = 0;

Функция CreateFrame() интерфейса Direct3DRM применяется для инициализации указателя meshframe. Обратите внимание, что в качестве первого аргумента функции CreateFrame() передается указатель на фрейм scene. Это означает, что новый фрейм (meshframe) будет дочерним для фрейма scene.

Затем вызывается функция AddVisual() для присоединения конструктора сеток meshbuilder к новому фрейму. К одному фрейму можно добавить несколько сеток, но обычно к одному фрейму присоединяется только одна сетка.

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

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

Создание источника света

Второе действие, выполняемое функцией CreateScene(), — создание источника зонального света:

LPDIRECT3DRMLIGHT slight;
d3drm->CreateLightRGB(D3DRMLIGHT_SPOT,
        D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00),
       &slight);

Функция CreateLightRGB() интерфейса Direct3DRM применяется для создания источника света slight (s — сокращение от spotlight, т.е. прожектор). Константа D3DRMLIGHT_SPOT задает тип создаваемого источника света. Другие возможные значения — D3DRMLIGHT_AMBIENT, D3DRMLIGHT_DIRECTIONAL, D3DRMLIGHT_PARALLELPOINT и D3DRMLIGHT_POINT.

Затем создается фрейм с именем slightframe:

LPDIRECT3DRMFRAME slightframe;
d3drm->CreateFrame(scene, &slightframe);

Этот фрейм, аналогично фрейму конструктора сеток, использует в качестве родителя фрейм scene. Затем, посредством функции SetPosition() задается местоположение нового фрейма:

slightframe->SetPosition (scene,
        D3DVALUE(0),D3DVALUE(20),D3DVALUE(-20));

Первый аргумент задает систему координат, а остальные указывают новое местоположение фрейма. Функция SetPosition() для определения нового местоположения фрейма использует систему координат указанного фрейма. В данном примере в качестве системы координат используется корневой фрейм сцены, поэтому задающие местоположение значения указывают абсолютную позицию. Корневой фрейм расположен в начале координат, поэтому источник света будет помещен на 20 единиц выше начала координат и на 20 единиц позади. Если бы, например, мы указали те же самые значения, но в качестве системы координат задали фрейм, расположенный в точке <0, 100, 0>, позиция нового фрейма была бы <0, 120, –20>.

Далее, с помощью функции SetOrientation() задается ориентация фрейма slightframe:

slightframe->SetOrientation(scene,
            D3DVALUE(0), D3DVALUE(-20), D3DVALUE(20),
            D3DVALUE(0), D3DVALUE(1), D3DVALUE(0));

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

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

В нашем коде мы используем передний вектор <0, –20, 20>. Это значит, что лицевая грань фрейма направлена к точке <0, –20, 20>, таким образом, лицевая грань фрейма будет на 20 единиц ниже и на 20 единиц ближе (относительно начала координат). В качестве верхнего вектора мы оставим предлагаемый по умолчанию вектор <0, 1, 0>. Мы подробнее изучим фреймы и их ориентацию в главе 7.

Дальнейшая часть кода функции CreateScene() осуществляет создание и настройку параметров источника зонального света.

slightframe->AddLight(slight);
slightframe->AddMoveCallback(MoveLight, NULL);
slight->Release();
slight = 0;
slightframe->Release();
slightframe = 0;

Функция AddLight() применяется для присоединения источника света к только что настроеному фрейму.

Затем вызывается функция AddMoveCallback(). Эта функция применяется для установки функции обратного вызова, которая будет использоваться для изменения позиции и ориентации фрейма во время выполнения программы. Мы поговорим об обратных вызовах и о функции AddMoveCallback() позже в этой главе.

В конце происходит освобождение указателей slight и slightframe чтобы уведомить объекты, что эти указатели больше не будут использоваться (их область видимости ограничена функцией CreateScene()).

Создание порта просмотра

Третий и последний этап, реализуемый функцией CreateScene() — создание порта просмотра. Выполняющий эти действия код выглядит следующим образом:

d3drm->CreateFrame(scene, &camera);
camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50));
d3drm->CreateViewport(device, camera, 0, 0,
        device->GetWidth(), device->GetHeight(),
        &viewport);

Перед созданием порта просмотра необходимо создать фрейм, который будет использоваться для задания местоположения и ориентации порта просмотра. Вызов функции CreateFrame() интерфейса Direct3DRM инициализирует указатель camera. Вспомните, что указатель camera предоставляется классом RMWin поэтому в функции CreateScene() он не объявляется.

Местоположение фрейма camera задается посредством функции SetPosition(). Местоположение и ориентация фрейма камеры определяют точку с которой будет просматриваться сцена. Вспомните, что при создании сетки и ее привязке к фрейму мы не изменяли местоположение фрейма. Это значит, что сетка отображается в начале координат <0, 0, 0>. Если мы хотим увидеть эту сетку в созданной области просмотра необходимо сместить фрейм камеры от начала координат. Мы используем функцию SetPosition() чтобы разместить фрейм камеры на 50 единиц дальше начала координат.

Область просмотра создается функцией CreateViewport(). Первый аргумент — device был создан функцией CreateDevice(). Второй аргумент — это только что созданный фрейм camera. Следующие четыре аргумента определяют местоположение и размеры области просмотра. Местоположение области просмотра — 0, 0, (верхний левый угол устройства) а размеры совпадают с размерами устройства (вспомните, что при создании устройства мы использовали размеры клиентской области окна). Последний аргумент представляет собой адрес указателя на новую область просмотра, который будет инициализирован по завершении функции.

В конце функция CreateScene() возвращает TRUE что указывает классу RMWin на успешное создание сцены.


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

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