Sabtu, 12 Januari 2013

Animasi 2D dengan Direct3D Bagian 1

Animasi 2D dengan Direct3D Bagian 1 PDF Cetak Email


Ini adalah tutorial pendek yang fokus pada pemrograman grafis 2D dalam lingkungan Direct3D. Lebih spesifik lagi, tutorial ini tentang animasi 2D dengan interface ID3DXSprite, sebuah pembungkus fungsionalitas menampilkan sprite 2D dalam grafis 3D.

Mengapa artikel ini tentang 2D?

Karena saya penggemar game-game side scroller seperti Sega Sonic atau Mario Bros.

Mengapa menggunakan Direct3D?

Sejak versi 8, M$ telah menyatakan bahwa DirectDraw sudah deprecated dan tidak lagi dikembangkan dan M$ akan fokus mengembangkan DirectX Graphics yakni API baru hasil penggabungan DirectDraw dan Direct3D (Direct X versi 7 kebawah) untuk urusan grafik. So suatu saat di masa datang, mungkin DirectDraw sudah tidak di-support.
Pada tutorial kali ini semua kode menggunakan DirectX 8 (karena sistem saya masih pake DirectX 8 , anda harus menginstall DirectX 8 untuk bisa menjalankan contoh program yang ada disini. Pada tutorial, saya akan menggunakan istilah Direct3D untuk mengacu pada DirectX Graphics.

Mungkinkah membuat aplikasi 3D dengan Delphi?

Tentu! Oleh karena itu, download dulu unit konversi header DirectX dari SourceForge (DirectX 9) atau di sini (DirectX 8.1). Source code bisa didownload disini. Cukup introduksinya. Ayo mulai..

Dasar-Dasar Direct3D

Inisialisasi Direct3D

Fungsi Direct3DCreate8() tersedia untuk proses inisialisasi mendapatkan instance ke interface IDirect3D8 yang dideklarasi di unit DirectXGraphics.pas. Contoh
var
  d3d8:IDirect3D8;

begin
  d3d8:=Direct3DCreate8(D3D_SDK_VERSION);
  ...
Bila sukses d3d8 akan berisi pointer ke interface IDirect3D8 yang selanjutnya dapat kita pergunakan untuk mendapatkan instance IDirect3DDevice8 yang merupakan interface yang merwakili kartu grafis yang ada pada system. Idirect3D8 juga kita pergunakan untuk keperluan pengecekan kapabilitas kartu grafis.

Inisialisasi Device

Selama eksekusi aplikasi, kita akan sering menggunakan device untuk proses inisialisasi mode grafis, transformasi, menciptakan tekstur, menampilkan hasil render ke screen dan lain-lain. Device direpresentasikan oleh interface IDirect3DDevice8. Untuk mendapatkan pointer interface ini kita menggunakan fungsi CreateDevice() milik IDirect3D8
function CreateDevice(const Adapter : Cardinal;
                      const DeviceType : TD3DDevType;
                      hFocusWindow : HWND;
                      BehaviorFlags : LongWord;
                      var pPresentationParameters:
                          TD3DPresent_Parameters;
                     out ppReturnedDeviceInterface :
                     IDirect3DDevice8) : HResult; stdcall;
Adapter adalah nilai ordinal vga card. Untuk mengacu pada VGA card utama kita set parapeter ini dengan D3DADAPTER_DEFAULT.
  • DeviceType :tipe device isi dengan D3DDEVTYPE_HAL untuk menggunakan HAL.
  • hFocusWindow:Handle yang akan menjadi target.
  • BehaviorFlags adalah parameter yang menentukan sifat device. Isi dengan D3DCREATE_HARDWARE_VERTEX_PROCESSING untuk menggunakan VGA card yang mendukung 3D acceleration atau D3DCREATE_SOFTWARE_VERTEX_PROCESSING bila tidak memiliki 3D accelerator. D3DCREATE_HARDWARE_VERTEX_PROCESSING dan D3DCREATE_SOFTWARE)VERTEX_PROCESSING bersifat mutual ekslusif, yakni kita hanya bisa menggunakan salah satu tapi tidak keduad-duanya.
  • pPresentationParameters berisi informasi seperti resolusi mode grafis, kedalaman warna, jumlah backbuffer dan sebagainya. Untuk saat ini, field yang penting adalah:
    • BackBufferWidth dan BackBufferHeight kita pergunakan untuk mengatur resolusi layar contoh 800x600, 1024x768 dan lain-lain. Jika resolusi tidak didukung, CreteDevice akan gagal. Tips: sebaiknya gunakan resolusi mode grafis yang standar yang banyak didukung oleh vga card seperti 800x600 atau 1024x768.
    • Windowed kita pergunakan untuk mengatur apakah aplikasi berjalan secara fullscreen atau dalam window. Jika diisi false aplikasi berjalan pada mode fullscreen. Pada mode fullscreen, aplikasi kita dapat menggunakan page flipping atau blitting untuk proses double buffering. Sedangkan pada mode windowed, double buffering hanya menggunakan blitting.
      Catatan: Page flipping maupun blitting adalah teknik double buffering yang umum dipakai. Double buffering dikembangkan untuk meminimalkan terjadinya flicker yakni dengan memanfaatkan dua buah buffer (atau triple buffering bila tiga) yakni buffer utama biasa disebut front buffer atau primary buffer, buffer ini adalah buffer yang terlihat dan sebuah buffer tambahan yang tidak terlihat (back buffer). Proses penggambaran dilakukan di back buffer. Setelah penggambaran selesai, back buffer ditransfer ke front buffer.
      Page flipping adalah proses mengubah front buffer menjadi back buffer dan sebaliknya dengan mengubah alamat start scanning kartu grafis. Blitting adalah singkatan dari Block Binary transfer mengkopi isi back buffer ke front buffer. Secara teoritis page flipping lebih cepat dari blitting, karena proses menampilkan isi back buffer sehingga dapat dilihat tidak melibatkan pengkopian data.
    • BackBufferFormat kita pergunakan untuk menentukn kedalaman warna yang kita inginkan. Contoh untuk menjalankan aplikasipada kedalaman warna 32 Bit, kita menggunakan D3DFMT_A8R8G8B8 dimana tiap pixel terdiri atas komponen Alpha(A) =8 bit, Red (R)= 8 bit, Green(G)= 8 bit dan Blue(B)=8 bit. Alpa menentukan tingkat transparansi pixel tersbut jika dicampur dengan warna lain. Untuk mode 16 bit kita bisa menggunakan D3DFMT_R5G6B5 dengan R=5 bit G=6 bit dan B=5 bit tanpa komponen alpha. Untuk mode 24 bit konstanta yang bisa kita pakai adalah D3DFMT_R8G8B8 tanpa alpha
    • SwapEffect menentukan bagaimana proses pertukaran back buffer dan front buffer. Jika disi D3DSWAPEFFECT_DISCARD, maka setelah swap back buffer dan front buffer, isi back buffer menjadi berubah sehingga kita tidak bisa mengasumsikan bahwa back buffer isinya akan sama dengan sebelum swap. Dengan menggunakan D3DSWAPEFFECT_DISCARD kita memberitahukan kepada DirectX untuk memilih mekanisme swap yang paling optimal untuk buffer kita. Jika kita isi dengan D3DSWAPEFFECT_FLIP kita meberithaukan DirectX untuk menggunakan page Flipping. dan D3DSWAPEFFECT_COPY serta D3DSWAPEFFECT_VSYNC_COPY untuk blitting. VSYNC melakukan blitting dengan melakukan sinkronisasi vertical trace guna meminimalkan efek tear-off.
    • hDeviceWindow handle window yang akan menjadi target proses render. Jika 0 hFocusWindow yang akan digunakan.
    ppReturnedDeviceInterface interface yang akan menerima pointer IDirect3DDevice8
