{***************************************************************
  Delphi DirectX 8.1
  (c) Mega Informatic 2006-2007

  Расширение возможностей стандартных классов и типов
  необходимых в разработке игры

  unit D3DGUI.pas -
    Программирование GUI - Graphic User Interface

    

  Обновления смотри на сайтах -

  http://www.megainformatic.boom.ru
  http://www.megainformaticsite.pochta.ru

  14/12/2007 - добавление альтернативного вывода 2D-изображений - TD3DSprite
  31/07/2007, 15/10/2007 - создание класса TD3DImage2D
****************************************************************}
unit D3DGUI;

interface

uses Windows, SysUtils, Direct3D8, D3DX8, DXUtil, D3DUtil, DXErr8;

const
  Unitname = 'D3DGUI.pas';

type
  TTexFilename = array[0..MAX_PATH] of Char;

{  =========================================================

   TD3DSprite

   альтернативный класс для вывода 2D-изображений в 3D-сцене

   ==========================================================

   Правила использования аналогичны TD3DImage2D

   отличия:

   - в методе инициализации приложения InitDeviceObjects
      SomeSprite := TD3DSprite.Create(m_d3dfmtTexture);
      if SomeSprite = nil then Exit;
      result := SomeSprite.InitDeviceObjects(m_pd3dDevice, SplashTex2);
      SomeSprite.Width :=  64; //размеры спрайта в текстуре
      SomeSprite.Height := 64;
      SomeSprite.Position.x := 0.2;

   - в методе RestoreDeviceObjects приложения вызвать метод
     SomeSprite.RestoreDeviceObjects(Camera);
     //указав ссылку на камеру используемую в приложении!!!

   - в методе отрисовки Render приложения
      SomeSprite.FramesPerLine := 7; //количество спрайтов в строке
      SomeSprite.Delay := 15;  //временная задержка между кадрами
      SomeSprite.fMaxFrame := 18; //максимальный кадр 
      SomeSprite.TextureOffsetX := 0; //смещение до начала спрайта в текстуре
      SomeSprite.TextureOffsetY := 270;

      SomeSprite.fSize := 0.05; //масштаб спрайта (квадратные размеры)
      SomeSprite.Draw(SomeSprite.Position.x, -0.2, 0.9,
        DrawD3DSprite_Filtered, Alpha);
      //для вывода прямоугольного объекта использовать DrawScaled  

   - в методе FrameMove приложения можно осуществить анимацию и движение
     fTimeElapsed := DXUtil_Timer( TIMER_GETELAPSEDTIME );
     SomeSprite.FrameMove(fTimeElapsed);
     SomeSprite.Position.x := SomeSprite.Position.x - 0.05*fTimeElapsed;

   - методы Draw и DrawScaled производят вывод в относительных координатах
       Viewport.  
}

const
  D3DFVF_D3DSprite = (D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_TEX1);

  // Draw Flags
  DrawD3DSprite_Filtered = $0001;

type
  TD3DSpriteVertex = packed record
    p: TD3DXVector3;
    color: DWORD;
    tu, tv: Single;
  end;

  PD3DSpriteVertexArray = ^TD3DSpriteVertexArray;
  TD3DSpriteVertexArray = array[0..0] of TD3DSpriteVertex;

  TD3DSprite = class
    m_pd3dDevice: IDirect3DDevice8;       // A D3DDevice used for rendering
    m_Camera: CD3DCamera;                 // Камера для показа 2D-спрайта
    m_pTexture: IDirect3DTexture8;        // The d3d texture for this sprite
    m_d3dfmtTexture: D3DFORMAT;
    m_pVB: IDirect3DVertexBuffer8;        // VertexBuffer for rendering sprite
    m_dwTexWidth: DWORD;                  // Texture dimensions
    m_dwTexHeight: DWORD;
    m_fScale: Single;
    m_fTexCoords: array[0..3] of Single;

    m_bZEnable: Boolean;

    // Stateblocks for setting and restoring render states
    m_dwSavedStateBlock: DWORD;
    m_dwDrawStateBlock: DWORD;

    Position: TD3DXVector3;
    Color: Cardinal;
    fSize: Single;
    Width, Height: Integer;
    TextureOffsetX, TextureOffsetY: Integer;
    Frame: Single;
    FramesPerLine: Integer;
    Delay: Single;
    fMaxFrame: Single;

  private
    FColorKey: Cardinal;
    FTexFilename: TTexFilename;

    procedure SetColorKey(Color: Cardinal);
  protected
    function LoadTextureFromFile(filename: TFileName): HResult;

  public

    constructor Create(TextureFormat: TD3DFormat = D3DFMT_A8R8G8B8);
    destructor Destroy; override;    

    function Draw(x, y, z: Single; dwFlags: Cardinal; Alpha: Byte): HResult;
    function DrawScaled(x, y, z: Single; fXScale, fYScale: Single;
      Flags: Cardinal; Alpha: Byte): HResult;

    function FrameMove(fTimeLapsed: Single): HRESULT;  

    // Initializing and destroying device-dependent objects
    function InitDeviceObjects(pd3dDevice: IDirect3DDevice8;
      TextureFilename: TFilename): HRESULT;
    function RestoreDeviceObjects(pCamera: CD3DCamera): HRESULT;
    function InvalidateDeviceObjects: HRESULT;
    function DeleteDeviceObjects: HRESULT;

    property ColorKey: Cardinal read FColorKey write SetColorKey;
    property TexFilename: TTexFilename read FTexFilename;
  end;

