//DJXJet2D v1.0
unit DJXJet2D;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  DJXTextures,
  DJXTimer,
  DJXRender,
  DJXClasses,
  DJXUtils,
  DJX,
  D3DX9,
  Direct3D9;


type
  TDJXJet2D = class;

  TDJXJet2D = class
  private
    m_DJX: TDanJetX;
    m_VB: TDJXVB;
    m_IB: TDJXIB;
    m_Render: TDJXVBRender;
    m_MaxConstants: DWORD;
    m_ConstantsPerSprite: DWORD;
    m_ConstantsOffset: DWORD;
    m_SpriteCount: integer;
    m_MaxSprites: integer;
    m_PrevTexture: IDirect3DBaseTexture9;
    m_PrevVB: IDirect3DVertexBuffer9;
    m_PrevIB: IDirect3DIndexBuffer9;
    m_PrevVBOffset: DWORD;
    m_PrevVBStride: DWORD;
    m_PrevFVF: DWORD;
    m_PrevVS: IDirect3DVertexShader9;
    m_CurTexture: IDirect3DBaseTexture9;
    m_CurSprites: integer;
    m_VS: IDirect3DVertexShader9;
    m_ShaderEnable: boolean;
    m_VBPtr: Pointer;
  protected
    procedure InitVB(SpriteCount: integer);
    procedure InitIB(SpriteCount: integer);
    procedure Render;
    procedure CreateShader;
  public
    constructor Create(DanJetX: TDanJetX; AutoInit: boolean = true);
    destructor Destroy; override;
    property VS: boolean read m_ShaderEnable write m_ShaderEnable;
    procedure Init(MaxSprites: integer = 0);
    procedure BeginRender;
    procedure EndRender;
    procedure Draw4Col(
      x0, y0, x1, y1, x2, y2, x3, y3: single;
      Col0, Col1, Col2, Col3: TDJXColor;
      Texture: TDJXTexture;
      tu0, tv0, tu1, tv1, tu2, tv2, tu3, tv3: single
    );
    procedure Draw(
      X, Y, Width, Height: single;
      Rotation: single;
      Color: TDJXColor;
      Texture: TDJXTexture;
      TexCoordLeft, TexCoordTop, TexCoordRight, TexCoordBottom: single
    ); overload;
    procedure Draw(
      X, Y, Width, Height: single;
      Rotation: single;
      Color: TDJXColor;
      Texture: TDJXTexture;
      FlipTopBottom: boolean = false;
      FlipLeftRight: boolean = false;
      ImageIndex: integer = 0;
      TileWidth: integer = 0;
      TileHeight: integer = 0
    ); overload;
    procedure OnLostDevice;
    procedure OnResetDevice;
  end;

implementation

//TDJXJet2D BEGIN
constructor TDJXJet2D.Create(DanJetX: TDanJetX; AutoInit: boolean = true);
begin
  inherited Create;
  m_DJX := DanJetX;
  m_Render := TDJXVBRender.Create(m_DJX);
  m_VB := TDJXVB.Create(m_DJX);
  m_IB := TDJXIB.Create(m_DJX);
  m_ConstantsPerSprite := 5;
  m_ConstantsOffset := 3;
  m_MaxConstants := m_DJX.DeviceCaps.MaxVertexShaderConst;
  m_MaxSprites := trunc((m_MaxConstants - m_ConstantsOffset) / m_ConstantsPerSprite);
  m_ShaderEnable := false;
  if AutoInit then
  Init(m_MaxSprites);
  CreateShader;
end;
destructor TDJXJet2D.Destroy;
begin
  m_VB.Free;
  m_IB.Free;
  m_Render.Free;
  safe_release(m_CurTexture);
  safe_release(m_PrevTexture);
  safe_release(m_VS);
  inherited Destroy;
end;

procedure TDJXJet2D.InitVB(SpriteCount: integer);
var
  i: integer;