jika CreateDevice() gagal ppReturnedDeviceInterface sama dengan nil. Contoh:
if accel3DSupported then
  behaviour:=D3DCREATE_HARDWARE_VERTEX_PROCESSING 
else
  behaviour:=D3DCREATE_SOFTWARE_VERTEX_PROCESSING;

ZeroMemory(presentParam,sizeof(TD3DPresent_Parameters));

presentParam.hFocusWindow:=Form1.Handle;
presentParam.BackBufferCount:=1;
presentParam.BackBufferWidth:=800;
presentParam.BackBufferHeight:=600;
presentParam.SwapEffect:=D3DSWAPEFFECT_DISCARD;
presentparam.BackBufferFormat:=D3DFMT_A8R8G8B8;

d3d8.CreateDevice(D3DADAPTER_DEFAULT,
                  D3DDEVTYPE_HAL,
                  behaviour,
                  presentParam,
                  d3d8Dev); 

Mengecek Apakah 3D accelerator tersedia.

Agar kita bisa memanfaatkan kemampuan kartu grafis dengan optimal, kita perlu memastikan bahwa kartu grafis kita mendukung 3D acceleration. UNtuk mengecek kapabilitaskartu grafis tersedia metode IDirect3D8.GetDeviceCaps().
function GetDeviceCaps(const Adapter : Cardinal;
                       const DeviceType : TD3DDevType;
                       out pCaps : TD3DCaps8) : HResult; stdcall;

