 /* 'mrScreen.cpp' */

 /* Komplementarny plik nagłówka */
#include "mrScreen.h"

 /* Obiekt singleton */
mrScreen * mrScreen::m_pkSingleton = NULL;

 /* Konstruktor domyślny */
mrScreen::mrScreen (void)
{
 m_pkD3D       = NULL;
 m_pkD3DDevice = NULL;
 m_hWindow     = NULL;
 m_iFPS        = 0;

 assert (!m_pkSingleton);
 m_pkSingleton = this;
}

 /* Konstruktor przyporządkowujący */
mrScreen::mrScreen (HWND hWindow)
{
 m_pkD3D       = NULL;
 m_pkD3DDevice = NULL;
 m_iFPS        = 0;
 m_hWindow     = hWindow;

 assert (!m_pkSingleton);
 m_pkSingleton = this;

 Init (hWindow);
}

 /* Destruktor domyślny */
mrScreen::~mrScreen (void)
{
 if (NULL != m_pkD3DDevice)
 {
  m_pkD3DDevice->Release ();
  m_pkD3DDevice = NULL;
 }
 if (NULL != m_pkD3D)
 {
  m_pkD3D->Release ();
  m_pkD3D = NULL;
 }
 m_iFPS        = 0;

 assert (m_pkSingleton);
 m_pkSingleton = NULL;
}

 /* Inicjalizacja Direct3D */
mrError32 mrScreen::Init (HWND hWindow)
{
  /* Utwórz obiekt Direct3D */
 m_pkD3D = Direct3DCreate8 (D3D_SDK_VERSION);
 m_hWindow     = hWindow;

 if (NULL == m_pkD3D)
 {
  return mrErrorInitDirect3D;
 }
 return mrNoError;
}

 /* Ustawia tryb ekranu / tworzy urządzenie Direct3D */
