// в этом файле лежат все функции WinAPI
#include <windows.h>
// подключаем заголовочный файл DirectX 9 SDK
#include <d3d9.h>
#include <d3dx9.h>
#include <math.h>

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

LPDIRECT3D9 pDirect3D = NULL;
LPDIRECT3DDEVICE9 pDirect3DDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 pBufferVertices = NULL;
LPDIRECT3DINDEXBUFFER9 pBufferIndexes = NULL;
LPDIRECT3DVERTEXBUFFER9 pBufferVertices1 = NULL;
LPDIRECT3DINDEXBUFFER9 pBufferIndexes1 = NULL;
DWORD OneTick = 0;

struct TRIANGLEVERTEX
{
    float x, y, z;
    float nx, ny, nz;
};

#define D3DFVF_TRIANGLEVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)

HINSTANCE hInstance;

// Функция обработки сообщений - прототип
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
// Функция инициализации Direct3D - прототип
HRESULT InitDirect3D(HWND hwnd);
// Функция рендеринга Direct3D - прототип
void RenderingDirect3D(void);
// Функция освобождения захваченных ресурсов - прототип
void DeleteDirect3D(void);
// Функция инициализации объекта (буфера и индексов вершин) - прототип
HRESULT InitObject(void);
// Установка матриц преобоазования - прототип
void Matrix(void);
void Matrix1(void);
// Функция установки освещения и материалов - прототип
void LightMaterial(void);
// Функция инициализации и вывода текста - прототип
void MyTextOut(char *TextString);