Adapter : nilai ordinal kartu grafis. Seperti biasa bisa disisi D3DADAPTER_DEFAULT untuk mengacu pada primary adapter. DeviceType.: tipe device D3DDEVTYPE_HAL atau D3DDEVTYPE_REF pCaps :struktur yang akan diisi info kapabilitas kartu grafis
Untuk mengecek apakah 3D accelerator tersedia kita perlu mengecek variable pCaps.DevCaps apakah mengandung nilai D3DDEVCAPS_HWTRANSFORMANDLIGHT. Contoh:
d3d8.GetDeviceCaps(D3DADAPTER_DEFAULT, 
                            D3DDEVTYPE_HAL, aCaps);

accel3DSupported:=(aCaps.DevCaps 
                               AND                               
D3DDEVCAPS_HWTRANSFORMANDLIGHT 
=  D3DDEVCAPS_HWTRANSFORMANDLIGHT);

Membersihkan Isi Back Buffer

Untuk membersihkan buffer, baik itu buffer untuk proses render, depth buffer atau stencil buffer kita menggunakan IDirect3DDevice8.Clear().
function Clear(const Count : LongWord; 
          pRects : PD3DRect; 
         const Flags : LongWord; 
         const Color : TD3DColor;
         const Z : Single;  
         const Stencil : LongWord) : HResult; stdcall;
Count berisi jumlah rectangle yang ada pada pRects. Jika pRects=nil maka Count harus diset 0 pRects berisi rectangle-rectangle yang akan diclear jika diisi nil, maka seluruh buffer akan di clear. Flags mengindikasikan buffer yang akan diclear. D3DCLEAR_STENCIL menyebabkan stencil buffer diclear dengan nilai yang terkandung pada parameter Stencil. D3DCLEAR_ZBUFFER mengclear depth buffer dengan nilai yang ada pada parameter Z dan D3DCLEAR_TARGET mengclear render target dengan warna yang tersimpan di Color.
Untuk tutorial ini depth buffer dan stencil buffer belum relevan untuk dibahas. Untuk membersihkan (lebih tepatnya mengisi) seluruh buffer render dengan sebuah warna merah (ARGB=$FFFF0000) contohnya adalah sebagai berikut:
d3dDev.Clear(0,nil, D3DCLEAR_TARGET, $FFFF0000,0,0);

Memulai dan Mengakhiri Proses Menggambar Object 3D

Sebelum menggambar obyek 3D, kita wajib memanggil BeginScene() dan mengakhirinya dengan EndScene(), dimana diantara dua pemanggilan fungsi diatas adalah kode-kode untuk menyusun obyek 3D.
d3ddev.BeginScene;
//gambar obyek-obyek disini
d3ddev.EndScene;
Pasangan BeginScene/EndScene harus dipanggil walaupun kita tidak menggambar obyek apa-apa.

Menampilkan Isi Back Buffer Ke Front Buffer

