#include <fstream>
#include <stdio.h>
#include <iostream>
using namespace std;


#ifndef _MSC_VER
#define sqrtf (float)sqrt
#endif

#ifndef _MSC_VER
#define sinf (float)sin
#endif

#ifndef _MSC_VER
#define cosf (float)cos
#endif

// в этом файле лежат все функции 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;
    D3DCOLOR color;
};

#define D3DFVF_TRIANGLEVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)

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);

// Функция которая является входной точкой приложения  
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", "Вращение куба без 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;

    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);
    return S_OK;
}
// Функция рендеринга Direct3D
void RenderingDirect3D(void)
{
    if (pDirect3DDevice == NULL)
        return;

    pDirect3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(172, 221, 101), 1.0f, 0);

    pDirect3DDevice->BeginScene();

    Matrix();

    pDirect3DDevice->SetStreamSource(0, pBufferVertices, 0, sizeof(TRIANGLEVERTEX));
    pDirect3DDevice->SetIndices(pBufferIndexes);
    pDirect3DDevice->SetFVF(D3DFVF_TRIANGLEVERTEX);
    pDirect3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 36, 0, 12);
    
    Matrix1();

    pDirect3DDevice->SetStreamSource(0, pBufferVertices1, 0, sizeof(TRIANGLEVERTEX));
    pDirect3DDevice->SetIndices(pBufferIndexes1);
    pDirect3DDevice->SetFVF(D3DFVF_TRIANGLEVERTEX);
    pDirect3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 36, 0, 12);

    pDirect3DDevice->EndScene();

    pDirect3DDevice->Present(NULL, NULL, NULL, NULL);
}
// Функция освобождения захваченных ресурсов
void DeleteDirect3D(void)
{
    if (pBufferVertices != NULL)
        pBufferVertices->Release();
    if (pBufferVertices1 != NULL)
        pBufferVertices1->Release();
    if (pBufferIndexes != NULL)
        pBufferIndexes->Release();
    if (pBufferIndexes1 != NULL)
        pBufferIndexes1->Release();
    if (pDirect3DDevice != NULL)
        pDirect3DDevice->Release();
    if (pDirect3D != NULL)
        pDirect3D->Release();
}
// Функция инициализации объекта (буфера и индексов вершин)
HRESULT InitObject(void)
{
    // Создаём вершины для куба
    TRIANGLEVERTEX Vertices[] =
    {
       {2.5f, -1.9f, 1.6f, 0x000000},
       {2.5f,  3.0f, 1.6f, 0x0000ff},
       {7.5f,  3.0f, 1.6f, 0x00ff00},
       {7.5f, -1.9f, 1.6f, 0x00ffff},
       {7.5f, -1.9f, 6.6f, 0xff0000},
       {7.5f,  3.0f, 6.6f, 0xff00ff},
       {2.5f,  3.0f, 6.6f, 0xffff00},
       {2.5f, -1.9f, 6.6f, 0xffffff}
    };
    TRIANGLEVERTEX Vertices1[] =
    {
       {2.5f, -1.9f, 1.6f, 0x000000},
       {2.5f,  3.0f, 1.6f, 0x0000ff},
       {7.5f,  3.0f, 1.6f, 0x00ff00},
       {7.5f, -1.9f, 1.6f, 0x00ffff},
       {7.5f, -1.9f, 6.6f, 0xff0000},
       {7.5f,  3.0f, 6.6f, 0xff00ff},
       {2.5f,  3.0f, 6.6f, 0xffff00},
       {2.5f, -1.9f, 6.6f, 0xffffff}
    };
    // Заполняем индексный буфер
    unsigned short Indexes[36] =
    {
        0, 1, 2,
        0, 2, 3,
        4, 5, 6,
        4, 6, 7,
        3, 2, 5,
        3, 5, 4,
        7, 6, 1,
        7, 1, 0,
        1, 6, 5,
        1, 5, 2,
        3, 4, 7,
        3, 7, 0
    };
    unsigned short Indexes1[36] =
    {
        0, 1, 2,
        0, 2, 3,
        4, 5, 6,
        4, 6, 7,
        3, 2, 5,
        3, 5, 4,
        7, 6, 1,
        7, 1, 0,
        1, 6, 5,
        1, 5, 2,
        3, 4, 7,
        3, 7, 0
    };
    // Создаём буфер вершин
    if (FAILED(pDirect3DDevice->CreateVertexBuffer(8*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();
    
    // Создаём буфер вершин
    if (FAILED(pDirect3DDevice->CreateVertexBuffer(8*sizeof(TRIANGLEVERTEX), 0, D3DFVF_TRIANGLEVERTEX, D3DPOOL_DEFAULT,
        &pBufferVertices1, NULL)))
        return E_FAIL;

    void *pBV1;
    if (FAILED(pBufferVertices1->Lock(0, sizeof(Vertices1), (void**)&pBV1, 0)))
        return E_FAIL;

    memcpy(pBV1, Vertices1, sizeof(Vertices1));

    pBufferVertices1->Unlock();

    // Создаём индексный буфер
    if (FAILED(pDirect3DDevice->CreateIndexBuffer(36*sizeof(Indexes1), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT,
        &pBufferIndexes1, NULL)))
        return E_FAIL;

    void *pBI1;
    if (FAILED(pBufferIndexes1->Lock(0, sizeof(Indexes1), (void**)&pBI1, 0)))
        return E_FAIL;

    memcpy(pBI1, Indexes1, sizeof(Indexes1));

    pBufferIndexes1->Unlock();

    return S_OK;
}
// Установка матриц преобоазования
void Matrix(void)
{
    // Мировая матрица
    D3DXMATRIX MatrixWorld;
    // Матрица перемещения
    D3DXMATRIX MatrixTranslation;
    // Матрица вращения
    D3DXMATRIX MatrixRotation;
    // Матрица вида
    D3DXMATRIX MatrixView;
    //матрица проекции
    D3DXMATRIX MatrixProjection;
    float fAngle;
    float BeginAngle;
    float rps;
    UINT iTime;
    
    D3DXMatrixIdentity(&MatrixWorld);
    
    BeginAngle = D3DX_PI/4.0f;
    rps = 0.05f;
    iTime = GetTickCount();

    if(!OneTick)
      OneTick = iTime;
    fAngle = BeginAngle + (iTime -  OneTick) * (D3DX_PI * 2.0f * rps / 1000.0f);

    // Поворот
    D3DXMatrixRotationY(&MatrixRotation, fAngle);
    // Перемещаем мировую матрицу
    D3DXMatrixTranslation(&MatrixTranslation, -5.0f, -0.6f, -4.1f);
    
    D3DXMatrixMultiply(&MatrixWorld, &MatrixTranslation, &MatrixRotation);
    // Устанавливаем мировую матрицу
    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 MatrixWorld;
    // Матрица перемещения
    D3DXMATRIX MatrixTranslation;
    // Матрица вращения
    D3DXMATRIX MatrixRotation;
    // Матрица вида
    D3DXMATRIX MatrixView;
    //матрица проекции
    D3DXMATRIX MatrixProjection;
    float fAngle;
    float BeginAngle;
    float rps;
    UINT iTime;

    D3DXMatrixIdentity(&MatrixWorld);

    BeginAngle = D3DX_PI/4.0f;
    rps = 0.05f;
    iTime = GetTickCount();

    if(!OneTick)
      OneTick = iTime;
    fAngle = BeginAngle + (iTime -  OneTick) * (D3DX_PI * 2.0f * rps / 1000.0f);

    // Поворот
    D3DXMatrixRotationY(&MatrixRotation, fAngle);

	MatrixRotation._41 = 10;
	MatrixRotation._42 = 10;

    // Перемещаем мировую матрицу
//    D3DXMatrixTranslation(&MatrixTranslation, 30.0f, 20.6f, -4.1f);

//    D3DXMatrixMultiply(&MatrixWorld, &MatrixWorld, &MatrixTranslation);
    // Устанавливаем мировую матрицу
    pDirect3DDevice->SetTransform(D3DTS_WORLD, &MatrixRotation);

    // Изменяем видовую матрицу
    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);
}



