netlib.narod.ru | < Назад | Оглавление | Далее > |
Ранее в этой главе мы определили порядок, в котором должны создаваться интерфейсы Direct3D. Рис. 4.4 иллюстрирует необходимый порядок действий, а также показывает, какие этапы являются стандартными, а какие зависят от приложения.
Теперь, когда мы познакомились с выбранной структурой классов, настало время пересмотреть порядок создания интерфейсов с учетом списка классов и функций-членов. Рис. 4.6. почти полностью повторяет рис. 4.4, за исключением того, что на нем указаны функции, ответственные за каждый из этапов.
Рис. 4.6. Порядок создания интерфейсов с разделением по категориям
Как и было сказано, класс RMWin создает стандартные объекты, а класс SampleWin создает объекты, зависящие от приложения.
Теперь давайте рассмотрим все участвующие в инициализации программы функции в порядке их вызова.
Функция InitInstance() — это виртуальная функция класса CWinApp, переопределяемая для выполнения инициализации. Собственные версии функции InitInstance() предоставляют и класс RMApp и класс SampleApp.
Функция InitInstance() класса SampleApp отвечает за создание объекта окна. В ней можно установить некоторые параметры. Функция SampleApp::InitInstance() выглядит следующим образом:
BOOL SampleApp::InitInstance() { #ifdef _DEBUG afxTraceEnabled = FALSE; #endif SampleWin* win = new SampleWin; if (!win->Create("Sample Application", IDI_ICON, IDR_MAINMENU)) return FALSE; win->SetColorModel(D3DCOLOR_MONO); m_pMainWnd = win; return RMApp::InitInstance(); }
Первые три строки функции инициализируют функции трассировки MFC. Макроопределение TRACE удобно использовать для вывода диагностических сообщений во время выполнения программы. По умолчанию использование макроопределения TRACE разрешено, но выполняется оно очень медленно. Если вы часто используете это макроопределение, оно окажет видимый эффект на производительность вашего приложения (особенно, если применять его в функции, вызываемой при каждомм обновлении экрана). Переменная afxTraceEnabled применяется для активации или деактивации макроопределений TRACE. Присваивание этой переменной значения FALSE запрещает функции трассировки. Обратите внимание, что значение этой переменной присваивается в блоке условной компиляции. Это связано с тем, что функции трассировки доступны только в приложениях, компилируемых в режиме отладки (DEBUG). Если при компиляции не включается отладочная информация (выбран режим Release), переменная afxTraceEnabled не существует, и необходимо гарантировать, что обращение к ней будет выполняться только в режиме отладки.
Затем создается экземпляр класса SampleWin. Функция Create() вызывается для инициализации окна и ей передаются три аргумента. Первый аргумент — это строка, отображаемая в заголовке окна. Второй — идентификатор ресурса для значка приложения, а третий — идентификатор ресурса для меню.
После вызова функции Create() (и проверки успешности ее завершения), вызывается функция SetColorModel(). Константа D3DCOLOR_MONO указывает, что вместо цветовой модели RGB мы будем применять цветовую модель Ramp (монохромную). Цветовая модель Ramp выбирается по умолчанию, так что нет необходимости вызывать эту функцию, но мы включим ее, чтобы отметить правильное место для задания цветовой модели. Вызов функции SetColorModel() на другом этапе выполнения программы не окажет ожидаемого действия.
В завершение переменной CWnd::m_pMainWnd присваивается указатель на новое окно. Это важный этап, поскольку MFC использует данную переменную для доступа к окну.
Функция SampleApp::InitInstance() завершается вызовом функции RMApp::InitInstance() код которой приведен ниже:
BOOL RMApp::InitInstance() { ASSERT(m_pMainWnd); m_pMainWnd->ShowWindow(SW_SHOWNORMAL); m_pMainWnd->UpdateWindow(); return TRUE; }
В самом начале функция проверяет присвоено ли значение переменной m_pMainWnd. Макроопределение ASSERT прерывает выполнение программы и выводит окно сообщения, если значение переменной равно NULL.
Затем вызываются функции ShowWindow() и UpdateWindow(). Эти функции наследуются от класса CWnd и необходимы для инициализации окна.
И, наконец, возвращается значение TRUE. Если при фыполнении функции InitInstance() возникла ошибка, функция должна возвратить FALSE, чтобы уведомить MFC о невозможности инициализации приложения.
Функция OnCreate() наследуется классом RMWin от класса CWnd. Она вызывается в процессе создания окна (после того, как окно создано, но до его вывода на экран) и является удобным местом для инициализации Direct3D. Код функции выглядит так:
int RMWin::OnCreate(LPCREATESTRUCT lpCreateStruct) { HRESULT r; r = Direct3DRMCreate(&d3drm); if (r != D3DRM_OK) { TRACE("failed to create D3DRM object\n"); return -1; } return 0; }
Для инициализации указателя на интерфейс Direct3DRM вызывается функция Direct3DRMCreate(). Переменная d3drm применяется во многих местах программы для вызова различных функций интерфейса Direct3DRM.
Затем проверяется значение, возвращенное функцией Direct3DRMCreate(). Любое значение, отличное от D3DRM_OK свидетельствует об ошибке. В случае возникновения ошибки, отображается диагностическое сообщение и функция возвращает –1. Это указывает MFC на необходимость прервать создание окна. Если все прошло успешно, функция возвращает 0.
СОВЕТ |
Использование макроопределения TRACE. Если (или когда) при работе с программами с CD-ROM у вас возникнут проблемы, убедитесь, что у вас разрешено использование макроопределения TRACE и вы используете отладочную версию DirectX. И примеры с компакт-диска и отладочная версия DirectX будут выводить сообщения об ошибках, только когда код, откомпилирован в режиме DEBUG, и исполняется непосредственно из среды Visual C++. |
Функция 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 о необходимости прервать исполнение приложения.
Функция GetGUID() применяется для получения GUID (глобального уникального идентификатора) идентифицирующего создаваемое функцией CreateDeviceFromClipper() устройство. Если вместо вызова функции GetGUID() использовать значение NULL, функция CreateDeviceFromClipper() автоматически выберет устройство с цветовой моделью Ramp. Мы вызываем функцию GetGUID() чтобы иметь возможность выбрать цветовую модель Ramp или RGB. Текст функции GetGUID() приведен в листинге 4.2.
Листинг 4.2. Функция GetGUID() |
GUID* RMWin::GetGUID() { static GUID* lpguid; HRESULT r; D3DFINDDEVICESEARCH searchdata; memset(&searchdata, 0, sizeof searchdata); searchdata.dwSize = sizeof searchdata; searchdata.dwFlags = D3DFDS_COLORMODEL; searchdata.dcmColorModel = colormodel; static D3DFINDDEVICERESULT resultdata; memset(&resultdata, 0, sizeof resultdata); resultdata.dwSize = sizeof resultdata; LPDIRECTDRAW ddraw; r = DirectDrawCreate(NULL, &ddraw, NULL); if (r != DD_OK) { TRACE("DirectDrawCreate failed\n"); return NULL; } LPDIRECT3D d3d; r = ddraw->QueryInterface(IID_IDirect3D, (void**)&d3d); if (r != D3DRM_OK) { TRACE("d3drm->QueryInterface failed\n"); ddraw->Release(); return NULL; } r = d3d->FindDevice(&searchdata, &resultdata); if (r == D3D_OK) lpguid = &resultdata.guid; else { TRACE("FindDevice failure\n"); lpguid = NULL; } d3d->Release(); ddraw->Release(); return lpguid; } |
Перед тем, как продолжить, посмотрим, почему функция GetGUID() такая сложная. Наша задача — получить GUID для заданного устройства Direct3D. Казалось бы, интерфейс Direct3DRM должен предоставлять функцию, выполняющую эту работу. Возможно, библиотека Direct3D и могла бы быть разработана таким способом, но этого не произошло.
Мы применяем абстрактный режим Direct3D, а абстрактный режим в свою очередь зависит от непосредственного режима, который фактически выполняет визуализацию. Это означает, что устройство Direct3D является конструкцией непосредственного режима, и для поиска требуемого устройства необходимо воспользоваься интерфейсом непосредственного режима. Функции непосредственного режима доступны через COM-интерфейс Direct3D.
Мы можем получить указатель на интерфейс Direct3D создав интерфейс DirectDraw и вызвав его функцию QueryInterface(). Это возможно, поскольку объект DirectDraw начиная с DirectX версии 2 поддерживает интерфейс Direct3D. Если бы мы работали с DirectX версии 1, такой вызов QueryInterface() закончился бы неудачей.
Давайте взглянем на начало функции GetGUID() (листинг 4.2). Первое, что делает функция, — это подготовка двух структур, применяемых для хранения информации об устройстве. Структура searchdata используется для указания запрашиваемой цветовой модели, а в структуре resultdata хранятся результаты поиска. Ниже приведен фрагмент программы, создающий и инициализирующий эти структуры:
D3DFINDDEVICESEARCH searchdata; memset(&searchdata, 0, sizeof searchdata); searchdata.dwSize = sizeof searchdata; searchdata.dwFlags = D3DFDS_COLORMODEL; searchdata.dcmColorModel = colormodel; static D3DFINDDEVICERESULT resultdata; memset(&resultdata, 0, sizeof resultdata); resultdata.dwSize = sizeof resultdata;
Функция memset() обнуляет все поля, после чего полю dwSize присваивается размер структуры.
СОВЕТ |
Поле dwSize. Требование, чтобы поле структуры содержало размер самой структуры, может показаться довольно глупым, но это сделано, чтобы позволить расширение функциональности без модификации старых программ. Microsoft может увеличить размер структуры в будущей версии Direct3D, а программы, написанные сегодня, будут работать, потому что Direct3D сможет определить, основываясь на значении поля dwSize, какая версия структуры используется. |
Полю dwFlags присваивается значение константы D3DFDS_COLORMODEL — это указывает, что единственным критерием поиска является заданная цветовая модель устройства. Полю dcmColorModel присваивается значение переменной colormodel. По умолчанию значение переменной colormodel равно D3DCOLOR_MONO, но оно может быть изменено функцией SetColorModel() (см. выше функцию InitInstance()).
На следующем этапе работы функции GetGUID() создается интерфейс DirectDraw:
LPDIRECTDRAW ddraw; r = DirectDrawCreate(NULL, &ddraw, NULL); if (r != DD_OK) { TRACE("DirectDrawCreate failed\n"); return NULL; }
Функция DirectDrawCreate() применяется для получения указателя на интерфейс DirectDraw. Если вызов функции завершается неудачно, макроопределение TRACE отображает соответствующее сообщение, после чего возвращается NULL.
Как только указатель на интерфейс DirectDraw получен, у объекта запрашивается интерфейс Direct3D:
LPDIRECT3D d3d; r = ddraw->QueryInterface(IID_IDirect3D, (void**)&d3d); if (r != D3DRM_OK) { TRACE("d3drm->QueryInterface failed\n"); ddraw->Release(); return NULL; }
Константа IID_IDirect3D представляет собой GUID интерфейса Direct3D и применяется здесь, чтобы указать функции QueryInterface() какой интерфейс мы ищем. Если функция завершается нормально, переменная d3d указывает на экземпляр интерфейса Direct3D. При возникновении ошибки выводится отладочное сообщение и функция возвращает NULL предварительно освободив интерфейс DirectDraw.
Теперь мы можем выполнить поиск GUID устройства:
r = d3d->FindDevice(&searchdata, &resultdata); if (r == D3D_OK) lpguid = &resultdata.guid; else { TRACE("FindDevice failure\n"); lpguid = NULL; }
Функция FindDevice() интерфейса Direct3D получает в качестве аргументов указатели на две подготовленные ранее структуры. Если функция возвращает D3D_OK, значит GUID найден и переменной lpguid присваивается указатель на него.
Обратите внимание, что структура resultdata и переменная lpguid объявлены как статические. Это сделано потому, что GUID представляет собой 128-разрядное значение. Объявив эти переменные статическими, мы можем возвратить указатель на GUID, а не делать копию его значения.
Перед завершением работы функция GetGUID() освобождает интерфейсы DirectDraw и Direct3D.
netlib.narod.ru | < Назад | Оглавление | Далее > |