Agar semua yang kita gambar dapat terlihat dilayar, kita harus menampilkannya dengan fungsi Present().
function Present(pSourceRect, pDestRect : PRect; 
                 const hDestWindowOverride : HWND; 
                 pDirtyRegion : PRgnData) : HResult; stdcall;
  • pSourceRect rectangle yang akan dikopi. Jika diisi nil maka seluruh buffer akan dikopi. Harus diisi nil bila device dicreate dengan swap effect D3DSWAPEFFECT_COPY atau D3DSWAPEFFECT_VSYNC_COPY.
  • pDestRect rectangle tujuan. Sama dengan pSourceRect.
  • hDestWindowOverride handle window yang akan dijadikan target render. Jika 0 TD3Dpresent_Parameters.hDeviceWindow yang akan digunakan.
  • pDirtyRegion belum digunakan dan harus diisi nil.
Contoh:
d3ddev.Present(nil,nil,nil,0,nil);

Tekstur

Tekstur adalah istilah untuk gambar yang ditempelkan pada poligon, guna meningkatkan tingkat realistis obyek 3D. Di Direct3D 8 tekstur direpresentasikan oleh interface IDirect3DTexture8

Menciptakan Tekstur dari File Gambar

DirectX memiliki kumpulan utility yang bernama D3DX, salah satu fungsi pada D3DX ini adalah D3DXCreateTextureFromFile() dan D3DXCreateTextureFromFileEx() keduanya dideklarasi di d3dx8.pas. Fungsi yang terakhir adalah perluasan fungsi yang pertama dan yang akan kita pergunakan untuk menciptakan tektur.

Sprite

Sprite adalah istilah dalam game programming yang mengacu pada gambar karakter-karakter pada sebuah game. Ciri utama sprite adalah mengandung informasi transparansi dimana warna-warna yang dianggap transparan (color key) tidak akan ditampilkan. Kursor mouse adalah contoh sprite.

ID3DXSprite

D3DX menyediakan interface untuk mempermudah mainpulasi sprite menggunakan ID3DXSprite.

Menciptakan Sprite

Fungsi D3DXCreateSprite() kita pergunakan untuk mendapatkan pointer instance ID3DXSprite.
function D3DXCreateSprite(const pDevice : IDirect3DDevice8; 
                        out ppSprite : ID3DXSprite) : HResult; stdcall;
Contoh:
D3DXCreateSprite(d3dDev,aSprite);

Memulai dan Mengahiri proses Menggambar Sprite

Untuk memulai proses menggambar sprite, kita harus memanggil ID3DXSprite.Begin dan ID3DXSprite.End. Perhatikan bahwa karena Begin dan End adalah kata tercadang, maka pada header konversi, fungsi ini di rename menjadi _Begin dan _End
aSprite._Begin;
//lakukan sesuatu dengan sprite
aSprite._End;

Implementasi Aplikasi Animasi

Cukup sekian dulu introduksi Direct3D, mari kita buat implementasi framework untuk aplikasi 2D. Kita akan membuat aplikasi dimana kita akan dapat menggerakkan karakter Guile (Street Fighter), mengontrol Guile untuk menampilkan aksi memukul dan menendang.

TEngine

Kita akan membuat wrapper class untuk proses inisialisasi Direct3D dan rendering. Fitur yang dimiliki kelas ini adalah:
  • Memiliki metode untuk proses inisialisasi device yang diimplementasi dalam procedure InitDevice;
  • Memiliki metode untuk membersihkan buffer melalui procedure Clear
  • Memiliki metode untuk menampilkan proses rendering.
  • Memiliki metode untuk menggambar semua obyek 3D yang diimplementasi oleh procedure abstrak Draw. Kelas turunan harus mengimplementasikan metode ini.

T3DCollection

Adalah kelas basis untuk kumpulan item-item pada engine 3D ini. Kelas ini memiliki sebuah property bertipe Tengine dan diturunkan dari TCollection

Texture wrapper class

Kelas enkapsulasi manajemen tekstur terdiri atas dua TtextureCollection dan Ttexture. Ttexture menghandle inisialisasi IDirect3DTexture8 dan loading file gambar. TtextureCollection menghandle manajemen Ttexture. TtextureCollection diturunkan dari T3Dcollection. Ttextur sendiri diturunkan dari TcollectionItem
unit u_3D_engine;