{  =========================================================

   TD3DImage2D

   класс для вывода 2D-изображений в 3D-сцене
   ==========================================================

   Правила использования

   - в методе создания приложения создать экземпляр класса
     Some2DImage := TD3DImage2D.Create(TextureFormat); // TextureFormat = формат
                                                       // изображения, например
                                                       // D3DFMT_A8R8G8B8
   - в методе инициализации 3D-сцены приложения (InitDeviceObjects, CreateDeviceObjects)
       вызвать метод инициализации экземпляра класса
     Some2DImage.InitDeviceObjects(pd3dDevice, TextureFilename);
     // TextureFilename - путь и имя файла текстуры изображения

   - в методе RestoreDeviceObjects приложения вызвать метод
     Some2DImage.RestoreDeviceObjects;

   - в методе InvalidateDeviceObjects приложения
     Some2DImage.InvalidateDeviceObjects;

   - в методе DestroyDeviceObjects приложения
     Some2DImage.DeleteDeviceObjects;

   - в методе уничтожения приложения
     SAFE_DELETE(Some2DImage);

   ============================================================

   Для задания ключевого (прозрачного) цвета служит свойство
   ColorKey (read/write)

   Some2DImage.ColorKey := $00000000;

   Для хранения имени и пути загруженного файла текстуры свойство-
   TexFilename (read only)

   Some2DImage.TexFilename;


   В методах отрисовки 3D-сцены приложения используются методы

   Some2DImage.Draw(x, y, Flags, Alpha);
   // x, y - координаты в пикселях экрана; Flags - дополнительные
   //   опции отрисовки (DrawD3DImage2D_Filtered - для линейной фильтрации)
   //   Alpha (0..255) - полупрозрачность изображения - от прозрачного (0) до
   //     полностью непрозрачного (255).


   
}




const
  D3DFVF_D3DImage2D = (D3DFVF_XYZRHW or D3DFVF_DIFFUSE or D3DFVF_TEX1);

  // Draw Flags
  DrawD3DImage2D_Filtered = $0001;

type
  TD3DImage2DVertex = packed record
    p: TD3DXVector4;
    color: DWORD;
    tu, tv: Single;
  end;



  TD3DImage2D = class
    m_pd3dDevice: IDirect3DDevice8;       // A D3DDevice used for rendering
    m_pTexture: IDirect3DTexture8;        // The d3d texture for this font
    m_d3dfmtTexture: D3DFORMAT;
    m_pVB: IDirect3DVertexBuffer8;        // VertexBuffer for rendering text
    m_dwTexWidth: DWORD;                  // Texture dimensions
    m_dwTexHeight: DWORD;
    m_fTextScale: Single;
    m_fTexCoords: array[0..3] of Single;

    m_bZEnable: Boolean;

    // Stateblocks for setting and restoring render states
    m_dwSavedStateBlock: DWORD;
    m_dwDrawImage2DStateBlock: DWORD;

    Position: TD3DXVector3;
    fSize: Single;
    TextureOffsetX, TextureOffsetY: Integer;
    Frame: Single;
    FramesPerLine: Integer;
  private
    FColorKey: Cardinal;
    FTexFilename: TTexFilename;

    procedure SetColorKey(Color: Cardinal);
  protected
    function LoadTextureFromFile(filename: TFileName): HResult;

  public

    constructor Create(TextureFormat: TD3DFormat = D3DFMT_A8R8G8B8);
    destructor Destroy; override;    

    function Draw(x, y: Single; dwFlags: Cardinal; Alpha: Byte): HResult;
    function DrawScaled(x, y, z: Single; fXScale, fYScale: Single;
      Flags: Cardinal; Alpha: Byte): HResult;

    // Initializing and destroying device-dependent objects
    function InitDeviceObjects(pd3dDevice: IDirect3DDevice8;
      TextureFilename: TFilename): HRESULT;
    function RestoreDeviceObjects: HRESULT;
    function InvalidateDeviceObjects: HRESULT;
    function DeleteDeviceObjects: HRESULT;

    property ColorKey: Cardinal read FColorKey write SetColorKey;
    property TexFilename: TTexFilename read FTexFilename;
      //write FTexFilename;
  end;

  function Init2DVertex(const p: TD3DXVector4; color: TD3DColor;
    tu, tv: Single): TD3DImage2DVertex;
  {function InitD3DSpriteVertex(const p: TD3DXVector3; color: TD3DColor;
    tu, tv: Single): TD3DSpriteVertex;}  

implementation

{  TD3DSprite  }

constructor TD3DSprite.Create(TextureFormat: TD3DFormat = D3DFMT_A8R8G8B8);
begin
  Position.X := 0;
  Position.Y := 0;
  Position.Z := 0.9;
  Color := $FFFFFFFF;

  fSize := 1.0;
  Width := 0;
  Height := 0;
  TextureOffsetX := 0;
  TextureOffsetY := 0;

  Frame := 0;
  FramesPerLine := 1;
  Delay := 0.001;
  fMaxFrame := 0;

  m_pd3dDevice          := nil;
  m_pTexture            := nil;
  m_pVB                 := nil;
  m_Camera              := nil;

  m_bZEnable := False;

  m_d3dfmtTexture := TextureFormat;

  m_dwSavedStateBlock    := 0;
  m_dwDrawStateBlock := 0;

  ZeroMemory(@FTexFilename, SizeOf(TTexFilename));