begin
  m_VB.Init(SpriteCount * 4, D3DUSAGE_WRITEONLY);
  for i := 0 to SpriteCount - 1 do
  begin
    m_VB.Vertices[i * 4 + 0] := DJXVertexFlex(-0.5, -0.5, 1, 0, 0, 0, $ffffffff, i * m_ConstantsPerSprite);
    m_VB.Vertices[i * 4 + 1] := DJXVertexFlex(0.5, -0.5, 1, 0, 0, 0, $ffffffff, i * m_ConstantsPerSprite);
    m_VB.Vertices[i * 4 + 2] := DJXVertexFlex(0.5, 0.5, 1, 0, 0, 0, $ffffffff, i * m_ConstantsPerSprite);
    m_VB.Vertices[i * 4 + 3] := DJXVertexFlex(-0.5, 0.5, 1, 0, 0, 0, $ffffffff, i * m_ConstantsPerSprite);
  end;
  m_VB.SetVertices(0, SpriteCount * 4);
  m_VBPtr := m_VB.VertexPointer;
end;
procedure TDJXJet2D.InitIB(SpriteCount: integer);
var
  i: integer;
begin
  m_IB.Init(SpriteCount * 6, D3DUSAGE_WRITEONLY);
  for i := 0 to SpriteCount - 1 do
  begin
    m_IB.Indices[i * 6 + 0] := i * 4 + 0;
    m_IB.Indices[i * 6 + 1] := i * 4 + 1;
    m_IB.Indices[i * 6 + 2] := i * 4 + 3;
    m_IB.Indices[i * 6 + 3] := i * 4 + 1;
    m_IB.Indices[i * 6 + 4] := i * 4 + 2;
    m_IB.Indices[i * 6 + 5] := i * 4 + 3;
  end;
  m_IB.SetIndices(0, SpriteCount * 6);
end;
procedure TDJXJet2D.Render;
begin
  if not m_ShaderEnable then
  m_VB.SetVertices(0, m_CurSprites * 4);
  m_Render.DrawIndexedTriangleList(0, 0, m_CurSprites * 4, 0, m_CurSprites * 2);
  m_CurSprites := 0;
end;
procedure TDJXJet2D.CreateShader;
const
  Jet2D: array[0..195] of byte = (
    $00,$02,$FE,$FF,$51,$00,$00,$05,$00,$00,
    $0F,$A0,$00,$00,$00,$40,$00,$00,$80,$3F,
    $00,$00,$00,$3F,$00,$00,$00,$00,$1F,$00,
    $00,$02,$00,$00,$00,$80,$00,$00,$0F,$90,
    $1F,$00,$00,$02,$05,$00,$00,$80,$01,$00,
    $0F,$90,$02,$00,$00,$03,$00,$00,$01,$80,
    $01,$00,$00,$90,$02,$00,$00,$A0,$2E,$00,
    $00,$02,$00,$00,$01,$B0,$00,$00,$00,$80,
    $15,$00,$00,$04,$00,$00,$07,$80,$00,$00,
    $E4,$90,$00,$20,$E4,$A0,$00,$00,$00,$B0,
    $04,$00,$00,$04,$00,$00,$03,$C0,$00,$00,
    $E4,$80,$01,$00,$E4,$A0,$01,$00,$FE,$A0,
    $01,$00,$00,$02,$00,$00,$0C,$C0,$00,$00,
    $55,$A0,$02,$00,$00,$03,$00,$00,$0F,$80,
    $00,$00,$E4,$90,$00,$00,$AA,$A0,$04,$00,
    $00,$06,$00,$00,$03,$E0,$00,$00,$E4,$80,
    $03,$20,$FE,$A0,$00,$00,$00,$B0,$03,$20,
    $E4,$A0,$00,$00,$00,$B0,$01,$00,$00,$03,
    $00,$00,$0F,$D0,$04,$20,$E4,$A0,$00,$00,
    $00,$B0,$FF,$FF,$00,$00
  );
begin
  m_DJX.Device.CreateVertexShader(@Jet2D, m_VS);
end;