interface
uses classes,windows,sysutils,directxGraphics;


type

  E3DError=class(Exception);

  TEngineParam=record
     Handle:HWND;
     Fullscreen:boolean;
     ResolutionWidth:integer;
     ResolutionHeight:integer;
     TotalBackBuffer:integer;
     ColorDepth:integer;
     InitDeviceOnCreate:boolean;
  end;

  TEngine=class(TObject)
  private
    FDirect3DObj:IDirect3D8;
    FDirect3DDevice:IDirect3DDevice8;

    function CreateD3D:IDirect3D8;
    function CreateDevice(const d3d:IDirect3D8):IDirect3DDevice8;
    function HwVertexProcSupported:boolean;
  protected
    FEngineParam:TEngineParam;
    FPresentParam:TD3DPresent_Parameters;

    procedure BeginDraw;
    procedure Draw;virtual;abstract;
    procedure EndDraw;
  public
    constructor Create(Param:TEngineParam);virtual;
    destructor Destroy;override;

    procedure InitDevice;

    procedure Clear(const ClearCol:TD3DColor);
    procedure Render;
  published
    property Direct3DObj:IDirect3D8 read FDirect3DObj;
    property Direct3DDevice:IDirect3DDevice8 read FDirect3DDevice;
  end;

  T3DCollection=class(TCollection)
  private
    FEngine: TEngine;
    procedure SetEngine(const Value: TEngine);
  published
    property Engine:TEngine read FEngine write SetEngine;
  end;

  T3DObject=class(TCollectionItem)
  private
  protected
    FVertexBuff:IDirect3DVertexBuffer8;
    FIndexBuff:IDirect3DIndexBuffer8;
  public
    procedure Init;virtual;abstract;
    procedure Draw;virtual;abstract;
  end;

  TTexture=class(TCollectionItem)
  private
    FTextureObj: IDirect3DTexture8;
    FColorKey: TD3DColor;
    procedure SetColorKey(const Value: TD3DColor);
  public
    destructor Destroy;override;
    procedure LoadFromFile(const filename:string);
  published
    property TextureObj:IDirect3DTexture8 read FTextureObj;
    property ColorKey:TD3DColor read FColorKey write SetColorKey;
  end;

  TTextureCollection=class(T3DCollection)
  end;


implementation
uses d3dx8;

{ TEngine }

procedure TEngine.BeginDraw;
begin
  if FDirect3DDevice<>nil then
    FDirect3DDevice.BeginScene;
end;

procedure TEngine.Clear(const ClearCol:TD3DColor);
begin
  if FDirect3DDevice<>nil then
    FDirect3DDevice.Clear(0,nil,D3DCLEAR_TARGET,ClearCol,0,0);

end;

constructor TEngine.Create(Param: TEngineParam);
begin
  FEngineParam:=Param;
  FDirect3DObj:=CreateD3D;
  if FEngineParam.InitDeviceOnCreate then
    InitDevice;
end;

function TEngine.CreateD3D: IDirect3D8;
begin
  result:=Direct3DCreate8(D3D_SDK_VERSION);
end;


function TEngine.CreateDevice(const d3d: IDirect3D8): IDirect3DDevice8;
var hr:HResult;
   vertexProcessing:cardinal;
begin
  if d3dnil then
  begin
    ZeroMemory(@FPresentParam,sizeof(TD3DPresent_Parameters));

    FPresentParam.Windowed:=not FEngineParam.Fullscreen;
    FPresentParam.BackBufferWidth:=FEngineParam.ResolutionWidth;
    FPresentParam.BackBufferHeight:=FEngineParam.ResolutionHeight;
    FPresentParam.BackBufferCount:=FEngineParam.TotalBackBuffer;
    FPresentParam.BackBufferFormat:=FEngineParam.ColorDepth;
    FPresentParam.SwapEffect:=D3DSWAPEFFECT_DISCARD;

    //3D hardware acceleration tersedia?
    if HwVertexProcSupported then
      vertexProcessing:=D3DCREATE_HARDWARE_VERTEXPROCESSING
    else
      vertexProcessing:=D3DCREATE_SOFTWARE_VERTEXPROCESSING;

    hr:=d3d.CreateDevice(D3DADAPTER_DEFAULT,
                   D3DDEVTYPE_HAL,
                   FEngineParam.Handle,
                   vertexProcessing,
                   FPresentParam,
                   result);
    if result=nil then
      raise E3DError.Create(D3DXErrorString(hr));
  end else
   result:=nil;


