Ну что, как вам код? Приходит понимание потихоньку? В общем-то, ничего сложного. В главной функции программы мы делаем исключительно две вещи:
1. Создаём и показываем на экране окно, получив его хэндл.
2. Инициализируем Direct3D.
Собственно, с окном всё совсем элементарно - крутится цикл, который получает и отправляет сообщения, типа нажатия кнопок мыши, кнопки закрыть, клавиш и прочее - как бы, все виндовые программы общаются, пересылая друг другу такие вот сообщения. И наша программа умеет обрабатывать только одно - сообщение о выходе.
В перерывах же (между обработкой всех поступающих сообщений) вызывается функция render_frame(), которая рисует один кадр, воспользовавшись уже инициализированным Direct3D.
С инициализацией всё чуть сложнее, но не более. Вот код функции:
void initD3D(HWND hWnd) {
d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
}
И для её работы у нас есть две загадочных переменных:
LPDIRECT3D9 d3d; //Указатель на Direct3D-интерфейс
LPDIRECT3DDEVICE9 d3ddev; //Указатель на устройство
Соу. Что это за херня такая?!! - спросите вы. Давайте немножко углубимся в теорию - для лучшего понимания.
Компьютер ни черта не знает ни про мониторы, ни про видеокарты, ни, тем более, про рисование - у него нет творческой жилки (уж тем более) фантазии. Всё, что он умеет - это менять в памяти нолики на единички и обратно. Как для него выглядит картинка на мониторе?
Представим, что монитор у нас двухцветный - чёрно-белый. И вот что на нём нарисовано:
Буква "А". Монитор у нас, кстати, аж 20 на 20 пикселей.
Напомню, что система координат у нас довольно хитрая - две оси, горизонтальная и вертикальная, а точка отсчёта - верхний-левый угол экрана:
Белые пиксели у нас имеют значение 0, чёрные - 1. Что ж нам нужно сделать, чтобы получить букву "А" на экране?
Компьютер не понимает, что у нас есть две оси - он сидит только длинную простыню:
000000000000000...
И так далее. Раз у нас монитор размерами 20х20 пикселей, то получается, что у нас есть 20 строчек по 20 пикселей. 0 - белых, 1 - чёрных. Вот и выходит:
111000000000000000000000000000000000
101000000000000000000000000000000000
111000000000000000000000000000000000
101000000000000000000000000000000000
000000000000000000000000000000000000
000000000000000000000000000000000000
Примерно такая картина. Компьютер не знает размеров или разрешений - у него есть просто длинная строчка цифр, но вот видеокарте можно дать понять, что каждые 20 пикселей надо переходить на строчку ниже (прибавлять по вертикальной оси единичку).
Похоже? По-моему, вполне.
Дык вот. Что есть Direct3D? Direct3D - это кусок DirectX, отвечающий за рисование. По сути это - набор функций, берущих на себя всю ответственность за командование видеокартой и монитором. Не будем вдаваться в лишние сейчас подробности. Просто командуем, d3d - нарисуй мне прямоугольник! Вот тебе видеокарта! Рисуй, я сказал! - и оно рисует.
Собственно, чтобы командовать этим набором функций - нам надо его как-то иметь при себе. Этим занимается офигенска команда
[Direct3DCreate9] - создаёт новый объект Direct3D, возвращая указатель на него. Добавляем первый комментарий:
void initD3D(HWND hWnd) {
d3d = Direct3DCreate9(D3D_SDK_VERSION); //Создаём и получаем указатель на объект d3d
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
}
Так, клёво. Объект, к которому можно обратиться и приказать ему рисовать, мы получили. Осталось выяснить, где рисовать - то есть достучаться до нашей видеокарты. Двумя строчками ниже мы объявляем специальную структуру, нужную для этого самого "достучаться". Заодно заполняем некоторые параметры:
void initD3D(HWND hWnd) {
d3d = Direct3DCreate9(D3D_SDK_VERSION); //Создаём и получаем указатель на объект d3d
D3DPRESENT_PARAMETERS d3dpp; //Структура, описывающая устройство ZeroMemory(&d3dpp, sizeof(d3dpp)); //Очищаем её
d3dpp.Windowed = TRUE; //Говорим, что мы в окне
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; //Скучно и не интересно
d3dpp.hDeviceWindow = hWnd; //Скармливаем хэндл окна
d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev); //Получаем устройство!
}
Итак, что же происходит? Объявили структуру для описания устройства, очистили её, сказали, что собираемся рисовать в окне, выдали на растерзание хэндл нужного окна, после чего - вызвали офигенскую
[CreateDevice], возвращающую (кто бы мог подумать!) указатель на устройство. А устройством этим является наша видеокарта, да-да!
Если в голове укладывается плохо, то просто запомните, что объект D3D - это палитра и кисть, а устройство D3D - это холст. Чтобы нарисовать на холсте, надо провести по нему кистью - делов-то!
Дело остаётся за малым, а именно - за рисованием. Как мы помним, у нас крутится цикл, который вызывает render_frame(). Вот её код:
void render_frame(void) {
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
d3ddev->BeginScene();
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
ЩИТО оно делает? А всё просто. Мы обращаемся к нашему устройству и говорим ему
[очистить] всю область рисования нежным синим цветом. После этого мы вызываем две функции, которые подразумевают под собой начало и конец рисования (помните BeginPaint и EndPaint и предыдущих статей?), ничего не рисуем и говорим показать результат а экране. И он показывает!
Всё, созерцайте, а то я и так уже разошёлся.