// Функция которая является входной точкой приложения
int WINAPI WinMain(HINSTANCE hInst,	HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow)
{
    WNDCLASSEX windowsclass;
    HWND hwnd;
    MSG msg;
    hInstance = hInst;

    windowsclass.cbSize = sizeof(WNDCLASSEX);
    windowsclass.style = CS_DBLCLKS|CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
    windowsclass.lpfnWndProc = MainWinProc;
    windowsclass.cbClsExtra = 0;
    windowsclass.cbWndExtra = 0;
    windowsclass.hInstance = hInst;
    windowsclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    windowsclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    windowsclass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    windowsclass.lpszMenuName = NULL;
    windowsclass.lpszClassName = "WINDOWSCLASS";
    windowsclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&windowsclass))
        return 0;

    hwnd = CreateWindowEx(NULL, "WINDOWSCLASS", "Вращение двух кубов c Z-буфером на DirecX", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
        0, 0, 770, 500, NULL, NULL, hInst, NULL);
    if (!hwnd)
        return 0;

    if (SUCCEEDED(InitDirect3D(hwnd)))
    {
        if (SUCCEEDED(InitObject()))
        {
            ShowWindow(hwnd, SW_SHOWDEFAULT);
            UpdateWindow(hwnd);

            ZeroMemory(&msg, sizeof(msg));
            while (msg.message != WM_QUIT)
            {
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else
                    RenderingDirect3D();
            }
        }
    }
    return (int)msg.wParam;
}
// Функция обработки сообщений
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        // События Создания объектов
    case WM_CREATE:
        break;

        //Обработка сообщений от элементов управления
    case WM_COMMAND:
        break;

        //WM_PAINT - для рисования
    case WM_PAINT:
        break;

        // WM_DESTROY - для закрытия окна
    case WM_DESTROY:
        DeleteDirect3D();
        PostQuitMessage(0);
        break;
    }
    return (DefWindowProc(hwnd, msg, wparam, lparam));
}
// Функция инициализации Direct3D
HRESULT InitDirect3D(HWND hwnd)
{
    if (NULL == (pDirect3D = Direct3DCreate9(D3D_SDK_VERSION)))
        return E_FAIL;

    D3DDISPLAYMODE Display;
    if (FAILED(pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &Display)))
        return E_FAIL;

    D3DPRESENT_PARAMETERS Direct3DParameter;
    ZeroMemory(&Direct3DParameter, sizeof(Direct3DParameter));
    Direct3DParameter.Windowed = true;
    Direct3DParameter.SwapEffect = D3DSWAPEFFECT_DISCARD;
    Direct3DParameter.BackBufferFormat = Display.Format;
    // Включаем z-буфер
    Direct3DParameter.EnableAutoDepthStencil = TRUE;
    // Формат поверхности z-буфера
	Direct3DParameter.AutoDepthStencilFormat = D3DFMT_D16;

    if (FAILED(pDirect3D -> CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &Direct3DParameter, &pDirect3DDevice)))
        return E_FAIL;
    pDirect3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    pDirect3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
    // Включаем z-буфер
    pDirect3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    return S_OK;
}
// Функция рендеринга Direct3D
void RenderingDirect3D(void)
{
    if (pDirect3DDevice == NULL)
        return;

    pDirect3DDevice->Clear(0, NULL, D3DCLEAR_TARGET |  D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(172, 221, 101), 1.0f, 0);

    pDirect3DDevice->BeginScene();

    LightMaterial();

    Matrix();

    pDirect3DDevice->SetStreamSource(0, pBufferVertices, 0, sizeof(TRIANGLEVERTEX));
    pDirect3DDevice->SetIndices(pBufferIndexes);
    pDirect3DDevice->SetFVF(D3DFVF_TRIANGLEVERTEX);
    pDirect3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 36, 0, 12);

    LightMaterial();

    Matrix1();

    pDirect3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 36, 0, 12);

	MyTextOut("Hello");

    pDirect3DDevice->EndScene();

    pDirect3DDevice->Present(NULL, NULL, NULL, NULL);
}
// Функция освобождения захваченных ресурсов
void DeleteDirect3D(void)
{
    if (pBufferVertices != NULL)
        pBufferVertices->Release();
    if (pBufferIndexes != NULL)
        pBufferIndexes->Release();
    if (pDirect3DDevice != NULL)
        pDirect3DDevice->Release();
    if (pDirect3D != NULL)
        pDirect3D->Release();
}
// Функция инициализации объекта (буфера и индексов вершин)
HRESULT InitObject(void)
{
     // Создаём вершины для куба
        TRIANGLEVERTEX Vertices[] =
        {
            { 5.0f, -5.0f, -5.0f, 0.0f, 0.0f, -1.0f},	//	A1
		    { 5.0f,  5.0f, -5.0f, 0.0f, 0.0f, -1.0f},	//	B1
		    {-5.0f,  5.0f, -5.0f, 0.0f, 0.0f, -1.0f},	//	C1
		    {-5.0f, -5.0f, -5.0f, 0.0f, 0.0f, -1.0f},	//	D1

		    {-5.0f, -5.0f, -5.0f, -1.0f,	0.0f, 0.0f},	//	A2
		    {-5.0f,  5.0f, -5.0f, -1.0f,	0.0f, 0.0f},	//	B2
		    {-5.0f,  5.0f,  5.0f, -1.0f,	0.0f, 0.0f},	//	C2
		    {-5.0f, -5.0f,  5.0f, -1.0f,	0.0f, 0.0f},	//	D2

		    {-5.0f, -5.0f, 5.0f, 0.0f, 0.0f, 1.0f},	//	A3
		    {-5.0f,  5.0f, 5.0f, 0.0f, 0.0f, 1.0f},	//	B3
		    { 5.0f,  5.0f, 5.0f, 0.0f, 0.0f, 1.0f},	//	C3
		    { 5.0f, -5.0f, 5.0f, 0.0f, 0.0f, 1.0f},	//	D3

		    {5.0f,	-5.0f,	5.0f, 1.0f, 0.0f, 0.0f},	//	A4
		    {5.0f,	 5.0f,  5.0f, 1.0f, 0.0f, 0.0f},	//	B4
		    {5.0f,	 5.0f, -5.0f, 1.0f, 0.0f, 0.0f},	//	C4
		    {5.0f,	-5.0f, -5.0f, 1.0f, 0.0f, 0.0f},	//	D4

		    { 5.0f, -5.0f, -5.0f, 0.0f, -1.0f, 0.0f},	//	A5
		    {-5.0f, -5.0f, -5.0f, 0.0f, -1.0f, 0.0f},	//	B5
		    {-5.0f, -5.0f,  5.0f, 0.0f, -1.0f, 0.0f},	//	C5
		    { 5.0f, -5.0f,  5.0f, 0.0f, -1.0f, 0.0f},	//	D5

		    { 5.0f, 5.0f,	 5.0f,	0.0f, 1.0f,	0.0f},	//	A6
		    {-5.0f, 5.0f,	 5.0f,	0.0f, 1.0f,	0.0f},	//	B6
		    {-5.0f, 5.0f,	-5.0f,	0.0f, 1.0f,	0.0f},	//	C6
		    { 5.0f, 5.0f,	-5.0f,	0.0f, 1.0f,	0.0f}	//	D6
        };

    // Заполняем индексный буфер
    unsigned short Indexes[36] =
    {
        0,1,2,
        2,3,0,
		4,5,6,
        6,7,4,
		8,9,10,
        10,11,8,
		12,13,14,
        14,15,12,
		16,17,18,
        18,19,16,
		20,21,22,
        22,23,20
    };
    // Создаём буфер вершин
    if (FAILED(pDirect3DDevice->CreateVertexBuffer(24*sizeof(TRIANGLEVERTEX), 0, D3DFVF_TRIANGLEVERTEX, D3DPOOL_DEFAULT,
        &pBufferVertices, NULL)))
        return E_FAIL;

    void *pBV;
    if (FAILED(pBufferVertices->Lock(0, sizeof(Vertices), (void**)&pBV, 0)))
        return E_FAIL;

    memcpy(pBV, Vertices, sizeof(Vertices));

    pBufferVertices->Unlock();

    // Создаём индексный буфер
    if (FAILED(pDirect3DDevice->CreateIndexBuffer(36*sizeof(Indexes), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT,
        &pBufferIndexes, NULL)))
        return E_FAIL;

    void *pBI;
    if (FAILED(pBufferIndexes->Lock(0, sizeof(Indexes), (void**)&pBI, 0)))
        return E_FAIL;

    memcpy(pBI, Indexes, sizeof(Indexes));

    pBufferIndexes->Unlock();

    return S_OK;
}
// Установка матриц преобоазования
void Matrix(void)
{
    // Матрица вида
    D3DXMATRIX MatrixView;
    //матрица проекции
    D3DXMATRIX MatrixProjection;
    D3DXMATRIX MatrixWorld;
    D3DXMATRIX MatrixTranslation;
    D3DXMATRIX MatrixRotation;

    float BeginAngle;
    float rps;
    UINT iTime;
    float fAngle;

    BeginAngle = D3DX_PI/4.0f;
    rps = 0.5f;
    iTime = GetTickCount();

    if(!OneTick)
        OneTick = iTime;
    fAngle = BeginAngle + (iTime -  OneTick) * (D3DX_PI * 2.0f * rps / 1000.0f);
    
    D3DXMatrixRotationY(&MatrixRotation, fAngle);                                      // поворот первого куба
    D3DXMatrixTranslation(&MatrixTranslation, 0.0f, 0.0f, 0.0f);                       // положение первого куба
    D3DXMatrixMultiply(&MatrixWorld, &MatrixRotation, &MatrixTranslation);
    pDirect3DDevice->SetTransform(D3DTS_WORLD, &MatrixWorld);

    // Изменяем видовую матрицу
    D3DXMatrixLookAtLH(&MatrixView,      // полученная в итоге видовая матрица
        &D3DXVECTOR3(0.0f, 0.0f, 80.0f), // точка, из которой смотрим
        &D3DXVECTOR3(0.0f, 0.0f, 0.0f),  // куда смотрим
        &D3DXVECTOR3(0.0f, 1.0f, 0.0f)); // направление верха
    // Устанавливаем видовую матрицу
    pDirect3DDevice->SetTransform(D3DTS_VIEW, &MatrixView);
    // Изменяем матрицу проекции
    D3DXMatrixPerspectiveFovLH(&MatrixProjection, // полученная итоговая матрица проекции
        D3DX_PI/4,                                // поле зрения в направлении оси Y в радианах
        1.54f,                                    // соотношения сторон экрана 770/500=1.54
        10.0f,                                    // передний план отсечения сцены
        200.0f);                                  // задний план отсечения сцены
    // Устанавливаем матрицу проекции
    pDirect3DDevice->SetTransform(D3DTS_PROJECTION, &MatrixProjection);
}
// Установка матриц преобоазования
void Matrix1(void)
{
    // Матрица вида
    D3DXMATRIX MatrixView;
    //матрица проекции
    D3DXMATRIX MatrixProjection;
    D3DXMATRIX MatrixWorld;
    D3DXMATRIX MatrixTranslation;
    D3DXMATRIX MatrixRotation;

    float BeginAngle;
    float rps;
    UINT iTime;
    float fAngle;

    BeginAngle = D3DX_PI/4.0f;
    rps = 0.5f;
    iTime = GetTickCount();

    if(!OneTick)
        OneTick = iTime;
    fAngle = BeginAngle + (iTime -  OneTick) * (D3DX_PI * 2.0f * rps / 1000.0f);

    D3DXMatrixRotationY(&MatrixRotation, fAngle);                                      // поворот второго куба
    D3DXMatrixTranslation(&MatrixTranslation, 10.0f, 10.0f, 0.0f);                     // положение второго куба
    D3DXMatrixMultiply(&MatrixWorld, &MatrixRotation, &MatrixTranslation);
    pDirect3DDevice->SetTransform(D3DTS_WORLD, &MatrixWorld);

    // Изменяем видовую матрицу
    D3DXMatrixLookAtLH(&MatrixView,      // полученная в итоге видовая матрица
        &D3DXVECTOR3(0.0f, 0.0f, 50.0f), // точка, из которой смотрим
        &D3DXVECTOR3(0.0f, 0.0f, 0.0f),  // куда смотрим
        &D3DXVECTOR3(0.0f, 1.0f, 0.0f)); // направление верха
    // Устанавливаем видовую матрицу
    pDirect3DDevice->SetTransform(D3DTS_VIEW, &MatrixView);
    // Изменяем матрицу проекции
    D3DXMatrixPerspectiveFovLH(&MatrixProjection, // полученная итоговая матрица проекции
        D3DX_PI/4,                                // поле зрения в направлении оси Y в радианах
        1.54f,                                    // соотношения сторон экрана 770/500=1.54
        10.0f,                                    // передний план отсечения сцены
        200.0f);                                  // задний план отсечения сцены
    // Устанавливаем матрицу проекции
    pDirect3DDevice->SetTransform(D3DTS_PROJECTION, &MatrixProjection);
}
// Функция установки освещения и материалов
void LightMaterial(void)
{
	// Создаём материал
	D3DMATERIAL9 Material;
	// Создаём освещение
	D3DLIGHT9 Light;
	D3DXVECTOR3 VectorDir;

	// Обнуляем память под материал
	ZeroMemory(&Material, sizeof(D3DMATERIAL9));
    // Задаём цвета для диффузного и цвета подсветки
	Material.Diffuse.r = Material.Ambient.r = 0.5f;
	Material.Diffuse.g = Material.Ambient.g = 1.0f;
	Material.Diffuse.b = Material.Ambient.b = 0.0f;
	Material.Diffuse.a = Material.Ambient.a = 1.0f;
	// Устанавливаем материал
	pDirect3DDevice->SetMaterial(&Material);
	// Обнуляем память под освещение
	ZeroMemory(&Light, sizeof(D3DLIGHT9));
    // Тип света
	Light.Type = D3DLIGHT_DIRECTIONAL;
	// Количество красного в свете
	Light.Diffuse.r = 1.0f;
	Light.Diffuse.g = 1.0f;
	Light.Diffuse.b = 1.0f;
	// Максимальное расстояние действия источника света
	Light.Range = 1000.0f;
	// Вектор, задающий направление света
	VectorDir = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
	
	// Нормализуем вектор VectorDir и записываем в свойства
    // направления Light
	D3DXVec3Normalize((D3DXVECTOR3*)&Light.Direction, &VectorDir);
	// Устанавливаем свет
	pDirect3DDevice->SetLight(0, &Light);
	// Включаем свет с соответсвующим индексом
	pDirect3DDevice->LightEnable(0, TRUE);
	// Устанавливаем параметры рендеринга, включая освещение
	pDirect3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
	// Устанавливаем параметры рендеринга, включая окружающее
    // освещение
	pDirect3DDevice->SetRenderState(D3DRS_AMBIENT, 0);
}
void MyTextOut(char *TextString)
{  
     // Получение дескриптора контекста устройства
     HDC hdc = CreateCompatibleDC(0);
   
	 // Обявление переменную которая является экземпляром структуры
	 LOGFONT lf;
	 // Очищаем экземпляр структуры
     ZeroMemory(&lf, sizeof(LOGFONT));
  
	 // Заполняем экземпляр структуры
	 lf.lfHeight    = 25;    // в логических единицах
     lf.lfWidth     = 12;    // в логических единицах
     lf.lfWeight    = 500;   // насыщенность, диапазон 0(тонкий) - 1000(жирный)
     lf.lfItalic    = false; // false - означает курсив
     lf.lfUnderline = false; // false - означает подчеркивание 
     lf.lfStrikeOut = false; // false - перечеркнутый шрифт
     lf.lfCharSet   = DEFAULT_CHARSET; // Номер набора символов шрифта - таблицы кодировки
     lstrcpy(lf.lfFaceName, "Times New Roman"); // гарнитура шрифта

     // Создаем шрифт и выбираем его в контексте устройства
     HFONT hFont;    // 
     HFONT hFontOld; // дескриптор старого шрифта

	 // Создаём объект HFONT на основе заполненной стрктуры
     hFont = CreateFontIndirect(&lf);
     // Делаем данный шрифт текущим шрифтом
     hFontOld = (HFONT)SelectObject(hdc, hFont);

     // Указател на Меш
     LPD3DXMESH Text = NULL;
	 // Создание трехмерной сетки, представляющую строку текста
     D3DXCreateText(pDirect3DDevice, hdc, TextString, 0.001f, 0.4f, &Text, 0, 0);

	 // Вывод текста
	 Text->DrawSubset(0);

     // Восстанавление бывшего до этого шрифта
     SelectObject(hdc, hFontOld);
	 // Освобождение памяти указателя на шрифт
     DeleteObject(hFont);
	 // Освобождение памяти дискриптора контекста устройства
     DeleteDC(hdc);
     // Освобождение памяти указателя на меш
	 if (Text != NULL)
		Text->Release();
}