mrError32 mrScreen::SetMode (mrUInt32 iFullscreen, mrUInt16 iWidth, 
                             mrUInt16 iHeight, mrUInt16 iDepth, 
                             bool bHardware)
{
 D3DPRESENT_PARAMETERS kPresentParams; 
 mrUInt32              iDeviceType;

  /* Ustawia typ urządzenia, jakie ma być utworzone (sprzętowe czy programowe) */
 if (!bHardware)
 {
  iDeviceType = D3DDEVTYPE_REF;
 }
 else
 {
  iDeviceType = D3DDEVTYPE_HAL;
 }

  /* Wyzeruj parametry prezentacji i ustaw efekt zamiany */
 ZeroMemory (&kPresentParams, sizeof (D3DPRESENT_PARAMETERS) );
 kPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
 
  /* Jeżeli tryb niepełnoekranowy */
 if (!iFullscreen)
 {
  D3DDISPLAYMODE kCurrentMode;

   /* Pobierz informację o bieżącym trybie */
  if (FAILED (m_pkD3D->GetAdapterDisplayMode (D3DADAPTER_DEFAULT,
              &kCurrentMode) ))
  {
   return mrErrorGetAdapterDisplayMode;
  }

   /* Ustaw tryb niepełnoekranowy i format bufora tylnego kompatybilny
      z formatem bieżącym */
  kPresentParams.Windowed         = true;
  kPresentParams.BackBufferCount  = 1;
  kPresentParams.BackBufferFormat = kCurrentMode.Format;

   /* Spróbuj utworzyć urządzenie */
  if (FAILED (m_pkD3D->CreateDevice (D3DADAPTER_DEFAULT, 
                                (D3DDEVTYPE) iDeviceType, 
                                m_hWindow,
                                D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                &kPresentParams, &m_pkD3DDevice ) ))
  {
   return mrErrorCreateDevice;
  }
 }
 else
 {
   /* Ustaw tryb pełnoekranowy i informacje dotyczące ekranu w tym trybie */
  kPresentParams.Windowed         = false;
  kPresentParams.BackBufferCount  = 1;
  kPresentParams.BackBufferWidth  = iWidth;
  kPresentParams.BackBufferHeight = iHeight;

  kPresentParams.FullScreen_RefreshRateInHz = 
                                 D3DPRESENT_RATE_DEFAULT;
  kPresentParams.FullScreen_PresentationInterval = 
                                 D3DPRESENT_INTERVAL_DEFAULT;

   /* Jeżeli tryb 16-bitowy, spróbuj utworzyć urządzenie przy pomocy różnych 16-bitowych formatów
      kolorów pikseli */
  if (iDepth == 16)
  {
   kPresentParams.BackBufferFormat = D3DFMT_R5G6B5;
   if (FAILED (m_pkD3D->CreateDevice (D3DADAPTER_DEFAULT, 
                                   (D3DDEVTYPE) iDeviceType, 
                                   m_hWindow,
                                   D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                   &kPresentParams, &m_pkD3DDevice ) ))
   {
    kPresentParams.BackBufferFormat = D3DFMT_X1R5G5B5;
    if (FAILED (m_pkD3D->CreateDevice (D3DADAPTER_DEFAULT, 
                                    (D3DDEVTYPE) iDeviceType, 
                                    m_hWindow,
                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                    &kPresentParams, &m_pkD3DDevice ) ))
    {
     kPresentParams.BackBufferFormat = D3DFMT_A1R5G5B5;
     if (FAILED (m_pkD3D->CreateDevice (D3DADAPTER_DEFAULT, 
                                     (D3DDEVTYPE) iDeviceType, 
                                     m_hWindow,
                                     D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                     &kPresentParams, &m_pkD3DDevice ) ))
     {
      return mrErrorCreateDevice;
     }
    }
   }
  }
   /* Jeżeli tryb 32-bitowy, spróbuj utworzyć urządzenie przy pomocy różnych 32-bitowych formatów
      kolorów pikseli */
  else
  {
   kPresentParams.BackBufferFormat = D3DFMT_A8R8G8B8;
   if (FAILED (m_pkD3D->CreateDevice (D3DADAPTER_DEFAULT, 
                                   (D3DDEVTYPE) iDeviceType, 
                                   m_hWindow,
                                   D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                   &kPresentParams, &m_pkD3DDevice ) ))
   {
    kPresentParams.BackBufferFormat = D3DFMT_X8R8G8B8;
    if (FAILED (m_pkD3D->CreateDevice (D3DADAPTER_DEFAULT, 
                                    (D3DDEVTYPE) iDeviceType, 
                                    m_hWindow,
                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                    &kPresentParams, &m_pkD3DDevice ) ))
    {
     return mrErrorCreateDevice;
    }
   }
  }
  
 }
 m_iFormat = kPresentParams.BackBufferFormat;

  /* Ustaw stany renderowania */
 m_pkD3DDevice->SetRenderState (D3DRS_CULLMODE, D3DCULL_NONE);
 m_pkD3DDevice->SetRenderState (D3DRS_LIGHTING, FALSE);
 m_pkD3DDevice->SetRenderState (D3DRS_ALPHABLENDENABLE,   TRUE);
 m_pkD3DDevice->SetRenderState (D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
 m_pkD3DDevice->SetRenderState (D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

  /* Ustaw stany kolorów tesktury */
 m_pkD3DDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE);
 m_pkD3DDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);

 return mrNoError;
}

 /* Wyzeruj okno na określony kolor */
mrError32 mrScreen::Clear (mrUInt8 iRed, mrUInt8 iGreen, mrUInt8 iBlue, 
                           mrUInt8 iAlpha)
{
 mrUInt32 iColor;
 iColor = D3DCOLOR_RGBA (iRed, iGreen, iBlue, iAlpha);

  /* Wyzeruj okno na określony kolor */
 if (FAILED (m_pkD3DDevice->Clear (0, NULL, D3DCLEAR_TARGET, iColor, 
             0, 0) ))
 {
  return mrErrorClear;
 }
 return mrNoError;
}

 /* Rozpocznij renderowanie */
mrError32 mrScreen::StartFrame (void)
{
 m_kTimer.Update ();

  /* Rozpocznij renderowanie */
 if (FAILED (m_pkD3DDevice->BeginScene () ))
 {
  return mrErrorBeginScene;
 }
 return mrNoError;
}

 /* Zakończ renderowanie */
mrError32 mrScreen::EndFrame (void)
{
  /* Zakończ renderowanie */
 if (FAILED (m_pkD3DDevice->EndScene () ))
 {
  return mrErrorEndScene;
 }

  /* Wyświetl dane na ekranie */
 if (FAILED (m_pkD3DDevice->Present (NULL, NULL, NULL, NULL) ))
 {
  return mrErrorPresent;
 }

  /* Oblicz ilość klatek na sekundę */
 m_kTimer.Update ();
 m_iFPS = (mrUInt32) (1 / m_kTimer.GetDelta ());

 return mrNoError;
}


