// в этом файле лежат все функции 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); //, long x1, long y1, long x2, long y2, D3DCOLOR TextColor);

// Функция которая является входной точкой приложения
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(TEXT("Hello")); //, 10, 10, 800, 800, D3DCOLOR_ARGB(150,0,150,14));

    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) //, long x1, long y1, long x2, long y2, D3DCOLOR TextColor)
{  
     // Получение дескриптора контекста устройства
     HDC hdc = CreateCompatibleDC(0);
   
	      // Создаем шрифт и выбираем его в контексте устройства
     HFONT hFont;    // 
     HFONT hFontOld; // дескриптор старого шрифта

	 hFont = CreateFont(0, 0, 0, 0, FW_NORMAL, 1, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Times New Roman")); 

     // Делаем данный шрифт текущим шрифтом
     hFontOld = (HFONT)SelectObject(hdc, hFont);

     // Указател на Меш
     LPD3DXMESH Text = NULL;
	 // Создание трехмерной сетки, представляющую строку текста
     D3DXCreateText(pDirect3DDevice, hdc, TextString, 0.001f, 0.4f, &Text, NULL, NULL);

     // Вывод текста
     Text->DrawSubset(0);

     // Восстанавление бывшего до этого шрифта
     SelectObject(hdc, hFontOld);
	 // Освобождение памяти указателя на шрифт
     DeleteObject(hFont);
	 // Освобождение памяти дискриптора контекста устройства
     DeleteDC(hdc);
     // Освобождение памяти указателя на меш
	 if (Text != NULL)
		Text->Release();
}