end;

destructor TEngine.Destroy;
begin
  FDirect3DDevice:=nil;
  FDirect3DObj:=nil;
  inherited;
end;

procedure TEngine.EndDraw;
begin
  if FDirect3DDevice<>nil then
    FDirect3DDevice.EndScene;

end;

function TEngine.HwVertexProcSupported: boolean;
var caps:TD3DCaps8;
begin
  FDirect3DObj.GetDeviceCaps(D3DADAPTER_DEFAULT,
                    D3DDEVTYPE_HAL,caps);

  result:=((caps.DevCaps and D3DDEVCAPS_HWTRANSFORMANDLIGHT)0);
end;

procedure TEngine.InitDevice;
begin
  FDirect3DDevice:=CreateDevice(FDirect3DObj);
end;


procedure TEngine.Render;
begin
  BeginDraw;
  try
    Draw;
    if FDirect3DDevice<>nil then
      FDirect3DDevice.Present(nil,nil,0,nil);
  finally
    EndDraw;
  end;

end;

{ T3DCollection }

procedure T3DCollection.SetEngine(const Value: TEngine);
begin
  FEngine := Value;
end;

{ TTexture }

destructor TTexture.Destroy;
begin
  FTextureObj:=nil;
  inherited;

end;

procedure TTexture.LoadFromFile(const filename: string);
var atexcollection:TTextureCollection;
    hr:HResult;
begin
  if Collection is TTextureCollection then
  begin
    aTexCollection:=Collection as TTextureCollection;
    if (aTexCollection.Engine.Direct3DDevice<>nil) and
       fileExists(filename) then
    begin
      hr:=D3DXCreateTextureFromFileEx(aTexCollection.Engine.Direct3DDevice,
                        PChar(filename),
                        D3DX_DEFAULT,
                        D3DX_DEFAULT,
                        D3DX_DEFAULT,
                        0,
                        D3DFMT_UNKNOWN,
                        D3DPOOL_MANAGED,
                        D3DX_FILTER_NONE,
                        D3DX_FILTER_NONE,
                        FColorKey,
                        nil,nil,
                        FTextureObj);
       if FTextureObj=nil then
        raise E3DError.Create(D3DXErrorString(hr));
    end;
  end;

end;

procedure TTexture.SetColorKey(const Value: TD3DColor);
begin
  FColorKey := Value;
end;

end.

Implementasi 2D Engine

OK, selanjutnya kita buat implementasi untuk menangani sprite. Proses manajemen sebuah sprite akan dienkapsulasi dalam kelas TSprite sedangkan TSprite diatur dalam TspriteCollection. Sama seperti
unit u2DSpr;

interface

uses classes,directxgraphics,d3dx8,u_3D_engine;