mrError32 mrScreen::DrawLine (mrReal32 fX1, mrReal32 fY1, 
                         mrReal32 fX2, mrReal32 fY2,
                         mrUInt8 iRed, mrUInt8 iGreen, 
                         mrUInt8 iBlue, mrUInt8 iAlpha)
{
 mrUInt32 iColor;
 iColor = D3DCOLOR_RGBA (iRed, iGreen, iBlue, iAlpha);

  /* Utwórz wierchołki prostokąta */
 mrVertex kVertices [] =
 {  /* x, y, z, w, kolor, współrzędne tekstury (u,v) */
  {fX1, fY1, 0, 1.0f, iColor, 0, 0},
  {fX2, fY2, 0, 1.0f, iColor, 0, 0},
 };
 
  /* Narysuj linię */
 mrScreen::GetSingleton ()->GetDevice ()->SetVertexShader (
                                      D3DFVF_MIRUSVERTEX);
 if (FAILED (mrScreen::GetSingleton ()->GetDevice ()->DrawPrimitiveUP (
                                      D3DPT_LINELIST, 2, kVertices, 
                                      sizeof (mrVertex))) )
 {
  return mrErrorDrawPrimitive;
 }

 return mrNoError;
}

mrError32 mrScreen::DrawRectangle (mrReal32 fX1, mrReal32 fY1,
                                   mrReal32 fX2, mrReal32 fY2, 
                                   mrUInt8 iRed, mrUInt8 iGreen, 
                                   mrUInt8 iBlue, mrUInt8 iAlpha)
{
 mrUInt32 iColor;
 iColor = D3DCOLOR_RGBA (iRed, iGreen, iBlue, iAlpha);
 
  /* Tworzenie wierchołków prostokąta */
 mrVertex kVertices [] =
 {  /* x, y, z, w, kolor, współrzędne tekstury (u,v) */
  {fX1, fY1, 0, 1.0f, iColor, 0, 0},
  {fX2, fY1, 0, 1.0f, iColor, 0, 0},
  {fX2, fY2, 0, 1.0f, iColor, 0, 0},
  {fX1, fY2, 0, 1.0f, iColor, 0, 0},
  {fX1, fY1, 0, 1.0f, iColor, 0, 0},
 };
 
  /* Narysuj linię */
 mrScreen::GetSingleton ()->GetDevice ()->SetVertexShader (
                                      D3DFVF_MIRUSVERTEX);
 if (FAILED (mrScreen::GetSingleton ()->GetDevice ()->DrawPrimitiveUP (
                                      D3DPT_LINESTRIP, 4, kVertices, 
                                      sizeof (mrVertex))) )
 {
  return mrErrorDrawPrimitive;
 }

 return mrNoError;
}

