netlib.narod.ru | < Назад | Оглавление | Далее > |
Во всех демонстрационных программах, которые мы рассмотрели в предыдущих главах, мы не задавали параметры поля зрения или угол обзора камеры. Это означает, что мы использовали устанавливаемое по умолчанию значение поля зрения, равное 0.5.
Для изменения поля зрения порта просмотра используется функция SetField() интерфейса Direct3DRMViewport. Меньшие значения уменьшают поле зрения порта просмотра и оказывают эффект, сравнимый с использованием телеобъектива. Большие значения увеличивают поле зрения порта просмотра, подобно широкоугольному объективу.
В фотографии линза, используемая для изменения поля зрения, называется трансфокатором. Трансфокаторы позволяют получить крупный план удаленных объектов и общий план для просмотра больших пейзажей.
В приложении Zoom мы воспользуемся функцией SetField() для настройки поля зрения порта просмотра. Приложение размещает сетку в начале координат, а потом демонстрирует результаты трансфокации, используя размещенную в фиксированной позиции камеру. Внешний вид окна приложения Zoom показан на рис. 9.1 (учтите, что на рисунке вы не увидите результаты изменения угла зрения).
Рис. 9.1. Приложение Zoom
Если вы запустите приложение Zoom, вам покажется, что сетка перемещается. Но, хотя сетка и вращается, ее местоположение остается неизменным. Иллюзия перемещения вызывается изменениями параметров поля зрения порта просмотра.
Приложение Zoom поддерживает обычное меню Render, позволяющее во время работы программы изменять используемый метод визуализации. Кроме того, в приложении есть меню Animation. Оно позволяет изменять используемый способ анимации (линейную или сплайновую).
Приложение Zoom демонстрирует следующие технологии:
Функциональные возможности приложения Zoom сосредоточены в классе ZoomWin:
class ZoomWin : public RMWin { public: ZoomWin(); BOOL CreateScene(); protected: //{{AFX_MSG(ZoomWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); afx_msg void OnAnimationLinear(); afx_msg void OnAnimationSpline(); afx_msg void OnUpdateAnimationLinear(CCmdUI* pCmdUI); afx_msg void OnUpdateAnimationSpline(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void AdjustField(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); private: LPDIRECT3DRMMESHBUILDER meshbuilder; static LPDIRECT3DRMFRAME zoomframe; static LPDIRECT3DRMANIMATION animation; };
В классе объявлены две открытые функции: конструктор и функция CreateScene(). Конструктор используется для инициализации динамических членов данных класса. (Статические члены данных класса получают нулевые значения автоматически, поэтому нет никакой необходимости заниматься их инициализацией.) Поскольку в классе есть только один динамический член данных, код выглядит следующим образом:
ZoomWin::ZoomWin() { meshbuilder = 0; }
Функция CreateScene() создает сцену приложения. Мы рассмотрим ее код чуть позже.
Далее следует объявление десяти защищенных функций. Первые шесть из них являются обработчиками сообщений, необходимыми для реализации меню Render. Оставшиеся четыре функции необходимы для поддержки меню Animation. Функции, относящиеся к меню Animation мы рассмотрим чуть позже. Функции, реализующие меню Render аналогичны подобным функциям из рассмотренных ранее приложений.
Потом расположено объявление функции обратного вызова AdjustField(). Эта функция используется для изменения параметров поля зрения порта просмотра во время работы программы.
В самом конце объявлены три закрытых члена данных:
LPDIRECT3DRMMESHBUILDER meshbuilder; static LPDIRECT3DRMFRAME zoomframe; static LPDIRECT3DRMANIMATION animation;
Указатель meshbuilder используется для доступа к сетке демонстрационной программы. Он применяется в функции CreateScene() и в шести защищенных обработчиках сообщений меню Render. Переменная zoomframe является указателем на пустой фрейм, а переменная animation — это указатель на интерфейс Direct3DRMAnimation. Мы будем использовать интерфейс анимации и пустой фрейм при «анимации» поля зрения порта просмотра. О том, как это работает, мы поговорим во время обсуждения функции AdjustField().
Сцена в приложении Zoom создается функцией CreateScene(), код которой приведен в листинге 9.1.
Листинг 9.1. Функция ZoomWin::CreateScene() |
BOOL ZoomWin::CreateScene() { // ------- СЕТКА -------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_Z_MESH); resinfo.lpType = "MESH"; d3drm->CreateMeshBuilder(&meshbuilder); meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); meshbuilder->SetColorRGB(D3DVALUE(.67), D3DVALUE(.82), D3DVALUE(.94)); ScaleMesh(meshbuilder, D3DVALUE(25)); // ------- МАТЕРИАЛ ---------- LPDIRECT3DRMMATERIAL material; d3drm->CreateMaterial(D3DVALUE(10), &material); meshbuilder->SetMaterial(material); material->Release(); material = 0; // ------ ФРЕЙМ СЕТКИ ------ LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->SetRotation(scene, D3DVALUE(1), D3DVALUE(.4), D3DVALUE(0), D3DVALUE(.1)); meshframe->AddVisual(meshbuilder); meshframe->Release(); meshframe = 0; // ------- АНИМАЦИЯ -------- d3drm->CreateAnimation(&animation); animation->SetOptions(D3DRMANIMATION_SPLINEPOSITION | D3DRMANIMATION_CLOSED | D3DRMANIMATION_POSITION); animation->AddPositionKey(D3DVALUE(0), D3DVALUE(.4), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(24), D3DVALUE(5), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(49), D3DVALUE(1), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(74), D3DVALUE(5), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(99), D3DVALUE(.4), D3DVALUE(0), D3DVALUE(0)); d3drm->CreateFrame(scene, &zoomframe); animation->SetFrame(zoomframe); // --------- СВЕТ -------- LPDIRECT3DRMLIGHT dlight; d3drm->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &dlight); LPDIRECT3DRMLIGHT alight; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(.40), D3DVALUE(.40), D3DVALUE(.40), &alight); LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); lightframe->AddLight(dlight); lightframe->AddLight(alight); dlight->Release(); dlight = 0; alight->Release(); alight = 0; lightframe->Release(); lightframe = 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); camera->AddMoveCallback(AdjustField, NULL); return TRUE; } |
Функция CreateScene() выполняет следующие действия:
На первом этапе мы воспользуемся интерфейсом Direct3DRMMeshBuilder чтобы загрузить сетку из файла. Давайте еще раз взглянем на код:
D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_Z_MESH); resinfo.lpType = "MESH"; meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); meshbuilder->SetColorRGB(D3DVALUE(.67), D3DVALUE(.82), D3DVALUE(.94)); ScaleMesh(meshbuilder, D3DVALUE(25));
Структура D3DRMLOADRESOURCE используется для идентификации элемента ресурсов приложения, содержащего сетку. Член данных meshbuilder инициализируется функцией CreateMeshBuilder() интерфейса Direct3DRM. Затем вызывается функция Load(), которая и загружает сетку. Цвет сетки назначается с помощью функции SetColorRGB(). После выполнения всех этих действий сетка масштабируется до размера в 25 единиц с помощью функции ScaleMesh().
Теперь к сетке применяется материал:
LPDIRECT3DRMMATERIAL material; d3drm->CreateMaterial(D3DVALUE(10), &material); meshbuilder->SetMaterial(material); material->Release(); material = 0;
Материал позволяет осуществлять точную настройку внешнегго вида сетки путем изменения интенсивности и цвета отраженного света. В данном случае мы используем материал, придающий сетке вид отполированной поверхности.
СОВЕТ |
Тестирование параметров материала. Xpose — это законченная программа просмотра объектов, созданная с использованием рассмотренных в этой книге технологий. И исходный код и исполняемый файл этой программы содержатся на прилагаемом к книге CD-ROM. Самый быстрый способ познакомиться с параметрами материала и тем, какое влияние они оказывают на внешний вид сетки — провести несколько экспериментов с программой Xpose. Диалоговое окно Material Settings программы Xpose позволяет изменить параметры материала и сразу же увидеть результат. |
Для создания материала используется функция CreateMaterial() интерфейса Direct3DRM. Новый материал применяется к сетке с помощью функции SetMaterial() интерфейса Direct3DRMMeshBuilder. После выполнения этих действий локальный указатель material освобождается.
На третьем этапе создается фрейм для сетки:
LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->SetRotation(scene, D3DVALUE(1), D3DVALUE(.4), D3DVALUE(0), D3DVALUE(.1)); meshframe->AddVisual(meshbuilder); meshframe->Release(); meshframe = 0;
Фрейм создается функцией CreateFrame() интерфейса Direct3DRM. С помощью функции SetRotation() новому фрейму назначаются атрибуты вращения. Назначение атрибутов является единственной технологией, используемой для анимации фрейма сетки. Во время работы программы фрейм остается в начале координат (позиции по умолчанию). После того, как конструктор сеток будет присоединен к новому фрейму с помощью функции AddVisual(), указатель meshframe освобождается.
Затем конструируется анимационная последовательность, которая будет использоваться для управления полем зрения порта просмотра. Выполняющий эти действия код выглядит так:
d3drm->CreateAnimation(&animation); animation->SetOptions(D3DRMANIMATION_SPLINEPOSITION | D3DRMANIMATION_CLOSED | D3DRMANIMATION_POSITION); animation->AddPositionKey(D3DVALUE(0), D3DVALUE(.4), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(24), D3DVALUE(5), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(49), D3DVALUE(1), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(74), D3DVALUE(5), D3DVALUE(0), D3DVALUE(0)); animation->AddPositionKey(D3DVALUE(99), D3DVALUE(.4), D3DVALUE(0), D3DVALUE(0)); d3drm->CreateFrame(scene, &zoomframe); animation->SetFrame(zoomframe);
Сначала с помощью функции CreateAnimation() интерфейса Direct3DRM создается анимация. Затем вызывается функция SetOptions() интерфейса Direct3DRMAnimation чтобы задать параметры анимации. Мы используем константу D3DRMANIMATION_SPLINEPOSITION чтобы указать, что желаем получить сплайновую анимацию (а не линейную). Этот параметр может быть изменен с помощью меню Animation приложения Zoom. Константа D3DRMANIMATION_CLOSED указывает, что анимационная последовательность должна непрерывно повторяться. Это позволяет использовать непрерывно увеличивающиеся значения времени для повторного выполнения анимационной последовательности. Константа D3DRMANIMATION_POSITION указывает, что анимационная последовательность будет изменять только местоположение объекта (мы не используем константу D3DRMANIMATION_SCALEANDROTATION поскольку, как вы вскоре увидите, мы не используем анимацию для изменения вращения или масштаба объекта).
Затем с помощью функции AddPositionKey() к анимации добавляются пять позиционных ключей. Первым аргументом функции AddPositionKey() является значение, указывающее в какой момент времени данный ключ должен вступить в действие. Оставшиеся три аргумента задают связанную с новым ключом позицию. Обратите внимание, что устанавливаемые ключи отличаются только значением координаты по оси X. Эта ось выбрана произвольно. Мы могли бы использовать оси Y или Z, поскольку мы создаем линейную, или одномерную, анимационную последовательность. Мы будем использовать в анимационной последовательности только ось X.
Затем создается фрейм с именем zoomframe. Новый фрейм присоединяется к анимационной последовательности с помощью функции SetFrame() интерфейса Direct3DRMAnimation.
Фрейм zoomframe является пустым, но он используется не так, как использовались пустые фреймы в приложениях Firefly (глава 6) или Decal (глава 5). Этот пустой фрейм не будет использоваться как родитель для других фреймов. Вместо этого он будет анимироваться созданной ранее анимационной последовательностью, а его координаты будут применяться для установки параметров поля зрения порта просмотра. Обратите внимание, что значение координаты по оси X в анимационной последовательности изменяется в диапазоне от 0.4 до 5.0. Эти значения не будут интерпретироваться как позиции. Вместо этого, они будут использоваться в качестве параметров поля зрения и передаваться в аргументах функции SetField() интерфейса Direct3DRMViewport.
Возвратимся к шести этапам работы функции CreateScene(). На пятом этапе создаются два источника света. Мы пропустим обсуждение этого этапа. Источники света были предметом обсуждения в главе 6.
На шестом, завершающем этапе, выполняется создание порта просмотра:
d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); camera->AddMoveCallback(AdjustField, NULL); return TRUE;
Сначала создается и размещается фрейм camera. Затем этот фрейм передается в аргументе функции CreateViewport() интерфейса Direct3DRM. Последнее, что делает функция CreateScene() — установка функции обратного вызова AdjustField() с помощью функции AddMoveCallback(). Для установки функции обратного вызова мы используем фрейм camera, но с тем же успехом может быть использован любой другой фрейм сцены.
Функция AdjustField() является функцией обратного вызова, которая управляет полем зрения порта просмотра. Код этой функции выглядит так:
void ZoomWin::AdjustField(LPDIRECT3DRMFRAME, void*, D3DVALUE) { static D3DVALUE time; time += D3DVALUE(.5); animation->SetTime(time); D3DVECTOR pos; zoomframe->GetPosition(0, &pos); viewport->SetField(pos.x); }
Для управления анимационной последовательностью в функции применяется статическая переменная time. Значение этой переменной увеличивается при каждом вызове функции AdjustField(). После того, как значение переменной time увеличено, осуществляется установка нового времени в анимации с помощью функции SetTime() интерфейса Direct3DRMAnimation.
Затем мы получаем координаты анимируемого фрейма (zoomframe). Вспомните, что позицией фрейма zoomframe управляет анимационная последовательность. Когда с помощью функции SetTime() мы устанавливаем новое время анимации, позиция фрейма zoomframe изменяется в соответствии с заданной анимационной последовательностью. Для получения новых координат фрейма используется функция GetPosition().
В заключение значение координаты фрейма по оси X используется в качестве нового значения угла зрения для порта просмотра. Новое значение устанавливается с помощью функции SetField() интерфейса Direct3DRMViewport. Вспомните, что viewport это статический член данных класса RMWin. Поэтому мы можем обращаться к нему из функций обратного вызова.
Приложение Zoom позволяет во время работы выбирать тип анимационной последовательности (линейная или сплайновая) с помощью меню Animation. Для этого вызывается функция SetOptions() интерфейса Direct3DRMAnimation с различными наборами флагов. Две функции, которые отвечают за работу команд меню Animation, выглядят следующим образом:
void ZoomWin::OnAnimationLinear() { animation->SetOptions(D3DRMANIMATION_LINEARPOSITION | D3DRMANIMATION_CLOSED | D3DRMANIMATION_POSITION); } void ZoomWin::OnAnimationSpline() { animation->SetOptions(D3DRMANIMATION_SPLINEPOSITION | D3DRMANIMATION_CLOSED | D3DRMANIMATION_POSITION); }
Поддержка меню Animation включает также отображение флажка слева от пункта меню, соответствующего выбранному в данный момент режиму анимации. Две функции, отвечающие за вывод флажков рядом с пунктами меню выглядят следующим образом:
void ZoomWin::OnUpdateAnimationLinear(CCmdUI* pCmdUI) { D3DRMANIMATIONOPTIONS options = animation->GetOptions(); pCmdUI->SetCheck(options & D3DRMANIMATION_LINEARPOSITION); } void ZoomWin::OnUpdateAnimationSpline(CCmdUI* pCmdUI) { D3DRMANIMATIONOPTIONS options = animation->GetOptions(); pCmdUI->SetCheck(options & D3DRMANIMATION_SPLINEPOSITION); }
Для получения текущих параметров анимации эти функции вызывают функцию GetOptions() интерфейса Direct3DRMAnimation. Функция SetCheck() используется для установки флажка рядом с пунктом меню, соответствующим активному в данный момент режиму.
netlib.narod.ru | < Назад | Оглавление | Далее > |