Функция CreateDevice() отвечает за создание стандартных интерфейсов Direct3D, наиболее важным из которых является интерфейс Direct3DRMDevice. Функция CreateDevice() является закрытой, поэтому классы, производные от RMWin ничего не знают о выполняемых ею действиях. Полный текст функции приведен в листинге 4.1.
Листинг 4.1. Функция RMWin::CreateDevice() |
BOOL RMWin::CreateDevice() { HRESULT r; r = DirectDrawCreateClipper(0, &clipper, NULL); if (r != D3DRM_OK) { TRACE("failed to create D3D clipper\n"); return FALSE; } r = clipper->SetHWnd(NULL, m_hWnd); if (r != DD_OK) { TRACE("failed in SetHWnd call\n"); return FALSE; } RECT rect; ::GetClientRect(m_hWnd, &rect); r = d3drm->CreateDeviceFromClipper(clipper, GetGUID(), rect.right, rect.bottom, &device); if (r != D3DRM_OK) { TRACE("CreateDeviceFromClipper failed\n"); return FALSE; } device->SetQuality(D3DRMRENDER_GOURAUD); HDC hdc = ::GetDC(m_hWnd); int bpp = ::GetDeviceCaps(hdc, BITSPIXEL); ::ReleaseDC(m_hWnd, hdc); switch (bpp) { case 8: device->SetDither(FALSE); break; case 16: device->SetShades(32); d3drm->SetDefaultTextureColors(64); d3drm->SetDefaultTextureShades(32); device->SetDither(FALSE); break; case 24: case 32: device->SetShades(256); d3drm->SetDefaultTextureColors(64); d3drm->SetDefaultTextureShades(256); device->SetDither(FALSE); break; } r = d3drm->CreateFrame(NULL, &scene); if (r != D3DRM_OK) { TRACE("CreateFrame(&scene) failed\n"); return FALSE; } if (CreateScene() == FALSE) { AfxMessageBox("CreateScene() failed"); return FALSE; } ASSERT( camera ); ASSERT( viewport ); return TRUE; } |
Первое, что вы должны знать о функции CreateDevice(): она присавивает зачения трем переменным класса RMWin — clipper, device и scene. Эти переменные являются защищенными членами класса RMWin, поэтому они доступны для членов класса SampleWin. Это позволяет классу SampleWin пользоваться интерфейсами, созданными классом RMWin.
Сначала присваивается значение переменной clipper. Функция DirectDrawCreateClipper() применяется для получения указателя на интерфейс DirectDrawClipper. Объект отсечения (clipper) — это конструкция DirectDraw, управляющая обновлением окна и позволяющая приложениям DirectDraw и Direct3D корректно работать в оконной среде. Объект отсечения получил свое название в связи с тем фактом, что перекрывающиеся окна должны отображаться в соответствии с тем, какие из их частей видимы. Прямоугольное отсечение обрабатывается Windows, и объект отсечения представляет соответствующую функциональность.
После создания объекта отсечения вызывается функция SetHWnd() интерфейса DirectDrawClipper. Тем самым окну назначается объект отсечения, который будет управлять им. Передаваемая в качестве аргумента переменная m_hWnd, является дескриптором окна, и инициализируется MFC.
Затем вызываются функции GetClientRect() и CreateDeviceFromClipper(). GetClientRect() — это функция Win32, получающая размеры клиентской области окна (клиентская область это внутренняя часть окна не включающая рамку и меню). Функция CreateDeviceFromClipper() является членом интерфейса Direct3DRM и применяется для создания указателя на интерфейс Direct3DRMDevice.
Функция CreateDeviceFromClipper() получает несколько параметров и заслуживает более пристального изучения. Вот как выглядит вызов этой функции из функции CreateDevice():
r = d3drm->CreateDeviceFromClipper(clipper, GetGUID(), rect.right, rect.bottom, &device);Первый аргумент CreateDeviceFromClipper() — это указатель на интерфейс DirectDrawClipper. Второй аргумент — значение, возвращаемое функцией GetGUID(). Эту функцию мы опишем после того, как завершим знакомство с функцией CreateDevice().
Третий и четвертый аргументы — ширина и высота клиентской области окна. Благодаря этому, функция CreateDeviceFromClipper() создает устройство точно соответствующее размерам окна.
СОВЕТ | Изменение размеров окна. Размер устройства Direct3D не может быть изменен. Если изменяются размеры окна необходимо уничтожить существующее устройство, после чего создать новое в соответствии с новыми размерами окна. О том, как это делается, мы поговорим в разделе, посвященном описанию функции RMWin::OnSize(). |
В качестве последнего аргумента передается адрес переменной устройства, что дает возможность инициализировать указатель на новое устройство.
После того, как устройство создано, настроим его параметры для применения визуализации по методу Гуро, посредством функции SetQuality():
device->SetQuality(D3DRMRENDER_GOURAUD);Сразу после создания устройства при визуализации по умолчанию используется равномерная закраска. Мы изменяем этот параметр, чтобы наши программы при визуализации по умолчанию применяли метод Гуро. В дальнейшем, при создании специализированных интерфейсов приложения, этот параметр можно переопределить.
Следующая задача, выполняемая функцией CreateDevice(), — получение глубины пикселей текущего видеорежима:
HDC hdc = ::GetDC(m_hWnd); int bpp = ::GetDeviceCaps(hdc, BITSPIXEL); ::ReleaseDC(m_hWnd, hdc);Функция GetDeviceCaps() вызывается, чтобы присвоить значение, равное количеству битов, используемых для представления одного пиксела, переменной bpp. Это значение определяет максимальное количество одновременно выводимых цветов для текущего видеорежима. Затем значение переменной bpp используется в операторе switch, устанавливающем некоторые параметры объектов Direct3DRMDevice и Direct3DRM. Оптимальные значения параметров зависят от конкретного приложения. Значения, используемые в функции CreateDevice() являются хорошей отправной точкой, но только экспериментирование позволит добиться наилучших результатов для вашего приложения.
Затем, с помощью функции CreateFrame() интерфейса Direct3DRM создается корневой фрейм сцены:
r = d3drm->CreateFrame(NULL, &scene);С технической точки зрения, корневой фрейм является частью сцены и должен создаваться в той части кода, которая зависит от приложения. Однако на практике все сцены содержат корневой фрейм, поэтому его создание в общей части вполне оправдано.
Далее вызывается функция CreateScene():
if (CreateScene() == FALSE) { AfxMessageBox("CreateScene() failed"); return FALSE; }Функция CreateScene() переопределяется в классе SampleWin, чтобы создавать необходимые для конкретного приложения сцены. Функция CreateScene() может применяться для создания любой сцены, но с одним условием: в ней должны инициализироваться переменные camera и viewport. Чуть позже мы подробнее рассмотрим функцию CreateScene().
Последние четыре строки функции CreateDevice() выглядят следующим образом:
CreateScene(); ASSERT(camera); ASSERT(viewport); return TRUE;Макроопределение ASSERT проверяет инициализированы ли переменные camera и viewport и прерывает выполнение приложения с соответствующим сообщением, если этого не было сделано.
Наконец, функция CreateDevice() возвращает TRUE. Если вы вернетесь к листингу 4.1, то увидите, что при возникновении какой-либо ошибки, макроопределение TRACE выводит сообщение и возвращается FALSE. Возврат значения FALSE уведомляет класс RMWin о необходимости прервать исполнение приложения.