end;

destructor TD3DSprite.Destroy;
begin
  InvalidateDeviceObjects;
  DeleteDeviceObjects;
  inherited Destroy;
end;

{ ===========================================
   Draw 2D image in 3D Scene - рисование 2D-изображения в 3D-Сцене
   x, y, z - координаты в координатной системе Viewport (-1..1);
   Flags :
     - DrawD3DSprite_Filtered - линейная фильтрация текстуры
   Alpha - 0..255 - полупрозрачность изображения от полной прозрачности (0) до
     полной непрозрачности (255).
  ===========================================}
function TD3DSprite.Draw(x, y, z: Single; dwFlags: Cardinal; Alpha: Byte): HResult;
var
  v: PD3DSpriteVertexArray;
  h: Single;
  i, dwColor: Cardinal;

  fX, fY, fZ, x1, x2, y1, y2, u1, v1, tu1, tu2, tv1, tv2: Single;
  mat: TD3DMatrix;

begin
  if (m_pd3dDevice = nil) then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  // Setup renderstate
  m_pd3dDevice.CaptureStateBlock(m_dwSavedStateBlock);
  m_pd3dDevice.ApplyStateBlock(m_dwDrawStateBlock);
  m_pd3dDevice.SetVertexShader(D3DFVF_D3DSprite);
  m_pd3dDevice.SetPixelShader(0);
  m_pd3dDevice.SetStreamSource(0, m_pVB, SizeOf(TD3DSpriteVertex));

  // Set filter states
  if (dwFlags and DrawD3DSprite_Filtered) = DrawD3DSprite_Filtered then
  begin
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
  end;

  v := nil;  

  Position.x := x;
  Position.y := y;
  Position.z := z;

  dwColor := Alpha;
  Color := (dwColor shl 24) or $00FFFFFF;

  fX := Position.x;
  fY := Position.y;
  fZ := Position.z;  

  x1 := -fSize;
  x2 := fSize;
  y1 := -fSize;
  y2 := fSize;

  u1 := TextureOffsetX + Width *
    (Round(Frame) mod FramesPerLine);
  v1 := TextureOffsetY + Height *
    (Round(Frame) div FramesPerLine);

  {tu1 := m_fTexCoords[0];
  tv1 := m_fTexCoords[1];
  tu2 := m_fTexCoords[2];
  tv2 := m_fTexCoords[3];}
  tu1 := u1 / (m_dwTexHeight-1.0);
  tv1 := v1 / (m_dwTexHeight-1.0);
  tu2 := (u1 + Width -1) / (m_dwTexHeight-1.0);
  tv2 := (v1 + Height-1) / (m_dwTexHeight-1.0);

  // Set texture
  m_pd3dDevice.SetTexture( 0, m_pTexture );

  // Translate the billboard into place
  mat := m_Camera.GetBillboardMatrix;
  mat._41 := fX;
  mat._42 := fY;
  mat._43 := fZ;
  m_pd3dDevice.SetTransform( D3DTS_WORLD, mat );

  h := 0.1;

  m_pVB.Lock( 0, 0, PBYTE(v), 0 );
  i := 0;

  v[i].p := D3DXVECTOR3(x1, y1+h, 0);
  v[i].color := Color;
  v[i].tu := tu1; v[i].tv := tv2;
  v[i+1].p := D3DXVECTOR3(x1,y2+h,0);
  v[i+1].color := Color; v[i+1].tu := tu1; v[i+1].tv := tv1;
  v[i+2].p := D3DXVECTOR3(x2,y1+h,0);
  v[i+2].color := Color; v[i+2].tu := tu2; v[i+2].tv := tv2;
  v[i+3].p := D3DXVECTOR3(x2,y2+h,0);
  v[i+3].color := Color; v[i+3].tu := tu2; v[i+3].tv := tv1;
  m_pVB.Unlock;

  // Render the billboarded sprite
  m_pd3dDevice.DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

  // Restore the modified renderstates
  m_pd3dDevice.ApplyStateBlock(m_dwSavedStateBlock);

  Result:= S_OK;
end;

{function InitD3DSpriteVertex(const p: TD3DXVector3; color: TD3DColor;
  tu, tv: Single): TD3DSpriteVertex;
var
  v: TD3DSpriteVertex;
begin
  v.p := p;   v.color := color;   v.tu := tu;   v.tv := tv;
  Result:= v;
end;}

//==============================================================================
// Name: DrawScaled
// Desc: Draws scaled 2D Image.  Note that x and y are in viewport coordinates
//       (ranging from -1 to +1).  fXScale and fYScale are the size fraction
//       relative to the entire viewport.  For example, a fXScale of 0.25 is
//       1/8th of the screen width.  This allows you to output image at a fixed
//       fraction of the viewport, even if the screen or window size changes.
//-----------------------------------------------------------------------------

