unit MainFm;

//---------------------------------------------------------------------------
interface

//---------------------------------------------------------------------------
uses
  Messages, SysUtils, Classes, Controls, Forms, Dialogs;

//---------------------------------------------------------------------------
type
  TMainForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    GameTicks: Integer;

    procedure OnDeviceCreate(Sender: TObject; Param: Pointer;
     var Handled: Boolean);

    procedure TimerEvent(Sender: TObject);
    procedure ProcessEvent(Sender: TObject);
    procedure RenderEvent(Sender: TObject);

    procedure WMDisplayChange(var message:TMessage); message WM_DISPLAYCHANGE;
  public
    { Public declarations }
  end;

//---------------------------------------------------------------------------
var
  MainForm: TMainForm;

//---------------------------------------------------------------------------
implementation
uses
 Vectors2, Vectors2px, AsphyreTimer, AsphyreFactory, AsphyreTypes, AsphyreDb,
 AbstractDevices, AsphyreImages, AsphyreFonts, DX9Providers, GameTypes,
 AbstractCanvas;
{$R *.dfm}

//---------------------------------------------------------------------------
procedure TMainForm.FormCreate(Sender: TObject);
begin
 // Enable Delphi debugger
 ReportMemoryLeaksOnShutdown:= DebugHook <> 0;

 // Set the display size
 DisplaySize:= Point2px(ClientWidth, ClientHeight);

 // Indicate that we're using DirectX 9
 Factory.UseProvider(idDirectX9);

 // Create Asphyre components in run-time.
 GameDevice:= Factory.CreateDevice();
 GameCanvas:= Factory.CreateCanvas();
 GameImages:= TAsphyreImages.Create();

 GameFonts:= TAsphyreFonts.Create();
 GameFonts.Images:= GameImages;
 GameFonts.Canvas:= GameCanvas;

 MediaASDb:= TASDb.Create();
 MediaASDb.FileName:= ExtractFilePath(ParamStr(0)) + 'media.asdb';
 MediaASDb.OpenMode:= opReadOnly;

 GameDevice.WindowHandle:= Self.Handle;
 GameDevice.Size    := DisplaySize;
 GameDevice.Windowed:= True;
 GameDevice.VSync   := False;

 EventDeviceCreate.Subscribe(OnDeviceCreate, 0);

 // Attempt to initialize Asphyre device.
 if (not GameDevice.Initialize()) then
  begin
   ShowMessage('Failed to initialize Asphyre device.');
   Application.Terminate();
   Exit;
  end;

 // Create rendering timer.
 Timer.OnTimer  := TimerEvent;
 Timer.OnProcess:= ProcessEvent;
 Timer.Speed    := 60.0;
 Timer.MaxFPS   := 4000;
 Timer.Enabled  := True;
end;

//---------------------------------------------------------------------------
procedure TMainForm.FormDestroy(Sender: TObject);
begin
 Timer.Enabled:= False;

 // Release all Asphyre components.
 FreeAndNil(GameFonts);
 FreeAndNil(GameImages);
 FreeAndNil(MediaASDb);
 FreeAndNil(GameCanvas);
 FreeAndNil(GameDevice);
end;

//---------------------------------------------------------------------------
procedure TMainForm.OnDeviceCreate(Sender: TObject; Param: Pointer;
 var Handled: Boolean);
var
 Success: Boolean;
begin
 // This variable returns "Success" to Device initialization, so if you
 // set it to False, device creation will fail.
 Success:= PBoolean(Param)^;

 GameImages.RemoveAll();
 GameFonts.RemoveAll();

 // This image is used by our bitmap font.
 GameImages.AddFromASDb('/tahoma9b.image', MediaASDb, '', False);

 fontTahoma9b:= GameFonts.Insert('/media.asdb | /tahoma9b.xml',
  'tahoma9b.image');

 imageLena:= GameImages.AddFromASDb('/lena.image', MediaASDb, '', True);

 Success:=
  Success and
  (imageLena <> -1)and
  (fontTahoma9b <> -1);

 PBoolean(Param)^:= Success;