mrError32 mrScreen::DrawCircle (mrReal32 iCenterX, mrReal32 iCenterY, 
                                mrReal32 iRadius, mrUInt8 iRed, 
                                mrUInt8 iGreen, mrUInt8 iBlue, 
                                mrUInt8 iAlpha, mrUInt32 iVertices)
{
 mrUInt32 iColor;
 iColor = D3DCOLOR_RGBA (iRed, iGreen, iBlue, iAlpha);

 mrVertex * pkVertices;
  /* Zarezerwuj pamięć dla nowych wierchołków */
 pkVertices = new mrVertex [iVertices + 1];

 mrReal32 fAngle = 0;
 mrReal32 fComplete;

 mrUInt32 iVertex;

  /* Oblicz pozycję każdego wierchołka */
 for (iVertex = 0; iVertex < iVertices; iVertex ++)
 {
   /* Procent okręgu, który został już narysowany */
  fComplete = (mrReal32)iVertex / (mrReal32)iVertices;
  pkVertices [iVertex].m_fX = (mrReal32) ((mrReal32)iCenterX + 
                ((mrReal32)iRadius * cos (6.2831f*fComplete)));
  pkVertices [iVertex].m_fY = (mrReal32) ((mrReal32)iCenterY + 
                ((mrReal32)iRadius * sin (6.2831f*fComplete)));

  pkVertices [iVertex].m_fZ     = 0;
  pkVertices [iVertex].m_fRHW   = 1.0f;
  pkVertices [iVertex].m_iColor = iColor;
  pkVertices [iVertex].m_ftU    = 0;
  pkVertices [iVertex].m_ftV    = 0;
 }

  /* Domknij okrąg */
 pkVertices [iVertex].m_fX = pkVertices [0].m_fX;
 pkVertices [iVertex].m_fY = pkVertices [0].m_fY;

 pkVertices [iVertex].m_fZ     = 0;
 pkVertices [iVertex].m_fRHW   = 1.0f;
 pkVertices [iVertex].m_iColor = iColor;
 pkVertices [iVertex].m_ftU    = 0;
 pkVertices [iVertex].m_ftV    = 0;

  /* Narysuj okrąg */
 mrScreen::GetSingleton ()->GetDevice ()->SetVertexShader (
                                      D3DFVF_MIRUSVERTEX);
 if (FAILED (mrScreen::GetSingleton ()->GetDevice ()->DrawPrimitiveUP (
                                      D3DPT_LINESTRIP, iVertices,
                                      pkVertices, sizeof (mrVertex))) )
 {
  return mrErrorDrawPrimitive;
 }
 delete [] pkVertices;
 return mrNoError;
}

 /* Sprawdź, czy tryb jest obsługiwany */
mrBool32 mrScreen::IsModeSupported (mrUInt16 iWidth, mrUInt16 iHeight,
                                    mrUInt16 iDepth)
{
 mrUInt32       iNumberOfModes;
 mrUInt32       iMode;
 D3DDISPLAYMODE kMode;

  /* Pobierz liczbę dostępnych trybów */
 iNumberOfModes = m_pkD3D->GetAdapterModeCount (D3DADAPTER_DEFAULT);

  /* Dla każdego trybu sprawdź czy tryb jest równy */
 for (iMode = 0; iMode < iNumberOfModes; iMode ++)
 {
   /* Pobierz informacje o trybie */
  m_pkD3D->EnumAdapterModes (D3DADAPTER_DEFAULT, iMode, &kMode);

   /* Porównaj rozmiary ekranu */
  if ((iWidth == kMode.Width) && (iHeight == kMode.Height))
  {
    /* Porównaj głębie kolorów */
   if (iDepth == 16)
   {
    if ((kMode.Format == D3DFMT_R5G6B5) || 
        (kMode.Format == D3DFMT_X1R5G5B5) || 
        (kMode.Format == D3DFMT_A1R5G5B5) )
    {
     return mrTrue;
    }
   }
   else
   {
    if ((kMode.Format == D3DFMT_A8R8G8B8) || 
        (kMode.Format == D3DFMT_X8R8G8B8))
    {
     return mrTrue;
    }  
   }
  }
 }

 return mrFalse;
}

 /* Ukazuje lub ukrywa kursor */
void mrScreen::ShowCursor (mrUInt32 iShowCursor)
{
 m_pkD3DDevice->ShowCursor (iShowCursor);
}

 /* Zwraca urządzenie Direct3D */
LPDIRECT3DDEVICE8 mrScreen::GetDevice (void)
{
 return m_pkD3DDevice;
}

 /* Zwraca liczbę klatek na sekundę */
mrUInt32 mrScreen::GetFPS (void)
{
 return m_iFPS;
}

 /* Zwraca format bufora tylnego */
mrUInt32 mrScreen::GetFormat (void)
{
 return m_iFormat;
}

 /* Zwraca głębię kolorów bufora tylnego */
mrUInt32 mrScreen::GetBitdepth (void)
{
 mrUInt32 iBitdepth;

 switch (m_iFormat)
 {
 case D3DFMT_R5G6B5:
 case D3DFMT_X1R5G5B5:
 case D3DFMT_A1R5G5B5:
  iBitdepth = 16;
  break;
 case D3DFMT_A8R8G8B8:
 case D3DFMT_X8R8G8B8:
  iBitdepth = 32;
  break;
 }

 return iBitdepth;
}

 /* Zwraca singleton mrScreen */
mrScreen * mrScreen::GetSingleton (void)
{
 assert (m_pkSingleton);
 return m_pkSingleton;
}