{function TD3DSprite.DrawScaled(x, y, z: Single; fXScale, fYScale: Single;
  Flags: Cardinal; Alpha: Byte): HResult;

var
  vp: TD3DViewport8;
  sx, sy, sz, rhw: Single;
  pVertices: ^TD3DImage2DVertex;

  tx1, ty1, tx2, ty2: Single;
  w, h, fImageHeight: Single;
  dwColor: Cardinal;

begin
  if (m_pd3dDevice = nil) then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  // Set up renderstate
  m_pd3dDevice.CaptureStateBlock(m_dwSavedStateBlock);
  m_pd3dDevice.ApplyStateBlock(m_dwDrawStateBlock);
  m_pd3dDevice.SetVertexShader(D3DFVF_D3DImage2D);
  m_pd3dDevice.SetPixelShader(0);
  m_pd3dDevice.SetStreamSource(0, m_pVB, SizeOf(TD3DImage2DVertex));

  // Set filter states
  if (Flags and DrawD3DSprite_Filtered) = DrawD3DSprite_Filtered then
  begin
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
  end;

  m_pd3dDevice.GetViewport(vp);
  sx  := (x+1.0)*vp.Width/2;
  sy  := (y-1.0)*vp.Height/2;
  sz  := z;
  rhw := 1.0;

  fImageHeight := (m_fTexCoords[3] - m_fTexCoords[1]) * m_dwTexHeight;

  // Fill vertex buffer

  m_pVB.Lock(0, 0, PByte(pVertices), D3DLOCK_DISCARD);

  tx1 := m_fTexCoords[0];
  ty1 := m_fTexCoords[1];
  tx2 := m_fTexCoords[2];
  ty2 := m_fTexCoords[3];

  w := (tx2-tx1)*m_dwTexWidth;
  h := (ty2-ty1)*m_dwTexHeight;

  //w := w*(fXScale*vp.Width);
  w := w*((fXScale*vp.Height)/fImageHeight);
  h := h*((fYScale*vp.Height)/fImageHeight);

  dwColor := Alpha;
  dwColor := (dwColor shl 24) or $00FFFFFF;

  pVertices^ := Init2DVertex(D3DXVector4(sx+0-0.5, sy+h-0.5, sz, rhw), dwColor, tx1, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+0-0.5, sy+0-0.5, sz, rhw), dwColor, tx1, ty1); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+w-0.5, sy+h-0.5, sz, rhw), dwColor, tx2, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+w-0.5, sy+0-0.5, sz, rhw), dwColor, tx2, ty1); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+w-0.5, sy+h-0.5, sz, rhw), dwColor, tx2, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+0-0.5, sy+0-0.5, sz, rhw), dwColor, tx1, ty1); Inc(pVertices);

  // Unlock, render, and relock the vertex buffer
  m_pVB.Unlock;
  m_pd3dDevice.DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
  m_pVB.Lock(0, 0, PByte(pVertices), D3DLOCK_DISCARD);

  // Restore the modified renderstates
  m_pd3dDevice.ApplyStateBlock(m_dwSavedStateBlock);

  Result:= S_OK;
end;}

function TD3DSprite.DrawScaled(x, y, z: Single; fXScale, fYScale: Single;
  Flags: Cardinal; Alpha: Byte): HResult;
  
var
  v: PD3DSpriteVertexArray;
  h: Single;
  i, dwColor: Cardinal;

  fX, fY, fZ, x1, x2, y1, y2, u1, v1, tu1, tu2, tv1, tv2: Single;
  mat: TD3DMatrix;

begin
  if (m_pd3dDevice = nil) then
  begin
    Result := E_FAIL;
    Exit;
  end;

  // Setup renderstate
  m_pd3dDevice.CaptureStateBlock(m_dwSavedStateBlock);
  m_pd3dDevice.ApplyStateBlock(m_dwDrawStateBlock);
  m_pd3dDevice.SetVertexShader(D3DFVF_D3DSprite);
  m_pd3dDevice.SetPixelShader(0);
  m_pd3dDevice.SetStreamSource(0, m_pVB, SizeOf(TD3DSpriteVertex));

  // Set filter states
  if (Flags and DrawD3DSprite_Filtered) = DrawD3DSprite_Filtered then
  begin
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
  end;

  v := nil;  

  Position.x := x;
  Position.y := y;
  Position.z := z;

  dwColor := Alpha;
  Color := (dwColor shl 24) or $00FFFFFF;

  fX := Position.x;
  fY := Position.y;
  fZ := Position.z;  

  x1 := -fXScale;////-fSize;
  x2 := fXScale;////fSize;
  y1 := -fYScale;////-fSize;
  y2 := fYScale;////fSize;

  u1 := TextureOffsetX + Width *
    (Round(Frame) mod FramesPerLine);
  v1 := TextureOffsetY + Height *
    (Round(Frame) div FramesPerLine);

  {tu1 := m_fTexCoords[0];
  tv1 := m_fTexCoords[1];
  tu2 := m_fTexCoords[2];
  tv2 := m_fTexCoords[3];}
  tu1 := u1 / (m_dwTexHeight-1.0);
  tv1 := v1 / (m_dwTexHeight-1.0);
  tu2 := (u1 + Width -1) / (m_dwTexHeight-1.0);
  tv2 := (v1 + Height-1) / (m_dwTexHeight-1.0);

  // Set texture
  m_pd3dDevice.SetTexture( 0, m_pTexture );

  // Translate the billboard into place
  mat := m_Camera.GetBillboardMatrix;
  mat._41 := fX;
  mat._42 := fY;
  mat._43 := fZ;
  m_pd3dDevice.SetTransform( D3DTS_WORLD, mat );

  h := 0.1;

  m_pVB.Lock( 0, 0, PBYTE(v), 0 );
  i := 0;

  v[i].p := D3DXVECTOR3(x1, y1+h, 0);
  v[i].color := Color;
  v[i].tu := tu1; v[i].tv := tv2;
  v[i+1].p := D3DXVECTOR3(x1,y2+h,0);
  v[i+1].color := Color; v[i+1].tu := tu1; v[i+1].tv := tv1;
  v[i+2].p := D3DXVECTOR3(x2,y1+h,0);
  v[i+2].color := Color; v[i+2].tu := tu2; v[i+2].tv := tv2;
  v[i+3].p := D3DXVECTOR3(x2,y2+h,0);
  v[i+3].color := Color; v[i+3].tu := tu2; v[i+3].tv := tv1;
  m_pVB.Unlock;

  // Render the billboarded sprite
  m_pd3dDevice.DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

  // Restore the modified renderstates
  m_pd3dDevice.ApplyStateBlock(m_dwSavedStateBlock);

  Result:= S_OK;
