Пришло время взглянуть на функцию InitMainSurfaces(). Хочу напомнить, что функция InitMainSurfaces() вызывается из функции OnCreate() сразу после обращения к функции InitDisplayMode(). Функция InitMainSurfaces() создает первичную и вторичную поверхности вместе с Z-буфером. Код функции приведен в листинге10.5.
Листинг 10.5. Функция InitMainSurfaces() |
BOOL RMWin::InitMainSurfaces() { if (primsurf) { primsurf->Release(); primsurf = 0; } if (zbufsurf) { zbufsurf->Release(); zbufsurf = 0; } DDSURFACEDESC desc; desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_BACKBUFFERCOUNT | DDSD_CAPS; desc.dwBackBufferCount = 1; desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; ddraw->CreateSurface(&desc, &primsurf, 0); DDSCAPS ddscaps; ddscaps.dwCaps = DDSCAPS_BACKBUFFER; primsurf->GetAttachedSurface(&ddscaps, &backsurf); memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(DDSURFACEDESC); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_ZBUFFERBITDEPTH; desc.dwWidth = modewidth; desc.dwHeight = modeheight; desc.dwZBufferBitDepth = 16; desc.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_SYSTEMMEMORY; ddraw->CreateSurface(&desc, &zbufsurf, 0); backsurf->AddAttachedSurface(zbufsurf); return TRUE; } |
Сначала функция InitMainSurfaces() освобождает любые существующие первичные поверхности и Z-буферы:
if (primsurf) { primsurf->Release(); primsurf = 0; } if (zbufsurf) { zbufsurf->Release(); zbufsurf = 0; }Функция OnCreate() (которая вызывает функцию InitMainSurfaces()) вызывается только однажды — когда создается окно программы — а в этот момент не существует ни первичной поверхности, ни Z-буфера. Однако функция InitMainSurfaces() используется другими функциями для изменения видеорежима. Поэтому существующие первичная поверхность и Z-буфер должны быть освобождены перед созданием новых. Вторичную поверхность освобождать не требуется, поскольку она будет уничтожена вместе с первичной поверхностью.
Затем создается первичная поверхность:
DDSURFACEDESC desc; desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_BACKBUFFERCOUNT | DDSD_CAPS; desc.dwBackBufferCount = 1; desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; ddraw->CreateSurface(&desc, &primsurf, 0);Поверхность, которую мы хотим создать, должна быть описана в структуре DDSURFACEDESC. Поле dwSize этой структуры должно содержать размер структуры. Поле dwFlags используется для хранения набора флагов, указывающих, какие поля структуры мы будем инициализировать. В нашем случае используются поля dwBackBufferCount и ddsCaps, поэтому мы указываем флаги DDSD_BACKBUFFERCOUNT и DDSD_CAPS.
Флаги возможностей поверхности (DDSCAPS_PRIMARYSURFACE, DDSCAPS_FLIP и DDSCAPS_COMPLEX) указывают, что мы создаем первичную поверхность с возможностью переключения страниц. Флаг DDSCAPS_3DDEVICE сообщает DirectDraw, что мы будем использовать новую поверхность для создания устройства Direct3D.
Фактическое создание поверхностей выполняет функция CreateSurface() интерфейса DirectDraw. В качестве первого аргумента мы передаем подготовленную ранее структуру DDSURFACEDESC. Во втором аргументе передается адрес указателя на интерфейс DirectDrawSurface. Указатель (primsurf) является членом класса RMWin и будет использоваться в дальнейшем для выполнения переключения страниц.
Обратите внимание, что значение поля dwBackBufferCount структуры DDSURFACEDESC равно единице. Это означает, что мы проинформировали DirectDraw о наличии у первичной поверхности одного вторичного буфера. Фактически, DirectDraw создает вторичный буфер вместе с первичной поверхностью. Все что нам остается сделать — получить указатель на вторичный буфер:
DDSCAPS ddscaps; ddscaps.dwCaps = DDSCAPS_BACKBUFFER; primsurf->GetAttachedSurface(&ddscaps, &backsurf);Позднее мы будем использовать указатель backsurf для сохранения выводимого изображения перед его перемещением, или переключением, на первичную поверхность.
Теперь мы создаем Z-буфер и присоединяем его к поверхности backsurf:
memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(DDSURFACEDESC); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_ZBUFFERBITDEPTH; desc.dwWidth = modewidth; desc.dwHeight = modeheight; desc.dwZBufferBitDepth = 16; desc.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_SYSTEMMEMORY; ddraw->CreateSurface(&desc, &zbufsurf, 0); backsurf->AddAttachedSurface(zbufsurf);Для описания поверхности Z-буфера нам потребуется экземпляр структуры DDSURFACEDESC. Вместо того, чтобы объявлять еще одну копию, воспользуемся уже существующим экземпляром desc, который использовался при создании первичной поверхности. Очистим структуру с помощью функции memset(), присвоив всем ее полям нулевые значения. Затем присвоим требуемые значения полям dwSize и dwFlags.
Флаги, присвоенные полю dwFlags указывают, что мы будем задавать ширину и высоту поверхности, ее возможности, а также глубину Z-буфера. Соответствующим полям структуры присваиваются значения. Размеры Z-буфера должны быть равны размерам первичного и вторичного буферов. Поскольку размеры первичного и вторичного буферов всегда равны размеру экрана в текущем видеорежиме, для присваивания значений полям dwWidth и dwHeight можно использовать переменные modewidth и modeheight.
Мы создаем 16-разрядный Z-буфер, присваивая соответствующее значение полю. Глубина Z-буфера определяет точность работы кода удаления невидимых поверхностей. 8-разрядный Z-буфер может хранить только 256 различных значений Z, или расстояний, поэтому его возможности весьма ограничены. 16-разрядный Z-буфер предоставляет 65 535 различных значений и подходит для большинства случаев Z-буферизации. Сложные сцены могут потребовать наличия 24- или 32-разрядного Z-буфера.
Константа DDSCAPS_SYSTEMMEMORY используется для указания, что Z-буфер должен размещаться в системной памяти, а не в памяти видеокарты. Видеопамяти может не хватать для трехмерной графики (особенно на видеокартах с 2 мегабайтами памяти). Хранение Z-буфера в системной памяти освобождает память видеокарты для хранения отображаемых поверхностей, таких как спрайты и фоновые изображения. Хранение отображаемых данных в видеопамяти более предпочтительно, поскольку ускоряет работу приложения так как видеокарты обычно выполняют копирование блоков видеопамяти гораздо быстрее, чем копирование из системной памяти в видеопамять.
На последнем этапе выполняется создание поверхности Z-буфера и ее присоединение к поверхности backsurf:
ddraw->CreateSurface(&desc, &zbufsurf, 0); backsurf->AddAttachedSurface(zbufsurf);После того, как поверхность Z-буфера присоединена, Direct3D использует ее автоматически. Больше никаких манипуляций с Z-буфером не требуется.