// ============================================================================
 // Filename: DOT3DINPUT.cpp
 // Description: DirectInput Mouse and Dot3 Bumpmapping. This code corresponds
 //              to DirectX Techniques Series 1 tutorial 5.
 //
 // Created by wizard version 0.1 Beta.
 //
 // Found a bug in this code framework? Need some help? Come to www.32bits.co.uk
 // for support, feedback and the best DirectX dev community on the net!
 // ============================================================================

 #define WIN32_LEAN_AND_MEAN

 #include <windows.h>
 #include <d3d8.h>
 #include <d3dx8.h>
 #include <dxerr8.h>

 // Include helper functions.
 #include "D3DFuncs.h"
 #include "DIMouse.h"


 // Function declarations
 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
 LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );

 HRESULT AppInit();
 HRESULT AppLoop();
 HRESULT AppShutDown();
 HRESULT Render();


 // Globals required for all apps
 static char szAppname[]="[32Bits.co.uk] DInput Mouse & Dot3 Bumpmap";
 LPDIRECT3D8 g_pD3D;
 LPDIRECT3DDEVICE8 g_pDevice;
 HWND g_hWnd;

 D3DCURRENTSETTINGS g_D3DSettings;

 // Globals specifically for this app

 LPDIRECT3DVERTEXBUFFER8 g_pVertexBuffer;
 LPDIRECT3DTEXTURE8      g_pBumpMapTexture;
 LPDIRECT3DTEXTURE8      g_pNormalMapTexture;

 CDIMouse*               g_pDIMouse;


 // Change our normal vertex struct to include members for texturemapping...
 typedef struct _tagSimpleTexMapVertex
 {
     float       x,y,z;
     D3DCOLOR    dwDiffuse;
     float       tu,tv;
 } SIMPLETEXMAPVERTEX;

 // ...and update our FVF define to match.
 #define FVF_SIMPLETEXMAPVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)


 // =====================================================================================
 //                      Application code begins here
 // =====================================================================================


 // =====================================================================================
 // Function Name: WinMain
 // Purpose: Application entry point
 // =====================================================================================

 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
 {
     WNDCLASSEX wc;

     ZeroMemory(&wc, sizeof(WNDCLASSEX));
     wc.cbSize=sizeof(WNDCLASSEX);                       // size of the window struct in bytes
     wc.style=CS_HREDRAW | CS_VREDRAW | CS_OWNDC;        // window styles to use
     wc.lpfnWndProc=MsgProc;                             // function name of event handler
     wc.hInstance=hInstance;                             // handle to this apps instance
     wc.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);// background colour of window
     wc.hIcon= LoadIcon(NULL, IDI_APPLICATION);          // icon for the app window
     wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);         // icon when minimized to taskbar
     wc.hCursor=LoadCursor(NULL, IDC_ARROW);             // cursor to use for this window
     wc.lpszClassName=szAppname;                         // name for this class

     // Register the window class
     RegisterClassEx( &wc );

     g_D3DSettings.m_nDeviceWidth=800;
     g_D3DSettings.m_nDeviceHeight=600;
     g_D3DSettings.m_fScreenAspect=(float)g_D3DSettings.m_nDeviceWidth / (float)g_D3DSettings.m_nDeviceHeight;

     // Create the application's window
     g_hWnd = CreateWindow(szAppname, szAppname, WS_OVERLAPPEDWINDOW, 10, 10,
                           g_D3DSettings.m_nDeviceWidth, g_D3DSettings.m_nDeviceHeight,
                           NULL, NULL, wc.hInstance, NULL );

     // Show the window
     ShowWindow(g_hWnd, nCmdShow);
     UpdateWindow(g_hWnd);

     if(FAILED(AppInit()))
     {
         UnregisterClass(szAppname, wc.hInstance);
         return -1;
     }

     // Enter the message loop
     MSG msg;
     ZeroMemory(&msg, sizeof(msg));
     int count=0;
     while(msg.message != WM_QUIT)
     {
         if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
         {
             TranslateMessage(&msg);
             DispatchMessage(&msg);
         }
         else
         {
             AppLoop();
         }
     }

     AppShutDown();
     UnregisterClass(szAppname, wc.hInstance);
     return 0;
 }


 // =====================================================================================
 // Function Name: MsgProc
 // Purpose: Message handler for main app window
 // =====================================================================================

 LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
 {
     switch( msg )
     {
     case WM_KEYDOWN:
         {
             switch(wParam)
             {

             // Exit app on space or escape keypress
             case VK_SPACE:
             case VK_ESCAPE:
                 {
                     PostQuitMessage(0);
                     return 0;
                 }
             }

             return DefWindowProc(hWnd, msg, wParam, lParam);
         }

     case WM_DESTROY:
         {
             PostQuitMessage(0);
             return 0;
         }

     default:
         return DefWindowProc(hWnd, msg, wParam, lParam);
     }

 }


 // =====================================================================================
 // Function Name: AppInit
 // Purpose: Initialisation for D3D and the app's global objects
 // =====================================================================================

 HRESULT AppInit()
 {
     HRESULT rslt=0;

     g_pD3D=Direct3DCreate8(D3D_SDK_VERSION);
     if(g_pD3D==NULL)
     {
         return D3DError(E_FAIL, __LINE__, __FILE__, "Failed to create a D3D8 object.");
     }


     // Populate our struct with how we want to set up D3D...
     g_D3DSettings.m_bWindowed=TRUE;
     g_D3DSettings.m_bMultiSampling=FALSE;
     g_D3DSettings.m_D3DFormat=D3DFMT_X8R8G8B8;

     // ...and pass it to our function to create the device!
     rslt=InitDirect3DDevice(g_hWnd, g_D3DSettings, g_pD3D, &g_pDevice);
     if(FAILED(rslt))
     {
         return E_FAIL;
     }



     // ===================================================================================
     // Set up our Projection, View and World transformations
     // ===================================================================================

     // Create a matrix to store our Projection transform. Null all the fields.
     D3DXMATRIX matProjection;
     ZeroMemory(&matProjection, sizeof(matProjection));

     // Use D3DX to create a left handed cartesian Field Of View transform
     D3DXMatrixPerspectiveFovLH(&matProjection, D3DX_PI/4, g_D3DSettings.m_fScreenAspect,
                                1.0f, 100.0f);

     // Tell D3D to use our Projection matrix for the projection transformation stage
     rslt=g_pDevice->SetTransform(D3DTS_PROJECTION, &matProjection);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set Projection Transform."); }



     // Create a matrix to store our View transform. Null all the fields.
     D3DXMATRIX matView;
     ZeroMemory(&matView, sizeof(matView));

     // Use D3DX to create a Look At matrix from eye, lookat and up vectors.
     D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(0.0f, 0.0f, -5.0f),
                                  &D3DXVECTOR3(0.0f, 0.0f, 0.0f),
                                  &D3DXVECTOR3(0.0f, 1.0f,  0.0f));

     // Tell D3D to use our View matrix for the view transformation stage
     rslt=g_pDevice->SetTransform(D3DTS_VIEW, &matView);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set View Transform."); }



     // Create a matrix to store our World transform
     D3DXMATRIX matWorld;
     // Set the matrix to an identity matrix (one that makes no change)
     D3DXMatrixIdentity(&matWorld);

     // Tell D3D to use our World matrix for the world transformation stage
     rslt=g_pDevice->SetTransform(D3DTS_WORLD, &matWorld);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to set World Transform."); }



     // ===================================================================================
     // Set up our scene states
     // ===================================================================================

     // Set our culling & lighting renderstates
     g_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
     g_pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);


     // ===================================================================================
     // Create the objects for this app
     // ===================================================================================

     // Create a quad for texturemapping the bumpmap onto, and assign each vertex a texcoord:

     SIMPLETEXMAPVERTEX Quad[4];
     Quad[0].x=-1.0f; Quad[0].y=-1.0f; Quad[0].z=0.0f;
     Quad[0].tu=0.0f;
     Quad[0].tv=1.0f;
     Quad[0].dwDiffuse=D3DCOLOR_XRGB(255,255,255);

     Quad[1].x=-1.0f; Quad[1].y=1.0f; Quad[1].z=0.0f;
     Quad[1].tu=0.0f;
     Quad[1].tv=0.0f;
     Quad[1].dwDiffuse=D3DCOLOR_XRGB(255,255,255);

     Quad[2].x=1.0f; Quad[2].y=-1.0f; Quad[2].z=0.0f;
     Quad[2].tu=1.0f;
     Quad[2].tv=1.0f;
     Quad[2].dwDiffuse=D3DCOLOR_XRGB(255,255,255);

     Quad[3].x=1.0f; Quad[3].y=1.0f; Quad[3].z=0.0f;
     Quad[3].tu=1.0f;
     Quad[3].tv=0.0f;
     Quad[3].dwDiffuse=D3DCOLOR_XRGB(255,255,255);



     // Next, create our vertex buffer using the properties of our custom struct and FVF
     rslt=g_pDevice->CreateVertexBuffer(sizeof(Quad), 0, FVF_SIMPLETEXMAPVERTEX, D3DPOOL_DEFAULT,
                                        &g_pVertexBuffer);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "CreateVertexBuffer() failed."); }

     // Now lock the vertex buffer...
     BYTE* pVerticeLock=0;
     rslt=g_pVertexBuffer->Lock(0, sizeof(Quad), &pVerticeLock, 0);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Failed to lock Vertex Buffer."); }

     // ...and copy our array of vertices straight into it
     CopyMemory(pVerticeLock, &Quad, sizeof(Quad));

     // Remember to unlock the vertex buffer once we're done.
     g_pVertexBuffer->Unlock();


     // ===================================================================================
     // Create our texture surface for texturemapping
     // ===================================================================================

     // Create the texture surface from the file
     rslt=D3DXCreateTextureFromFileEx(g_pDevice, "bumpmap.png", 256, 256,
                                      1, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,
                                      D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL,
                                      &g_pBumpMapTexture);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not create texture."); }


     // Create a brand new texture to receive our normal map
     rslt=D3DXCreateTexture(g_pDevice, 256, 256, 1, D3DX_DEFAULT, D3DFMT_A8R8G8B8,
                            D3DPOOL_DEFAULT, &g_pNormalMapTexture);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not create texture 1"); }


     // Calculate the normal map from the source png image, and write it to the normal map texture
     rslt = D3DXComputeNormalMap(g_pNormalMapTexture, g_pBumpMapTexture, NULL, 0, D3DX_CHANNEL_RED, 1.0f);
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not create normal map texture"); }


     // ===================================================================================
     // Initialise our DirectInput mouse
     // ===================================================================================

     // Create the mouse on the heap, we might want to kill it later.
     g_pDIMouse = new CDIMouse;
     g_pDIMouse->Initialise(g_hWnd, g_D3DSettings.m_nDeviceWidth, g_D3DSettings.m_nDeviceHeight);


     return S_OK;
 }

 // =====================================================================================
 // Function Name: AppLoop
 // Purpose: Controls the flow of the app, called by the messageloop.
 // =====================================================================================

 HRESULT AppLoop()
 {
     // Update the mouse position
     g_pDIMouse->Update();

     return Render();
 }

 // =====================================================================================
 // Function Name: AppShutDown
 // Purpose: Called when the app exits. Releases all global COM interfaces.
 // =====================================================================================

 HRESULT AppShutDown()
 {
     // Don't forget to release the vertex buffer - it's a COM interface!
     if(g_pVertexBuffer)
         g_pVertexBuffer->Release();

     if(g_pBumpMapTexture)
         g_pBumpMapTexture->Release();
     if(g_pNormalMapTexture)
         g_pNormalMapTexture->Release();

     // Mouse destructor handles the DirectInput deallocation
     delete g_pDIMouse;

     if(g_pDevice)
         g_pDevice->Release();
     if(g_pD3D)
         g_pD3D->Release();

     return S_OK;
 }


 // =====================================================================================
 // Function Name: Render()
 // Purpose: Main rendering function to perform D3D drawing
 // =====================================================================================

 HRESULT Render()
 {
     HRESULT rslt=NULL;

     // ====================================================================================
     // - Do all the usual checks to make sure we have the right pointers, etc...
     // ====================================================================================

     // Make sure we have a valid D3D Device
     if(!g_pDevice) { return E_FAIL; }

     // Return if the device is not ready
     rslt=ValidateDevice(g_pDevice, g_D3DSettings);

     // Clear the back buffer
     if(g_pDIMouse->IsMouseButtonDown(DIMOUSE_LEFTBUTTON))
         g_pDevice->Clear(0,0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,100,55), 1.0f, 0);
     else
         g_pDevice->Clear(0,0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,55), 1.0f, 0);

     rslt=g_pDevice->BeginScene();
     if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "BeginScene() failed."); }

     // ====================================================================================
     // - Do our drawing operations
     // ====================================================================================


     // Tell D3D we want to use our vertex buffer as the "source stream"
     g_pDevice->SetStreamSource(0, g_pVertexBuffer, sizeof(SIMPLETEXMAPVERTEX));

     // Tell D3D we want to use our custom FVF define
     g_pDevice->SetVertexShader(FVF_SIMPLETEXMAPVERTEX);

     // We are going to use the mouse cursor position as the light source for the bump
     // mapping. In other words, we need to create a vector from the cursor to the origin
     D3DXVECTOR3 vLight;
     POINT kPoint;
     g_pDIMouse->GetAbsolutePos(&kPoint);

     // Create a vector from the cursor position (no Z axis for this, the mouse is in 2D)
     vLight.x = (((float)kPoint.x / (float)g_D3DSettings.m_nDeviceWidth) - 1);
     vLight.y = (((float)kPoint.y / (float)g_D3DSettings.m_nDeviceHeight) - 1);
     vLight.z = 1.0f;

     // Normalize the vector. See the tutorial for more info! Normalization reduces the
     // magnitude of a vector to 1, but keeps the original direction. This makes it a
     // unit vector. Also see the Cheaters Guide to 3D Maths! 
     D3DXVec3Normalize( &vLight, &vLight );

     // Now that we have our light vector, we need to turn it back into a greyscale
     // RGB to use to modulate the texture with. Remember that we've normalized this
     // vector, so .x and .y will be between 0.0 and 1.0f (which is why we need to
     // * 127.0f and + 128.0f). Calculate it yourself, if vLight.x was 0.5f.
     DWORD r = (DWORD)(127.0f * vLight.x + 128.0f);
     DWORD g = (DWORD)(127.0f * vLight.y + 128.0f);
     DWORD b = (DWORD)(127.0f * vLight.z + 128.0f);

     DWORD dwFactor = D3DCOLOR_XRGB(r, g, b);

     // Set the texture factor to the rgb calculated above. For more info on the texture
     // factor and how it works with the D3DTA_TFACTOR state, refer to DirectX Basics
     // Series 3 tutorial 4.
     g_pDevice->SetRenderState(D3DRS_TEXTUREFACTOR, dwFactor);

     // Modulate the texture (the normal map) with the light vector (stored
     // above in the texture factor). From the SDK: "Modulate the components of each argument
     // as signed components, add their products; then replicate the sum to all color
     // channels, including alpha".
     g_pDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
     g_pDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_DOTPRODUCT3 );
     g_pDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_TFACTOR );

     // Set the texture to be the normal map
     g_pDevice->SetTexture( 0, g_pNormalMapTexture );

     // And finally tell D3D to draw our bumpmapped quad!
     g_pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

     // If all that looked too simple to do bumpmapping, don't worry - you're right. Most
     // of the "magic" takes place in the D3DTOP_DOTPRODUCT3 texture stage, which does
     // all the per pixel calculations for us.

     // ====================================================================================
     // - Render the mouse last so that Z-ordering keeps the cursor on top
     // ====================================================================================

     g_pDIMouse->Render(g_pDevice);

     // ====================================================================================
     // - Clean up and present the back buffer to be page flipped
     // ====================================================================================

     g_pDevice->EndScene();

     // Present the back buffer to the display adapter to be drawn
     g_pDevice->Present(NULL, NULL, NULL, NULL);


     return S_OK;
 }