end;

function TD3DSprite.FrameMove(fTimeLapsed: Single): HResult;
begin
  Frame := Frame + Delay * fTimeLapsed;
  // Keep animation frame in bounds
  if Frame < 0.0 then
    Frame := Frame + fMaxFrame;
  if Frame >= fMaxFrame then
    Frame := Frame - fMaxFrame;

  result := S_OK;  
end;


function TD3DSprite.InitDeviceObjects(pd3dDevice: IDirect3DDevice8;
  TextureFilename: TFilename): HRESULT;
var
  d3dCaps: TD3DCaps8;
begin
  // Keep a local copy of the device
  m_pd3dDevice := pd3dDevice;
  if not Assigned(m_pd3dDevice) then begin
    Result := E_POINTER;
    DXTrace(Unitname, $FFFFFFFF, Result, 'pointer to IDirect3DDevice8 interface is nil!!!');
    Exit;
  end;


  //m_dwTexWidht = m_dwTexHeight и устанавливаются после загрузки текстуры методом
  // LoadImageFromFile;
  //выход, если изображение не загружено и нет текстуры
  result := LoadTextureFromFile(TextureFilename);
  if Failed(result) then Exit;

  if m_pTexture = nil then Exit;

  m_fScale := 1.0; // Draw into texture without scaling

  // If requested texture is too big, use a smaller texture
  // and scale up when rendering.

  m_pd3dDevice.GetDeviceCaps(d3dCaps);

  if (m_dwTexWidth > d3dCaps.MaxTextureWidth) then
  begin
    m_fScale := d3dCaps.MaxTextureWidth / m_dwTexWidth;
    m_dwTexHeight := d3dCaps.MaxTextureWidth;
    m_dwTexWidth := m_dwTexHeight;
  end;

  m_fTexCoords[0] := 0;
  m_fTexCoords[1] := 0;
  m_fTexCoords[2] := 1;
  m_fTexCoords[3] := 1;

  result := S_OK;
end;

function TD3DSprite.RestoreDeviceObjects(pCamera: CD3DCamera): HRESULT;
var
  which: LongWord;
begin
  // Create vertex buffer for the letters
  Result:= m_pd3dDevice.CreateVertexBuffer(6*SizeOf(TD3DSpriteVertex),
                                           D3DUSAGE_WRITEONLY or D3DUSAGE_DYNAMIC,
                                           0, D3DPOOL_DEFAULT, m_pVB);
  if FAILED(Result) then Exit;

  m_Camera := pCamera;
  if not Assigned(m_Camera) then begin
    Result := E_POINTER;
    DXTrace(Unitname, $FFFFFFFF, Result, 'pointer to Camera is nil!!!');
    Exit;
  end;

  // Create the state blocks for rendering text
  for which:= 0 to 1 do
  begin
    m_pd3dDevice.BeginStateBlock;
    m_pd3dDevice.SetTexture(0, m_pTexture);

    if m_bZEnable then
      m_pd3dDevice.SetRenderState(D3DRS_ZENABLE, iTrue)
    else
      m_pd3dDevice.SetRenderState(D3DRS_ZENABLE, iFalse);

    m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, 1);
    m_pd3dDevice.SetRenderState(D3DRS_SRCBLEND,         D3DBLEND_SRCALPHA);
    m_pd3dDevice.SetRenderState(D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHATESTENABLE,  1);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHAREF,         $08);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHAFUNC,        D3DCMP_GREATEREQUAL);
    m_pd3dDevice.SetRenderState(D3DRS_FILLMODE,         D3DFILL_SOLID);
    m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,         D3DCULL_CCW);
    m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE,    0);
    m_pd3dDevice.SetRenderState(D3DRS_CLIPPING,         1);
    m_pd3dDevice.SetRenderState(D3DRS_EDGEANTIALIAS,    0);
    m_pd3dDevice.SetRenderState(D3DRS_CLIPPLANEENABLE,  0);
    m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND,      0);
    m_pd3dDevice.SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, 0);
    m_pd3dDevice.SetRenderState(D3DRS_FOGENABLE,        0);

    m_pd3dDevice.SetRenderState(D3DRS_ZWRITEENABLE, Cardinal(True) );

    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_POINT);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_POINT);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_NONE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    m_pd3dDevice.SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_DISABLE);
    m_pd3dDevice.SetTextureStageState(1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE);

    if (which = 0) then
      m_pd3dDevice.EndStateBlock(m_dwSavedStateBlock)
    else
      m_pd3dDevice.EndStateBlock(m_dwDrawStateBlock);
  end;

  Result:= S_OK;
