// в этом файле лежат все функции WinAPI
#include <windows.h>
// подключаем заголовочный файл DirectX 9 SDK
#include <d3d9.h>
#include <d3dx9.h>
// Для работы с mesh
#include <d3dx9mesh.h>
#include <math.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
LPDIRECT3D9 pDirect3D = NULL;
LPDIRECT3DDEVICE9 pDirect3DDevice = NULL;
// Указатель на Mesh
LPD3DXMESH pMesh = NULL;
// Указател на буфер mesh-а
LPD3DXBUFFER pMeshBuffer = NULL;
// Указатель на буфер материалов
D3DMATERIAL9 *pMeshMaterial = NULL;
// Указатель на буфер текстур
LPDIRECT3DTEXTURE9 *pMeshTexture = NULL;
// Количество материала и текстур
DWORD dwNumber = 0;
DWORD OneTick = 0;
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 InitMesh(void);
// Установка матриц преобоазования - прототип
void Matrix(void);
// Рисует созданную mesh - прототип
void DrawMyMesh(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", "Загрузка Х-файла с вращением", WS_OVERLAPPEDWINDOW|
   WS_VISIBLE, 0, 0, 770, 500, NULL, NULL, hInst, NULL);
    if (!hwnd)
        return 0;
    if (SUCCEEDED(InitDirect3D(hwnd)))
    {
        if (SUCCEEDED(InitMesh()))
        {
            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_LIGHTING, false);
    // Включаем z-буфер
    pDirect3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    return S_OK;
}
// Функция рендеринга Direct3D
void RenderingDirect3D(void)
{
    if (pDirect3DDevice == NULL)
       return;
    // Очистка z-буфера
    pDirect3DDevice->Clear(0, NULL, D3DCLEAR_TARGET |  D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(172, 221, 101), 1.0f, 0);
    pDirect3DDevice->BeginScene();
 
    Matrix();
    DrawMyMesh();
    pDirect3DDevice->EndScene();
    pDirect3DDevice->Present(NULL, NULL, NULL, NULL);
}
// Функция освобождения захваченных ресурсов
void DeleteDirect3D(void)
{
    if (pDirect3DDevice != NULL)
        pDirect3DDevice->Release();
    if (pDirect3D != NULL)
        pDirect3D->Release();
    if (pMeshMaterial != NULL) 
        delete [] pMeshMaterial;
    if (pMeshTexture)
    {
        for(DWORD i = 0; i < dwNumber; i++)
        {
            if (pMeshTexture[i])
               pMeshTexture[i]->Release();
        }
        delete [] pMeshTexture;
    }
    if (pMesh != NULL)
        pMesh->Release();
}
// Функция инициализации объекта (буфера и индексов вершин)
HRESULT InitMesh(void)
{
    if (FAILED(D3DXLoadMeshFromX("Tiger.x", D3DXMESH_SYSTEMMEM, pDirect3DDevice, NULL, &pMeshBuffer, NULL, &dwNumber, &pMesh)))
	   return E_FAIL;
	// Извлекаем свойства материала и названия{имена} структуры
    D3DXMATERIAL *D3DXMeshMaterial = (D3DXMATERIAL *)pMeshBuffer->GetBufferPointer();
    pMeshMaterial = new D3DMATERIAL9[dwNumber];
    pMeshTexture   = new LPDIRECT3DTEXTURE9[dwNumber];
    for (DWORD i = 0; i < dwNumber; i++)
    {
        // Копируем материал
        pMeshMaterial[i] = D3DXMeshMaterial[i].MatD3D;
        // Установить окружающего свет
        pMeshMaterial[i].Ambient = pMeshMaterial[i].Diffuse;
        // Загружаем текстуру
        if ( FAILED(D3DXCreateTextureFromFile(pDirect3DDevice, D3DXMeshMaterial[i].pTextureFilename, &pMeshTexture[i])))
           pMeshTexture[i] = NULL;
    }
    // Уничтожаем буфер материала
    pMeshBuffer->Release();
    return S_OK;
}
// Рисует созданную mesh
void DrawMyMesh(void)
{
     for (DWORD i = 0; i < dwNumber; i++)
     {
         // Устанавливаем материал и текстуру
         pDirect3DDevice->SetMaterial(&pMeshMaterial[i]);
         pDirect3DDevice->SetTexture(0, pMeshTexture[i]);
         // Рисуем Меш
         pMesh->DrawSubset(i);
     }
}
// Установка матриц преобоазования
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, 10.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);
}