unit _Scene;

interface

uses
  _Forms,
  _Geometry,
  _OpenGL,    
  OpenGL,
  Windows,
  Classes;

const
  MAX_SCENES = 5;
  SM_NONE = -1;
  SM_GAME = 0;
  SM_MENU = 1;
  SM_LOGO = 2;

type
TBasicScene =  class
  public
    procedure Reset;
    procedure Pause;
    procedure Render; virtual; abstract;
    procedure Update(var Delta: double); virtual; abstract;
    procedure Control; virtual; abstract;
  end;

TSceneManager = class(TBasicScene)
  private
    FSceneList:    TList;
    FCurrentScene: TBasicScene;
  public
    constructor Create(DefaultScene: TBasicScene);
    function  AddScene(NewScene: TBasicScene): TBasicScene;
    procedure SetScene(Scene: TBasicScene);
    procedure Render; override;
    procedure Update(var Delta: double); override;
    procedure Control; override;
    procedure Free;
end;

TRender = class;  

TCamera = class;

TScene = class(TBasicScene)//OpenGL renderable scene factory
  private
    FRenderCount: Byte;
    FRender: array of TRender;
    FCamera: TCamera;
  public
    ClearColor: TVector3;
    Constructor Create(OpenGL: TOpenGL);
    function AddRender(NewRender: TRender): TRender;
    procedure Render; override;
    procedure Update(var Delta: double); override;
    procedure Control; override;
    procedure Free;
    property Camera: TCamera read FCamera write FCamera;
end;

TRender = class(TBasicScene)//OpenGL Render
  private
    procedure SetParentScene(ParentScene: TScene);
  public
    FParentScene: TScene;
    procedure Render; virtual; 
    procedure Update(var Delta: double); virtual;
    procedure Control; virtual;
end;

//Cameras

TCamera = class    
  private
    FView: TMatrix4;
    FAngles: TVector2; //x, y angle
    FFocusDistance: Single;
    FPosition: TVector3;
  public
    constructor Create;
    procedure UseCamera; overload;
    property Position: TVector3 read FPosition write FPosition;
    property Distance: Single read FFocusDistance write FFocusDistance;
    property Angles: TVector2 read FAngles write FAngles;
end;

var
  SceneManager:TSceneManager;

implementation

//BasicScene
{$REGION 'Basic Scene'}
procedure TBasicScene.Reset;
begin

end;

procedure TBasicScene.Pause;
begin

end;
{$ENDREGION}

//Scene Manager
{$REGION 'Scene Manager'}
constructor TSceneManager.Create(DefaultScene:TBasicScene);
begin
  FSceneList:= TList.Create;
  FCurrentScene:=DefaultScene;
  FSceneList.Add(DefaultScene);  
  FCurrentScene.Reset;
end;

procedure TSceneManager.Render;
begin
  FCurrentScene.Render;   
end;

procedure TSceneManager.Update(var Delta: double);
begin
  FCurrentScene.Update(Delta);
end;

procedure TSceneManager.Control;
begin
  FCurrentScene.Control;
end;

function TSceneManager.AddScene(NewScene: TBasicScene): TBasicScene;
begin
  FSceneList.Add(NewScene);
  Result:= NewScene;
end;

procedure TSceneManager.SetScene(Scene: TBasicScene);
var
  i: Byte;
  inList: Boolean;
begin
  FCurrentScene.Pause;
  inList:= false;
  for i:=0 to FSceneList.Count - 1 do
    if (FSceneList[i] = Scene) then
      begin
        FCurrentScene:= Scene;
        inList:= true;
        Break;
      end;
  if not inList then
    begin
      FSceneList.Add(Scene);
      FCurrentScene:= Scene;
    end;             
  FCurrentScene.Reset;
end;

procedure TSceneManager.Free;
var
  i: Byte;
begin
  FCurrentScene.Free;
  for i:=0 to FSceneList.Count - 1 do
    TBasicScene(FSceneList.Items[i]).Free;
  FSceneList.Free;
end;
{$ENDREGION}

//Scene Render
{$REGION 'Scene'}
constructor TScene.Create(OpenGL: TOpenGL);
begin
  GL:= OpenGL;
  FRenderCount:= 0;          
  SetLength(FRender, FRenderCount + 1);
  ClearColor.Create(0, 0, 0);
  Self.FCamera:= TCamera.Create;
end;

function TScene.AddRender(NewRender: TRender): TRender;
begin
  FRender[FRenderCount]:= NewRender;
  FRender[FRenderCount].SetParentScene(Self);
  FRenderCount:= FRenderCount + 1;
  SetLength(FRender, FRenderCount+1);
  Result:= FRender[FRenderCount];
end;

procedure TScene.Render;
var
  i: Byte;
begin                
  gl.BeginRender;
  GL.ClearColor(ClearColor);
  Camera.UseCamera;
  for i:=0 to FRenderCount - 1 do
    if FRender[i] <> nil then
      FRender[i].Render;
  gl.EndRender;
end;

procedure TScene.Update(var Delta: double);
var
  i: Byte;
begin
  for i:=0 to FRenderCount - 1 do
    if FRender[i] <> nil then
      FRender[i].Update(Delta);
end;

procedure TScene.Control;
var
  i: Byte;
begin
  for i:=0 to FRenderCount - 1 do
    if FRender[i] <> nil then
      FRender[i].Control;
end;

procedure TScene.Free;    
var
  i: Byte;
begin
  for i:=0 to FRenderCount - 1 do
    if FRender[i] <> nil then
      FRender[i].Free;
end;
{$ENDREGION}

//Render
{$REGION 'Render'}   
procedure TRender.SetParentScene(ParentScene: TScene);
begin
  FParentScene:= ParentScene;
end;

procedure TRender.Render;
begin

end;

procedure TRender.Update(var Delta: double);
begin

end;

procedure TRender.Control;
begin

end;
{$ENDREGION}

//Camera
{$REGION 'Basic Camera'}
constructor TCamera.Create;
begin
  FView:= IDENTITY_MATRIX4;   
  FAngles.Vector2(0, 0);
  FFocusDistance:= 0;
  Position.Vector3(0, 0, 0);
end;

procedure TCamera.UseCamera;
begin
  //FAngles.X:= FAngles.X + 1;
  FView.Create(IDENTITY_MATRIX3, FPosition);
  FView:=
      FView *
      Matrix4(
        Rotate(FAngles.X, Vector3(0, 0, 1)) * Rotate(FAngles.Y, Vector3(-1, 0, 0)),
        Vector3(0, 0, FFocusDistance));
  glMultMatrixf(@FView);
end;


{$ENDREGION}
end.