end;

function TD3DSprite.InvalidateDeviceObjects: HRESULT;
begin
  SAFE_RELEASE(m_pVB);

  // Delete the state blocks
  if (m_pd3dDevice <> nil) then
  begin
    if (m_dwSavedStateBlock <> 0) then
      m_pd3dDevice.DeleteStateBlock(m_dwSavedStateBlock);
    if (m_dwDrawStateBlock <> 0) then
      m_pd3dDevice.DeleteStateBlock(m_dwDrawStateBlock);
  end;

  m_dwSavedStateBlock    := 0;
  m_dwDrawStateBlock := 0;

  Result:= S_OK;
end;

function TD3DSprite.DeleteDeviceObjects: HRESULT;
begin
  SAFE_RELEASE(m_pTexture);
  m_pd3dDevice:= nil;
  m_Camera := nil;
  Result:= S_OK;
end;

function TD3DSprite.LoadTextureFromFile(filename: TFileName): HResult;
var
  SurfDesc: TD3DSurface_Desc;

begin
  result := E_FAIL;

  if filename = '' then Exit;

  StrPCopy(FTexFilename, filename);

  result := D3DUtil_CreateTexture(m_pd3dDevice, FTexFilename,
    m_pTexture, m_d3dfmtTexture);
  if Failed(result) then Exit;

  SetColorKey($00000000);

  result := m_pTexture.GetLevelDesc(0, SurfDesc);
  if Failed(result) then Exit;

  m_dwTexWidth := SurfDesc.Width;
  m_dwTexHeight := SurfDesc.Height;

  Width := m_dwTexWidth;
  Height := m_dwTexHeight;
end;

procedure TD3DSprite.SetColorKey(Color: Cardinal);
begin
  if Assigned(m_pTexture) and (Color <> FColorKey) then begin
    FColorKey := Color;
    D3DUtil_SetColorKey(m_pTexture, FColorKey);
  end;
end;


{  TD3DImage2D  }

constructor TD3DImage2D.Create(TextureFormat: TD3DFormat = D3DFMT_A8R8G8B8);
begin
  Position.X := 0;
  Position.Y := 0;
  Position.Z := 0.9;

  fSize := 1.0;
  TextureOffsetX := 0;
  TextureOffsetY := 0;

  Frame := 0;
  FramesPerLine := 1;

  m_pd3dDevice          := nil;
  m_pTexture            := nil;
  m_pVB                 := nil;

  m_bZEnable := False;

  m_d3dfmtTexture := TextureFormat;

  m_dwSavedStateBlock    := 0;
  m_dwDrawImage2DStateBlock := 0;

  ZeroMemory(@FTexFilename, SizeOf(TTexFilename));
end;

destructor TD3DImage2D.Destroy;
begin
  InvalidateDeviceObjects;
  DeleteDeviceObjects;
  inherited Destroy;
end;



//==============================================================================
// Name: DrawScaled
// Desc: Draws scaled 2D Image.  Note that x and y are in viewport coordinates
//       (ranging from -1 to +1).  fXScale and fYScale are the size fraction
//       relative to the entire viewport.  For example, a fXScale of 0.25 is
//       1/8th of the screen width.  This allows you to output image at a fixed
//       fraction of the viewport, even if the screen or window size changes.
//-----------------------------------------------------------------------------

function TD3DImage2D.DrawScaled(x, y, z: Single; fXScale, fYScale: Single;
  Flags: Cardinal; Alpha: Byte): HResult;

var
  vp: TD3DViewport8;
  sx, sy, sz, rhw: Single;
  pVertices: ^TD3DImage2DVertex;

  tx1, ty1, tx2, ty2: Single;
  w, h, fImageHeight: Single;
  dwColor: Cardinal;

