unit MainFm;
//---------------------------------------------------------------------------
// MainFm.pas                                           Modified: 02-Feb-2009
// Basic 3D example - Asphyre Sphinx                              Version 1.0
//---------------------------------------------------------------------------
// Important Notice:
//
// If you modify/use this code or one of its parts either in original or
// modified form, you must comply with Mozilla Public License v1.1,
// specifically section 3, "Distribution Obligations". Failure to do so will
// result in the license breach, which will be resolved in the court.
// Remember that violating author's rights is considered a serious crime in
// many countries. Thank you!
//
// !! Please *read* Mozilla Public License 1.1 document located at:
//  http://www.mozilla.org/MPL/
//---------------------------------------------------------------------------
// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is MainFm.pas.
//
// The Initial Developer of the Original Code is Yuriy Kotsarenko.
// Portions created by Yuriy Kotsarenko are Copyright (C) 2000 - 2009,
// Yuriy Kotsarenko. All Rights Reserved.
//---------------------------------------------------------------------------
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;

    procedure CreateLights();
  public
    { Public declarations }
  end;

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

//---------------------------------------------------------------------------
implementation
uses
 Vectors2, Vectors2px, Vectors3, AsphyreTypes, AsphyreTimer, AsphyreFactory,
 AsphyreDb, AbstractDevices, AsphyreImages, AsphyreFonts, DX7Providers,
 GameTypes, AbstractCanvas, AsphyreScenes, AsphyreMeshes, AbstractRasterizer,
 AsphyreLights, AsphyreColors;
{$R *.dfm}

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

 // This is the initial size of our rendering surface.
 DisplaySize:= Point2px(ClientWidth, ClientHeight);

 // In this example we use DirectX 7 provider. OpenGL and DirectX 9 providers
 // could also work. Add "OGLProviders" and "DX9Providers" to USES and then
 // instead of "idDirectX7" put "idOpenGL" or "idDirectX9".
 Factory.UseProvider(idDirectX7);

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

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

 GameScene:= TAsphyreScene.Create();
 GameScene.Raster     := GameRaster;
 GameScene.DisplaySize:= DisplaySize;

 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);

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

 // Create the lights that will be used in our 3D scene.
 CreateLights();

 // 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;

 FreeAndNil(GameFonts);
 FreeAndNil(GameImages);
 FreeAndNil(MediaASDb);
 FreeAndNil(GameScene);
 FreeAndNil(GameRaster);
 FreeAndNil(GameCanvas);
 FreeAndNil(GameDevice);
end;

//---------------------------------------------------------------------------
procedure TMainForm.OnDeviceCreate(Sender: TObject; Param: Pointer;
 var Handled: Boolean);
var
 Success: Boolean;
 Mesh: TAsphyreMesh;
begin
 Success:= PBoolean(Param)^;

 // Remove all previously loaded artwork if such exist, just in case.
 GameImages.RemoveAll();
 GameFonts.RemoveAll();

 // Load the font's image and the character descriptions.
 GameImages.AddFromASDb('Corbel.image', MediaASDb, '', False);
 fontCorbel:= GameFonts.Insert('/media.asdb | Corbel.xml', 'Corbel.image');

 // This image will be used as texture for our 3D cube.
 imageBricks:= GameImages.AddFromASDb('Bricks.image', MediaASDb);

 // This is our 3D cube itself.
 Mesh:= TAsphyreMesh.Create();
 if (not Mesh.LoadFromASDb('cube.mesh', MediaASDb)) then FreeAndNil(Mesh);

 if (Mesh <> nil) then
  meshCube:= Meshes.Include(Mesh);

 // Make sure everything has been loaded properly.
 Success:=
  Success and
  (imageBricks <> -1)and
  (meshCube <> -1)and
  (fontCorbel <> -1);

 PBoolean(Param)^:= Success;
end;

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

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

//---------------------------------------------------------------------------
procedure TMainForm.RenderEvent(Sender: TObject);
var
 Phi: Single;
begin
 // The following call is not exactly necessary (since it's called
 // automatically), unless you have drawn 2D stuff before that.
 GameRaster.ResetStates();

 // The following parameter is important to preserve 3D object sizes on
 // different resolutions.
 GameScene.AspectRatio:= DisplaySize.y / DisplaySize.x;

 // Below the view matrix is configured, which is basically our "camera" in
 // the 3D scene.
 ViewMtx.LoadIdentity();

 // Here we specify our camera position and orientation.
 ViewMtx.LookAt(
  // Where are we located?
  Vector3(150.0, 150.0, 150.0),
  // What position are we looking at?
  Vector3(0.0, 0.0, 0.0),
  // The following vector defines our camera's "roof" (i.e. camera's top)
  AxisYVec3);

 // The following call must always be made before drawing 3D stuff.
 GameScene.BeginScene();

 // Initially, rescale our cube to 100x100x100 size.
 // By default, it has unitary size of 1x1x1.
 WorldMtx.LoadIdentity();
 WorldMtx.Scale(100.0);

 // Rotate the cube and move it around.
 Phi:= GameTicks * Pi / 100.0;
 WorldMtx.RotateX(Phi);
 WorldMtx.Translate(Sin(Phi) * 50.0, Sin(Phi) * 50.0, Sin(Phi) * 50.0);

 // Place the cube mesh in our world scene.
 GameScene.Draw(Meshes[meshCube], WorldMtx.RawMtx, GameImages[imageBricks]);

 // The following call must always follow after rendering 3D stuff.
 GameScene.EndScene(ViewMtx.RawMtx);

 // The 3D scene has been made, now display it on the screen.
 GameScene.Present(
  Point2(DisplaySize.x * 0.5, DisplaySize.y * 0.5),
  DisplaySize);

 // The raster class is used to draw triangles on the screen from the 3D scene.
 // Make sure to flush its buffers before drawing 2D stuff.
 GameRaster.Flush();

 // In order to continue drawing 2D stuff, we need to make this call so that
 // the canvas is ready.
 GameCanvas.ResetStates();

 // Display the information text.
 GameFonts[fontCorbel].TextOut(
  Point2(4.0, 4.0),
  'FPS: ' + IntToStr(Timer.FrameRate),
  cColor2($FFFFEC99, $FFF78900));

 // The following call is not necessary here, unless you want to render 3D
 // stuff afterwards. To avoid having problems later on, leave it as is.
 GameCanvas.Flush();
end;

//---------------------------------------------------------------------------
procedure TMainForm.WMDisplayChange(var message: TMessage);
begin
 if (GameDevice <> nil)and(GameDevice.Active)and(GameDevice.Windowed) then
  GameDevice.Reset();
end;

//---------------------------------------------------------------------------
procedure TMainForm.CreateLights();
var
 Ambient: TAsphyreAmbientLight;
begin
 // Our scene is not going to be lit with anything, so we just set the ambient
 // light to maximum value.
 Ambient:= TAsphyreAmbientLight.Create();
 Ambient.Color:= cColor(255, 255, 255, 0);

 GameScene.Lights.Insert(Ambient);
end;

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