procedure TDJXJet2D.Init(MaxSprites: integer = 0);
begin
  if m_ShaderEnable then
  begin
    m_VB.VF_Color := false;
    m_VB.VF_Normals := false;
    m_VB.VF_TexCount := 1;
    m_VB.VF_2D := false;
  end
  else
  begin
    m_VB.VF_Color := true;
    m_VB.VF_Normals := false;
    m_VB.VF_TexCount := 1;
    m_VB.VF_2D := true;
  end;
  if ((MaxSprites > m_MaxSprites) and m_ShaderEnable) or (MaxSprites = 0) then
  m_SpriteCount := m_MaxSprites
  else
  m_SpriteCount := MaxSprites;
  InitVB(m_SpriteCount);
  InitIB(m_SpriteCount);
end;
procedure TDJXJet2D.BeginRender;
var
  v4_ScreenTransform: TD3DXVector4;
  v4_ConstOffset: TD3DXVector4;
begin
  m_DJX.Device.GetTexture(0, m_PrevTexture);
  m_DJX.Device.GetStreamSource(0, m_PrevVB, m_PrevVBOffset, m_PrevVBStride);
  m_DJX.Device.GetFVF(m_PrevFVF);
  m_DJX.Device.GetIndices(m_PrevIB);
  m_DJX.Device.GetVertexShader(m_PrevVS);
  m_CurTexture := m_PrevTexture;
  m_CurSprites := 0;
  m_VB.SetToDevice;
  m_IB.SetToDevice;
  if m_ShaderEnable then
  begin
    v4_ScreenTransform := D3DXVector4(2 / m_DJX.ViewPort.Width, -2 / m_DJX.ViewPort.Height, -1, 1);
    v4_ConstOffset := D3DXVector4(m_ConstantsOffset, 0, 0, 0);
    m_DJX.Device.SetVertexShaderConstantF(1, @v4_ScreenTransform, 1);
    m_DJX.Device.SetVertexShaderConstantF(2, @v4_ConstOffset, 1);
    m_DJX.Device.SetVertexShader(m_VS);
  end;
end;
procedure TDJXJet2D.EndRender;
begin
  if (m_CurSprites > 0) then Render;
  m_DJX.Device.SetTexture(0, m_PrevTexture);
  m_DJX.Device.SetStreamSource(0, m_PrevVB, m_PrevVBOffset, m_PrevVBStride);
  m_DJX.Device.SetFVF(m_PrevFVF);
  m_DJX.Device.SetIndices(m_PrevIB);
  m_DJX.Device.SetVertexShader(m_PrevVS);
end;
procedure TDJXJet2D.Draw4Col(
      x0, y0, x1, y1, x2, y2, x3, y3: single;
      Col0, Col1, Col2, Col3: TDJXColor;
      Texture: TDJXTexture;
      tu0, tv0, tu1, tv1, tu2, tv2, tu3, tv3: single
    );
type
  TVertex = record
    x, y, z, rhw: single;
    color: DWORD;
    tu, tv: single;
  end;
  PVertexArray = ^TVertexArray;
  TVertexArray = array[0..0] of TVertex;
var
  Vertices: PVertexArray;
begin
  if m_ShaderEnable then exit;
  if (m_CurTexture <> Texture.Texture) then
  begin
    if (m_CurSprites > 0) then 
    Render;
    Texture.SetToDevice(0);
    m_CurTexture := Texture.Texture;
  end;

  Vertices := m_VBPtr;
  Vertices^[m_CurSprites * 4 + 0].x := x0;
  Vertices^[m_CurSprites * 4 + 0].y := y0;
  Vertices^[m_CurSprites * 4 + 0].z := 1;
  Vertices^[m_CurSprites * 4 + 0].rhw := 1;
  Vertices^[m_CurSprites * 4 + 0].color := Col0;
  Vertices^[m_CurSprites * 4 + 0].tu := tu0;
  Vertices^[m_CurSprites * 4 + 0].tv := tv0;
  Vertices^[m_CurSprites * 4 + 1].x := x1;
  Vertices^[m_CurSprites * 4 + 1].y := y1;
  Vertices^[m_CurSprites * 4 + 1].z := 1;
  Vertices^[m_CurSprites * 4 + 1].rhw := 1;
  Vertices^[m_CurSprites * 4 + 1].color := Col1;
  Vertices^[m_CurSprites * 4 + 1].tu := tu1;
  Vertices^[m_CurSprites * 4 + 1].tv := tv1;
  Vertices^[m_CurSprites * 4 + 2].x := x2;
  Vertices^[m_CurSprites * 4 + 2].y := y2;
  Vertices^[m_CurSprites * 4 + 2].z := 1;
  Vertices^[m_CurSprites * 4 + 2].rhw := 1;
  Vertices^[m_CurSprites * 4 + 2].color := Col2;
  Vertices^[m_CurSprites * 4 + 2].tu := tu2;
  Vertices^[m_CurSprites * 4 + 2].tv := tv2;
  Vertices^[m_CurSprites * 4 + 3].x := x3;
  Vertices^[m_CurSprites * 4 + 3].y := y3;
  Vertices^[m_CurSprites * 4 + 3].z := 1;
  Vertices^[m_CurSprites * 4 + 3].rhw := 1;
  Vertices^[m_CurSprites * 4 + 3].color := Col3;
  Vertices^[m_CurSprites * 4 + 3].tu := tu3;
  Vertices^[m_CurSprites * 4 + 3].tv := tv3;
    
  inc(m_CurSprites);
  if m_CurSprites >= m_MaxSprites then
  Render;