begin
  if (m_pd3dDevice = nil) then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  // Set up renderstate
  m_pd3dDevice.CaptureStateBlock(m_dwSavedStateBlock);
  m_pd3dDevice.ApplyStateBlock(m_dwDrawImage2DStateBlock);
  m_pd3dDevice.SetVertexShader(D3DFVF_D3DImage2D);
  m_pd3dDevice.SetPixelShader(0);
  m_pd3dDevice.SetStreamSource(0, m_pVB, SizeOf(TD3DImage2DVertex));

  // Set filter states
  if (Flags and DrawD3DImage2D_Filtered) = DrawD3DImage2D_Filtered then
  begin
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
  end;

  m_pd3dDevice.GetViewport(vp);
  sx  := (x+1.0)*vp.Width/2;
  sy  := (y-1.0)*vp.Height/2;
  sz  := z;
  rhw := 1.0;

  fImageHeight := (m_fTexCoords[3] - m_fTexCoords[1]) * m_dwTexHeight;

  // Fill vertex buffer

  m_pVB.Lock(0, 0, PByte(pVertices), D3DLOCK_DISCARD);

  tx1 := m_fTexCoords[0];
  ty1 := m_fTexCoords[1];
  tx2 := m_fTexCoords[2];
  ty2 := m_fTexCoords[3];

  w := (tx2-tx1)*m_dwTexWidth;
  h := (ty2-ty1)*m_dwTexHeight;

  //w := w*(fXScale*vp.Width);
  w := w*((fXScale*vp.Height)/fImageHeight);
  h := h*((fYScale*vp.Height)/fImageHeight);

  dwColor := Alpha;
  dwColor := (dwColor shl 24) or $00FFFFFF;

  pVertices^ := Init2DVertex(D3DXVector4(sx+0-0.5, sy+h-0.5, sz, rhw), dwColor, tx1, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+0-0.5, sy+0-0.5, sz, rhw), dwColor, tx1, ty1); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+w-0.5, sy+h-0.5, sz, rhw), dwColor, tx2, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+w-0.5, sy+0-0.5, sz, rhw), dwColor, tx2, ty1); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+w-0.5, sy+h-0.5, sz, rhw), dwColor, tx2, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(sx+0-0.5, sy+0-0.5, sz, rhw), dwColor, tx1, ty1); Inc(pVertices);

  // Unlock, render, and relock the vertex buffer
  m_pVB.Unlock;
  m_pd3dDevice.DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

  // Restore the modified renderstates
  m_pd3dDevice.ApplyStateBlock(m_dwSavedStateBlock);

  Result:= S_OK;
end;

function TD3DImage2D.InitDeviceObjects(pd3dDevice: IDirect3DDevice8;
  TextureFilename: TFilename): HRESULT;
var
  d3dCaps: TD3DCaps8;
begin
  // Keep a local copy of the device
  m_pd3dDevice := pd3dDevice;
  if not Assigned(m_pd3dDevice) then begin
    Result := E_POINTER;
    DXTrace(Unitname, $FFFFFFFF, Result, 'pointer to IDirect3DDevice8 interface is nil!!!');
    Exit;
  end;

  //m_dwTexWidht = m_dwTexHeight и устанавливаются после загрузки текстуры методом
  // LoadImageFromFile;
  //выход, если изображение не загружено и нет текстуры
  result := LoadTextureFromFile(TextureFilename);
  if Failed(result) then Exit;

  if m_pTexture = nil then Exit;

  m_fTextScale := 1.0; // Draw into texture without scaling

  // If requested texture is too big, use a smaller texture
  // and scale up when rendering.

  m_pd3dDevice.GetDeviceCaps(d3dCaps);

  if (m_dwTexWidth > d3dCaps.MaxTextureWidth) then
  begin
    m_fTextScale := d3dCaps.MaxTextureWidth / m_dwTexWidth;
    m_dwTexHeight := d3dCaps.MaxTextureWidth;
    m_dwTexWidth := m_dwTexHeight;
  end;

  m_fTexCoords[0] := 0;
  m_fTexCoords[1] := 0;
  m_fTexCoords[2] := 1;
  m_fTexCoords[3] := 1;

  result := S_OK;
end;

function TD3DImage2D.RestoreDeviceObjects: HRESULT;
var
  which: LongWord;
begin
  // Create vertex buffer for the letters
  Result:= m_pd3dDevice.CreateVertexBuffer(6*SizeOf(TD3DImage2DVertex),
                                           D3DUSAGE_WRITEONLY or D3DUSAGE_DYNAMIC,
                                           0, D3DPOOL_DEFAULT, m_pVB);
  if FAILED(Result) then Exit;

  // Create the state blocks for rendering text
  for which:= 0 to 1 do
  begin
    m_pd3dDevice.BeginStateBlock;
    m_pd3dDevice.SetTexture(0, m_pTexture);

    if m_bZEnable then
      m_pd3dDevice.SetRenderState(D3DRS_ZENABLE, iTrue)
    else
      m_pd3dDevice.SetRenderState(D3DRS_ZENABLE, iFalse);

    m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, 1);
    m_pd3dDevice.SetRenderState(D3DRS_SRCBLEND,         D3DBLEND_SRCALPHA);
    m_pd3dDevice.SetRenderState(D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHATESTENABLE,  1);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHAREF,         $08);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHAFUNC,        D3DCMP_GREATEREQUAL);
    m_pd3dDevice.SetRenderState(D3DRS_FILLMODE,         D3DFILL_SOLID);
    m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,         D3DCULL_CCW);
    m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE,    0);
    m_pd3dDevice.SetRenderState(D3DRS_CLIPPING,         1);
    m_pd3dDevice.SetRenderState(D3DRS_EDGEANTIALIAS,    0);
    m_pd3dDevice.SetRenderState(D3DRS_CLIPPLANEENABLE,  0);
    m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND,      0);
    m_pd3dDevice.SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, 0);
    m_pd3dDevice.SetRenderState(D3DRS_FOGENABLE,        0);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_POINT);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_POINT);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_NONE);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    m_pd3dDevice.SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_DISABLE);
    m_pd3dDevice.SetTextureStageState(1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE);

    if (which = 0) then
      m_pd3dDevice.EndStateBlock(m_dwSavedStateBlock)
    else
      m_pd3dDevice.EndStateBlock(m_dwDrawImage2DStateBlock);
  end;

  Result:= S_OK;