end;

//---------------------------------------------------------------------------
procedure TMainForm.TimerEvent(Sender: TObject);
begin
 GameDevice.Render(RenderEvent, $000000);
 Timer.Process();
end;

//---------------------------------------------------------------------------
procedure TMainForm.ProcessEvent(Sender: TObject);
begin
 Inc(GameTicks);
end;

//---------------------------------------------------------------------------
procedure TMainForm.RenderEvent(Sender: TObject);
var
 j, i: Integer;
 Omega, Kappa: Single;
begin
 // Draw gray background.
 for j:= 0 to (DisplaySize.y div 40) - 1 do
  for i:= 0 to (DisplaySize.x div 40) - 1 do
   GameCanvas.FillQuad(
    pBounds4(i * 40, j * 40, 40, 40),
    cColor4($FF585858, $FF505050, $FF484848, $FF404040));

 for i:= 0 to (DisplaySize.x div 40) - 1 do
  GameCanvas.Line(
   Point2(i * 40.0, 0.0),
   Point2(i * 40.0, DisplaySize.y),
   $FF505050, $FF505050);

 for j:= 0 to (DisplaySize.y div 40) - 1 do
  GameCanvas.Line(
   Point2(0.0, j * 40.0),
   Point2(DisplaySize.x, j * 40.0),
   $FF505050, $FF505050);

 // Draw an animated hole.
 GameCanvas.QuadHole(
  Point2(0.0, 0.0),
  Point2(DisplaySize.x, DisplaySize.y),
  Point2(
   DisplaySize.x * 0.5 + Cos(GameTicks * 0.0073) * DisplaySize.x * 0.25,
   DisplaySize.y * 0.5 + Sin(GameTicks * 0.00312) * DisplaySize.y * 0.25),
  Point2(80.0, 100.0),
  $20FFFFFF, $80955BFF, 16);

 // Draw the image of famous Lenna.
 GameCanvas.MipMapping:= True;

 GameCanvas.UseImagePx(GameImages[imageLena], pxBounds4(0, 0, 512, 512));
 GameCanvas.TexMap(pRotate4c(
  Point2(400.0, 300.0),
  Point2(300.0, 300.0),
  GameTicks * 0.01),
  cAlpha4(128));

 // Draw an animated Arc.
 Omega:= GameTicks * 0.0274;
 Kappa:= 1.25 * Pi + Sin(GameTicks * 0.01854) * 0.5 * Pi;

 GameCanvas.FillArc(Point2(DisplaySize.x * 0.1, DisplaySize.y * 0.9),
  Point2(75.0, 50.0), Omega, Omega + Kappa, 32,
  cColor4($FFFF0000, $FF00FF00, $FF0000FF, $FFFFFFFF));

 // Draw an animated Ribbon.
 Omega:= GameTicks * 0.02231;
 Kappa:= 1.25 * Pi + Sin(GameTicks * 0.024751) * 0.5 * Pi;

 GameCanvas.FillRibbon(Point2(DisplaySize.x * 0.9, DisplaySize.y * 0.85),
  Point2(25.0, 20.0), Point2(70.0, 80.0), Omega, Omega + Kappa, 32,
  cColor4($FFFF0000, $FF00FF00, $FF0000FF, $FFFFFFFF));

 GameFonts[fontTahoma9b].TextOut(
  Point2(4.0, 4.0),
  'FPS: ' + IntToStr(Timer.FrameRate),
  cColor2($FFFFE887, $FFFF0000), 1.0);
end;

//---------------------------------------------------------------------------
procedure TMainForm.WMDisplayChange(var message: TMessage);
begin
 // This event happens when user changes screen resolution.
 //
 // We also detect if the screen resolution has changed because of our
 // full screen mode, in which case we simply exit.
 if (Tag = 1) then Exit;

 if (GameDevice <> nil)and(GameDevice.Active)and(GameDevice.Windowed) then
  GameDevice.Reset();
end;

//---------------------------------------------------------------------------
end.