end;
procedure TDJXJet2D.Draw(
      X, Y, Width, Height: single;
      Rotation: single;
      Color: TDJXColor;
      Texture: TDJXTexture;
      TexCoordLeft, TexCoordTop, TexCoordRight, TexCoordBottom: single
    );
type
  TVertex = record
    x, y, z, rhw: single;
    color: DWORD;
    tu, tv: single;
  end;
  PVertexArray = ^TVertexArray;
  TVertexArray = array[0..0] of TVertex;
var
  v4_TexCoords: TD3DXVector4;
  m4x4_Transform: TD3DXMatrix;
  m4x4_Scale, m4x4_Rotation, m4x4_Position: TD3DXMatrix;
  v4_Color: TD3DXVector4;
  v2_Pos: TD3DXVector2;
  Vertices: PVertexArray;
begin
  if (m_CurTexture <> Texture.Texture) then
  begin
    if (m_CurSprites > 0) then 
    Render;
    Texture.SetToDevice(0);
    m_CurTexture := Texture.Texture;
  end;
  
  if m_ShaderEnable then
  begin
    v4_TexCoords := D3DXVector4(TexCoordLeft, TexCoordTop, TexCoordRight - TexCoordLeft, TexCoordBottom - TexCoordTop);
    v4_Color := D3DXVector4(DJXColorR(Color) / 255, DJXColorG(Color) / 255, DJXColorB(Color) / 255, DJXColorA(Color) / 255);
    D3DXMatrixScaling(m4x4_Scale, Width, Height, 1);
    D3DXMatrixRotationZ(m4x4_Rotation, Rotation);
    D3DXMatrixTranslation(m4x4_Position, Width / 2 + X, Height / 2 + Y, 0);
    D3DXMatrixMultiply(m4x4_Transform, m4x4_Scale, m4x4_Rotation);
    D3DXMatrixMultiplyTranspose(m4x4_Transform, m4x4_Transform, m4x4_Position);
    m_DJX.Device.SetVertexShaderConstantF(
      m_ConstantsOffset + m_CurSprites * m_ConstantsPerSprite + 0,
      @m4x4_Transform, 3
    );
    m_DJX.Device.SetVertexShaderConstantF(
      m_ConstantsOffset + m_CurSprites * m_ConstantsPerSprite + 3,
      @v4_TexCoords, 1
    );
    m_DJX.Device.SetVertexShaderConstantF(
      m_ConstantsOffset + m_CurSprites * m_ConstantsPerSprite + 4,
      @v4_Color, 1
    );
  end
  else
  begin
    Vertices := m_VBPtr;
    D3DXMatrixScaling(m4x4_Scale, Width, Height, 1);
    D3DXMatrixRotationZ(m4x4_Rotation, Rotation);
    D3DXMatrixTranslation(m4x4_Position, Width / 2 + X, Height / 2 + Y, 0);
    D3DXMatrixMultiply(m4x4_Transform, m4x4_Scale, m4x4_Rotation);
    D3DXMatrixMultiply(m4x4_Transform, m4x4_Transform, m4x4_Position);
    v2_Pos := D3DXVector2(-0.5, -0.5);
    D3DXVec2TransformCoord(v2_Pos, v2_Pos, m4x4_Transform);
    Vertices^[m_CurSprites * 4 + 0].x := v2_Pos.x;
    Vertices^[m_CurSprites * 4 + 0].y := v2_Pos.y;
    Vertices^[m_CurSprites * 4 + 0].z := 1;
    Vertices^[m_CurSprites * 4 + 0].rhw := 1;
    Vertices^[m_CurSprites * 4 + 0].color := Color;
    Vertices^[m_CurSprites * 4 + 0].tu := TexCoordLeft;
    Vertices^[m_CurSprites * 4 + 0].tv := TexCoordTop;
    v2_Pos := D3DXVector2(0.5, -0.5);
    D3DXVec2TransformCoord(v2_Pos, v2_Pos, m4x4_Transform);
    Vertices^[m_CurSprites * 4 + 1].x := v2_Pos.x;
    Vertices^[m_CurSprites * 4 + 1].y := v2_Pos.y;
    Vertices^[m_CurSprites * 4 + 1].z := 1;
    Vertices^[m_CurSprites * 4 + 1].rhw := 1;
    Vertices^[m_CurSprites * 4 + 1].color := Color;
    Vertices^[m_CurSprites * 4 + 1].tu := TexCoordRight;
    Vertices^[m_CurSprites * 4 + 1].tv := TexCoordTop;
    v2_Pos := D3DXVector2(0.5, 0.5);
    D3DXVec2TransformCoord(v2_Pos, v2_Pos, m4x4_Transform);
    Vertices^[m_CurSprites * 4 + 2].x := v2_Pos.x;
    Vertices^[m_CurSprites * 4 + 2].y := v2_Pos.y;
    Vertices^[m_CurSprites * 4 + 2].z := 1;
    Vertices^[m_CurSprites * 4 + 2].rhw := 1;
    Vertices^[m_CurSprites * 4 + 2].color := Color;
    Vertices^[m_CurSprites * 4 + 2].tu := TexCoordRight;
    Vertices^[m_CurSprites * 4 + 2].tv := TexCoordBottom;
    v2_Pos := D3DXVector2(-0.5, 0.5);
    D3DXVec2TransformCoord(v2_Pos, v2_Pos, m4x4_Transform);
    Vertices^[m_CurSprites * 4 + 3].x := v2_Pos.x;
    Vertices^[m_CurSprites * 4 + 3].y := v2_Pos.y;
    Vertices^[m_CurSprites * 4 + 3].z := 1;
    Vertices^[m_CurSprites * 4 + 3].rhw := 1;
    Vertices^[m_CurSprites * 4 + 3].color := Color;
    Vertices^[m_CurSprites * 4 + 3].tu := TexCoordLeft;
    Vertices^[m_CurSprites * 4 + 3].tv := TexCoordBottom;
  end;
  inc(m_CurSprites);
  if m_CurSprites >= m_MaxSprites then
  Render;
end;
procedure TDJXJet2D.Draw(
      X, Y, Width, Height: single;
      Rotation: single;
      Color: TDJXColor;
      Texture: TDJXTexture;
      FlipTopBottom: boolean = false;
      FlipLeftRight: boolean = false;
      ImageIndex: integer = 0;
      TileWidth: integer = 0;
      TileHeight: integer = 0
    );
var
  tu0, tv0, tu1, tv1, tu2, tv2, tu3, tv3: single;
begin
  SetTileTexture(
    tu0, tv0, tu1, tv1, tu2, tv2, tu3, tv3,
    Texture, FlipTopBottom, FlipLeftRight,
    ImageIndex, TileWidth, TileHeight,
    m_DJX.TruncatePixel
  );
  Draw(
    X, Y, Width, Height,
    Rotation, Color, Texture,
    tu0, tv0, tu2, tv2
  );
end;
procedure TDJXJet2D.OnLostDevice;
begin
  safe_release(m_VS);
end;
procedure TDJXJet2D.OnResetDevice;
begin
  CreateShader;
end;
//TDJXJet2D END


end.