end;

function TD3DImage2D.InvalidateDeviceObjects: HRESULT;
begin
  SAFE_RELEASE(m_pVB);

  // Delete the state blocks
  if (m_pd3dDevice <> nil) then
  begin
    if (m_dwSavedStateBlock <> 0) then
      m_pd3dDevice.DeleteStateBlock(m_dwSavedStateBlock);
    if (m_dwDrawImage2DStateBlock <> 0) then
      m_pd3dDevice.DeleteStateBlock(m_dwDrawImage2DStateBlock);
  end;

  m_dwSavedStateBlock    := 0;
  m_dwDrawImage2DStateBlock := 0;

  Result:= S_OK;
end;

function TD3DImage2D.DeleteDeviceObjects: HRESULT;
begin
  SAFE_RELEASE(m_pTexture);
  m_pd3dDevice:= nil;
  Result:= S_OK;
end;

function TD3DImage2D.LoadTextureFromFile(filename: TFileName): HResult;
var
  SurfDesc: TD3DSurface_Desc;

begin
  result := E_FAIL;

  if filename = '' then Exit;

  StrPCopy(FTexFilename, filename);

  result := D3DUtil_CreateTexture(m_pd3dDevice, FTexFilename,
    m_pTexture, m_d3dfmtTexture);
  if Failed(result) then Exit;

  SetColorKey($00000000);

  result := m_pTexture.GetLevelDesc(0, SurfDesc);
  if Failed(result) then Exit;

  m_dwTexWidth := SurfDesc.Width;
  m_dwTexHeight := SurfDesc.Height;
end;

function Init2DVertex(const p: TD3DXVector4; color: TD3DColor;
  tu, tv: Single): TD3DImage2DVertex;
var
  v: TD3DImage2DVertex;
begin
  v.p := p;   v.color := color;   v.tu := tu;   v.tv := tv;
  Result:= v;
end;

{ ===========================================
   Draw 2D image in 3D Scene - рисование 2D-изображения в 3D-Сцене
   x, y - координаты в пикселях;
   Flags :
     - DrawD3DImage2D_Filtered - линейная фильтрация текстуры
   Alpha - 0..255 - полупрозрачность изображения от полной прозрачности (0) до
     полной непрозрачности (255).
  ===========================================}
function TD3DImage2D.Draw(x, y: Single; dwFlags: Cardinal; Alpha: Byte): HResult;
var
  pVertices: ^TD3DImage2DVertex;
  tx1, ty1, tx2, ty2: Single;
  w, h: Single;
  dwColor: Cardinal;

begin
  if (m_pd3dDevice = nil) then
  begin
    Result:= E_FAIL;
    Exit;
  end;

  // Setup renderstate
  m_pd3dDevice.CaptureStateBlock(m_dwSavedStateBlock);
  m_pd3dDevice.ApplyStateBlock(m_dwDrawImage2DStateBlock);
  m_pd3dDevice.SetVertexShader(D3DFVF_D3DImage2D);
  m_pd3dDevice.SetPixelShader(0);
  m_pd3dDevice.SetStreamSource(0, m_pVB, SizeOf(TD3DImage2DVertex));

  // Set filter states
  if (dwFlags and DrawD3DImage2D_Filtered) = DrawD3DImage2D_Filtered then
  begin
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
    m_pd3dDevice.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
  end;

  // Fill vertex buffer
  pVertices:= nil;

  m_pVB.Lock(0, 0, PByte(pVertices), D3DLOCK_DISCARD);

  tx1 := m_fTexCoords[0];
  ty1 := m_fTexCoords[1];
  tx2 := m_fTexCoords[2];
  ty2 := m_fTexCoords[3];

  w := (tx2-tx1) * m_dwTexWidth  / m_fTextScale;
  h := (ty2-ty1) * m_dwTexHeight / m_fTextScale;

  dwColor := Alpha;
  dwColor := (dwColor shl 24) or $00FFFFFF;

  pVertices^ := Init2DVertex(D3DXVector4(x+0-0.5,y+h-0.5,0.9,1.0), dwColor, tx1, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(x+0-0.5,y+0-0.5,0.9,1.0), dwColor, tx1, ty1); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(x+w-0.5,y+h-0.5,0.9,1.0), dwColor, tx2, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(x+w-0.5,y+0-0.5,0.9,1.0), dwColor, tx2, ty1); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(x+w-0.5,y+h-0.5,0.9,1.0), dwColor, tx2, ty2); Inc(pVertices);
  pVertices^ := Init2DVertex(D3DXVector4(x+0-0.5,y+0-0.5,0.9,1.0), dwColor, tx1, ty1); Inc(pVertices);

  // Unlock, render, and relock the vertex buffer
  m_pVB.Unlock;
  m_pd3dDevice.DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

  // Restore the modified renderstates
  m_pd3dDevice.ApplyStateBlock(m_dwSavedStateBlock);

  Result:= S_OK;
end;



procedure TD3DImage2D.SetColorKey(Color: Cardinal);
begin
  if Assigned(m_pTexture) and (Color <> FColorKey) then begin
    FColorKey := Color;
    D3DUtil_SetColorKey(m_pTexture, FColorKey);
  end;
end;


end.
 