type
  TSpriteCollection=class(T3DCollection)
  private
     FSprite:ID3DXSprite;
     procedure Init;
  public
     destructor Destroy;override;
     procedure BeginDraw;
     procedure EndDraw;
  published
     property SpriteObj:ID3DXSprite read FSprite;
  end;

  TSprite=class(T3DObject)
  private
    FSprite:ID3DXSprite;
    FTexture: TTexture;
    FRotation: single;
    FColor: TD3DColor;
    FScaling: TD3DXVector2;
    FRotationCenter: TD3DXVector2;
    FTranslation: TD3DXVector2;
    FAlpha: byte;

    procedure SetTexture(const Value: TTexture);
    procedure SetColor(const Value: TD3DColor);
    procedure SetRotation(const Value: single);
    procedure SetRotationCenter(const Value: TD3DXVector2);
    procedure SetScaling(const Value: TD3DXVector2);
    procedure SetTranslation(const Value: TD3DXVector2);
    procedure SetAlpha(const Value: byte);
  public
    constructor Create(ACollection: TCollection);override;
    destructor Destroy;override;
    procedure Init;override;
    procedure Draw;override;

    procedure DrawEx(const translVect,
                              scaleVect,
                              rotCenter:PD3DXVector2;
                        const rot:single;
                        const Acolor:TD3DColor;
                        const alphaValue:byte);

    published
       property Texture:TTexture read FTexture write SetTexture;
       property Translation:TD3DXVector2 read FTranslation write SetTranslation;
       property X:single read FTranslation.x write FTranslation.x;
       property Y:single read FTranslation.y write FTranslation.y;
       property RotationCenter:TD3DXVector2 read FRotationCenter write SetRotationCenter;
       property Scaling:TD3DXVector2 read FScaling write SetScaling;
       property Rotation:single read FRotation write SetRotation;
       property Color:TD3DColor read FColor write SetColor;
       property Alpha:byte read FAlpha write SetAlpha;
    end;


implementation

{ TSprite }
constructor TSprite.Create(ACollection: TCollection);
var asprCollect:TSpriteCollection;
begin
  inherited;
  FSprite:=nil;
  if ACollection is TSpriteCollection then
  begin
    asprCollect:=ACollection as TSpriteCollection;
    if asprCollect.SpriteObj=nil then
      asprCollect.Init;

    FSprite:=asprCollect.SpriteObj;
  end;

  FTranslation.x:=0;
  FTranslation.Y:=0;

  FRotationCenter.x:=0;
  FRotationCenter.y:=0;
  FRotation:=0;

  FScaling.x:=1;
  FScaling.y:=1;
  FColor:=$FFFFFFFF;

end;

destructor TSprite.Destroy;
begin
  FSprite:=nil;
  inherited;
end;

procedure TSprite.Draw;
begin
  DrawEx(@FTranslation,
         @FScaling,
         @FRotationCenter,
         FRotation,
         FColor,
         FAlpha);
end;


procedure TSprite.DrawEx(const translVect, scaleVect,
  rotCenter: PD3DXVector2; const rot: single;
  const AColor:TD3DColor;const AlphaValue:byte);
begin
  if (FSprite<>nil) and
     (FTexture<>nil) and
     (FTexture.TextureObj<>nil) then
  begin
    FSprite.Draw(FTexture.TextureObj,nil,
                 scaleVect,
                 rotCenter,rot,
                 translVect,
                 (AlphaValue shl 24) or AColor);
  end;

end;

procedure TSprite.Init;
begin
end;

procedure TSprite.SetAlpha(const Value: byte);
begin
  FAlpha := Value;
end;

procedure TSprite.SetColor(const Value: TD3DColor);

begin
  FColor := Value;
end;

procedure TSprite.SetRotation(const Value: single);
begin
  FRotation := Value;
end;

procedure TSprite.SetRotationCenter(const Value: TD3DXVector2);
begin
  FRotationCenter := Value;

end;

procedure TSprite.SetScaling(const Value: TD3DXVector2);
begin
  FScaling := Value;
end;

procedure TSprite.SetTexture(const Value: TTexture);
begin
  FTexture := Value;
end;


procedure TSprite.SetTranslation(const Value: TD3DXVector2);
begin
  FTranslation := Value;
end;

{ TSpriteCollection }

procedure TSpriteCollection.BeginDraw;
begin
  if FSprite<>nil then
    FSprite._Begin;
end;


destructor TSpriteCollection.Destroy;
begin
  FSprite:=nil;
  inherited;
end;

procedure TSpriteCollection.EndDraw;
begin
  if FSprite<>nil then
    FSprite._End;
end;


procedure TSpriteCollection.Init;
begin
  if (Engine<>nil) and (Engine.Direct3DDevice<>nil) then
    D3DXCreateSprite(Engine.Direct3DDevice,FSprite)
  else
    FSprite:=nil;
end;


Tidak ada komentar:

Posting Komentar